2019-01-30 05:52:43 +02:00
|
|
|
--[[
|
|
|
|
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'
|
2019-02-01 20:32:00 +02:00
|
|
|
local math = require 'utils.math'
|
2019-01-30 05:52:43 +02:00
|
|
|
local Server = require 'features.server'
|
|
|
|
local Ranks = require 'resources.ranks'
|
|
|
|
local Colors = require 'resources.color_presets'
|
|
|
|
|
|
|
|
local Config = global.config.rank_system
|
|
|
|
|
|
|
|
-- Localized functions
|
2019-02-01 20:32:00 +02:00
|
|
|
local clamp = math.clamp
|
2019-02-11 07:58:32 +02:00
|
|
|
local clear_table = table.clear_table
|
2019-01-30 05:52:43 +02:00
|
|
|
|
|
|
|
-- Constants
|
|
|
|
local ranking_data_set = 'rankings'
|
2019-02-02 21:52:00 +02:00
|
|
|
local nth_tick = 54001 -- nearest prime to 15 minutes in ticks
|
2019-01-30 21:52:11 +02:00
|
|
|
local rank_name_lookup = {}
|
2019-02-01 20:32:00 +02:00
|
|
|
local sorted_ranks = {}
|
|
|
|
local rank_to_index = {}
|
2019-01-30 21:52:11 +02:00
|
|
|
|
|
|
|
for k, v in pairs(Ranks) do
|
2019-02-13 03:20:35 +02:00
|
|
|
rank_name_lookup[v] = {'ranks.' .. k}
|
2019-01-30 21:52:11 +02:00
|
|
|
end
|
2019-02-01 20:32:00 +02:00
|
|
|
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
|
|
|
|
|
2019-01-30 05:52:43 +02:00
|
|
|
-- Local vars
|
|
|
|
local Public = {}
|
2019-02-20 20:52:30 +02:00
|
|
|
local set_player_rank
|
2019-01-30 05:52:43 +02:00
|
|
|
|
2019-01-30 22:55:48 +02:00
|
|
|
-- Global register vars
|
|
|
|
local player_ranks = {}
|
|
|
|
local guests = {}
|
2019-01-30 05:52:43 +02:00
|
|
|
|
|
|
|
Global.register(
|
|
|
|
{
|
2019-01-30 22:55:48 +02:00
|
|
|
player_ranks = player_ranks,
|
|
|
|
guests = guests
|
2019-01-30 05:52:43 +02:00
|
|
|
},
|
|
|
|
function(tbl)
|
|
|
|
player_ranks = tbl.player_ranks
|
2019-01-30 22:55:48 +02:00
|
|
|
guests = tbl.guests
|
2019-01-30 05:52:43 +02:00
|
|
|
end
|
|
|
|
)
|
|
|
|
|
|
|
|
-- Local functions
|
|
|
|
|
2019-02-12 19:09:47 +02:00
|
|
|
--- 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)
|
2019-02-01 20:32:00 +02:00
|
|
|
local index = rank_to_index[current_rank]
|
|
|
|
|
|
|
|
local new_index = clamp(index + change, 1, #sorted_ranks)
|
|
|
|
|
|
|
|
return sorted_ranks[new_index]
|
|
|
|
end
|
|
|
|
|
2019-01-30 21:52:11 +02:00
|
|
|
--- 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()
|
2019-01-30 05:52:43 +02:00
|
|
|
local auto_trusted = Ranks.auto_trusted
|
2019-01-30 21:52:11 +02:00
|
|
|
local guest = Ranks.guest
|
2019-01-30 05:52:43 +02:00
|
|
|
local time_for_trust = Config.time_for_trust
|
2019-01-30 22:55:48 +02:00
|
|
|
local equal_or_greater_than = Public.equal_or_greater_than
|
|
|
|
local equal = Public.equal
|
2019-01-30 05:52:43 +02:00
|
|
|
local set_data = Server.set_data
|
|
|
|
|
2019-02-02 22:24:31 +02:00
|
|
|
for index, p in pairs(guests) do
|
2019-01-30 22:55:48 +02:00
|
|
|
if not p or not p.valid then
|
2019-02-02 22:24:31 +02:00
|
|
|
guests[index] = nil
|
2019-01-30 22:55:48 +02:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2019-02-02 22:24:31 +02:00
|
|
|
local p_name = p.name
|
2019-01-30 22:55:48 +02:00
|
|
|
if equal_or_greater_than(p_name, auto_trusted) then
|
2019-02-02 22:24:31 +02:00
|
|
|
guests[index] = nil
|
2019-01-30 22:55:48 +02:00
|
|
|
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)
|
2019-02-02 22:24:31 +02:00
|
|
|
guests[index] = nil
|
2019-01-30 23:02:56 +02:00
|
|
|
elseif not p.connected then
|
2019-02-02 22:24:31 +02:00
|
|
|
guests[index] = nil
|
2019-01-30 05:52:43 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-02-11 07:58:32 +02:00
|
|
|
--- On callback, overwrites player rank entries with data entries.
|
2019-01-30 05:52:43 +02:00
|
|
|
local sync_ranks_callback =
|
|
|
|
Token.register(
|
|
|
|
function(data)
|
2019-02-13 22:01:38 +02:00
|
|
|
if not data or not data.entries then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2019-02-11 07:58:32 +02:00
|
|
|
clear_table(player_ranks)
|
2019-02-02 05:24:32 +02:00
|
|
|
for k, v in pairs(data.entries) do
|
|
|
|
player_ranks[k] = v
|
|
|
|
end
|
2019-01-30 05:52:43 +02:00
|
|
|
end
|
|
|
|
)
|
|
|
|
|
|
|
|
local function on_player_joined(event)
|
2019-02-02 22:24:31 +02:00
|
|
|
local index = event.player_index
|
|
|
|
local player = Game.get_player_by_index(index)
|
2019-01-30 05:52:43 +02:00
|
|
|
if not player then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2019-02-02 22:24:31 +02:00
|
|
|
local player_name = player.name
|
2019-01-30 22:55:48 +02:00
|
|
|
if Public.equal(player_name, Ranks.guest) then
|
2019-02-02 22:24:31 +02:00
|
|
|
guests[index] = player
|
2019-01-30 22:55:48 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
--- Fix for legacy name storage
|
2019-01-30 05:52:43 +02:00
|
|
|
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
|
|
|
|
|
2019-02-13 01:46:22 +02:00
|
|
|
--- 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
|
|
|
|
|
2019-02-08 23:54:23 +02:00
|
|
|
--- Returns the table of players in the ranking system
|
|
|
|
-- @return <table>
|
|
|
|
function Public.get_player_table()
|
|
|
|
return player_ranks
|
|
|
|
end
|
|
|
|
|
2019-01-30 05:52:43 +02:00
|
|
|
--- Returns the player's rank as a name.
|
|
|
|
-- @param player_name <string>
|
2019-02-14 21:45:24 +02:00
|
|
|
-- @return <LocalisedString>
|
2019-01-30 05:52:43 +02:00
|
|
|
function Public.get_player_rank_name(player_name)
|
2019-01-30 21:52:11 +02:00
|
|
|
return rank_name_lookup[get_player_rank(player_name)]
|
2019-01-30 05:52:43 +02:00
|
|
|
end
|
2019-02-13 01:46:22 +02:00
|
|
|
local get_player_rank_name = Public.get_player_rank_name
|
2019-01-30 05:52:43 +02:00
|
|
|
|
|
|
|
--- Returns the player's rank as a name.
|
|
|
|
-- @param player_name <string>
|
|
|
|
-- @return <table>
|
|
|
|
function Public.get_player_rank_color(player_name)
|
2019-02-13 01:46:22 +02:00
|
|
|
local rank_name = get_player_rank_name(player_name)
|
2019-01-30 05:52:43 +02:00
|
|
|
return Colors[rank_name]
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Returns the rank's name.
|
|
|
|
-- @param rank <number>
|
2019-02-14 21:45:24 +02:00
|
|
|
-- @return <LocalisedString>
|
2019-01-30 05:52:43 +02:00
|
|
|
function Public.get_rank_name(rank)
|
2019-01-30 21:52:11 +02:00
|
|
|
return rank_name_lookup[rank]
|
2019-01-30 05:52:43 +02:00
|
|
|
end
|
2019-02-13 01:46:22 +02:00
|
|
|
local get_rank_name = Public.get_rank_name
|
2019-01-30 05:52:43 +02:00
|
|
|
|
|
|
|
--- Returns the rank's color.
|
|
|
|
-- @param rank <table>
|
|
|
|
function Public.get_rank_color(rank)
|
2019-02-13 14:37:45 +02:00
|
|
|
return Colors[rank]
|
2019-01-30 05:52:43 +02:00
|
|
|
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)
|
2019-01-30 21:52:11 +02:00
|
|
|
local p_rank = get_player_rank(player_name)
|
2019-01-30 05:52:43 +02:00
|
|
|
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)
|
2019-01-30 21:52:11 +02:00
|
|
|
local p_rank = get_player_rank(player_name)
|
2019-01-30 05:52:43 +02:00
|
|
|
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)
|
2019-01-30 21:52:11 +02:00
|
|
|
local p_rank = get_player_rank(player_name)
|
2019-01-30 05:52:43 +02:00
|
|
|
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)
|
2019-01-30 21:52:11 +02:00
|
|
|
local p_rank = get_player_rank(player_name)
|
2019-01-30 05:52:43 +02:00
|
|
|
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)
|
2019-01-30 21:52:11 +02:00
|
|
|
local p_rank = get_player_rank(player_name)
|
2019-01-30 05:52:43 +02:00
|
|
|
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)
|
2019-01-30 21:52:11 +02:00
|
|
|
local p_rank = get_player_rank(player_name)
|
2019-01-30 05:52:43 +02:00
|
|
|
return p_rank <= rank
|
|
|
|
end
|
|
|
|
|
2019-01-30 21:52:11 +02:00
|
|
|
--- Take a player and attempts to increase their rank by 1
|
|
|
|
-- @param player_name <string>
|
2019-02-14 21:45:24 +02:00
|
|
|
-- @return <LocalisedString|nil> new rank name or nil if already at highest rank
|
2019-01-30 21:52:11 +02:00
|
|
|
function Public.increase_player_rank(player_name)
|
2019-02-01 20:32:00 +02:00
|
|
|
local current_rank = (get_player_rank(player_name))
|
2019-02-12 19:09:47 +02:00
|
|
|
local new_rank = change_rank_by_number(current_rank, 1)
|
2019-02-01 20:32:00 +02:00
|
|
|
if current_rank == new_rank then
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
2019-01-30 21:52:11 +02:00
|
|
|
local new_rank_name = rank_name_lookup[new_rank]
|
2019-02-20 22:13:19 +02:00
|
|
|
set_player_rank(player_name, new_rank)
|
|
|
|
return new_rank_name
|
2019-01-30 21:52:11 +02:00
|
|
|
end
|
|
|
|
|
2019-02-12 19:09:47 +02:00
|
|
|
--- 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>
|
2019-02-14 21:45:24 +02:00
|
|
|
-- @return <boolean> <LocalisedString> success/failure, and LocalisedString of the player's rank
|
2019-02-12 19:09:47 +02:00
|
|
|
function Public.increase_player_rank_to(player_name, rank)
|
|
|
|
if Public.less_than(player_name, rank) then
|
2019-02-20 20:52:30 +02:00
|
|
|
set_player_rank(player_name, rank)
|
2019-02-14 10:03:34 +02:00
|
|
|
return true, get_rank_name(rank)
|
2019-02-12 19:09:47 +02:00
|
|
|
else
|
2019-02-14 10:03:34 +02:00
|
|
|
return false, get_player_rank_name(player_name)
|
2019-02-12 19:09:47 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-01-30 21:52:11 +02:00
|
|
|
--- Take a player and attempts to decrease their rank by 1
|
|
|
|
-- @param player_name <string>
|
2019-02-14 21:45:24 +02:00
|
|
|
-- @return <LocalisedString|nil> new rank name or nil if already at lowest rank
|
2019-01-30 21:52:11 +02:00
|
|
|
function Public.decrease_player_rank(player_name)
|
2019-02-01 20:32:00 +02:00
|
|
|
local current_rank = (get_player_rank(player_name))
|
2019-02-12 19:09:47 +02:00
|
|
|
local new_rank = change_rank_by_number(current_rank, -1)
|
2019-02-01 20:32:00 +02:00
|
|
|
if current_rank == new_rank then
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
2019-01-30 21:52:11 +02:00
|
|
|
local new_rank_name = rank_name_lookup[new_rank]
|
2019-02-20 22:13:19 +02:00
|
|
|
set_player_rank(player_name, new_rank)
|
|
|
|
return new_rank_name
|
2019-01-30 21:52:11 +02:00
|
|
|
end
|
|
|
|
|
2019-02-12 19:09:47 +02:00
|
|
|
--- 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>
|
2019-02-14 21:45:24 +02:00
|
|
|
-- @return <boolean> <LocalisedString> success/failure, and LocalisedString of the player's rank
|
2019-02-12 19:09:47 +02:00
|
|
|
function Public.decrease_player_rank_to(player_name, rank)
|
|
|
|
if Public.greater_than(player_name, rank) then
|
2019-02-20 20:52:30 +02:00
|
|
|
set_player_rank(player_name, rank)
|
2019-02-14 10:03:34 +02:00
|
|
|
return true, get_rank_name(rank)
|
2019-02-12 19:09:47 +02:00
|
|
|
else
|
2019-02-14 10:03:34 +02:00
|
|
|
return false, get_player_rank_name(player_name)
|
2019-02-12 19:09:47 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-01-30 05:52:43 +02:00
|
|
|
--- Sets a player's rank
|
|
|
|
-- @param player_name <string>
|
|
|
|
-- @param rank <number>
|
2019-02-12 19:09:47 +02:00
|
|
|
-- @return <boolean> success/failure
|
2019-02-02 23:15:36 +02:00
|
|
|
function Public.set_player_rank(player_name, rank)
|
2019-01-30 05:52:43 +02:00
|
|
|
if Public.equal(player_name, rank) then
|
2019-02-12 19:09:47 +02:00
|
|
|
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
|
2019-01-30 05:52:43 +02:00
|
|
|
else
|
|
|
|
player_ranks[player_name] = rank
|
|
|
|
Server.set_data(ranking_data_set, player_name, rank)
|
2019-02-12 19:09:47 +02:00
|
|
|
return true
|
2019-01-30 05:52:43 +02:00
|
|
|
end
|
|
|
|
end
|
2019-02-20 20:52:30 +02:00
|
|
|
set_player_rank = Public.set_player_rank
|
2019-01-30 05:52:43 +02:00
|
|
|
|
2019-02-12 19:09:47 +02:00
|
|
|
--- Resets a player's rank to guest (or higher if a user meets the criteria for automatic rank)
|
2019-01-30 05:52:43 +02:00
|
|
|
-- @param player_name <string>
|
2019-02-14 21:45:24 +02:00
|
|
|
-- @return <boolean> <LocalisedString> boolean for success/failure, LocalisedString of rank name
|
2019-02-02 23:15:36 +02:00
|
|
|
function Public.reset_player_rank(player_name)
|
2019-01-30 05:52:43 +02:00
|
|
|
local guest_rank = Ranks.guest
|
|
|
|
local auto_trusted = Ranks.auto_trusted
|
|
|
|
|
|
|
|
if Public.equal(player_name, guest_rank) then
|
2019-02-14 10:03:34 +02:00
|
|
|
return false, get_rank_name(guest_rank)
|
2019-01-30 05:52:43 +02:00
|
|
|
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
|
2019-02-20 20:52:30 +02:00
|
|
|
set_player_rank(player_name, rank)
|
2019-01-30 05:52:43 +02:00
|
|
|
else
|
|
|
|
rank = guest_rank
|
2019-02-20 20:52:30 +02:00
|
|
|
set_player_rank(player_name, rank)
|
2019-01-30 05:52:43 +02:00
|
|
|
end
|
2019-02-14 10:03:34 +02:00
|
|
|
return true, get_rank_name(rank)
|
2019-01-30 05:52:43 +02:00
|
|
|
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)
|
|
|
|
|
2019-02-02 05:24:32 +02:00
|
|
|
Event.add(Server.events.on_server_started, Public.sync_ranks)
|
|
|
|
|
|
|
|
Event.on_nth_tick(nth_tick, check_promote_to_auto_trusted)
|
|
|
|
|
2019-01-30 05:52:43 +02:00
|
|
|
Server.on_data_set_changed(
|
|
|
|
ranking_data_set,
|
|
|
|
function(data)
|
|
|
|
player_ranks[data.key] = data.value
|
|
|
|
end
|
|
|
|
)
|
|
|
|
|
|
|
|
return Public
|