From 1b55d26eea116913c92b5cb2b0228914b9013758 Mon Sep 17 00:00:00 2001 From: Tai Kedzierski Date: Mon, 28 Jan 2019 13:32:32 +0000 Subject: [PATCH 01/11] Untested towns implementation * Player can allow all players to visit them * Player can ban/unban visitors, without changing their guest status (and thus bypassing the levvy) * Exiled players get punted back to their spawn on approaching exile location --- README.md | 44 ++++++++---- init.lua | 12 +--- lua/commands.lua | 5 +- lua/guestlists.lua | 162 +++++++++++++++++++++++++++++++++--------- lua/pregeneration.lua | 8 ++- settingtypes.txt | 2 + 6 files changed, 173 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index 057c81f..042d94b 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ Causes players to receive a spawn point anywhere on the map. Players will likely ## Features * Player is assigned randomized spawnpoint on joining + * New players will not spawn into protected areas * Player will respawn at their spawnpoint if they die. - * If `beds` spawning is active, then beds can be used to reset the players' spawn point. -* Players will not spawn in spaces that are protected + * If `beds` spawning is active, then beds can be used to set players' re-spawn point (they still go to their main spawnpoint on invoking `/spawn`). * Commands * Players can return to their spawn point with the `/spawn` command if they have `spawn` privilege. * Players can invite other players to join their spawn - see "Spawn invites" below @@ -24,18 +24,28 @@ Randomized spawning typically causes players to spawn far from eachother. If pla The player issuing the invite (host) must typically pay a levvy when adding another player. * `/spawn add ` - allow another player to visit your spawn directly, or lift their exile -* `/spawn kick ` - revoke rights to visit you, and if they are in your space, returns them to their own spawn +* `/spawn kick ` - revoke rights to visit you + * if the exiled player gets close to your spawn, they are kicked back to their own spawn * `/spawn visit ` - visit a player's spawn * `/spawn guests` - see who you have added to your spawn * `/spawn hosts` - see who has added you to their spawn +### Town hosting + +You can host a town from your spawn if you wish. Hosting a town means that any player who connects to the server will be able to visit your spawn. You can still `/spawn kick ` individually in this mode. If you switch off town hosting, only allowed guests in your normal guestlist can visit. + +There is no levvy on hosting a town. + +* `/spawn town { open | close }` - switch town hosting on or off. +* `/spawn town { ban | unban } ` - ban or unban a player from a town + ## Settings Note that the spawn generation is performed in the background on a timer, allowing storing a collection of random spawn points to be generated ahead of time. *Generic settings used* -* `name` - on servers, sets the name of the admin, players can spawn in areas protected by the admin. +* `name` - used for knowing the server admin's name * `water_level` - Spawns are always set above water level, default `1` * `static_spawnpoint` - main position the player will start at, default `{0,0,0}` * `enable_bed_respawn` - from `beds` mod - if active, then respawning will happen at beds, instead of randomized spawnpoint @@ -43,36 +53,40 @@ Note that the spawn generation is performed in the background on a timer, allowi *rspawn-specific settings* * Settings related to spawn generation - * `rspawn.max_pregen` - maximum number of spawn points to pre-generate, default `5` - * `rspawn.search_radius` - lateral radius around random point, within which a spawn poitn will be sought, default `32` - * `rspawn.gen_frequency` - how frequently (in seconds) to generate a new spawn point, default `30` + * `rspawn.max_pregen` - maximum number of spawn points to pre-generate, default `20` + * `rspawn.search_radius` - lateral radius around random point, within which a spawn point will be sought, default `32` + * `rspawn.gen_frequency` - how frequently (in seconds) to generate a new spawn point, default `30`, increase this on slower servers * `rspawn.spawn_anywhere` - whether to spawn anywhere in the world at sea level * default `true` * if `false`, will randomize around the static spawn point * `rspawn.cooldown_time` - how many seconds between two uses of `/newspawn`, per player -* `rspawn.levvy_name` - name of the block to use as levvy charge on the player issuing an invitation, default `default:cobble` -* `rspawn.levvy_qtty` - number of blocks to levvy from the player who issued the invitation, default `10` * `rspawn.kick_on_fail` - whether to kick the player if a randomized spawn cannot be set, default `false` * `rspawn.spawn_block` - place this custom block under the user's spawn point +* Guestlist and town related settings + * `rspawn.levvy_name` - name of the block to use as levvy charge on the player issuing an invitation, default `default:cobble` + * `rspawn.levvy_qtty` - number of blocks to levvy from the player who issued the invitation, default `10` + * `rspawn.kick_period` - how frequently to check if exiled players are too near their locus of exile, default `1` (seconds) + * `rspawn.exile_distance` - distance from exile locus at which player gets bounced back to their own spawn, default `64` (nodes) * `rspawn.debug` - whether to print debugging messages, default `false` -* Bounds limiting - you can limit the random spawning to a given area if you wish: +* Bounds limiting - you can limit the random spawning search area to a given subsection of the global map if you wish: * `rspawn.min_x`, `rspawn.max_x`, `rspawn.min_z`, `rspawn.max_z` as expected ## Troubleshooting +As admin, you will receive notifications of inability to generate spawns when players join without being set a spawn. + You can turn on `rspawn.debug = true` to see debug in logs. -If the generation log shows `0 air nodes found within ` on more than 2-3 consecutive tries, you may want to check the max number of forceloaded blocks configured - see `max_forceloaded_blocks`. +Spawn generation uses a temporary forceload to read the blocks in the area ; it then releases the forceload after operating, so should not depend on the `max_forceloaded_blocks` setting. -This should be at least `2*(rspawn.search_radius^3) / (16^3)`, so with the default `rspawn.search_radius = 32`, you should have at least `max_forceloaded_blocks = 8` +If the generation log shows `0 air nodes found within ` on more than 2-3 consecutive tries, you may want to check that another mod is not forceloading blocks and then not subsequently clearing them. -Also check that another mod is not forceloading blocks and not clearing them. - -You may also find some mods (rarely) do permanent forceloads. In your world folder `~/.minetest/worlds/` there should eb a `force_loaded.txt` - see that its contents are simply `return {}`; if there is data in the table, then something else is forceloading blocks. +You may also find some mods do permanent forceloads by design (though this should be rare). In your world folder `~/.minetest/worlds/` there should eb a `force_loaded.txt` - see that its contents are simply `return {}`; if there is data in the table, then something else is forceloading blocks with permanence. Resolutions in order of best to worst: * identify the mod and have it clear them properly (ideal) + * on UNIX/Linux you should be able to run `grep -rl forceload ~/.minetest/mods/` to see all mod files where forceloading is being done * increase the max number of forceloaded blocks * (not great - you will effectively be simply mitigating a forceloaded-blocks-related memory leak) * Stop minetest, delete the `force_loaded.txt` file, and start it again diff --git a/init.lua b/init.lua index 38fd4b6..4c92f37 100644 --- a/init.lua +++ b/init.lua @@ -3,14 +3,6 @@ rspawn.playerspawns = {} local mpath = minetest.get_modpath("rspawn") -local function notnil_or(d, v) - if v == nil then - return d - else - return v - end -end - -- Water level, plus one to ensure we are above the sea. local water_level = tonumber(minetest.settings:get("water_level", "0") ) local radial_step = 16 @@ -24,8 +16,8 @@ rspawn.bedspawn = minetest.setting_getbool("enable_bed_respawn") ~= false -- fro -- rSpawn specific settings rspawn.debug_on = minetest.settings:get_bool("rspawn.debug") -rspawn.spawnanywhere = notnil_or(true, minetest.settings:get_bool("rspawn.spawn_anywhere") ) -rspawn.kick_on_fail = notnil_or(false, minetest.settings:get_bool("rspawn.kick_on_fail")) +rspawn.spawnanywhere = minetest.settings:get_bool("rspawn.spawn_anywhere") ~= false +rspawn.kick_on_fail = minetest.settings:get_bool("rspawn.kick_on_fail") == true rspawn.max_pregen_spawns = tonumber(minetest.settings:get("rspawn.max_pregen") or 5) rspawn.search_radius = tonumber(minetest.settings:get("rspawn.search_radius") or 32) rspawn.gen_frequency = tonumber(minetest.settings:get("rspawn.gen_frequency") or 30) diff --git a/lua/commands.lua b/lua/commands.lua index 0b138f5..bc523fc 100644 --- a/lua/commands.lua +++ b/lua/commands.lua @@ -7,7 +7,7 @@ local cooldown_time = tonumber(minetest.settings:get("rspawn.cooldown_time")) or minetest.register_privilege("spawn", "Can teleport to a spawn position and manage shared spawns.") minetest.register_privilege("setspawn", "Can manually set a spawn point.") minetest.register_privilege("newspawn", "Can get a new randomized spawn position.") -minetest.register_privilege("spawnadmin", "Can clean up timers and set new spawns for players.") +minetest.register_privilege("spawnadmin", "Can set new spawns for players.") -- Support functions @@ -35,7 +35,7 @@ end minetest.register_chatcommand("spawn", { description = "Teleport to your spawn, or manage guests in your spawn.", - params = "[ add | visit | kick | guests | hosts ]", + params = "[ add | visit | kick | guests | hosts | town { open | close | ban | unban } ]", privs = "spawn", func = function(playername, args) local target = rspawn.playerspawns[playername] @@ -57,6 +57,7 @@ minetest.register_chatcommand("spawn", { ["add"] = function(commandername,targetname) rspawn.guestlists:addplayer(commandername,targetname) end, ["visit"] = function(commandername,targetname) rspawn.guestlists:visitplayer(targetname, commandername) end, ["kick"] = function(commandername,targetname) rspawn.guestlists:exileplayer(commandername, targetname) end, + ["town"] = function(commandername,mode) rspawn.guestlists:townset(commandername, mode) end, }) do if args[1] == command then diff --git a/lua/guestlists.lua b/lua/guestlists.lua index 99b339a..ba27c5c 100644 --- a/lua/guestlists.lua +++ b/lua/guestlists.lua @@ -1,13 +1,20 @@ +-- API holder object rspawn.guestlists = {} --- invitations[guest] = host -rspawn.invitations = {} +local kick_step = 0 -local invite_charge = {} +local kick_period = tonumber(minetest.settings:get("rspawn.kick_period")) or 1 +local exile_distance = tonumber(minetest.settings:get("rspawn.exile_distance")) or 64 -levvy_name = minetest.settings:get("rspawn.levvy_name") or "default:cobble" -levvy_qtty = tonumber(minetest.settings:get("rspawn.levvy_qtty")) or 10 -levvy_nicename = "cobblestone" +local GUEST_BAN = 0 +local GUEST_ALLOW = 1 + +-- Levvy helpers +-- FIXME Minetest API might actually be able to do this cross-stacks with a single call at inventory level. + +local levvy_name = minetest.settings:get("rspawn.levvy_name") or "default:cobble" +local levvy_qtty = tonumber(minetest.settings:get("rspawn.levvy_qtty")) or 10 +local levvy_nicename = "cobblestone" minetest.after(0,function() if minetest.registered_items[levvy_name] then @@ -19,11 +26,6 @@ minetest.after(0,function() end end) -local function canvisit(hostname, guestname) - local glist = rspawn.playerspawns["guest lists"][hostname] or {} - return glist[guestname] == 1 -end - local function find_levvy(player) -- return itemstack index, and stack itself, with qtty removed -- or none if not found/not enough found @@ -101,17 +103,33 @@ function rspawn:consume_levvy(player) return false end +-- Visitation rights check + +local function canvisit(hostname, guestname) + local host_glist = rspawn.playerspawns["guest lists"][hostname] or {} + local global_glist = rspawn.playerspawns["town lists"] or {} + + return ( + -- Host has specific guest entry and guest is not banned + (host_glist[guestname] and (host_glist[guestname] == GUEST_BAN or host_glist[guestname] == GUEST_TOWNBAN)) or + -- Host is global host + (not host_glist[guestname] and global_glist[hostname]) + ) +end + +-- Operational functions (to be invoked by /command) + function rspawn.guestlists:addplayer(hostname, guestname) local guestlist = rspawn.playerspawns["guest lists"][hostname] or {} if guestlist[guestname] ~= nil then - if guestlist[guestname] == 0 then + if guestlist[guestname] == GUEST_BAN then minetest.chat_send_player(guestname, hostname.." let you back into their spawn.") end - guestlist[guestname] = 1 + guestlist[guestname] = GUEST_ALLOW elseif rspawn:consume_levvy(minetest.get_player_by_name(hostname) ) then -- Automatically notifies host if they don't have enough - guestlist[guestname] = 1 + guestlist[guestname] = GUEST_ALLOW minetest.chat_send_player(guestname, hostname.." added you to their spawn! You can now visit them with /spawn visit "..hostname) else return @@ -125,8 +143,8 @@ end function rspawn.guestlists:exileplayer(hostname, guestname) local guestlist = rspawn.playerspawns["guest lists"][hostname] or {} - if guestlist[guestname] == 1 then - guestlist[guestname] = 0 + if guestlist[guestname] == GUEST_ALLOW then + guestlist[guestname] = GUEST_BAN rspawn.playerspawns["guest lists"][hostname] = guestlist else @@ -135,31 +153,28 @@ function rspawn.guestlists:exileplayer(hostname, guestname) end minetest.chat_send_player(guestname, hostname.." banishes you!") - rspawn.guestlists:kick(hostname, guestname) rspawn:spawnsave() end -function rspawn.guestlists:kick(hostname, guestname) - local guest = minetest.get_player_by_name(guestname) - local guestpos = guest:getpos() - local hostspawnpos = rspawn.playerspawns[hostname] - local guestspawnpos = rspawn.playerspawns[guestname] - - if vector.distance(guestpos, hostspawnpos) < 32 then - guest:setpos(guestspawnpos) - end -end - function rspawn.guestlists:listguests(hostname) local guests = "" local guestlist = rspawn.playerspawns["guest lists"][hostname] or {} + local global_hosts = rspawn.playerspawns["town lists"] or {} + if global_hosts[hostname] then + guests = ", You are an active town host." + end + for guestname,status in pairs(guestlist) do - if status == 1 then status = "" else status = " (exiled)" end + if status == GUEST_ALLOW then status = "" else status = " (exiled)" end guests = guests..", "..guestname..status end + if guests == "" then + guests = ", No guests, not hosting a town." + end + minetest.chat_send_player(hostname, guests:sub(3)) end @@ -169,13 +184,26 @@ function rspawn.guestlists:listhosts(guestname) for hostname,hostguestlist in pairs(rspawn.playerspawns["guest lists"]) do for gname,status in pairs(hostguestlist) do if guestname == gname then - if status == 1 then status = "" else status = " (exiled)" end - - hosts = hosts..", "..hostname..status + if status == GUEST_ALLOWED then + hosts = hosts..", "..hostname + end end end end + local global_hostlist = rspawn.playerspawns["town lists"] + for _,hostname in ipairs(global_hostlist) do + if global_hostlist[hostname]["town status"] == "on" and + global_hostlist[hostname][guestname] ~= GUEST_BAN + then + hosts = hosts..", "..hostname + end + end + + if hosts == "" then + hosts = ", (no visitable hosts)" + end + minetest.chat_send_player(guestname, hosts:sub(3)) end @@ -194,3 +222,73 @@ function rspawn.guestlists:visitplayer(hostname, guestname) minetest.chat_send_player(guestname, "Could not visit "..hostname) end end + +function rspawn.guestlists:townset(hostname, params) + params = params:split(" ") + + local mode = params[1] + local guestname = params[2] + local global_glist = rspawn.playerspawns["town lists"] or {} + local town_banlist = global_glist[hostname] or {} + + if mode == "open" then + town_banlist["town status"] = "on" + minetest.chat_send_all(hostname.." is opened as a town!") + + elseif mode == "close" then + town_banlist["town status"] = "off" + minetest.chat_send_all(hostname.." is not currently a town - only guests may directly visit.") + + elseif mode == "ban" and guestname then + town_banlist[guestname] = GUEST_BAN + minetest.chat_send_all(guestname.." is exiled from "..hostname.."'s town.") + + elseif mode == "unban" and guestname then + town_banlist[guestname] = nil + minetest.chat_send_all(guestname.." is no longer exiled from "..hostname.."'s town.") + + else + minetest.chat_send_player(hostname, "Unknown parameterless town operation: "..mode) + return + end + + global_glist[hostname] = town_banlist + rspawn.playerspawns["town lists"] = global_glist + + rspawn:spawnsave() +end + +-- Exile check +minetest.register_globalstep(function(dtime) + if kick_step < kick_period then + kick_step = kick_step + dtime + return + else + kick_step = 0 + end + + for _,guest in ipairs(minetest.get_connected_players()) do + local guestpos = guest:getpos() + local guestname = guest:get_player_name() + + for _,player_list_name in ipairs({"guest lists", "town lists"}) do + for hostname,host_guestlist in pairs(rspawn.playesrpawns[player_list_name]) do + + if host_guestlist[guestname] == GUEST_BAN then + local vdist = vector.distance(guestpos, rspawn.playerspawns[hostname]) + + if vdist < exile_distance then + guest:setpos(rspawn.playerspawns[guestname]) + minetest.chat_send_player(guestname, "You got too close to "..hostname.."'s turf.") + return + + elseif vdist < exile_distance*1.5 then + minetest.chat_send_player(guestname, "You are getting too close to "..hostname.."'s turf.") + return + end + end + end + end + + end +end) diff --git a/lua/pregeneration.lua b/lua/pregeneration.lua index 141738f..1b568b5 100644 --- a/lua/pregeneration.lua +++ b/lua/pregeneration.lua @@ -64,8 +64,14 @@ function rspawn:get_next_spawn() if len_pgen() > 0 then nspawn = get_pgen(len_pgen() ) - rspawn:debug("Returning pregenerated spawn",nspawn) set_pgen(len_pgen(), nil) + + -- Someone might have claimed the area since. + if minetest.is_protected(nspawn, "") then + return rspawn:get_next_spawn() + else + rspawn:debug("Returning pregenerated spawn",nspawn) + end end return nspawn diff --git a/settingtypes.txt b/settingtypes.txt index df898c8..fc212d5 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -7,6 +7,8 @@ rspawn.gen_frequency (Spawnpoint generation frequency [seconds]) string 60 rspawn.spawn_block (Node to place under new spawn point) string rspawn.levvy_name (Levvy itemstring) string "default:cobble" rspawn.levvy_qtty (Levvy quantity) string 10 +rspawn.kick_period (Exile kick check period) string 1 +rspawn.exile_distance (Exile distance) string 64 rspawn.cooldown_time (Cooldown between /newspawn uses) string 300 rspawn.min_x (Westmost bounds) string -31000 rspawn.max_x (Eastmost bounds) string 31000 From 13044aa68a57a0b59f71d111d9427baf53a8fbf1 Mon Sep 17 00:00:00 2001 From: Tai Kedzierski Date: Mon, 28 Jan 2019 14:15:37 +0000 Subject: [PATCH 02/11] allow explicit guests to kick from host --- README.md | 7 ++++--- lua/commands.lua | 5 +++-- lua/guestlists.lua | 17 ++++++++++++++--- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 042d94b..034c7a4 100644 --- a/README.md +++ b/README.md @@ -23,12 +23,13 @@ Randomized spawning typically causes players to spawn far from eachother. If pla The player issuing the invite (host) must typically pay a levvy when adding another player. -* `/spawn add ` - allow another player to visit your spawn directly, or lift their exile -* `/spawn kick ` - revoke rights to visit you +* `/spawn add ` - allow another player to visit your spawn directly (levvy must be paid), or lift their exile (no levvy to pay) +* `/spawn exile ` - revoke rights to visit you * if the exiled player gets close to your spawn, they are kicked back to their own spawn +* `/spawn kick ` - if you are a guest of `hostplayer`, you can exile `targetplayer` on their behalf * `/spawn visit ` - visit a player's spawn * `/spawn guests` - see who you have added to your spawn -* `/spawn hosts` - see who has added you to their spawn +* `/spawn hosts` - see whose spawns you may visit ### Town hosting diff --git a/lua/commands.lua b/lua/commands.lua index bc523fc..ba8cfb7 100644 --- a/lua/commands.lua +++ b/lua/commands.lua @@ -56,7 +56,8 @@ minetest.register_chatcommand("spawn", { ["hosts"] = function() rspawn.guestlists:listhosts(playername) end, ["add"] = function(commandername,targetname) rspawn.guestlists:addplayer(commandername,targetname) end, ["visit"] = function(commandername,targetname) rspawn.guestlists:visitplayer(targetname, commandername) end, - ["kick"] = function(commandername,targetname) rspawn.guestlists:exileplayer(commandername, targetname) end, + ["exile"] = function(commandername,targetname) rspawn.guestlists:exileplayer(commandername, targetname) end, + ["kick"] = function(commandername, params) rspawn.guestlists:kickplayer(commandername, params) end, ["town"] = function(commandername,mode) rspawn.guestlists:townset(commandername, mode) end, }) do @@ -73,7 +74,7 @@ minetest.register_chatcommand("spawn", { end end - minetest.chat_send_player(playername, "Please check '/help spawn'") + minetest.chat_send_player(playername, "Bad command. Please check '/help spawn'") end }) diff --git a/lua/guestlists.lua b/lua/guestlists.lua index ba27c5c..1fc149f 100644 --- a/lua/guestlists.lua +++ b/lua/guestlists.lua @@ -140,7 +140,7 @@ function rspawn.guestlists:addplayer(hostname, guestname) rspawn:spawnsave() end -function rspawn.guestlists:exileplayer(hostname, guestname) +function rspawn.guestlists:exileplayer(hostname, guestname, callername) local guestlist = rspawn.playerspawns["guest lists"][hostname] or {} if guestlist[guestname] == GUEST_ALLOW then @@ -148,7 +148,7 @@ function rspawn.guestlists:exileplayer(hostname, guestname) rspawn.playerspawns["guest lists"][hostname] = guestlist else - minetest.chat_send_player(hostname, guestname.." is not in your accepted guests list.") + minetest.chat_send_player(callername or hostname, guestname.." is not in accepted guests list for "..hostname) return end @@ -156,6 +156,17 @@ function rspawn.guestlists:exileplayer(hostname, guestname) rspawn:spawnsave() end +function rspawn.guestlists:kickplayer(callername, params) + params = params:split(" ") + local hostname = params[2] + local target = params[1] + + -- Caller is an explicit non-exiled guest + if rspawn.playerspawns[hostname] and rspawn.playerspawns[hostname][callername] == GUEST_ALLOW then + rspawb.guestlists:exileplayer(hostname, guestname) + end +end + function rspawn.guestlists:listguests(hostname) local guests = "" local guestlist = rspawn.playerspawns["guest lists"][hostname] or {} @@ -196,7 +207,7 @@ function rspawn.guestlists:listhosts(guestname) if global_hostlist[hostname]["town status"] == "on" and global_hostlist[hostname][guestname] ~= GUEST_BAN then - hosts = hosts..", "..hostname + hosts = hosts..", "..hostname.." (town)" end end From 84edde3d56146149a209d25fb6a689d25b71abb0 Mon Sep 17 00:00:00 2001 From: Tai Kedzierski Date: Mon, 28 Jan 2019 19:56:33 +0000 Subject: [PATCH 03/11] working towns --- README.md | 2 +- lua/commands.lua | 6 +++--- lua/guestlists.lua | 45 +++++++++++++++++++++++++++++---------------- 3 files changed, 33 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 034c7a4..9cdda2a 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ Note that the spawn generation is performed in the background on a timer, allowi * Guestlist and town related settings * `rspawn.levvy_name` - name of the block to use as levvy charge on the player issuing an invitation, default `default:cobble` * `rspawn.levvy_qtty` - number of blocks to levvy from the player who issued the invitation, default `10` - * `rspawn.kick_period` - how frequently to check if exiled players are too near their locus of exile, default `1` (seconds) + * `rspawn.kick_period` - how frequently to check if exiled players are too near their locus of exile, default `3` (seconds) * `rspawn.exile_distance` - distance from exile locus at which player gets bounced back to their own spawn, default `64` (nodes) * `rspawn.debug` - whether to print debugging messages, default `false` * Bounds limiting - you can limit the random spawning search area to a given subsection of the global map if you wish: diff --git a/lua/commands.lua b/lua/commands.lua index ba8cfb7..2a1505e 100644 --- a/lua/commands.lua +++ b/lua/commands.lua @@ -39,7 +39,7 @@ minetest.register_chatcommand("spawn", { privs = "spawn", func = function(playername, args) local target = rspawn.playerspawns[playername] - local args = args:split(" ") + local args = args:split(" ", false, 1) if #args == 0 then if target then @@ -50,7 +50,7 @@ minetest.register_chatcommand("spawn", { minetest.chat_send_player(playername, "You have no spawn position!") return end - elseif #args < 3 then + elseif #args < 4 then for command,action in pairs({ ["guests"] = function() rspawn.guestlists:listguests(playername) end, ["hosts"] = function() rspawn.guestlists:listhosts(playername) end, @@ -67,7 +67,7 @@ minetest.register_chatcommand("spawn", { return elseif #args == 1 then - action() + action(playername) return end end diff --git a/lua/guestlists.lua b/lua/guestlists.lua index 1fc149f..9b8e05d 100644 --- a/lua/guestlists.lua +++ b/lua/guestlists.lua @@ -3,7 +3,7 @@ rspawn.guestlists = {} local kick_step = 0 -local kick_period = tonumber(minetest.settings:get("rspawn.kick_period")) or 1 +local kick_period = tonumber(minetest.settings:get("rspawn.kick_period")) or 3 local exile_distance = tonumber(minetest.settings:get("rspawn.exile_distance")) or 64 local GUEST_BAN = 0 @@ -107,13 +107,21 @@ end local function canvisit(hostname, guestname) local host_glist = rspawn.playerspawns["guest lists"][hostname] or {} - local global_glist = rspawn.playerspawns["town lists"] or {} + local town_lists = rspawn.playerspawns["town lists"] or {} return ( - -- Host has specific guest entry and guest is not banned - (host_glist[guestname] and (host_glist[guestname] == GUEST_BAN or host_glist[guestname] == GUEST_TOWNBAN)) or - -- Host is global host - (not host_glist[guestname] and global_glist[hostname]) + -- Guest not explicitly banned + ( + not host_glist[guestname] or + host_glist[guestname] ~= GUEST_BAN + ) + and + -- Host is open town, and guest is not banned + ( + town_lists[hostname] and + town_lists[hostname]["town status"] == "on" and + town_lists[hostname][guestname] ~= GUEST_BAN + ) ) end @@ -202,10 +210,10 @@ function rspawn.guestlists:listhosts(guestname) end end - local global_hostlist = rspawn.playerspawns["town lists"] - for _,hostname in ipairs(global_hostlist) do - if global_hostlist[hostname]["town status"] == "on" and - global_hostlist[hostname][guestname] ~= GUEST_BAN + local global_hostlist = rspawn.playerspawns["town lists"] or {} + for hostname,host_banlist in pairs(global_hostlist) do + if host_banlist["town status"] == "on" and + host_banlist[guestname] ~= GUEST_BAN then hosts = hosts..", "..hostname.." (town)" end @@ -235,12 +243,13 @@ function rspawn.guestlists:visitplayer(hostname, guestname) end function rspawn.guestlists:townset(hostname, params) + params = params or "" params = params:split(" ") local mode = params[1] local guestname = params[2] - local global_glist = rspawn.playerspawns["town lists"] or {} - local town_banlist = global_glist[hostname] or {} + local town_lists = rspawn.playerspawns["town lists"] or {} + local town_banlist = town_lists[hostname] or {} if mode == "open" then town_banlist["town status"] = "on" @@ -250,6 +259,10 @@ function rspawn.guestlists:townset(hostname, params) town_banlist["town status"] = "off" minetest.chat_send_all(hostname.." is not currently a town - only guests may directly visit.") + elseif mode == "status" then + minetest.chat_send_player(hostname, "Town mode is: "..town_banlist["town status"]) + return + elseif mode == "ban" and guestname then town_banlist[guestname] = GUEST_BAN minetest.chat_send_all(guestname.." is exiled from "..hostname.."'s town.") @@ -259,12 +272,12 @@ function rspawn.guestlists:townset(hostname, params) minetest.chat_send_all(guestname.." is no longer exiled from "..hostname.."'s town.") else - minetest.chat_send_player(hostname, "Unknown parameterless town operation: "..mode) + minetest.chat_send_player(hostname, "Unknown parameterless town operation: "..tostring(mode) ) return end - global_glist[hostname] = town_banlist - rspawn.playerspawns["town lists"] = global_glist + town_lists[hostname] = town_banlist + rspawn.playerspawns["town lists"] = town_lists rspawn:spawnsave() end @@ -283,7 +296,7 @@ minetest.register_globalstep(function(dtime) local guestname = guest:get_player_name() for _,player_list_name in ipairs({"guest lists", "town lists"}) do - for hostname,host_guestlist in pairs(rspawn.playesrpawns[player_list_name]) do + for hostname,host_guestlist in pairs(rspawn.playerspawns[player_list_name] or {}) do if host_guestlist[guestname] == GUEST_BAN then local vdist = vector.distance(guestpos, rspawn.playerspawns[hostname]) From 17a3f379939f2060aa1ec2cbb51558d74f6f394e Mon Sep 17 00:00:00 2001 From: Tai Kedzierski Date: Tue, 29 Jan 2019 22:15:01 +0000 Subject: [PATCH 04/11] allow admins and moderators not to be banned from towns, prevent self ban --- lua/guestlists.lua | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/lua/guestlists.lua b/lua/guestlists.lua index 9b8e05d..fe2ed8a 100644 --- a/lua/guestlists.lua +++ b/lua/guestlists.lua @@ -149,6 +149,10 @@ function rspawn.guestlists:addplayer(hostname, guestname) end function rspawn.guestlists:exileplayer(hostname, guestname, callername) + if hostname == guestname then + minetest.chat_send_player(hostname, "Cannot ban yourself!") + return + end local guestlist = rspawn.playerspawns["guest lists"][hostname] or {} if guestlist[guestname] == GUEST_ALLOW then @@ -253,17 +257,17 @@ function rspawn.guestlists:townset(hostname, params) if mode == "open" then town_banlist["town status"] = "on" - minetest.chat_send_all(hostname.." is opened as a town!") + minetest.chat_send_all(hostname.." is opens access to all!") elseif mode == "close" then town_banlist["town status"] = "off" - minetest.chat_send_all(hostname.." is not currently a town - only guests may directly visit.") + minetest.chat_send_all(hostname.." closes town access - only guests may directly visit.") elseif mode == "status" then minetest.chat_send_player(hostname, "Town mode is: "..town_banlist["town status"]) return - elseif mode == "ban" and guestname then + elseif mode == "ban" and guestname and guestname ~= hostname then town_banlist[guestname] = GUEST_BAN minetest.chat_send_all(guestname.." is exiled from "..hostname.."'s town.") @@ -292,23 +296,27 @@ minetest.register_globalstep(function(dtime) end for _,guest in ipairs(minetest.get_connected_players()) do - local guestpos = guest:getpos() local guestname = guest:get_player_name() + local playerprivs = minetest.get_player_privs(guestname) - for _,player_list_name in ipairs({"guest lists", "town lists"}) do - for hostname,host_guestlist in pairs(rspawn.playerspawns[player_list_name] or {}) do + if not (playerprivs.basic_privs or playerprivs.server) then + local guestpos = guest:getpos() - if host_guestlist[guestname] == GUEST_BAN then - local vdist = vector.distance(guestpos, rspawn.playerspawns[hostname]) + for _,player_list_name in ipairs({"guest lists", "town lists"}) do + for hostname,host_guestlist in pairs(rspawn.playerspawns[player_list_name] or {}) do - if vdist < exile_distance then - guest:setpos(rspawn.playerspawns[guestname]) - minetest.chat_send_player(guestname, "You got too close to "..hostname.."'s turf.") - return + if host_guestlist[guestname] == GUEST_BAN then + local vdist = vector.distance(guestpos, rspawn.playerspawns[hostname]) - elseif vdist < exile_distance*1.5 then - minetest.chat_send_player(guestname, "You are getting too close to "..hostname.."'s turf.") - return + if vdist < exile_distance then + guest:setpos(rspawn.playerspawns[guestname]) + minetest.chat_send_player(guestname, "You got too close to "..hostname.."'s turf.") + return + + elseif vdist < exile_distance*1.5 then + minetest.chat_send_player(guestname, "You are getting too close to "..hostname.."'s turf.") + return + end end end end From 330f06e7441f683ffdad2b3fe81b7af23a86a3dd Mon Sep 17 00:00:00 2001 From: Tai Kedzierski Date: Tue, 29 Jan 2019 22:52:55 +0000 Subject: [PATCH 05/11] working towns --- README.md | 5 ++-- lua/commands.lua | 1 - lua/guestlists.lua | 62 ++++++++++++++++++++++++++++++---------------- 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 9cdda2a..275a791 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,10 @@ Randomized spawning typically causes players to spawn far from eachother. If pla The player issuing the invite (host) must typically pay a levvy when adding another player. * `/spawn add ` - allow another player to visit your spawn directly (levvy must be paid), or lift their exile (no levvy to pay) -* `/spawn exile ` - revoke rights to visit you +* `/spawn kick []` + * revoke rights to visit you * if the exiled player gets close to your spawn, they are kicked back to their own spawn -* `/spawn kick ` - if you are a guest of `hostplayer`, you can exile `targetplayer` on their behalf + * if you are an explicit guest of `hostplayer` (not just a town visitor), you can exile `targetplayer` on their behalf * `/spawn visit ` - visit a player's spawn * `/spawn guests` - see who you have added to your spawn * `/spawn hosts` - see whose spawns you may visit diff --git a/lua/commands.lua b/lua/commands.lua index 2a1505e..6fa7528 100644 --- a/lua/commands.lua +++ b/lua/commands.lua @@ -56,7 +56,6 @@ minetest.register_chatcommand("spawn", { ["hosts"] = function() rspawn.guestlists:listhosts(playername) end, ["add"] = function(commandername,targetname) rspawn.guestlists:addplayer(commandername,targetname) end, ["visit"] = function(commandername,targetname) rspawn.guestlists:visitplayer(targetname, commandername) end, - ["exile"] = function(commandername,targetname) rspawn.guestlists:exileplayer(commandername, targetname) end, ["kick"] = function(commandername, params) rspawn.guestlists:kickplayer(commandername, params) end, ["town"] = function(commandername,mode) rspawn.guestlists:townset(commandername, mode) end, }) do diff --git a/lua/guestlists.lua b/lua/guestlists.lua index fe2ed8a..da9579e 100644 --- a/lua/guestlists.lua +++ b/lua/guestlists.lua @@ -103,26 +103,29 @@ function rspawn:consume_levvy(player) return false end +local function d(stuff) + minetest.debug(dump(stuff)) +end + -- Visitation rights check local function canvisit(hostname, guestname) local host_glist = rspawn.playerspawns["guest lists"][hostname] or {} local town_lists = rspawn.playerspawns["town lists"] or {} - return ( - -- Guest not explicitly banned - ( - not host_glist[guestname] or - host_glist[guestname] ~= GUEST_BAN - ) - and - -- Host is open town, and guest is not banned - ( - town_lists[hostname] and - town_lists[hostname]["town status"] == "on" and - town_lists[hostname][guestname] ~= GUEST_BAN - ) - ) + local explicitly_banned = host_glist[guestname] == GUEST_BAN + + local explicitly_banned_from_town = town_lists[hostname] and + town_lists[hostname][guestname] == GUEST_BAN + + local open_town = town_lists[hostname] and town_lists[hostname]["town status"] == "on" + + if explicitly_banned or explicitly_banned_from_town then + return false + elseif open_town then + return true + end + return false end -- Operational functions (to be invoked by /command) @@ -146,12 +149,13 @@ function rspawn.guestlists:addplayer(hostname, guestname) minetest.chat_send_player(hostname, guestname.." is allowed to visit your spawn.") rspawn.playerspawns["guest lists"][hostname] = guestlist rspawn:spawnsave() + minetest.log("action", "rspawn - "..hostname.." adds "..guestname.." to their spawn") end -function rspawn.guestlists:exileplayer(hostname, guestname, callername) +function rspawn.guestlists:exileplayer(hostname, guestname, notifyname) if hostname == guestname then minetest.chat_send_player(hostname, "Cannot ban yourself!") - return + return false end local guestlist = rspawn.playerspawns["guest lists"][hostname] or {} @@ -160,22 +164,36 @@ function rspawn.guestlists:exileplayer(hostname, guestname, callername) rspawn.playerspawns["guest lists"][hostname] = guestlist else - minetest.chat_send_player(callername or hostname, guestname.." is not in accepted guests list for "..hostname) - return + minetest.chat_send_player(notifyname or hostname, guestname.." is not in accepted guests list for "..hostname) + return false end - minetest.chat_send_player(guestname, hostname.." banishes you!") + minetest.chat_send_player(guestname, "You may no longer visit "..hostname) + minetest.log("action", "rspawn - "..hostname.." exiles "..guestname) rspawn:spawnsave() + return true end function rspawn.guestlists:kickplayer(callername, params) params = params:split(" ") + local guestname = params[1] local hostname = params[2] - local target = params[1] -- Caller is an explicit non-exiled guest - if rspawn.playerspawns[hostname] and rspawn.playerspawns[hostname][callername] == GUEST_ALLOW then - rspawb.guestlists:exileplayer(hostname, guestname) + if hostname then + if rspawn.playerspawns["guest lists"][hostname] and rspawn.playerspawns["guest lists"][hostname][callername] == GUEST_ALLOW then + if rspawn.guestlists:exileplayer(hostname, guestname, callername) then + minetest.chat_send_player(callername, "Evicted "..guestname.." from "..hostname.."'s spawn") + minetest.log("action", "rspawn - "..callername.." evicts "..guestname.." on behalf of "..hostname) + end + else + minetest.chat_send_player(callername, "You are not permitted to act on behalf of "..hostname) + end + else + if rspawn.guestlists:exileplayer(callername, guestname) then + minetest.chat_send_player(callername, "Evicted "..guestname.." from "..callername.."'s spawn") + minetest.log("action", "rspawn - "..callername.." evicts "..guestname) + end end end From 7543d6bbc7bf8806193ac97c81f04ada3f69ac98 Mon Sep 17 00:00:00 2001 From: Tai Kedzierski Date: Tue, 29 Jan 2019 23:16:53 +0000 Subject: [PATCH 06/11] delegated ban should be on towns, not on spawns --- README.md | 6 ++--- lua/commands.lua | 2 +- lua/guestlists.lua | 67 +++++++++++++++++++++++++++++++++------------- 3 files changed, 53 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 275a791..07f5c6b 100644 --- a/README.md +++ b/README.md @@ -24,10 +24,9 @@ Randomized spawning typically causes players to spawn far from eachother. If pla The player issuing the invite (host) must typically pay a levvy when adding another player. * `/spawn add ` - allow another player to visit your spawn directly (levvy must be paid), or lift their exile (no levvy to pay) -* `/spawn kick []` +* `/spawn kick ` * revoke rights to visit you * if the exiled player gets close to your spawn, they are kicked back to their own spawn - * if you are an explicit guest of `hostplayer` (not just a town visitor), you can exile `targetplayer` on their behalf * `/spawn visit ` - visit a player's spawn * `/spawn guests` - see who you have added to your spawn * `/spawn hosts` - see whose spawns you may visit @@ -39,7 +38,8 @@ You can host a town from your spawn if you wish. Hosting a town means that any p There is no levvy on hosting a town. * `/spawn town { open | close }` - switch town hosting on or off. -* `/spawn town { ban | unban } ` - ban or unban a player from a town +* `/spawn town { ban | unban } []` - ban or unban a player from a town + * Town owners can use this, as well as unexiled guests of the town owner ## Settings diff --git a/lua/commands.lua b/lua/commands.lua index 6fa7528..4ada423 100644 --- a/lua/commands.lua +++ b/lua/commands.lua @@ -35,7 +35,7 @@ end minetest.register_chatcommand("spawn", { description = "Teleport to your spawn, or manage guests in your spawn.", - params = "[ add | visit | kick | guests | hosts | town { open | close | ban | unban } ]", + params = "[ add | visit | kick | guests | hosts | town { open | close | ban [] | unban [] } ]", privs = "spawn", func = function(playername, args) local target = rspawn.playerspawns[playername] diff --git a/lua/guestlists.lua b/lua/guestlists.lua index da9579e..849195e 100644 --- a/lua/guestlists.lua +++ b/lua/guestlists.lua @@ -152,7 +152,7 @@ function rspawn.guestlists:addplayer(hostname, guestname) minetest.log("action", "rspawn - "..hostname.." adds "..guestname.." to their spawn") end -function rspawn.guestlists:exileplayer(hostname, guestname, notifyname) +function rspawn.guestlists:exileplayer(hostname, guestname) if hostname == guestname then minetest.chat_send_player(hostname, "Cannot ban yourself!") return false @@ -164,7 +164,7 @@ function rspawn.guestlists:exileplayer(hostname, guestname, notifyname) rspawn.playerspawns["guest lists"][hostname] = guestlist else - minetest.chat_send_player(notifyname or hostname, guestname.." is not in accepted guests list for "..hostname) + minetest.chat_send_player(hostname, guestname.." is not in accepted guests list for "..hostname) return false end @@ -174,21 +174,10 @@ function rspawn.guestlists:exileplayer(hostname, guestname, notifyname) return true end -function rspawn.guestlists:kickplayer(callername, params) - params = params:split(" ") - local guestname = params[1] - local hostname = params[2] +function rspawn.guestlists:kickplayer(callername, guestname) -- Caller is an explicit non-exiled guest if hostname then - if rspawn.playerspawns["guest lists"][hostname] and rspawn.playerspawns["guest lists"][hostname][callername] == GUEST_ALLOW then - if rspawn.guestlists:exileplayer(hostname, guestname, callername) then - minetest.chat_send_player(callername, "Evicted "..guestname.." from "..hostname.."'s spawn") - minetest.log("action", "rspawn - "..callername.." evicts "..guestname.." on behalf of "..hostname) - end - else - minetest.chat_send_player(callername, "You are not permitted to act on behalf of "..hostname) - end else if rspawn.guestlists:exileplayer(callername, guestname) then minetest.chat_send_player(callername, "Evicted "..guestname.." from "..callername.."'s spawn") @@ -249,6 +238,8 @@ function rspawn.guestlists:listhosts(guestname) end function rspawn.guestlists:visitplayer(hostname, guestname) + if not (hostname and guestname) then return end + local guest = minetest.get_player_by_name(guestname) local hostpos = rspawn.playerspawns[hostname] @@ -264,7 +255,49 @@ function rspawn.guestlists:visitplayer(hostname, guestname) end end +local function townban(callername, guestname, hostname) + if not (callername and guestname) then return end + + hostname = hostname or callername + if hostname == callername or (rspawn.playerspawns["guest lists"][hostname] and rspawn.playerspawns["guest lists"][hostname][callername] == GUEST_ALLOW) then + if not rspawn.playerspawns["town lists"][hostname] then + minetest.chat_send_player(callername, "No such town "..hostname) + return + end + + rspawn.playerspawns["town lists"][hostname][guestname] = GUEST_BAN + + minetest.chat_send_player(callername, "Evicted "..guestname.." from "..hostname.."'s spawn") + minetest.log("action", "rspawn - "..callername.." evicts "..guestname.." on behalf of "..hostname) + else + minetest.chat_send_player(callername, "You are not permitted to act on behalf of "..hostname) + end + rspawn:spawnsave() +end + +local function townunban(callername, guestname, hostname) + if not (callername and guestname) then return end + + hostname = hostname or callername + if hostname == callername or (rspawn.playerspawns["guest lists"][hostname] and rspawn.playerspawns["guest lists"][hostname][callername] == GUEST_ALLOW) then + if not rspawn.playerspawns["town lists"][hostname] then + minetest.chat_send_player(callername, "No such town "..hostname) + return + end + + rspawn.playerspawns["town lists"][hostname][guestname] = nil + + minetest.chat_send_player(callername, "Evicted "..guestname.." from "..hostname.."'s spawn") + minetest.log("action", "rspawn - "..callername.." evicts "..guestname.." on behalf of "..hostname) + else + minetest.chat_send_player(callername, "You are not permitted to act on behalf of "..hostname) + end + rspawn:spawnsave() +end + function rspawn.guestlists:townset(hostname, params) + if not hostname then return end + params = params or "" params = params:split(" ") @@ -286,12 +319,10 @@ function rspawn.guestlists:townset(hostname, params) return elseif mode == "ban" and guestname and guestname ~= hostname then - town_banlist[guestname] = GUEST_BAN - minetest.chat_send_all(guestname.." is exiled from "..hostname.."'s town.") + townban(hostname, guestname, params[3]) elseif mode == "unban" and guestname then - town_banlist[guestname] = nil - minetest.chat_send_all(guestname.." is no longer exiled from "..hostname.."'s town.") + townunban(hostname, guestname, params[3]) else minetest.chat_send_player(hostname, "Unknown parameterless town operation: "..tostring(mode) ) From 82f252a7130f7027dff529adec8b7aabdc4e24cb Mon Sep 17 00:00:00 2001 From: Tai Kedzierski Date: Tue, 29 Jan 2019 23:22:35 +0000 Subject: [PATCH 07/11] initialize town lists on data load --- lua/data.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lua/data.lua b/lua/data.lua index 629f45c..b790251 100644 --- a/lua/data.lua +++ b/lua/data.lua @@ -88,6 +88,9 @@ function rspawn:spawnload() local pregens = rspawn.playerspawns["pre gen"] or {} rspawn.playerspawns["pre gen"] = pregens + local towns = rspawn.playerspawns["town lists"] or {} + rspawn.playerspawns["town lists"] = towns + reconcile_original_spawns() reconcile_guestlist_spawns() From 976853c6cbe1f6692d30217a26e5491abe21202e Mon Sep 17 00:00:00 2001 From: Tai Kedzierski Date: Wed, 30 Jan 2019 10:22:15 +0000 Subject: [PATCH 08/11] Added logging, isolated act_on_behalf, fixed auto kick proximity Specifically, if a player's spawn is close to a spawn they are banned from, and they are close to their spawn, we avoid kicking them in loop. --- README.md | 13 ++++++--- lua/debugging.lua | 8 +++++- lua/guestlists.lua | 67 +++++++++++++++++++++++++++++----------------- 3 files changed, 60 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 07f5c6b..58e27ab 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # `[rspawn]` Randomized Spawning for Minetest -Causes players to receive a spawn point anywhere on the map. Players will likely spawn veeery far from eachother into prisitine areas. +Causes players to receive a spawn point anywhere on the map. Players will likely spawn very far from eachother into prisitine areas. ## Features @@ -10,7 +10,8 @@ Causes players to receive a spawn point anywhere on the map. Players will likely * If `beds` spawning is active, then beds can be used to set players' re-spawn point (they still go to their main spawnpoint on invoking `/spawn`). * Commands * Players can return to their spawn point with the `/spawn` command if they have `spawn` privilege. - * Players can invite other players to join their spawn - see "Spawn invites" below + * Players can invite other players to join their spawn - see "Spawn guests" below + * Players can allow any other player to visit their spawn - see "Town hosting" below * Players can request a new spawn point by typing `/newspawn` if they have the `newspawn` privilege. * Players can set their spawn point by typing `/setspawn` if they have the `setspawn` privelege. * Moderator players can assign a new random spawn for another player using `/playerspawn` if they have the `spawnadmin` privilege. @@ -31,6 +32,8 @@ The player issuing the invite (host) must typically pay a levvy when adding anot * `/spawn guests` - see who you have added to your spawn * `/spawn hosts` - see whose spawns you may visit +Guests can help the spawn owner manage bans on their town. + ### Town hosting You can host a town from your spawn if you wish. Hosting a town means that any player who connects to the server will be able to visit your spawn. You can still `/spawn kick ` individually in this mode. If you switch off town hosting, only allowed guests in your normal guestlist can visit. @@ -41,6 +44,10 @@ There is no levvy on hosting a town. * `/spawn town { ban | unban } []` - ban or unban a player from a town * Town owners can use this, as well as unexiled guests of the town owner +Explicit guests can ban/unban other players from a town. + +Town owner can forcibly ban a player by first adding the player to their guest list, and then exiling them. Guests cannot override this. + ## Settings Note that the spawn generation is performed in the background on a timer, allowing storing a collection of random spawn points to be generated ahead of time. @@ -94,7 +101,7 @@ Resolutions in order of best to worst: * Stop minetest, delete the `force_loaded.txt` file, and start it again * (bad - some things in the mods using the forceload mechanism may break) -## Singple Player Mode +## Single Player Mode This mod is mainly intended for use on servers with multiple players. diff --git a/lua/debugging.lua b/lua/debugging.lua index 4da10d3..7765506 100644 --- a/lua/debugging.lua +++ b/lua/debugging.lua @@ -1,4 +1,10 @@ +function rspawn:d(stuff) + -- Quick debugging + minetest.debug(dump(stuff)) +end + function rspawn:debug(message, data) + -- Debugging from setting if not rspawn.debug_on then return end @@ -8,7 +14,7 @@ function rspawn:debug(message, data) if data ~= nil then debug_data = " :: "..dump(data) end - local debug_string = "rspawn : "..message..debug_data + local debug_string = "[rspawn] DEBUG : "..message..debug_data minetest.debug(debug_string) end diff --git a/lua/guestlists.lua b/lua/guestlists.lua index 849195e..f126bfd 100644 --- a/lua/guestlists.lua +++ b/lua/guestlists.lua @@ -32,7 +32,7 @@ local function find_levvy(player) local i if not player then - minetest.log("action", "Tried to access undefined player") + minetest.log("error", "[rspawn] Levvy : Tried to access undefined player") return false end @@ -41,7 +41,7 @@ local function find_levvy(player) local total_count = 0 if not player_inv then - minetest.log("action", "Could not access inventory for "..pname) + minetest.log("error", "[rspawn] Levvy : Could not access inventory for "..pname) return false end @@ -67,7 +67,7 @@ end function rspawn:consume_levvy(player) if not player then - minetest.log("action", "Tried to access undefined player") + minetest.log("error", "[rspawn] Levvy : Tried to access undefined player") return false end @@ -103,10 +103,6 @@ function rspawn:consume_levvy(player) return false end -local function d(stuff) - minetest.debug(dump(stuff)) -end - -- Visitation rights check local function canvisit(hostname, guestname) @@ -136,12 +132,14 @@ function rspawn.guestlists:addplayer(hostname, guestname) if guestlist[guestname] ~= nil then if guestlist[guestname] == GUEST_BAN then minetest.chat_send_player(guestname, hostname.." let you back into their spawn.") + minetest.log("action", "[rspawn] "..hostname.." lifted exile on "..guestname) end guestlist[guestname] = GUEST_ALLOW elseif rspawn:consume_levvy(minetest.get_player_by_name(hostname) ) then -- Automatically notifies host if they don't have enough guestlist[guestname] = GUEST_ALLOW minetest.chat_send_player(guestname, hostname.." added you to their spawn! You can now visit them with /spawn visit "..hostname) + minetest.log("action", "[rspawn] "..hostname.." added "..guestname.." to their spawn") else return end @@ -149,7 +147,6 @@ function rspawn.guestlists:addplayer(hostname, guestname) minetest.chat_send_player(hostname, guestname.." is allowed to visit your spawn.") rspawn.playerspawns["guest lists"][hostname] = guestlist rspawn:spawnsave() - minetest.log("action", "rspawn - "..hostname.." adds "..guestname.." to their spawn") end function rspawn.guestlists:exileplayer(hostname, guestname) @@ -174,15 +171,10 @@ function rspawn.guestlists:exileplayer(hostname, guestname) return true end -function rspawn.guestlists:kickplayer(callername, guestname) - - -- Caller is an explicit non-exiled guest - if hostname then - else - if rspawn.guestlists:exileplayer(callername, guestname) then - minetest.chat_send_player(callername, "Evicted "..guestname.." from "..callername.."'s spawn") - minetest.log("action", "rspawn - "..callername.." evicts "..guestname) - end +function rspawn.guestlists:kickplayer(hostname, guestname) + if rspawn.guestlists:exileplayer(hostname, guestname) then + minetest.chat_send_player(hostname, "Evicted "..guestname.." from your spawn") + minetest.log("action", "rspawn - "..hostname.." evicts "..guestname) end end @@ -195,8 +187,16 @@ function rspawn.guestlists:listguests(hostname) guests = ", You are an active town host." end + -- Explicit guests for guestname,status in pairs(guestlist) do - if status == GUEST_ALLOW then status = "" else status = " (exiled)" end + if status == GUEST_ALLOW then status = "" else status = " (exiled guest)" end + + guests = guests..", "..guestname..status + end + + -- Town bans - always list so this can be maanged even when town is closed + for guestname,status in pairs(global_hosts[hostname] or {}) do + if status == GUEST_ALLOW then status = "" else status = " (banned from town)" end guests = guests..", "..guestname..status end @@ -249,17 +249,27 @@ function rspawn.guestlists:visitplayer(hostname, guestname) end if guest and canvisit(hostname, guestname) then + minetest.log("action", "[rspawn] "..guestname.." visits "..hostname.." (/spawn visit)") guest:setpos(hostpos) else minetest.chat_send_player(guestname, "Could not visit "..hostname) end end +local function act_on_behalf(hostname, callername) + return hostname == callername or -- caller is the town owner, always allow + ( -- caller can act on behalf of town owner + rspawn.playerspawns["guest lists"][hostname] and + rspawn.playerspawns["guest lists"][hostname][callername] == GUEST_ALLOW + ) +end + local function townban(callername, guestname, hostname) if not (callername and guestname) then return end hostname = hostname or callername - if hostname == callername or (rspawn.playerspawns["guest lists"][hostname] and rspawn.playerspawns["guest lists"][hostname][callername] == GUEST_ALLOW) then + + if act_on_behalf(hostname, callername) then if not rspawn.playerspawns["town lists"][hostname] then minetest.chat_send_player(callername, "No such town "..hostname) return @@ -268,7 +278,7 @@ local function townban(callername, guestname, hostname) rspawn.playerspawns["town lists"][hostname][guestname] = GUEST_BAN minetest.chat_send_player(callername, "Evicted "..guestname.." from "..hostname.."'s spawn") - minetest.log("action", "rspawn - "..callername.." evicts "..guestname.." on behalf of "..hostname) + minetest.log("action", "[rspawn] - "..callername.." evicts "..guestname.." on behalf of "..hostname) else minetest.chat_send_player(callername, "You are not permitted to act on behalf of "..hostname) end @@ -279,7 +289,7 @@ local function townunban(callername, guestname, hostname) if not (callername and guestname) then return end hostname = hostname or callername - if hostname == callername or (rspawn.playerspawns["guest lists"][hostname] and rspawn.playerspawns["guest lists"][hostname][callername] == GUEST_ALLOW) then + if act_on_behalf(hostname, callername) then if not rspawn.playerspawns["town lists"][hostname] then minetest.chat_send_player(callername, "No such town "..hostname) return @@ -288,7 +298,7 @@ local function townunban(callername, guestname, hostname) rspawn.playerspawns["town lists"][hostname][guestname] = nil minetest.chat_send_player(callername, "Evicted "..guestname.." from "..hostname.."'s spawn") - minetest.log("action", "rspawn - "..callername.." evicts "..guestname.." on behalf of "..hostname) + minetest.log("action", "[rspawn] - "..callername.." lifts eviction on "..guestname.." on behalf of "..hostname) else minetest.chat_send_player(callername, "You are not permitted to act on behalf of "..hostname) end @@ -309,10 +319,12 @@ function rspawn.guestlists:townset(hostname, params) if mode == "open" then town_banlist["town status"] = "on" minetest.chat_send_all(hostname.." is opens access to all!") + minetest.log("action", "[rspawn] town: "..hostname.." sets their spawn to open") elseif mode == "close" then town_banlist["town status"] = "off" minetest.chat_send_all(hostname.." closes town access - only guests may directly visit.") + minetest.log("action", "[rspawn] town: "..hostname.." sets their spawn to closed") elseif mode == "status" then minetest.chat_send_player(hostname, "Town mode is: "..town_banlist["town status"]) @@ -355,14 +367,21 @@ minetest.register_globalstep(function(dtime) for hostname,host_guestlist in pairs(rspawn.playerspawns[player_list_name] or {}) do if host_guestlist[guestname] == GUEST_BAN then + -- Check distance of guest from banned pos local vdist = vector.distance(guestpos, rspawn.playerspawns[hostname]) - if vdist < exile_distance then + -- Check distance of guest from their own pos + -- If their spawn is very close to one they are banned from, + -- and they are close to their own, kick should not occur + local sdist = vector.distance(guestpos, rspawn.playerspawns[guestname]) + + if vdist < exile_distance and sdist > exile_distance then guest:setpos(rspawn.playerspawns[guestname]) minetest.chat_send_player(guestname, "You got too close to "..hostname.."'s turf.") + minetest.log("action", "[rspawn] Auto-kicked "..guestname.." for being too close to "..hostname.."'s spawn") return - elseif vdist < exile_distance*1.5 then + elseif vdist < exile_distance*1.5 and sdist > exile_distance then minetest.chat_send_player(guestname, "You are getting too close to "..hostname.."'s turf.") return end From 241d9479324ccd78d11d1a787cfb047a469e5e24 Mon Sep 17 00:00:00 2001 From: Tai Kedzierski Date: Wed, 30 Jan 2019 10:55:08 +0000 Subject: [PATCH 09/11] allow listing towns, announce open towns on player join --- lua/guestlists.lua | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/lua/guestlists.lua b/lua/guestlists.lua index f126bfd..30ff944 100644 --- a/lua/guestlists.lua +++ b/lua/guestlists.lua @@ -305,6 +305,21 @@ local function townunban(callername, guestname, hostname) rspawn:spawnsave() end +local function listtowns() + local town_lists = rspawn.playerspawns["town lists"] or {} + local open_towns = "" + + for townname,banlist in pairs(town_lists) do + if banlist["town status"] == "on" then + open_towns = open_towns..", "..townname + end + end + + if open_towns ~= "" then + return open_towns:sub(3) + end +end + function rspawn.guestlists:townset(hostname, params) if not hostname then return end @@ -336,6 +351,13 @@ function rspawn.guestlists:townset(hostname, params) elseif mode == "unban" and guestname then townunban(hostname, guestname, params[3]) + elseif mode == nil or mode == "" then + local open_towns = listtowns() + if not open_towns then + open_towns = "(none yet)" + end + minetest.chat_send_player(hostname, open_towns) + else minetest.chat_send_player(hostname, "Unknown parameterless town operation: "..tostring(mode) ) return @@ -392,3 +414,12 @@ minetest.register_globalstep(function(dtime) end end) + +-- Announce towns! + +minetest.register_on_joinplayer(function(player) + local open_towns = listtowns() + if open_towns then + minetest.chat_send_player(player:get_player_name(), "Currently open towns: "..open_towns..". Visit with '/spawn visit ' !") + end +end) From eb72c829e96678ecba2899be403662c807054692 Mon Sep 17 00:00:00 2001 From: Tai Kedzierski Date: Wed, 30 Jan 2019 11:44:49 +0000 Subject: [PATCH 10/11] do not return after kicking one exiled player --- lua/guestlists.lua | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lua/guestlists.lua b/lua/guestlists.lua index 30ff944..781cc18 100644 --- a/lua/guestlists.lua +++ b/lua/guestlists.lua @@ -378,14 +378,14 @@ minetest.register_globalstep(function(dtime) kick_step = 0 end - for _,guest in ipairs(minetest.get_connected_players()) do + for _x,guest in ipairs(minetest.get_connected_players()) do local guestname = guest:get_player_name() local playerprivs = minetest.get_player_privs(guestname) if not (playerprivs.basic_privs or playerprivs.server) then local guestpos = guest:getpos() - for _,player_list_name in ipairs({"guest lists", "town lists"}) do + for _y,player_list_name in ipairs({"guest lists", "town lists"}) do for hostname,host_guestlist in pairs(rspawn.playerspawns[player_list_name] or {}) do if host_guestlist[guestname] == GUEST_BAN then @@ -401,11 +401,9 @@ minetest.register_globalstep(function(dtime) guest:setpos(rspawn.playerspawns[guestname]) minetest.chat_send_player(guestname, "You got too close to "..hostname.."'s turf.") minetest.log("action", "[rspawn] Auto-kicked "..guestname.." for being too close to "..hostname.."'s spawn") - return elseif vdist < exile_distance*1.5 and sdist > exile_distance then minetest.chat_send_player(guestname, "You are getting too close to "..hostname.."'s turf.") - return end end end From bd8e2e0718859772720775077f2fa71dfd08a90d Mon Sep 17 00:00:00 2001 From: Tai Kedzierski Date: Wed, 30 Jan 2019 19:44:57 +0000 Subject: [PATCH 11/11] don't list 'town status' in guests --- lua/guestlists.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lua/guestlists.lua b/lua/guestlists.lua index 781cc18..10d6c75 100644 --- a/lua/guestlists.lua +++ b/lua/guestlists.lua @@ -196,9 +196,11 @@ function rspawn.guestlists:listguests(hostname) -- Town bans - always list so this can be maanged even when town is closed for guestname,status in pairs(global_hosts[hostname] or {}) do - if status == GUEST_ALLOW then status = "" else status = " (banned from town)" end + if guestname ~= "town status" then + if status == GUEST_ALLOW then status = "" else status = " (banned from town)" end - guests = guests..", "..guestname..status + guests = guests..", "..guestname..status + end end if guests == "" then @@ -297,7 +299,7 @@ local function townunban(callername, guestname, hostname) rspawn.playerspawns["town lists"][hostname][guestname] = nil - minetest.chat_send_player(callername, "Evicted "..guestname.." from "..hostname.."'s spawn") + minetest.chat_send_player(callername, "Allowed "..guestname.." back to town "..hostname) minetest.log("action", "[rspawn] - "..callername.." lifts eviction on "..guestname.." on behalf of "..hostname) else minetest.chat_send_player(callername, "You are not permitted to act on behalf of "..hostname)