From af82f5117cb7cf85040247f54df5a2fc4d76e268 Mon Sep 17 00:00:00 2001 From: Vladislav Tsendrovskii Date: Sat, 30 Oct 2021 03:35:43 +0300 Subject: [PATCH] Fix #472: get rid of races.txt * Use player meta for storing race, skin, gender, race privs, second chance * Add converter script races.txt -> races.sql --- mods/lord/Game/lord_classes/init.lua | 287 ++++++++++++++++++++------- util/transfer_races.lua | 95 +++++++++ 2 files changed, 313 insertions(+), 69 deletions(-) create mode 100644 util/transfer_races.lua diff --git a/mods/lord/Game/lord_classes/init.lua b/mods/lord/Game/lord_classes/init.lua index 2471b098d..9c97b9f4f 100644 --- a/mods/lord/Game/lord_classes/init.lua +++ b/mods/lord/Game/lord_classes/init.lua @@ -3,10 +3,6 @@ local table_has_key local SL = minetest.get_translator("lord_classes") ---[[ - TODO: Move I/O-related functions into a separate mod (lord_util?) ---]] - --[[ Definitions --]] @@ -15,7 +11,6 @@ local form_header = "size[7,4]".. "background[7,4;1,1;gui_formbg.png;true]" races = { - save_path = minetest.get_worldpath() .. "/races.txt", update_cbs = {}, init_cbs = {}, } @@ -117,20 +112,6 @@ local function ensure_table_struct() cache.skins = ensure_table(cache.skins) end --- Load serialized classes from file --- Returns false when failed, true otherwise -function races.load() - local input = io.open(races.save_path, "r") - if not input then - return false - end - local content = input:read("*all") - cache = minetest.deserialize(content) - input:close() - return true -end - -races.load() ensure_table_struct() -- Serialize and save the races @@ -208,7 +189,22 @@ end -- Returns the race and the gender of specified player function races.get_race_and_gender(name) - return cache.players[name] or races.default + local player = minetest.get_player_by_name(name) + local pmeta = player:get_meta() + + local race = pmeta:get_string("player:race") + if race == nil or race == "" then + race = races.default[1] + pmeta:set_string("player:race", race) + end + + local gender = pmeta:get_string("player:gender") + if gender == nil or gender == "" then + gender = races.default[2] + pmeta:set_string("player:gender", gender) + end + + return {race, gender} end -- Now faction is binded to race @@ -225,75 +221,156 @@ function races.get_gender(name) return races.get_race_and_gender(name)[2] end +function races.get_granted_privs(name) + local player = minetest.get_player_by_name(name) + local pmeta = player:get_meta() + + local privs = pmeta:get_string("player:granted_privs") + if privs ~= "" then + return minetest.deserialize(privs) + elseif table_has_key(cache.granted_privs, name) then + privs = cache.granted_privs[name] + else + local race = races.get_race(name) + privs = races.list[race].granted_privs or {} + end + pmeta:set_string("player:granted_privs", minetest.serialize(privs)) + return privs +end + +function races.get_revoked_privs(name) + local player = minetest.get_player_by_name(name) + local pmeta = player:get_meta() + + local privs = pmeta:get_string("player:revoked_privs") + if privs ~= "" then + return minetest.deserialize(privs) + elseif table_has_key(cache.revoked_privs, name) then + privs = cache.revoked_privs[name] + else + local race = races.get_race(name) + privs = races.list[race].revoked_privs or {} + end + pmeta:set_string("player:revoked_privs", minetest.serialize(privs)) + return privs +end + function races.set_race_and_gender(name, race_and_gender, show_message) local valid = races.validate(race_and_gender) if not valid then return false end + local old_granted_privs = races.get_granted_privs(name) + local old_revoked_privs = races.get_revoked_privs(name) + local race = race_and_gender[1] - races.update_privileges(name, races.list[race].granted_privs, - races.list[race].revoked_privs) + local new_granted_privs = races.list[race].granted_privs + local new_revoked_privs = races.list[race].revoked_privs - cache.players[name] = race_and_gender --- races.update_player(name, race_and_gender, races.default_skin) + races.update_privileges(name, old_granted_privs, old_revoked_privs, + new_granted_privs, new_revoked_privs) -- Notify player if show_message then minetest.chat_send_player(name, SL("change_" .. race)) end + local player = minetest.get_player_by_name(name) + local pmeta = player:get_meta() + pmeta:set_string("player:race", race_and_gender[1]) + pmeta:set_string("player:gender", race_and_gender[2]) + +-- races.update_player(name, race_and_gender, races.default_skin) return true end function races.get_skin(name) - return cache.skins[name] or races.default_skin + local player = minetest.get_player_by_name(name) + local pmeta = player:get_meta() + local skin = pmeta:get_int("player:skin") + if skin == nil or skin == 0 then + if table_has_key(cache.skins, name) then + -- legacy compatibility + skin = cache.skins[name] + else + skin = races.default_skin + end + pmeta:set_int("player:skin", skin) + end + return skin end function races.set_skin(name, skin) - cache.skins[name] = skin + local player = minetest.get_player_by_name(name) + local pmeta = player:get_meta() + pmeta:set_int("player:skin", skin) + races.update_player(name, races.get_race_and_gender(name), skin) end -- Tinker with privs -function races.update_privileges(name, granted_privs, revoked_privs) - local privs = minetest.get_player_privs(name) +local function has_value(array, val) + for _, v in ipairs(array) do + if v == val then + return true + end + end + return false +end - -- Create tables if they don't exist - cache.granted_privs[name] = cache.granted_privs[name] or {} - cache.revoked_privs[name] = cache.revoked_privs[name] or {} +function races.update_privileges(name, old_granted_privs, old_revoked_privs, + new_granted_privs, new_revoked_privs) + + local privs = minetest.get_player_privs(name) -- Step #1: Restore normal privileges - for priv_name, _ in pairs(privs) do - if cache.granted_privs[name][priv_name] then - cache.granted_privs[name][priv_name] = nil - privs[priv_name] = nil + if old_granted_privs then + for priv_name, _ in pairs(privs) do + if has_value(old_granted_privs, priv_name) then + privs[priv_name] = nil + end end end - for priv_name, _ in pairs(cache.revoked_privs[name]) do - cache.revoked_privs[name][priv_name] = nil - privs[priv_name] = true + if old_revoked_privs then + for _,priv_name in ipairs(old_revoked_privs) do + privs[priv_name] = true + end end -- Step #2: Grant/revoke new privileges - if granted_privs then - for _, priv_name in pairs(granted_privs) do + if new_granted_privs then + for _, priv_name in ipairs(new_granted_privs) do if not privs[priv_name] then -- Player hasn't this privilege privs[priv_name] = true - cache.granted_privs[name][priv_name] = true end end end - if revoked_privs then - for _, priv_name in pairs(revoked_privs) do + if new_revoked_privs then + for _, priv_name in ipairs(new_revoked_privs) do privs[priv_name] = nil - cache.revoked_privs[name][priv_name] = true end end minetest.set_player_privs(name, privs) + + local player = minetest.get_player_by_name(name) + local pmeta = player:get_meta() + + if new_granted_privs then + pmeta:set_string("player:granted_privs", minetest.serialize(new_granted_privs)) + else + pmeta:set_string("player:granted_privs", minetest.serialize({})) + end + + if new_revoked_privs then + pmeta:set_string("player:revoked_privs", minetest.serialize(new_revoked_privs)) + else + pmeta:set_string("player:revoked_privs", minetest.serialize({})) + end + end -- Converts user-friendly names to the corresponding internal names @@ -477,7 +554,6 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) minetest.after(0.1, races.show_change_form, name) elseif fields.ok then races.set_skin(name, tonumber(fields.skin)) - races.save() elseif fields.skin then local r = races.get_race_and_gender(name) minetest.after(0.1, races.show_skin_change_form, r[1], r[2], tonumber(fields.skin), name) @@ -485,30 +561,104 @@ minetest.register_on_player_receive_fields(function(player, formname, fields) end end) +function races.player_is_known(name) + local player = minetest.get_player_by_name(name) + if not player then + return false + end + + local pmeta = player:get_meta() + local race = pmeta:get_string("player:race") + if race ~= nil and race ~= "" then + return true + end + + if table_has_key(cache.players, name) then + return true + end + + return false +end + +function races.has_second_chance(name) + local player = minetest.get_player_by_name(name) + local pmeta = player:get_meta() + local can_change = pmeta:get_string("player:has_second_chance") + + if can_change == "" or can_change == nil then + if table_has_key(cache.can_change, name) then + -- legacy compatibility + can_change = cache.can_change[name] + else + can_change = true + end + pmeta:set_string("player:has_second_chance", tostring(can_change)) + return can_change + end + + return can_change == "true" +end + +function races.grant_second_chance(name) + local player = minetest.get_player_by_name(name) + local pmeta = player:get_meta() + pmeta:set_string("player:has_second_chance", "true") +end + +function races.revoke_second_chance(name) + local player = minetest.get_player_by_name(name) + local pmeta = player:get_meta() + pmeta:set_string("player:has_second_chance", "false") +end + +function races.print_player_race(name) + local race = races.get_race(name) + local gender = races.get_gender(name) + local skin = races.get_skin(name) + local can_change = races.has_second_chance(name) + local granted_privs = races.get_granted_privs(name) + local revoked_privs = races.get_revoked_privs(name) + + minetest.log("Player "..name.." : race="..race.." gender="..gender.." skin="..skin) + if can_change then + minetest.log("Has second chance") + else + minetest.log("Dosn't have second chance") + end + + minetest.log("Granted privs:") + for _, v in ipairs(granted_privs) do + minetest.log(" "..v) + end + + minetest.log("Revoked privs:") + for _, v in ipairs(revoked_privs) do + minetest.log(" "..v) + end + +end + minetest.register_on_joinplayer(function(player) local name = player:get_player_name() - if table_has_key(cache.players, name) then -- Player is registered already - local r = races.get_race_and_gender(name) - if races.list[r[1]].cannot_be_selected then + local race + local gender + local skin + + if races.player_is_known(name) then -- Player is registered already + race = races.get_race(name) + if races.list[race].cannot_be_selected then races.show_change_form(name) - races.init_player(name, r, races.get_skin(name)) - return - end - r = races.get_race_and_gender(name) - races.set_race_and_gender(name, r, false) - -- Player is registered, but has no skin - if cache.skins[name] == nil then - cache.skins[name] = races.default_skin end - - races.init_player(name, r, races.get_skin(name)) else races.show_change_form(name) - cache.can_change[name] = true - local r = races.get_race_and_gender(name) - races.init_player(name, r, races.get_skin(name)) + races.grant_second_chance(name) end + + race = races.get_race(name) + gender = races.get_gender(name) + skin = races.get_skin(name) + races.init_player(name, {race, gender}, skin) end) minetest.register_chatcommand("second_chance", { @@ -516,14 +666,11 @@ minetest.register_chatcommand("second_chance", { privs = {}, description = SL("Second chance"), func = function(name, params) - if cache.can_change[name] == nil then - cache.can_change[name] = true - end - if not cache.can_change[name] then + if not races.has_second_chance(name) then return false, SL("Won't give another chance") end races.show_change_form(name) - cache.can_change[name] = false + races.revoke_second_chance(name) end }) @@ -546,11 +693,13 @@ minetest.register_chatcommand("give_chance", { end -- Check if player exists - if not cache.players[args[1]] then - return false, string.format(SL("Player '%s' does not exist"), args[1]) + -- TODO: handle case of logged out player + local username = args[1] + if not races.player_is_known(username) then + return false, string.format(SL("Player '%s' does not exist"), username) end - races.show_change_form(args[1]) + races.grant_second_chance(username) end }) diff --git a/util/transfer_races.lua b/util/transfer_races.lua new file mode 100644 index 000000000..ef51c5f37 --- /dev/null +++ b/util/transfer_races.lua @@ -0,0 +1,95 @@ +-- This script is created to convert old races.txt file into SQL file +-- for import into minetest database +-- +-- Generated SQL file adds records into table "player_metadata" +-- If minetest uses sqlite database, it can be imported by +-- +-- sqlite3 players.sqlite < races.sql +-- +-- In case of other database read the documentation to database. +-- +-- How to generate SQL file: +-- +-- luajit transfer_races.lua races.txt > races.sql + +function read_races(filename) + local f = io.open(filename, "rt") + if f then + local content = f:read() + f:close() + local races = loadstring(content) + return races() + end + return nil +end + +function escape_str(s) + s = s:gsub('\'', '\'\'') + return "\'"..s.."\'" +end + +function recode_races(races) + local revoked_privs = races["revoked_privs"] + local granted_privs = races["granted_privs"] + local players = races["players"] + local can_change = races["can_change"] + local skins = races["skins"] + + for name, privs in pairs(revoked_privs) do + local val = "return {" + for priv,revoked in pairs(privs) do + if revoked then + val = val..'"'..tostring(priv)..'",' + end + end + val = val.."}" + local cmd = "INSERT INTO player_metadata (player, metadata, value) ".. + "VALUES ("..escape_str(name)..",'player:revoked_privs',".. + escape_str(tostring(val))..");" + print(cmd) + end + + for name, privs in pairs(granted_privs) do + local val = "return {" + for priv,granted in pairs(privs) do + if granted then + val = val..'"'..tostring(priv)..'",' + end + end + val = val.."}" + local cmd = "INSERT INTO player_metadata (player, metadata, value) ".. + "VALUES ("..escape_str(name)..",'player:granted_privs',".. + escape_str(tostring(val))..");" + print(cmd) + end + + for name, change in pairs(can_change) do + local cmd = "INSERT INTO player_metadata (player, metadata, value) ".. + "VALUES ("..escape_str(name)..",'player:has_second_chance','".. + tostring(change).."');" + print(cmd) + end + + for name, skin in pairs(skins) do + local cmd = "INSERT INTO player_metadata (player, metadata, value) ".. + "VALUES ("..escape_str(name)..",'player:skin','".. + tostring(skin).."');" + print(cmd) + end + + for name, player in pairs(players) do + local race = player[1] + local gender = player[2] + local cmd = "INSERT INTO player_metadata (player, metadata, value) ".. + "VALUES ("..escape_str(name)..",'player:gender','".. + tostring(gender).."');" + print(cmd) + local cmd = "INSERT INTO player_metadata (player, metadata, value) ".. + "VALUES ("..escape_str(name)..",'player:race','".. + tostring(race).."');" + print(cmd) + end +end + +local races = read_races(arg[1]) +recode_races(races)