Compare commits

..

166 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
7c7529ae39 use new kernel settings 2023-06-07 21:29:29 +02:00
6fcbcfce8d added kernel settings 2023-06-07 21:29:10 +02:00
86793bfdc1 terminal kill runnig apps 2023-06-07 20:10:51 +02:00
644ebe1c05 improved HelloWord service 2023-06-07 20:09:54 +02:00
7aa5698486 improved Processhandle 2023-06-07 20:09:34 +02:00
9a9041bd98 killing non existing process does not throw error 2023-06-07 20:07:37 +02:00
f92d777ee1 added EndOfLoop 2023-06-07 20:07:03 +02:00
f85ff77728 added energyStorage perf 2023-06-07 15:15:50 +02:00
179 changed files with 7831 additions and 3222 deletions

3
.gitignore vendored
View File

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

View File

@@ -1,36 +1,68 @@
BUNDLE_NAME = bundle.lua BUNDLE_NAME = bundle.lua
HAXE_NAME = haxe.lua HAXE_NAME = haxe.lua
MINIFYD_NAME = bundle.min.lua MINIFYD_NAME = bundle.min.lua
HAXE_ZIP_NAME = "haxe.zip"
UNPACK_NAME = unpack.lua
UNPACK_POLYFILLED_NAME = unpack.polyfill.lua
UNPACK_MINIFYD_NAME = unpack.min.lua
BUILD_DIR = build BUILD_DIR = build
HAXE_FLAGS = HAXE_FLAGS = -D message.reporting=pretty
POLYFILLED_NAME = bundle.polyfill.lua POLYFILLED_NAME = bundle.polyfill.lua
POLYFILL_SRC = src/polyfill.lua POLYFILL_SRC = src/polyfill.lua
CREAFTOS_PATH = craftos CREAFTOS_PATH = craftos
BUILD_HXML = build.hxml
HAXE_PATH := $(BUILD_DIR)/$(HAXE_NAME) HAXE_PATH := $(BUILD_DIR)/$(HAXE_NAME)
MIN_PATH := $(BUILD_DIR)/$(MINIFYD_NAME) MIN_PATH := $(BUILD_DIR)/$(MINIFYD_NAME)
POLYFILL_PATH := $(BUILD_DIR)/$(POLYFILLED_NAME) POLYFILL_PATH := $(BUILD_DIR)/$(POLYFILLED_NAME)
UNPACK_PATH := $(BUILD_DIR)/$(UNPACK_NAME)
UNPACK_MINIFYD_PATH := $(BUILD_DIR)/$(UNPACK_MINIFYD_NAME)
HAXE_ZIP_PATH := $(BUILD_DIR)/$(HAXE_ZIP_NAME)
UNPACK_POLYFILLED_PATH := $(BUILD_DIR)/$(UNPACK_POLYFILLED_NAME)
all: clean build all: clean build unpack
build: HAXE_FLAGS += --main kernel.Entrypoint -D analyzer-optimize build: HAXE_FLAGS += -D analyzer-optimize -D no-traces
build: $(MIN_PATH) build: $(MIN_PATH)
debug: HAXE_FLAGS += -D webconsole -D error_stack --debug debug: HAXE_FLAGS += -D webconsole -D error_stack --debug
debug: build debug: $(MIN_PATH)
unpack: $(UNPACK_MINIFYD_PATH) $(HAXE_ZIP_PATH)
$(HAXE_PATH): HAXE_FLAGS += --main kernel.Entrypoint --lua $(HAXE_PATH)
$(HAXE_PATH): $(shell find src -name '*.hx') $(HAXE_PATH): $(shell find src -name '*.hx')
haxe build.hxml $(HAXE_FLAGS) haxe $(BUILD_HXML) $(HAXE_FLAGS)
$(POLYFILL_PATH): $(POLYFILL_SRC) $(HAXE_PATH) $(POLYFILL_PATH): $(POLYFILL_SRC) $(HAXE_PATH)
cat $(POLYFILL_SRC) $(HAXE_PATH) > $@ cat $(POLYFILL_SRC) $(HAXE_PATH) > $@
$(MIN_PATH): $(POLYFILL_PATH) $(MIN_PATH): $(POLYFILL_PATH)
node minify.js $(POLYFILL_PATH) $@ node tools/minify.js < $(POLYFILL_PATH) > $@
$(HAXE_ZIP_PATH): $(MIN_PATH)
node tools/zlibDeflate.js < $(MIN_PATH) > $@
$(UNPACK_PATH): HAXE_FLAGS += --main Unpack -D analyzer-optimize --lua $(UNPACK_PATH)
$(UNPACK_PATH): $(shell find src -name '*.hx')
haxe $(BUILD_HXML) $(HAXE_FLAGS)
$(UNPACK_POLYFILLED_PATH): $(UNPACK_PATH)
cat $(POLYFILL_SRC) $(UNPACK_PATH) > $@
$(UNPACK_MINIFYD_PATH): $(UNPACK_POLYFILLED_PATH)
node tools/minify.js < $(UNPACK_POLYFILLED_PATH) > $@
.PHONY: deps .PHONY: deps
deps: deps: deps-hx deps-node
haxelib install all --always && yarn install
.PHONY: deps-hx
deps-hx:
haxelib install all --always
.PHONY: deps-node
deps-node:
npm install
.PHONY: clean .PHONY: clean
clean: clean:
@@ -39,7 +71,7 @@ clean:
.PHONY: watch .PHONY: watch
watch: watch:
find src -name "*.hx" | entr make debug find src | entr make debug
.PHONY: emulator .PHONY: emulator
emulator: emulator:
@@ -47,4 +79,12 @@ emulator:
.PHONY: webconsole .PHONY: webconsole
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 # 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/) [CC wiki](https://tweaked.cc/)

View File

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

View File

