Compare commits

...

8 Commits

Author SHA1 Message Date
ecf4f9c719 routing fix 2022-03-01 12:59:23 +01:00
4903014ebd webconsole id in url 2022-03-01 12:59:02 +01:00
1a808055ea improved README 2022-03-01 12:58:37 +01:00
2402eb1a44 kernel events handle in own func 2022-03-01 12:57:03 +01:00
639392c6de Net setup interf in function 2022-03-01 12:56:15 +01:00
3e275e2b06 webconsole improvements 2022-03-01 12:55:23 +01:00
a20d3bc07e Timer float 2022-03-01 12:54:36 +01:00
d4703fb725 TermIO scroll fix 2022-02-28 20:57:40 +01:00
11 changed files with 124 additions and 100 deletions

View File

@@ -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.

View File

@@ -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();
})

View File

@@ -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();
});
}
}

View File

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

View File

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

View File

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

View File

@@ -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.
**/

View File

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

View File

@@ -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,

View File

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

View File

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