diff options
| author | Alexander M Pickering <alex@cogarr.net> | 2025-01-12 22:45:37 -0600 |
|---|---|---|
| committer | Alexander M Pickering <alex@cogarr.net> | 2025-01-12 22:45:37 -0600 |
| commit | 90ee66a3a6aae10fd84f3f43844db55229933e37 (patch) | |
| tree | f723f918871c3296636ef2538a1a29a23050e520 /src | |
| parent | decb72f936060a65bff18e9cbf33642ea3a71cd0 (diff) | |
| download | ggj25-90ee66a3a6aae10fd84f3f43844db55229933e37.tar.gz ggj25-90ee66a3a6aae10fd84f3f43844db55229933e37.tar.bz2 ggj25-90ee66a3a6aae10fd84f3f43844db55229933e37.zip | |
work
Diffstat (limited to 'src')
| -rw-r--r-- | src/controller.moon | 16 | ||||
| -rw-r--r-- | src/controller_bridge.js | 36 | ||||
| -rw-r--r-- | src/controller_test.moon | 8 | ||||
| -rw-r--r-- | src/js_bridge.js | 66 | ||||
| -rw-r--r-- | src/main.lua | 7 | ||||
| -rw-r--r-- | src/net.moon | 114 | ||||
| -rw-r--r-- | src/net_test.moon | 44 | ||||
| -rw-r--r-- | src/preload.lua | 8 | ||||
| -rw-r--r-- | src/rng.moon | 30 | ||||
| -rw-r--r-- | src/router.moon | 245 | ||||
| -rw-r--r-- | src/router_test.moon | 49 | ||||
| -rw-r--r-- | src/ui.moon | 6 | ||||
| -rw-r--r-- | src/ui/button.moon | 4 | ||||
| -rw-r--r-- | src/ui/textbox.moon | 2 | ||||
| -rw-r--r-- | src/util.lua | 9 | ||||
| -rw-r--r-- | src/world.moon | 9 |
16 files changed, 577 insertions, 76 deletions
diff --git a/src/controller.moon b/src/controller.moon new file mode 100644 index 0000000..b86062e --- /dev/null +++ b/src/controller.moon @@ -0,0 +1,16 @@ + +ui = require("ui") + +controller = {} + +am.eval_js(require("controller_bridge")) + +controller.pump = () -> + print("pumping controller") + am.eval_js("CONT.loop()") + state = am.eval_js("CONT.last_state") + print(state) + + +controller + diff --git a/src/controller_bridge.js b/src/controller_bridge.js new file mode 100644 index 0000000..84bc040 --- /dev/null +++ b/src/controller_bridge.js @@ -0,0 +1,36 @@ + +window.addEventListener("gamepadconnected", function(e) { + console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.", + e.gamepad.index, + e.gamepad.id, + e.gamepad.buttons.length, + e.gamepad.axes.length + ); + var i; + for(i = 0; i < e.gamepad.buttons.length; i++){ + CONT.last_state.buttons[i] = false; + } + for(i = 0; i < e.gamepad.axes.length; i++){ + CONT.last_state.axes[i] = 0; + } + CONT.gp = navigator.getGamepads()[0]; +}); + +window.CONT = { + messages: [], + gp: null, + last_state: { + buttons: [], + axes: [] + }, + loop: function() { + var i; + if(CONT.gp == null) return; + for(i = 0; i < CONT.gp.buttons.length; i++){ + CONT.last_state.buttons[i] = CONT.gp.buttons[i].pressed; + } + for(i = 0; i< CONT.gp.axes.length; i++){ + CONT.last_state.axes[i] = CONT.gp.axes[i]; + } + } +}; diff --git a/src/controller_test.moon b/src/controller_test.moon new file mode 100644 index 0000000..45d1dcb --- /dev/null +++ b/src/controller_test.moon @@ -0,0 +1,8 @@ +controller = require("controller") +ui = require("ui") + +button = ui.button(0,0,100,100,"pump") +button.on = () => + controller.pump! + + diff --git a/src/js_bridge.js b/src/js_bridge.js index 0b5ba08..ecbbd70 100644 --- a/src/js_bridge.js +++ b/src/js_bridge.js @@ -10,8 +10,10 @@ function genRanHex(size) { window.PEER = { event_queue: [], peers: {}, - message_queue: [], + peer_message_queue: [], + connection_message_queue: [], connections: {}, + to_connect: [], // Sometimes we have to wait a tick before the connection is ready. create: function(tbl) { var name = tbl.name; var options = tbl.options; @@ -19,6 +21,11 @@ window.PEER = { var peer = new Peer(name, options); PEER.peers[name] = peer; }, + delete_peer: function(tbl) { + var name = tbl.name; + console.log("[JS] Deleting peer " + name); + delete PEER.peers[name]; + }, on: function(tbl) { var name = tbl.name; var e = tbl.e; @@ -27,10 +34,12 @@ window.PEER = { PEER.peers[name].on(e, function(data) { console.log("[JS] Peer " + name + " received " + e); if(e == "connection"){ - PEER.connections[[name,data.peer]] = data; + PEER.connections[[name,data.peer].sort()] = data; + console.log("[JS] Peer.connections is now"); + console.log(PEER.connections); data = [name,data.peer]; // rewrite connections } - PEER.message_queue.push({"message":message, "data":{ + PEER.peer_message_queue.push({"message":message, "data":{ "call": "on", "peer": name, "e": e, @@ -39,12 +48,17 @@ window.PEER = { }); }, connect: function(tbl) { - var name = tbl.name; - var id = tbl.id; - console.log("[JS] connecting " + name + " to " + id); - var conn = PEER.peers[name].connect(id); - PEER.connections[[name,id]] = conn; - return [name,id]; + var source = tbl.source; + var dest = tbl.dest; + console.log("[JS] connecting " + source + " to " + dest); + var conn = PEER.peers[source].connect(dest); + PEER.connections[[source,dest].sort()] = conn; + // Send a hello to always establish a connection + console.log("[JS] sending hello");// doesn't seem to show up in the output, but its needed so we don't drop the first message. + conn.send("Hello"); + console.log("[JS] Connect called, PEER.connections is"); + console.log(PEER.connections); + return [source,dest]; }, disconnect: function(tbl) { PEER.peers[tbl.name].disconnect(); @@ -56,13 +70,13 @@ window.PEER = { PEER.peers[tbl.name].destroy(); }, send: function(tbl){ - var name = tbl.name; - var id = tbl.id; + var source = tbl.source; + var dest = tbl.dest; var data = tbl.data; - console.log("[JS] " + name + " is sending " + data + " to " + id); - console.log(PEER.connections[[name,id]]); + console.log("[JS] " + source + " is sending " + data + " to " + dest); + console.log(PEER.connections[[source,dest].sort()]); console.log(data); - PEER.connections[[name,id]].send(data); + PEER.connections[[source,dest].sort()].send(data); }, close: function(tbl){ var name = tbl.name; @@ -70,20 +84,28 @@ window.PEER = { PEER.connections[[name,id]].close(); }, conn_on: function(tbl){ - var name = tbl.name; - var id = tbl.id; + var source = tbl.source; + var dest = tbl.dest; var e = tbl.e; var message = tbl.message; - console.log("[JS] Setting hook for [" + name + "," + id + "] " + e + "," + message); - PEER.connections[[name,id]].on(e, function(c) { - console.log("[JS] connection between " + name + " and " + id + " received " + e); - PEER.message_queue.push({"message":message, "data":{ + console.log("[JS] Setting hook for [" + dest + "," + source + "] " + e + "," + message); + console.log(PEER.connections[[dest,source]]); + console.log(PEER.connections); + PEER.connections[[dest,source].sort()].on(e, function(c) { + console.log("[JS] connection between " + dest + " and " + source + " received " + e); + PEER.connection_message_queue.push({"message":message, "data":{ "call": "on", - "peer": name, - "id": id, + "peer": source, + "dest": dest, "e": e, "data": c }}); }); }, + conn_field: function(tbl){ + var name = tbl.name; + var id = tbl.id; + var field = tbl.field; + return PEER.connections[[name,id].sort()][field]; + } }; diff --git a/src/main.lua b/src/main.lua index 55b7ef5..9f55c43 100644 --- a/src/main.lua +++ b/src/main.lua @@ -9,9 +9,14 @@ local ui = require("ui") win.scene:append(ui.node) --require("world_test") --require("net_test") -require("ui_test") +--require("ui_test") +--require("router_test") +require("controller_test") require("log").observe(function(chunk) + if chunk.tags.ui then + return + end print(table.concat({"[",chunk.level:upper(),"]",os.date()," > ",chunk.message})) end) --am.eval_js(require("js_bridge")) diff --git a/src/net.moon b/src/net.moon index b22d6f2..acd75c7 100644 --- a/src/net.moon +++ b/src/net.moon @@ -1,54 +1,103 @@ -- Handles the bridge to javascript to do peer-to-peer connections - log = require("log") +rng = require("rng") +util = require("util") + net = {} -net.initalize = () -> +initalized = false +initalize = () -> am.eval_js(require("js_bridge")) + initalized = true net.call = (method, args) -> - print("About to eval") + if not initalized + initalize! result = am.eval_js("window.PEER." .. method .. "(" .. am.to_json(args) .. ")") - print("Done evaling") result -net.pull = () -> - messages = am.eval_js("window.PEER.message_queue") - am.eval_js("window.PEER.message_queue = []") +net.pull_peers = () -> + if not initalized + initalize! + messages = am.eval_js("window.PEER.peer_message_queue") + am.eval_js("window.PEER.peer_message_queue = []") + messages + +net.pull_connections = () -> + if not initalized + initalize! + messages = am.eval_js("window.PEER.connection_message_queue") + am.eval_js("window.PEER.connection_message_queue = []") messages callbacks = {} peers = {} connections = {} +--Connections are always create js side, this is just it's lua representation class Connection + @connections = {} + @methods = util.reverse({"data","open","close","error"}) new: (source, dest) => @source = source @dest = dest + @get: (source, dest) => + key = table.concat({source,dest},",") + if @connections[key] + return @connections[key] + @@connections[key] = Connection(source,dest) + @@connections[key] on: (event, callback) => + if not @@methods[event] + error("Tried to set an unknown event (" .. event .. ") on a connection") newid = #callbacks + 1 callbacks[newid] = callback - net.call("conn_on", {name: @source, id: @dest, e: event, message: newid}) + while am.eval_js('window.PEER.connections[["' .. @source .. '","' .. @dest .. '"].sort()] == null') + print("Waiting for peer") + coroutine.yield("Waiting for peer") + --not backwards, "on" is always called on the incomming connection + net.call("conn_on", {source: @dest, dest: @source, e: event, message: newid}) send: (msgname, msg) => net.validate(msgname, msg) - net.call("send",{name: @source, id: @dest, data: msg}) + net.call("send",{source: @source, dest: @dest, data: {msgname, msg}}) class Peer + @methods = util.reverse({"open","connection","call","close","disconnected","error"}) new: (id) => - net.call("create",{name: id}) - if id - @id = id - peers[id] = @ + -- We can't create peers with peerjs-generated random ids, + -- their names are too hard to type. + -- Instead use (Year, Month), and append 4 random digits + -- to make a unique id (people can't join past midnight at the + -- end of the month, oh well) + -- Also needs to handle id-already-taken errors. + id = id or @generate_id! + while peers[id] + id = @generate_id! + @id = id + peers[@id] = @ + net.call("create",{name: @id}) + log.info("Creating peer: " .. @id, {"net"}) + generate_id: () => + os.date("%Y%e") .. rng.randomstring("ab",1) --.. rng.numstring(4) + replace_id: () => + log.info("Regenerating id for peer: " .. @id, {"net"}) + -- peers[@id] = nil TODO: uncomment, this breaks when running multiple peers from the same tab. + net.call("delete_peer",{name: @id}) + @id = @generate_id! + peers[@id] = @ + net.call("create", {name: @id}) on: (event, callback) => + if not @@methods[event] + error("Tried to set an unknown event (" .. event .. ") on a peer.") newid = #callbacks + 1 callbacks[newid] = callback net.call("on",{name: @id, message:newid, e: event}) connect: (id, options) => - conn = net.call("connect", {name: @id, id: id}) + conn = net.call("connect", {source: @id, dest: id}) log.info("Got connection: " .. tostring(conn), {"net"}) - Connection(conn[1],conn[2]) + Connection\get(conn[1],conn[2]) net.Peer = Peer @@ -84,9 +133,10 @@ net.register_message = (name, format) -> assert(type(format) == "table", "Format must be a table") format.required = format.required or {} format.optional = format.optional or {} - assert(next(format.required) or next(format.optional), "No fields found") + if not (next(format.required) or next(format.optional)) + log.warn("Message " .. name .. " registered with no fields.") for set in *({format.required, format.optional}) - for field, type_ in pairs(format.required) + for field, type_ in pairs(set) if type(type_) == "string" key = string.format("%s\0%s\0%s",name,field,type_) if not formatcache[key] @@ -97,6 +147,7 @@ net.register_message = (name, format) -> message_callbacks[name] = {} net.validate = (name, message) -> + log.info("Validating message:" .. tostring(message), {"net"}) assert(type(message) == "table", "Message must be a table") format = messages[name] required = {} @@ -122,27 +173,48 @@ net.listen = (name, callback, id) -> net.defen = (name, id) -> message_callbacks[name][id] = nil +-- net.route = (conn, name, data) -> +-- if message_callbacks[name] +-- for id, callback in pairs(message_callbacks[name]) +-- ret = message_callbacks[name](conn, + rewrite_events = { connection: (message) -> - message.data.data = Connection(message.data.data[1], message.data.data[2]) + conn = Connection\get(message.data.data[2], message.data.data[1]) + assert(conn, "Failed to build conn?") + assert(conn.source and conn.dest) + message.data.data = conn } net.pump = () -> - msg_ = net.pull! - log.info("Processing " .. tostring(#msg_) .. " messages", {"net"}) + msg_ = net.pull_peers! + log.info("Processing " .. tostring(#msg_) .. " peer messages", {"net"}) for message in *msg_ log.info(tostring(message), {"net", message.data.peer}) if rewrite_events[message.data.e] log.info("Rewriting data due to " .. message.data.e .. " event", {"net", message.data.peer}) rewrite_events[message.data.e](message) log.info(tostring(message), {"net", message.data.peer}) + if not message.data.peer and message.data.e == "open" + log.info("Setting peerid for a peer that didn't have one " ..tostring(message), {"net"}) peer = peers[message.data.peer] - assert(peer, "Failed to find peer:" .. message.data.peer) + assert(peer, "Failed to find peer:" .. message.data.peer .. " peers:" .. tostring(net.peers!)) callback = callbacks[message.message] assert(callback, "Failed to find callback " .. message.message .. " on peer " .. message.data.peer) callback(peer,message.data) + msg_ = net.pull_connections! + log.info("Processing " .. tostring(#msg_) .. " connection messages", {"net"}) + for message in *msg_ + log.info(tostring(message), {"net", message.data.peer}) + connection = Connection\get(message.dest, message.peer) + callback = callbacks[message.message] + assert(callback, "Fakled to find callback " .. message.message .. " for message" .. tostring(message)) + callback(connection, message.data) + net.peers = () -> peers +initalize! + net diff --git a/src/net_test.moon b/src/net_test.moon index c4989ab..25d79ca 100644 --- a/src/net_test.moon +++ b/src/net_test.moon @@ -6,7 +6,6 @@ test_button = ui.button(0,0,100,100,"Test") pull_button = ui.button(100,0,100,100,"Pull") send_button = ui.button(200,0,100,100,"Send") -net.initalize! peer1, peer2, conn = nil, nil, nil test_button.on = (e) => net.register_message("HelloRequest",{ @@ -28,7 +27,7 @@ test_button.on = (e) => print("Peer 1 received data!") ) peer1\on("error",(data) => - print("Peer 1 error:", data) + error("Peer 1 error:" .. tostring(data)) ) peer1\on("connection",(message)=> print("Peer1 connection:", message) @@ -37,25 +36,36 @@ test_button.on = (e) => ) ) peer2 = net.Peer() - peer2\on("open", (data) => - print("Peer2 opened",data) - conn = peer2\connect("blah-blah3") - conn\on("data",(data) => - print("Peer2 data:",data) + peer2_startup = (peer) -> + peer\on("open", (data) => + print("Peer2 opened",data) + conn = peer\connect(peer1.id) + conn\on("data",(data) => + print("Peer2 data:",data) + ) ) - ) - peer2\on("connection", (data) => - print("Peer2 connected", data) - ) - peer2\on("error", (data) => - error("Net error: " .. tostring(data)) - ) - peer2\on("data", (data) => - print("Peer2 on data",data) - ) + peer\on("connection", (message) => + print("Peer2 connected", message) + message.data\on("data",(data)=> + print("Peer2 received data:", data) + ) + ) + peer\on("error", (message) => + print("peer2 error:", message) + if message.data.type == "unavailable-id" + peer\replace_id! + peer2_startup(peer) + return + error("Peer2 error: " .. tostring(data)) + ) + peer\on("data", (data) => + print("Peer2 on data",data) + ) + peer2_startup(peer2) pull_button.on = (e) => net.pump! + print("Peers:" .. tostring(net.peers!)) send_button.on = (e) => conn\send("HelloRequest",{ diff --git a/src/preload.lua b/src/preload.lua index 8610cbc..ca2dc9a 100644 --- a/src/preload.lua +++ b/src/preload.lua @@ -7,22 +7,22 @@ local old_traceback = debug.traceback debug.traceback = function(...) local orig_traceback = old_traceback(...) local noprint = {} - return orig_traceback:gsub("([ \t]*)([%w/_]+%.lua):(%d+):([^\n]*)",function(spaces, filename, linenum, rest) + return orig_traceback:gsub("([%w/_]+%.lua):(%d+):",function(filename, linenum) --If our file is not moonscript, we won't have a debug file local debugfile = am.load_string(filename .. ".X") if not debugfile then - return spaces .. filename .. ":" .. linenum .. ":" .. rest + return filename .. ":" .. linenum .. ":" end debugfile = debugfile .. "\n" for line in debugfile:gmatch("([^\n]+)\n") do - _,_,pos,lua,moon = line:find("(%d+)%s+(%d+):%b[] >> (%d+)") + local _,_,pos,lua,moon = line:find("(%d+)%s+(%d+):%b[] >> (%d+)") if pos and lua and moon and tonumber(linenum) == tonumber(lua) then filename = filename:gsub(".lua$",".moon") linenum = moon break end end - return string.format("%s%s:%d: %s", spaces, filename, linenum, rest) + return string.format("%s:%d:", filename, linenum) end) end diff --git a/src/rng.moon b/src/rng.moon new file mode 100644 index 0000000..d732386 --- /dev/null +++ b/src/rng.moon @@ -0,0 +1,30 @@ + +rng = {} +totally_random_seed = tonumber(os.date("%Y%H%M%S")) +math.randomseed(totally_random_seed) + +rng.generator = (seed, m, n) -> + seed = seed or tonumber(os.date("%Y%S")) + co = coroutine.wrap(() -> + while true + math.randomseed(seed) + seed = math.random(m,n) + coroutine.yield(seed) + ) + co, seed + +rng.randomstring = (charset, length) -> + t = {} + charset_len = #charset + for i = 1, length + char = math.random(charset_len) + t[i] = charset\sub(char,char) + table.concat(t) + +rng.hexstring = (length) -> + rng.randomstring("0123456789ABCDEF", length) + +rng.numstring = (length) -> + rng.randomstring("0123456789", length) + +rng diff --git a/src/router.moon b/src/router.moon new file mode 100644 index 0000000..7ef9bfc --- /dev/null +++ b/src/router.moon @@ -0,0 +1,245 @@ +-- Network router, implements RAFT consensus with modificationes +net = require("net") +log = require("log") + +router = {} + +-- Election messages +net.register_message("Prevote",{required:{peer:"string"}}) +net.register_message("SurveyElection",{}) +net.register_message("ResponseElection",{required:{candidate: "string"}}) +net.register_message("CompleteElection",{required:{elected: "string"}}) + +-- Cluster state +net.register_message("RequestClusterInfo",{}) +net.register_message("RespondClusterInfo",{required:{ + peerlist: "table" + elected: "string" + term:"number" + prevotes: "table" +}}) + +net.register_message("RequestWorldInfo",{}) +net.register_message("RespondWorldInfo",{required:{ + entities: "table" +}}) + +--Heartbeat +net.register_message("RequestHeartbeat",{required:{n1:"number",n2:"number"}}) +net.register_message("RespondHeartbeat",{required:{n:"number",time:"number"}}) + +--Suggestion/commit, use ("peer","entity","time") as key to find messages later to commit. +-- -> peers can only suggest 1 state change per entity, per time +--Suggestions are broadcast from peers +net.register_message("Suggest",{required:{ + entity:"number" + state:"table" + peer:"string" + time:"number" +}}) +--Commits are broadcast from the elected peer +net.register_message("Commit",{required:{ + nonce: "number" + entity:"number" + peer:"string" + suggest_time:"number" + commit_time:"number" +}}) +-- When a peer misses a committed message, re-request it +net.register_message("RequestReplay",{required:{nonce: "number"}}) +net.register_message("RespondReplay",{required:{ + nonce: "number" + entity: "number" + peer: "string" + state: "table" + suggest_time: "number" + commit_time: "number" +}}) +--Rejections are also broadcast from the elected peer +net.register_message("Reject",{required:{ + nonce: "number" + entity:"number" + peer:"string" + suggest_time:"number" + reject_time:"number" +}}) +--Suggest + commit from the elected peer, so we can throw away old state. +net.register_message("Simplify",{required:{ + entity:"number" + state:"table" + time:"number" +}}) + +--Testing +net.register_message("Raw",{optional:{s:"string"}}) + +class Queue + new: () => + @queue = {} + push: (item) => + table.insert(@queue, item) + pop: () => + table.remove(@queue, 1) + +class Router + new: () => + @peerlist = {} + @routes = {} + + -- "uninitalized", "peer", "candidate", "elected" + @state = "unitialized" + + -- [peerid] = "votes for peerid" + @prevotes = {} + @term = 0 + + -- The uncommited queue + @uncommited = Queue! + + initalize: (id) => + @set_route("RequestClusterInfo",(conn, message) => + print("Requested cluster info:", message) + peerlist = {} + for peerid, _ in pairs(@peerlist) + table.insert(peerlist, peerid) + conn\send("RespondClusterInfo",{ + peerlist: peerlist + elected: @elected + term: @term + prevotes: @prevotes + }) + true + ) + peer_setup = (peer) -> + peer\on("error",(message) => + if message.data.type == "unavailable-id" + peer\replace_id! + peer_setup(peer) + return + error("Peer setup error: " .. tostring(message)) + ) + peer\on("open",(message) => + print("Peer",peer.id, "opened:",message) + peer.open = true + ) + peer = net.Peer! + @peer = peer + peer_setup(peer) + while not peer.open + coroutine.yield("Waiting for open") + net.pump! + peerlist = @peerlist + router = @ + peer\on("connection",(message) => + print("Peer",peer.id, "got connection", message) + assert(message.data.dest == peer.id) + peerlist[message.data.source] = message.data + message.data\send("Raw",{s: "Hello after conn"}) + message.data\on("data",(datamsg) => + print("Peer ",peer.id," got data:",datamsg) + router\route(message.data, datamsg.data[1], datamsg.data[2]) + print("done routing") + ) + message.data\on("error",(msg) => + error(msg) + ) + ) + if id + print("Doing id fork") + connected = false + conn = peer\connect(id) + conn\on("open", (message) => + print("Conn got open message") + connected = true + ) + while not connected + coroutine.yield("Waiting for client to connect") + net.pump! + conn\on("error", (message) => + error(message) + ) + --Assume we vote for ourselves, and our peer votes for themselves + @prevotes[peer.id] = peer.id + @prevotes[id] = id + @state = "peer" + log.info("Peer passed, I'm a peer of " .. id, {"net"}) + --while not connected + -- coroutine.yield("Give it a second to connect") + -- net.pump! + got_hello = false + conn\on("data", (message) => + print("Peer got message:", message) + got_hello = true + ) + while not got_hello + coroutine.yield("Waiting for hello") + net.pump! + @peerlist[peer.id] = peer + @peerlist[id] = conn + clusterinfo = @sync(conn, "RequestClusterInfo", {}) + print("Got cluster info:", clusterinfo) + + else + log.info("No peer passed, I'm the elected peer: " .. peer.id, {"net"}) + @state = "elected" + @elected = peer.id + @term += 1 + -- Vote for ourselves + @prevotes[peer.id] = peer.id + -- Add ourselves to the peerlist + @peerlist[peer.id] = peer + while true + coroutine.yield(peer.id) + net.pump! + + sync: (conn, msgfmt, msg) => + ret = nil + conn\on("data", (message) => + ret = message + ) + conn\send(msgfmt, msg) + while not ret + coroutine.yield("Waiting on " .. msgfmt) + net.pump! + return ret + + broadcast: (msgfmt, message) => + for peerid, conn in pairs(@peerlist) + if peerid ~= @peer.id + conn\send(msgfmt, message) + + send_elected: (msgfmt, message) => + elected_conn = @peerlist[@elected] + elected_conn\send(msgfmt, message) + + set_route: (msgfmt, callback) => + if @routes[msgfmt] + log.warn("Overwriting callback for message " .. msgfmt, {"net"}) + @routes[msgfmt] = {callback} + + listen: (msgfmt, callback, id) => + id = id or {} + @routes[msgfmt] = @routes[msgfmt] or {} + @routes[msgfmt][id] = callback + + defen: (msgfmt, id) => + assert(@routes[msgfmt]) + assert(@routes[msgfmt][id]) + @routes[msgfmt][id] = nil + + route: (conn, msgfmt, message) => + print("Got route",conn,msgfmt,message) + assert(type(msgfmt) == "string", "Message format must be a string") + assert(type(message) == "table", "Message must be a table") + if @routes[msgfmt] + routed_any = false + for _, r in pairs(@routes[msgfmt]) + routed_any = true + if r(@, conn, message) + break + if not routed_any + log.warn("No routes found for message format:" .. msgfmt, {"net"}) + else + log.warn("No message callback registered for format " .. msgfmt .. " routes are: " .. tostring(@routes), {"net"}) + +{:Router} diff --git a/src/router_test.moon b/src/router_test.moon new file mode 100644 index 0000000..e8d67c8 --- /dev/null +++ b/src/router_test.moon @@ -0,0 +1,49 @@ + +import Router from require("router") +ui = require("ui") + +button_client = ui.button(0,0,100,100,"Client") +client_id = ui.textbox(0,100,100,32,"") +button_server = ui.button(-300,0,100,100,"Server") +co = nil +button_client.on = () => + if not co + co = coroutine.create((id) -> + router = Router! + print("Made router!") + router\initalize(id) + print("Finished initalize!") + router + ) + print("Pumping... " .. coroutine.status(co)) + if coroutine.status(co) ~= "dead" + succ, err = coroutine.resume(co, client_id.text.text) + if not succ + error(debug.traceback(co,err)) + if type(err) == "string" + print(err) + @text.text = err + else + router = err + print("Got to the end of co!") +button_server.on = () => + if not co + co = coroutine.create(() -> + router = Router! + router\initalize! + router + ) + print("Pumping... " .. coroutine.status(co)) + if coroutine.status(co) ~= "dead" + succ, err = coroutine.resume(co) + if not succ + error(debug.traceback(co,err)) + if err + if coroutine.status(co) ~= "dead" + print(err) + @text.text = err + else + print("Got router", err) + @text.text = err.peer.id + + diff --git a/src/ui.moon b/src/ui.moon index 7a06522..4746302 100644 --- a/src/ui.moon +++ b/src/ui.moon @@ -1,5 +1,6 @@ hc = require("party.hardoncollider.init") win = require("window") +log = require("log") Button = require("ui.button") Joystick = require("ui.joystick") Textbox = require("ui.textbox") @@ -29,6 +30,7 @@ ui.joystick = (x,y,r) -> ui.node\append(joystick.node) bounds = ui_world\circle(x,y,r) ui.events.touch[bounds] = joystick + joystick ui.textbox = (x,y,width,height,value,placeholder) -> value = value or "" @@ -38,6 +40,7 @@ ui.textbox = (x,y,width,height,value,placeholder) -> bounds = ui_world\rectangle(x,y,width,height) ui.events.mouse[bounds] = textbox ui.events.keyboard[textbox] = true + textbox ui.node = am.group! @@ -69,10 +72,13 @@ ui.node\action(() -> match = ui.events.mouse[collider] if match has_fire(match) + --log.info("Found button under mouse:" .. tostring(match), {"ui","mouseover"}) match\fire(mo_tbl) if down + log.info("Found button under mouse:" .. tostring(match), {"ui","mousedown"}) match\fire(md_tbl) if up + log.info("Found button under mouse:" .. tostring(match), {"ui","mouseup"}) match\fire(mu_tbl) if math.length(wheel) > 0 etbl = diff --git a/src/ui/button.moon b/src/ui/button.moon index 789ecae..5915bc9 100644 --- a/src/ui/button.moon +++ b/src/ui/button.moon @@ -65,10 +65,6 @@ class Button am.translate(w - @@up_lower_right.width, -(h - @@up_lower_right.height))\append( @@up_lower_right )) - @up_sprites\append( - am.translate(@@up_upper_left.width, -@@up_upper_right.height)\append( - am.text(text, "left","top", color.am_color.foreground) - )) @down_sprites\append(@@down_upper_left) @down_sprites\append( am.translate(@@down_upper_left.width,0)\append( diff --git a/src/ui/textbox.moon b/src/ui/textbox.moon index 3c6151d..c1bd521 100644 --- a/src/ui/textbox.moon +++ b/src/ui/textbox.moon @@ -65,7 +65,6 @@ class Textbox extends Button update_cursor_pos: () => @.cursor("translate").x = @cursor_pos * 9 fire: (e) => - print("cursor pos is", @cursor_pos) if e.event == "mouse_down" @down! if @on @@ -74,7 +73,6 @@ class Textbox extends Button if add_key t = @text.text for key in *e.data - print("analyzing key:",key) if key == "delete" or key == "backspace" @cursor_pos -=1 if @cursor_pos < 0 diff --git a/src/util.lua b/src/util.lua index 8fccab2..52597b2 100644 --- a/src/util.lua +++ b/src/util.lua @@ -81,4 +81,13 @@ function tostring(el) return old_tostring(el) end +function util.reverse(tbl, val) + val = val or true + local ret = {} + for _,v in ipairs(tbl) do + ret[v] = val + end + return ret +end + return util diff --git a/src/world.moon b/src/world.moon index d511e88..3e4e09a 100644 --- a/src/world.moon +++ b/src/world.moon @@ -6,13 +6,12 @@ ecs = require("ecs") --Use a collider to decide what to render x = { world_e: ecs.Entity(1) - world_x: 0 + -- local offsets from the world + world_x: 0 world_y: 0 + -- Have we selected an input type yet? controller_selected: false - hosting: false - peer_channels: {} - network_proposed: {} - network_commited: {} + -- Level information level: { graphics:{} entities:{} |
