Compare commits

...

75 Commits

Author SHA1 Message Date
c665401fd8 use .call instead of native in Inventory peripheral 2025-05-23 15:39:44 +02:00
2799a0be3d added GeoScanner peripheral 2024-10-27 16:08:38 +01:00
e527dd5b6a added simple 3D navigation 2024-10-27 16:07:23 +01:00
fe17b4fd67 added inspect command to turtleCtl 2024-10-20 23:23:58 +02:00
90b4015ba1 get chunk from position 2024-10-20 22:20:33 +02:00
2dd85c2b26 added Tags class for list of classes 2024-10-20 22:18:23 +02:00
1d60e13792 idfk 2024-10-18 16:48:38 +02:00
9d6e8a366b fixed SinglePromise reset order 2024-10-18 16:37:46 +02:00
5167533c6d Ignore GPS messages from other dimension
refs #2
2024-10-17 00:14:39 +02:00
aac527ae89 added a timeout and improvements to GPS and INS
closes #6
2024-10-16 23:45:53 +02:00
af6a4c840b added SinglePromise and SingleTimeoutPromise 2024-10-16 23:42:21 +02:00
afbd1dfd68 improved Vector stuff 2024-10-16 23:39:07 +02:00
b305594ea4 improved networking 2024-10-15 01:19:36 +02:00
df7991763d added PaN 2024-10-14 21:42:52 +02:00
e0f8d274e7 renamed Pos and Pos3 2024-10-14 21:40:26 +02:00
08f41ccb0b constrained vec to only accept numbers 2024-10-14 12:11:14 +02:00
28ec48cc85 added PriorityQueue for later use 2024-10-12 20:58:10 +02:00
4f2b6e7c53 fixed uninitialized children array in RootElement 2024-08-17 01:11:38 +02:00
2e0bda7a6e renamed Perf to Peri 2024-08-17 00:58:01 +02:00
87c93f3ae0 TurtleCtl times parameter optional 2024-08-17 00:57:03 +02:00
628aef06e3 oops 2024-08-17 00:55:52 +02:00
2603527b67 pretty error reporting 2024-08-17 00:48:53 +02:00
39a7da716c improved logging system 2024-08-17 00:47:15 +02:00
afc0309222 switched from yarn to npm 2024-08-13 00:13:24 +02:00
0d9ce5d6b1 start terminal on boot when in debug 2024-08-13 00:11:37 +02:00
be58ed1c05 removed unused files 2024-08-13 00:10:47 +02:00
fe16799bbb added inventory peripheral 2024-08-12 15:16:32 +02:00
5f5e899f6e rewrote circle mine to use plans 2024-08-11 20:18:25 +02:00
8546659ae0 first implementation of turtle plans 2024-08-11 20:17:37 +02:00
249a48807a use digEmpty function in tunnel command 2024-08-05 17:09:23 +02:00
be405887e2 added digEmpty function 2024-08-05 17:09:02 +02:00
73e2ccdcb8 added turtle helper: combine 2024-08-05 17:01:41 +02:00
b05dae958d tunnel command to turtle ctl 2024-08-05 16:58:46 +02:00
3e993b84eb added turtle ctl dig command 2024-08-05 15:41:03 +02:00
09fef78f9a added doc for applications 2024-08-05 14:39:16 +02:00
7d30d9d971 added build doc 2024-08-05 13:59:23 +02:00
29285641a4 removed unused import 2024-08-05 13:54:31 +02:00
37eab6f8e2 debug guard for build info 2024-08-05 13:53:33 +02:00
f95c262c57 put new cli parser to use in CLIAppBase 2024-05-08 16:08:35 +02:00
5f42941d76 added cli arg parser 2024-05-08 16:07:11 +02:00
1108eab403 fixed line printing in Terminal 2024-05-08 15:33:40 +02:00
e5b990ae61 added CircleMine bin 2024-05-03 22:45:26 +02:00
5925f851c4 trace can now be used in debug mode 2024-05-02 15:09:21 +02:00
d13831f213 added DroneInterface peripheral stub #12 2024-05-02 15:07:40 +02:00
a9f6adcd9d added refuel functions to TurtleCtl 2024-05-02 15:04:15 +02:00
757d7098cf changed Item to be an enum abstract 2024-05-02 15:03:51 +02:00
4e69bda3c8 added fuel related functions to InvManager 2024-05-02 15:03:19 +02:00
755e5ff6b4 added getSlotsByCount 2024-05-02 15:02:51 +02:00
9353ccba8a refactored InvManager and State 2024-04-25 00:36:48 +02:00
5c71ab1c30 removed unused turtle stuff 2024-04-22 22:41:38 +02:00
36f97f09d5 redone turtle cli 2024-04-22 22:40:55 +02:00
e04021425a added native serialization for kv store
available as a flag: kv_use_native
2024-04-19 13:00:13 +02:00
d89956a626 added scroll back to terminal 2024-04-19 11:18:37 +02:00
dd146d1caf fixed inspect for Peripherals 2024-04-18 23:11:16 +02:00
adb758bd53 added unpack & moved build stuff 2024-04-16 11:05:28 +02:00
baae1428bd added exporter service 2024-04-13 12:08:02 +02:00
7bd18c1a4c added exporter and interface for redstone 2024-04-13 12:07:46 +02:00
99489e37df fixed hardcoded redstone type name 2024-04-13 12:06:55 +02:00
86f6dbf302 added Pocket 2024-04-10 14:40:45 +02:00
4836cae3fa added speaker peripheral 2024-04-09 20:07:36 +02:00
69ca3f3282 added RPC to Concepts 2024-03-21 00:22:53 +01:00
3484503ba1 added NameSystem 2024-03-21 00:03:15 +01:00
1efa8fa212 renamed to nameserver in kernel settings 2024-03-21 00:02:57 +01:00
9541b653b5 fixed ServiceManager not loading kvstore 2024-03-21 00:01:37 +01:00
4ab3d868c1 interface name consistency 2024-03-13 10:39:22 +01:00
2ab5a38894 removed SRSC 2024-03-13 10:27:31 +01:00
e3875f76f6 removed hello world examples 2024-03-12 21:48:10 +01:00
fdcb81b565 removed export system 2024-03-12 21:47:49 +01:00
72654b1036 added formatter install in makefile 2024-03-12 21:44:51 +01:00
fe85c33d64 remade RPC system 2024-03-12 21:44:08 +01:00
1d9e08641e added defrag to turtle command 2024-01-29 01:25:15 +01:00
638d1acbe0 TurtleSlot is now guaranteed to be in range 2024-01-29 01:24:49 +01:00
815ccdaab9 added equipTool to InvManager 2024-01-29 01:05:40 +01:00
c0741c48aa fixed null for args in process handle 2024-01-29 00:46:51 +01:00
e2ab296e79 added terminal args for runnig command on start 2024-01-29 00:10:21 +01:00
142 changed files with 4070 additions and 2225 deletions

View File

@@ -1,32 +1,57 @@
BUNDLE_NAME = bundle.lua BUNDLE_NAME = bundle.lua
HAXE_NAME = haxe.lua HAXE_NAME = haxe.lua
MINIFYD_NAME = bundle.min.lua MINIFYD_NAME = bundle.min.lua
HAXE_ZIP_NAME = "haxe.zip"
UNPACK_NAME = unpack.lua
UNPACK_POLYFILLED_NAME = unpack.polyfill.lua
UNPACK_MINIFYD_NAME = unpack.min.lua
BUILD_DIR = build BUILD_DIR = build
HAXE_FLAGS = HAXE_FLAGS = -D message.reporting=pretty
POLYFILLED_NAME = bundle.polyfill.lua POLYFILLED_NAME = bundle.polyfill.lua
POLYFILL_SRC = src/polyfill.lua POLYFILL_SRC = src/polyfill.lua
CREAFTOS_PATH = craftos CREAFTOS_PATH = craftos
BUILD_HXML = build.hxml
HAXE_PATH := $(BUILD_DIR)/$(HAXE_NAME) HAXE_PATH := $(BUILD_DIR)/$(HAXE_NAME)
MIN_PATH := $(BUILD_DIR)/$(MINIFYD_NAME) MIN_PATH := $(BUILD_DIR)/$(MINIFYD_NAME)
POLYFILL_PATH := $(BUILD_DIR)/$(POLYFILLED_NAME) POLYFILL_PATH := $(BUILD_DIR)/$(POLYFILLED_NAME)
UNPACK_PATH := $(BUILD_DIR)/$(UNPACK_NAME)
UNPACK_MINIFYD_PATH := $(BUILD_DIR)/$(UNPACK_MINIFYD_NAME)
HAXE_ZIP_PATH := $(BUILD_DIR)/$(HAXE_ZIP_NAME)
UNPACK_POLYFILLED_PATH := $(BUILD_DIR)/$(UNPACK_POLYFILLED_NAME)
all: clean build all: clean build unpack
build: HAXE_FLAGS += --main kernel.Entrypoint -D analyzer-optimize build: HAXE_FLAGS += -D analyzer-optimize -D no-traces
build: $(MIN_PATH) build: $(MIN_PATH)
debug: HAXE_FLAGS += -D webconsole -D error_stack --debug debug: HAXE_FLAGS += -D webconsole -D error_stack --debug
debug: build debug: $(MIN_PATH)
unpack: $(UNPACK_MINIFYD_PATH) $(HAXE_ZIP_PATH)
$(HAXE_PATH): HAXE_FLAGS += --main kernel.Entrypoint --lua $(HAXE_PATH)
$(HAXE_PATH): $(shell find src -name '*.hx') $(HAXE_PATH): $(shell find src -name '*.hx')
haxe build.hxml $(HAXE_FLAGS) haxe $(BUILD_HXML) $(HAXE_FLAGS)
$(POLYFILL_PATH): $(POLYFILL_SRC) $(HAXE_PATH) $(POLYFILL_PATH): $(POLYFILL_SRC) $(HAXE_PATH)
cat $(POLYFILL_SRC) $(HAXE_PATH) > $@ cat $(POLYFILL_SRC) $(HAXE_PATH) > $@
$(MIN_PATH): $(POLYFILL_PATH) $(MIN_PATH): $(POLYFILL_PATH)
node minify.js $(POLYFILL_PATH) $@ node tools/minify.js < $(POLYFILL_PATH) > $@
$(HAXE_ZIP_PATH): $(MIN_PATH)
node tools/zlibDeflate.js < $(MIN_PATH) > $@
$(UNPACK_PATH): HAXE_FLAGS += --main Unpack -D analyzer-optimize --lua $(UNPACK_PATH)
$(UNPACK_PATH): $(shell find src -name '*.hx')
haxe $(BUILD_HXML) $(HAXE_FLAGS)
$(UNPACK_POLYFILLED_PATH): $(UNPACK_PATH)
cat $(POLYFILL_SRC) $(UNPACK_PATH) > $@
$(UNPACK_MINIFYD_PATH): $(UNPACK_POLYFILLED_PATH)
node tools/minify.js < $(UNPACK_POLYFILLED_PATH) > $@
.PHONY: deps .PHONY: deps
deps: deps-hx deps-node deps: deps-hx deps-node
@@ -37,7 +62,7 @@ deps-hx:
.PHONY: deps-node .PHONY: deps-node
deps-node: deps-node:
yarn install npm install
.PHONY: clean .PHONY: clean
clean: clean:
@@ -54,8 +79,12 @@ emulator:
.PHONY: webconsole .PHONY: webconsole
webconsole: webconsole:
node console.js node tools/console.js
.PHONY: format .PHONY: format
format: format:
haxelib run formatter -s src haxelib run formatter -s src
.PHONY: format-deps
format-deps:
haxelib install formatter

View File

@@ -10,5 +10,3 @@
-D lua-ver 5.1 -D lua-ver 5.1
--macro include("bin") --macro include("bin")
--lua build/haxe.lua

View File

@@ -1,32 +0,0 @@
const http = require('http');
function time() {
let now = new Date();
return `${now.getHours().toString().padStart(2, "0")}:${now.getMinutes().toString().padStart(2, 0)}:${now.getSeconds().toString().padStart(2, 0)}`;
}
const server = http.createServer((req, res) => {
if (req.method != "POST") {
res.writeHead(400);
return;
}
var id = req.url.substring(1);
let data = "";
req.on('data', chunk => {
data += chunk;
})
req.on('end', () => {
console.log(`[${time()}][${id}]${data}`);
res.writeHead(200);
res.end();
})
});
console.log("Listening on port 8080")
server.listen(8080);

View File

@@ -57,7 +57,7 @@ Also peripherals can be made accessible via the network. More on that later.
var back = Peripheral.getRedstone("back"); var back = Peripheral.getRedstone("back");
back.setOutput(true); back.setOutput(true);
var drive Peripheral.getDrive("drive_0"); var drive = Peripheral.getDrive("drive_0");
drive.eject(); drive.eject();
``` ```
@@ -111,10 +111,9 @@ A collection of `Pixel` is called a `Canvas` which is nothing more than a 2D arr
The concept of processes tryes to encapsulate programs. A process is basically an interface with the `run(handle: ProcessHandle)` method. The concept of processes tryes to encapsulate programs. A process is basically an interface with the `run(handle: ProcessHandle)` method.
The idea is that you can register all you disposable resources in the handle and they will be disposed when the process is killed or crashes. The idea is that you can register all you disposable resources in the handle and they will be disposed when the process is killed or crashes.
A process can be used as a command on the terminal or as a service. See [bin/HelloWorld.hx](../src/bin/HelloWorld.hx) for an example. A process can be used as a command on the terminal or as a service. Basically everything that runs and is not part of the kernel is a process.
Basically everything that runs and is not part of the kernel is a process.
In order for you program to be used it needs to be registered in the `BinStore` and the `DCEHack` manually. More on that at [Applications](#applications).
# EndOfLoop # EndOfLoop
@@ -156,3 +155,99 @@ TurtleMutex.runInTThread(() -> {
} }
}); });
``` ```
# RPC
With the help of dark and badly documented magic also known as "macros", we can create quickly create remote procedure call Classes to call functions on other
computers. A problem that arises is that since all data gets send over the network that we kinda lose the type safty. We cloud trust ourself to cast
the result of the request to the right type or we cloud just make use of macros. The RPC macro will create a RPC class that implements all functions of an
interface just that the return type is wrapped in a Promise. Now if we call a function of that RPC class it fires a request to the other computer and waits
for a response. On the other side the service makes use of that generated package handle function for the RPC class.
A simple example:
```haxe
interface IExampleRPC {
function addNumber(a:Int, b:Int):Int;
}
@:build(macros.rpc.RPC.buildRPC(IExampleRPC))
class ExampleRPC extends RPCBase {}
@:build(macros.Binstore.includeBin("Example SRV", ["example-srv"]))
class ExampleService implements IProcess implements IExampleRPC {
private var handle:ProcessHandle;
public function new() {}
public function run(handle:ProcessHandle) {
this.handle = handle;
kernel.net.Net.registerProto("example", (pack) -> {
ExampleRPC.handlePackage(this, pack);
});
}
public function addNumber(a:Int, b:Int):Int {
return a + b;
}
}
// ...
var rpc = new ExampleRPC(12,"example");
rpc.addNumber(3,7).handle((p)->{
switch p {
case Success(r):
Log.info('3+7=$r');
case Failure(err):
Log.error('Error: $err');
}
});
```
# Build system and flags
We use `make` to build the project. If you want a prod build just run `make` and if you want the debug build run `make debug`.
The `build` directory should contain all needed files.
- `bundle.min.lua`: The minified final file
- `bundle.polyfill.lua`: The same as above just not minified. use this when debugging.
- `haxe.lua`: Intermediate file. Polyfill not yet added.
- `haxe.zip`: The compressed `bundle.min.lua`.
- `unpack.*`: Same as the bundle files. Used to unpack and run the `haxe.zip` file.
The follwing haxe flags can be used in the code or can be set in the makefile.
- `debug`: Is set when debug
- `kv_use_native`: If set use the native CC serialize and unserialize functions instead of the Haxe ones. See [KVStore.hx](../src/lib/KVStore.hx) for more.
# Applications
If you want to make an application you just need to create a class that implements IProcess and and has no arguments in the constructor.
In order to have the programm available from the terminal you need to add the `Binstore.includeBin` macro for the class. Here is an example:
```haxe
@:build(macros.Binstore.includeBin("HelloWorld", ["hello", "helloworld"]))
class HelloWorld implements IProcess {
public function new() {}
public function run(handle:ProcessHandle) {
handle.writeLine("Hello World");
handle.close(true);
}
}
```
Let's break this down:
- The `includeBin` macro is required to automaticly inlcude the app and to defeat the DCE optimisation. In early versions refections were used but DCE did not like that
- The first argument on `includeBin` is the name of the app. Only ever used in displaying.
- The second argument are the aliases. These are used to call the app from the terminal and other places
- The constructor can't have any arguments. It's also recommended to not do any major setup stuff in there.
- The `run` method is the entry point in the app.
- The handle is used to interact with the outside world. (theoretically at least)
- The `handle.close(true)` call is required to end a process. The app does not end when the run function is finished. The first arguments represents if the app was successfull.

View File

@@ -1,10 +0,0 @@
const fs = require("fs");
const luamin = require("luamin");
const haxeOutput = fs.readFileSync(process.argv[2] ?? "build/Haxe.min.lua",{encoding:"utf8"});
const minified = luamin.minify(haxeOutput);
fs.writeFileSync(process.argv[3] ?? "build/Haxe.min.lua",minified);
console.log("minified lua");

35
package-lock.json generated Normal file
View File

@@ -0,0 +1,35 @@
{
"name": "haxe",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "haxe",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"luamin": "^1.0.4"
}
},
"node_modules/luamin": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/luamin/-/luamin-1.0.4.tgz",
"integrity": "sha1-lEUptY/G+k0x6s4uA1PUEhDw49M= sha512-z1h0bclRD/QGsS/Dz4Skp9z0qPTmmm+IKcrCapGmdTczPWVdN9f9jk6WqkNrcifQr8+n9Pzsm9WkwpOH1JBUAQ==",
"dependencies": {
"luaparse": "^0.2.1"
},
"bin": {
"luamin": "bin/luamin"
}
},
"node_modules/luaparse": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/luaparse/-/luaparse-0.2.1.tgz",
"integrity": "sha1-qo9WEysN6X0388mRqd9C4OF/ZWw= sha512-VKBcryd5nJte4ZNR29NOk8F/UkMipjeb4yoxcSS51z6QAzg9DXUC2WsfLniS0J1eh3pr/ZL3e9ha6V8fhoLbBQ==",
"bin": {
"luaparse": "bin/luaparse"
}
}
}
}

View File

