2 Commits

Author SHA1 Message Date
0000cfb474 Remove old vector_extras code and use the MapDataStorage 2020-04-14 08:49:21 +02:00
9aa0e6ab3d Add MapDataStorage code as replacement for get_data_from_pos etc.
In comparison to using Minetest's hash_node_position, this saves additional information about the z-only and zy-only components, which is what the vector_extras code did.
In comparison to the old vector_extras code, this does not create lots of lua tables, which is slow, but instead uses one table as hashmap.
2020-04-14 08:49:21 +02:00
7 changed files with 341 additions and 79 deletions

View File

@ -1,11 +1,11 @@
# MoreMesecons
Based on Mesecons by Jeija
By @paly2 and @HybridDog
Based on Mesecons by Jeija <br/>
By @paly2 and @HybridDog <br/>
With the participation of @LeMagnesium (bugfix), @Ataron (textures), @JAPP (texture).
Dependencies: [Mesecons](https://github.com/Jeija/minetest-mod-mesecons/)
Optional dependencies: [vector_extras](https://github.com/HybridDog/vector_extras/) [digilines](https://github.com/minetest-mods/digilines)
Dependencies: [Mesecons](https://github.com/Jeija/minetest-mod-mesecons/) <br/>
Optional dependencies: [digilines](https://github.com/minetest-mods/digilines)
MoreMesecons is a mod for minetest which adds some mesecons items.

View File

@ -1,45 +1,35 @@
-- see wireless jammer
local get = vector.get_data_from_pos
local set = vector.set_data_to_pos
local remove = vector.remove_data_from_pos
local storage = minetest.get_mod_storage()
local jammers = minetest.deserialize(storage:get_string("jammers")) or {}
local jammers = moremesecons.load_MapDataStorage_legacy(storage, "jammers_v2",
"jammers")
local function update_mod_storage()
storage:set_string("jammers", minetest.serialize(jammers))
storage:set_string("jammers_v2", jammers:serialize())
end
local function add_jammer(pos)
if get(jammers, pos.z,pos.y,pos.x) then
if jammers:getAt(pos) then
return
end
set(jammers, pos.z,pos.y,pos.x, true)
jammers:setAt(pos, true)
update_mod_storage()
end
local function remove_jammer(pos)
remove(jammers, pos.z,pos.y,pos.x)
jammers:removeAt(pos)
update_mod_storage()
end
local function is_jammed(pos)
local JAMMER_MAX_DISTANCE = moremesecons.setting("jammer", "max_distance", 10, 1)
local pz,py,px = vector.unpack(pos)
for z,yxs in pairs(jammers) do
if math.abs(pz-z) <= JAMMER_MAX_DISTANCE then
for y,xs in pairs(yxs) do
if math.abs(py-y) <= JAMMER_MAX_DISTANCE then
for x in pairs(xs) do
if math.abs(px-x) <= JAMMER_MAX_DISTANCE
and (px-x)^2+(py-y)^2+(pz-z)^2 <= JAMMER_MAX_DISTANCE^2 then
return true
end
end
end
end
local minp = vector.subtract(pos, JAMMER_MAX_DISTANCE)
local maxp = vector.add(pos, JAMMER_MAX_DISTANCE)
for p in jammers:iter(minp, maxp) do
local d = vector.subtract(pos, p)
if d.x ^ 2 + d.y ^ 2 + d.z ^ 2 <= JAMMER_MAX_DISTANCE^2 then
return true
end
end
return false

View File

@ -1,7 +1,3 @@
--[[
vector_extras there: https://github.com/HybridDog/vector_extras
]]
local templates = {MoreMesecons = {
logic = [[-- AND
port.a = pin.b and pin.c

View File

@ -1,19 +1,19 @@
local storage = minetest.get_mod_storage()
local teleporters = minetest.deserialize(storage:get_string("teleporters")) or {}
local teleporters_rids = minetest.deserialize(storage:get_string("teleporters_rids")) or {}
local jammers = minetest.deserialize(storage:get_string("jammers")) or {}
local teleporters_rids = moremesecons.load_MapDataStorage_legacy(storage,
"teleporters_rids_v2", "teleporters_rids")
local function update_mod_storage()
storage:set_string("teleporters", minetest.serialize(teleporters))
storage:set_string("teleporters_rids", minetest.serialize(teleporters_rids))
storage:set_string("teleporters_rids_v2", teleporters_rids:serialize())
end
local function register(pos)
if not vector.get_data_from_pos(teleporters_rids, pos.z,pos.y,pos.x) then
if not teleporters_rids:getAt(pos) then
table.insert(teleporters, pos)
vector.set_data_to_pos(teleporters_rids, pos.z,pos.y,pos.x, #teleporters)
teleporters_rids:setAt(pos, #teleporters)
update_mod_storage()
end
end
@ -89,10 +89,10 @@ minetest.register_node("moremesecons_teleporter:teleporter", {
sounds = default.node_sound_stone_defaults(),
on_construct = register,
on_destruct = function(pos)
local RID = vector.get_data_from_pos(teleporters_rids, pos.z,pos.y,pos.x)
local RID = teleporters_rids:getAt(pos)
if RID then
table.remove(teleporters, RID)
vector.remove_data_from_pos(teleporters_rids, pos.z,pos.y,pos.x)
teleporters_rids:removeAt(pos)
update_mod_storage()
end
end,

View File

@ -1 +0,0 @@
vector_extras?

View File

@ -52,53 +52,328 @@ function moremesecons.remove_data_from_pos(sto, pos)
sto.storage:set_string(sto.name, minetest.serialize(sto.tab))
end
-- Vector helpers
-- All the following functions are from the vector_extras mod (https://github.com/HybridDog/vector_extras).
-- If you enable that mod, its functions will be used instead of the ones defined below
-- Some additional vector helpers
if not vector.get_data_from_pos then
function vector.get_data_from_pos(tab, z,y,x)
local data = tab[z]
if data then
data = data[y]
if data then
return data[x]
end
end
end
-- The same as minetest.hash_node_position; I copied it to ensure backwards
-- compatibility and used hexadecimal number notation
local function node_position_key(pos)
return (pos.z + 0x8000) * 0x10000 * 0x10000
+ (pos.y + 0x8000) * 0x10000
+ pos.x + 0x8000
end
if not vector.set_data_to_pos then
function vector.set_data_to_pos(tab, z,y,x, data)
if tab[z] then
if tab[z][y] then
tab[z][y][x] = data
local MapDataStorage = {}
setmetatable(MapDataStorage, {__call = function()
local obj = {}
setmetatable(obj, MapDataStorage)
return obj
end})
MapDataStorage.__index = {
getAt = function(self, pos)
return self[node_position_key(pos)]
end,
setAt = function(self, pos, data)
-- If x, y or z is omitted, the key corresponds to a position outside
-- of the map (hopefully), so it can be used to skip lines and planes
local vi_z = (pos.z + 0x8000) * 0x10000 * 0x10000
local vi_zy = vi_z + (pos.y + 0x8000) * 0x10000
local vi = vi_zy + pos.x + 0x8000
local is_new = self[vi] == nil
self[vi] = data
if is_new then
self[vi_z] = (self[vi_z] or 0) + 1
self[vi_zy] = (self[vi_zy] or 0) + 1
end
end,
setAtI = function(self, vi, data)
local vi_zy = vi - vi % 0x10000
local vi_z = vi - vi % (0x10000 * 0x10000)
local is_new = self[vi] == nil
self[vi] = data
if is_new then
self[vi_z] = (self[vi_z] or 0) + 1
self[vi_zy] = (self[vi_zy] or 0) + 1
end
end,
removeAt = function(self, pos)
local vi_z = (pos.z + 0x8000) * 0x10000 * 0x10000
local vi_zy = vi_z + (pos.y + 0x8000) * 0x10000
local vi = vi_zy + pos.x + 0x8000
if self[vi] == nil then
-- Nothing to remove
return
end
self[vi] = nil
-- Update existence information for the xy plane and x line
self[vi_z] = self[vi_z] - 1
if self[vi_z] == 0 then
self[vi_z] = nil
self[vi_zy] = nil
return
end
self[vi_zy] = self[vi_zy] - 1
if self[vi_zy] == 0 then
self[vi_zy] = nil
end
end,
iter = function(self, pos1, pos2)
local ystride = 0x10000
local zstride = 0x10000 * 0x10000
-- Skip z values where no data can be found
pos1 = vector.new(pos1)
local vi_z = (pos1.z + 0x8000) * 0x10000 * 0x10000
while not self[vi_z] do
pos1.z = pos1.z + 1
vi_z = vi_z + zstride
if pos1.z > pos2.z then
-- There are no values to iterate through
return function() return end
end
end
-- Skipping y values is not yet implemented and may require much code
local xrange = pos2.x - pos1.x + 1
local yrange = pos2.y - pos1.y + 1
local zrange = pos2.z - pos1.z + 1
-- x-only and y-only parts of the vector index of pos1
local vi_y = (pos1.y + 0x8000) * 0x10000
local vi_x = pos1.x + 0x8000
local y = 0
local z = 0
local vi = node_position_key(pos1)
local pos = vector.new(pos1)
local nextaction = vi + xrange
pos.x = pos.x - 1
vi = vi - 1
local function iterfunc()
-- continue along x until it needs to jump
vi = vi + 1
pos.x = pos.x + 1
if vi ~= nextaction then
local v = self[vi]
if v == nil then
-- No data here
return iterfunc()
end
-- The returned position must not be changed
return pos, v
end
-- Reset x position
vi = vi - xrange
-- Go along y until pos2.y is exceeded
while true do
y = y + 1
pos.y = pos.y + 1
-- Set vi to index(pos1.x, pos1.y + y, pos1.z + z)
vi = vi + ystride
if y == yrange then
break
end
if self[vi - vi_x] then
nextaction = vi + xrange
vi = vi - 1
pos.x = pos1.x - 1
return iterfunc()
end
-- Nothing along this x line, so increase y again
end
-- Go back along y
vi = vi - yrange * ystride
y = 0
pos.y = pos1.y
-- Go along z until pos2.z is exceeded
while true do
z = z + 1
pos.z = pos.z + 1
vi = vi + zstride
if z == zrange then
-- Cuboid finished, return nil
return
end
if self[vi - vi_x - vi_y] then
y = 0
nextaction = vi + xrange
vi = vi - 1
pos.x = pos1.x - 1
return iterfunc()
end
-- Nothing in this xy plane, so increase z again
end
end
return iterfunc
end,
iterAll = function(self)
local previous_vi = nil
local function iterfunc()
local vi, v = next(self, previous_vi)
previous_vi = vi
if not vi then
return
end
tab[z][y] = {[x] = data}
return
local z = math.floor(vi / (0x10000 * 0x10000))
vi = vi - z * 0x10000 * 0x10000
local y = math.floor(vi / 0x10000)
if y == 0 or z == 0 then
-- The index does not refer to a position inside the map
return iterfunc()
end
local x = vi - y * 0x10000 - 0x8000
y = y - 0x8000
z = z - 0x8000
return {x=x, y=y, z=z}, v
end
tab[z] = {[y] = {[x] = data}}
return iterfunc
end,
serialize = function(self)
local serialize_data = minetest.serialize
local indices = {}
local values = {}
local i = 1
for pos, v in self:iterAll() do
local vi = node_position_key(pos)
-- Convert the double reversible to a string;
-- minetest.serialize does not (yet) do this
indices[i] = ("%a"):format(vi)
values[i] = serialize_data(v)
end
result = {
version = "MapDataStorage_v1",
indices = "return {" .. table.concat(indices, ",") .. "}",
values = "return {" .. table.concat(values, ",") .. "}",
}
return minetest.serialize(result)
end,
}
MapDataStorage.deserialize = function(txtdata)
local data = minetest.deserialize(txtdata)
if data.version ~= "MapDataStorage_v1" then
minetest.log("error", "Unknown MapDataStorage version: " ..
data.version)
end
-- I assume that minetest.deserialize correctly deserializes the indices,
-- which are in the %a format
indices = minetest.deserialize(data.indices)
values = minetest.deserialize(data.values)
data = MapDataStorage()
for i = 1,#indices do
local vi = indices[i]
local v = values[i]
data:setAtI(vi, v)
end
return data
end
moremesecons.MapDataStorage = MapDataStorage
-- Legacy
-- vector_extras there: https://github.com/HybridDog/vector_extras
-- Creates a MapDataStorage object from old vector_extras generated table
function moremesecons.load_old_data_from_pos(t)
local data = MapDataStorage()
for z, yxv in pairs(t) do
for y, xv in pairs(yxv) do
for x, v in pairs(xv) do
data:setAt({x=x, y=y, z=z}, v)
end
end
end
return data
end
if not vector.remove_data_from_pos then
function vector.remove_data_from_pos(tab, z,y,x)
if vector.get_data_from_pos(tab, z,y,x) == nil then
return
end
tab[z][y][x] = nil
if not next(tab[z][y]) then
tab[z][y] = nil
end
if not next(tab[z]) then
tab[z] = nil
end
function moremesecons.load_old_dfp_storage(modstorage, name)
local data = minetest.deserialize(modstorage:get_string(name))
if not data then
return
end
return moremesecons.load_old_data_from_pos(data)
end
if not vector.unpack then
function vector.unpack(pos)
return pos.z, pos.y, pos.x
function moremesecons.load_MapDataStorage_legacy(modstorage, name, oldname)
local t_old = moremesecons.load_old_dfp_storage(modstorage, oldname)
local t
if t_old and t_old ~= "" then
t = t_old
modstorage:set_string(name, t:serialize())
modstorage:set_string(oldname, nil)
return t
end
t = modstorage:get_string("teleporters_rids_v2")
if t and t ~= "" then
return MapDataStorage.deserialize(t)
end
return MapDataStorage()
end
-- This testing code shows an example usage of the MapDataStorage code
local function do_test()
print("Test if iter returns correct positions when a lot is set")
local data = MapDataStorage()
local k = 0
for x = -5, 3 do
for y = -5, 3 do
for z = -5, 3 do
k = k + 1
data:setAt({x=x, y=y, z=z}, k)
end
end
end
local expected_positions = {}
for z = -4, 2 do
for y = -4, 2 do
for x = -4, 2 do
expected_positions[#expected_positions+1] = {x=x, y=y, z=z}
end
end
end
local i = 0
for pos, v in data:iter({x=-4, y=-4, z=-4}, {x=2, y=2, z=2}) do
i = i + 1
assert(vector.equals(pos, expected_positions[i]))
end
print("Test if iter works correctly on a corner")
local found = false
for pos, v in data:iter({x=-8, y=-7, z=-80}, {x=-5, y=-5, z=-5}) do
assert(not found)
found = true
assert(vector.equals(pos, {x=-5, y=-5, z=-5}))
end
assert(found)
print("Test if iter finds all corners")
local expected_positions = {}
local k = 1
for _, z in ipairs({-9, -6}) do
for _, y in ipairs({-9, -6}) do
for _, x in ipairs({-8, -6}) do
local pos = {x=x, y=y, z=z}
expected_positions[#expected_positions+1] = pos
data:setAt(pos, k)
k = k + 1
end
end
end
local i = 1
for pos, v in data:iter({x=-8, y=-9, z=-9}, {x=-6, y=-6, z=-6}) do
assert(v == i)
assert(vector.equals(pos, expected_positions[i]))
i = i + 1
--~ print("found " .. minetest.pos_to_string(pos))
end
assert(i == 8 + 1, "Not enough or too many corners found")
--~ data:iterAll()
end
--~ do_test()

View File

@ -458,16 +458,18 @@ if storage:get_string("wireless_meta_2") == "" then
minetest.log("action", "[moremesecons_wireless] Migrating mod storage data...")
local jammers_1 = minetest.deserialize(storage:get_string("jammers"))
local get = vector.get_data_from_pos
local set = vector.set_data_to_pos
local remove = vector.remove_data_from_pos
local get = function(t, pos)
-- FIXME: this does not test explicitly for false,
-- but channel is never false
return t[pos.z] and t[pos.z][pos.y] and t[pos.z][pos.y][pos.x]
end
for z, data_z in pairs(wireless_meta_1.owners) do
for y, data_y in pairs(data_z) do
for x, owner in pairs(data_y) do
local pos = {x = x, y = y, z = z}
set_owner(pos, owner)
set_channel(pos, get(wireless_meta_1.channels, z,y,x))
set_channel(pos, get(wireless_meta_1.channels, pos))
end
end
end