Compare commits

...

158 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
64f4304626 fixed invManager wrong invState update
thanks chatGPT
2024-01-29 00:08:13 +01:00
8cd105b84c start termianl if not advanced computer 2024-01-28 16:46:57 +01:00
ce0e89f5b3 improved turtle defrag 2024-01-28 16:13:09 +01:00
c9e0ceef32 fixed turtle defrag 2024-01-28 15:46:37 +01:00
6170fdd0d3 added turtle defrag 2024-01-28 15:40:28 +01:00
3fce8d515a added InvManager.hx 2024-01-26 21:24:43 +01:00
fe88e065ab minor turtle improvements 2024-01-26 21:23:56 +01:00
e2916a213a added turtles to concepts 2024-01-22 23:37:02 +01:00
22509932c3 fixed pre-commit hook for deleted files 2024-01-22 23:36:41 +01:00
f109305f48 added bin info to other bins 2024-01-22 23:36:00 +01:00
295d284bc1 ALL NEW improved Binstore
also automaticly inlcude everything in src/bin
2024-01-22 03:34:19 +01:00
92deb7177f added ui events to LayerdRootElement 2024-01-21 20:50:08 +01:00
3e09bcfad2 also stop turtle main loop on exit 2024-01-21 20:20:59 +01:00
762f72c160 added LayerdRootElement 2024-01-21 16:32:55 +01:00
1d2a43155a added mirror function to structure 2023-11-19 01:43:58 +01:00
9bb6b8bf2b newline support for TextElement 2023-11-16 02:29:41 +01:00
7f8c0d154c added rotate to structure 2023-11-16 01:38:40 +01:00
f9c10cfc0b improved turtle Printer 2023-11-15 20:39:08 +01:00
be89ca12bd added rotate to Layer 2023-11-15 20:38:26 +01:00
ca79714e8d added textRepresentation to Layer 2023-11-15 20:35:23 +01:00
e0d4844890 fixes terminal newline handling 2023-11-15 17:42:22 +01:00
12eb9d05de added Structure Printer helper 2023-11-15 01:05:05 +01:00
e39720b63d upadted CC luca code url 2023-11-07 18:14:56 +01:00
e593fc52c4 added promise support for RPC macro 2023-08-10 21:52:02 +02:00
ad0c62d6e5 added buildserver 2023-08-09 15:22:12 +02:00
0cac0053e3 fixed native cc call to getMethods 2023-08-06 22:04:44 +02:00
a568c9cdd8 added example excavate app 2023-08-05 11:42:38 +02:00
44772557ba fixed INS when no GPS fix 2023-08-05 11:42:15 +02:00
0540cc465a added example turtle program 2023-08-03 18:01:12 +02:00
21387cd8e7 you can stop the turtle thread now 2023-08-02 17:14:54 +02:00
a46760e587 added seperate turtle thread 2023-08-02 16:32:52 +02:00
c83f457968 clipAppBase run final 2023-08-02 16:31:40 +02:00
b8f5ffb93a made Turtle a static class 2023-08-01 13:19:01 +02:00
3c59e045de precommit add formatted files 2023-08-01 13:16:18 +02:00
4b8bc87db9 added printArgs debug function 2023-08-01 13:15:56 +02:00
0c7280a2f5 fixed turtle output text 2023-07-31 16:42:17 +02:00
98b8465436 isTurtle check 2023-07-31 16:40:11 +02:00
016063e3be formatting 2023-07-30 23:34:43 +02:00
0fc2d4d397 made SRSC a rpc thing 2023-07-30 23:34:35 +02:00
da3f00acb6 added precommit hook 2023-07-30 23:33:51 +02:00
db50a93512 improved RPC macro 2023-07-30 23:18:51 +02:00
f3a13e4247 rpc now can handle args 2023-07-30 19:02:59 +02:00
91972107eb BIG FORMATING COMMIT 2023-07-30 15:55:22 +02:00
088fce0aaa made ServiceManager a static class 2023-07-30 15:48:22 +02:00
3b3c69ee56 made GPS and INS a static class 2023-07-30 15:45:30 +02:00
5a9d463192 made GPS a static class 2023-07-30 15:42:02 +02:00
97c906e013 made Net a static class 2023-07-30 15:38:46 +02:00
505d318ffb made Routing a static class 2023-07-30 15:35:43 +02:00
89f209130e made BinStore a static class 2023-07-27 20:45:37 +02:00
74d0232160 made WindowManager a static class 2023-07-27 20:41:23 +02:00
9deea0ee98 made peripherals a static class 2023-07-27 20:38:23 +02:00
a93ee1cddf made Log static class 2023-07-27 20:35:36 +02:00
e1e0180502 changed kernel events to static class 2023-07-27 20:30:39 +02:00
adc5ab1849 makefile watch all files 2023-07-27 20:30:11 +02:00
1a523cb3ce added startup file 2023-07-27 20:15:41 +02:00
dfaabea00d added styles to TextElement 2023-07-11 13:17:33 +02:00
8683eaf17a added very simple GPS GUI 2023-07-10 23:29:22 +02:00
5fa6c3ecbf added RPC macro 2023-07-10 22:56:07 +02:00
10a061c41b automatic add Process to DCEHack 2023-07-09 15:29:26 +02:00
c390519393 some ui stuff 2023-07-08 14:01:38 +02:00
4f881117cf added macro to export perph 2023-07-08 12:51:08 +02:00
4084659a4a added more doc to GUI stuff 2023-06-30 23:35:57 +02:00
63e279f879 added BigReactor Perph 2023-06-30 23:02:52 +02:00
f68ae00098 dynamic app selection on home context 2023-06-30 19:06:46 +02:00
2937de86d6 select output on home context 2023-06-30 18:42:23 +02:00
788c839937 made a func public 2023-06-30 18:42:06 +02:00
90e76c8cd9 clear screen on shutdown 2023-06-30 18:15:02 +02:00
d2873d6353 added list for srsc 2023-06-30 16:19:00 +02:00
d1f9104aba makefile: deps for hx and js 2023-06-30 15:34:51 +02:00
07ad65d5cf added ID bin 2023-06-26 20:16:31 +02:00
3a2613d916 improved terminal with history 2023-06-26 20:04:26 +02:00
dbd8038851 removed unused import 2023-06-26 20:04:00 +02:00
7bfe594b4b improved exporter 2023-06-26 19:06:24 +02:00
4dcc060e9a added type to IPerph 2023-06-26 19:05:48 +02:00
f124525d2d added computer perph 2023-06-26 19:05:10 +02:00
8333cabdc8 added some docs 2023-06-25 21:23:13 +02:00
7b55241e00 fixed Synopsys in srv 2023-06-25 21:21:33 +02:00
b60c9774e1 fixed missing Synopsys in Redstone 2023-06-25 21:21:15 +02:00
7861135c05 fixed missing synopsis in Drive 2023-06-25 21:20:52 +02:00
909aac941c added export to Redstone 2023-06-25 19:06:48 +02:00
e686afb298 implmented basic ressource exporting 2023-06-25 19:05:57 +02:00
7996dd062d changed synaopsis to srv 2023-06-25 19:02:45 +02:00
9d6979c8e8 added a type to Package and added GenericPackage
This forces you to use cast to if you want to force the package to have
a specific type
2023-06-07 23:42:23 +02:00
179 changed files with 7738 additions and 3317 deletions

3
.gitignore vendored
View File

@@ -1,2 +1,3 @@
/build
/node_modules
node_modules
/dump

View File

