Compare commits
357 Commits
c78bdab7e4
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
c665401fd8
|
|||
|
2799a0be3d
|
|||
|
e527dd5b6a
|
|||
|
fe17b4fd67
|
|||
|
90b4015ba1
|
|||
|
2dd85c2b26
|
|||
|
1d60e13792
|
|||
|
9d6e8a366b
|
|||
|
5167533c6d
|
|||
|
aac527ae89
|
|||
|
af6a4c840b
|
|||
|
afbd1dfd68
|
|||
|
b305594ea4
|
|||
|
df7991763d
|
|||
|
e0f8d274e7
|
|||
|
08f41ccb0b
|
|||
|
28ec48cc85
|
|||
|
4f2b6e7c53
|
|||
|
2e0bda7a6e
|
|||
|
87c93f3ae0
|
|||
|
628aef06e3
|
|||
|
2603527b67
|
|||
|
39a7da716c
|
|||
|
afc0309222
|
|||
|
0d9ce5d6b1
|
|||
|
be58ed1c05
|
|||
|
fe16799bbb
|
|||
|
5f5e899f6e
|
|||
|
8546659ae0
|
|||
|
249a48807a
|
|||
|
be405887e2
|
|||
|
73e2ccdcb8
|
|||
|
b05dae958d
|
|||
|
3e993b84eb
|
|||
|
09fef78f9a
|
|||
|
7d30d9d971
|
|||
|
29285641a4
|
|||
|
37eab6f8e2
|
|||
|
f95c262c57
|
|||
|
5f42941d76
|
|||
|
1108eab403
|
|||
|
e5b990ae61
|
|||
|
5925f851c4
|
|||
|
d13831f213
|
|||
|
a9f6adcd9d
|
|||
|
757d7098cf
|
|||
|
4e69bda3c8
|
|||
|
755e5ff6b4
|
|||
|
9353ccba8a
|
|||
|
5c71ab1c30
|
|||
|
36f97f09d5
|
|||
|
e04021425a
|
|||
|
d89956a626
|
|||
|
dd146d1caf
|
|||
|
adb758bd53
|
|||
|
baae1428bd
|
|||
|
7bd18c1a4c
|
|||
|
99489e37df
|
|||
|
86f6dbf302
|
|||
|
4836cae3fa
|
|||
|
69ca3f3282
|
|||
|
3484503ba1
|
|||
|
1efa8fa212
|
|||
|
9541b653b5
|
|||
|
4ab3d868c1
|
|||
|
2ab5a38894
|
|||
|
e3875f76f6
|
|||
|
fdcb81b565
|
|||
|
72654b1036
|
|||
|
fe85c33d64
|
|||
|
1d9e08641e
|
|||
|
638d1acbe0
|
|||
|
815ccdaab9
|
|||
|
c0741c48aa
|
|||
|
e2ab296e79
|
|||
|
64f4304626
|
|||
|
8cd105b84c
|
|||
|
ce0e89f5b3
|
|||
|
c9e0ceef32
|
|||
|
6170fdd0d3
|
|||
|
3fce8d515a
|
|||
|
fe88e065ab
|
|||
|
e2916a213a
|
|||
|
22509932c3
|
|||
|
f109305f48
|
|||
|
295d284bc1
|
|||
|
92deb7177f
|
|||
|
3e09bcfad2
|
|||
|
762f72c160
|
|||
|
1d2a43155a
|
|||
|
9bb6b8bf2b
|
|||
|
7f8c0d154c
|
|||
|
f9c10cfc0b
|
|||
|
be89ca12bd
|
|||
|
ca79714e8d
|
|||
|
e0d4844890
|
|||
|
12eb9d05de
|
|||
|
e39720b63d
|
|||
| e593fc52c4 | |||
| ad0c62d6e5 | |||
| 0cac0053e3 | |||
| a568c9cdd8 | |||
| 44772557ba | |||
| 0540cc465a | |||
| 21387cd8e7 | |||
| a46760e587 | |||
| c83f457968 | |||
| b8f5ffb93a | |||
| 3c59e045de | |||
| 4b8bc87db9 | |||
| 0c7280a2f5 | |||
| 98b8465436 | |||
| 016063e3be | |||
| 0fc2d4d397 | |||
| da3f00acb6 | |||
| db50a93512 | |||
| f3a13e4247 | |||
| 91972107eb | |||
| 088fce0aaa | |||
| 3b3c69ee56 | |||
| 5a9d463192 | |||
| 97c906e013 | |||
| 505d318ffb | |||
| 89f209130e | |||
| 74d0232160 | |||
| 9deea0ee98 | |||
| a93ee1cddf | |||
| e1e0180502 | |||
| adc5ab1849 | |||
| 1a523cb3ce | |||
| dfaabea00d | |||
| 8683eaf17a | |||
| 5fa6c3ecbf | |||
| 10a061c41b | |||
| c390519393 | |||
| 4f881117cf | |||
| 4084659a4a | |||
| 63e279f879 | |||
| f68ae00098 | |||
| 2937de86d6 | |||
| 788c839937 | |||
| 90e76c8cd9 | |||
| d2873d6353 | |||
| d1f9104aba | |||
| 07ad65d5cf | |||
| 3a2613d916 | |||
| dbd8038851 | |||
| 7bfe594b4b | |||
| 4dcc060e9a | |||
| f124525d2d | |||
| 8333cabdc8 | |||
| 7b55241e00 | |||
| b60c9774e1 | |||
| 7861135c05 | |||
| 909aac941c | |||
| e686afb298 | |||
| 7996dd062d | |||
| 9d6979c8e8 | |||
| 7c7529ae39 | |||
| 6fcbcfce8d | |||
| 86793bfdc1 | |||
| 644ebe1c05 | |||
| 7aa5698486 | |||
| 9a9041bd98 | |||
| f92d777ee1 | |||
| f85ff77728 | |||
| 13fe7fbab1 | |||
| d0c01800a6 | |||
| fd2b53167e | |||
| 2051f486d4 | |||
| f8b226bae5 | |||
| cd6808e62c | |||
| 30304ec5e6 | |||
| 00a9c8dc4f | |||
| c86d0e1d8b | |||
| 8c041766c9 | |||
| 779933be32 | |||
| 06b3e37138 | |||
| 2a7f02c5e3 | |||
| e11a9383ee | |||
| 182a2bce7d | |||
| 05a220110d | |||
| 9b10ff1d14 | |||
| 0c9f186d8a | |||
| 560c85ea61 | |||
| cb7e284313 | |||
| 563211af6f | |||
| 5985b0c8be | |||
| f35c98f912 | |||
| a343db133e | |||
| dd2e9a4993 | |||
| eb39131056 | |||
| c4260aa719 | |||
| e73b4c4d14 | |||
| 94d02d60af | |||
| 16b7db779f | |||
| ec63a65ba3 | |||
| ec89107262 | |||
| 8dca828cf3 | |||
| 2da337b8a9 | |||
| 17be4149db | |||
| f9aadbcbe9 | |||
| bd3402fc39 | |||
| 747bde4aa6 | |||
| 655439461a | |||
| b9452cd598 | |||
| 0c5775560c | |||
| a1fdca689f | |||
| b9fcbd9040 | |||
| ba05ff7645 | |||
| 14f7b6c6df | |||
| f8f5f5e5c7 | |||
| bf378deea2 | |||
| df39865c3d | |||
| 4a7b57c47f | |||
| 4f8db600dc | |||
| 7fe52b1a8a | |||
| da78641aec | |||
| 7480afc5a1 | |||
| 4fbd064439 | |||
| f7c320c123 | |||
| 409c4fb411 | |||
| 82827ed921 | |||
| 793639c69d | |||
| 4ae6fb4bc7 | |||
| 9f6bdf6851 | |||
| 347e210f5f | |||
| d0670325f2 | |||
| 8991a99585 | |||
| 05fa6203d1 | |||
| 204e464b1b | |||
| 84d437104d | |||
| b0be0ba8b4 | |||
| a4e4e103bd | |||
| 3e7d993662 | |||
| dabc42ea25 | |||
| af8241505f | |||
| d56b871554 | |||
| 8934d40c0b | |||
| 8f7739c26a | |||
| a1ce5957d1 | |||
| 9e128eaad2 | |||
| 881aad743d | |||
| df86bae738 | |||
| c731dcef36 | |||
| e14138c7a0 | |||
| d684360547 | |||
| a9eb569a02 | |||
| 77d330a71d | |||
| 391c6b19fd | |||
| 632173d122 | |||
| 7aa1306077 | |||
| 15b7112348 | |||
| 2cbcc86dce | |||
| f71505ce28 | |||
| cb1f892e6d | |||
| b54a25eec6 | |||
| a87f50eec7 | |||
| 95a9ab63d0 | |||
| f6c0feda4b | |||
| 916831743e | |||
| 72e71c8e36 | |||
| 8180599c04 | |||
| 0b965d32b6 | |||
| f988c79e2f | |||
| 45a8851e2a | |||
| a6ed7818da | |||
| 3cb1811dcb | |||
| c6e5a836ea | |||
| a7dbdff535 | |||
| 2b8aa06117 | |||
| 1863462b44 | |||
| 209e40d0d5 | |||
| 621dfb71ca | |||
| 8d279b31a5 | |||
| 6a933768d9 | |||
| 879e68d70d | |||
| ec7535ccb6 | |||
| f21dd48106 | |||
| f7e7059641 | |||
| 3219738799 | |||
| 5ab1a46840 | |||
| 1ad78c23bb | |||
| ceb55e86dc | |||
| e1bb9c3ac3 | |||
| aca39ea46f | |||
| b40e293d6e | |||
| dffb493d4f | |||
| a5c023c78a | |||
| 82939e53cc | |||
| 96c6595846 | |||
| 8339c69ecc | |||
| 943391dddc | |||
| fa62e3a2eb | |||
| a1bb41f8a2 | |||
| 57bf49bea9 | |||
| 24d9268b4a | |||
| a75f86cbc1 | |||
| 35be02dbe2 | |||
| aee8c0fd3c | |||
| f3141a0f99 | |||
| 72dfcb1aa1 | |||
| 7f6e8bb273 | |||
| 7ff19b3ed5 | |||
| e777736e6e | |||
| de35f34173 | |||
| edc3195192 | |||
| 47c38ac731 | |||
| 60b369fe27 | |||
| 1b9009d8e3 | |||
| 3304d10da5 | |||
| 7c4fc95584 | |||
| 672826c326 | |||
| 335992a338 | |||
| 6b3f0760ca | |||
| df77704d1c | |||
| ce45bf2726 | |||
| 1aa93574c8 | |||
| 33c9e3ab6c | |||
| 46f03fbfe4 | |||
| 043d431e94 | |||
| 1881d8f488 | |||
| 3b6bc85271 | |||
| bddb80c42d | |||
| 1aa3b43d8c | |||
| daff654867 | |||
| 242db10083 | |||
| 8318cc5c21 | |||
| eb43c51e50 | |||
| d65b06a9b4 | |||
| 66aa84d896 | |||
| bca366d7e5 | |||
| bbdbef889a | |||
| ecf4f9c719 | |||
| 4903014ebd | |||
| 1a808055ea | |||
| 2402eb1a44 | |||
| 639392c6de | |||
| 3e275e2b06 | |||
| a20d3bc07e | |||
| d4703fb725 | |||
| c96d06653a | |||
| ebd9709c3d | |||
| e695fc9b6d | |||
| 33731778ea | |||
| 418952bd25 | |||
| e2431666e1 | |||
| fd745d623c | |||
| da3eda84d3 | |||
| 5c1b142630 | |||
| ec00115216 | |||
| fd1428c749 | |||
| 0ad907f74a | |||
| f21e49c520 | |||
| 71deddc86a | |||
| 1d2d4d88f8 | |||
| 306c28454b |
19
.editorconfig
Normal file
19
.editorconfig
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
tab_width = 4
|
||||||
|
|
||||||
|
[*.hx]
|
||||||
|
indent_style = tab
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
[package.json]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
/build
|
/build
|
||||||
/node_modules
|
node_modules
|
||||||
|
/dump
|
||||||
|
|||||||
79
Makefile
79
Makefile
@@ -1,39 +1,90 @@
|
|||||||
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
|
||||||
BUILD_DIR := build
|
HAXE_ZIP_NAME = "haxe.zip"
|
||||||
HAXE_FLAGS = -D webconsole
|
UNPACK_NAME = unpack.lua
|
||||||
|
UNPACK_POLYFILLED_NAME = unpack.polyfill.lua
|
||||||
|
UNPACK_MINIFYD_NAME = unpack.min.lua
|
||||||
|
BUILD_DIR = build
|
||||||
|
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
|
||||||
|
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 $(MIN_PATH)
|
all: clean build unpack
|
||||||
|
|
||||||
|
build: HAXE_FLAGS += -D analyzer-optimize -D no-traces
|
||||||
build: $(MIN_PATH)
|
build: $(MIN_PATH)
|
||||||
|
|
||||||
$(HAXE_PATH): $(shell find src -name '*.hx')
|
debug: HAXE_FLAGS += -D webconsole -D error_stack --debug
|
||||||
haxe build.hxml $(HAXE_FLAGS)
|
debug: $(MIN_PATH)
|
||||||
|
|
||||||
$(MIN_PATH): $(POLYFILL_PATH)
|
unpack: $(UNPACK_MINIFYD_PATH) $(HAXE_ZIP_PATH)
|
||||||
node minify.js $(POLYFILL_PATH) $@
|
|
||||||
|
$(HAXE_PATH): HAXE_FLAGS += --main kernel.Entrypoint --lua $(HAXE_PATH)
|
||||||
|
$(HAXE_PATH): $(shell find src -name '*.hx')
|
||||||
|
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) > $@
|
||||||
|
|
||||||
deps: package.json build.hxml
|
$(MIN_PATH): $(POLYFILL_PATH)
|
||||||
haxelib install all --always && yarn install
|
node tools/minify.js < $(POLYFILL_PATH) > $@
|
||||||
|
|
||||||
|
$(HAXE_ZIP_PATH): $(MIN_PATH)
|
||||||
|
node tools/zlibDeflate.js < $(MIN_PATH) > $@
|
||||||
|
|
||||||
|
$(UNPACK_PATH): HAXE_FLAGS += --main Unpack -D analyzer-optimize --lua $(UNPACK_PATH)
|
||||||
|
$(UNPACK_PATH): $(shell find src -name '*.hx')
|
||||||
|
haxe $(BUILD_HXML) $(HAXE_FLAGS)
|
||||||
|
|
||||||
|
$(UNPACK_POLYFILLED_PATH): $(UNPACK_PATH)
|
||||||
|
cat $(POLYFILL_SRC) $(UNPACK_PATH) > $@
|
||||||
|
|
||||||
|
$(UNPACK_MINIFYD_PATH): $(UNPACK_POLYFILLED_PATH)
|
||||||
|
node tools/minify.js < $(UNPACK_POLYFILLED_PATH) > $@
|
||||||
|
|
||||||
|
.PHONY: deps
|
||||||
|
deps: deps-hx deps-node
|
||||||
|
|
||||||
|
.PHONY: deps-hx
|
||||||
|
deps-hx:
|
||||||
|
haxelib install all --always
|
||||||
|
|
||||||
|
.PHONY: deps-node
|
||||||
|
deps-node:
|
||||||
|
npm install
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(BUILD_DIR)
|
rm -rf $(BUILD_DIR)
|
||||||
|
mkdir $(BUILD_DIR)
|
||||||
|
|
||||||
|
.PHONY: watch
|
||||||
watch:
|
watch:
|
||||||
find src -name "*.hx" | entr make build
|
find src | entr make debug
|
||||||
|
|
||||||
debug:
|
.PHONY: emulator
|
||||||
craftos --mount-ro /=build
|
emulator:
|
||||||
|
$(CREAFTOS_PATH) --mount-ro /=$(shell pwd)/$(BUILD_DIR)
|
||||||
|
|
||||||
|
.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
|
||||||
46
README.md
Normal file
46
README.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
General purpose "operation system" for [ComputerCraft](https://tweaked.cc/) build with [Haxe](https://haxe.org/).
|
||||||
|
|
||||||
|
# Features
|
||||||
|
|
||||||
|
- TCP like rednet messages
|
||||||
|
- Rednet message routing
|
||||||
|
- Hardware abstraction
|
||||||
|
- Virtual screens to switch between multiple GUI apps
|
||||||
|
- Reactive UI framework
|
||||||
|
- Solid base to easily add applications
|
||||||
|
|
||||||
|
# Building
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
- `haxe` compiler
|
||||||
|
- `haxelib` for dependencies
|
||||||
|
- `nodejs` and package manager like `yarn`
|
||||||
|
- `make`
|
||||||
|
|
||||||
|
run `make deps && make`. The `bundle.min.lua` inside the `build` dir is the final file.
|
||||||
|
|
||||||
|
# Useful links
|
||||||
|
|
||||||
|
[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/)
|
||||||
|
|
||||||
|
[Tinkerbell Core Library](https://haxetink.github.io/tink_core/#/)
|
||||||
|
|
||||||
|
# Development
|
||||||
|
|
||||||
|
There are a couple of tool to your disposal that makes working easier.
|
||||||
|
|
||||||
|
## Watch
|
||||||
|
|
||||||
|
Run `make watch` to recompile when a file changed.
|
||||||
|
|
||||||
|
## Emulator
|
||||||
|
|
||||||
|
You could use Minecraft to run the program or you could use [craftos pc](https://www.craftos-pc.cc/) as an emulator. Just install it and run `make emulator`.
|
||||||
|
There is an AppImage available [here](https://github.com/MCJack123/craftos2/releases).
|
||||||
|
|
||||||
|
## Websconsole
|
||||||
|
|
||||||
|
Use `make webconsole` to run a http server that prints the log output of the program to the terminal. Run `haxe` with the `-D webconsole` flag.
|
||||||
|
You may need to [allow connections](https://github.com/cc-tweaked/CC-Tweaked/wiki/Allowing-access-to-local-IPs) to any ip when using webconsole ingame.
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
-p src
|
-p src
|
||||||
--main Startup
|
|
||||||
|
|
||||||
--library cctweaked:git:https://git.kapelle.org/niklas/cctweaked-haxelib.git
|
--library cctweaked:git:https://git.kapelle.org/niklas/cctweaked-haxelib.git
|
||||||
--library tink_core
|
--library tink_core
|
||||||
|
--library compiletime
|
||||||
|
|
||||||
--dce full
|
--dce full
|
||||||
|
|
||||||
--lua build/haxe.lua
|
|
||||||
-D lua-vanilla
|
-D lua-vanilla
|
||||||
-D lua-ver 5.1
|
-D lua-ver 5.1
|
||||||
|
|
||||||
|
--macro include("bin")
|
||||||
|
|||||||
24
buildServer/Dockerfile
Normal file
24
buildServer/Dockerfile
Normal 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
105
buildServer/index.js
Normal 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
701
buildServer/package-lock.json
generated
Normal 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
11
buildServer/package.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
30
console.js
30
console.js
@@ -1,30 +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;
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = "";
|
|
||||||
|
|
||||||
req.on('data', chunk => {
|
|
||||||
data += chunk;
|
|
||||||
})
|
|
||||||
|
|
||||||
req.on('end', () => {
|
|
||||||
console.log(`[${time()}]${data}`);
|
|
||||||
res.writeHead(200);
|
|
||||||
res.end();
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("Listening on port 8080")
|
|
||||||
server.listen(8080);
|
|
||||||
253
doc/Concepts.md
Normal file
253
doc/Concepts.md
Normal 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
112
doc/Manual.md
Normal 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
|
||||||
10
minify.js
10
minify.js
@@ -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
35
package-lock.json
generated
Normal 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
15
pre-commit.sh
Executable 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
|
||||||
|
|
||||||
@@ -1,49 +1,29 @@
|
|||||||
import kernel.Init;
|
|
||||||
import lib.ui.Observable;
|
|
||||||
import lib.ui.TextElement;
|
|
||||||
import lib.ui.ReactiveUI;
|
|
||||||
import kernel.ui.WindowManager;
|
import kernel.ui.WindowManager;
|
||||||
import kernel.KernelEvents;
|
import kernel.ps.ProcessManager;
|
||||||
|
import kernel.binstore.BinStore;
|
||||||
using util.Extender.LambdaExtender;
|
import kernel.MainTerm;
|
||||||
|
import lib.HomeContext;
|
||||||
|
|
||||||
class Startup {
|
class Startup {
|
||||||
public static function main() {
|
public static function main() {
|
||||||
Init.initKernel();
|
#if debug
|
||||||
|
var term = BinStore.instantiate("terminal");
|
||||||
exampleUI();
|
var pid = ProcessManager.run(term, {
|
||||||
|
// args: ["debug"]
|
||||||
KernelEvents.instance.startEventLoop();
|
|
||||||
}
|
|
||||||
|
|
||||||
static function exampleProgramm() {
|
|
||||||
var context = WindowManager.instance.createNewContext();
|
|
||||||
|
|
||||||
context.clickSignal.handle(data -> {
|
|
||||||
context.setCursorPos(data.pos.x,data.pos.y);
|
|
||||||
context.write("x");
|
|
||||||
});
|
});
|
||||||
|
var ctx = WindowManager.getContextByPID(pid);
|
||||||
|
WindowManager.focusContextToOutput(ctx[0], "main");
|
||||||
|
#else
|
||||||
|
if (MainTerm.instance.isColor()) {
|
||||||
|
var main = new HomeContext();
|
||||||
|
|
||||||
context.write("Example programm");
|
main.run();
|
||||||
|
} else {
|
||||||
|
var term = BinStore.instantiate("terminal");
|
||||||
|
var pid = ProcessManager.run(term, {});
|
||||||
|
var ctx = WindowManager.getContextByPID(pid);
|
||||||
|
WindowManager.focusContextToOutput(ctx[0], "main");
|
||||||
}
|
}
|
||||||
|
#end
|
||||||
static function exampleUI() {
|
|
||||||
var context = WindowManager.instance.createNewContext();
|
|
||||||
|
|
||||||
var text = new Observable("Hello world");
|
|
||||||
|
|
||||||
var ui = new ReactiveUI(context,[
|
|
||||||
new TextElement(text),
|
|
||||||
new TextElement(text,Green,Red),
|
|
||||||
]);
|
|
||||||
|
|
||||||
ui.render();
|
|
||||||
|
|
||||||
context.clickSignal.handle(data -> {
|
|
||||||
text.set("Holla mundo");
|
|
||||||
});
|
|
||||||
|
|
||||||
WindowManager.instance.focusContextToOutput(context,"main");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
29
src/Unpack.hx
Normal file
29
src/Unpack.hx
Normal 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}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
103
src/bin/Disk.hx
Normal file
103
src/bin/Disk.hx
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package bin;
|
||||||
|
|
||||||
|
import kernel.peripherals.Drive;
|
||||||
|
import lib.CLIAppBase;
|
||||||
|
import kernel.peripherals.Peripherals.Peripheral;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
using Lambda;
|
||||||
|
|
||||||
|
@:build(macros.Binstore.includeBin("Disk", ["disk"]))
|
||||||
|
class Disk extends CLIAppBase {
|
||||||
|
public function new() {
|
||||||
|
registerSyncSubcommand("ls", (args) -> {
|
||||||
|
Peripheral.getAllDrives().foreach(drive -> {
|
||||||
|
var addr = drive.getAddr();
|
||||||
|
var label = drive.getDiskLabel();
|
||||||
|
var id = drive.getDiskID();
|
||||||
|
|
||||||
|
if (drive.isDiskPresent()) {
|
||||||
|
if (drive.hasAudio()) {
|
||||||
|
handle.writeLine('${addr} => ${label} [AUDIO]');
|
||||||
|
} else {
|
||||||
|
handle.writeLine('${addr} => ${label} (${id})');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handle.writeLine('${addr} => [NO DISK]');
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
registerSyncSubcommand("play", (args) -> {
|
||||||
|
return audioDiskPlayPause(args.getString("addr"), true);
|
||||||
|
}, [Peripheral("addr", Drive.TYPE_NAME)]);
|
||||||
|
|
||||||
|
registerSyncSubcommand("stop", (args) -> {
|
||||||
|
return audioDiskPlayPause(args.getString("addr"), false);
|
||||||
|
}, [Peripheral("addr", Drive.TYPE_NAME)]);
|
||||||
|
|
||||||
|
registerSyncSubcommand("eject", (args) -> {
|
||||||
|
var driveAddr = args.getString("addr");
|
||||||
|
var drive = Peripheral.getDrive(driveAddr);
|
||||||
|
|
||||||
|
if (!drive.isDiskPresent()) {
|
||||||
|
handle.writeLine("No disk in drive: " + driveAddr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
drive.ejectDisk();
|
||||||
|
return true;
|
||||||
|
}, [Peripheral("addr", Drive.TYPE_NAME)]);
|
||||||
|
|
||||||
|
registerSyncSubcommand("label", (args) -> {
|
||||||
|
var driveAddr = args.getString("addr");
|
||||||
|
var drive = Peripheral.getDrive(driveAddr);
|
||||||
|
var label:Null<String> = args.getString("label");
|
||||||
|
|
||||||
|
if (!drive.isDiskPresent()) {
|
||||||
|
handle.writeLine("No disk in drive: " + driveAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (label == null || label == "") {
|
||||||
|
handle.writeLine(drive.getDiskLabel());
|
||||||
|
} else {
|
||||||
|
var err = drive.setDiskLabel(label);
|
||||||
|
if (err != null) {
|
||||||
|
handle.writeLine("Failed to set lable");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}, [Peripheral("addr", Drive.TYPE_NAME), Optional(String("label"))]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function audioDiskPlayPause(driveAddr:String, play:Bool):Bool {
|
||||||
|
var drive = Peripheral.getDrive(driveAddr);
|
||||||
|
|
||||||
|
if (drive == null) {
|
||||||
|
handle.writeLine("Drive not found: " + driveAddr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!drive.isDiskPresent()) {
|
||||||
|
handle.writeLine("No disk in drive: " + driveAddr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!drive.hasAudio()) {
|
||||||
|
handle.writeLine("Disk in drive: " + driveAddr + " does not have audio");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (play) {
|
||||||
|
drive.playAudio();
|
||||||
|
} else {
|
||||||
|
drive.stopAudio();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
72
src/bin/GPS.hx
Normal file
72
src/bin/GPS.hx
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package bin;
|
||||||
|
|
||||||
|
import lib.CLIAppBase;
|
||||||
|
import kernel.gps.INS;
|
||||||
|
import lib.WorldPos;
|
||||||
|
import lib.Vec.Vec3;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
@:build(macros.Binstore.includeBin("GPS", ["gps"]))
|
||||||
|
class GPS extends CLIAppBase {
|
||||||
|
public function new() {
|
||||||
|
registerSyncSubcommand("set", (args) -> {
|
||||||
|
var x:Float = args.getFloat("x");
|
||||||
|
var y:Float = args.getFloat("y");
|
||||||
|
var z:Float = args.getFloat("z");
|
||||||
|
|
||||||
|
var pos:WorldPos = new Vec3<Float>(x, y, z);
|
||||||
|
|
||||||
|
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) {
|
||||||
|
handle.writeLine('Position x:${pos.x} y:${pos.y} z:${pos.z}');
|
||||||
|
} else {
|
||||||
|
handle.writeLine("Position not available");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var acc = kernel.gps.GPS.getAccuracy();
|
||||||
|
if (acc == 1) {
|
||||||
|
handle.writeLine("Accuracy: Low");
|
||||||
|
} else if (acc == 2) {
|
||||||
|
handle.writeLine("Accuracy: Medium");
|
||||||
|
} else if (acc == 3) {
|
||||||
|
handle.writeLine("Accuracy: High");
|
||||||
|
}
|
||||||
|
|
||||||
|
var ins = INS.getHeading();
|
||||||
|
if (ins != null) {
|
||||||
|
handle.writeLine('INS heading: ${ins.x} y:${ins.y} z:${ins.z}');
|
||||||
|
} else {
|
||||||
|
handle.writeLine("INS heading not available");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
registerAsyncSubcommand("locate", (args) -> {
|
||||||
|
return kernel.gps.GPS.locate().map((result) -> {
|
||||||
|
switch result {
|
||||||
|
case Success(pos):
|
||||||
|
handle.writeLine('Position x:${pos.x} y:${pos.y} z:${pos.z}');
|
||||||
|
case Failure(err):
|
||||||
|
handle.writeLine("Position not available: " + err);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
registerAsyncSubcommand("ins", (args) -> {
|
||||||
|
return INS.align().map((_) -> {
|
||||||
|
handle.writeLine("INS aligned");
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/bin/ID.hx
Normal file
14
src/bin/ID.hx
Normal 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
63
src/bin/KSettings.hx
Normal 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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
65
src/bin/KernelLog.hx
Normal file
65
src/bin/KernelLog.hx
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package bin;
|
||||||
|
|
||||||
|
import kernel.ps.ProcessHandle;
|
||||||
|
import kernel.ps.IProcess;
|
||||||
|
import lib.Color;
|
||||||
|
import lib.MathI;
|
||||||
|
import kernel.log.Log;
|
||||||
|
import kernel.ui.WindowContext;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
@:build(macros.Binstore.includeBin("Log", ["log"]))
|
||||||
|
class KernelLog implements IProcess {
|
||||||
|
private var handle:ProcessHandle;
|
||||||
|
private var ctx:WindowContext;
|
||||||
|
|
||||||
|
public function new() {}
|
||||||
|
|
||||||
|
public function run(handle:ProcessHandle):Void {
|
||||||
|
this.handle = handle;
|
||||||
|
|
||||||
|
var statelessCtx = handle.createStatelessWindowContext();
|
||||||
|
this.ctx = statelessCtx.ctx;
|
||||||
|
|
||||||
|
statelessCtx.setRenderFunc(this.render);
|
||||||
|
|
||||||
|
Log.onLog.handle(() -> {
|
||||||
|
statelessCtx.requestRender();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private function render() {
|
||||||
|
ctx.clear();
|
||||||
|
ctx.setCursorPos(0, 0);
|
||||||
|
|
||||||
|
var lines = Log.getLines();
|
||||||
|
var height = ctx.getSize().y;
|
||||||
|
var start = MathI.max(lines.length - height, 0);
|
||||||
|
|
||||||
|
for (i in start...lines.length) {
|
||||||
|
var line = lines[i];
|
||||||
|
|
||||||
|
switch (line.level) {
|
||||||
|
case Info:
|
||||||
|
ctx.setTextColor(Color.White);
|
||||||
|
ctx.write("[INFO] ");
|
||||||
|
case Warn:
|
||||||
|
ctx.setTextColor(Color.Yellow);
|
||||||
|
ctx.write("[WARN] ");
|
||||||
|
case Error:
|
||||||
|
ctx.setTextColor(Color.Red);
|
||||||
|
ctx.write("[ERRO] ");
|
||||||
|
case Debug:
|
||||||
|
ctx.setTextColor(Color.Gray);
|
||||||
|
ctx.write("[DEBG] ");
|
||||||
|
case Silly:
|
||||||
|
ctx.setTextColor(Color.Gray);
|
||||||
|
ctx.write("[SILL] ");
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.write(line.message);
|
||||||
|
ctx.setCursorPos(0, ctx.getCursorPos().y + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/bin/LSPS.hx
Normal file
22
src/bin/LSPS.hx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package bin;
|
||||||
|
|
||||||
|
import kernel.ps.ProcessManager;
|
||||||
|
import kernel.ps.ProcessHandle;
|
||||||
|
import kernel.ps.IProcess;
|
||||||
|
|
||||||
|
@:build(macros.Binstore.includeBin("LSPS", ["lsps"]))
|
||||||
|
class LSPS implements IProcess {
|
||||||
|
public function new() {}
|
||||||
|
|
||||||
|
public function run(handle:ProcessHandle) {
|
||||||
|
var pids = ProcessManager.listProcesses();
|
||||||
|
|
||||||
|
handle.writeLine('Count: ${pids.length}');
|
||||||
|
|
||||||
|
for (pid in pids) {
|
||||||
|
handle.writeLine('${pid}');
|
||||||
|
}
|
||||||
|
|
||||||
|
handle.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
57
src/bin/Net.hx
Normal file
57
src/bin/Net.hx
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package bin;
|
||||||
|
|
||||||
|
import lib.CLIAppBase;
|
||||||
|
import kernel.peripherals.Peripherals.Peripheral;
|
||||||
|
import kernel.net.Routing;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
@:build(macros.Binstore.includeBin("Net", ["net"]))
|
||||||
|
class Net extends CLIAppBase {
|
||||||
|
public function new() {
|
||||||
|
registerSyncSubcommand("route", (args) -> {
|
||||||
|
var routes = Routing.getRouteTable();
|
||||||
|
|
||||||
|
for (k => v in routes) {
|
||||||
|
handle.writeLine('${k} => ${v.interf.name()}(${v.cost})');
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
registerSyncSubcommand("iface", (args) -> {
|
||||||
|
var modems = Peripheral.getAllModems();
|
||||||
|
|
||||||
|
for (modem in modems) {
|
||||||
|
handle.writeLine(modem.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
registerSyncSubcommand("proto", (args) -> {
|
||||||
|
var protos = kernel.net.Net.getActiveProtocols();
|
||||||
|
|
||||||
|
for (proto in protos) {
|
||||||
|
handle.writeLine(proto);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
registerAsyncSubcommand("ping", (args) -> {
|
||||||
|
var toID:Int = args.getInt("id");
|
||||||
|
|
||||||
|
return kernel.net.Net.ping(toID).map(result -> {
|
||||||
|
switch (result) {
|
||||||
|
case Success(_):
|
||||||
|
handle.write("Ping succeeded");
|
||||||
|
case Failure(failure):
|
||||||
|
handle.write("Ping failed: " + failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}, [Int("id")]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package bin;
|
|
||||||
|
|
||||||
import lib.TermIO;
|
|
||||||
import kernel.net.Net;
|
|
||||||
import kernel.ui.WindowManager;
|
|
||||||
import kernel.ui.WindowContext;
|
|
||||||
import lib.IUserProgramm;
|
|
||||||
|
|
||||||
class NetTest implements IUserProgramm {
|
|
||||||
private final windowContext:WindowContext = WindowManager.instance.createNewContext();
|
|
||||||
private final writer:TermIO;
|
|
||||||
|
|
||||||
public function new() {
|
|
||||||
writer = new TermIO(windowContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function drawUI() {
|
|
||||||
var allNeighbors = Net.instance.getAllNeighbors();
|
|
||||||
|
|
||||||
for (neighbor in allNeighbors) {
|
|
||||||
writer.writeLn(""+neighbor,Green);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function getName():String {
|
|
||||||
return "Network tester";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
33
src/bin/Peri.hx
Normal file
33
src/bin/Peri.hx
Normal 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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/bin/Redstone.hx
Normal file
27
src/bin/Redstone.hx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package bin;
|
||||||
|
|
||||||
|
import lib.CLIAppBase;
|
||||||
|
import kernel.peripherals.Peripherals.Peripheral;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
@:build(macros.Binstore.includeBin("Redstone", ["redstone", "rs"]))
|
||||||
|
class Redstone extends CLIAppBase {
|
||||||
|
public function new() {
|
||||||
|
registerSyncSubcommand("on", (args) -> {
|
||||||
|
Peripheral.getRedstone(args.getString("side")).setOutput(true);
|
||||||
|
return true;
|
||||||
|
}, [Side("side")]);
|
||||||
|
|
||||||
|
registerSyncSubcommand("off", (args) -> {
|
||||||
|
Peripheral.getRedstone(args.getString("side")).setOutput(false);
|
||||||
|
return true;
|
||||||
|
}, [Side("side")]);
|
||||||
|
|
||||||
|
registerSyncSubcommand("get", (args) -> {
|
||||||
|
var value = Peripheral.getRedstone(args.getString("side")).getAnalogInput();
|
||||||
|
handle.write("Analog input: " + value);
|
||||||
|
return true;
|
||||||
|
}, [Side("side")]);
|
||||||
|
}
|
||||||
|
}
|
||||||
60
src/bin/Service.hx
Normal file
60
src/bin/Service.hx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package bin;
|
||||||
|
|
||||||
|
import kernel.service.ServiceManager;
|
||||||
|
import lib.CLIAppBase;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
@:build(macros.Binstore.includeBin("Service", ["srv"]))
|
||||||
|
class Service extends CLIAppBase {
|
||||||
|
public function new() {
|
||||||
|
registerSyncSubcommand("start", (args) -> {
|
||||||
|
var result = ServiceManager.start(args.getString("name"));
|
||||||
|
return handleResult(result);
|
||||||
|
}, [String("name")]);
|
||||||
|
|
||||||
|
registerSyncSubcommand("stop", (args) -> {
|
||||||
|
var result = ServiceManager.stop(args.getString("name"));
|
||||||
|
return handleResult(result);
|
||||||
|
}, [String("name")]);
|
||||||
|
|
||||||
|
registerSyncSubcommand("register", (args) -> {
|
||||||
|
var name = args.getString("name");
|
||||||
|
var binName = args.getString("binary");
|
||||||
|
var rest = args.getRest();
|
||||||
|
|
||||||
|
var result = ServiceManager.register(name, binName, rest);
|
||||||
|
return handleResult(result);
|
||||||
|
}, [String("name"), String("binary"), Rest("args")]);
|
||||||
|
|
||||||
|
registerSyncSubcommand("unregister", (args) -> {
|
||||||
|
var result = ServiceManager.unregister(args.getString("name"));
|
||||||
|
return handleResult(result);
|
||||||
|
}, [String("name")]);
|
||||||
|
|
||||||
|
registerSyncSubcommand("list", (args) -> {
|
||||||
|
var list = ServiceManager.listRunning();
|
||||||
|
|
||||||
|
for (name in list) {
|
||||||
|
this.handle.writeLine(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
registerSyncSubcommand("enable", (args) -> {
|
||||||
|
ServiceManager.enable(args.getString("name"));
|
||||||
|
return true;
|
||||||
|
}, [String("name")]);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
71
src/bin/Speaker.hx
Normal 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"))
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
226
src/bin/Terminal.hx
Normal file
226
src/bin/Terminal.hx
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
package bin;
|
||||||
|
|
||||||
|
import lib.MathI;
|
||||||
|
import kernel.EndOfLoop;
|
||||||
|
import lua.NativeStringTools;
|
||||||
|
import kernel.binstore.BinStore;
|
||||||
|
import kernel.ps.ProcessHandle;
|
||||||
|
import kernel.ps.IProcess;
|
||||||
|
import kernel.ps.ProcessManager;
|
||||||
|
import lib.Color;
|
||||||
|
import kernel.ui.WindowContext;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
@:build(macros.Binstore.includeBin("Terminal", ["terminal"]))
|
||||||
|
class Terminal implements IProcess {
|
||||||
|
private static inline final MAX_BACKLOG:Int = 100;
|
||||||
|
|
||||||
|
private var handle:ProcessHandle;
|
||||||
|
|
||||||
|
private var ctx:WindowContext;
|
||||||
|
private var requestRender:() -> Void;
|
||||||
|
|
||||||
|
private var input:String = "";
|
||||||
|
private var backlog:Array<String> = [];
|
||||||
|
private var history:Array<String> = [];
|
||||||
|
private var historyIndex:Int = 0;
|
||||||
|
|
||||||
|
private var scrollBack = 0;
|
||||||
|
|
||||||
|
private var runningPID:PID = -1;
|
||||||
|
|
||||||
|
public function new() {}
|
||||||
|
|
||||||
|
public function run(handle:ProcessHandle):Void {
|
||||||
|
this.handle = handle;
|
||||||
|
|
||||||
|
var statelessContext = handle.createStatelessWindowContext();
|
||||||
|
|
||||||
|
this.ctx = statelessContext.ctx;
|
||||||
|
this.requestRender = statelessContext.requestRender;
|
||||||
|
|
||||||
|
statelessContext.setRenderFunc(this.render);
|
||||||
|
|
||||||
|
// Add input event handlers
|
||||||
|
handle.addCallbackLink(this.ctx.onChar.handle(char -> {
|
||||||
|
if (this.runningPID > 0)
|
||||||
|
return;
|
||||||
|
this.input += char;
|
||||||
|
this.requestRender();
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Add key event handlers
|
||||||
|
handle.addCallbackLink(this.ctx.onKey.handle(e -> {
|
||||||
|
switch (e.keyCode) {
|
||||||
|
case 259: // Backspace
|
||||||
|
if (this.runningPID > 0)
|
||||||
|
return;
|
||||||
|
this.input = this.input.substr(0, this.input.length - 1);
|
||||||
|
this.requestRender();
|
||||||
|
case 257: // Enter
|
||||||
|
if (this.runningPID > 0)
|
||||||
|
return;
|
||||||
|
this.backlog.push("> " + this.input);
|
||||||
|
this.backlog.push("");
|
||||||
|
|
||||||
|
var command = this.input;
|
||||||
|
this.input = "";
|
||||||
|
this.scrollBack = 0;
|
||||||
|
this.requestRender();
|
||||||
|
this.historyIndex = 0;
|
||||||
|
this.invokeCommand(command);
|
||||||
|
case 269: // END
|
||||||
|
this.stopCurrentPS();
|
||||||
|
case 265: // UP
|
||||||
|
if (this.historyIndex < this.history.length) {
|
||||||
|
this.historyIndex++;
|
||||||
|
this.input = this.history[this.history.length - this.historyIndex];
|
||||||
|
this.requestRender();
|
||||||
|
}
|
||||||
|
case 266: // PAGE UP
|
||||||
|
this.scrollBack = MathI.min(scrollBack + 1, this.backlog.length - 1);
|
||||||
|
this.requestRender();
|
||||||
|
case 267: // PAGE DOWN
|
||||||
|
this.scrollBack = MathI.max(scrollBack - 1, 0);
|
||||||
|
this.requestRender();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.requestRender();
|
||||||
|
|
||||||
|
if (handle.args.length > 0) {
|
||||||
|
var arg1 = handle.args[0];
|
||||||
|
|
||||||
|
this.backlog.push("> " + arg1);
|
||||||
|
this.backlog.push("");
|
||||||
|
|
||||||
|
EndOfLoop.endOfLoop(() -> {
|
||||||
|
this.invokeCommand(arg1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function stopCurrentPS() {
|
||||||
|
if (this.runningPID < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessManager.kill(this.runningPID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function render() {
|
||||||
|
var size = this.ctx.getSize();
|
||||||
|
var linesAvailable = size.y - 1;
|
||||||
|
|
||||||
|
var withoutScrollBack = (this.backlog.length - linesAvailable);
|
||||||
|
var start:Int = withoutScrollBack - scrollBack;
|
||||||
|
|
||||||
|
for (i in 0...linesAvailable) {
|
||||||
|
var line = this.backlog[start + i];
|
||||||
|
|
||||||
|
this.ctx.setCursorPos(0, i);
|
||||||
|
this.ctx.clearLine();
|
||||||
|
|
||||||
|
if (line != null) {
|
||||||
|
this.ctx.write(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ctx.setCursorPos(0, size.y - 1);
|
||||||
|
this.ctx.clearLine();
|
||||||
|
|
||||||
|
this.ctx.setTextColor(Color.Blue);
|
||||||
|
this.ctx.write("> ");
|
||||||
|
|
||||||
|
this.ctx.setTextColor(Color.White);
|
||||||
|
this.ctx.write(this.input);
|
||||||
|
|
||||||
|
if (this.runningPID < 0) {
|
||||||
|
this.ctx.setCursorBlink(true);
|
||||||
|
} else {
|
||||||
|
this.ctx.setCursorBlink(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function invokeCommand(command:String):Void {
|
||||||
|
var args = this.parseArgs(command);
|
||||||
|
|
||||||
|
if (args.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.history.push(command);
|
||||||
|
|
||||||
|
if (this.history.length > MAX_BACKLOG) {
|
||||||
|
this.history.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
var commandName = args[0];
|
||||||
|
|
||||||
|
// Handle built-in commands
|
||||||
|
switch (commandName) {
|
||||||
|
case "clear":
|
||||||
|
this.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var commandArgs:Array<String> = args.slice(1);
|
||||||
|
|
||||||
|
var ps = BinStore.instantiate(commandName);
|
||||||
|
if (ps == null) {
|
||||||
|
this.backlog.push("Unknown command: " + commandName);
|
||||||
|
this.requestRender();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.runningPID = ProcessManager.run(ps, {
|
||||||
|
args: commandArgs,
|
||||||
|
onWrite: (s:String) -> {
|
||||||
|
if (s == "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var splits = s.split("\n");
|
||||||
|
|
||||||
|
for (i => split in splits) {
|
||||||
|
if (i == 0) {
|
||||||
|
this.backlog[this.backlog.length - 1] += split;
|
||||||
|
} else {
|
||||||
|
this.backlog.push(split);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.requestRender();
|
||||||
|
},
|
||||||
|
onExit: (success:Bool) -> {
|
||||||
|
this.runningPID = -1;
|
||||||
|
if (this.backlog[this.backlog.length - 1] == "") {
|
||||||
|
this.backlog.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.requestRender();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.ctx.setCursorBlink(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Convter a command string into an array of arguments where the first element is the command name
|
||||||
|
**/
|
||||||
|
private function parseArgs(command:String):Array<String> {
|
||||||
|
// TODO: tim and quote handling
|
||||||
|
return command.split(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
private function clear() {
|
||||||
|
this.backlog = [];
|
||||||
|
this.requestRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function moveCursorToInput() {
|
||||||
|
var size = this.ctx.getSize();
|
||||||
|
this.ctx.setCursorPos(this.input.length + 2, size.y - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
180
src/bin/TurtleCtl.hx
Normal file
180
src/bin/TurtleCtl.hx
Normal 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
41
src/bin/debug/Debug.hx
Normal 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
10
src/bin/debug/DebugRPC.hx
Normal 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 {}
|
||||||
27
src/bin/debug/DebugService.hx
Normal file
27
src/bin/debug/DebugService.hx
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
67
src/bin/exporter/Exporter.hx
Normal file
67
src/bin/exporter/Exporter.hx
Normal 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
71
src/bin/ns/NameSystem.hx
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
71
src/bin/ns/NameSystemCLI.hx
Normal file
71
src/bin/ns/NameSystemCLI.hx
Normal 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")]);
|
||||||
|
}
|
||||||
|
}
|
||||||
31
src/bin/ns/NameSystemRPC.hx
Normal file
31
src/bin/ns/NameSystemRPC.hx
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/bin/pathfinder/PFClient.hx
Normal file
59
src/bin/pathfinder/PFClient.hx
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
158
src/bin/turtle/CircleMine.hx
Normal file
158
src/bin/turtle/CircleMine.hx
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
56
src/bin/turtle/CircleMinePlan.hx
Normal file
56
src/bin/turtle/CircleMinePlan.hx
Normal 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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
67
src/bin/turtle/Excavate.hx
Normal file
67
src/bin/turtle/Excavate.hx
Normal 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
31
src/bin/turtle/Patrol.hx
Normal 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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/kernel/ButtonType.hx
Normal file
7
src/kernel/ButtonType.hx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package kernel;
|
||||||
|
|
||||||
|
enum ButtonType {
|
||||||
|
Left;
|
||||||
|
Middle;
|
||||||
|
Right;
|
||||||
|
}
|
||||||
28
src/kernel/EndOfLoop.hx
Normal file
28
src/kernel/EndOfLoop.hx
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/kernel/Entrypoint.hx
Normal file
22
src/kernel/Entrypoint.hx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package kernel;
|
||||||
|
|
||||||
|
import kernel.log.Log;
|
||||||
|
|
||||||
|
class Entrypoint {
|
||||||
|
public static function main() {
|
||||||
|
try {
|
||||||
|
Init.initKernel();
|
||||||
|
} catch (e) {
|
||||||
|
Log.error('Error in init: ${e.toString()}');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Startup.main();
|
||||||
|
} catch (e) {
|
||||||
|
Log.error('Error in startup: ${e.toString()}');
|
||||||
|
}
|
||||||
|
|
||||||
|
KernelEvents.startEventLoop();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,22 +1,44 @@
|
|||||||
package kernel;
|
package kernel;
|
||||||
|
|
||||||
import util.Debug;
|
#if debug
|
||||||
|
import lib.Debug;
|
||||||
|
#end
|
||||||
|
import kernel.service.ServiceManager;
|
||||||
|
import kernel.fs.FS;
|
||||||
|
import kernel.gps.GPS;
|
||||||
|
import kernel.log.Log;
|
||||||
|
import kernel.net.Routing;
|
||||||
import kernel.ui.WindowManager;
|
import kernel.ui.WindowManager;
|
||||||
import kernel.peripherals.Peripherals.Peripheral;
|
|
||||||
import kernel.net.Net;
|
import kernel.net.Net;
|
||||||
|
|
||||||
class Init {
|
class Init {
|
||||||
|
@:allow(kernel.KernelEvents)
|
||||||
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.
|
||||||
KernelEvents.instance = new KernelEvents();
|
|
||||||
Peripheral.instance = new Peripheral();
|
|
||||||
|
|
||||||
WindowManager.instance = new WindowManager();
|
|
||||||
MainTerm.instance = new MainTerm();
|
|
||||||
Net.instance = new Net();
|
|
||||||
|
|
||||||
Log.init();
|
Log.init();
|
||||||
|
KernelEvents.init();
|
||||||
|
|
||||||
|
WindowManager.init();
|
||||||
|
MainTerm.instance = new MainTerm();
|
||||||
|
|
||||||
|
Routing.init();
|
||||||
|
Net.init();
|
||||||
|
|
||||||
|
GPS.init();
|
||||||
|
|
||||||
|
// Register default terminate handler
|
||||||
|
KernelEvents.onTerminate.handle(_ -> {
|
||||||
|
KernelEvents.shutdown();
|
||||||
|
});
|
||||||
|
|
||||||
|
#if debug
|
||||||
Debug.printBuildInfo();
|
Debug.printBuildInfo();
|
||||||
|
#end
|
||||||
|
|
||||||
|
if (!FS.exists("/var/ns")) {
|
||||||
|
FS.makeDir("/var/ns");
|
||||||
|
}
|
||||||
|
|
||||||
|
ServiceManager.init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,48 +1,366 @@
|
|||||||
package kernel;
|
package kernel;
|
||||||
|
|
||||||
import cc.OS;
|
import kernel.turtle.TurtleMutex;
|
||||||
import util.EventBus;
|
import kernel.turtle.Turtle;
|
||||||
|
import kernel.peripherals.Peripherals.Peripheral;
|
||||||
|
import kernel.log.Log;
|
||||||
|
import lib.ScreenPos;
|
||||||
|
import cc.HTTP.HTTPResponse;
|
||||||
|
import lua.TableTools;
|
||||||
|
import lua.Coroutine;
|
||||||
|
import haxe.Exception;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
using lua.Table;
|
using lua.Table;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Class for interacting with the native pullEvent system.
|
Class for interacting with the native pullEvent system.
|
||||||
**/
|
**/
|
||||||
class KernelEvents{
|
class KernelEvents {
|
||||||
public static var instance:KernelEvents;
|
/**
|
||||||
|
Depends on: (Nothing)
|
||||||
|
**/
|
||||||
|
public static var onAlarm(default, null):Signal<Int>;
|
||||||
|
|
||||||
|
public static var onChar(default, null):Signal<String>;
|
||||||
|
public static var onDisk(default, null):Signal<String>;
|
||||||
|
public static var onDiskEject(default, null):Signal<String>;
|
||||||
|
public static var onHttpCheck(default, null):Signal<{url:String, success:Bool, failReason:Any}>;
|
||||||
|
public static var onHttpFailure(default, null):Signal<{url:String, failReason:String, handle:HTTPResponse}>;
|
||||||
|
public static var onHttpSuccess(default, null):Signal<{url:String, handle:HTTPResponse}>;
|
||||||
|
public static var onKey(default, null):Signal<{keyCode:Int, isHeld:Bool}>;
|
||||||
|
public static var onKeyUp(default, null):Signal<Int>;
|
||||||
|
public static var onModemMessage(default, null):Signal<{
|
||||||
|
addr:String,
|
||||||
|
channel:Int,
|
||||||
|
replyChannel:Int,
|
||||||
|
message:Dynamic,
|
||||||
|
distance:Null<Float>
|
||||||
|
}>;
|
||||||
|
public static var onMonitorResize(default, null):Signal<String>;
|
||||||
|
public static var onMonitorTouch(default, null):Signal<{addr:String, pos:ScreenPos}>;
|
||||||
|
public static var onMouseClick(default, null):Signal<{button:ButtonType, pos:ScreenPos}>;
|
||||||
|
public static var onMouseDrag(default, null):Signal<{button:ButtonType, pos:ScreenPos}>;
|
||||||
|
public static var onMouseScroll(default, null):Signal<{dir:Int, pos:ScreenPos}>;
|
||||||
|
public static var onMouseUp(default, null):Signal<{button:ButtonType, pos:ScreenPos}>;
|
||||||
|
public static var onPaste(default, null):Signal<String>;
|
||||||
|
public static var onPeripheral(default, null):Signal<String>;
|
||||||
|
public static var onPeripheralDetach(default, null):Signal<String>;
|
||||||
|
public static var onRedstone(default, null):Signal<Noise>;
|
||||||
|
public static var onSpeakerAudioEmpty(default, null):Signal<String>;
|
||||||
|
public static var onTaskComplete(default, null):Signal<{id:Int, success:Bool, failedReason:String}>;
|
||||||
|
public static var onTermResize(default, null):Signal<Noise>;
|
||||||
|
public static var onTerminate(default, null):Signal<Noise>;
|
||||||
|
public static var onTimer(default, null):Signal<Int>;
|
||||||
|
public static var onTurtleInventory(default, null):Signal<Noise>;
|
||||||
|
public static var onWebsocketClose(default, null):Signal<String>;
|
||||||
|
public static var onWebsocketFailure(default, null):Signal<{url:String, failReason:String}>;
|
||||||
|
public static var onWebsocketMessage(default, null):Signal<{url:String, message:String, isBinary:Bool}>;
|
||||||
|
public static var onWebsocketSuccess(default, null):Signal<{url:String, handle:Any}>;
|
||||||
|
|
||||||
|
private static final onAlarmTrigger:SignalTrigger<Int> = Signal.trigger();
|
||||||
|
private static final onCharTrigger:SignalTrigger<String> = Signal.trigger();
|
||||||
|
private static final onDiskTrigger:SignalTrigger<String> = Signal.trigger();
|
||||||
|
private static final onDiskEjectTrigger:SignalTrigger<String> = Signal.trigger();
|
||||||
|
private static final onHttpCheckTrigger:SignalTrigger<{url:String, success:Bool, failReason:Any}> = Signal.trigger();
|
||||||
|
private static final onHttpFailureTrigger:SignalTrigger<{url:String, failReason:String, handle:HTTPResponse}> = Signal.trigger();
|
||||||
|
private static final onHttpSuccessTrigger:SignalTrigger<{url:String, handle:HTTPResponse}> = Signal.trigger();
|
||||||
|
private static final onKeyTrigger:SignalTrigger<{keyCode:Int, isHeld:Bool}> = Signal.trigger();
|
||||||
|
private static final onKeyUpTrigger:SignalTrigger<Int> = Signal.trigger();
|
||||||
|
private static final onModemMessageTrigger:SignalTrigger<{
|
||||||
|
addr:String,
|
||||||
|
channel:Int,
|
||||||
|
replyChannel:Int,
|
||||||
|
message:Dynamic,
|
||||||
|
distance:Null<Float>
|
||||||
|
}> = Signal.trigger();
|
||||||
|
private static final onMonitorResizeTrigger:SignalTrigger<String> = Signal.trigger();
|
||||||
|
private static final onMonitorTouchTrigger:SignalTrigger<{addr:String, pos:ScreenPos}> = Signal.trigger();
|
||||||
|
private static final onMouseClickTrigger:SignalTrigger<{button:ButtonType, pos:ScreenPos}> = Signal.trigger();
|
||||||
|
private static final onMouseDragTrigger:SignalTrigger<{button:ButtonType, pos:ScreenPos}> = Signal.trigger();
|
||||||
|
private static final onMouseScrollTrigger:SignalTrigger<{dir:Int, pos:ScreenPos}> = Signal.trigger();
|
||||||
|
private static final onMouseUpTrigger:SignalTrigger<{button:ButtonType, pos:ScreenPos}> = Signal.trigger();
|
||||||
|
private static final onPasteTrigger:SignalTrigger<String> = Signal.trigger();
|
||||||
|
private static final onPeripheralTrigger:SignalTrigger<String> = Signal.trigger();
|
||||||
|
private static final onPeripheralDetachTrigger:SignalTrigger<String> = Signal.trigger();
|
||||||
|
private static final onRednetMessageTrigger:SignalTrigger<{sender:Int, message:Any, protocol:Any}> = Signal.trigger();
|
||||||
|
private static final onRedstoneTrigger:SignalTrigger<Noise> = Signal.trigger();
|
||||||
|
private static final onSpeakerAudioEmptyTrigger:SignalTrigger<String> = Signal.trigger();
|
||||||
|
private static final onTaskCompleteTrigger:SignalTrigger<{id:Int, success:Bool, failedReason:String}> = Signal.trigger();
|
||||||
|
private static final onTermResizeTrigger:SignalTrigger<Noise> = Signal.trigger();
|
||||||
|
private static final onTerminateTrigger:SignalTrigger<Noise> = Signal.trigger();
|
||||||
|
private static final onTimerTrigger:SignalTrigger<Int> = Signal.trigger();
|
||||||
|
private static final onTurtleInventoryTrigger:SignalTrigger<Noise> = Signal.trigger();
|
||||||
|
private static final onWebsocketCloseTrigger:SignalTrigger<String> = Signal.trigger();
|
||||||
|
private static final onWebsocketFailureTrigger:SignalTrigger<{url:String, failReason:String}> = Signal.trigger();
|
||||||
|
private static final onWebsocketMessageTrigger:SignalTrigger<{url:String, message:String, isBinary:Bool}> = Signal.trigger();
|
||||||
|
private static final onWebsocketSuccessTrigger:SignalTrigger<{url:String, handle:Any}> = Signal.trigger();
|
||||||
|
|
||||||
|
private static var stopLoop:Bool = false;
|
||||||
|
private static var turtleCoroutine:Coroutine<Dynamic>;
|
||||||
|
|
||||||
@:allow(kernel.Init)
|
@:allow(kernel.Init)
|
||||||
private function new () {}
|
private static function init() {
|
||||||
|
onAlarm = onAlarmTrigger.asSignal();
|
||||||
private var eventBus: util.EventBus<Array<Dynamic>> = new EventBus();
|
onChar = onCharTrigger.asSignal();
|
||||||
|
onDisk = onDiskTrigger.asSignal();
|
||||||
|
onDiskEject = onDiskEjectTrigger.asSignal();
|
||||||
|
onHttpCheck = onHttpCheckTrigger.asSignal();
|
||||||
|
onHttpFailure = onHttpFailureTrigger.asSignal();
|
||||||
|
onHttpSuccess = onHttpSuccessTrigger.asSignal();
|
||||||
|
onKey = onKeyTrigger.asSignal();
|
||||||
|
onKeyUp = onKeyUpTrigger.asSignal();
|
||||||
|
onModemMessage = onModemMessageTrigger.asSignal();
|
||||||
|
onMonitorResize = onMonitorResizeTrigger.asSignal();
|
||||||
|
onMonitorTouch = onMonitorTouchTrigger.asSignal();
|
||||||
|
onMouseClick = onMouseClickTrigger.asSignal();
|
||||||
|
onMouseDrag = onMouseDragTrigger.asSignal();
|
||||||
|
onMouseScroll = onMouseScrollTrigger.asSignal();
|
||||||
|
onMouseUp = onMouseUpTrigger.asSignal();
|
||||||
|
onPaste = onPasteTrigger.asSignal();
|
||||||
|
onPeripheral = onPeripheralTrigger.asSignal();
|
||||||
|
onPeripheralDetach = onPeripheralDetachTrigger.asSignal();
|
||||||
|
onRedstone = onRedstoneTrigger.asSignal();
|
||||||
|
onSpeakerAudioEmpty = onSpeakerAudioEmptyTrigger.asSignal();
|
||||||
|
onTaskComplete = onTaskCompleteTrigger.asSignal();
|
||||||
|
onTermResize = onTermResizeTrigger.asSignal();
|
||||||
|
onTerminate = onTerminateTrigger.asSignal();
|
||||||
|
onTimer = onTimerTrigger.asSignal();
|
||||||
|
onTurtleInventory = onTurtleInventoryTrigger.asSignal();
|
||||||
|
onWebsocketClose = onWebsocketCloseTrigger.asSignal();
|
||||||
|
onWebsocketFailure = onWebsocketFailureTrigger.asSignal();
|
||||||
|
onWebsocketMessage = onWebsocketMessageTrigger.asSignal();
|
||||||
|
onWebsocketSuccess = onWebsocketSuccessTrigger.asSignal();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Start pulling events. Blocking.
|
Start pulling events. Blocking.
|
||||||
**/
|
**/
|
||||||
public function startEventLoop() {
|
@:allow(kernel.Entrypoint)
|
||||||
// Log.info("Starting event loop");
|
private static function startEventLoop() {
|
||||||
while (true){
|
if (Turtle.isTurtle()) {
|
||||||
var event:Table<Int, Dynamic> = OS.pullEventRaw();
|
turtleLoop();
|
||||||
|
} else {
|
||||||
|
runMainLoop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@:allow(kernel.turtle.TurtleMutex)
|
||||||
|
private static function startTurtleCoroutine() {
|
||||||
|
turtleCoroutine = Coroutine.create(runTurtleLoop);
|
||||||
|
Coroutine.resume(turtleCoroutine);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function turtleLoop() {
|
||||||
|
startTurtleCoroutine();
|
||||||
|
|
||||||
|
while (!stopLoop) {
|
||||||
|
var eventData = pullEvents();
|
||||||
|
|
||||||
|
if (eventData[1] == "turtle_response" || eventData[1] == "tthread") {
|
||||||
|
var result = Coroutine.resume(turtleCoroutine, TableTools.unpack(eventData));
|
||||||
|
|
||||||
|
if (!result.success) {
|
||||||
|
Log.error('Error while running turtle thread: ${result.result}');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Coroutine.status(turtleCoroutine) == Dead) {
|
||||||
|
Log.error('Turtle thread died');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fireSignal(eventData[1], eventData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function runTurtleLoop() {
|
||||||
|
while (!stopLoop) {
|
||||||
|
if ((TableTools.pack(Coroutine.yield()))[1] != "tthread")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (stopLoop)
|
||||||
|
continue;
|
||||||
|
TurtleMutex.runThreadFunc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function runMainLoop() {
|
||||||
|
while (!stopLoop) {
|
||||||
|
var event:Table<Int, Dynamic> = pullEvents();
|
||||||
|
|
||||||
var eventName:String = event[1];
|
var eventName:String = event[1];
|
||||||
|
try {
|
||||||
if (eventName == "terminate"){
|
fireSignal(eventName, event);
|
||||||
return;
|
} catch (e:Dynamic) {
|
||||||
|
Log.error('Error while handling event: $eventName: ${e}');
|
||||||
}
|
}
|
||||||
|
|
||||||
eventBus.emit(eventName,event.toArray());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function on(eventName:String, callback:Array<Dynamic> -> Void):EventBusListner {
|
public static function shutdown() {
|
||||||
return eventBus.on(eventName,callback);
|
// clearing screens
|
||||||
|
for (screen in Peripheral.getAllScreens()) {
|
||||||
|
screen.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function once(eventName:String, callback:Array<Dynamic> -> Void):EventBusListner {
|
Log.info('Shutting down event loop');
|
||||||
return eventBus.once(eventName,callback);
|
stopLoop = true;
|
||||||
|
MainTerm.instance.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function removeListner(id:EventBusListner) {
|
private static function pullEvents():Table<Int, Dynamic> {
|
||||||
return eventBus.removeListner(id);
|
return cast TableTools.pack(Coroutine.yield(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function fireSignal(eventName:String, event:Table<Int, Dynamic>) {
|
||||||
|
switch eventName {
|
||||||
|
case "alarm":
|
||||||
|
onAlarmTrigger.trigger(event[2]);
|
||||||
|
case "char":
|
||||||
|
onCharTrigger.trigger(event[2]);
|
||||||
|
case "disk":
|
||||||
|
onDiskTrigger.trigger(event[2]);
|
||||||
|
case "disk_eject":
|
||||||
|
onDiskEjectTrigger.trigger(event[2]);
|
||||||
|
case "http_check":
|
||||||
|
onHttpCheckTrigger.trigger({url: event[2], success: event[3], failReason: event[4]});
|
||||||
|
case "http_failure":
|
||||||
|
onHttpFailureTrigger.trigger({url: event[2], failReason: event[3], handle: event[4]});
|
||||||
|
case "http_success":
|
||||||
|
onHttpSuccessTrigger.trigger({url: event[2], handle: event[3]});
|
||||||
|
case "key":
|
||||||
|
onKeyTrigger.trigger({keyCode: event[2], isHeld: event[3]});
|
||||||
|
case "key_up":
|
||||||
|
onKeyUpTrigger.trigger(event[2]);
|
||||||
|
case "modem_message":
|
||||||
|
onModemMessageTrigger.trigger({
|
||||||
|
addr: event[2],
|
||||||
|
channel: event[3],
|
||||||
|
replyChannel: event[4],
|
||||||
|
message: event[5],
|
||||||
|
distance: event[6]
|
||||||
|
});
|
||||||
|
case "monitor_resize":
|
||||||
|
onMonitorResizeTrigger.trigger(event[2]);
|
||||||
|
case "monitor_touch":
|
||||||
|
onMonitorTouchTrigger.trigger({addr: event[2], pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
|
||||||
|
case "mouse_click":
|
||||||
|
onMouseClickTrigger.trigger({button: ccButtonToEnum(event[2]), pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
|
||||||
|
case "mouse_drag":
|
||||||
|
onMouseDragTrigger.trigger({button: ccButtonToEnum(event[2]), pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
|
||||||
|
case "mouse_scroll":
|
||||||
|
onMouseScrollTrigger.trigger({dir: event[2], pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
|
||||||
|
case "mouse_up":
|
||||||
|
onMouseUpTrigger.trigger({button: ccButtonToEnum(event[2]), pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
|
||||||
|
case "paste":
|
||||||
|
onPasteTrigger.trigger(event[2]);
|
||||||
|
case "peripheral":
|
||||||
|
onPeripheralTrigger.trigger(event[2]);
|
||||||
|
case "peripheral_detach":
|
||||||
|
onPeripheralDetachTrigger.trigger(event[2]);
|
||||||
|
case "redstone":
|
||||||
|
onRedstoneTrigger.trigger(null);
|
||||||
|
case "speaker_audio_empty":
|
||||||
|
onSpeakerAudioEmptyTrigger.trigger(event[2]);
|
||||||
|
case "task_complete":
|
||||||
|
onTaskCompleteTrigger.trigger({id: event[2], success: event[3], failedReason: event[4]});
|
||||||
|
case "term_resize":
|
||||||
|
onTermResizeTrigger.trigger(null);
|
||||||
|
case "terminate":
|
||||||
|
onTerminateTrigger.trigger(null);
|
||||||
|
case "timer":
|
||||||
|
onTimerTrigger.trigger(event[2]);
|
||||||
|
case "turtle_inventory":
|
||||||
|
onTurtleInventoryTrigger.trigger(null);
|
||||||
|
case "websocket_closed":
|
||||||
|
onWebsocketCloseTrigger.trigger(event[2]);
|
||||||
|
case "websocket_failure":
|
||||||
|
onWebsocketFailureTrigger.trigger({url: event[2], failReason: event[3]});
|
||||||
|
case "websocket_message":
|
||||||
|
onWebsocketMessageTrigger.trigger({url: event[2], message: event[3], isBinary: event[4]});
|
||||||
|
case "websocket_success":
|
||||||
|
onWebsocketSuccessTrigger.trigger({url: event[2], handle: event[3]});
|
||||||
|
case "endofloop":
|
||||||
|
EndOfLoop.run();
|
||||||
|
default:
|
||||||
|
Log.error('Unknown event: $eventName');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function ccButtonToEnum(button:Dynamic):ButtonType {
|
||||||
|
switch button {
|
||||||
|
case 1:
|
||||||
|
return Left;
|
||||||
|
case 2:
|
||||||
|
return Middle;
|
||||||
|
case 3:
|
||||||
|
return Right;
|
||||||
|
default:
|
||||||
|
throw new Exception("Invalid input");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@:allow(lib.Debug)
|
||||||
|
private static function printListenerCount() {
|
||||||
|
if (onAlarmTrigger.getLength() > 0)
|
||||||
|
Log.debug("onAlarm: " + onAlarmTrigger.getLength());
|
||||||
|
if (onCharTrigger.getLength() > 0)
|
||||||
|
Log.debug("onChar: " + onCharTrigger.getLength());
|
||||||
|
if (onDiskTrigger.getLength() > 0)
|
||||||
|
Log.debug("onDisk: " + onDiskTrigger.getLength());
|
||||||
|
if (onDiskEjectTrigger.getLength() > 0)
|
||||||
|
Log.debug("onDiskEject: " + onDiskEjectTrigger.getLength());
|
||||||
|
if (onHttpCheckTrigger.getLength() > 0)
|
||||||
|
Log.debug("onHttpCheck: " + onHttpCheckTrigger.getLength());
|
||||||
|
if (onHttpFailureTrigger.getLength() > 0)
|
||||||
|
Log.debug("onHttpFailure: " + onHttpFailureTrigger.getLength());
|
||||||
|
if (onHttpSuccessTrigger.getLength() > 0)
|
||||||
|
Log.debug("onHttpSuccess: " + onHttpSuccessTrigger.getLength());
|
||||||
|
if (onKeyTrigger.getLength() > 0)
|
||||||
|
Log.debug("onKey: " + onKeyTrigger.getLength());
|
||||||
|
if (onKeyUpTrigger.getLength() > 0)
|
||||||
|
Log.debug("onKeyUp: " + onKeyUpTrigger.getLength());
|
||||||
|
if (onModemMessageTrigger.getLength() > 0)
|
||||||
|
Log.debug("onModemMessage: " + onModemMessageTrigger.getLength());
|
||||||
|
if (onMonitorResizeTrigger.getLength() > 0)
|
||||||
|
Log.debug("onMonitorResize: " + onMonitorResizeTrigger.getLength());
|
||||||
|
if (onMonitorTouchTrigger.getLength() > 0)
|
||||||
|
Log.debug("onMonitorTouch: " + onMonitorTouchTrigger.getLength());
|
||||||
|
if (onMouseClickTrigger.getLength() > 0)
|
||||||
|
Log.debug("onMouseClick: " + onMouseClickTrigger.getLength());
|
||||||
|
if (onMouseDragTrigger.getLength() > 0)
|
||||||
|
Log.debug("onMouseDrag: " + onMouseDragTrigger.getLength());
|
||||||
|
if (onMouseScrollTrigger.getLength() > 0)
|
||||||
|
Log.debug("onMouseScroll: " + onMouseScrollTrigger.getLength());
|
||||||
|
if (onMouseUpTrigger.getLength() > 0)
|
||||||
|
Log.debug("onMouseUp: " + onMouseUpTrigger.getLength());
|
||||||
|
if (onPasteTrigger.getLength() > 0)
|
||||||
|
Log.debug("onPaste: " + onPasteTrigger.getLength());
|
||||||
|
if (onPeripheralTrigger.getLength() > 0)
|
||||||
|
Log.debug("onPeripheral: " + onPeripheralTrigger.getLength());
|
||||||
|
if (onPeripheralDetachTrigger.getLength() > 0)
|
||||||
|
Log.debug("onPeripheralDetach: " + onPeripheralDetachTrigger.getLength());
|
||||||
|
if (onRedstoneTrigger.getLength() > 0)
|
||||||
|
Log.debug("onRedstone: " + onRedstoneTrigger.getLength());
|
||||||
|
if (onSpeakerAudioEmptyTrigger.getLength() > 0)
|
||||||
|
Log.debug("onSpeakerAudioEmpty: " + onSpeakerAudioEmptyTrigger.getLength());
|
||||||
|
if (onTaskCompleteTrigger.getLength() > 0)
|
||||||
|
Log.debug("onTaskComplete: " + onTaskCompleteTrigger.getLength());
|
||||||
|
if (onTermResizeTrigger.getLength() > 0)
|
||||||
|
Log.debug("onTermResize: " + onTermResizeTrigger.getLength());
|
||||||
|
if (onTerminateTrigger.getLength() > 0)
|
||||||
|
Log.debug("onTerminate: " + onTerminateTrigger.getLength());
|
||||||
|
if (onTimerTrigger.getLength() > 0)
|
||||||
|
Log.debug("onTimer: " + onTimerTrigger.getLength());
|
||||||
|
if (onTurtleInventoryTrigger.getLength() > 0)
|
||||||
|
Log.debug("onTurtleInventory: " + onTurtleInventoryTrigger.getLength());
|
||||||
|
if (onWebsocketCloseTrigger.getLength() > 0)
|
||||||
|
Log.debug("onWebsocketClose: " + onWebsocketCloseTrigger.getLength());
|
||||||
|
if (onWebsocketFailureTrigger.getLength() > 0)
|
||||||
|
Log.debug("onWebsocketFailure: " + onWebsocketFailureTrigger.getLength());
|
||||||
|
if (onWebsocketMessageTrigger.getLength() > 0)
|
||||||
|
Log.debug("onWebsocketMessage: " + onWebsocketMessageTrigger.getLength());
|
||||||
|
if (onWebsocketSuccessTrigger.getLength() > 0)
|
||||||
|
Log.debug("onWebsocketSuccess: " + onWebsocketSuccessTrigger.getLength());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
58
src/kernel/KernelSettings.hx
Normal file
58
src/kernel/KernelSettings.hx
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
package kernel;
|
|
||||||
|
|
||||||
import kernel.ui.WindowContext;
|
|
||||||
import kernel.ui.WindowManager;
|
|
||||||
import lib.TermWriteable;
|
|
||||||
import lib.TermIO;
|
|
||||||
|
|
||||||
/**
|
|
||||||
Log messages to specified output.
|
|
||||||
**/
|
|
||||||
class Log {
|
|
||||||
private static var context:WindowContext;
|
|
||||||
private static var writer:TermIO;
|
|
||||||
|
|
||||||
@:allow(kernel.Init)
|
|
||||||
private static function init() {
|
|
||||||
Log.context = WindowManager.instance.createNewContext();
|
|
||||||
Log.writer = new TermIO(Log.context);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function setMainoutout(newOutput: TermWriteable) {
|
|
||||||
writer = new TermIO(newOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function info(msg: Dynamic, ?pos:haxe.PosInfos){
|
|
||||||
writer.writeLn("[INFO]["+pos.className+"]: "+Std.string(msg));
|
|
||||||
#if webconsole
|
|
||||||
Debug.printWeb("[INFO]["+pos.className+"]: "+Std.string(msg));
|
|
||||||
#end
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function warn(msg: Dynamic, ?pos:haxe.PosInfos){
|
|
||||||
writer.writeLn("[WARN]["+pos.className+"]: "+Std.string(msg),Yellow);
|
|
||||||
#if webconsole
|
|
||||||
Debug.printWeb("[WARN]["+pos.className+"]: "+Std.string(msg));
|
|
||||||
#end
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function error(msg: Dynamic,?pos:haxe.PosInfos) {
|
|
||||||
writer.writeLn("[ERRO]["+pos.className+"]: "+Std.string(msg),Red);
|
|
||||||
#if webconsole
|
|
||||||
Debug.printWeb("[ERRO]["+pos.className+"]: "+Std.string(msg));
|
|
||||||
#end
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function debug(msg: Dynamic,?pos:haxe.PosInfos) {
|
|
||||||
writer.writeLn("[DEBG]["+pos.className+"]: "+Std.string(msg),Gray);
|
|
||||||
#if webconsole
|
|
||||||
Debug.printWeb("[DEBG]["+pos.className+"]: "+Std.string(msg));
|
|
||||||
#end
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function moveToOutput(addr: String) {
|
|
||||||
WindowManager.instance.focusContextToOutput(context,addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +1,22 @@
|
|||||||
package kernel;
|
package kernel;
|
||||||
|
|
||||||
using tink.CoreApi;
|
import lib.ScreenPos;
|
||||||
|
import kernel.ui.ITermWriteable;
|
||||||
import lib.TermWriteable;
|
|
||||||
import cc.Term;
|
import cc.Term;
|
||||||
import util.Vec.Vec2;
|
import lib.Vec.Vec2;
|
||||||
import util.Color;
|
import lib.Color;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Represents the main computer screen.
|
Represents the main computer screen.
|
||||||
**/
|
**/
|
||||||
class MainTerm implements TermWriteable{
|
class MainTerm implements ITermWriteable {
|
||||||
|
/**
|
||||||
|
Depends on: KernelEvents,
|
||||||
|
**/
|
||||||
public static var instance:MainTerm;
|
public static var instance:MainTerm;
|
||||||
|
|
||||||
public var onResize(default, null):Signal<Vec2<Int>>;
|
public var onResize(default, null):Signal<Vec2<Int>>;
|
||||||
|
|
||||||
private var onResizeTrigger:SignalTrigger<Vec2<Int>>;
|
private var onResizeTrigger:SignalTrigger<Vec2<Int>>;
|
||||||
@@ -21,12 +26,11 @@ class MainTerm implements TermWriteable{
|
|||||||
this.onResizeTrigger = Signal.trigger();
|
this.onResizeTrigger = Signal.trigger();
|
||||||
this.onResize = this.onResizeTrigger.asSignal();
|
this.onResize = this.onResizeTrigger.asSignal();
|
||||||
|
|
||||||
KernelEvents.instance.on("term_resize",params ->{
|
KernelEvents.onTermResize.handle(_ -> {
|
||||||
onResizeTrigger.trigger(getSize());
|
onResizeTrigger.trigger(getSize());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function write(text:String) {
|
public function write(text:String) {
|
||||||
Term.write(text);
|
Term.write(text);
|
||||||
}
|
}
|
||||||
@@ -35,7 +39,7 @@ class MainTerm implements TermWriteable{
|
|||||||
Term.scroll(y);
|
Term.scroll(y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCursorPos():Vec2<Int> {
|
public function getCursorPos():ScreenPos {
|
||||||
var rtn = Term.getCursorPos();
|
var rtn = Term.getCursorPos();
|
||||||
return {
|
return {
|
||||||
x: rtn.x - 1,
|
x: rtn.x - 1,
|
||||||
@@ -44,7 +48,7 @@ class MainTerm implements TermWriteable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function setCursorPos(x:Int, y:Int) {
|
public function setCursorPos(x:Int, y:Int) {
|
||||||
Term.setCursorPos(x + 1,y + 1);
|
Term.setCursorPos(x + 1, y + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCursorBlink():Bool {
|
public function getCursorBlink():Bool {
|
||||||
@@ -56,7 +60,7 @@ class MainTerm implements TermWriteable{
|
|||||||
Term.setCursorBlink(blink);
|
Term.setCursorBlink(blink);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSize():Vec2<Int> {
|
public function getSize():ScreenPos {
|
||||||
var rtn = Term.getSize();
|
var rtn = Term.getSize();
|
||||||
return {
|
return {
|
||||||
x: rtn.width,
|
x: rtn.width,
|
||||||
@@ -73,22 +77,29 @@ class MainTerm implements TermWriteable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getTextColor():Color {
|
public function getTextColor():Color {
|
||||||
return ColorConvert.ccToColor(Term.getTextColor());
|
return Term.getTextColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setTextColor(colour:Color) {
|
public function setTextColor(color:Color) {
|
||||||
Term.setTextColor(ColorConvert.colorToCC(colour));
|
Term.setTextColor(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBackgroundColor():Color {
|
public function getBackgroundColor():Color {
|
||||||
return ColorConvert.ccToColor(Term.getBackgroundColor());
|
return Term.getBackgroundColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setBackgroundColor(color:Color) {
|
public function setBackgroundColor(color:Color) {
|
||||||
Term.setBackgroundColor(ColorConvert.colorToCC(color));
|
Term.setBackgroundColor(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isColor():Bool {
|
public function isColor():Bool {
|
||||||
return Term.isColor();
|
return Term.isColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function reset() {
|
||||||
|
this.setBackgroundColor(Black);
|
||||||
|
this.setTextColor(White);
|
||||||
|
this.clear();
|
||||||
|
this.setCursorPos(0, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,28 @@
|
|||||||
package kernel;
|
package kernel;
|
||||||
|
|
||||||
|
import cc.OS;
|
||||||
|
|
||||||
using tink.CoreApi;
|
using tink.CoreApi;
|
||||||
|
|
||||||
import util.EventBus.EventBusListner;
|
|
||||||
import cc.OS;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Wrapper class for using timer.
|
Wrapper class for using timers.
|
||||||
**/
|
**/
|
||||||
class Timer {
|
class Timer {
|
||||||
private final timerID:Int;
|
private final timerID:Int;
|
||||||
private final callback:Callback<Noise>;
|
private final callback:Callback<Noise>;
|
||||||
private final timerListner:EventBusListner;
|
private final timerLink:CallbackLink;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Create new timer with timeout in seconds.
|
Create new timer with timeout in seconds.
|
||||||
**/
|
**/
|
||||||
public function new(timeout: Int, callback: Callback<Noise>) {
|
public function new(timeout:Float, callback:Callback<Noise>) {
|
||||||
timerID = OS.startTimer(timeout);
|
timerID = OS.startTimer(timeout);
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
|
|
||||||
timerListner = KernelEvents.instance.on("timer",(params)->{
|
timerLink = KernelEvents.onTimer.handle(timerID -> {
|
||||||
if (params[1] == timerID){
|
if (this.timerID == timerID) {
|
||||||
callback.invoke(null);
|
callback.invoke(null);
|
||||||
KernelEvents.instance.removeListner(timerListner);
|
timerLink.cancel();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -33,6 +32,6 @@ class Timer {
|
|||||||
**/
|
**/
|
||||||
public function cancle() {
|
public function cancle() {
|
||||||
OS.cancelTimer(timerID);
|
OS.cancelTimer(timerID);
|
||||||
KernelEvents.instance.removeListner(timerListner);
|
timerLink.cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/kernel/binstore/Bin.hx
Normal file
12
src/kernel/binstore/Bin.hx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package kernel.binstore;
|
||||||
|
|
||||||
|
import kernel.ps.IProcess;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Represents a callable program.
|
||||||
|
**/
|
||||||
|
typedef Bin = {
|
||||||
|
c:Void->IProcess,
|
||||||
|
name:String,
|
||||||
|
aliases:Array<String>,
|
||||||
|
}
|
||||||
45
src/kernel/binstore/BinStore.hx
Normal file
45
src/kernel/binstore/BinStore.hx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package kernel.binstore;
|
||||||
|
|
||||||
|
import kernel.ps.IProcess;
|
||||||
|
import macros.Binstore;
|
||||||
|
|
||||||
|
class BinStore {
|
||||||
|
private static final bins:Array<Bin> = populateStore();
|
||||||
|
|
||||||
|
private static function populateStore()
|
||||||
|
return {
|
||||||
|
var bins:Array<Bin> = [];
|
||||||
|
Binstore.generateBinStore();
|
||||||
|
return bins;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function instantiate(alias:String):Null<IProcess> {
|
||||||
|
for (bin in bins) {
|
||||||
|
for (a in bin.aliases) {
|
||||||
|
if (a == alias) {
|
||||||
|
return bin.c();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getBinByAlias(alias:String):Null<Bin> {
|
||||||
|
for (bin in bins) {
|
||||||
|
for (a in bin.aliases) {
|
||||||
|
if (a == alias) {
|
||||||
|
return bin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getNameByAlias(alias:String):Null<String> {
|
||||||
|
var bin = getBinByAlias(alias);
|
||||||
|
if (bin == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return bin.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
103
src/kernel/fs/FS.hx
Normal file
103
src/kernel/fs/FS.hx
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package kernel.fs;
|
||||||
|
|
||||||
|
import haxe.ds.ReadOnlyArray;
|
||||||
|
import kernel.fs.FileHandler.WriteBinaryHandle;
|
||||||
|
import kernel.fs.FileHandler.ReadBinaryHandle;
|
||||||
|
import kernel.fs.FileHandler.WriteHandle;
|
||||||
|
import kernel.fs.FileHandler.ReadHandle;
|
||||||
|
import cc.FileSystem;
|
||||||
|
|
||||||
|
using lua.Table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Wrapper to interact with the filesystem.
|
||||||
|
**/
|
||||||
|
class FS {
|
||||||
|
public static inline function list(path:String):ReadOnlyArray<String> {
|
||||||
|
return FileSystem.list(path).toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function combine(base:String, part:String):String {
|
||||||
|
return FileSystem.combine(base, part);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function getName(path:String):String {
|
||||||
|
return FileSystem.getName(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function getDir(path:String):String {
|
||||||
|
return FileSystem.getDir(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function getSize(path:String):Int {
|
||||||
|
return FileSystem.getSize(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function exists(path:String):Bool {
|
||||||
|
return FileSystem.exists(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function isDir(path:String):Bool {
|
||||||
|
return FileSystem.isDir(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function isReadOnly(path:String):Bool {
|
||||||
|
return FileSystem.isReadOnly(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function makeDir(path:String):Void {
|
||||||
|
FileSystem.makeDir(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function move(src:String, dest:String):Void {
|
||||||
|
FileSystem.move(src, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function copy(src:String, dest:String):Void {
|
||||||
|
FileSystem.copy(src, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function delete(path:String):Void {
|
||||||
|
FileSystem.delete(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function openRead(path:String):ReadHandle {
|
||||||
|
return FileSystem.open(path, Read);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function openWrite(path:String):WriteHandle {
|
||||||
|
return FileSystem.open(path, Write);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function openAppend(path:String):WriteHandle {
|
||||||
|
return FileSystem.open(path, Append);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function openReadBinary(path:String):ReadBinaryHandle {
|
||||||
|
return FileSystem.open(path, BinaryRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function openWriteBinary(path:String):WriteBinaryHandle {
|
||||||
|
return FileSystem.open(path, BinaryWrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function openAppendBinary(path:String):WriteBinaryHandle {
|
||||||
|
return FileSystem.open(path, BinaryAppend);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function find(pattern:String):ReadOnlyArray<String> {
|
||||||
|
return FileSystem.find(pattern).toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function getFreeSpace(path:String):Int {
|
||||||
|
return FileSystem.getFreeSpace(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function getCapacity(path:String):Int {
|
||||||
|
return FileSystem.getCapacity(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function attributes(path:String):FileAttributes {
|
||||||
|
return FileSystem.attributes(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
112
src/kernel/fs/FileHandler.hx
Normal file
112
src/kernel/fs/FileHandler.hx
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
package kernel.fs;
|
||||||
|
|
||||||
|
import cc.FileSystem.FileHandle;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
abstract ReadHandle(FileHandle) from FileHandle {
|
||||||
|
public inline function new(handle:FileHandle) {
|
||||||
|
this = handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function readLine(?withTrailing:Bool = false):Null<String> {
|
||||||
|
return this.readLine(withTrailing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function readAll():Null<String> {
|
||||||
|
return this.readAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function read(?count:Int = 1):Null<String> {
|
||||||
|
return this.read(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function close():Void {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract WriteHandle(FileHandle) from FileHandle {
|
||||||
|
public inline function new(handle:FileHandle) {
|
||||||
|
this = handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function write(data:String):Void {
|
||||||
|
this.write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function writeLine(data:String):Void {
|
||||||
|
this.writeLine(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function flush():Void {
|
||||||
|
this.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function close():Void {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract ReadBinaryHandle(FileHandle) from FileHandle {
|
||||||
|
public inline function new(handle:FileHandle) {
|
||||||
|
this = handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function readLine(?withTrailing:Bool = false):Null<String> {
|
||||||
|
return this.readLine(withTrailing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function readAll():Null<String> {
|
||||||
|
return this.readAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function read(count:Int):Null<String> {
|
||||||
|
return this.read(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function readByte():Null<Int> {
|
||||||
|
return this.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function seek(?whence:BinarySeekWhence = Current, ?offset:Int):Void {
|
||||||
|
this.seek(whence, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function close():Void {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract WriteBinaryHandle(FileHandle) from FileHandle {
|
||||||
|
public inline function new(handle:FileHandle) {
|
||||||
|
this = handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function write(data:String):Void {
|
||||||
|
this.write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
public inline function writeByte(data:Int):Void {
|
||||||
|
// this.write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function flush():Void {
|
||||||
|
this.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function close():Void {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function seek(?whence:BinarySeekWhence = Current, ?offset:Int):Void {
|
||||||
|
this.seek(whence, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum abstract BinarySeekWhence(String) to String {
|
||||||
|
var Set = "set"; // Relative to the beginning of the file.
|
||||||
|
var Current = "cur"; // Relative to the current position. This is the default.
|
||||||
|
var End = "end"; // Relative to the end of the file.
|
||||||
|
}
|
||||||
244
src/kernel/gps/GPS.hx
Normal file
244
src/kernel/gps/GPS.hx
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
package kernel.gps;
|
||||||
|
|
||||||
|
import lib.SingleTimeoutPromise;
|
||||||
|
import kernel.log.Log;
|
||||||
|
import lib.KVStore;
|
||||||
|
import kernel.net.Net;
|
||||||
|
import kernel.net.INetworkInterface;
|
||||||
|
import kernel.net.Package;
|
||||||
|
import lib.WorldPos;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Determines the position of the computer based on the distance to other computers.
|
||||||
|
When receiving a message from another computer via wireless, the distance is also received.
|
||||||
|
You need at least 3 computers that know their position to determine the position of the computer.
|
||||||
|
**/
|
||||||
|
class GPS {
|
||||||
|
private static inline final TIMEOUT:Int = 1;
|
||||||
|
|
||||||
|
private static var shouldRespond = true;
|
||||||
|
private static var shouldDoWholeNumberCheck = true;
|
||||||
|
private static var posAccuracy = 0; // 0 = unkown, 1 = (ins,best guess), 2 = (stored/manual,should be right), 3 = (gps,confirmed)
|
||||||
|
private static var cachedPosition:WorldPos;
|
||||||
|
private static var lastPositionResponse:Array<{pos:WorldPos, dist:Float}> = [];
|
||||||
|
|
||||||
|
private static final locatePromise:SingleTimeoutPromise<WorldPos> = new SingleTimeoutPromise(TIMEOUT, brodcastPositionRequest);
|
||||||
|
|
||||||
|
@:allow(kernel.Init)
|
||||||
|
private static function init() {
|
||||||
|
loadCachedPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function setManualPosition(pos:WorldPos) {
|
||||||
|
var kvstore = new KVStore("gps");
|
||||||
|
kvstore.set("mpos", pos);
|
||||||
|
kvstore.save();
|
||||||
|
|
||||||
|
if (cachedPosition == null) {
|
||||||
|
cachedPosition = pos;
|
||||||
|
posAccuracy = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@:allow(kernel.gps.INS)
|
||||||
|
private static function setINSPosition(pos:WorldPos) {
|
||||||
|
cachedPosition = pos;
|
||||||
|
posAccuracy = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getPosition():Null<WorldPos> {
|
||||||
|
return cachedPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getAccuracy():Int {
|
||||||
|
return posAccuracy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function invalidatePosition() {
|
||||||
|
cachedPosition = null;
|
||||||
|
posAccuracy = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function locate():Promise<WorldPos> {
|
||||||
|
return locatePromise.request();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function persistCachedPositon() {
|
||||||
|
if (cachedPosition == null)
|
||||||
|
return;
|
||||||
|
var kvstore = new KVStore("gps");
|
||||||
|
|
||||||
|
kvstore.set("cpos", cachedPosition);
|
||||||
|
kvstore.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function loadCachedPosition() {
|
||||||
|
var kvstore = new KVStore("gps");
|
||||||
|
kvstore.load();
|
||||||
|
|
||||||
|
var mPos:Null<WorldPos> = kvstore.get("mpos"); // Manual set position
|
||||||
|
var cPos:Null<WorldPos> = kvstore.get("cpos"); // Cached position
|
||||||
|
|
||||||
|
if (mPos != null && cPos != null && mPos == cPos) {
|
||||||
|
// Both are the same, so we can use the cached position
|
||||||
|
cachedPosition = mPos;
|
||||||
|
posAccuracy = 3;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mPos != null && cPos != null && mPos != cPos) {
|
||||||
|
// Both are different, so we can use the manual position
|
||||||
|
cachedPosition = mPos;
|
||||||
|
posAccuracy = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mPos == null && cPos != null) {
|
||||||
|
// No manual position set, so we can use the cached position
|
||||||
|
cachedPosition = cPos;
|
||||||
|
posAccuracy = 2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mPos != null && cPos == null) {
|
||||||
|
// No cached position, so we can use the manual position
|
||||||
|
cachedPosition = mPos;
|
||||||
|
posAccuracy = 2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function brodcastPositionRequest() {
|
||||||
|
Net.brodcastGPSRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@:allow(kernel.net.Net)
|
||||||
|
private static function handlePackage(pack:Package<Noise>, dist:Null<Float>, iface:INetworkInterface) {
|
||||||
|
if (dist == null) {
|
||||||
|
// Message comes from another dimension and has no distance.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (pack.type) {
|
||||||
|
case GPSRequest:
|
||||||
|
if (!shouldRespond)
|
||||||
|
return;
|
||||||
|
if (posAccuracy < 2)
|
||||||
|
return;
|
||||||
|
if (cachedPosition == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var response = new Package(Net.networkID, pack.fromID, pack.msgID, GPSResponse(cachedPosition.x, cachedPosition.y, cachedPosition.z,), null, 0);
|
||||||
|
iface.send(pack.fromID, Net.networkID, response);
|
||||||
|
case GPSResponse(x, y, z):
|
||||||
|
if (lastPositionResponse.contains({pos: {x: x, y: y, z: z}, dist: dist}))
|
||||||
|
return; // Ignore duplicate responses
|
||||||
|
|
||||||
|
lastPositionResponse.push({pos: {x: x, y: y, z: z}, dist: dist});
|
||||||
|
|
||||||
|
// TODO: wait for a few seconds before calculating the position, so we can get more responses
|
||||||
|
|
||||||
|
if (lastPositionResponse.length < 4)
|
||||||
|
return; // We need at least 3 responses to calculate the position
|
||||||
|
|
||||||
|
var calculatedPosition = calculatePosition();
|
||||||
|
|
||||||
|
if (calculatedPosition != null) {
|
||||||
|
calculatedPosition = calculatedPosition.round();
|
||||||
|
}
|
||||||
|
|
||||||
|
lastPositionResponse = []; // Reset the response array
|
||||||
|
|
||||||
|
if (calculatedPosition == null)
|
||||||
|
return;
|
||||||
|
cachedPosition = calculatedPosition;
|
||||||
|
posAccuracy = 3;
|
||||||
|
|
||||||
|
locatePromise.resolve(calculatedPosition);
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function calculatePosition():Null<WorldPos> {
|
||||||
|
if (lastPositionResponse.length < 3)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// do a simple trilateration with the last 3 responses for now
|
||||||
|
var p1 = lastPositionResponse[0].pos;
|
||||||
|
var p2 = lastPositionResponse[1].pos;
|
||||||
|
var p3 = lastPositionResponse[2].pos;
|
||||||
|
|
||||||
|
var r1 = lastPositionResponse[0].dist;
|
||||||
|
var r2 = lastPositionResponse[1].dist;
|
||||||
|
var r3 = lastPositionResponse[2].dist;
|
||||||
|
|
||||||
|
var result = trilateration(p1, p2, p3, r1, r2, r3);
|
||||||
|
|
||||||
|
if (result.a.close(result.b))
|
||||||
|
return result.a;
|
||||||
|
|
||||||
|
// If we have more than 3 responses, we can use the 4th response to determine which of the two positions is correct
|
||||||
|
// TODO: if this is a pocket computer we cant use this since it can move freely
|
||||||
|
if (lastPositionResponse.length > 3) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (Math.abs(err1 - err2) < 0.0001) {
|
||||||
|
Log.warn("Failed to determine GPS position via 4th fix");
|
||||||
|
return null; // The two positions are essentially the same, so we cant determine which one is correct
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err1 < err2)
|
||||||
|
return result.a;
|
||||||
|
if (err2 < err1)
|
||||||
|
return result.b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// last resort, just use the position that is closest to a whole number (whithin a resonable margin of error)
|
||||||
|
if (!shouldDoWholeNumberCheck)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// TODO: mark the position as not so accurate if we use this method
|
||||||
|
|
||||||
|
var err1 = (result.a - result.a.round()).length();
|
||||||
|
var err2 = (result.b - result.b.round()).length();
|
||||||
|
|
||||||
|
if (err1 < err2 && err1 < 0.0001) {
|
||||||
|
return result.a;
|
||||||
|
} else if (err2 < 0.0001) {
|
||||||
|
return result.b;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Determines the position(s) of a point given 3 other points and the distance to each of them.
|
||||||
|
**/
|
||||||
|
private static function trilateration(p1:WorldPos, p2:WorldPos, p3:WorldPos, r1:Float, r2:Float, r3:Float):Pair<WorldPos, WorldPos> {
|
||||||
|
var a2b = p2 - p1;
|
||||||
|
var a2c = p3 - p1;
|
||||||
|
|
||||||
|
var d = a2b.length();
|
||||||
|
var ex = a2b.normalize();
|
||||||
|
var i = ex.dot(a2c);
|
||||||
|
var ey = (a2c - ex * i).normalize();
|
||||||
|
var j = ey.dot(a2c);
|
||||||
|
var ez:WorldPos = ex.cross(ey);
|
||||||
|
|
||||||
|
var x = (r1 * r1 - r2 * r2 + d * d) / (2 * d);
|
||||||
|
var y = (r1 * r1 - r3 * r3 - x * x + (x - i) * (x - i) + j * j) / (2 * j);
|
||||||
|
|
||||||
|
var result = p1 + ex * x + ey * y;
|
||||||
|
|
||||||
|
var zSquared = r1 * r1 - x * x - y * y;
|
||||||
|
if (zSquared > 0) {
|
||||||
|
var z = Math.sqrt(zSquared);
|
||||||
|
return new Pair(result + (ez * z), result - (ez * z));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Pair(result, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
197
src/kernel/gps/INS.hx
Normal file
197
src/kernel/gps/INS.hx
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
package kernel.gps;
|
||||||
|
|
||||||
|
import lib.SinglePromise;
|
||||||
|
import kernel.turtle.Turtle;
|
||||||
|
import lib.WorldPos;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
class INS {
|
||||||
|
private static var heading:Null<WorldPos> = null;
|
||||||
|
private static var alingment:Int = 1; // 0 = degraded, 1 = not aligned, 2 = aligned
|
||||||
|
private static final alignPromise:SinglePromise<Noise> = new SinglePromise(startAlign);
|
||||||
|
|
||||||
|
@:allow(kernel.turtle.Turtle)
|
||||||
|
private static function moveForward() {
|
||||||
|
if (heading == null) {
|
||||||
|
alingment = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
move(heading);
|
||||||
|
}
|
||||||
|
|
||||||
|
@:allow(kernel.turtle.Turtle)
|
||||||
|
private static function moveBackward() {
|
||||||
|
if (heading == null) {
|
||||||
|
alingment = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
move(heading.negate());
|
||||||
|
}
|
||||||
|
|
||||||
|
@:allow(kernel.turtle.Turtle)
|
||||||
|
private static function moveUp() {
|
||||||
|
move({x: 0, y: 1, z: 0});
|
||||||
|
}
|
||||||
|
|
||||||
|
@:allow(kernel.turtle.Turtle)
|
||||||
|
private static function moveDown() {
|
||||||
|
move({x: 0, y: -1, z: 0});
|
||||||
|
}
|
||||||
|
|
||||||
|
@:allow(kernel.turtle.Turtle)
|
||||||
|
private static function turnLeft() {
|
||||||
|
if (heading == null)
|
||||||
|
return;
|
||||||
|
if (heading.x == 0 && heading.z == -1) {
|
||||||
|
heading = {x: -1, y: 0, z: 0};
|
||||||
|
} else if (heading.x == -1 && heading.z == 0) {
|
||||||
|
heading = {x: 0, y: 0, z: 1};
|
||||||
|
} else if (heading.x == 0 && heading.z == 1) {
|
||||||
|
heading = {x: 1, y: 0, z: 0};
|
||||||
|
} else if (heading.x == 1 && heading.z == 0) {
|
||||||
|
heading = {x: 0, y: 0, z: -1};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@:allow(kernel.turtle.Turtle)
|
||||||
|
private static function turnRight() {
|
||||||
|
if (heading == null)
|
||||||
|
return;
|
||||||
|
if (heading.x == 0 && heading.z == -1) {
|
||||||
|
heading = {x: 1, y: 0, z: 0};
|
||||||
|
} else if (heading.x == -1 && heading.z == 0) {
|
||||||
|
heading = {x: 0, y: 0, z: -1};
|
||||||
|
} else if (heading.x == 0 && heading.z == 1) {
|
||||||
|
heading = {x: -1, y: 0, z: 0};
|
||||||
|
} else if (heading.x == 1 && heading.z == 0) {
|
||||||
|
heading = {x: 0, y: 0, z: 1};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function move(dir:Null<WorldPos>) {
|
||||||
|
if (alignPromise.isRunning()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var pos = GPS.getPosition();
|
||||||
|
if (pos == null || dir == null)
|
||||||
|
return;
|
||||||
|
var newPos = pos + dir;
|
||||||
|
GPS.setINSPosition(newPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getHeading():Null<WorldPos> {
|
||||||
|
return heading;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function align():Promise<Noise> {
|
||||||
|
return alignPromise.request();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function startAlign():Void {
|
||||||
|
if (Turtle.getFuelLevel() < 2) {
|
||||||
|
alignPromise.reject(new Error("Not enough fuel to align"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GPS.locate().handle((result) -> {
|
||||||
|
switch result {
|
||||||
|
case Failure(err):
|
||||||
|
alignPromise.reject(new Error("Failed to locate 1st positon: " + err));
|
||||||
|
return;
|
||||||
|
case Success(pos1):
|
||||||
|
var moved = tryMoving();
|
||||||
|
|
||||||
|
if (moved == -1) {
|
||||||
|
alignPromise.reject(new Error("Can't move"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GPS.locate().handle((result2) -> {
|
||||||
|
switch result2 {
|
||||||
|
case Failure(err):
|
||||||
|
alignPromise.reject(new Error("GPS not available for 2nd position: " + err));
|
||||||
|
return;
|
||||||
|
case Success(pos2):
|
||||||
|
var cHeading = calcHeading(pos1, pos2, moved);
|
||||||
|
if (cHeading == null) {
|
||||||
|
alignPromise.reject(new Error("Can't calculate heading"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
heading = cHeading;
|
||||||
|
moveBack(moved);
|
||||||
|
GPS.setINSPosition(pos1);
|
||||||
|
|
||||||
|
alignPromise.resolve(Noise);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// -1 = not moved, 0 = back, 1 = forward, 2 = left, 3 = right
|
||||||
|
private static function tryMoving():Int {
|
||||||
|
if (Turtle.back().isSuccess()) {
|
||||||
|
return 0;
|
||||||
|
} else if (Turtle.forward().isSuccess()) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
Turtle.turnLeft(); // TODO: Check if successfull
|
||||||
|
if (Turtle.forward().isSuccess()) {
|
||||||
|
return 2;
|
||||||
|
} else if (Turtle.back().isSuccess()) {
|
||||||
|
return 3;
|
||||||
|
} else {
|
||||||
|
// Can't move
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function calcHeading(pos1:WorldPos, pos2:WorldPos, moved:Int):Null<WorldPos> {
|
||||||
|
if (moved == 0) {
|
||||||
|
return pos1 - pos2;
|
||||||
|
} else if (moved == 1) {
|
||||||
|
return pos2 - pos1;
|
||||||
|
} else if (moved == 2) {
|
||||||
|
return rotatePos3ToRight(pos2 - pos1);
|
||||||
|
} else if (moved == 3) {
|
||||||
|
return rotatePos3ToLeft(pos2 - pos1);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function moveBack(moved:Int) {
|
||||||
|
if (moved == 0) {
|
||||||
|
Turtle.forward();
|
||||||
|
} else if (moved == 1) {
|
||||||
|
Turtle.back();
|
||||||
|
} else if (moved == 2) {
|
||||||
|
Turtle.back();
|
||||||
|
Turtle.turnRight();
|
||||||
|
} else if (moved == 3) {
|
||||||
|
Turtle.forward();
|
||||||
|
Turtle.turnRight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function rotatePos3ToRight(pos:WorldPos):WorldPos {
|
||||||
|
if (pos.x == 0 && pos.z == -1) {
|
||||||
|
return {x: 1, y: 0, z: 0};
|
||||||
|
} else if (pos.x == -1 && pos.z == 0) {
|
||||||
|
return {x: 0, y: 0, z: -1};
|
||||||
|
} else if (pos.x == 0 && pos.z == 1) {
|
||||||
|
return {x: -1, y: 0, z: 0};
|
||||||
|
} else if (pos.x == 1 && pos.z == 0) {
|
||||||
|
return {x: 0, y: 0, z: 1};
|
||||||
|
} else {
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function rotatePos3ToLeft(pos3:WorldPos):WorldPos {
|
||||||
|
return rotatePos3ToRight(rotatePos3ToRight(rotatePos3ToRight(pos3)));
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/kernel/http/HTTPFailure.hx
Normal file
21
src/kernel/http/HTTPFailure.hx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package kernel.http;
|
||||||
|
|
||||||
|
using lua.Table;
|
||||||
|
|
||||||
|
class HTTPFailure {
|
||||||
|
public final reason:String;
|
||||||
|
public final statusCode:Null<StatusCode>;
|
||||||
|
public final headers:Map<String, String>;
|
||||||
|
public final body:String;
|
||||||
|
|
||||||
|
@:allow(kernel.http)
|
||||||
|
private function new(failReason:String, ?handle:cc.HTTP.HTTPResponse) {
|
||||||
|
this.reason = failReason;
|
||||||
|
if (handle != null) {
|
||||||
|
this.statusCode = handle.getResponseCode();
|
||||||
|
this.headers = handle.getResponseHeaders().toMap();
|
||||||
|
this.body = handle.readAll();
|
||||||
|
handle.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/kernel/http/HTTPRequest.hx
Normal file
28
src/kernel/http/HTTPRequest.hx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package kernel.http;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Wrapper for the native HTTP request function.
|
||||||
|
**/
|
||||||
|
class Http {
|
||||||
|
public static function request(url:String, ?body:String, ?options:String):Future<Outcome<HTTPResponse, HTTPFailure>> {
|
||||||
|
return new Future<Outcome<HTTPResponse, HTTPFailure>>((resolve) -> {
|
||||||
|
KernelEvents.onHttpFailure.handle((params) -> {
|
||||||
|
if (params.url == url) {
|
||||||
|
resolve(Failure(new HTTPFailure(params.failReason, params.handle)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
KernelEvents.onHttpSuccess.handle((params) -> {
|
||||||
|
if (params.url == url) {
|
||||||
|
resolve(Success(new HTTPResponse(params.handle)));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
cc.HTTP.request(url, body);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/kernel/http/HTTPResponse.hx
Normal file
16
src/kernel/http/HTTPResponse.hx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package kernel.http;
|
||||||
|
|
||||||
|
using lua.Table;
|
||||||
|
|
||||||
|
class HTTPResponse {
|
||||||
|
public final statusCode:StatusCode;
|
||||||
|
public final headers:Map<String, String>;
|
||||||
|
public final body:String;
|
||||||
|
|
||||||
|
@:allow(kernel.http)
|
||||||
|
private function new(handle:cc.HTTP.HTTPResponse) {
|
||||||
|
this.statusCode = handle.getResponseCode();
|
||||||
|
this.headers = handle.getResponseHeaders().toMap();
|
||||||
|
this.body = handle.readAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
65
src/kernel/http/StatusCode.hx
Normal file
65
src/kernel/http/StatusCode.hx
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package kernel.http;
|
||||||
|
|
||||||
|
enum abstract StatusCode(Int) from Int {
|
||||||
|
var Continue = 100;
|
||||||
|
var SwitchingProtocols = 101;
|
||||||
|
var Processing = 102;
|
||||||
|
var OK = 200;
|
||||||
|
var Created = 201;
|
||||||
|
var Accepted = 202;
|
||||||
|
var NonAuthoritativeInformation = 203;
|
||||||
|
var NoContent = 204;
|
||||||
|
var ResetContent = 205;
|
||||||
|
var PartialContent = 206;
|
||||||
|
var MultiStatus = 207;
|
||||||
|
var AlreadyReported = 208;
|
||||||
|
var IMUsed = 226;
|
||||||
|
var MultipleChoices = 300;
|
||||||
|
var MovedPermanently = 301;
|
||||||
|
var Found = 302;
|
||||||
|
var SeeOther = 303;
|
||||||
|
var NotModified = 304;
|
||||||
|
var UseProxy = 305;
|
||||||
|
var SwitchProxy = 306;
|
||||||
|
var TemporaryRedirect = 307;
|
||||||
|
var PermanentRedirect = 308;
|
||||||
|
var BadRequest = 400;
|
||||||
|
var Unauthorized = 401;
|
||||||
|
var PaymentRequired = 402;
|
||||||
|
var Forbidden = 403;
|
||||||
|
var NotFound = 404;
|
||||||
|
var MethodNotAllowed = 405;
|
||||||
|
var NotAcceptable = 406;
|
||||||
|
var ProxyAuthenticationRequired = 407;
|
||||||
|
var RequestTimeout = 408;
|
||||||
|
var Conflict = 409;
|
||||||
|
var Gone = 410;
|
||||||
|
var LengthRequired = 411;
|
||||||
|
var PreconditionFailed = 412;
|
||||||
|
var PayloadTooLarge = 413;
|
||||||
|
var URITooLong = 414;
|
||||||
|
var UnsupportedMediaType = 415;
|
||||||
|
var RangeNotSatisfiable = 416;
|
||||||
|
var ExpectationFailed = 417;
|
||||||
|
var ImATeapot = 418;
|
||||||
|
var MisdirectedRequest = 421;
|
||||||
|
var UnprocessableEntity = 422;
|
||||||
|
var Locked = 423;
|
||||||
|
var FailedDependency = 424;
|
||||||
|
var UpgradeRequired = 426;
|
||||||
|
var PreconditionRequired = 428;
|
||||||
|
var TooManyRequests = 429;
|
||||||
|
var RequestHeaderFieldsTooLarge = 431;
|
||||||
|
var UnavailableForLegalReasons = 451;
|
||||||
|
var InternalServerError = 500;
|
||||||
|
var NotImplemented = 501;
|
||||||
|
var BadGateway = 502;
|
||||||
|
var ServiceUnavailable = 503;
|
||||||
|
var GatewayTimeout = 504;
|
||||||
|
var HTTPVersionNotSupported = 505;
|
||||||
|
var VariantAlsoNegotiates = 506;
|
||||||
|
var InsufficientStorage = 507;
|
||||||
|
var LoopDetected = 508;
|
||||||
|
var NotExtended = 510;
|
||||||
|
var NetworkAuthenticationRequired = 511;
|
||||||
|
}
|
||||||
133
src/kernel/log/Log.hx
Normal file
133
src/kernel/log/Log.hx
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
package kernel.log;
|
||||||
|
|
||||||
|
import haxe.ds.ReadOnlyArray;
|
||||||
|
import haxe.macro.Context;
|
||||||
|
#if (webconsole && !macro)
|
||||||
|
import lib.Debug;
|
||||||
|
#end
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Central logging system.
|
||||||
|
**/
|
||||||
|
class Log {
|
||||||
|
private static inline final MAX_LINES:Int = 100;
|
||||||
|
|
||||||
|
public static var onLog(default, null):Signal<LogLine>;
|
||||||
|
|
||||||
|
private static final onLogTrigger:SignalTrigger<LogLine> = new SignalTrigger();
|
||||||
|
private static final logLines:Array<LogLine> = [];
|
||||||
|
|
||||||
|
@:allow(kernel.Init)
|
||||||
|
private static function init() {
|
||||||
|
onLog = onLogTrigger.asSignal();
|
||||||
|
|
||||||
|
#if debug
|
||||||
|
haxe.Log.trace = function(v:Dynamic, ?infos:haxe.PosInfos) {
|
||||||
|
Log._debug(v, infos.className);
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function _info(msg:Dynamic, ?origin:String) {
|
||||||
|
log({
|
||||||
|
level: Info,
|
||||||
|
message: Std.string(msg),
|
||||||
|
time: 0,
|
||||||
|
origin: origin
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function _warn(msg:Dynamic, ?origin:String) {
|
||||||
|
log({
|
||||||
|
level: Warn,
|
||||||
|
message: Std.string(msg),
|
||||||
|
time: 0,
|
||||||
|
origin: origin
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function _error(msg:Dynamic, ?origin:String) {
|
||||||
|
log({
|
||||||
|
level: Error,
|
||||||
|
message: Std.string(msg),
|
||||||
|
time: 0,
|
||||||
|
origin: origin
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function _debug(msg:Dynamic, ?origin:String) {
|
||||||
|
#if debug
|
||||||
|
log({
|
||||||
|
level: Debug,
|
||||||
|
message: Std.string(msg),
|
||||||
|
time: 0,
|
||||||
|
origin: origin
|
||||||
|
});
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function _silly(msg:Dynamic, ?origin:String) {
|
||||||
|
log({
|
||||||
|
level: Silly,
|
||||||
|
message: Std.string(msg),
|
||||||
|
time: 0,
|
||||||
|
origin: origin
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function log(line:LogLine) {
|
||||||
|
logLines.push(line);
|
||||||
|
|
||||||
|
if (logLines.length > MAX_LINES) {
|
||||||
|
logLines.shift();
|
||||||
|
}
|
||||||
|
onLogTrigger.trigger(line);
|
||||||
|
|
||||||
|
#if (webconsole && !macro)
|
||||||
|
Debug.logToWebconsole(line);
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getLines():ReadOnlyArray<LogLine> {
|
||||||
|
return logLines;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if macro
|
||||||
|
private static function getOrigin():String {
|
||||||
|
var localClass = Context.getLocalClass().get();
|
||||||
|
return localClass.name;
|
||||||
|
}
|
||||||
|
#end
|
||||||
|
|
||||||
|
public macro static function info(msg:ExprOf<Dynamic>) {
|
||||||
|
return macro {
|
||||||
|
Log._info(${msg}, $v{getOrigin()});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public macro static function warn(msg:ExprOf<Dynamic>) {
|
||||||
|
return macro {
|
||||||
|
Log._warn(${msg}, $v{getOrigin()});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public macro static function error(msg:ExprOf<Dynamic>) {
|
||||||
|
return macro {
|
||||||
|
Log._error(${msg}, $v{getOrigin()});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public macro static function debug(msg:ExprOf<Dynamic>) {
|
||||||
|
return macro {
|
||||||
|
Log._debug(${msg}, $v{getOrigin()});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public macro static function silly(msg:ExprOf<Dynamic>) {
|
||||||
|
return macro {
|
||||||
|
Log._silly(${msg}, $v{getOrigin()});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/kernel/log/LogLevel.hx
Normal file
9
src/kernel/log/LogLevel.hx
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package kernel.log;
|
||||||
|
|
||||||
|
enum LogLevel {
|
||||||
|
Info;
|
||||||
|
Warn;
|
||||||
|
Error;
|
||||||
|
Debug;
|
||||||
|
Silly;
|
||||||
|
}
|
||||||
8
src/kernel/log/LogLine.hx
Normal file
8
src/kernel/log/LogLine.hx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package kernel.log;
|
||||||
|
|
||||||
|
typedef LogLine = {
|
||||||
|
level:LogLevel,
|
||||||
|
message:String,
|
||||||
|
time:Int,
|
||||||
|
?origin:String,
|
||||||
|
}
|
||||||
19
src/kernel/net/INetworkInterface.hx
Normal file
19
src/kernel/net/INetworkInterface.hx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package kernel.net;
|
||||||
|
|
||||||
|
import kernel.net.Package.GenericPackage;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
A object that is able to send and receive messages.
|
||||||
|
**/
|
||||||
|
interface INetworkInterface {
|
||||||
|
public function listen(chan:Int):Void;
|
||||||
|
public function close(chan:Int):Void;
|
||||||
|
public function isListening(chan:Int):Bool;
|
||||||
|
public function closeAll():Void;
|
||||||
|
public function send(chan:Int, replyChan:Int, payload:Any):Void;
|
||||||
|
public function name():String;
|
||||||
|
public function getBaseRoutingCost():Int;
|
||||||
|
public var onMessage(default, null):Signal<{pack:GenericPackage, ?dist:Float}>;
|
||||||
|
}
|
||||||
56
src/kernel/net/Loopback.hx
Normal file
56
src/kernel/net/Loopback.hx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package kernel.net;
|
||||||
|
|
||||||
|
import kernel.net.Package.GenericPackage;
|
||||||
|
import kernel.log.Log;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Virtual network interface that handles packages to the same id as the sender.
|
||||||
|
**/
|
||||||
|
class Loopback implements INetworkInterface {
|
||||||
|
public static final instance:Loopback = new Loopback();
|
||||||
|
|
||||||
|
public var onMessage(default, null):Signal<{pack:GenericPackage, dist:Null<Float>}>;
|
||||||
|
|
||||||
|
private final onMessageTrigger:SignalTrigger<{pack:GenericPackage, dist:Null<Float>}> = Signal.trigger();
|
||||||
|
private var openChans:Array<Int> = [];
|
||||||
|
|
||||||
|
private function new() {
|
||||||
|
this.onMessage = onMessageTrigger.asSignal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function listen(chan:Int) {
|
||||||
|
if (!this.openChans.contains(chan)) {
|
||||||
|
this.openChans.push(chan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close(chan:Int) {
|
||||||
|
this.openChans.remove(chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isListening(chan:Int):Bool {
|
||||||
|
return this.openChans.contains(chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function closeAll() {
|
||||||
|
this.openChans = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function send(chan:Int, replyChan:Int, payload:Any) {
|
||||||
|
if (this.openChans.contains(chan)) {
|
||||||
|
this.onMessageTrigger.trigger({pack: payload, dist: null});
|
||||||
|
} else {
|
||||||
|
Log.silly("Loopback got package on non open channel");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function name():String {
|
||||||
|
return "loopback";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBaseRoutingCost():Int {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,146 +1,179 @@
|
|||||||
package kernel.net;
|
package kernel.net;
|
||||||
|
|
||||||
|
import kernel.net.Package.GenericPackage;
|
||||||
|
import kernel.gps.GPS;
|
||||||
|
import haxe.ds.ReadOnlyArray;
|
||||||
|
import kernel.net.Package.NetworkID;
|
||||||
import kernel.peripherals.Peripherals.Peripheral;
|
import kernel.peripherals.Peripherals.Peripheral;
|
||||||
import kernel.Log;
|
import kernel.log.Log;
|
||||||
import kernel.KernelEvents;
|
|
||||||
import haxe.Exception;
|
|
||||||
import util.Promise;
|
|
||||||
import kernel.Timer;
|
import kernel.Timer;
|
||||||
import util.EventBus;
|
|
||||||
import cc.OS;
|
import cc.OS;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
using Lambda;
|
using Lambda;
|
||||||
using util.Extender.LambdaExtender;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Class responsible for everything network related.
|
Class responsible for everything network related.
|
||||||
Used to send and recceive packages.
|
Used to send and recceive packages.
|
||||||
**/
|
**/
|
||||||
class Net{
|
class Net {
|
||||||
public static var instance:Net;
|
/**
|
||||||
|
Depends on: KernelEvents
|
||||||
|
**/
|
||||||
|
public static inline final BRODCAST_PORT:Int = 65533;
|
||||||
|
|
||||||
|
public static inline final MESSAGE_TIMEOUT:Int = 3;
|
||||||
|
public static inline final DEFAULT_TTL:Int = 10;
|
||||||
|
|
||||||
|
public static final networkID:NetworkID = OS.getComputerID();
|
||||||
|
private static final responseBus:Map<Int, Callback<Outcome<GenericPackage, Error>>> = new Map();
|
||||||
|
private static final protoHandlers:Map<String, Callback<GenericPackage>> = new Map();
|
||||||
|
private static var interfaces:Array<INetworkInterface>;
|
||||||
|
|
||||||
@:allow(kernel.Init)
|
@:allow(kernel.Init)
|
||||||
private function new () {
|
private static function init() {
|
||||||
KernelEvents.instance.on("modem_message",(params)->{
|
interfaces = [for (e in Peripheral.getAllModems()) e]; // TODO: is this the way to do it?
|
||||||
var pack = Package.fromEvent(params);
|
interfaces.push(Loopback.instance);
|
||||||
handelIncomming(pack,params[1]);
|
|
||||||
|
for (interf in interfaces) {
|
||||||
|
setupInterf(interf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle when a modem has been removed
|
||||||
|
KernelEvents.onPeripheralDetach.handle((addr) -> {
|
||||||
|
var idx = interfaces.findIndex((i) -> {
|
||||||
|
return i.name() == addr;
|
||||||
});
|
});
|
||||||
allModems = Peripheral.instance.getModems();
|
if (idx == -1) {
|
||||||
open();
|
return;
|
||||||
discoverNeighbors();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static inline final BRODCAST_PORT:Int = 65533;
|
Log.debug('Removed modem on addr: $addr');
|
||||||
public static inline final MESSAGE_TIMEOUT:Int = 3;
|
|
||||||
|
|
||||||
private var networkID:Int = OS.getComputerID();
|
interfaces.splice(idx, 1);
|
||||||
private var responseBus: util.EventBus<Package> = new EventBus();
|
Routing.removeInterface(addr);
|
||||||
private var protoHandlers: Map<String,Package -> Void> = new Map();
|
|
||||||
private var allModems:Array<kernel.peripherals.Modem>;
|
|
||||||
private var routingTable: Map<Int,kernel.peripherals.Modem> = new Map();
|
|
||||||
|
|
||||||
private function handelIncomming(pack: Package, ?addr:String) {
|
|
||||||
switch pack.type {
|
|
||||||
case Data(_):
|
|
||||||
routeTo(pack);
|
|
||||||
case DataNoResponse(_):
|
|
||||||
routeTo(pack);
|
|
||||||
case Response | RouteDiscoverResponse:
|
|
||||||
responseBus.emit(Std.string(pack.msgID),pack);
|
|
||||||
case RouteDiscover:
|
|
||||||
handleRoute(pack,addr);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function newRoutPackage(): Package {
|
|
||||||
var pack: Package = {
|
|
||||||
type: RouteDiscover,
|
|
||||||
toID: BRODCAST_PORT,
|
|
||||||
msgID: generateMessageID(),
|
|
||||||
fromID: networkID,
|
|
||||||
data: null,
|
|
||||||
}
|
|
||||||
|
|
||||||
return pack;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function discoverNeighbors() {
|
|
||||||
for (modem in allModems) {
|
|
||||||
var pack = newRoutPackage();
|
|
||||||
|
|
||||||
var timeout: Timer = null;
|
|
||||||
var responeListner = responseBus.on(Std.string(pack.msgID),pack -> {
|
|
||||||
addRoute(pack.fromID,modem.addr);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
timeout = new Timer(MESSAGE_TIMEOUT,() -> {
|
// Handle when a new modem is connected
|
||||||
responseBus.removeListner(responeListner);
|
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);
|
||||||
});
|
});
|
||||||
|
|
||||||
modem.transmit(BRODCAST_PORT,OS.getComputerID(),pack);
|
setupPingHandle();
|
||||||
}
|
|
||||||
|
Routing.setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function handleRoute(pack: Package, addr: String) {
|
private static function setupPingHandle() {
|
||||||
addRoute(pack.fromID,addr);
|
registerProto("icmp", pack -> {
|
||||||
|
switch pack.data.type {
|
||||||
// Respond to peer
|
case "ping":
|
||||||
var response: Package = {
|
respondTo(pack, "pong");
|
||||||
toID: pack.fromID,
|
case "died":
|
||||||
fromID: OS.getComputerID(),
|
// If we get a "died" message from a node when one of our packages ttl hits 0
|
||||||
msgID: pack.msgID,
|
// the `data.msgId` prop is the message id
|
||||||
type: RouteDiscoverResponse,
|
var msgID:Int = pack.data.msgID;
|
||||||
data: null
|
if (responseBus.exists(msgID)) {
|
||||||
|
responseBus[msgID].invoke(Outcome.Failure(new Error("TTL reached 0")));
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
for (reponseModem in allModems.filter(m -> m.addr == addr)) {
|
Log.silly('Unknown icmp message: ${pack.data}');
|
||||||
reponseModem.transmit(pack.fromID,networkID,response);
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
private function addRoute(toID: Int,addr: String) {
|
|
||||||
Log.debug("Added new route to "+toID+" via "+addr);
|
|
||||||
routingTable.set(toID,allModems.find(item -> item.addr == addr));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAllNeighbors(): Array<Int> {
|
|
||||||
return routingTable.mapi((index, item) -> index);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function generateMessageID(): Int {
|
|
||||||
return Std.random(2147483647); // TODO: better uniqe number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Open all wireless and wired modem.
|
Enables the interface to receive messages.
|
||||||
**/
|
**/
|
||||||
private function open() {
|
private static function setupInterf(interf:INetworkInterface) {
|
||||||
for (m in allModems) {
|
interf.onMessage.handle(e -> handle(e.pack, interf, e.dist));
|
||||||
m.open(networkID);
|
interf.listen(networkID);
|
||||||
m.open(BRODCAST_PORT);
|
interf.listen(BRODCAST_PORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Called when a new package comes in.
|
||||||
|
**/
|
||||||
|
private static function handle(pack:GenericPackage, interf:INetworkInterface, ?dist:Float) {
|
||||||
|
if (pack.toID == networkID || pack.toID == Net.BRODCAST_PORT) {
|
||||||
|
switch pack.type {
|
||||||
|
case Data(_) | DataNoResponse(_):
|
||||||
|
// Let a local proccess handle it
|
||||||
|
routeToProto(pack);
|
||||||
|
case Response:
|
||||||
|
// Got a response to a send message. Invoke the callback
|
||||||
|
if (responseBus.exists(pack.msgID)) {
|
||||||
|
responseBus[pack.msgID].invoke(Outcome.Success(pack));
|
||||||
|
}
|
||||||
|
case RouteDiscover(_) | RouteDiscoverResponse(_) | RouteDiscoverUpdate(_):
|
||||||
|
// Delegate to Routing
|
||||||
|
Routing.handleRoutePackage(cast pack, interf);
|
||||||
|
case GPSRequest | GPSResponse(_):
|
||||||
|
if (dist == null) {
|
||||||
|
Log.silly("Got a GPS package but no distance was provided");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Delegate to GPS
|
||||||
|
GPS.handlePackage(cast pack, dist, interf);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// New message received but its not ment for us. Forward if possible.
|
||||||
|
forwardPackage(pack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function generateMessageID():Int {
|
||||||
|
return Std.random(2147483647); // TODO: better uniqe number
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Send a message. Dont care if its reaches its destination nor it has a response.
|
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:GenericPackage = {
|
||||||
var pack: Package = {
|
|
||||||
toID: dest,
|
toID: dest,
|
||||||
fromID: networkID,
|
fromID: networkID,
|
||||||
msgID: generateMessageID(),
|
msgID: generateMessageID(),
|
||||||
type: DataNoResponse(proto),
|
type: DataNoResponse(proto),
|
||||||
data: data
|
data: data,
|
||||||
|
ttl: Net.DEFAULT_TTL,
|
||||||
}
|
}
|
||||||
|
|
||||||
sendRaw(pack);
|
sendRaw(pack);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function respondTo(pack: Package,data: Dynamic) {
|
private static function forwardPackage(pack:GenericPackage) {
|
||||||
if (pack.type.match(DataNoResponse(_))){
|
if (pack.ttl == 0) {
|
||||||
Log.warn("Responed to a no response package. Ignoring");
|
if (pack.type.match(Data(_))) {
|
||||||
|
// If the package is a data package and the ttl hits 0
|
||||||
|
// we send a "died" message to the sender
|
||||||
|
sendAndForget(pack.fromID, "icmp", {type: "died", msgID: pack.msgID});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop package
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pack.ttl--;
|
||||||
|
|
||||||
|
if (!sendRaw(pack)) {
|
||||||
|
// Cant forward
|
||||||
|
Log.silly('Received a message that could not be forwarded to ${pack.toID}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function respondTo(pack:GenericPackage, data:Dynamic) {
|
||||||
|
if (!pack.type.match(Data(_))) {
|
||||||
|
Log.warn("Responding to a package that does require responding. Ignoring.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,76 +182,143 @@ class Net{
|
|||||||
sendRaw(response);
|
sendRaw(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function routeTo(pack: Package) {
|
/**
|
||||||
var proto: String = switch pack.type {
|
Send to package to the localy register handler based on the proto
|
||||||
|
**/
|
||||||
|
private static function routeToProto(pack:GenericPackage) {
|
||||||
|
var proto = switch pack.type {
|
||||||
case Data(proto):
|
case Data(proto):
|
||||||
proto;
|
proto;
|
||||||
case DataNoResponse(proto):
|
case DataNoResponse(proto):
|
||||||
proto;
|
proto;
|
||||||
case _:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!protoHandlers.exists(proto) && protoHandlers[proto] != null){
|
if (!protoHandlers.exists(proto)) {
|
||||||
|
Log.warn('Trying to route package to proto: $proto but nothing was register');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
protoHandlers[proto](pack);
|
protoHandlers[proto].invoke(pack);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function sendRaw(pack: Package){
|
/**
|
||||||
|
Just send the package to the right modem.
|
||||||
if (pack.toID == networkID){
|
Returns true if message was send
|
||||||
// Loopback
|
**/
|
||||||
handelIncomming(pack);
|
private static function sendRaw(pack:GenericPackage):Bool {
|
||||||
}else{
|
var route = Routing.getRouteToID(pack.toID);
|
||||||
if (routingTable.exists(pack.toID)){
|
if (route == null) {
|
||||||
routingTable[pack.toID].transmit(pack.toID,pack.fromID,pack);
|
return false;
|
||||||
}else{
|
|
||||||
// Route not found
|
|
||||||
// TODO: forward package or report not reachable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendAndAwait(dest: Int,proto:String,data: Dynamic): Promise<Package> {
|
route.interf.send(route.rep, networkID, pack);
|
||||||
return new Promise<Package>((resolve, reject) -> {
|
return true;
|
||||||
var pack: Package = {
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Send a message and wait for a response.
|
||||||
|
**/
|
||||||
|
public static function sendAndAwait<T>(dest:NetworkID, proto:String, data:T):Promise<GenericPackage> {
|
||||||
|
return new Promise<GenericPackage>((resolve, reject) -> {
|
||||||
|
var pack:GenericPackage = {
|
||||||
toID: dest,
|
toID: dest,
|
||||||
fromID: networkID,
|
fromID: networkID,
|
||||||
msgID: generateMessageID(),
|
msgID: generateMessageID(),
|
||||||
type: Data(proto),
|
type: Data(proto),
|
||||||
data: data
|
data: data,
|
||||||
|
ttl: Net.DEFAULT_TTL,
|
||||||
}
|
}
|
||||||
|
|
||||||
var timeout: Timer = null;
|
var timeout:Timer = null;
|
||||||
var responeListner = responseBus.once(Std.string(pack.msgID),p -> {
|
|
||||||
resolve(p);
|
responseBus[pack.msgID] = ((reponse:Outcome<GenericPackage, Error>) -> {
|
||||||
if (timeout != null){
|
switch reponse {
|
||||||
|
case Success(pack):
|
||||||
|
resolve(pack);
|
||||||
|
case Failure(err):
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always remove the timeout
|
||||||
|
if (timeout != null) {
|
||||||
timeout.cancle();
|
timeout.cancle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
responseBus.remove(pack.msgID);
|
||||||
});
|
});
|
||||||
|
|
||||||
timeout = new Timer(MESSAGE_TIMEOUT,() -> {
|
timeout = new Timer(MESSAGE_TIMEOUT, () -> {
|
||||||
responseBus.removeListner(responeListner);
|
responseBus.remove(pack.msgID);
|
||||||
reject(new Exception("Timeout"));
|
reject(new Error(InternalError, "Message timeout"));
|
||||||
});
|
});
|
||||||
|
|
||||||
sendRaw(pack);
|
if (!sendRaw(pack)) {
|
||||||
|
reject(new Error("ID unreachable"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// No callback link for you
|
||||||
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function registerProto(proto: String,cb: Package -> Void) {
|
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
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
protoHandlers[proto] = cb;
|
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.
|
||||||
|
**/
|
||||||
|
public static function ping(toID:NetworkID):Promise<Noise> {
|
||||||
|
return new Promise<Noise>((resolve, reject) -> {
|
||||||
|
sendAndAwait(toID, "icmp", {type: "ping"}).handle(pack -> {
|
||||||
|
switch pack {
|
||||||
|
case Success(_):
|
||||||
|
resolve(Noise);
|
||||||
|
case Failure(err):
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getActiveProtocols():ReadOnlyArray<String> {
|
||||||
|
var arr = new Array<String>();
|
||||||
|
|
||||||
|
for (proto in protoHandlers.keys()) {
|
||||||
|
arr.push(proto);
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@:allow(kernel.gps.GPS)
|
||||||
|
private static function brodcastGPSRequest() {
|
||||||
|
var pack:Package<Noise> = {
|
||||||
|
fromID: networkID,
|
||||||
|
toID: Net.BRODCAST_PORT,
|
||||||
|
ttl: 0, // Prevent forwarding
|
||||||
|
msgID: generateMessageID(),
|
||||||
|
type: GPSRequest,
|
||||||
|
data: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (modem in Peripheral.getAllModems()) {
|
||||||
|
if (!modem.isWireless())
|
||||||
|
continue;
|
||||||
|
modem.send(Net.BRODCAST_PORT, networkID, pack);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,55 +1,57 @@
|
|||||||
package kernel.net;
|
package kernel.net;
|
||||||
|
|
||||||
|
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();
|
RouteDiscover(reachableIDs:Array<{id:NetworkID, cost:Int}>);
|
||||||
RouteDiscoverResponse();
|
RouteDiscoverResponse(reachableIDs:Array<{id:NetworkID, cost:Int}>);
|
||||||
|
RouteDiscoverUpdate(reachableIDs:Array<{id:NetworkID, cost:Int}>);
|
||||||
|
GPSResponse(x:Float, y:Float, z:Float);
|
||||||
|
GPSRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Representing a network package.
|
Representing a network package.
|
||||||
**/
|
**/
|
||||||
@:structInit class Package {
|
@:structInit class Package<T> {
|
||||||
public final fromID:Int;
|
public final fromID:NetworkID;
|
||||||
public final toID:Int;
|
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 function new(fromID:NetworkID, toID:NetworkID, msgID:Int, type:PackageTypes, data:T, ttl:Int) {
|
||||||
Parse package from an `modem_message` event.
|
this.fromID = fromID;
|
||||||
**/
|
this.toID = toID;
|
||||||
public static function fromEvent(params: Array<Dynamic>): Package {
|
this.msgID = msgID;
|
||||||
var payload = params[4];
|
this.type = type;
|
||||||
|
this.data = data;
|
||||||
return {
|
this.ttl = ttl;
|
||||||
fromID: params[3],
|
|
||||||
toID: params[2],
|
|
||||||
msgID: payload.msgID,
|
|
||||||
type: payload.type,
|
|
||||||
data: payload.data,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
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,
|
||||||
msgID: msgID,
|
msgID: msgID,
|
||||||
type: Response,
|
type: Response,
|
||||||
data: newData
|
data: newData,
|
||||||
|
ttl: Net.DEFAULT_TTL,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
204
src/kernel/net/Routing.hx
Normal file
204
src/kernel/net/Routing.hx
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
package kernel.net;
|
||||||
|
|
||||||
|
import kernel.log.Log;
|
||||||
|
import kernel.peripherals.Peripherals.Peripheral;
|
||||||
|
import kernel.net.INetworkInterface;
|
||||||
|
import kernel.net.Package.NetworkID;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
@:structInit typedef Route = {
|
||||||
|
interf:INetworkInterface,
|
||||||
|
rep:NetworkID,
|
||||||
|
cost:Int
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Manages network routing.
|
||||||
|
**/
|
||||||
|
class Routing {
|
||||||
|
/**
|
||||||
|
Depends on: Peripheral
|
||||||
|
**/
|
||||||
|
public static inline final UPDATE_WAIT_TIME:Float = 1;
|
||||||
|
|
||||||
|
public static var onNewNeigbor(default, null):Signal<Int>;
|
||||||
|
|
||||||
|
private static final onNewNeigborTrigger:SignalTrigger<NetworkID> = Signal.trigger();
|
||||||
|
private static final routingTable:Map<NetworkID, Route> = new Map();
|
||||||
|
private static var routeUpdateInQueue:Bool = false;
|
||||||
|
|
||||||
|
@:allow(kernel.Init)
|
||||||
|
private static function init() {
|
||||||
|
onNewNeigbor = onNewNeigborTrigger.asSignal();
|
||||||
|
}
|
||||||
|
|
||||||
|
@:allow(kernel.net.Net)
|
||||||
|
private static function setup() {
|
||||||
|
routingTable.set(Net.networkID, {interf: Loopback.instance, cost: 0, rep: Net.networkID});
|
||||||
|
brodcastRoutingTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Prepares an brodcast of the current routing table. If a brodcast is already in queue it will be ignored.
|
||||||
|
After UPDATE_WAIT_TIME seconds the routing table will be brodcasted. This is done to prevent spamming the network.
|
||||||
|
**/
|
||||||
|
private static function prepareRouteUpdate() {
|
||||||
|
if (routeUpdateInQueue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
routeUpdateInQueue = true;
|
||||||
|
|
||||||
|
new Timer(UPDATE_WAIT_TIME, () -> {
|
||||||
|
brodcastRoutingTable();
|
||||||
|
routeUpdateInQueue = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Brodcast the current routing table to all peers.
|
||||||
|
**/
|
||||||
|
private static function brodcastRoutingTable() {
|
||||||
|
var pack = newRoutDiscoverPackage();
|
||||||
|
|
||||||
|
for (modem in Peripheral.getAllModems()) {
|
||||||
|
modem.send(Net.BRODCAST_PORT, Net.networkID, pack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Handle incomming packages that involve route discovery.
|
||||||
|
**/
|
||||||
|
@:allow(kernel.net.Net)
|
||||||
|
private static function handleRoutePackage(pack:Package<Noise>, interf:INetworkInterface):Void {
|
||||||
|
addPossibleRoute(pack.fromID, interf, 0, pack.fromID);
|
||||||
|
|
||||||
|
switch pack.type {
|
||||||
|
case RouteDiscoverResponse(routes):
|
||||||
|
// A request for routes has been answerd
|
||||||
|
for (route in routes) {
|
||||||
|
addPossibleRoute(route.id, interf, route.cost, pack.fromID);
|
||||||
|
}
|
||||||
|
case RouteDiscover(routes):
|
||||||
|
// Received a request for available routes
|
||||||
|
for (route in routes) {
|
||||||
|
addPossibleRoute(route.id, interf, route.cost, pack.fromID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Respond to peer
|
||||||
|
var response:Package<Noise> = {
|
||||||
|
toID: pack.fromID,
|
||||||
|
fromID: Net.networkID,
|
||||||
|
msgID: null,
|
||||||
|
type: RouteDiscoverResponse(genRouteList()),
|
||||||
|
data: null,
|
||||||
|
ttl: 0, // Prevent forwarding
|
||||||
|
}
|
||||||
|
|
||||||
|
interf.send(response.toID, Net.networkID, response);
|
||||||
|
case RouteDiscoverUpdate(routes):
|
||||||
|
// Received an update of routes
|
||||||
|
for (route in routes) {
|
||||||
|
addPossibleRoute(route.id, interf, route.cost, pack.fromID);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
Log.silly("Expected package to be a Route discover package");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Generate a list of routes to send to a peer based on the current routing table.
|
||||||
|
**/
|
||||||
|
private static function genRouteList():Array<{id:NetworkID, cost:Int}> {
|
||||||
|
var routes:Array<{id:NetworkID, cost:Int}> = [];
|
||||||
|
for (k => v in routingTable) {
|
||||||
|
routes.push({
|
||||||
|
id: k,
|
||||||
|
cost: v.cost
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Create a new packate to brodcast our routes.
|
||||||
|
**/
|
||||||
|
private static function newRoutDiscoverPackage():Package<Noise> {
|
||||||
|
var pack:Package<Noise> = {
|
||||||
|
type: RouteDiscover(genRouteList()),
|
||||||
|
toID: Net.BRODCAST_PORT,
|
||||||
|
msgID: null,
|
||||||
|
fromID: Net.networkID,
|
||||||
|
data: null,
|
||||||
|
ttl: 0, // Prevent forwarding
|
||||||
|
}
|
||||||
|
|
||||||
|
return pack;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Called when a route to a client has been disoverd.
|
||||||
|
Its possible to be called multiple times with the same id but different addr.
|
||||||
|
**/
|
||||||
|
private static function addPossibleRoute(toID:Int, interf:INetworkInterface, cost:Int, rep:NetworkID) {
|
||||||
|
if (toID == Net.networkID) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fullCost = cost + interf.getBaseRoutingCost();
|
||||||
|
|
||||||
|
if (routingTable.exists(toID)) {
|
||||||
|
if (routingTable[toID].cost > fullCost) {
|
||||||
|
routingTable[toID] = {interf: interf, cost: cost + interf.getBaseRoutingCost(), rep: rep};
|
||||||
|
prepareRouteUpdate();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
routingTable[toID] = {interf: interf, cost: cost + interf.getBaseRoutingCost(), rep: rep};
|
||||||
|
onNewNeigborTrigger.trigger(toID);
|
||||||
|
prepareRouteUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRouteToID(networkID:NetworkID):{interf:INetworkInterface, rep:NetworkID} {
|
||||||
|
if (networkID == Net.networkID) {
|
||||||
|
return {interf: Loopback.instance, rep: Net.networkID};
|
||||||
|
}
|
||||||
|
|
||||||
|
var route:Null<Route> = routingTable[networkID];
|
||||||
|
|
||||||
|
if (route == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return {interf: route.interf, rep: route.rep};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRouteTable():Map<NetworkID, Route> {
|
||||||
|
return routingTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Call when an interface is no longer available.
|
||||||
|
Will rebrodcast routing requests.
|
||||||
|
**/
|
||||||
|
@:allow(kernel.net.Net)
|
||||||
|
private static function removeInterface(addr:String) {
|
||||||
|
// Remove the interface from the routing table
|
||||||
|
for (to => modem in routingTable) {
|
||||||
|
if (modem.interf.name() == addr) {
|
||||||
|
routingTable.remove(to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since some routes to some host could be lost
|
||||||
|
// we rebrodcast on all interfaces again
|
||||||
|
brodcastRoutingTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@:allow(kernel.net.Net)
|
||||||
|
private static function addInterface(iface:INetworkInterface) {
|
||||||
|
var pack = newRoutDiscoverPackage();
|
||||||
|
iface.send(Net.BRODCAST_PORT, Net.networkID, pack);
|
||||||
|
}
|
||||||
|
}
|
||||||
181
src/kernel/peripherals/BigReactor.hx
Normal file
181
src/kernel/peripherals/BigReactor.hx
Normal 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
|
||||||
|
}
|
||||||
50
src/kernel/peripherals/Computer.hx
Normal file
50
src/kernel/peripherals/Computer.hx
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
102
src/kernel/peripherals/Drive.hx
Normal file
102
src/kernel/peripherals/Drive.hx
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package kernel.peripherals;
|
||||||
|
|
||||||
|
import cc.Peripheral;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
class Drive implements IPeripheral {
|
||||||
|
public static inline final TYPE_NAME:String = "drive";
|
||||||
|
|
||||||
|
public final onDiskInsert:Signal<Noise>;
|
||||||
|
public final onDiskEject:Signal<Noise>;
|
||||||
|
|
||||||
|
private final addr:String;
|
||||||
|
private final native:cc.periphs.Disk;
|
||||||
|
private final onDiskInsertTrigger:SignalTrigger<Noise> = Signal.trigger();
|
||||||
|
private final onDiskEjectTrigger:SignalTrigger<Noise> = Signal.trigger();
|
||||||
|
|
||||||
|
@:allow(kernel.peripherals)
|
||||||
|
private function new(addr:String) {
|
||||||
|
this.addr = addr;
|
||||||
|
this.native = Peripheral.wrap(addr);
|
||||||
|
|
||||||
|
this.onDiskInsert = this.onDiskInsertTrigger.asSignal();
|
||||||
|
this.onDiskEject = this.onDiskEjectTrigger.asSignal();
|
||||||
|
|
||||||
|
KernelEvents.onDisk.handle((addr) -> {
|
||||||
|
if (addr == this.addr) {
|
||||||
|
this.onDiskInsertTrigger.trigger(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
KernelEvents.onDiskEject.handle((addr) -> {
|
||||||
|
if (addr == this.addr) {
|
||||||
|
this.onDiskEjectTrigger.trigger(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAddr():String {
|
||||||
|
return this.addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType():String {
|
||||||
|
return TYPE_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function isDiskPresent():Bool {
|
||||||
|
return this.native.isDiskPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
The label of the disk, or `null` if either no disk is inserted or the disk doesn't have a label.
|
||||||
|
**/
|
||||||
|
public inline function getDiskLabel():Null<String> {
|
||||||
|
return this.native.getDiskLabel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function clearDiskLabel() {
|
||||||
|
this.native.setDiskLabel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function setDiskLabel(label:String):Null<Error> {
|
||||||
|
try {
|
||||||
|
this.native.setDiskLabel(label);
|
||||||
|
return null;
|
||||||
|
} catch (e:Dynamic) {
|
||||||
|
return new Error("Invalid label");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function hasData():Bool {
|
||||||
|
return this.native.hasData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function hasAudio():Bool {
|
||||||
|
return this.native.hasAudio();
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getMountPath():Null<String> {
|
||||||
|
return this.getMountPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getAudioTitle():Null<String> {
|
||||||
|
return this.native.getAudioTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function playAudio() {
|
||||||
|
this.native.playAudio();
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function stopAudio() {
|
||||||
|
this.native.stopAudio();
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function ejectDisk() {
|
||||||
|
this.native.ejectDisk();
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getDiskID():Int {
|
||||||
|
return this.native.getDiskID();
|
||||||
|
}
|
||||||
|
}
|
||||||
491
src/kernel/peripherals/DroneInterface.hx
Normal file
491
src/kernel/peripherals/DroneInterface.hx
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/kernel/peripherals/EnergyStorage.hx
Normal file
29
src/kernel/peripherals/EnergyStorage.hx
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
76
src/kernel/peripherals/GeoScanner.hx
Normal file
76
src/kernel/peripherals/GeoScanner.hx
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/kernel/peripherals/IPeripheral.hx
Normal file
6
src/kernel/peripherals/IPeripheral.hx
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package kernel.peripherals;
|
||||||
|
|
||||||
|
interface IPeripheral {
|
||||||
|
public function getAddr():String;
|
||||||
|
public function getType():String;
|
||||||
|
}
|
||||||
@@ -1,31 +1,123 @@
|
|||||||
package kernel.peripherals;
|
package kernel.peripherals;
|
||||||
|
|
||||||
class Item {
|
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 Inventory {
|
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 {
|
public function size():Int {
|
||||||
|
return Peripheral.call(this.addr, "size");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function list(): Map<Int,Item> {
|
/**
|
||||||
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getItemDetail(slot: Int): Item {
|
return rtn;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function pushItems(toName: String, fromSlot: Int, ?limit:Int, toSlot: Int): Int {
|
/**
|
||||||
|
Get detailed information about an item.
|
||||||
|
**/
|
||||||
|
public function getItemDetail(slot:Int):DetailedItemInfo {
|
||||||
|
return new DetailedItemInfo(Peripheral.call(this.addr, "getItemDetail", slot + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function pullItems(fromName: String, fromSlot: Int, ?limit:Int, ?toSlot: Int): Int {
|
/**
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,101 +1,88 @@
|
|||||||
package kernel.peripherals;
|
package kernel.peripherals;
|
||||||
|
|
||||||
import haxe.exceptions.NotImplementedException;
|
import cc.Peripheral;
|
||||||
import haxe.Exception;
|
import kernel.log.Log;
|
||||||
|
import kernel.net.Package;
|
||||||
|
import kernel.net.INetworkInterface;
|
||||||
|
|
||||||
using lua.Table;
|
using tink.CoreApi;
|
||||||
|
|
||||||
class Modem {
|
class Modem implements INetworkInterface implements IPeripheral {
|
||||||
private final nativ:cc.periphs.Modem.Modem;
|
public static inline final TYPE_NAME:String = "modem";
|
||||||
|
|
||||||
public final addr:String;
|
public final addr:String;
|
||||||
|
public var onMessage(default, null):Signal<{pack:GenericPackage, dist:Null<Float>}>;
|
||||||
|
|
||||||
|
private final onMessageTrigger:SignalTrigger<{pack:GenericPackage, dist:Null<Float>}> = Signal.trigger();
|
||||||
|
private final native:cc.periphs.Modem.Modem;
|
||||||
|
|
||||||
@:allow(kernel.peripherals)
|
@:allow(kernel.peripherals)
|
||||||
private function new(nativePeripherals: cc.periphs.Modem.Modem,addr: String) {
|
private function new(addr:String) {
|
||||||
this.nativ = nativePeripherals;
|
this.onMessage = onMessageTrigger.asSignal();
|
||||||
|
this.native = Peripheral.wrap(addr);
|
||||||
this.addr = addr;
|
this.addr = addr;
|
||||||
|
|
||||||
|
KernelEvents.onModemMessage.handle(params -> {
|
||||||
|
try {
|
||||||
|
if (params.addr == this.addr) {
|
||||||
|
var pack:GenericPackage = {
|
||||||
|
fromID: params.message.fromID,
|
||||||
|
toID: params.message.toID,
|
||||||
|
msgID: params.message.msgID,
|
||||||
|
type: params.message.type,
|
||||||
|
data: params.message.data,
|
||||||
|
ttl: params.message.ttl,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.onMessageTrigger.trigger({pack: pack, dist: params.distance});
|
||||||
|
}
|
||||||
|
} catch (e:Dynamic) {
|
||||||
|
Log.error("Error while parsing modem message");
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function open(chan: Int) {
|
public function isWireless():Bool {
|
||||||
nativ.open(chan);
|
return native.isWireless();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isOpen(chan: Int): Bool {
|
public function listen(chan:Int) {
|
||||||
return nativ.isOpen(chan);
|
native.open(chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function close(chan: Int) {
|
public function isListening(chan:Int):Bool {
|
||||||
nativ.close(chan);
|
return native.isOpen(chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function closAll() {
|
public function closeAll() {
|
||||||
nativ.closeAll();
|
native.closeAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function transmit(chan: Int,replyChan: Int,payload: Any) {
|
public function send(chan:Int, replyChan:Int, payload:Any) {
|
||||||
nativ.transmit(chan,replyChan,payload);
|
native.transmit(chan, replyChan, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isWireless(): Bool {
|
public function name():String {
|
||||||
return nativ.isWireless();
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getNamesRemote():Array<String> {
|
public function close(chan:Int) {
|
||||||
if (isWireless()){
|
native.close(chan);
|
||||||
throw new Exception("'getNamesRemote' only works with wired modems");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nativ.getNamesRemote().toArray();
|
public function getAddr():String {
|
||||||
|
return this.addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isPresentRemote(name: String): Bool {
|
public function getType():String {
|
||||||
if (isWireless()){
|
return TYPE_NAME;
|
||||||
throw new Exception("'isPresentRemote' only works with wired modems");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nativ.isPresentRemote(name);
|
public function getBaseRoutingCost():Int {
|
||||||
|
if (this.native.isWireless()) {
|
||||||
|
return 2; // Prefere messages over cable
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTypeRemote(name: String): String {
|
|
||||||
if (isWireless()){
|
|
||||||
throw new Exception("'getTypeRemote' only works with wired modems");
|
|
||||||
}
|
|
||||||
|
|
||||||
return nativ.getTypeRemote(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function hasTypeRemote(name:String, type:String):Bool {
|
|
||||||
if (isWireless()){
|
|
||||||
throw new Exception("'hasTypeRemote' only works with wired modems");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Missing in upstream API
|
|
||||||
throw new haxe.exceptions.NotImplementedException();
|
|
||||||
// return nativ.hasRemoteType(name,type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getMethodsRemote(name: String): Array<String> {
|
|
||||||
if (isWireless()){
|
|
||||||
throw new Exception("'getMethodsRemote' only works with wired modems");
|
|
||||||
}
|
|
||||||
|
|
||||||
return nativ.getMethodsRemote(name).toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function callRemote(remoteName: String, method:String):Dynamic {
|
|
||||||
if (isWireless()){
|
|
||||||
throw new Exception("'callRemote' only works with wired modems");
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: implment or solve differently
|
|
||||||
throw new haxe.exceptions.NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getNameLocal(): String {
|
|
||||||
if (isWireless()){
|
|
||||||
throw new Exception("'getNameLocal' only works with wired modems");
|
|
||||||
}
|
|
||||||
|
|
||||||
return nativ.getNameLocal();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,55 +5,191 @@ import kernel.peripherals.Screen;
|
|||||||
|
|
||||||
using lua.Table;
|
using lua.Table;
|
||||||
using Lambda;
|
using Lambda;
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Class responseable for retrieving peripherals.
|
Class responseable for retrieving peripherals.
|
||||||
**/
|
**/
|
||||||
class Peripheral {
|
class Peripheral {
|
||||||
public static var instance:Peripheral;
|
public static function getAllAddresses():Array<String> {
|
||||||
|
return cc.Peripheral.getNames().toArray();
|
||||||
@:allow(kernel.Init)
|
|
||||||
private function new() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Get all connected screens.
|
|
||||||
**/
|
|
||||||
public function getScreens(): Array<Screen> {
|
|
||||||
var allScreens = cc.Peripheral.getNames().toArray().filter(s -> cc.Peripheral.getType(s) == "monitor");
|
|
||||||
return allScreens.map(s -> return new Screen((cc.Peripheral.wrap(s):Dynamic),s));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getScreen(addr: String): Screen {
|
public static function isPresent(addr:String):Bool {
|
||||||
if (!getAllPeripheralsAddr().exists(item -> item == addr)){
|
return cc.Peripheral.isPresent(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getTypes(addr:String):Array<String> {
|
||||||
|
if (!cc.Peripheral.isPresent(addr)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return cc.Peripheral.getType(addr).toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function findAddrByType(type:String):Array<String> {
|
||||||
|
return getAllAddresses().filter(addr -> getTypes(addr).contains(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function safeGetAddr(addr:String, type:String):Null<String> {
|
||||||
|
if (!isPresent(addr)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Screen((cc.Peripheral.wrap(addr):Dynamic),addr);
|
var types = getTypes(addr);
|
||||||
|
if (!types.contains(type)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function inspect(addr:String):Null<{types:Array<String>, methods:Array<String>}> {
|
||||||
|
if (!isPresent(addr)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var types = getTypes(addr);
|
||||||
|
var methods = cc.Peripheral.getMethods(addr).toArray();
|
||||||
|
|
||||||
|
return {
|
||||||
|
types: types,
|
||||||
|
methods: methods
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Get all connected modems.
|
Cast peripheral to a specific type.
|
||||||
|
This is a temporary solution, maybe forever.
|
||||||
**/
|
**/
|
||||||
public function getModems(): Array<Modem> {
|
public static function getFromType(addr:String, type:String):Null<IPeripheral> {
|
||||||
var allModems = cc.Peripheral.getNames().toArray().filter(s -> cc.Peripheral.getType(s) == "modem");
|
switch (type) {
|
||||||
return allModems.map(s -> return new Modem((cc.Peripheral.wrap(s): Dynamic),s));
|
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;
|
||||||
Get all connected wireless modems.
|
|
||||||
**/
|
|
||||||
public function getWirelessModems(): Array<Modem> {
|
|
||||||
return getModems().filter(modem -> return modem.isWireless());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static function getScreen(addr:String):Null<Screen> {
|
||||||
Get all connected wired modems.
|
var addr = safeGetAddr(addr, Screen.TYPE_NAME);
|
||||||
**/
|
if (addr == null)
|
||||||
public function getWiredModems(): Array<Modem> {
|
return null;
|
||||||
return getModems().filter(modem -> return !modem.isWireless());
|
return new Screen(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAllPeripheralsAddr(): Array<String> {
|
public static function getAllScreens():Array<Screen> {
|
||||||
return cc.Peripheral.getNames().toArray();
|
return [for (addr in findAddrByType(Screen.TYPE_NAME)) new Screen(addr)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getModem(addr:String):Null<Modem> {
|
||||||
|
var addr = safeGetAddr(addr, Modem.TYPE_NAME);
|
||||||
|
if (addr == null)
|
||||||
|
return null;
|
||||||
|
return new Modem(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getAllModems():Array<Modem> {
|
||||||
|
return [for (addr in findAddrByType(Modem.TYPE_NAME)) new Modem(addr)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getDrive(addr:String):Null<Drive> {
|
||||||
|
var addr = safeGetAddr(addr, Drive.TYPE_NAME);
|
||||||
|
if (addr == null)
|
||||||
|
return null;
|
||||||
|
return new Drive(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getAllDrives():Array<Drive> {
|
||||||
|
return [for (addr in findAddrByType(Drive.TYPE_NAME)) new Drive(addr)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRedstone(side:String):Redstone {
|
||||||
|
// TODO: maybe handle restone differently to not duplicate event listeners
|
||||||
|
return new Redstone(side);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getPrinter(addr:String):Null<Printer> {
|
||||||
|
var addr = safeGetAddr(addr, Printer.TYPE_NAME);
|
||||||
|
if (addr == null)
|
||||||
|
return null;
|
||||||
|
return new Printer(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getAllPrinters():Array<Printer> {
|
||||||
|
return [for (addr in findAddrByType(Printer.TYPE_NAME)) new Printer(addr)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getEnergyStorage(addr:String):Null<EnergyStorage> {
|
||||||
|
var addr = safeGetAddr(addr, EnergyStorage.TYPE_NAME);
|
||||||
|
if (addr == null)
|
||||||
|
return null;
|
||||||
|
return new EnergyStorage(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getAllEnergyStorages():Array<EnergyStorage> {
|
||||||
|
return [for (addr in findAddrByType(EnergyStorage.TYPE_NAME)) new EnergyStorage(addr)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getComputer(addr:String):Null<Computer> {
|
||||||
|
var addr = safeGetAddr(addr, Computer.TYPE_NAME);
|
||||||
|
if (addr == null)
|
||||||
|
return null;
|
||||||
|
return new Computer(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getAllComputers():Array<Computer> {
|
||||||
|
return [for (addr in findAddrByType(Computer.TYPE_NAME)) new Computer(addr)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getSpeaker(addr:String):Null<Speaker> {
|
||||||
|
var addr = safeGetAddr(addr, Speaker.TYPE_NAME);
|
||||||
|
if (addr == null)
|
||||||
|
return null;
|
||||||
|
return new Speaker(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getAllSpeakers():Array<Speaker> {
|
||||||
|
return [for (addr in findAddrByType(Speaker.TYPE_NAME)) new Speaker(addr)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getInventory(addr:String):Null<Inventory> {
|
||||||
|
var addr = safeGetAddr(addr, Inventory.TYPE_NAME);
|
||||||
|
if (addr == null)
|
||||||
|
return null;
|
||||||
|
return new Inventory(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getAllInventorys():Array<Inventory> {
|
||||||
|
return [for (addr in findAddrByType(Inventory.TYPE_NAME)) new Inventory(addr)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getGeoScanner(addr:String):Null<GeoScanner> {
|
||||||
|
var addr = safeGetAddr(addr, GeoScanner.TYPE_NAME);
|
||||||
|
if (addr == null)
|
||||||
|
return null;
|
||||||
|
return new GeoScanner(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getAllGeoScanners():Array<GeoScanner> {
|
||||||
|
return [for (addr in findAddrByType(GeoScanner.TYPE_NAME)) new GeoScanner(addr)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
60
src/kernel/peripherals/Printer.hx
Normal file
60
src/kernel/peripherals/Printer.hx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package kernel.peripherals;
|
||||||
|
|
||||||
|
import cc.Peripheral;
|
||||||
|
import lib.Rect;
|
||||||
|
import lib.ScreenPos;
|
||||||
|
|
||||||
|
class Printer implements IPeripheral {
|
||||||
|
public static inline final TYPE_NAME:String = "printer";
|
||||||
|
|
||||||
|
private final native:cc.periphs.Printer.Printer;
|
||||||
|
private final addr:String;
|
||||||
|
|
||||||
|
public function new(addr:String) {
|
||||||
|
this.native = Peripheral.wrap(addr);
|
||||||
|
this.addr = addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAddr():String {
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType():String {
|
||||||
|
return TYPE_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write(text:String) {}
|
||||||
|
|
||||||
|
public function getCurserPos():ScreenPos {
|
||||||
|
return new ScreenPos({x: 0, y: 0});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCurserPos(pos:ScreenPos) {
|
||||||
|
this.native.setCursorPos(pos.x, pos.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPageSize():Rect {
|
||||||
|
var pos = this.native.getPageSize();
|
||||||
|
return new Rect({x: 0, y: 0}, {x: pos.x, y: pos.y});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newPage():Bool {
|
||||||
|
return this.native.newPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function endPage():Bool {
|
||||||
|
return this.native.endPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPageTitle(title:String) {
|
||||||
|
this.native.setPageTitle(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getInkLevel():Float {
|
||||||
|
return this.native.getInkLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPaperLevel():Int {
|
||||||
|
return this.native.getPaperLevel();
|
||||||
|
}
|
||||||
|
}
|
||||||
127
src/kernel/peripherals/Redstone.hx
Normal file
127
src/kernel/peripherals/Redstone.hx
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
package kernel.peripherals;
|
||||||
|
|
||||||
|
import kernel.peripherals.interfaces.IRedstone;
|
||||||
|
import haxe.ds.ReadOnlyArray;
|
||||||
|
import lib.Color;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
abstract BundleMask(Int) from cc.Colors.Color to cc.Colors.Color {
|
||||||
|
public inline function new(i:Int) {
|
||||||
|
this = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
@:from
|
||||||
|
public static function fromColor(c:Color) {
|
||||||
|
return new BundleMask(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@:op(A + B)
|
||||||
|
@:op(A | B)
|
||||||
|
public inline function combine(rhs:BundleMask):BundleMask {
|
||||||
|
return this | rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@:op(A + B)
|
||||||
|
@:op(A | B)
|
||||||
|
public inline function combineWithColor(rhs:Color):BundleMask {
|
||||||
|
return this | rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getComponents():ReadOnlyArray<Color> {
|
||||||
|
var components:Array<Color> = [];
|
||||||
|
var mask = 1;
|
||||||
|
for (i in 0...16) {
|
||||||
|
if ((this & mask) > 0) {
|
||||||
|
components.push(mask);
|
||||||
|
}
|
||||||
|
mask = mask << 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return components;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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>;
|
||||||
|
|
||||||
|
private final onChangeTrigger:SignalTrigger<Noise> = Signal.trigger();
|
||||||
|
|
||||||
|
private final addr:Side;
|
||||||
|
|
||||||
|
private var analogInputState:Int;
|
||||||
|
private var bundleInputState:BundleMask;
|
||||||
|
|
||||||
|
@:allow(kernel.peripherals)
|
||||||
|
private function new(side:Side) {
|
||||||
|
this.addr = side;
|
||||||
|
this.onChange = this.onChangeTrigger.asSignal();
|
||||||
|
|
||||||
|
updateState();
|
||||||
|
|
||||||
|
KernelEvents.onRedstone.handle(() -> {
|
||||||
|
if ((this.getAnalogInput() != this.analogInputState) || (this.bundleInputState != this.getBundledInput())) {
|
||||||
|
updateState();
|
||||||
|
this.onChangeTrigger.trigger(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAddr():String {
|
||||||
|
return this.addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType():String {
|
||||||
|
return TYPE_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function updateState() {
|
||||||
|
this.analogInputState = cc.Redstone.getAnalogInput(this.addr);
|
||||||
|
this.bundleInputState = cc.Redstone.getBundledInput(this.addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function setOutput(on:Bool):Void {
|
||||||
|
this.analogInputState = 15;
|
||||||
|
cc.Redstone.setOutput(this.addr, on);
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getOutput():Bool {
|
||||||
|
return cc.Redstone.getOutput(this.addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getInput():Bool {
|
||||||
|
return cc.Redstone.getInput(this.addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function setAnalogOutput(strength:Int):Void {
|
||||||
|
this.analogInputState = strength;
|
||||||
|
cc.Redstone.setAnalogOutput(this.addr, strength);
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getAnalogOutput():Int {
|
||||||
|
return cc.Redstone.getAnalogOutput(this.addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getAnalogInput():Int {
|
||||||
|
return cc.Redstone.getAnalogInput(this.addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function setBundledOutput(output:BundleMask) {
|
||||||
|
this.bundleInputState = output;
|
||||||
|
cc.Redstone.setBundledOutput(this.addr, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getBundledOutput():BundleMask {
|
||||||
|
return cc.Redstone.getBundledOutput(this.addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getBundledInput():BundleMask {
|
||||||
|
return cc.Redstone.getBundledInput(this.addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function testBundledInput(mask:Color):Bool {
|
||||||
|
return cc.Redstone.testBundledInput(this.addr, mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,31 +1,33 @@
|
|||||||
package kernel.peripherals;
|
package kernel.peripherals;
|
||||||
|
|
||||||
|
import cc.Peripheral;
|
||||||
|
import lib.ScreenPos;
|
||||||
|
import cc.Term.TerminalSize;
|
||||||
|
import kernel.ui.ITermWriteable;
|
||||||
|
import lib.Vec.Vec2;
|
||||||
|
import lib.Color;
|
||||||
|
|
||||||
using tink.CoreApi;
|
using tink.CoreApi;
|
||||||
|
|
||||||
import cc.Term.TerminalSize;
|
class Screen implements ITermWriteable implements IPeripheral {
|
||||||
import lib.TermWriteable;
|
public static inline final TYPE_NAME:String = "monitor";
|
||||||
import util.Vec.Vec2;
|
|
||||||
import util.Color;
|
|
||||||
|
|
||||||
class Screen implements TermWriteable{
|
|
||||||
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>>;
|
public final onResize:Signal<Vec2<Int>>;
|
||||||
public var onResize(default,null):Signal<Vec2<Int>>;
|
|
||||||
|
|
||||||
|
|
||||||
@:allow(kernel.peripherals)
|
@:allow(kernel.peripherals)
|
||||||
public function new(nativePeripherals: cc.periphs.Monitor.Monitor,addr: String) {
|
private function new(addr:String) {
|
||||||
this.onResizeTrigger = Signal.trigger();
|
|
||||||
this.onResize = onResizeTrigger.asSignal();
|
this.onResize = onResizeTrigger.asSignal();
|
||||||
|
|
||||||
this.nativ = nativePeripherals;
|
this.nativ = Peripheral.wrap(addr);
|
||||||
this.addr = addr;
|
this.addr = addr;
|
||||||
|
|
||||||
KernelEvents.instance.on("monitor_resize",params -> {
|
KernelEvents.onMonitorResize.handle(addr -> {
|
||||||
if (params[1] == this.addr){
|
if (addr == this.addr) {
|
||||||
onResizeTrigger.trigger(getSize());
|
onResizeTrigger.trigger(getSize());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -33,15 +35,19 @@ class Screen implements TermWriteable{
|
|||||||
setTextScale(0.5);
|
setTextScale(0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAddr(): String {
|
public function getAddr():String {
|
||||||
return this.addr;
|
return this.addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTextScale(): Float {
|
public function getType():String {
|
||||||
|
return TYPE_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTextScale():Float {
|
||||||
return nativ.getTextScale();
|
return nativ.getTextScale();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setTextScale(scale: Float) {
|
public function setTextScale(scale:Float) {
|
||||||
nativ.setTextScale(scale);
|
nativ.setTextScale(scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +59,7 @@ class Screen implements TermWriteable{
|
|||||||
nativ.scroll(y);
|
nativ.scroll(y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCursorPos():Vec2<Int> {
|
public function getCursorPos():ScreenPos {
|
||||||
var rtn = nativ.getCursorPos();
|
var rtn = nativ.getCursorPos();
|
||||||
return {
|
return {
|
||||||
x: rtn.x - 1,
|
x: rtn.x - 1,
|
||||||
@@ -62,7 +68,7 @@ class Screen implements TermWriteable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function setCursorPos(x:Int, y:Int) {
|
public function setCursorPos(x:Int, y:Int) {
|
||||||
nativ.setCursorPos(x + 1,y + 1);
|
nativ.setCursorPos(x + 1, y + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCursorBlink():Bool {
|
public function getCursorBlink():Bool {
|
||||||
@@ -74,7 +80,6 @@ class Screen implements TermWriteable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getSize():Vec2<Int> {
|
public function getSize():Vec2<Int> {
|
||||||
// FIXME: this will not compile. Has to be changes upstream
|
|
||||||
var size:TerminalSize = nativ.getSize();
|
var size:TerminalSize = nativ.getSize();
|
||||||
return {
|
return {
|
||||||
x: size.width,
|
x: size.width,
|
||||||
@@ -91,22 +96,29 @@ class Screen implements TermWriteable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getTextColor():Color {
|
public function getTextColor():Color {
|
||||||
return ColorConvert.ccToColor(nativ.getTextColor());
|
return nativ.getTextColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setTextColor(colour:Color) {
|
public function setTextColor(color:Color) {
|
||||||
nativ.setTextColor(ColorConvert.colorToCC(colour));
|
nativ.setTextColor(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getBackgroundColor():Color {
|
public function getBackgroundColor():Color {
|
||||||
return ColorConvert.ccToColor(nativ.getBackgroundColor());
|
return nativ.getBackgroundColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setBackgroundColor(color:Color) {
|
public function setBackgroundColor(color:Color) {
|
||||||
nativ.setBackgroundColor(ColorConvert.colorToCC(color));
|
nativ.setBackgroundColor(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isColor():Bool {
|
public function isColor():Bool {
|
||||||
return nativ.isColor();
|
return nativ.isColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function reset() {
|
||||||
|
this.setBackgroundColor(Black);
|
||||||
|
this.setTextColor(White);
|
||||||
|
this.clear();
|
||||||
|
this.setCursorPos(0, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
30
src/kernel/peripherals/Side.hx
Normal file
30
src/kernel/peripherals/Side.hx
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package kernel.peripherals;
|
||||||
|
|
||||||
|
enum abstract Side(String) to String {
|
||||||
|
var Top = "top";
|
||||||
|
var Bottom = "bottom";
|
||||||
|
var Left = "left";
|
||||||
|
var Right = "right";
|
||||||
|
var Front = "front";
|
||||||
|
var Back = "back";
|
||||||
|
|
||||||
|
@:from
|
||||||
|
static public function fromString(s:String) {
|
||||||
|
switch (s) {
|
||||||
|
case "top":
|
||||||
|
return Top;
|
||||||
|
case "bottom":
|
||||||
|
return Bottom;
|
||||||
|
case "left":
|
||||||
|
return Left;
|
||||||
|
case "right":
|
||||||
|
return Right;
|
||||||
|
case "front":
|
||||||
|
return Front;
|
||||||
|
case "back":
|
||||||
|
return Back;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
61
src/kernel/peripherals/Speaker.hx
Normal file
61
src/kernel/peripherals/Speaker.hx
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/kernel/peripherals/exports/RedstoneExport.hx
Normal file
7
src/kernel/peripherals/exports/RedstoneExport.hx
Normal 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 {}
|
||||||
17
src/kernel/peripherals/interfaces/IRedstone.hx
Normal file
17
src/kernel/peripherals/interfaces/IRedstone.hx
Normal 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;
|
||||||
|
}
|
||||||
34
src/kernel/pocket/Pocket.hx
Normal file
34
src/kernel/pocket/Pocket.hx
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/kernel/ps/IProcess.hx
Normal file
8
src/kernel/ps/IProcess.hx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package kernel.ps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Defines an independent process that can be run by the kernel.
|
||||||
|
**/
|
||||||
|
interface IProcess {
|
||||||
|
public function run(handle:ProcessHandle):Void;
|
||||||
|
}
|
||||||
123
src/kernel/ps/ProcessHandle.hx
Normal file
123
src/kernel/ps/ProcessHandle.hx
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
package kernel.ps;
|
||||||
|
|
||||||
|
import kernel.turtle.TurtleMutex;
|
||||||
|
import kernel.ps.ProcessManager.PID;
|
||||||
|
import kernel.ui.WindowContext;
|
||||||
|
import kernel.ui.WindowManager;
|
||||||
|
import haxe.ds.ReadOnlyArray;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
typedef HandleConfig = {
|
||||||
|
?args:Array<String>,
|
||||||
|
?onWrite:Callback<String>,
|
||||||
|
?onExit:Callback<Bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
class ProcessHandle {
|
||||||
|
public var args(get, null):ReadOnlyArray<String>;
|
||||||
|
|
||||||
|
private final pid:PID;
|
||||||
|
private final config:HandleConfig;
|
||||||
|
private final closeFuture:Future<Bool>;
|
||||||
|
private var closeFutureResolev:Bool->Void;
|
||||||
|
private final windowContexts:Array<WindowContext> = [];
|
||||||
|
private final cbLinks:Array<CallbackLink> = [];
|
||||||
|
private final deferFuncs:Array<Void->Void> = [];
|
||||||
|
private var hasExited:Bool = false;
|
||||||
|
|
||||||
|
@:allow(kernel.ps.ProcessManager)
|
||||||
|
private function new(config:HandleConfig, pid:PID) {
|
||||||
|
this.config = config;
|
||||||
|
this.pid = pid;
|
||||||
|
|
||||||
|
this.closeFuture = new Future<Bool>((trigger) -> {
|
||||||
|
this.closeFutureResolev = trigger;
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.config.onExit != null) {
|
||||||
|
this.closeFuture.handle(this.config.onExit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onExit():Future<Bool> {
|
||||||
|
return this.closeFuture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function close(success:Bool = true):Void {
|
||||||
|
this.hasExited = true;
|
||||||
|
this.dispose();
|
||||||
|
|
||||||
|
EndOfLoop.endOfLoop(() -> {
|
||||||
|
this.closeFutureResolev(success);
|
||||||
|
});
|
||||||
|
ProcessManager.removeProcess(this.pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function write(message:String):Void {
|
||||||
|
if (this.hasExited)
|
||||||
|
return;
|
||||||
|
if (this.config.onWrite != null) {
|
||||||
|
this.config.onWrite.invoke(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function writeLine(message:String):Void {
|
||||||
|
this.write(message + "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createBufferdWindowContext():WindowContext {
|
||||||
|
var ctx = WindowManager.createNewContext();
|
||||||
|
this.windowContexts.push(ctx);
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createStatelessWindowContext():{ctx:WindowContext, setRenderFunc:(() -> Void)->Void, requestRender:() -> Void} {
|
||||||
|
var ctx = WindowManager.createNewStatelessContext();
|
||||||
|
this.windowContexts.push(ctx.ctx);
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getWindowContexts():ReadOnlyArray<WindowContext> {
|
||||||
|
return this.windowContexts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function dispose() {
|
||||||
|
for (link in this.cbLinks) {
|
||||||
|
link.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (func in this.deferFuncs) {
|
||||||
|
func();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPid():PID {
|
||||||
|
return this.pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addCallbackLink(link:CallbackLink) {
|
||||||
|
this.cbLinks.push(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addDeferFunc(func:Void->Void) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
60
src/kernel/ps/ProcessManager.hx
Normal file
60
src/kernel/ps/ProcessManager.hx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package kernel.ps;
|
||||||
|
|
||||||
|
import kernel.log.Log;
|
||||||
|
import kernel.ps.ProcessHandle.HandleConfig;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
typedef PID = Int;
|
||||||
|
|
||||||
|
class ProcessManager {
|
||||||
|
private static final processList = new Map<PID, ProcessHandle>();
|
||||||
|
|
||||||
|
public static function run(process:IProcess, config:HandleConfig):PID {
|
||||||
|
var pid = createPID();
|
||||||
|
var handle = new ProcessHandle(config, pid);
|
||||||
|
|
||||||
|
processList.set(pid, handle);
|
||||||
|
|
||||||
|
try {
|
||||||
|
process.run(handle);
|
||||||
|
} catch (e:Dynamic) {
|
||||||
|
Log.error("Error while running process: " + e);
|
||||||
|
handle.close(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function kill(pid:PID) {
|
||||||
|
if (!processList.exists(pid)) {
|
||||||
|
Log.warn("Trying to kill non-existing process: " + pid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var handle = processList.get(pid);
|
||||||
|
|
||||||
|
handle.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function createPID():PID {
|
||||||
|
// TODO: better PID generation
|
||||||
|
|
||||||
|
// generate a random PID
|
||||||
|
return Math.ceil(Math.random() * 1000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@:allow(kernel.ui.WindowManager)
|
||||||
|
private static function getProcess(pid:PID):Null<ProcessHandle> {
|
||||||
|
return processList.get(pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@:allow(kernel.ps.ProcessHandle)
|
||||||
|
private static function removeProcess(pid:PID):Void {
|
||||||
|
processList.remove(pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function listProcesses():Array<PID> {
|
||||||
|
return [for (pid in processList.keys()) pid];
|
||||||
|
}
|
||||||
|
}
|
||||||
38
src/kernel/service/Service.hx
Normal file
38
src/kernel/service/Service.hx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package kernel.service;
|
||||||
|
|
||||||
|
import kernel.ps.IProcess;
|
||||||
|
import kernel.ps.ProcessManager;
|
||||||
|
import kernel.binstore.BinStore;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
class Service {
|
||||||
|
public final binName:String;
|
||||||
|
public final name:String;
|
||||||
|
public final args:Array<String>;
|
||||||
|
public var pid:PID;
|
||||||
|
public var ps:IProcess;
|
||||||
|
|
||||||
|
@:allow(kernel.service.ServiceManager)
|
||||||
|
private function new(binName:String, name:String, ?args:Array<String>) {
|
||||||
|
this.binName = binName;
|
||||||
|
this.name = name;
|
||||||
|
this.args = args ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function start() {
|
||||||
|
var ps = BinStore.instantiate(this.binName);
|
||||||
|
|
||||||
|
if (ps == null) {
|
||||||
|
throw new Error('Bin ${this.binName} not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ps = ps;
|
||||||
|
|
||||||
|
this.pid = ProcessManager.run(this.ps, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stop() {
|
||||||
|
ProcessManager.kill(this.pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
149
src/kernel/service/ServiceManager.hx
Normal file
149
src/kernel/service/ServiceManager.hx
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
package kernel.service;
|
||||||
|
|
||||||
|
import kernel.log.Log;
|
||||||
|
import kernel.binstore.BinStore;
|
||||||
|
import lib.KVStore;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
class ServiceManager {
|
||||||
|
private static final services:Map<String, Service> = new Map();
|
||||||
|
|
||||||
|
@:allow(kernel.Init)
|
||||||
|
private static function init() {
|
||||||
|
startAllEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Add a service to be automatically started.
|
||||||
|
**/
|
||||||
|
public static function enable(name:String):Outcome<Noise, String> {
|
||||||
|
if (!services.exists(name)) {
|
||||||
|
return Failure("Service must be started before enable");
|
||||||
|
}
|
||||||
|
|
||||||
|
var store = KVStore.getStoreForClass();
|
||||||
|
store.load();
|
||||||
|
|
||||||
|
var enabled = store.get("enabled", []);
|
||||||
|
enabled.push(name);
|
||||||
|
store.set("enabled", enabled);
|
||||||
|
|
||||||
|
store.save();
|
||||||
|
|
||||||
|
return Success(Noise);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Remove a service from being automatically started.
|
||||||
|
**/
|
||||||
|
private static function disable(name:String):Outcome<Noise, String> {
|
||||||
|
var store = KVStore.getStoreForClass();
|
||||||
|
store.load();
|
||||||
|
|
||||||
|
var enabled:Array<String> = store.get("enabled");
|
||||||
|
var index = enabled.indexOf(name);
|
||||||
|
if (index == -1) {
|
||||||
|
return Failure("Service not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
enabled.splice(index, 1);
|
||||||
|
store.save();
|
||||||
|
|
||||||
|
return Success(Noise);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function startAllEnabled() {
|
||||||
|
var store = KVStore.getStoreForClass();
|
||||||
|
store.load();
|
||||||
|
|
||||||
|
var enabled:Array<String> = store.get("enabled", []);
|
||||||
|
for (name in enabled) {
|
||||||
|
start(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function load(name:String):Null<Service> {
|
||||||
|
var store = new KVStore('service/${name}');
|
||||||
|
store.load();
|
||||||
|
if (!store.exists("service")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return store.get("service");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function register(name:String, binName:String, args:Array<String>):Outcome<Noise, String> {
|
||||||
|
if (BinStore.getNameByAlias(binName) == null) {
|
||||||
|
return Failure("bin not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (load(name) != null) {
|
||||||
|
return Failure("service already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
var service = new Service(binName, name, args);
|
||||||
|
|
||||||
|
var store = new KVStore('service/${name}');
|
||||||
|
store.set("service", service);
|
||||||
|
store.save();
|
||||||
|
|
||||||
|
Log.info('Service ${name} registered');
|
||||||
|
return Success(Noise);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function unregister(name:String):Outcome<Noise, String> {
|
||||||
|
if (services.exists(name)) {
|
||||||
|
return Failure("service is running");
|
||||||
|
}
|
||||||
|
|
||||||
|
KVStore.removeNamespace('service/${name}');
|
||||||
|
Log.info('Service ${name} unregistered');
|
||||||
|
return Success(Noise);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function start(name:String):Outcome<Noise, String> {
|
||||||
|
var service = load(name);
|
||||||
|
if (service == null) {
|
||||||
|
return Failure("service not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
service.start();
|
||||||
|
services.set(name, service);
|
||||||
|
|
||||||
|
Log.info('Service ${name} started');
|
||||||
|
return Success(Noise);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function stop(name:String):Outcome<Noise, String> {
|
||||||
|
if (!services.exists(name)) {
|
||||||
|
return Failure("service not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
312
src/kernel/turtle/Turtle.hx
Normal file
312
src/kernel/turtle/Turtle.hx
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
package kernel.turtle;
|
||||||
|
|
||||||
|
import kernel.turtle.Types;
|
||||||
|
import kernel.gps.INS;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
class Turtle {
|
||||||
|
public static final MAX_SLOTS:Int = 16;
|
||||||
|
|
||||||
|
public static function isTurtle():Bool {
|
||||||
|
return cc.Turtle != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function conterToOutcome(r:cc.Turtle.TurtleActionResult):Outcome<Noise, String> {
|
||||||
|
if (r.successful) {
|
||||||
|
return Outcome.Success(null);
|
||||||
|
} else {
|
||||||
|
if (r.error != null) {
|
||||||
|
return Outcome.Failure(r.error);
|
||||||
|
} else {
|
||||||
|
return Outcome.Failure("Unknown error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function forward():Outcome<Noise, String> {
|
||||||
|
var r = cc.Turtle.forward();
|
||||||
|
var r2 = conterToOutcome(r);
|
||||||
|
if (r2.isSuccess())
|
||||||
|
INS.moveForward();
|
||||||
|
return r2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function back():Outcome<Noise, String> {
|
||||||
|
var r = cc.Turtle.back();
|
||||||
|
var r2 = conterToOutcome(r);
|
||||||
|
if (r2.isSuccess())
|
||||||
|
INS.moveBackward();
|
||||||
|
return r2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function up():Outcome<Noise, String> {
|
||||||
|
var r = cc.Turtle.up();
|
||||||
|
var r2 = conterToOutcome(r);
|
||||||
|
if (r2.isSuccess())
|
||||||
|
INS.moveUp();
|
||||||
|
return r2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function down():Outcome<Noise, String> {
|
||||||
|
var r = cc.Turtle.down();
|
||||||
|
var r2 = conterToOutcome(r);
|
||||||
|
if (r2.isSuccess())
|
||||||
|
INS.moveDown();
|
||||||
|
return r2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function turnLeft():Outcome<Noise, String> {
|
||||||
|
var r = cc.Turtle.turnLeft();
|
||||||
|
var r2 = conterToOutcome(r);
|
||||||
|
if (r2.isSuccess())
|
||||||
|
INS.turnRight();
|
||||||
|
return r2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function turnRight():Outcome<Noise, String> {
|
||||||
|
var r = cc.Turtle.turnRight();
|
||||||
|
var r2 = conterToOutcome(r);
|
||||||
|
if (r2.isSuccess())
|
||||||
|
INS.turnRight();
|
||||||
|
return r2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Dig in the provided direction.
|
||||||
|
Keep in mind that digging on a empty space is considerd a failure.
|
||||||
|
Also see `digEmpty`.
|
||||||
|
**/
|
||||||
|
public static function dig(dir:InteractDirections, ?toolSide:ToolSide):Outcome<Noise, String> {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
return conterToOutcome(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Dig in the provided direction.
|
||||||
|
Does not fail if there is nothing to dig.
|
||||||
|
Also see `dig`.
|
||||||
|
**/
|
||||||
|
public static function digEmpty(dir:InteractDirections, ?toolSide:ToolSide):Outcome<Noise, String> {
|
||||||
|
var r:cc.Turtle.TurtleActionResult;
|
||||||
|
|
||||||
|
// FIXME: upstream needs to be fixed to accept ToolSide
|
||||||
|
switch dir {
|
||||||
|
case Front:
|
||||||
|
r = cc.Turtle.dig();
|
||||||
|
case Up:
|
||||||
|
r = cc.Turtle.digUp();
|
||||||
|
case Down:
|
||||||
|
r = cc.Turtle.digDown();
|
||||||
|
}
|
||||||
|
var result = conterToOutcome(r);
|
||||||
|
|
||||||
|
switch (result) {
|
||||||
|
case Success(_):
|
||||||
|
return result;
|
||||||
|
case Failure(failure):
|
||||||
|
if (failure == "Nothing to dig here") {
|
||||||
|
return Success(null);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function place(dir:InteractDirections):Outcome<Noise, String> {
|
||||||
|
var r:cc.Turtle.TurtleActionResult;
|
||||||
|
switch dir {
|
||||||
|
case Front:
|
||||||
|
r = cc.Turtle.place();
|
||||||
|
case Up:
|
||||||
|
r = cc.Turtle.placeUp();
|
||||||
|
case Down:
|
||||||
|
r = cc.Turtle.placeDown();
|
||||||
|
}
|
||||||
|
return conterToOutcome(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function placeSign(dir:InteractDirections, text:String):Outcome<Noise, String> {
|
||||||
|
var r:cc.Turtle.TurtleActionResult;
|
||||||
|
switch dir {
|
||||||
|
case Front:
|
||||||
|
r = cc.Turtle.place(text);
|
||||||
|
case Up:
|
||||||
|
r = cc.Turtle.placeUp(); // FIXME: fix upstream to accept text
|
||||||
|
case Down:
|
||||||
|
r = cc.Turtle.placeDown();
|
||||||
|
}
|
||||||
|
return conterToOutcome(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function drop(dir:InteractDirections, ?count:Int):Outcome<Noise, String> {
|
||||||
|
var r = cc.Turtle.drop(count);
|
||||||
|
return conterToOutcome(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function selectSlot(slot:TurtleSlot):Outcome<Noise, Noise> {
|
||||||
|
var r = cc.Turtle.select(slot.toCCSlot());
|
||||||
|
|
||||||
|
return (r) ? Outcome.Success(null) : Outcome.Failure("Slot out of bounds");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getItemCount(?slot:TurtleSlot):Int {
|
||||||
|
return cc.Turtle.getItemCount(slot.toCCSlot());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getItemSpace(?slot:TurtleSlot):Int {
|
||||||
|
return cc.Turtle.getItemSpace(slot.toCCSlot());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function detect(dir:InteractDirections):Bool {
|
||||||
|
switch dir {
|
||||||
|
case Front:
|
||||||
|
return cc.Turtle.detect();
|
||||||
|
case Up:
|
||||||
|
return cc.Turtle.detectUp();
|
||||||
|
case Down:
|
||||||
|
return cc.Turtle.detectDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function compareToSlot(dir:InteractDirections):Bool {
|
||||||
|
switch dir {
|
||||||
|
case Front:
|
||||||
|
return cc.Turtle.compare();
|
||||||
|
case Up:
|
||||||
|
return cc.Turtle.compareUp();
|
||||||
|
case Down:
|
||||||
|
return cc.Turtle.compareDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function attack(dir:InteractDirections, ?toolSide:ToolSide):Outcome<Noise, String> {
|
||||||
|
var r:cc.Turtle.TurtleActionResult;
|
||||||
|
|
||||||
|
// FIXEM: upstream needs to be fixed to accept ToolSide
|
||||||
|
switch dir {
|
||||||
|
case Front:
|
||||||
|
r = cc.Turtle.attack();
|
||||||
|
case Up:
|
||||||
|
r = cc.Turtle.attackUp();
|
||||||
|
case Down:
|
||||||
|
r = cc.Turtle.attackDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
return conterToOutcome(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function suckItem(dir:InteractDirections, ?ammount:Int):Outcome<Noise, String> {
|
||||||
|
// TODO: ammount in bounds?
|
||||||
|
|
||||||
|
var r:cc.Turtle.TurtleActionResult;
|
||||||
|
|
||||||
|
switch dir {
|
||||||
|
case Front:
|
||||||
|
r = cc.Turtle.suck(ammount);
|
||||||
|
case Up:
|
||||||
|
r = cc.Turtle.suckUp(ammount);
|
||||||
|
case Down:
|
||||||
|
r = cc.Turtle.suckDown(ammount);
|
||||||
|
}
|
||||||
|
return conterToOutcome(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getFuelLevel():Int {
|
||||||
|
var r = cc.Turtle.getFuelLevel(); // FIXME: can be a string. Has to be fixed upstream
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function refuel(?ammount:Int):Outcome<Noise, String> {
|
||||||
|
var r = cc.Turtle.refuel(ammount);
|
||||||
|
return conterToOutcome(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function canRefultWithSlot():Bool {
|
||||||
|
var r = cc.Turtle.refuel(0);
|
||||||
|
return r.successful;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function compareSlot(otherSlot:TurtleSlot):Bool {
|
||||||
|
return cc.Turtle.compareTo(otherSlot.toCCSlot());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function transfer(from:TurtleSlot, to:TurtleSlot, ?count:Int):Outcome<Noise, Noise> {
|
||||||
|
selectSlot(from);
|
||||||
|
var r = cc.Turtle.transferTo(to.toCCSlot(), count);
|
||||||
|
return r ? Outcome.Success(null) : Outcome.Failure(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getSelectedSlot():TurtleSlot {
|
||||||
|
return cc.Turtle.getSelectedSlot() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getFuelLimit():Int {
|
||||||
|
return cc.Turtle.getFuelLimit(); // FIXME: can be a string. Has to be fixed upstream
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function equip(side:ToolSide):Outcome<Noise, String> {
|
||||||
|
switch side {
|
||||||
|
case Left:
|
||||||
|
return conterToOutcome(cc.Turtle.equipLeft());
|
||||||
|
case Right:
|
||||||
|
return conterToOutcome(cc.Turtle.equipRight());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function inspect(dir:InteractDirections):Outcome<BlockInspect, String> {
|
||||||
|
var r:cc.Turtle.TurtleInspectResult;
|
||||||
|
switch dir {
|
||||||
|
case Front:
|
||||||
|
r = cc.Turtle.inspect();
|
||||||
|
case Up:
|
||||||
|
r = cc.Turtle.inspectUp();
|
||||||
|
case Down:
|
||||||
|
r = cc.Turtle.inspectDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!r.successful) {
|
||||||
|
return Outcome.Failure(r.result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check if this is correct
|
||||||
|
return Outcome.Success({
|
||||||
|
name: (r.result : cc.Turtle.TurtleBlockDetail).name,
|
||||||
|
state: (r.result : cc.Turtle.TurtleBlockDetail).state,
|
||||||
|
metadata: (r.result : cc.Turtle.TurtleBlockDetail).metadata,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getItemDetail(slot:TurtleSlot, ?detailed:Bool = false):Option<ItemInspect> {
|
||||||
|
var r = cc.Turtle.getItemDetail(slot.toCCSlot()); // FIXME: can take detailed as flag. Has to be fixed upstream
|
||||||
|
|
||||||
|
if (r == null) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check if this is correct
|
||||||
|
return Some({
|
||||||
|
name: r.name,
|
||||||
|
count: r.count,
|
||||||
|
damage: r.damage,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function craft(?limit:Int = 64):Outcome<Noise, String> {
|
||||||
|
if (limit < 1 || limit > 64) {
|
||||||
|
return Outcome.Failure("Crafting limit out of bounds");
|
||||||
|
}
|
||||||
|
|
||||||
|
var r = cc.Turtle.craft(limit);
|
||||||
|
return conterToOutcome(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
52
src/kernel/turtle/TurtleMutex.hx
Normal file
52
src/kernel/turtle/TurtleMutex.hx
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/kernel/turtle/TurtleSlot.hx
Normal file
26
src/kernel/turtle/TurtleSlot.hx
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/kernel/turtle/Types.hx
Normal file
24
src/kernel/turtle/Types.hx
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package kernel.turtle;
|
||||||
|
|
||||||
|
enum ToolSide {
|
||||||
|
Left;
|
||||||
|
Right;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum InteractDirections {
|
||||||
|
Front;
|
||||||
|
Up;
|
||||||
|
Down;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef BlockInspect = {
|
||||||
|
public var name:String;
|
||||||
|
public var metadata:Int;
|
||||||
|
public var state:Dynamic;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef ItemInspect = {
|
||||||
|
public var name:String;
|
||||||
|
public var damage:Int;
|
||||||
|
public var count:Int;
|
||||||
|
}
|
||||||
180
src/kernel/ui/BufferedVirtualTermWriter.hx
Normal file
180
src/kernel/ui/BufferedVirtualTermWriter.hx
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
package kernel.ui;
|
||||||
|
|
||||||
|
import lib.ScreenPos;
|
||||||
|
import lib.Vec.Vec2;
|
||||||
|
import lib.Color;
|
||||||
|
import kernel.ui.ITermWriteable;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
A term writer that can switch beetween its internal buffer and another termwriter.
|
||||||
|
The other target is most of the time a real screen
|
||||||
|
**/
|
||||||
|
class BufferedVirtualTermWriter implements IVirtualTermWriter extends TermBuffer {
|
||||||
|
private static final defaultSize:Vec2<Int> = {x: 50, y: 50};
|
||||||
|
|
||||||
|
private var target:ITermWriteable;
|
||||||
|
private var enabled:Bool = false;
|
||||||
|
private var onResizeLink:CallbackLink;
|
||||||
|
|
||||||
|
@:allow(kernel.ui)
|
||||||
|
private function new(?target:ITermWriteable) {
|
||||||
|
setTarget(target);
|
||||||
|
|
||||||
|
if (enabled) {
|
||||||
|
enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function enable() {
|
||||||
|
if (target != null) {
|
||||||
|
enabled = true;
|
||||||
|
super.copyBufferToTarget(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function disable() {
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function isEnabled():Bool {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTarget(newTarget:ITermWriteable) {
|
||||||
|
if (newTarget != null) {
|
||||||
|
super.setSize(newTarget.getSize());
|
||||||
|
|
||||||
|
// Remove old target event listner
|
||||||
|
if (onResizeLink != null) {
|
||||||
|
onResizeLink.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new target event listner
|
||||||
|
onResizeLink = newTarget.onResize.handle(newSize -> {
|
||||||
|
setSuperSize(newSize);
|
||||||
|
});
|
||||||
|
|
||||||
|
newTarget.reset();
|
||||||
|
|
||||||
|
target = newTarget;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setSuperSize(size:Vec2<Int>) {
|
||||||
|
super.setSize(target.getSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// TermWriteable functions.
|
||||||
|
//
|
||||||
|
public override function write(text:String) {
|
||||||
|
if (isEnabled()) {
|
||||||
|
target.write(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.write(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function scroll(y:Int) {
|
||||||
|
if (isEnabled()) {
|
||||||
|
target.scroll(y);
|
||||||
|
}
|
||||||
|
super.scroll(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function getCursorPos():ScreenPos {
|
||||||
|
if (isEnabled()) {
|
||||||
|
return target.getCursorPos();
|
||||||
|
} else {
|
||||||
|
return super.getCursorPos();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function setCursorPos(x:Int, y:Int) {
|
||||||
|
if (isEnabled()) {
|
||||||
|
target.setCursorPos(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.setCursorPos(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function getCursorBlink():Bool {
|
||||||
|
if (isEnabled()) {
|
||||||
|
return target.getCursorBlink();
|
||||||
|
} else {
|
||||||
|
return super.getCursorBlink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function setCursorBlink(blink:Bool) {
|
||||||
|
if (isEnabled()) {
|
||||||
|
target.setCursorBlink(blink);
|
||||||
|
}
|
||||||
|
super.setCursorBlink(blink);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function getSize():Vec2<Int> {
|
||||||
|
// TODO: make sense ?
|
||||||
|
if (target != null) {
|
||||||
|
return target.getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function clear() {
|
||||||
|
if (isEnabled()) {
|
||||||
|
target.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
super.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function clearLine() {
|
||||||
|
if (isEnabled()) {
|
||||||
|
target.clearLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
super.clearLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function getTextColor():Color {
|
||||||
|
if (isEnabled()) {
|
||||||
|
return target.getTextColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.getTextColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function setTextColor(color:Color) {
|
||||||
|
if (isEnabled()) {
|
||||||
|
target.setTextColor(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.setTextColor(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function getBackgroundColor():Color {
|
||||||
|
if (isEnabled()) {
|
||||||
|
return target.getBackgroundColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.getBackgroundColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function setBackgroundColor(color:Color) {
|
||||||
|
if (isEnabled()) {
|
||||||
|
target.setBackgroundColor(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
super.setBackgroundColor(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function isColor():Bool {
|
||||||
|
throw new haxe.exceptions.NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
46
src/kernel/ui/ITermWriteable.hx
Normal file
46
src/kernel/ui/ITermWriteable.hx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package kernel.ui;
|
||||||
|
|
||||||
|
import lib.ScreenPos;
|
||||||
|
import lib.Color;
|
||||||
|
import lib.Vec.Vec2;
|
||||||
|
|
||||||
|
using tink.CoreApi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Interface describing a terminal. E.g. the main computer screen or a external screen.
|
||||||
|
**/
|
||||||
|
interface ITermWriteable {
|
||||||
|
public var onResize(default, null):Signal<Vec2<Int>>;
|
||||||
|
|
||||||
|
public function write(text:String):Void;
|
||||||
|
public function scroll(y:Int):Void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Even though CC is 1 based we use a 0 based index.
|
||||||
|
**/
|
||||||
|
public function getCursorPos():ScreenPos;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Even though CC is 1 based we use a 0 based index.
|
||||||
|
**/
|
||||||
|
public function setCursorPos(x:Int, y:Int):Void;
|
||||||
|
|
||||||
|
public function getCursorBlink():Bool;
|
||||||
|
public function setCursorBlink(blink:Bool):Void;
|
||||||
|
public function getSize():Vec2<Int>;
|
||||||
|
public function clear():Void;
|
||||||
|
public function clearLine():Void;
|
||||||
|
public function getTextColor():Color;
|
||||||
|
public function setTextColor(color:Color):Void;
|
||||||
|
public function getBackgroundColor():Color;
|
||||||
|
public function setBackgroundColor(color:Color):Void;
|
||||||
|
public function isColor():Bool;
|
||||||
|
|
||||||
|
// public function setPaletteColor(...);
|
||||||
|
// public function getPaletteColor(color);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Clears the screen, resetes the courser to (0,0) and resetes the color to Black and White.
|
||||||
|
**/
|
||||||
|
public function reset():Void;
|
||||||
|
}
|
||||||
12
src/kernel/ui/IVirtualTermWriter.hx
Normal file
12
src/kernel/ui/IVirtualTermWriter.hx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package kernel.ui;
|
||||||
|
|
||||||
|
/**
|
||||||
|
A VirtualTermWriter is a TermWriteable that can be enabled or disabled.
|
||||||
|
When disabled, it will not write to its target. When enabled, it will.
|
||||||
|
**/
|
||||||
|
interface IVirtualTermWriter extends ITermWriteable {
|
||||||
|
public function enable():Void;
|
||||||
|
public function disable():Void;
|
||||||
|
public function isEnabled():Bool;
|
||||||
|
public function setTarget(newTarget:ITermWriteable):Void;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user