im getting there
This commit is contained in:
parent
02cdbc3bc2
commit
de2b3ee100
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,2 @@
|
||||
|
||||
test.lua
|
||||
test2.lua
|
||||
|
||||
13
changes.md
13
changes.md
@ -4,7 +4,14 @@ 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!
|
||||
|
||||
Changed:
|
||||
#Added:
|
||||
- require("multi.integration.networkManager")
|
||||
- multi:newNode([name])
|
||||
- multi:newMaster(name)
|
||||
|
||||
**Note:** There are many ways to work this. You could send functions/methods to a node like haw systemThreadedJobQueue work. Or you could write the methods you want in advance in each node file and send over the command to run the method with arguments ... and it will return the results. Network threading is different than system threading. Data transfer is really slow compared to system threading. In fact the main usage for this feature in the library is mearly for experments. Right now I honestly do not know what I want to do with this feature and what I am going to add to this feature. The ablitiy to use this frature like a system thread will be possible, but there are some things that differ.
|
||||
|
||||
#Changed:
|
||||
- multi:mainloop(settings) -- now takes a table of settings
|
||||
- multi:uManager(settings) -- now takes a table of settings
|
||||
- connections:holdUT(n) can take a number now. Where they will not continue until it gets triggered **n** times Added 3 updated ago, forgot to list it as a new feature
|
||||
@ -28,13 +35,13 @@ multi = require("multi")
|
||||
require("multi.integration.luvitManager").init(thread,timer) -- Luvit does not cuttently have support for the global table or threads.
|
||||
```
|
||||
|
||||
Improvements:
|
||||
#Improvements:
|
||||
- Updated the ThreadedConsole, now 100x faster!
|
||||
- Updated the ThreadedConections, .5x faster!
|
||||
- Both multi:uManager(settings) and multi:mainloop(settings) provide about the same performance! Though uManager is slightly slower due to function overhead, but still really close.
|
||||
- Revamped pausing mulit-objects they now take up less memory when being used
|
||||
|
||||
Removed:
|
||||
#Removed:
|
||||
- require("multi.all") -- We are going into a new version of the library so this is nolonger needed
|
||||
- require("multi.compat.backwards[1,5,0]") -- This is really old and is no longer supported going forward
|
||||
- multi:Do_Order()
|
||||
|
||||
@ -21,7 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
require("bin")
|
||||
local bin = pcall(require,"bin")
|
||||
local multi = {}
|
||||
multi.Version = "2.0.0"
|
||||
multi._VERSION = "2.0.0"
|
||||
@ -2189,4 +2189,4 @@ multi.load_updater:OnUpdate(function(self)
|
||||
self.Parent.dStepB = os.clock()
|
||||
end
|
||||
end)
|
||||
return multi
|
||||
return multi
|
||||
|
||||
@ -1,45 +1,269 @@
|
||||
-- CURRENT TASK: newNetThread()
|
||||
|
||||
local multi = require("multi")
|
||||
local net = require("net")
|
||||
local nGLOBAL = {}
|
||||
local nTHREAD = {}
|
||||
-- This integration is a bit different than the others! Nodes should include both the system threading integration and the network integration. This is because each node has a "main thread" as well. The examples will make this clear!
|
||||
multi.defaultNetworkPort = 30341 -- This port has a meaning to it... convert to base 36 and see what you get.
|
||||
function multi:setdefaultNetworkPort(port)
|
||||
multi.defaultNetworkPort = port
|
||||
local bin = require("bin")
|
||||
|
||||
-- Commands that the master and node will respect, max of 256 commands
|
||||
local CMD_ERROR = 0x00
|
||||
local CMD_PING = 0x01
|
||||
local CMD_PONG = 0x02
|
||||
local CMD_QUEUE = 0x03
|
||||
local CMD_TASK = 0x04
|
||||
local CMD_INITNODE = 0x05
|
||||
local CMD_INITMASTER = 0x06
|
||||
local CMD_GLOBAL = 0x07
|
||||
local CMD_LOAD = 0x08
|
||||
|
||||
local char = string.char
|
||||
local byte = string.byte
|
||||
|
||||
-- Helper for piecing commands
|
||||
local function pieceCommand(cmd,...)
|
||||
local tab = {...}
|
||||
table.insert(tab,1,cmd)
|
||||
return table.concat(tab)
|
||||
end
|
||||
|
||||
-- Internal queue system for network queues
|
||||
local queue = {}
|
||||
queue.__index = queue
|
||||
function queue:newQueue()
|
||||
local c = {}
|
||||
setmetatable(c,self)
|
||||
return c
|
||||
end
|
||||
function queue:push(data)
|
||||
table.insert(self,packData(data))
|
||||
end
|
||||
function queue:raw_push(data) -- Internal usage only
|
||||
table.insert(self,data)
|
||||
end
|
||||
function queue:pop()
|
||||
return resolveData(table.remove(self,1))
|
||||
end
|
||||
function queue:peek()
|
||||
return self[1]
|
||||
end
|
||||
multi.OnNetQueue = multi:newConnection()
|
||||
multi.OnGUpdate = multi:newConnection()
|
||||
|
||||
-- internal global system
|
||||
local GLOBAL = {}
|
||||
local THREAD = {}
|
||||
local PROXY = {}
|
||||
setmetatable(GLOBAL,{
|
||||
__index = function(t,k)
|
||||
return PROXY[k]
|
||||
end,
|
||||
__newindex = function(t,k,v)
|
||||
local v = v
|
||||
PROXY[k] = v
|
||||
multi.OnGUpdate:Fire(k,packData(v))
|
||||
end
|
||||
})
|
||||
|
||||
-- Managing the data that goes through the system
|
||||
local function packData(data)
|
||||
-- returns the data that was sterilized
|
||||
local dat = bin.new()
|
||||
dat:addBlock(#type(data),1)
|
||||
dat:addBlock(type(data)) -- The type is first
|
||||
if type(data)=="table" then
|
||||
dat:addBlock(data,nil,"t")
|
||||
elseif type(data) == "userdata" then
|
||||
error("Cannot sterilize userdata!")
|
||||
elseif type(data) == "number" then
|
||||
dat:addBlock(data,nil,"d")
|
||||
elseif type(data) == "string" then
|
||||
dat:addBlock(#data,4)
|
||||
dat:addBlock(data,nil,"s")
|
||||
elseif type(data) == "boolean" then
|
||||
dat:addBlock(data,1)
|
||||
elseif type(data) == "function" then
|
||||
dat:addBlock(data,nil,"f")
|
||||
end
|
||||
return dat.data
|
||||
end
|
||||
local function resolveData(data)
|
||||
-- returns the data that was sterilized
|
||||
local dat = bin.new(data)
|
||||
local tlen = dat:getBlock("n",1)
|
||||
local tp = dat:getBlock("s",tlen)
|
||||
if tp=="table" then
|
||||
return dat:getBlock("t")
|
||||
elseif tp=="number" then
|
||||
return dat:getBlock("d")
|
||||
elseif tp=="string" then
|
||||
local num = dat:getBlock("n",4)
|
||||
return dat:getBlock("s",num)
|
||||
elseif tp=="boolean" then
|
||||
return dat:getBlock("b",1)
|
||||
elseif tp=="function" then
|
||||
return dat:getBlock("f")
|
||||
end
|
||||
end
|
||||
|
||||
-- The main driving force of the network manager: Nodes
|
||||
function multi:newNode(name,settings)
|
||||
-- Here we have to use the net library to broadcast our node across the network
|
||||
local port = multi.defaultNetworkPort
|
||||
math.randomseed(os.time())
|
||||
local name = name or multi.randomString(8)
|
||||
if settings then
|
||||
port = settings.port or port
|
||||
-- When I think of more they will be added here
|
||||
end
|
||||
local node = {}
|
||||
node.server = net:newUDPServer(port) -- hosts the node using the default port
|
||||
node.port = multi.defaultNetworkPort
|
||||
node.name = name
|
||||
node.server = net:newUDPServer(0) -- hosts the node using the default port
|
||||
node.port = node.server.port
|
||||
node.connections = net.ClientCache
|
||||
node.queue = newQueue()
|
||||
if settings then
|
||||
if settings.crossTalk then
|
||||
net.OnCastedClientInfo(function(client,name,ip,port)
|
||||
multi.OnGUpdate(function(k,v)
|
||||
client:send(table.concat{char(CMD_GLOBAL),k,"|",v})
|
||||
end)
|
||||
print("Found a new node! Node_List:")
|
||||
for i,v in pairs(node.connections) do
|
||||
print(i)
|
||||
end
|
||||
client:send(char(CMD_INITNODE)..name) -- Tell the node that you are a node 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
|
||||
if cmd == CMD_PING then
|
||||
|
||||
elseif cmd == CMD_PONG then
|
||||
|
||||
elseif cmd == CMD_QUEUE then
|
||||
local name,d=dat:match("(.-)|(.+)")
|
||||
multi.OnNetQueue:Fire(name,d)
|
||||
elseif cmd == CMD_GLOBAL then
|
||||
local k,v = dat:match("(.-)|(.+)")
|
||||
PROXY[k]=resolveData(v)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
net:newCastedClients("NODE_(.+)")
|
||||
end
|
||||
end
|
||||
-- Lets tell the network we are alive!
|
||||
node.server.OnDataRecieved(function(server,data,cid,ip,port)
|
||||
print("Got data from the master or another node!")
|
||||
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
|
||||
if cmd == CMD_PING then
|
||||
|
||||
elseif cmd == CMD_PONG then
|
||||
|
||||
elseif cmd == CMD_QUEUE then
|
||||
local name,d=dat:match("(.-)|(.+)")
|
||||
multi.OnNetQueue:Fire(name,d)
|
||||
elseif cmd == CMD_TASK then
|
||||
|
||||
elseif cmd == CMD_INITNODE then
|
||||
print("Connected with another node!")
|
||||
node.nodes[dat]={server,ip,port}
|
||||
multi.OnGUpdate(function(k,v)
|
||||
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!")
|
||||
node.masters[dat]={server,ip,port}
|
||||
multi.OnGUpdate(function(k,v)
|
||||
server:send(ip,table.concat{char(CMD_GLOBAL),k,"|",v},port)
|
||||
end)
|
||||
-- set this up
|
||||
elseif cmd == CMD_GLOBAL then
|
||||
local k,v = dat:match("(.-)|(.+)")
|
||||
PROXY[k]=resolveData(v)
|
||||
elseif cmd == CMD_LOAD then
|
||||
local load = multi:getLoad()
|
||||
server:send(ip,node.name.."|"..load,port)
|
||||
end
|
||||
end)
|
||||
function node:sendTo(name,data)
|
||||
self.connections[name]:send(data)
|
||||
end
|
||||
node.server:broadcast("NODE_"..name)
|
||||
return node
|
||||
end
|
||||
|
||||
-- Masters
|
||||
function multi:newMaster(name,settings) -- You will be able to have more than one master connecting to a node if that is what you want to do. I want you to be able to have the freedom to code any way that you want to code.
|
||||
local master = {}
|
||||
master.clients = net.ClientCache -- Link to the client cache that is created on the net interface
|
||||
master.name = name
|
||||
master.conn = multi:newConnection()
|
||||
master.queue = queue:newQueue()
|
||||
master.connections = net.ClientCache -- Link to the client cache that is created on the net interface
|
||||
function master:newNetworkThread(name,func) -- If name specified then it will be sent to the specified node! Otherwise the least worked node will get the job
|
||||
local fData = CMD_TASK..packData(func)
|
||||
if not name then
|
||||
local name = self:getFreeNode()
|
||||
self:sendTo(name,data)
|
||||
else
|
||||
local name = "NODE_"..name
|
||||
self:sendTo(name,data)
|
||||
end
|
||||
end
|
||||
function master:sendTo(name,data)
|
||||
self.connections["NODE_"..name]:send(data)
|
||||
end
|
||||
function master:getFreeNode()
|
||||
local count = 0
|
||||
for i,v in pairs(self.connections)
|
||||
v:send(CMD_LOAD)
|
||||
count=count+1
|
||||
end
|
||||
local list = {}
|
||||
local ref = self.conn(function(name,load)
|
||||
list[#list+1]={load,name}
|
||||
end)
|
||||
self.conn:holdUT(count) -- we need to wait until we got a response from each node
|
||||
ref:Remove() -- We need to destroy that connection that we made
|
||||
local min = math.huge
|
||||
local ref
|
||||
for i = 1,#list do
|
||||
if list[i][1]<min then
|
||||
min = list[i][1]
|
||||
ref = list[i][2]
|
||||
end
|
||||
end
|
||||
return ref
|
||||
end
|
||||
net.OnCastedClientInfo(function(client,name,ip,port)
|
||||
print("Found a new node!")
|
||||
-- Do the handshake and start up stuff here
|
||||
client:send("Hello!")
|
||||
multi.OnGUpdate(function(k,v)
|
||||
client:send(table.concat{char(CMD_GLOBAL),k,"|",v})
|
||||
end)
|
||||
print("Found a new node! Node_List:")
|
||||
for i,v in pairs(master.connections) do
|
||||
print(i)
|
||||
end
|
||||
client:send(char(CMD_INITMASTER)..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
|
||||
if cmd == CMD_ERROR then
|
||||
|
||||
elseif cmd == CMD_PING then
|
||||
|
||||
elseif cmd == CMD_PONG then
|
||||
|
||||
elseif cmd == CMD_QUEUE then
|
||||
local name,d=dat:match("(.-)|(.+)")
|
||||
multi.OnNetQueue:Fire(name,d)
|
||||
elseif cmd == CMD_GLOBAL then
|
||||
local k,v = dat:match("(.-)|(.+)")
|
||||
PROXY[k]=resolveData(v)
|
||||
elseif cmd == CMD_LOAD then
|
||||
local name,load = dat:match("(.-)|(.+)")
|
||||
master.conn:Fire(name,tonumber(load))
|
||||
end
|
||||
end)
|
||||
end)
|
||||
net:newCastedClients("NODE_(.+)") -- Searches for nodes and connects to them, the master.clients table will contain them by name
|
||||
return master
|
||||
end
|
||||
-- For the same reasons that the other integrations have this
|
||||
multi.integration.nGLOBAL=nGLOBAL
|
||||
multi.integration.nTHREAD=nTHREAD
|
||||
return {init=function()
|
||||
return nGLOBAL, nTHREAD
|
||||
|
||||
-- The init function that gets returned
|
||||
return {init = function()
|
||||
return GLOBAL,THREAD
|
||||
end}
|
||||
10
node.lua
Normal file
10
node.lua
Normal file
@ -0,0 +1,10 @@
|
||||
package.path="?/init.lua;?.lua;"..package.path
|
||||
multi = require("multi")
|
||||
local GLOBAL, THREAD = require("multi.integration.lanesManager").init()
|
||||
require("multi.integration.networkManager")
|
||||
master = multi:newNode()
|
||||
settings = {
|
||||
priority = 0, -- 1 or 2
|
||||
protect = false,
|
||||
}
|
||||
multi:mainloop(settings)
|
||||
11
test.lua
Normal file
11
test.lua
Normal file
@ -0,0 +1,11 @@
|
||||
package.path="?/init.lua;?.lua;"..package.path
|
||||
multi = require("multi")
|
||||
local GLOBAL, THREAD = require("multi.integration.lanesManager").init()
|
||||
require("multi.integration.networkManager")
|
||||
master = multi:newMaster("Main")
|
||||
settings = {
|
||||
priority = 0, -- 1 or 2
|
||||
protect = false,
|
||||
}
|
||||
multi:mainloop(settings)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user