@@ -1,36 +1,68 @@
BUNDLE_NAME = bundle.lua
HAXE_NAME = haxe.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
HAXE_FLAGS =
HAXE_FLAGS = -D message.reporting=pretty
POLYFILLED_NAME = bundle.polyfill.lua
POLYFILL_SRC = src/polyfill.lua
CREAFTOS_PATH = craftos
BUILD_HXML = build.hxml
HAXE_PATH := $(BUILD_DIR)/$(HAXE_NAME)
MIN_PATH := $(BUILD_DIR)/$(MINIFYD_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)
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 build.hxml $(HAXE_FLAGS)
haxe $(BUILD_HXML) $(HAXE_FLAGS)
$(POLYFILL_PATH): $(POLYFILL_SRC) $(HAXE_PATH)
cat $(POLYFILL_SRC) $(HAXE_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
deps:
haxelib install all --always && yarn install
deps: deps-hx deps-node
.PHONY: deps-hx
deps-hx:
haxelib install all --always
.PHONY: deps-node
deps-node:
npm install
.PHONY: clean
clean:
@@ -39,7 +71,7 @@ clean:
.PHONY: watch
watch:
find src -name "*.hx" | entr make debug
find src | entr make debug
.PHONY: emulator
emulator:
@@ -47,4 +79,12 @@ emulator:
.PHONY: webconsole
webconsole:
node console.js
node tools/console.js
.PHONY: format
format:
haxelib run formatter -s src
.PHONY: format-deps
format-deps:
haxelib install formatter

View File

@@ -21,7 +21,7 @@ run `make deps && make`. The `bundle.min.lua` inside the `build` dir is the fina
# Useful links
[CC lua code](https://github.com/cc-tweaked/CC-Tweaked/tree/mc-1.19.x/projects/core/src/main/resources/data/computercraft/lua)
[CC lua code](https://github.com/cc-tweaked/CC-Tweaked/tree/mc-1.20.x/projects/core/src/main/resources/data/computercraft/lua)
[CC wiki](https://tweaked.cc/)

View File

@@ -9,4 +9,4 @@
-D lua-vanilla
-D lua-ver 5.1
--lua build/haxe.lua
--macro include("bin")

24
buildServer/Dockerfile Normal file
View File

@@ -0,0 +1,24 @@
FROM haxe:4.3-bullseye
RUN apt-get update && apt-get install -y \
git \
make \
&& rm -rf /var/lib/apt/lists/*
RUN curl -fsSL https://deb.nodesource.com/setup_current.x | bash - && \
apt-get install -y nodejs
RUN mkdir -p /app
RUN mkdir -p /repo
WORKDIR /app
COPY index.js /app/index.js
COPY package.json /app/package.json
RUN npm install
ENV REPO_DIR=/repo
EXPOSE 3000
CMD ["node", "index.js"]

105
buildServer/index.js Normal file
View File

@@ -0,0 +1,105 @@
const express = require('express');
const childProcess = require('child_process');
const fs = require('fs').promises;
const path = require('path');
const async = require('async');
const bodyParser = require('body-parser');
const workingDir = process.env.REPO_DIR ?? "repo";
const buildQueue = async.queue(async (task) => {
return build(task);
});
const app = express();
app.use(bodyParser.json());
app.post('/build', async (req, res) => {
try{
let output = await buildQueue.push(req.body);
res.status(200);
res.send(output);
}catch(e){
res.status(500);
res.send(e);
}
});
app.post('/pull', async (req, res) => {
try{
await pull();
res.status(200);
res.send("Pulled");
}catch(e){
res.status(500);
res.send(e);
}
});
main();
async function build(body){
return await make();
}
async function main() {
await setup();
app.listen(3000, () => console.log('Server running on port 3000'));
process.on('SIGINT', async () => {
console.log("Shutting down");
process.exit(0);
});
}
async function pull(){
await runCommand("git pull");
}
async function make(makeCommand = "make build"){
try{
await runCommand(makeCommand);
let build = await fs.readFile(path.join(workingDir,"build/bundle.min.lua"), "utf8");
return build;
}catch(e){
console.log("Build failed");
}finally{
await runCommand("make clean");
}
}
async function setup() {
try {
await fs.mkdir(workingDir);
} catch (e) {
if (e.code !== "EEXIST") {
throw e;
}
}
if ((await fs.readdir(workingDir)).length !== 0) {
return;
}
console.log("Setting up repo");
await runCommand("git clone https://git.kapelle.org/niklas/cc-haxe.git .");
await runCommand("make deps");
}
function runCommand(cmd) {
return new Promise((resolve, reject) => {
childProcess.exec(cmd,{
cwd: workingDir,
}, (err, stdout, stderr) => {
if (err) {
reject(err);
}
console.log(stdout);
console.log(stderr);
resolve();
});
});
}

701
buildServer/package-lock.json generated Normal file
View File

@@ -0,0 +1,701 @@
{
"name": "buildServer",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "buildServer",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"async": "^3.2.4",
"body-parser": "^1.20.2",
"express": "^4.18.2"
}
},
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
"license": "MIT",
"dependencies": {
"mime-types": "~2.1.34",
"negotiator": "0.6.3"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
"license": "MIT"
},
"node_modules/async": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz",
"integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ=="
},
"node_modules/body-parser": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
"license": "MIT",
"dependencies": {
"safe-buffer": "5.2.1"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/content-type": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
"license": "MIT"
},
"node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/destroy": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
"license": "MIT",
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
"license": "MIT"
},
"node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
"license": "MIT"
},
"node_modules/etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/express": {
"version": "4.18.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
"license": "MIT",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.1",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.5.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.2.0",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.7",
"qs": "6.11.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.18.0",
"serve-static": "1.15.0",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"engines": {
"node": ">= 0.10.0"
}
},
"node_modules/express/node_modules/body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.1",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/express/node_modules/raw-body": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/finalhandler": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
"license": "MIT",
"dependencies": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
"statuses": "2.0.1",
"unpipe": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"license": "MIT"
},
"node_modules/get-intrinsic": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
"integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.1"
},
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/has-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
"license": "MIT",
"dependencies": {
"depd": "2.0.0",
"inherits": "2.0.4",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"toidentifier": "1.0.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC"
},
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
"license": "MIT",
"engines": {
"node": ">= 0.10"
}
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==",
"license": "MIT"
},
"node_modules/methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"license": "MIT",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT"
},
"node_modules/negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/object-inspect": {
"version": "1.12.3",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
"license": "MIT",
"dependencies": {
"ee-first": "1.1.1"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==",
"license": "MIT"
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
"license": "MIT",
"dependencies": {
"forwarded": "0.2.0",
"ipaddr.js": "1.9.1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.0.4"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/raw-body": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/send": {
"version": "0.18.0",
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
"license": "MIT",
"dependencies": {
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"mime": "1.6.0",
"ms": "2.1.3",
"on-finished": "2.4.1",
"range-parser": "~1.2.1",
"statuses": "2.0.1"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/send/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/serve-static": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
"license": "MIT",
"dependencies": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
"send": "0.18.0"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
"license": "ISC"
},
"node_modules/side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
"license": "MIT",
"dependencies": {
"call-bind": "^1.0.0",
"get-intrinsic": "^1.0.2",
"object-inspect": "^1.9.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
"license": "MIT",
"engines": {
"node": ">=0.6"
}
},
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
"license": "MIT",
"dependencies": {
"media-typer": "0.3.0",
"mime-types": "~2.1.24"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
"license": "MIT",
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
}
}
}

11
buildServer/package.json Normal file
View File

@@ -0,0 +1,11 @@
{
"name": "buildServer",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"async": "^3.2.4",
"body-parser": "^1.20.2",
"express": "^4.18.2"
}
}

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);

253
doc/Concepts.md Normal file
View File

@@ -0,0 +1,253 @@
Use this if you want to know how the OS works.
# Network
Every computer has a id assigned by the mod. It is a unique number that identifies the computer and cannot be changed.
The native implementation of the network stack works with channels. If you listen on a channel you will receive all messages sent to that channel.
Every computer listens to the channel with its id and a brodcast channel.
The broadcast channel is used for Routing Tables and GPS packets.
Network messages can be forwarded by other computers. This is done by the Routing Table. The routing algorithm prefers wire over wireless connections.
Packages also have a time-to-live (TTL) value. This is used to prevent packages from being forwarded forever. You will get a response if a package is dropped because of TTL.
There is also a concept of protocols. A protocol is used to distinguish between different types of packages.
A protocol is basically a string and that is used to forward packages to the correct handler. You can use `registerProto` to listen for packages with a specific protocol.
There are 2 ways of sending messages to other computers: `sendAndAwait` and `sendAndForget`.
`sendAndAwait` will wait for a response from the remote computer and return it.
`sendAndForget` will send the message and return immediately and does not care about the response.
You can compare it to UDP and TCP.
## Usage
```haxe
import net.Net;
var data = {"foo": "bar"};
Net.sendAndAwait(netID,"protoname",data).map((response)->{
switch (response){
case Success(data):
trace(data);
case Failure(error):
trace(error);
}
});
Net.registerProto("res",(pack: GenericPackage)->{
var requestPack: Package<MyType> = cast pack; // Try not to use Dynamic
requestPack.respond("Hello Back");
});
```
# Peripherals
Peripherals are devices that are connected to the computer. They can be used to interact with the world.
Every peripheral has an address and a type. The address can be "back" or "right" to refer to the peripheral on the back or right side of the computer or
something like "energyCell_0" to refer to something connected via cable. Peripherals can be accessed via the Peripheral class.
Also peripherals can be made accessible via the network. More on that later.
## Usage
```haxe
var back = Peripheral.getRedstone("back");
back.setOutput(true);
var drive = Peripheral.getDrive("drive_0");
drive.eject();
```
# GUI
If you want to write something to the screen you have to create a `WindowContext` via the `WindowManager`. This allows programs to write to the screen without interfering with each other.
There are currently 2 types of `WindowContext`: the `BufferedVirtualTermWriter` that stores the state of the screen in a buffer and prints it to the screen
when it is activeted and the `StatelessVirtualTermWriter` which calls a render method when it is activated. Currently i prefer the `StatelessVirtualTermWriter` because its not so heavy on the RAM but both work.
They both can be used just like the [nativ implmentation](https://tweaked.cc/module/term.html).
## Usage
```haxe
var ctx = WindowManager.createNewBufferedContext();
ctx.setCursorPos(0, 0);
ctx.setCursorBlink(false);
ctx.setBackgroundColor(Blue);
ctx.setForegroundColor(White);
ctx.write("Hello world!");
```
## Under the hood
There are a number of interfaces and classes that needs to be explained to understand how the GUI works.
`TermWriteable` is an interface that allows the usage of the normal CC terminal write methods. Stuff like `write`, `setCursorPos` and `setCursorBlink` are defined here. This is of course implemented by the physical screens and the main terminal.
Most of the time you will not write directory to a real screen but to a `VirtualTermWriter` which extends `TermWriteable` with some more methods like `enable`
and `setTarget`. The `setTarget` is used as the proxy target of a `VirtualTermWriter` and with `enable` and `disable` you can enable and disable the forwarding of the write methods to the target.
The `StatelessVirtualTermWriter` and `BufferedVirtualTermWriter` are both `VirtualTermWriter`. They can have a real output as a target. Or they can have another `VirtualTermWriter` as target like the `BufferedVirtualTermWriter` which uses a `TermBuffer` as an intermediate target.
All of that is just for printing to the screen. If you want to read input you have to use the `WindowContext` which is a `TermWriteable`.
`WindowContext` also handles events like `onClick` or `onKey`. This is need so that the right program gets the input depending on the active window on the
screen or terminal.
All of the `WindowContext` are managed by the `WindowManager`. The `WindowManager` also delegates the events to the right `WindowContext`.
## GUI helper classes
Because we want a more abstract way of writing to the screen we have some "helper" classes. I call them "helper" but they a very essential to the GUI.
First there is the `Pixel` class which is nothing more that a char and a foreground and background color.
A collection of `Pixel` is called a `Canvas` which is nothing more than a 2D array of `Pixel` with some functions strapped to it.
# Proceses
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.
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.
More on that at [Applications](#applications).
# EndOfLoop
You can imagine the whole runtime like the event loop is JS. The `EndOfLoop` class is used to register callbacks that are called at the end of the loop.
This is like the `setTimeout(0, callback)` in JS.
# Turtle Thread
Computercraft is mostly event-based. Listening to `Coroutine.yield()` waits for events with an option parameter for filtering events.
The problem with turtle arises when we call turtle functions like `move` or `place`. These functions wait internally for
the operation to finish with a `Coroutine.yield("turtle_response")` call. There is no way to prevent this. So in order to keep the main loop from
being blocked, we run all the turtle code in a separate thread.
There are no real threads in Lua, but we can create a coroutine for the turtles. Lua now switches between the main thread and the turtle thread.
Switching occurs when a `turtle_response` events is fired. This will be forwarded to the turtle thread. Once the turtle thread has run its code
and waits for the turtle operation to finish, we switch back to the main thread. The main thread is running normally until a `turtle_response` fires
and the circle repeats.
Be aware that no one is stopping you from running turtle commands in the main thread.
See `KernelEvents.hx` for more.
## Using Turtle threads
Example:
```haxe
// In the context of a process
if (!handle.claimTurtleMutex()) {
Log.warn("Failed to claim turtle thread");
return;
}
// Btw. no one is stopping you from calling this without claiming the mutex.
TurtleMutex.runInTThread(() -> {
while(true){
Turtle.turnLeft();
}
});
```
# 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.

112
doc/Manual.md Normal file
View File

@@ -0,0 +1,112 @@
Use this if you want to know how to use the OS.
# Commands
## Disk
Interact with a disk drive.
Synopsys:
- `disk ls` list all disk drives and its content
- `disk play <drive>` play an audio disk inside a disk drive
- `disk stop <drive>` stops playing audio
- `disk eject <drive>` ejects the disk
- `disk lable <dive> [lable]` Reads or sets a lable on a disk.
## GPS
Interact with the buildin GPS and INS (Inertial Navigation System) system. The INS is used to update a turtle's position when it is moving.
The GPS has 4 states of accuracy:
- `0` Unknown position
- `1` Position from INS or its best guess
- `2` Position from manual input or the last known position
- `3` Position from GPS
In case of a turtle the GPS accuracy will be set to `1`.
Synopsys:
- `gps set <x> <y> <z>` set the position manually. This will get saved.
- `gps status` prints the current GPS accuracy, position and INS status.
- `gps locate` try to locate the current position. Accuracy will be set to `3` if successful.
- `gps ins` perform an INS alignment. Used to get the current heading.
## KSettings
Get and set Kernel settings.
Current settings are:
- `hostname` the hostname of the computer
- `sitecontroller` the id of the site controller
Synopsys:
- `ks get <setting>` print the value of a setting
- `ks set <setting> <value>` set the value of a setting
- `ks list` list all settings
## LSPS
List all processes running on the computer. Very simple and not very useful other than for debugging.
## Net
Interact with the network stack.
Synopsys:
- `net route` print the routing table
- `net iface` list all network interfaces
- `net proto` list all network currently active protocols
- `net ping <id>` ping a remote computer by id
## Peripherals
Interact with the peripherals connected to the computer.
Synopsys:
- `perf inspect <peripheral>` print all types and methods of a peripheral
- `perf list` list all peripherals
## Redstone
Interact with the redstone. Support for Project Red's Bundle Cable is partialy implemented.
Synopsys:
- `rs on <side> ` turn on redstone on a side
- `rs off <side> ` turn off redstone on a side
- `rs get <side> ` get the redstone state on a side
## Service
Interact with the service manager. A service is a program that runs in the background and can be automatically started when the computer boots.
A service is a normal program. In order to create a new service you must register a command with arguments under a name.
After that you can start the service by its name. If it is running you can enable it so it will be started when the computer boots.
Synopsys:
- `srv start <name> ` start a service
- `srv stop <name> ` stop a service
- `srv register <name> <binary> [args...]` register a service
- `srv unregister <name> ` unregister a service
- `srv list` list all services
- `srv enable <name> ` enable a service. Must be started before it can be enabled.
## Turtle
Interact with a turtle. Very simple. More to come maybe.
Synopsys:
- `turtle forward` move the turtle forward
- `turtle back` move the turtle back
- `turtle up` move the turtle up
- `turtle down` move the turtle down
- `turtle left` turn the turtle left
- `turtle right` turn the turtle right

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"
}
}
}
}

15
pre-commit.sh Executable file
View File

@@ -0,0 +1,15 @@
#!/usr/bin/env sh
# Run:
# ln -s ../../pre-commit.sh .git/hooks/pre-commit
set -e
# format
make format
git diff --name-only --cached --diff-filter=d | xargs -l git add
# build
make build

View File

@@ -1,10 +1,29 @@
import kernel.ui.WindowManager;
import kernel.ps.ProcessManager;
import kernel.binstore.BinStore;
import kernel.MainTerm;
import lib.HomeContext;
class Startup {
public static function main() {
var main = new HomeContext();
#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()) {
var main = new HomeContext();
main.run();
main.run();
} else {
var term = BinStore.instantiate("terminal");
var pid = ProcessManager.run(term, {});
var ctx = WindowManager.getContextByPID(pid);
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,26 +1,28 @@
package bin;
import kernel.peripherals.Drive;
import lib.CLIAppBase;
import kernel.peripherals.Peripherals.Peripheral;
using tink.CoreApi;
using Lambda;
@:build(macros.Binstore.includeBin("Disk", ["disk"]))
class Disk extends CLIAppBase {
public function new() {
registerSyncSubcommand("ls", (args)->{
Peripheral.instance.getAllDrives().foreach(drive -> {
registerSyncSubcommand("ls", (args) -> {
Peripheral.getAllDrives().foreach(drive -> {
var addr = drive.getAddr();
var label = drive.getDiskLabel();
var id = drive.getDiskID();
if (drive.isDiskPresent()){
if (drive.hasAudio()){
if (drive.isDiskPresent()) {
if (drive.hasAudio()) {
handle.writeLine('${addr} => ${label} [AUDIO]');
}else{
} else {
handle.writeLine('${addr} => ${label} (${id})');
}
}else {
} else {
handle.writeLine('${addr} => [NO DISK]');
}
@@ -28,100 +30,71 @@ class Disk extends CLIAppBase {
});
});
registerSyncSubcommand("play", (args)->{
if (args.length < 1){
handle.writeLine("Missing drive address");
return false;
}
return audioDiskPlayPause(args[0], true);
},"<drive>");
registerSyncSubcommand("play", (args) -> {
return audioDiskPlayPause(args.getString("addr"), true);
}, [Peripheral("addr", Drive.TYPE_NAME)]);
registerSyncSubcommand("stop", (args) -> {
if (args.length < 1){
handle.writeLine("Missing drive address");
return false;
}
return audioDiskPlayPause(args.getString("addr"), false);
}, [Peripheral("addr", Drive.TYPE_NAME)]);
return audioDiskPlayPause(args[0], false);
});
registerSyncSubcommand("eject", (args) -> {
var driveAddr = args.getString("addr");
var drive = Peripheral.getDrive(driveAddr);
registerSyncSubcommand("eject", (args)->{
if (args.length < 1){
handle.writeLine("Missing drive address");
return false;
}
var driveAddr = args[0];
var drive = Peripheral.instance.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);
return false;
}
drive.ejectDisk();
return true;
},"<drive>");
}, [Peripheral("addr", Drive.TYPE_NAME)]);
registerSyncSubcommand("lable",(args) -> {
if (args.length < 1){
handle.writeLine("Missing drive address");
return false;
}
registerSyncSubcommand("label", (args) -> {
var driveAddr = args.getString("addr");
var drive = Peripheral.getDrive(driveAddr);
var label:Null<String> = args.getString("label");
var driveAddr = args[0];
var drive = Peripheral.instance.getDrive(driveAddr);
var label:String = args[1];
if (drive == null){
handle.writeLine("Drive not found: " + driveAddr);
}
if (!drive.isDiskPresent()){
if (!drive.isDiskPresent()) {
handle.writeLine("No disk in drive: " + driveAddr);
}
if (label == null || label == ""){
if (label == null || label == "") {
handle.writeLine(drive.getDiskLabel());
}else{
var err = drive.setDiskLabel(label);
if (err != null){
} else {
var err = drive.setDiskLabel(label);
if (err != null) {
handle.writeLine("Failed to set lable");
return false;
}
}
return true;
},"<drive> [label]");
}, [Peripheral("addr", Drive.TYPE_NAME), Optional(String("label"))]);
}
private function audioDiskPlayPause(driveAddr: String, play: Bool): Bool {
var drive = Peripheral.instance.getDrive(driveAddr);
private function audioDiskPlayPause(driveAddr:String, play:Bool):Bool {
var drive = Peripheral.getDrive(driveAddr);
if (drive == null){
if (drive == null) {
handle.writeLine("Drive not found: " + driveAddr);
return false;
}
if (!drive.isDiskPresent()){
if (!drive.isDiskPresent()) {
handle.writeLine("No disk in drive: " + driveAddr);
return false;
}
if (!drive.hasAudio()){
if (!drive.hasAudio()) {
handle.writeLine("Disk in drive: " + driveAddr + " does not have audio");
return false;
}
if (play){
if (play) {
drive.playAudio();
}else{
} else {
drive.stopAudio();
}

View File

@@ -2,27 +2,28 @@ package bin;
import lib.CLIAppBase;
import kernel.gps.INS;
import lib.Pos3;
import lib.WorldPos;
import lib.Vec.Vec3;
using tink.CoreApi;
@:build(macros.Binstore.includeBin("GPS", ["gps"]))
class GPS extends CLIAppBase {
public function new() {
registerSyncSubcommand("set", (args)->{
var x: Float = Std.parseFloat(args[0]);
var y: Float = Std.parseFloat(args[1]);
var z: Float = Std.parseFloat(args[2]);
registerSyncSubcommand("set", (args) -> {
var x:Float = args.getFloat("x");
var y:Float = args.getFloat("y");
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.instance.setManualPosition(pos);
kernel.gps.GPS.setManualPosition(pos);
return true;
},"<x> <y> <z>");
}, [Float("x"), Float("y"), Float("z")]);
registerSyncSubcommand("status",(args)->{
var pos = kernel.gps.GPS.instance.getPosition();
registerSyncSubcommand("status", (args) -> {
var pos = kernel.gps.GPS.getPosition();
if (pos != null) {
handle.writeLine('Position x:${pos.x} y:${pos.y} z:${pos.z}');
} else {
@@ -30,16 +31,16 @@ class GPS extends CLIAppBase {
return true;
}
var acc = kernel.gps.GPS.instance.getAccuracy();
if (acc == 1){
var acc = kernel.gps.GPS.getAccuracy();
if (acc == 1) {
handle.writeLine("Accuracy: Low");
} else if (acc == 2){
} else if (acc == 2) {
handle.writeLine("Accuracy: Medium");
} else if (acc == 3){
} else if (acc == 3) {
handle.writeLine("Accuracy: High");
}
var ins = INS.instance.getHeading();
var ins = INS.getHeading();
if (ins != null) {
handle.writeLine('INS heading: ${ins.x} y:${ins.y} z:${ins.z}');
} else {
@@ -49,19 +50,20 @@ class GPS extends CLIAppBase {
return true;
});
registerAsyncSubcommand("locate",(args)->{
return kernel.gps.GPS.instance.locate().map((pos)->{
if (pos != null) {
handle.writeLine('Position x:${pos.x} y:${pos.y} z:${pos.z}');
} else {
handle.writeLine("Position not available");
registerAsyncSubcommand("locate", (args) -> {
return kernel.gps.GPS.locate().map((result) -> {
switch result {
case Success(pos):
handle.writeLine('Position x:${pos.x} y:${pos.y} z:${pos.z}');
case Failure(err):
handle.writeLine("Position not available: " + err);
}
return true;
});
});
registerAsyncSubcommand("ins",(args)->{
return INS.instance.align().map((_)->{
registerAsyncSubcommand("ins", (args) -> {
return INS.align().map((_) -> {
handle.writeLine("INS aligned");
return true;
});

View File

@@ -1,16 +0,0 @@
package bin;
import kernel.ps.ProcessHandle;
import kernel.ps.Process;
using tink.CoreApi;
class HelloWorld implements Process {
public function new() {}
public function run(handle:ProcessHandle) {
handle.write("Hello World!");
handle.close();
}
}

View File

@@ -1,26 +0,0 @@
package bin;
import kernel.Timer;
import kernel.ps.ProcessHandle;
import kernel.ps.Process;
class HelloWorldService implements Process {
private var timer:Timer;
public function new() {}
public function run(handle:ProcessHandle) {
handle.write("Hello World! Started\n");
this.startTimer(handle);
handle.addDeferFunc(()->{
timer.cancle();
});
}
public function startTimer(handle: ProcessHandle) {
this.timer = new Timer(5, function() {
handle.write("Hello World!\n");
this.startTimer(handle);
});
}
}

14
src/bin/ID.hx Normal file
View File

@@ -0,0 +1,14 @@
package bin;
import kernel.ps.ProcessHandle;
import kernel.ps.IProcess;
@:build(macros.Binstore.includeBin("ID", ["id"]))
class ID implements IProcess {
public function new() {}
public function run(handle:ProcessHandle) {
handle.writeLine("ID: " + kernel.net.Net.networkID);
handle.close();
}
}

View File

@@ -3,65 +3,61 @@ package bin;
import kernel.KernelSettings;
import lib.CLIAppBase;
@:build(macros.Binstore.includeBin("Kernel settings", ["ksettings", "kset"]))
class KSettings extends CLIAppBase {
public function new() {
registerSyncSubcommand("get", (args)->{
var key = args[0];
public function new() {
registerSyncSubcommand("get", (args) -> {
var key = args.getString("key");
if (key == null) {
handle.writeLine("Key not specified");
return false;
}
var value = switch (key) {
case "hostname":
KernelSettings.hostname;
case "nameServer":
Std.string(KernelSettings.nameServer);
default:
null;
}
var value = switch (key){
case "hostname":
KernelSettings.hostname;
case "sitecontroller":
Std.string(KernelSettings.siteController);
default:
null;
}
if (value == null) {
handle.writeLine("Key not found or not set");
return false;
}
if (value == null) {
handle.writeLine("Key not found or not set");
return false;
}
handle.writeLine(value);
return true;
}, [String("key")]);
handle.writeLine(value);
return true;
}," <key>");
registerSyncSubcommand("set", (args) -> {
var key = args.getString("key");
registerSyncSubcommand("set", (args)->{
var key = args[0];
if (key == null) {
handle.writeLine("Key not specified");
return false;
}
if (key == null) {
handle.writeLine("Key not specified");
return false;
}
var value = args.getString("value");
var value = args[1];
if (value == null) {
handle.writeLine("Value not specified");
return false;
}
if (value == null) {
handle.writeLine("Value not specified");
return false;
}
switch (key) {
case "hostname":
KernelSettings.hostname = value;
case "nameServer":
KernelSettings.nameServer = Std.parseInt(value);
default:
handle.writeLine("Key not found");
return false;
}
return true;
}, [String("key"), String("value")]);
switch (key){
case "hostname":
KernelSettings.hostname = value;
case "sitecontroller":
KernelSettings.siteController = Std.parseInt(value);
default:
handle.writeLine("Key not found");
return false;
}
return true;
}," <key> <value>");
registerSyncSubcommand("list", (args)->{
handle.writeLine("hostname");
handle.writeLine("sitecontroller");
return true;
});
}
registerSyncSubcommand("list", (args) -> {
handle.writeLine("hostname");
handle.writeLine("nameServer");
return true;
});
}
}

View File

@@ -1,22 +1,22 @@
package bin;
import kernel.ps.ProcessHandle;
import kernel.ps.Process;
import kernel.ps.IProcess;
import lib.Color;
import lib.MathI;
import kernel.log.Log;
import kernel.ui.WindowContext;
import lib.ui.UIApp;
using tink.CoreApi;
class KernelLog implements Process {
private var handle: ProcessHandle;
private var ctx: WindowContext;
@:build(macros.Binstore.includeBin("Log", ["log"]))
class KernelLog implements IProcess {
private var handle:ProcessHandle;
private var ctx:WindowContext;
public function new() {}
public function run(handle: ProcessHandle):Void {
public function run(handle:ProcessHandle):Void {
this.handle = handle;
var statelessCtx = handle.createStatelessWindowContext();
@@ -24,23 +24,23 @@ class KernelLog implements Process {
statelessCtx.setRenderFunc(this.render);
Log.instance.onLog.handle(()->{
Log.onLog.handle(() -> {
statelessCtx.requestRender();
});
}
private function render() {
ctx.clear();
ctx.setCursorPos(0,0);
ctx.setCursorPos(0, 0);
var lines = Log.instance.getLines();
var lines = Log.getLines();
var height = ctx.getSize().y;
var start = MathI.max(lines.length - height,0);
var start = MathI.max(lines.length - height, 0);
for (i in start...lines.length) {
var line = lines[i];
switch (line.level){
switch (line.level) {
case Info:
ctx.setTextColor(Color.White);
ctx.write("[INFO] ");
@@ -59,7 +59,7 @@ class KernelLog implements Process {
}
ctx.write(line.message);
ctx.setCursorPos(0,ctx.getCursorPos().y + 1);
ctx.setCursorPos(0, ctx.getCursorPos().y + 1);
}
}
}

View File

@@ -2,20 +2,21 @@ package bin;
import kernel.ps.ProcessManager;
import kernel.ps.ProcessHandle;
import kernel.ps.Process;
import kernel.ps.IProcess;
class LSPS implements Process {
public function new() {}
@:build(macros.Binstore.includeBin("LSPS", ["lsps"]))
class LSPS implements IProcess {
public function new() {}
public function run(handle:ProcessHandle) {
var pids = ProcessManager.listProcesses();
var pids = ProcessManager.listProcesses();
handle.writeLine('Count: ${pids.length}');
handle.writeLine('Count: ${pids.length}');
for (pid in pids) {
handle.writeLine('${pid}');
}
for (pid in pids) {
handle.writeLine('${pid}');
}
handle.close();
}
handle.close();
}
}

View File

@@ -6,20 +6,21 @@ import kernel.net.Routing;
using tink.CoreApi;
@:build(macros.Binstore.includeBin("Net", ["net"]))
class Net extends CLIAppBase {
public function new() {
registerSyncSubcommand("route", (args)->{
var routes = Routing.instance.getRouteTable();
registerSyncSubcommand("route", (args) -> {
var routes = Routing.getRouteTable();
for(k => v in routes) {
for (k => v in routes) {
handle.writeLine('${k} => ${v.interf.name()}(${v.cost})');
}
return true;
});
registerSyncSubcommand("iface", (args)->{
var modems = Peripheral.instance.getAllModems();
registerSyncSubcommand("iface", (args) -> {
var modems = Peripheral.getAllModems();
for (modem in modems) {
handle.writeLine(modem.name());
@@ -28,8 +29,8 @@ class Net extends CLIAppBase {
return true;
});
registerSyncSubcommand("proto",(args)->{
var protos = kernel.net.Net.instance.getActiveProtocols();
registerSyncSubcommand("proto", (args) -> {
var protos = kernel.net.Net.getActiveProtocols();
for (proto in protos) {
handle.writeLine(proto);
@@ -38,20 +39,11 @@ class Net extends CLIAppBase {
return true;
});
registerAsyncSubcommand("ping",(args)->{
if (args.length < 1) {
return Future.sync(false);
}
registerAsyncSubcommand("ping", (args) -> {
var toID:Int = args.getInt("id");
var toID:Null<Int> = Std.parseInt(args[0]);
if (toID == null) {
handle.write("Invalid ID");
return Future.sync(false);
}
return kernel.net.Net.instance.ping(toID).map(result -> {
switch (result){
return kernel.net.Net.ping(toID).map(result -> {
switch (result) {
case Success(_):
handle.write("Ping succeeded");
case Failure(failure):
@@ -60,6 +52,6 @@ class Net extends CLIAppBase {
return true;
});
},"<id>");
}, [Int("id")]);
}
}

View File

@@ -1,38 +0,0 @@
package bin;
import kernel.peripherals.Peripherals.Peripheral;
import lib.CLIAppBase;
class Perf extends CLIAppBase {
public function new() {
registerSyncSubcommand("inspect",(args)->{
if (args.length < 1) return false;
var result = Peripheral.instance.inspect(args[0]);
if (result == null){
handle.writeLine("No peripheral found on side "+args[0]);
return true;
}
handle.writeLine("Types:");
for (type in result.types){
handle.writeLine(" "+type);
}
handle.writeLine("Methods:");
for (method in result.methods){
handle.writeLine(" "+method);
}
return true;
},"<side>");
registerSyncSubcommand("list",(args)->{
for (addr in Peripheral.instance.getAllAddresses()){
handle.writeLine('$addr => ${Peripheral.instance.getTypes(addr).join(", ")}');
}
return true;
});
}
}

33
src/bin/Peri.hx Normal file
View File

@@ -0,0 +1,33 @@
package bin;
import kernel.peripherals.Peripherals.Peripheral;
import lib.CLIAppBase;
@:build(macros.Binstore.includeBin("Peripheral", ["ph", "peripheral"]))
class Peri extends CLIAppBase {
public function new() {
registerSyncSubcommand("inspect", (args) -> {
var addr = args.getString("addr");
var result = Peripheral.inspect(addr);
handle.writeLine("Types:");
for (type in result.types) {
handle.writeLine(" " + type);
}
handle.writeLine("Methods:");
for (method in result.methods) {
handle.writeLine(" " + method);
}
return true;
}, [Addr("addr")]);
registerSyncSubcommand("list", (args) -> {
for (addr in Peripheral.getAllAddresses()) {
handle.writeLine('$addr => ${Peripheral.getTypes(addr).join(", ")}');
}
return true;
});
}
}

View File

@@ -5,22 +5,23 @@ import kernel.peripherals.Peripherals.Peripheral;
using tink.CoreApi;
class Redstone extends CLIAppBase{
@:build(macros.Binstore.includeBin("Redstone", ["redstone", "rs"]))
class Redstone extends CLIAppBase {
public function new() {
registerSyncSubcommand("on", (args)-> {
Peripheral.instance.getRedstone(args[0]).setOutput(true);
registerSyncSubcommand("on", (args) -> {
Peripheral.getRedstone(args.getString("side")).setOutput(true);
return true;
});
}, [Side("side")]);
registerSyncSubcommand("off", (args)-> {
Peripheral.instance.getRedstone(args[0]).setOutput(false);
registerSyncSubcommand("off", (args) -> {
Peripheral.getRedstone(args.getString("side")).setOutput(false);
return true;
});
}, [Side("side")]);
registerSyncSubcommand("get", (args)-> {
var value = Peripheral.instance.getRedstone(args[0]).getAnalogInput();
registerSyncSubcommand("get", (args) -> {
var value = Peripheral.getRedstone(args.getString("side")).getAnalogInput();
handle.write("Analog input: " + value);
return true;
});
}, [Side("side")]);
}
}

View File

@@ -5,82 +5,56 @@ import lib.CLIAppBase;
using tink.CoreApi;
@:build(macros.Binstore.includeBin("Service", ["srv"]))
class Service extends CLIAppBase {
public function new() {
registerSyncSubcommand("start", (args) ->{
if (args.length < 1) {
return false;
}
public function new() {
registerSyncSubcommand("start", (args) -> {
var result = ServiceManager.start(args.getString("name"));
return handleResult(result);
}, [String("name")]);
var name = args[0];
registerSyncSubcommand("stop", (args) -> {
var result = ServiceManager.stop(args.getString("name"));
return handleResult(result);
}, [String("name")]);
var result = ServiceManager.instance.start(name);
return handleResult(result);
},"Start a service");
registerSyncSubcommand("register", (args) -> {
var name = args.getString("name");
var binName = args.getString("binary");
var rest = args.getRest();
registerSyncSubcommand("stop", (args) ->{
if (args.length < 1) {
return false;
}
var result = ServiceManager.register(name, binName, rest);
return handleResult(result);
}, [String("name"), String("binary"), Rest("args")]);
var name = args[0];
registerSyncSubcommand("unregister", (args) -> {
var result = ServiceManager.unregister(args.getString("name"));
return handleResult(result);
}, [String("name")]);
var result = ServiceManager.instance.stop(name);
return handleResult(result);
},"Stop a service");
registerSyncSubcommand("list", (args) -> {
var list = ServiceManager.listRunning();
registerSyncSubcommand("register", (args) ->{
if (args.length < 2) {
return false;
}
for (name in list) {
this.handle.writeLine(name);
}
var name = args[0];
var binName = args[1];
var rest = args.slice(2);
return true;
});
var result = ServiceManager.instance.register(name, binName, rest);
return handleResult(result);
},"Register a new service");
registerSyncSubcommand("enable", (args) -> {
ServiceManager.enable(args.getString("name"));
return true;
}, [String("name")]);
}
registerSyncSubcommand("unregister", (args) ->{
if (args.length < 2) {
return false;
}
var name = args[0];
var result = ServiceManager.instance.unregister(name);
return handleResult(result);
},"Unregister a service");
registerSyncSubcommand("list", (args) ->{
var list = ServiceManager.instance.listRunning();
for (name in list) {
this.handle.writeLine(name);
}
return true;
},"List all services");
registerSyncSubcommand("enable", (args) ->{
if (args.length < 1) {
return false;
}
ServiceManager.instance.enable(args[0]);
return true;
},"Enable a service");
}
private function handleResult(res: Outcome<Noise,String>): Bool {
switch (res) {
case Success(_):
return true;
case Failure(e):
this.handle.write(e);
return false;
}
}
private function handleResult(res:Outcome<Noise, String>):Bool {
switch (res) {
case Success(_):
return true;
case Failure(e):
this.handle.write(e);
return false;
}
}
}

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,60 +1,104 @@
package bin;
import kernel.log.Log;
import lib.MathI;
import kernel.EndOfLoop;
import lua.NativeStringTools;
import kernel.binstore.BinStore;
import kernel.ps.ProcessHandle;
import kernel.ps.Process;
import kernel.ps.IProcess;
import kernel.ps.ProcessManager;
import lib.Color;
import kernel.ui.WindowContext;
using tink.CoreApi;
class Terminal implements Process {
private var context:WindowContext;
@:build(macros.Binstore.includeBin("Terminal", ["terminal"]))
class Terminal implements IProcess {
private static inline final MAX_BACKLOG:Int = 100;
private var handle:ProcessHandle;
private var ctx:WindowContext;
private var requestRender:() -> Void;
private var input:String = "";
private var backlog:Array<String> = [];
private var handle:ProcessHandle;
private var requestRender: () -> Void;
private var history:Array<String> = [];
private var historyIndex:Int = 0;
private var scrollBack = 0;
private var runningPID:PID = -1;
public function new() {}
public function run(handle: ProcessHandle): Void {
public function run(handle:ProcessHandle):Void {
this.handle = handle;
var statelessContext = handle.createStatelessWindowContext();
this.context = statelessContext.ctx;
this.ctx = statelessContext.ctx;
this.requestRender = statelessContext.requestRender;
statelessContext.setRenderFunc(this.render);
handle.addCallbackLink(this.context.onChar.handle(char -> {
if (this.runningPID > 0) return;
// Add input event handlers
handle.addCallbackLink(this.ctx.onChar.handle(char -> {
if (this.runningPID > 0)
return;
this.input += char;
this.requestRender();
}));
handle.addCallbackLink(this.context.onKey.handle(e -> {
// Add key event handlers
handle.addCallbackLink(this.ctx.onKey.handle(e -> {
switch (e.keyCode) {
case 259: // Backspace
if (this.runningPID > 0) return;
if (this.runningPID > 0)
return;
this.input = this.input.substr(0, this.input.length - 1);
this.requestRender();
case 257: // Enter
if (this.runningPID > 0) return;
if (this.runningPID > 0)
return;
this.backlog.push("> " + this.input);
this.backlog.push("");
var command = this.input;
this.input = "";
this.scrollBack = 0;
this.requestRender();
this.historyIndex = 0;
this.invokeCommand(command);
case 269: // END
this.stopCurrentPS();
case 265: // UP
if (this.historyIndex < this.history.length) {
this.historyIndex++;
this.input = this.history[this.history.length - this.historyIndex];
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();
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() {
@@ -66,42 +110,37 @@ class Terminal implements Process {
}
private function render() {
redrawBacklog();
redrawInput();
}
private function redrawBacklog() {
var size = this.context.getSize();
var size = this.ctx.getSize();
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) {
var line = this.backlog[start + i];
this.context.setCursorPos(0, i);
this.context.clearLine();
this.ctx.setCursorPos(0, i);
this.ctx.clearLine();
if (line != null) {
this.context.write(line);
this.ctx.write(line);
}
}
this.moveCursorToInput();
}
private function redrawInput() {
var size = this.context.getSize();
this.ctx.setCursorPos(0, size.y - 1);
this.ctx.clearLine();
this.context.setCursorPos(0, size.y - 1);
this.context.clearLine();
this.ctx.setTextColor(Color.Blue);
this.ctx.write("> ");
this.context.setTextColor(Color.Blue);
this.context.write("> ");
this.ctx.setTextColor(Color.White);
this.ctx.write(this.input);
this.context.setTextColor(Color.White);
this.context.write(this.input);
this.context.setCursorBlink(true);
if (this.runningPID < 0) {
this.ctx.setCursorBlink(true);
} else {
this.ctx.setCursorBlink(false);
}
}
private function invokeCommand(command:String):Void {
@@ -110,6 +149,13 @@ class Terminal implements Process {
if (args.length == 0) {
return;
}
this.history.push(command);
if (this.history.length > MAX_BACKLOG) {
this.history.shift();
}
var commandName = args[0];
// Handle built-in commands
@@ -120,34 +166,28 @@ class Terminal implements Process {
}
var commandArgs:Array<String> = args.slice(1);
var hadInput = false;
var ps = getProgByName(commandName);
var ps = BinStore.instantiate(commandName);
if (ps == null) {
this.backlog.push("Unknown command: " + commandName);
this.redrawBacklog();
this.redrawInput();
this.requestRender();
return;
}
this.runningPID = ProcessManager.run(ps,{
this.runningPID = ProcessManager.run(ps, {
args: commandArgs,
onWrite: (s:String) -> {
if (s == "") {
return;
}
if (!hadInput) {
// Add a new line, so that the input is not on the same line as the command
this.backlog.push("");
hadInput = true;
}
var splits = s.split("\n");
for (line in s.split("\n")) {
if (line == ""){
this.backlog.push("");
for (i => split in splits) {
if (i == 0) {
this.backlog[this.backlog.length - 1] += split;
} else {
this.backlog[this.backlog.length - 1] += s;
this.backlog.push(split);
}
}
@@ -163,9 +203,12 @@ class Terminal implements Process {
}
});
this.context.setCursorBlink(false);
this.ctx.setCursorBlink(false);
}
/**
Convter a command string into an array of arguments where the first element is the command name
**/
private function parseArgs(command:String):Array<String> {
// TODO: tim and quote handling
return command.split(" ");
@@ -173,20 +216,11 @@ class Terminal implements Process {
private function clear() {
this.backlog = [];
this.redrawBacklog();
}
private function getProgByName(name:String):Process {
var bin = BinStore.instance.getBinByAlias(name);
if (bin == null) {
return null;
}
return Type.createInstance(bin.c,[]);
this.requestRender();
}
private function moveCursorToInput() {
var size = this.context.getSize();
this.context.setCursorPos(this.input.length + 2, size.y - 1);
var size = this.ctx.getSize();
this.ctx.setCursorPos(this.input.length + 2, size.y - 1);
}
}

View File

@@ -1,44 +0,0 @@
package bin;
import lib.CLIAppBase;
using tink.CoreApi;
class Turtle extends CLIAppBase {
public function new() {
registerSyncSubcommand("forward", (args)->{
return perform(kernel.turtle.Turtle.instance.forward());
});
registerSyncSubcommand("back", (args)->{
return perform(kernel.turtle.Turtle.instance.back());
});
registerSyncSubcommand("left", (args)->{
return perform(kernel.turtle.Turtle.instance.turnLeft());
});
registerSyncSubcommand("right", (args)->{
return perform(kernel.turtle.Turtle.instance.turnRight());
});
registerSyncSubcommand("up", (args)->{
return perform(kernel.turtle.Turtle.instance.up());
});
registerSyncSubcommand("down", (args)->{
return perform(kernel.turtle.Turtle.instance.down());
});
}
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");
}
});
}
}

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

@@ -0,0 +1,59 @@
package bin.pathfinder;
import lib.WorldPos;
import lib.ui.elements.IUIElement;
import lib.ui.elements.TextElement;
import lib.ui.elements.RootElement;
import kernel.ui.WindowContext;
import kernel.ps.ProcessHandle;
import kernel.ps.IProcess;
class PFClient implements IProcess {
private var handle:ProcessHandle;
private var ctx:WindowContext;
private var requestRender:Void->Void;
private var root:RootElement;
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 RootElement();
this.root.setTitle("Pathfinder");
this.ctx.delegateEvents(this.root);
this.requestRender();
}
private function render() {
var acc = kernel.gps.GPS.getAccuracy();
var pos:WorldPos = kernel.gps.GPS.getPosition() ?? {x: 0, y: 0, z: 0};
var childre:Array<IUIElement> = [
new TextElement('Acc: ${acc}'),
new TextElement('Pos: X:${pos.x} Y:${pos.y} Z:${pos.z}'),
new TextElement('UPDATE', {
style: {bgColor: Gray},
uiEvents: {
onClick: () -> {
kernel.gps.GPS.locate().handle((pos) -> {
this.requestRender();
});
}
}
}),
];
this.root.setChildren(childre);
this.root.render(ctx.getSize()).renderToContext(ctx);
}
}

