im getting there

This commit is contained in:
Ryan Ward 2018-06-18 01:12:42 -04:00
parent 02cdbc3bc2
commit de2b3ee100
6 changed files with 280 additions and 29 deletions

1
.gitignore vendored
View File

@ -1,3 +1,2 @@
test.lua
test2.lua test2.lua

View File

@ -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! **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:mainloop(settings) -- now takes a table of settings
- multi:uManager(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 - 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. 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 ThreadedConsole, now 100x faster!
- Updated the ThreadedConections, .5x 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. - 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 - 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.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 - require("multi.compat.backwards[1,5,0]") -- This is really old and is no longer supported going forward
- multi:Do_Order() - multi:Do_Order()

View File

@ -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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
]] ]]
require("bin") local bin = pcall(require,"bin")
local multi = {} local multi = {}
multi.Version = "2.0.0" multi.Version = "2.0.0"
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() self.Parent.dStepB = os.clock()
end end
end) end)
return multi return multi

View File

@ -1,45 +1,269 @@
-- CURRENT TASK: newNetThread()
local multi = require("multi") local multi = require("multi")
local net = require("net") local net = require("net")
local nGLOBAL = {} local bin = require("bin")
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! -- Commands that the master and node will respect, max of 256 commands
multi.defaultNetworkPort = 30341 -- This port has a meaning to it... convert to base 36 and see what you get. local CMD_ERROR = 0x00
function multi:setdefaultNetworkPort(port) local CMD_PING = 0x01
multi.defaultNetworkPort = port 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 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) function multi:newNode(name,settings)
-- Here we have to use the net library to broadcast our node across the network -- Here we have to use the net library to broadcast our node across the network
local port = multi.defaultNetworkPort
math.randomseed(os.time()) math.randomseed(os.time())
local name = name or multi.randomString(8) 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 = {} local node = {}
node.server = net:newUDPServer(port) -- hosts the node using the default port node.name = name
node.port = multi.defaultNetworkPort 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! -- Lets tell the network we are alive!
node.server.OnDataRecieved(function(server,data,cid,ip,port) 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) end)
function node:sendTo(name,data)
self.connections[name]:send(data)
end
node.server:broadcast("NODE_"..name) node.server:broadcast("NODE_"..name)
return node return node
end 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. 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 = {} 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) net.OnCastedClientInfo(function(client,name,ip,port)
print("Found a new node!") multi.OnGUpdate(function(k,v)
-- Do the handshake and start up stuff here client:send(table.concat{char(CMD_GLOBAL),k,"|",v})
client:send("Hello!") 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) end)
net:newCastedClients("NODE_(.+)") -- Searches for nodes and connects to them, the master.clients table will contain them by name net:newCastedClients("NODE_(.+)") -- Searches for nodes and connects to them, the master.clients table will contain them by name
return master return master
end end
-- For the same reasons that the other integrations have this
multi.integration.nGLOBAL=nGLOBAL -- The init function that gets returned
multi.integration.nTHREAD=nTHREAD return {init = function()
return {init=function() return GLOBAL,THREAD
return nGLOBAL, nTHREAD
end} end}

10
node.lua Normal file
View 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
View 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)