diff --git a/config/warnings.lua b/config/warnings.lua new file mode 100644 index 00000000..5beace17 --- /dev/null +++ b/config/warnings.lua @@ -0,0 +1,19 @@ +--- Config file for the warning system, this is very similar to reports but is for the use of moderators rather than normal users. +return { + actions = { -- what actions are taking at number of warnings + -- if a localized string is used then __1__ will by_player_name and __2__ will be the current warning count (auto inserted) + {'warnings.received'}, + {'warnings.received'}, + {'warnings.received',{'warnings.pre-kick'}}, + function(player,by_player_name,number_of_warnings) + game.kick_player(player,{'warnings.received',by_player_name,number_of_warnings,{'warnings.kick'}}) + end, + {'warnings.received',{'warnings.pre-pre-ban'}}, + {'warnings.received',{'warnings.pre-ban'}}, + function(player,by_player_name,number_of_warnings) + game.ban_player(player,{'warnings.received',by_player_name,number_of_warnings,{'warnings.ban',{'info.website-link'}}}) + end + }, + temp_warning_cool_down=30, -- time for a temp warning (given by script) to be removed (in minutes) + temp_warning_limit=5 -- the number of temp warnings (given by script) that are allowed before full warnings are given +} \ No newline at end of file diff --git a/locale/en/addons.cfg b/locale/en/addons.cfg index d2d99105..07441927 100644 --- a/locale/en/addons.cfg +++ b/locale/en/addons.cfg @@ -25,4 +25,14 @@ custom-commands=We use custom commands, such as /tag and /me, use /chelp for mor read-readme=Make sure you have read the Readme (It can be found through the question mark on the top left) softmod=We run a softmod on our servers. A softmod is a custom scenario that runs on this server, example is the player list. redmew=We dont talk about redmew here; they beat us to 1000 members ;-; -lhd=All trains must be LHD! This is a long standing rule on our servers, please resepect this. \ No newline at end of file +lhd=All trains must be LHD! This is a long standing rule on our servers, please resepect this. + +[warnings] +received=You recived a warning from __1__. You have __2__ warnings. __3__ +pre-kick=This is your last warning before you are kicked. +kick=You were kicked for having too many warnings; you may rejoy if you wish. +pre-pre-ban=You are close to reciving a ban; successful ban appeals are unlikely. +pre-ban=This your LAST warning before you are BANNED! successful ban appeals are unlikely. +ban=You were banned for having too many warnings; Vist __1__ to request a ban appeal. +script-warning=You are reciving script warnings; if you recive too many you will recive a permiment warning (__1__/__2__) +script-wrning-removed=A script warning has expired (__1__/__2__) \ No newline at end of file diff --git a/locale/en/commands.cfg b/locale/en/commands.cfg index 8bf0ea4b..182899f8 100644 --- a/locale/en/commands.cfg +++ b/locale/en/commands.cfg @@ -28,4 +28,9 @@ report-not-reported=The player had no reports on them. report-player-count-title=The following players have reports against them: report-player-report-title=__1__ has the following reports agasinst them: report-list=__1__: __2__ -report-removed=__1__ has one or more reports removed by __2__. \ No newline at end of file +report-removed=__1__ has one or more reports removed by __2__. +warnings-received=__1__ recived a warning from __2__ for __3__. +warnings-player=__1__ has __2__ warnings and __3__/__4__ script warnings. +warnings-list-tilte=The following player have this many warnings (and this many script warnings): +warnings-list=__1__: __2__ (__3__/__4__) +warnings-cleared=__1__ had all they warnings cleared by __2__. \ No newline at end of file diff --git a/modules/addons/warnings-control.lua b/modules/addons/warnings-control.lua new file mode 100644 index 00000000..18e12d19 --- /dev/null +++ b/modules/addons/warnings-control.lua @@ -0,0 +1,216 @@ +local Game = require 'utils.game' +local Global = require 'utils.global' +local Event = require 'utils.event' +local config = require 'config.warnings' +require 'utils.table' + +local Public = { + user_warnings={}, + user_temp_warnings={}, + player_warning_added = script.generate_event_name(), + player_warning_removed = script.generate_event_name(), + player_temp_warning_added = script.generate_event_name(), + player_temp_warning_removed = script.generate_event_name() +} + +Global.register({ + Public.user_warnings, + Public.user_temp_warnings +},function(tbl) + Public.user_warnings = tbl[1] + Public.user_temp_warnings = tbl[2] +end) + +local function event_emit(event,player,by_player_name) + local warnings = Public.user_warnings[player.name] or {} + local temp_warnings = Public.user_temp_warnings[player.name] or {} + script.raise_event(event,{ + name=event, + tick=game.tick, + player_index=player.index, + by_player_name=by_player_name, + warning_count=#warnings, + temp_warning_count=#temp_warnings + }) +end + +--- Adds X number (default 1) of warnings to a player from the given player +-- @tparam player LuaPlayer the player to add the warning to +-- @tparam[opt=''] by_player_name string the name of the player doing the action +-- @tparam[opt=1] count number the number of warnings to add +-- @treturn number the new number of warnings +function Public.add_warnings(player,by_player_name,count) + player = Game.get_player_from_any(player) + if not player then return end + count = count or 1 + by_player_name = by_player_name or '' + local warnings = Public.user_warnings[player.name] + if not warnings then + Public.user_warnings[player.name] = {} + warnings = Public.user_warnings[player.name] + end + for _=1,count do + table.insert(warnings,by_player_name) + event_emit(Public.player_warning_added,player,by_player_name) + end + return #warnings +end + +--- Removes X number (default 1) of warnings from a player, removes in order fifo +-- @tparam player LuaPlayer the player to remove the warnings from +-- @tparam[opt=''] by_playey_name string the name of the player doing the action +-- @tparam[opt=1] count number the number of warnings to remove (if greater than current warning count then all are removed) +-- @treturn number the new number of warnings +function Public.remove_warnings(player,by_player_name,count) + player = Game.get_player_from_any(player) + if not player then return end + count = count or 1 + by_player_name = by_player_name or '' + local warnings = Public.user_warnings[player.name] + if not warnings then return end + for _=1,count do + if #warnings == 0 then break end + table.remove(warnings,1) + event_emit(Public.player_warning_removed,player,by_player_name) + end + if #warnings == 0 then + Public.user_warnings[player.name] = nil + return 0 + end + return #warnings +end + +--- Clears all warnings from a player, emits event multiple times as if remove_warnings was used +-- @tparam player LuaPlayer the player to clear the warnings of +-- @tparam[oot=''] by_player_name string the name of the player who is doing the action +-- @treturn boolean true if the warnings were cleared, nil if error +function Public.clear_warnings(player,by_player_name) + player = Game.get_player_from_any(player) + if not player then return end + local warnings = Public.user_warnings[player.name] + if not warnings then return end + by_player_name = by_player_name or '' + for _=1,#warnings do + event_emit(Public.player_warning_removed,player,by_player_name) + end + Public.user_warnings[player.name] = {} + return true +end + +--- Gets the number of warnings that a player has, raw table will contain the names of who gave warnings +-- @tparam player LuaPlayer the player to get the warnings of +-- @tparam[opt=false] raw_table when true will return a table which contains who gave warnings (the table stored in global) +-- @treturn number the number of warnings a player has, a table if raw_table is true +function Public.get_warnings(player,raw_table) + player = Game.get_player_from_any(player) + if not player then return end + local warnings = Public.user_warnings[player.name] or {} + if raw_table then + return warnings + else + return #warnings + end +end + +--- Adds a temp warning to a player that will timeout after some time, used for script given warnings (ie silent to outside players as a buffer) +-- @tparam player LuaPlayer the player to give the warnings to +-- @tparam[opt=1] count number the number of warnings to give to the player +-- @treturn number the new number of warnings +function Public.add_temp_warnings(player,count) + player = Game.get_player_from_any(player) + if not player then return end + count = count or 1 + local warnings = Public.user_temp_warnings[player.name] + if not warnings then + Public.user_temp_warnings[player.name] = {} + warnings = Public.user_temp_warnings[player.name] + end + for _=1,count do + table.insert(warnings,game.tick) + event_emit(Public.player_temp_warning_added,player,'') + end + return #warnings +end + +-- temp warnings cant be removed on demand only after X amount of time +local temp_warning_cool_down = config.temp_warning_cool_down*3600 +Event.on_nth_tick(temp_warning_cool_down/4,function() + local check_time = game.tick-temp_warning_cool_down + for player_name,temp_warnings in pairs(Public.user_temp_warnings) do + local player = Game.get_player_from_any(player) + for index,time in pairs(temp_warnings) do + if time <= check_time then + table.remove(temp_warnings,index) + player.print{'warnings.script-warning-removed',#temp_warnings,config.temp_warning_limit} + event_emit(Public.player_temp_warning_removed,player,'') + end + end + if #temp_warnings == 0 then + Public.user_temp_warnings[player_name] = nil + end + end +end) + +--- Clears all temp warnings from a player, emits events as if the warnings had been removed due to time +-- @tparam player LuaPlayer the player to clear the warnings of +-- @tparam[opt=''] by_player_name string the name of the player doing the action +-- @treturn boolean true if the warnings were cleared, nil for error +function Public.clear_temp_warnings(player,by_player_name) + player = Game.get_player_from_any(player) + if not player then return end + local warnings = Public.user_temp_warnings[player.name] + if not warnings then return end + by_player_name = by_player_name or '' + for _=1,#warnings do + event_emit(Public.player_temp_warning_removed,player,by_player_name) + end + Public.user_temp_warnings[player.name] = {} + return true +end + +--- Gets the number of temp warnings, raw table is a table of when temp warnings were given +-- @tparam player LuaPlayer the player to get the warnings of +-- @tparam[opt=false] raw_table if true will return a table of ticks when warnings were added (the global table) +-- @treturn number the number of warnings which the player has, a table if raw_table is true +function Public.get_temp_warnings(player,raw_table) + player = Game.get_player_from_any(player) + if not player then return end + local warnings = Public.user_temp_warnings[player.name] or {} + if raw_table then + return warnings + else + return #warnings + end +end + +-- when a player gets a warning the actions in config are ran +Event.add(Public.player_warning_added,function(event) + local action = config.actions[event.warning_count] + if not action then return end + local player = Game.get_player_by_index(event.player_index) + if type(action) == 'function' then + -- player: player who got the warnings,by_player_name: player who gave the last warning,number_of_warnings: the current number of warnings + local success,err = pcall(action,player,event.by_player_name,event.warning_count) + if not success then error(err) end + elseif type(action) == 'table' then + -- {locale,by_player_name,number_of_warning,...} + local current_action = table.deep_copy(action) + table.insert(current_action,1,event.by_player_name) + table.insert(current_action,1,event.warning_count) + player.print(current_action) + elseif type(action) == 'string' then + player.print(action) + end +end) + +-- when a player gets a tempo warnings it is checked that it is not above the max +Event.add(Public.player_temp_warning_added,function(event) + local player = Game.get_player_by_index(event.player_index) + if event.temp_warning_count > config.temp_warning_limit then + Public.add_warnings(event.player_index,event.by_player_name) + else + player.print{'warnings.script-warning',event.temp_warning_count,config.temp_warning_limit} + end +end) + +return Public \ No newline at end of file diff --git a/modules/commands/warnings.lua b/modules/commands/warnings.lua new file mode 100644 index 00000000..c4fe12f1 --- /dev/null +++ b/modules/commands/warnings.lua @@ -0,0 +1,57 @@ +local Commands = require 'expcore.commands' +local WarningsControl = require 'modules.addons.warnings-control' +local format_chat_player_name = ext_require('expcore.common','format_chat_player_name') +local config = require 'config.warnings' +require 'config.command_parse_roles' + +Commands.new_command('give-warning','Gives a warning to a player; may lead to automatic script action.') +:add_param('player',false,'player-role') +:add_param('reason',false) +:add_alias('warn') +:enable_auto_concat() +:register(function(player,action_player,reason,raw) + WarningsControl.add_warnings(action_player,player.name) + local action_player_name_color = format_chat_player_name(action_player) + local by_player_name_color = format_chat_player_name(player) + game.print{'exp-commands.warnings-received',action_player_name_color,by_player_name_color,reason} +end) + +Commands.new_command('get-warnings','Gets the number of warnings a player has. If no player then lists all players and the number of warnings they have.') +:add_param('player',true,'player') +:add_alias('warnings','list-warnings') +:register(function(player,action_player,raw) + if action_player then + local warnings = WarningsControl.get_warnings(action_player) + local script_warnings = WarningsControl.get_temp_warnings(action_player) + local action_player_name_color = format_chat_player_name(action_player) + Commands.print{'exp-commands.warnings-player',action_player_name_color,warnings,script_warnings,config.temp_warning_limit} + else + local rtn = {} + local user_warnings = WarningsControl.user_warnings + local user_temp_warnings = WarningsControl.user_temp_warnings + for player_name,warnings in pairs(user_warnings) do + rtn[player_name] = {#warnings,0} + end + for player_name,warnings in pairs(user_temp_warnings) do + if not rtn[player_name] then + rtn[player_name] = {0,0} + end + rtn[player_name][2] = #warnings + end + Commands.print{'exp-commands.warnings-list-tilte'} + for player_name,warnings in pairs(rtn) do + local player_name_color = format_chat_player_name(player_name) + Commands.print{'exp-commands.warnings-list',player_name_color,warnings[1],warnings[2],config.temp_warning_limit} + end + end +end) + +Commands.new_command('clear-warnigns','Clears all warnings (and script warnings) from a player') +:add_param('player',false,'player') +:register(function(player,action_player,raw) + WarningsControl.clear_warnings(player,player.name) + WarningsControl.clear_temp_warnings(player,player.name) + local action_player_name_color = format_chat_player_name(action_player) + local by_player_name_color = format_chat_player_name(player) + game.print{'exp-commands.warnings-cleared',action_player_name_color,by_player_name_color} +end) \ No newline at end of file