--[[ 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 Utils = require 'utils.core' 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 format = string.format local clamp = math.clamp -- 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] = 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 local function change_rank(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 --- Gets a player's rank. Intentionally not exposed. local function get_player_rank(player_name) return player_ranks[player_name] or 0 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) 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 --- Returns the player's rank as a name. -- @param player_name -- @return function Public.get_player_rank_name(player_name) return rank_name_lookup[get_player_rank(player_name)] end --- Returns the player's rank as a name. -- @param player_name -- @return function Public.get_player_rank_color(player_name) local rank_name = Public.get_player_rank_name(player_name) return Colors[rank_name] end --- Returns the rank's name. -- @param rank -- @return function Public.get_rank_name(rank) return rank_name_lookup[rank] end --- Returns the rank's color. -- @param rank
function Public.get_rank_color(rank) local rank_name = Public.get_rank_name(rank) return Colors[rank_name] end --- Evaluates if a player's rank is equal to the rank provided -- @param player_name -- @param rank -- @return 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 -- @param rank -- @return 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 -- @param rank -- @return 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 -- @param rank -- @return 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 -- @param rank -- @return 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 -- @param rank -- @return 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 -- @return 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(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 by 1 -- @param player_name -- @return 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(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 --- Sets a player's rank -- @param player_name -- @param rank function Public.set_player_rank(player_name, rank) local actor = Utils.get_actor() if Public.equal(player_name, rank) then Game.player_print(format('%s is %s rank already.', player_name, Public.get_rank_name(rank))) else player_ranks[player_name] = rank Server.set_data(ranking_data_set, player_name, rank) game.print(format("%s set %s's rank to %s.", actor, player_name, Public.get_rank_name(rank))) end end --- Resets a player's rank to the lowest rank based on playtime (guest or auto_trust) -- @param player_name function Public.reset_player_rank(player_name) local actor = Utils.get_actor() local guest_rank = Ranks.guest local auto_trusted = Ranks.auto_trusted if Public.equal(player_name, guest_rank) then Game.player_print(format('%s is %s rank already.', player_name, Public.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 player_ranks[player_name] = auto_trusted Server.set_data(ranking_data_set, player_name, auto_trusted) rank = auto_trusted else player_ranks[player_name] = nil Server.set_data(ranking_data_set, player_name, nil) rank = guest_rank end game.print(format("%s set %s's rank to %s.", actor, player_name, Public.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