Merge branch 'open-towns'
commit
4b7df3c860
63
README.md
63
README.md
|
@ -1,16 +1,17 @@
|
|||
# `[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
|
||||
|
||||
* 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
|
||||
* 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.
|
||||
|
@ -23,11 +24,29 @@ 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 <player>` - allow another player to visit your spawn directly, or lift their exile
|
||||
* `/spawn kick <player>` - revoke rights to visit you, and if they are in your space, returns them to their own spawn
|
||||
* `/spawn add <player>` - allow another player to visit your spawn directly (levvy must be paid), or lift their exile (no levvy to pay)
|
||||
* `/spawn kick <targetplayer>`
|
||||
* revoke rights to visit you
|
||||
* if the exiled player gets close to your spawn, they are kicked back to their own spawn
|
||||
* `/spawn visit <player>` - 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
|
||||
|
||||
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 <playername>` 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 } <playername> [<town>]` - 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
|
||||
|
||||
|
@ -35,7 +54,7 @@ Note that the spawn generation is performed in the background on a timer, allowi
|
|||
|
||||
*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,42 +62,46 @@ 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 `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 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 <x>` 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 <x>` 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/<yourworld>` 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/<yourworld>` 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
|
||||
* (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.
|
||||
|
||||
|
|
12
init.lua
12
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)
|
||||
|
|
|
@ -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,11 +35,11 @@ end
|
|||
|
||||
minetest.register_chatcommand("spawn", {
|
||||
description = "Teleport to your spawn, or manage guests in your spawn.",
|
||||
params = "[ add <player> | visit <player> | kick <player> | guests | hosts ]",
|
||||
params = "[ add <player> | visit <player> | kick <player> | guests | hosts | town { open | close | ban <player> [<town>] | unban <player> [<town>] } ]",
|
||||
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,13 +50,14 @@ 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,
|
||||
["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,
|
||||
["kick"] = function(commandername, params) rspawn.guestlists:kickplayer(commandername, params) end,
|
||||
["town"] = function(commandername,mode) rspawn.guestlists:townset(commandername, mode) end,
|
||||
}) do
|
||||
|
||||
if args[1] == command then
|
||||
|
@ -65,14 +66,14 @@ minetest.register_chatcommand("spawn", {
|
|||
return
|
||||
|
||||
elseif #args == 1 then
|
||||
action()
|
||||
action(playername)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
minetest.chat_send_player(playername, "Please check '/help spawn'")
|
||||
minetest.chat_send_player(playername, "Bad command. Please check '/help spawn'")
|
||||
end
|
||||
})
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 3
|
||||
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,18 +26,13 @@ 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
|
||||
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
|
||||
|
||||
|
@ -39,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
|
||||
|
||||
|
@ -65,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
|
||||
|
||||
|
@ -101,18 +103,43 @@ 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 town_lists = rspawn.playerspawns["town lists"] or {}
|
||||
|
||||
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)
|
||||
|
||||
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.")
|
||||
minetest.log("action", "[rspawn] "..hostname.." lifted exile on "..guestname)
|
||||
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)
|
||||
minetest.log("action", "[rspawn] "..hostname.." added "..guestname.." to their spawn")
|
||||
else
|
||||
return
|
||||
end
|
||||
|
@ -123,30 +150,31 @@ function rspawn.guestlists:addplayer(hostname, guestname)
|
|||
end
|
||||
|
||||
function rspawn.guestlists:exileplayer(hostname, guestname)
|
||||
if hostname == guestname then
|
||||
minetest.chat_send_player(hostname, "Cannot ban yourself!")
|
||||
return false
|
||||
end
|
||||
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
|
||||
minetest.chat_send_player(hostname, guestname.." is not in your accepted guests list.")
|
||||
return
|
||||
minetest.chat_send_player(hostname, guestname.." is not in accepted guests list for "..hostname)
|
||||
return false
|
||||
end
|
||||
|
||||
minetest.chat_send_player(guestname, hostname.." banishes you!")
|
||||
rspawn.guestlists:kick(hostname, guestname)
|
||||
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: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)
|
||||
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
|
||||
|
||||
|
@ -154,12 +182,31 @@ 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
|
||||
|
||||
-- Explicit guests
|
||||
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 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 guestname ~= "town status" then
|
||||
if status == GUEST_ALLOW then status = "" else status = " (banned from town)" end
|
||||
|
||||
guests = guests..", "..guestname..status
|
||||
end
|
||||
end
|
||||
|
||||
if guests == "" then
|
||||
guests = ", No guests, not hosting a town."
|
||||
end
|
||||
|
||||
minetest.chat_send_player(hostname, guests:sub(3))
|
||||
end
|
||||
|
||||
|
@ -169,17 +216,32 @@ 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"] 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
|
||||
end
|
||||
|
||||
if hosts == "" then
|
||||
hosts = ", (no visitable hosts)"
|
||||
end
|
||||
|
||||
minetest.chat_send_player(guestname, hosts:sub(3))
|
||||
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]
|
||||
|
||||
|
@ -189,8 +251,175 @@ 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 act_on_behalf(hostname, callername) 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 act_on_behalf(hostname, callername) 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, "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)
|
||||
end
|
||||
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
|
||||
|
||||
params = params or ""
|
||||
params = params:split(" ")
|
||||
|
||||
local mode = params[1]
|
||||
local guestname = params[2]
|
||||
local town_lists = rspawn.playerspawns["town lists"] or {}
|
||||
local town_banlist = town_lists[hostname] or {}
|
||||
|
||||
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"])
|
||||
return
|
||||
|
||||
elseif mode == "ban" and guestname and guestname ~= hostname then
|
||||
townban(hostname, guestname, params[3])
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
town_lists[hostname] = town_banlist
|
||||
rspawn.playerspawns["town lists"] = town_lists
|
||||
|
||||
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 _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 _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
|
||||
-- Check distance of guest from banned pos
|
||||
local vdist = vector.distance(guestpos, rspawn.playerspawns[hostname])
|
||||
|
||||
-- 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")
|
||||
|
||||
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.")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
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 <townname>' !")
|
||||
end
|
||||
end)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue