Compare commits
8 Commits
c96d06653a
...
ecf4f9c719
| Author | SHA1 | Date | |
|---|---|---|---|
| ecf4f9c719 | |||
| 4903014ebd | |||
| 1a808055ea | |||
| 2402eb1a44 | |||
| 639392c6de | |||
| 3e275e2b06 | |||
| a20d3bc07e | |||
| d4703fb725 |
@@ -6,7 +6,7 @@ General purpose "operation system" for [ComputerCraft](https://tweaked.cc/) buil
|
||||
- Rednet message routing
|
||||
- Hardware abstraction
|
||||
- Virtual screens to switch between multiple GUI apps
|
||||
- Reactive UI framwork
|
||||
- Reactive UI framework
|
||||
|
||||
# Building
|
||||
|
||||
@@ -18,6 +18,12 @@ Requirements:
|
||||
|
||||
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.16.x/src/main/resources/data/computercraft/lua)
|
||||
|
||||
[CC wiki](https://tweaked.cc/)
|
||||
|
||||
# Development
|
||||
|
||||
There are a couple of tool to your disposal that makes working easier.
|
||||
@@ -28,9 +34,9 @@ Run `make watch` to recompile when a file changed.
|
||||
|
||||
## Emulator
|
||||
|
||||
You could use minecraft to run the programm or you could use [craftos pc](https://www.craftos-pc.cc/) as an emulator. Just install it and run `make 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`.
|
||||
|
||||
## Websconsole
|
||||
|
||||
Use `make webconsole` to run a http server that prints the log output of the programm to the terminal. Run `haxe` with the `-D webconsole` flag.
|
||||
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.
|
||||
@@ -13,6 +13,8 @@ const server = http.createServer((req, res) => {
|
||||
return;
|
||||
}
|
||||
|
||||
var id = req.url.substring(1);
|
||||
|
||||
let data = "";
|
||||
|
||||
req.on('data', chunk => {
|
||||
@@ -20,7 +22,7 @@ const server = http.createServer((req, res) => {
|
||||
})
|
||||
|
||||
req.on('end', () => {
|
||||
console.log(`[${time()}]${data}`);
|
||||
console.log(`[${time()}][${id}]${data}`);
|
||||
res.writeHead(200);
|
||||
res.end();
|
||||
})
|
||||
|
||||
@@ -12,10 +12,6 @@ class Init {
|
||||
public static function initKernel() {
|
||||
// Init singeltons here because haxe is confused about the order to create them.
|
||||
KernelEvents.instance = new KernelEvents();
|
||||
MainLoop.add(() -> {
|
||||
KernelEvents.instance.startEventLoop();
|
||||
},1);
|
||||
|
||||
Peripheral.instance = new Peripheral();
|
||||
|
||||
WindowManager.instance = new WindowManager();
|
||||
@@ -25,8 +21,6 @@ class Init {
|
||||
Routing.instance = new Routing();
|
||||
Net.instance = new Net();
|
||||
|
||||
Routing.instance.init();
|
||||
|
||||
// Register default terminate handler
|
||||
KernelEvents.instance.onTerminate.handle(_->{
|
||||
OS.reboot();
|
||||
@@ -34,7 +28,13 @@ class Init {
|
||||
|
||||
Debug.printBuildInfo();
|
||||
|
||||
|
||||
Log.moveToOutput("main");
|
||||
|
||||
Routing.instance.init();
|
||||
|
||||
MainLoop.add(()->{
|
||||
KernelEvents.instance.startEventLoop();
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,78 +138,82 @@ class KernelEvents {
|
||||
|
||||
var eventName:String = event[1];
|
||||
|
||||
switch eventName {
|
||||
case "alarm":
|
||||
this.onAlarmTrigger.trigger(event[2]);
|
||||
case "char":
|
||||
this.onCharTrigger.trigger(event[2]);
|
||||
case "disk":
|
||||
this.onDiskTrigger.trigger(event[2]);
|
||||
case "disk_eject":
|
||||
this.onDiskEjectTrigger.trigger(event[2]);
|
||||
case "http_check":
|
||||
this.onHttpCheckTrigger.trigger({url: event[2], success: event[3], failReason: event[4]});
|
||||
case "http_failure":
|
||||
this.onHttpFailureTrigger.trigger({url: event[2], failReason: event[3], handle: event[4]});
|
||||
case "http_success":
|
||||
this.onHttpSuccessTrigger.trigger({url: event[2], handle: event[3]});
|
||||
case "key":
|
||||
this.onKeyTrigger.trigger({keyCode: event[2], isHeld: event[3]});
|
||||
case "key_up":
|
||||
this.onKeyUpTrigger.trigger(event[2]);
|
||||
case "modem_message":
|
||||
this.onModemMessageTrigger.trigger({
|
||||
addr: event[2],
|
||||
channel: event[3],
|
||||
replyChannel: event[4],
|
||||
message: event[5],
|
||||
distance: event[6]
|
||||
});
|
||||
case "monitor_resize":
|
||||
this.onMonitorResizeTrigger.trigger(event[2]);
|
||||
case "monitor_touch":
|
||||
this.onMonitorTouchTrigger.trigger({addr: event[2], pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
|
||||
case "mouse_click":
|
||||
this.onMouseClickTrigger.trigger({button: ccButtonToEnum(event[2]), pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
|
||||
case "mouse_drag":
|
||||
this.onMouseDragTrigger.trigger({button: ccButtonToEnum(event[2]), pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
|
||||
case "mouse_scroll":
|
||||
this.onMouseScrollTrigger.trigger({dir: event[2], pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
|
||||
case "mouse_up":
|
||||
this.onMouseUpTrigger.trigger({button: ccButtonToEnum(event[2]), pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
|
||||
case "paste":
|
||||
this.onPasteTrigger.trigger(event[2]);
|
||||
case "peripheral":
|
||||
this.onPeripheralTrigger.trigger(event[2]);
|
||||
case "peripheral_detach":
|
||||
this.onPeripheralDetachTrigger.trigger(event[2]);
|
||||
case "rednet_message":
|
||||
this.onRednetMessageTrigger.trigger({sender: event[2], message: event[3], protocol: event[4]});
|
||||
case "redstone":
|
||||
this.onRedstoneTrigger.trigger(null);
|
||||
case "speaker_audio_empty":
|
||||
this.onSpeakerAudioEmptyTrigger.trigger(event[2]);
|
||||
case "task_complete":
|
||||
this.onTaskCompleteTrigger.trigger({id: event[2], success: event[3], failedReason: event[4]});
|
||||
case "term_resize":
|
||||
this.onTermResizeTrigger.trigger(null);
|
||||
case "terminate":
|
||||
this.onTerminateTrigger.trigger(null);
|
||||
case "timer":
|
||||
this.onTimerTrigger.trigger(event[2]);
|
||||
case "turtle_inventory":
|
||||
this.onTurtleInventoryTrigger.trigger(null);
|
||||
case "websocket_closed":
|
||||
this.onWebsocketCloseTrigger.trigger(event[2]);
|
||||
case "websocket_failure":
|
||||
this.onWebsocketFailureTrigger.trigger({url: event[2], failReason: event[3]});
|
||||
case "websocket_message":
|
||||
this.onWebsocketMessageTrigger.trigger({url: event[2], message: event[3], isBinary: event[4]});
|
||||
case "websocket_success":
|
||||
this.onWebsocketSuccessTrigger.trigger({url: event[2], handle: event[3]});
|
||||
default:
|
||||
Log.error("Unknown cc event: " + eventName);
|
||||
}
|
||||
fireSignal(eventName,event);
|
||||
}
|
||||
}
|
||||
|
||||
private function fireSignal(eventName: String,event:Table<Int, Dynamic> ) {
|
||||
switch eventName {
|
||||
case "alarm":
|
||||
this.onAlarmTrigger.trigger(event[2]);
|
||||
case "char":
|
||||
this.onCharTrigger.trigger(event[2]);
|
||||
case "disk":
|
||||
this.onDiskTrigger.trigger(event[2]);
|
||||
case "disk_eject":
|
||||
this.onDiskEjectTrigger.trigger(event[2]);
|
||||
case "http_check":
|
||||
this.onHttpCheckTrigger.trigger({url: event[2], success: event[3], failReason: event[4]});
|
||||
case "http_failure":
|
||||
this.onHttpFailureTrigger.trigger({url: event[2], failReason: event[3], handle: event[4]});
|
||||
case "http_success":
|
||||
this.onHttpSuccessTrigger.trigger({url: event[2], handle: event[3]});
|
||||
case "key":
|
||||
this.onKeyTrigger.trigger({keyCode: event[2], isHeld: event[3]});
|
||||
case "key_up":
|
||||
this.onKeyUpTrigger.trigger(event[2]);
|
||||
case "modem_message":
|
||||
this.onModemMessageTrigger.trigger({
|
||||
addr: event[2],
|
||||
channel: event[3],
|
||||
replyChannel: event[4],
|
||||
message: event[5],
|
||||
distance: event[6]
|
||||
});
|
||||
case "monitor_resize":
|
||||
this.onMonitorResizeTrigger.trigger(event[2]);
|
||||
case "monitor_touch":
|
||||
this.onMonitorTouchTrigger.trigger({addr: event[2], pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
|
||||
case "mouse_click":
|
||||
this.onMouseClickTrigger.trigger({button: ccButtonToEnum(event[2]), pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
|
||||
case "mouse_drag":
|
||||
this.onMouseDragTrigger.trigger({button: ccButtonToEnum(event[2]), pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
|
||||
case "mouse_scroll":
|
||||
this.onMouseScrollTrigger.trigger({dir: event[2], pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
|
||||
case "mouse_up":
|
||||
this.onMouseUpTrigger.trigger({button: ccButtonToEnum(event[2]), pos: {x: (event[3] : Int) - 1, y: (event[4] : Int) - 1}});
|
||||
case "paste":
|
||||
this.onPasteTrigger.trigger(event[2]);
|
||||
case "peripheral":
|
||||
this.onPeripheralTrigger.trigger(event[2]);
|
||||
case "peripheral_detach":
|
||||
this.onPeripheralDetachTrigger.trigger(event[2]);
|
||||
case "rednet_message":
|
||||
this.onRednetMessageTrigger.trigger({sender: event[2], message: event[3], protocol: event[4]});
|
||||
case "redstone":
|
||||
this.onRedstoneTrigger.trigger(null);
|
||||
case "speaker_audio_empty":
|
||||
this.onSpeakerAudioEmptyTrigger.trigger(event[2]);
|
||||
case "task_complete":
|
||||
this.onTaskCompleteTrigger.trigger({id: event[2], success: event[3], failedReason: event[4]});
|
||||
case "term_resize":
|
||||
this.onTermResizeTrigger.trigger(null);
|
||||
case "terminate":
|
||||
this.onTerminateTrigger.trigger(null);
|
||||
case "timer":
|
||||
this.onTimerTrigger.trigger(event[2]);
|
||||
case "turtle_inventory":
|
||||
this.onTurtleInventoryTrigger.trigger(null);
|
||||
case "websocket_closed":
|
||||
this.onWebsocketCloseTrigger.trigger(event[2]);
|
||||
case "websocket_failure":
|
||||
this.onWebsocketFailureTrigger.trigger({url: event[2], failReason: event[3]});
|
||||
case "websocket_message":
|
||||
this.onWebsocketMessageTrigger.trigger({url: event[2], message: event[3], isBinary: event[4]});
|
||||
case "websocket_success":
|
||||
this.onWebsocketSuccessTrigger.trigger({url: event[2], handle: event[3]});
|
||||
default:
|
||||
Log.error("Unknown cc event: " + eventName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,28 +31,28 @@ class Log {
|
||||
public static function info(msg:Dynamic, ?pos:haxe.PosInfos) {
|
||||
writer.writeLn("[INFO][" + pos.className + "]: " + Std.string(msg));
|
||||
#if webconsole
|
||||
Debug.printWeb("["+Net.instance.networkID+"][INFO][" + pos.className + "]: " + Std.string(msg));
|
||||
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("["+Net.instance.networkID+"][WARN][" + pos.className + "]: " + Std.string(msg));
|
||||
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("["+Net.instance.networkID+"][ERRO][" + pos.className + "]: " + Std.string(msg));
|
||||
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("["+Net.instance.networkID+"][DEBG][" + pos.className + "]: " + Std.string(msg));
|
||||
Debug.printWeb("[DEBG][" + pos.className + "]: " + Std.string(msg));
|
||||
#end
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ class Timer {
|
||||
/**
|
||||
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);
|
||||
this.callback = callback;
|
||||
|
||||
|
||||
@@ -34,12 +34,16 @@ class Net {
|
||||
this.interfaces.push(Loopback.instance);
|
||||
|
||||
for (interf in interfaces){
|
||||
interf.onMessage.handle(pack -> handle(pack,interf));
|
||||
interf.listen(networkID);
|
||||
interf.listen(BRODCAST_PORT);
|
||||
setupInterf(interf);
|
||||
}
|
||||
}
|
||||
|
||||
private function setupInterf(interf: INetworkInterface) {
|
||||
interf.onMessage.handle(pack -> handle(pack,interf));
|
||||
interf.listen(networkID);
|
||||
interf.listen(BRODCAST_PORT);
|
||||
}
|
||||
|
||||
/**
|
||||
Called when a new package comes in.
|
||||
**/
|
||||
|
||||
@@ -74,7 +74,11 @@ class Routing {
|
||||
data: null
|
||||
}
|
||||
|
||||
interf.send(pack.fromID,Net.instance.networkID,response);
|
||||
// HACK: Because Lua is singelthreaded the computer we respond to can get overwhelmed with
|
||||
// the responses and can swollow events.
|
||||
new Timer(Net.instance.networkID / 3,()->{
|
||||
interf.send(response.toID,Net.instance.networkID,response);
|
||||
});
|
||||
}
|
||||
|
||||
private function genRouteList(): Array<{id:NetworkID,cost:Int}> {
|
||||
@@ -109,18 +113,16 @@ class Routing {
|
||||
return;
|
||||
}
|
||||
|
||||
Log.debug("a");
|
||||
|
||||
var fullCost = cost+interf.getBaseRoutingCost();
|
||||
|
||||
if (this.routingTable.exists(toID)){
|
||||
if (this.routingTable[toID].cost > fullCost){
|
||||
Log.debug("Better route: " + toID + " -> " + interf.name() + ":$"+fullCost);
|
||||
Log.info("Better route: " + toID + " -> " + interf.name() + ":$"+fullCost);
|
||||
this.routingTable[toID] = {interf:interf,cost:cost + interf.getBaseRoutingCost()};
|
||||
}
|
||||
}else{
|
||||
this.routingTable[toID] = {interf:interf,cost:cost + interf.getBaseRoutingCost()};
|
||||
Log.debug("New route: " + toID + " -> " + interf.name() + ":$"+fullCost);
|
||||
Log.info("New route: " + toID + " -> " + interf.name() + ":$"+fullCost);
|
||||
this.onNewNeigborTrigger.trigger(toID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ class Modem implements INetworkInterface implements IPeripheral {
|
||||
KernelEvents.instance.onModemMessage.handle(params ->{
|
||||
if (params.addr == this.addr){
|
||||
var pack:Package = {
|
||||
fromID: params.replyChannel,
|
||||
toID: params.channel,
|
||||
fromID: params.message.fromID,
|
||||
toID: params.message.toID,
|
||||
msgID: params.message.msgID,
|
||||
type: params.message.type,
|
||||
data: params.message.data,
|
||||
|
||||
@@ -22,6 +22,11 @@ class TermIO {
|
||||
}
|
||||
|
||||
var size = output.getSize();
|
||||
var cPos = output.getCursorPos();
|
||||
|
||||
if (cPos.y >= size.y){
|
||||
newLine();
|
||||
}
|
||||
|
||||
for (i in 0...Math.floor(text.length / size.x) + 1) {
|
||||
output.write(text.substr(i * size.x, size.x));
|
||||
@@ -38,7 +43,7 @@ class TermIO {
|
||||
|
||||
if (cPos.y == output.getSize().y) {
|
||||
output.scroll(1);
|
||||
output.setCursorPos(0, cPos.y);
|
||||
output.setCursorPos(0, cPos.y - 1);
|
||||
} else {
|
||||
output.setCursorPos(0, cPos.y + 1);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package util;
|
||||
|
||||
import cc.HTTP;
|
||||
import kernel.net.Net;
|
||||
import cc.ComputerCraft;
|
||||
import kernel.Log;
|
||||
|
||||
@@ -17,7 +18,7 @@ class Debug {
|
||||
|
||||
#if webconsole
|
||||
public static function printWeb(msg:String) {
|
||||
HTTP.post("http://127.0.0.1:8080/", msg);
|
||||
HTTP.post("http://127.0.0.1:8080/"+Net.instance.networkID, msg);
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user