View File

@@ -1,74 +0,0 @@
package bin.srsc;
import kernel.net.Package.NetworkID;
import lib.RessourceNames;
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);
}
return RessourceNames.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);
}
return RessourceNames.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);
}
return RessourceNames.unregister(args[0]).map((res) -> {
switch (res) {
case Success(data):
handle.writeLine("Success");
case Failure(error):
handle.writeLine("Error: " + error);
}
return true;
});
}, "<name>");
}
}

View File

@@ -1,32 +0,0 @@
package bin.srsc;
import kernel.net.Package.NetworkID;
typedef GetRequest = {
public var ?type:String;
public var name:String;
}
typedef GetResponse = {
public var success:Bool;
public var netID:NetworkID;
}
typedef RegisterRequest = {
public var ?type:String;
public var name:String;
public var netID:NetworkID;
}
typedef RegisterResponse = {
public var success:Bool;
}
typedef UnregisterRequest = {
public var ?type:String;
public var name:String;
}
typedef UnregisterResponse = {
public var success:Bool;
}

View File

@@ -1,104 +0,0 @@
package bin.srsc;
import kernel.log.Log;
import lib.KVStore;
import bin.srsc.PackageTypes;
import kernel.net.Package;
import kernel.ps.ProcessHandle;
import kernel.ps.Process;
/**
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.
**/
class SiteRessourceController implements Process {
public static inline final SITE_CONTROLLER_RESSOURCE_MANAGER_PROTO:String = "srsc";
private var handle:ProcessHandle;
private var ressources:Map<String, NetworkID>;
public function new() {}
public function run(handle:ProcessHandle) {
this.handle = handle;
load();
// Register proto
kernel.net.Net.instance.registerProto(SITE_CONTROLLER_RESSOURCE_MANAGER_PROTO, this.handleMsg);
}
private function handleMsg(pkg:Package) {
/*
There are 3 types of messages:
- Register a new ressource name
- Unregister a ressource name
- Get the responsible NetID for a ressource name
*/
switch (pkg.data.type) {
case "register":
pkg.respond(handleRegister(pkg.data));
case "unregister":
pkg.respond(handleUnregister(pkg.data));
case "get":
pkg.respond(handleGet(pkg.data));
default:
handle.writeLine("Unknown message type: " + pkg.data.type);
}
}
private function handleGet(data:GetRequest):GetResponse {
var res = get(data.name);
return {success: res != null, netID: res};
}
private function handleRegister(data:RegisterRequest):RegisterResponse {
var res = register(data.name, data.netID);
return {success: res};
}
private function handleUnregister(data:UnregisterRequest):UnregisterResponse {
unregister(data.name);
return {success: true};
}
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;
}
public function unregister(name:String) {
if (ressources.exists(name)) {
ressources.remove(name);
}
save();
}
public function get(name:String):Null<NetworkID> {
return ressources.get(name);
}
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

@@ -0,0 +1,67 @@
package bin.turtle;
import lib.turtle.Helper;
import lib.turtle.TurtleAppBase;
class Excavate extends TurtleAppBase {
private var x:Int = 0;
private var y:Int = 0;
private var z:Int = 0;
public function new() {
super(init, turtleFunction);
}
private function init() {
if (handle.args.length < 2) {
handle.writeLine("Usage: excavate <front> <right> <down>");
handle.close(false);
}
var parsedX = Std.parseInt(handle.args[0]);
var parsedY = Std.parseInt(handle.args[1]);
var parsedZ = Std.parseInt(handle.args[2]);
if (parsedX == null || parsedY == null || parsedZ == null) {
handle.writeLine("Invalid arguments");
handle.close(false);
}
this.x = parsedX;
this.y = parsedY;
this.z = parsedZ;
}
private function turtleFunction() {
for (z in 0...this.z) {
for (y in 0...this.y) {
for (x in 0...this.x) {
kernel.turtle.Turtle.dig(Down);
if (x < this.x - 1) {
kernel.turtle.Turtle.forward();
}
}
if (y < this.y - 1) {
if (y % 2 == 0) {
kernel.turtle.Turtle.turnRight();
kernel.turtle.Turtle.forward();
kernel.turtle.Turtle.turnRight();
} else {
kernel.turtle.Turtle.turnLeft();
kernel.turtle.Turtle.forward();
kernel.turtle.Turtle.turnLeft();
}
}
}
kernel.turtle.Turtle.dig(Down);
kernel.turtle.Turtle.down();
Helper.times(kernel.turtle.Turtle.turnLeft, 2);
}
handle.close(true);
}
}

31
src/bin/turtle/Patrol.hx Normal file
View File

@@ -0,0 +1,31 @@
package bin.turtle;
import kernel.turtle.TurtleMutex;
import kernel.ps.ProcessHandle;
import kernel.ps.IProcess;
class Patrol implements IProcess {
private var handle:ProcessHandle;
public function new() {}
public function run(handle:ProcessHandle) {
this.handle = handle;
if (!handle.claimTurtleMutex()) {
handle.writeLine("Failed to claim turtle mutex");
handle.close();
}
handle.writeLine("Patroling");
TurtleMutex.runInTThread(() -> {
while (true) {
kernel.turtle.Turtle.forward();
kernel.turtle.Turtle.forward();
kernel.turtle.Turtle.forward();
kernel.turtle.Turtle.turnLeft();
}
});
}
}

View File

@@ -1,25 +0,0 @@
package kernel;
@:keep
class DCEHack {
// Dont actually call this
public static function load() {
return [
new bin.Disk(),
new bin.GPS(),
new bin.HelloWorld(),
new bin.KernelLog(),
new bin.LSPS(),
new bin.Net(),
new bin.Redstone(),
new bin.Service(),
new bin.Terminal(),
new bin.Turtle(),
new bin.HelloWorldService(),
new bin.srsc.SiteRessourceController(),
new bin.srsc.CLI(),
new bin.Perf(),
new bin.KSettings(),
];
}
}

View File

@@ -3,22 +3,26 @@ package kernel;
import cc.OS;
/**
Make sure that a function is called at the end of the current event loop.
Like setTimeout(func, 0) in JavaScript.
Make sure that a function is called at the end of the current event loop.
Like setTimeout(func, 0) in JavaScript.
**/
class EndOfLoop {
private static var backlog:Array<Void -> Void> = [];
private static var isQueued = false;
private static var backlog:Array<Void->Void> = [];
private static var isQueued = false;
public static function endOfLoop(func: Void -> Void) {
backlog.push(func);
if (!isQueued) { OS.queueEvent("endofloop", null); }
}
public static function endOfLoop(func:Void->Void) {
backlog.push(func);
if (!isQueued) {
OS.queueEvent("endofloop", null);
}
}
@:allow(kernel.KernelEvents)
private static function run() {
for (func in backlog) { func(); }
backlog = [];
isQueued = false;
}
@:allow(kernel.KernelEvents)
private static function run() {
for (func in backlog) {
func();
}
backlog = [];
isQueued = false;
}
}

View File

@@ -6,15 +6,17 @@ class Entrypoint {
public static function main() {
try {
Init.initKernel();
}catch(e){
} catch (e) {
Log.error('Error in init: ${e.toString()}');
return;
}
try {
Startup.main();
}catch(e){
} catch (e) {
Log.error('Error in startup: ${e.toString()}');
}
KernelEvents.startEventLoop();
}
}

View File