@@ -2,66 +2,68 @@ package bin;
import lib.CLIAppBase; import lib.CLIAppBase;
import kernel.gps.INS; import kernel.gps.INS;
import lib.Pos3; import lib.WorldPos;
import lib.Vec.Vec3; import lib.Vec.Vec3;
using tink.CoreApi; using tink.CoreApi;
@:build(macros.Binstore.includeBin("GPS", ["gps"]))
class GPS extends CLIAppBase { class GPS extends CLIAppBase {
public function new() { public function new() {
registerSyncSubcommand("set", (args)->{ registerSyncSubcommand("set", (args) -> {
var x: Float = Std.parseFloat(args[0]); var x:Float = args.getFloat("x");
var y: Float = Std.parseFloat(args[1]); var y:Float = args.getFloat("y");
var z: Float = Std.parseFloat(args[2]); var z:Float = args.getFloat("z");
var pos: Pos3 = new Vec3<Float>(x, y, z);
kernel.gps.GPS.instance.setManualPosition(pos);
return true;
},"<x> <y> <z>");
registerSyncSubcommand("status",(args)->{ var pos:WorldPos = new Vec3<Float>(x, y, z);
var pos = kernel.gps.GPS.instance.getPosition();
kernel.gps.GPS.setManualPosition(pos);
return true;
}, [Float("x"), Float("y"), Float("z")]);
registerSyncSubcommand("status", (args) -> {
var pos = kernel.gps.GPS.getPosition();
if (pos != null) { if (pos != null) {
handle.writeLine('Position x:${pos.x} y:${pos.y} z:${pos.z}'); handle.writeLine('Position x:${pos.x} y:${pos.y} z:${pos.z}');
} else { } else {
handle.writeLine("Position not available"); handle.writeLine("Position not available");
return true; return true;
} }
var acc = kernel.gps.GPS.instance.getAccuracy(); var acc = kernel.gps.GPS.getAccuracy();
if (acc == 1){ if (acc == 1) {
handle.writeLine("Accuracy: Low"); handle.writeLine("Accuracy: Low");
} else if (acc == 2){ } else if (acc == 2) {
handle.writeLine("Accuracy: Medium"); handle.writeLine("Accuracy: Medium");
} else if (acc == 3){ } else if (acc == 3) {
handle.writeLine("Accuracy: High"); handle.writeLine("Accuracy: High");
} }
var ins = INS.instance.getHeading(); var ins = INS.getHeading();
if (ins != null) { if (ins != null) {
handle.writeLine('INS heading: ${ins.x} y:${ins.y} z:${ins.z}'); handle.writeLine('INS heading: ${ins.x} y:${ins.y} z:${ins.z}');
} else { } else {
handle.writeLine("INS heading not available"); handle.writeLine("INS heading not available");
} }
return true; return true;
}); });
registerAsyncSubcommand("locate",(args)->{ registerAsyncSubcommand("locate", (args) -> {
return kernel.gps.GPS.instance.locate().map((pos)->{ return kernel.gps.GPS.locate().map((result) -> {
if (pos != null) { switch result {
handle.writeLine('Position x:${pos.x} y:${pos.y} z:${pos.z}'); case Success(pos):
} else { handle.writeLine('Position x:${pos.x} y:${pos.y} z:${pos.z}');
handle.writeLine("Position not available"); case Failure(err):
handle.writeLine("Position not available: " + err);
} }
return true; return true;
}); });
}); });
registerAsyncSubcommand("ins",(args)->{ registerAsyncSubcommand("ins", (args) -> {
return INS.instance.align().map((_)->{ return INS.align().map((_) -> {
handle.writeLine("INS aligned"); handle.writeLine("INS aligned");
return true; 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.service.ServiceManager;
import kernel.log.Log;
import kernel.Timer;
import kernel.ps.ProcessHandle;
import kernel.ps.Process;
class HelloWorldService implements Process {
public function new() {}
public function run(handle:ProcessHandle) {
Log.debug("HelloWorldService started");
handle.write("Hello World! Started\n");
// this.startTimer(handle);
var srv: HelloWorldService = ServiceManager.instance.get("HelloWorldService");
srv.startTimer(handle);
}
public function startTimer(handle: ProcessHandle) {
new Timer(1000, 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();
}
}

63
src/bin/KSettings.hx Normal file
View File

@@ -0,0 +1,63 @@
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.getString("key");
var value = switch (key) {
case "hostname":
KernelSettings.hostname;
case "nameServer":
Std.string(KernelSettings.nameServer);
default:
null;
}
if (value == null) {
handle.writeLine("Key not found or not set");
return false;
}
handle.writeLine(value);
return true;
}, [String("key")]);
registerSyncSubcommand("set", (args) -> {
var key = args.getString("key");
if (key == null) {
handle.writeLine("Key not specified");
return false;
}
var value = args.getString("value");
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")]);
registerSyncSubcommand("list", (args) -> {
handle.writeLine("hostname");
handle.writeLine("nameServer");
return true;
});
}
}

View File

@@ -1,22 +1,22 @@
package bin; package bin;
import kernel.ps.ProcessHandle; import kernel.ps.ProcessHandle;
import kernel.ps.Process; import kernel.ps.IProcess;
import lib.Color; import lib.Color;
import lib.MathI; import lib.MathI;
import kernel.log.Log; import kernel.log.Log;
import kernel.ui.WindowContext; import kernel.ui.WindowContext;
import lib.ui.UIApp;
using tink.CoreApi; using tink.CoreApi;
class KernelLog implements Process { @:build(macros.Binstore.includeBin("Log", ["log"]))
private var handle: ProcessHandle; class KernelLog implements IProcess {
private var ctx: WindowContext; private var handle:ProcessHandle;
private var ctx:WindowContext;
public function new() {} public function new() {}
public function run(handle: ProcessHandle):Void { public function run(handle:ProcessHandle):Void {
this.handle = handle; this.handle = handle;
var statelessCtx = handle.createStatelessWindowContext(); var statelessCtx = handle.createStatelessWindowContext();
@@ -24,23 +24,23 @@ class KernelLog implements Process {
statelessCtx.setRenderFunc(this.render); statelessCtx.setRenderFunc(this.render);
Log.instance.onLog.handle(()->{ Log.onLog.handle(() -> {
statelessCtx.requestRender(); statelessCtx.requestRender();
}); });
} }
private function render() { private function render() {
ctx.clear(); 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 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) { for (i in start...lines.length) {
var line = lines[i]; var line = lines[i];
switch (line.level){ switch (line.level) {
case Info: case Info:
ctx.setTextColor(Color.White); ctx.setTextColor(Color.White);
ctx.write("[INFO] "); ctx.write("[INFO] ");
@@ -59,7 +59,7 @@ class KernelLog implements Process {
} }
ctx.write(line.message); 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.ProcessManager;
import kernel.ps.ProcessHandle; import kernel.ps.ProcessHandle;
import kernel.ps.Process; import kernel.ps.IProcess;
class LSPS implements Process { @:build(macros.Binstore.includeBin("LSPS", ["lsps"]))
public function new() {} class LSPS implements IProcess {
public function new() {}
public function run(handle:ProcessHandle) { 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) { for (pid in pids) {
handle.writeLine('${pid}'); handle.writeLine('${pid}');
} }
handle.close(); handle.close();
} }
} }

View File

@@ -6,20 +6,21 @@ import kernel.net.Routing;
using tink.CoreApi; using tink.CoreApi;
@:build(macros.Binstore.includeBin("Net", ["net"]))
class Net extends CLIAppBase { class Net extends CLIAppBase {
public function new() { public function new() {
registerSyncSubcommand("route", (args)->{ registerSyncSubcommand("route", (args) -> {
var routes = Routing.instance.getRouteTable(); var routes = Routing.getRouteTable();
for(k => v in routes) { for (k => v in routes) {
handle.writeLine('${k} => ${v.interf.name()}(${v.cost})'); handle.writeLine('${k} => ${v.interf.name()}(${v.cost})');
} }
return true; return true;
}); });
registerSyncSubcommand("iface", (args)->{ registerSyncSubcommand("iface", (args) -> {
var modems = Peripheral.instance.getAllModems(); var modems = Peripheral.getAllModems();
for (modem in modems) { for (modem in modems) {
handle.writeLine(modem.name()); handle.writeLine(modem.name());
@@ -28,8 +29,8 @@ class Net extends CLIAppBase {
return true; return true;
}); });
registerSyncSubcommand("proto",(args)->{ registerSyncSubcommand("proto", (args) -> {
var protos = kernel.net.Net.instance.getActiveProtocols(); var protos = kernel.net.Net.getActiveProtocols();
for (proto in protos) { for (proto in protos) {
handle.writeLine(proto); handle.writeLine(proto);
@@ -38,20 +39,11 @@ class Net extends CLIAppBase {
return true; return true;
}); });
registerAsyncSubcommand("ping",(args)->{ registerAsyncSubcommand("ping", (args) -> {
if (args.length < 1) { var toID:Int = args.getInt("id");
return Future.sync(false);
} return kernel.net.Net.ping(toID).map(result -> {
switch (result) {
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){
case Success(_): case Success(_):
handle.write("Ping succeeded"); handle.write("Ping succeeded");
case Failure(failure): case Failure(failure):
@@ -60,6 +52,6 @@ class Net extends CLIAppBase {
return true; 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; using tink.CoreApi;
class Redstone extends CLIAppBase{ @:build(macros.Binstore.includeBin("Redstone", ["redstone", "rs"]))
class Redstone extends CLIAppBase {
public function new() { public function new() {
registerSyncSubcommand("on", (args)-> { registerSyncSubcommand("on", (args) -> {
Peripheral.instance.getRedstone(args[0]).setOutput(true); Peripheral.getRedstone(args.getString("side")).setOutput(true);
return true; return true;
}); }, [Side("side")]);
registerSyncSubcommand("off", (args)-> { registerSyncSubcommand("off", (args) -> {
Peripheral.instance.getRedstone(args[0]).setOutput(false); Peripheral.getRedstone(args.getString("side")).setOutput(false);
return true; return true;
}); }, [Side("side")]);
registerSyncSubcommand("get", (args)-> { registerSyncSubcommand("get", (args) -> {
var value = Peripheral.instance.getRedstone(args[0]).getAnalogInput(); var value = Peripheral.getRedstone(args.getString("side")).getAnalogInput();
handle.write("Analog input: " + value); handle.write("Analog input: " + value);
return true; return true;
}); }, [Side("side")]);
} }
} }

View File

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

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,91 +1,146 @@
package bin; package bin;
import lib.MathI;
import kernel.EndOfLoop;
import lua.NativeStringTools;
import kernel.binstore.BinStore; import kernel.binstore.BinStore;
import kernel.ps.ProcessHandle; import kernel.ps.ProcessHandle;
import kernel.ps.Process; import kernel.ps.IProcess;
import kernel.ps.ProcessManager; import kernel.ps.ProcessManager;
import lib.Color; import lib.Color;
import kernel.ui.WindowContext; import kernel.ui.WindowContext;
using tink.CoreApi; using tink.CoreApi;
class Terminal implements Process { @:build(macros.Binstore.includeBin("Terminal", ["terminal"]))
private var context:WindowContext; 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 input:String = "";
private var backlog:Array<String> = []; private var backlog:Array<String> = [];
private var handle:ProcessHandle; private var history:Array<String> = [];
private var requestRender: () -> Void; private var historyIndex:Int = 0;
private var scrollBack = 0;
private var runningPID:PID = -1;
public function new() {} public function new() {}
public function run(handle: ProcessHandle): Void { public function run(handle:ProcessHandle):Void {
this.handle = handle; this.handle = handle;
var statelessContext = handle.createStatelessWindowContext(); var statelessContext = handle.createStatelessWindowContext();
this.context = statelessContext.ctx; this.ctx = statelessContext.ctx;
this.requestRender = statelessContext.requestRender; this.requestRender = statelessContext.requestRender;
statelessContext.setRenderFunc(this.render); statelessContext.setRenderFunc(this.render);
this.context.onChar.handle(char -> { // Add input event handlers
handle.addCallbackLink(this.ctx.onChar.handle(char -> {
if (this.runningPID > 0)
return;
this.input += char; this.input += char;
this.requestRender(); this.requestRender();
}); }));
this.context.onKey.handle(e -> { // Add key event handlers
if (e.keyCode == 259) { // Backspace handle.addCallbackLink(this.ctx.onKey.handle(e -> {
this.input = this.input.substr(0, this.input.length - 1); switch (e.keyCode) {
this.requestRender(); case 259: // Backspace
} else if (e.keyCode == 257) { // Enter if (this.runningPID > 0)
this.backlog.push("> " + this.input); return;
var command = this.input; this.input = this.input.substr(0, this.input.length - 1);
this.input = ""; this.requestRender();
this.requestRender(); case 257: // Enter
this.invokeCommand(command); 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(); 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() {
if (this.runningPID < 0) {
return;
}
ProcessManager.kill(this.runningPID);
} }
private function render() { private function render() {
redrawBacklog(); var size = this.ctx.getSize();
redrawInput();
}
private function redrawBacklog() {
var size = this.context.getSize();
var linesAvailable = size.y - 1; var linesAvailable = size.y - 1;
var start:Int = this.backlog.length - linesAvailable; var withoutScrollBack = (this.backlog.length - linesAvailable);
var start:Int = withoutScrollBack - scrollBack;
for (i in 0...linesAvailable) { for (i in 0...linesAvailable) {
var line = this.backlog[start + i]; var line = this.backlog[start + i];
this.context.setCursorPos(0, i); this.ctx.setCursorPos(0, i);
this.context.clearLine(); this.ctx.clearLine();
if (line != null) { if (line != null) {
this.context.write(line); this.ctx.write(line);
} }
} }
this.moveCursorToInput();
}
private function redrawInput() { this.ctx.setCursorPos(0, size.y - 1);
var size = this.context.getSize(); this.ctx.clearLine();
this.context.setCursorPos(0, size.y - 1); this.ctx.setTextColor(Color.Blue);
this.context.clearLine(); this.ctx.write("> ");
this.context.setTextColor(Color.Blue); this.ctx.setTextColor(Color.White);
this.context.write("> "); this.ctx.write(this.input);
this.context.setTextColor(Color.White); if (this.runningPID < 0) {
this.context.write(this.input); this.ctx.setCursorBlink(true);
} else {
this.context.setCursorBlink(true); this.ctx.setCursorBlink(false);
}
} }
private function invokeCommand(command:String):Void { private function invokeCommand(command:String):Void {
@@ -94,6 +149,13 @@ class Terminal implements Process {
if (args.length == 0) { if (args.length == 0) {
return; return;
} }
this.history.push(command);
if (this.history.length > MAX_BACKLOG) {
this.history.shift();
}
var commandName = args[0]; var commandName = args[0];
// Handle built-in commands // Handle built-in commands
@@ -104,40 +166,35 @@ class Terminal implements Process {
} }
var commandArgs:Array<String> = args.slice(1); var commandArgs:Array<String> = args.slice(1);
var hadInput = false;
var ps = getProgByName(commandName); var ps = BinStore.instantiate(commandName);
if (ps == null) { if (ps == null) {
this.backlog.push("Unknown command: " + commandName); this.backlog.push("Unknown command: " + commandName);
this.redrawBacklog(); this.requestRender();
this.redrawInput();
return; return;
} }
ProcessManager.run(ps,{ this.runningPID = ProcessManager.run(ps, {
args: commandArgs, args: commandArgs,
onWrite: (s:String) -> { onWrite: (s:String) -> {
if (s == "") { if (s == "") {
return; return;
} }
if (!hadInput) { var splits = s.split("\n");
// Add a new line, so that the input is not on the same line as the command
this.backlog.push("");
hadInput = true;
}
for (line in s.split("\n")) { for (i => split in splits) {
if (line == ""){ if (i == 0) {
this.backlog.push(""); this.backlog[this.backlog.length - 1] += split;
} else { } else {
this.backlog[this.backlog.length - 1] += s; this.backlog.push(split);
} }
} }
this.requestRender(); this.requestRender();
}, },
onExit: (success:Bool) -> { onExit: (success:Bool) -> {
this.runningPID = -1;
if (this.backlog[this.backlog.length - 1] == "") { if (this.backlog[this.backlog.length - 1] == "") {
this.backlog.pop(); this.backlog.pop();
} }
@@ -146,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> { private function parseArgs(command:String):Array<String> {
// TODO: tim and quote handling // TODO: tim and quote handling
return command.split(" "); return command.split(" ");
@@ -156,20 +216,11 @@ class Terminal implements Process {
private function clear() { private function clear() {
this.backlog = []; this.backlog = [];
this.redrawBacklog(); this.requestRender();
}
private function getProgByName(name:String):Process {
var bin = BinStore.instance.getBinByAlias(name);
if (bin == null) {
return null;
}
return Type.createInstance(bin.c,[]);
} }
private function moveCursorToInput() { private function moveCursorToInput() {
var size = this.context.getSize(); var size = this.ctx.getSize();
this.context.setCursorPos(this.input.length + 2, size.y - 1); 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,92 +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 < 2) {
handle.writeLine("Not enough arguments");
return Future.sync(false);
}
var id:NetworkID = Std.parseInt(args[0]);
if (id == null) {
handle.writeLine("Invalid id");
return Future.sync(false);
}
return RessourceNames.get(args[1], id).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;
});
}, "<id> <name>");
registerAsyncSubcommand("register", (args) -> {
if (args.length < 3) {
handle.writeLine("Not enough arguments");
return Future.sync(false);
}
var id:NetworkID = Std.parseInt(args[0]);
if (id == null) {
handle.writeLine("Invalid id");
return Future.sync(false);
}
var id2:NetworkID = Std.parseInt(args[2]);
if (id2 == null) {
handle.writeLine("Invalid id");
return Future.sync(false);
}
return RessourceNames.register(args[1], id2, id).map((res) -> {
switch (res) {
case Success(data):
handle.writeLine("Success");
case Failure(error):
handle.writeLine("Error: " + error);
}
return true;
});
}, "<id> <name> <id>");
registerAsyncSubcommand("unregister", (args) -> {
if (args.length < 2) {
handle.writeLine("Not enough arguments");
return Future.sync(false);
}
var id:NetworkID = Std.parseInt(args[0]);
if (id == null) {
handle.writeLine("Invalid id");
return Future.sync(false);
}
return RessourceNames.unregister(args[1], id).map((res) -> {
switch (res) {
case Success(data):
handle.writeLine("Success");
case Failure(error):
handle.writeLine("Error: " + error);
}
return true;
});
}, "<id> <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,24 +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(),
];
}
}

28
src/kernel/EndOfLoop.hx Normal file
View File

@@ -0,0 +1,28 @@
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.
**/
class EndOfLoop {
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);
}
}
@: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() { public static function main() {
try { try {
Init.initKernel(); Init.initKernel();
}catch(e){ } catch (e) {
Log.error('Error in init: ${e.toString()}'); Log.error('Error in init: ${e.toString()}');
return; return;
} }
try { try {
Startup.main(); Startup.main();
}catch(e){ } catch (e) {
Log.error('Error in startup: ${e.toString()}'); Log.error('Error in startup: ${e.toString()}');
} }
KernelEvents.startEventLoop();
} }
} }

View File

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

View File

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

@@ -0,0 +1,58 @@
package kernel;
import cc.OS;
import kernel.net.Package.NetworkID;
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 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 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 {
return _hostname;
}
private static inline function set_hostname(value:String):String {
OS.setComputerLabel(value);
set("hostname", value);
_hostname = value;
return value;
}
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_nameServer(value:NetworkID):NetworkID {
if (value == null) {
return get_nameServer();
}
set("nameServer", value);
_nameServer = value;
return value;
}
}

View File

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

View File

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

View File

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

View File

@@ -1,63 +1,45 @@
package kernel.binstore; package kernel.binstore;
import bin.Perf; import kernel.ps.IProcess;
import bin.srsc.CLI; import macros.Binstore;
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;
class BinStore { class BinStore {
public static var instance: BinStore; private static final bins:Array<Bin> = populateStore();
private final store:ReadOnlyArray<Bin> = [ private static function populateStore()
{c: Disk, name: "Disk", aliases: ["disk"]}, return {
{c: GPS, name: "GPS", aliases: ["gps"]}, var bins:Array<Bin> = [];
{c: HelloWorld, name: "HelloWorld", aliases: ["hello"]}, Binstore.generateBinStore();
{c: KernelLog, name: "KernelLog", aliases: ["log"]}, return bins;
{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"]}
];
@:allow(kernel.Init) public static function instantiate(alias:String):Null<IProcess> {
private function new() { 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> { public static function getNameByAlias(alias:String):Null<String> {
for (bin in store) { var bin = getBinByAlias(alias);
if (bin.name == name) { if (bin == null) {
return bin; return null;
} }
} return bin.name;
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;
}
} }

View File

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

View File

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

View File

@@ -1,11 +1,12 @@
package kernel.gps; package kernel.gps;
import lib.SingleTimeoutPromise;
import kernel.log.Log; import kernel.log.Log;
import lib.KVStore; import lib.KVStore;
import kernel.net.Net; import kernel.net.Net;
import kernel.net.INetworkInterface; import kernel.net.INetworkInterface;
import kernel.net.Package; import kernel.net.Package;
import lib.Pos3; import lib.WorldPos;
using tink.CoreApi; using tink.CoreApi;
@@ -15,24 +16,24 @@ using tink.CoreApi;
You need at least 3 computers that know their position to determine the position of the computer. You need at least 3 computers that know their position to determine the position of the computer.
**/ **/
class GPS { class GPS {
public static var instance:GPS; private static inline final TIMEOUT:Int = 1;
private var shouldRespond = true; private static var shouldRespond = true;
private var shouldDoWholeNumberCheck = true; private static var shouldDoWholeNumberCheck = true;
private var posAccuracy = 0; // 0 = unkown, 1 = (ins,best guess), 2 = (stored/manual,should be right), 3 = (gps,confirmed) private static var posAccuracy = 0; // 0 = unkown, 1 = (ins,best guess), 2 = (stored/manual,should be right), 3 = (gps,confirmed)
private var cachedPosition:Pos3; private static var cachedPosition:WorldPos;
private var lastPositionResponse: Array<{pos:Pos3,dist:Float}> = []; 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) @:allow(kernel.Init)
private function new() { private static function init() {
this.loadCachedPosition(); loadCachedPosition();
} }
public function setManualPosition(pos:Pos3) { public static function setManualPosition(pos:WorldPos) {
var kvstore = new KVStore("gps"); var kvstore = new KVStore("gps");
kvstore.set("mpos",pos); kvstore.set("mpos", pos);
kvstore.save(); kvstore.save();
if (cachedPosition == null) { if (cachedPosition == null) {
@@ -42,52 +43,43 @@ class GPS {
} }
@:allow(kernel.gps.INS) @:allow(kernel.gps.INS)
private function setINSPosition(pos:Pos3) { private static function setINSPosition(pos:WorldPos) {
cachedPosition = pos; cachedPosition = pos;
posAccuracy = 1; posAccuracy = 1;
} }
public function getPosition():Null<Pos3> { public static function getPosition():Null<WorldPos> {
return cachedPosition; return cachedPosition;
} }
public function getAccuracy():Int { public static function getAccuracy():Int {
return posAccuracy; return posAccuracy;
} }
public function invalidatePosition() { public static function invalidatePosition() {
cachedPosition = null; cachedPosition = null;
posAccuracy = 0; posAccuracy = 0;
} }
public function locate():Future<Null<Pos3>> { public static function locate():Promise<WorldPos> {
// TODO: implenet a timeout return locatePromise.request();
// TODO: dont send a request twice if the last one is still pending or we moved
return new Future<Null<Pos3>>((resolve)->{
this.futureResolve = resolve;
sendPositionRequest();
return null;
});
} }
private function resolveFuture(pos:Null<Pos3>) { private static function persistCachedPositon() {
this.futureResolve(pos); if (cachedPosition == null)
} return;
private function persistCachedPositon() {
if (cachedPosition == null) return;
var kvstore = new KVStore("gps"); var kvstore = new KVStore("gps");
kvstore.set("cpos",cachedPosition); kvstore.set("cpos", cachedPosition);
kvstore.save(); kvstore.save();
} }
private function loadCachedPosition() { private static function loadCachedPosition() {
var kvstore = new KVStore("gps"); var kvstore = new KVStore("gps");
kvstore.load(); kvstore.load();
var mPos:Null<Pos3> = kvstore.get("mpos"); // Manual set position var mPos:Null<WorldPos> = kvstore.get("mpos"); // Manual set position
var cPos:Null<Pos3> = kvstore.get("cpos"); // Cached position var cPos:Null<WorldPos> = kvstore.get("cpos"); // Cached position
if (mPos != null && cPos != null && mPos == cPos) { if (mPos != null && cPos != null && mPos == cPos) {
// Both are the same, so we can use the cached position // Both are the same, so we can use the cached position
@@ -96,7 +88,7 @@ class GPS {
return; 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 // Both are different, so we can use the manual position
cachedPosition = mPos; cachedPosition = mPos;
posAccuracy = 1; posAccuracy = 1;
@@ -116,51 +108,62 @@ class GPS {
posAccuracy = 2; posAccuracy = 2;
return; return;
} }
} }
private function sendPositionRequest() { private static function brodcastPositionRequest() {
Net.instance.brodcastGPSRequest(); Net.brodcastGPSRequest();
} }
@:allow(kernel.net.Net) @: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) { switch (pack.type) {
case GPSRequest: case GPSRequest:
if (!shouldRespond) return; if (!shouldRespond)
if (posAccuracy < 2) return; return;
if (cachedPosition == null) return; if (posAccuracy < 2)
return;
if (cachedPosition == null)
return;
var response = new Package(Net.instance.networkID,pack.fromID, pack.msgID, GPSResponse(cachedPosition),null,0); var response = new Package(Net.networkID, pack.fromID, pack.msgID, GPSResponse(cachedPosition.x, cachedPosition.y, cachedPosition.z,), null, 0);
iface.send(pack.fromID,Net.instance.networkID,response); iface.send(pack.fromID, Net.networkID, response);
case GPSResponse(pos): case GPSResponse(x, y, z):
if (lastPositionResponse.contains({pos:pos,dist:dist})) return; // Ignore duplicate responses 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 // 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(); var calculatedPosition = calculatePosition();
if (calculatedPosition != null){ if (calculatedPosition != null) {
calculatedPosition = calculatedPosition.round(); calculatedPosition = calculatedPosition.round();
} }
lastPositionResponse = []; // Reset the response array lastPositionResponse = []; // Reset the response array
if (calculatedPosition == null) return; if (calculatedPosition == null)
return;
cachedPosition = calculatedPosition; cachedPosition = calculatedPosition;
posAccuracy = 3; posAccuracy = 3;
resolveFuture(calculatedPosition); locatePromise.resolve(calculatedPosition);
default: default:
} }
} }
private function calculatePosition():Null<Pos3> { private static function calculatePosition():Null<WorldPos> {
if (lastPositionResponse.length < 3) return null; if (lastPositionResponse.length < 3)
return null;
// do a simple trilateration with the last 3 responses for now // do a simple trilateration with the last 3 responses for now
var p1 = lastPositionResponse[0].pos; var p1 = lastPositionResponse[0].pos;
@@ -171,13 +174,14 @@ class GPS {
var r2 = lastPositionResponse[1].dist; var r2 = lastPositionResponse[1].dist;
var r3 = lastPositionResponse[2].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 // 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 // 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 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); 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 return null; // The two positions are essentially the same, so we cant determine which one is correct
} }
if (err1 < err2) return result.a; if (err1 < err2)
if (err2 < err1) return result.b; 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) // 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 // 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. 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 a2b = p2 - p1;
var a2c = p3 - p1; var a2c = p3 - p1;
@@ -219,7 +226,7 @@ class GPS {
var i = ex.dot(a2c); var i = ex.dot(a2c);
var ey = (a2c - ex * i).normalize(); var ey = (a2c - ex * i).normalize();
var j = ey.dot(a2c); var j = ey.dot(a2c);
var ez = ex.cross(ey); var ez:WorldPos = ex.cross(ey);
var x = (r1 * r1 - r2 * r2 + d * d) / (2 * d); var x = (r1 * r1 - r2 * r2 + d * d) / (2 * d);
var y = (r1 * r1 - r3 * r3 - x * x + (x - i) * (x - i) + j * j) / (2 * j); var y = (r1 * r1 - r3 * r3 - x * x + (x - i) * (x - i) + j * j) / (2 * j);
@@ -229,9 +236,9 @@ class GPS {
var zSquared = r1 * r1 - x * x - y * y; var zSquared = r1 * r1 - x * x - y * y;
if (zSquared > 0) { if (zSquared > 0) {
var z = Math.sqrt(zSquared); 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; package kernel.gps;
import kernel.log.Log; import lib.SinglePromise;
import kernel.turtle.Turtle; import kernel.turtle.Turtle;
import lib.Pos3; import lib.WorldPos;
using tink.CoreApi; using tink.CoreApi;
class INS { class INS {
public static var instance:INS; private static var heading:Null<WorldPos> = null;
private static var alingment:Int = 1; // 0 = degraded, 1 = not aligned, 2 = aligned
private var heading: Null<Pos3> = null; private static final alignPromise:SinglePromise<Noise> = new SinglePromise(startAlign);
private var alingment: Int = 1; // 0 = degraded, 1 = not aligned, 2 = aligned
@:allow(kernel.Init)
private function new() {
}
@:allow(kernel.turtle.Turtle) @:allow(kernel.turtle.Turtle)
private function moveForward() { private static function moveForward() {
if (heading == null) { if (heading == null) {
this.alingment = 0; alingment = 0;
return; return;
} }
move(heading); move(heading);
} }
@:allow(kernel.turtle.Turtle) @:allow(kernel.turtle.Turtle)
private function moveBackward() { private static function moveBackward() {
if (heading == null) { if (heading == null) {
this.alingment = 0; alingment = 0;
return; return;
} }
move(heading.negate()); move(heading.negate());
} }
@:allow(kernel.turtle.Turtle) @:allow(kernel.turtle.Turtle)
private function moveUp() { private static function moveUp() {
move({x: 0, y: 1, z: 0}); move({x: 0, y: 1, z: 0});
} }
@:allow(kernel.turtle.Turtle) @:allow(kernel.turtle.Turtle)
private function moveDown() { private static function moveDown() {
move({x: 0, y: -1, z: 0}); move({x: 0, y: -1, z: 0});
} }
@:allow(kernel.turtle.Turtle) @:allow(kernel.turtle.Turtle)
private function turnLeft() { private static function turnLeft() {
if (heading == null) return; if (heading == null)
return;
if (heading.x == 0 && heading.z == -1) { if (heading.x == 0 && heading.z == -1) {
heading = {x: -1, y: 0, z: 0}; heading = {x: -1, y: 0, z: 0};
} else if (heading.x == -1 && heading.z == 0) { } else if (heading.x == -1 && heading.z == 0) {
@@ -60,8 +55,9 @@ class INS {
} }
@:allow(kernel.turtle.Turtle) @:allow(kernel.turtle.Turtle)
private function turnRight() { private static function turnRight() {
if (heading == null) return; if (heading == null)
return;
if (heading.x == 0 && heading.z == -1) { if (heading.x == 0 && heading.z == -1) {
heading = {x: 1, y: 0, z: 0}; heading = {x: 1, y: 0, z: 0};
} else if (heading.x == -1 && heading.z == 0) { } else if (heading.x == -1 && heading.z == 0) {
@@ -73,81 +69,78 @@ class INS {
} }
} }
private function move(dir: Null<Pos3>) { private static function move(dir:Null<WorldPos>) {
Log.debug('INS move: $dir'); if (alignPromise.isRunning()) {
var pos = GPS.instance.getPosition(); return;
}
var pos = GPS.getPosition();
if (pos == null || dir == null)
return;
var newPos = pos + dir; var newPos = pos + dir;
GPS.instance.setINSPosition(newPos); GPS.setINSPosition(newPos);
} }
public function getHeading():Null<Pos3> { public static function getHeading():Null<WorldPos> {
return heading; return heading;
} }
public function align(): Promise<Noise> { public static function align():Promise<Noise> {
Log.info("Aligning INS"); return alignPromise.request();
return new Promise<Noise>((resolve,reject)->{ }
if (Turtle.instance.getFuelLevel() < 2){ public static function startAlign():Void {
Log.warn("Not enough fuel to align"); if (Turtle.getFuelLevel() < 2) {
reject(new Error("Not enough fuel to align")); alignPromise.reject(new Error("Not enough fuel to align"));
return null; 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 // -1 = not moved, 0 = back, 1 = forward, 2 = left, 3 = right
private function tryMoving():Int { private static function tryMoving():Int {
if (Turtle.instance.back().isSuccess()) { if (Turtle.back().isSuccess()) {
return 0; return 0;
} else if (Turtle.instance.forward().isSuccess()) { } else if (Turtle.forward().isSuccess()) {
return 1; return 1;
} else { } else {
Turtle.instance.turnLeft(); // TODO: Check if successfull Turtle.turnLeft(); // TODO: Check if successfull
if (Turtle.instance.forward().isSuccess()){ if (Turtle.forward().isSuccess()) {
return 2; return 2;
} else if (Turtle.instance.back().isSuccess()) { } else if (Turtle.back().isSuccess()) {
return 3; return 3;
} else { } else {
// Can't move // 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) { if (moved == 0) {
return pos1 - pos2; return pos1 - pos2;
} else if (moved == 1) { } else if (moved == 1) {
@@ -170,27 +163,21 @@ class INS {
} }
} }
private function moveBack(moved:Int) { private static function moveBack(moved:Int) {
if (moved == 0) { if (moved == 0) {
Turtle.instance.forward(); Turtle.forward();
// cc.Turtle.forward();
} else if (moved == 1) { } else if (moved == 1) {
Turtle.instance.back(); Turtle.back();
// cc.Turtle.back();
} else if (moved == 2) { } else if (moved == 2) {
Turtle.instance.back(); Turtle.back();
// cc.Turtle.back(); Turtle.turnRight();
Turtle.instance.turnRight();
// cc.Turtle.turnRight();
} else if (moved == 3) { } else if (moved == 3) {
Turtle.instance.forward(); Turtle.forward();
// cc.Turtle.forward(); Turtle.turnRight();
Turtle.instance.turnRight();
// cc.Turtle.turnRight();
} }
} }
private function rotatePos3ToRight(pos:Pos3):Pos3 { private static function rotatePos3ToRight(pos:WorldPos):WorldPos {
if (pos.x == 0 && pos.z == -1) { if (pos.x == 0 && pos.z == -1) {
return {x: 1, y: 0, z: 0}; return {x: 1, y: 0, z: 0};
} else if (pos.x == -1 && pos.z == 0) { } else if (pos.x == -1 && pos.z == 0) {
@@ -204,8 +191,7 @@ class INS {
} }
} }
private function rotatePos3ToLeft(pos3:Pos3):Pos3 { private static function rotatePos3ToLeft(pos3:WorldPos):WorldPos {
return rotatePos3ToRight(rotatePos3ToRight(rotatePos3ToRight(pos3))); return rotatePos3ToRight(rotatePos3ToRight(rotatePos3ToRight(pos3)));
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,17 +1,19 @@
package kernel.net; package kernel.net;
import kernel.net.Package.GenericPackage;
using tink.CoreApi; using tink.CoreApi;
/** /**
A object that is able to send and receive messages. A object that is able to send and receive messages.
**/ **/
interface INetworkInterface { interface INetworkInterface {
public function listen(chan: Int):Void; public function listen(chan:Int):Void;
public function close(chan: Int):Void; public function close(chan:Int):Void;
public function isListening(chan: Int): Bool; public function isListening(chan:Int):Bool;
public function closeAll(): Void; public function closeAll():Void;
public function send(chan: Int,replyChan: Int,payload: Any):Void; public function send(chan:Int, replyChan:Int, payload:Any):Void;
public function name():String; public function name():String;
public function getBaseRoutingCost():Int; 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; package kernel.net;
import kernel.net.Package.GenericPackage;
import kernel.log.Log; import kernel.log.Log;
using tink.CoreApi; using tink.CoreApi;
/** /**
@@ -9,17 +11,17 @@ using tink.CoreApi;
class Loopback implements INetworkInterface { class Loopback implements INetworkInterface {
public static final instance:Loopback = new Loopback(); 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 final onMessageTrigger:SignalTrigger<{pack:GenericPackage, dist:Null<Float>}> = Signal.trigger();
private var openChans: Array<Int> = []; private var openChans:Array<Int> = [];
private function new() { private function new() {
this.onMessage = onMessageTrigger.asSignal(); this.onMessage = onMessageTrigger.asSignal();
} }
public function listen(chan:Int) { public function listen(chan:Int) {
if (!this.openChans.contains(chan)){ if (!this.openChans.contains(chan)) {
this.openChans.push(chan); this.openChans.push(chan);
} }
} }
@@ -37,9 +39,9 @@ class Loopback implements INetworkInterface {
} }
public function send(chan:Int, replyChan:Int, payload:Any) { public function send(chan:Int, replyChan:Int, payload:Any) {
if (this.openChans.contains(chan)){ if (this.openChans.contains(chan)) {
this.onMessageTrigger.trigger({pack:payload,dist:null}); this.onMessageTrigger.trigger({pack: payload, dist: null});
}else{ } else {
Log.silly("Loopback got package on non open channel"); Log.silly("Loopback got package on non open channel");
} }
} }

View File

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

View File

@@ -1,32 +1,31 @@
package kernel.net; package kernel.net;
import lib.Pos3;
typedef NetworkID = Int; typedef NetworkID = Int;
typedef GenericPackage = Package<Dynamic>;
enum PackageTypes { enum PackageTypes {
Data(proto:String); Data(proto:String);
DataNoResponse(proto:String); DataNoResponse(proto:String);
Response; Response;
RouteDiscover(reachableIDs: Array<{id:NetworkID,cost:Int}>); RouteDiscover(reachableIDs:Array<{id:NetworkID, cost:Int}>);
RouteDiscoverResponse(reachableIDs: Array<{id:NetworkID,cost:Int}>); RouteDiscoverResponse(reachableIDs:Array<{id:NetworkID, cost:Int}>);
RouteDiscoverUpdate(reachableIDs: Array<{id:NetworkID,cost:Int}>); RouteDiscoverUpdate(reachableIDs:Array<{id:NetworkID, cost:Int}>);
GPSResponse(pos:Pos3); GPSResponse(x:Float, y:Float, z:Float);
GPSRequest(); GPSRequest();
} }
/** /**
Representing a network package. Representing a network package.
**/ **/
@:structInit class Package { @:structInit class Package<T> {
public final fromID:NetworkID; public final fromID:NetworkID;
public final toID:NetworkID; public final toID:NetworkID;
public final msgID:Int; public final msgID:Int;
public final type:PackageTypes; public final type:PackageTypes;
public final data:Dynamic; public final data:T;
public var ttl: Int; 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.fromID = fromID;
this.toID = toID; this.toID = toID;
this.msgID = msgID; this.msgID = msgID;
@@ -38,7 +37,7 @@ enum PackageTypes {
/** /**
Create package that can be used as a response. Create package that can be used as a response.
**/ **/
public function createResponse(newData:Dynamic):Package { public function createResponse<T2>(newData:T2):Package<T2> {
return { return {
toID: fromID, toID: fromID,
fromID: toID, fromID: toID,
@@ -50,9 +49,9 @@ enum PackageTypes {
} }
/** /**
Wrapper for `Net.instance.respondTo`. Wrapper for `Net.respondTo`.
**/ **/
public function respond(data:Dynamic) { 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 Depends on: Peripheral
**/ **/
public static var instance:Routing;
public static inline final UPDATE_WAIT_TIME:Float = 1; 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 static final onNewNeigborTrigger:SignalTrigger<NetworkID> = Signal.trigger();
private final routingTable:Map<NetworkID, Route> = new Map(); private static final routingTable:Map<NetworkID, Route> = new Map();
private var routeUpdateInQueue:Bool = false; private static var routeUpdateInQueue:Bool = false;
@:allow(kernel.Init) @:allow(kernel.Init)
private function new() { private static function init() {
this.onNewNeigbor = this.onNewNeigborTrigger.asSignal(); onNewNeigbor = onNewNeigborTrigger.asSignal();
} }
@:allow(kernel.Init) @:allow(kernel.net.Net)
private function init() { private static function setup() {
routingTable.set(Net.instance.networkID, {interf: Loopback.instance, cost: 0, rep: Net.instance.networkID}); routingTable.set(Net.networkID, {interf: Loopback.instance, cost: 0, rep: Net.networkID});
brodcastRoutingTable(); 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. 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. After UPDATE_WAIT_TIME seconds the routing table will be brodcasted. This is done to prevent spamming the network.
**/ **/
private function prepareRouteUpdate() { private static function prepareRouteUpdate() {
if (this.routeUpdateInQueue) { if (routeUpdateInQueue) {
return; return;
} }
this.routeUpdateInQueue = true; routeUpdateInQueue = true;
new Timer(UPDATE_WAIT_TIME, () -> { new Timer(UPDATE_WAIT_TIME, () -> {
brodcastRoutingTable(); brodcastRoutingTable();
this.routeUpdateInQueue = false; routeUpdateInQueue = false;
}); });
} }
/** /**
Brodcast the current routing table to all peers. Brodcast the current routing table to all peers.
**/ **/
private function brodcastRoutingTable() { private static function brodcastRoutingTable() {
var pack = newRoutDiscoverPackage(); var pack = newRoutDiscoverPackage();
for (modem in Peripheral.instance.getAllModems()) { for (modem in Peripheral.getAllModems()) {
modem.send(Net.BRODCAST_PORT, Net.instance.networkID, pack); modem.send(Net.BRODCAST_PORT, Net.networkID, pack);
} }
} }
@@ -73,53 +71,48 @@ class Routing {
Handle incomming packages that involve route discovery. Handle incomming packages that involve route discovery.
**/ **/
@:allow(kernel.net.Net) @: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); addPossibleRoute(pack.fromID, interf, 0, pack.fromID);
var shouldRespond:Bool = switch pack.type { switch pack.type {
case RouteDiscoverResponse(routes): case RouteDiscoverResponse(routes):
// A request for routes has been answerd
for (route in routes) { for (route in routes) {
addPossibleRoute(route.id, interf, route.cost, pack.fromID); addPossibleRoute(route.id, interf, route.cost, pack.fromID);
} }
false;
case RouteDiscover(routes): case RouteDiscover(routes):
// Received a request for available routes
for (route in routes) { for (route in routes) {
addPossibleRoute(route.id, interf, route.cost, pack.fromID); addPossibleRoute(route.id, interf, route.cost, pack.fromID);
} }
true;
// Respond to peer
var response:Package<Noise> = {
toID: pack.fromID,
fromID: Net.networkID,
msgID: null,
type: RouteDiscoverResponse(genRouteList()),
data: null,
ttl: 0, // Prevent forwarding
}
interf.send(response.toID, Net.networkID, response);
case RouteDiscoverUpdate(routes): case RouteDiscoverUpdate(routes):
// Received an update of routes
for (route in routes) { for (route in routes) {
addPossibleRoute(route.id, interf, route.cost, pack.fromID); addPossibleRoute(route.id, interf, route.cost, pack.fromID);
} }
false;
default: default:
Log.error("Expected package to be a Route discover package"); Log.silly("Expected package to be a Route discover package");
false;
}; };
if (!shouldRespond) {
return;
}
// Respond to peer
var response:Package = {
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. 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}> = []; var routes:Array<{id:NetworkID, cost:Int}> = [];
for (k => v in this.routingTable) { for (k => v in routingTable) {
routes.push({ routes.push({
id: k, id: k,
cost: v.cost cost: v.cost
@@ -128,12 +121,15 @@ class Routing {
return routes; 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()), type: RouteDiscover(genRouteList()),
toID: Net.BRODCAST_PORT, toID: Net.BRODCAST_PORT,
msgID: null, msgID: null,
fromID: Net.instance.networkID, fromID: Net.networkID,
data: null, data: null,
ttl: 0, // Prevent forwarding ttl: 0, // Prevent forwarding
} }
@@ -145,31 +141,31 @@ class Routing {
Called when a route to a client has been disoverd. Called when a route to a client has been disoverd.
Its possible to be called multiple times with the same id but different addr. 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) { private static function addPossibleRoute(toID:Int, interf:INetworkInterface, cost:Int, rep:NetworkID) {
if (toID == Net.instance.networkID) { if (toID == Net.networkID) {
return; return;
} }
var fullCost = cost + interf.getBaseRoutingCost(); var fullCost = cost + interf.getBaseRoutingCost();
if (this.routingTable.exists(toID)) { if (routingTable.exists(toID)) {
if (this.routingTable[toID].cost > fullCost) { if (routingTable[toID].cost > fullCost) {
this.routingTable[toID] = {interf: interf, cost: cost + interf.getBaseRoutingCost(), rep: rep}; routingTable[toID] = {interf: interf, cost: cost + interf.getBaseRoutingCost(), rep: rep};
this.prepareRouteUpdate(); prepareRouteUpdate();
} }
} else { } else {
this.routingTable[toID] = {interf: interf, cost: cost + interf.getBaseRoutingCost(), rep: rep}; routingTable[toID] = {interf: interf, cost: cost + interf.getBaseRoutingCost(), rep: rep};
this.onNewNeigborTrigger.trigger(toID); onNewNeigborTrigger.trigger(toID);
this.prepareRouteUpdate(); prepareRouteUpdate();
} }
} }
public function getRouteToID(networkID:NetworkID):{interf:INetworkInterface, rep:NetworkID} { public static function getRouteToID(networkID:NetworkID):{interf:INetworkInterface, rep:NetworkID} {
if (networkID == Net.instance.networkID) { if (networkID == Net.networkID) {
return {interf: Loopback.instance, rep: Net.instance.networkID}; return {interf: Loopback.instance, rep: Net.networkID};
} }
var route:Null<Route> = this.routingTable[networkID]; var route:Null<Route> = routingTable[networkID];
if (route == null) { if (route == null) {
return null; return null;
@@ -178,7 +174,31 @@ class Routing {
} }
} }
public function getRouteTable():Map<NetworkID, Route> { public static function getRouteTable():Map<NetworkID, Route> {
return this.routingTable; return routingTable;
}
/**
Call when an interface is no longer available.
Will rebrodcast routing requests.
**/
@:allow(kernel.net.Net)
private static function removeInterface(addr:String) {
// Remove the interface from the routing table
for (to => modem in routingTable) {
if (modem.interf.name() == addr) {
routingTable.remove(to);
}
}
// Since some routes to some host could be lost
// we rebrodcast on all interfaces again
brodcastRoutingTable();
}
@:allow(kernel.net.Net)
private static function addInterface(iface:INetworkInterface) {
var pack = newRoutDiscoverPackage();
iface.send(Net.BRODCAST_PORT, Net.networkID, pack);
} }
} }

View File

@@ -0,0 +1,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; package kernel.peripherals;
import cc.Peripheral; import cc.Peripheral;
using tink.CoreApi; using tink.CoreApi;
class Drive implements IPeripheral { class Drive implements IPeripheral {
@@ -15,21 +16,21 @@ class Drive implements IPeripheral {
private final onDiskEjectTrigger:SignalTrigger<Noise> = Signal.trigger(); private final onDiskEjectTrigger:SignalTrigger<Noise> = Signal.trigger();
@:allow(kernel.peripherals) @:allow(kernel.peripherals)
private function new(addr: String) { private function new(addr:String) {
this.addr = addr; this.addr = addr;
this.native = Peripheral.wrap(addr); this.native = Peripheral.wrap(addr);
this.onDiskInsert = this.onDiskInsertTrigger.asSignal(); this.onDiskInsert = this.onDiskInsertTrigger.asSignal();
this.onDiskEject = this.onDiskEjectTrigger.asSignal(); this.onDiskEject = this.onDiskEjectTrigger.asSignal();
KernelEvents.instance.onDisk.handle((addr) ->{ KernelEvents.onDisk.handle((addr) -> {
if (addr == this.addr){ if (addr == this.addr) {
this.onDiskInsertTrigger.trigger(null); this.onDiskInsertTrigger.trigger(null);
} }
}); });
KernelEvents.instance.onDiskEject.handle((addr)->{ KernelEvents.onDiskEject.handle((addr) -> {
if (addr == this.addr){ if (addr == this.addr) {
this.onDiskEjectTrigger.trigger(null); this.onDiskEjectTrigger.trigger(null);
} }
}); });
@@ -39,14 +40,18 @@ class Drive implements IPeripheral {
return this.addr; return this.addr;
} }
public inline function isDiskPresent(): Bool { public function getType():String {
return TYPE_NAME;
}
public inline function isDiskPresent():Bool {
return this.native.isDiskPresent(); 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. 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(); return this.native.getDiskLabel();
} }
@@ -54,11 +59,11 @@ class Drive implements IPeripheral {
this.native.setDiskLabel(); this.native.setDiskLabel();
} }
public inline function setDiskLabel(label: String): Null<Error> { public inline function setDiskLabel(label:String):Null<Error> {
try { try {
this.native.setDiskLabel(label); this.native.setDiskLabel(label);
return null; return null;
} catch (e: Dynamic) { } catch (e:Dynamic) {
return new Error("Invalid label"); return new Error("Invalid label");
} }
} }
@@ -75,7 +80,7 @@ class Drive implements IPeripheral {
return this.getMountPath(); return this.getMountPath();
} }
public inline function getAudioTitle(): Null<String> { public inline function getAudioTitle():Null<String> {
return this.native.getAudioTitle(); return this.native.getAudioTitle();
} }
@@ -91,7 +96,7 @@ class Drive implements IPeripheral {
this.native.ejectDisk(); this.native.ejectDisk();
} }
public inline function getDiskID(): Int { public inline function getDiskID():Int {
return this.native.getDiskID(); 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

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

View File

@@ -1,6 +1,5 @@
package kernel.peripherals; package kernel.peripherals;
import haxe.ds.ReadOnlyArray;
import kernel.peripherals.Modem; import kernel.peripherals.Modem;
import kernel.peripherals.Screen; import kernel.peripherals.Screen;
@@ -12,24 +11,15 @@ using tink.CoreApi;
Class responseable for retrieving peripherals. Class responseable for retrieving peripherals.
**/ **/
class Peripheral { class Peripheral {
/** public static function getAllAddresses():Array<String> {
Depends on: KernelEvents
**/
public static var instance:Peripheral;
@:allow(kernel.Init)
private function new() {
}
public function getAllAddresses(): Array<String> {
return cc.Peripheral.getNames().toArray(); return cc.Peripheral.getNames().toArray();
} }
public function isPresent(addr: String): Bool { public static function isPresent(addr:String):Bool {
return cc.Peripheral.isPresent(addr); 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)) { if (!cc.Peripheral.isPresent(addr)) {
return []; return [];
} }
@@ -37,11 +27,11 @@ class Peripheral {
return cc.Peripheral.getType(addr).toArray(); 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)); 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)) { if (!isPresent(addr)) {
return null; return null;
} }
@@ -54,18 +44,13 @@ class Peripheral {
return addr; 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)) { if (!isPresent(addr)) {
return null; return null;
} }
var types = getTypes(addr); var types = getTypes(addr);
var methodsMap = cc.Peripheral.getMethods(addr).toArray(); var methods = cc.Peripheral.getMethods(addr).toArray();
var methods: Array<String> = [];
for (method in methodsMap) {
methods.push(method);
}
return { return {
types: types, types: types,
@@ -73,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); var addr = safeGetAddr(addr, Screen.TYPE_NAME);
if (addr == null) return null; if (addr == null)
return null;
return new Screen(addr); return new Screen(addr);
} }
public static function getAllScreens():Array<Screen> {
public function getAllScreens(): Array<Screen> { return [for (addr in findAddrByType(Screen.TYPE_NAME)) new Screen(addr)];
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); var addr = safeGetAddr(addr, Modem.TYPE_NAME);
if (addr == null) return null; if (addr == null)
return null;
return new Modem(addr); return new Modem(addr);
} }
public function getAllModems(): Array<Modem> { public static function getAllModems():Array<Modem> {
return [ for (addr in findAddrByType(Modem.TYPE_NAME)) new Modem(addr)]; 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); var addr = safeGetAddr(addr, Drive.TYPE_NAME);
if (addr == null) return null; if (addr == null)
return null;
return new Drive(addr); return new Drive(addr);
} }
public function getAllDrives(): Array<Drive> { public static function getAllDrives():Array<Drive> {
return [ for (addr in findAddrByType(Drive.TYPE_NAME)) new Drive(addr)]; 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 // TODO: maybe handle restone differently to not duplicate event listeners
return new Redstone(side); 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); var addr = safeGetAddr(addr, Printer.TYPE_NAME);
if (addr == null) return null; if (addr == null)
return null;
return new Printer(addr); return new Printer(addr);
} }
public function getAllPrinters(): Array<Printer> { public static function getAllPrinters():Array<Printer> {
return [ for (addr in findAddrByType(Printer.TYPE_NAME)) new Printer(addr)]; 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); var addr = safeGetAddr(addr, EnergyStorage.TYPE_NAME);
if (addr == null) return null; if (addr == null)
return null;
return new EnergyStorage(addr); return new EnergyStorage(addr);
} }
public function getAllEnergyStorages(): Array<EnergyStorage> { public static function getAllEnergyStorages():Array<EnergyStorage> {
return [ for (addr in findAddrByType(EnergyStorage.TYPE_NAME)) new EnergyStorage(addr)]; 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 cc.Peripheral;
import lib.Rect; import lib.Rect;
import lib.Pos; import lib.ScreenPos;
class Printer implements IPeripheral { class Printer implements IPeripheral {
public static inline final TYPE_NAME:String = "printer"; public static inline final TYPE_NAME:String = "printer";
@@ -10,7 +10,7 @@ class Printer implements IPeripheral {
private final native:cc.periphs.Printer.Printer; private final native:cc.periphs.Printer.Printer;
private final addr:String; private final addr:String;
public function new(addr: String) { public function new(addr:String) {
this.native = Peripheral.wrap(addr); this.native = Peripheral.wrap(addr);
this.addr = addr; this.addr = addr;
} }
@@ -19,36 +19,38 @@ class Printer implements IPeripheral {
return addr; return addr;
} }
public function write(text: String){ public function getType():String {
return TYPE_NAME;
} }
public function getCurserPos(): Pos { public function write(text:String) {}
return new Pos({x: 0, y: 0});
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); this.native.setCursorPos(pos.x, pos.y);
} }
public function getPageSize(): Rect { public function getPageSize():Rect {
var pos = this.native.getPageSize(); var pos = this.native.getPageSize();
return new Rect({x: 0, y: 0}, {x: pos.x, y: pos.y}); 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(); return this.native.newPage();
} }
public function endPage(): Bool{ public function endPage():Bool {
return this.native.endPage(); return this.native.endPage();
} }
public function setPageTitle(title: String){ public function setPageTitle(title:String) {
this.native.setPageTitle(title); this.native.setPageTitle(title);
} }
public function getInkLevel(): Float{ public function getInkLevel():Float {
return this.native.getInkLevel(); return this.native.getInkLevel();
} }

View File

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

View File

@@ -1,21 +1,21 @@
package kernel.peripherals; package kernel.peripherals;
import cc.Peripheral; import cc.Peripheral;
import lib.Pos; import lib.ScreenPos;
import cc.Term.TerminalSize; import cc.Term.TerminalSize;
import kernel.ui.TermWriteable; import kernel.ui.ITermWriteable;
import lib.Vec.Vec2; import lib.Vec.Vec2;
import lib.Color; import lib.Color;
using tink.CoreApi; using tink.CoreApi;
class Screen implements TermWriteable implements IPeripheral { class Screen implements ITermWriteable implements IPeripheral {
public static inline final TYPE_NAME:String = "monitor"; public static inline final TYPE_NAME:String = "monitor";
private final nativ:cc.periphs.Monitor.Monitor; private final nativ:cc.periphs.Monitor.Monitor;
private final addr:String; 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>>; public final onResize:Signal<Vec2<Int>>;
@@ -26,7 +26,7 @@ class Screen implements TermWriteable implements IPeripheral {
this.nativ = Peripheral.wrap(addr); this.nativ = Peripheral.wrap(addr);
this.addr = addr; this.addr = addr;
KernelEvents.instance.onMonitorResize.handle(addr -> { KernelEvents.onMonitorResize.handle(addr -> {
if (addr == this.addr) { if (addr == this.addr) {
onResizeTrigger.trigger(getSize()); onResizeTrigger.trigger(getSize());
} }
@@ -39,6 +39,10 @@ class Screen implements TermWriteable implements IPeripheral {
return this.addr; return this.addr;
} }
public function getType():String {
return TYPE_NAME;
}
public function getTextScale():Float { public function getTextScale():Float {
return nativ.getTextScale(); return nativ.getTextScale();
} }
@@ -55,7 +59,7 @@ class Screen implements TermWriteable implements IPeripheral {
nativ.scroll(y); nativ.scroll(y);
} }
public function getCursorPos():Pos { public function getCursorPos():ScreenPos {
var rtn = nativ.getCursorPos(); var rtn = nativ.getCursorPos();
return { return {
x: rtn.x - 1, x: rtn.x - 1,
@@ -115,6 +119,6 @@ class Screen implements TermWriteable implements IPeripheral {
this.setBackgroundColor(Black); this.setBackgroundColor(Black);
this.setTextColor(White); this.setTextColor(White);
this.clear(); 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"; var Back = "back";
@:from @:from
static public function fromString(s: String) { static public function fromString(s:String) {
switch (s) { switch (s) {
case "top": case "top":
return 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. Defines an independent process that can be run by the kernel.
**/ **/
interface Process { interface IProcess {
public function run(handle: ProcessHandle): Void; public function run(handle:ProcessHandle):Void;
} }

View File

@@ -1,33 +1,37 @@
package kernel.ps; package kernel.ps;
import kernel.turtle.TurtleMutex;
import kernel.ps.ProcessManager.PID; import kernel.ps.ProcessManager.PID;
import kernel.ui.WindowContext; import kernel.ui.WindowContext;
import kernel.ui.WindowManager; import kernel.ui.WindowManager;
import haxe.ds.ReadOnlyArray; import haxe.ds.ReadOnlyArray;
using tink.CoreApi; using tink.CoreApi;
typedef HandleConfig = { typedef HandleConfig = {
?args: Array<String>, ?args:Array<String>,
?onWrite: Callback<String>, ?onWrite:Callback<String>,
?onExit: Callback<Bool>, ?onExit:Callback<Bool>,
} }
class ProcessHandle { 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 config:HandleConfig;
private final closeFuture: Future<Bool>; private final closeFuture:Future<Bool>;
private var closeFutureResolev: Bool -> Void; private var closeFutureResolev:Bool->Void;
private final windowContexts: Array<WindowContext> = []; private final windowContexts:Array<WindowContext> = [];
private final cbLinks:Array<CallbackLink> = []; private final cbLinks:Array<CallbackLink> = [];
private final deferFuncs:Array<Void->Void> = [];
private var hasExited:Bool = false;
@:allow(kernel.ps.ProcessManager) @:allow(kernel.ps.ProcessManager)
private function new(config: HandleConfig,pid: PID) { private function new(config:HandleConfig, pid:PID) {
this.config = config; this.config = config;
this.pid = pid; this.pid = pid;
this.closeFuture = new Future<Bool>((trigger)->{ this.closeFuture = new Future<Bool>((trigger) -> {
this.closeFutureResolev = trigger; this.closeFutureResolev = trigger;
return null; return null;
}); });
@@ -37,39 +41,45 @@ class ProcessHandle {
} }
} }
public function onExit(): Future<Bool> { public function onExit():Future<Bool> {
return this.closeFuture; return this.closeFuture;
} }
public function close(success: Bool = true): Void { public function close(success:Bool = true):Void {
this.hasExited = true;
this.dispose(); this.dispose();
this.closeFutureResolev(success);
EndOfLoop.endOfLoop(() -> {
this.closeFutureResolev(success);
});
ProcessManager.removeProcess(this.pid); ProcessManager.removeProcess(this.pid);
} }
public function write(message: String): Void { public function write(message:String):Void {
if (this.config.onWrite != null){ if (this.hasExited)
return;
if (this.config.onWrite != null) {
this.config.onWrite.invoke(message); this.config.onWrite.invoke(message);
} }
} }
public function writeLine(message: String): Void { public function writeLine(message:String):Void {
this.write(message + "\n"); this.write(message + "\n");
} }
public function createBufferdWindowContext(): WindowContext { public function createBufferdWindowContext():WindowContext {
var ctx = WindowManager.instance.createNewContext(); var ctx = WindowManager.createNewContext();
this.windowContexts.push(ctx); this.windowContexts.push(ctx);
return ctx; return ctx;
} }
public function createStatelessWindowContext(): {ctx:WindowContext, setRenderFunc: (() -> Void) -> Void, requestRender:() -> Void} { public function createStatelessWindowContext():{ctx:WindowContext, setRenderFunc:(() -> Void)->Void, requestRender:() -> Void} {
var ctx = WindowManager.instance.createNewStatelessContext(); var ctx = WindowManager.createNewStatelessContext();
this.windowContexts.push(ctx.ctx); this.windowContexts.push(ctx.ctx);
return ctx; return ctx;
} }
public function getWindowContexts(): ReadOnlyArray<WindowContext> { public function getWindowContexts():ReadOnlyArray<WindowContext> {
return this.windowContexts; return this.windowContexts;
} }
@@ -77,17 +87,37 @@ class ProcessHandle {
for (link in this.cbLinks) { for (link in this.cbLinks) {
link.cancel(); link.cancel();
} }
for (func in this.deferFuncs) {
func();
}
} }
public function getPid(): PID { public function getPid():PID {
return this.pid; return this.pid;
} }
public function addCallbackLink(link: CallbackLink) { public function addCallbackLink(link:CallbackLink) {
this.cbLinks.push(link); this.cbLinks.push(link);
} }
function get_args():ReadOnlyArray<String> { public function addDeferFunc(func:Void->Void) {
return this.config.args; this.deferFuncs.push(func);
}
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,27 +8,28 @@ using tink.CoreApi;
typedef PID = Int; typedef PID = Int;
class ProcessManager { class ProcessManager {
private static final processList = new Map<PID,ProcessHandle>(); private static final processList = new Map<PID, ProcessHandle>();
public static function run(process:Process, config: HandleConfig):PID { public static function run(process:IProcess, config:HandleConfig):PID {
var pid = createPID(); var pid = createPID();
var handle = new ProcessHandle(config, pid); var handle = new ProcessHandle(config, pid);
processList.set(pid, handle); processList.set(pid, handle);
try{ try {
process.run(handle); process.run(handle);
}catch(e:Dynamic){ } catch (e:Dynamic) {
Log.error("Error while running process: " + e); Log.error("Error while running process: " + e);
handle.close(false); handle.close(false);
} }
return pid; return pid;
} }
public static function kill(pid: PID) { public static function kill(pid:PID) {
if (!processList.exists(pid)){ if (!processList.exists(pid)) {
throw new Error("Process with PID " + pid + " does not exist"); Log.warn("Trying to kill non-existing process: " + pid);
return;
} }
var handle = processList.get(pid); var handle = processList.get(pid);
@@ -36,7 +37,7 @@ class ProcessManager {
handle.close(); handle.close();
} }
private static function createPID(): PID { private static function createPID():PID {
// TODO: better PID generation // TODO: better PID generation
// generate a random PID // generate a random PID
@@ -44,7 +45,7 @@ class ProcessManager {
} }
@:allow(kernel.ui.WindowManager) @:allow(kernel.ui.WindowManager)
private static function getProcess(pid:PID):Null<ProcessHandle>{ private static function getProcess(pid:PID):Null<ProcessHandle> {
return processList.get(pid); return processList.get(pid);
} }

View File

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

View File

@@ -7,137 +7,143 @@ import lib.KVStore;
using tink.CoreApi; using tink.CoreApi;
class ServiceManager { 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() { Add a service to be automatically started.
this.startAllEnabled(); **/
} 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(); var store = KVStore.getStoreForClass();
store.load();
var enabled = store.get("enabled",[]); var enabled = store.get("enabled", []);
enabled.push(name); enabled.push(name);
store.set("enabled",enabled); store.set("enabled", enabled);
store.save(); store.save();
}
/** return Success(Noise);
Remove a service from being automatically started. }
**/
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;
}
enabled.splice(index,1); /**
store.save(); Remove a service from being automatically started.
} **/
private static function disable(name:String):Outcome<Noise, String> {
var store = KVStore.getStoreForClass();
store.load();
private function startAllEnabled() { var enabled:Array<String> = store.get("enabled");
var store = KVStore.getStoreForClass(); var index = enabled.indexOf(name);
var enabled: Array<String> = store.get("enabled",[]); if (index == -1) {
for (name in enabled){ return Failure("Service not found");
this.start(name); }
}
}
private function load(name: String): Null<Service> { enabled.splice(index, 1);
var store = new KVStore('service/${name}'); store.save();
store.load();
if (!store.exists("service")){
return null;
}
return store.get("service"); return Success(Noise);
} }
public function register(name: String, binName: String,args: Array<String>): Outcome<Noise,String> { private static function startAllEnabled() {
if (BinStore.instance.getBinByAlias(binName) == null){ var store = KVStore.getStoreForClass();
return Failure("bin not found"); store.load();
}
if (this.load(name) != null){ var enabled:Array<String> = store.get("enabled", []);
return Failure("service already exists"); for (name in enabled) {
} start(name);
}
}
var service = new Service(binName,name,args); private static function load(name:String):Null<Service> {
var store = new KVStore('service/${name}');
store.load();
if (!store.exists("service")) {
return null;
}
var store = new KVStore('service/${name}'); return store.get("service");
store.set("service",service); }
store.save();
Log.info('Service ${name} registered'); public static function register(name:String, binName:String, args:Array<String>):Outcome<Noise, String> {
return Success(Noise); if (BinStore.getNameByAlias(binName) == null) {
} return Failure("bin not found");
}
public function unregister(name: String): Outcome<Noise,String> { if (load(name) != null) {
if (this.services.exists(name)){ return Failure("service already exists");
return Failure("service is running"); }
}
KVStore.removeNamespace('service/${name}'); var service = new Service(binName, name, args);
Log.info('Service ${name} unregistered');
return Success(Noise);
}
public function start(name: String): Outcome<Noise,String> {
var service = this.load(name);
if (service == null){
return Failure("service not found");
}
service.start(); var store = new KVStore('service/${name}');
this.services.set(name,service); store.set("service", service);
store.save();
Log.info('Service ${name} started'); Log.info('Service ${name} registered');
return Success(Noise); return Success(Noise);
} }
public function stop(name: String): Outcome<Noise,String> { public static function unregister(name:String):Outcome<Noise, String> {
if (!this.services.exists(name)){ if (services.exists(name)) {
return Failure("service not found"); return Failure("service is running");
} }
var service = this.services.get(name); KVStore.removeNamespace('service/${name}');
service.stop(); Log.info('Service ${name} unregistered');
this.services.remove(name); return Success(Noise);
}
Log.info('Service ${name} stopped'); public static function start(name:String):Outcome<Noise, String> {
return Success(Noise); var service = load(name);
} if (service == null) {
return Failure("service not found");
}
public function listRunning(): Array<String> { service.start();
var running = []; services.set(name, service);
for (name in this.services.keys()){
running.push(name);
}
return running;
}
public function get(name: String): Null<Dynamic> { Log.info('Service ${name} started');
if (!this.services.exists(name)){ return Success(Noise);
return null; }
}
// TODO: Maybe there is a way to check types here? public static function stop(name:String):Outcome<Noise, String> {
if (!services.exists(name)) {
var srv = this.services.get(name); return Failure("service not found");
return srv.ps; }
}
var service = services.get(name);
service.stop();
services.remove(name);
Log.info('Service ${name} stopped');
return Success(Noise);
}
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; package kernel.turtle;
import kernel.log.Log;
import kernel.turtle.Types; import kernel.turtle.Types;
import kernel.gps.INS; import kernel.gps.INS;
using tink.CoreApi; using tink.CoreApi;
class Turtle { class Turtle {
public static var instance:Turtle;
public static final MAX_SLOTS:Int = 16; 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 { 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) { if (r.successful) {
return Outcome.Success(null); return Outcome.Success(null);
} else { } 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 r = cc.Turtle.forward();
var r2 = conterToOutcome(r); var r2 = conterToOutcome(r);
if (r2.isSuccess()) INS.instance.moveForward(); if (r2.isSuccess())
INS.moveForward();
return r2; return r2;
} }
public function back():Outcome<Noise, String> { public static function back():Outcome<Noise, String> {
var r = cc.Turtle.back(); var r = cc.Turtle.back();
var r2 = conterToOutcome(r); var r2 = conterToOutcome(r);
if (r2.isSuccess()) INS.instance.moveBackward(); if (r2.isSuccess())
INS.moveBackward();
return r2; return r2;
} }
public function up():Outcome<Noise, String> { public static function up():Outcome<Noise, String> {
var r = cc.Turtle.up(); var r = cc.Turtle.up();
var r2 = conterToOutcome(r); var r2 = conterToOutcome(r);
if (r2.isSuccess()) INS.instance.moveUp(); if (r2.isSuccess())
INS.moveUp();
return r2; return r2;
} }
public function down():Outcome<Noise, String> { public static function down():Outcome<Noise, String> {
var r = cc.Turtle.down(); var r = cc.Turtle.down();
var r2 = conterToOutcome(r); var r2 = conterToOutcome(r);
if (r2.isSuccess()) INS.instance.moveDown(); if (r2.isSuccess())
INS.moveDown();
return r2; return r2;
} }
public function turnLeft():Outcome<Noise, String> { public static function turnLeft():Outcome<Noise, String> {
var r = cc.Turtle.turnLeft(); var r = cc.Turtle.turnLeft();
var r2 = conterToOutcome(r); var r2 = conterToOutcome(r);
if (r2.isSuccess()) INS.instance.turnRight(); if (r2.isSuccess())
INS.turnRight();
return r2; return r2;
} }
public function turnRight():Outcome<Noise, String> { public static function turnRight():Outcome<Noise, String> {
var r = cc.Turtle.turnRight(); var r = cc.Turtle.turnRight();
var r2 = conterToOutcome(r); var r2 = conterToOutcome(r);
if (r2.isSuccess()) INS.instance.turnRight(); if (r2.isSuccess())
INS.turnRight();
return r2; 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; var r:cc.Turtle.TurtleActionResult;
// FIXME: upstream needs to be fixed to accept ToolSide // FIXME: upstream needs to be fixed to accept ToolSide
@@ -90,7 +92,37 @@ class Turtle {
return conterToOutcome(r); 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; var r:cc.Turtle.TurtleActionResult;
switch dir { switch dir {
case Front: case Front:
@@ -103,7 +135,7 @@ class Turtle {
return conterToOutcome(r); 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; var r:cc.Turtle.TurtleActionResult;
switch dir { switch dir {
case Front: case Front:
@@ -116,32 +148,26 @@ class Turtle {
return conterToOutcome(r); 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); var r = cc.Turtle.drop(count);
return conterToOutcome(r); return conterToOutcome(r);
} }
public function selectSlot(slot:TurtleSlot):Outcome<Noise, Noise> { public static function selectSlot(slot:TurtleSlot):Outcome<Noise, Noise> {
// TODO: slot in bounds? var r = cc.Turtle.select(slot.toCCSlot());
var r = cc.Turtle.select(slot + 1);
return (r) ? Outcome.Success(null) : Outcome.Failure("Slot out of bounds"); return (r) ? Outcome.Success(null) : Outcome.Failure("Slot out of bounds");
} }
public function getItemCount(?slot:TurtleSlot):Int { public static function getItemCount(?slot:TurtleSlot):Int {
// TODO: slot in bounds? return cc.Turtle.getItemCount(slot.toCCSlot());
return cc.Turtle.getItemCount(slot + 1);
} }
public function getItemSpace(?slot:TurtleSlot):Int { public static function getItemSpace(?slot:TurtleSlot):Int {
// TODO: slot in bounds? return cc.Turtle.getItemSpace(slot.toCCSlot());
return cc.Turtle.getItemSpace(slot + 1);
} }
public function detect(dir:InteractDirections):Bool { public static function detect(dir:InteractDirections):Bool {
switch dir { switch dir {
case Front: case Front:
return cc.Turtle.detect(); 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 { switch dir {
case Front: case Front:
return cc.Turtle.compare(); 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; var r:cc.Turtle.TurtleActionResult;
// FIXEM: upstream needs to be fixed to accept ToolSide // FIXEM: upstream needs to be fixed to accept ToolSide
@@ -179,7 +205,7 @@ class Turtle {
return conterToOutcome(r); 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? // TODO: ammount in bounds?
var r:cc.Turtle.TurtleActionResult; var r:cc.Turtle.TurtleActionResult;
@@ -195,40 +221,40 @@ class Turtle {
return conterToOutcome(r); 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 var r = cc.Turtle.getFuelLevel(); // FIXME: can be a string. Has to be fixed upstream
return r; 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); var r = cc.Turtle.refuel(ammount);
return conterToOutcome(r); return conterToOutcome(r);
} }
public function canRefultWithSlot():Bool { public static function canRefultWithSlot():Bool {
var r = cc.Turtle.refuel(0); var r = cc.Turtle.refuel(0);
return r.successful; return r.successful;
} }
public function compareSlot(otherSlot:TurtleSlot):Bool { public static function compareSlot(otherSlot:TurtleSlot):Bool {
return cc.Turtle.compareTo(otherSlot + 1); return cc.Turtle.compareTo(otherSlot.toCCSlot());
} }
public function transfer(from:TurtleSlot, to:TurtleSlot, ?count:Int):Outcome<Noise, Noise> { public static function transfer(from:TurtleSlot, to:TurtleSlot, ?count:Int):Outcome<Noise, Noise> {
this.selectSlot(from); selectSlot(from);
var r = cc.Turtle.transferTo(to + 1, count); var r = cc.Turtle.transferTo(to.toCCSlot(), count);
return r ? Outcome.Success(null) : Outcome.Failure(null); return r ? Outcome.Success(null) : Outcome.Failure(null);
} }
public function getSelectedSlot():TurtleSlot { public static function getSelectedSlot():TurtleSlot {
return cc.Turtle.getSelectedSlot() - 1; 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 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 { switch side {
case Left: case Left:
return conterToOutcome(cc.Turtle.equipLeft()); 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; var r:cc.Turtle.TurtleInspectResult;
switch dir { switch dir {
case Front: case Front:
@@ -260,8 +286,8 @@ class Turtle {
}); });
} }
public function getItemDetail(?detailed:Bool = false, ?slot:TurtleSlot):Option<ItemInspect> { public static function getItemDetail(slot:TurtleSlot, ?detailed:Bool = false):Option<ItemInspect> {
var r = cc.Turtle.getItemDetail(slot + 1); // FIXME: can take detailed as flag. Has to be fixed upstream var r = cc.Turtle.getItemDetail(slot.toCCSlot()); // FIXME: can take detailed as flag. Has to be fixed upstream
if (r == null) { if (r == null) {
return None; return None;
@@ -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) { if (limit < 1 || limit > 64) {
return Outcome.Failure("Crafting limit out of bounds"); 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 damage:Int;
public var count:Int; public var count:Int;
} }
typedef TurtleSlot = Int;

View File

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

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