@@ -6,6 +6,14 @@ import lib.HomeContext;
class Startup { class Startup {
public static function main() { public static function main() {
#if debug
var term = BinStore.instantiate("terminal");
var pid = ProcessManager.run(term, {
// args: ["debug"]
});
var ctx = WindowManager.getContextByPID(pid);
WindowManager.focusContextToOutput(ctx[0], "main");
#else
if (MainTerm.instance.isColor()) { if (MainTerm.instance.isColor()) {
var main = new HomeContext(); var main = new HomeContext();
@@ -16,5 +24,6 @@ class Startup {
var ctx = WindowManager.getContextByPID(pid); var ctx = WindowManager.getContextByPID(pid);
WindowManager.focusContextToOutput(ctx[0], "main"); WindowManager.focusContextToOutput(ctx[0], "main");
} }
#end
} }
} }

29
src/Unpack.hx Normal file
View File

@@ -0,0 +1,29 @@
import lua.Lua;
import haxe.io.Bytes;
import kernel.fs.FS;
import haxe.zip.Uncompress;
class Unpack {
public static function main() {
var filename = "/haxe.zip";
var handle = FS.openReadBinary(filename);
var size = FS.attributes(filename).size;
var data = Bytes.alloc(size);
for (i in 0...size) {
data.set(i, handle.readByte());
}
var uncompressed = Uncompress.run(data);
var res = Lua.load(uncompressed.toString()); // FIXME: Haxe is missing some parameters. This does not work.
var f = res.func; // Required for silly haxe bug.
if (res.message == null) {
f();
} else {
trace('Failed: ${res.message}');
}
}
}

View File

@@ -1,5 +1,6 @@
package bin; package bin;
import kernel.peripherals.Drive;
import lib.CLIAppBase; import lib.CLIAppBase;
import kernel.peripherals.Peripherals.Peripheral; import kernel.peripherals.Peripherals.Peripheral;
@@ -30,37 +31,17 @@ class Disk extends CLIAppBase {
}); });
registerSyncSubcommand("play", (args) -> { registerSyncSubcommand("play", (args) -> {
if (args.length < 1) { return audioDiskPlayPause(args.getString("addr"), true);
handle.writeLine("Missing drive address"); }, [Peripheral("addr", Drive.TYPE_NAME)]);
return false;
}
return audioDiskPlayPause(args[0], true);
}, "<drive>");
registerSyncSubcommand("stop", (args) -> { registerSyncSubcommand("stop", (args) -> {
if (args.length < 1) { return audioDiskPlayPause(args.getString("addr"), false);
handle.writeLine("Missing drive address"); }, [Peripheral("addr", Drive.TYPE_NAME)]);
return false;
}
return audioDiskPlayPause(args[0], false);
}, "<drive>");
registerSyncSubcommand("eject", (args) -> { registerSyncSubcommand("eject", (args) -> {
if (args.length < 1) { var driveAddr = args.getString("addr");
handle.writeLine("Missing drive address");
return false;
}
var driveAddr = args[0];
var drive = Peripheral.getDrive(driveAddr); var drive = Peripheral.getDrive(driveAddr);
if (drive == null) {
handle.writeLine("Drive not found: " + driveAddr);
return false;
}
if (!drive.isDiskPresent()) { if (!drive.isDiskPresent()) {
handle.writeLine("No disk in drive: " + driveAddr); handle.writeLine("No disk in drive: " + driveAddr);
return false; return false;
@@ -68,21 +49,12 @@ class Disk extends CLIAppBase {
drive.ejectDisk(); drive.ejectDisk();
return true; return true;
}, "<drive>"); }, [Peripheral("addr", Drive.TYPE_NAME)]);
registerSyncSubcommand("lable", (args) -> { registerSyncSubcommand("label", (args) -> {
if (args.length < 1) { var driveAddr = args.getString("addr");
handle.writeLine("Missing drive address");
return false;
}
var driveAddr = args[0];
var drive = Peripheral.getDrive(driveAddr); var drive = Peripheral.getDrive(driveAddr);
var label:String = args[1]; var label:Null<String> = args.getString("label");
if (drive == null) {
handle.writeLine("Drive not found: " + driveAddr);
}
if (!drive.isDiskPresent()) { if (!drive.isDiskPresent()) {
handle.writeLine("No disk in drive: " + driveAddr); handle.writeLine("No disk in drive: " + driveAddr);
@@ -99,7 +71,7 @@ class Disk extends CLIAppBase {
} }
return true; return true;
}, "<drive> [label]"); }, [Peripheral("addr", Drive.TYPE_NAME), Optional(String("label"))]);
} }
private function audioDiskPlayPause(driveAddr:String, play:Bool):Bool { private function audioDiskPlayPause(driveAddr:String, play:Bool):Bool {

View File

@@ -2,7 +2,7 @@ package bin;
import lib.CLIAppBase; import lib.CLIAppBase;
import kernel.gps.INS; import kernel.gps.INS;
import lib.Pos3; import lib.WorldPos;
import lib.Vec.Vec3; import lib.Vec.Vec3;
using tink.CoreApi; using tink.CoreApi;
@@ -11,16 +11,16 @@ using tink.CoreApi;
class GPS extends CLIAppBase { class GPS extends CLIAppBase {
public function new() { public function new() {
registerSyncSubcommand("set", (args) -> { registerSyncSubcommand("set", (args) -> {
var x:Float = Std.parseFloat(args[0]); var x:Float = args.getFloat("x");
var y:Float = Std.parseFloat(args[1]); var y:Float = args.getFloat("y");
var z:Float = Std.parseFloat(args[2]); var z:Float = args.getFloat("z");
var pos:Pos3 = new Vec3<Float>(x, y, z); var pos:WorldPos = new Vec3<Float>(x, y, z);
kernel.gps.GPS.setManualPosition(pos); kernel.gps.GPS.setManualPosition(pos);
return true; return true;
}, "<x> <y> <z>"); }, [Float("x"), Float("y"), Float("z")]);
registerSyncSubcommand("status", (args) -> { registerSyncSubcommand("status", (args) -> {
var pos = kernel.gps.GPS.getPosition(); var pos = kernel.gps.GPS.getPosition();
@@ -51,11 +51,12 @@ class GPS extends CLIAppBase {
}); });
registerAsyncSubcommand("locate", (args) -> { registerAsyncSubcommand("locate", (args) -> {
return kernel.gps.GPS.locate().map((pos) -> { return kernel.gps.GPS.locate().map((result) -> {
if (pos != null) { switch result {
handle.writeLine('Position x:${pos.x} y:${pos.y} z:${pos.z}'); case Success(pos):
} else { handle.writeLine('Position x:${pos.x} y:${pos.y} z:${pos.z}');
handle.writeLine("Position not available"); case Failure(err):
handle.writeLine("Position not available: " + err);
} }
return true; return true;
}); });

View File

@@ -1,18 +0,0 @@
package bin;
import kernel.log.Log;
import kernel.ps.ProcessHandle;
import kernel.ps.Process;
using tink.CoreApi;
@:build(macros.Binstore.includeBin("Hello world", ["hello"]))
class HelloWorld implements Process {
public function new() {}
public function run(handle:ProcessHandle) {
handle.write("Hello World!");
handle.close();
}
}

View File

@@ -1,74 +0,0 @@
package bin;
import kernel.log.Log;
import lib.ui.elements.TextElement;
import lib.Pos;
import lib.ui.elements.UIElement;
import lib.ui.elements.LayerdRootElement;
import kernel.ui.WindowContext;
import kernel.ps.ProcessHandle;
import kernel.ps.Process;
@:build(macros.Binstore.includeBin("HelloWorld-GUI", ["hello-gui"]))
class HelloWorldGUI implements Process {
private var handle:ProcessHandle;
private var ctx:WindowContext;
private var requestRender:Void->Void;
private var root:LayerdRootElement;
public function new() {}
public function run(handle:ProcessHandle) {
this.handle = handle;
var stateless = handle.createStatelessWindowContext();
this.ctx = stateless.ctx;
this.requestRender = stateless.requestRender;
stateless.setRenderFunc(this.render);
this.root = new LayerdRootElement();
this.ctx.delegateEvents(this.root);
this.requestRender();
}
private function render() {
var children:Array<{element:UIElement, offset:Pos}> = [
{
element: new TextElement("Hello World", {
uiEvents: {
onClick: () -> {
Log.debug("Hello World");
}
}
}),
offset: new Pos({x: 0, y: 0})
},
{
element: new TextElement("Holla Mundo", {
uiEvents: {
onClick: () -> {
Log.debug("Holla Mundo");
}
}
}),
offset: new Pos({x: 0, y: 1})
},
{
element: new TextElement("Ayyy", {
uiEvents: {
onClick: () -> {
Log.debug("Ayy");
}
}
}),
offset: new Pos({x: 4, y: 1})
}
];
this.root.setChildren(children);
this.root.render(ctx.getSize()).renderToContext(ctx);
}
}

View File

@@ -1,28 +0,0 @@
package bin;
import kernel.log.Log;
import macros.rpc.RPC;
import kernel.ps.ProcessHandle;
import kernel.ps.Process;
using tink.CoreApi;
@:build(macros.rpc.RPC.buildRPC())
class HelloWorldService implements Process {
private var handle:ProcessHandle;
public function new() {}
public function run(handle:ProcessHandle) {
this.handle = handle;
RPC.generateRPCPackageHandle();
}
@rpc
public function getNumber(arg1:Int, arg2:Int):Int {
Log.debug(arg1);
Log.debug(arg2);
return 42;
}
}

View File

@@ -1,10 +1,10 @@
package bin; package bin;
import kernel.ps.ProcessHandle; import kernel.ps.ProcessHandle;
import kernel.ps.Process; import kernel.ps.IProcess;
@:build(macros.Binstore.includeBin("ID", ["id"])) @:build(macros.Binstore.includeBin("ID", ["id"]))
class ID implements Process { class ID implements IProcess {
public function new() {} public function new() {}
public function run(handle:ProcessHandle) { public function run(handle:ProcessHandle) {

View File

@@ -7,18 +7,13 @@ import lib.CLIAppBase;
class KSettings extends CLIAppBase { class KSettings extends CLIAppBase {
public function new() { public function new() {
registerSyncSubcommand("get", (args) -> { registerSyncSubcommand("get", (args) -> {
var key = args[0]; var key = args.getString("key");
if (key == null) {
handle.writeLine("Key not specified");
return false;
}
var value = switch (key) { var value = switch (key) {
case "hostname": case "hostname":
KernelSettings.hostname; KernelSettings.hostname;
case "sitecontroller": case "nameServer":
Std.string(KernelSettings.siteController); Std.string(KernelSettings.nameServer);
default: default:
null; null;
} }
@@ -30,17 +25,17 @@ class KSettings extends CLIAppBase {
handle.writeLine(value); handle.writeLine(value);
return true; return true;
}, " <key>"); }, [String("key")]);
registerSyncSubcommand("set", (args) -> { registerSyncSubcommand("set", (args) -> {
var key = args[0]; var key = args.getString("key");
if (key == null) { if (key == null) {
handle.writeLine("Key not specified"); handle.writeLine("Key not specified");
return false; return false;
} }
var value = args[1]; var value = args.getString("value");
if (value == null) { if (value == null) {
handle.writeLine("Value not specified"); handle.writeLine("Value not specified");
@@ -50,18 +45,18 @@ class KSettings extends CLIAppBase {
switch (key) { switch (key) {
case "hostname": case "hostname":
KernelSettings.hostname = value; KernelSettings.hostname = value;
case "sitecontroller": case "nameServer":
KernelSettings.siteController = Std.parseInt(value); KernelSettings.nameServer = Std.parseInt(value);
default: default:
handle.writeLine("Key not found"); handle.writeLine("Key not found");
return false; return false;
} }
return true; return true;
}, " <key> <value>"); }, [String("key"), String("value")]);
registerSyncSubcommand("list", (args) -> { registerSyncSubcommand("list", (args) -> {
handle.writeLine("hostname"); handle.writeLine("hostname");
handle.writeLine("sitecontroller"); handle.writeLine("nameServer");
return true; return true;
}); });
} }

View File

@@ -1,17 +1,16 @@
package bin; package bin;
import kernel.ps.ProcessHandle; import kernel.ps.ProcessHandle;
import kernel.ps.Process; import kernel.ps.IProcess;
import lib.Color; import lib.Color;
import lib.MathI; import lib.MathI;
import kernel.log.Log; import kernel.log.Log;
import kernel.ui.WindowContext; import kernel.ui.WindowContext;
import lib.ui.UIApp;
using tink.CoreApi; using tink.CoreApi;
@:build(macros.Binstore.includeBin("Log", ["log"])) @:build(macros.Binstore.includeBin("Log", ["log"]))
class KernelLog implements Process { class KernelLog implements IProcess {
private var handle:ProcessHandle; private var handle:ProcessHandle;
private var ctx:WindowContext; private var ctx:WindowContext;

View File

@@ -2,10 +2,10 @@ package bin;
import kernel.ps.ProcessManager; import kernel.ps.ProcessManager;
import kernel.ps.ProcessHandle; import kernel.ps.ProcessHandle;
import kernel.ps.Process; import kernel.ps.IProcess;
@:build(macros.Binstore.includeBin("LSPS", ["lsps"])) @:build(macros.Binstore.includeBin("LSPS", ["lsps"]))
class LSPS implements Process { class LSPS implements IProcess {
public function new() {} public function new() {}
public function run(handle:ProcessHandle) { public function run(handle:ProcessHandle) {

View File

@@ -40,16 +40,7 @@ class Net extends CLIAppBase {
}); });
registerAsyncSubcommand("ping", (args) -> { registerAsyncSubcommand("ping", (args) -> {
if (args.length < 1) { var toID:Int = args.getInt("id");
return Future.sync(false);
}
var toID:Null<Int> = Std.parseInt(args[0]);
if (toID == null) {
handle.write("Invalid ID");
return Future.sync(false);
}
return kernel.net.Net.ping(toID).map(result -> { return kernel.net.Net.ping(toID).map(result -> {
switch (result) { switch (result) {
@@ -61,6 +52,6 @@ class Net extends CLIAppBase {
return true; return true;
}); });
}, "<id>"); }, [Int("id")]);
} }
} }

View File

@@ -4,18 +4,11 @@ import kernel.peripherals.Peripherals.Peripheral;
import lib.CLIAppBase; import lib.CLIAppBase;
@:build(macros.Binstore.includeBin("Peripheral", ["ph", "peripheral"])) @:build(macros.Binstore.includeBin("Peripheral", ["ph", "peripheral"]))
class Perf extends CLIAppBase { class Peri extends CLIAppBase {
public function new() { public function new() {
registerSyncSubcommand("inspect", (args) -> { registerSyncSubcommand("inspect", (args) -> {
if (args.length < 1) var addr = args.getString("addr");
return false; var result = Peripheral.inspect(addr);
var result = Peripheral.inspect(args[0]);
if (result == null) {
handle.writeLine("No peripheral found on side " + args[0]);
return true;
}
handle.writeLine("Types:"); handle.writeLine("Types:");
for (type in result.types) { for (type in result.types) {
@@ -28,7 +21,7 @@ class Perf extends CLIAppBase {
} }
return true; return true;
}, "<side>"); }, [Addr("addr")]);
registerSyncSubcommand("list", (args) -> { registerSyncSubcommand("list", (args) -> {
for (addr in Peripheral.getAllAddresses()) { for (addr in Peripheral.getAllAddresses()) {

View File

@@ -9,19 +9,19 @@ using tink.CoreApi;
class Redstone extends CLIAppBase { class Redstone extends CLIAppBase {
public function new() { public function new() {
registerSyncSubcommand("on", (args) -> { registerSyncSubcommand("on", (args) -> {
Peripheral.getRedstone(args[0]).setOutput(true); Peripheral.getRedstone(args.getString("side")).setOutput(true);
return true; return true;
}, "<side>"); }, [Side("side")]);
registerSyncSubcommand("off", (args) -> { registerSyncSubcommand("off", (args) -> {
Peripheral.getRedstone(args[0]).setOutput(false); Peripheral.getRedstone(args.getString("side")).setOutput(false);
return true; return true;
}, "<side>"); }, [Side("side")]);
registerSyncSubcommand("get", (args) -> { registerSyncSubcommand("get", (args) -> {
var value = Peripheral.getRedstone(args[0]).getAnalogInput(); var value = Peripheral.getRedstone(args.getString("side")).getAnalogInput();
handle.write("Analog input: " + value); handle.write("Analog input: " + value);
return true; return true;
}, "<side>"); }, [Side("side")]);
} }
} }

View File

@@ -9,50 +9,28 @@ using tink.CoreApi;
class Service extends CLIAppBase { class Service extends CLIAppBase {
public function new() { public function new() {
registerSyncSubcommand("start", (args) -> { registerSyncSubcommand("start", (args) -> {
if (args.length < 1) { var result = ServiceManager.start(args.getString("name"));
return false;
}
var name = args[0];
var result = ServiceManager.start(name);
return handleResult(result); return handleResult(result);
}, "<name>"); }, [String("name")]);
registerSyncSubcommand("stop", (args) -> { registerSyncSubcommand("stop", (args) -> {
if (args.length < 1) { var result = ServiceManager.stop(args.getString("name"));
return false;
}
var name = args[0];
var result = ServiceManager.stop(name);
return handleResult(result); return handleResult(result);
}, "<name>"); }, [String("name")]);
registerSyncSubcommand("register", (args) -> { registerSyncSubcommand("register", (args) -> {
if (args.length < 2) { var name = args.getString("name");
return false; var binName = args.getString("binary");
} var rest = args.getRest();
var name = args[0];
var binName = args[1];
var rest = args.slice(2);
var result = ServiceManager.register(name, binName, rest); var result = ServiceManager.register(name, binName, rest);
return handleResult(result); return handleResult(result);
}, "<name> <binary> [args...]"); }, [String("name"), String("binary"), Rest("args")]);
registerSyncSubcommand("unregister", (args) -> { registerSyncSubcommand("unregister", (args) -> {
if (args.length < 2) { var result = ServiceManager.unregister(args.getString("name"));
return false;
}
var name = args[0];
var result = ServiceManager.unregister(name);
return handleResult(result); return handleResult(result);
}, "<name>"); }, [String("name")]);
registerSyncSubcommand("list", (args) -> { registerSyncSubcommand("list", (args) -> {
var list = ServiceManager.listRunning(); var list = ServiceManager.listRunning();
@@ -65,13 +43,9 @@ class Service extends CLIAppBase {
}); });
registerSyncSubcommand("enable", (args) -> { registerSyncSubcommand("enable", (args) -> {
if (args.length < 1) { ServiceManager.enable(args.getString("name"));
return false;
}
ServiceManager.enable(args[0]);
return true; return true;
}, "<name>"); }, [String("name")]);
} }
private function handleResult(res:Outcome<Noise, String>):Bool { private function handleResult(res:Outcome<Noise, String>):Bool {

71
src/bin/Speaker.hx Normal file
View File

@@ -0,0 +1,71 @@
package bin;
import kernel.peripherals.Peripherals.Peripheral;
import lib.CLIAppBase;
using tink.CoreApi;
@:build(macros.Binstore.includeBin("Speaker", ["speaker", "sp"]))
class Speaker extends CLIAppBase {
public function new() {
registerSyncSubcommand("note", (args) -> {
var sp = Peripheral.getSpeaker(args.getString("addr"));
var note = args.getString("note");
var r;
if (args.hasArg("pitch")) {
if (args.hasArg("volume")) {
r = sp.playNote(note, args.getFloat("volume"), args.getInt("pitch"));
} else {
r = sp.playNote(note, args.getFloat("volume"));
}
} else {
r = sp.playNote(note);
}
switch r {
case Failure(failure):
handle.writeLine(failure);
return false;
case Success(_):
return true;
}
}, [
Peripheral("addr", kernel.peripherals.Speaker.TYPE_NAME),
String("note"),
Optional(Int("pitch")),
Optional(Float("volume"))
]);
registerSyncSubcommand("sound", (args) -> {
var sp = Peripheral.getSpeaker(args.getString("addr"));
var sound = args.getString("sound");
var r;
if (args.hasArg("volume")) {
if (args.hasArg("pitch")) {
r = sp.playSound(sound, args.getFloat("volume"), args.getFloat("pitch"));
} else {
r = sp.playSound(sound, args.getFloat("volume"));
}
} else {
r = sp.playSound(sound);
}
switch r {
case Failure(failure):
handle.writeLine(failure);
return false;
case Success(_):
return true;
}
}, [
Peripheral("addr", kernel.peripherals.Speaker.TYPE_NAME),
String("sound"),
Optional(Float("pitch")),
Optional(Float("volume"))
]);
}
}

View File

@@ -1,9 +1,11 @@
package bin; package bin;
import lib.MathI;
import kernel.EndOfLoop;
import lua.NativeStringTools; import lua.NativeStringTools;
import kernel.binstore.BinStore; import kernel.binstore.BinStore;
import kernel.ps.ProcessHandle; import kernel.ps.ProcessHandle;
import kernel.ps.Process; import kernel.ps.IProcess;
import kernel.ps.ProcessManager; import kernel.ps.ProcessManager;
import lib.Color; import lib.Color;
import kernel.ui.WindowContext; import kernel.ui.WindowContext;
@@ -11,7 +13,7 @@ import kernel.ui.WindowContext;
using tink.CoreApi; using tink.CoreApi;
@:build(macros.Binstore.includeBin("Terminal", ["terminal"])) @:build(macros.Binstore.includeBin("Terminal", ["terminal"]))
class Terminal implements Process { class Terminal implements IProcess {
private static inline final MAX_BACKLOG:Int = 100; private static inline final MAX_BACKLOG:Int = 100;
private var handle:ProcessHandle; private var handle:ProcessHandle;
@@ -24,6 +26,8 @@ class Terminal implements Process {
private var history:Array<String> = []; private var history:Array<String> = [];
private var historyIndex:Int = 0; private var historyIndex:Int = 0;
private var scrollBack = 0;
private var runningPID:PID = -1; private var runningPID:PID = -1;
public function new() {} public function new() {}
@@ -58,8 +62,11 @@ class Terminal implements Process {
if (this.runningPID > 0) if (this.runningPID > 0)
return; return;
this.backlog.push("> " + this.input); this.backlog.push("> " + this.input);
this.backlog.push("");
var command = this.input; var command = this.input;
this.input = ""; this.input = "";
this.scrollBack = 0;
this.requestRender(); this.requestRender();
this.historyIndex = 0; this.historyIndex = 0;
this.invokeCommand(command); this.invokeCommand(command);
@@ -71,10 +78,27 @@ class Terminal implements Process {
this.input = this.history[this.history.length - this.historyIndex]; this.input = this.history[this.history.length - this.historyIndex];
this.requestRender(); this.requestRender();
} }
case 266: // PAGE UP
this.scrollBack = MathI.min(scrollBack + 1, this.backlog.length - 1);
this.requestRender();
case 267: // PAGE DOWN
this.scrollBack = MathI.max(scrollBack - 1, 0);
this.requestRender();
} }
})); }));
this.requestRender(); this.requestRender();
if (handle.args.length > 0) {
var arg1 = handle.args[0];
this.backlog.push("> " + arg1);
this.backlog.push("");
EndOfLoop.endOfLoop(() -> {
this.invokeCommand(arg1);
});
}
} }
private function stopCurrentPS() { private function stopCurrentPS() {
@@ -89,7 +113,8 @@ class Terminal implements Process {
var size = this.ctx.getSize(); var size = this.ctx.getSize();
var linesAvailable = size.y - 1; var linesAvailable = size.y - 1;
var start:Int = this.backlog.length - linesAvailable; var withoutScrollBack = (this.backlog.length - linesAvailable);
var start:Int = withoutScrollBack - scrollBack;
for (i in 0...linesAvailable) { for (i in 0...linesAvailable) {
var line = this.backlog[start + i]; var line = this.backlog[start + i];
@@ -156,24 +181,13 @@ class Terminal implements Process {
return; return;
} }
var mfunc = NativeStringTools.gmatch(s, "(.-)(\n)"); var splits = s.split("\n");
while (true) { for (i => split in splits) {
var found = mfunc(); if (i == 0) {
this.backlog[this.backlog.length - 1] += split;
if (found == null) {
break;
}
if (found == "\n") {
this.backlog.push("");
} else { } else {
this.backlog.push(found); this.backlog.push(split);
}
// Trim the backlog if it's too long
if (this.backlog.length > MAX_BACKLOG) {
this.backlog.shift();
} }
} }

View File

@@ -1,52 +0,0 @@
package bin;
import lib.CLIAppBase;
using tink.CoreApi;
@:build(macros.Binstore.includeBin("Turtle", ["turtle", "t"]))
class Turtle extends CLIAppBase {
public function new() {
registerSyncSubcommand("forward", (args) -> {
return checkAvailable() && perform(kernel.turtle.Turtle.forward());
});
registerSyncSubcommand("back", (args) -> {
return checkAvailable() && perform(kernel.turtle.Turtle.back());
});
registerSyncSubcommand("left", (args) -> {
return checkAvailable() && perform(kernel.turtle.Turtle.turnLeft());
});
registerSyncSubcommand("right", (args) -> {
return checkAvailable() && perform(kernel.turtle.Turtle.turnRight());
});
registerSyncSubcommand("up", (args) -> {
return checkAvailable() && perform(kernel.turtle.Turtle.up());
});
registerSyncSubcommand("down", (args) -> {
return checkAvailable() && perform(kernel.turtle.Turtle.down());
});
}
private function checkAvailable():Bool {
if (!kernel.turtle.Turtle.isTurtle()) {
handle.write("This is not a turtle!");
return false;
}
return true;
}
private function perform(outcome:Outcome<Noise, String>):Bool {
switch outcome {
case Success(_):
return true;
case Failure(error):
handle.write(error);
return false;
}
}
}

180
src/bin/TurtleCtl.hx Normal file
View File

@@ -0,0 +1,180 @@
package bin;
import kernel.turtle.Types.ToolSide;
import lib.turtle.Helper;
import kernel.turtle.TurtleMutex;
import kernel.turtle.Turtle;
import lib.turtle.InvManager;
import lib.CLIAppBase;
using tink.CoreApi;
@:build(macros.Binstore.includeBin("Turtle", ["turtle", "t"]))
class TurtleCtl extends CLIAppBase {
public function new() {
registerAsyncSubcommand("f", (args) -> {
return asynPerform(Turtle.forward, args.getInt("times") ?? 1);
}, [Optional(Int("times"))]);
registerAsyncSubcommand("b", (args) -> {
return asynPerform(Turtle.back, args.getInt("times") ?? 1);
}, [Optional(Int("times"))]);
registerAsyncSubcommand("l", (args) -> {
return asynPerform(Turtle.turnLeft, args.getInt("times") ?? 1);
}, [Optional(Int("times"))]);
registerAsyncSubcommand("r", (args) -> {
return asynPerform(Turtle.turnRight, args.getInt("times") ?? 1);
}, [Optional(Int("times"))]);
registerAsyncSubcommand("u", (args) -> {
return asynPerform(Turtle.up, args.getInt("times") ?? 1);
}, [Optional(Int("times"))]);
registerAsyncSubcommand("d", (args) -> {
return asynPerform(Turtle.down, args.getInt("times") ?? 1);
}, [Optional(Int("times"))]);
registerAsyncSubcommand("defrag", (args) -> {
return asynPerform(() -> {
// TODO: when defrag can fail return that error
InvManager.defrag();
return Success(null);
}, 1);
});
registerSyncSubcommand("fuel", (args) -> {
var lvl = Turtle.getFuelLevel();
var limit = Turtle.getFuelLimit();
handle.writeLine('${lvl}/${limit} (${(lvl / limit) * 100}%)');
return true;
});
registerAsyncSubcommand("fuel-sources", (args) -> {
return asynPerform(() -> {
var items = InvManager.getCombustableItems();
for (i in items) {
handle.writeLine(i);
}
return Success(null);
}, 1);
});
registerAsyncSubcommand("refuel", (args) -> {
var refuelTo = Turtle.getFuelLimit();
var arg = args.getString("to");
if (arg != null) {
var split = arg.split("%");
if (split.length > 1) {
// Is percentage
var parsed = Std.parseFloat(split[0]);
if (parsed == null) {
handle.writeLine("Failed to parse ammount");
return Future.sync(false);
}
refuelTo = Math.round(refuelTo * (parsed / 100));
} else {
// Is absolute
var parsed = Std.parseInt(arg);
if (parsed == null) {
handle.writeLine("Failed to parse ammount");
return Future.sync(false);
}
refuelTo = parsed;
}
}
return asynPerform(() -> {
InvManager.refuel(refuelTo, []);
return Success(null);
}, 1);
}, [Optional(String("to"))]);
registerAsyncSubcommand("dig", (args) -> {
var direction = args.getString("direction");
switch (direction) {
case "front" | "f":
return asynPerform(() -> Turtle.dig(Front), 1);
case "top" | "t" | "up" | "u":
return asynPerform(() -> Turtle.dig(Up), 1);
case "bot" | "bottom" | "b" | "down" | "d":
return asynPerform(() -> Turtle.dig(Down), 1);
default:
return Future.sync(false);
}
}, [String("direction")]);
registerAsyncSubcommand("tunnel", (args) -> {
var len = args.getInt("length");
return asynPerform(Helper.combine.bind([Turtle.digEmpty.bind(Front), Turtle.forward, Turtle.digEmpty.bind(Up)]), len);
}, [Int("length")]);
registerSyncSubcommand("inspect", (args) -> {
var res = Turtle.inspect(Front);
switch res {
case Failure(err):
handle.writeLine("Failed: " + err);
return false;
case Success(data):
handle.writeLine('Name: ${data.name}');
handle.writeLine("Tags:");
for (tag in data.tags.sortByName()) {
handle.writeLine(' ${tag}');
}
handle.writeLine("State:");
handle.writeLine(data.state);
}
return true;
}, []);
}
private function asynPerform(op:Void->Outcome<Noise, String>, times:Int):Future<Bool> {
return new Future<Bool>((wakeup) -> {
if (times == 0) {
wakeup(false);
return null;
}
if (!Turtle.isTurtle()) {
handle.write("This is not a turtle!");
wakeup(false);
return null;
}
if (!handle.claimTurtleMutex()) {
handle.writeLine("Failed to claim mutex");
wakeup(false);
return null;
}
TurtleMutex.runInTThread(() -> {
for (i in 0...times) {
switch op() {
case Success(_):
case Failure(failure):
handle.writeLine('Failed: $failure');
wakeup(false);
return;
}
}
wakeup(true);
});
return null;
});
}
}

41
src/bin/debug/Debug.hx Normal file
View File

@@ -0,0 +1,41 @@
package bin.debug;
import kernel.log.Log;
import lib.turtle.InvManager;
import kernel.ps.ProcessHandle;
import kernel.ps.IProcess;
/**
Use this to test whatever you are working on. It will also print debug statements.
IDK if you commit changes in this file. It will not be included in non debug build.
**/
#if debug
@:build(macros.Binstore.includeBin("Debug", ["dbg", "debug"]))
#end
class Debug implements IProcess {
public function new() {}
public function run(handle:ProcessHandle) {
var link = Log.onLog.handle((line) -> {
handle.writeLine('[${line.level}] ${line.message}');
});
handle.addDeferFunc(() -> {
link.cancel();
});
// Add your stuff here
// -----
var rpc = new bin.debug.DebugRPC.DebugRPCImpl(1, "debug");
var a = rpc.addNumber(1, 2);
// rpc.addNumber(1,2).handle((e)->{
// Log.debug(e);
// handle.close();
// });
// -----
}
}

10
src/bin/debug/DebugRPC.hx Normal file
View File

@@ -0,0 +1,10 @@
package bin.debug;
import macros.rpc.RPCBase;
interface DebugRPC {
function addNumber(a:Int, b:Int):Int;
}
@:build(macros.rpc.RPC.buildRPC(DebugRPC))
class DebugRPCImpl extends RPCBase {}

View File

@@ -0,0 +1,27 @@
package bin.debug;
import bin.debug.DebugRPC.DebugRPCImpl;
import macros.rpc.RPC;
import kernel.ps.ProcessHandle;
import kernel.ps.IProcess;
#if debug
@:build(macros.Binstore.includeBin("Debug SRV", ["dbg-srv", "debug-srv"]))
#end
class DebugService implements IProcess implements DebugRPC {
private var handle:ProcessHandle;
public function new() {}
public function run(handle:ProcessHandle) {
this.handle = handle;
kernel.net.Net.registerProto("debug", (pack) -> {
DebugRPCImpl.handlePackage(this, pack);
});
}
public function addNumber(a:Int, b:Int):Int {
return a + b;
}
}

View File

@@ -0,0 +1,67 @@
package bin.exporter;
import kernel.net.Package.GenericPackage;
import kernel.peripherals.exports.RedstoneExport;
import kernel.peripherals.Peripherals.Peripheral;
import kernel.ps.ProcessHandle;
import kernel.ps.IProcess;
/**
A service to expose local peripherals to the network.
**/
@:build(macros.Binstore.includeBin("Exporter", ["exporter"]))
class Exporter implements IProcess {
public function new() {}
public function run(handle:ProcessHandle) {
if (handle.args.length < 2) {
handle.writeLine("Not enough args: <addr> <type>");
handle.close(false);
}
var addr = handle.args[0];
var expectedType = handle.args[1];
// Check if peripheral is present. Not the case for redstone.
if (expectedType != kernel.peripherals.Redstone.TYPE_NAME && !Peripheral.isPresent(addr)) {
handle.writeLine('Address: $addr not present');
handle.close(false);
}
var types = Peripheral.getTypes(addr);
// Check if the correct type is present. Not the case for redstone.
if (expectedType != kernel.peripherals.Redstone.TYPE_NAME && !types.contains(expectedType)) {
handle.writeLine('Expected type not machted on address: $addr. Wanted $expectedType got $types');
handle.close(false);
}
var peri = Peripheral.getFromType(addr, expectedType);
if (peri == null) {
handle.writeLine('Failed to create peripheral for address: $addr');
handle.close(false);
}
var handePackFunc:(GenericPackage) -> Bool = null;
// Cast to RPC class
// TODO: There must be a smarter way to do this.
switch expectedType {
case kernel.peripherals.Redstone.TYPE_NAME:
handePackFunc = (p) -> {
return RedstoneExport.handlePackage(cast peri, p);
};
// TODO: More peripherals
}
handle.writeLine('Listening on export:$addr with type $expectedType');
// Listen for packages
kernel.net.Net.registerProto('export:$addr', (p) -> {
if (!handePackFunc(p)) {
handle.writeLine("Failed handle package on export");
}
});
}
}

View File

@@ -1,58 +0,0 @@
package bin.exporter;
import lib.exporter.Export;
import lib.exporter.IExportable;
import kernel.peripherals.Peripherals.Peripheral;
import kernel.service.ServiceManager;
import lib.exporter.Import;
import lib.CLIAppBase;
class Res extends CLIAppBase {
public function new() {
registerAsyncSubcommand("get", (args) -> {
var url = args[0];
return Import.get(url).map((res) -> {
switch (res) {
case Success(data):
handle.writeLine(Std.string(data));
case Failure(err):
handle.writeLine("Error: ");
handle.writeLine(Std.string(err));
}
return true;
});
}, "<url>");
registerAsyncSubcommand("register", (args) -> {
var srv:Null<ResManager> = ServiceManager.get("resmgr");
var addr = args[0];
var name = args[1];
if (srv == null) {
handle.writeLine("Error: resmgr not found");
return false;
}
var perf:kernel.peripherals.Redstone = Peripheral.getRedstone(addr);
if (perf == null) {
handle.writeLine("Error: peripheral not found");
return false;
}
return srv.register(name, new Export(perf)).map((res) -> {
switch (res) {
case Success(_):
handle.writeLine("Success");
return true;
case Failure(err):
handle.writeLine("Error: ");
handle.writeLine(Std.string(err));
return false;
}
});
}, "<addr> <name>");
}
}

View File

@@ -1,90 +0,0 @@
package bin.exporter;
import kernel.KernelSettings;
import kernel.peripherals.Peripherals.Peripheral;
import lib.KVStore;
import lib.exporter.Request;
import kernel.ps.ProcessHandle;
import lib.exporter.Export;
import kernel.ps.Process;
import kernel.net.Package;
import kernel.net.Net;
import kernel.net.Package.GenericPackage;
using tink.CoreApi;
class ResManager implements Process {
private var handle:ProcessHandle;
private var exports:Map<String, Export> = [];
public function new() {}
public function run(handle:ProcessHandle) {
this.handle = handle;
Net.registerProto("res", handlePackage);
load();
}
public function register(id:String, export:Export):Promise<Noise> {
if (exports.exists(id)) {
return Promise.reject(new Error("Ressource already exists: " + id));
}
return registerName(id).next((success) -> {
exports.set(id, export);
persist();
return null;
});
}
private function handlePackage(pack:GenericPackage) {
var requestPack:Package<Request> = cast pack;
var id = requestPack.data.id;
if (!exports.exists(id)) {
requestPack.respond(lib.exporter.Response.NotFound);
return;
}
var export = exports.get(id);
var response = export.handleRequest(requestPack.data);
requestPack.respond(response);
}
private function registerName(id:String) {
var rpc = new SiteRessourceControllerRPC(KernelSettings.siteController);
return rpc.register(id, Net.networkID);
}
private function persist() {
var store = new KVStore("export");
var saveExports:Array<{name:String, addr:String, type:String}> = [for (k => v in this.exports) {name: k, addr: v.getAddr(), type: v.getType()}];
store.set("exports", saveExports);
store.save();
}
private function load() {
var store = new KVStore("export");
var savedExports:Array<{name:String, addr:String, type:String}> = store.get("exports", []);
for (export in savedExports) {
var perph = Peripheral.getFromType(export.addr, export.type);
if (perph == null) {
handle.writeLine('Could not load export: ${export.name} on ${export.addr}');
continue;
}
// I dont know if cast is the best way to do this
// But since we know that this is a IExportable we can do this (I think)
exports.set(export.name, new Export(cast perph));
handle.writeLine('Loaded export: ${export.name} on ${export.addr}');
}
}
}

71
src/bin/ns/NameSystem.hx Normal file
View File

@@ -0,0 +1,71 @@
package bin.ns;
import lib.KVStore;
import haxe.ds.StringMap;
import kernel.net.Package.NetworkID;
import bin.ns.NameSystemRPC.INameSystemRPC;
import kernel.ps.ProcessHandle;
import kernel.ps.IProcess;
using Lambda;
using tink.CoreApi;
@:build(macros.Binstore.includeBin("NameSystem", ["ns", "namesystem"]))
class NameSystem implements IProcess implements INameSystemRPC {
private var handle:ProcessHandle;
private var idRecords:StringMap<NetworkID> = new StringMap();
public function new() {}
public function run(handle:ProcessHandle) {
this.handle = handle;
this.load();
kernel.net.Net.registerProto("ns", (p) -> {
NameSystemRPC.handlePackage(this, p);
});
}
private function load() {
var kv = new KVStore("NameSystem");
kv.load();
this.idRecords = kv.get("idRecords", new StringMap());
}
private function save() {
var kv = new KVStore("NameSystem");
kv.set("idRecords", this.idRecords);
kv.save();
}
public function setIDRecord(name:String, id:NetworkID):Noise {
this.idRecords.set(name, id);
this.save();
return Noise;
}
public function getIDRecord(name:String):Null<NetworkID> {
return this.idRecords.get(name);
}
public function removeIDRecord(name:String):Noise {
this.idRecords.remove(name);
this.save();
return Noise;
}
public function listIDRecords():Array<{name:String, id:NetworkID}> {
var rtn = [];
for (k => v in this.idRecords) {
rtn.push({name: k, id: v});
}
return rtn;
}
}

View File

@@ -0,0 +1,71 @@
package bin.ns;
import lib.CLIAppBase;
using tink.CoreApi;
@:build(macros.Binstore.includeBin("NameSystemCLI", ["ns-cli"]))
class NameSystemCLI extends CLIAppBase {
public function new() {
registerAsyncSubcommand("get", (args) -> {
var ns = NameSystemRPC.getDefault();
return ns.getIDRecord(args.getString("name")).map((r) -> {
switch r {
case Success(data):
handle.writeLine('Resolved: $data');
case Failure(failure):
handle.writeLine('Failed: $failure');
}
return true;
});
}, [String("name")]);
registerAsyncSubcommand("register", (args) -> {
var id = args.getInt("id");
var ns = NameSystemRPC.getDefault();
return ns.setIDRecord(args.getString("name"), id).map((r) -> {
switch r {
case Success(_):
handle.writeLine('Set');
case Failure(failure):
handle.writeLine('Failed: $failure');
}
return true;
});
}, [String("name"), Int("id")]);
registerAsyncSubcommand("list", (args) -> {
var ns = NameSystemRPC.getDefault();
return ns.listIDRecords().map((r) -> {
switch r {
case Success(data):
handle.writeLine('List: $data');
case Failure(failure):
handle.writeLine('Failed: $failure');
}
return true;
});
});
registerAsyncSubcommand("unregister", (args) -> {
var ns = NameSystemRPC.getDefault();
return ns.removeIDRecord(args.getString("name")).map((r) -> {
switch r {
case Success(_):
handle.writeLine('Unregisterd');
case Failure(failure):
handle.writeLine('Failed: $failure');
}
return true;
});
}, [String("name")]);
}
}

View File

@@ -0,0 +1,31 @@
package bin.ns;
import kernel.KernelSettings;
import macros.rpc.RPCBase;
import kernel.net.Package.NetworkID;
using tink.CoreApi;
interface INameSystemRPC {
function setIDRecord(name:String, id:NetworkID):Noise;
function getIDRecord(name:String):Null<NetworkID>;
function removeIDRecord(name:String):Noise;
function listIDRecords():Array<{name:String, id:NetworkID}>;
}
@:build(macros.rpc.RPC.buildRPC(INameSystemRPC))
class NameSystemRPC extends RPCBase {
public static function getDefault():NameSystemRPC {
return new NameSystemRPC(KernelSettings.nameServer, "ns");
}
public static function resolve(input:String):Promise<Null<NetworkID>> {
if (input == "12345") {
return Promise.resolve(Std.parseInt(input));
}
var ns = getDefault();
return ns.getIDRecord(input);
}
}

View File

@@ -1,14 +1,14 @@
package bin.pathfinder; package bin.pathfinder;
import lib.Pos3; import lib.WorldPos;
import lib.ui.elements.UIElement; import lib.ui.elements.IUIElement;
import lib.ui.elements.TextElement; import lib.ui.elements.TextElement;
import lib.ui.elements.RootElement; import lib.ui.elements.RootElement;
import kernel.ui.WindowContext; import kernel.ui.WindowContext;
import kernel.ps.ProcessHandle; import kernel.ps.ProcessHandle;
import kernel.ps.Process; import kernel.ps.IProcess;
class PFClient implements Process { class PFClient implements IProcess {
private var handle:ProcessHandle; private var handle:ProcessHandle;
private var ctx:WindowContext; private var ctx:WindowContext;
@@ -36,9 +36,9 @@ class PFClient implements Process {
private function render() { private function render() {
var acc = kernel.gps.GPS.getAccuracy(); var acc = kernel.gps.GPS.getAccuracy();
var pos:Pos3 = kernel.gps.GPS.getPosition() ?? {x: 0, y: 0, z: 0}; var pos:WorldPos = kernel.gps.GPS.getPosition() ?? {x: 0, y: 0, z: 0};
var childre:Array<UIElement> = [ var childre:Array<IUIElement> = [
new TextElement('Acc: ${acc}'), new TextElement('Acc: ${acc}'),
new TextElement('Pos: X:${pos.x} Y:${pos.y} Z:${pos.z}'), new TextElement('Pos: X:${pos.x} Y:${pos.y} Z:${pos.z}'),
new TextElement('UPDATE', { new TextElement('UPDATE', {

View File

@@ -1,99 +0,0 @@
package bin.srsc;
import kernel.KernelSettings;
import kernel.net.Package.NetworkID;
import lib.CLIAppBase;
using tink.CoreApi;
class CLI extends CLIAppBase {
public function new() {
registerAsyncSubcommand("get", (args) -> {
if (args.length < 1) {
handle.writeLine("Not enough arguments");
return Future.sync(false);
}
var rpc = new SiteRessourceControllerRPC(KernelSettings.siteController);
return rpc.get(args[0]).map((res) -> {
switch (res) {
case Success(data):
if (data == null) {
handle.writeLine("Not found");
} else {
handle.writeLine("Found: " + data);
}
case Failure(error):
handle.writeLine("Error: " + error);
}
return true;
});
}, "<name>");
registerAsyncSubcommand("register", (args) -> {
if (args.length < 2) {
handle.writeLine("Not enough arguments");
return Future.sync(false);
}
var id:NetworkID = Std.parseInt(args[1]);
if (id == null) {
handle.writeLine("Invalid id");
return Future.sync(false);
}
var rpc = new SiteRessourceControllerRPC(KernelSettings.siteController);
return rpc.register(args[0], id).map((res) -> {
switch (res) {
case Success(data):
handle.writeLine("Success");
case Failure(error):
handle.writeLine("Error: " + error);
}
return true;
});
}, "<name> <id>");
registerAsyncSubcommand("unregister", (args) -> {
if (args.length < 1) {
handle.writeLine("Not enough arguments");
return Future.sync(false);
}
var rpc = new SiteRessourceControllerRPC(KernelSettings.siteController);
// FIXME
return Future.sync(true);
// return rpc.unregister(args[0]).map((res) -> {
// switch (res) {
// case Success(_):
// handle.writeLine("Success");
// case Failure(error):
// handle.writeLine("Error: " + error);
// }
// return true;
// });
}, "<name>");
registerAsyncSubcommand("list", (args) -> {
var rpc = new SiteRessourceControllerRPC(KernelSettings.siteController);
return rpc.list().map((res) -> {
switch (res) {
case Success(data):
for (name in data) {
handle.writeLine(name);
}
case Failure(error):
handle.writeLine("Error: " + error);
}
return true;
});
});
}
}

View File

@@ -1,76 +0,0 @@
package bin.srsc;
import macros.rpc.RPC;
import lib.KVStore;
import kernel.net.Package;
import kernel.ps.ProcessHandle;
import kernel.ps.Process;
using tink.CoreApi;
/**
The SiteRessourceController is responsible for the management of ressources on the site.
It makes sure that ressources are have a unique id and that they are not duplicated.
It also keeps track of the responsible NetID for each ressource. Kinda like a DNS.
**/
@:build(macros.rpc.RPC.buildRPC())
class SiteRessourceController implements Process {
private var handle:ProcessHandle;
private var ressources:Map<String, NetworkID>;
public function new() {}
public function run(handle:ProcessHandle) {
this.handle = handle;
load();
RPC.generateRPCPackageHandle();
}
@rpc
public function register(name:String, netID:NetworkID):Bool {
if (ressources.exists(name) && ressources.get(name) != netID) {
return false;
}
ressources.set(name, netID);
save();
return true;
}
@rpc
public function unregister(name:String):Void {
if (ressources.exists(name)) {
ressources.remove(name);
}
save();
}
@rpc
public function get(name:String):Null<NetworkID> {
return ressources.get(name);
}
@rpc
public function list():Array<String> {
return [for (k in ressources.keys()) k];
}
private function load() {
var store = KVStore.getStoreForClass();
var data:Null<Map<String, NetworkID>> = store.get("ressources");
if (data != null) {
this.ressources = data;
} else {
this.ressources = new Map<String, NetworkID>();
}
}
private function save() {
var store = KVStore.getStoreForClass();
store.set("ressources", this.ressources);
store.save();
}
}

View File

@@ -0,0 +1,158 @@
package bin.turtle;
import kernel.turtle.Turtle;
import lib.turtle.TurtleAppBase;
@:build(macros.Binstore.includeBin("Circle Mine", ["circle-mine", "cm"]))
class CircleMine extends TurtleAppBase {
private var rings:Int;
private var skip:Int = 0;
private var depth:Int = 0;
public function new() {
super(initFunc, turtleFunc);
}
private function initFunc() {
if (handle.args.length < 1) {
handle.writeLine("Not enough args");
handle.close(false);
return;
}
this.rings = Std.parseInt(handle.args[0]);
if (this.rings == null) {
handle.writeLine("Failed to parse args");
handle.close(false);
return;
}
if (handle.args.length > 1) {
this.skip = Std.parseInt(handle.args[1]);
if (this.skip == null) {
handle.writeLine("Failed to parse args");
handle.close(false);
return;
}
}
}
private function turtleFunc() {
// Go down 1 layer
if (skip == 0) {
Turtle.dig(Down);
Turtle.down();
Turtle.dig(Down);
} else {
// Move to outer ring
for (i in 0...(skip * 3) - 1) {
Turtle.forward();
}
}
// Start mining
for (i in skip...(rings + skip)) {
if (i == 0) {
center();
handle.writeLine("Center done");
} else {
circle(i * 3 + 1);
handle.writeLine('Ring $i done');
}
}
// Go back to starting pos.
for (i in 0...((rings + skip) * 3 - 1)) {
Turtle.back();
}
handle.close();
}
private function circle(radius:Int) {
step();
step();
frontDig();
// Do a 1/2 of a side of a ring
Turtle.turnRight();
for (i in 0...radius) {
step();
}
// Do the other 2 sides
for (s in 0...3) {
Turtle.turnRight();
for (i in 0...radius) {
step();
}
middle();
for (i in 0...radius) {
step();
}
}
// Complete the 1/2 of the first side
Turtle.turnRight();
for (i in 0...radius) {
step();
}
Turtle.turnLeft();
Turtle.forward();
}
private function middle() {
Turtle.turnRight();
Turtle.dig(Front);
Turtle.down();
Turtle.dig(Front);
Turtle.turnLeft();
Turtle.turnLeft();
Turtle.dig(Front);
Turtle.up();
Turtle.dig(Front);
Turtle.turnRight();
}
private function step() {
Turtle.dig(Front);
Turtle.forward();
Turtle.dig(Down);
}
private function frontDig() {
Turtle.dig(Front);
Turtle.down();
Turtle.dig(Front);
Turtle.up();
}
private function center() {
Turtle.dig(Down);
step();
frontDig();
Turtle.turnRight();
for (i in 0...3) {
step();
Turtle.turnRight();
step();
Turtle.turnLeft();
frontDig();
Turtle.turnRight();
}
step();
Turtle.turnRight();
Turtle.forward();
Turtle.turnLeft();
Turtle.forward();
}
}

View File

@@ -0,0 +1,56 @@
package bin.turtle;
import lib.turtle.planner.Plan;
import kernel.ps.ProcessHandle;
import kernel.ps.IProcess;
@:build(macros.Binstore.includeBin("Circle Mine Plan", ["cmp"]))
class CircleMinePlan implements IProcess {
public function new() {}
public function run(handle:ProcessHandle) {
var rings:Int = 3;
var skip:Int = 0;
var p = Plan.newPlan();
p = p.repeat([Forward], (skip * 3) - 1); // Move to outer ring or stay if none skiped
for (i in skip...(rings + skip)) {
if (i == 0) {
p = p.act([Clear(Down), Down, Clear(Down)]) // Move down a layer
.subplan(center());
} else {
p = p.subplan(circle(i * 3 + 1));
}
}
p.repeat([Back], (rings + skip) * 3 - 1);
p.begin(handle);
}
private function circle(radius:Int):Plan {
return Plan.newPlan()
.act([FullTunnel, FullTunnel, HalfTunnel])
.act([TurnRight])
.repeat([FullTunnel], radius)
.repateSubplan(Plan.newPlan().act([TurnRight]).repeat([FullTunnel], radius).act([
TurnRight, Clear(Front), Down, Clear(Front), TurnLeft, TurnLeft, Clear(Front), Up, Clear(Front), TurnRight
]).repeat([FullTunnel], radius), 3)
.act([TurnRight])
.repeat([FullTunnel], radius)
.act([TurnLeft, Forward]);
}
private function center():Plan {
return Plan.newPlan()
.act([Clear(Down)])
.act([FullTunnel])
.act([Clear(Front), Down, Clear(Front), Up])
.act([TurnRight])
.repeat([FullTunnel, TurnRight, FullTunnel, TurnLeft, HalfTunnel, TurnRight], 3)
.act([FullTunnel])
.act([TurnRight, Forward, TurnLeft, Forward]);
}
}

View File

@@ -2,9 +2,9 @@ package bin.turtle;
import kernel.turtle.TurtleMutex; import kernel.turtle.TurtleMutex;
import kernel.ps.ProcessHandle; import kernel.ps.ProcessHandle;
import kernel.ps.Process; import kernel.ps.IProcess;
class Patrol implements Process { class Patrol implements IProcess {
private var handle:ProcessHandle; private var handle:ProcessHandle;
public function new() {} public function new() {}

View File

@@ -1,11 +1,13 @@
package kernel; package kernel;
#if debug
import lib.Debug;
#end
import kernel.service.ServiceManager; import kernel.service.ServiceManager;
import kernel.fs.FS; import kernel.fs.FS;
import kernel.gps.GPS; import kernel.gps.GPS;
import kernel.log.Log; import kernel.log.Log;
import kernel.net.Routing; import kernel.net.Routing;
import lib.Debug;
import kernel.ui.WindowManager; import kernel.ui.WindowManager;
import kernel.net.Net; import kernel.net.Net;
@@ -29,7 +31,9 @@ class Init {
KernelEvents.shutdown(); KernelEvents.shutdown();
}); });
#if debug
Debug.printBuildInfo(); Debug.printBuildInfo();
#end
if (!FS.exists("/var/ns")) { if (!FS.exists("/var/ns")) {
FS.makeDir("/var/ns"); FS.makeDir("/var/ns");

View File

@@ -4,7 +4,7 @@ import kernel.turtle.TurtleMutex;
import kernel.turtle.Turtle; import kernel.turtle.Turtle;
import kernel.peripherals.Peripherals.Peripheral; import kernel.peripherals.Peripherals.Peripheral;
import kernel.log.Log; import kernel.log.Log;
import lib.Pos; import lib.ScreenPos;
import cc.HTTP.HTTPResponse; import cc.HTTP.HTTPResponse;
import lua.TableTools; import lua.TableTools;
import lua.Coroutine; import lua.Coroutine;
@@ -38,11 +38,11 @@ class KernelEvents {
distance:Null<Float> distance:Null<Float>
}>; }>;
public static var onMonitorResize(default, null):Signal<String>; public static var onMonitorResize(default, null):Signal<String>;
public static var onMonitorTouch(default, null):Signal<{addr:String, pos:Pos}>; public static var onMonitorTouch(default, null):Signal<{addr:String, pos:ScreenPos}>;
public static var onMouseClick(default, null):Signal<{button:ButtonType, pos:Pos}>; public static var onMouseClick(default, null):Signal<{button:ButtonType, pos:ScreenPos}>;
public static var onMouseDrag(default, null):Signal<{button:ButtonType, pos:Pos}>; public static var onMouseDrag(default, null):Signal<{button:ButtonType, pos:ScreenPos}>;
public static var onMouseScroll(default, null):Signal<{dir:Int, pos:Pos}>; public static var onMouseScroll(default, null):Signal<{dir:Int, pos:ScreenPos}>;
public static var onMouseUp(default, null):Signal<{button:ButtonType, pos:Pos}>; public static var onMouseUp(default, null):Signal<{button:ButtonType, pos:ScreenPos}>;
public static var onPaste(default, null):Signal<String>; public static var onPaste(default, null):Signal<String>;
public static var onPeripheral(default, null):Signal<String>; public static var onPeripheral(default, null):Signal<String>;
public static var onPeripheralDetach(default, null):Signal<String>; public static var onPeripheralDetach(default, null):Signal<String>;
@@ -75,11 +75,11 @@ class KernelEvents {
distance:Null<Float> distance:Null<Float>
}> = Signal.trigger(); }> = Signal.trigger();
private static final onMonitorResizeTrigger:SignalTrigger<String> = Signal.trigger(); private static final onMonitorResizeTrigger:SignalTrigger<String> = Signal.trigger();
private static final onMonitorTouchTrigger:SignalTrigger<{addr:String, pos:Pos}> = Signal.trigger(); private static final onMonitorTouchTrigger:SignalTrigger<{addr:String, pos:ScreenPos}> = Signal.trigger();
private static final onMouseClickTrigger:SignalTrigger<{button:ButtonType, pos:Pos}> = Signal.trigger(); private static final onMouseClickTrigger:SignalTrigger<{button:ButtonType, pos:ScreenPos}> = Signal.trigger();
private static final onMouseDragTrigger:SignalTrigger<{button:ButtonType, pos:Pos}> = Signal.trigger(); private static final onMouseDragTrigger:SignalTrigger<{button:ButtonType, pos:ScreenPos}> = Signal.trigger();
private static final onMouseScrollTrigger:SignalTrigger<{dir:Int, pos:Pos}> = Signal.trigger(); private static final onMouseScrollTrigger:SignalTrigger<{dir:Int, pos:ScreenPos}> = Signal.trigger();
private static final onMouseUpTrigger:SignalTrigger<{button:ButtonType, pos:Pos}> = Signal.trigger(); private static final onMouseUpTrigger:SignalTrigger<{button:ButtonType, pos:ScreenPos}> = Signal.trigger();
private static final onPasteTrigger:SignalTrigger<String> = Signal.trigger(); private static final onPasteTrigger:SignalTrigger<String> = Signal.trigger();
private static final onPeripheralTrigger:SignalTrigger<String> = Signal.trigger(); private static final onPeripheralTrigger:SignalTrigger<String> = Signal.trigger();
private static final onPeripheralDetachTrigger:SignalTrigger<String> = Signal.trigger(); private static final onPeripheralDetachTrigger:SignalTrigger<String> = Signal.trigger();

View File

@@ -39,20 +39,20 @@ class KernelSettings {
return value; return value;
} }
public static var siteController(get, set):NetworkID; public static var nameServer(get, set):NetworkID;
private static var _siteController:NetworkID = get("siteController"); private static var _nameServer:NetworkID = get("nameServer");
private static function get_siteController():NetworkID { private static function get_nameServer():NetworkID {
return _siteController; return _nameServer;
} }
private static function set_siteController(value:NetworkID):NetworkID { private static function set_nameServer(value:NetworkID):NetworkID {
if (value == null) { if (value == null) {
return get_siteController(); return get_nameServer();
} }
set("siteController", value); set("nameServer", value);
_siteController = value; _nameServer = value;
return value; return value;
} }
} }

View File

@@ -1,7 +1,7 @@
package kernel; package kernel;
import lib.Pos; import lib.ScreenPos;
import kernel.ui.TermWriteable; import kernel.ui.ITermWriteable;
import cc.Term; import cc.Term;
import lib.Vec.Vec2; import lib.Vec.Vec2;
import lib.Color; import lib.Color;
@@ -11,7 +11,7 @@ using tink.CoreApi;
/** /**
Represents the main computer screen. Represents the main computer screen.
**/ **/
class MainTerm implements TermWriteable { class MainTerm implements ITermWriteable {
/** /**
Depends on: KernelEvents, Depends on: KernelEvents,
**/ **/
@@ -39,7 +39,7 @@ class MainTerm implements TermWriteable {
Term.scroll(y); Term.scroll(y);
} }
public function getCursorPos():Pos { public function getCursorPos():ScreenPos {
var rtn = Term.getCursorPos(); var rtn = Term.getCursorPos();
return { return {
x: rtn.x - 1, x: rtn.x - 1,
@@ -60,7 +60,7 @@ class MainTerm implements TermWriteable {
Term.setCursorBlink(blink); Term.setCursorBlink(blink);
} }
public function getSize():Pos { public function getSize():ScreenPos {
var rtn = Term.getSize(); var rtn = Term.getSize();
return { return {
x: rtn.width, x: rtn.width,

View File

@@ -1,12 +1,12 @@
package kernel.binstore; package kernel.binstore;
import kernel.ps.Process; import kernel.ps.IProcess;
/** /**
Represents a callable program. Represents a callable program.
**/ **/
typedef Bin = { typedef Bin = {
c:Void->Process, c:Void->IProcess,
name:String, name:String,
aliases:Array<String>, aliases:Array<String>,
} }

View File

@@ -1,6 +1,6 @@
package kernel.binstore; package kernel.binstore;
import kernel.ps.Process; import kernel.ps.IProcess;
import macros.Binstore; import macros.Binstore;
class BinStore { class BinStore {
@@ -13,7 +13,7 @@ class BinStore {
return bins; return bins;
} }
public static function instantiate(alias:String):Null<Process> { public static function instantiate(alias:String):Null<IProcess> {
for (bin in bins) { for (bin in bins) {
for (a in bin.aliases) { for (a in bin.aliases) {
if (a == alias) { if (a == alias) {

View File

@@ -1,11 +1,12 @@
package kernel.gps; package kernel.gps;
import lib.SingleTimeoutPromise;
import kernel.log.Log; import kernel.log.Log;
import lib.KVStore; import lib.KVStore;
import kernel.net.Net; import kernel.net.Net;
import kernel.net.INetworkInterface; import kernel.net.INetworkInterface;
import kernel.net.Package; import kernel.net.Package;
import lib.Pos3; import lib.WorldPos;
using tink.CoreApi; using tink.CoreApi;
@@ -15,20 +16,22 @@ using tink.CoreApi;
You need at least 3 computers that know their position to determine the position of the computer. You need at least 3 computers that know their position to determine the position of the computer.
**/ **/
class GPS { class GPS {
private static inline final TIMEOUT:Int = 1;
private static var shouldRespond = true; private static var shouldRespond = true;
private static var shouldDoWholeNumberCheck = true; private static var shouldDoWholeNumberCheck = true;
private static var posAccuracy = 0; // 0 = unkown, 1 = (ins,best guess), 2 = (stored/manual,should be right), 3 = (gps,confirmed) private static var posAccuracy = 0; // 0 = unkown, 1 = (ins,best guess), 2 = (stored/manual,should be right), 3 = (gps,confirmed)
private static var cachedPosition:Pos3; private static var cachedPosition:WorldPos;
private static var lastPositionResponse:Array<{pos:Pos3, dist:Float}> = []; private static var lastPositionResponse:Array<{pos:WorldPos, dist:Float}> = [];
private static var futureResolve:(pos:Null<Pos3>) -> Void = null; private static final locatePromise:SingleTimeoutPromise<WorldPos> = new SingleTimeoutPromise(TIMEOUT, brodcastPositionRequest);
@:allow(kernel.Init) @:allow(kernel.Init)
private static function init() { private static function init() {
loadCachedPosition(); loadCachedPosition();
} }
public static function setManualPosition(pos:Pos3) { public static function setManualPosition(pos:WorldPos) {
var kvstore = new KVStore("gps"); var kvstore = new KVStore("gps");
kvstore.set("mpos", pos); kvstore.set("mpos", pos);
kvstore.save(); kvstore.save();
@@ -40,12 +43,12 @@ class GPS {
} }
@:allow(kernel.gps.INS) @:allow(kernel.gps.INS)
private static function setINSPosition(pos:Pos3) { private static function setINSPosition(pos:WorldPos) {
cachedPosition = pos; cachedPosition = pos;
posAccuracy = 1; posAccuracy = 1;
} }
public static function getPosition():Null<Pos3> { public static function getPosition():Null<WorldPos> {
return cachedPosition; return cachedPosition;
} }
@@ -58,18 +61,8 @@ class GPS {
posAccuracy = 0; posAccuracy = 0;
} }
public static function locate():Future<Null<Pos3>> { public static function locate():Promise<WorldPos> {
// TODO: implenet a timeout return locatePromise.request();
// TODO: dont send a request twice if the last one is still pending or we moved
return new Future<Null<Pos3>>((resolve) -> {
futureResolve = resolve;
sendPositionRequest();
return null;
});
}
private static function resolveFuture(pos:Null<Pos3>) {
futureResolve(pos);
} }
private static function persistCachedPositon() { private static function persistCachedPositon() {
@@ -85,8 +78,8 @@ class GPS {
var kvstore = new KVStore("gps"); var kvstore = new KVStore("gps");
kvstore.load(); kvstore.load();
var mPos:Null<Pos3> = kvstore.get("mpos"); // Manual set position var mPos:Null<WorldPos> = kvstore.get("mpos"); // Manual set position
var cPos:Null<Pos3> = kvstore.get("cpos"); // Cached position var cPos:Null<WorldPos> = kvstore.get("cpos"); // Cached position
if (mPos != null && cPos != null && mPos == cPos) { if (mPos != null && cPos != null && mPos == cPos) {
// Both are the same, so we can use the cached position // Both are the same, so we can use the cached position
@@ -117,12 +110,17 @@ class GPS {
} }
} }
private static function sendPositionRequest() { private static function brodcastPositionRequest() {
Net.brodcastGPSRequest(); Net.brodcastGPSRequest();
} }
@:allow(kernel.net.Net) @:allow(kernel.net.Net)
private static function handlePackage(pack:Package<Noise>, dist:Float, iface:INetworkInterface) { private static function handlePackage(pack:Package<Noise>, dist:Null<Float>, iface:INetworkInterface) {
if (dist == null) {
// Message comes from another dimension and has no distance.
return;
}
switch (pack.type) { switch (pack.type) {
case GPSRequest: case GPSRequest:
if (!shouldRespond) if (!shouldRespond)
@@ -132,13 +130,13 @@ class GPS {
if (cachedPosition == null) if (cachedPosition == null)
return; return;
var response = new Package(Net.networkID, pack.fromID, pack.msgID, GPSResponse(cachedPosition), null, 0); var response = new Package(Net.networkID, pack.fromID, pack.msgID, GPSResponse(cachedPosition.x, cachedPosition.y, cachedPosition.z,), null, 0);
iface.send(pack.fromID, Net.networkID, response); iface.send(pack.fromID, Net.networkID, response);
case GPSResponse(pos): case GPSResponse(x, y, z):
if (lastPositionResponse.contains({pos: pos, dist: dist})) if (lastPositionResponse.contains({pos: {x: x, y: y, z: z}, dist: dist}))
return; // Ignore duplicate responses return; // Ignore duplicate responses
lastPositionResponse.push({pos: pos, dist: dist}); lastPositionResponse.push({pos: {x: x, y: y, z: z}, dist: dist});
// TODO: wait for a few seconds before calculating the position, so we can get more responses // TODO: wait for a few seconds before calculating the position, so we can get more responses
@@ -158,12 +156,12 @@ class GPS {
cachedPosition = calculatedPosition; cachedPosition = calculatedPosition;
posAccuracy = 3; posAccuracy = 3;
resolveFuture(calculatedPosition); locatePromise.resolve(calculatedPosition);
default: default:
} }
} }
private static function calculatePosition():Null<Pos3> { private static function calculatePosition():Null<WorldPos> {
if (lastPositionResponse.length < 3) if (lastPositionResponse.length < 3)
return null; return null;
@@ -219,7 +217,7 @@ class GPS {
/** /**
Determines the position(s) of a point given 3 other points and the distance to each of them. Determines the position(s) of a point given 3 other points and the distance to each of them.
**/ **/
private static function trilateration(p1:Pos3, p2:Pos3, p3:Pos3, r1:Float, r2:Float, r3:Float):Pair<Pos3, Pos3> { private static function trilateration(p1:WorldPos, p2:WorldPos, p3:WorldPos, r1:Float, r2:Float, r3:Float):Pair<WorldPos, WorldPos> {
var a2b = p2 - p1; var a2b = p2 - p1;
var a2c = p3 - p1; var a2c = p3 - p1;
@@ -228,7 +226,7 @@ class GPS {
var i = ex.dot(a2c); var i = ex.dot(a2c);
var ey = (a2c - ex * i).normalize(); var ey = (a2c - ex * i).normalize();
var j = ey.dot(a2c); var j = ey.dot(a2c);
var ez = ex.cross(ey); var ez:WorldPos = ex.cross(ey);
var x = (r1 * r1 - r2 * r2 + d * d) / (2 * d); var x = (r1 * r1 - r2 * r2 + d * d) / (2 * d);
var y = (r1 * r1 - r3 * r3 - x * x + (x - i) * (x - i) + j * j) / (2 * j); var y = (r1 * r1 - r3 * r3 - x * x + (x - i) * (x - i) + j * j) / (2 * j);

View File

@@ -1,14 +1,15 @@
package kernel.gps; package kernel.gps;
import kernel.log.Log; import lib.SinglePromise;
import kernel.turtle.Turtle; import kernel.turtle.Turtle;
import lib.Pos3; import lib.WorldPos;
using tink.CoreApi; using tink.CoreApi;
class INS { class INS {
private static var heading:Null<Pos3> = null; private static var heading:Null<WorldPos> = null;
private static var alingment:Int = 1; // 0 = degraded, 1 = not aligned, 2 = aligned private static var alingment:Int = 1; // 0 = degraded, 1 = not aligned, 2 = aligned
private static final alignPromise:SinglePromise<Noise> = new SinglePromise(startAlign);
@:allow(kernel.turtle.Turtle) @:allow(kernel.turtle.Turtle)
private static function moveForward() { private static function moveForward() {
@@ -68,7 +69,10 @@ class INS {
} }
} }
private static function move(dir:Null<Pos3>) { private static function move(dir:Null<WorldPos>) {
if (alignPromise.isRunning()) {
return;
}
var pos = GPS.getPosition(); var pos = GPS.getPosition();
if (pos == null || dir == null) if (pos == null || dir == null)
return; return;
@@ -76,58 +80,53 @@ class INS {
GPS.setINSPosition(newPos); GPS.setINSPosition(newPos);
} }
public static function getHeading():Null<Pos3> { public static function getHeading():Null<WorldPos> {
return heading; return heading;
} }
public static function align():Promise<Noise> { public static function align():Promise<Noise> {
Log.info("Aligning INS"); return alignPromise.request();
return new Promise<Noise>((resolve, reject) -> { }
if (Turtle.getFuelLevel() < 2) {
Log.warn("Not enough fuel to align"); public static function startAlign():Void {
reject(new Error("Not enough fuel to align")); if (Turtle.getFuelLevel() < 2) {
return null; alignPromise.reject(new Error("Not enough fuel to align"));
return;
}
GPS.locate().handle((result) -> {
switch result {
case Failure(err):
alignPromise.reject(new Error("Failed to locate 1st positon: " + err));
return;
case Success(pos1):
var moved = tryMoving();
if (moved == -1) {
alignPromise.reject(new Error("Can't move"));
return;
}
GPS.locate().handle((result2) -> {
switch result2 {
case Failure(err):
alignPromise.reject(new Error("GPS not available for 2nd position: " + err));
return;
case Success(pos2):
var cHeading = calcHeading(pos1, pos2, moved);
if (cHeading == null) {
alignPromise.reject(new Error("Can't calculate heading"));
return;
}
heading = cHeading;
moveBack(moved);
GPS.setINSPosition(pos1);
alignPromise.resolve(Noise);
}
});
} }
GPS.locate().handle((pos1) -> {
Log.debug('pos1: $pos1');
if (pos1 == null) {
Log.warn("GPS not available for 1st position");
reject(new Error("GPS not available"));
return;
}
var moved = tryMoving();
if (moved == -1) {
Log.warn("Can't move");
reject(new Error("Can't move"));
return;
}
GPS.locate().handle((pos2) -> {
Log.debug('pos2: $pos2');
if (pos2 == null) {
Log.warn("GPS not available for 2nd position");
reject(new Error("GPS not available for 2nd position"));
return;
}
var cHeading = calcHeading(pos1, pos2, moved);
if (cHeading == null) {
Log.error("Can't calculate heading");
reject(new Error("Can't calculate heading"));
return;
}
heading = cHeading;
moveBack(moved);
GPS.setINSPosition(pos1);
resolve(Noise);
});
});
return null;
}); });
} }
@@ -150,7 +149,7 @@ class INS {
} }
} }
private static function calcHeading(pos1:Pos3, pos2:Pos3, moved:Int):Null<Pos3> { private static function calcHeading(pos1:WorldPos, pos2:WorldPos, moved:Int):Null<WorldPos> {
if (moved == 0) { if (moved == 0) {
return pos1 - pos2; return pos1 - pos2;
} else if (moved == 1) { } else if (moved == 1) {
@@ -167,24 +166,18 @@ class INS {
private static function moveBack(moved:Int) { private static function moveBack(moved:Int) {
if (moved == 0) { if (moved == 0) {
Turtle.forward(); Turtle.forward();
// cc.Turtle.forward();
} else if (moved == 1) { } else if (moved == 1) {
Turtle.back(); Turtle.back();
// cc.Turtle.back();
} else if (moved == 2) { } else if (moved == 2) {
Turtle.back(); Turtle.back();
// cc.Turtle.back();
Turtle.turnRight(); Turtle.turnRight();
// cc.Turtle.turnRight();
} else if (moved == 3) { } else if (moved == 3) {
Turtle.forward(); Turtle.forward();
// cc.Turtle.forward();
Turtle.turnRight(); Turtle.turnRight();
// cc.Turtle.turnRight();
} }
} }
private static function rotatePos3ToRight(pos:Pos3):Pos3 { private static function rotatePos3ToRight(pos:WorldPos):WorldPos {
if (pos.x == 0 && pos.z == -1) { if (pos.x == 0 && pos.z == -1) {
return {x: 1, y: 0, z: 0}; return {x: 1, y: 0, z: 0};
} else if (pos.x == -1 && pos.z == 0) { } else if (pos.x == -1 && pos.z == 0) {
@@ -198,7 +191,7 @@ class INS {
} }
} }
private static function rotatePos3ToLeft(pos3:Pos3):Pos3 { private static function rotatePos3ToLeft(pos3:WorldPos):WorldPos {
return rotatePos3ToRight(rotatePos3ToRight(rotatePos3ToRight(pos3))); return rotatePos3ToRight(rotatePos3ToRight(rotatePos3ToRight(pos3)));
} }
} }

View File

@@ -1,7 +1,8 @@
package kernel.log; package kernel.log;
import haxe.ds.ReadOnlyArray; import haxe.ds.ReadOnlyArray;
#if webconsole import haxe.macro.Context;
#if (webconsole && !macro)
import lib.Debug; import lib.Debug;
#end #end
@@ -21,32 +22,62 @@ class Log {
@:allow(kernel.Init) @:allow(kernel.Init)
private static function init() { private static function init() {
onLog = onLogTrigger.asSignal(); onLog = onLogTrigger.asSignal();
}
public static function info(msg:Dynamic, ?pos:haxe.PosInfos) {
log({level: Info, message: Std.string(msg), time: 0}, pos);
}
public static function warn(msg:Dynamic, ?pos:haxe.PosInfos) {
log({level: Warn, message: Std.string(msg), time: 0}, pos);
}
public static function error(msg:Dynamic, ?pos:haxe.PosInfos) {
log({level: Error, message: Std.string(msg), time: 0}, pos);
}
public static function debug(msg:Dynamic, ?pos:haxe.PosInfos) {
#if debug #if debug
log({level: Debug, message: Std.string(msg), time: 0}, pos); haxe.Log.trace = function(v:Dynamic, ?infos:haxe.PosInfos) {
Log._debug(v, infos.className);
}
#end #end
} }
public static function silly(msg:Dynamic, ?pos:haxe.PosInfos) { public static function _info(msg:Dynamic, ?origin:String) {
log({level: Silly, message: Std.string(msg), time: 0}, pos); log({
level: Info,
message: Std.string(msg),
time: 0,
origin: origin
});
} }
private static function log(line:LogLine, ?pos:haxe.PosInfos) { public static function _warn(msg:Dynamic, ?origin:String) {
line.origin = pos.className; log({
level: Warn,
message: Std.string(msg),
time: 0,
origin: origin
});
}
public static function _error(msg:Dynamic, ?origin:String) {
log({
level: Error,
message: Std.string(msg),
time: 0,
origin: origin
});
}
public static function _debug(msg:Dynamic, ?origin:String) {
#if debug
log({
level: Debug,
message: Std.string(msg),
time: 0,
origin: origin
});
#end
}
public static function _silly(msg:Dynamic, ?origin:String) {
log({
level: Silly,
message: Std.string(msg),
time: 0,
origin: origin
});
}
private static function log(line:LogLine) {
logLines.push(line); logLines.push(line);
if (logLines.length > MAX_LINES) { if (logLines.length > MAX_LINES) {
@@ -54,12 +85,49 @@ class Log {
} }
onLogTrigger.trigger(line); onLogTrigger.trigger(line);
#if webconsole #if (webconsole && !macro)
Debug.printWeb('[${Std.string(line.level)}][${line.origin}] ${line.message}'); Debug.logToWebconsole(line);
#end #end
} }
public static function getLines():ReadOnlyArray<LogLine> { public static function getLines():ReadOnlyArray<LogLine> {
return logLines; return logLines;
} }
#if macro
private static function getOrigin():String {
var localClass = Context.getLocalClass().get();
return localClass.name;
}
#end
public macro static function info(msg:ExprOf<Dynamic>) {
return macro {
Log._info(${msg}, $v{getOrigin()});
}
}
public macro static function warn(msg:ExprOf<Dynamic>) {
return macro {
Log._warn(${msg}, $v{getOrigin()});
}
}
public macro static function error(msg:ExprOf<Dynamic>) {
return macro {
Log._error(${msg}, $v{getOrigin()});
}
}
public macro static function debug(msg:ExprOf<Dynamic>) {
return macro {
Log._debug(${msg}, $v{getOrigin()});
}
}
public macro static function silly(msg:ExprOf<Dynamic>) {
return macro {
Log._silly(${msg}, $v{getOrigin()});
}
}
} }

View File

@@ -39,6 +39,35 @@ class Net {
setupInterf(interf); setupInterf(interf);
} }
// Handle when a modem has been removed
KernelEvents.onPeripheralDetach.handle((addr) -> {
var idx = interfaces.findIndex((i) -> {
return i.name() == addr;
});
if (idx == -1) {
return;
}
Log.debug('Removed modem on addr: $addr');
interfaces.splice(idx, 1);
Routing.removeInterface(addr);
});
// Handle when a new modem is connected
KernelEvents.onPeripheral.handle((addr) -> {
if (!Peripheral.getTypes(addr).contains("modem")) {
return;
}
Log.debug('Added modem on addr: $addr');
var modem = Peripheral.getModem(addr);
interfaces.push(modem);
setupInterf(modem);
Routing.addInterface(modem);
});
setupPingHandle(); setupPingHandle();
Routing.setup(); Routing.setup();
@@ -62,6 +91,9 @@ class Net {
}); });
} }
/**
Enables the interface to receive messages.
**/
private static function setupInterf(interf:INetworkInterface) { private static function setupInterf(interf:INetworkInterface) {
interf.onMessage.handle(e -> handle(e.pack, interf, e.dist)); interf.onMessage.handle(e -> handle(e.pack, interf, e.dist));
interf.listen(networkID); interf.listen(networkID);
@@ -135,12 +167,13 @@ class Net {
if (!sendRaw(pack)) { if (!sendRaw(pack)) {
// Cant forward // Cant forward
Log.silly('Received a message that could not be forwarded to ${pack.toID}');
} }
} }
public static function respondTo(pack:GenericPackage, data:Dynamic) { public static function respondTo(pack:GenericPackage, data:Dynamic) {
if (pack.type.match(DataNoResponse(_))) { if (!pack.type.match(Data(_))) {
Log.warn("Responed to a no response package. Ignoring"); Log.warn("Responding to a package that does require responding. Ignoring.");
return; return;
} }
@@ -212,6 +245,8 @@ class Net {
if (timeout != null) { if (timeout != null) {
timeout.cancle(); timeout.cancle();
} }
responseBus.remove(pack.msgID);
}); });
timeout = new Timer(MESSAGE_TIMEOUT, () -> { timeout = new Timer(MESSAGE_TIMEOUT, () -> {

View File

@@ -1,7 +1,5 @@
package kernel.net; package kernel.net;
import lib.Pos3;
typedef NetworkID = Int; typedef NetworkID = Int;
typedef GenericPackage = Package<Dynamic>; typedef GenericPackage = Package<Dynamic>;
@@ -12,7 +10,7 @@ enum PackageTypes {
RouteDiscover(reachableIDs:Array<{id:NetworkID, cost:Int}>); RouteDiscover(reachableIDs:Array<{id:NetworkID, cost:Int}>);
RouteDiscoverResponse(reachableIDs:Array<{id:NetworkID, cost:Int}>); RouteDiscoverResponse(reachableIDs:Array<{id:NetworkID, cost:Int}>);
RouteDiscoverUpdate(reachableIDs:Array<{id:NetworkID, cost:Int}>); RouteDiscoverUpdate(reachableIDs:Array<{id:NetworkID, cost:Int}>);
GPSResponse(pos:Pos3); GPSResponse(x:Float, y:Float, z:Float);
GPSRequest(); GPSRequest();
} }

View File

@@ -74,42 +74,37 @@ class Routing {
private static function handleRoutePackage(pack:Package<Noise>, interf:INetworkInterface):Void { private static function handleRoutePackage(pack:Package<Noise>, interf:INetworkInterface):Void {
addPossibleRoute(pack.fromID, interf, 0, pack.fromID); addPossibleRoute(pack.fromID, interf, 0, pack.fromID);
var shouldRespond:Bool = switch pack.type { switch pack.type {
case RouteDiscoverResponse(routes): case RouteDiscoverResponse(routes):
// A request for routes has been answerd
for (route in routes) { for (route in routes) {
addPossibleRoute(route.id, interf, route.cost, pack.fromID); addPossibleRoute(route.id, interf, route.cost, pack.fromID);
} }
false;
case RouteDiscover(routes): case RouteDiscover(routes):
// Received a request for available routes
for (route in routes) { for (route in routes) {
addPossibleRoute(route.id, interf, route.cost, pack.fromID); addPossibleRoute(route.id, interf, route.cost, pack.fromID);
} }
true;
// Respond to peer
var response:Package<Noise> = {
toID: pack.fromID,
fromID: Net.networkID,
msgID: null,
type: RouteDiscoverResponse(genRouteList()),
data: null,
ttl: 0, // Prevent forwarding
}
interf.send(response.toID, Net.networkID, response);
case RouteDiscoverUpdate(routes): case RouteDiscoverUpdate(routes):
// Received an update of routes
for (route in routes) { for (route in routes) {
addPossibleRoute(route.id, interf, route.cost, pack.fromID); addPossibleRoute(route.id, interf, route.cost, pack.fromID);
} }
false;
default: default:
Log.error("Expected package to be a Route discover package"); Log.silly("Expected package to be a Route discover package");
false;
}; };
if (!shouldRespond) {
return;
}
// Respond to peer
var response:Package<Noise> = {
toID: pack.fromID,
fromID: Net.networkID,
msgID: null,
type: RouteDiscoverResponse(genRouteList()),
data: null,
ttl: 0, // Prevent forwarding
}
interf.send(response.toID, Net.networkID, response);
} }
/** /**
@@ -126,6 +121,9 @@ class Routing {
return routes; return routes;
} }
/**
Create a new packate to brodcast our routes.
**/
private static function newRoutDiscoverPackage():Package<Noise> { private static function newRoutDiscoverPackage():Package<Noise> {
var pack:Package<Noise> = { var pack:Package<Noise> = {
type: RouteDiscover(genRouteList()), type: RouteDiscover(genRouteList()),
@@ -179,4 +177,28 @@ class Routing {
public static function getRouteTable():Map<NetworkID, Route> { public static function getRouteTable():Map<NetworkID, Route> {
return routingTable; return routingTable;
} }
/**
Call when an interface is no longer available.
Will rebrodcast routing requests.
**/
@:allow(kernel.net.Net)
private static function removeInterface(addr:String) {
// Remove the interface from the routing table
for (to => modem in routingTable) {
if (modem.interf.name() == addr) {
routingTable.remove(to);
}
}
// Since some routes to some host could be lost
// we rebrodcast on all interfaces again
brodcastRoutingTable();
}
@:allow(kernel.net.Net)
private static function addInterface(iface:INetworkInterface) {
var pack = newRoutDiscoverPackage();
iface.send(Net.BRODCAST_PORT, Net.networkID, pack);
}
} }

View File

@@ -0,0 +1,491 @@
package kernel.peripherals;
import lib.Item;
import haxe.exceptions.NotImplementedException;
/**
See https://github.com/MineMaarten/PneumaticCraft/blob/master/resources/assets/pneumaticcraft/wiki/en_US/block/droneInterface.txt
**/
class DroneInterface implements IPeripheral {
public static inline final TYPE_NAME:String = "drone_interface";
private final addr:String;
@:allow(kernel.peripherals)
private function new(addr:String) {
this.addr = addr;
}
public function getAddr():String {
return this.addr;
}
public function getType():String {
return DroneInterface.TYPE_NAME;
}
/**
Returns true if a Drone has connected with this Drone Interface
(when the Drone's program has arrived at the ComputerCraft piece and made a connection).
**/
public function isConnectedToDrone():Bool {
throw new NotImplementedException();
}
/**
Returns the pressure of the connected Drone.
**/
public function getDronePressure() {
throw new NotImplementedException();
}
/**
Returns a table of 3 double values containing the x,y and z position respectively of the Drone.
**/
public function getDronePosition() {
throw new NotImplementedException();
}
/**
Stops the ComputerCraft piece in the Drone, and allows the Drone's program to proceed to the next puzzle piece.
**/
public function exitPiece() {
throw new NotImplementedException();
}
/**
Returns a table of all the current selectable actions (like 'dig' or 'place').
**/
public function getAllActions() {
/*
pneumaticcraft:entity_attack
pneumaticcraft:dig
pneumaticcraft:harvest
pneumaticcraft:place
pneumaticcraft:block_right_click
pneumaticcraft:entity_right_click
pneumaticcraft:pickup_item
pneumaticcraft:drop_item
pneumaticcraft:void_item
pneumaticcraft:void_liquid
pneumaticcraft:inventory_export
pneumaticcraft:inventory_import
pneumaticcraft:liquid_export
pneumaticcraft:liquid_import
pneumaticcraft:entity_export
pneumaticcraft:entity_import
pneumaticcraft:rf_import
pneumaticcraft:rf_export
pneumaticcraft:goto
pneumaticcraft:teleport
pneumaticcraft:emit_redstone
pneumaticcraft:rename
pneumaticcraft:suicide
pneumaticcraft:crafting
pneumaticcraft:standby
pneumaticcraft:logistics
pneumaticcraft:edit_sign
pneumaticcraft:condition_redstone
pneumaticcraft:condition_light
pneumaticcraft:condition_item_inventory
pneumaticcraft:condition_block
pneumaticcraft:condition_liquid_inventory
pneumaticcraft:condition_pressure
pneumaticcraft:drone_condition_item
pneumaticcraft:drone_condition_liquid
pneumaticcraft:drone_condition_entity
pneumaticcraft:drone_condition_pressure
pneumaticcraft:drone_condition_upgrades
pneumaticcraft:condition_rf
pneumaticcraft:drone_condition_rf
pneumaticcraft:computer_control
*/
throw new NotImplementedException();
}
/**
Sets the place/dig order of the Drone.
**/
public function setBlockOrder(order:String) {
throw new NotImplementedException();
}
/**
Returns a table of all the possible area types (filled, frame, sphere, ..).
**/
public function getAreaTypes() {
throw new NotImplementedException();
}
/**
Adds an area to the current stored area of the Drone. When using the latter method, x1, y1, and z1, represent
the first point (which would be the first GPS Tool normally), and x2, y2, and z2 represent the second point.
**/
public function addArea(x:Int, y:Int, z:Int) {
throw new NotImplementedException();
}
/**
Removes an area from the current stored area (like blacklisting).
**/
public function removeArea(x:Int, y:Int, z:Int) {
throw new NotImplementedException();
}
/**
Clears the current stored area.
**/
public function clearArea() {
throw new NotImplementedException();
}
/**
Will show the current stored area using the area renderer you are used to.
**/
public function showArea() {
throw new NotImplementedException();
}
/**
Will stop showing the area stored in the Drone.
**/
public function hideArea() {
throw new NotImplementedException();
}
/**
Acts as you've put a Item Filter piece on the right of a piece (making it whitelisting).
The item/block name is in Minecraft format, i.e. "minecraft:stone", or "pneumaticcraft:pressureTube".
The 'useXXX' are all booleans that determine what filters will be used (same functionality as the check boxes in the Item Filter puzzle piece).
**/
public function addWhitelistItemFilter(item:Item, metadata:String, useMetadata:Bool, useNBT:Bool, useOreDictionary:Bool, useModSimilarity:Bool) {
throw new NotImplementedException();
}
/**
Like the addWhitelistItemFilter(...), but to blacklist items.
**/
public function addBlacklistItemFilter(item:Item, metadata:String, useMetadata:Bool, useNBT:Bool, useOreDictionary:Bool, useModSimilarity:Bool) {
throw new NotImplementedException();
}
/**
Clears all the whitelisted item filters stored.
**/
public function clearWhitelistItemFilter() {
throw new NotImplementedException();
}
/**
Clears all the blacklisted item filters stored.
**/
public function clearBlacklistItemFilter() {
throw new NotImplementedException();
}
/**
Adds a text to the whitelisted texts. Used to specify a filter for the Entity Attack action for example.
**/
public function addWhitelistText(text:String) {
throw new NotImplementedException();
}
/**
Adds a text to the blacklisted texts. Used to specify a filter for the Entity Attack action for example.
**/
public function addBlacklistText(text:String) {
throw new NotImplementedException();
}
/**
Clears the stored whitelisted texts.
**/
public function clearWhitelistText() {
throw new NotImplementedException();
}
/**
Clears the stored blacklisted texts.
**/
public function clearBlacklistText() {
throw new NotImplementedException();
}
/**
Acts as you've put a Liquid Filter piece on the right of a piece (making it whitelisting).
You can either give it the 'registry name', or the localized name.
**/
public function addWhitelistLiquidFilter(liquidName:String) {
throw new NotImplementedException();
}
/**
Like the addWhitelistLiquidFilter(...), but to blacklist liquids.
**/
public function addBlacklistLiquidFilter(liquidName:String) {
throw new NotImplementedException();
}
/**
Clears all the whitelisted liquid filters stored.
**/
public function clearWhitelistLiquidFilter() {
throw new NotImplementedException();
}
/**
Clears all the blacklisted liquid filters stored.
**/
public function clearBlacklistLiquidFilter() {
throw new NotImplementedException();
}
/**
Sets the strength the redstone will be emitting when the "emitRedstone" action would be set.
**/
public function setEmittingRedstone(strength:Int) {
throw new NotImplementedException();
}
/**
Sets the name the Drone will be named to when the "rename" action would be set.
**/
public function setRenameString(name:String) {
throw new NotImplementedException();
}
/**
When the "dropItem" action would be set, this determines whether or not
the item will be dropped with a random velocity, or straight down.
**/
public function setDropStraight(to:Bool) {
throw new NotImplementedException();
}
/**
This says whether or not the Drone has a maximum of imported/exported/dropped pieces.
If true, also use setCount().
**/
public function setUseCount(count:Int) {
throw new NotImplementedException();
}
/**
This configures the maximum amount of imported/exported/dropped items, and also is the amount that's checked when doing a condition check.
**/
public function setCount(count:Int) {
throw new NotImplementedException();
}
/**
Used in conditions only. When true, all checked blocks need to satisfy the condition requirements (>=10 etcetera).
**/
public function setIsAndFunction(to:Bool) {
throw new NotImplementedException();
}
/**
Used in conditions only.
Says whether or not the condition is checking for an equal amount (=) or equal and higher than amount (>=).
The amount can be set using setCount().
**/
public function setOperator(op:String) {
throw new NotImplementedException();
}
/**
Returns true/false. Used in conditions only.
Will return true/false depending on whether or not the condition is satisfied.
Drone Conditions can be checked right after setting 'setAction()'.
Note that you need to wait until 'isActionDone' if you're dealing with a non Drone Condition.
**/
public function evaluateCondition():Bool {
throw new NotImplementedException();
}
/**
Sets the specific side to be accessible or not.
Used in the Inventory Im- and Export actions to set which side of the inventory the Drone can access.
It is also used for the Place action to determine how to place the block.
**/
public function setSide(side:String, accessible:Bool) {
throw new NotImplementedException();
}
/**
Same as setSide(side: String, accessible: Bool), now in one function to set all sides at once (6x boolean).
**/
public function setSides(down:Bool, up:Bool, north:Bool, south:Bool, east:Bool, west:Bool) {
throw new NotImplementedException();
}
/**
This says whether or not the Drone has a maximum actions performed on a block at a time before the command is considered 'done'.
Applies to the Place, Dig and Right-Click block program. If true, also use setMaxActions().
**/
public function setUseMaxActions(to:Bool) {
throw new NotImplementedException();
}
/**
This configures the maximum amount of actions performed on blocks before the command is considered 'done'.
This applies to the Place, Dig an Right-Click block program. Be sure to also call setUseMaxActions(true) to enable usage of this.
**/
public function setMaxActions(max:Int) {
throw new NotImplementedException();
}
/**
Sets up the crafting grid so when the Crafting instruction is called, this recipe will be used.
You need to specify all 9 items making up the recipe.
For empty spaces supply nil. The naming format is the same as for supplying item filters.
TODO: args: <item/block name>, <item/block name>, ...(9x)
**/
public function setCraftingGrid() {
throw new NotImplementedException();
}
/**
Only used in the right click command, this will set whether or not the fake player is sneaking while right clicking.
**/
public function setSneaking(sneaking:Bool) {
throw new NotImplementedException();
}
/**
Only used in the Liquid Export command, when set to true the Drone will be allowed to place down fluid blocks.
**/
public function setPlaceFluidBlocks(to:Bool) {
throw new NotImplementedException();
}
/**
Sets the[link{pneumaticcraft:progwidget/coordinateOperator}] variable[link{}] of this Drone.
When passing 'true', the coordinate will be set to (1,0,0).
Alternatively, false will be setting it to (0,0,0).
It is possible to set global variables (#) as well.
TODO: args
setVariable(<variable name>, <true/false>)
setVariable(<variable name>, <x>)
setVariable(<variable name>, <x>, <y>, <z>)
**/
public function setVariable(name:String, to:Dynamic) {
throw new NotImplementedException();
}
/**
Returns the value of the variable from this Drone (x, y and z).
It is possible to get global (#) and special variables ($) as well.
**/
public function getVariable(name:String):Dynamic {
throw new NotImplementedException();
}
/**
Sets the text that's going to be set to Signs using the Edit Sign command.
TODO: args <line1>, <line2>, ..., <lineN>
**/
public function setSignText(lines:Array<String>) {
throw new NotImplementedException();
}
/**
String that represents the action. This should be one of the actions returned by getAllActions().
**/
public function setAction(action:String) {
throw new NotImplementedException();
}
/**
Returns a string that represents the last action set by 'setAction'.
Will return nil when no action is set.
Can be used to make sure to only call 'isActionDone()' when this method does not return nil.
**/
public function getAction():String {
throw new NotImplementedException();
}
/**
Stops the current running action.
**/
public function abortAction() {
throw new NotImplementedException();
}
/**
Returns true if the current action is done (goto has arrived at the target location,
inventory import can't import anymore, dig has dug every possible block, ..).
**/
public function isActionDone():Bool {
throw new NotImplementedException();
}
/**
When the Drone was targeting an Entity (in the Entity Attack program), this will stop attacking that target.
**/
public function forgetTarget() {
throw new NotImplementedException();
}
/**
Will get the amount of inserted upgrades of the given type.
This type is the index of the upgrade (in creative, or NEI), starting with 0.
Or the metadata value when you use NEI.
A Speed upgrade for example has an index of 5.
**/
public function getUpgrades(upgrade:String) {
throw new NotImplementedException();
}
/**
Unkown. Showed up in the inspect.
**/
public function setCanSteal(canSteal:Bool) {
throw new NotImplementedException();
}
/**
Unkown. Showed up in the inspect.
**/
public function setRequiresTool(requiresTool:Bool) {
throw new NotImplementedException();
}
/**
Unkown. Showed up in the inspect.
**/
public function setCheckLineOfSight(check:Bool) {
throw new NotImplementedException();
}
/**
Unkown. Showed up in the inspect.
**/
public function setRightClickType(to:String) {
throw new NotImplementedException();
}
/**
Unkown. Showed up in the inspect.
**/
public function getDronePositionVec() {
throw new NotImplementedException();
}
/**
Unkown. Showed up in the inspect.
**/
public function getOwnerID() {
throw new NotImplementedException();
}
/**
Unkown. Showed up in the inspect.
**/
public function getOwnerName():String {
throw new NotImplementedException();
}
}

View File

@@ -1,9 +1,6 @@
package kernel.peripherals; package kernel.peripherals;
import lib.exporter.ExportConfig; class EnergyStorage implements IPeripheral {
import lib.exporter.IExportable;
class EnergyStorage implements IPeripheral implements IExportable {
public static inline final TYPE_NAME:String = "energyCell"; public static inline final TYPE_NAME:String = "energyCell";
private final addr:String; private final addr:String;
@@ -29,13 +26,4 @@ class EnergyStorage implements IPeripheral implements IExportable {
public function getType():String { public function getType():String {
return TYPE_NAME; return TYPE_NAME;
} }
public function export():ExportConfig {
return {
getDelegates: [
"energy" => _ -> Number(this.getEnergy()),
"capacity" => _ -> Number(this.getEnergyCapacity()),
],
}
}
} }

View File

@@ -0,0 +1,76 @@
package kernel.peripherals;
import lib.BlockPos;
import lib.Tags;
import cc.Peripheral;
using tink.CoreApi;
using lua.Table;
@:structInit typedef ScanBlock = {
name:String,
tags:Tags,
pos:BlockPos
}
class GeoScanner implements IPeripheral {
public static inline final TYPE_NAME:String = "geoScanner";
private final addr:String;
public function new(addr:String) {
this.addr = addr;
}
public function getAddr():String {
return this.addr;
}
public function getType():String {
return TYPE_NAME;
}
/**
Returns the current time remaining until then next scan() can be ran.
**/
public function getScanCooldown():Int {
return Peripheral.call(this.addr, "getScanCooldown");
}
/**
Returns the cost in FE for a scan with the given radius.
Returns null of scan of this radius is not posible.
**/
public function cost(radius:Int):Null<Int> {
return Peripheral.call(this.addr, "cost", radius);
}
/**
Returns a list of data about all blocks in the radius.
X,Y,Z are relative to the geoscanner block.
Air blocks are not included.
List is unsorted.
**/
public function scan(radius:Int):Outcome<Array<ScanBlock>, String> {
// TODO: Handel fail state
var result:lua.Table<Int, {
x:Int,
y:Int,
z:Int,
name:String,
tags:lua.Table<Int, String>
}> = Peripheral.call(this.addr, "scan", radius);
return Success(result.toArray().map((e) -> {
name: e.name,
tags: Tags.fromIntTable(e.tags),
pos: new BlockPos(e.x, e.y, e.z)
}));
}
public function chunkAnalyze():Outcome<Map<String, Int>, String> {
var result:lua.Table<String, Int> = Peripheral.call(this.addr, "chunkAnalyze");
return Success(result.toMap());
}
}

View File

@@ -0,0 +1,123 @@
package kernel.peripherals;
import kernel.log.Log;
import lib.Debug;
import cc.Peripheral;
import lib.Tags;
import lib.Item;
import cc.periphs.ItemStorage.ReducedItemInfo;
import lua.Table;
using Lambda;
class ItemInfo {
public final count:Int;
public final name:Item;
public final nbt:Null<String>;
@:allow(kernel.peripherals.Inventory)
private function new(from:ReducedItemInfo) {
this.count = from.count;
this.name = from.name;
this.nbt = from.nbt;
}
}
class DetailedItemInfo extends ItemInfo {
public final displayName:String;
public final maxCount:Int;
public final tags:Tags;
public final durability:Null<Float>;
public final damage:Null<Int>;
public final maxDamage:Null<Int>;
public final enchantments:Array<{displayName:String, level:Int, name:String}>;
@:allow(kernel.peripherals.Inventory)
private function new(from:cc.periphs.ItemStorage.DetailedItemInfo) {
super(from);
this.tags = Tags.fromBoolTable(from.tags);
this.displayName = from.displayName;
this.maxCount = from.maxCount;
this.durability = from.durability;
this.damage = from.damage;
this.maxDamage = from.maxDamage;
if (from.enchantments != null) {
this.enchantments = from.enchantments;
} else {
this.enchantments = [];
}
}
}
class Inventory implements IPeripheral {
public static inline final TYPE_NAME:String = "inventory";
private final addr:String;
public function new(addr:String) {
this.addr = addr;
}
public function getAddr():String {
return this.addr;
}
public function getType():String {
return TYPE_NAME;
}
/**
Get the size of this inventory.
**/
public function size():Int {
return Peripheral.call(this.addr, "size");
}
/**
List all items in this inventory.
**/
public function list():Map<Int, ItemInfo> {
var list:Map<Int, ReducedItemInfo> = Table.toMap(Peripheral.call(this.addr, "list"));
var rtn:Map<Int, ItemInfo> = new Map();
for (k => v in list) {
rtn.set(k - 1, new ItemInfo(v));
}
return rtn;
}
/**
Get detailed information about an item.
**/
public function getItemDetail(slot:Int):DetailedItemInfo {
return new DetailedItemInfo(Peripheral.call(this.addr, "getItemDetail", slot + 1));
}
/**
Get the maximum number of items which can be stored in this slot.
Typically this will be limited to 64 items.
However, some inventorwies (such as barrels or caches) can store hundreds or thousands of items in one slot.
Keep in mind that this does not depend on that item stored in this slot.
**/
public function getItemLimit(slot:Int):Int {
return Peripheral.call(this.addr, "getItemLimit", slot + 1);
}
/**
Push items from one inventory to another connected one. Needs to be on the same wired network.
**/
public function pushItems(to:String, fromSlot:Int, ?limit:Int, ?toSlot:Int):Int {
return Peripheral.call(this.addr, "pushItems", to, fromSlot + 1, limit, toSlot);
}
/**
Pull items from a connected inventory into this one. Needs to be on the same wired network.
**/
public function pullItems(from:String, fromSlot:Int, ?limit:Int, ?toSlot:Int):Int {
return Peripheral.call(this.addr, "pullItems", from, fromSlot + 1, limit, toSlot);
}
}

View File

@@ -50,12 +50,7 @@ class Peripheral {
} }
var types = getTypes(addr); var types = getTypes(addr);
var methodsMap = cc.Peripheral.getMethods(addr).toMap(); var methods = cc.Peripheral.getMethods(addr).toArray();
var methods:Array<String> = [];
for (k => v in methodsMap) {
methods.push(k);
}
return { return {
types: types, types: types,
@@ -81,8 +76,14 @@ class Peripheral {
return getModem(addr); return getModem(addr);
case Printer.TYPE_NAME: case Printer.TYPE_NAME:
return getPrinter(addr); return getPrinter(addr);
case "redstone": case Speaker.TYPE_NAME:
return getSpeaker(addr);
case Redstone.TYPE_NAME:
return getRedstone(addr); return getRedstone(addr);
case Inventory.TYPE_NAME:
return getInventory(addr);
case GeoScanner.TYPE_NAME:
return getGeoScanner(addr);
} }
return null; return null;
@@ -158,4 +159,37 @@ class Peripheral {
public static function getAllComputers():Array<Computer> { public static function getAllComputers():Array<Computer> {
return [for (addr in findAddrByType(Computer.TYPE_NAME)) new Computer(addr)]; return [for (addr in findAddrByType(Computer.TYPE_NAME)) new Computer(addr)];
} }
public static function getSpeaker(addr:String):Null<Speaker> {
var addr = safeGetAddr(addr, Speaker.TYPE_NAME);
if (addr == null)
return null;
return new Speaker(addr);
}
public static function getAllSpeakers():Array<Speaker> {
return [for (addr in findAddrByType(Speaker.TYPE_NAME)) new Speaker(addr)];
}
public static function getInventory(addr:String):Null<Inventory> {
var addr = safeGetAddr(addr, Inventory.TYPE_NAME);
if (addr == null)
return null;
return new Inventory(addr);
}
public static function getAllInventorys():Array<Inventory> {
return [for (addr in findAddrByType(Inventory.TYPE_NAME)) new Inventory(addr)];
}
public static function getGeoScanner(addr:String):Null<GeoScanner> {
var addr = safeGetAddr(addr, GeoScanner.TYPE_NAME);
if (addr == null)
return null;
return new GeoScanner(addr);
}
public static function getAllGeoScanners():Array<GeoScanner> {
return [for (addr in findAddrByType(GeoScanner.TYPE_NAME)) new GeoScanner(addr)];
}
} }

View File

@@ -2,7 +2,7 @@ package kernel.peripherals;
import cc.Peripheral; import cc.Peripheral;
import lib.Rect; import lib.Rect;
import lib.Pos; import lib.ScreenPos;
class Printer implements IPeripheral { class Printer implements IPeripheral {
public static inline final TYPE_NAME:String = "printer"; public static inline final TYPE_NAME:String = "printer";
@@ -25,11 +25,11 @@ class Printer implements IPeripheral {
public function write(text:String) {} public function write(text:String) {}
public function getCurserPos():Pos { public function getCurserPos():ScreenPos {
return new Pos({x: 0, y: 0}); return new ScreenPos({x: 0, y: 0});
} }
public function setCurserPos(pos:Pos) { public function setCurserPos(pos:ScreenPos) {
this.native.setCursorPos(pos.x, pos.y); this.native.setCursorPos(pos.x, pos.y);
} }

View File

@@ -1,7 +1,6 @@
package kernel.peripherals; package kernel.peripherals;
import lib.exporter.ExportConfig; import kernel.peripherals.interfaces.IRedstone;
import lib.exporter.IExportable;
import haxe.ds.ReadOnlyArray; import haxe.ds.ReadOnlyArray;
import lib.Color; import lib.Color;
@@ -43,8 +42,7 @@ abstract BundleMask(Int) from cc.Colors.Color to cc.Colors.Color {
} }
} }
@:build(macros.Exporter.buildExport()) class Redstone implements IPeripheral implements IRedstone {
class Redstone implements IPeripheral implements IExportable {
public static inline final TYPE_NAME:String = "redstone"; // TODO: there is technically not a type for redstone. public static inline final TYPE_NAME:String = "redstone"; // TODO: there is technically not a type for redstone.
public final onChange:Signal<Noise>; public final onChange:Signal<Noise>;
@@ -89,12 +87,10 @@ class Redstone implements IPeripheral implements IExportable {
cc.Redstone.setOutput(this.addr, on); cc.Redstone.setOutput(this.addr, on);
} }
@export("output")
public inline function getOutput():Bool { public inline function getOutput():Bool {
return cc.Redstone.getOutput(this.addr); return cc.Redstone.getOutput(this.addr);
} }
@export("input")
public inline function getInput():Bool { public inline function getInput():Bool {
return cc.Redstone.getInput(this.addr); return cc.Redstone.getInput(this.addr);
} }

View File

@@ -1,15 +1,15 @@
package kernel.peripherals; package kernel.peripherals;
import cc.Peripheral; import cc.Peripheral;
import lib.Pos; import lib.ScreenPos;
import cc.Term.TerminalSize; import cc.Term.TerminalSize;
import kernel.ui.TermWriteable; import kernel.ui.ITermWriteable;
import lib.Vec.Vec2; import lib.Vec.Vec2;
import lib.Color; import lib.Color;
using tink.CoreApi; using tink.CoreApi;
class Screen implements TermWriteable implements IPeripheral { class Screen implements ITermWriteable implements IPeripheral {
public static inline final TYPE_NAME:String = "monitor"; public static inline final TYPE_NAME:String = "monitor";
private final nativ:cc.periphs.Monitor.Monitor; private final nativ:cc.periphs.Monitor.Monitor;
@@ -59,7 +59,7 @@ class Screen implements TermWriteable implements IPeripheral {
nativ.scroll(y); nativ.scroll(y);
} }
public function getCursorPos():Pos { public function getCursorPos():ScreenPos {
var rtn = nativ.getCursorPos(); var rtn = nativ.getCursorPos();
return { return {
x: rtn.x - 1, x: rtn.x - 1,

View File

@@ -0,0 +1,61 @@
package kernel.peripherals;
import cc.Peripheral;
using tink.CoreApi;
class Speaker implements IPeripheral {
public static inline final TYPE_NAME:String = "speaker";
private final addr:String;
public function new(addr:String) {
this.addr = addr;
}
public function getType():String {
return Speaker.TYPE_NAME;
}
public function getAddr():String {
return this.addr;
}
/**
Plays a note block note through the speaker.
The pitch argument uses semitones as the unit. This directly maps to the number of clicks on a note block.
For reference, 0, 12, and 24 map to F#, and 6 and 18 map to C.
A maximum of 8 notes can be played in a single tick. If this limit is hit, this function will return an error.
**/
public function playNote(instrument:String, ?volume:Float = 1.0, ?pitch:Int = 12):Outcome<Noise, String> {
if (Peripheral.call(addr, "playNote", instrument, volume, pitch)) {
return Success(null);
} else {
return Failure("maximum reached");
}
}
public function playSound(sound:String, ?volume:Float = 1.0, ?pitch:Float = 1.0):Outcome<Noise, String> {
try {
if (Peripheral.call(addr, "playSound", sound, volume, pitch)) {
return Success(null);
} else {
return Failure("Sound still playing");
}
} catch (e) {
return Failure("Sound does not exist");
}
}
public function playAudio(buffer:Array<Int>, ?volume:Float = 1.0):Outcome<Noise, String> {
try {
if (Peripheral.call(addr, "playAudio", buffer, volume)) {
return Success(null);
} else {
return Failure("Buffer full");
}
} catch (e) {
return Failure("Buffer malformed");
}
}
}

View File

@@ -0,0 +1,7 @@
package kernel.peripherals.exports;
import macros.rpc.RPCBase;
import kernel.peripherals.interfaces.IRedstone;
@:build(macros.rpc.RPC.buildRPC(IRedstone))
class RedstoneExport extends RPCBase {}

View File

@@ -0,0 +1,17 @@
package kernel.peripherals.interfaces;
import cc.Colors.Color;
import kernel.peripherals.Redstone.BundleMask;
interface IRedstone {
function setOutput(on:Bool):Void;
function getOutput():Bool;
function getInput():Bool;
function setAnalogOutput(strength:Int):Void;
function getAnalogOutput():Int;
function getAnalogInput():Int;
function setBundledOutput(output:BundleMask):Void;
function getBundledOutput():BundleMask;
function getBundledInput():BundleMask;
function testBundledInput(mask:Color):Bool;
}

View File

@@ -0,0 +1,34 @@
package kernel.pocket;
using tink.CoreApi;
class Pocket {
public static function isPocket():Bool {
return cc.Pocket != null;
}
/**
Search the player's inventory for another upgrade, replacing the existing one with that item if found.
This inventory search starts from the player's currently selected slot, allowing you to prioritise upgrades.
**/
public static function equipBack():Outcome<Noise, String> {
var r = cc.Pocket.equipBack();
if (r.successful) {
return Success(Noise);
}
return Failure(r.error);
}
/**
Remove the pocket computer's current upgrade.
**/
public static function unequipBack():Outcome<Noise, String> {
var r = cc.Pocket.unequipBack();
if (r.successful) {
return Success(Noise);
}
return Failure(r.error);
}
}

View File

@@ -3,6 +3,6 @@ package kernel.ps;
/** /**
Defines an independent process that can be run by the kernel. Defines an independent process that can be run by the kernel.
**/ **/
interface Process { interface IProcess {
public function run(handle:ProcessHandle):Void; public function run(handle:ProcessHandle):Void;
} }

View File

@@ -106,7 +106,7 @@ class ProcessHandle {
} }
public function get_args():ReadOnlyArray<String> { public function get_args():ReadOnlyArray<String> {
return this.config.args; return this.config.args != null ? this.config.args : [];
} }
public function claimTurtleMutex():Bool { public function claimTurtleMutex():Bool {

View File

@@ -10,7 +10,7 @@ typedef PID = Int;
class ProcessManager { class ProcessManager {
private static final processList = new Map<PID, ProcessHandle>(); private static final processList = new Map<PID, ProcessHandle>();
public static function run(process:Process, config:HandleConfig):PID { public static function run(process:IProcess, config:HandleConfig):PID {
var pid = createPID(); var pid = createPID();
var handle = new ProcessHandle(config, pid); var handle = new ProcessHandle(config, pid);

View File

@@ -1,6 +1,6 @@
package kernel.service; package kernel.service;
import kernel.ps.Process; import kernel.ps.IProcess;
import kernel.ps.ProcessManager; import kernel.ps.ProcessManager;
import kernel.binstore.BinStore; import kernel.binstore.BinStore;
@@ -11,7 +11,7 @@ class Service {
public final name:String; public final name:String;
public final args:Array<String>; public final args:Array<String>;
public var pid:PID; public var pid:PID;
public var ps:Process; public var ps:IProcess;
@:allow(kernel.service.ServiceManager) @:allow(kernel.service.ServiceManager)
private function new(binName:String, name:String, ?args:Array<String>) { private function new(binName:String, name:String, ?args:Array<String>) {

View File

@@ -17,37 +17,46 @@ class ServiceManager {
/** /**
Add a service to be automatically started. Add a service to be automatically started.
**/ **/
public static function enable(name:String) { public static function enable(name:String):Outcome<Noise, String> {
if (!services.exists(name)) { if (!services.exists(name)) {
return; // Service must be started return Failure("Service must be started before enable");
} }
var store = KVStore.getStoreForClass(); var store = KVStore.getStoreForClass();
store.load();
var enabled = store.get("enabled", []); var enabled = store.get("enabled", []);
enabled.push(name); enabled.push(name);
store.set("enabled", enabled); store.set("enabled", enabled);
store.save(); store.save();
return Success(Noise);
} }
/** /**
Remove a service from being automatically started. Remove a service from being automatically started.
**/ **/
private static function disable(name:String) { private static function disable(name:String):Outcome<Noise, String> {
var store = KVStore.getStoreForClass(); var store = KVStore.getStoreForClass();
store.load();
var enabled:Array<String> = store.get("enabled"); var enabled:Array<String> = store.get("enabled");
var index = enabled.indexOf(name); var index = enabled.indexOf(name);
if (index == -1) { if (index == -1) {
return; return Failure("Service not found");
} }
enabled.splice(index, 1); enabled.splice(index, 1);
store.save(); store.save();
return Success(Noise);
} }
private static function startAllEnabled() { private static function startAllEnabled() {
var store = KVStore.getStoreForClass(); var store = KVStore.getStoreForClass();
store.load();
var enabled:Array<String> = store.get("enabled", []); var enabled:Array<String> = store.get("enabled", []);
for (name in enabled) { for (name in enabled) {
start(name); start(name);

View File

@@ -72,6 +72,11 @@ class Turtle {
return r2; return r2;
} }
/**
Dig in the provided direction.
Keep in mind that digging on a empty space is considerd a failure.
Also see `digEmpty`.
**/
public static function dig(dir:InteractDirections, ?toolSide:ToolSide):Outcome<Noise, String> { public static function dig(dir:InteractDirections, ?toolSide:ToolSide):Outcome<Noise, String> {
var r:cc.Turtle.TurtleActionResult; var r:cc.Turtle.TurtleActionResult;
@@ -87,6 +92,36 @@ class Turtle {
return conterToOutcome(r); return conterToOutcome(r);
} }
/**
Dig in the provided direction.
Does not fail if there is nothing to dig.
Also see `dig`.
**/
public static function digEmpty(dir:InteractDirections, ?toolSide:ToolSide):Outcome<Noise, String> {
var r:cc.Turtle.TurtleActionResult;
// FIXME: upstream needs to be fixed to accept ToolSide
switch dir {
case Front:
r = cc.Turtle.dig();
case Up:
r = cc.Turtle.digUp();
case Down:
r = cc.Turtle.digDown();
}
var result = conterToOutcome(r);
switch (result) {
case Success(_):
return result;
case Failure(failure):
if (failure == "Nothing to dig here") {
return Success(null);
}
return result;
}
}
public static function place(dir:InteractDirections):Outcome<Noise, String> { public static function place(dir:InteractDirections):Outcome<Noise, String> {
var r:cc.Turtle.TurtleActionResult; var r:cc.Turtle.TurtleActionResult;
switch dir { switch dir {
@@ -119,23 +154,17 @@ class Turtle {
} }
public static function selectSlot(slot:TurtleSlot):Outcome<Noise, Noise> { public static function selectSlot(slot:TurtleSlot):Outcome<Noise, Noise> {
// TODO: slot in bounds? var r = cc.Turtle.select(slot.toCCSlot());
var r = cc.Turtle.select(slot + 1);
return (r) ? Outcome.Success(null) : Outcome.Failure("Slot out of bounds"); return (r) ? Outcome.Success(null) : Outcome.Failure("Slot out of bounds");
} }
public static function getItemCount(?slot:TurtleSlot):Int { public static function getItemCount(?slot:TurtleSlot):Int {
// TODO: slot in bounds? return cc.Turtle.getItemCount(slot.toCCSlot());
return cc.Turtle.getItemCount(slot + 1);
} }
public static function getItemSpace(?slot:TurtleSlot):Int { public static function getItemSpace(?slot:TurtleSlot):Int {
// TODO: slot in bounds? return cc.Turtle.getItemSpace(slot.toCCSlot());
return cc.Turtle.getItemSpace(slot + 1);
} }
public static function detect(dir:InteractDirections):Bool { public static function detect(dir:InteractDirections):Bool {
@@ -208,12 +237,12 @@ class Turtle {
} }
public static function compareSlot(otherSlot:TurtleSlot):Bool { public static function compareSlot(otherSlot:TurtleSlot):Bool {
return cc.Turtle.compareTo(otherSlot + 1); return cc.Turtle.compareTo(otherSlot.toCCSlot());
} }
public static function transfer(from:TurtleSlot, to:TurtleSlot, ?count:Int):Outcome<Noise, Noise> { public static function transfer(from:TurtleSlot, to:TurtleSlot, ?count:Int):Outcome<Noise, Noise> {
selectSlot(from); selectSlot(from);
var r = cc.Turtle.transferTo(to + 1, count); var r = cc.Turtle.transferTo(to.toCCSlot(), count);
return r ? Outcome.Success(null) : Outcome.Failure(null); return r ? Outcome.Success(null) : Outcome.Failure(null);
} }
@@ -258,7 +287,7 @@ class Turtle {
} }
public static function getItemDetail(slot:TurtleSlot, ?detailed:Bool = false):Option<ItemInspect> { public static function getItemDetail(slot:TurtleSlot, ?detailed:Bool = false):Option<ItemInspect> {
var r = cc.Turtle.getItemDetail(slot + 1); // FIXME: can take detailed as flag. Has to be fixed upstream var r = cc.Turtle.getItemDetail(slot.toCCSlot()); // FIXME: can take detailed as flag. Has to be fixed upstream
if (r == null) { if (r == null) {
return None; return None;

View File

@@ -0,0 +1,26 @@
package kernel.turtle;
using tink.CoreApi;
/**
The slot nummber for turtle inventory. 0 based. Assured to be in range.
**/
abstract TurtleSlot(Int) to Int {
public function new(i:Int) {
if (i >= 0 && i < Turtle.MAX_SLOTS) {
this = i;
} else {
throw new Error("Slot not in range: " + i);
}
}
@:from
public static function fromInt(i:Int) {
return new TurtleSlot(i);
}
@:allow(kernel.turtle)
private inline function toCCSlot():Int {
return this + 1;
}
}

View File

@@ -22,8 +22,3 @@ typedef ItemInspect = {
public var damage:Int; public var damage:Int;
public var count:Int; public var count:Int;
} }
/**
The slot nummber for turtle inventory. 0 based.
**/
typedef TurtleSlot = Int;

View File

@@ -1,9 +1,9 @@
package kernel.ui; package kernel.ui;
import lib.Pos; import lib.ScreenPos;
import lib.Vec.Vec2; import lib.Vec.Vec2;
import lib.Color; import lib.Color;
import kernel.ui.TermWriteable; import kernel.ui.ITermWriteable;
using tink.CoreApi; using tink.CoreApi;
@@ -11,15 +11,15 @@ using tink.CoreApi;
A term writer that can switch beetween its internal buffer and another termwriter. A term writer that can switch beetween its internal buffer and another termwriter.
The other target is most of the time a real screen The other target is most of the time a real screen
**/ **/
class BufferedVirtualTermWriter implements VirtualTermWriter extends TermBuffer { class BufferedVirtualTermWriter implements IVirtualTermWriter extends TermBuffer {
private static final defaultSize:Vec2<Int> = {x: 50, y: 50}; private static final defaultSize:Vec2<Int> = {x: 50, y: 50};
private var target:TermWriteable; private var target:ITermWriteable;
private var enabled:Bool = false; private var enabled:Bool = false;
private var onResizeLink:CallbackLink; private var onResizeLink:CallbackLink;
@:allow(kernel.ui) @:allow(kernel.ui)
private function new(?target:TermWriteable) { private function new(?target:ITermWriteable) {
setTarget(target); setTarget(target);
if (enabled) { if (enabled) {
@@ -44,7 +44,7 @@ class BufferedVirtualTermWriter implements VirtualTermWriter extends TermBuffer
return enabled; return enabled;
} }
public function setTarget(newTarget:TermWriteable) { public function setTarget(newTarget:ITermWriteable) {
if (newTarget != null) { if (newTarget != null) {
super.setSize(newTarget.getSize()); super.setSize(newTarget.getSize());
@@ -86,7 +86,7 @@ class BufferedVirtualTermWriter implements VirtualTermWriter extends TermBuffer
super.scroll(y); super.scroll(y);
} }
public override function getCursorPos():Pos { public override function getCursorPos():ScreenPos {
if (isEnabled()) { if (isEnabled()) {
return target.getCursorPos(); return target.getCursorPos();
} else { } else {

View File

@@ -1,6 +1,6 @@
package kernel.ui; package kernel.ui;
import lib.Pos; import lib.ScreenPos;
import lib.Color; import lib.Color;
import lib.Vec.Vec2; import lib.Vec.Vec2;
@@ -9,7 +9,7 @@ using tink.CoreApi;
/** /**
Interface describing a terminal. E.g. the main computer screen or a external screen. Interface describing a terminal. E.g. the main computer screen or a external screen.
**/ **/
interface TermWriteable { interface ITermWriteable {
public var onResize(default, null):Signal<Vec2<Int>>; public var onResize(default, null):Signal<Vec2<Int>>;
public function write(text:String):Void; public function write(text:String):Void;
@@ -18,7 +18,7 @@ interface TermWriteable {
/** /**
Even though CC is 1 based we use a 0 based index. Even though CC is 1 based we use a 0 based index.
**/ **/
public function getCursorPos():Pos; public function getCursorPos():ScreenPos;
/** /**
Even though CC is 1 based we use a 0 based index. Even though CC is 1 based we use a 0 based index.

View File

@@ -4,9 +4,9 @@ package kernel.ui;
A VirtualTermWriter is a TermWriteable that can be enabled or disabled. A VirtualTermWriter is a TermWriteable that can be enabled or disabled.
When disabled, it will not write to its target. When enabled, it will. When disabled, it will not write to its target. When enabled, it will.
**/ **/
interface VirtualTermWriter extends TermWriteable { interface IVirtualTermWriter extends ITermWriteable {
public function enable():Void; public function enable():Void;
public function disable():Void; public function disable():Void;
public function isEnabled():Bool; public function isEnabled():Bool;
public function setTarget(newTarget:TermWriteable):Void; public function setTarget(newTarget:ITermWriteable):Void;
} }

View File

@@ -1,7 +1,7 @@
package kernel.ui; package kernel.ui;
import kernel.log.Log; import kernel.log.Log;
import lib.Pos; import lib.ScreenPos;
import lib.Vec.Vec2; import lib.Vec.Vec2;
import lib.Color; import lib.Color;
@@ -13,18 +13,18 @@ using tink.CoreApi;
The render function is only called when needed. The render function is only called when needed.
You can also request a re-render by calling `requestRender`. You can also request a re-render by calling `requestRender`.
**/ **/
class StatelessVirtualTermWriter implements VirtualTermWriter { class StatelessVirtualTermWriter implements IVirtualTermWriter {
public var onResize(default, null):Signal<Vec2<Int>>; public var onResize(default, null):Signal<Vec2<Int>>;
private var onResizeTrigger:SignalTrigger<Vec2<Int>> = Signal.trigger(); private var onResizeTrigger:SignalTrigger<Vec2<Int>> = Signal.trigger();
private var target:TermWriteable; private var target:ITermWriteable;
private var enabled:Bool = false; private var enabled:Bool = false;
private var renderFunc:Null<Void->Void> = null; private var renderFunc:Null<Void->Void> = null;
private var renderRequested:Bool = false; private var renderRequested:Bool = false;
private var onResizeLink:CallbackLink; private var onResizeLink:CallbackLink;
@:allow(kernel.ui) @:allow(kernel.ui)
private function new(?target:TermWriteable) { private function new(?target:ITermWriteable) {
onResize = onResizeTrigger.asSignal(); onResize = onResizeTrigger.asSignal();
setTarget(target); setTarget(target);
} }
@@ -70,7 +70,7 @@ class StatelessVirtualTermWriter implements VirtualTermWriter {
enabled = false; enabled = false;
} }
public function setTarget(newTarget:TermWriteable) { public function setTarget(newTarget:ITermWriteable) {
if (newTarget == null) { if (newTarget == null) {
return; return;
} }
@@ -110,8 +110,8 @@ class StatelessVirtualTermWriter implements VirtualTermWriter {
target.scroll(y); target.scroll(y);
} }
public inline function getCursorPos():Pos { public inline function getCursorPos():ScreenPos {
return enabled ? target.getCursorPos() : new Pos({x: 0, y: 0}); return enabled ? target.getCursorPos() : new ScreenPos({x: 0, y: 0});
} }
public inline function setCursorPos(x:Int, y:Int) { public inline function setCursorPos(x:Int, y:Int) {

View File

@@ -1,9 +1,9 @@
package kernel.ui; package kernel.ui;
import lib.Pos; import lib.ScreenPos;
import lib.Vec.Vec2; import lib.Vec.Vec2;
import lib.Color; import lib.Color;
import kernel.ui.TermWriteable; import kernel.ui.ITermWriteable;
using tink.CoreApi; using tink.CoreApi;
@@ -12,13 +12,13 @@ using tink.CoreApi;
even if its not displayed right now. When the GUI gets displayed again even if its not displayed right now. When the GUI gets displayed again
this buffer will be written to the screen. this buffer will be written to the screen.
**/ **/
class TermBuffer implements TermWriteable { class TermBuffer implements ITermWriteable {
/** /**
format [y][x]. First index is the line. Second index the char in the line. format [y][x]. First index is the line. Second index the char in the line.
**/ **/
private var screenBuffer:Array<Array<Pixel>>; private var screenBuffer:Array<Array<Pixel>>;
private var cursorPos:Pos = {x: 0, y: 0}; private var cursorPos:ScreenPos = {x: 0, y: 0};
private var currentTextColor:Color = White; private var currentTextColor:Color = White;
private var currentBgColor:Color = Black; private var currentBgColor:Color = Black;
private var cursorBlink:Bool = false; private var cursorBlink:Bool = false;
@@ -60,7 +60,7 @@ class TermBuffer implements TermWriteable {
} }
} }
private function copyBufferToTarget(target:TermWriteable) { private function copyBufferToTarget(target:ITermWriteable) {
target.setCursorPos(0, 0); target.setCursorPos(0, 0);
target.setBackgroundColor(Black); target.setBackgroundColor(Black);
target.setTextColor(White); target.setTextColor(White);
@@ -91,7 +91,7 @@ class TermBuffer implements TermWriteable {
target.setCursorBlink(cursorBlink); target.setCursorBlink(cursorBlink);
} }
private function safeWriteScreenBuffer(pos:Pos, char:String) { private function safeWriteScreenBuffer(pos:ScreenPos, char:String) {
if (screenBuffer.length > pos.y && screenBuffer[pos.y].length > pos.x) { if (screenBuffer.length > pos.y && screenBuffer[pos.y].length > pos.x) {
screenBuffer[pos.y][pos.x].char = char; screenBuffer[pos.y][pos.x].char = char;
screenBuffer[pos.y][pos.x].bg = currentBgColor; screenBuffer[pos.y][pos.x].bg = currentBgColor;
@@ -121,7 +121,7 @@ class TermBuffer implements TermWriteable {
]); ]);
} }
public function getCursorPos():Pos { public function getCursorPos():ScreenPos {
return cursorPos; return cursorPos;
} }

View File

@@ -1,42 +1,42 @@
package kernel.ui; package kernel.ui;
import lib.ui.rendere.UIEventDelegate; import lib.ui.rendere.IUIEventDelegate;
import lib.Pos; import lib.ScreenPos;
import lib.Color; import lib.Color;
import kernel.ButtonType; import kernel.ButtonType;
import lib.Vec.Vec2; import lib.Vec.Vec2;
import kernel.ui.TermWriteable; import kernel.ui.ITermWriteable;
using tink.CoreApi; using tink.CoreApi;
/** /**
The main object you interact with when writing anything to the screen. The main object you interact with when writing anything to the screen.
**/ **/
class WindowContext implements TermWriteable { class WindowContext implements ITermWriteable {
private final writer:VirtualTermWriter; private final writer:IVirtualTermWriter;
@:allow(kernel.ui.WindowManager) private var eventDelegate:Null<UIEventDelegate>; @:allow(kernel.ui.WindowManager) private var eventDelegate:Null<IUIEventDelegate>;
public var onClick(default, null):Signal<{button:ButtonType, pos:Pos}>; public var onClick(default, null):Signal<{button:ButtonType, pos:ScreenPos}>;
public var onKey(default, null):Signal<{keyCode:Int, isHeld:Bool}>; public var onKey(default, null):Signal<{keyCode:Int, isHeld:Bool}>;
public var onKeyUp(default, null):Signal<Int>; public var onKeyUp(default, null):Signal<Int>;
public var onMouseDrag(default, null):Signal<{button:ButtonType, pos:Pos}>; public var onMouseDrag(default, null):Signal<{button:ButtonType, pos:ScreenPos}>;
public var onMouseScroll(default, null):Signal<{dir:Int, pos:Pos}>; public var onMouseScroll(default, null):Signal<{dir:Int, pos:ScreenPos}>;
public var onMouseUp(default, null):Signal<{button:ButtonType, pos:Pos}>; public var onMouseUp(default, null):Signal<{button:ButtonType, pos:ScreenPos}>;
public var onPaste(default, null):Signal<String>; public var onPaste(default, null):Signal<String>;
public var onChar(default, null):Signal<String>; public var onChar(default, null):Signal<String>;
@:allow(kernel.ui.WindowManager) private final onClickTrigger:SignalTrigger<{button:ButtonType, pos:Pos}>; @:allow(kernel.ui.WindowManager) private final onClickTrigger:SignalTrigger<{button:ButtonType, pos:ScreenPos}>;
@:allow(kernel.ui.WindowManager) private final onKeyTrigger:SignalTrigger<{keyCode:Int, isHeld:Bool}>; @:allow(kernel.ui.WindowManager) private final onKeyTrigger:SignalTrigger<{keyCode:Int, isHeld:Bool}>;
@:allow(kernel.ui.WindowManager) private final onKeyUpTrigger:SignalTrigger<Int>; @:allow(kernel.ui.WindowManager) private final onKeyUpTrigger:SignalTrigger<Int>;
@:allow(kernel.ui.WindowManager) private final onMouseDragTrigger:SignalTrigger<{button:ButtonType, pos:Pos}>; @:allow(kernel.ui.WindowManager) private final onMouseDragTrigger:SignalTrigger<{button:ButtonType, pos:ScreenPos}>;
@:allow(kernel.ui.WindowManager) private final onMouseScrollTrigger:SignalTrigger<{dir:Int, pos:Pos}>; @:allow(kernel.ui.WindowManager) private final onMouseScrollTrigger:SignalTrigger<{dir:Int, pos:ScreenPos}>;
@:allow(kernel.ui.WindowManager) private final onMouseUpTrigger:SignalTrigger<{button:ButtonType, pos:Pos}>; @:allow(kernel.ui.WindowManager) private final onMouseUpTrigger:SignalTrigger<{button:ButtonType, pos:ScreenPos}>;
@:allow(kernel.ui.WindowManager) private final onPasteTrigger:SignalTrigger<String>; @:allow(kernel.ui.WindowManager) private final onPasteTrigger:SignalTrigger<String>;
@:allow(kernel.ui.WindowManager) private final onCharTrigger:SignalTrigger<String>; @:allow(kernel.ui.WindowManager) private final onCharTrigger:SignalTrigger<String>;
@:allow(kernel.ui.WindowManager) @:allow(kernel.ui.WindowManager)
private function new(writer:VirtualTermWriter) { private function new(writer:IVirtualTermWriter) {
this.writer = writer; this.writer = writer;
this.onResize = writer.onResize; this.onResize = writer.onResize;
@@ -62,7 +62,7 @@ class WindowContext implements TermWriteable {
public var onResize(default, null):Signal<Vec2<Int>>; public var onResize(default, null):Signal<Vec2<Int>>;
@:allow(kernel.ui) @:allow(kernel.ui)
private inline function setTarget(target:TermWriteable) { private inline function setTarget(target:ITermWriteable) {
writer.setTarget(target); writer.setTarget(target);
} }
@@ -89,7 +89,7 @@ class WindowContext implements TermWriteable {
writer.scroll(y); writer.scroll(y);
} }
public inline function getCursorPos():Pos { public inline function getCursorPos():ScreenPos {
return writer.getCursorPos(); return writer.getCursorPos();
} }
@@ -145,7 +145,7 @@ class WindowContext implements TermWriteable {
Delegate events to an UIEventDelegate. Delegate events to an UIEventDelegate.
Set to null to stop delegating events. Set to null to stop delegating events.
**/ **/
public inline function delegateEvents(delegate:Null<UIEventDelegate>) { public inline function delegateEvents(delegate:Null<IUIEventDelegate>) {
this.eventDelegate = delegate; this.eventDelegate = delegate;
} }
} }

View File

@@ -3,7 +3,7 @@ package kernel.ui;
import kernel.ps.ProcessManager; import kernel.ps.ProcessManager;
import kernel.ps.ProcessManager.PID; import kernel.ps.ProcessManager.PID;
import haxe.ds.ReadOnlyArray; import haxe.ds.ReadOnlyArray;
import kernel.ui.TermWriteable; import kernel.ui.ITermWriteable;
import kernel.peripherals.Peripherals.Peripheral; import kernel.peripherals.Peripherals.Peripheral;
/** /**
@@ -143,7 +143,7 @@ class WindowManager {
Move context to output. If output is "main", context will be moved to main screen. Move context to output. If output is "main", context will be moved to main screen.
**/ **/
public static function focusContextToOutput(context:WindowContext, output:String) { public static function focusContextToOutput(context:WindowContext, output:String) {
var target:TermWriteable; var target:ITermWriteable;
if (output == "main") { if (output == "main") {
target = MainTerm.instance; target = MainTerm.instance;
currentMainContext = context; currentMainContext = context;

12
src/lib/Block.hx Normal file
View File

@@ -0,0 +1,12 @@
package lib;
@:structInit
class Block {
public final name:PaN;
public final tags:Tags;
public function new(name:String, tags:Tags) {
this.name = name;
this.tags = tags;
}
}

114
src/lib/BlockBlockMap.hx Normal file
View File

@@ -0,0 +1,114 @@
package lib;
import kernel.peripherals.GeoScanner.ScanBlock;
import lib.BlockMap;
@:forward
abstract BlockBlockMap(BlockMap<Block>) {
public inline function new() {
this = new BlockMap<Block>();
}
public static function fromScan(scan:Array<ScanBlock>):BlockBlockMap {
var map:BlockBlockMap = new BlockBlockMap();
for (block in scan) {
map.set(block.pos, new Block(block.name, block.tags));
}
return map;
}
/**
Navigate from one point into another.
Returns the path to take.
Based on A*
**/
public function nav(from:BlockPos, to:BlockPos):Array<BlockPos> {
if (!canPass(to)) {
trace("End is blocked");
return [];
}
var openSet = new PriorityQueue();
openSet.insert(from, 0);
var cameFrom:BlockMap<BlockPos> = new BlockMap();
var gCost:BlockMap<Int> = new BlockMap(); // G cost is the distance from start
gCost.set(from, 0);
var fCost:BlockMap<Float> = new BlockMap(); // F cost is the combines cost of moving to this pos (G cost) + the distnace to the end
fCost.set(from, from.distance(to));
while (!openSet.isEmpty()) {
var current = openSet.extractMin(); // Check the pos with the lowest F cost
if (current.equals(to)) {
var totalPath = [];
var currentPathPos = to;
while (cameFrom.exists(currentPathPos)) {
totalPath.unshift(currentPathPos);
currentPathPos = cameFrom.get(currentPathPos);
}
totalPath.unshift(from);
return totalPath;
}
for (neighbor in current.neighbors()) {
if (!canPass(neighbor)) {
continue;
}
var newGCost = gCost.get(current) + 1; // Movment cost to neighbor is always 1 as we can't move diagonally
if (newGCost < (gCost.get(neighbor) ?? 9999)) {
cameFrom.set(neighbor, current);
gCost.set(neighbor, newGCost);
fCost.set(neighbor, newGCost + neighbor.distance(to));
if (!openSet.containsElement(neighbor)) {
openSet.insert(neighbor, fCost.get(neighbor));
}
}
}
}
return [];
}
/**
Some block can be passed by the turtle.
Some blocks get destroyed by it.
**/
public function canPass(k:BlockPos):Bool {
var block = this.get(k);
if (block == null) {
return true;
}
switch block.name {
case "minecraft:water":
return true;
case "minecaft:lava":
case "mimecraft:grass":
case "minecraft:hanging_roots":
case "minecraft:warped_roots":
case "minecraft:dead_bush":
case "minecraft:fern":
case "minecraft:vine":
case "minecraft:glow_lichen":
case "minecraft:seagrass":
case "minecraft:crimson_roots":
case "minecraft:nether_sprouts":
case "minecraft:snow":
case "minecraft:rose_bush":
case "minecraft:fire":
return true;
}
return false;
}
}

48
src/lib/BlockMap.hx Normal file
View File

@@ -0,0 +1,48 @@
package lib;
import haxe.ds.IntMap;
/**
Map values to positions in 3D space. For representing blocks from a scan see `BlockBlockMap`.
The only reason this exsists is that the abstract class for BlockPos does not
satisfy the constraint of HashMap. The hashCode function needs to be on a real class and not on
an abstract class. Thanks Obama.
**/
@:forward(iterator, clear)
abstract BlockMap<T>(IntMap<T>) {
/**
Creates a new HashMap.
**/
public inline function new() {
this = new IntMap();
}
/**
See `Map.set`
**/
@:arrayAccess public inline function set(k:BlockPos, v:T) {
this.set(k.hashCode(), v);
}
/**
See `Map.get`
**/
@:arrayAccess public inline function get(k:BlockPos) {
return this.get(k.hashCode());
}
/**
See `Map.exists`
**/
public inline function exists(k:BlockPos) {
return this.exists(k.hashCode());
}
/**
See `Map.remove`
**/
public inline function remove(k:BlockPos) {
return this.remove(k.hashCode());
}
}

78
src/lib/BlockPos.hx Normal file
View File

@@ -0,0 +1,78 @@
package lib;
import lib.Vec.Vec2;
import lib.Vec.Vec3;
/**
Represents a Point in a 3D `Int` System.
Basicly a wrapper for Vec3<Int> with some extra functions.
`Y` represents the height of the point.
**/
@:forward
abstract BlockPos(Vec3<Int>) from Vec3<Int> to Vec3<Int> {
public inline function new(x:Int, y:Int, z:Int) {
this = new Vec3(x, y, z);
}
@:op(A + B)
public inline function add(rhs:BlockPos):BlockPos {
return this.add(rhs);
}
@:op(A - B)
public inline function sub(rhs:BlockPos):BlockPos {
return this.sub(rhs);
}
@:op(A * B)
public inline function multiplyScalar(rhs:Int):BlockPos {
return this.multiplyScalar(rhs);
}
@:op(-A)
public inline function negate():BlockPos {
return this.negate();
}
@:op(A == B)
public function equals(rhs:BlockPos):Bool {
return this.x == rhs.x && this.y == rhs.y && this.z == rhs.z;
}
@:op(A != B)
public function notEquals(rhs:BlockPos):Bool {
return !equals(rhs);
}
public function hashCode():Int {
return (this.x * 73856093) ^ (this.y * 19349663) ^ (this.z * 83492791);
}
public function toString():String {
return 'BlockPos(${this.x}, ${this.y}, ${this.z})';
}
/**
Returns the chunk the position is in.
**/
public function chunk():Vec2<Int> {
var x = Math.floor(this.x / 16);
var z = Math.floor(this.z / 16);
return new Vec2(x, z);
}
/**
Returns a list of positions neighboring the block. No Diagonal.
**/
public function neighbors():Array<BlockPos> {
return [
new BlockPos(this.x + 1, this.y + 0, this.z + 0), // Front
new BlockPos(this.x - 1, this.y + 0, this.z + 0), // Back
new BlockPos(this.x + 0, this.y + 0, this.z - 1), // Left
new BlockPos(this.x + 0, this.y + 0, this.z + 1), // Right
new BlockPos(this.x + 0, this.y - 1, this.z + 0), // Bot
new BlockPos(this.x + 0, this.y + 1, this.z + 0), // Top
];
}
}

View File

@@ -1,16 +1,18 @@
package lib; package lib;
import kernel.ps.Process; import lib.args.CLIArgs;
import lib.args.ArgType;
import kernel.ps.IProcess;
import kernel.ps.ProcessHandle; import kernel.ps.ProcessHandle;
using tink.CoreApi; using tink.CoreApi;
abstract class CLIAppBase implements Process { abstract class CLIAppBase implements IProcess {
private var handle:ProcessHandle; private var handle:ProcessHandle;
private final _subcommandsSync:Map<String, (Array<String>) -> Bool> = []; private final _subcommandsSync:Map<String, (CLIArgs) -> Bool> = [];
private final _subcommandsAsync:Map<String, (Array<String>) -> Future<Bool>> = []; private final _subcommandsAsync:Map<String, (CLIArgs) -> Future<Bool>> = [];
private final _subcommandsSynopsis:Array<String> = []; private final _subcommandsArgs:Map<String, Array<ArgType>> = [];
public final function run(handle:ProcessHandle) { public final function run(handle:ProcessHandle) {
this.handle = handle; this.handle = handle;
@@ -26,10 +28,19 @@ abstract class CLIAppBase implements Process {
var args = handle.args.slice(1); var args = handle.args.slice(1);
if (_subcommandsSync.exists(subcommand)) { if (_subcommandsSync.exists(subcommand)) {
var result = _subcommandsSync[subcommand](args); var argParser = new CLIArgs(_subcommandsArgs.get(subcommand));
return handle.close(result); if (!argParser.parse(args)) {
handle.writeLine(argParser.getError());
return handle.close(false);
}
return handle.close(_subcommandsSync[subcommand](argParser));
} else if (_subcommandsAsync.exists(subcommand)) { } else if (_subcommandsAsync.exists(subcommand)) {
_subcommandsAsync[subcommand](args).handle(handle.close); var argParser = new CLIArgs(_subcommandsArgs.get(subcommand));
if (!argParser.parse(args)) {
handle.writeLine(argParser.getError());
return handle.close(false);
}
_subcommandsAsync[subcommand](argParser).handle(handle.close);
} else { } else {
handle.writeLine("Unknown subcommand: " + subcommand); handle.writeLine("Unknown subcommand: " + subcommand);
printHelp(); printHelp();
@@ -37,21 +48,21 @@ abstract class CLIAppBase implements Process {
} }
} }
private function registerSyncSubcommand(command:String, callback:(Array<String>) -> Bool, synopsis:String = null) { private function registerSyncSubcommand(command:String, callback:(CLIArgs) -> Bool, args:Array<ArgType> = null) {
_subcommandsSync.set(command, callback); _subcommandsSync.set(command, callback);
_subcommandsSynopsis.push(command + " " + (synopsis ?? "")); _subcommandsArgs.set(command, args ?? []);
} }
private function registerAsyncSubcommand(command:String, callback:(Array<String>) -> Future<Bool>, synopsis:String = null) { private function registerAsyncSubcommand(command:String, callback:(CLIArgs) -> Future<Bool>, args:Array<ArgType> = null) {
_subcommandsAsync.set(command, callback); _subcommandsAsync.set(command, callback);
_subcommandsSynopsis.push(command + " " + (synopsis ?? "")); _subcommandsArgs.set(command, args ?? []);
} }
private function printHelp() { private function printHelp() {
handle.writeLine("Usage: <subcommand> [args]"); handle.writeLine("Usage: <subcommand> [args]");
handle.writeLine("Subcommands:"); handle.writeLine("Subcommands:");
for (subcommand in _subcommandsSynopsis) { for (k => v in this._subcommandsArgs) {
handle.writeLine(" " + subcommand); handle.writeLine(' $k ${CLIArgs.getSynopsis(v)}');
} }
} }
} }

View File

@@ -1,7 +1,7 @@
package lib; package lib;
import kernel.http.HTTPRequest.Http;
import lua.TableTools; import lua.TableTools;
import kernel.KernelEvents;
import kernel.log.Log; import kernel.log.Log;
import lua.NativeStringTools; import lua.NativeStringTools;
import lib.ui.Canvas; import lib.ui.Canvas;
@@ -9,6 +9,7 @@ import cc.ComputerCraft;
#if webconsole #if webconsole
import cc.HTTP; import cc.HTTP;
import kernel.net.Net; import kernel.net.Net;
import kernel.log.LogLine;
#end #end
class Debug { class Debug {
@@ -66,8 +67,8 @@ class Debug {
#end #end
#if webconsole #if webconsole
public static function printWeb(msg:String) { public static function logToWebconsole(line:LogLine) {
HTTP.request("http://127.0.0.1:8080/" + Net.networkID, msg); Http.request('http://127.0.0.1:8080/log/${Net.networkID}/${line.level.getIndex()}', '[${line.origin}] ${line.message}').eager();
} }
#end #end
} }

View File

@@ -1,69 +0,0 @@
package lib;
import haxe.Exception;
class LambdaExtender {
/**
Returns the first element if there are exectly one element present.
Throws exception if not.
**/
static public function single<T>(it:Iterable<T>):T {
var elem:T = null;
for (t in it) {
if (elem != null) {
throw new Exception("Multiple elements found");
}
elem = t;
}
if (elem == null) {
throw new Exception("No element found");
}
return elem;
}
/**
Like `single` but when no element was found return the default value.
**/
static public function singleOrDefault<T>(it:Iterable<T>, defaultValue:T):T {
var elem:T = null;
for (t in it) {
if (elem != null) {
throw new Exception("Multiple elements found");
}
elem = t;
}
if (elem == null) {
return defaultValue;
}
return elem;
}
/**
Returns the first element.
Throws execption if no first element found.
**/
static public function first<T>(it:Iterable<T>):T {
for (t in it) {
return t;
}
throw new Exception("No element found");
}
/**
Like `first` only if no first element was found it returns the defalt value.
**/
static public function firstOrDefault<T>(it:Iterable<T>, defaultValue:T):T {
var iter = it.iterator();
if (iter.hasNext()) {
return iter.next();
}
return defaultValue;
}
}

View File

@@ -4,10 +4,10 @@ import kernel.log.Log;
import kernel.binstore.BinStore; import kernel.binstore.BinStore;
import kernel.peripherals.Screen; import kernel.peripherals.Screen;
import kernel.peripherals.Peripherals.Peripheral; import kernel.peripherals.Peripherals.Peripheral;
import kernel.ps.Process; import kernel.ps.IProcess;
import kernel.ps.ProcessManager; import kernel.ps.ProcessManager;
import bin.KernelLog; import bin.KernelLog;
import lib.ui.elements.UIElement; import lib.ui.elements.IUIElement;
import lib.ui.elements.TextElement; import lib.ui.elements.TextElement;
import lib.ui.elements.RootElement; import lib.ui.elements.RootElement;
import kernel.KernelEvents; import kernel.KernelEvents;
@@ -148,7 +148,7 @@ class HomeContext {
var workspaceIDs:Array<Int> = [for (k => v in workspaces) k]; var workspaceIDs:Array<Int> = [for (k => v in workspaces) k];
workspaceIDs.sort((a, b) -> a - b); workspaceIDs.sort((a, b) -> a - b);
var children:Array<UIElement> = [ var children:Array<IUIElement> = [
for (i in workspaceIDs) for (i in workspaceIDs)
new TextElement('Switch to ${i + 1}', {uiEvents: {onClick: this.handleSelectContext.bind(i)}}) new TextElement('Switch to ${i + 1}', {uiEvents: {onClick: this.handleSelectContext.bind(i)}})
]; ];

View File

@@ -3,7 +3,8 @@ package lib;
/** /**
Represents an item in the game. Represents an item in the game.
**/ **/
abstract Item(String) to String { @:forward
enum abstract Item(PaN) to String {
public inline function new(name:String) { public inline function new(name:String) {
// Check if the name is valid. in the format `mod:item_name` e.g. `minecraft:apple` // Check if the name is valid. in the format `mod:item_name` e.g. `minecraft:apple`
// TODO: implement // TODO: implement
@@ -16,7 +17,6 @@ abstract Item(String) to String {
return new Item(s); return new Item(s);
} }
function getBase():String { var Coal = "minecraft:coal";
return this.split(":")[0]; var Charcoal = "minecraft:charcoal";
}
} }

View File

@@ -1,12 +1,17 @@
package lib; package lib;
import haxe.Serializer;
import haxe.Unserializer;
import kernel.fs.FS; import kernel.fs.FS;
import haxe.ds.StringMap; import haxe.ds.StringMap;
#if kv_use_native
import cc.TextUtils;
#else
import haxe.Unserializer;
import haxe.Serializer;
#end
/** /**
Key value store with persistence. Key value store with persistence.
Use flag kv_use_native to use the cc serialization.
**/ **/
class KVStore { class KVStore {
private var kvStore:StringMap<Dynamic> = new StringMap(); private var kvStore:StringMap<Dynamic> = new StringMap();
@@ -43,14 +48,21 @@ class KVStore {
public function save() { public function save() {
var handle = FS.openWrite(getNamespaceFile(this.namespace)); var handle = FS.openWrite(getNamespaceFile(this.namespace));
#if kv_use_native
handle.write(TextUtils.serialize(this.kvStore));
#else
handle.write(Serializer.run(this.kvStore)); handle.write(Serializer.run(this.kvStore));
#end
handle.close(); handle.close();
} }
private function parseFile(content:String) { private function parseFile(content:String) {
#if kv_use_native
this.kvStore = TextUtils.unserialize(content);
#else
var unserializer = new Unserializer(content); var unserializer = new Unserializer(content);
this.kvStore = unserializer.unserialize(); this.kvStore = unserializer.unserialize();
#end
} }
public inline function set(key:String, value:Dynamic) { public inline function set(key:String, value:Dynamic) {

View File

@@ -1,24 +0,0 @@
package lib;
class ObjMerge {
public static function merge<T>(obj1:T, obj2:T):T {
if (obj1 == null) {
return obj2;
}
if (obj2 == null) {
return obj1;
}
var rtn:T = Reflect.copy(obj1);
var fields = Reflect.fields(obj2);
for (field in fields) {
if (Reflect.getProperty(obj1, field) == null) {
Reflect.setProperty(rtn, field, Reflect.getProperty(obj2, field));
}
}
return (rtn : T);
}
}

18
src/lib/PaN.hx Normal file
View File

@@ -0,0 +1,18 @@
package lib;
/**
Provider and name. e.g. "minecraft:dirt", "mod:item", "minecraft:overworld"
**/
abstract PaN(String) to String from String {
public inline function new(pan:String) {
this = pan;
}
public function getProvider():String {
return this.split(":")[0];
}
public function getName():String {
return this.split(":")[1];
}
}

View File

@@ -1,46 +0,0 @@
package lib;
import lib.Vec.Vec2;
/**
Reporesents a Point in a 2D `Int` System.
Basicly a wrapper for Vec2<Int> with some extra functions.
**/
@:forward(x, y)
abstract Pos(Vec2<Int>) from Vec2<Int> to Vec2<Int> {
inline public function new(i:Vec2<Int>) {
this = i;
}
@:op(A + B)
public function add(rhs:Vec2<Int>):Pos {
return new Pos({
y: this.y + rhs.y,
x: this.x + rhs.x,
});
}
@:op(A - B)
public function sub(rhs:Vec2<Int>):Pos {
return new Pos({
y: this.y - rhs.y,
x: this.x - rhs.x,
});
}
@:op(A * B)
public function multiply(rhs:Vec2<Int>):Pos {
return new Pos({
y: this.y * rhs.y,
x: this.x * rhs.x,
});
}
@:op(-A)
public function negate():Pos {
return new Pos({
y: -this.y,
x: -this.x,
});
}
}

View File

@@ -1,111 +0,0 @@
package lib;
import lib.Vec.Vec3;
/**
Reporesents a Point in a 3D `Float` System.
Basicly a wrapper for Vec3<Float> with some extra functions.
`Y` represents the height of the point.
**/
@:forward(x, y, z)
abstract Pos3(Vec3<Float>) from Vec3<Float> to Vec3<Float> {
inline public function new(i:Vec3<Float>) {
this = i;
}
@:op(A + B)
public function add(rhs:Vec3<Float>):Pos3 {
return new Pos3({
y: this.y + rhs.y,
x: this.x + rhs.x,
z: this.z + rhs.z
});
}
@:op(A - B)
public function sub(rhs:Vec3<Float>):Pos3 {
return new Pos3({
y: this.y - rhs.y,
x: this.x - rhs.x,
z: this.z - rhs.z
});
}
@:op(A * B)
public function multiplyScalar(rhs:Float):Pos3 {
return new Pos3({
y: this.y * rhs,
x: this.x * rhs,
z: this.z * rhs
});
}
@:op(A / B)
public function divideScalar(rhs:Float):Pos3 {
return new Pos3({
y: this.y / rhs,
x: this.x / rhs,
z: this.z / rhs
});
}
@:op(-A)
public function negate():Pos3 {
return new Pos3({
y: -this.y,
x: -this.x,
z: -this.z
});
}
public function dot(rhs:Vec3<Float>):Float {
return this.x * rhs.x + this.y * rhs.y + this.z * rhs.z;
}
public function cross(rhs:Vec3<Float>):Pos3 {
return new Pos3({
x: this.y * rhs.z - this.z * rhs.y,
y: this.z * rhs.x - this.x * rhs.z,
z: this.x * rhs.y - this.y * rhs.x
});
}
public function normalize():Pos3 {
var l = length();
return multiplyScalar(1 / l);
}
public function length():Float {
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
}
@:op(A == B)
public function equals(rhs:Pos3):Bool {
return close(rhs);
}
@:op(A != B)
public function notEquals(rhs:Pos3):Bool {
return !close(rhs);
}
public function close(rhs:Pos3, epsilon:Float = 0.001):Bool {
return Math.abs(this.x - rhs.x) < epsilon && Math.abs(this.y - rhs.y) < epsilon && Math.abs(this.z - rhs.z) < epsilon;
}
public function toString():String {
return 'Pos3(${this.x}, ${this.y}, ${this.z})';
}
public function round():Pos3 {
return new Pos3({
x: Math.fround(this.x),
y: Math.fround(this.y),
z: Math.fround(this.z)
});
}
public function distance(rhs:Pos3):Float {
return Math.sqrt(Math.pow(this.x - rhs.x, 2) + Math.pow(this.y - rhs.y, 2) + Math.pow(this.z - rhs.z, 2));
}
}

69
src/lib/PriorityQueue.hx Normal file
View File

@@ -0,0 +1,69 @@
package lib;
using Lambda;
abstract PriorityQueue<T>(Array<{prio:Float, val:T}>) {
public inline function new() {
this = [];
}
public function containsElement(element:T):Bool {
return this.exists((e) -> e.val == element);
}
public function isEmpty():Bool {
return this.length == 0;
}
public function insert(e:T, prio:Float) {
this.push({prio: prio, val: e});
bubbleUp(this.length - 1);
}
public function extractMin():Null<T> {
if (this.length == 0) {
return null;
}
swap(0, this.length - 1);
var minElement = this.pop();
bubbleDown(0);
return minElement.val;
}
private function bubbleUp(index:Int) {
var parentIndex:Int = Math.floor((index - 1) / 2);
if (index > 0 && this[index].prio < this[parentIndex].prio) {
swap(index, parentIndex);
bubbleUp(parentIndex);
}
}
private function bubbleDown(index:Int) {
var leftIndex = 2 * index + 1;
var rightIndex = 2 * index + 2;
var smallest = index;
if (leftIndex < this.length && this[leftIndex].prio < this[smallest].prio) {
smallest = leftIndex;
}
if (rightIndex < this.length && this[rightIndex].prio < this[smallest].prio) {
smallest = rightIndex;
}
if (smallest != index) {
swap(index, smallest);
bubbleDown(smallest);
}
}
private function swap(i:Int, j:Int) {
var tmp = this[i];
this[i] = this[j];
this[j] = tmp;
}
}

View File

@@ -1,10 +1,12 @@
package lib; package lib;
class Rect { import kernel.peripherals.Screen;
private final tl:Pos;
private final br:Pos;
public function new(p1:Pos, p2:Pos) { class Rect {
public final tl:ScreenPos;
public final br:ScreenPos;
public function new(p1:ScreenPos, p2:ScreenPos) {
this.tl = { this.tl = {
x: MathI.min(p1.x, p2.x), x: MathI.min(p1.x, p2.x),
y: MathI.min(p1.y, p2.y) y: MathI.min(p1.y, p2.y)
@@ -20,11 +22,11 @@ class Rect {
return getWidth() * getHight(); return getWidth() * getHight();
} }
public function isInside(p:Pos):Bool { public function isInside(p:ScreenPos):Bool {
return (p.x >= tl.x && p.x <= br.x) && (p.y >= tl.y && p.y <= br.y); return (p.x >= tl.x && p.x <= br.x) && (p.y >= tl.y && p.y <= br.y);
} }
public function isOutside(p:Pos):Bool { public function isOutside(p:ScreenPos):Bool {
return !this.isInside(p); return !this.isInside(p);
} }
@@ -36,7 +38,7 @@ class Rect {
return br.x - tl.x; return br.x - tl.x;
} }
public function offset(pos:Pos) { public function offset(pos:ScreenPos) {
tl.x += pos.x; tl.x += pos.x;
tl.y += pos.y; tl.y += pos.y;
br.x += pos.x; br.x += pos.x;

34
src/lib/ScreenPos.hx Normal file
View File

@@ -0,0 +1,34 @@
package lib;
import lib.Vec.Vec2;
/**
Reporesents a Point in a 2D `Int` System.
Basicly a wrapper for Vec2<Int> with some extra functions.
**/
@:forward(x, y)
abstract ScreenPos(Vec2<Int>) from Vec2<Int> to Vec2<Int> {
inline public function new(i:Vec2<Int>) {
this = i;
}
@:op(A + B)
public inline function add(rhs:Vec2<Int>):ScreenPos {
return this.add(rhs);
}
@:op(A - B)
public inline function sub(rhs:Vec2<Int>):ScreenPos {
return this.sub(rhs);
}
@:op(A * B)
public inline function multiply(rhs:Vec2<Int>):ScreenPos {
return this.multiply(rhs);
}
@:op(-A)
public inline function negate():ScreenPos {
return this.negate();
}
}

55
src/lib/SinglePromise.hx Normal file
View File

@@ -0,0 +1,55 @@
package lib;
import kernel.EndOfLoop;
using tink.CoreApi;
class SinglePromise<T> {
private var inProgress:Bool = false;
private var interalPromise:Promise<T> = null;
private var interalResolve:T->Void = null;
private var interalReject:(Error->Void) = null;
private final activate:Void->Void;
public function new(activate:Void->Void) {
this.activate = activate;
}
public function request():Promise<T> {
if (this.inProgress) {
trace("Is progress");
return this.interalPromise;
}
this.inProgress = true;
this.interalPromise = new Promise((resolve, reject) -> {
this.interalResolve = resolve;
this.interalReject = reject;
this.activate();
return null;
});
return this.interalPromise;
}
public function isRunning():Bool {
return this.inProgress;
}
public function resolve(val:T) {
if (this.inProgress) {
this.inProgress = false;
this.interalResolve(val);
}
}
public function reject(err:Error) {
if (this.inProgress) {
this.inProgress = false;
this.interalReject(err);
}
}
}

View File

@@ -0,0 +1,64 @@
package lib;
import kernel.Timer;
using tink.CoreApi;
class SingleTimeoutPromise<T> {
private var inProgress:Bool = false;
private var interalPromise:Promise<T> = null;
private var interalResolve:T->Void = null;
private var interalReject:(Error->Void) = null;
private var timer:Timer = null;
private final activate:Void->Void;
private final timeout:Int;
public function new(timeout:Int, activate:Void->Void) {
this.activate = activate;
this.timeout = timeout;
}
public function request():Promise<T> {
if (this.inProgress) {
return this.interalPromise;
}
this.inProgress = true;
this.interalPromise = new Promise((resolve, reject) -> {
this.interalResolve = resolve;
this.interalReject = reject;
this.activate();
this.timer = new Timer(this.timeout, () -> {
this.reject(new Error("Timeout"));
});
return null;
});
return this.interalPromise;
}
public function isRunning():Bool {
return this.inProgress;
}
public function resolve(val:T) {
if (this.inProgress) {
this.inProgress = false;
this.timer.cancle();
this.interalResolve(val);
}
}
public function reject(err:Error) {
if (this.inProgress) {
this.inProgress = false;
this.timer.cancle();
this.interalReject(err);
}
}
}

49
src/lib/Tags.hx Normal file
View File

@@ -0,0 +1,49 @@
package lib;
import lib.PaN;
using Lambda;
using lua.Table;
/**
Represents a list a minecraft tags.
Needed because tags are SOMETIMES retuned not as arrays.
**/
@:forward
enum abstract Tags(Array<PaN>) from Array<PaN> to Array<PaN> {
inline public function new(arr:Array<String>) {
this = arr;
}
/**
From values that are object with the tag as the filed and the value set to true.
**/
@:from
public static function fromBoolTable(from:Table<String, Bool>) {
var rtn = [];
for (k => _ in Table.toMap(from)) {
rtn.push(k);
}
return new Tags(rtn);
}
/**
From values that are lua arrays.
**/
@:from
public static function fromIntTable(from:Table<Int, String>) {
return new Tags(from.toArray());
}
public function sortByName():Tags {
var copy = this.copy();
copy.sort((a:String, b:String) -> {
return if (a < b) -1 else 1;
});
return copy;
}
}

View File

@@ -1,6 +1,6 @@
package lib; package lib;
@:structInit class Vec2<T> { @:structInit class Vec2<T:Float> {
public var x:T; public var x:T;
public var y:T; public var y:T;
@@ -8,16 +8,96 @@ package lib;
this.x = x; this.x = x;
this.y = y; this.y = y;
} }
public function add(rhs:Vec2<T>):Vec2<T> {
return {
y: this.y + rhs.y,
x: this.x + rhs.x,
};
}
public function sub(rhs:Vec2<T>):Vec2<T> {
return {
y: this.y - rhs.y,
x: this.x - rhs.x,
};
}
public function multiply(rhs:Vec2<T>):Vec2<T> {
return {
y: this.y * rhs.y,
x: this.x * rhs.x,
};
}
public function negate():Vec2<T> {
return {
y: -this.y,
x: -this.x,
};
}
} }
@:structInit class Vec3<T> { @:structInit class Vec3<T:Float> {
public var x:T; public final x:T;
public var y:T; public final y:T;
public var z:T; public final z:T;
public function new(x:T, y:T, z:T) { public function new(x:T, y:T, z:T) {
this.x = x; this.x = x;
this.y = y; this.y = y;
this.z = z; this.z = z;
} }
public function add(rhs:Vec3<T>):Vec3<T> {
return {
x: this.x + rhs.x,
y: this.y + rhs.y,
z: this.z + rhs.z
};
}
public function sub(rhs:Vec3<T>):Vec3<T> {
return {
x: this.x - rhs.x,
y: this.y - rhs.y,
z: this.z - rhs.z
};
}
public function multiplyScalar(rhs:T):Vec3<T> {
return {
x: this.x * rhs,
y: this.y * rhs,
z: this.z * rhs
};
}
public function negate():Vec3<T> {
return {
x: -this.x,
y: -this.y,
z: -this.z
};
}
public function dot(rhs:Vec3<T>):Float {
return this.x * rhs.x + this.y * rhs.y + this.z * rhs.z;
}
public function cross(rhs:Vec3<T>):Vec3<T> {
return {
x: this.y * rhs.z - this.z * rhs.y,
y: this.z * rhs.x - this.x * rhs.z,
z: this.x * rhs.y - this.y * rhs.x
};
}
public function length():Float {
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
}
public function distance(rhs:Vec3<T>):Float {
return Math.sqrt(Math.pow(this.x - rhs.x, 2) + Math.pow(this.y - rhs.y, 2) + Math.pow(this.z - rhs.z, 2));
}
} }

Some files were not shown because too many files have changed in this diff Show More