@@ -1,65 +1,44 @@
package kernel;
#if debug
import lib.Debug;
#end
import kernel.service.ServiceManager;
import kernel.binstore.BinStore;
import kernel.gps.INS;
import kernel.fs.FS;
import kernel.gps.GPS;
import kernel.log.Log;
import kernel.turtle.Turtle;
import haxe.MainLoop;
import kernel.net.Routing;
import cc.OS;
import lib.Debug;
import kernel.ui.WindowManager;
import kernel.peripherals.Peripherals.Peripheral;
import kernel.net.Net;
import kernel.DCEHack; // Important for DCE hack
class Init {
@:allow(kernel.KernelEvents)
private static var mainEvent:MainEvent;
public static function initKernel() {
// Init singeltons here because haxe is confused about the order to create them.
Log.instance = new Log();
KernelEvents.instance = new KernelEvents();
Peripheral.instance = new Peripheral();
Log.init();
KernelEvents.init();
WindowManager.instance = new WindowManager();
WindowManager.init();
MainTerm.instance = new MainTerm();
BinStore.instance = new BinStore();
Routing.init();
Net.init();
if (Turtle.isTurtle()){
Turtle.instance = new Turtle();
}
Routing.instance = new Routing();
Net.instance = new Net();
GPS.instance = new GPS();
INS.instance = new INS();
GPS.init();
// Register default terminate handler
KernelEvents.instance.onTerminate.handle(_->{
OS.reboot();
KernelEvents.onTerminate.handle(_ -> {
KernelEvents.shutdown();
});
#if debug
Debug.printBuildInfo();
Routing.instance.init();
#end
if (!FS.exists("/var/ns")) {
FS.makeDir("/var/ns");
}
Init.mainEvent = MainLoop.add(()->{
KernelEvents.instance.startEventLoop();
});
ServiceManager.instance = new ServiceManager();
ServiceManager.init();
}
}

View File

@@ -1,9 +1,10 @@
package kernel;
import haxe.MainLoop;
import cc.OS;
import kernel.turtle.TurtleMutex;
import kernel.turtle.Turtle;
import kernel.peripherals.Peripherals.Peripheral;
import kernel.log.Log;
import lib.Pos;
import lib.ScreenPos;
import cc.HTTP.HTTPResponse;
import lua.TableTools;
import lua.Coroutine;
@@ -19,169 +20,220 @@ class KernelEvents {
/**
Depends on: (Nothing)
**/
public static var instance:KernelEvents;
public static var onAlarm(default, null):Signal<Int>;
public final onAlarm:Signal<Int>;
public final onChar:Signal<String>;
public final onDisk:Signal<String>;
public final onDiskEject:Signal<String>;
public final onHttpCheck:Signal<{url:String, success:Bool, failReason:Any}>;
public final onHttpFailure:Signal<{url:String, failReason:String, handle:HTTPResponse}>;
public final onHttpSuccess:Signal<{url:String, handle:HTTPResponse}>;
public final onKey:Signal<{keyCode:Int, isHeld:Bool}>;
public final onKeyUp:Signal<Int>;
public final onModemMessage:Signal<{
public static var onChar(default, null):Signal<String>;
public static var onDisk(default, null):Signal<String>;
public static var onDiskEject(default, null):Signal<String>;
public static var onHttpCheck(default, null):Signal<{url:String, success:Bool, failReason:Any}>;
public static var onHttpFailure(default, null):Signal<{url:String, failReason:String, handle:HTTPResponse}>;
public static var onHttpSuccess(default, null):Signal<{url:String, handle:HTTPResponse}>;
public static var onKey(default, null):Signal<{keyCode:Int, isHeld:Bool}>;
public static var onKeyUp(default, null):Signal<Int>;
public static var onModemMessage(default, null):Signal<{
addr:String,
channel:Int,
replyChannel:Int,
message:Dynamic,
distance:Null<Float>
}>;
public final onMonitorResize:Signal<String>;
public final onMonitorTouch:Signal<{addr:String, pos:Pos}>;
public final onMouseClick:Signal<{button:ButtonType, pos:Pos}>;
public final onMouseDrag:Signal<{button:ButtonType, pos:Pos}>;
public final onMouseScroll:Signal<{dir:Int, pos:Pos}>;
public final onMouseUp:Signal<{button:ButtonType, pos:Pos}>;
public final onPaste:Signal<String>;
public final onPeripheral:Signal<String>;
public final onPeripheralDetach:Signal<String>;
public final onRedstone:Signal<Noise>;
public final onSpeakerAudioEmpty:Signal<String>;
public final onTaskComplete:Signal<{id:Int, success:Bool, failedReason:String}>;
public final onTermResize:Signal<Noise>;
public final onTerminate:Signal<Noise>;
public final onTimer:Signal<Int>;
public final onTurtleInventory:Signal<Noise>;
public final onWebsocketClose:Signal<String>;
public final onWebsocketFailure:Signal<{url:String, failReason:String}>;
public final onWebsocketMessage:Signal<{url:String, message:String, isBinary:Bool}>;
public final onWebsocketSuccess:Signal<{url:String, handle:Any}>;
public static var onMonitorResize(default, null):Signal<String>;
public static var onMonitorTouch(default, null):Signal<{addr:String, pos:ScreenPos}>;
public static var onMouseClick(default, null):Signal<{button:ButtonType, pos:ScreenPos}>;
public static var onMouseDrag(default, null):Signal<{button:ButtonType, pos:ScreenPos}>;
public static var onMouseScroll(default, null):Signal<{dir:Int, pos:ScreenPos}>;
public static var onMouseUp(default, null):Signal<{button:ButtonType, pos:ScreenPos}>;
public static var onPaste(default, null):Signal<String>;
public static var onPeripheral(default, null):Signal<String>;
public static var onPeripheralDetach(default, null):Signal<String>;
public static var onRedstone(default, null):Signal<Noise>;
public static var onSpeakerAudioEmpty(default, null):Signal<String>;
public static var onTaskComplete(default, null):Signal<{id:Int, success:Bool, failedReason:String}>;
public static var onTermResize(default, null):Signal<Noise>;
public static var onTerminate(default, null):Signal<Noise>;
public static var onTimer(default, null):Signal<Int>;
public static var onTurtleInventory(default, null):Signal<Noise>;
public static var onWebsocketClose(default, null):Signal<String>;
public static var onWebsocketFailure(default, null):Signal<{url:String, failReason:String}>;
public static var onWebsocketMessage(default, null):Signal<{url:String, message:String, isBinary:Bool}>;
public static var onWebsocketSuccess(default, null):Signal<{url:String, handle:Any}>;
private final onAlarmTrigger:SignalTrigger<Int> = Signal.trigger();
private final onCharTrigger:SignalTrigger<String> = Signal.trigger();
private final onDiskTrigger:SignalTrigger<String> = Signal.trigger();
private final onDiskEjectTrigger:SignalTrigger<String> = Signal.trigger();
private final onHttpCheckTrigger:SignalTrigger<{url:String, success:Bool, failReason:Any}> = Signal.trigger();
private final onHttpFailureTrigger:SignalTrigger<{url:String, failReason:String, handle:HTTPResponse}> = Signal.trigger();
private final onHttpSuccessTrigger:SignalTrigger<{url:String, handle:HTTPResponse}> = Signal.trigger();
private final onKeyTrigger:SignalTrigger<{keyCode:Int, isHeld:Bool}> = Signal.trigger();
private final onKeyUpTrigger:SignalTrigger<Int> = Signal.trigger();
private final onModemMessageTrigger:SignalTrigger<{
private static final onAlarmTrigger:SignalTrigger<Int> = Signal.trigger();
private static final onCharTrigger:SignalTrigger<String> = Signal.trigger();
private static final onDiskTrigger:SignalTrigger<String> = Signal.trigger();
private static final onDiskEjectTrigger:SignalTrigger<String> = Signal.trigger();
private static final onHttpCheckTrigger:SignalTrigger<{url:String, success:Bool, failReason:Any}> = Signal.trigger();
private static final onHttpFailureTrigger:SignalTrigger<{url:String, failReason:String, handle:HTTPResponse}> = Signal.trigger();
private static final onHttpSuccessTrigger:SignalTrigger<{url:String, handle:HTTPResponse}> = Signal.trigger();
private static final onKeyTrigger:SignalTrigger<{keyCode:Int, isHeld:Bool}> = Signal.trigger();
private static final onKeyUpTrigger:SignalTrigger<Int> = Signal.trigger();
private static final onModemMessageTrigger:SignalTrigger<{
addr:String,
channel:Int,
replyChannel:Int,
message:Dynamic,
distance:Null<Float>
}> = Signal.trigger();
private final onMonitorResizeTrigger:SignalTrigger<String> = Signal.trigger();
private final onMonitorTouchTrigger:SignalTrigger<{addr:String, pos:Pos}> = Signal.trigger();
private final onMouseClickTrigger:SignalTrigger<{button:ButtonType, pos:Pos}> = Signal.trigger();
private final onMouseDragTrigger:SignalTrigger<{button:ButtonType, pos:Pos}> = Signal.trigger();
private final onMouseScrollTrigger:SignalTrigger<{dir:Int, pos:Pos}> = Signal.trigger();
private final onMouseUpTrigger:SignalTrigger<{button:ButtonType, pos:Pos}> = Signal.trigger();
private final onPasteTrigger:SignalTrigger<String> = Signal.trigger();
private final onPeripheralTrigger:SignalTrigger<String> = Signal.trigger();
private final onPeripheralDetachTrigger:SignalTrigger<String> = Signal.trigger();
private final onRednetMessageTrigger:SignalTrigger<{sender:Int, message:Any, protocol:Any}> = Signal.trigger();
private final onRedstoneTrigger:SignalTrigger<Noise> = Signal.trigger();
private final onSpeakerAudioEmptyTrigger:SignalTrigger<String> = Signal.trigger();
private final onTaskCompleteTrigger:SignalTrigger<{id:Int, success:Bool, failedReason:String}> = Signal.trigger();
private final onTermResizeTrigger:SignalTrigger<Noise> = Signal.trigger();
private final onTerminateTrigger:SignalTrigger<Noise> = Signal.trigger();
private final onTimerTrigger:SignalTrigger<Int> = Signal.trigger();
private final onTurtleInventoryTrigger:SignalTrigger<Noise> = Signal.trigger();
private final onWebsocketCloseTrigger:SignalTrigger<String> = Signal.trigger();
private final onWebsocketFailureTrigger:SignalTrigger<{url:String, failReason:String}> = Signal.trigger();
private final onWebsocketMessageTrigger:SignalTrigger<{url:String, message:String, isBinary:Bool}> = Signal.trigger();
private final onWebsocketSuccessTrigger:SignalTrigger<{url:String, handle:Any}> = Signal.trigger();
private static final onMonitorResizeTrigger:SignalTrigger<String> = Signal.trigger();
private static final onMonitorTouchTrigger:SignalTrigger<{addr:String, pos:ScreenPos}> = Signal.trigger();
private static final onMouseClickTrigger:SignalTrigger<{button:ButtonType, pos:ScreenPos}> = Signal.trigger();
private static final onMouseDragTrigger:SignalTrigger<{button:ButtonType, pos:ScreenPos}> = Signal.trigger();
private static final onMouseScrollTrigger:SignalTrigger<{dir:Int, pos:ScreenPos}> = Signal.trigger();
private static final onMouseUpTrigger:SignalTrigger<{button:ButtonType, pos:ScreenPos}> = Signal.trigger();
private static final onPasteTrigger:SignalTrigger<String> = Signal.trigger();
private static final onPeripheralTrigger:SignalTrigger<String> = Signal.trigger();
private static final onPeripheralDetachTrigger:SignalTrigger<String> = Signal.trigger();
private static final onRednetMessageTrigger:SignalTrigger<{sender:Int, message:Any, protocol:Any}> = Signal.trigger();
private static final onRedstoneTrigger:SignalTrigger<Noise> = Signal.trigger();
private static final onSpeakerAudioEmptyTrigger:SignalTrigger<String> = Signal.trigger();
private static final onTaskCompleteTrigger:SignalTrigger<{id:Int, success:Bool, failedReason:String}> = Signal.trigger();
private static final onTermResizeTrigger:SignalTrigger<Noise> = Signal.trigger();
private static final onTerminateTrigger:SignalTrigger<Noise> = Signal.trigger();
private static final onTimerTrigger:SignalTrigger<Int> = Signal.trigger();
private static final onTurtleInventoryTrigger:SignalTrigger<Noise> = Signal.trigger();
private static final onWebsocketCloseTrigger:SignalTrigger<String> = Signal.trigger();
private static final onWebsocketFailureTrigger:SignalTrigger<{url:String, failReason:String}> = Signal.trigger();
private static final onWebsocketMessageTrigger:SignalTrigger<{url:String, message:String, isBinary:Bool}> = Signal.trigger();
private static final onWebsocketSuccessTrigger:SignalTrigger<{url:String, handle:Any}> = Signal.trigger();
private var stopLoop:Bool = false;
private static var stopLoop:Bool = false;
private static var turtleCoroutine:Coroutine<Dynamic>;
@:allow(kernel.Init)
private function new() {
this.onAlarm = onAlarmTrigger.asSignal();
this.onChar = onCharTrigger.asSignal();
this.onDisk = onDiskTrigger.asSignal();
this.onDiskEject = onDiskEjectTrigger.asSignal();
this.onHttpCheck = onHttpCheckTrigger.asSignal();
this.onHttpFailure = onHttpFailureTrigger.asSignal();
this.onHttpSuccess = onHttpSuccessTrigger.asSignal();
this.onKey = onKeyTrigger.asSignal();
this.onKeyUp = onKeyUpTrigger.asSignal();
this.onModemMessage = onModemMessageTrigger.asSignal();
this.onMonitorResize = onMonitorResizeTrigger.asSignal();
this.onMonitorTouch = onMonitorTouchTrigger.asSignal();
this.onMouseClick = onMouseClickTrigger.asSignal();
this.onMouseDrag = onMouseDragTrigger.asSignal();
this.onMouseScroll = onMouseScrollTrigger.asSignal();
this.onMouseUp = onMouseUpTrigger.asSignal();
this.onPaste = onPasteTrigger.asSignal();
this.onPeripheral = onPeripheralTrigger.asSignal();
this.onPeripheralDetach = onPeripheralDetachTrigger.asSignal();
this.onRedstone = onRedstoneTrigger.asSignal();
this.onSpeakerAudioEmpty = onSpeakerAudioEmptyTrigger.asSignal();
this.onTaskComplete = onTaskCompleteTrigger.asSignal();
this.onTermResize = onTermResizeTrigger.asSignal();
this.onTerminate = onTerminateTrigger.asSignal();
this.onTimer = onTimerTrigger.asSignal();
this.onTurtleInventory = onTurtleInventoryTrigger.asSignal();
this.onWebsocketClose = onWebsocketCloseTrigger.asSignal();
this.onWebsocketFailure = onWebsocketFailureTrigger.asSignal();
this.onWebsocketMessage = onWebsocketMessageTrigger.asSignal();
this.onWebsocketSuccess = onWebsocketSuccessTrigger.asSignal();
private static function init() {
onAlarm = onAlarmTrigger.asSignal();
onChar = onCharTrigger.asSignal();
onDisk = onDiskTrigger.asSignal();
onDiskEject = onDiskEjectTrigger.asSignal();
onHttpCheck = onHttpCheckTrigger.asSignal();
onHttpFailure = onHttpFailureTrigger.asSignal();
onHttpSuccess = onHttpSuccessTrigger.asSignal();
onKey = onKeyTrigger.asSignal();
onKeyUp = onKeyUpTrigger.asSignal();
onModemMessage = onModemMessageTrigger.asSignal();
onMonitorResize = onMonitorResizeTrigger.asSignal();
onMonitorTouch = onMonitorTouchTrigger.asSignal();
onMouseClick = onMouseClickTrigger.asSignal();
onMouseDrag = onMouseDragTrigger.asSignal();
onMouseScroll = onMouseScrollTrigger.asSignal();
onMouseUp = onMouseUpTrigger.asSignal();
onPaste = onPasteTrigger.asSignal();
onPeripheral = onPeripheralTrigger.asSignal();
onPeripheralDetach = onPeripheralDetachTrigger.asSignal();
onRedstone = onRedstoneTrigger.asSignal();
onSpeakerAudioEmpty = onSpeakerAudioEmptyTrigger.asSignal();
onTaskComplete = onTaskCompleteTrigger.asSignal();
onTermResize = onTermResizeTrigger.asSignal();
onTerminate = onTerminateTrigger.asSignal();
onTimer = onTimerTrigger.asSignal();
onTurtleInventory = onTurtleInventoryTrigger.asSignal();
onWebsocketClose = onWebsocketCloseTrigger.asSignal();
onWebsocketFailure = onWebsocketFailureTrigger.asSignal();
onWebsocketMessage = onWebsocketMessageTrigger.asSignal();
onWebsocketSuccess = onWebsocketSuccessTrigger.asSignal();
}
/**
Start pulling events. Blocking.
**/
@:allow(kernel.Init)
private function startEventLoop() {
@:allow(kernel.Entrypoint)
private static function startEventLoop() {
if (Turtle.isTurtle()) {
turtleLoop();
} else {
runMainLoop();
}
}
@:allow(kernel.turtle.TurtleMutex)
private static function startTurtleCoroutine() {
turtleCoroutine = Coroutine.create(runTurtleLoop);
Coroutine.resume(turtleCoroutine);
}
private static function turtleLoop() {
startTurtleCoroutine();
while (!stopLoop) {
var eventData = pullEvents();
if (eventData[1] == "turtle_response" || eventData[1] == "tthread") {
var result = Coroutine.resume(turtleCoroutine, TableTools.unpack(eventData));
if (!result.success) {
Log.error('Error while running turtle thread: ${result.result}');
}
if (Coroutine.status(turtleCoroutine) == Dead) {
Log.error('Turtle thread died');
}
} else {
fireSignal(eventData[1], eventData);
}
}
}
private static function runTurtleLoop() {
while (!stopLoop) {
if ((TableTools.pack(Coroutine.yield()))[1] != "tthread")
continue;
if (stopLoop)
continue;
TurtleMutex.runThreadFunc();
}
}
private static function runMainLoop() {
while (!stopLoop) {
var event:Table<Int, Dynamic> = pullEvents();
var eventName:String = event[1];
try {
fireSignal(eventName,event);
}catch(e:Dynamic) {
fireSignal(eventName, event);
} catch (e:Dynamic) {
Log.error('Error while handling event: $eventName: ${e}');
}
}
}
public function shutdown() {
public static function shutdown() {
// clearing screens
for (screen in Peripheral.getAllScreens()) {
screen.reset();
}
Log.info('Shutting down event loop');
this.stopLoop = true;
stopLoop = true;
MainTerm.instance.reset();
Init.mainEvent.stop();
}
private function pullEvents():Table<Int, Dynamic> {
private static function pullEvents():Table<Int, Dynamic> {
return cast TableTools.pack(Coroutine.yield(null));
}
private function fireSignal(eventName: String,event:Table<Int, Dynamic> ) {
private static function fireSignal(eventName:String, event:Table<Int, Dynamic>) {
switch eventName {
case "alarm":
this.onAlarmTrigger.trigger(event[2]);
onAlarmTrigger.trigger(event[2]);
case "char":
this.onCharTrigger.trigger(event[2]);
onCharTrigger.trigger(event[2]);
case "disk":
this.onDiskTrigger.trigger(event[2]);
onDiskTrigger.trigger(event[2]);
case "disk_eject":
this.onDiskEjectTrigger.trigger(event[2]);
onDiskEjectTrigger.trigger(event[2]);
case "http_check":
this.onHttpCheckTrigger.trigger({url: event[2], success: event[3], failReason: event[4]});
onHttpCheckTrigger.trigger({url: event[2], success: event[3], failReason: event[4]});
case "http_failure":
this.onHttpFailureTrigger.trigger({url: event[2], failReason: event[3], handle: event[4]});
onHttpFailureTrigger.trigger({url: event[2], failReason: event[3], handle: event[4]});
case "http_success":
this.onHttpSuccessTrigger.trigger({url: event[2], handle: event[3]});
onHttpSuccessTrigger.trigger({url: event[2], handle: event[3]});
case "key":
this.onKeyTrigger.trigger({keyCode: event[2], isHeld: event[3]});
onKeyTrigger.trigger({keyCode: event[2], isHeld: event[3]});
case "key_up":
this.onKeyUpTrigger.trigger(event[2]);
onKeyUpTrigger.trigger(event[2]);
case "modem_message":
this.onModemMessageTrigger.trigger({
onModemMessageTrigger.trigger({
addr: event[2],
channel: event[3],
replyChannel: event[4],
@@ -189,45 +241,45 @@ class KernelEvents {
distance: event[6]
});
case "monitor_resize":
this.onMonitorResizeTrigger.trigger(event[2]);
onMonitorResizeTrigger.trigger(event[2]);
case "monitor_touch":
this.onMonitorTouchTrigger.trigger({addr: event[2], pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
onMonitorTouchTrigger.trigger({addr: event[2], pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
case "mouse_click":
this.onMouseClickTrigger.trigger({button: ccButtonToEnum(event[2]), pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
onMouseClickTrigger.trigger({button: ccButtonToEnum(event[2]), pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
case "mouse_drag":
this.onMouseDragTrigger.trigger({button: ccButtonToEnum(event[2]), pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
onMouseDragTrigger.trigger({button: ccButtonToEnum(event[2]), pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
case "mouse_scroll":
this.onMouseScrollTrigger.trigger({dir: event[2], pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
onMouseScrollTrigger.trigger({dir: event[2], pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
case "mouse_up":
this.onMouseUpTrigger.trigger({button: ccButtonToEnum(event[2]), pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
onMouseUpTrigger.trigger({button: ccButtonToEnum(event[2]), pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
case "paste":
this.onPasteTrigger.trigger(event[2]);
onPasteTrigger.trigger(event[2]);
case "peripheral":
this.onPeripheralTrigger.trigger(event[2]);
onPeripheralTrigger.trigger(event[2]);
case "peripheral_detach":
this.onPeripheralDetachTrigger.trigger(event[2]);
onPeripheralDetachTrigger.trigger(event[2]);
case "redstone":
this.onRedstoneTrigger.trigger(null);
onRedstoneTrigger.trigger(null);
case "speaker_audio_empty":
this.onSpeakerAudioEmptyTrigger.trigger(event[2]);
onSpeakerAudioEmptyTrigger.trigger(event[2]);
case "task_complete":
this.onTaskCompleteTrigger.trigger({id: event[2], success: event[3], failedReason: event[4]});
onTaskCompleteTrigger.trigger({id: event[2], success: event[3], failedReason: event[4]});
case "term_resize":
this.onTermResizeTrigger.trigger(null);
onTermResizeTrigger.trigger(null);
case "terminate":
this.onTerminateTrigger.trigger(null);
onTerminateTrigger.trigger(null);
case "timer":
this.onTimerTrigger.trigger(event[2]);
onTimerTrigger.trigger(event[2]);
case "turtle_inventory":
this.onTurtleInventoryTrigger.trigger(null);
onTurtleInventoryTrigger.trigger(null);
case "websocket_closed":
this.onWebsocketCloseTrigger.trigger(event[2]);
onWebsocketCloseTrigger.trigger(event[2]);
case "websocket_failure":
this.onWebsocketFailureTrigger.trigger({url: event[2], failReason: event[3]});
onWebsocketFailureTrigger.trigger({url: event[2], failReason: event[3]});
case "websocket_message":
this.onWebsocketMessageTrigger.trigger({url: event[2], message: event[3], isBinary: event[4]});
onWebsocketMessageTrigger.trigger({url: event[2], message: event[3], isBinary: event[4]});
case "websocket_success":
this.onWebsocketSuccessTrigger.trigger({url: event[2], handle: event[3]});
onWebsocketSuccessTrigger.trigger({url: event[2], handle: event[3]});
case "endofloop":
EndOfLoop.run();
default:
@@ -249,36 +301,66 @@ class KernelEvents {
}
@:allow(lib.Debug)
private function printListenerCount() {
if (onAlarmTrigger.getLength() > 0) Log.debug("onAlarm: " + onAlarmTrigger.getLength());
if (onCharTrigger.getLength() > 0) Log.debug("onChar: " + onCharTrigger.getLength());
if (onDiskTrigger.getLength() > 0) Log.debug("onDisk: " + onDiskTrigger.getLength());
if (onDiskEjectTrigger.getLength() > 0) Log.debug("onDiskEject: " + onDiskEjectTrigger.getLength());
if (onHttpCheckTrigger.getLength() > 0) Log.debug("onHttpCheck: " + onHttpCheckTrigger.getLength());
if (onHttpFailureTrigger.getLength() > 0) Log.debug("onHttpFailure: " + onHttpFailureTrigger.getLength());
if (onHttpSuccessTrigger.getLength() > 0) Log.debug("onHttpSuccess: " + onHttpSuccessTrigger.getLength());
if (onKeyTrigger.getLength() > 0) Log.debug("onKey: " + onKeyTrigger.getLength());
if (onKeyUpTrigger.getLength() > 0) Log.debug("onKeyUp: " + onKeyUpTrigger.getLength());
if (onModemMessageTrigger.getLength() > 0) Log.debug("onModemMessage: " + onModemMessageTrigger.getLength());
if (onMonitorResizeTrigger.getLength() > 0) Log.debug("onMonitorResize: " + onMonitorResizeTrigger.getLength());
if (onMonitorTouchTrigger.getLength() > 0) Log.debug("onMonitorTouch: " + onMonitorTouchTrigger.getLength());
if (onMouseClickTrigger.getLength() > 0) Log.debug("onMouseClick: " + onMouseClickTrigger.getLength());
if (onMouseDragTrigger.getLength() > 0) Log.debug("onMouseDrag: " + onMouseDragTrigger.getLength());
if (onMouseScrollTrigger.getLength() > 0) Log.debug("onMouseScroll: " + onMouseScrollTrigger.getLength());
if (onMouseUpTrigger.getLength() > 0) Log.debug("onMouseUp: " + onMouseUpTrigger.getLength());
if (onPasteTrigger.getLength() > 0) Log.debug("onPaste: " + onPasteTrigger.getLength());
if (onPeripheralTrigger.getLength() > 0) Log.debug("onPeripheral: " + onPeripheralTrigger.getLength());
if (onPeripheralDetachTrigger.getLength() > 0) Log.debug("onPeripheralDetach: " + onPeripheralDetachTrigger.getLength());
if (onRedstoneTrigger.getLength() > 0) Log.debug("onRedstone: " + onRedstoneTrigger.getLength());
if (onSpeakerAudioEmptyTrigger.getLength() > 0) Log.debug("onSpeakerAudioEmpty: " + onSpeakerAudioEmptyTrigger.getLength());
if (onTaskCompleteTrigger.getLength() > 0) Log.debug("onTaskComplete: " + onTaskCompleteTrigger.getLength());
if (onTermResizeTrigger.getLength() > 0) Log.debug("onTermResize: " + onTermResizeTrigger.getLength());
if (onTerminateTrigger.getLength() > 0) Log.debug("onTerminate: " + onTerminateTrigger.getLength());
if (onTimerTrigger.getLength() > 0) Log.debug("onTimer: " + onTimerTrigger.getLength());
if (onTurtleInventoryTrigger.getLength() > 0) Log.debug("onTurtleInventory: " + onTurtleInventoryTrigger.getLength());
if (onWebsocketCloseTrigger.getLength() > 0) Log.debug("onWebsocketClose: " + onWebsocketCloseTrigger.getLength());
if (onWebsocketFailureTrigger.getLength() > 0) Log.debug("onWebsocketFailure: " + onWebsocketFailureTrigger.getLength());
if (onWebsocketMessageTrigger.getLength() > 0) Log.debug("onWebsocketMessage: " + onWebsocketMessageTrigger.getLength());
if (onWebsocketSuccessTrigger.getLength() > 0) Log.debug("onWebsocketSuccess: " + onWebsocketSuccessTrigger.getLength());
private static function printListenerCount() {
if (onAlarmTrigger.getLength() > 0)
Log.debug("onAlarm: " + onAlarmTrigger.getLength());
if (onCharTrigger.getLength() > 0)
Log.debug("onChar: " + onCharTrigger.getLength());
if (onDiskTrigger.getLength() > 0)
Log.debug("onDisk: " + onDiskTrigger.getLength());
if (onDiskEjectTrigger.getLength() > 0)
Log.debug("onDiskEject: " + onDiskEjectTrigger.getLength());
if (onHttpCheckTrigger.getLength() > 0)
Log.debug("onHttpCheck: " + onHttpCheckTrigger.getLength());
if (onHttpFailureTrigger.getLength() > 0)
Log.debug("onHttpFailure: " + onHttpFailureTrigger.getLength());
if (onHttpSuccessTrigger.getLength() > 0)
Log.debug("onHttpSuccess: " + onHttpSuccessTrigger.getLength());
if (onKeyTrigger.getLength() > 0)
Log.debug("onKey: " + onKeyTrigger.getLength());
if (onKeyUpTrigger.getLength() > 0)
Log.debug("onKeyUp: " + onKeyUpTrigger.getLength());
if (onModemMessageTrigger.getLength() > 0)
Log.debug("onModemMessage: " + onModemMessageTrigger.getLength());
if (onMonitorResizeTrigger.getLength() > 0)
Log.debug("onMonitorResize: " + onMonitorResizeTrigger.getLength());
if (onMonitorTouchTrigger.getLength() > 0)
Log.debug("onMonitorTouch: " + onMonitorTouchTrigger.getLength());
if (onMouseClickTrigger.getLength() > 0)
Log.debug("onMouseClick: " + onMouseClickTrigger.getLength());
if (onMouseDragTrigger.getLength() > 0)
Log.debug("onMouseDrag: " + onMouseDragTrigger.getLength());
if (onMouseScrollTrigger.getLength() > 0)
Log.debug("onMouseScroll: " + onMouseScrollTrigger.getLength());
if (onMouseUpTrigger.getLength() > 0)
Log.debug("onMouseUp: " + onMouseUpTrigger.getLength());
if (onPasteTrigger.getLength() > 0)
Log.debug("onPaste: " + onPasteTrigger.getLength());
if (onPeripheralTrigger.getLength() > 0)
Log.debug("onPeripheral: " + onPeripheralTrigger.getLength());
if (onPeripheralDetachTrigger.getLength() > 0)
Log.debug("onPeripheralDetach: " + onPeripheralDetachTrigger.getLength());
if (onRedstoneTrigger.getLength() > 0)
Log.debug("onRedstone: " + onRedstoneTrigger.getLength());
if (onSpeakerAudioEmptyTrigger.getLength() > 0)
Log.debug("onSpeakerAudioEmpty: " + onSpeakerAudioEmptyTrigger.getLength());
if (onTaskCompleteTrigger.getLength() > 0)
Log.debug("onTaskComplete: " + onTaskCompleteTrigger.getLength());
if (onTermResizeTrigger.getLength() > 0)
Log.debug("onTermResize: " + onTermResizeTrigger.getLength());
if (onTerminateTrigger.getLength() > 0)
Log.debug("onTerminate: " + onTerminateTrigger.getLength());
if (onTimerTrigger.getLength() > 0)
Log.debug("onTimer: " + onTimerTrigger.getLength());
if (onTurtleInventoryTrigger.getLength() > 0)
Log.debug("onTurtleInventory: " + onTurtleInventoryTrigger.getLength());
if (onWebsocketCloseTrigger.getLength() > 0)
Log.debug("onWebsocketClose: " + onWebsocketCloseTrigger.getLength());
if (onWebsocketFailureTrigger.getLength() > 0)
Log.debug("onWebsocketFailure: " + onWebsocketFailureTrigger.getLength());
if (onWebsocketMessageTrigger.getLength() > 0)
Log.debug("onWebsocketMessage: " + onWebsocketMessageTrigger.getLength());
if (onWebsocketSuccessTrigger.getLength() > 0)
Log.debug("onWebsocketSuccess: " + onWebsocketSuccessTrigger.getLength());
}
}

View File

@@ -6,51 +6,53 @@ import lib.KVStore;
import cc.Settings;
class KernelSettings {
private static inline function setAllowStartup(value: Bool) {
Settings.set("shell.allow_startup", value);
}
private static inline function setAllowStartup(value:Bool) {
Settings.set("shell.allow_startup", value);
}
private static inline function setAllowStartupFromFloppy(value: Bool) {
Settings.set("shell.allow_disk_startup", value);
}
private static inline function setAllowStartupFromFloppy(value:Bool) {
Settings.set("shell.allow_disk_startup", value);
}
private static function set(name: String, value: Dynamic) {
var kvstore = new KVStore("kernel");
kvstore.set(name, value);
kvstore.save();
}
private static function set(name:String, value:Dynamic) {
var kvstore = new KVStore("kernel");
kvstore.set(name, value);
kvstore.save();
}
private static function get(name: String): Null<Dynamic> {
var kvstore = new KVStore("kernel");
return kvstore.get(name);
}
private static function get(name:String):Null<Dynamic> {
var kvstore = new KVStore("kernel");
return kvstore.get(name);
}
public static var hostname(get,set): String;
private static var _hostname:String = get("hostname");
private static inline function get_hostname():String {
public static var hostname(get, set):String;
private static var _hostname:String = get("hostname");
private static inline function get_hostname():String {
return _hostname;
}
private static inline function set_hostname(value:String):String {
OS.setComputerLabel(value);
set("hostname", value);
_hostname = value;
return value;
private static inline function set_hostname(value:String):String {
OS.setComputerLabel(value);
set("hostname", value);
_hostname = value;
return value;
}
public static var siteController(get,set): NetworkID;
private static var _siteController:NetworkID = get("siteController");
private static function get_siteController():NetworkID {
return _siteController;
public static var nameServer(get, set):NetworkID;
private static var _nameServer:NetworkID = get("nameServer");
private static function get_nameServer():NetworkID {
return _nameServer;
}
private static function set_siteController(value:NetworkID):NetworkID {
if (value == null) {
return get_siteController();
}
private static function set_nameServer(value:NetworkID):NetworkID {
if (value == null) {
return get_nameServer();
}
set("siteController", value);
_siteController = value;
return value;
set("nameServer", value);
_nameServer = value;
return value;
}
}

View File

@@ -1,7 +1,7 @@
package kernel;
import lib.Pos;
import kernel.ui.TermWriteable;
import lib.ScreenPos;
import kernel.ui.ITermWriteable;
import cc.Term;
import lib.Vec.Vec2;
import lib.Color;
@@ -11,7 +11,7 @@ using tink.CoreApi;
/**
Represents the main computer screen.
**/
class MainTerm implements TermWriteable {
class MainTerm implements ITermWriteable {
/**
Depends on: KernelEvents,
**/
@@ -26,7 +26,7 @@ class MainTerm implements TermWriteable {
this.onResizeTrigger = Signal.trigger();
this.onResize = this.onResizeTrigger.asSignal();
KernelEvents.instance.onTermResize.handle(_ -> {
KernelEvents.onTermResize.handle(_ -> {
onResizeTrigger.trigger(getSize());
});
}
@@ -39,7 +39,7 @@ class MainTerm implements TermWriteable {
Term.scroll(y);
}
public function getCursorPos():Pos {
public function getCursorPos():ScreenPos {
var rtn = Term.getCursorPos();
return {
x: rtn.x - 1,
@@ -60,7 +60,7 @@ class MainTerm implements TermWriteable {
Term.setCursorBlink(blink);
}
public function getSize():Pos {
public function getSize():ScreenPos {
var rtn = Term.getSize();
return {
x: rtn.width,
@@ -100,6 +100,6 @@ class MainTerm implements TermWriteable {
this.setBackgroundColor(Black);
this.setTextColor(White);
this.clear();
this.setCursorPos(0,0);
this.setCursorPos(0, 0);
}
}

View File

@@ -19,7 +19,7 @@ class Timer {
timerID = OS.startTimer(timeout);
this.callback = callback;
timerLink = KernelEvents.instance.onTimer.handle(timerID -> {
timerLink = KernelEvents.onTimer.handle(timerID -> {
if (this.timerID == timerID) {
callback.invoke(null);
timerLink.cancel();

View File

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

View File

@@ -1,65 +1,45 @@
package kernel.binstore;
import bin.KSettings;
import bin.Perf;
import bin.srsc.CLI;
import bin.srsc.SiteRessourceController;
import bin.HelloWorldService;
import bin.Service;
import bin.LSPS;
import bin.Turtle;
import bin.Terminal;
import bin.Redstone;
import bin.Net;
import bin.KernelLog;
import bin.HelloWorld;
import bin.GPS;
import bin.Disk;
import haxe.ds.ReadOnlyArray;
import kernel.ps.IProcess;
import macros.Binstore;
class BinStore {
public static var instance: BinStore;
private static final bins:Array<Bin> = populateStore();
private final store:ReadOnlyArray<Bin> = [
{c: Disk, name: "Disk", aliases: ["disk"]},
{c: GPS, name: "GPS", aliases: ["gps"]},
{c: HelloWorld, name: "HelloWorld", aliases: ["hello"]},
{c: KernelLog, name: "KernelLog", aliases: ["log"]},
{c: Net, name: "Net", aliases: ["net"]},
{c: Redstone, name: "Redstone", aliases: ["redstone","rs"]},
{c: Terminal, name: "Terminal", aliases: ["terminal","term"]},
{c: Turtle, name: "Turtle", aliases: ["turtle"]},
{c: LSPS, name: "PM", aliases: ["lsps"]},
{c: Service, name: "Service", aliases: ["service","srv"]},
{c: HelloWorldService, name: "HelloWorldService", aliases: ["hello-service"] },
{c: SiteRessourceController, name: "SiteRessourceController", aliases: ["srsc"]},
{c: CLI, name: "SRSC CLI", aliases: ["srsc-cli"]},
{c: Perf, name: "Perf", aliases: ["perf"]},
{c: KSettings, name: "KSettings", aliases: ["ksettings","ks"]},
];
private static function populateStore()
return {
var bins:Array<Bin> = [];
Binstore.generateBinStore();
return bins;
}
@:allow(kernel.Init)
private function new() {
public static function instantiate(alias:String):Null<IProcess> {
for (bin in bins) {
for (a in bin.aliases) {
if (a == alias) {
return bin.c();
}
}
}
return null;
}
}
private static function getBinByAlias(alias:String):Null<Bin> {
for (bin in bins) {
for (a in bin.aliases) {
if (a == alias) {
return bin;
}
}
}
return null;
}
public function getBinByName(name:String):Null<Bin> {
for (bin in store) {
if (bin.name == name) {
return bin;
}
}
return null;
}
public function getBinByAlias(alias:String):Null<Bin> {
for (bin in store) {
for (a in bin.aliases) {
if (a == alias) {
return bin;
}
}
}
return null;
}
public static function getNameByAlias(alias:String):Null<String> {
var bin = getBinByAlias(alias);
if (bin == null) {
return null;
}
return bin.name;
}
}

View File

@@ -13,79 +13,79 @@ using lua.Table;
Wrapper to interact with the filesystem.
**/
class FS {
public static inline function list(path: String):ReadOnlyArray<String> {
public static inline function list(path:String):ReadOnlyArray<String> {
return FileSystem.list(path).toArray();
}
public static inline function combine(base: String, part: String): String {
return FileSystem.combine(base,part);
public static inline function combine(base:String, part:String):String {
return FileSystem.combine(base, part);
}
public static inline function getName(path: String): String {
public static inline function getName(path:String):String {
return FileSystem.getName(path);
}
public static inline function getDir(path: String): String {
public static inline function getDir(path:String):String {
return FileSystem.getDir(path);
}
public static inline function getSize(path: String):Int {
public static inline function getSize(path:String):Int {
return FileSystem.getSize(path);
}
public static inline function exists(path: String):Bool {
public static inline function exists(path:String):Bool {
return FileSystem.exists(path);
}
public static inline function isDir(path: String): Bool {
public static inline function isDir(path:String):Bool {
return FileSystem.isDir(path);
}
public static inline function isReadOnly(path: String):Bool {
public static inline function isReadOnly(path:String):Bool {
return FileSystem.isReadOnly(path);
}
public static inline function makeDir(path: String):Void {
public static inline function makeDir(path:String):Void {
FileSystem.makeDir(path);
}
public static inline function move(src:String,dest:String):Void {
FileSystem.move(src,dest);
public static inline function move(src:String, dest:String):Void {
FileSystem.move(src, dest);
}
public static inline function copy(src:String,dest:String):Void {
FileSystem.copy(src,dest);
public static inline function copy(src:String, dest:String):Void {
FileSystem.copy(src, dest);
}
public static inline function delete(path: String):Void {
public static inline function delete(path:String):Void {
FileSystem.delete(path);
}
public static inline function openRead(path:String): ReadHandle{
return FileSystem.open(path,Read);
public static inline function openRead(path:String):ReadHandle {
return FileSystem.open(path, Read);
}
public static inline function openWrite(path: String): WriteHandle {
return FileSystem.open(path,Write);
public static inline function openWrite(path:String):WriteHandle {
return FileSystem.open(path, Write);
}
public static inline function openAppend(path: String): WriteHandle {
return FileSystem.open(path,Append);
public static inline function openAppend(path:String):WriteHandle {
return FileSystem.open(path, Append);
}
public static inline function openReadBinary(path:String): ReadBinaryHandle {
return FileSystem.open(path,BinaryRead);
public static inline function openReadBinary(path:String):ReadBinaryHandle {
return FileSystem.open(path, BinaryRead);
}
public static inline function openWriteBinary(path: String): WriteBinaryHandle {
return FileSystem.open(path,BinaryWrite);
public static inline function openWriteBinary(path:String):WriteBinaryHandle {
return FileSystem.open(path, BinaryWrite);
}
public static inline function openAppendBinary(path: String): WriteBinaryHandle {
return FileSystem.open(path,BinaryAppend);
public static inline function openAppendBinary(path:String):WriteBinaryHandle {
return FileSystem.open(path, BinaryAppend);
}
public static inline function find(pattern: String):ReadOnlyArray<String> {
public static inline function find(pattern:String):ReadOnlyArray<String> {
return FileSystem.find(pattern).toArray();
}
@@ -97,7 +97,7 @@ class FS {
return FileSystem.getCapacity(path);
}
public static inline function attributes(path: String):FileAttributes {
public static inline function attributes(path:String):FileAttributes {
return FileSystem.attributes(path);
}
}

View File

@@ -5,107 +5,103 @@ import cc.FileSystem.FileHandle;
using tink.CoreApi;
abstract ReadHandle(FileHandle) from FileHandle {
public inline function new(handle:FileHandle) {
this = handle;
}
public inline function readLine(?withTrailing:Bool = false):Null<String>{
public inline function readLine(?withTrailing:Bool = false):Null<String> {
return this.readLine(withTrailing);
}
public inline function readAll():Null<String>{
public inline function readAll():Null<String> {
return this.readAll();
}
public inline function read(?count:Int = 1):Null<String>{
public inline function read(?count:Int = 1):Null<String> {
return this.read(count);
}
public inline function close():Void{
public inline function close():Void {
this.close();
}
}
abstract WriteHandle(FileHandle) from FileHandle {
public inline function new(handle:FileHandle) {
this = handle;
}
public inline function write(data:String):Void{
public inline function write(data:String):Void {
this.write(data);
}
public inline function writeLine(data:String):Void{
public inline function writeLine(data:String):Void {
this.writeLine(data);
}
public inline function flush():Void{
public inline function flush():Void {
this.flush();
}
public inline function close():Void{
public inline function close():Void {
this.close();
}
}
abstract ReadBinaryHandle(FileHandle) from FileHandle {
public inline function new(handle:FileHandle) {
this = handle;
}
public inline function readLine(?withTrailing:Bool = false):Null<String>{
public inline function readLine(?withTrailing:Bool = false):Null<String> {
return this.readLine(withTrailing);
}
public inline function readAll():Null<String>{
public inline function readAll():Null<String> {
return this.readAll();
}
public inline function read(count:Int):Null<String>{
public inline function read(count:Int):Null<String> {
return this.read(count);
}
public inline function readByte():Null<Int>{
public inline function readByte():Null<Int> {
return this.read();
}
public inline function seek(?whence:BinarySeekWhence = Current, ?offset:Int):Void{
this.seek(whence,offset);
public inline function seek(?whence:BinarySeekWhence = Current, ?offset:Int):Void {
this.seek(whence, offset);
}
public inline function close():Void{
public inline function close():Void {
this.close();
}
}
abstract WriteBinaryHandle(FileHandle) from FileHandle {
public inline function new(handle:FileHandle) {
this = handle;
}
public inline function write(data: String):Void{
public inline function write(data:String):Void {
this.write(data);
}
// TODO
public inline function writeByte(data: Int):Void{
public inline function writeByte(data:Int):Void {
// this.write(data);
}
public inline function flush():Void{
public inline function flush():Void {
this.flush();
}
public inline function close():Void{
public inline function close():Void {
this.close();
}
public inline function seek(?whence:BinarySeekWhence = Current, ?offset:Int):Void{
this.seek(whence,offset);
public inline function seek(?whence:BinarySeekWhence = Current, ?offset:Int):Void {
this.seek(whence, offset);
}
}

View File

@@ -1,11 +1,12 @@
package kernel.gps;
import lib.SingleTimeoutPromise;
import kernel.log.Log;
import lib.KVStore;
import kernel.net.Net;
import kernel.net.INetworkInterface;
import kernel.net.Package;
import lib.Pos3;
import lib.WorldPos;
using tink.CoreApi;
@@ -15,24 +16,24 @@ using tink.CoreApi;
You need at least 3 computers that know their position to determine the position of the computer.
**/
class GPS {
public static var instance:GPS;
private static inline final TIMEOUT:Int = 1;
private var shouldRespond = true;
private var shouldDoWholeNumberCheck = true;
private var posAccuracy = 0; // 0 = unkown, 1 = (ins,best guess), 2 = (stored/manual,should be right), 3 = (gps,confirmed)
private var cachedPosition:Pos3;
private var lastPositionResponse: Array<{pos:Pos3,dist:Float}> = [];
private static var shouldRespond = 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 cachedPosition:WorldPos;
private static var lastPositionResponse:Array<{pos:WorldPos, dist:Float}> = [];
private var futureResolve: (pos:Null<Pos3>) -> Void = null;
private static final locatePromise:SingleTimeoutPromise<WorldPos> = new SingleTimeoutPromise(TIMEOUT, brodcastPositionRequest);
@:allow(kernel.Init)
private function new() {
this.loadCachedPosition();
private static function init() {
loadCachedPosition();
}
public function setManualPosition(pos:Pos3) {
public static function setManualPosition(pos:WorldPos) {
var kvstore = new KVStore("gps");
kvstore.set("mpos",pos);
kvstore.set("mpos", pos);
kvstore.save();
if (cachedPosition == null) {
@@ -42,52 +43,43 @@ class GPS {
}
@:allow(kernel.gps.INS)
private function setINSPosition(pos:Pos3) {
private static function setINSPosition(pos:WorldPos) {
cachedPosition = pos;
posAccuracy = 1;
}
public function getPosition():Null<Pos3> {
public static function getPosition():Null<WorldPos> {
return cachedPosition;
}
public function getAccuracy():Int {
public static function getAccuracy():Int {
return posAccuracy;
}
public function invalidatePosition() {
public static function invalidatePosition() {
cachedPosition = null;
posAccuracy = 0;
}
public function locate():Future<Null<Pos3>> {
// TODO: implenet a timeout
// TODO: dont send a request twice if the last one is still pending or we moved
return new Future<Null<Pos3>>((resolve)->{
this.futureResolve = resolve;
sendPositionRequest();
return null;
});
public static function locate():Promise<WorldPos> {
return locatePromise.request();
}
private function resolveFuture(pos:Null<Pos3>) {
this.futureResolve(pos);
}
private function persistCachedPositon() {
if (cachedPosition == null) return;
private static function persistCachedPositon() {
if (cachedPosition == null)
return;
var kvstore = new KVStore("gps");
kvstore.set("cpos",cachedPosition);
kvstore.set("cpos", cachedPosition);
kvstore.save();
}
private function loadCachedPosition() {
private static function loadCachedPosition() {
var kvstore = new KVStore("gps");
kvstore.load();
var mPos:Null<Pos3> = kvstore.get("mpos"); // Manual set position
var cPos:Null<Pos3> = kvstore.get("cpos"); // Cached position
var mPos:Null<WorldPos> = kvstore.get("mpos"); // Manual set position
var cPos:Null<WorldPos> = kvstore.get("cpos"); // Cached position
if (mPos != null && cPos != null && mPos == cPos) {
// Both are the same, so we can use the cached position
@@ -96,7 +88,7 @@ class GPS {
return;
}
if (mPos != null && cPos != null && mPos != cPos){
if (mPos != null && cPos != null && mPos != cPos) {
// Both are different, so we can use the manual position
cachedPosition = mPos;
posAccuracy = 1;
@@ -116,51 +108,62 @@ class GPS {
posAccuracy = 2;
return;
}
}
private function sendPositionRequest() {
Net.instance.brodcastGPSRequest();
private static function brodcastPositionRequest() {
Net.brodcastGPSRequest();
}
@:allow(kernel.net.Net)
private function handlePackage(pack:Package, 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) {
case GPSRequest:
if (!shouldRespond) return;
if (posAccuracy < 2) return;
if (cachedPosition == null) return;
if (!shouldRespond)
return;
if (posAccuracy < 2)
return;
if (cachedPosition == null)
return;
var response = new Package(Net.instance.networkID,pack.fromID, pack.msgID, GPSResponse(cachedPosition),null,0);
iface.send(pack.fromID,Net.instance.networkID,response);
case GPSResponse(pos):
if (lastPositionResponse.contains({pos:pos,dist:dist})) return; // Ignore duplicate responses
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);
case GPSResponse(x, y, z):
if (lastPositionResponse.contains({pos: {x: x, y: y, z: z}, dist: dist}))
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
if (lastPositionResponse.length < 4) return; // We need at least 3 responses to calculate the position
if (lastPositionResponse.length < 4)
return; // We need at least 3 responses to calculate the position
var calculatedPosition = calculatePosition();
if (calculatedPosition != null){
if (calculatedPosition != null) {
calculatedPosition = calculatedPosition.round();
}
lastPositionResponse = []; // Reset the response array
if (calculatedPosition == null) return;
if (calculatedPosition == null)
return;
cachedPosition = calculatedPosition;
posAccuracy = 3;
resolveFuture(calculatedPosition);
locatePromise.resolve(calculatedPosition);
default:
}
}
private function calculatePosition():Null<Pos3> {
if (lastPositionResponse.length < 3) return null;
private static function calculatePosition():Null<WorldPos> {
if (lastPositionResponse.length < 3)
return null;
// do a simple trilateration with the last 3 responses for now
var p1 = lastPositionResponse[0].pos;
@@ -171,13 +174,14 @@ class GPS {
var r2 = lastPositionResponse[1].dist;
var r3 = lastPositionResponse[2].dist;
var result = trilateration(p1,p2,p3,r1,r2,r3);
var result = trilateration(p1, p2, p3, r1, r2, r3);
if (result.a.close(result.b)) return result.a;
if (result.a.close(result.b))
return result.a;
// If we have more than 3 responses, we can use the 4th response to determine which of the two positions is correct
// TODO: if this is a pocket computer we cant use this since it can move freely
if (lastPositionResponse.length > 3){
if (lastPositionResponse.length > 3) {
var err1 = Math.abs(result.a.distance(lastPositionResponse[3].pos) - lastPositionResponse[3].dist);
var err2 = Math.abs(result.b.distance(lastPositionResponse[3].pos) - lastPositionResponse[3].dist);
@@ -186,12 +190,15 @@ class GPS {
return null; // The two positions are essentially the same, so we cant determine which one is correct
}
if (err1 < err2) return result.a;
if (err2 < err1) return result.b;
if (err1 < err2)
return result.a;
if (err2 < err1)
return result.b;
}
// last resort, just use the position that is closest to a whole number (whithin a resonable margin of error)
if (!shouldDoWholeNumberCheck) return null;
if (!shouldDoWholeNumberCheck)
return null;
// TODO: mark the position as not so accurate if we use this method
@@ -210,7 +217,7 @@ class GPS {
/**
Determines the position(s) of a point given 3 other points and the distance to each of them.
**/
private 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 a2c = p3 - p1;
@@ -219,7 +226,7 @@ class GPS {
var i = ex.dot(a2c);
var ey = (a2c - ex * i).normalize();
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 y = (r1 * r1 - r3 * r3 - x * x + (x - i) * (x - i) + j * j) / (2 * j);
@@ -229,9 +236,9 @@ class GPS {
var zSquared = r1 * r1 - x * x - y * y;
if (zSquared > 0) {
var z = Math.sqrt(zSquared);
return new Pair(result + (ez * z),result - (ez * z));
return new Pair(result + (ez * z), result - (ez * z));
}
return new Pair(result,result);
return new Pair(result, result);
}
}

View File

@@ -1,53 +1,48 @@
package kernel.gps;
import kernel.log.Log;
import lib.SinglePromise;
import kernel.turtle.Turtle;
import lib.Pos3;
import lib.WorldPos;
using tink.CoreApi;
class INS {
public static var instance:INS;
private var heading: Null<Pos3> = null;
private var alingment: Int = 1; // 0 = degraded, 1 = not aligned, 2 = aligned
@:allow(kernel.Init)
private function new() {
}
private static var heading:Null<WorldPos> = null;
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)
private function moveForward() {
private static function moveForward() {
if (heading == null) {
this.alingment = 0;
alingment = 0;
return;
}
move(heading);
}
@:allow(kernel.turtle.Turtle)
private function moveBackward() {
private static function moveBackward() {
if (heading == null) {
this.alingment = 0;
alingment = 0;
return;
}
move(heading.negate());
}
@:allow(kernel.turtle.Turtle)
private function moveUp() {
private static function moveUp() {
move({x: 0, y: 1, z: 0});
}
@:allow(kernel.turtle.Turtle)
private function moveDown() {
private static function moveDown() {
move({x: 0, y: -1, z: 0});
}
@:allow(kernel.turtle.Turtle)
private function turnLeft() {
if (heading == null) return;
private static function turnLeft() {
if (heading == null)
return;
if (heading.x == 0 && heading.z == -1) {
heading = {x: -1, y: 0, z: 0};
} else if (heading.x == -1 && heading.z == 0) {
@@ -60,8 +55,9 @@ class INS {
}
@:allow(kernel.turtle.Turtle)
private function turnRight() {
if (heading == null) return;
private static function turnRight() {
if (heading == null)
return;
if (heading.x == 0 && heading.z == -1) {
heading = {x: 1, y: 0, z: 0};
} else if (heading.x == -1 && heading.z == 0) {
@@ -73,81 +69,78 @@ class INS {
}
}
private function move(dir: Null<Pos3>) {
Log.debug('INS move: $dir');
var pos = GPS.instance.getPosition();
private static function move(dir:Null<WorldPos>) {
if (alignPromise.isRunning()) {
return;
}
var pos = GPS.getPosition();
if (pos == null || dir == null)
return;
var newPos = pos + dir;
GPS.instance.setINSPosition(newPos);
GPS.setINSPosition(newPos);
}
public function getHeading():Null<Pos3> {
public static function getHeading():Null<WorldPos> {
return heading;
}
public function align(): Promise<Noise> {
Log.info("Aligning INS");
return new Promise<Noise>((resolve,reject)->{
public static function align():Promise<Noise> {
return alignPromise.request();
}
if (Turtle.instance.getFuelLevel() < 2){
Log.warn("Not enough fuel to align");
reject(new Error("Not enough fuel to align"));
return null;
public static function startAlign():Void {
if (Turtle.getFuelLevel() < 2) {
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.instance.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.instance.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 heading = calcHeading(pos1,pos2,moved);
if (heading == null) {
Log.error("Can't calculate heading");
reject(new Error("Can't calculate heading"));
return;
}
this.heading = heading;
moveBack(moved);
GPS.instance.setINSPosition(pos1);
resolve(Noise);
});
});
return null;
});
}
// -1 = not moved, 0 = back, 1 = forward, 2 = left, 3 = right
private function tryMoving():Int {
if (Turtle.instance.back().isSuccess()) {
private static function tryMoving():Int {
if (Turtle.back().isSuccess()) {
return 0;
} else if (Turtle.instance.forward().isSuccess()) {
} else if (Turtle.forward().isSuccess()) {
return 1;
} else {
Turtle.instance.turnLeft(); // TODO: Check if successfull
if (Turtle.instance.forward().isSuccess()){
Turtle.turnLeft(); // TODO: Check if successfull
if (Turtle.forward().isSuccess()) {
return 2;
} else if (Turtle.instance.back().isSuccess()) {
} else if (Turtle.back().isSuccess()) {
return 3;
} else {
// Can't move
@@ -156,7 +149,7 @@ class INS {
}
}
private 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) {
return pos1 - pos2;
} else if (moved == 1) {
@@ -170,27 +163,21 @@ class INS {
}
}
private function moveBack(moved:Int) {
private static function moveBack(moved:Int) {
if (moved == 0) {
Turtle.instance.forward();
// cc.Turtle.forward();
Turtle.forward();
} else if (moved == 1) {
Turtle.instance.back();
// cc.Turtle.back();
Turtle.back();
} else if (moved == 2) {
Turtle.instance.back();
// cc.Turtle.back();
Turtle.instance.turnRight();
// cc.Turtle.turnRight();
Turtle.back();
Turtle.turnRight();
} else if (moved == 3) {
Turtle.instance.forward();
// cc.Turtle.forward();
Turtle.instance.turnRight();
// cc.Turtle.turnRight();
Turtle.forward();
Turtle.turnRight();
}
}
private function rotatePos3ToRight(pos:Pos3):Pos3 {
private static function rotatePos3ToRight(pos:WorldPos):WorldPos {
if (pos.x == 0 && pos.z == -1) {
return {x: 1, y: 0, z: 0};
} else if (pos.x == -1 && pos.z == 0) {
@@ -204,8 +191,7 @@ class INS {
}
}
private function rotatePos3ToLeft(pos3:Pos3):Pos3 {
private static function rotatePos3ToLeft(pos3:WorldPos):WorldPos {
return rotatePos3ToRight(rotatePos3ToRight(rotatePos3ToRight(pos3)));
}
}

View File

@@ -3,16 +3,15 @@ package kernel.http;
using lua.Table;
class HTTPFailure {
public final reason:String;
public final statusCode:Null<StatusCode>;
public final headers:Map<String,String>;
public final headers:Map<String, String>;
public final body:String;
@:allow(kernel.http)
private function new(failReason: String, ?handle: cc.HTTP.HTTPResponse) {
private function new(failReason:String, ?handle:cc.HTTP.HTTPResponse) {
this.reason = failReason;
if (handle != null){
if (handle != null) {
this.statusCode = handle.getResponseCode();
this.headers = handle.getResponseHeaders().toMap();
this.body = handle.readAll();

View File

@@ -6,21 +6,21 @@ using tink.CoreApi;
Wrapper for the native HTTP request function.
**/
class Http {
public static function request(url:String,?body:String,?options: String):Future<Outcome<HTTPResponse,HTTPFailure>> {
return new Future<Outcome<HTTPResponse,HTTPFailure>>((resolve) -> {
KernelEvents.instance.onHttpFailure.handle((params)->{
if (params.url == url){
resolve(Failure(new HTTPFailure(params.failReason,params.handle)));
public static function request(url:String, ?body:String, ?options:String):Future<Outcome<HTTPResponse, HTTPFailure>> {
return new Future<Outcome<HTTPResponse, HTTPFailure>>((resolve) -> {
KernelEvents.onHttpFailure.handle((params) -> {
if (params.url == url) {
resolve(Failure(new HTTPFailure(params.failReason, params.handle)));
}
});
KernelEvents.instance.onHttpSuccess.handle((params) -> {
if (params.url == url){
KernelEvents.onHttpSuccess.handle((params) -> {
if (params.url == url) {
resolve(Success(new HTTPResponse(params.handle)));
}
});
cc.HTTP.request(url,body);
cc.HTTP.request(url, body);
return null;
});

View File

@@ -3,13 +3,12 @@ package kernel.http;
using lua.Table;
class HTTPResponse {
public final statusCode:StatusCode;
public final headers:Map<String,String>;
public final headers:Map<String, String>;
public final body:String;
@:allow(kernel.http)
private function new(handle: cc.HTTP.HTTPResponse) {
private function new(handle:cc.HTTP.HTTPResponse) {
this.statusCode = handle.getResponseCode();
this.headers = handle.getResponseHeaders().toMap();
this.body = handle.readAll();

View File

@@ -1,7 +1,8 @@
package kernel.log;
import haxe.ds.ReadOnlyArray;
#if webconsole
import haxe.macro.Context;
#if (webconsole && !macro)
import lib.Debug;
#end
@@ -11,43 +12,72 @@ using tink.CoreApi;
Central logging system.
**/
class Log {
public static var instance:Log;
private static inline final MAX_LINES:Int = 100;
public final onLog:Signal<LogLine>;
public static var onLog(default, null):Signal<LogLine>;
private final onLogTrigger:SignalTrigger<LogLine> = new SignalTrigger();
private final logLines:Array<LogLine> = [];
private static final onLogTrigger:SignalTrigger<LogLine> = new SignalTrigger();
private static final logLines:Array<LogLine> = [];
public function new() {
@:allow(kernel.Init)
private static function init() {
onLog = onLogTrigger.asSignal();
}
public static function info(msg:Dynamic, ?pos:haxe.PosInfos) {
instance.log({level: Info, message: Std.string(msg),time: 0},pos);
}
public static function warn(msg:Dynamic, ?pos:haxe.PosInfos) {
instance.log({level: Warn, message: Std.string(msg),time: 0},pos);
}
public static function error(msg:Dynamic, ?pos:haxe.PosInfos) {
instance.log({level: Error, message: Std.string(msg),time: 0},pos);
}
public static function debug(msg:Dynamic, ?pos:haxe.PosInfos) {
#if debug
instance.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
}
public static function silly(msg:Dynamic, ?pos:haxe.PosInfos) {
instance.log({level: Silly, message: Std.string(msg),time: 0},pos);
public static function _info(msg:Dynamic, ?origin:String) {
log({
level: Info,
message: Std.string(msg),
time: 0,
origin: origin
});
}
private function log(line: LogLine, ?pos:haxe.PosInfos) {
line.origin = pos.className;
public static function _warn(msg:Dynamic, ?origin:String) {
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);
if (logLines.length > MAX_LINES) {
@@ -55,12 +85,49 @@ class Log {
}
onLogTrigger.trigger(line);
#if webconsole
Debug.printWeb('[${Std.string(line.level)}][${line.origin}] ${line.message}');
#if (webconsole && !macro)
Debug.logToWebconsole(line);
#end
}
public function getLines():ReadOnlyArray<LogLine> {
public static function getLines():ReadOnlyArray<LogLine> {
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

@@ -1,8 +1,8 @@
package kernel.log;
typedef LogLine = {
level: LogLevel,
message: String,
time: Int,
?origin: String,
level:LogLevel,
message:String,
time:Int,
?origin:String,
}

View File

@@ -1,17 +1,19 @@
package kernel.net;
import kernel.net.Package.GenericPackage;
using tink.CoreApi;
/**
A object that is able to send and receive messages.
**/
interface INetworkInterface {
public function listen(chan: Int):Void;
public function close(chan: Int):Void;
public function isListening(chan: Int): Bool;
public function closeAll(): Void;
public function send(chan: Int,replyChan: Int,payload: Any):Void;
public function listen(chan:Int):Void;
public function close(chan:Int):Void;
public function isListening(chan:Int):Bool;
public function closeAll():Void;
public function send(chan:Int, replyChan:Int, payload:Any):Void;
public function name():String;
public function getBaseRoutingCost():Int;
public var onMessage (default, null): Signal<{pack:Package,?dist:Float}>;
public var onMessage(default, null):Signal<{pack:GenericPackage, ?dist:Float}>;
}

View File

@@ -1,6 +1,8 @@
package kernel.net;
import kernel.net.Package.GenericPackage;
import kernel.log.Log;
using tink.CoreApi;
/**
@@ -9,17 +11,17 @@ using tink.CoreApi;
class Loopback implements INetworkInterface {
public static final instance:Loopback = new Loopback();
public var onMessage(default, null):Signal<{pack:Package,dist:Null<Float>}>;
public var onMessage(default, null):Signal<{pack:GenericPackage, dist:Null<Float>}>;
private final onMessageTrigger: SignalTrigger<{pack:Package,dist:Null<Float>}> = Signal.trigger();
private var openChans: Array<Int> = [];
private final onMessageTrigger:SignalTrigger<{pack:GenericPackage, dist:Null<Float>}> = Signal.trigger();
private var openChans:Array<Int> = [];
private function new() {
this.onMessage = onMessageTrigger.asSignal();
}
public function listen(chan:Int) {
if (!this.openChans.contains(chan)){
if (!this.openChans.contains(chan)) {
this.openChans.push(chan);
}
}
@@ -37,9 +39,9 @@ class Loopback implements INetworkInterface {
}
public function send(chan:Int, replyChan:Int, payload:Any) {
if (this.openChans.contains(chan)){
this.onMessageTrigger.trigger({pack:payload,dist:null});
}else{
if (this.openChans.contains(chan)) {
this.onMessageTrigger.trigger({pack: payload, dist: null});
} else {
Log.silly("Loopback got package on non open channel");
}
}

View File

@@ -1,5 +1,6 @@
package kernel.net;
import kernel.net.Package.GenericPackage;
import kernel.gps.GPS;
import haxe.ds.ReadOnlyArray;
import kernel.net.Package.NetworkID;
@@ -19,33 +20,64 @@ class Net {
/**
Depends on: KernelEvents
**/
public static var instance:Net;
public static inline final BRODCAST_PORT:Int = 65533;
public static inline final MESSAGE_TIMEOUT:Int = 3;
public static inline final DEFAULT_TTL:Int = 10;
public final networkID:NetworkID = OS.getComputerID();
private final responseBus:Map<Int, Callback<Outcome<Package,Error>>> = new Map();
private final protoHandlers:Map<String, Callback<Package>> = new Map();
private var interfaces:Array<INetworkInterface>;
public static final networkID:NetworkID = OS.getComputerID();
private static final responseBus:Map<Int, Callback<Outcome<GenericPackage, Error>>> = new Map();
private static final protoHandlers:Map<String, Callback<GenericPackage>> = new Map();
private static var interfaces:Array<INetworkInterface>;
@:allow(kernel.Init)
private function new() {
this.interfaces = [for (e in Peripheral.instance.getAllModems()) e ]; // TODO: is this the way to do it?
this.interfaces.push(Loopback.instance);
private static function init() {
interfaces = [for (e in Peripheral.getAllModems()) e]; // TODO: is this the way to do it?
interfaces.push(Loopback.instance);
for (interf in interfaces){
for (interf in interfaces) {
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();
Routing.setup();
}
private function setupPingHandle() {
this.registerProto("icmp", pack -> {
private static function setupPingHandle() {
registerProto("icmp", pack -> {
switch pack.data.type {
case "ping":
this.respondTo(pack, "pong");
respondTo(pack, "pong");
case "died":
// If we get a "died" message from a node when one of our packages ttl hits 0
// the `data.msgId` prop is the message id
@@ -59,8 +91,11 @@ class Net {
});
}
private function setupInterf(interf: INetworkInterface) {
interf.onMessage.handle(e -> handle(e.pack,interf,e.dist));
/**
Enables the interface to receive messages.
**/
private static function setupInterf(interf:INetworkInterface) {
interf.onMessage.handle(e -> handle(e.pack, interf, e.dist));
interf.listen(networkID);
interf.listen(BRODCAST_PORT);
}
@@ -68,8 +103,8 @@ class Net {
/**
Called when a new package comes in.
**/
private function handle(pack:Package,interf: INetworkInterface, ?dist: Float) {
if (pack.toID == this.networkID || pack.toID == Net.BRODCAST_PORT){
private static function handle(pack:GenericPackage, interf:INetworkInterface, ?dist:Float) {
if (pack.toID == networkID || pack.toID == Net.BRODCAST_PORT) {
switch pack.type {
case Data(_) | DataNoResponse(_):
// Let a local proccess handle it
@@ -81,30 +116,30 @@ class Net {
}
case RouteDiscover(_) | RouteDiscoverResponse(_) | RouteDiscoverUpdate(_):
// Delegate to Routing
Routing.instance.handleRoutePackage(pack,interf);
Routing.handleRoutePackage(cast pack, interf);
case GPSRequest | GPSResponse(_):
if (dist == null) {
Log.silly("Got a GPS package but no distance was provided");
return;
}
// Delegate to GPS
GPS.instance.handlePackage(pack,dist,interf);
GPS.handlePackage(cast pack, dist, interf);
}
}else{
} else {
// New message received but its not ment for us. Forward if possible.
forwardPackage(pack);
}
}
private function generateMessageID():Int {
private static function generateMessageID():Int {
return Std.random(2147483647); // TODO: better uniqe number
}
/**
Send a message. Dont care if its reaches its destination nor it has a response.
**/
public function sendAndForget(dest:Int, proto:String, data:Dynamic) {
var pack:Package = {
public static function sendAndForget(dest:Int, proto:String, data:Dynamic) {
var pack:GenericPackage = {
toID: dest,
fromID: networkID,
msgID: generateMessageID(),
@@ -116,13 +151,12 @@ class Net {
sendRaw(pack);
}
private function forwardPackage(pack: Package) {
if (pack.ttl == 0){
private static function forwardPackage(pack:GenericPackage) {
if (pack.ttl == 0) {
if (pack.type.match(Data(_))) {
// If the package is a data package and the ttl hits 0
// we send a "died" message to the sender
sendAndForget(pack.fromID, "icmp", {type:"died", msgID: pack.msgID});
sendAndForget(pack.fromID, "icmp", {type: "died", msgID: pack.msgID});
}
// Drop package
@@ -131,14 +165,15 @@ class Net {
pack.ttl--;
if (!sendRaw(pack)){
if (!sendRaw(pack)) {
// Cant forward
Log.silly('Received a message that could not be forwarded to ${pack.toID}');
}
}
public function respondTo(pack:Package, data:Dynamic) {
if (pack.type.match(DataNoResponse(_))) {
Log.warn("Responed to a no response package. Ignoring");
public static function respondTo(pack:GenericPackage, data:Dynamic) {
if (!pack.type.match(Data(_))) {
Log.warn("Responding to a package that does require responding. Ignoring.");
return;
}
@@ -150,7 +185,7 @@ class Net {
/**
Send to package to the localy register handler based on the proto
**/
private function routeToProto(pack:Package) {
private static function routeToProto(pack:GenericPackage) {
var proto = switch pack.type {
case Data(proto):
proto;
@@ -172,22 +207,22 @@ class Net {
Just send the package to the right modem.
Returns true if message was send
**/
private function sendRaw(pack:Package): Bool {
var route = Routing.instance.getRouteToID(pack.toID);
if (route == null){
private static function sendRaw(pack:GenericPackage):Bool {
var route = Routing.getRouteToID(pack.toID);
if (route == null) {
return false;
}
route.interf.send(route.rep,this.networkID,pack);
route.interf.send(route.rep, networkID, pack);
return true;
}
/**
Send a message and wait for a response.
**/
public function sendAndAwait(dest:NetworkID, proto:String, data:Dynamic):Promise<Package> {
return new Promise<Package>((resolve, reject) -> {
var pack:Package = {
public static function sendAndAwait<T>(dest:NetworkID, proto:String, data:T):Promise<GenericPackage> {
return new Promise<GenericPackage>((resolve, reject) -> {
var pack:GenericPackage = {
toID: dest,
fromID: networkID,
msgID: generateMessageID(),
@@ -198,8 +233,7 @@ class Net {
var timeout:Timer = null;
responseBus[pack.msgID] = ((reponse:Outcome<Package,Error>) -> {
responseBus[pack.msgID] = ((reponse:Outcome<GenericPackage, Error>) -> {
switch reponse {
case Success(pack):
resolve(pack);
@@ -211,14 +245,16 @@ class Net {
if (timeout != null) {
timeout.cancle();
}
responseBus.remove(pack.msgID);
});
timeout = new Timer(MESSAGE_TIMEOUT, () -> {
responseBus.remove(pack.msgID);
reject(new Error(InternalError,"Message timeout"));
reject(new Error(InternalError, "Message timeout"));
});
if (!sendRaw(pack)){
if (!sendRaw(pack)) {
reject(new Error("ID unreachable"));
}
@@ -227,7 +263,7 @@ class Net {
});
}
public function registerProto(proto:String, callback:Callback<Package>) {
public static function registerProto(proto:String, callback:Callback<GenericPackage>) {
if (protoHandlers.exists(proto)) {
// Failed. Handler already exist.
// TODO: return error
@@ -237,16 +273,16 @@ class Net {
protoHandlers[proto] = callback;
}
public function removeProto(proto:String) {
public static function removeProto(proto:String) {
protoHandlers.remove(proto);
}
/**
Sends a ping package to the given id. Returns true if there was a response.
**/
public function ping(toID: NetworkID): Promise<Noise> {
return new Promise<Noise>((resolve,reject)->{
this.sendAndAwait(toID,"icmp",{type:"ping"}).handle(pack -> {
public static function ping(toID:NetworkID):Promise<Noise> {
return new Promise<Noise>((resolve, reject) -> {
sendAndAwait(toID, "icmp", {type: "ping"}).handle(pack -> {
switch pack {
case Success(_):
resolve(Noise);
@@ -258,7 +294,7 @@ class Net {
});
}
public function getActiveProtocols(): ReadOnlyArray<String> {
public static function getActiveProtocols():ReadOnlyArray<String> {
var arr = new Array<String>();
for (proto in protoHandlers.keys()) {
@@ -269,8 +305,8 @@ class Net {
}
@:allow(kernel.gps.GPS)
private function brodcastGPSRequest() {
var pack: Package = {
private static function brodcastGPSRequest() {
var pack:Package<Noise> = {
fromID: networkID,
toID: Net.BRODCAST_PORT,
ttl: 0, // Prevent forwarding
@@ -279,9 +315,10 @@ class Net {
data: null,
};
for (modem in Peripheral.instance.getAllModems()) {
if (!modem.isWireless()) continue;
modem.send(Net.BRODCAST_PORT, Net.instance.networkID, pack);
for (modem in Peripheral.getAllModems()) {
if (!modem.isWireless())
continue;
modem.send(Net.BRODCAST_PORT, networkID, pack);
}
}
}

View File

@@ -1,32 +1,31 @@
package kernel.net;
import lib.Pos3;
typedef NetworkID = Int;
typedef GenericPackage = Package<Dynamic>;
enum PackageTypes {
Data(proto:String);
DataNoResponse(proto:String);
Response;
RouteDiscover(reachableIDs: Array<{id:NetworkID,cost:Int}>);
RouteDiscoverResponse(reachableIDs: Array<{id:NetworkID,cost:Int}>);
RouteDiscoverUpdate(reachableIDs: Array<{id:NetworkID,cost:Int}>);
GPSResponse(pos:Pos3);
RouteDiscover(reachableIDs:Array<{id:NetworkID, cost:Int}>);
RouteDiscoverResponse(reachableIDs:Array<{id:NetworkID, cost:Int}>);
RouteDiscoverUpdate(reachableIDs:Array<{id:NetworkID, cost:Int}>);
GPSResponse(x:Float, y:Float, z:Float);
GPSRequest();
}
/**
Representing a network package.
**/
@:structInit class Package {
@:structInit class Package<T> {
public final fromID:NetworkID;
public final toID:NetworkID;
public final msgID:Int;
public final type:PackageTypes;
public final data:Dynamic;
public var ttl: Int;
public final data:T;
public var ttl:Int;
public function new(fromID:NetworkID, toID:NetworkID, msgID:Int, type:PackageTypes, data:Dynamic, ttl:Int) {
public function new(fromID:NetworkID, toID:NetworkID, msgID:Int, type:PackageTypes, data:T, ttl:Int) {
this.fromID = fromID;
this.toID = toID;
this.msgID = msgID;
@@ -38,7 +37,7 @@ enum PackageTypes {
/**
Create package that can be used as a response.
**/
public function createResponse(newData:Dynamic):Package {
public function createResponse<T2>(newData:T2):Package<T2> {
return {
toID: fromID,
fromID: toID,
@@ -50,9 +49,9 @@ enum PackageTypes {
}
/**
Wrapper for `Net.instance.respondTo`.
Wrapper for `Net.respondTo`.
**/
public function respond(data:Dynamic) {
Net.instance.respondTo(this, data);
Net.respondTo(this, data);
}
}

View File

@@ -20,24 +20,22 @@ class Routing {
/**
Depends on: Peripheral
**/
public static var instance:Routing;
public static inline final UPDATE_WAIT_TIME:Float = 1;
public final onNewNeigbor:Signal<Int>;
public static var onNewNeigbor(default, null):Signal<Int>;
private final onNewNeigborTrigger:SignalTrigger<NetworkID> = Signal.trigger();
private final routingTable:Map<NetworkID, Route> = new Map();
private var routeUpdateInQueue:Bool = false;
private static final onNewNeigborTrigger:SignalTrigger<NetworkID> = Signal.trigger();
private static final routingTable:Map<NetworkID, Route> = new Map();
private static var routeUpdateInQueue:Bool = false;
@:allow(kernel.Init)
private function new() {
this.onNewNeigbor = this.onNewNeigborTrigger.asSignal();
private static function init() {
onNewNeigbor = onNewNeigborTrigger.asSignal();
}
@:allow(kernel.Init)
private function init() {
routingTable.set(Net.instance.networkID, {interf: Loopback.instance, cost: 0, rep: Net.instance.networkID});
@:allow(kernel.net.Net)
private static function setup() {
routingTable.set(Net.networkID, {interf: Loopback.instance, cost: 0, rep: Net.networkID});
brodcastRoutingTable();
}
@@ -45,27 +43,27 @@ class Routing {
Prepares an brodcast of the current routing table. If a brodcast is already in queue it will be ignored.
After UPDATE_WAIT_TIME seconds the routing table will be brodcasted. This is done to prevent spamming the network.
**/
private function prepareRouteUpdate() {
if (this.routeUpdateInQueue) {
private static function prepareRouteUpdate() {
if (routeUpdateInQueue) {
return;
}
this.routeUpdateInQueue = true;
routeUpdateInQueue = true;
new Timer(UPDATE_WAIT_TIME, () -> {
brodcastRoutingTable();
this.routeUpdateInQueue = false;
routeUpdateInQueue = false;
});
}
/**
Brodcast the current routing table to all peers.
**/
private function brodcastRoutingTable() {
private static function brodcastRoutingTable() {
var pack = newRoutDiscoverPackage();
for (modem in Peripheral.instance.getAllModems()) {
modem.send(Net.BRODCAST_PORT, Net.instance.networkID, pack);
for (modem in Peripheral.getAllModems()) {
modem.send(Net.BRODCAST_PORT, Net.networkID, pack);
}
}
@@ -73,53 +71,48 @@ class Routing {
Handle incomming packages that involve route discovery.
**/
@:allow(kernel.net.Net)
private function handleRoutePackage(pack:Package, interf:INetworkInterface):Void {
private static function handleRoutePackage(pack:Package<Noise>, interf:INetworkInterface):Void {
addPossibleRoute(pack.fromID, interf, 0, pack.fromID);
var shouldRespond:Bool = switch pack.type {
switch pack.type {
case RouteDiscoverResponse(routes):
// A request for routes has been answerd
for (route in routes) {
addPossibleRoute(route.id, interf, route.cost, pack.fromID);
}
false;
case RouteDiscover(routes):
// Received a request for available routes
for (route in routes) {
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):
// Received an update of routes
for (route in routes) {
addPossibleRoute(route.id, interf, route.cost, pack.fromID);
}
false;
default:
Log.error("Expected package to be a Route discover package");
false;
Log.silly("Expected package to be a Route discover package");
};
if (!shouldRespond) {
return;
}
// Respond to peer
var response:Package = {
toID: pack.fromID,
fromID: Net.instance.networkID,
msgID: null,
type: RouteDiscoverResponse(genRouteList()),
data: null,
ttl: 0, // Prevent forwarding
}
interf.send(response.toID, Net.instance.networkID, response);
}
/**
Generate a list of routes to send to a peer based on the current routing table.
**/
private function genRouteList():Array<{id:NetworkID, cost:Int}> {
private static function genRouteList():Array<{id:NetworkID, cost:Int}> {
var routes:Array<{id:NetworkID, cost:Int}> = [];
for (k => v in this.routingTable) {
for (k => v in routingTable) {
routes.push({
id: k,
cost: v.cost
@@ -128,12 +121,15 @@ class Routing {
return routes;
}
private function newRoutDiscoverPackage():Package {
var pack:Package = {
/**
Create a new packate to brodcast our routes.
**/
private static function newRoutDiscoverPackage():Package<Noise> {
var pack:Package<Noise> = {
type: RouteDiscover(genRouteList()),
toID: Net.BRODCAST_PORT,
msgID: null,
fromID: Net.instance.networkID,
fromID: Net.networkID,
data: null,
ttl: 0, // Prevent forwarding
}
@@ -145,31 +141,31 @@ class Routing {
Called when a route to a client has been disoverd.
Its possible to be called multiple times with the same id but different addr.
**/
private function addPossibleRoute(toID:Int, interf:INetworkInterface, cost:Int, rep:NetworkID) {
if (toID == Net.instance.networkID) {
private static function addPossibleRoute(toID:Int, interf:INetworkInterface, cost:Int, rep:NetworkID) {
if (toID == Net.networkID) {
return;
}
var fullCost = cost + interf.getBaseRoutingCost();
if (this.routingTable.exists(toID)) {
if (this.routingTable[toID].cost > fullCost) {
this.routingTable[toID] = {interf: interf, cost: cost + interf.getBaseRoutingCost(), rep: rep};
this.prepareRouteUpdate();
if (routingTable.exists(toID)) {
if (routingTable[toID].cost > fullCost) {
routingTable[toID] = {interf: interf, cost: cost + interf.getBaseRoutingCost(), rep: rep};
prepareRouteUpdate();
}
} else {
this.routingTable[toID] = {interf: interf, cost: cost + interf.getBaseRoutingCost(), rep: rep};
this.onNewNeigborTrigger.trigger(toID);
this.prepareRouteUpdate();
routingTable[toID] = {interf: interf, cost: cost + interf.getBaseRoutingCost(), rep: rep};
onNewNeigborTrigger.trigger(toID);
prepareRouteUpdate();
}
}
public function getRouteToID(networkID:NetworkID):{interf:INetworkInterface, rep:NetworkID} {
if (networkID == Net.instance.networkID) {
return {interf: Loopback.instance, rep: Net.instance.networkID};
public static function getRouteToID(networkID:NetworkID):{interf:INetworkInterface, rep:NetworkID} {
if (networkID == Net.networkID) {
return {interf: Loopback.instance, rep: Net.networkID};
}
var route:Null<Route> = this.routingTable[networkID];
var route:Null<Route> = routingTable[networkID];
if (route == null) {
return null;
@@ -178,7 +174,31 @@ class Routing {
}
}
public function getRouteTable():Map<NetworkID, Route> {
return this.routingTable;
public static function getRouteTable():Map<NetworkID, Route> {
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,181 @@
package kernel.peripherals;
import cc.Peripheral;
using tink.CoreApi;
class BigReactor implements IPeripheral {
public static inline final TYPE_NAME:String = "BigReactors-Reactor";
private final addr:String;
public function new(addr:String) {
this.addr = addr;
}
public function getAddr():String {
return addr;
}
public function getType():String {
return TYPE_NAME;
}
public inline function getActive():Bool {
return Peripheral.call(addr, "getActive");
}
public inline function getNumberOfControlRods():Int {
return Peripheral.call(addr, "getNumberOfControlRods");
}
public inline function getEnergyStored():Float {
return Peripheral.call(addr, "getEnergyStored");
}
public inline function getFuelTemperature():Float {
return Peripheral.call(addr, "getFuelTemperature");
}
public inline function getCasingTemperature():Float {
return Peripheral.call(addr, "getCasingTemperature");
}
public inline function getFuelAmount():Int {
return Peripheral.call(addr, "getFuelAmount");
}
public inline function getWasteAmount():Int {
return Peripheral.call(addr, "getWasteAmount");
}
public inline function getFuelAmountMax():Int {
return Peripheral.call(addr, "getFuelAmountMax");
}
public function getControlRodName(controlRodIndex:Int):String {
// TODO: check in bounds
return Peripheral.call(addr, "getControlRodName", controlRodIndex);
}
public function getControlRodLevel(controlRodIndex:Int):Int {
// TODO: check in bounds
return Peripheral.call(addr, "getControlRodLevel", controlRodIndex);
}
/**
If the reactor is actively cooled, returns the amount of hot fluid produced in the past tick, in milli-Buckets (mB).
**/
public inline function getEnergyProducedLastTick():Float {
return Peripheral.call(addr, "getEnergyProducedLastTick");
}
/**
If the reactor is passively cooled, always returns 0.
**/
public inline function getHotFluidProducedLastTick():Float {
return Peripheral.call(addr, "getHotFluidProducedLastTick");
}
public inline function getCoolantType():Null<String> {
return Peripheral.call(addr, "getCoolantType");
}
/**
in milli-buckets (mB)
**/
public inline function getCoolantAmount():Int {
return Peripheral.call(addr, "getCoolantAmount");
}
/**
in milli-buckets (mB)
**/
public inline function getCoolantAmountMax():Int {
return Peripheral.call(addr, "getCoolantAmountMax");
}
public inline function getHotFluidType():Null<String> {
return Peripheral.call(addr, "getHotFluidType");
}
/**
in milli-buckets (mB)
**/
public inline function getHotFluidAmount():Int {
return Peripheral.call(addr, "getHotFluidAmount");
}
/**
Returns the reactivity level of the reactor's fuel. 100 = 100 percent
**/
public inline function getFuelReactivity():Int {
return Peripheral.call(addr, "getFuelReactivity");
}
public inline function getFuelConsumedLastTick():Float {
return Peripheral.call(addr, "getFuelConsumedLastTick");
}
public inline function isActivelyCooled():Bool {
return Peripheral.call(addr, "isActivelyCooled");
}
public inline function setActive(active:Bool):Void {
Peripheral.call(addr, "setActive", active);
}
/**
0 (not inserted) to 100 (fully inserted)
**/
public inline function setAllControlRodLevels(level:Int) {
Peripheral.call(addr, "setAllControlRodLevels", level);
}
/**
0 (not inserted) to 100 (fully inserted)
**/
public function setControlRodLevel(index:Int, level:Int):Void {
// TODO: check in bounds
Peripheral.call(addr, "setControlRodLevel", index, level);
}
public inline function doEjectWaste():Void {
throw new Error("Not implemented");
}
public inline function doEjectFuel():Void {
throw new Error("Not implemented");
}
public inline function getControlRodLocation(index:Int):Void {
throw new Error("Not implemented");
}
public inline function getEnergyStoredAsText():String {
return Peripheral.call(addr, "getEnergyStoredAsText");
}
public inline function getVariant():String {
return Peripheral.call(addr, "getVariant");
}
public inline function setControlRodName(index:Int, name:String):Void {
Peripheral.call(addr, "setControlRodName", index, name);
}
// Useless methods
// getFuelStats
// getEnergyStats
// getCoolantFluidStats
// getHotFluidStats
// TODO: need research
// isMethodAvailable(method: String): Bool
// mbGetMaximumCoordinate(): Pos3
// mbGetMinimumCoordinate(): Pos3
// mbGetMultiblockControllerTypeName(): String
// mbIsAssembled(): Bool
// mbIsConnected(): Bool
// mbIsDisassembled(): Bool
// mbIsPaused(): Bool
}

View File

@@ -0,0 +1,50 @@
package kernel.peripherals;
import cc.Peripheral;
import kernel.net.Package.NetworkID;
class Computer implements IPeripheral {
public static inline final TYPE_NAME:String = "computer";
private final addr:String;
@:allow(kernel.peripherals)
private function new(addr:String) {
this.addr = addr;
}
public function getAddr():String {
return addr;
}
public function getType():String {
return Computer.TYPE_NAME;
}
public function isOn():Bool {
return Peripheral.call(addr, "isOn");
}
public function getLabel():Null<String> {
return Peripheral.call(addr, "getLabel");
}
/**
Return -1 if no ID set yet
**/
public function getID():NetworkID {
return Peripheral.call(addr, "getID");
}
public function reboot() {
Peripheral.call(addr, "reboot");
}
public function shutdown() {
Peripheral.call(addr, "shutdown");
}
public function turnOn() {
Peripheral.call(addr, "turnOn");
}
}

View File

@@ -1,6 +1,7 @@
package kernel.peripherals;
import cc.Peripheral;
using tink.CoreApi;
class Drive implements IPeripheral {
@@ -15,21 +16,21 @@ class Drive implements IPeripheral {
private final onDiskEjectTrigger:SignalTrigger<Noise> = Signal.trigger();
@:allow(kernel.peripherals)
private function new(addr: String) {
private function new(addr:String) {
this.addr = addr;
this.native = Peripheral.wrap(addr);
this.onDiskInsert = this.onDiskInsertTrigger.asSignal();
this.onDiskEject = this.onDiskEjectTrigger.asSignal();
KernelEvents.instance.onDisk.handle((addr) ->{
if (addr == this.addr){
KernelEvents.onDisk.handle((addr) -> {
if (addr == this.addr) {
this.onDiskInsertTrigger.trigger(null);
}
});
KernelEvents.instance.onDiskEject.handle((addr)->{
if (addr == this.addr){
KernelEvents.onDiskEject.handle((addr) -> {
if (addr == this.addr) {
this.onDiskEjectTrigger.trigger(null);
}
});
@@ -39,14 +40,18 @@ class Drive implements IPeripheral {
return this.addr;
}
public inline function isDiskPresent(): Bool {
public function getType():String {
return TYPE_NAME;
}
public inline function isDiskPresent():Bool {
return this.native.isDiskPresent();
}
/**
The label of the disk, or `null` if either no disk is inserted or the disk doesn't have a label.
**/
public inline function getDiskLabel(): Null<String> {
public inline function getDiskLabel():Null<String> {
return this.native.getDiskLabel();
}
@@ -54,11 +59,11 @@ class Drive implements IPeripheral {
this.native.setDiskLabel();
}
public inline function setDiskLabel(label: String): Null<Error> {
public inline function setDiskLabel(label:String):Null<Error> {
try {
this.native.setDiskLabel(label);
return null;
} catch (e: Dynamic) {
} catch (e:Dynamic) {
return new Error("Invalid label");
}
}
@@ -75,7 +80,7 @@ class Drive implements IPeripheral {
return this.getMountPath();
}
public inline function getAudioTitle(): Null<String> {
public inline function getAudioTitle():Null<String> {
return this.native.getAudioTitle();
}
@@ -91,7 +96,7 @@ class Drive implements IPeripheral {
this.native.ejectDisk();
}
public inline function getDiskID(): Int {
public inline function getDiskID():Int {
return this.native.getDiskID();
}
}

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,25 +1,29 @@
package kernel.peripherals;
class EnergyStorage implements IPeripheral{
public static inline final TYPE_NAME:String = "energyCell";
class EnergyStorage implements IPeripheral {
public static inline final TYPE_NAME:String = "energyCell";
private final addr:String;
private final native: cc.periphs.EnergyStorage;
private final addr:String;
private final native:cc.periphs.EnergyStorage;
public function new(addr: String) {
this.addr = addr;
this.native = cc.Peripheral.wrap(addr);
}
public function new(addr:String) {
this.addr = addr;
this.native = cc.Peripheral.wrap(addr);
}
public function getEnergy(): Int {
return this.native.getEnergy();
}
public function getEnergy():Int {
return this.native.getEnergy();
}
public function getEnergyCapacity(): Int {
return this.native.getEnergyCapacity();
}
public function getEnergyCapacity():Int {
return this.native.getEnergyCapacity();
}
public function getAddr():String {
return this.addr;
}
public function getType():String {
return TYPE_NAME;
}
}

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

@@ -1,5 +1,6 @@
package kernel.peripherals;
interface IPeripheral {
public function getAddr(): String;
public function getAddr():String;
public function getType():String;
}

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

@@ -11,9 +11,9 @@ class Modem implements INetworkInterface implements IPeripheral {
public static inline final TYPE_NAME:String = "modem";
public final addr:String;
public var onMessage(default, null):Signal<{pack:Package,dist:Null<Float>}>;
public var onMessage(default, null):Signal<{pack:GenericPackage, dist:Null<Float>}>;
private final onMessageTrigger:SignalTrigger<{pack:Package,dist:Null<Float>}> = Signal.trigger();
private final onMessageTrigger:SignalTrigger<{pack:GenericPackage, dist:Null<Float>}> = Signal.trigger();
private final native:cc.periphs.Modem.Modem;
@:allow(kernel.peripherals)
@@ -22,10 +22,10 @@ class Modem implements INetworkInterface implements IPeripheral {
this.native = Peripheral.wrap(addr);
this.addr = addr;
KernelEvents.instance.onModemMessage.handle(params ->{
try{
if (params.addr == this.addr){
var pack:Package = {
KernelEvents.onModemMessage.handle(params -> {
try {
if (params.addr == this.addr) {
var pack:GenericPackage = {
fromID: params.message.fromID,
toID: params.message.toID,
msgID: params.message.msgID,
@@ -36,7 +36,7 @@ class Modem implements INetworkInterface implements IPeripheral {
this.onMessageTrigger.trigger({pack: pack, dist: params.distance});
}
}catch(e:Dynamic){
} catch (e:Dynamic) {
Log.error("Error while parsing modem message");
}
});
@@ -74,10 +74,14 @@ class Modem implements INetworkInterface implements IPeripheral {
return this.addr;
}
public function getType():String {
return TYPE_NAME;
}
public function getBaseRoutingCost():Int {
if (this.native.isWireless()){
if (this.native.isWireless()) {
return 2; // Prefere messages over cable
}else{
} else {
return 1;
}
}

View File

@@ -11,24 +11,15 @@ using tink.CoreApi;
Class responseable for retrieving peripherals.
**/
class Peripheral {
/**
Depends on: KernelEvents
**/
public static var instance:Peripheral;
@:allow(kernel.Init)
private function new() {
}
public function getAllAddresses(): Array<String> {
public static function getAllAddresses():Array<String> {
return cc.Peripheral.getNames().toArray();
}
public function isPresent(addr: String): Bool {
public static function isPresent(addr:String):Bool {
return cc.Peripheral.isPresent(addr);
}
public function getTypes(addr: String): Array<String> {
public static function getTypes(addr:String):Array<String> {
if (!cc.Peripheral.isPresent(addr)) {
return [];
}
@@ -36,11 +27,11 @@ class Peripheral {
return cc.Peripheral.getType(addr).toArray();
}
private function findAddrByType(type: String): Array<String> {
public static function findAddrByType(type:String):Array<String> {
return getAllAddresses().filter(addr -> getTypes(addr).contains(type));
}
private function safeGetAddr(addr: String, type: String): Null<String> {
private static function safeGetAddr(addr:String, type:String):Null<String> {
if (!isPresent(addr)) {
return null;
}
@@ -53,18 +44,13 @@ class Peripheral {
return addr;
}
public function inspect(addr: String): Null<{ types: Array<String>, methods: Array<String>}> {
public static function inspect(addr:String):Null<{types:Array<String>, methods:Array<String>}> {
if (!isPresent(addr)) {
return null;
}
var types = getTypes(addr);
var methodsMap = cc.Peripheral.getMethods(addr).toArray();
var methods: Array<String> = [];
for (method in methodsMap) {
methods.push(method);
}
var methods = cc.Peripheral.getMethods(addr).toArray();
return {
types: types,
@@ -72,59 +58,138 @@ class Peripheral {
};
}
public function getScreen(addr: String): Null<Screen> {
/**
Cast peripheral to a specific type.
This is a temporary solution, maybe forever.
**/
public static function getFromType(addr:String, type:String):Null<IPeripheral> {
switch (type) {
case Computer.TYPE_NAME:
return getComputer(addr);
case Screen.TYPE_NAME:
return getScreen(addr);
case Drive.TYPE_NAME:
return getDrive(addr);
case EnergyStorage.TYPE_NAME:
return getEnergyStorage(addr);
case Modem.TYPE_NAME:
return getModem(addr);
case Printer.TYPE_NAME:
return getPrinter(addr);
case Speaker.TYPE_NAME:
return getSpeaker(addr);
case Redstone.TYPE_NAME:
return getRedstone(addr);
case Inventory.TYPE_NAME:
return getInventory(addr);
case GeoScanner.TYPE_NAME:
return getGeoScanner(addr);
}
return null;
}
public static function getScreen(addr:String):Null<Screen> {
var addr = safeGetAddr(addr, Screen.TYPE_NAME);
if (addr == null) return null;
if (addr == null)
return null;
return new Screen(addr);
}
public function getAllScreens(): Array<Screen> {
return [ for (addr in findAddrByType(Screen.TYPE_NAME)) new Screen(addr)];
public static function getAllScreens():Array<Screen> {
return [for (addr in findAddrByType(Screen.TYPE_NAME)) new Screen(addr)];
}
public function getModem(addr: String): Null<Modem> {
public static function getModem(addr:String):Null<Modem> {
var addr = safeGetAddr(addr, Modem.TYPE_NAME);
if (addr == null) return null;
if (addr == null)
return null;
return new Modem(addr);
}
public function getAllModems(): Array<Modem> {
return [ for (addr in findAddrByType(Modem.TYPE_NAME)) new Modem(addr)];
public static function getAllModems():Array<Modem> {
return [for (addr in findAddrByType(Modem.TYPE_NAME)) new Modem(addr)];
}
public function getDrive(addr: String): Null<Drive> {
public static function getDrive(addr:String):Null<Drive> {
var addr = safeGetAddr(addr, Drive.TYPE_NAME);
if (addr == null) return null;
if (addr == null)
return null;
return new Drive(addr);
}
public function getAllDrives(): Array<Drive> {
return [ for (addr in findAddrByType(Drive.TYPE_NAME)) new Drive(addr)];
public static function getAllDrives():Array<Drive> {
return [for (addr in findAddrByType(Drive.TYPE_NAME)) new Drive(addr)];
}
public function getRedstone(side: String): Redstone {
public static function getRedstone(side:String):Redstone {
// TODO: maybe handle restone differently to not duplicate event listeners
return new Redstone(side);
}
public function getPrinter(addr: String):Null<Printer> {
public static function getPrinter(addr:String):Null<Printer> {
var addr = safeGetAddr(addr, Printer.TYPE_NAME);
if (addr == null) return null;
if (addr == null)
return null;
return new Printer(addr);
}
public function getAllPrinters(): Array<Printer> {
return [ for (addr in findAddrByType(Printer.TYPE_NAME)) new Printer(addr)];
public static function getAllPrinters():Array<Printer> {
return [for (addr in findAddrByType(Printer.TYPE_NAME)) new Printer(addr)];
}
public function getEnergyStorage(addr: String): Null<EnergyStorage> {
public static function getEnergyStorage(addr:String):Null<EnergyStorage> {
var addr = safeGetAddr(addr, EnergyStorage.TYPE_NAME);
if (addr == null) return null;
if (addr == null)
return null;
return new EnergyStorage(addr);
}
public function getAllEnergyStorages(): Array<EnergyStorage> {
return [ for (addr in findAddrByType(EnergyStorage.TYPE_NAME)) new EnergyStorage(addr)];
public static function getAllEnergyStorages():Array<EnergyStorage> {
return [for (addr in findAddrByType(EnergyStorage.TYPE_NAME)) new EnergyStorage(addr)];
}
public static function getComputer(addr:String):Null<Computer> {
var addr = safeGetAddr(addr, Computer.TYPE_NAME);
if (addr == null)
return null;
return new Computer(addr);
}
public static function getAllComputers():Array<Computer> {
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 lib.Rect;
import lib.Pos;
import lib.ScreenPos;
class Printer implements IPeripheral {
public static inline final TYPE_NAME:String = "printer";
@@ -10,7 +10,7 @@ class Printer implements IPeripheral {
private final native:cc.periphs.Printer.Printer;
private final addr:String;
public function new(addr: String) {
public function new(addr:String) {
this.native = Peripheral.wrap(addr);
this.addr = addr;
}
@@ -19,36 +19,38 @@ class Printer implements IPeripheral {
return addr;
}
public function write(text: String){
public function getType():String {
return TYPE_NAME;
}
public function getCurserPos(): Pos {
return new Pos({x: 0, y: 0});
public function write(text:String) {}
public function getCurserPos():ScreenPos {
return new ScreenPos({x: 0, y: 0});
}
public function setCurserPos(pos: Pos){
public function setCurserPos(pos:ScreenPos) {
this.native.setCursorPos(pos.x, pos.y);
}
public function getPageSize(): Rect {
public function getPageSize():Rect {
var pos = this.native.getPageSize();
return new Rect({x: 0, y: 0}, {x: pos.x, y: pos.y});
}
public function newPage(): Bool{
public function newPage():Bool {
return this.native.newPage();
}
public function endPage(): Bool{
public function endPage():Bool {
return this.native.endPage();
}
public function setPageTitle(title: String){
public function setPageTitle(title:String) {
this.native.setPageTitle(title);
}
public function getInkLevel(): Float{
public function getInkLevel():Float {
return this.native.getInkLevel();
}

View File

@@ -1,37 +1,38 @@
package kernel.peripherals;
import kernel.peripherals.interfaces.IRedstone;
import haxe.ds.ReadOnlyArray;
import lib.Color;
using tink.CoreApi;
abstract BundleMask(Int) from cc.Colors.Color to cc.Colors.Color {
abstract BundleMask(Int) from cc.Colors.Color to cc.Colors.Color {
public inline function new(i:Int) {
this = i;
}
@:from
public static function fromColor(c: Color) {
public static function fromColor(c:Color) {
return new BundleMask(c);
}
@:op(A + B)
@:op(A | B)
public inline function combine(rhs: BundleMask):BundleMask {
public inline function combine(rhs:BundleMask):BundleMask {
return this | rhs;
}
@:op(A + B)
@:op(A | B)
public inline function combineWithColor(rhs: Color):BundleMask {
public inline function combineWithColor(rhs:Color):BundleMask {
return this | rhs;
}
public function getComponents(): ReadOnlyArray<Color> {
var components: Array<Color> = [];
public function getComponents():ReadOnlyArray<Color> {
var components:Array<Color> = [];
var mask = 1;
for (i in 0...16){
if ((this & mask) > 0 ){
for (i in 0...16) {
if ((this & mask) > 0) {
components.push(mask);
}
mask = mask << 1;
@@ -41,7 +42,8 @@ abstract BundleMask(Int) from cc.Colors.Color to cc.Colors.Color {
}
}
class Redstone implements IPeripheral {
class Redstone implements IPeripheral implements IRedstone {
public static inline final TYPE_NAME:String = "redstone"; // TODO: there is technically not a type for redstone.
public final onChange:Signal<Noise>;
@@ -53,25 +55,28 @@ class Redstone implements IPeripheral {
private var bundleInputState:BundleMask;
@:allow(kernel.peripherals)
private function new(side: Side) {
private function new(side:Side) {
this.addr = side;
this.onChange = this.onChangeTrigger.asSignal();
updateState();
KernelEvents.instance.onRedstone.handle(()->{
if ((this.getAnalogInput() != this.analogInputState) || (this.bundleInputState != this.getBundledInput())){
KernelEvents.onRedstone.handle(() -> {
if ((this.getAnalogInput() != this.analogInputState) || (this.bundleInputState != this.getBundledInput())) {
updateState();
this.onChangeTrigger.trigger(null);
}
});
}
public function getAddr():String {
return this.addr;
}
public function getType():String {
return TYPE_NAME;
}
private function updateState() {
this.analogInputState = cc.Redstone.getAnalogInput(this.addr);
this.bundleInputState = cc.Redstone.getBundledInput(this.addr);
@@ -79,33 +84,33 @@ class Redstone implements IPeripheral {
public inline function setOutput(on:Bool):Void {
this.analogInputState = 15;
cc.Redstone.setOutput(this.addr,on);
cc.Redstone.setOutput(this.addr, on);
}
public inline function getOutput(): Bool {
public inline function getOutput():Bool {
return cc.Redstone.getOutput(this.addr);
}
public inline function getInput(): Bool {
public inline function getInput():Bool {
return cc.Redstone.getInput(this.addr);
}
public inline function setAnalogOutput(strength:Int): Void {
public inline function setAnalogOutput(strength:Int):Void {
this.analogInputState = strength;
cc.Redstone.setAnalogOutput(this.addr,strength);
cc.Redstone.setAnalogOutput(this.addr, strength);
}
public inline function getAnalogOutput(): Int {
public inline function getAnalogOutput():Int {
return cc.Redstone.getAnalogOutput(this.addr);
}
public inline function getAnalogInput(): Int {
public inline function getAnalogInput():Int {
return cc.Redstone.getAnalogInput(this.addr);
}
public inline function setBundledOutput(output: BundleMask) {
public inline function setBundledOutput(output:BundleMask) {
this.bundleInputState = output;
cc.Redstone.setBundledOutput(this.addr,output);
cc.Redstone.setBundledOutput(this.addr, output);
}
public inline function getBundledOutput():BundleMask {
@@ -116,8 +121,7 @@ class Redstone implements IPeripheral {
return cc.Redstone.getBundledInput(this.addr);
}
public inline function testBundledInput(mask: Color): Bool {
return cc.Redstone.testBundledInput(this.addr,mask);
public inline function testBundledInput(mask:Color):Bool {
return cc.Redstone.testBundledInput(this.addr, mask);
}
}

View File

@@ -1,21 +1,21 @@
package kernel.peripherals;
import cc.Peripheral;
import lib.Pos;
import lib.ScreenPos;
import cc.Term.TerminalSize;
import kernel.ui.TermWriteable;
import kernel.ui.ITermWriteable;
import lib.Vec.Vec2;
import lib.Color;
using tink.CoreApi;
class Screen implements TermWriteable implements IPeripheral {
class Screen implements ITermWriteable implements IPeripheral {
public static inline final TYPE_NAME:String = "monitor";
private final nativ:cc.periphs.Monitor.Monitor;
private final addr:String;
private final onResizeTrigger:SignalTrigger<Vec2<Int>> = Signal.trigger();
private final onResizeTrigger:SignalTrigger<Vec2<Int>> = Signal.trigger();
public final onResize:Signal<Vec2<Int>>;
@@ -26,7 +26,7 @@ class Screen implements TermWriteable implements IPeripheral {
this.nativ = Peripheral.wrap(addr);
this.addr = addr;
KernelEvents.instance.onMonitorResize.handle(addr -> {
KernelEvents.onMonitorResize.handle(addr -> {
if (addr == this.addr) {
onResizeTrigger.trigger(getSize());
}
@@ -39,6 +39,10 @@ class Screen implements TermWriteable implements IPeripheral {
return this.addr;
}
public function getType():String {
return TYPE_NAME;
}
public function getTextScale():Float {
return nativ.getTextScale();
}
@@ -55,7 +59,7 @@ class Screen implements TermWriteable implements IPeripheral {
nativ.scroll(y);
}
public function getCursorPos():Pos {
public function getCursorPos():ScreenPos {
var rtn = nativ.getCursorPos();
return {
x: rtn.x - 1,
@@ -115,6 +119,6 @@ class Screen implements TermWriteable implements IPeripheral {
this.setBackgroundColor(Black);
this.setTextColor(White);
this.clear();
this.setCursorPos(0,0);
this.setCursorPos(0, 0);
}
}

View File

@@ -9,7 +9,7 @@ enum abstract Side(String) to String {
var Back = "back";
@:from
static public function fromString(s: String) {
static public function fromString(s:String) {
switch (s) {
case "top":
return Top;

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.
**/
interface Process {
public function run(handle: ProcessHandle): Void;
interface IProcess {
public function run(handle:ProcessHandle):Void;
}

View File

@@ -1,35 +1,37 @@
package kernel.ps;
import kernel.turtle.TurtleMutex;
import kernel.ps.ProcessManager.PID;
import kernel.ui.WindowContext;
import kernel.ui.WindowManager;
import haxe.ds.ReadOnlyArray;
using tink.CoreApi;
typedef HandleConfig = {
?args: Array<String>,
?onWrite: Callback<String>,
?onExit: Callback<Bool>,
?args:Array<String>,
?onWrite:Callback<String>,
?onExit:Callback<Bool>,
}
class ProcessHandle {
public var args(get,null): ReadOnlyArray<String>;
public var args(get, null):ReadOnlyArray<String>;
private final pid: PID;
private final pid:PID;
private final config:HandleConfig;
private final closeFuture: Future<Bool>;
private var closeFutureResolev: Bool -> Void;
private final windowContexts: Array<WindowContext> = [];
private final closeFuture:Future<Bool>;
private var closeFutureResolev:Bool->Void;
private final windowContexts:Array<WindowContext> = [];
private final cbLinks:Array<CallbackLink> = [];
private final deferFuncs:Array<Void -> Void> = [];
private var hasExited: Bool = false;
private final deferFuncs:Array<Void->Void> = [];
private var hasExited:Bool = false;
@:allow(kernel.ps.ProcessManager)
private function new(config: HandleConfig,pid: PID) {
private function new(config:HandleConfig, pid:PID) {
this.config = config;
this.pid = pid;
this.closeFuture = new Future<Bool>((trigger)->{
this.closeFuture = new Future<Bool>((trigger) -> {
this.closeFutureResolev = trigger;
return null;
});
@@ -39,42 +41,45 @@ class ProcessHandle {
}
}
public function onExit(): Future<Bool> {
public function onExit():Future<Bool> {
return this.closeFuture;
}
public function close(success: Bool = true): Void {
public function close(success:Bool = true):Void {
this.hasExited = true;
this.dispose();
EndOfLoop.endOfLoop(() ->{this.closeFutureResolev(success);});
EndOfLoop.endOfLoop(() -> {
this.closeFutureResolev(success);
});
ProcessManager.removeProcess(this.pid);
}
public function write(message: String): Void {
if (this.hasExited) return;
if (this.config.onWrite != null){
public function write(message:String):Void {
if (this.hasExited)
return;
if (this.config.onWrite != null) {
this.config.onWrite.invoke(message);
}
}
public function writeLine(message: String): Void {
public function writeLine(message:String):Void {
this.write(message + "\n");
}
public function createBufferdWindowContext(): WindowContext {
var ctx = WindowManager.instance.createNewContext();
public function createBufferdWindowContext():WindowContext {
var ctx = WindowManager.createNewContext();
this.windowContexts.push(ctx);
return ctx;
}
public function createStatelessWindowContext(): {ctx:WindowContext, setRenderFunc: (() -> Void) -> Void, requestRender:() -> Void} {
var ctx = WindowManager.instance.createNewStatelessContext();
public function createStatelessWindowContext():{ctx:WindowContext, setRenderFunc:(() -> Void)->Void, requestRender:() -> Void} {
var ctx = WindowManager.createNewStatelessContext();
this.windowContexts.push(ctx.ctx);
return ctx;
}
public function getWindowContexts(): ReadOnlyArray<WindowContext> {
public function getWindowContexts():ReadOnlyArray<WindowContext> {
return this.windowContexts;
}
@@ -88,19 +93,31 @@ class ProcessHandle {
}
}
public function getPid(): PID {
public function getPid():PID {
return this.pid;
}
public function addCallbackLink(link: CallbackLink) {
public function addCallbackLink(link:CallbackLink) {
this.cbLinks.push(link);
}
public function addDeferFunc(func: Void -> Void) {
public function addDeferFunc(func:Void->Void) {
this.deferFuncs.push(func);
}
function get_args():ReadOnlyArray<String> {
return this.config.args;
public function get_args():ReadOnlyArray<String> {
return this.config.args != null ? this.config.args : [];
}
public function claimTurtleMutex():Bool {
if (TurtleMutex.claim(this.pid)) {
this.addDeferFunc(() -> {
TurtleMutex.release(this.pid);
});
return true;
}
return false;
}
}

View File

@@ -8,17 +8,17 @@ using tink.CoreApi;
typedef PID = Int;
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 handle = new ProcessHandle(config, pid);
processList.set(pid, handle);
try{
try {
process.run(handle);
}catch(e:Dynamic){
} catch (e:Dynamic) {
Log.error("Error while running process: " + e);
handle.close(false);
}
@@ -26,8 +26,8 @@ class ProcessManager {
return pid;
}
public static function kill(pid: PID) {
if (!processList.exists(pid)){
public static function kill(pid:PID) {
if (!processList.exists(pid)) {
Log.warn("Trying to kill non-existing process: " + pid);
return;
}
@@ -37,7 +37,7 @@ class ProcessManager {
handle.close();
}
private static function createPID(): PID {
private static function createPID():PID {
// TODO: better PID generation
// generate a random PID
@@ -45,7 +45,7 @@ class ProcessManager {
}
@:allow(kernel.ui.WindowManager)
private static function getProcess(pid:PID):Null<ProcessHandle>{
private static function getProcess(pid:PID):Null<ProcessHandle> {
return processList.get(pid);
}

View File

@@ -1,39 +1,38 @@
package kernel.service;
import kernel.ps.Process;
import kernel.ps.IProcess;
import kernel.ps.ProcessManager;
import kernel.binstore.BinStore;
using tink.CoreApi;
class Service {
public final binName:String;
public final name:String;
public final args:Array<String>;
public var pid:PID;
public var ps: Process;
public final binName:String;
public final name:String;
public final args:Array<String>;
public var pid:PID;
public var ps:IProcess;
@:allow(kernel.service.ServiceManager)
private function new(binName: String,name: String,?args: Array<String> ) {
this.binName = binName;
this.name = name;
this.args = args ?? [];
}
@:allow(kernel.service.ServiceManager)
private function new(binName:String, name:String, ?args:Array<String>) {
this.binName = binName;
this.name = name;
this.args = args ?? [];
}
public function start() {
var bin = BinStore.instance.getBinByAlias(this.binName);
public function start() {
var ps = BinStore.instantiate(this.binName);
if (bin == null){
throw new Error('Bin ${this.binName} not found');
}
if (ps == null) {
throw new Error('Bin ${this.binName} not found');
}
this.ps = Type.createInstance(bin.c,this.args);
this.ps = ps;
this.pid = ProcessManager.run(this.ps,{});
}
public function stop() {
ProcessManager.kill(this.pid);
}
this.pid = ProcessManager.run(this.ps, {});
}
public function stop() {
ProcessManager.kill(this.pid);
}
}

View File

@@ -7,137 +7,143 @@ import lib.KVStore;
using tink.CoreApi;
class ServiceManager {
public static var instance: ServiceManager;
private static final services:Map<String, Service> = new Map();
private final services:Map<String,Service> = new Map();
@:allow(kernel.Init)
private static function init() {
startAllEnabled();
}
@:allow(kernel.Init)
private function new() {
this.startAllEnabled();
}
/**
Add a service to be automatically started.
**/
public static function enable(name:String):Outcome<Noise, String> {
if (!services.exists(name)) {
return Failure("Service must be started before enable");
}
/**
Add a service to be automatically started.
**/
public function enable(name: String) {
if (!this.services.exists(name)){
return; // Service must be started
}
var store = KVStore.getStoreForClass();
store.load();
var store = KVStore.getStoreForClass();
var enabled = store.get("enabled", []);
enabled.push(name);
store.set("enabled", enabled);
var enabled = store.get("enabled",[]);
enabled.push(name);
store.set("enabled",enabled);
store.save();
store.save();
}
return Success(Noise);
}
/**
Remove a service from being automatically started.
**/
private function disable(name: String) {
var store = KVStore.getStoreForClass();
var enabled: Array<String> = store.get("enabled");
var index = enabled.indexOf(name);
if (index == -1){
return;
}
/**
Remove a service from being automatically started.
**/
private static function disable(name:String):Outcome<Noise, String> {
var store = KVStore.getStoreForClass();
store.load();
enabled.splice(index,1);
store.save();
}
var enabled:Array<String> = store.get("enabled");
var index = enabled.indexOf(name);
if (index == -1) {
return Failure("Service not found");
}
private function startAllEnabled() {
var store = KVStore.getStoreForClass();
var enabled: Array<String> = store.get("enabled",[]);
for (name in enabled){
this.start(name);
}
}
enabled.splice(index, 1);
store.save();
private function load(name: String): Null<Service> {
var store = new KVStore('service/${name}');
store.load();
if (!store.exists("service")){
return null;
}
return Success(Noise);
}
return store.get("service");
}
private static function startAllEnabled() {
var store = KVStore.getStoreForClass();
store.load();
public function register(name: String, binName: String,args: Array<String>): Outcome<Noise,String> {
if (BinStore.instance.getBinByAlias(binName) == null){
return Failure("bin not found");
}
var enabled:Array<String> = store.get("enabled", []);
for (name in enabled) {
start(name);
}
}
if (this.load(name) != null){
return Failure("service already exists");
}
private static function load(name:String):Null<Service> {
var store = new KVStore('service/${name}');
store.load();
if (!store.exists("service")) {
return null;
}
var service = new Service(binName,name,args);
return store.get("service");
}
var store = new KVStore('service/${name}');
store.set("service",service);
store.save();
public static function register(name:String, binName:String, args:Array<String>):Outcome<Noise, String> {
if (BinStore.getNameByAlias(binName) == null) {
return Failure("bin not found");
}
Log.info('Service ${name} registered');
return Success(Noise);
}
if (load(name) != null) {
return Failure("service already exists");
}
public function unregister(name: String): Outcome<Noise,String> {
if (this.services.exists(name)){
return Failure("service is running");
}
var service = new Service(binName, name, args);
KVStore.removeNamespace('service/${name}');
Log.info('Service ${name} unregistered');
return Success(Noise);
}
var store = new KVStore('service/${name}');
store.set("service", service);
store.save();
public function start(name: String): Outcome<Noise,String> {
var service = this.load(name);
if (service == null){
return Failure("service not found");
}
Log.info('Service ${name} registered');
return Success(Noise);
}
service.start();
this.services.set(name,service);
public static function unregister(name:String):Outcome<Noise, String> {
if (services.exists(name)) {
return Failure("service is running");
}
Log.info('Service ${name} started');
return Success(Noise);
}
KVStore.removeNamespace('service/${name}');
Log.info('Service ${name} unregistered');
return Success(Noise);
}
public function stop(name: String): Outcome<Noise,String> {
if (!this.services.exists(name)){
return Failure("service not found");
}
public static function start(name:String):Outcome<Noise, String> {
var service = load(name);
if (service == null) {
return Failure("service not found");
}
var service = this.services.get(name);
service.stop();
this.services.remove(name);
service.start();
services.set(name, service);
Log.info('Service ${name} stopped');
return Success(Noise);
}
Log.info('Service ${name} started');
return Success(Noise);
}
public function listRunning(): Array<String> {
var running = [];
for (name in this.services.keys()){
running.push(name);
}
return running;
}
public static function stop(name:String):Outcome<Noise, String> {
if (!services.exists(name)) {
return Failure("service not found");
}
public function get(name: String): Null<Dynamic> {
if (!this.services.exists(name)){
return null;
}
var service = services.get(name);
service.stop();
services.remove(name);
// TODO: Maybe there is a way to check types here?
Log.info('Service ${name} stopped');
return Success(Noise);
}
var srv = this.services.get(name);
return srv.ps;
}
public static function listRunning():Array<String> {
var running = [];
for (name in services.keys()) {
running.push(name);
}
return running;
}
public static function get(name:String):Null<Dynamic> {
if (!services.exists(name)) {
return null;
}
// TODO: Maybe there is a way to check types here?
var srv = services.get(name);
return srv.ps;
}
}

View File

@@ -1,27 +1,18 @@
package kernel.turtle;
import kernel.log.Log;
import kernel.turtle.Types;
import kernel.gps.INS;
using tink.CoreApi;
class Turtle {
public static var instance:Turtle;
public static final MAX_SLOTS:Int = 16;
@:allow(kernel.Init)
private function new() {
if (!Turtle.isTurtle()) {
Log.warn("Tried to initialize Turtle, but it is not available.");
}
}
public static function isTurtle():Bool {
return true; // TODO: Implement
return cc.Turtle != null;
}
private function conterToOutcome(r:cc.Turtle.TurtleActionResult):Outcome<Noise, String> {
private static function conterToOutcome(r:cc.Turtle.TurtleActionResult):Outcome<Noise, String> {
if (r.successful) {
return Outcome.Success(null);
} else {
@@ -33,49 +24,60 @@ class Turtle {
}
}
public function forward():Outcome<Noise, String> {
public static function forward():Outcome<Noise, String> {
var r = cc.Turtle.forward();
var r2 = conterToOutcome(r);
if (r2.isSuccess()) INS.instance.moveForward();
if (r2.isSuccess())
INS.moveForward();
return r2;
}
public function back():Outcome<Noise, String> {
public static function back():Outcome<Noise, String> {
var r = cc.Turtle.back();
var r2 = conterToOutcome(r);
if (r2.isSuccess()) INS.instance.moveBackward();
if (r2.isSuccess())
INS.moveBackward();
return r2;
}
public function up():Outcome<Noise, String> {
public static function up():Outcome<Noise, String> {
var r = cc.Turtle.up();
var r2 = conterToOutcome(r);
if (r2.isSuccess()) INS.instance.moveUp();
if (r2.isSuccess())
INS.moveUp();
return r2;
}
public function down():Outcome<Noise, String> {
public static function down():Outcome<Noise, String> {
var r = cc.Turtle.down();
var r2 = conterToOutcome(r);
if (r2.isSuccess()) INS.instance.moveDown();
if (r2.isSuccess())
INS.moveDown();
return r2;
}
public function turnLeft():Outcome<Noise, String> {
public static function turnLeft():Outcome<Noise, String> {
var r = cc.Turtle.turnLeft();
var r2 = conterToOutcome(r);
if (r2.isSuccess()) INS.instance.turnRight();
if (r2.isSuccess())
INS.turnRight();
return r2;
}
public function turnRight():Outcome<Noise, String> {
public static function turnRight():Outcome<Noise, String> {
var r = cc.Turtle.turnRight();
var r2 = conterToOutcome(r);
if (r2.isSuccess()) INS.instance.turnRight();
if (r2.isSuccess())
INS.turnRight();
return r2;
}
public function dig(dir:InteractDirections, ?toolSide:ToolSide):Outcome<Noise, String> {
/**
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> {
var r:cc.Turtle.TurtleActionResult;
// FIXME: upstream needs to be fixed to accept ToolSide
@@ -90,7 +92,37 @@ class Turtle {
return conterToOutcome(r);
}
public function place(dir:InteractDirections):Outcome<Noise, String> {
/**
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> {
var r:cc.Turtle.TurtleActionResult;
switch dir {
case Front:
@@ -103,7 +135,7 @@ class Turtle {
return conterToOutcome(r);
}
public function placeSign(dir:InteractDirections, text:String):Outcome<Noise, String> {
public static function placeSign(dir:InteractDirections, text:String):Outcome<Noise, String> {
var r:cc.Turtle.TurtleActionResult;
switch dir {
case Front:
@@ -116,32 +148,26 @@ class Turtle {
return conterToOutcome(r);
}
public function drop(dir:InteractDirections, ?count:Int):Outcome<Noise, String> {
public static function drop(dir:InteractDirections, ?count:Int):Outcome<Noise, String> {
var r = cc.Turtle.drop(count);
return conterToOutcome(r);
}
public function selectSlot(slot:TurtleSlot):Outcome<Noise, Noise> {
// TODO: slot in bounds?
var r = cc.Turtle.select(slot + 1);
public static function selectSlot(slot:TurtleSlot):Outcome<Noise, Noise> {
var r = cc.Turtle.select(slot.toCCSlot());
return (r) ? Outcome.Success(null) : Outcome.Failure("Slot out of bounds");
}
public function getItemCount(?slot:TurtleSlot):Int {
// TODO: slot in bounds?
return cc.Turtle.getItemCount(slot + 1);
public static function getItemCount(?slot:TurtleSlot):Int {
return cc.Turtle.getItemCount(slot.toCCSlot());
}
public function getItemSpace(?slot:TurtleSlot):Int {
// TODO: slot in bounds?
return cc.Turtle.getItemSpace(slot + 1);
public static function getItemSpace(?slot:TurtleSlot):Int {
return cc.Turtle.getItemSpace(slot.toCCSlot());
}
public function detect(dir:InteractDirections):Bool {
public static function detect(dir:InteractDirections):Bool {
switch dir {
case Front:
return cc.Turtle.detect();
@@ -152,7 +178,7 @@ class Turtle {
}
}
public function compareToSlot(dir:InteractDirections):Bool {
public static function compareToSlot(dir:InteractDirections):Bool {
switch dir {
case Front:
return cc.Turtle.compare();
@@ -163,7 +189,7 @@ class Turtle {
}
}
public function attack(dir:InteractDirections, ?toolSide:ToolSide):Outcome<Noise, String> {
public static function attack(dir:InteractDirections, ?toolSide:ToolSide):Outcome<Noise, String> {
var r:cc.Turtle.TurtleActionResult;
// FIXEM: upstream needs to be fixed to accept ToolSide
@@ -179,7 +205,7 @@ class Turtle {
return conterToOutcome(r);
}
public function suckItem(dir:InteractDirections, ?ammount:Int):Outcome<Noise, String> {
public static function suckItem(dir:InteractDirections, ?ammount:Int):Outcome<Noise, String> {
// TODO: ammount in bounds?
var r:cc.Turtle.TurtleActionResult;
@@ -195,40 +221,40 @@ class Turtle {
return conterToOutcome(r);
}
public function getFuelLevel():Int {
public static function getFuelLevel():Int {
var r = cc.Turtle.getFuelLevel(); // FIXME: can be a string. Has to be fixed upstream
return r;
}
public function refuel(?ammount:Int):Outcome<Noise, String> {
public static function refuel(?ammount:Int):Outcome<Noise, String> {
var r = cc.Turtle.refuel(ammount);
return conterToOutcome(r);
}
public function canRefultWithSlot():Bool {
public static function canRefultWithSlot():Bool {
var r = cc.Turtle.refuel(0);
return r.successful;
}
public function compareSlot(otherSlot:TurtleSlot):Bool {
return cc.Turtle.compareTo(otherSlot + 1);
public static function compareSlot(otherSlot:TurtleSlot):Bool {
return cc.Turtle.compareTo(otherSlot.toCCSlot());
}
public function transfer(from:TurtleSlot, to:TurtleSlot, ?count:Int):Outcome<Noise, Noise> {
this.selectSlot(from);
var r = cc.Turtle.transferTo(to + 1, count);
public static function transfer(from:TurtleSlot, to:TurtleSlot, ?count:Int):Outcome<Noise, Noise> {
selectSlot(from);
var r = cc.Turtle.transferTo(to.toCCSlot(), count);
return r ? Outcome.Success(null) : Outcome.Failure(null);
}
public function getSelectedSlot():TurtleSlot {
public static function getSelectedSlot():TurtleSlot {
return cc.Turtle.getSelectedSlot() - 1;
}
public function getFuelLimit():Int {
public static function getFuelLimit():Int {
return cc.Turtle.getFuelLimit(); // FIXME: can be a string. Has to be fixed upstream
}
public function equip(side:ToolSide):Outcome<Noise, String> {
public static function equip(side:ToolSide):Outcome<Noise, String> {
switch side {
case Left:
return conterToOutcome(cc.Turtle.equipLeft());
@@ -237,7 +263,7 @@ class Turtle {
}
}
public function inspect(dir:InteractDirections):Outcome<BlockInspect, String> {
public static function inspect(dir:InteractDirections):Outcome<BlockInspect, String> {
var r:cc.Turtle.TurtleInspectResult;
switch dir {
case Front:
@@ -260,8 +286,8 @@ class Turtle {
});
}
public function getItemDetail(?detailed:Bool = false, ?slot:TurtleSlot):Option<ItemInspect> {
var r = cc.Turtle.getItemDetail(slot + 1); // FIXME: can take detailed as flag. Has to be fixed upstream
public static function getItemDetail(slot:TurtleSlot, ?detailed:Bool = false):Option<ItemInspect> {
var r = cc.Turtle.getItemDetail(slot.toCCSlot()); // FIXME: can take detailed as flag. Has to be fixed upstream
if (r == null) {
return None;
@@ -275,7 +301,7 @@ class Turtle {
});
}
public function craft(?limit:Int = 64):Outcome<Noise, String> {
public static function craft(?limit:Int = 64):Outcome<Noise, String> {
if (limit < 1 || limit > 64) {
return Outcome.Failure("Crafting limit out of bounds");
}

View File

@@ -0,0 +1,52 @@
package kernel.turtle;
import kernel.log.Log;
import cc.OS;
import kernel.ps.ProcessManager.PID;
/**
Make sure only one process can access the turtle at a time.
**/
class TurtleMutex {
private static var claimedPid:PID = -1;
public static var threadFunc:() -> Void;
@:allow(kernel.ps.ProcessHandle)
private static function claim(pid:PID):Bool {
if (claimedPid == -1) {
claimedPid = pid;
return true;
}
return false;
}
@:allow(kernel.ps.ProcessHandle)
private static function release(pid:PID) {
if (claimedPid == pid) {
claimedPid = -1;
stopTurtleThread();
}
}
@:allow(kernel.KernelEvents)
private static function runThreadFunc() {
if (threadFunc != null) {
try {
threadFunc();
} catch (e) {
Log.error("Error in tthread: " + e);
}
threadFunc = null;
}
}
public static function runInTThread(func:() -> Void) {
threadFunc = func;
OS.queueEvent("tthread");
}
public static function stopTurtleThread() {
threadFunc = null;
KernelEvents.startTurtleCoroutine();
}
}

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,5 +22,3 @@ typedef ItemInspect = {
public var damage:Int;
public var count:Int;
}
typedef TurtleSlot = Int;

View File

@@ -1,9 +1,9 @@
package kernel.ui;
import lib.Pos;
import lib.ScreenPos;
import lib.Vec.Vec2;
import lib.Color;
import kernel.ui.TermWriteable;
import kernel.ui.ITermWriteable;
using tink.CoreApi;
@@ -11,15 +11,15 @@ using tink.CoreApi;
A term writer that can switch beetween its internal buffer and another termwriter.
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 var target:TermWriteable;
private var target:ITermWriteable;
private var enabled:Bool = false;
private var onResizeLink:CallbackLink;
@:allow(kernel.ui)
private function new(?target:TermWriteable) {
private function new(?target:ITermWriteable) {
setTarget(target);
if (enabled) {
@@ -44,7 +44,7 @@ class BufferedVirtualTermWriter implements VirtualTermWriter extends TermBuffer
return enabled;
}
public function setTarget(newTarget:TermWriteable) {
public function setTarget(newTarget:ITermWriteable) {
if (newTarget != null) {
super.setSize(newTarget.getSize());
@@ -86,7 +86,7 @@ class BufferedVirtualTermWriter implements VirtualTermWriter extends TermBuffer
super.scroll(y);
}
public override function getCursorPos():Pos {
public override function getCursorPos():ScreenPos {
if (isEnabled()) {
return target.getCursorPos();
} else {
@@ -103,15 +103,15 @@ class BufferedVirtualTermWriter implements VirtualTermWriter extends TermBuffer
}
public override function getCursorBlink():Bool {
if (isEnabled()){
if (isEnabled()) {
return target.getCursorBlink();
}else{
} else {
return super.getCursorBlink();
}
}
public override function setCursorBlink(blink:Bool) {
if (isEnabled()){
if (isEnabled()) {
target.setCursorBlink(blink);
}
super.setCursorBlink(blink);

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