Major refactor, pulling api functions out into a separate file to protect the internal data. Not backwards compatible.

This commit is contained in:
FaceDeer 2021-01-06 05:39:09 -07:00
parent ae4dc73fd8
commit fc64db04c8
3 changed files with 512 additions and 413 deletions

389
api.lua Normal file
View File

@ -0,0 +1,389 @@
local S = minetest.get_translator(minetest.get_current_modname())
-- pocket data tables have the following properties:
-- pending = true -- pocket is being initialized, don't teleport there just yet
-- destination = a vector relative to the pocket's minp that is where new arrivals teleport tonumber
-- name = a name for the pocket.
-- owner = if set, this pocket is "owned" by this particular player.
-- protected = if true, this pocket is protected and only the owner can modify its contents
-- minp = the lower corner of the pocket's region
local pockets_by_name = {}
local player_origin = {}
local pockets_deleted = {} -- record deleted pockets for possible later undeletion, indexed by hash
local personal_pockets = {} -- to be filled out if personal pockets are enabled
local protected_areas = AreaStore()
--The world is a cube of side length 61840. Coordinates go from -30912 to 30927 in any direction.
--The side length is a multiple of 80
--773 * 80 = 61840
-- so there are 773 * 773 = 597529 chunk in a horizontal layer. Should be plenty for distributing these things in.
local mapgen_chunksize = tonumber(minetest.get_mapgen_setting("chunksize"))
local mapblock_size = mapgen_chunksize * 16 -- should be 80 in almost all cases, but avoiding hardcoding it for extensibility
local block_grid_dimension = math.floor(61840 / mapblock_size) -- should be 773
local min_coordinate = -30912
local layer_elevation = tonumber(minetest.settings:get("pocket_dimensions_altitude")) or 30000
layer_elevation = math.floor(layer_elevation / mapblock_size) * mapblock_size - 32 -- round to mapblock boundary
pocket_dimensions.pocket_size = mapblock_size
--------------------------------------------------------------------------------------
-- Loading and saving data
local filename = minetest.get_worldpath() .. "/pocket_dimensions_data.lua"
local load_data = function()
local f, e = loadfile(filename)
if f then
local data = f()
pockets_by_name = data.pockets_by_name
player_origin = data.player_origin
pockets_deleted = data.pockets_deleted
if personal_pockets_enabled then
for name, pocket_data in pairs(pockets_by_name) do
if pocket_data.personal and pocket_data.owner then
if personal_pockets[pocket_data.owner] then
minetest.log("error", "[pocket_dimensions] "
.. pocket_data.owner .. " owns multiple personal pockets, "
.. personal_pockets[pocket_data.owner].name .. " and " .. pocket_data.name
.. ".")
end
personal_pockets[pocket_data.owner] = pocket_data
end
end
end
else
return
end
-- add saved protected areas
for name, pocket_data in pairs(pockets_by_name) do
if pocket_data.protected then
protected_areas:insert_area(pocket_data.minp, vector.add(pocket_data.minp, mapblock_size), pocket_data.owner)
end
end
end
local save_data = function()
local data = {}
data.pockets_by_name = pockets_by_name
data.player_origin = player_origin
data.pockets_deleted = pockets_deleted
local file, e = io.open(filename, "w");
if not file then
return error(e);
end
file:write(minetest.serialize(data))
file:close()
end
load_data()
--------------------------------------------------------------
-- protection
local old_is_protected = minetest.is_protected
function minetest.is_protected(pos, name)
if minetest.check_player_privs(name, "protection_bypass") then
return false
end
local protection = protected_areas:get_areas_for_pos(pos, false, true)
for _, area in pairs(protection) do
if area.data ~= name then
return true
end
end
return old_is_protected(pos, name)
end
pocket_dimensions.set_protection = function(pocket_data, protection)
pocket_data.protected = protection
-- clear any existing protection
protected = protected_areas:get_areas_for_pos(pocket_data.minp)
for id, _ in pairs(protected) do -- there should only be one result
protected_areas:remove_area(id)
end
-- add protection if warranted
if pocket_data.protected then
protected_areas:insert_area(pocket_data.minp, vector.add(pocket_data.minp, mapblock_size), pocket_data.owner or "")
end
minetest.log("action", "[pocket_dimensions] Protection ownership of pocket dimension " .. pocket_data.name .. " set to " .. tostring(pocket_data.protected))
save_data()
end
-----------------------------------------------------------------------------------------------------
-- check if players have got out of pocket dimensions by other means and clear their origin locations
local since_last_check = 0
minetest.register_globalstep(function(dtime)
since_last_check = since_last_check + dtime
if since_last_check > 10 then
for name, _ in pairs(player_origin) do
local pos = minetest.get_player_by_name(name):get_pos()
if pos.y < layer_elevation or pos.y > layer_elevation + mapblock_size then
player_origin[name] = nil -- somehow, player escaped the pocket dimension layer
since_last_check = 0 -- note that we changed data
end
end
if since_last_check == 0 then
save_data()
return
end
since_last_check = 0
end
end)
------------------------------------------------------------------------------------------------
pocket_dimensions.get_pocket = function(pocket_name)
return pockets_by_name[string.lower(pocket_name)]
end
pocket_dimensions.get_all_pockets = function()
local ret = {}
for name, def in pairs(pockets_by_name) do
table.insert(ret, def)
end
return ret
end
pocket_dimensions.get_deleted_pockets = function()
local ret = {}
for hash, def in pairs(pockets_deleted) do
table.insert(ret, def)
end
return ret
end
pocket_dimensions.pocket_containing_pos = function(pos)
for name, pocket_data in pairs(pockets_by_name) do
local pos_diff = vector.subtract(pos, pocket_data.minp)
if pos_diff.y >=0 and pos_diff.y <= mapblock_size and -- check y first to eliminate possibility player's not in a pocket dimension at all
pos_diff.x >=0 and pos_diff.x <= mapblock_size and
pos_diff.z >=0 and pos_diff.z <= mapblock_size then
return pocket_data
end
end
end
pocket_dimensions.rename_pocket = function(old_name, new_name)
local new_name_lower = string.lower(new_name)
local old_name_lower = string.lower(old_name)
if pockets_by_name[new_name_lower] or not pockets_by_name[old_name_lower] then
return false
end
pockets_by_name[new_name_lower] = pocket_data
pockets_by_name[old_name_lower] = nil
pocket_data.name = new_name
save_data()
return true
end
pocket_dimensions.set_destination = function(pocket_data, destination)
local dest = vector.round(destination)
assert(dest.x > pocket_data.minp.x and dest.y > pocket_data.minp.y and dest.z > pocket_data.minp.z
and dest.x < pocket_data.minp.x + mapblock_size
and dest.y < pocket_data.minp.y + mapblock_size
and dest.z < pocket_data.minp.z + mapblock_size,
"[pocket_dimensions] attempting to set destination point " ..
minetest.pos_to_string(dest) ..
" that wasn't within pocket dimension "..
pocket_data.name)
pocket_data.destination = dest
save_data()
end
---------------------------------------------------------------------------------
-- entering and exiting
------------------------------------------------------------------------------------------
-- Teleport effects
local particle_node_pos_spread = vector.new(0.5,0.5,0.5)
local particle_user_pos_spread = vector.new(0.5,1.5,0.5)
local particle_speed_spread = vector.new(0.1,0.1,0.1)
local particle_poof = function(pos)
minetest.add_particlespawner({
amount = 100,
time = 0.1,
minpos = vector.subtract(pos, particle_node_pos_spread),
maxpos = vector.add(pos, particle_user_pos_spread),
minvel = particle_speed_spread,
maxvel = particle_speed_spread,
minacc = {x=0, y=0, z=0},
maxacc = {x=0, y=0, z=0},
minexptime = 0.1,
maxexptime = 0.5,
minsize = 1,
maxsize = 1,
collisiondetection = false,
vertical = false,
texture = "pocket_dimensions_spark.png",
})
end
local teleport_player = function(player, dest)
local source_pos = player:get_pos()
particle_poof(source_pos)
minetest.sound_play({name="pocket_dimensions_teleport_from"}, {pos = source_pos}, true)
player:set_pos(dest)
particle_poof(dest)
minetest.sound_play({name="pocket_dimensions_teleport_to"}, {pos = dest}, true)
end
pocket_dimensions.teleport_player_to_pocket = function(player_name, pocket_name)
local pocket_data = pocket_dimensions.get_pocket(pocket_name)
if pocket_data == nil or pocket_data.pending then
return false
end
local player = minetest.get_player_by_name(player_name)
if not player_origin[player_name] then
player_origin[player_name] = player:get_pos()
save_data()
end
teleport_player(player, pocket_data.destination)
return true
end
-- returns a place to put players if they have no origin recorded
local get_fallback_origin = function()
local spawnpoint = minetest.setting_get_pos("static_spawnpoint")
if not spawnpoint then
local x = math.random()*1000 - 500
local z = math.random()*1000 - 500
local y =minetest.get_spawn_level(x,z)
spawnpoint = {x=x,y=y,z=z}
end
end
pocket_dimensions.return_player_to_origin = function(player_name)
local player = minetest.get_player_by_name(player_name)
local origin = player_origin[player_name]
if origin then
teleport_player(player, origin)
player_origin[player_name] = nil
save_data()
return
end
-- If the player's lost their origin data somehow, dump them somewhere using the spawn system to find an adequate place.
local spawnpoint = get_fallback_origin()
minetest.log("error", "[pocket_dimensions] Somehow "..name.." was at "..minetest.pos_to_string(clicker:get_pos())..
" inside a pocket dimension but they had no origin point recorded when they tried to leave. Sending them to "..
minetest.pos_to_string(spawnpoint).." as a fallback.")
teleport_player(clicker, spawnpoint)
end
-------------------------------------------------------------------------------------
-- pocket creation
local mapgens = {}
pocket_dimensions.register_pocket_type = function(type_name, mapgen_callback)
mapgens[type_name] = mapgen_callback
end
local emerge_callback = function(blockpos, action, calls_remaining, pocket_data)
local mapgen_callback = mapgens[pocket_data.type]
assert(mapgen_callback, "[pocket_dimensions] pocket type " .. pocket_data.type .. " had no registered mapgen callback")
local dest = mapgen_callback(pocket_data)
pocket_dimensions.set_destination(pocket_data, dest)
pocket_data.pending = nil
save_data()
minetest.log("action", "[pocket_dimensions] Finished initializing terrain map for pocket dimension " .. pocket_data.name)
end
pocket_dimensions.create_pocket = function(pocket_name, pocket_data_override)
pocket_data_override = pocket_data_override or {}
pocket_data_override.type = pocket_data_override.type or "grassy"
if pocket_name == nil or pocket_name == "" then
return false, S("Please provide a name for the pocket dimension")
end
local pocket_name_lower = string.lower(pocket_name)
if pockets_by_name[pocket_name_lower] then
return false, S("The name @1 is already in use.", pocket_name)
end
local count = 0
while count < 100 do
local x = math.random(0, block_grid_dimension) * mapblock_size + min_coordinate
local z = math.random(0, block_grid_dimension) * mapblock_size + min_coordinate
local pos = {x=x, y=layer_elevation, z=z}
if pocket_dimensions.pocket_containing_pos(pos) == nil then
local pocket_data = {pending=true, minp=pos, name=pocket_name}
pockets_by_name[pocket_name_lower] = pocket_data
for key, value in pairs(pocket_data_override) do
pocket_data[key] = value
end
minetest.emerge_area(pos, pos, emerge_callback, pocket_data)
save_data()
minetest.log("action", "[pocket_dimensions] Created a pocket dimension named " .. pocket_name .. " at " .. minetest.pos_to_string(pos))
return true, S("Pocket dimension @1 created", pocket_name)
end
end
return false, S("Failed to find a new location for this pocket dimension.")
end
pocket_dimensions.delete_pocket = function(pocket_data)
local pocket_name_lower = string.lower(pocket_data.name)
local pocket_hash = minetest.hash_node_position(pocket_data.minp)
pockets_deleted[pocket_hash] = pocket_data
pockets_by_name[pocket_name_lower] = nil
for name, personal_pocket_data in pairs(personal_pockets) do
if pocket_data == personal_pocket_data then
-- we're deleting a personal pocket, remove its record
personal_pockets[name] = nil
break
end
end
save_data()
minetest.log("action", "[pocket_dimensions] Deleted the pocket dimension " .. pocket_data.name .. " at " .. minetest.pos_to_string(pocket_data.minp))
return true, S("Deleted pocket dimension @1 at @2. Note that this doesn't affect the map, just moves this pocket dimension out of regular access and into the deleted list.", pocket_data.name, minetest.pos_to_string(pocket_data.minp))
end
pocket_dimensions.undelete_pocket = function(pocket_data)
local pocket_hash = minetest.hash_node_position(pocket_data.minp)
local pocket_name_lower = string.lower(pocket_data.name)
if pockets_by_name[pocket_name_lower] then
return false, S("Cannot undelete, a pocket dimension with the name @1 already exists", pocket_name)
end
pockets_deleted[pocket_hash] = nil
pockets_by_name[pocket_name_lower] = pocket_data
if pocket_data.personal and pocket_data.owner and not personal_pockets[pocket_data.owner] then
-- it was a personal pocket and the player hasn't created a new one, so restore that association
personal_pockets[pocket_data.owner] = pocket_data
end
save_data()
minetest.log("action", "[pocket_dimensions] Undeleted the pocket dimension " .. pocket_data.name .. " at " .. minetest.pos_to_string(pocket_data.minp))
return true, S("Undeleted pocket dimension @1 at @2. Note that this doesn't affect the map, just moves this pocket dimension out of regular access and into the deleted list.", pocket_data.name, minetest.pos_to_string(pocket_data.minp))
end
------------------------------------------------------------------
-- TODO: some cross-validation to ensure only owned pockets are personal and only one pocket is personal per player
pocket_dimensions.get_personal_pocket = function(player_name)
return personal_pockets[player_name]
end
pocket_dimensions.set_personal_pocket = function(pocket_data, player_name)
if pocket_data.personal and not player_name then
-- clear personal pocket
personal_pockets[player] = nil
pocket_data.personal = nil
else
pocket_data.personal = true
pocket_data.owner = player_name
personal_pockets[player_name] = pocket_data
end
save_data()
end
pocket_dimensions.set_owner = function(pocket_data, player_name)
pocket_data.owner = player_name
save_data()
end

