diff --git a/init.lua b/init.lua index d43d4ec..09c7ea7 100644 --- a/init.lua +++ b/init.lua @@ -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!") diff --git a/src/commands.lua b/src/commands.lua index ebd7457..2080189 100644 --- a/src/commands.lua +++ b/src/commands.lua @@ -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 | accept | decline ]", + description = "Teleport to spawn position, or manage invitations. See you current invitation with '/spawn invite'", + params = "[ invite [] | 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 }) diff --git a/src/forceload.lua b/src/forceload.lua index 46bfe8a..41d507b 100644 --- a/src/forceload.lua +++ b/src/forceload.lua @@ -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 diff --git a/src/invites.lua b/src/invites.lua new file mode 100644 index 0000000..3da515f --- /dev/null +++ b/src/invites.lua @@ -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 diff --git a/src/pregeneration.lua b/src/pregeneration.lua index 687d62f..b6c4488 100644 --- a/src/pregeneration.lua +++ b/src/pregeneration.lua @@ -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