Added moduole: ExpGamingCore.Server

This commit is contained in:
Cooldude2606
2018-06-01 16:12:39 +01:00
parent 22d3efc4a1
commit 5eb8600411
9 changed files with 302 additions and 204 deletions

View File

@@ -149,16 +149,15 @@ Manager.verbose('Current state is now: "selfInit"; The verbose state is: '..tost
--- Used to avoid conflicts in the global table --- Used to avoid conflicts in the global table
-- @usage global[key] -- used like the normal global table -- @usage global[key] -- used like the normal global table
-- @usage global{'foo','bar'} -- sets the default value -- @usage global{'foo','bar'} -- sets the default value
-- @tparam[opt={}] table default the default value of global -- @usage global(true) -- restores global to default
-- @tparam[opt={}] ?table|true default the default value of global, if true then default is restored
-- @treturn table the new global table for that module -- @treturn table the new global table for that module
Manager.global=setmetatable({__defaults={},__global={ Manager.global=setmetatable({__defaults={},__global={
__call=function(tbl,default) Manager.global(default) end, __call=function(tbl,default) return Manager.global(default) end,
__index=function(tbl,key) return Manager.global() == tbl and nil or rawget(Manager.global(),key) end, __index=function(tbl,key) return Manager.global() == tbl and nil or rawget(Manager.global(),key) end,
__newindex=function(tbl,key,value) rawset(Manager.global(),key,value) end, __newindex=function(tbl,key,value) rawset(Manager.global(),key,value) end,
__pairs=function(tbl) __pairs=function(tbl)
Manager.verbose('Global Pair 1')
local tbl = Manager.global() local tbl = Manager.global()
Manager.verbose('Global Pair 2')
local function next_pair(tbl,k) local function next_pair(tbl,k)
k, v = next(tbl, k) k, v = next(tbl, k)
if type(v) ~= nil then return k,v end if type(v) ~= nil then return k,v end
@@ -177,7 +176,7 @@ Manager.global=setmetatable({__defaults={},__global={
if not rawget(global,dir) then new_dir=true Manager.verbose('Added Global Dir: '..path) rawset(global,dir,{}) end if not rawget(global,dir) then new_dir=true Manager.verbose('Added Global Dir: '..path) rawset(global,dir,{}) end
global = rawget(global,dir) global = rawget(global,dir)
end end
if new_dir and rawget(rawget(tbl,'__defaults'),tostring(module_name)) then if (new_dir or default == true) and rawget(rawget(tbl,'__defaults'),tostring(module_name)) then
Manager.verbose('Set Global Dir: '..path..' to its default') Manager.verbose('Set Global Dir: '..path..' to its default')
for key,value in pairs(rawget(rawget(tbl,'__defaults'),tostring(module_name))) do rawset(global,key,value) end for key,value in pairs(rawget(rawget(tbl,'__defaults'),tostring(module_name))) do rawset(global,key,value) end
end end

View File

@@ -1,60 +1,67 @@
--[[ --- Adds a thread system and event listening and a admin bypass (recommend to disable /c and use optional /interface)
Explosive Gaming -- @module ExpGamingCore.Server
-- @alias Server
-- @author Cooldude2606
-- @license https://github.com/explosivegaming/scenario/blob/master/LICENSE
This file can be used with permission but this and the credit below must remain in the file.
Contact a member of management on our discord to seek permission to use our code.
Any changes that you may make to the code are yours but that does not make the script yours.
Discord: https://discord.gg/r6dC2uK
]]
--Please Only Edit Below This Line-----------------------------------------------------------
-- server allows control over threads and other features the devs missed out
local Server = {} local Server = {}
Server._thread = {}
--- Returns a un-used uuid (better system needed) --- Global Table
-- @usage obj.uuid = Server.new_uuid() -- @table global
local global = global{
all={_n=0}, -- a list of every thread (indexed by uuid)
queue={}, -- an index for threads which will be resolved (contains uuids)
tick={}, -- an index for threads which will run every tick (contains uuids)
timeout={}, -- an index for threads which will timeout (contains uuids)
events={}, -- an index of threads based on event ids (contains uuids)
paused={}, -- an index of pasued threads (contains uuids)
named={}, -- a name index for thread uuids
print_to={}, -- contains players that event details will be printed to
uuid=nil -- contains the random number generator for the uuid system
}
--- Used to generate a new uuid for the thread system
-- @usage local uuid = tostring(Server.uuid) -- calling tostring locks the value
-- @treturn string the new uuid -- @treturn string the new uuid
function Server.new_uuid() Server.uuid = add_metatable({},function()
local uuid = tostring(Server._uuid()()) if not global.uuid then global.uuid = game.create_random_generator() end
uuid = string.to_hex('uuid'..uuid) return global.uuid()
return uuid end,function()
end return string.to_hex(tostring(Server.uuid()))
end)
-- use this to change the location of the server uuids --- Redirect to the thread index
function Server._uuid(reset) -- @usage Server.threads -- return #global.all
global.exp_core = not reset and global.exp_core or {} -- @usage Server.threads -- return global.all
global.exp_core.uuids = not reset and global.exp_core.uuids or game.create_random_generator() -- @treturn[1] number the number of threads
return global.exp_core.uuids -- @treturn[2] table table of all threads
end Server.threads = setmetatable({},{
__call=function(tbl) return global.all._n end,
__index=function(tbl,key) return rawget(global.all,key) end,
__newindex=function(tbl,key,value) rawset(global.all,key,value) end,
__pairs=function(tbl)
local tbl = global.all
local function next_pair(tbl,k)
k, v = next(tbl, k)
if type(v) ~= nil and k ~= '_n' then return k,v end
end
return next_pair, tbl, nil
end
})
--- Returns either the number of threads or a able of threads --- Generates a new thread object
-- @usage Server.threads() -- return {...} -- @usage Server.new_thread{name='foo',data={}}
-- Server.threads(true) -- return int -- @tparam table obj the atributes to give to the thread
-- @tparam[opt=nil] bolean count true to return the number of threads -- @treturn Server._thread the new thread created
-- @return either a list of threads or a number function Server.new_thread(obj) return Server._thread:create(obj) end
function Server.threads(count)
return count and Server._threads().all._n or Server._threads().all
end
-- use this to change the location of the server threads --- Used to get a thread via uuid or name (if one is assied)
-- all stores the threads indexed uuid, the other three only store the uuid's to index in the all table
function Server._threads(reset)
global.exp_core = not reset and global.exp_core or {}
global.exp_core.threads = not reset and global.exp_core.threads or {print_to={},queue={},tick={},timeout={},events={},all={_n=0},paused={},named={}}
return global.exp_core.threads
end
-- see thread:create (this was done so thread can remain local)
function Server.new_thread(obj)
return Server._thread:create(obj)
end
--- Used to get a thread via it's uuid or by name if one is given
-- @usage Server.get_thread('decon') -- return thread -- @usage Server.get_thread('decon') -- return thread
-- @param mixed either a uuid or the name given to a thread -- @param mixed either a uuid or the name given to a thread
-- @treturn table the thread by that name or uuid -- @treturn[1] Server._thread the thread by that name or uuid
-- @treturn[2] boolean if false is returned then no thread existes
function Server.get_thread(mixed) function Server.get_thread(mixed)
local threads = Server._threads() local threads = global
if threads.named[mixed] then return threads.all[threads.named[mixed]] if threads.named[mixed] then return threads.all[threads.named[mixed]]
elseif threads.paused[mixed] then return threads.all[threads.paused[mixed]] elseif threads.paused[mixed] then return threads.all[threads.paused[mixed]]
elseif threads.all[mixed] then return threads.all[mixed] elseif threads.all[mixed] then return threads.all[mixed]
@@ -63,33 +70,29 @@ end
--- Adds a thread into the resolve queue, can be used to lower lag --- Adds a thread into the resolve queue, can be used to lower lag
-- @usage Server.queue_thread(thread) -- return true/false -- @usage Server.queue_thread(thread) -- return true/false
-- @tparam table thread_to_queue the thread to add to the queue must have a resolve function (must be open) -- @tparam Server._thread thread_to_queue the thread to be added to the queue, must be open and have a on_resolve function
-- @treturn boolean was the thread added -- @treturn boolean was it added successfuly
function Server.queue_thread(thread_to_queue) function Server.queue_thread(thread_to_queue)
if not thread_to_queue and not thread_to_queue.valid and not thread_to_queue:valid() then return false end if not thread_to_queue and not thread_to_queue.valid and not thread_to_queue:valid() then return false end
if not thread_to_queue._resolve then return false end if not thread_to_queue._resolve then return false end
table.insert(Server._threads().queue,thread_to_queue.uuid) table.insert(global.queue,thread_to_queue.uuid)
return true return true
end end
--- Closes all active threads, can use force if it causes errors --- Closes all active threads, can use force if it causes errors
-- @usage Server.close_all_threads() -- @usage Server.close_all_threads() -- asks all threads to close
-- Server.close_all_threads(true) -- use if no force makes errors -- @usage Server.close_all_threads(true) -- forcefuly close all threads
-- @tparam bolean with_force use force when closing -- @tparam bolean with_force use force when closing
function Server.close_all_threads(with_force) function Server.close_all_threads(with_force)
if not with_force then if not with_force then
for uuid,next_thread in pairs(Server.threads()) do for uuid,thread in pairs(Server.threads) do thread:close() end
if uuid ~= '_n' then next_thread:close() end else global(true) end
end
else
Server._threads(true)
end
end end
--- Runs all the theads which have opened with an on_tick event --- Runs all the theads which have opened with an on_tick event
-- @usage Server.run_tick_threads() -- @usage Server.run_tick_threads()
function Server.run_tick_threads() function Server.run_tick_threads()
table.each(Server._threads().tick,function(uuid) table.each(global.tick,function(uuid)
local next_thread = Server.get_thread(uuid) local next_thread = Server.get_thread(uuid)
if next_thread and next_thread:valid() and next_thread._tick then if next_thread and next_thread:valid() and next_thread._tick then
local success, err = pcall(next_thread._tick,next_thread) local success, err = pcall(next_thread._tick,next_thread)
@@ -101,7 +104,7 @@ end
--- Checks the timeout on all active timeout threads --- Checks the timeout on all active timeout threads
-- @usage Server.check_timeouts() -- @usage Server.check_timeouts()
function Server.check_timeouts() function Server.check_timeouts()
table.each(Server._threads().timeout,function(uuid) table.each(global.timeout,function(uuid)
local next_thread = Server.get_thread(uuid) local next_thread = Server.get_thread(uuid)
if next_thread and next_thread:valid() then if next_thread and next_thread:valid() then
next_thread:check_timeout() next_thread:check_timeout()
@@ -109,35 +112,51 @@ function Server.check_timeouts()
end) end)
end end
-- for use in debuging --- Used to print event info to a player
function Server._thread_handler_debuger(player,event,state) -- @usage Server._thread_debuger('Cooldude2606','on_player_died',true) -- will output event info to 'Cooldude2606' for 'on_player_died'
-- @tparam ?name|index|LuaPlayer player the player that the info will be returned to
-- @tparam ?name|index event the event that info will be returned fo
-- @tparam[opt=toggle] boolean state will info be returned, nil to toggle current state
function Server._thread_debuger(player,event,state)
local player = Game.get_player(player) local player = Game.get_player(player)
local print_to = Server._threads().print_to local event = tonumber(event) or Manager.event.names[event]
local print_to = global.print_to
print_to[player.index] = print_to[player.index] or {} print_to[player.index] = print_to[player.index] or {}
print_to[player.index][event] = state if state then print_to[player.index][event] = state
elseif print_to[player.index][event] then print_to[player.index][event] = false
else print_to[player.index][event] = true end
end end
--- Calles all threads on a certain game event (used with script.on_event) --- Calles all threads on a certain game event (used with script.on_event)
-- @local Server._thread_handler
-- @usage script.on_event(defines.events,Server._thread_handler) -- adds this handler
-- @tparam table event the event that is called -- @tparam table event the event that is called
function Server._thread_handler(event) function Server._thread_handler(event)
table.each(Server._threads().print_to,function(print_to,player_index,event) -- returns to players who have set _thread_debuger to trye
table.each(global.print_to,function(print_to,player_index,event)
if event.name == defines.events.on_tick then return true end if event.name == defines.events.on_tick then return true end
if print_to[event.name] then if print_to[event.name] then
player_return(event,defines.textcolor.bg,player_index) player_return(event,defines.textcolor.bg,player_index)
end end
end,event) end,event)
-- gets the thread uuids
local event_id = event.name local event_id = event.name
local threads = Server._threads().events[event_id] local threads = global.events[event_id]
if not threads then return end if not threads then return end
-- loops over the uuids
table.each(threads,function(uuid) table.each(threads,function(uuid)
local next_thread = Server.get_thread(uuid) local thread = Server.get_thread(uuid)
if next_thread and next_thread:valid() then if thread and thread:valid() then
if is_type(next_thread._events[event_id],'function') then if is_type(thread._events[event_id],'function') then
local success, err = pcall(next_thread._events[event_id],next_thread,event) -- runs the function in the same env it was created (avoids desyncs)
if not success then next_thread:error(err) end local sandbox, success, err = Manager.sandbox(thread._events[event_id],thread._env,thread,event)
-- if there is an error it asks the thread to deal with it
if not success then thread:error(err) end
end end
end end
end) end)
end end
script.on_event(defines.events,Server._thread_handler)
--[[ cant be used V --[[ cant be used V
--- Adds a event handler to tell threads about events --- Adds a event handler to tell threads about events
@@ -146,78 +165,96 @@ end
-- @treturn bolean if the handler was added -- @treturn bolean if the handler was added
function Server.add_thread_handler(event) function Server.add_thread_handler(event)
if not is_type(event,'number') then return false end if not is_type(event,'number') then return false end
local threads = Server._threads() local threads = global
if not threads.events[event] then if not threads.events[event] then
threads.events[event] = {} threads.events[event] = {}
Event.register(event,Server._thread_handler) script.on_event(event,Server._thread_handler)
return true return true
end end
return false return false
end end
]] ]]
--- Given a string or function it will run that function and return any values --- Acts as a bypass for running functions, can accept a string
-- @usage Server.interface('local x = 1+1 print(x) return x') -- return 2 -- @usage Server.interface('local x = 1+1 print(x) return x') -- return 2
-- Server.interface('local x = 1+1 print(x)',thread) -- no return -- @usage Server.interface('local x = 1+1 print(x)',true) -- will creat a thread to run as root (this is the bypass)
-- @param callback either a function or string which will be ran via pcall -- @tparam ?string|function callback function to be ran
-- @param[opt] use_thread give a thread for the interface to run on (does not need to be open, but cant use on_resolve) -- @tparam[opt] ?Server._thread|true use_thread run the command on a premade thread or let it make its own
-- @tparam[opt] table env run the env to run the command in must have _env key as true to be
-- @param[opt] ... any args you want to pass to the function -- @param[opt] ... any args you want to pass to the function
function Server.interface(callback,use_thread,...) -- @return if no thread then it will return the value(s) returned by the callback
function Server.interface(callback,use_thread,env,...)
if use_thread then if use_thread then
if use_thread == true then use_thread = Server.new_thread{data={callback,...}} end -- if use_thread is true then it makes a new thread
if use_thread == true then use_thread = Server.new_thread{data={callback,env,...}} end
-- creates the resolve function for the thread
use_thread:on_event('resolve',function(thread) use_thread:on_event('resolve',function(thread)
if is_type(thread.data[1],'function') then local callback = table.remove(thread.data,1)
local success, err = pcall(unpack(thread.data)) callback = is_type(callback,'function') and callback or loadstring(callback)
local env = table.remove(thread.data,1)
if is_type(env,'table') and env._env == true then
local sandbox, success, err = Manager.sandbox(callback,env,unpack(thread.data))
if not success then error(err) end if not success then error(err) end
return err return err
else else
local callback = table.remove(thread.data,1) local sandbox, success, err = Manager.sandbox(callback,{},env,unpack(thread.data))
local success, err = pcall(loadstring(callback),unpack(thread.data))
if not success then error(err) end if not success then error(err) end
return err return err
end end
end) end)
-- opens the thread and then queues it
use_thread:open() use_thread:open()
Server.queue_thread(use_thread) Server.queue_thread(use_thread)
else else
if is_type(callback,'function') then local callback = is_type(callback,'function') and callback or loadstring(callback)
local success, err = pcall(callback,...) if is_type(env,'table') and env._env == true then
return success, err local sandbox, success, err = Manager.sandbox(callback,env,unpack(thread.data))
if not success then error(err) end
return err
else else
local success, err = pcall(loadstring(callback),...) local sandbox, success, err = Manager.sandbox(callback,{},env,unpack(thread.data))
return success, err if not success then error(err) end
return err
end end
return false
end end
end end
-- thread allows you to run fuinction async to the main game --- The class for the server threads, allows abbilty to run async function
-- @class Thread
-- @alias Server._thread
Server._thread = {}
--- Returns a new thread object --- Returns a new thread object
-- @usage new_thread = thread:create() -- @usage new_thread = thread:create()
-- @tparam[opt={}] table obj all are opt {timeout=int,name=str,data=any} advanced users can prefix with _function to avoid the on_function functions -- @tparam[opt={}] table obj all values are opt {timeout=int,name=str,data=any}
-- @treturn table the new thread object -- @treturn Server._thread the new thread object
function Server._thread:create(obj) function Server._thread:create(obj)
local obj = obj or {} local obj = obj or {}
setmetatable(obj,{__index=Server._thread}) setmetatable(obj,{__index=Server._thread})
obj.uuid = Server.new_uuid() obj.uuid = tostring(Server.uuid)
obj._env = get_env()
obj._env.obj = nil -- provents infinte recusion
return obj return obj
end end
-- see Server.queue_thread - this just opens it first --- Opens and queses a thread
-- @usage Server._thread:queue() -- returns true/false
-- @treturn boolean was the thread queued successfuly
-- @see Server.queue_thread
function Server._thread:queue() function Server._thread:queue()
self:open() self:open()
return Server.queue_thread(self) return Server.queue_thread(self)
end end
--- Test if the thread has all requied parts --- Test if the thread has all requied parts
-- @usage if thread:valid() then end -- @usage if thread:valid() then end -- basic test for valid
-- @tparam bolean skip_location_check true to skip the location check -- @tparam[opt=false] bolean skip_location_check true to skip the location checking
-- @treturn bolean is the thread valid -- @treturn boolean is the thread valid
function Server._thread:valid(skip_location_check) function Server._thread:valid(skip_location_check)
local skip_location_check = skip_location_check or false local skip_location_check = skip_location_check or false
if is_type(self.uuid,'string') and if is_type(self.uuid,'string') and
skip_location_check or is_type(self.opened,'number') and skip_location_check or is_type(self.opened,'number') and
skip_location_check or is_type(Server._threads().all[self.uuid],'table') and skip_location_check or is_type(global.all[self.uuid],'table') and
is_type(self.timeout) or is_type(self.timeout,'number') and is_type(self.timeout) or is_type(self.timeout,'number') and
is_type(self.name) or is_type(self.name,'string') and is_type(self.name) or is_type(self.name,'string') and
is_type(self._close) or is_type(self._close,'function') and is_type(self._close) or is_type(self._close,'function') and
@@ -226,64 +263,78 @@ function Server._thread:valid(skip_location_check)
is_type(self._resolve) or is_type(self._resolve,'function') and is_type(self._resolve) or is_type(self._resolve,'function') and
is_type(self._success) or is_type(self._success,'function') and is_type(self._success) or is_type(self._success,'function') and
is_type(self._error) or is_type(self._error,'function') then is_type(self._error) or is_type(self._error,'function') then
-- all above must be true to be vaild, must accept nil and function
return true return true
end end
return false return false
end end
--- Opens the thread by storing it in a place the server object can find it --- Opens the thread; indexs this thread in the global index
-- @usage thread:open() -- return true -- @usage thread:open() -- return true
-- @treturn bolean if the thread was opened -- @treturn bolean if the thread was opened successfuly
function Server._thread:open() function Server._thread:open()
-- if the thread is valid and not already opended
if not self:valid(true) or self.opened then return false end if not self:valid(true) or self.opened then return false end
local threads = Server._threads()
local uuid = self.uuid local uuid = self.uuid
-- sets the thread to open, this is the tick it was opened
self.opened = game.tick self.opened = game.tick
threads.all[uuid] = threads.all[uuid] or self -- creats the global index
threads.all._n = threads.all._n+1 global.all[uuid] = global.all[uuid] or self
if threads.paused[self.name] then threads.paused[self.name] = nil end global.all._n = global.all._n+1
if is_type(self.timeout,'number') then table.insert(threads.timeout,uuid) end -- indexs the thread in other places if it has more function
if is_type(self._tick,'function') then table.insert(threads.tick,uuid) end -- if it was paused before (ie did not run any events) then the index is removed from the paused index
if is_type(self.name,'string') then threads.named[self.name] = threads.named[self.name] or self.uuid end if global.paused[self.name] then global.paused[self.name] = nil end
-- if it has a timeout or on_tick handler then it is indexed in those indexs
if is_type(self.timeout,'number') then table.insert(global.timeout,uuid) end
if is_type(self._tick,'function') then table.insert(global.tick,uuid) end
-- if the thread is given a name then a index from the name to uuid is made
if is_type(self.name,'string') then global.named[self.name] = global.named[self.name] or self.uuid end
-- if there are event handlers then it will loop over them and add them to the event index
if is_type(self._events,'table') then if is_type(self._events,'table') then
table.each(self._events,function(callback,event,threads,uuid) table.each(self._events,function(callback,event,global,uuid)
-- cant be used V -- cant be used V
--Server.add_thread_handler(event) --Server.add_thread_handler(event)
if not threads.events[event] then threads.events[event] = {} end if not global.events[event] then global.events[event] = {} end
table.insert(threads.events[event],uuid) table.insert(global.events[event],uuid)
end,threads,self.uuid) end,global,self.uuid)
end end
return true return true
end end
--- Inverse of thread:open() - it removes the thread and calles on_close --- Inverse of thread:open() - Removes all indexs to this thread, most cases this will cause it to become inassible
-- @usage thread:close() -- return true -- @usage thread:close() -- return true
-- @treturn bolean if the thread had a on_close function -- @treturn boolean if the thread had a on_close function
function Server._thread:close() function Server._thread:close()
local threads = Server._threads()
local uuid = self.uuid local uuid = self.uuid
local _return = false local _return = false
-- if there is a call to the threads on close event, will later return true
if is_type(self._close,'function') then pcall(self._close,self) _return = true end if is_type(self._close,'function') then pcall(self._close,self) _return = true end
local value,key = table.find(threads.queue,function(v,k,uuid) return v == uuid end,uuid) -- will search every possible location for this thread and remove it
if key then table.remove(threads.queue,key) end local value,key = table.find(global.queue,function(v,k,uuid) return v == uuid end,uuid)
local value,key = table.find(threads.timeout,function(v,k,uuid) return v == uuid end,uuid) if key then table.remove(global.queue,key) end -- queue
if key then table.remove(threads.timeout,key) end local value,key = table.find(global.timeout,function(v,k,uuid) return v == uuid end,uuid)
local value,key = table.find(threads.tick,function(v,k,uuid) return v == uuid end,uuid) if key then table.remove(global.timeout,key) end -- timeouts
if key then table.remove(threads.tick,key) end local value,key = table.find(global.tick,function(v,k,uuid) return v == uuid end,uuid)
if key then table.remove(global.tick,key) end -- on_tick
-- then will look for it in the event handlers and remove it if found
if is_type(self._events,'table') then if is_type(self._events,'table') then
table.each(self._events,function(callback,event) table.each(self._events,function(callback,event)
if threads.events[event] then if global.events[event] then
local value,key = table.find(threads.events[event],function(v,k,uuid) return v == uuid end,uuid) local value,key = table.find(global.events[event],function(v,k,uuid) return v == uuid end,uuid)
if key then table.remove(threads.events[event],key) end if key then table.remove(global.events[event],key) end
-- cant be used V -- cant be used V
--if #threads.events[event] == 0 then Event.remove(event,Server.game_event) threads.events[event] = nil end --if #global.events[event] == 0 then Event.remove(event,Server.game_event) global.events[event] = nil end
end end
end) end)
end end
-- sets the thread to closed
self.opened=nil self.opened=nil
-- unless the thread has a name or is assied to be reopened
if self.reopen == true then self:open() else if self.reopen == true then self:open() else
if is_type(self.name,'string') then threads.paused[self.name]=self.uuid -- if it has a name but not assied to reopen then it will become 'pasued'
else threads.all[uuid] = nil threads.all._n = threads.all._n-1 end if is_type(self.name,'string') then global.paused[self.name]=self.uuid
-- else it will just be wiped from the global index
else global.all[uuid] = nil global.all._n = global.all._n-1 end
end end
return _return return _return
end end
@@ -294,107 +345,105 @@ end
-- @treturn bolean true if the thread called on_success or on_error -- @treturn bolean true if the thread called on_success or on_error
function Server._thread:resolve(...) function Server._thread:resolve(...)
local _return = false local _return = false
-- checks if the resolve haddler is still present
if is_type(self._resolve,'function') then if is_type(self._resolve,'function') then
local success, err = pcall(self._resolve,self,...) local sandbox, success, err = Manager.sandbox(self._resolve,thread._env,self,...)
if success then if success then
-- if it was successful then it will attemp to call the success handler
if is_type(self._success,'function') then if is_type(self._success,'function') then
Server.interface(function(thread,err) -- interface is used as a way to delay the success call till the next tick
local success,err = pcall(thread._success,thread,err) Server.interface(function(thread,err)
local sandbox, success, err = Manager.sandbox(thread._success,thread._env,thread,err)
if not success then thread:error(err) end if not success then thread:error(err) end
end,true,self,err) end,true,self,err)
-- later returns true if there was a call to the success handler
_return = true _return = true
end end
else -- if there is an error the thread is asked to deal with it, returns true/false based on result of handler
_return = self:error(err) else _return = self:error(err) end
end
end end
-- closes the thread as it is no longer needed as its command has be ran
self:close() self:close()
return _return return _return
end end
--- Checks the timeout on a thread - if timedout then it calles on_timeout and closes --- Checks the timeout on a thread - if timed out then it calles on_timeout and closes
-- @usage thread:check_timeout() -- return true -- @usage thread:check_timeout() -- return true
-- @treturn bolean if the thread timedout -- @treturn bolean if the thread timed out
function Server._thread:check_timeout() function Server._thread:check_timeout()
local _return = false local _return = false
-- makes sure the thread is still valid
if not self:valid() then return false end if not self:valid() then return false end
-- checks if the thread has been opened longer than its time out period
if is_type(self.timeout,'number') and game.tick >= (self.opened+self.timeout) then if is_type(self.timeout,'number') and game.tick >= (self.opened+self.timeout) then
if is_type(self._timeout,'function') then if is_type(self._timeout,'function') then
pcall(self._timeout,self) -- we do not care if the time out has caused an error as it is in most cases an error in its own right
Manager.sandbox(self._timeout,thread._env,self)
end end
_return = true _return = true
-- closes the thread to provent any further event calls
self:close() self:close()
end end
return _return return _return
end end
--- Rasies an error on this thread --- Used to check and raise the error handler of the thread, if not present it raises an error
-- @usage thread:error(err) -- return true -- @usage thread:error(err) -- return true
-- @param err the err to be rasied -- @tparam string err the err to be rasied
-- @treturn bolean did the thread handdle the error -- @treturn boolean did the thread have an error handler
function Server._thread:error(err) function Server._thread:error(err)
local _return = false local _return = false
if is_type(self._error,'function') then if is_type(self._error,'function') then
pcall(self._error,self,err) pcall(self._error,self,err)
_return = true _return = true
else else error(err) end
error(err)
end
return _return return _return
end end
--- Set function to run then an event is called on a thread, none of them are 'needed' but you are advised to have atleast one
-- @usage thread:on_event('close',function) -- return true --- Set function to run then an event is triggered, none of them are 'needed' but you are advised to have atleast one
-- events = ['close','timeout','tick','resolve','success','error'] -- @usage thread:on_event('close',function) -- if event is not one below then a game event is used
-- if event is a number then it is asumed to be a game event -- @usage thread_only_events = ['close','timeout','tick','resolve','success','error']
-- @tparam string event the name of the event that it is called on -- @tparam ?string|index event the name of the event that the function should be called on
-- @tparam function callback the function which is called on the event -- @tparam function callback the function which is called by the event trigger
-- @treturn table returns self so that there can be chained -- @treturn table returns self so that they can be chained together
function Server._thread:on_event(event,callback) function Server._thread:on_event(event,callback)
local events = {'close','timeout','tick','resolve','success','error'} local events = {'close','timeout','tick','resolve','success','error'}
-- seaches the above list for the event
local value = table.find(events,function(v,k,find) return v == string.lower(find) end,event) local value = table.find(events,function(v,k,find) return v == string.lower(find) end,event)
if value and is_type(callback,'function') then if value and is_type(callback,'function') then
-- if it is a thread_only_event then it will add it to its core values
self['_'..value] = callback self['_'..value] = callback
elseif is_type(event,'number') and is_type(callback,'function') then elseif is_type(event,'number') and is_type(callback,'function') then
-- other wise it is appended to the event index of the thread
if not self._events then self._events = {} end if not self._events then self._events = {} end
self._events[event] = callback self._events[event] = callback
end end
-- returns self to allowing chaining of on_event():on_event():on_event() etc..
return self return self
end end
Event.register(defines.events.on_tick,function(event) script.on_event(defines.events.on_tick,function(event)
-- uses its own on_tick event so that other actions can be tested for
if event.tick < 10 then return end if event.tick < 10 then return end
local threads = Server._threads() if #global.tick > 0 then Server.run_tick_threads() end -- on tick events
if #threads.tick > 0 then Server.run_tick_threads() end if #global.timeout > 0 then Server.check_timeouts() end -- timeout checks
if #threads.timeout > 0 then Server.check_timeouts() end if #global.queue > 0 then -- resolve one thread
if #threads.queue > 0 then local current_thread = global.all[global.queue[1]]
local current_thread = threads.all[threads.queue[1]]
if current_thread and current_thread:valid() then current_thread:resolve() end if current_thread and current_thread:valid() then current_thread:resolve() end
end end
end) end)
Event.register(-2,function(event) script.on_event(-2,function(event)
local threads = Server.threads() -- sets up metatable again so that threads contiune to work
for uuid,thread in pairs(threads) do for uuid,thread in pairs(Server.threads) do setmetatable(thread,{__index=Server._thread}) end
if uuid ~= '_n' then setmetatable(thread,{__index=Server._thread}) end
end
end) end)
Server.on_init=function(self) Server.on_init=function(self)
Event.register(defines.events,Server._thread_handler) if loaded_modules.commands then require(module_path..'/src/commands') end
if pcall(function() return commands._expgaming end) then
commands.add_command('interface', 'Runs the given input from the script', {'code',true}, function(event,args)
local callback = args.code
if not string.find(callback,'%s') and not string.find(callback,'return') then callback = 'return '..callback end
if game.player then callback = 'local player, surface, force, position, entity, tile = game.player, game.player.surface, game.player.force, game.player.position, game.player.selected, game.player.surface.get_tile(game.player.position);'..callback end
if Ranking and Ranking.get_rank and game.player then callback = 'local rank = Ranking.get_rank(game.player);'..callback end
local success, err = Server.interface(callback)
if not success and is_type(err,'string') then local _end = string.find(err,'stack traceback') if _end then err = string.sub(err,0,_end-2) end end
if err or err == false then player_return(err) end
end)
end
end end
return Server return Server
--[[ --[[
Thread Example: Thread Example:
@@ -424,5 +473,22 @@ return Server
end) end)
thread:open() thread:open()
all on_event functions can be chained from the thread creation rather than use varibles all on_event functions can be chained from the thread creation rather than use varibles eg:
Server.new_thread{
name='tree-decon',
data={}
}:on_event('tick',function(self)
local trees = self.data
if #trees == 0 then return end
local tree = table.remove(trees,1)
if tree.valid then tree.destroy() end
end):on_event('error',function(self,err)
-- cant see how this can cause an error
-- but this is where error handling goes
-- any event including on_resolve and on_tick can raise this
end):on_event(defines.events.on_marked_for_deconstruction,function(self,event)
if event.entity.type == 'tree' then
table.insert(self.data,event.entity)
end
end):open()
]] ]]

View File

@@ -0,0 +1,31 @@
--- Description - A small description that will be displayed on the doc
-- @submodule ExpGamingCore.Server
-- @alias Server
-- @author Cooldude2606
-- @license https://github.com/explosivegaming/scenario/blob/master/LICENSE
--- This file will be loaded when ExpGamingCore.Commands is present
-- @function _comment
commands.add_command('interface', 'Runs the given input from the script', {'code',true}, function(event,args)
local callback = args.code
-- looks for spaces, if non the it will prefix the command with return
if not string.find(callback,'%s') and not string.find(callback,'return') then callback = 'return '..callback end
-- sets up an env for the command to run in
local env = {_env=true,}
if game.player then
env.player = game.player
env.surface = game.player.surface
env.force = game.player.force
env.position = game.player.position
env.entity = game.player.selected
env.tile = game.player.surface.get_tile(game.player.position)
if Ranking and Ranking.get_rank then env.rank = Ranking.get_rank(game.player) end
end
-- runs the function
local success, err = Server.interface(callback,false,env)
-- if there is an error then it will remove the stacktrace and return the error
if not success and is_type(err,'string') then local _end = string.find(err,'stack traceback') if _end then err = string.sub(err,0,_end-2) end end
-- if there is a value returned that is not nill then it will return that value
if err or err == false then player_return(err) end
end)

View File

@@ -1,4 +1,4 @@
--- Description - A small description that will be displayed on the doc --- Allows syncing with an outside server and info panle.
-- @module ExpGamingCore.Sync -- @module ExpGamingCore.Sync
-- @alias Sync -- @alias Sync
-- @author Cooldude2606 -- @author Cooldude2606
@@ -6,19 +6,28 @@
local Sync = {} local Sync = {}
local Sync_updates = {} local Sync_updates = {}
-- sets up the global for this module --- Global Table
global{ -- @table global
server_name='Factorio Server', local global = global{
server_description='A factorio server for everyone', server_name='Factorio Server', -- the server name
reset_time='On Demand', server_description='A factorio server for everyone', -- a short description of the server
time='Day Mth 00 00:00:00 UTC Year', reset_time='On Demand', -- the reset time of the server
time_set={0,'0.00M'}, time='Day Mth 00 00:00:00 UTC Year', -- the last knowen irl time
last_update={0,'0.00M'}, time_set={0,'0.00M'}, -- the last in game time that the time was set
time_period={18000,'5.00M'}, last_update={0,'0.00M'}, -- the last time that this info was updated
players={online={'Offline'},n_online=0,all={'Offline'},n_all=0,admins_online=0,afk_players=0,times={'Offline'}}, time_period={18000,'5.00M'}, -- how often this infomation is updated
ranks={'Offline'}, players={
rockets=0, online={'Offline'}, -- list of all players online
mods={'Offline'} n_online=0, -- the number of players online
all={'Offline'}, -- list of all player on or offline
n_all=0, -- the number of players who have joined the server
admins_online=0, -- the number of admins online
afk_players=0, -- the number of afk players
times={'Offline'} -- the play times of every player
}, -- a sub list of players in the game
ranks={'Offline'}, -- a list of player ranks
rockets=0, -- the number of rockets launched
mods={'Offline'} -- the mods which are loaded
} }
--- Used to standidise the tick format for any sync info --- Used to standidise the tick format for any sync info

View File

@@ -4,7 +4,7 @@
-- @author Cooldude2606 -- @author Cooldude2606
-- @license https://github.com/explosivegaming/scenario/blob/master/LICENSE -- @license https://github.com/explosivegaming/scenario/blob/master/LICENSE
--- This file will be loaded when ExpGamingCore/Gui is present --- This file will be loaded when ExpGamingCore.Gui is present
-- @function _comment -- @function _comment
local Sync_gui_functions = {} local Sync_gui_functions = {}

View File

@@ -4,7 +4,7 @@
-- @author Cooldude2606 -- @author Cooldude2606
-- @license https://github.com/explosivegaming/scenario/blob/master/LICENSE -- @license https://github.com/explosivegaming/scenario/blob/master/LICENSE
--- This file will be loaded when ExpGamingCore/Ranking is present --- This file will be loaded when ExpGamingCore.Ranking is present
-- @function _comment -- @function _comment
--- Used as a redirect to Ranking._base_preset that will set the rank given to a player apon joining --- Used as a redirect to Ranking._base_preset that will set the rank given to a player apon joining

View File

@@ -52,6 +52,7 @@
"location": "url", "location": "url",
"dependencies": { "dependencies": {
"ExpGamingLib": ">=3.0.0", "ExpGamingLib": ">=3.0.0",
"FactorioStdLib.Table": ">=0.8.0",
"ExpGamingCore/Ranking": "?>=3.0.0", "ExpGamingCore/Ranking": "?>=3.0.0",
"ExpGamingCore/Commands": "?>=3.0.0" "ExpGamingCore/Commands": "?>=3.0.0"
} }

View File

@@ -1,17 +1,9 @@
--[[
Explosive Gaming
This file can be used with permission but this and the credit below must remain in the file.
Contact a member of management on our discord to seek permission to use our code.
Any changes that you may make to the code are yours but that does not make the script yours.
Discord: https://discord.gg/r6dC2uK
]]
--Please Only Edit Below This Line-----------------------------------------------------------
--- Adds some common functions used though out all ExpGaming modules --- Adds some common functions used though out all ExpGaming modules
-- @module ExpGamingLib -- @module ExpGamingLib
-- @alias ExpLib -- @alias ExpLib
-- @author Cooldude2606 -- @author Cooldude2606
-- @license https://github.com/explosivegaming/scenario/blob/master/LICENSE
local module_verbose = false -- there is no verbose in this file so true will do nothing local module_verbose = false -- there is no verbose in this file so true will do nothing
local ExpLib = {} local ExpLib = {}

View File

@@ -12,6 +12,6 @@ return {
--['Ranking']='/modules/ExpGamingCore/Ranking', --['Ranking']='/modules/ExpGamingCore/Ranking',
--['commands']='/modules/ExpGamingCore/Commands', --['commands']='/modules/ExpGamingCore/Commands',
--['Gui']='/modules/ExpGamingCore/Gui', --['Gui']='/modules/ExpGamingCore/Gui',
--['Server']='/modules/ExpGamingCore/Server', ['Server']='/modules/ExpGamingCore/Server',
['Sync']='/modules/ExpGamingCore/Sync', ['Sync']='/modules/ExpGamingCore/Sync',
} }