Add player invites

* Allow players to invite others to use their spawn (permanently)
* change pregeneration to be actioned relative to frequency
This commit is contained in:
Tai Kedzierski 2019-01-02 16:49:44 +00:00
parent e3c57fc241
commit b298d680cf
5 changed files with 236 additions and 38 deletions

View File

@ -12,7 +12,7 @@ local function notnil_or(d, v)
end
-- Water level, plus one to ensure we are above the sea.
local water_level = tonumber(minetest.settings:get("water_level", "1") )+1
local water_level = tonumber(minetest.settings:get("water_level", "0") )
local radial_step = 16
-- Setting with no namespace for interoperability
@ -34,6 +34,7 @@ rspawn.gen_frequency = tonumber(minetest.settings:get("rspawn.gen_frequency") or
rspawn.spawn_block = minetest.settings:get("rspawn.spawn_block")
dofile(mpath.."/src/data.lua")
dofile(mpath.."/src/invites.lua")
dofile(mpath.."/src/commands.lua")
dofile(mpath.."/src/forceload.lua")
dofile(mpath.."/src/debugging.lua")
@ -87,6 +88,7 @@ function rspawn:newspawn(pos, radius)
if under.walkable
and not over.walkable
and not minetest.is_protected(anode, rspawn.adminname)
and not (under.groups and under.groups.leaves ) -- no spawning on treetops!
and daylight_above(7, anode) then
validnodes[#validnodes+1] = anode
end
@ -109,8 +111,7 @@ function rspawn:genpos()
if rspawn.spawnanywhere then
pos = {
x = math.random(-30000,30000),
--y = math.random(water_level, water_level+20),
y = water_level, -- always at waterlevel
y = water_level, -- always start at waterlevel
z = math.random(-30000,30000),
}
end
@ -118,7 +119,7 @@ function rspawn:genpos()
return pos
end
local function confirm_new_spawn(name, newpos)
function rspawn:set_player_spawn(name, newpos)
local spos = minetest.pos_to_string(newpos)
rspawn.debug("Saving spawn for "..name, spos)
@ -140,21 +141,18 @@ function rspawn:set_newplayer_spawn(player)
local newpos = rspawn:get_next_spawn()
if newpos then
confirm_new_spawn(playername, newpos)
rspawn:set_player_spawn(playername, newpos)
else
if rspawn.adminname ~= "singleplayer" or playername ~= rspawn.adminname then
minetest.chat_send_player(playername, "Please wait until a spawn point is available ...")
minetest.after(15, function()
rspawn:set_newplayer_spawn(player)
end)
elseif rspawn.kick_on_fail then
-- We did not get a new position
if rspawn.kick_on_fail then
minetest.kick_player(playername, "No personalized spawn points available - please try again later.")
else
minetest.chat_send_player(playername, "Could not get custom spawn! Retrying in "..rspawn.gen_frequency.." seconds")
minetest.after(gen_frequency, function()
minetest.after(rspawn.gen_frequency+2, function()
rspawn:set_newplayer_spawn(player)
end)
end
@ -168,7 +166,7 @@ function rspawn:renew_player_spawn(playername)
local newpos = rspawn:get_next_spawn()
if newpos then
confirm_new_spawn(playername, newpos)
rspawn:set_player_spawn(playername, newpos)
else
minetest.chat_send_player(playername, "Could not get custom spawn!")

View File

@ -19,7 +19,7 @@ local function splitstring(sdata, sep)
if idx then
tdata[#tdata+1] = sdata:sub(1,idx-1)
sdata = sdata:sub(idx+1, idx:len() )
sdata = sdata:sub(idx+1, sdata:len() )
else -- last element
tdata[#tdata+1] = sdata
@ -33,8 +33,8 @@ end
-- Commands
minetest.register_chatcommand("spawn", {
description = "Teleport to spawn position.",
params = "[ invite <player> | accept | decline ]",
description = "Teleport to spawn position, or manage invitations. See you current invitation with '/spawn invite'",
params = "[ invite [<player>] | accept | decline ]",
privs = "spawn",
func = function(name, args)
local target = rspawn.playerspawns[name]
@ -43,23 +43,35 @@ minetest.register_chatcommand("spawn", {
if #args == 0 then
if target then
minetest.get_player_by_name(name):setpos(target)
return
else
minetest.chat_send_player(name, "You have no spawn position!")
return
end
elseif args[1] == "accept" then
accept_invitation(name) -- TODO, only one at a time, must be accepted or declined
rspawn.invites:accept(name)
-- TODO, only one at a time, must be accepted or declined, and DO move player - not to be used lightly
return
elseif args[1] == "decline" then
decline_invitation(name) -- TODO, free up invitation slot
rspawn.invites:decline(name) -- TODO, free up invitation slot
return
elseif#args == 2 and args[1] == "invite" then
invite_player_fromto(name, args[2]) -- TODO, and DO move player - not to be used lightly
else
minetest.chat_send_player(name, "Please check '/help spawn'")
elseif args[1] == "invite" then
if #args == 2 then
rspawn.invites:invite_player_fromto(name, args[2]) -- TODO
return
elseif #args == 1 then
rspawn.invites:show_invite_for(name) -- TODO
return
end
end
minetest.chat_send_player(name, "Please check '/help spawn'")
end
})

View File

@ -1,3 +1,6 @@
local forceloading_happening = false
local function forceload_operate(pos1, pos2, handler)
local i,j,k
@ -11,13 +14,22 @@ local function forceload_operate(pos1, pos2, handler)
end
function rspawn:forceload_blocks_in(pos1, pos2)
if forceloading_happening then
rspawn:debug("Forceload operation already underway - abort")
return false
end
rspawn:debug("Forceloading blocks -----------¬", {pos1=minetest.pos_to_string(pos1),pos2=minetest.pos_to_string(pos2)})
forceloading_happening = true
minetest.emerge_area(pos1, pos2)
forceload_operate(pos1, pos2, minetest.forceload_block)
return true
end
function rspawn:forceload_free_blocks_in(pos1, pos2)
rspawn:debug("Freeing forceloaded blocks ____/", {pos1=minetest.pos_to_string(pos1),pos2=minetest.pos_to_string(pos2)})
forceload_operate(pos1, pos2, minetest.forceload_free_block)
forceloading_happening = false
end

176
src/invites.lua Normal file
View File

@ -0,0 +1,176 @@
rspawn.invites = {}
-- invitations[guest] = host
rspawn.invitations = {}
local invite_charge = {}
levvy_name = minetest.settings:get("rspawn.levvy_name") or "default:cobble"
levvy_qtty = minetest.settings:get("rspawn.levvy_qtty") or 99
levvy_nicename = "cobblestone"
if minetest.registered_nodes[levvy_name] then
levvy_nicename = minetest.registered_nodes[levvy_name].description
else
minetest.debug("No such node "..levvy_name.." -- reverting to defaults.")
levvy_name = "default:cobble"
levvy_qtty = 99
end
local function get_players(p1name, p2name)
-- Check both players are online.
-- It is easier to implement agains online players than to manage offline interactions
local err, p1, p2
local errmsg_generic = " is not online."
if not p1name then
minetest.log("error", "Missing p1name")
return nil,nil,"Internal error."
elseif not p2name then
minetest.log("error", "Missing p2name")
return nil,nil,"Internal error."
end
p1 = minetest.get_player_by_name(p1name)
p2 = minetest.get_player_by_name(p2name)
if not p1 then err = p1name..errmsg_generic end
if not p2 then err = p2name..errmsg_generic end
return p1,p2,err
end
function rspawn.invites:invite_player_fromto(hostname, guestname)
local host,guest = get_players(hostname, guestname)
if err then
minetest.chat_send_player(hostname, err)
return
end
if not rspawn.invitations[guestname] then
rspawn.invitations[guestname] = hostname
else
minetest.chat_send_player(hostname, guestname.." already has a pending invitation, and cannot be invited.")
return
end
local hostspawn_s = minetest.pos_to_string(rspawn.playerspawns[hostname])
minetest.chat_send_player(guestname, hostname.." invited you to join their spawn point.\nIf you accept, your spawn point will be set to "..hostspawn_s.." and you will be taken there immediately.\n This cannot be undone.\n\nRun '/spawn accept' to accept, '/spawn decline' to decline and clear the invite.")
minetest.chat_send_player(hostname,
"You have invited "..guestname.." to join your spawn.\nIf they accept, you will be charged \n\n "..levvy_qtty.." "..levvy_nicename.." \n\nwhich will be taken from your inventory."
)
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
local pname = player:get_player_name()
local player_inv = minetest.get_inventory({type='player', name = pname})
local total_count = 0
for i = 1,32 do
local itemstack = player_inv:get_stack('main', i)
local itemname = itemstack:get_name()
if itemname == levvy_name then
if itemstack:get_count() >= levvy_qtty then
return true
else
total_count = total_count + itemstack:get_count()
if total_count >= (levvy_qtty) then
return true
end
end
end
end
minetest.chat_send_player(pname, "You do not have enough "..levvy_nicename.." to pay the spawn levvy for your invitaiton.")
return false
end
local function consume_levvy(player)
local i
local pname = player:get_player_name()
local player_inv = minetest.get_inventory({type='player', name = pname})
local total_count = 0
-- TODO combine find_levvy and consume_levvy so that we're
-- not scouring the inventory twice...
if find_levvy(player) then
for i = 1,32 do
local itemstack = player_inv:get_stack('main', i)
local itemname = itemstack:get_name()
if itemname == levvy_name then
if itemstack:get_count() >= levvy_qtty then
itemstack:take_item(levvy_qtty)
player_inv:set_stack('main', i, itemstack)
return true
else
total_count = total_count + itemstack:get_count()
itemstack:clear()
player_inv:set_stack('main', i, itemstack)
if total_count >= (levvy_qtty) then
return true
end
end
end
end
end
return false
end
function rspawn.invites:accept(guestname)
local hostname = rspawn.invitations[guestname]
if not hostname then
minetest.chat_send_player(guestname, "No invitation to accept.")
return
end
local host,guest = get_players(hostname, guestname)
if err then
minetest.chat_send_player(guestname, err)
return
end
if consume_levvy(minetest.get_player_by_name(hostname) ) then -- Systematically notifies host if they don't have enough
local hostspawn = rspawn.playerspawns[hostname]
rspawn:set_player_spawn(guestname, hostspawn) -- sets new spawn position, saves, teleports player
else -- Host was notified, now notify guest
minetest.chat_send_player(guestname, hostname.." was unable to pay the levvy. Invitation could not be accepted.")
end
end
function rspawn.invites:decline(guestname)
local hostname = rspawn.invitations[guestname]
if hostname then
rspawn.invitations[guestname] = nil
-- Player not online, message simply ignored.
minetest.chat_send_player(guestname, "Declined invitation to join "..hostname.."'s spawn for now.")
minetest.chat_send_player(hostname, guestname.." declined to join your spawn point for now.")
else
minetest.chat_send_player(guestname, "No invitation to decline.")
end
end
function rspawn.invites:show_invite_for(guestname)
local hostname = rspawn.invitations[guestname]
if hostname then
minetest.chat_send_player(guestname, "You have been invited to join "..hostname.." at "..minetest.pos_to_string(rspawn.playerspawns[hostname]))
else
minetest.chat_send_player(guestname, "No pending invitation.")
end
end

View File

@ -26,21 +26,23 @@ local function push_new_spawn()
local random_pos = rspawn:genpos()
local pos1,pos2 = rspawn:get_positions_for(random_pos, rspawn.search_radius)
rspawn:forceload_blocks_in(pos1, pos2)
if rspawn:forceload_blocks_in(pos1, pos2) then
minetest.after(rspawn.gen_frequency*0.8, function()
-- Let the forceload do its thing, then act
minetest.after(10, function()
-- Let the forceload do its thing, then act
local newpos = rspawn:newspawn(random_pos, rspawn.search_radius)
if newpos then
rspawn:debug("Generated "..minetest.pos_to_string(newpos))
set_pgen(len_pgen()+1, newpos )
else
rspawn:debug("Failed to generate new spawn point to push")
end
local newpos = rspawn:newspawn(random_pos, rspawn.search_radius)
if newpos then
rspawn:debug("Generated "..minetest.pos_to_string(newpos))
set_pgen(len_pgen()+1, newpos )
else
rspawn:debug("Failed to generate new spawn point to push")
end
rspawn:forceload_free_blocks_in(pos1, pos2)
end)
rspawn:forceload_free_blocks_in(pos1, pos2)
end)
else
rspawn:debug("Failed to push new spawn point - preexisting operation took precedence.")
end
end
minetest.register_globalstep(function(dtime)
@ -63,8 +65,6 @@ function rspawn:get_next_spawn()
nspawn = get_pgen(len_pgen() )
rspawn:debug("Returning pregenerated spawn",nspawn)
set_pgen(len_pgen(), nil)
else
push_new_spawn()
end
return nspawn