diff --git a/config/_file_loader.lua b/config/_file_loader.lua index ce035ac0..5fd71d8d 100644 --- a/config/_file_loader.lua +++ b/config/_file_loader.lua @@ -12,7 +12,6 @@ return { 'modules.commands.me', 'modules.commands.kill', 'modules.commands.admin-chat', - 'modules.commands.tag', 'modules.commands.teleport', 'modules.commands.cheat-mode', 'modules.commands.ratio', @@ -27,13 +26,9 @@ return { 'modules.commands.spawn', 'modules.commands.warnings', 'modules.commands.find', - 'modules.commands.bonus', 'modules.commands.home', - 'modules.commands.quickbar', --- Addons - 'modules.addons.station-auto-name', - 'modules.addons.greetings', 'modules.addons.chat-popups', 'modules.addons.damage-popups', 'modules.addons.death-logger', @@ -42,11 +37,20 @@ return { 'modules.addons.compilatron', 'modules.addons.scorched-earth', 'modules.addons.pollution-grading', - 'modules.addons.random-player-colours', + 'modules.addons.station-auto-name', 'modules.addons.discord-alerts', 'modules.addons.chat-reply', 'modules.addons.tree-decon', + --- Data + 'modules.data.statistics', + 'modules.data.player-colours', + 'modules.data.greetings', + 'modules.data.quickbar', + 'modules.data.alt-view', + 'modules.data.tag', + 'modules.data.bonus', + --- GUI 'modules.gui.readme', 'modules.gui.rocket-info', diff --git a/config/expcore/roles.lua b/config/expcore/roles.lua index 4d9f9dad..c1904ada 100644 --- a/config/expcore/roles.lua +++ b/config/expcore/roles.lua @@ -140,9 +140,7 @@ Roles.new_role('Sponsor','Spon') 'command/home-set', 'command/home-get', 'command/return', - 'fast-tree-decon', - 'command/load-quickbar', - 'command/save-quickbar' + 'fast-tree-decon' } Roles.new_role('Supporter','Sup') @@ -152,7 +150,8 @@ Roles.new_role('Supporter','Sup') :set_parent('Veteran') :allow{ 'command/jail', - 'command/unjail' + 'command/unjail', + 'command/join-message' } Roles.new_role('Partner','Part') @@ -187,7 +186,8 @@ Roles.new_role('Member','Mem') 'gui/task-list/add', 'gui/task-list/edit', 'gui/warp-list/add', - 'gui/warp-list/edit' + 'gui/warp-list/edit', + 'command/save-quickbar' } Roles.new_role('Regular','Reg') @@ -220,8 +220,9 @@ local default = Roles.new_role('Guest','') 'command/report', 'command/ratio', 'command/server-ups', - 'command/data-preference', - 'command/set-data-preference', + 'command/save-data', + 'command/preference', + 'command/set-preference', 'gui/player-list', 'gui/rocket-info', 'gui/science-info', diff --git a/config/statistics.lua b/config/statistics.lua new file mode 100644 index 00000000..36c60da3 --- /dev/null +++ b/config/statistics.lua @@ -0,0 +1,31 @@ +--- A list of all tracked statistics and the events which trigger them +-- @config Statistics + +local e = defines.events -- order as per lua api as it was easier just to go down the list +return { + Playtime = true, --- @setting Playtime If playtime is tracked for a player, play time measured in minutes + AfkTime = true, --- @setting AfkTime If afk time is tracked for a player, play time measured in minutes, afk is once a player does nothing for 5 minutes + DistanceTraveled = true, --- @setting DistanceTraveled If distance traveled is checked, only counts if not afk + MachinesRemoved = true, --- @setting MachinesRemoved If removed machines are tracked, includes marked for decon and player mined entity + TreesDestroyed = true, --- @setting OreMined If ore mined is tracked for a player, includes marked for decon and player mined entity but only trees + OreMined = true, --- @setting OreMined If ore mined is tracked for a player, includes player mined entity but only ore + DamageDealt = true, --- @setting DamageDealt If damage dealt is tracked for a player, includes any damage to entities not on the same force or neutral + Kills = true, --- @setting Kills If kills are tracked for a player, includes all kills not on same force or neutral + RocketsLaunched = true, --- @setting RocketsLaunched If the number of rockets launched should be tracked, done for all players on the force + ResearchCompleted = true, --- @setting ResearchCompleted If the number of researches completed should be tracked, done for all players on the force + counters = { --- @setting counters Simple statistics that just go up by one each time an event happens + MachinesBuilt = e.on_built_entity, + MapTagsMade = e.on_chart_tag_added, + ChatMessages = e.on_console_chat, + CommandsUsed = e.on_console_command, + ItemsPickedUp = e.on_picked_up_item, + TilesBuilt = e.on_player_built_tile, + ItemsCrafted = e.on_player_crafted_item, + MapsPlayed = e.on_player_created, + DeconstructionPlanerUsed = e.on_player_deconstructed_area, + Deaths = e.on_player_died, + JoinCount = e.on_player_joined_game, + TilesRemoved = e.on_player_mined_tile, + CapsulesUsed = e.on_player_used_capsule + } +} \ No newline at end of file diff --git a/docs/config.ld b/docs/config.ld index 30e23b4b..68c0c7a5 100644 --- a/docs/config.ld +++ b/docs/config.ld @@ -19,6 +19,7 @@ new_type("core", "Core", true) new_type("control", "Control", true) new_type("addon", "Addons", true) new_type("gui", "Guis", true) +new_type("data", "Data", true) new_type("commands", "Commands", true) new_type("config", "Configs", true, "Settings") diff --git a/expcore/datastore.lua b/expcore/datastore.lua index be6848e4..ffa23f99 100644 --- a/expcore/datastore.lua +++ b/expcore/datastore.lua @@ -153,6 +153,7 @@ local Datastores = {} local Datastore = {} local Data = {} local copy = table.deep_copy +local trace = debug.traceback --- Save datastores in the global table global.datastores = Data @@ -250,7 +251,6 @@ function DatastoreManager.ingest(action, datastoreName, key, valueJson) elseif action == 'propagate' or action == 'request' then local success, value = pcall(game.json_to_table, valueJson) if not success or value == nil then value = tonumber(valueJson) or valueJson end - datastore:raw_set(key) -- clear any existing data value = datastore:raise_event('on_load', key, value) datastore:set(key, value) @@ -360,7 +360,7 @@ function Datastore:raw_set(key, value) end end -local function serialize_error(err) error('An error ocurred in a datastore serializer: '..err) end +local function serialize_error(err) error('An error ocurred in a datastore serializer: '..trace(err)) end --[[-- Internal, Return the serialized key @tparam any rawKey The key that needs to be serialized, if it is already a string then it is returned @treturn string The key after it has been serialized @@ -389,11 +389,11 @@ self:write_action('save', 'TestKey', 'Foo') ]] function Datastore:write_action(action, key, value) - local data = {action, self.name, '"'..key..'"'} + local data = {action, self.name, key} if value ~= nil then - data[4] = type(value) == 'table' and '"'..game.table_to_json(value)..'"' or '"'..tostring(value)..'"' + data[4] = type(value) == 'table' and game.table_to_json(value) or value end - game.write_file('datastore.out', table.concat(data, ' ')..'\n', true, 0) + game.write_file('ext/datastore.out', table.concat(data, ' ')..'\n', true, 0) end ----- Datastore Local @@ -517,10 +517,10 @@ ExampleData:increment('TestNumber') function Datastore:increment(key, delta) key = self:serialize(key) local value = self:raw_get(key) or 0 - return Datastore:set(key, value + (delta or 1)) + return self:set(key, value + (delta or 1)) end -local function update_error(err) error('An error ocurred in datastore update: '..err, 2) end +local function update_error(err) error('An error ocurred in datastore update: '..trace(err), 2) end --[[-- Use a function to update the value locally, will trigger on_update then on_save, save_to_disk and auto_save is required for on_save @tparam any key The key that you want to apply the update to, must be a string unless a serializer is set @tparam function callback The function that will be used to update the value at this key @@ -560,7 +560,7 @@ function Datastore:remove(key) if self.parent and self.parent.auto_save then return self.parent:save(key) end end -local function filter_error(err) print('An error ocurred in a datastore filter:', err) end +local function filter_error(err) log('An error ocurred in a datastore filter:'..trace(err)) end --[[-- Internal, Used to filter elements from a table @tparam table tbl The table that will have the filter applied to it @tparam[opt] function callback The function that will be used as a filter, if none giving then the provided table is returned @@ -737,7 +737,7 @@ end ----- Events -- @section events -local function event_error(err) print('An error ocurred in a datastore event handler:', err) end +local function event_error(err) log('An error ocurred in a datastore event handler: '..trace(err)) end --[[-- Internal, Raise an event on this datastore @tparam string event_name The name of the event to raise for this datastore @tparam string key The key that this event is being raised for @@ -752,7 +752,7 @@ value = self:raise_event('on_save', key, value) function Datastore:raise_event(event_name, key, value, source) -- Raise the event for the children of this datastore if source ~= 'child' and next(self.children) then - if type(value) ~= 'table' then value = self:raw_get(key, true) end + if type(value) ~= 'table' then value = {} end for value_name, child in pairs(self.children) do value[value_name] = child:raise_event(event_name, key, value[value_name], 'parent') end @@ -769,7 +769,7 @@ function Datastore:raise_event(event_name, key, value, source) -- Raise the event for the parent of this datastore if source ~= 'parent' and self.parent then - self.parent:raise_event(event_name, key, self.parent:raw_get(key), 'child') + self.parent:raise_event(event_name, key, self.parent:raw_get(key, true), 'child') end -- If this is the save event and the table is empty then return nil diff --git a/expcore/player_data.lua b/expcore/player_data.lua index 057ae404..9d8ea963 100644 --- a/expcore/player_data.lua +++ b/expcore/player_data.lua @@ -42,6 +42,7 @@ end) ]] local Event = require 'utils.event' --- @dep utils.event +local Async = require 'expcore.async' --- @dep expcore.async local Datastore = require 'expcore.datastore' --- @dep expcore.datastore local Commands = require 'expcore.commands' --- @dep expcore.commands require 'config.expcore.command_general_parse' --- @dep config.expcore.command_general_parse @@ -58,7 +59,7 @@ DataSavingPreference:set_default('All') --- Sets your data saving preference -- @command set-data-preference -Commands.new_command('set-data-preference', 'Allows you to set your data saving preference') +Commands.new_command('set-preference', 'Allows you to set your data saving preference') :add_param('option', false, 'string-options', PreferenceEnum) :register(function(player, option) DataSavingPreference:set(player, option) @@ -67,16 +68,45 @@ end) --- Gets your data saving preference -- @command data-preference -Commands.new_command('data-preference', 'Shows you what your current data saving preference is') +Commands.new_command('preference', 'Shows you what your current data saving preference is') :register(function(player) return {'expcore-data.get-preference', DataSavingPreference:get(player)} end) +--- Gets your data and writes it to a file +Commands.new_command('save-data', 'Writes all your player data to a file on your computer') +:register(function(player) + player.print{'expcore-data.get-data'} + game.write_file('expgaming_player_data.json', game.table_to_json(PlayerData:get(player, {})), false, player.index) +end) + +--- Async function called after 5 seconds with no player data loaded +local check_data_loaded = Async.register(function(player) + local player_data = PlayerData:get(player) + if not player_data or not player_data.valid then + player.print{'expcore-data.data-failed'} + Datastore.ingest('request', 'PlayerData', player.name, '{"valid":false}') + end +end) + +--- When player data loads tell the player if the load had failed previously +PlayerData:on_load(function(player_name, player_data) + if not player_data or player_data.valid == false then return end + local existing_data = PlayerData:get(player_name) + if existing_data and existing_data.valid == false then + game.players[player_name].print{'expcore-data.data-restore'} + end + player_data.valid = true +end) + --- Remove data that the player doesnt want to have stored PlayerData:on_save(function(player_name, player_data) local dataPreference = DataSavingPreference:get(player_name) dataPreference = PreferenceEnum[dataPreference] - if dataPreference == PreferenceEnum.All then return player_data end + if dataPreference == PreferenceEnum.All then + player_data.valid = nil + return player_data + end local saved_player_data = { PlayerRequired = player_data.PlayerRequired, DataSavingPreference = PreferenceEnum[dataPreference] } if dataPreference <= PreferenceEnum.Settings then saved_player_data.PlayerSettings = player_data.PlayerSettings end @@ -92,12 +122,18 @@ end) --- Load player data when they join Event.add(defines.events.on_player_joined_game, function(event) - PlayerData:request(game.players[event.player_index]) + local player = game.players[event.player_index] + Async.wait(300, check_data_loaded, player) + PlayerData:request(player) end) --- Unload player data when they leave Event.add(defines.events.on_player_left_game, function(event) - PlayerData:unload(game.players[event.player_index]) + local player = game.players[event.player_index] + local player_data = PlayerData:get(player) + if player_data.valid == true then + PlayerData:unload(player) + else PlayerData:raw_set(player) end end) ----- Module Return ----- diff --git a/locale/en/addons.cfg b/locale/en/addons.cfg index 4cb4c837..ed0d9123 100644 --- a/locale/en/addons.cfg +++ b/locale/en/addons.cfg @@ -77,7 +77,4 @@ get-mead-1= Filling the drinking horn get-mead-2= Skål! get-beer-1= 🍺 Pouring A Glass 🍺 get-beer-2= 🍻 Chears Mate 🍻 -verify=Please return to our discord and type r!verify __1__ - -[greetings] -greet=[color=0,1,0] Welcome to explosive gaming community server! If you like the server join our discord: __1__ [/color] \ No newline at end of file +verify=Please return to our discord and type r!verify __1__ \ No newline at end of file diff --git a/locale/en/data.cfg b/locale/en/data.cfg new file mode 100644 index 00000000..6816c16b --- /dev/null +++ b/locale/en/data.cfg @@ -0,0 +1,6 @@ +[join-message] +greet=[color=0,1,0] Welcome to explosive gaming community server! If you like the server join our discord: __1__ [/color] +message-set=Your join message has been updated. + +[quickbar] +saved=Your quickbar filters have been saved. \ No newline at end of file diff --git a/locale/en/expcore.cfg b/locale/en/expcore.cfg index 5fd4fefe..7d50cd29 100644 --- a/locale/en/expcore.cfg +++ b/locale/en/expcore.cfg @@ -39,4 +39,7 @@ left-button-tooltip=Hide all open windows. [expcore-data] set-preference=You data saving preference has been set to __1__. Existing data will not be effected until you rejoin. -get-preference=You data saving preference is __1__. Use /set-data-preference to change this. \ No newline at end of file +get-preference=You data saving preference is __1__. Use /set-preference to change this. Use /save-data to get a local copy of your data. +get-data=Your player data has been writen to file, location: factorio/script_output/expgaming_player_data.json +data-failed=Your player data has failed to load. Any changes to your data will not be saved. +data-restore=Your player data has been restored and changes will now save when you leave. \ No newline at end of file diff --git a/modules/addons/greetings.lua b/modules/addons/greetings.lua deleted file mode 100644 index 093cf1b5..00000000 --- a/modules/addons/greetings.lua +++ /dev/null @@ -1,26 +0,0 @@ ---- Greets players on join --- @addon greetings - -local Event = require 'utils.event' --- @dep utils.event -local Game = require 'utils.game' --- @dep utils.event -local config = require 'config.join_messages' --- @dep config.join_messages -local Global = require 'utils.global' --- @dep utils.global -require 'overrides.table' - -Global.register(config, function(tbl) - config = tbl -end) - -local greet = -function(event) - local player = Game.get_player_by_index(event.player_index) - local custom_message = config[player.name] - if custom_message then - game.print(custom_message, player.color) - else - player.print{'greetings.greet', {'links.discord'}} - end - -end - -Event.add(defines.events.on_player_joined_game, greet) \ No newline at end of file diff --git a/modules/addons/random-player-colours.lua b/modules/addons/random-player-colours.lua deleted file mode 100644 index 17b95ddb..00000000 --- a/modules/addons/random-player-colours.lua +++ /dev/null @@ -1,29 +0,0 @@ ---- Gives players random colours when they join, also applies preset colours to those who have them --- @addon Player-Colours - -local Colours = require 'utils.color_presets' --- @dep utils.color_presets -local Game = require 'utils.game' --- @dep utils.game -local Event = require 'utils.event' --- @dep utils.event -local config = require 'config.preset_player_colours' --- @dep config.preset_player_colours -local Global = require 'utils.global' --- @dep utils.global -require 'overrides.table' - -Global.register(config, function(tbl) - config = tbl -end) - -Event.add(defines.events.on_player_created, function(event) - local player = Game.get_player_by_index(event.player_index) - local color = 'white' - if config.players[player.name] then - color = config.players[player.name] - else - while config.disallow[color] do - color = table.get_random_dictionary_entry(Colours, true) - end - color = Colours[color] - end - color = {r=color.r/255, g=color.g/255, b=color.b/255, a=0.5} - player.color = color - player.chat_color = color -end) \ No newline at end of file diff --git a/modules/commands/quickbar.lua b/modules/commands/quickbar.lua deleted file mode 100644 index c297ddfe..00000000 --- a/modules/commands/quickbar.lua +++ /dev/null @@ -1,40 +0,0 @@ ---[[-- Commands Module - Quickbar - - Adds a command that allows players to load Quickbar presets - @commands Quickbar -]] - -local Commands = require 'expcore.commands' --- @dep expcore.commands -local config = require 'config.preset_player_quickbar' --- @dep config.preset_player_quickbar - ---- Loads your quickbar preset --- @command load-quickbar -Commands.new_command('load-quickbar', 'Loads your preset Quickbar items') -:add_alias('load-toolbar') -:register(function(player) - if config[player.name] then - local custom_quickbar = config[player.name] - for i, item_name in pairs(custom_quickbar) do - if item_name ~= nil and item_name ~= '' then - player.set_quick_bar_slot(i, item_name) - end - end - else - Commands.error('Quickbar preset not found') - end -end) - ---- Saves your quickbar preset to the script-output folder --- @command save-quickbar -Commands.new_command('save-quickbar', 'Saves your Quickbar preset items to file') -:add_alias('save-toolbar') -:register(function(player) - local quickbar_names = {} - for i=1, 100 do - local slot = player.get_quick_bar_slot(i) - if slot ~= nil then - quickbar_names[i] = slot.name - end - end - game.write_file("quickbar_preset.txt", player.name .. " = " .. serpent.line(quickbar_names) .. "\n", true) - Commands.print("Quickbar saved") -end) diff --git a/modules/data/alt-view.lua b/modules/data/alt-view.lua new file mode 100644 index 00000000..83aca25a --- /dev/null +++ b/modules/data/alt-view.lua @@ -0,0 +1,21 @@ +--- Stores if you use alt mode or not and auto applies it +-- @data Alt-View + +local Event = require 'utils.event' ---@dep utils.event + +--- Stores the join message that the player have +local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data +local UsesAlt = PlayerData.Settings:combine('UsesAlt') +UsesAlt:set_default(false) + +--- When your data loads apply alt view if you have it enabled +UsesAlt:on_load(function(player_name, uses_alt) + local player = game.players[player_name] + player.game_view_settings.show_entity_info = uses_alt or false +end) + +--- When alt view is toggled update this +Event.add(defines.events.on_player_toggled_alt_mode, function(event) + local player = game.players[event.player_index] + UsesAlt:set(player, player.game_view_settings.show_entity_info) +end) \ No newline at end of file diff --git a/modules/commands/bonus.lua b/modules/data/bonus.lua similarity index 57% rename from modules/commands/bonus.lua rename to modules/data/bonus.lua index 28327eaa..2275d543 100644 --- a/modules/commands/bonus.lua +++ b/modules/data/bonus.lua @@ -1,22 +1,19 @@ --[[-- Commands Module - Bonus - Adds a command that allows players to have increased stats - @commands Bonus + @data Bonus ]] -local Commands = require 'expcore.commands' --- @dep expcore.commands local Roles = require 'expcore.roles' --- @dep expcore.roles local Event = require 'utils.event' --- @dep utils.event -local Game = require 'utils.game' --- @dep utils.game -local Store = require 'expcore.store' --- @dep expcore.store local config = require 'config.bonuses' --- @dep config.bonuses +local Commands = require 'expcore.commands' --- @dep expcore.commands require 'config.expcore.command_general_parse' --- Store bonus percentages keyed by player name -local bonus_store = Store.register(function(player) - return player.name -end) +--- Stores the bonus for the player +local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data +local PlayerBonus = PlayerData.Settings:combine('Bonus') --- Apply a bonus amount to a player +--- Apply a bonus amount to a player local function apply_bonus(player, amount) if not amount then return end for bonus, min_max in pairs(config) do @@ -25,6 +22,11 @@ local function apply_bonus(player, amount) end end +--- When store is updated apply new bonus to the player +PlayerBonus:on_update(function(player_name, player_bonus) + apply_bonus(game.players[player_name], player_bonus or 0) +end) + --- Changes the amount of bonus you receive -- @command bonus -- @tparam number amount range 0-50 the percent increase for your bonus @@ -32,41 +34,32 @@ Commands.new_command('bonus', 'Changes the amount of bonus you receive') :add_param('amount', 'integer-range', 0,50) :register(function(player, amount) local percent = amount/100 - Store.set(bonus_store, player, percent) + PlayerBonus:set(player, percent) Commands.print{'expcom-bonus.set', amount} Commands.print({'expcom-bonus.wip'}, 'orange') end) --- When store is updated apply new bonus to the player -Store.watch(bonus_store, function(value, category) - local player = Game.get_player_from_any(category) - apply_bonus(player, value) -end) - --- When a player respawns re-apply bonus +--- When a player respawns re-apply bonus Event.add(defines.events.on_player_respawned, function(event) - local player = Game.get_player_by_index(event.player_index) - local value = Store.get(bonus_store, player) - apply_bonus(player, value) + local player = game.players[event.player_index] + apply_bonus(player, PlayerBonus:get(player)) end) --- When a player dies allow them to have instant respawn +--- When a player dies allow them to have instant respawn Event.add(defines.events.on_player_died, function(event) - local player = Game.get_player_by_index(event.player_index) + local player = game.players[event.player_index] if Roles.player_has_flag(player, 'instance-respawn') then player.ticks_to_respawn = 120 end end) --- Remove bonus if a player no longer has access to the command +--- Remove bonus if a player no longer has access to the command local function role_update(event) - local player = Game.get_player_by_index(event.player_index) + local player = game.players[event.player_index] if not Roles.player_allowed(player, 'command/bonus') then - Store.clear(bonus_store, player) + PlayerBonus:remove(player) end end Event.add(Roles.events.on_role_assigned, role_update) -Event.add(Roles.events.on_role_unassigned, role_update) - -return bonus_store \ No newline at end of file +Event.add(Roles.events.on_role_unassigned, role_update) \ No newline at end of file diff --git a/modules/data/greetings.lua b/modules/data/greetings.lua new file mode 100644 index 00000000..84def7e3 --- /dev/null +++ b/modules/data/greetings.lua @@ -0,0 +1,33 @@ +--- Greets players on join +-- @data Greetings + +local config = require 'config.join_messages' --- @dep config.join_messages +local Commands = require 'expcore.commands' ---@dep expcore.commands +require 'config.expcore.command_general_parse' + +--- Stores the join message that the player have +local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data +local CustomMessages = PlayerData.Settings:combine('JoinMessage') + +--- When a players data loads show their message +CustomMessages:on_load(function(player_name, player_message) + local player = game.players[player_name] + local custom_message = player_message or config[player_name] + if custom_message then + game.print(custom_message, player.color) + else + player.print{'join-message.greet', {'links.discord'}} + end +end) + +--- Set your custom join message +-- @command join-message +-- @tparam string message The custom join message that will be used +Commands.new_command('join-message', 'Sets your custom join message') +:add_param('message', false, 'string-max-length', 255) +:enable_auto_concat() +:register(function(player, message) + if not player then return end + CustomMessages:set(player, message) + return {'join-message.message-set'} +end) \ No newline at end of file diff --git a/modules/data/player-colours.lua b/modules/data/player-colours.lua new file mode 100644 index 00000000..bed8012f --- /dev/null +++ b/modules/data/player-colours.lua @@ -0,0 +1,50 @@ +--- Gives players random colours when they join, also applies preset colours to those who have them +-- @data Player-Colours + +local Event = require 'utils.event' --- @dep utils.event +local Colours = require 'utils.color_presets' --- @dep utils.color_presets +local config = require 'config.preset_player_colours' --- @dep config.preset_player_colours + +--- Stores the colour that the player wants +local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data +local PlayerColours = PlayerData.Settings:combine('Colour') + +--- Used to compact player colours to take less space +local floor = math.floor +local function compact(colour) + return { + floor(colour.r * 255), + floor(colour.g * 255), + floor(colour.b * 255) + } +end + +--- When your data loads apply the players colour, or a random on if none is saved +PlayerColours:on_load(function(player_name, player_colour) + if not player_colour then + local preset = config.players[player_name] + if preset then + player_colour = {preset, preset} + else + local colour_name = 'white' + while config.disallow[colour_name] do + colour_name = table.get_random_dictionary_entry(Colours, true) + end + player_colour = {Colours[colour_name], Colours[colour_name]} + end + end + + local player = game.players[player_name] + player.color = player_colour[1] + player.chat_color = player_colour[2] +end) + +--- Save the players color when they use the color command +Event.add(defines.events.on_console_command, function(event) + if event.command ~= 'color' then return end + if event.parameters == '' then return end + if not event.player_index then return end + local player = game.players[event.player_index] + if not player or not player.valid then return end + PlayerColours:set(player, {compact(player.color), compact(player.chat_color)}) +end) \ No newline at end of file diff --git a/modules/data/quickbar.lua b/modules/data/quickbar.lua new file mode 100644 index 00000000..d2352a7f --- /dev/null +++ b/modules/data/quickbar.lua @@ -0,0 +1,45 @@ +--[[-- Commands Module - Quickbar + - Adds a command that allows players to load Quickbar presets + @data Quickbar +]] + +local Commands = require 'expcore.commands' --- @dep expcore.commands +local config = require 'config.preset_player_quickbar' --- @dep config.preset_player_quickbar + +--- Stores the quickbar filters for a player +local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data +local PlayerFilters = PlayerData.Settings:combine('QuickbarFilters') + +--- Loads your quickbar preset +PlayerFilters:on_load(function(player_name, filters) + if not filters then filters = config[player_name] end + if not filters then return end + local player = game.players[player_name] + for i, item_name in pairs(filters) do + if item_name ~= nil and item_name ~= '' then + player.set_quick_bar_slot(i, item_name) + end + end +end) + +--- Saves your quickbar preset to the script-output folder +-- @command save-quickbar +Commands.new_command('save-quickbar', 'Saves your Quickbar preset items to file') +:add_alias('save-toolbar') +:register(function(player) + local filters = {} + for i = 1, 100 do + local slot = player.get_quick_bar_slot(i) + if slot ~= nil then + filters[i] = slot.name + end + end + + if next(filters) then + PlayerFilters:set(player, filters) + else + PlayerFilters:remove(player) + end + + return {'quickbar.saved'} +end) \ No newline at end of file diff --git a/modules/data/statistics.lua b/modules/data/statistics.lua new file mode 100644 index 00000000..fd554b8b --- /dev/null +++ b/modules/data/statistics.lua @@ -0,0 +1,138 @@ + +local Event = require 'utils.event' ---@dep utils.event +local config = require 'config.statistics' ---@dep config.statistics +local floor = math.floor +local afk_required = 5*3600 -- 5 minutes + +--- Stores the statistics on a player +local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data +local AllPlayerData = PlayerData.All +local Statistics = PlayerData.Statistics + +--- Update your statistics with any which happened before the data was valid +Statistics:on_load(function(player_name, player_statistics) + local existing_data = AllPlayerData:get(player_name) + if existing_data and existing_data.valid then return end + local counters = config.counters + for key, value in pairs(Statistics:get(player_name, {})) do + if config[key] or counters[key] then + if not player_statistics[key] then + player_statistics[key] = value + else + player_statistics[key] = player_statistics[key] + value + end + end + end + return player_statistics +end) + +--- Add Playtime and AfkTime if it is enabled +if config.Playtime or config.AfkTime then + local playtime, afk_time + if config.Playtime then playtime = Statistics:combine('Playtime') end + if config.AfkTime then afk_time = Statistics:combine('AfkTime') end + Event.on_nth_tick(3600, function() + if game.tick == 0 then return end + for _, player in pairs(game.connected_players) do + if playtime then playtime:increment(player) end + if afk_time and player.afk_time > afk_required then afk_time:increment(player) end + end + end) +end + +--- Add DistanceTraveled if it is enabled +if config.DistanceTraveled then + local stat = Statistics:combine('DistanceTraveled') + Event.add(defines.events.on_player_changed_position, function(event) + local player = game.players[event.player_index] + if not player.valid or not player.connected or player.afk_time > afk_required then return end + stat:increment(player) + end) +end + +--- Add MachinesRemoved and TreesDestroyed and config.OreMined if it is enabled +if config.MachinesRemoved or config.TreesDestroyed or config.OreMined then + local machines, trees, ore + if config.MachinesRemoved then machines = Statistics:combine('MachinesRemoved') end + if config.TreesDestroyed then trees = Statistics:combine('TreesDestroyed') end + if config.OreMined then ore = Statistics:combine('OreMined') end + local function on_event(event) + if not event.player_index then return end -- Check player is valid + local player = game.players[event.player_index] + if not player.valid or not player.connected then return end + local entity = event.entity -- Check entity is valid + if not entity.valid then return end + if entity.type == 'resource' then ore:increment(player) + elseif entity.type == 'tree' then trees:increment(player) + elseif entity.force == player.force then machines:increment(player) end + end + Event.add(defines.events.on_marked_for_deconstruction, on_event) + Event.add(defines.events.on_player_mined_entity, on_event) +end + +--- Add DamageDealt if it is enabled +if config.DamageDealt then + local stat = Statistics:combine('DamageDealt') + Event.add(defines.events.on_entity_damaged, function(event) + local character = event.cause -- Check character is valid + if not character.valid or character.type ~= 'character' then return end + local player = character.player -- Check player is valid + if not player.valid or not player.connected then return end + local entity = event.entity -- Check entity is valid + if not entity.valid or entity.force == player.force or entity.force.name == 'neutral' then return end + stat:increment(player, floor(event.final_damage_amount)) + end) +end + +--- Add Kills if it is enabled +if config.DamageDealt then + local stat = Statistics:combine('Kills') + Event.add(defines.events.on_entity_died, function(event) + local character = event.cause -- Check character is valid + if not character.valid or character.type ~= 'character' then return end + local player = character.player -- Check player is valid + if not player.valid or not player.connected then return end + local entity = event.entity -- Check entity is valid + if not entity.valid or entity.force == player.force or entity.force.name == 'neutral' then return end + stat:increment(player) + end) +end + +--- Add RocketsLaunched if it is enabled +if config.RocketsLaunched then + local stat = Statistics:combine('RocketsLaunched') + Event.add(defines.events.on_rocket_launched, function(event) + local silo = event.rocket_silo -- Check silo is valid + if not silo or not silo.valid then return end + local force = silo.force -- Check force is valid + if not force or not force.valid then return end + for _, player in pairs(force.connected_players) do + stat:increment(player) + end + end) +end + +--- Add RocketsLaunched if it is enabled +if config.ResearchCompleted then + local stat = Statistics:combine('ResearchCompleted') + Event.add(defines.events.on_research_finished, function(event) + local research = event.research -- Check research is valid + if event.by_script or not research or not research.valid then return end + local force = research.force -- Check force is valid + if not force or not force.valid then return end + for _, player in pairs(force.connected_players) do + stat:increment(player) + end + end) +end + +--- Add all the remaining statistics from the config +for statistic, event_name in pairs(config.counters) do + local stat = Statistics:combine(statistic) + Event.add(event_name, function(event) + if not event.player_index then return end + local player = game.players[event.player_index] + if not player.valid or not player.connected then return end + stat:increment(player) + end) +end \ No newline at end of file diff --git a/modules/commands/tag.lua b/modules/data/tag.lua similarity index 71% rename from modules/commands/tag.lua rename to modules/data/tag.lua index 7b256f9c..d01b6918 100644 --- a/modules/commands/tag.lua +++ b/modules/data/tag.lua @@ -1,6 +1,6 @@ --[[-- Commands Module - Tag - Adds a command that allows players to have a custom tag after their name - @commands Tag + @data Tag ]] local Commands = require 'expcore.commands' --- @dep expcore.commands @@ -8,6 +8,20 @@ local Roles = require 'expcore.roles' --- @dep expcore.roles require 'config.expcore.command_general_parse' require 'config.expcore.command_role_parse' +--- Stores the tag for a player +local PlayerData = require 'expcore.player_data' --- @dep expcore.player_data +local PlayerTags = PlayerData.Settings:combine('Tag') + +--- When your tag is updated then apply the changes +PlayerTags:on_update(function(player_name, player_tag) + local player = game.players[player_name] + if player_tag == nil or player_tag == '' then + player.tag = '' + else + player.tag = '- '..player_tag + end +end) + --- Sets your player tag. -- @command tag -- @tparam string tag the tag that will be after the name, there is a max length @@ -15,7 +29,7 @@ Commands.new_command('tag', 'Sets your player tag.') :add_param('tag', false, 'string-max-length', 20) :enable_auto_concat() :register(function(player, tag) - player.tag = '- '..tag + PlayerTags:set(player, tag) end) --- Clears your tag. Or another player if you are admin. @@ -29,10 +43,10 @@ end} :register(function(player, action_player) if action_player.index == player.index then -- no player given so removes your tag - action_player.tag = '' + PlayerTags:remove(action_player) elseif Roles.player_allowed(player, 'command/clear-tag/always') then -- player given and user is admin so clears that player's tag - action_player.tag = '' + PlayerTags:remove(action_player) else -- user is not admin and tried to clear another users tag return Commands.error{'expcore-commands.unauthorized'} diff --git a/utils/event_core.lua b/utils/event_core.lua index b34715e1..9525167a 100644 --- a/utils/event_core.lua +++ b/utils/event_core.lua @@ -11,11 +11,16 @@ local event_handlers = {} -- map of nth_tick to handlers[] local on_nth_tick_event_handlers = {} -local pcall = pcall +local trace = debug.traceback +local xpcall = xpcall local log = log local script_on_event = script.on_event local script_on_nth_tick = script.on_nth_tick +local function handler_error(err) + log('\n\t'..trace(err)) +end + local function call_handlers(handlers, event) if _DEBUG then for i = 1, #handlers do @@ -24,11 +29,7 @@ local function call_handlers(handlers, event) end else for i = 1, #handlers do - local handler = handlers[i] - local success, error = pcall(handler, event) - if not success then - log('\n\t'..error) - end + xpcall(handlers[i], handler_error, event) end end end