diff --git a/changes.md b/changes.md index ba28777..515c7bd 100644 --- a/changes.md +++ b/changes.md @@ -5,15 +5,45 @@ Update: 2.0.0 Big update **Note:** After doing some testing, I have noticed that using multi-objects are slightly, quite a bit, faster than using (coroutines)multi:newthread(). Only create a thread if there is no other possibility! System threads are different and will improve performance if you know what you are doing. Using a (coroutine)thread as a loop with a timer is slower than using a TLoop! If you do not need the holding features I strongly recommend that you use the multi-objects. This could be due to the scheduler that I am using, and I am looking into improving the performance of the scheduler for (coroutine)threads. This is still a work in progress so expect things to only get better as time passes! #Added: -- require("multi.integration.networkManager") -- multi:newNode(tbl: settings) -- multi:newMaster(tbl: settings) -- multi:nodeManager(port) +- `nGLOBAL = require("multi.integration.networkManager").init()` +- `node = multi:newNode(tbl: settings)` +- `master = multi:newMaster(tbl: settings)` +- `multi:nodeManager(port)` + +Node: +- node:sendTo(name,data) +- node:pushTo(name,data) +- node:peek() +- node:pop() + +Master: +- master:doToAll(func) +- master:register(name,node,func) +- master:execute(name,node,...) +- master:newNetworkThread(tname,func,name,...) +- master:getFreeNode() +- master:getRandomNode() +- master:sendTo(name,data) +- master:pushTo(name,data) +- master:peek() +- master:pop() Note: These examples assume that you have already connected the nodes to the node manager. Also you do not need to use the node manager, but sometimes broadcast does not work as expected and the master doesnot connect to the nodes. Using the node manager offers nice features like: removing nodes from the master when they have disconnected, and automatically telling the master when nodes have been added. **NodeManager.lua** ```lua +package.path="?/init.lua;?.lua;"..package.path +multi = require("multi") +local GLOBAL, THREAD = require("multi.integration.lanesManager").init() +nGLOBAL = require("multi.integration.networkManager").init() +multi:nodeManager(12345) -- Host a node manager on port: 12345 +print("Node Manager Running...") +settings = { + priority = 0, -- 1 or 2 + protect = false, +} +multi:mainloop(settings) +-- Thats all you need to run the node manager, everything else is done automatically ``` diff --git a/multi/integration/networkManager.lua b/multi/integration/networkManager.lua index dc0cc90..35e19a9 100644 --- a/multi/integration/networkManager.lua +++ b/multi/integration/networkManager.lua @@ -29,26 +29,23 @@ local function pieceCommand(cmd,...) end -- Internal queue system for network queues -local queue = {} -queue.__index = queue -function queue:newQueue(name,master) - if not name then error("You must include a name in order to create a queue!") end +local Queue = {} +Queue.__index = Queue +function Queue:newQueue() local c = {} - c.name = name - c.master = master setmetatable(c,self) return c end -function queue:push(data) - master:doToAll(char(CMD_QUEUE)..packData(data)) -end -function queue:raw_push(data) -- Internal usage only +function Queue:push(data) table.insert(self,data) end -function queue:pop() - return resolveData(table.remove(self,1)) +function Queue:raw_push(data) -- Internal usage only + table.insert(self,data) end -function queue:peek() +function Queue:pop() + return table.remove(self,1) +end +function Queue:peek() return self[1] end local queues = {} @@ -162,7 +159,7 @@ function multi:newNode(settings) node.server = net:newUDPServer(0) -- hosts the node using the default port node.port = node.server.port node.connections = net.ClientCache - node.queue = queue:newQueue() + node.queue = Queue:newQueue() node.functions = bin.stream("RegisteredFunctions.dat",false) node.hasFuncs = {} if settings.managerDetails then @@ -195,6 +192,15 @@ function multi:newNode(settings) end end end + function node:pushTo(name,data) + node:sendTo(name,char(CMD_QUEUE)..packData(data)) + end + function node:peek() + return node.queue:peek() + end + function node:pop() + return node.queue:pop() + end node.loadRate=1 if settings then if settings.crossTalk then @@ -215,8 +221,7 @@ function multi:newNode(settings) elseif cmd == CMD_PONG then elseif cmd == CMD_QUEUE then - local name,d=dat:match("(.-)|(.+)") - multi.OnNetQueue:Fire(name,d) + queue:push(resolveData(dat)) elseif cmd == CMD_GLOBAL then local k,v = dat:match("(.-)|(.+)") PROXY[k]=resolveData(v) @@ -235,8 +240,7 @@ function multi:newNode(settings) elseif cmd == CMD_PONG then elseif cmd == CMD_QUEUE then - local name,d=dat:match("(.-)|(.+)") - multi.OnNetQueue:Fire(name,d) + node.queue:push(resolveData(dat)) elseif cmd == CMD_REG then if not settings.allowRemoteRegistering then print(ip..": has attempted to register a function when it is currently not allowed!") @@ -276,7 +280,7 @@ function multi:newNode(settings) server:send(ip,table.concat{char(CMD_GLOBAL),k,"|",v},port) end)-- set this up elseif cmd == CMD_INITMASTER then - print("Connected to the master!") + print("Connected to the master!",dat) node.connections[dat]={server,ip,port} multi.OnGUpdate(function(k,v) server:send(ip,table.concat{char(CMD_GLOBAL),k,"|",v},port) @@ -285,16 +289,15 @@ function multi:newNode(settings) server:send(ip,char(CMD_LOAD)..node.name.."|"..multi:getLoad(),port) end,node.loadRate) server:send(ip,char(CMD_LOAD)..node.name.."|"..multi:getLoad(),port) + server:send(ip,char(CMD_INITNODE)..node.name,port) elseif cmd == CMD_GLOBAL then local k,v = dat:match("(.-)|(.+)") PROXY[k]=resolveData(v) end end) - function node:sendToMaster(name,data) - self.connections[name]:send(data) - end - function node:sendToNode(name,data) - self.connections[name]:send(data) + function node:sendTo(name,data) + local conn = node.connections[name] + conn[1]:send(conn[2],data,conn[3]) end if not settings.noBroadCast then node.server:broadcast("NODE_"..name) @@ -311,7 +314,8 @@ function multi:newMaster(settings) -- You will be able to have more than one mas master.conn = multi:newConnection() master.conn2 = multi:newConnection() master.OnFirstNodeConnected = multi:newConnection() - master.queue = queue:newQueue() + master.OnNodeConnected = multi:newConnection() + master.queue = Queue:newQueue() master.connections = net.ClientCache -- Link to the client cache that is created on the net interface master.loads = {} master.trigger = multi:newFunction(function(self,node) @@ -328,8 +332,7 @@ function multi:newMaster(settings) -- You will be able to have more than one mas if cmd == "N" then local name,ip,port = data:match("(.-)|(.-)|(.+)") local c = net:newUDPClient(ip,port) - master.connections[name]=c - net.OnCastedClientInfo:Fire(c,name,ip,port) + net.OnCastedClientInfo:Fire(c,name,ip,port)master.connections[name]=c elseif cmd == "R" then local name = data:sub(2,-1) master.connections[name]=nil @@ -375,6 +378,15 @@ function multi:newMaster(settings) -- You will be able to have more than one mas temp:addBlock(args,#args) master:sendTo(node,temp.data) end + function master:pushTo(name,data) + master:sendTo(name,char(CMD_QUEUE)..packData(data)) + end + function master:peek() + return self.queue:peek() + end + function master:pop() + return self.queue:pop() + end function master:newNetworkThread(tname,func,name,...) -- If name specified then it will be sent to the specified node! Otherwise the least worked node will get the job local fData = packData(func) local tab = {...} @@ -411,18 +423,18 @@ function multi:newMaster(settings) -- You will be able to have more than one mas end end function master:sendTo(name,data) - if name:sub(1,5)=="NODE_" then - name = name:sub(6,-1) + if name:sub(1,5)~="NODE_" then + name = "NODE_"..name end - if self.connections["NODE_"..name]==nil then + if self.connections[name]==nil then multi:newTLoop(function(loop) - if self.connections["NODE_"..name]~=nil then - self.connections["NODE_"..name]:send(data) + if self.connections[name]~=nil then + self.connections[name]:send(data) loop:Desrtoy() end end,.1) else - self.connections["NODE_"..name]:send(data) + self.connections[name]:send(data) end end function master:getFreeNode() @@ -453,7 +465,7 @@ function multi:newMaster(settings) -- You will be able to have more than one mas nodename = i end client.OnClientReady(function() - client:send(char(CMD_INITMASTER)..name) -- Tell the node that you are a master trying to connect + client:send(char(CMD_INITMASTER)..master.name) -- Tell the node that you are a master trying to connect client.OnDataRecieved(function(client,data) local cmd = byte(data:sub(1,1)) -- the first byte is the command local dat = data:sub(2,-1) -- the data that you want to read @@ -464,9 +476,10 @@ function multi:newMaster(settings) -- You will be able to have more than one mas elseif cmd == CMD_PONG then + elseif cmd == CMD_INITNODE then + master.OnNodeConnected:Fire(dat) elseif cmd == CMD_QUEUE then - local name,d=dat:match("(.-)|(.+)") - multi.OnNetQueue:Fire(name,d) + master.queue:push(resolveData(dat)) elseif cmd == CMD_GLOBAL then local k,v = dat:match("(.-)|(.+)") PROXY[k]=resolveData(v) diff --git a/node.lua b/node.lua index 3f2239a..3a247bb 100644 --- a/node.lua +++ b/node.lua @@ -2,16 +2,33 @@ package.path="?/init.lua;?.lua;"..package.path multi = require("multi") local GLOBAL, THREAD = require("multi.integration.lanesManager").init() nGLOBAL = require("multi.integration.networkManager").init() -master = multi:newNode{ +node = multi:newNode{ crossTalk = false, -- default value, allows nodes to talk to eachother. WIP NOT READY YET! allowRemoteRegistering = true, -- allows you to register functions from the master on the node, default is false - name = nil, -- default value + name = "TESTNODE", -- default value noBroadCast = true, -- if using the node manager, set this to true to prevent the node from broadcasting managerDetails = {"localhost",12345}, -- connects to the node manager if one exists } function RemoteTest(a,b,c) -- a function that we will be executing remotely print("Yes I work!",a,b,c) + multi:newThread("waiter",function() + print("Hello!") + while true do + thread.sleep(2) + node:pushTo("Main","This is a test") + end + end) end +multi:newThread("some-test",function() + local dat = node:pop() + while true do + thread.skip(10) + if dat then + print(dat) + end + dat = node:pop() + end +end,"NODE_TESTNODE") settings = { priority = 0, -- 1 or 2 protect = false, -- if something goes wrong we will crash hard, but the speed gain is good diff --git a/test.lua b/test.lua index 9a8a58a..2b2cb10 100644 --- a/test.lua +++ b/test.lua @@ -11,26 +11,28 @@ master = multi:newMaster{ managerDetails = {"localhost",12345}, -- the details to connect to the node manager (ip,port) } -- Send to all the nodes that are connected to the master -master:doToAll(function(node_name) - master:register("TestFunc",node_name,function(msg) - print("It works: "..msg) - end) - multi:newAlarm(2):OnRing(function(alarm) - master:execute("TestFunc",node_name,"Hello!") - alarm:Destroy() - end) - multi:newThread("Checker",function() +master.OnNodeConnected(function(node) + print("Lets Go!") + master:execute("RemoteTest",node,1,2,3) + multi:newThread("waiter",function() + print("Hello!") while true do - thread.sleep(1) - if nGLOBAL["test"] then - print(nGLOBAL["test"]) - thread.kill() - end + thread.sleep(2) + print("sending") + master:pushTo(node,"This is a test 2") end end) - nGLOBAL["test2"]={age=22} end) - +multi:newThread("some-test",function() + local dat = master:pop() + while true do + thread.skip(10) + if dat then + print(dat) + end + dat = master:pop() + end +end,"NODE_TESTNODE") -- Starting the multitasker settings = { priority = 0, -- 0, 1 or 2