diff --git a/FactorioSoftmodManager.lua b/FactorioSoftmodManager.lua index 28c9c3b0..4d549d64 100644 --- a/FactorioSoftmodManager.lua +++ b/FactorioSoftmodManager.lua @@ -155,6 +155,7 @@ Manager.verbose('Current state is now: "selfInit"; The verbose state is: '..tost Manager.sandbox = setmetatable({ -- can not use existing keys of _G verbose=Manager.verbose, + loaded_modules=ReadOnlyManager, module_verbose=false, module_exports=false },{ diff --git a/modules/ExpGamingCore/Sync/control.lua b/modules/ExpGamingCore/Sync/control.lua index 862912bc..17a312ed 100644 --- a/modules/ExpGamingCore/Sync/control.lua +++ b/modules/ExpGamingCore/Sync/control.lua @@ -9,44 +9,38 @@ Discord: https://discord.gg/r6dC2uK --Please Only Edit Below This Line----------------------------------------------------------- -- this file is used to allow easy syncing with out side programes local Sync = {} -local Sync_gui_functions = {} local Sync_updates = {} ---- Used as a faster way to get to the ranking function, overrides previous --- @usage Sync.set_ranks{name=rank_name} -function Sync.set_ranks(...) - if Ranking then Ranking._base_preset(...) else error('Ranking module not installed') end -end - --- Used to standidise the tick format for any sync info -- @usage Sync.tick_format(60) -- return {60,'1.00M'} +-- @treturn {number,string} table containg both the raw number and clean version of a time function Sync.tick_format(tick) + if not is_type(tick,'number') then error('Tick was not given to Sync.tick_format',2) end return {tick,tick_to_display_format(tick)} end --- Prints to chat as if it were a player -- @usage Sync.print('Test','Cooldude2606') --- @param player_message the message to be printed in chat --- @param player_name the name of the player sending the message --- @param[opt] player_tag the tag apllied to the player's name --- @param[opt] player_colour the colour of the message --- @param[opt] prefix add a prefix before the chat eg [IRC] +-- @tparam string player_message the message to be printed in chat +-- @tparam string player_name the name of the player sending the message +-- @tparam[opt] string player_tag the tag apllied to the player's name +-- @tparam[opt] string player_colour the colour of the message, either hex or named colour +-- @tparam[opt] string prefix add a prefix before the chat eg [IRC] function Sync.print(player_message,player_name,player_tag,player_colour,prefix) - if not player_message then return 'No Message Found' end + if not player_message then error('No message given to Sync.print',2) end local player = game.player or game.players[player_name] local tag = player_tag and player_tag ~= '' and ' '..player_tag or '' - local colour = player_colour and player_colour ~= '' and player_colour or '#FFFFFF' + local colour = type(player_colour) == 'string' and player_colour or '#FFFFFF' local prefix = prefix and prefix..' ' or '' + -- if it is an ingame player it will over ride the given params if player then tag = ' '..player.tag colour = player.chat_color player_name = player.name else - if colour:find('#') then - colour = Color.from_hex(colour) - else - colour = defines.color[player_colour] - end + -- converts colour into the accepted factorio version + if colour:find('#') then colour = Color.from_hex(colour) + else colour = defines.color[player_colour] end end game.print(prefix..player_name..tag..': '..player_message,colour) end @@ -54,6 +48,7 @@ end --- Logs an embed to the json.data we use a js script to add things we cant here -- @usage Sync.emit_embeded{title='BAN',color='0x0',description='A player was banned' ... } -- @tparam table args a table which contains everything that the embeded will use +-- @table args -- @field title the tile of the embed -- @field color the color given in hex you can use Color.to_hex{r=0,g=0,b=0} -- @field description the description of the embed @@ -61,26 +56,30 @@ end -- @field fieldone the filed to add to the embed (key is name) (value is text) (start value with <> to make inline) -- @field fieldtwo the filed to add to the embed (key is name) (value is text) (start value with <> to make inline) function Sync.emit_embeded(args) - if not is_type(args,'table') then return end + if not is_type(args,'table') then error('Args table not given to Sync.emit_embeded',2) end local title = is_type(args.title,'string') and args.title or '' local color = is_type(args.color,'string') and args.color:find("0x") and args.color or '0x0' local description = is_type(args.description,'string') and args.description or '' local server_detail = is_type(args.server_detail,'string') and args.server_detail or '' - local mods_online = 'Mods Online: '..Sync.info().players.admins_online + local mods_online = 'Mods Online: '..Sync.info.players.admins_online + -- creates the first field given for every emit local done, fields = {title=true,color=true,description=true,server_detail=true}, {{ - name='Server Details', + name='Server Details', value='Server Name: {{ serverName }} Online Players: '..#game.connected_players..' '..mods_online..' Server Time: '..tick_to_display_format(game.tick)..' '..server_detail }} + -- for each value given in args it will create a new field for the embed for key, value in pairs(args) do if not done[key] then done[key] = true local f = {name=key,value='',inline=false} + -- if <> is present then it will cause the field to be inline if the previous local value, inline = value:gsub("<>",'',1) - f.value = value if inline > 0 then f.inline = true end + f.value = value table.insert(fields,f) end end + -- forms the data that will be emited to the file local log_data = { title=title, description=description, @@ -90,16 +89,19 @@ function Sync.emit_embeded(args) game.write_file('embeded.json',table.json(log_data)..'\n',true,0) end --- set up error handle +--- The error handle setup by sync to emit a discord embed for any errors +-- @function errorHandler +-- @tparam string err the error passed by the err control error.addHandler('Discord Emit',function(err) - local color = _G.Color and Color.to_hex(defines.textcolor.bg) or '0x0' + local color = Color and Color.to_hex(defines.textcolor.bg) or '0x0' Sync.emit_embeded{title='SCRIPT ERROR',color=color,description='There was an error in the script @Developers ',Error=err} end) ---- used to get the number of admins currently online --- @usage Sync.count_admins() --- @treturn int the number of admins online +--- Used to get the number of admins currently online +-- @usage Sync.count_admins() -- returns number +-- @treturn number the number of admins online function Sync.count_admins() + -- game check if not game then return 0 end local _count = 0 for _,player in pairs(game.connected_players) do @@ -108,10 +110,10 @@ function Sync.count_admins() return _count end ---- used to get the number of afk players defined by 2 min by default +--- Used to get the number of afk players defined by 2 min by default -- @usage Sync.count_afk() -- @tparam[opt=7200] int time in ticks that a player is called afk --- @treturn int the number of afk players +-- @treturn number the number of afk players function Sync.count_afk(time) if not game then return 0 end local time = time or 7200 @@ -122,27 +124,30 @@ function Sync.count_afk(time) return _count end ---- used to get the number of players in each rank and currently online +--- Used to get the number of players in each rank and currently online; if ExpGamingCore/Ranking is present then it will give more than admin and user -- @usage Sync.count_ranks() -- @treturn table contains the ranks and the players in that rank function Sync.count_ranks() if not game then return {'Offline'} end - if not Ranking then return {'Ranking module not installed'} end - local _ranks = {} - for power,rank in pairs(Ranking._ranks()) do - local players = rank:get_players() - for k,player in pairs(players) do players[k] = player.name end - local online = rank:get_players(true) - for k,player in pairs(online) do online[k] = player.name end - _ranks[rank.name] = {players=players,online=online,n_players=#players,n_online=#online} + local _ranks = {admin={online={},players={}},user={online={},players={}}} + for power,rank in pairs(game.players) do + if player.admin then + table.insert(_ranks.admin.players,player.name) + if player.connected then table.insert(_ranks.admin.online,player.name) end + else + table.insert(_ranks.user.players,player.name) + if player.connected then table.insert(_ranks.user.online,player.name) end + end end + _ranks.admin.n_players,_ranks.admin.n_online=#_ranks.admin.players,#_ranks.admin.online + _ranks.user.n_players,_ranks.user.n_online=#_ranks.user.players,#_ranks.user.online return _ranks end ---- used to get the number of players either online or all +--- Used to get a list of every player name with the option to limit to only online players -- @usage Sync.count_players() --- @tparam bolean online if true only get online players --- @treturn table contains player names +-- @tparam boolean online true will get only the online players +-- @treturn table table of player names function Sync.count_players(online) if not game then return {'Offline'} end local _players = {} @@ -152,9 +157,9 @@ function Sync.count_players(online) return players end ---- used to get the number of players resulting in there play times +--- Used to get a list of every player name with the amount of time they have played for -- @usage Sync.count_player_times() --- @treturn table contains players and each player is given a tick amount and a formated string +-- @treturn table table indexed by player name, each value contains the raw tick and then the clean string function Sync.count_player_times() if not game then return {'Offline'} end local _players = {} @@ -164,64 +169,50 @@ function Sync.count_player_times() return _players end ---- used to return the global list and set values in it --- @usage Sync.info{server_name='Factorio Server 2'} +--- used to get the global list that has been defined, also used to set that list +-- @usage Sync.info{server_name='Factorio Server 2'} -- returns true +-- @usage Sync.info -- table of info -- @tparam[opt=nil] table set keys to be replaced in the server info --- @return either returns success when setting or the info when not setting -function Sync.info(set) - if not global.exp_core then global.exp_core = {} end - if not global.exp_core.sync then global.exp_core.sync = { - 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=Sync.tick_format(0), - last_update=Sync.tick_format(0), - time_period=Sync.tick_format(18000), - players={ - online=Sync.count_players(true), - n_online=#game.connected_players, - all=Sync.count_players(), - n_all=#game.players, - admins_online=Sync.count_admins(), - afk_players=Sync.count_afk(), - times=Sync.count_player_times() - }, - ranks=Sync.count_ranks(), - rockets=game.forces['player'].get_item_launched('satellite'), - mods={'base'} - } end - if not set then return global.exp_core.sync - else +-- @treturn boolean success was the data set +Sync.info = setmetatable({},{ + __index=function(tbl,key) + if not global.exp_core then global.exp_core = {} end + if not global.exp_core.sync then global.exp_core.sync = { + 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=Sync.tick_format(0), + last_update=Sync.tick_format(0), + time_period=Sync.tick_format(18000), + players={ + online=Sync.count_players(true), + n_online=#game.connected_players, + all=Sync.count_players(), + n_all=#game.players, + admins_online=Sync.count_admins(), + afk_players=Sync.count_afk(), + times=Sync.count_player_times() + }, + ranks=Sync.count_ranks(), + rockets=game.forces['player'].get_item_launched('satellite'), + mods={'base'} + } end + return global.exp_core.sync[key] + end, + __call=function(tbl,set) + local _ = tbl.time -- used to create the global if not made if not is_type(set,'table') then return false end - for key,value in pairs(set) do - global.exp_core.sync[key] = value - end + for key,value in pairs(set) do global.exp_core.sync[key] = value end return true end -end +}) ---- used to return the global time and set its value --- @usage Sync.time('Sun Apr 1 18:44:30 UTC 2018') --- @tparam[opt=nil] string set the date time to be set --- @return either true false if setting or the date time and tick off set -function Sync.time(set) - local info = Sync.info() - if not set then return info.time..' (+'..(game.tick-info.time_set[1])..' Ticks)' - else - if not is_type(set,'string') then return false end - info.time = set - info.time_set[1] = game.tick - info.time_set[2] = tick_to_display_format(game.tick) - return true - end -end - ---- called to update values inside of the info +--- Called to update values inside of the info -- @usage Sync.update() -- @return all of the new info function Sync.update() - local info = Sync.info() + local info = Sync.info info.time_period[2] = tick_to_display_format(info.time_period[1]) info.last_update[1] = game.tick info.last_update[2] = tick_to_display_format(game.tick) @@ -253,112 +244,33 @@ end --- outputs the curent server info into a file -- @usage Sync.emit_data() function Sync.emit_data() - local info = Sync.info() + local info = Sync.info game.write_file('server-info.json',table.json(info),false,0) end -- will auto replace the file every 5 min by default -Event.register(defines.events.on_tick,function(event) - local time = Sync.info().time_period[1] +script.on_event('on_tick',function(event) + local time = Sync.info.time_period[1] if (event.tick%time)==0 then Sync.update() Sync.emit_data() end end) ---- Adds a emeltent to the sever info gui --- @usage Sync.add_to_gui('string') -- return trues --- @param element see examples before for what can be used, it can also be a return from Gui.inputs.add --- @treturn bolean based on weather it was successful or not -function Sync.add_to_gui(element,...) - if game then return false end - if is_type(element,'function') then - table.insert(Sync_gui_functions,{'function',element,...}) - elseif is_type(element,'table') then - if element.draw then table.insert(Sync_gui_functions,{'gui',element}) - else table.insert(Sync_gui_functions,{'table',element}) end - else table.insert(Sync_gui_functions,{'string',element}) end - return true +function Sync:on_init() + --- Used to return and set the current IRL time; not very good need a better way to do this + -- @usage Sync.time('Sun Apr 1 18:44:30 UTC 2018') + -- @usage Sync.time -- string + -- @tparam[opt=nil] string set the date time to be set + -- @treturn boolean if the datetime set was successful + Sync.time=add_metatable({},function(set) + local info = Sync.info + if not is_type(set,'string') then return false end + info.time = set + info.time_set[1] = game.tick + info.time_set[2] = tick_to_display_format(game.tick) + return true + end,function() local info = Sync.info return info.time..' (+'..(game.tick-info.time_set[1])..' Ticks)' end) + -- optinal dependies + if loaded_modules.Gui then verbose('ExpGamingCore.Gui is installed; Loading gui lib') require(module_path..'/lib/gui') end + if loaded_modules.Ranking then verbose('ExpGamingCore.Ranking is installed; Loading ranking lib') require(module_path..'/lib/ranking') end end - -Sync.on_init=function(self) - -- Examples for Sync.add_to_gui - -- adds a basic string to the table - Sync.add_to_gui('Welcome to the Explosive Gaming comunity! This is one of many servers which we host.') - if Ranking then - -- adds a string that can have depentant values - Sync.add_to_gui(function(player,frame) - return 'You have been assigned the rank \''..Ranking.get_rank(player).name..'\'' - end) - end - Sync.add_to_gui(function(player,frame) - return 'This server will reset at: '..Sync.info().reset_time - end) -end --- if readme is included then see addons/guis/readme.lua for more examples - --- used to load the gui infomation when _G.Gui is not yet loaded --- internal use not recomend to be used -function Sync._load() - local function label_format(label,width) - label.style.width = width - label.style.align = 'center' - label.style.single_line = false - end - Gui.center.add{ - name='server-info', - caption='Server Info', - tooltip='Basic info about the current server', - draw=function(self,frame) - frame.caption = '' - local info = Sync.info() - local frame = frame.add{type='flow',direction='vertical'} - local _flow = frame.add{type='flow'} - Gui.bar(_flow,200) - label_format(_flow.add{ - type='label', - caption='Welcome To '..info.server_name, - style='caption_label' - },180) - Gui.bar(_flow,200) - label_format(frame.add{ - type='label', - caption=info.server_description,style='description_label' - },600) - Gui.bar(frame,600) - local _frame = frame - local frame = frame.add{ - type='frame', - direction='vertical', - style='image_frame' - } - frame.style.width = 600 - local text_flow = frame.add{type='flow',direction='vertical'} - local button_flow = frame.add{type='table',column_count=3} - for _,element in pairs(table.deepcopy(Sync_gui_functions)) do - local _type = table.remove(element,1) - if _type == 'function' then - local success, err = pcall(table.remove(element,1),frame.player_index,frame,unpack(element)) - if not success then error(err) else - if is_type(err,'table') then - if element.draw then element:draw(button_flow).style.width = 195 - else label_format(text_flow.add{type='label',caption=err},585) end - else label_format(text_flow.add{type='label',caption=tostring(err)},585) end - end - elseif _type == 'gui' then element[1]:draw(button_flow).style.width = 195 - elseif _type == 'string' then label_format(text_flow.add{type='label',caption=tostring(element[1])},585) - elseif _type == 'table' then label_format(text_flow.add{type='label',caption=element[1]},585) end - end - _frame.add{ - type='label', - caption='Press Ecs or E to close; this is only visible once!', - style='fake_disabled_label' - }.style.font='default-small' - end} -end - --- opens the server info gui for all new joins except admins -Event.register(defines.events.on_player_joined_game,function(event) - local player = Game.get_player(event) - Gui.center.open(player,'server-info') -end) - return Sync \ No newline at end of file diff --git a/modules/ExpGamingCore/Sync/lib/gui.lua b/modules/ExpGamingCore/Sync/lib/gui.lua new file mode 100644 index 00000000..8a1c5771 --- /dev/null +++ b/modules/ExpGamingCore/Sync/lib/gui.lua @@ -0,0 +1,93 @@ +--- Description - A small description that will be displayed on the doc +-- @submodule ExpGamingCore.Sync +-- @alias Sync +-- @author Cooldude2606 +-- @license https://github.com/explosivegaming/scenario/blob/master/LICENSE + +-- this file will be loaded when ExpGamingCore/Gui is present + +local Sync_gui_functions = {} + +--- Adds a emeltent to the sever info gui +-- @usage Sync.add_to_gui('string') -- return true +-- @param element see examples before for what can be used, it can also be a return from Gui.inputs.add +-- @treturn bolean based on weather it was successful or not +function Sync.add_to_gui(element,...) + if game then return false end + if is_type(element,'function') then + table.insert(Sync_gui_functions,{'function',element,...}) + elseif is_type(element,'table') then + if element.draw then table.insert(Sync_gui_functions,{'gui',element}) + else table.insert(Sync_gui_functions,{'table',element}) end + else table.insert(Sync_gui_functions,{'string',element}) end + return true +end + +Sync.add_to_gui('Welcome to the Explosive Gaming comunity! This is one of many servers which we host.') +Sync.add_to_gui(function(player,frame) return 'This server will reset at: '..Sync.info.reset_time end) + +--- Formats a lable to be a certain format +-- @local label_format +local function label_format(label,width) + label.style.width = width + label.style.align = 'center' + label.style.single_line = false +end + +--- Creates a center gui that will appear on join +-- @local call to Gui.Center +Gui.center.add{ + name='server-info', + caption='Server Info', + tooltip='Basic info about the current server', + draw=function(self,frame) + frame.caption = '' + local info = Sync.info + local frame = frame.add{type='flow',direction='vertical'} + local _flow = frame.add{type='flow'} + Gui.bar(_flow,200) + label_format(_flow.add{ + type='label', + caption='Welcome To '..info.server_name, + style='caption_label' + },180) + Gui.bar(_flow,200) + label_format(frame.add{ + type='label', + caption=info.server_description,style='description_label' + },600) + Gui.bar(frame,600) + local _frame = frame + local frame = frame.add{ + type='frame', + direction='vertical', + style='image_frame' + } + frame.style.width = 600 + local text_flow = frame.add{type='flow',direction='vertical'} + local button_flow = frame.add{type='table',column_count=3} + for _,element in pairs(table.deepcopy(Sync_gui_functions)) do + local _type = table.remove(element,1) + if _type == 'function' then + local success, err = pcall(table.remove(element,1),frame.player_index,frame,unpack(element)) + if not success then error(err) else + if is_type(err,'table') then + if element.draw then element:draw(button_flow).style.width = 195 + else label_format(text_flow.add{type='label',caption=err},585) end + else label_format(text_flow.add{type='label',caption=tostring(err)},585) end + end + elseif _type == 'gui' then element[1]:draw(button_flow).style.width = 195 + elseif _type == 'string' then label_format(text_flow.add{type='label',caption=tostring(element[1])},585) + elseif _type == 'table' then label_format(text_flow.add{type='label',caption=element[1]},585) end + end + _frame.add{ + type='label', + caption='Press Ecs or E to close; this is only visible once!', + style='fake_disabled_label' + }.style.font='default-small' +end} + +script.on_event(defines.events.on_player_joined_game,function(event) + local player = Game.get_player(event) + Gui.center.open(player,'server-info') +end) \ No newline at end of file diff --git a/modules/ExpGamingCore/Sync/lib/ranking.lua b/modules/ExpGamingCore/Sync/lib/ranking.lua new file mode 100644 index 00000000..81e671da --- /dev/null +++ b/modules/ExpGamingCore/Sync/lib/ranking.lua @@ -0,0 +1,39 @@ +--- Description - A small description that will be displayed on the doc +-- @submodule ExpGamingCore.Sync +-- @alias Sync +-- @author Cooldude2606 +-- @license https://github.com/explosivegaming/scenario/blob/master/LICENSE + +-- this file will be loaded when ExpGamingCore/Ranking is present + +--- Used as a redirect to Ranking._base_preset that will set the rank given to a player apon joining +-- @usage Sync.set_ranks{player_name=rank_name,...} +-- @see Ranking._base_preset +function Sync.set_ranks(...) + Ranking._base_preset(...) +end + +--- Used to get the number of players in each rank and currently online +-- @usage Sync.count_ranks() +-- @treturn table contains the ranks and the players in that rank +function Sync.count_ranks() + if not game then return {'Offline'} end + if not Ranking then return {'Ranking module not installed'} end + local _ranks = {} + for power,rank in pairs(Ranking._ranks()) do + local players = rank:get_players() + for k,player in pairs(players) do players[k] = player.name end + local online = rank:get_players(true) + for k,player in pairs(online) do online[k] = player.name end + _ranks[rank.name] = {players=players,online=online,n_players=#players,n_online=#online} + end + return _ranks +end + +--- Adds a caption to the info gui that shows the rank given to the player +-- @local callback for Sync.add_to_gui +if Sync.add_to_gui then + Sync.add_to_gui(function(player,frame) + return 'You have been assigned the rank \''..Ranking.get_rank(player).name..'\'' + end) +end \ No newline at end of file diff --git a/modules/ExpGamingCore/softmod.json b/modules/ExpGamingCore/softmod.json index 70f629b5..2515b2d7 100644 --- a/modules/ExpGamingCore/softmod.json +++ b/modules/ExpGamingCore/softmod.json @@ -65,11 +65,14 @@ "location": "url", "dependencies": { "ExpGamingLib": ">=3.0.0", - "Ranking": "?>=3.0.0" + "FactorioStdLib.Color": ">=0.8.0", + "FactorioStdLib.Table": ">=0.8.0", + "ExpGamingCore.Ranking": "?>=3.0.0", + "ExpGamingCore.Gui": "?>=3.0.0" } } }, "author": "Cooldude2606", "contact": "Discord: Cooldude2606#5241", - "license": "https://github.com/badgamernl/explosivegaming-main/blob/master/LICENSE" + "license": "https://github.com/explosivegaming/scenario/blob/master/LICENSE" } \ No newline at end of file diff --git a/modules/ExpGamingLib/control.lua b/modules/ExpGamingLib/control.lua index 3cd59332..103d49b5 100644 --- a/modules/ExpGamingLib/control.lua +++ b/modules/ExpGamingLib/control.lua @@ -45,6 +45,23 @@ function ExpLib.get_env() return env end +--- Creats a table that will act like a string and a function +-- @usage add_metatable({},function) -- returns table +-- @tparam table tbl the table that will have its metatable set +-- @tparam[opt=tostring] function callback the function that will be used for the call +-- @tparam[opt=table.tostring] ?function|string string a function that resolves to a string or a string +-- @treturn table the new table with its metatable set +function ExpLib.add_metatable(tbl,callback,string) + if not ExpLib.is_type(tbl,'table') then error('No table given to add_metatable',2) end + local callback = ExpLib.is_type(callback,'function') and callback or tostring + local string = ExpLib.is_type(string,'function') and string or ExpLib.is_type(string,'string') and function() return string end or table.tostring + return setmetatable(tbl,{ + __tostring=string, + __concat=function(val1,val2) return type(val1) == 'string' and val1..string() or string()..val2 end, + __call=callback + }) +end + --- Compear types faster for faster valadation of prams -- @usage is_type('foo','string') -- return true -- @usage is_type('foo') -- return false diff --git a/modules/ExpGamingLib/softmod.json b/modules/ExpGamingLib/softmod.json index 0f433a9f..d247df06 100644 --- a/modules/ExpGamingLib/softmod.json +++ b/modules/ExpGamingLib/softmod.json @@ -12,6 +12,6 @@ }, "author": "Cooldude2606", "contact": "Discord: Cooldude2606#5241", - "license": "https://github.com/badgamernl/explosivegaming-main/blob/master/LICENSE" + "license": "https://github.com/explosivegaming/scenario/blob/master/LICENSE" } \ No newline at end of file