515
init.lua
View File

@ -2,19 +2,27 @@ pocket_dimensions = {}
local S = minetest.get_translator(minetest.get_current_modname())
local MP = minetest.get_modpath(minetest.get_current_modname())
dofile(MP.."/voxelarea_iterator.lua")
dofile(MP.."/api.lua")
--The world is a cube of side length 61840. Coordinates go from -30912 to 30927 in any direction.
--The side length is a multiple of 80
--773 * 80 = 61840
-- so there are 773 * 773 = 597529 chunk in a horizontal layer. Should be plenty for distributing these things in.
-- API
local get_pocket = pocket_dimensions.get_pocket
local get_all_pockets = pocket_dimensions.get_all_pockets
local get_deleted_pockets = pocket_dimensions.get_deleted_pockets
local rename_pocket = pocket_dimensions.rename_pocket
local register_pocket_type = pocket_dimensions.register_pocket_type
local create_pocket = pocket_dimensions.create_pocket
local delete_pocket = pocket_dimensions.delete_pocket
local undelete_pocket = pocket_dimensions.undelete_pocket
local set_protection = pocket_dimensions.set_protection
local pocket_containing_pos = pocket_dimensions.pocket_containing_pos
local set_destination = pocket_dimensions.set_destination
local get_personal_pocket = pocket_dimensions.get_personal_pocket
local set_personal_pocket = pocket_dimensions.set_personal_pocket
local set_owner = pocket_dimensions.set_owner
local teleport_player_to_pocket = pocket_dimensions.teleport_player_to_pocket
local return_player_to_origin = pocket_dimensions.return_player_to_origin
local mapgen_chunksize = tonumber(minetest.get_mapgen_setting("chunksize"))
local mapblock_size = mapgen_chunksize * 16 -- should be 80 in almost all cases, but avoiding hardcoding it for extensibility
local block_grid_dimension = math.floor(61840 / mapblock_size) -- should be 773
local min_coordinate = -30912
local layer_elevation = tonumber(minetest.settings:get("pocket_dimensions_altitude")) or 30000
layer_elevation = math.floor(layer_elevation / mapblock_size) * mapblock_size - 32 -- round to mapblock boundary
local pocket_size = pocket_dimensions.pocket_size
local personal_pockets_chat_command = minetest.settings:get_bool("pocket_dimensions_personal_pockets_chat_command", false)
local personal_pockets_key = minetest.settings:get_bool("pocket_dimensions_personal_pockets_key", false)
@ -50,155 +58,6 @@ if mcl_core_modpath then
end
-- pocket data tables have the following properties:
-- pending = true -- pocket is being initialized, don't teleport there just yet
-- destination = a vector relative to the pocket's minp that is where new arrivals teleport tonumber
-- name = a name for the pocket.
-- owner = if set, this pocket is "owned" by this particular player.
-- protected = if true, this pocket is protected and only the owner can modify its contents
-- minp = the lower corner of the pocket's region
local pockets_by_hash = {}
local pockets_by_name = {}
local player_origin = {}
local pockets_deleted = {} -- record deleted pockets for possible later undeletion, indexed by hash
local personal_pockets = {} -- to be filled out if personal pockets are enabled
local protected_areas = AreaStore()
-------------------------------------------------------------------------------------
-- protection override
local old_is_protected = minetest.is_protected
function minetest.is_protected(pos, name)
if minetest.check_player_privs(name, "protection_bypass") then
return false
end
local protection = protected_areas:get_areas_for_pos(pos, false, true)
for _, area in pairs(protection) do
if area.data ~= name then
return true
end
end
return old_is_protected(pos, name)
end
--------------------------------------------------------------------------------------
-- Loading and saving data
local filename = minetest.get_worldpath() .. "/pocket_dimensions_data.lua"
local load_data = function()
local f, e = loadfile(filename)
if f then
local data = f()
pockets_by_hash = data.pockets_by_hash
pockets_by_name = {} -- to be filled from pockets_by_hash
player_origin = data.player_origin
pockets_deleted = data.pockets_deleted
if personal_pockets_enabled then
for hash, pocket_data in pairs(pockets_by_hash) do
if pocket_data.personal then
personal_pockets[pocket_data.personal] = pocket_data
end
end
end
else
return
end
-- validate and add saved protected areas
for hash, pocket_data in pairs(pockets_by_hash) do
if hash ~= minetest.hash_node_position(pocket_data.minp) then
minetest.log("error", "[pocket_dimensions] Hash mismatch for " .. tostring(hash) .. ", " .. dump(pocket_data))
pocket_data.minp = minetest.get_position_from_hash(hash)
end
pockets_by_name[string.lower(pocket_data.name)] = pocket_data
if pocket_data.protected then
protected_areas:insert_area(pocket_data.minp, vector.add(pocket_data.minp, mapblock_size), pocket_data.owner)
end
end
end
local save_data = function()
local data = {}
data.pockets_by_hash = pockets_by_hash
data.player_origin = player_origin
data.pockets_deleted = pockets_deleted
local file, e = io.open(filename, "w");
if not file then
return error(e);
end
file:write(minetest.serialize(data))
file:close()
end
load_data()
----------------------------------------------------------------------------------------
-- check if players have got out of pocket dimensions by other means and clear their origin locations
local since_last_check = 0
minetest.register_globalstep(function(dtime)
since_last_check = since_last_check + dtime
if since_last_check > 10 then
for name, _ in pairs(player_origin) do
local pos = minetest.get_player_by_name(name):get_pos()
if pos.y < layer_elevation or pos.y > layer_elevation + mapblock_size then
player_origin[name] = nil -- somehow, player escaped the pocket dimension layer
since_last_check = 0 -- note that we changed data
end
end
if since_last_check == 0 then
save_data()
return
end
since_last_check = 0
end
end)
-- returns a place to put players if they have no origin recorded
local get_fallback_origin = function()
local spawnpoint = minetest.setting_get_pos("static_spawnpoint")
if not spawnpoint then
local x = math.random()*1000 - 500
local z = math.random()*1000 - 500
local y =minetest.get_spawn_level(x,z)
spawnpoint = {x=x,y=y,z=z}
end
end
------------------------------------------------------------------------------------------
-- Teleport effects
local particle_node_pos_spread = vector.new(0.5,0.5,0.5)
local particle_user_pos_spread = vector.new(0.5,1.5,0.5)
local particle_speed_spread = vector.new(0.1,0.1,0.1)
local particle_poof = function(pos)
minetest.add_particlespawner({
amount = 100,
time = 0.1,
minpos = vector.subtract(pos, particle_node_pos_spread),
maxpos = vector.add(pos, particle_user_pos_spread),
minvel = particle_speed_spread,
maxvel = particle_speed_spread,
minacc = {x=0, y=0, z=0},
maxacc = {x=0, y=0, z=0},
minexptime = 0.1,
maxexptime = 0.5,
minsize = 1,
maxsize = 1,
collisiondetection = false,
vertical = false,
texture = "pocket_dimensions_spark.png",
})
end
local teleport_player = function(player, dest)
local source_pos = player:get_pos()
particle_poof(source_pos)
minetest.sound_play({name="pocket_dimensions_teleport_from"}, {pos = source_pos}, true)
player:set_pos(dest)
particle_poof(dest)
minetest.sound_play({name="pocket_dimensions_teleport_to"}, {pos = dest}, true)
end
-----------------------------------------------------------------
-- Border materials
@ -220,24 +79,10 @@ local get_border_def = function(override)
can_dig = function(pos, player) return false end,
on_blast = function(pos, intensity) return false end,
on_punch = function(pos, node, clicker, pointed_thing)
local clicker_pos = clicker:get_pos()
if vector.distance(pos, clicker_pos) > 2 then
if vector.distance(pos, clicker:get_pos()) > 2 then
return
end
local name = clicker:get_player_name()
local origin = player_origin[name]
if origin then
teleport_player(clicker, origin)
player_origin[name] = nil
save_data()
return
end
-- If the player's lost their origin data somehow, dump them somewhere using the spawn system to find an adequate place.
local spawnpoint = get_fallback_origin()
minetest.log("error", "[pocket_dimensions] Somehow "..name.." was at "..minetest.pos_to_string(clicker:get_pos())..
" inside a pocket dimension but they had no origin point recorded when they tried to leave. Sending them to "..
minetest.pos_to_string(spawnpoint).." as a fallback.")
teleport_player(clicker, spawnpoint)
return_player_to_origin(clicker:get_player_name())
end,
on_construct = function(pos)
-- if somehow a player gets ahold of one of these, ensure they can't place it anywhere.
@ -275,15 +120,14 @@ minetest.register_node("pocket_dimensions:border_gray", get_border_def({
local c_border_gray = minetest.get_content_id("pocket_dimensions:border_gray")
---------------------------------------------------------------
-- Pocket creation
-- Pocket mapgens
local spread_magnitude = mapblock_size
local scale_magnitude = 5
local perlin_params = {
offset = 0,
scale = scale_magnitude,
spread = {x = spread_magnitude, y = spread_magnitude, z = spread_magnitude},
spread = {x = pocket_size, y = pocket_size, z = pocket_size},
seed = 577,
octaves = 5,
persist = 0.63,
@ -297,16 +141,13 @@ local perlin_noise = PerlinNoise(perlin_params)
-- is world-seeded. Nobody will ever notice.
local terrain_map = PerlinNoiseMap(
perlin_params,
{x=mapblock_size, y=mapblock_size, z=mapblock_size}
{x=pocket_size, y=pocket_size, z=pocket_size}
)
-- Once the map block for the new pocket dimension is loaded, this initializes its node layout and finds a default spot for arrivals to teleport to
local emerge_grassy_callback = function(blockpos, action, calls_remaining, pocket_data)
if pocket_data.pending ~= true then
return
end
local grassy_mapgen = function(pocket_data)
local minp = pocket_data.minp
local maxp = vector.add(minp, mapblock_size)
local maxp = vector.add(minp, pocket_size)
local vm = minetest.get_voxel_manip(minp, maxp)
local emin, emax = vm:get_emerged_area()
local data = vm:get_data()
@ -315,7 +156,7 @@ local emerge_grassy_callback = function(blockpos, action, calls_remaining, pocke
local terrain_values = terrain_map:get_2d_map(minp)
-- Default is down on the floor of the border walls, in case default mod isn't installed and no landscape is created
local middlep = {x=minp.x + math.floor(mapblock_size/2), y=2, z=minp.z + math.floor(mapblock_size/2)}
local middlep = {x=minp.x + math.floor(pocket_size/2), y=2, z=minp.z + math.floor(pocket_size/2)}
local tree_spots = {}
for vi, x, y, z in area:iterp_xyz(minp, maxp) do
@ -334,7 +175,7 @@ local emerge_grassy_callback = function(blockpos, action, calls_remaining, pocke
end
end
if middlep.x == x and middlep.z == z then
middlep.y = math.max(y + 1 - minp.y, mapblock_size/2) -- surface of the ground or water in the center of the block
middlep.y = math.max(y + 1, minp.y + pocket_size/2) -- surface of the ground or water in the center of the block
end
elseif y == terrain_level - 1 then
if below_water then
@ -363,21 +204,14 @@ local emerge_grassy_callback = function(blockpos, action, calls_remaining, pocke
end
end
middlep.x = math.floor(mapblock_size/2)
middlep.z = math.floor(mapblock_size/2)
pocket_data.pending = nil
pocket_data.destination = middlep
save_data()
minetest.log("action", "[pocket_dimensions] Finished initializing terrain map for pocket dimension " .. pocket_data.name)
return middlep
end
local emerge_cave_callback = function(blockpos, action, calls_remaining, pocket_data)
if pocket_data.pending ~= true then
return
end
register_pocket_type("grassy", grassy_mapgen)
local cave_mapgen = function(pocket_data)
local minp = pocket_data.minp
local maxp = vector.add(minp, mapblock_size)
local maxp = vector.add(minp, pocket_size)
local vm = minetest.get_voxel_manip(minp, maxp)
local emin, emax = vm:get_emerged_area()
local data = vm:get_data()
@ -385,7 +219,7 @@ local emerge_cave_callback = function(blockpos, action, calls_remaining, pocket_
local terrain_values = terrain_map:get_3d_map(minp)
local nearest_to_center = vector.add(minp, 2) -- start off down in the corner
local center = vector.add(minp, math.floor(mapblock_size/2))
local center = vector.add(minp, math.floor(pocket_size/2))
for vi, x, y, z in area:iterp_xyz(minp, maxp) do
if x == minp.x or x == maxp.x or y == minp.y or y == maxp.y or z == minp.z or z == maxp.z then
data[vi] = c_border_gray
@ -411,76 +245,10 @@ local emerge_cave_callback = function(blockpos, action, calls_remaining, pocket_
end
nearest_to_center.y = nearest_to_center.y + 2
pocket_data.pending = nil
pocket_data.destination = vector.subtract(nearest_to_center, minp)
save_data()
minetest.log("action", "[pocket_dimensions] Finished initializing map for pocket dimension " .. pocket_data.name)
return nearest_to_center
end
local create_new_pocket = function(pocket_name, player_name, pocket_data_override)
pocket_data_override = pocket_data_override or {}
pocket_data_override.type = pocket_data_override.type or "grassy"
if pocket_name == nil or pocket_name == "" then
if player_name then
minetest.chat_send_player(player_name, S("Please provide a name for the pocket dimension"))
end
return
end
if pockets_by_name[string.lower(pocket_name)] then
if player_name then
minetest.chat_send_player(player_name, S("The name @1 is already in use.", pocket_name))
end
return
end
local count = 0
while count < 100 do
local x = math.random(0, block_grid_dimension) * mapblock_size + min_coordinate
local z = math.random(0, block_grid_dimension) * mapblock_size + min_coordinate
local pos = {x=x, y=layer_elevation, z=z}
local hash = minetest.hash_node_position(pos)
if pockets_by_hash[hash] == nil and pockets_deleted[hash] == nil then
local pocket_data = {pending=true, minp=pos, name=pocket_name}
pockets_by_hash[hash] = pocket_data
pockets_by_name[string.lower(pocket_name)] = pocket_data
for key, value in pairs(pocket_data_override) do
pocket_data[key] = value
end
if pocket_data.type == "grassy" then
minetest.emerge_area(pos, pos, emerge_grassy_callback, pocket_data)
else
minetest.emerge_area(pos, pos, emerge_cave_callback, pocket_data)
end
save_data()
minetest.chat_send_player(player_name, S("Pocket dimension @1 created", pocket_name))
minetest.log("action", "[pocket_dimensions] " .. player_name .. " Created a pocket dimension named " .. pocket_name .. " at " .. minetest.pos_to_string(pos))
return pocket_data
end
end
if player_name then
minetest.chat_send_player(player_name, S("Failed to find a new location for this pocket dimension."))
end
return nil
end
local teleport_player_to_pocket = function(player_name, pocket_name)
local pocket_data = pockets_by_name[string.lower(pocket_name)]
if pocket_data == nil or pocket_data.pending then
return false
end
local dest = vector.add(pocket_data.minp, pocket_data.destination)
local player = minetest.get_player_by_name(player_name)
if not player_origin[player_name] then
player_origin[player_name] = player:get_pos()
save_data()
end
teleport_player(player, dest)
return true
end
register_pocket_type("cave", cave_mapgen)
-------------------------------------------------------------------------------
@ -495,7 +263,7 @@ local get_select_formspec = function(player_name)
.."label[0.5,0.6;"..S("Link to pocket dimension:").."]dropdown[1,1;4,0.5;pocket_select;"
}
local names = {}
for name, def in pairs(pockets_by_name) do
for _, def in pairs(get_all_pockets()) do
table.insert(names, minetest.formspec_escape(def.name))
end
table.sort(names)
@ -530,7 +298,7 @@ minetest.register_node("pocket_dimensions:portal", {
local meta = minetest.get_meta(pos)
local pocket_dest = minetest.string_to_pos(meta:get_string("pocket_dest"))
if pocket_dest then
local pocket_data = pockets_by_hash[minetest.hash_node_position(pocket_dest)]
local pocket_data = pocket_containing_pos(pocket_dest)
if pocket_data then
teleport_player_to_pocket(player_name, pocket_data.name)
return
@ -559,7 +327,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
if fields.pocket_select then
for _, name in pairs(state.names) do
if fields.pocket_select == name then
local pocket_data = pockets_by_name[string.lower(name)] -- TODO: minetest.formspec_escape may screw up this lookup, do something different
local pocket_data = get_pocket(name) -- TODO: minetest.formspec_escape may screw up this lookup, do something different
if pocket_data then
local meta = minetest.get_meta(state.pos)
meta:set_string("pocket_dest", minetest.pos_to_string(pocket_data.minp))
@ -575,18 +343,6 @@ end)
-------------------------------------------------------------------------------
-- Admin commands
local update_protected = function(pocket_data)
-- clear any existing protection
protected = protected_areas:get_areas_for_pos(pocket_data.minp)
for id, _ in pairs(protected) do
protected_areas:remove_area(id)
end
-- add protection if warranted
if pocket_data.protected then
protected_areas:insert_area(pocket_data.minp, vector.add(pocket_data.minp, mapblock_size), pocket_data.owner)
end
end
local formspec_state = {}
local get_admin_formspec = function(player_name)
formspec_state[player_name] = formspec_state[player_name] or {row_index=1}
@ -601,32 +357,36 @@ local get_admin_formspec = function(player_name)
formspec[#formspec+1] = "tablecolumns[text,tooltip="..S("Name")
..";text,tooltip="..S("Owner")
..";text,tooltip="..S("Protected")
if personal_pockets then
if personal_pockets_enabled then
formspec[#formspec+1] = ";text,tooltip="..S("Personal")
end
formspec[#formspec+1] = "]table[0.5,1.0;7,5.75;pocket_table;"
local table_to_use = pockets_by_name
local delete_label = S("Delete")
local undelete_toggle = "false"
local table_to_use
local delete_label
local undelete_toggle
if state.undelete == "true" then
table_to_use = pockets_deleted
table_to_use = get_deleted_pockets()
delete_label = S("Undelete")
undelete_toggle = "true"
else
table_to_use = get_all_pockets()
delete_label = S("Delete")
undelete_toggle = "false"
end
local i = 0
for _, dimension_data in pairs(table_to_use) do
for _, pocket_data in pairs(table_to_use) do
i = i + 1
if i == state.row_index then
state.selected_data = dimension_data
state.selected_data = pocket_data
end
local owner = dimension_data.owner or "<none>"
formspec[#formspec+1] = minetest.formspec_escape(dimension_data.name)
local owner = pocket_data.owner or "<none>"
formspec[#formspec+1] = minetest.formspec_escape(pocket_data.name)
..",".. minetest.formspec_escape(owner)
..","..tostring(dimension_data.protected or "false")
if personal_pockets then
formspec[#formspec+1] = ","..tostring(dimension_data.personal)
..","..tostring(pocket_data.protected or "false")
if personal_pockets_enabled then
formspec[#formspec+1] = ","..tostring(pocket_data.personal)
end
formspec[#formspec+1] = ","
end
@ -689,13 +449,9 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end
if fields.rename and fields.pocket_name ~= pocket_data.name then
if pockets_by_name[string.lower(fields.pocket_name)] then
if not rename_pocket(pocket_data.name, fields.pocket_name)then
minetest.chat_send_player(player_name, S("A pocket dimension with that name already exists"))
else
pockets_by_name[string.lower(fields.pocket_name)] = pocket_data
pockets_by_name[string.lower(pocket_data.name)] = nil
pocket_data.name = fields.pocket_name
save_data()
refresh=true
end
end
@ -706,53 +462,27 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end
if fields.create then
if create_new_pocket(fields.pocket_name, player_name, {type=fields.create_type}) then
local success, message = create_pocket(fields.pocket_name, {type=fields.create_type})
if success then
refresh = true
end
minetest.chat_send_player(player_name, message)
end
if fields.delete then
local pocket_hash = minetest.hash_node_position(pocket_data.minp)
if state.undelete == "true" then
if pockets_by_name[string.lower(pocket_data.name)] then
minetest.chat_send_player(player_name, S("Cannot undelete, a pocket dimension with that name already exists"))
else
pockets_deleted[pocket_hash] = nil
pockets_by_name[string.lower(pocket_data.name)] = pocket_data
pockets_by_hash[pocket_hash] = pocket_data
if pocket_data.personal and personal_pockets and not personal_pockets[pocket_data.personal] then
-- it was a personal pocket and the player hasn't created a new one, so restore that association
personal_pockets[pocket_data.personal] = pocket_data
end
minetest.chat_send_player(player_name, S("Undeleted pocket dimension @1 at @2. Note that this doesn't affect the map, just moves this pocket dimension out of regular access and into the deleted list.", pocket_data.name, minetest.pos_to_string(pocket_data.minp)))
minetest.log("action", "[pocket_dimensions] " .. player_name .. " undeleted the pocket dimension " .. pocket_data.name .. " at " .. minetest.pos_to_string(pocket_data.minp))
end
local success, message = undelete_pocket(pocket_data)
minetest.chat_send_player(player_name, message)
else
pockets_deleted[pocket_hash] = pocket_data
pockets_by_name[string.lower(pocket_data.name)] = nil
pockets_by_hash[pocket_hash] = nil
if personal_pockets then
for name, personal_pocket_data in pairs(personal_pockets) do
if pocket_data == personal_pocket_data then
-- we're deleting a personal pocket, remove its record
personal_pockets[name] = nil
break
end
end
end
minetest.chat_send_player(player_name, S("Deleted pocket dimension @1 at @2. Note that this doesn't affect the map, just moves this pocket dimension out of regular access and into the deleted list.", pocket_data.name, minetest.pos_to_string(pocket_data.minp)))
minetest.log("action", "[pocket_dimensions] " .. player_name .. " deleted the pocket dimension " .. pocket_data.name .. " at " .. minetest.pos_to_string(pocket_data.minp))
local success, message = delete_pocket(pocket_data)
minetest.chat_send_player(player_name, message)
end
save_data()
state.row_index = 1
refresh = true
end
if fields.protect then
pocket_data.protected = not pocket_data.protected
update_protected(pocket_data)
minetest.log("action", "[pocket_dimensions] " .. player_name .. " set protection ownership of pocket dimension " .. pocket_data.name .. " to " .. tostring(pocket_data.protected))
save_data()
set_protection(pocket_data, not pocket_data.protected)
refresh = true
end
@ -762,11 +492,10 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
if fields.set_owner and pocket_data.owner ~= fields.owner then
if fields.owner == "" then
pocket_data.owner = nil
set_owner(pocket_data, nil)
else
pocket_data.owner = fields.owner
set_owner(pocket_data, fields.owner)
end
save_data()
refresh = true
end
@ -775,8 +504,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end
end)
-------------------------------------------------------------------------------------------------------
-- Player commands
--------------------------------------------------------------------------------------------------------
-- Personal pockets
if personal_pockets_enabled then
function teleport_to_pending(pocket_name, player_name, count)
@ -792,7 +521,7 @@ if personal_pockets_enabled then
end
local teleport_to_personal_pocket = function(player_name)
local pocket_data = personal_pockets[player_name]
local pocket_data = get_personal_pocket(player_name)
if pocket_data then
teleport_player_to_pocket(player_name, pocket_data.name)
return
@ -800,21 +529,24 @@ if personal_pockets_enabled then
-- Find an unused default name
local new_pocket_name = player_name
if pockets_by_name[string.lower(new_pocket_name)] then
if get_pocket(new_pocket_name) then
local count = 1
local new_pocket_name_prefix = new_pocket_name
new_pocket_name = new_pocket_name_prefix .. " " .. count
while pockets_by_name[string.lower(new_pocket_name)] do
while get_pocket(new_pocket_name) do
count = count + 1
new_pocket_name = new_pocket_name_prefix .. " " .. count
end
end
end
pocket_data = create_new_pocket(new_pocket_name, player_name, {protected=true, owner=player_name, personal=player_name, type="grassy"})
if pocket_data then
personal_pockets[player_name] = pocket_data
local success, message = create_pocket(new_pocket_name, {type="grassy"})
if success then
pocket_data = get_pocket(new_pocket_name)
set_personal_pocket(pocket_data, player_name)
set_protection(pocket_data, true)
teleport_to_pending(new_pocket_name, player_name, 1)
end
minetest.chat_send_player(player_name, message)
end
if personal_pockets_chat_command then
@ -934,30 +666,26 @@ if personal_pockets_enabled then
end
end
-------------------------------------------------------------------------------------------------------
-- Player commands
minetest.register_chatcommand("pocket_entry", {
params = "",
-- privs = {}, -- TODO a new privilege here?
description = S("Set the entry point of the pocket dimension you're in to where you're standing."),
func = function(player_name, param)
local pos = minetest.get_player_by_name(player_name):get_pos()
-- Find the pocket the player's in
for hash, pocket_data in pairs(pockets_by_hash) do
local pos_diff = vector.subtract(pos, pocket_data.minp)
if pos_diff.y >=0 and pos_diff.y <= mapblock_size and -- check y first to eliminate possibility player's not in a pocket dimension at all
pos_diff.x >=0 and pos_diff.x <= mapblock_size and
pos_diff.z >=0 and pos_diff.z <= mapblock_size then
if player_name == pocket_data.owner or minetest.check_player_privs(player_name, "server") then
pocket_data.destination = vector.round(pos_diff)
save_data()
minetest.chat_send_player(player_name, S("The entry point for pocket dimension @1 has been updated", pocket_data.name))
else
minetest.chat_send_player(player_name, S("You don't have permission to change the entry point of pocket dimension @1.", pocket_data.name))
end
return
end
local pocket_data = pocket_containing_pos(pos)
if not pocket_data then
minetest.chat_send_player(player_name, S("You're not inside a pocket dimension right now."))
return
end
minetest.chat_send_player(player_name, S("You're not inside a pocket dimension right now."))
if player_name ~= pocket_data.owner and not minetest.check_player_privs(player_name, "server") then
minetest.chat_send_player(player_name, S("You don't have permission to change the entry point of pocket dimension @1.", pocket_data.name))
return
end
set_destination(pocket_data, pos)
minetest.chat_send_player(player_name, S("The entry point for pocket dimension @1 has been updated", pocket_data.name))
end,
})
@ -971,56 +699,35 @@ minetest.register_chatcommand("pocket_rename", {
return
end
local pos = minetest.get_player_by_name(player_name):get_pos()
-- Find the pocket the player's in
for hash, pocket_data in pairs(pockets_by_hash) do
local pos_diff = vector.subtract(pos, pocket_data.minp)
if pos_diff.y >=0 and pos_diff.y <= mapblock_size and -- check y first to eliminate possibility player's not in a pocket dimension at all
pos_diff.x >=0 and pos_diff.x <= mapblock_size and
pos_diff.z >=0 and pos_diff.z <= mapblock_size then
if player_name == pocket_data.owner or minetest.check_player_privs(player_name, "server") then
if pockets_by_name[string.lower(param)] then
minetest.chat_send_player(player_name, S("A pocket dimension with that name already exists"))
else
minetest.chat_send_player(player_name, S("The name of pocket dimension @1 has been changed to \"@2\".", pocket_data.name, param))
pockets_by_name[string.lower(pocket_data.name)] = nil
pockets_by_name[string.lower(param)] = pocket_data
pocket_data.name = param
save_data()
end
else
minetest.chat_send_player(player_name, S("You don't have permission to change the name of pocket dimension @1.", pocket_data.name))
end
return
end
local pocket_data = pocket_containing_pos(pos)
if not pocket_data then
minetest.chat_send_player(player_name, S("You're not inside a pocket dimension right now."))
return
end
if player_name ~= pocket_data.owner and not minetest.check_player_privs(player_name, "server") then
minetest.chat_send_player(player_name, S("You don't have permission to change the name of pocket dimension @1.", pocket_data.name))
return
end
local oldname = pocket_data.name
if rename_pocket(oldname, param) then
minetest.chat_send_player(player_name, S("The name of pocket dimension @1 has been changed to \"@2\".", oldname, param))
else
minetest.chat_send_player(player_name, S("A pocket dimension with that name already exists"))
end
minetest.chat_send_player(player_name, S("You're not inside a pocket dimension right now."))
end,
})
minetest.register_chatcommand("pocket_name", {
params = "",
-- privs = {}, -- TODO a new privilege here?
description = S("Finds the name of the pocket dimension you're inside right now."),
func = function(player_name, param)
local pos = minetest.get_player_by_name(player_name):get_pos()
-- Find the pocket the player's in
for hash, pocket_data in pairs(pockets_by_hash) do
local pos_diff = vector.subtract(pos, pocket_data.minp)
if pos_diff.y >=0 and pos_diff.y <= mapblock_size and -- check y first to eliminate possibility player's not in a pocket dimension at all
pos_diff.x >=0 and pos_diff.x <= mapblock_size and
pos_diff.z >=0 and pos_diff.z <= mapblock_size then
minetest.chat_send_player(player_name, S("You're inside pocket dimension \"@1\"", pocket_data.name))
return
end
local pocket_data = pocket_containing_pos(pos)
if pocket_data then
minetest.chat_send_player(player_name, S("You're inside pocket dimension \"@1\"", pocket_data.name))
else
minetest.chat_send_player(player_name, S("You're not inside a pocket dimension right now."))
end
minetest.chat_send_player(player_name, S("You're not inside a pocket dimension right now."))
end,
})
pocket_dimensions.teleport_player_to_pocket = teleport_player_to_pocket
})

View File

@ -2,13 +2,19 @@
### init.lua ###
### api.lua ###
Boundary of a pocket dimension=
Please provide a name for the pocket dimension=
The name @1 is already in use.=
Pocket dimension @1 created=
Failed to find a new location for this pocket dimension.=
Deleted pocket dimension @1 at @2. Note that this doesn't affect the map, just moves this pocket dimension out of regular access and into the deleted list.=
Cannot undelete, a pocket dimension with the name @1 already exists=
Undeleted pocket dimension @1 at @2. Note that this doesn't affect the map, just moves this pocket dimension out of regular access and into the deleted list.=
### init.lua ###
Boundary of a pocket dimension=
Link to pocket dimension:=
Pocket Dimension Access=
Portal to @1=
@ -16,8 +22,8 @@ Name=
Owner=
Protected=
Personal=
Delete=
Undelete=
Delete=
Rename=
Create=
Teleport To=
@ -27,9 +33,6 @@ Show deleted=
Administrate pocket dimensions=
This command is for server admins only.=
A pocket dimension with that name already exists=
Cannot undelete, a pocket dimension with that name already exists=
Undeleted pocket dimension @1 at @2. Note that this doesn't affect the map, just moves this pocket dimension out of regular access and into the deleted list.=
Deleted pocket dimension @1 at @2. Note that this doesn't affect the map, just moves this pocket dimension out of regular access and into the deleted list.=
Teleport to personal pocket dimension @1 failed after @2 tries.=
Teleport to your personal pocket dimension=
This tool can be used @1 times before breaking.=
@ -43,12 +46,12 @@ Entering the "/pocket_personal" chat command=
You can return to this pocket dimension by:=
You have spawned inside your own personal pocket dimension.@nTo leave, walk to within one meter of the pocket dimension's@nboundary and punch the barrier there.=
Set the entry point of the pocket dimension you're in to where you're standing.=
The entry point for pocket dimension @1 has been updated=
You don't have permission to change the entry point of pocket dimension @1.=
You're not inside a pocket dimension right now.=
You don't have permission to change the entry point of pocket dimension @1.=
The entry point for pocket dimension @1 has been updated=
Renames the pocket dimension you're inside.=
Please provide a name as a parameter to this command.=
The name of pocket dimension @1 has been changed to "@2".=
You don't have permission to change the name of pocket dimension @1.=
The name of pocket dimension @1 has been changed to "@2".=
Finds the name of the pocket dimension you're inside right now.=
You're inside pocket dimension "@1"=