1
0
mirror of https://github.com/Refactorio/RedMew.git synced 2025-03-03 14:53:01 +02:00

Merge pull request #713 from plague006/rank_system

Overhaul rank system, command, donator, add probation rank
This commit is contained in:
Matthew 2019-02-14 11:43:04 -05:00 committed by GitHub
commit d48946f357
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 906 additions and 436 deletions

View File

@ -39,6 +39,11 @@ global.config = {
map_settings = true,
difficulty = true
},
-- time before a player gets the auto-trusted rank, allowing them access to the deconstructions planner, nukes, etc.
rank_system = {
time_for_trust = 3*60*60*60, -- 3 hours
everyone_is_regular = false
},
-- saves players' lives if they have a small-plane in their inventory, also adds the small-plane to the market and must therefor be loaded first
train_saviour = {
enabled = true
@ -231,10 +236,6 @@ global.config = {
admin_commands = {
enabled = true
},
-- enables donators' on-join messages
donator_messages = {
enabled = true
},
player_colors = {
enabled = true
},

View File

@ -21,7 +21,7 @@ require 'features.server_commands'
-- Library modules
-- If missing, will cause other feature modules to fail
require 'features.player_create'
require 'features.user_groups'
require 'features.rank_system'
-- Feature modules
-- Each can be disabled safely
@ -46,9 +46,6 @@ end
if config.redmew_commands.enabled then
require 'features.redmew_commands'
end
if config.donator_messages.enabled then
require 'features.donator_messages'
end
if config.market.enabled then
require 'features.market'
end

View File

@ -1,12 +1,14 @@
local Task = require 'utils.task'
local Token = require 'utils.token'
local Global = require 'utils.global'
local UserGroups = require 'features.user_groups'
local Rank = require 'features.rank_system'
local Report = require 'features.report'
local Utils = require 'utils.core'
local Game = require 'utils.game'
local Event = require 'utils.event'
local Command = require 'utils.command'
local Color = require 'resources.color_presets'
local Ranks = require 'resources.ranks'
local format = string.format
local loadstring = loadstring
@ -72,13 +74,71 @@ end
--- Add or remove someone from the list of regulars
local function regular(args)
local add_target = args['name|remove']
local remove_target = args['name']
local add_remove = args['add|remove']
local name = args['name']
if remove_target and add_target == 'remove' then
UserGroups.remove_regular(remove_target)
else
UserGroups.add_regular(add_target)
if not game.players[name] then
Game.player_print('The player you targeted has never joined this game, please ensure no typo in name.', Color.red)
return
end
if add_remove == 'add' then
if Rank.less_than(name, Ranks.guest) then
-- Cannot promote someone on probation to regular. You must remove them from probation and then promote them.
Game.player_print({'admin_commands.regular_add_fail_probation'}, Color.red)
end
local success, rank = Rank.increase_player_rank_to(name, Ranks.regular)
if success then
-- __1__ promoted __2__ to __3__.
game.print({'admin_commands.regular_add_success', Utils.get_actor(), name, rank}, Color.yellow)
else
-- __1__ is already rank __2__.
Game.player_print({'admin_commands.regular_add_fail', name, rank}, Color.red)
end
elseif add_remove == 'remove' then
if Rank.equal(name, Ranks.regular) then
local new_rank = Rank.decrease_player_rank(name)
-- __1__ demoted __2__ to __3__.
game.print({'admin_commands.regular_remove_success', Utils.get_actor(), name, new_rank}, Color.yellow)
else
local rank_name = Rank.get_player_rank_name(name)
-- __1__ is rank __2__ their regular status cannot be removed.
Game.player_print({'admin_commands.regular_remove_fail', name, rank_name}, Color.red)
end
end
end
--- Add or remove someone from probation
local function probation(args)
local add_remove = args['add|remove']
local name = args['name']
local target_player = game.players[name]
if not target_player then
Game.player_print('The player you targeted has never joined this game, please ensure no typo in name.', Color.red)
return
end
if add_remove == 'add' then
local success = Rank.decrease_player_rank_to(name, Ranks.probation)
if success and Rank.equal(name, Ranks.admin) then
target_player.print(format('%s tried to put you on probation, can you believe that shit?', Utils.get_actor()), Color.yellow)
Game.player_print('You failed to put your fellow admin on probation. Shame on you for trying.', Color.yellow)
Rank.reset_player_rank(name)
elseif success then
game.print(format('%s put %s on probation.', Utils.get_actor(), name), Color.yellow)
target_player.print('You have been placed on probation. You have limited access to normal functions.', Color.yellow)
else
Game.player_print(format('%s already has probation rank or lower.', name), Color.red)
end
elseif add_remove == 'remove' then
local success = Rank.increase_player_rank_to(name, Ranks.guest)
if success then
game.print(format('%s took %s off of probation.', Utils.get_actor(), name), Color.yellow)
target_player.print('Your probation status has been removed. You may now perform functions as usual', Color.yellow)
else
Game.player_print(format('%s is not on probation.', name), Color.red)
end
end
end
@ -281,7 +341,7 @@ Command.add(
{
description = 'Admin chat. Messages all other admins.',
arguments = {'msg'},
admin_only = true,
required_rank = Ranks.admin,
capture_excess_arguments = true,
allowed_by_server = true
},
@ -293,7 +353,7 @@ Command.add(
{
description = 'silent-command',
arguments = {'str'},
admin_only = true,
required_rank = Ranks.admin,
capture_excess_arguments = true,
allowed_by_server = true
},
@ -304,7 +364,7 @@ Command.add(
'hax',
{
description = 'Toggles your hax (makes recipes cost nothing)',
admin_only = true
required_rank = Ranks.admin
},
toggle_cheat_mode
)
@ -313,7 +373,7 @@ Command.add(
'all-tech',
{
description = 'researches all technologies',
admin_only = true,
required_rank = Ranks.admin,
debug_only = true,
cheat_only = true
},
@ -323,21 +383,30 @@ Command.add(
Command.add(
'regular',
{
description = 'Add/remove player from regualrs. Use /regular <name> to add or /regular remove <name> to remove.',
arguments = {'name|remove', 'name'},
default_values = {['name'] = false},
admin_only = true,
capture_excess_arguments = false,
allowed_by_server = false
description = 'Add/remove player from regualrs. Use /regular <add|remove> <name> to add/remove a regular.',
arguments = {'add|remove', 'name'},
required_rank = Ranks.admin,
allowed_by_server = true
},
regular
)
Command.add(
'probation',
{
description = 'Add/remove player from probation. Use /probation <add|remove> <name> to add/remove someone from probation.',
arguments = {'add|remove', 'name'},
required_rank = Ranks.admin,
allowed_by_server = true
},
probation
)
Command.add(
'showreports',
{
description = 'Shows user reports',
admin_only = true
required_rank = Ranks.admin
},
show_reports
)
@ -347,7 +416,7 @@ Command.add(
{
description = 'Puts a player in jail',
arguments = {'player'},
admin_only = true,
required_rank = Ranks.admin,
allowed_by_server = true
},
jail_player
@ -358,7 +427,7 @@ Command.add(
{
description = 'Removes a player from jail',
arguments = {'player'},
admin_only = true,
required_rank = Ranks.admin,
allowed_by_server = true
},
unjail_player
@ -369,7 +438,7 @@ Command.add(
{
description = 'Temporarily bans a player',
arguments = {'player', 'minutes'},
admin_only = true,
required_rank = Ranks.admin,
allowed_by_server = true
},
tempban
@ -379,7 +448,7 @@ Command.add(
'pool',
{
description = 'Spawns a pool of water',
admin_only = true
required_rank = Ranks.admin
},
pool
)
@ -389,7 +458,7 @@ Command.add(
{
description = 'Teleports the player to you.',
arguments = {'player'},
admin_only = true
required_rank = Ranks.admin
},
invoke
)
@ -400,7 +469,7 @@ Command.add(
description = 'if blank, teleport to selected entity. mode = toggle tp mode where you can teleport to a placed ghost. player = teleport to player.',
arguments = {'mode|player'},
default_values = {['mode|player'] = false},
admin_only = true,
required_rank = Ranks.admin,
custom_help_text = '<blank|mode|player> 3 different uses: "/tp" to tp to selected entity. "/tp mode" to toggle tp mode. "/tp Newcott" to tp to Newcott'
},
teleport_command
@ -412,7 +481,7 @@ Command.add(
description = 'Revives the ghosts within the provided radius around you',
arguments = {'radius'},
default_values = {radius = 10},
admin_only = true
required_rank = Ranks.admin
},
revive_ghosts
)

View File

@ -3,6 +3,7 @@ local Global = require 'utils.global'
local Token = require 'utils.token'
local Command = require 'utils.command'
local Event = require 'utils.event'
local Ranks = require 'utils.ranks'
local random = math.random
local ceil = math.ceil
local floor = math.floor
@ -55,7 +56,7 @@ Command.add('particle-scale', {
description = 'Provide a fraction between 0 and 1 to lower or increase the amount of (max) particles. Leave empty to view the current values.',
arguments = {'fraction'},
default_values = {fraction = false},
admin_only = true,
required_rank = Ranks.admin,
allowed_by_server = true,
}, function (arguments, player)
local p = player and player.print or print

155
features/donator.lua Normal file
View File

@ -0,0 +1,155 @@
local Event = require 'utils.event'
local Server = require 'features.server'
local Game = require 'utils.game'
local Token = require 'utils.token'
local table = require 'utils.table'
local Global = require 'utils.global'
local Task = require 'utils.task'
local concat = table.concat
local insert = table.insert
local donator_data_set = 'donators'
local donators = {} -- global register
Global.register(
{
donators = donators
},
function(tbl)
donators = tbl.donators
end
)
local Public = {}
--- Prints the donator message with the color returned from the server
local print_after_timeout =
Token.register(
function(data)
local player = data.player
if not player.valid then
return
end
game.print(data.message, player.chat_color)
end
)
--- When a player joins, set a 1s timer to retrieve their color before printing their welcome message
local function player_joined(event)
local player = Game.get_player_by_index(event.player_index)
if not player or not player.valid then
return
end
local d = donators[player.name]
if not d then
return nil
end
local message = d.welcome_messages
if not message then
return
end
message = concat({'*** ', message, ' ***'})
Task.set_timeout_in_ticks(60, print_after_timeout, {player = player, message = message})
end
--- Returns the table of donators
-- @return <table>
function Public.get_donators_table()
return donators
end
--- Checks if a player is a donator
-- @param player_name <string>
-- @return <boolean>
function Public.is_donator(player_name)
return donators[player_name] ~= nil
end
--- Checks if a player has a specific donator perk
-- @param player_name <string>
-- @param perf_flag <number>
-- @return <boolean>
function Public.player_has_donator_perk(player_name, perk_flag)
local d = donators[player_name]
if not d then
return false
end
local flags = d.perk_flags
if not flags then
return false
end
return bit32.band(flags, perk_flag) == perk_flag
end
--- Sets the data for a donator, all existing data for the entry is removed
-- @param player_name <string>
-- @param data <table>
-- @return <string|nil>
function Public.set_donator_data(player_name, data)
donators[player_name] = data
Server.set_data(donator_data_set, player_name, data)
end
--- Changes the data for a donator with any data that is sent, only overwritten data is affected
-- @param player_name <string>
-- @param data <table>
-- @return <string|nil>
function Public.change_donator_data(player_name, data)
for k, v in pairs(data) do
donators[player_name][k] = v
end
Server.set_data(donator_data_set, player_name, donators[player_name])
end
--- Writes the data called back from the server into the donators table, overwriting any matching entries
local sync_donators_callback =
Token.register(
function(data)
for k, v in pairs(data.entries) do
donators[k] = v
end
end
)
--- Signals the server to retrieve the donators data set
function Public.sync_donators()
Server.try_get_all_data(donator_data_set, sync_donators_callback)
end
--- Prints a list of donators
function Public.print_donators()
local result = {}
for k, _ in pairs(donators) do
insert(result, k)
end
result = concat(result, ', ')
Game.player_print(result)
end
Event.add(
Server.events.on_server_started,
function()
Public.sync_donators()
end
)
Server.on_data_set_changed(
donator_data_set,
function(data)
donators[data.key] = data.value
end
)
Event.add(defines.events.on_player_joined_game, player_joined)
return Public

View File

@ -1,33 +0,0 @@
local Game = require 'utils.game'
local Event = require 'utils.event'
local UserGroups = require 'features.user_groups'
local Task = require 'utils.task'
local Token = require 'utils.token'
local print_after_timeout =
Token.register(
function(data)
local player = data.player
if not player.valid then
return
end
game.print(data.message, player.chat_color)
end
)
local function player_joined(event)
local player = Game.get_player_by_index(event.player_index)
if not player or not player.valid then
return
end
local message = UserGroups.get_donator_welcome_message(player.name)
if not message then
return
end
message = table.concat({'*** ', message, ' ***'})
Task.set_timeout_in_ticks(60, print_after_timeout, {player = player, message = message})
end
Event.add(defines.events.on_player_joined_game, player_joined)

View File

@ -1,12 +1,13 @@
local Gui = require 'utils.gui'
local Global = require 'utils.global'
local Event = require 'utils.event'
local UserGroups = require 'features.user_groups'
local Donator = require 'features.donator'
local Rank = require 'features.rank_system'
local Game = require 'utils.game'
local PlayerRewards = require 'utils.player_rewards'
local Color = require 'resources.color_presets'
local Server = require 'features.server'
local Token = require 'utils.token'
local Color = require 'resources.color_presets'
local format = string.format
@ -16,12 +17,6 @@ local config_prewards = config.player_rewards
local normal_color = Color.white
local focus_color = Color.dark_orange
local rank_colors = {
Color.white, -- Guest
Color.regular, -- Regular
Color.donator, -- Donator
Color.admin -- Admin
}
local reward_amount = 2
local reward_plural_indicator = reward_amount > 1 and 's' or ''
@ -60,7 +55,7 @@ Global.register(
{
rewarded_players = rewarded_players,
editable_info = editable_info,
primitives = primitives,
primitives = primitives
},
function(tbl)
rewarded_players = tbl.rewarded_players
@ -379,18 +374,14 @@ you're working on a project which requires it.]]
ranks_label_style.single_line = false
local player_rank_flow = ranks_flow.add {type = 'flow', direction = 'horizontal'}
player_rank_flow.add {type = 'label', caption = 'Your rank is:'}
if player.admin then
local label = player_rank_flow.add {type = 'label', caption = 'Admin'}
label.style.font_color = rank_colors[4]
elseif UserGroups.is_donator(player.name) then
local label = player_rank_flow.add {type = 'label', caption = 'Donator'}
label.style.font_color = rank_colors[3]
elseif UserGroups.is_regular(player.name) then
local label = player_rank_flow.add {type = 'label', caption = 'Regular'}
label.style.font_color = rank_colors[2]
else
local label = player_rank_flow.add {type = 'label', caption = 'Guest'}
label.style.font_color = rank_colors[1]
local player_name = player.name
local rank_label = player_rank_flow.add {type = 'label', caption = Rank.get_player_rank_name(player_name)}
rank_label.style.font_color = Rank.get_player_rank_color(player_name)
if Donator.is_donator(player_name) then
local donator_label = player_rank_flow.add {type = 'label', caption = {'ranks.donator'}}
donator_label.style.font_color = Color.donator
end
grid.add {type = 'sprite', sprite = 'entity/market'}

View File

@ -1,40 +1,35 @@
local Event = require 'utils.event'
local Global = require 'utils.global'
local Gui = require 'utils.gui'
local Donators = require 'resources.donators'
local UserGroups = require 'features.user_groups'
local Rank = require 'features.rank_system'
local Donator = require 'features.donator'
local PlayerStats = require 'features.player_stats'
local Utils = require 'utils.core'
local Report = require 'features.report'
local Game = require 'utils.game'
local Color = require 'resources.color_presets'
local table = require 'utils.table'
local Color = require 'resources.color_presets'
local poke_messages = require 'resources.poke_messages'
local player_sprites = require 'resources.player_sprites'
local random = math.random
local get_rank_color = Rank.get_rank_color
local get_rank_name = Rank.get_rank_name
local get_player_rank = Rank.get_player_rank
local donator_is_donator = Donator.is_donator
local poke_cooldown_time = 240 -- in ticks.
local sprite_time_step = 54000 -- in ticks
local symbol_asc = ''
local symbol_desc = ''
local focus_color = Color.dark_orange
local rank_colors = {
Color.white, -- Guest
Color.regular, -- Regular
Color.donator, -- Donator
Color.admin -- Admin
}
local donator_color = Color.donator
local rank_column_width = 100
local inv_sprite_time_step = 1 / sprite_time_step
local rank_perk_flag = Donators.donator_perk_flags.rank
local rank_names = {
'Guest',
'Regular',
'Donator',
'Admin'
}
local donator_label_caption = {'', '(', {'ranks.donator_abbreviation'}, ')'}
local player_poke_cooldown = {}
local player_pokes = {}
@ -91,21 +86,6 @@ local function format_distance(tiles)
return math.round(tiles * 0.001, 1) .. ' km'
end
local function get_rank_level(player)
if player.admin then
return 4
end
local name = player.name
if UserGroups.player_has_donator_perk(name, rank_perk_flag) then
return 3
elseif UserGroups.is_regular(name) then
return 2
end
return 1
end
local function do_poke_spam_protection(player)
if player.admin then
return true
@ -222,26 +202,49 @@ local column_builders = {
end
},
[rank_heading_name] = {
create_data = get_rank_level,
create_data = function(player)
local player_name = player.name
return {
rank = get_player_rank(player_name),
is_donator = donator_is_donator(player_name)
}
end,
sort = function(a, b)
return a < b
return a.rank < b.rank
end,
draw_heading = function(parent)
local label = parent.add {type = 'label', name = rank_heading_name, caption = 'Rank'}
local label_style = label.style
apply_heading_style(label_style)
label_style.width = 50
label_style.width = rank_column_width
return label
end,
draw_cell = function(parent, cell_data)
local label = parent.add {type = 'label', name = rank_cell_name, caption = rank_names[cell_data]}
local label_style = label.style
label_style.align = 'center'
label_style.font_color = rank_colors[cell_data]
label_style.width = 50
local is_donator = cell_data.is_donator
local rank = cell_data.rank
if is_donator then
local flow = parent.add {type = 'flow', name = rank_cell_name, direction = 'horizontal'}
local flow_style = flow.style
flow_style.align = 'center'
flow_style.width = rank_column_width
return label
local label_rank = flow.add {type = 'label', caption = get_rank_name(rank)}
label_rank.style.font_color = get_rank_color(rank)
local label_donator = flow.add {type = 'label', caption = donator_label_caption}
label_donator.style.font_color = donator_color
return flow
else
local label = parent.add {type = 'label', name = rank_cell_name, caption = get_rank_name(rank)}
local label_style = label.style
label_style.align = 'center'
label_style.font_color = get_rank_color(rank)
label_style.width = rank_column_width
return label
end
end
},
[distance_heading_name] = {

View File

@ -1,12 +1,13 @@
local Gui = require 'utils.gui'
local Global = require 'utils.global'
local Event = require 'utils.event'
local UserGroups = require 'features.user_groups'
local Rank = require 'features.rank_system'
local Game = require 'utils.game'
local math = require 'utils.math'
local Server = require 'features.server'
local Command = require 'utils.command'
local Color = require 'resources.color_presets'
local Ranks = require 'resources.ranks'
local insert = table.insert
@ -222,7 +223,7 @@ local function redraw_poll_viewer_content(data)
local question_flow = poll_viewer_content.add {type = 'table', column_count = 2}
if player.admin or UserGroups.is_regular(player.name) then
if Rank.equal_or_greater_than(player.name, Ranks.regular) then
local edit_button =
question_flow.add {
type = 'sprite-button',
@ -372,7 +373,7 @@ local function draw_main_frame(left, player)
local right_flow = bottom_flow.add {type = 'flow'}
right_flow.style.align = 'right'
if player.admin or UserGroups.is_regular(player.name) then
if Rank.equal_or_greater_than(player.name, Ranks.regular) then
local create_poll_button =
right_flow.add {type = 'button', name = create_poll_button_name, caption = 'Create Poll'}
apply_button_style(create_poll_button)
@ -1326,7 +1327,7 @@ Command.add(
'poll',
{
arguments = {'poll'},
regular_only = true,
required_rank = Ranks.regular,
allowed_by_server = true,
custom_help_text = '<{question = "question", answers = {"answer 1", "answer 2"}, duration = 300}> - Creates a new poll (Regulars only).',
log_command = true,

View File

@ -2,6 +2,7 @@ local Gui = require 'utils.gui'
local Utils = require 'utils.core'
local Game = require 'utils.game'
local Command = require 'utils.command'
local Ranks = require 'resources.ranks'
local close_name = Gui.uid_name()
@ -144,7 +145,7 @@ Command.add(
{
description = 'Shows a popup to all connected players',
arguments = {'message'},
admin_only = true,
required_rank = Ranks.admin,
capture_excess_arguments = true,
allowed_by_server = true
},
@ -156,7 +157,7 @@ Command.add(
{
description = 'Shows an update popup to all connected players',
arguments = {'version'},
admin_only = true,
required_rank = Ranks.admin,
capture_excess_arguments = true,
allowed_by_server = true
},
@ -168,7 +169,7 @@ Command.add(
{
description = 'Shows a popup to the player.',
arguments = {'player', 'message'},
admin_only = true,
required_rank = Ranks.admin,
capture_excess_arguments = true,
allowed_by_server = true
},

View File

@ -1,13 +1,13 @@
local Event = require 'utils.event'
local Gui = require 'utils.gui'
local Global = require 'utils.global'
local UserGroups = require 'features.user_groups'
local Rank = require 'features.rank_system'
local Game = require 'utils.game'
local Command = require 'utils.command'
local deafult_verb = 'expanded'
local Ranks = require 'resources.ranks'
local tag_groups = require 'resources.tag_groups'
local default_verb = 'expanded'
local player_tags = {}
local no_notify_players = {}
@ -21,7 +21,9 @@ Global.register(
)
local function notify_players(message)
for _, p in ipairs(game.connected_players) do
local players = game.connected_players
for i=1, #players do
local p = players[i]
if p.valid and not no_notify_players[p.index] then
p.print(message)
end
@ -69,7 +71,7 @@ local function change_player_tag(player, tag_name, silent)
player.tag = tag
local verb = tag_data.verb or deafult_verb
local verb = tag_data.verb or default_verb
if not silent then
notify_players(tag .. ' squad has `' .. verb .. '` with ' .. player.name)
@ -147,7 +149,7 @@ local function draw_main_frame_content(parent)
local row = grid.add {type = 'table', column_count = 4}
row.style.horizontal_spacing = 0
if player.admin or UserGroups.is_regular(player.name) then
if Rank.equal_or_greater_than(player.name, Ranks.regular) then
local edit_button =
row.add {
type = 'sprite-button',
@ -238,7 +240,7 @@ local function draw_main_frame(player)
right_flow.add {type = 'button', name = clear_button_name, caption = 'Clear Tag'}
if player.admin or UserGroups.is_regular(player.name) then
if Rank.equal_or_greater_than(player.name, Ranks.regular) then
right_flow.add {type = 'button', name = create_tag_button_name, caption = 'Create Tag'}
end
end
@ -589,7 +591,7 @@ Gui.on_click(
local verb = data.verb.text
if verb == '' then
verb = deafult_verb
verb = default_verb
end
Gui.remove_data_recursively(frame)
@ -688,7 +690,7 @@ Command.add(
{
description = "Sets a player's tag",
arguments = {'player', 'tag'},
admin_only = true,
required_rank = Ranks.admin,
capture_excess_arguments = true,
allowed_by_server = true
},

View File

@ -1,12 +1,13 @@
local Event = require 'utils.event'
local Gui = require 'utils.gui'
local Global = require 'utils.global'
local UserGroups = require 'features.user_groups'
local Rank = require 'features.rank_system'
local Utils = require 'utils.core'
local Game = require 'utils.game'
local Color = require 'resources.color_presets'
local math = require 'utils.math'
local Command = require 'utils.command'
local Color = require 'resources.color_presets'
local Ranks = require 'resources.ranks'
local normal_color = Color.white
local focus_color = Color.dark_orange
@ -311,7 +312,7 @@ local function redraw_tasks(data, enabled)
end
local function draw_main_frame(left, player)
local enabled = player.admin or UserGroups.is_regular(player.name)
local enabled = Rank.equal_or_greater_than(player.name, Ranks.regular)
local data = {}
@ -537,7 +538,7 @@ local function create_new_tasks(task_name, player)
local frame_data = Gui.get_data(frame)
frame_data.tasks_updated_label.caption = update_message
local enabled = p.admin or UserGroups.is_regular(p.name)
local enabled = Rank.equal_or_greater_than(p.name, Ranks.regular)
redraw_tasks(frame_data, enabled)
elseif notify then
draw_main_frame(left, p)
@ -605,7 +606,7 @@ local function player_joined(event)
label.caption = last_edit_message
label.tooltip = last_edit_message
local enabled = player.admin or UserGroups.is_regular(player.name)
local enabled = Rank.equal_or_greater_than(player.name, Ranks.regular)
redraw_tasks(data, enabled)
end
@ -821,7 +822,7 @@ Gui.on_click(
local frame = left[main_frame_name]
if frame and frame.valid then
local data = Gui.get_data(frame)
local enabled = p.admin or UserGroups.is_regular(p.name)
local enabled = Rank.equal_or_greater_than(p.name, Ranks.regular)
redraw_tasks(data, enabled)
elseif notify then
draw_main_frame(left, p)
@ -876,7 +877,7 @@ local function do_direction(event, sign)
local frame = p.gui.left[main_frame_name]
if frame and frame.valid then
local data = Gui.get_data(frame)
local enabled = p.admin or UserGroups.is_regular(p.name)
local enabled = Rank.equal_or_greater_than(p.name, Ranks.regular)
redraw_tasks(data, enabled)
end
end
@ -1056,7 +1057,7 @@ Gui.on_click(
local main_frame_data = Gui.get_data(main_frame)
main_frame_data.tasks_updated_label.caption = update_message
local enabled = p.admin or UserGroups.is_regular(p.name)
local enabled = Rank.equal_or_greater_than(p.name, Ranks.regular)
redraw_tasks(main_frame_data, enabled)
elseif notify then
draw_main_frame(left, p)
@ -1076,7 +1077,7 @@ Command.add(
{
description = 'Creates a new task.',
arguments = {'task'},
regular_only = true,
required_rank = Ranks.regular,
allowed_by_server = true,
log_command = true,
capture_excess_arguments = true,

View File

@ -4,6 +4,8 @@ local Command = require 'utils.command'
local Task = require 'utils.task'
local Token = require 'utils.token'
local Retailer = require 'features.retailer'
local Ranks = require 'resources.ranks'
local round = math.round
local insert = table.insert
local remove = table.remove
@ -118,7 +120,7 @@ end)
Command.add('lazy-bastard-bootstrap', {
description = 'Puts down the minimum requirements to get started',
admin_only = true,
required_rank = Ranks.admin,
}, function(_, player)
local surface = player.surface
local force = player.force

View File

@ -6,6 +6,7 @@ local PlayerStats = require 'features.player_stats'
local Game = require 'utils.game'
local Command = require 'utils.command'
local Retailer = require 'features.retailer'
local Ranks = require 'resources.ranks'
local market_items = require 'resources.market_items'
local fish_market_bonus_message = require 'resources.fish_messages'
@ -244,7 +245,7 @@ Command.add(
'market',
{
description = 'Places a market near you.',
admin_only = true,
required_rank = Ranks.admin,
},
spawn_market
)

View File

@ -1,14 +1,15 @@
local Event = require 'utils.event'
local UserGroups = require 'features.user_groups'
local Rank = require 'features.rank_system'
local Utils = require 'utils.core'
local Game = require 'utils.game'
local Server = require 'features.server'
local Ranks = require 'resources.ranks'
local format = string.format
local match = string.match
local function allowed_to_nuke(player)
return player.admin or UserGroups.is_regular(player.name) or ((player.online_time / 216000) > global.config.nuke_control.nuke_min_time_hours)
return Rank.equal_or_greater_than(player.name, Ranks.auto_trusted)
end
local function ammo_changed(event)

View File

@ -1,4 +1,6 @@
local Command = require 'utils.command'
local Ranks = require 'resources.ranks'
local format = string.format
local Performance = {}
@ -37,7 +39,7 @@ Command.add(
{
description = 'Sets the performance scale between 0.05 and 1. Will alter the game speed, manual mining speed, manual crafting speed and character running speed per force.',
arguments = {'scale'},
admin_only = true,
required_rank = Ranks.admin,
allowed_by_server = true
},
function(arguments, player)

View File

@ -4,6 +4,7 @@ local Command = require 'utils.command'
local Server = require 'features.server'
local Token = require 'utils.token'
local Utils = require 'utils.core'
local Ranks = require 'resources.ranks'
local serialize = serpent.line
@ -44,25 +45,23 @@ Command.add(
{
description = 'Set will save your current color for future maps. Reset will erase your saved color. Random will give you a random color.',
arguments = {'set-reset-random'},
admin_only = false,
regular_only = true,
allowed_by_server = false,
allowed_by_player = true
required_rank = Ranks.regular
},
function(args, player)
local player_name = player.name
if args['set-reset-random'] == 'set' then
local arg = args['set-reset-random']
if arg == 'set' then
local data = {
color = player.color,
chat_color = player.chat_color,
chat_color = player.chat_color
}
Server.set_data('colors', player_name, data)
player.print('Your color has been saved. Any time you join a redmew server your color will automatically be set.')
Utils.print_except(player_name .. ' has saved their color server-side for future maps. You can do the same! Check out /help redmew-color', player)
elseif args['set-reset-random'] == 'reset' then
elseif arg == 'reset' then
Server.set_data('colors', player_name, nil)
player.print('Your saved color (if you had one) has been removed.')
elseif args['set-reset-random'] == 'random' then
elseif arg == 'random' then
local color_data = Public.set_random_color(player)
player.print('Your color has been changed to: ' .. serialize(color_data))
else

View File

@ -3,21 +3,23 @@ local Game = require 'utils.game'
local Event = require 'utils.event'
local Global = require 'utils.global'
local Info = require 'features.gui.info'
local UserGroups = require 'features.user_groups'
local get_random_weighted = table.get_random_weighted
local memory = {
forces_initialized = {
player = false, -- default force for everyone
player = false -- default force for everyone
}
}
Global.register({
memory = memory,
}, function (tbl)
memory = tbl.memory
end)
Global.register(
{
memory = memory
},
function(tbl)
memory = tbl.memory
end
)
local function player_created(event)
local config = global.config.player_create
@ -56,19 +58,14 @@ local function player_created(event)
end
end
if not game.is_multiplayer() and not _DEBUG then
if _DEBUG and game.is_multiplayer() then
game.print('THIS MULTIPLAYER MAP IS IN DEBUG!!!')
elseif _DEBUG then
game.print("DON'T LAUNCH THIS MAP! DEBUG MODE IS ENABLED!!!")
elseif not _DEBUG and not game.is_multiplayer() then
player.print('To change your name in single-player, open chat and type the following /c game.player.name = "your_name"')
end
if _DEBUG and player.admin then
UserGroups.add_regular(player.name)
if game.is_multiplayer() then
game.print("THIS MULTIPLAYER MAP IS IN DEBUG!!!")
else
game.print("DON'T LAUNCH THIS MAP! DEBUG MODE IS ENABLED!!!")
end
end
if _CHEATS then
player.cheat_mode = true
local cheats = config.cheats

383
features/rank_system.lua Normal file
View File

@ -0,0 +1,383 @@
--[[
This rank system is meant to allow for easy addition or removal of ranks from the heirarchy.
Ranks can be freely modified in resources.ranks as all the relation of the ranks to one another is all that matters.
While all the public functions want rank as a number, modules should use references to resources.ranks and not have actual numbers.
To dissuade the use of numeric ranks, there is explicitly no get_rank function.
Ex: right way: Rank.equal(player_name, Rank.regular) wrong way: Rank.equal(player_name, 2)
]]
-- Dependencies
local Event = require 'utils.event'
local Game = require 'utils.game'
local Global = require 'utils.global'
local table = require 'utils.table'
local Token = require 'utils.token'
local math = require 'utils.math'
local Server = require 'features.server'
local Ranks = require 'resources.ranks'
local Colors = require 'resources.color_presets'
local Config = global.config.rank_system
-- Localized functions
local clamp = math.clamp
local clear_table = table.clear_table
-- Constants
local ranking_data_set = 'rankings'
local nth_tick = 54001 -- nearest prime to 15 minutes in ticks
local rank_name_lookup = {}
local sorted_ranks = {}
local rank_to_index = {}
for k, v in pairs(Ranks) do
rank_name_lookup[v] = {'ranks.' .. k}
end
for k, v in pairs(Ranks) do
sorted_ranks[#sorted_ranks + 1] = v
end
table.sort(sorted_ranks)
for k, v in pairs(sorted_ranks) do
rank_to_index[v] = k
end
-- Local vars
local Public = {}
-- Global register vars
local player_ranks = {}
local guests = {}
Global.register(
{
player_ranks = player_ranks,
guests = guests
},
function(tbl)
player_ranks = tbl.player_ranks
guests = tbl.guests
end
)
-- Local functions
--- Changes a rank by a fixed number
-- @param current_rank <number>
-- @param change <number> the number by which to increase or decrease a rank
local function change_rank_by_number(current_rank, change)
local index = rank_to_index[current_rank]
local new_index = clamp(index + change, 1, #sorted_ranks)
return sorted_ranks[new_index]
end
--- Check each online player and if their playtime is above the required cutoff, promote them to auto-trusted.
-- Only applies to players at the guest rank or higher
local function check_promote_to_auto_trusted()
local auto_trusted = Ranks.auto_trusted
local guest = Ranks.guest
local time_for_trust = Config.time_for_trust
local equal_or_greater_than = Public.equal_or_greater_than
local equal = Public.equal
local set_data = Server.set_data
for index, p in pairs(guests) do
if not p or not p.valid then
guests[index] = nil
return
end
local p_name = p.name
if equal_or_greater_than(p_name, auto_trusted) then
guests[index] = nil
elseif (p.online_time > time_for_trust) and equal(p_name, guest) then
player_ranks[p_name] = auto_trusted
set_data(ranking_data_set, p_name, auto_trusted)
guests[index] = nil
elseif not p.connected then
guests[index] = nil
end
end
end
--- On callback, overwrites player rank entries with data entries.
local sync_ranks_callback =
Token.register(
function(data)
if not data or not data.entries then
return
end
clear_table(player_ranks)
for k, v in pairs(data.entries) do
player_ranks[k] = v
end
end
)
local function on_player_joined(event)
local index = event.player_index
local player = Game.get_player_by_index(index)
if not player then
return
end
local player_name = player.name
if Public.equal(player_name, Ranks.guest) then
guests[index] = player
end
--- Fix for legacy name storage
local lowerCaseName = player_name:lower()
if player_name ~= lowerCaseName and player_ranks[lowerCaseName] then
local player_rank = player_ranks[lowerCaseName]
player_ranks[lowerCaseName] = nil
player_ranks[player_name] = player_rank
Server.set_data(ranking_data_set, lowerCaseName, nil)
Server.set_data(ranking_data_set, player_name, player_rank)
end
end
-- Exposed functions
--- Gets a player's rank. In cases of comparison, the appropriate functions should be used.
-- This function is exposed for the purpose of returning a numerical value for players for the
-- purposes of sorting.
-- Is the only place player.admin should be checked.
function Public.get_player_rank(player_name)
local player = game.players[player_name]
if player and player.valid and player.admin then
return Ranks.admin
elseif Config.everyone_is_regular then
return Ranks.regular
end
return player_ranks[player_name] or Ranks.guest
end
local get_player_rank = Public.get_player_rank
--- Returns the table of players in the ranking system
-- @return <table>
function Public.get_player_table()
return player_ranks
end
--- Returns the player's rank as a name.
-- @param player_name <string>
-- @return <string>
function Public.get_player_rank_name(player_name)
return rank_name_lookup[get_player_rank(player_name)]
end
local get_player_rank_name = Public.get_player_rank_name
--- Returns the player's rank as a name.
-- @param player_name <string>
-- @return <table>
function Public.get_player_rank_color(player_name)
local rank_name = get_player_rank_name(player_name)
return Colors[rank_name]
end
--- Returns the rank's name.
-- @param rank <number>
-- @return <string>
function Public.get_rank_name(rank)
return rank_name_lookup[rank]
end
local get_rank_name = Public.get_rank_name
--- Returns the rank's color.
-- @param rank <table>
function Public.get_rank_color(rank)
return Colors[rank]
end
--- Evaluates if a player's rank is equal to the rank provided
-- @param player_name <string>
-- @param rank <number>
-- @return <boolean>
function Public.equal(player_name, rank)
local p_rank = get_player_rank(player_name)
return p_rank == rank
end
--- Evaluates if a player's rank is not equal to the rank provided
-- @param player_name <string>
-- @param rank <number>
-- @return <boolean>
function Public.not_equal(player_name, rank)
local p_rank = get_player_rank(player_name)
return p_rank ~= rank
end
--- Evaluates if a player's rank is greater than the rank provided
-- @param player_name <string>
-- @param rank <number>
-- @return <boolean>
function Public.greater_than(player_name, rank)
local p_rank = get_player_rank(player_name)
return p_rank > rank
end
--- Evaluates if a player's rank is less than the rank provided
-- @param player_name <string>
-- @param rank <number>
-- @return <boolean>
function Public.less_than(player_name, rank)
local p_rank = get_player_rank(player_name)
return p_rank < rank
end
--- Evaluates if a player's rank is equal to or greater than the rank provided
-- @param player_name <string>
-- @param rank <number>
-- @return <boolean>
function Public.equal_or_greater_than(player_name, rank)
local p_rank = get_player_rank(player_name)
return p_rank >= rank
end
--- Evaluates if a player's rank is equal to or less than the rank provided
-- @param player_name <string>
-- @param rank <number>
-- @return <boolean>
function Public.equal_or_less_than(player_name, rank)
local p_rank = get_player_rank(player_name)
return p_rank <= rank
end
--- Take a player and attempts to increase their rank by 1
-- @param player_name <string>
-- @return <string|nil> new rank name or nil if already at highest rank
function Public.increase_player_rank(player_name)
local current_rank = (get_player_rank(player_name))
local new_rank = change_rank_by_number(current_rank, 1)
if current_rank == new_rank then
return nil
end
local new_rank_name = rank_name_lookup[new_rank]
if new_rank_name then
player_ranks[player_name] = new_rank
return new_rank_name
else
return nil
end
end
--- Take a player and attempts to increase their rank to the rank provided
-- Fails if player is already higher rank
-- @param player_name <string>
-- @param rank <number>
-- @return <boolean> <string> success/failure, and string name of the player's rank
function Public.increase_player_rank_to(player_name, rank)
if Public.less_than(player_name, rank) then
Public.set_player_rank(player_name, rank)
return true, get_rank_name(rank)
else
return false, get_player_rank_name(player_name)
end
end
--- Take a player and attempts to decrease their rank by 1
-- @param player_name <string>
-- @return <string|nil> new rank name or nil if already at lowest rank
function Public.decrease_player_rank(player_name)
local current_rank = (get_player_rank(player_name))
local new_rank = change_rank_by_number(current_rank, -1)
if current_rank == new_rank then
return nil
end
local new_rank_name = rank_name_lookup[new_rank]
if new_rank_name then
player_ranks[player_name] = new_rank
return new_rank_name
else
return nil
end
end
--- Take a player and attempts to decrease their rank to the rank provided
-- Fails if player is already lower rank
-- @param player_name <string>
-- @param rank <number>
-- @return <boolean> <string> success/failure, and string name of the player's rank
function Public.decrease_player_rank_to(player_name, rank)
if Public.greater_than(player_name, rank) then
Public.set_player_rank(player_name, rank)
return true, get_rank_name(rank)
else
return false, get_player_rank_name(player_name)
end
end
--- Sets a player's rank
-- @param player_name <string>
-- @param rank <number>
-- @return <boolean> success/failure
function Public.set_player_rank(player_name, rank)
if Public.equal(player_name, rank) then
return false
elseif rank == Ranks.guest then
player_ranks[player_name] = nil
Server.set_data(ranking_data_set, player_name, nil)
-- If we're dropping someone back down the guest, put them on the guests list
local player = game.players[player_name]
if player and player.valid then
guests[player.index] = player
end
return true
else
player_ranks[player_name] = rank
Server.set_data(ranking_data_set, player_name, rank)
return true
end
end
--- Resets a player's rank to guest (or higher if a user meets the criteria for automatic rank)
-- @param player_name <string>
-- @return <boolean> <string> boolean for success/failure, string as name of rank
function Public.reset_player_rank(player_name)
local guest_rank = Ranks.guest
local auto_trusted = Ranks.auto_trusted
if Public.equal(player_name, guest_rank) then
return false, get_rank_name(guest_rank)
else
local player = game.players[player_name]
local rank
if player and player.valid and (player.online_time > Config.time_for_trust) then
rank = auto_trusted
Public.set_player_rank(player_name, rank)
else
rank = guest_rank
Public.set_player_rank(player_name, rank)
end
return true, get_rank_name(rank)
end
end
function Public.sync_ranks()
Server.try_get_all_data(ranking_data_set, sync_ranks_callback)
end
-- Events
Event.add(defines.events.on_player_joined_game, on_player_joined)
Event.add(Server.events.on_server_started, Public.sync_ranks)
Event.on_nth_tick(nth_tick, check_promote_to_auto_trusted)
Server.on_data_set_changed(
ranking_data_set,
function(data)
player_ranks[data.key] = data.value
end
)
return Public

View File

@ -8,6 +8,8 @@ local Game = require 'utils.game'
local Command = require 'utils.command'
local Global = require 'utils.global'
local Ranks = require 'resources.ranks'
local primitives = {reactors_enabled = {global.config.reactor_meltdown.on_by_default}}
local wastelands = {}
local reactors = {}
@ -182,7 +184,7 @@ Command.add(
'meltdown',
{
description = 'Toggles if reactors blow up',
admin_only = true,
required_rank = Ranks.admin,
allowed_by_server = true,
log_command = true
},

View File

@ -1,13 +1,14 @@
local Game = require 'utils.game'
local Timestamp = require 'utils.timestamp'
local Command = require 'utils.command'
local Settings = require 'utils.redmew_settings'
local Utils = require 'utils.core'
local Report = require 'features.report'
local Server = require 'features.server'
local UserGroups = require 'features.user_groups'
local Walkabout = require 'features.walkabout'
local PlayerStats = require 'features.player_stats'
local Settings = require 'utils.redmew_settings'
local Rank = require 'features.rank_system'
local Donator = require 'features.donator'
local format = string.format
local ceil = math.ceil
@ -238,24 +239,25 @@ local function print_player_info(args, player)
local index = target.index
local info_t = {
'Name: ' .. name,
target.connected and 'Online: yes' or 'Online: no',
'Index: ' .. target.index,
'Rank: ' .. UserGroups.get_rank(target),
UserGroups.is_donator(target.name) and 'Donator: yes' or 'Donator: no',
'Time played: ' .. Utils.format_time(target.online_time),
'AFK time: ' .. Utils.format_time(target.afk_time or 0),
'Force: ' .. target.force.name,
'Surface: ' .. target.surface.name,
'Tag: ' .. target.tag,
'Distance walked: ' .. PlayerStats.get_walk_distance(index),
'Coin earned: ' .. PlayerStats.get_coin_earned(index),
'Coin spent: ' .. PlayerStats.get_coin_spent(index),
'Deaths: ' .. PlayerStats.get_death_count(index),
'Crafted items: ' .. PlayerStats.get_crafted_item(index),
'Chat messages: ' .. PlayerStats.get_console_chat(index)
'redmew_commands.whois_formatter',
{'format.1_colon_2', 'Name', name},
{'format.single_item', target.connected and 'Online: yes' or 'Online: no'},
{'format.1_colon_2', 'Index', target.index},
{'format.1_colon_2', 'Rank', Rank.get_player_rank_name(name)},
{'format.single_item', Donator.is_donator(target.name) and 'Donator: yes' or 'Donator: no'},
{'format.1_colon_2', 'Time played', Utils.format_time(target.online_time)},
{'format.1_colon_2', 'AFK time', Utils.format_time(target.afk_time or 0)},
{'format.1_colon_2', 'Force', target.force.name},
{'format.1_colon_2', 'Surface', target.surface.name},
{'format.1_colon_2', 'Tag', target.tag},
{'format.1_colon_2', 'Distance walked', PlayerStats.get_walk_distance(index)},
{'format.1_colon_2', 'Coin earned', PlayerStats.get_coin_earned(index)},
{'format.1_colon_2', 'Coin spent', PlayerStats.get_coin_spent(index)},
{'format.1_colon_2', 'Deaths', PlayerStats.get_death_count(index)},
{'format.1_colon_2', 'Crafted items', PlayerStats.get_crafted_item(index)},
{'format.1_colon_2', 'Chat messages', PlayerStats.get_console_chat(index)},
}
Game.player_print(concat(info_t, '\n- '))
Game.player_print(info_t)
if (not player or player.admin) and args.inventory then
local m_inventory = target.get_inventory(defines.inventory.player_main)
@ -377,15 +379,7 @@ Command.add(
Report.report_command
)
Command.add(
'regulars',
{
description = 'Prints a list of game regulars.',
allowed_by_server = true
},
UserGroups.print_regulars
)
-- No man's land / free for all
Command.add('redmew-setting-set', {
description = 'Set a setting for yourself',
arguments = {'setting_name', 'new_value'},

View File

@ -11,6 +11,7 @@ local Event = require 'utils.event'
local Utils = require 'utils.core'
local Global = require 'utils.global'
local table = require 'utils.table'
local Rank = require 'features.rank_system'
local config = global.config.redmew_qol
@ -75,7 +76,7 @@ local function pick_name()
return
end
local regulars = global.regulars
local regulars = Rank.get_player_table()
local reg
if table.size(regulars) == 0 then
reg = nil

View File

@ -518,7 +518,7 @@ local function set_scenario_version()
-- A 1 hour buffer is in place to account for potential playtime pre-upload.
if game.tick < 216000 and not global.redmew_version then
local time_string = Timestamp.to_string(Public.get_current_time())
global.redmew_version = 'Time of map launch: ' .. time_string
global.redmew_version = string.format('Time of map launch: %s UTC', time_string)
end
end

View File

@ -2,9 +2,11 @@ local Poll = {send_poll_result_to_discord = function () end}
if global.config.poll.enabled then
Poll = require 'features.gui.poll'
end
local UserGroups = require 'features.user_groups'
local Rank = require 'features.rank_system'
local Token = require 'utils.token'
local Server = require 'features.server'
local Donator = require 'features.donator'
--- This module is for the web server to call functions and raise events.
-- Not intended to be called by scripts.
@ -13,8 +15,8 @@ ServerCommands = {}
ServerCommands.get_poll_result = Poll.send_poll_result_to_discord
ServerCommands.regular_sync = UserGroups.sync_regulars
ServerCommands.donator_sync = UserGroups.sync_donators
ServerCommands.regular_sync = Rank.sync_ranks
ServerCommands.donator_sync = Donator.sync_donators
function ServerCommands.raise_callback(func_token, data)
local func = Token.get(func_token)

View File

@ -1,10 +1,10 @@
local Event = require 'utils.event'
local market_items = require 'resources.market_items'
local Global = require 'utils.global'
local Donators = require 'resources.donators'
local UserGroups = require 'features.user_groups'
local DonatorPerks = require 'resources.donator_perks'
local Donator = require 'features.donator'
local Game = require 'utils.game'
local train_perk_flag = Donators.donator_perk_flags.train
local train_perk_flag = DonatorPerks.train
local saviour_token_name = 'small-plane' -- item name for what saves players
local saviour_timeout = 180 -- number of ticks players are train immune after getting hit (roughly)
@ -66,7 +66,7 @@ local function on_pre_death(event)
local player_name = player.name
if UserGroups.player_has_donator_perk(player_name, train_perk_flag) then
if Donator.player_has_donator_perk(player_name, train_perk_flag) then
saved_players[player_index] = game_tick
save_player(player)

View File

@ -1,163 +0,0 @@
local Event = require 'utils.event'
local Utils = require 'utils.core'
local Server = require 'features.server'
local Donators = require 'resources.donators'
local Game = require 'utils.game'
local Token = require 'utils.token'
global.regulars = {}
global.donators = Donators.donators
local Module = {}
Module.is_regular =
function(player_name)
return Utils.cast_bool(global.regulars[player_name])
end
Module.add_regular = function(player_name)
local actor = Utils.get_actor()
if (Module.is_regular(player_name)) then
Game.player_print(player_name .. ' is already a regular.')
else
global.regulars[player_name] = true
Server.set_data('regulars', player_name, true)
game.print(actor .. ' promoted ' .. player_name .. ' to regular.')
end
end
Module.remove_regular = function(player_name)
local actor = Utils.get_actor()
if (Module.is_regular(player_name)) then
global.regulars[player_name] = nil
Server.set_data('regulars', player_name, nil)
game.print(player_name .. ' was demoted from regular by ' .. actor .. '.')
else
Game.player_print(player_name .. ' is not a regular.')
end
end
local sync_regulars_callback =
Token.register(
function(data)
global.regulars = data.entries or {}
end
)
function Module.sync_regulars()
Server.try_get_all_data('regulars', sync_regulars_callback)
end
Module.print_regulars = function()
local result = {}
for k, _ in pairs(global.regulars) do
table.insert(result, k)
end
result = table.concat(result, ', ')
Game.player_print(result)
end
function Module.get_rank(player)
if player.admin then
return 'Admin'
elseif Module.is_regular(player.name) then
return 'Regular'
else
return 'Guest'
end
end
function Module.is_donator(player_name)
return global.donators[player_name] ~= nil
end
function Module.player_has_donator_perk(player_name, perk_flag)
local d = global.donators[player_name]
if not d then
return false
end
local flags = d.perk_flags
if not flags then
return false
end
return bit32.band(flags, perk_flag) == perk_flag
end
function Module.get_donator_welcome_message(player_name)
local d = global.donators[player_name]
if not d then
return nil
end
return d.welcome_messages
end
function Module.set_donator(player_name, data)
global.donators[player_name] = data
Server.set_data('donators', player_name, data)
end
local sync_donators_callback =
Token.register(
function(data)
global.donators = data.entries or {}
end
)
function Module.sync_donators()
Server.try_get_all_data('donators', sync_donators_callback)
end
function Module.print_donators()
local result = {}
for k, _ in pairs(global.donators) do
table.insert(result, k)
end
result = table.concat(result, ', ')
Game.player_print(result)
end
Event.add(
defines.events.on_player_joined_game,
function(event)
local correctCaseName = Game.get_player_by_index(event.player_index).name
local lowerCaseName = correctCaseName:lower()
if correctCaseName ~= lowerCaseName and global.regulars[lowerCaseName] then
global.regulars[lowerCaseName] = nil
global.regulars[correctCaseName] = true
Server.set_data('regulars', lowerCaseName, nil)
Server.set_data('regulars', correctCaseName, true)
end
end
)
Event.add(
Server.events.on_server_started,
function()
Module.sync_regulars()
Module.sync_donators()
end
)
Server.on_data_set_changed(
'regulars',
function(data)
global.regulars[data.key] = data.value
end
)
Server.on_data_set_changed(
'donators',
function(data)
global.donators[data.key] = data.value
end
)
return Module

View File

@ -4,6 +4,7 @@ local Event = require 'utils.event'
local Token = require 'utils.token'
local Command = require 'utils.command'
local Global = require 'utils.global'
local Ranks = require 'resources.ranks'
local Public = {}
local return_player
@ -176,7 +177,7 @@ Command.add(
description = 'Send someone on a walk. Duration is in seconds.',
arguments = {'player', 'duration'},
default_values = {duration = 60},
admin_only = true,
required_rank = Ranks.admin,
allowed_by_server = true
},
walkabout

22
locale/en/redmew.cfg Normal file
View File

@ -0,0 +1,22 @@
[ranks]
probation=Probation
guest=Guest
auto_trusted=Auto Trusted
regular=Regular
admin=Admin
donator=Donator
donator_abbreviation=D
[format]
1_colon_2=__1__: __2__
single_item=__1__
[admin_commands]
regular_add_success=__1__ promoted __2__ to __3__.
regular_add_fail=__1__ is already rank __2__.
regular_add_fail_probation=Cannot promote someone on probation to regular. You must remove them from probation and then promote them.
regular_remove_success=__1__ demoted __2__ to __3__.
regular_remove_fail=__1__ is rank __2__ their regular status cannot be removed.
[redmew_commands]
whois_formatter=__1__\n__2__\n__3__\n__4__\n__5__\n__6__\n__7__\n__8__\n__9__\n__10__\n__11__\n__12__\n__13__\n__14__\n__15__\n__16__\n

View File

@ -1,4 +1,5 @@
local Command = require 'utils.command'
local Ranks = require 'resources.ranks'
local insert = table.insert
@ -210,7 +211,7 @@ Command.add(
{
arguments = {'size'},
default_values = {size = 6},
admin_only = true
required_rank = Ranks.admin
},
extract1
)
@ -219,7 +220,7 @@ Command.add(
{
arguments = {'size'},
default_values = {size = 6},
admin_only = true
required_rank = Ranks.admin
},
extract4
)

View File

@ -4,6 +4,7 @@ local Token = require 'utils.token'
local Server = require 'features.server'
local Popup = require 'features.gui.popup'
local Global = require 'utils.global'
local Ranks = require 'resources.ranks'
local server_player = {name = '<server>', print = print}
@ -51,7 +52,7 @@ Command.add(
description = 'Restarts the crashsite scenario.',
arguments = {'scenario_name'},
default_values = {scenario_name = 'crashsite'},
admin_only = true,
required_rank = Ranks.admin,
allowed_by_server = true
},
function(args, player)
@ -83,7 +84,7 @@ Command.add(
'abort',
{
description = 'Aborts the restart of the crashsite scenario.',
admin_only = true,
required_rank = Ranks.admin,
allowed_by_server = true
},
function(_, player)

View File

@ -5,6 +5,7 @@ local Event = require 'utils.event'
local Task = require 'utils.task'
local Retailer = require 'features.retailer'
local PlayerStats = require 'features.player_stats'
local Donator = require 'features.donator'
local RS = require 'map_gen.shared.redmew_surface'
local Server = require 'features.server'
local CrashSiteToast = require 'map_gen.maps.crash_site.crash_site_toast'
@ -1043,7 +1044,7 @@ local function do_capture_outpost(outpost_data)
return
end
local donators = global.donators
local donators = Donator.get_donators_table()
if next(donators) then
local donator = table.get_random_dictionary_entry(donators, true)
if donator then

View File

@ -10,6 +10,8 @@ local Template = require 'map_gen.maps.diggy.template'
local ScoreTable = require 'map_gen.maps.diggy.score_table'
local Command = require 'utils.command'
local CreateParticles = require 'features.create_particles'
local Ranks = require 'resources.ranks'
local random = math.random
local tonumber = tonumber
local pairs = pairs
@ -128,7 +130,7 @@ Command.add('diggy-clear-void', {
description = 'Clears the void in a given area but still triggers all events Diggy would when clearing void.',
arguments = {'left_top_x', 'left_top_y', 'width', 'height', 'surface_index'},
debug_only = true,
admin_only = true,
required_rank = Ranks.admin,
}, function(arguments)
local left_top_x = tonumber(arguments.left_top_x)
local left_top_y = tonumber(arguments.left_top_y)

View File

@ -4,9 +4,10 @@ local Event = require 'utils.event'
local naming_words = require 'resources.naming_words'
local Utils = require 'utils.core'
local Global = require 'utils.global'
local UserGroups = require 'features.user_groups'
local Rank = require 'features.rank_system'
local ScenarioInfo = require 'features.gui.info'
local Command = require 'utils.command'
local Ranks = require 'resources.ranks'
local format = string.format
local random = math.random
@ -36,7 +37,7 @@ Global.register(
--- Takes a player's real name, current silly name, and old silly name and adjusts
-- the silly_regulars table accordingly
local function check_regular(real_name, silly_name, old_silly_name)
if UserGroups.is_regular(real_name) then
if Rank.equal(real_name, Ranks.regular) then
global.silly_regulars[silly_name] = true
if old_silly_name then
global.silly_regulars[old_silly_name] = nil
@ -227,7 +228,7 @@ Command.add(
{
description = 'Gets the index of a player',
arguments = {'player'},
admin_only = true,
required_rank = Ranks.admin,
allowed_by_server = true
},
get_player_id

View File

@ -144,7 +144,16 @@ return {
gainsboro = {r = 220, g = 220, b = 220},
white_smoke = {r = 245, g = 245, b = 245},
white = {r = 255, g = 255, b = 255},
jailed = {r = 255, g = 255, b = 255},
probation = {r = 255, g = 255, b = 255},
guest = {r = 255, g = 255, b = 255},
auto_trusted = {r = 192, g = 192, b = 192},
regular = {r = 0.155, g = 0.540, b = 0.898},
admin = {r = 0.093, g = 0.768, b = 0.172},
donator = {r = 172.6, g = 70.2, b = 215.8},
admin = {r = 0.093, g = 0.768, b = 0.172}
[-10] = {r = 255, g = 255, b = 255},
[0] = {r = 255, g = 255, b = 255},
[10] = {r = 192, g = 192, b = 192},
[20] = {r = 0.155, g = 0.540, b = 0.898},
[30] = {r = 0.093, g = 0.768, b = 0.172}
}

View File

@ -0,0 +1,4 @@
return {
rank = 0x1,
train = 0x2
}

View File

@ -1,10 +0,0 @@
local Module = {}
Module.donator_perk_flags = {
rank = 0x1,
train = 0x2
}
Module.donators = {}
return Module

8
resources/ranks.lua Normal file
View File

@ -0,0 +1,8 @@
-- When adding/removing/changing ranks, rank_system has a migrate_data() function you can use to adjust the existing data.
return {
probation = -10,
guest = 0,
auto_trusted = 10,
regular = 20,
admin = 30,
}

View File

@ -1,13 +1,19 @@
require 'utils.table'
local UserGroups = require 'features.user_groups'
local Event = require 'utils.event'
local Game = require 'utils.game'
local Utils = require 'utils.core'
local Timestamp = require 'utils.timestamp'
local Rank = require 'features.rank_system'
local Donator = require 'features.donator'
local Server = require 'features.server'
local Ranks = require 'resources.ranks'
local insert = table.insert
local format = string.format
local next = next
local serialize = serpent.line
local match = string.match
local gmatch = string.gmatch
local get_rank_name = Rank.get_rank_name
local Command = {}
@ -29,13 +35,13 @@ local option_names = {
['description'] = 'A description of the command',
['arguments'] = 'A table of arguments, example: {"foo", "bar"} would map the first 2 arguments to foo and bar',
['default_values'] = 'A default value for a given argument when omitted, example: {bar = false}',
['regular_only'] = 'Set this to true if only regulars may execute this command',
['admin_only'] = 'Set this to true if only admins may execute this command',
['required_rank'] = 'Set this to determins what rank is required to execute a command',
['donator_only'] = 'Set this to true if only donators may execute this command',
['debug_only'] = 'Set this to true if it should be registered when _DEBUG is true',
['cheat_only'] = 'Set this to true if it should be registered when _CHEATS is true',
['allowed_by_server'] = 'Set to true if the server (host) may execute this command',
['allowed_by_player'] = 'Set to false to disable players from executing this command',
['log_command'] = 'Set to true to log commands. Always true when admin_only is enabled',
['log_command'] = 'Set to true to log commands. Always true when admin is required',
['capture_excess_arguments'] = 'Allows the last argument to be the remaining text in the command',
['custom_help_text'] = 'Sets a custom help text to override the auto-generated help',
}
@ -62,8 +68,8 @@ end
--- description = 'A description of the command',
--- arguments = {'foo', 'bar'}, -- maps arguments to these names in the given sequence
--- default_values = {bar = false}, -- gives a default value to 'bar' when omitted
--- regular_only = true, -- defaults to false
--- admin_only = true, -- defaults to false
--- required_rank = Ranks.regular, -- defaults to Ranks.guest
--- donator_only = true, -- defaults to false
--- debug_only = true, -- registers the command if _DEBUG is set to true, defaults to false
--- cheat_only = true, -- registers the command if _CHEATS is set to true, defaults to false
--- allowed_by_server = false, -- lets the server execute this, defaults to false
@ -84,15 +90,15 @@ function Command.add(command_name, options, callback)
local description = options.description or '[Undocumented command]'
local arguments = options.arguments or {}
local default_values = options.default_values or {}
local regular_only = options.regular_only or false
local admin_only = options.admin_only or false
local required_rank = options.required_rank or Ranks.guest
local donator_only = options.donator_only or false
local debug_only = options.debug_only or false
local cheat_only = options.cheat_only or false
local capture_excess_arguments = options.capture_excess_arguments or false
local custom_help_text = options.custom_help_text or false
local allowed_by_server = options.allowed_by_server or false
local allowed_by_player = options.allowed_by_player
local log_command = options.log_command or options.admin_only or false
local log_command = options.log_command or (required_rank >= Ranks.admin) or false
local argument_list_size = table_size(arguments)
local argument_list = ''
@ -129,11 +135,11 @@ function Command.add(command_name, options, callback)
local extra = ''
if allowed_by_server and not allowed_by_player then
extra = ' (Server Only)'
elseif allowed_by_player and admin_only then
extra = ' (Admin Only)'
elseif allowed_by_player and regular_only then
extra = ' (Regulars Only)'
extra = ' (Server only)'
elseif allowed_by_player and (required_rank > Ranks.guest) then
extra = format(' (Rank %s or above only)', get_rank_name(required_rank))
elseif allowed_by_player and donator_only then
extra = ' (Donator only)'
end
local help_text = custom_help_text or argument_list .. description .. extra
@ -157,13 +163,13 @@ function Command.add(command_name, options, callback)
return
end
if admin_only and not player.admin then
print(format("The command '%s' requires admin status to be be executed.", command_name))
if Rank.less_than(player_name, required_rank) then
print(format("The command '%s' requires %s rank or higher to be be executed.", command_name, get_rank_name(required_rank)))
return
end
if regular_only and not UserGroups.is_regular(player_name) and not player.admin then
print(format("The command '%s' is not available to guests.", command_name))
if donator_only and not Donator.is_donator(player_name) then
print(format("The command '%s' is only allowed for donators.", command_name))
return
end
end
@ -171,7 +177,7 @@ function Command.add(command_name, options, callback)
local named_arguments = {}
local from_command = {}
local raw_parameter_index = 1
for param in string.gmatch(command.parameter or '', '%S+') do
for param in gmatch(command.parameter or '', '%S+') do
if capture_excess_arguments and raw_parameter_index == argument_list_size then
if not from_command[raw_parameter_index] then
from_command[raw_parameter_index] = param
@ -217,7 +223,18 @@ function Command.add(command_name, options, callback)
end
if log_command then
log(format('[%s Command] %s, used: %s %s', admin_only and 'Admin' or 'Player', player_name, command_name, serialize(named_arguments)))
local tick = 'pre-game'
if game then
tick = Utils.format_time(game.tick)
end
local server_time = Server.get_current_time()
if server_time then
server_time = format('(Server time: %s)', Timestamp.to_string(server_time))
else
server_time = ''
end
log(format('%s(Map time: %s) [%s Command] %s, used: %s %s', server_time, tick, (options.required_rank >= Ranks.admin) and 'Admin' or 'Player', player_name, command_name, serialize(named_arguments)))
end
local success, error = pcall(function ()

View File

@ -32,7 +32,7 @@ function Module.distance(pos1, pos2)
end
--- Takes msg and prints it to all players except provided player
-- @param msg <string> The message to print
-- @param msg <string|table> table if locale is used
-- @param player <LuaPlayer> the player not to send the message to
-- @param color <table> the color to use for the message, defaults to white
function Module.print_except(msg, player, color)
@ -48,8 +48,8 @@ function Module.print_except(msg, player, color)
end
--- Prints a message to all online admins
--@param msg <string> The message to print
--@param source <LuaPlayer|string|nil> string must be the name of a player, nil for server.
-- @param msg <string|table> table if locale is used
-- @param source <LuaPlayer|string|nil> string must be the name of a player, nil for server.
function Module.print_admins(msg, source)
local source_name
local chat_color

View File

@ -64,9 +64,12 @@ function Game.get_player_from_any(obj)
end
--- Prints to player or console.
function Game.player_print(str)
-- @param str <string|table> table if locale is used
-- @param color <table> defaults to white
function Game.player_print(str, color)
color = color or Color.white
if game.player then
game.player.print(str)
game.player.print(str, color)
else
print(str)
end

View File

@ -2,6 +2,7 @@ local Global = require 'utils.global'
local Game = require 'utils.game'
local PlayerStats = require 'features.player_stats'
local Command = require 'utils.command'
local Ranks = require 'resources.ranks'
local format = string.format
local abs = math.abs
@ -110,7 +111,7 @@ Command.add(
description = 'Gives a reward to a target player (removes if quantity is negative)',
arguments = {'target', 'quantity', 'reason'},
default_values = {reason = false},
admin_only = true,
required_rank = Ranks.admin,
capture_excess_arguments = true,
allowed_by_server = true,
allowed_by_player = true

View File

@ -2,7 +2,6 @@
local random = math.random
local floor = math.floor
local remove = table.remove
local insert = table.insert
local tonumber = tonumber
local pairs = pairs
local table_size = table_size
@ -40,7 +39,7 @@ end
function table.add_all(t1, t2)
for k, v in pairs(t2) do
if tonumber(k) then
insert(t1, v)
t1[#t1 + 1] = v
else
t1[k] = v
end
@ -109,7 +108,7 @@ end
--- Chooses a random entry from a table
-- because this uses math.random, it cannot be used outside of events
-- @param t <table> to select an element from
-- @param t <table>
-- @param key <boolean> to indicate whether to return the key or value
-- @return <any> a random element of table t
function table.get_random_dictionary_entry(t, key)