diff --git a/FactorioSoftmodManager.lua b/FactorioSoftmodManager.lua index 9db5bfe6..93382980 100644 --- a/FactorioSoftmodManager.lua +++ b/FactorioSoftmodManager.lua @@ -149,16 +149,15 @@ Manager.verbose('Current state is now: "selfInit"; The verbose state is: '..tost --- Used to avoid conflicts in the global table -- @usage global[key] -- used like the normal global table -- @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 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, __newindex=function(tbl,key,value) rawset(Manager.global(),key,value) end, __pairs=function(tbl) - Manager.verbose('Global Pair 1') local tbl = Manager.global() - Manager.verbose('Global Pair 2') local function next_pair(tbl,k) k, v = next(tbl, k) 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 global = rawget(global,dir) 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') for key,value in pairs(rawget(rawget(tbl,'__defaults'),tostring(module_name))) do rawset(global,key,value) end end diff --git a/modules/ExpGamingCore/Server/control.lua b/modules/ExpGamingCore/Server/control.lua index d8c6347f..5e9b9522 100644 --- a/modules/ExpGamingCore/Server/control.lua +++ b/modules/ExpGamingCore/Server/control.lua @@ -1,60 +1,67 @@ ---[[ -Explosive Gaming +--- Adds a thread system and event listening and a admin bypass (recommend to disable /c and use optional /interface) +-- @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 = {} -Server._thread = {} ---- Returns a un-used uuid (better system needed) --- @usage obj.uuid = Server.new_uuid() +--- Global Table +-- @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 -function Server.new_uuid() - local uuid = tostring(Server._uuid()()) - uuid = string.to_hex('uuid'..uuid) - return uuid -end +Server.uuid = add_metatable({},function() + if not global.uuid then global.uuid = game.create_random_generator() end + return global.uuid() +end,function() + return string.to_hex(tostring(Server.uuid())) +end) --- use this to change the location of the server uuids -function Server._uuid(reset) - global.exp_core = not reset and global.exp_core or {} - global.exp_core.uuids = not reset and global.exp_core.uuids or game.create_random_generator() - return global.exp_core.uuids -end +--- Redirect to the thread index +-- @usage Server.threads -- return #global.all +-- @usage Server.threads -- return global.all +-- @treturn[1] number the number of threads +-- @treturn[2] table table of all threads +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 --- @usage Server.threads() -- return {...} --- Server.threads(true) -- return int --- @tparam[opt=nil] bolean count true to return the number of threads --- @return either a list of threads or a number -function Server.threads(count) - return count and Server._threads().all._n or Server._threads().all -end +--- Generates a new thread object +-- @usage Server.new_thread{name='foo',data={}} +-- @tparam table obj the atributes to give to the thread +-- @treturn Server._thread the new thread created +function Server.new_thread(obj) return Server._thread:create(obj) end --- use this to change the location of the server threads --- 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 +--- Used to get a thread via uuid or name (if one is assied) -- @usage Server.get_thread('decon') -- return 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) - local threads = Server._threads() + local threads = global if threads.named[mixed] then return threads.all[threads.named[mixed]] elseif threads.paused[mixed] then return threads.all[threads.paused[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 -- @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) --- @treturn boolean was the thread added +-- @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 it added successfuly 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._resolve then return false end - table.insert(Server._threads().queue,thread_to_queue.uuid) + table.insert(global.queue,thread_to_queue.uuid) return true end --- Closes all active threads, can use force if it causes errors --- @usage Server.close_all_threads() --- Server.close_all_threads(true) -- use if no force makes errors +-- @usage Server.close_all_threads() -- asks all threads to close +-- @usage Server.close_all_threads(true) -- forcefuly close all threads -- @tparam bolean with_force use force when closing function Server.close_all_threads(with_force) if not with_force then - for uuid,next_thread in pairs(Server.threads()) do - if uuid ~= '_n' then next_thread:close() end - end - else - Server._threads(true) - end + for uuid,thread in pairs(Server.threads) do thread:close() end + else global(true) end end --- Runs all the theads which have opened with an on_tick event -- @usage 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) if next_thread and next_thread:valid() and next_thread._tick then local success, err = pcall(next_thread._tick,next_thread) @@ -101,7 +104,7 @@ end --- Checks the timeout on all active timeout threads -- @usage 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) if next_thread and next_thread:valid() then next_thread:check_timeout() @@ -109,35 +112,51 @@ function Server.check_timeouts() end) end --- for use in debuging -function Server._thread_handler_debuger(player,event,state) +--- Used to print event info to a player +-- @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 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][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 + --- 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 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 print_to[event.name] then player_return(event,defines.textcolor.bg,player_index) end end,event) + -- gets the thread uuids local event_id = event.name - local threads = Server._threads().events[event_id] + local threads = global.events[event_id] if not threads then return end + -- loops over the uuids table.each(threads,function(uuid) - local next_thread = Server.get_thread(uuid) - if next_thread and next_thread:valid() then - if is_type(next_thread._events[event_id],'function') then - local success, err = pcall(next_thread._events[event_id],next_thread,event) - if not success then next_thread:error(err) end + local thread = Server.get_thread(uuid) + if thread and thread:valid() then + if is_type(thread._events[event_id],'function') then + -- runs the function in the same env it was created (avoids desyncs) + 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 +script.on_event(defines.events,Server._thread_handler) --[[ cant be used V --- Adds a event handler to tell threads about events @@ -146,78 +165,96 @@ end -- @treturn bolean if the handler was added function Server.add_thread_handler(event) if not is_type(event,'number') then return false end - local threads = Server._threads() + local threads = global if not threads.events[event] then threads.events[event] = {} - Event.register(event,Server._thread_handler) + script.on_event(event,Server._thread_handler) return true end return false 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 --- Server.interface('local x = 1+1 print(x)',thread) -- no return --- @param callback either a function or string which will be ran via pcall --- @param[opt] use_thread give a thread for the interface to run on (does not need to be open, but cant use on_resolve) +-- @usage Server.interface('local x = 1+1 print(x)',true) -- will creat a thread to run as root (this is the bypass) +-- @tparam ?string|function callback function to be ran +-- @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 -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 == 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) - if is_type(thread.data[1],'function') then - local success, err = pcall(unpack(thread.data)) + local callback = table.remove(thread.data,1) + 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 return err else - local callback = table.remove(thread.data,1) - local success, err = pcall(loadstring(callback),unpack(thread.data)) + local sandbox, success, err = Manager.sandbox(callback,{},env,unpack(thread.data)) if not success then error(err) end return err end end) + -- opens the thread and then queues it use_thread:open() Server.queue_thread(use_thread) else - if is_type(callback,'function') then - local success, err = pcall(callback,...) - return success, err + local callback = is_type(callback,'function') and callback or loadstring(callback) + 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 + return err else - local success, err = pcall(loadstring(callback),...) - return success, err + local sandbox, success, err = Manager.sandbox(callback,{},env,unpack(thread.data)) + if not success then error(err) end + return err end - return false 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 -- @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 --- @treturn table the new thread object +-- @tparam[opt={}] table obj all values are opt {timeout=int,name=str,data=any} +-- @treturn Server._thread the new thread object function Server._thread:create(obj) local obj = obj or {} 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 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() self:open() return Server.queue_thread(self) end --- Test if the thread has all requied parts --- @usage if thread:valid() then end --- @tparam bolean skip_location_check true to skip the location check --- @treturn bolean is the thread valid +-- @usage if thread:valid() then end -- basic test for valid +-- @tparam[opt=false] bolean skip_location_check true to skip the location checking +-- @treturn boolean is the thread valid function Server._thread:valid(skip_location_check) local skip_location_check = skip_location_check or false if is_type(self.uuid,'string') 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.name) or is_type(self.name,'string') 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._success) or is_type(self._success,'function') and 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 end return false 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 --- @treturn bolean if the thread was opened +-- @treturn bolean if the thread was opened successfuly 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 - local threads = Server._threads() local uuid = self.uuid + -- sets the thread to open, this is the tick it was opened self.opened = game.tick - threads.all[uuid] = threads.all[uuid] or self - threads.all._n = threads.all._n+1 - if threads.paused[self.name] then threads.paused[self.name] = nil end - if is_type(self.timeout,'number') then table.insert(threads.timeout,uuid) end - if is_type(self._tick,'function') then table.insert(threads.tick,uuid) end - if is_type(self.name,'string') then threads.named[self.name] = threads.named[self.name] or self.uuid end + -- creats the global index + global.all[uuid] = global.all[uuid] or self + global.all._n = global.all._n+1 + -- indexs the thread in other places if it has more function + -- if it was paused before (ie did not run any events) then the index is removed from the paused index + 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 - table.each(self._events,function(callback,event,threads,uuid) + table.each(self._events,function(callback,event,global,uuid) -- cant be used V --Server.add_thread_handler(event) - if not threads.events[event] then threads.events[event] = {} end - table.insert(threads.events[event],uuid) - end,threads,self.uuid) + if not global.events[event] then global.events[event] = {} end + table.insert(global.events[event],uuid) + end,global,self.uuid) end return true 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 --- @treturn bolean if the thread had a on_close function +-- @treturn boolean if the thread had a on_close function function Server._thread:close() - local threads = Server._threads() local uuid = self.uuid 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 - local value,key = table.find(threads.queue,function(v,k,uuid) return v == uuid end,uuid) - if key then table.remove(threads.queue,key) end - local value,key = table.find(threads.timeout,function(v,k,uuid) return v == uuid end,uuid) - if key then table.remove(threads.timeout,key) end - local value,key = table.find(threads.tick,function(v,k,uuid) return v == uuid end,uuid) - if key then table.remove(threads.tick,key) end + -- will search every possible location for this thread and remove it + local value,key = table.find(global.queue,function(v,k,uuid) return v == uuid end,uuid) + if key then table.remove(global.queue,key) end -- queue + local value,key = table.find(global.timeout,function(v,k,uuid) return v == uuid end,uuid) + if key then table.remove(global.timeout,key) end -- timeouts + 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 table.each(self._events,function(callback,event) - if threads.events[event] then - local value,key = table.find(threads.events[event],function(v,k,uuid) return v == uuid end,uuid) - if key then table.remove(threads.events[event],key) end + if global.events[event] then + local value,key = table.find(global.events[event],function(v,k,uuid) return v == uuid end,uuid) + if key then table.remove(global.events[event],key) end -- 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 + -- sets the thread to closed self.opened=nil + -- unless the thread has a name or is assied to be reopened if self.reopen == true then self:open() else - if is_type(self.name,'string') then threads.paused[self.name]=self.uuid - else threads.all[uuid] = nil threads.all._n = threads.all._n-1 end + -- if it has a name but not assied to reopen then it will become 'pasued' + 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 return _return end @@ -294,107 +345,105 @@ end -- @treturn bolean true if the thread called on_success or on_error function Server._thread:resolve(...) local _return = false + -- checks if the resolve haddler is still present 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 it was successful then it will attemp to call the success handler if is_type(self._success,'function') then - Server.interface(function(thread,err) - local success,err = pcall(thread._success,thread,err) + -- interface is used as a way to delay the success call till the next tick + 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 end,true,self,err) + -- later returns true if there was a call to the success handler _return = true end - else - _return = self:error(err) - end + -- if there is an error the thread is asked to deal with it, returns true/false based on result of handler + else _return = self:error(err) end end + -- closes the thread as it is no longer needed as its command has be ran self:close() return _return 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 --- @treturn bolean if the thread timedout +-- @treturn bolean if the thread timed out function Server._thread:check_timeout() local _return = false + -- makes sure the thread is still valid 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,'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 _return = true + -- closes the thread to provent any further event calls self:close() end return _return 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 --- @param err the err to be rasied --- @treturn bolean did the thread handdle the error +-- @tparam string err the err to be rasied +-- @treturn boolean did the thread have an error handler function Server._thread:error(err) local _return = false if is_type(self._error,'function') then pcall(self._error,self,err) _return = true - else - error(err) - end + else error(err) end return _return 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 --- events = ['close','timeout','tick','resolve','success','error'] --- if event is a number then it is asumed to be a game event --- @tparam string event the name of the event that it is called on --- @tparam function callback the function which is called on the event --- @treturn table returns self so that there can be chained + +--- Set function to run then an event is triggered, none of them are 'needed' but you are advised to have atleast one +-- @usage thread:on_event('close',function) -- if event is not one below then a game event is used +-- @usage thread_only_events = ['close','timeout','tick','resolve','success','error'] +-- @tparam ?string|index event the name of the event that the function should be called on +-- @tparam function callback the function which is called by the event trigger +-- @treturn table returns self so that they can be chained together function Server._thread:on_event(event,callback) 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) 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 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 self._events[event] = callback end + -- returns self to allowing chaining of on_event():on_event():on_event() etc.. return self 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 - local threads = Server._threads() - if #threads.tick > 0 then Server.run_tick_threads() end - if #threads.timeout > 0 then Server.check_timeouts() end - if #threads.queue > 0 then - local current_thread = threads.all[threads.queue[1]] + if #global.tick > 0 then Server.run_tick_threads() end -- on tick events + if #global.timeout > 0 then Server.check_timeouts() end -- timeout checks + if #global.queue > 0 then -- resolve one thread + local current_thread = global.all[global.queue[1]] if current_thread and current_thread:valid() then current_thread:resolve() end end end) -Event.register(-2,function(event) - local threads = Server.threads() - for uuid,thread in pairs(threads) do - if uuid ~= '_n' then setmetatable(thread,{__index=Server._thread}) end - end +script.on_event(-2,function(event) + -- sets up metatable again so that threads contiune to work + for uuid,thread in pairs(Server.threads) do setmetatable(thread,{__index=Server._thread}) end end) Server.on_init=function(self) - Event.register(defines.events,Server._thread_handler) - 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 + if loaded_modules.commands then require(module_path..'/src/commands') end end return Server + --[[ Thread Example: @@ -424,5 +473,22 @@ return Server end) 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() ]] \ No newline at end of file diff --git a/modules/ExpGamingCore/Server/src/commands.lua b/modules/ExpGamingCore/Server/src/commands.lua new file mode 100644 index 00000000..ce8ccd35 --- /dev/null +++ b/modules/ExpGamingCore/Server/src/commands.lua @@ -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) \ No newline at end of file diff --git a/modules/ExpGamingCore/Sync/control.lua b/modules/ExpGamingCore/Sync/control.lua index 00a0bdc0..d1ee6a31 100644 --- a/modules/ExpGamingCore/Sync/control.lua +++ b/modules/ExpGamingCore/Sync/control.lua @@ -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 -- @alias Sync -- @author Cooldude2606 @@ -6,19 +6,28 @@ local Sync = {} local Sync_updates = {} --- sets up the global for this module -global{ - server_name='Factorio Server', - server_description='A factorio server for everyone', - reset_time='On Demand', - time='Day Mth 00 00:00:00 UTC Year', - time_set={0,'0.00M'}, - last_update={0,'0.00M'}, - time_period={18000,'5.00M'}, - players={online={'Offline'},n_online=0,all={'Offline'},n_all=0,admins_online=0,afk_players=0,times={'Offline'}}, - ranks={'Offline'}, - rockets=0, - mods={'Offline'} +--- Global Table +-- @table global +local global = global{ + server_name='Factorio Server', -- the server name + server_description='A factorio server for everyone', -- a short description of the server + reset_time='On Demand', -- the reset time of the server + time='Day Mth 00 00:00:00 UTC Year', -- the last knowen irl time + time_set={0,'0.00M'}, -- the last in game time that the time was set + last_update={0,'0.00M'}, -- the last time that this info was updated + time_period={18000,'5.00M'}, -- how often this infomation is updated + players={ + online={'Offline'}, -- list of all players online + 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 diff --git a/modules/ExpGamingCore/Sync/src/gui.lua b/modules/ExpGamingCore/Sync/src/gui.lua index 9b120853..dc3b4b23 100644 --- a/modules/ExpGamingCore/Sync/src/gui.lua +++ b/modules/ExpGamingCore/Sync/src/gui.lua @@ -4,7 +4,7 @@ -- @author Cooldude2606 -- @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 local Sync_gui_functions = {} diff --git a/modules/ExpGamingCore/Sync/src/ranking.lua b/modules/ExpGamingCore/Sync/src/ranking.lua index 4925290b..fa719f2e 100644 --- a/modules/ExpGamingCore/Sync/src/ranking.lua +++ b/modules/ExpGamingCore/Sync/src/ranking.lua @@ -4,7 +4,7 @@ -- @author Cooldude2606 -- @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 --- Used as a redirect to Ranking._base_preset that will set the rank given to a player apon joining diff --git a/modules/ExpGamingCore/softmod.json b/modules/ExpGamingCore/softmod.json index 2515b2d7..752d42c8 100644 --- a/modules/ExpGamingCore/softmod.json +++ b/modules/ExpGamingCore/softmod.json @@ -52,6 +52,7 @@ "location": "url", "dependencies": { "ExpGamingLib": ">=3.0.0", + "FactorioStdLib.Table": ">=0.8.0", "ExpGamingCore/Ranking": "?>=3.0.0", "ExpGamingCore/Commands": "?>=3.0.0" } diff --git a/modules/ExpGamingLib/control.lua b/modules/ExpGamingLib/control.lua index 07d852d9..b44bf297 100644 --- a/modules/ExpGamingLib/control.lua +++ b/modules/ExpGamingLib/control.lua @@ -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 -- @module ExpGamingLib -- @alias ExpLib -- @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 ExpLib = {} diff --git a/modules/index.lua b/modules/index.lua index 873f1090..08e83cdb 100644 --- a/modules/index.lua +++ b/modules/index.lua @@ -12,6 +12,6 @@ return { --['Ranking']='/modules/ExpGamingCore/Ranking', --['commands']='/modules/ExpGamingCore/Commands', --['Gui']='/modules/ExpGamingCore/Gui', - --['Server']='/modules/ExpGamingCore/Server', + ['Server']='/modules/ExpGamingCore/Server', ['Sync']='/modules/ExpGamingCore/Sync', } \ No newline at end of file