409 lines
9.8 KiB
Lua
409 lines
9.8 KiB
Lua
local store=minetest.get_mod_storage()
|
|
|
|
do -- DESTROY minetest's forceload limit with FACTS and LOGIC
|
|
local smt=getmetatable(minetest.settings)
|
|
local oldget=smt.get
|
|
local forceload_block=minetest.forceload_block
|
|
local unlimit
|
|
function smt:get(key,...)
|
|
if self==minetest.settings and key=="max_forceloaded_blocks" and unlimit then
|
|
return "1e1000"
|
|
end
|
|
return oldget(self,key,...)
|
|
end
|
|
function minetest.forceload_block(...)
|
|
unlimit=true
|
|
local ret=forceload_block(...)
|
|
unlimit=false
|
|
return ret
|
|
end
|
|
end
|
|
local forceload_block=minetest.forceload_block
|
|
local forceload_free_block=minetest.forceload_free_block
|
|
|
|
local function blockpos_to_id(bp)
|
|
return ("%i,%i,%i"):format(bp.x,bp.y,bp.z)
|
|
end
|
|
|
|
local function id_to_blockpos(i)
|
|
local x,y,z=i:match("([^,]+),([^,]+),([^,]+)")
|
|
x,y,z=tonumber(x),tonumber(y),tonumber(z)
|
|
assert(x and y and z,dump{x,y,z})
|
|
return vector.new(x,y,z)
|
|
end
|
|
|
|
local persistent_floads=minetest.deserialize(store:get_string("forceloads")) or {}
|
|
local forceloads={
|
|
blocks={},
|
|
floads={}
|
|
}
|
|
|
|
local floaded=false
|
|
minetest.after(0,function()
|
|
for name,block in pairs(persistent_floads) do
|
|
forceloads.blocks[block]=forceloads.blocks[block] or {}
|
|
forceloads.blocks[block][name]=1
|
|
forceloads.floads[name]=block
|
|
forceload_block(vector.multiply(id_to_blockpos(block),16),true)
|
|
end
|
|
floaded=true
|
|
end)
|
|
|
|
local planned=false
|
|
local function savepf()
|
|
if planned then return end
|
|
planned=true
|
|
minetest.after(0,function()
|
|
if not planned then return end
|
|
store:set_string("forceloads",minetest.serialize(persistent_floads))
|
|
planned=false
|
|
end)
|
|
end
|
|
|
|
local lib={}
|
|
|
|
lib.FORCELOAD_DURING_INIT="can't forceload during init"
|
|
lib.NO_SUCH_FORCELOAD="no such forceload"
|
|
|
|
local start,stop,query
|
|
|
|
start = function(id,blockpos,persistent)
|
|
if not floaded then
|
|
return false,lib.FORCELOAD_DURING_INIT
|
|
end
|
|
blockpos=vector.floor(blockpos)
|
|
assert(type(id)=="string","id must be string")
|
|
local bpi=blockpos_to_id(blockpos)
|
|
|
|
if forceloads.floads[id] then
|
|
if forceloads.floads[id]==bpi then
|
|
if (not persistent)~=(not persistent_floads[id]) then
|
|
persistent_floads[id]=persistent and bpi or nil
|
|
forceloads.blocks[bpi][id]=persistent and 1 or 0
|
|
savepf()
|
|
end
|
|
return true
|
|
end
|
|
stop(id)
|
|
end
|
|
|
|
forceloads.floads[id]=bpi
|
|
|
|
local block=forceloads.blocks[bpi] or {}
|
|
forceloads.blocks[bpi]=block
|
|
local wasfree=not next(block)
|
|
|
|
block[id]=persistent and 1 or 0
|
|
if persistent then
|
|
persistent_floads[id]=bpi
|
|
savepf()
|
|
end
|
|
|
|
local pos=vector.multiply(blockpos,16)
|
|
if wasfree then
|
|
assert(forceload_block(pos,true),"forceload failed! shame on minetest")
|
|
end
|
|
return true
|
|
end
|
|
|
|
stop = function(id)
|
|
local bpi=forceloads.floads[id]
|
|
if not bpi then
|
|
return false,lib.NO_SUCH_FORCELOAD
|
|
end
|
|
local block=forceloads.blocks[bpi] or {}
|
|
assert(block[id],"forceload inconsistency")
|
|
local persistent=block[id]==1
|
|
if persistent then
|
|
assert(persistent_floads[id],"persistent forceload inconsistency")
|
|
persistent_floads[id]=nil
|
|
savepf()
|
|
end
|
|
local wasfree=not next(block)
|
|
block[id]=nil
|
|
forceloads.floads[id]=nil
|
|
block=next(block) and block or nil
|
|
forceloads.blocks[bpi]=block
|
|
local blockpos=id_to_blockpos(bpi)
|
|
local pos=vector.multiply(blockpos,16)
|
|
if not (wasfree or block) then
|
|
forceload_free_block(pos,true)
|
|
end
|
|
return true
|
|
end
|
|
|
|
local function areaiter(id,op1,op2,fn)
|
|
for y=op1.y,op2.y do
|
|
for x=op1.x,op2.x do
|
|
for z=op1.z,op2.z do
|
|
local blockpos=vector.new(x,y,z)
|
|
if not vector.equals(blockpos,op1) and
|
|
not vector.equals(blockpos,op2) then
|
|
local name=("m%s:%s"):format(blockpos_to_id(blockpos),id)
|
|
local ret={fn(blockpos,name)}
|
|
if ret[1] then
|
|
return unpack(ret,2)
|
|
end
|
|
end
|
|
end end end
|
|
end
|
|
|
|
function lib.start(id,p1,D,E)
|
|
local p2,persistent
|
|
if type(D)=="table" and D.x then
|
|
p2,persistent=D,E
|
|
else
|
|
p2,persistent=p1,D
|
|
end
|
|
p1,p2=vector.floor(p1),vector.floor(p2)
|
|
p1,p2=vector.combine(p1,p2,math.min),vector.combine(p1,p2,math.max)
|
|
|
|
local oldblocks={}
|
|
local floads=forceloads.floads
|
|
local op1,op2=floads["a:"..id],floads["b:"..id]
|
|
op1=op1 and id_to_blockpos(op1)
|
|
op2=op2 and id_to_blockpos(op2)
|
|
if op2 then
|
|
oldblocks["b:"..id]=true
|
|
end
|
|
if op2 and not op1 then error("forceload area inconsistency") end
|
|
if op1 and op2 then
|
|
areaiter(id,op1,op2,function(blockpos,name)
|
|
assert(floads[name],"forceload area inconsistency")
|
|
oldblocks[name]=true
|
|
end)
|
|
end
|
|
|
|
local ok,err=start("a:"..id,p1,persistent)
|
|
if not ok then return ok,err end
|
|
if not vector.equals(p1,p2) then
|
|
assert(start("b:"..id,p2,persistent))
|
|
oldblocks["b:"..id]=nil
|
|
areaiter(id,p1,p2,function(blockpos,name)
|
|
assert(start(name,blockpos,persistent))
|
|
oldblocks[name]=nil
|
|
end)
|
|
end
|
|
|
|
for name,_ in pairs(oldblocks) do
|
|
assert(stop(name),"forceload area inconsistency")
|
|
end
|
|
return true
|
|
end
|
|
|
|
function lib.stop(id)
|
|
local floads=forceloads.floads
|
|
local op1,op2=floads["a:"..id],floads["b:"..id]
|
|
op1=op1 and id_to_blockpos(op1)
|
|
op2=op2 and id_to_blockpos(op2)
|
|
local ok,err=stop("a:"..id)
|
|
if not ok then return ok,err end
|
|
if op2 then
|
|
assert(stop("b:"..id),"forceload area inconsistency")
|
|
areaiter(id,op1,op2,function(blockpos,name)
|
|
assert(stop(name),"forceload area inconsistency")
|
|
end)
|
|
end
|
|
return true
|
|
end
|
|
|
|
local function ge(a,b) return a>=b and 0 or 1 end
|
|
local function le(a,b) return a<=b and 0 or 1 end
|
|
|
|
local function process_flist(fl)
|
|
local floads={}
|
|
for k,v in pairs(fl) do
|
|
local c=k:sub(1,1)
|
|
if c=="a" or c=="b" or c=="m" then
|
|
local ct,id=k:match("(.-):(.+)")
|
|
local mi,ma,pers
|
|
local fload=floads[id]
|
|
if not fload then
|
|
local op1,op2=forceloads.floads["a:"..id],forceloads.floads["b:"..id]
|
|
op1=op1 and id_to_blockpos(op1)
|
|
op2=op2 and id_to_blockpos(op2)
|
|
fload={
|
|
pos1=op1,
|
|
pos2=op2 or op1,
|
|
persistent=v.persistent
|
|
}
|
|
floads[id]=fload
|
|
end
|
|
elseif c=="i" then
|
|
local bpi,tra,n=k:match("i:(.-):(.-):(.+)")
|
|
local blockpos=id_to_blockpos(bpi)
|
|
n=tonumber(n) or error("wtf")
|
|
local id="(minetest)"..tra..minetest.pos_to_string(blockpos)
|
|
local fload=floads[id] or {
|
|
pos1 = v.blockpos,
|
|
pos2 = v.blockpos,
|
|
persistent = v.persistent,
|
|
count = -math.huge
|
|
}
|
|
floads[id]=fload
|
|
fload.count=math.max(n+1,fload.count)
|
|
end
|
|
end
|
|
return floads
|
|
end
|
|
|
|
query = function(obj)
|
|
if not obj then
|
|
local floads={}
|
|
for k,v in pairs(forceloads.floads) do
|
|
floads[k]=query(k)
|
|
end
|
|
return floads
|
|
elseif type(obj)=="string" then
|
|
local fload=forceloads.floads[obj]
|
|
return fload and {
|
|
blockpos = id_to_blockpos(fload),
|
|
persistent = forceloads.blocks[fload][obj]==1
|
|
} or nil
|
|
end
|
|
obj=vector.apply(obj,math.floor)
|
|
local bpi=blockpos_to_id(obj)
|
|
local floads={}
|
|
for k,v in pairs(forceloads.blocks[bpi] or {}) do
|
|
floads[k]={
|
|
blockpos = vector.new(obj),
|
|
persistent = v==1
|
|
}
|
|
end
|
|
return floads
|
|
end
|
|
|
|
function lib.query(obj,o2)
|
|
if not obj then
|
|
return process_flist(query())
|
|
elseif type(obj)=="string" then
|
|
local mt,tra,p=obj:match("(%(minetest%))(.)(.+)")
|
|
if mt then
|
|
local blockpos=minetest.string_to_pos(p)
|
|
local floads=process_flist(query(blockpos))
|
|
return floads[obj]
|
|
end
|
|
local id="a:"..obj
|
|
local floads=process_flist({[id]=query(id)})
|
|
return floads[obj]
|
|
end
|
|
if o2 then
|
|
local floads={}
|
|
for x=obj.x,o2.x do
|
|
for y=obj.y,o2.y do
|
|
for z=obj.z,o2.z do
|
|
for k,v in pairs(query(vector.new(x,y,z))) do
|
|
floads[k]=v
|
|
end
|
|
end end end
|
|
return process_flist(floads)
|
|
end
|
|
return process_flist(query(obj))
|
|
end
|
|
|
|
minetest.forceload_block=nil
|
|
minetest.forceload_free_block=nil
|
|
|
|
local function mtfbp(pos,transient,n)
|
|
local blockpos=vector.apply(vector.multiply(pos,1/16),math.floor)
|
|
local id=("i:%s:%s:%s"):format(blockpos_to_id(blockpos),transient and "T" or "P",n)
|
|
return id,blockpos
|
|
end
|
|
|
|
function minetest.forceload_block(pos,transient)
|
|
local n=0
|
|
local id,blockpos=mtfbp(pos,transient,n)
|
|
while query(id) do
|
|
n=n+1
|
|
id,blockpos=mtfbp(pos,transient,n)
|
|
end
|
|
return not not start(id,blockpos,not transient)
|
|
end
|
|
|
|
function minetest.forceload_free_block(pos,transient)
|
|
local n=0
|
|
local id=mtfbp(pos,transient,n)
|
|
while query(id) do
|
|
oldid=id
|
|
n=n+1
|
|
id=mtfbp(pos,transient,n)
|
|
end
|
|
if not oldid then return nil end
|
|
return not not (stop(oldid))
|
|
end
|
|
|
|
_G[minetest.get_current_modname()]=lib
|
|
|
|
---[[ TEST ('---[[' = enabled, '--[[' = disabled)
|
|
do
|
|
local function pwrap(fn)
|
|
return function(...)
|
|
local args={...}
|
|
local ret={xpcall(function()return fn(unpack(args))end,debug.traceback)}
|
|
if not ret[1] then
|
|
return unpack(ret)
|
|
end
|
|
return unpack(ret,2)
|
|
end
|
|
end
|
|
|
|
local function chatify(fname,fn)
|
|
return pwrap(function(name,param)
|
|
local ret={fn(unpack(minetest.deserialize(("return {%s}"):format(param))))}
|
|
return true,fname..": "..dump(ret)
|
|
end)
|
|
end
|
|
|
|
minetest.register_chatcommand("afl:start",{
|
|
params = "{...}",
|
|
privs = {privs=true},
|
|
func = chatify("start",function(id,blockpos,b2,...)
|
|
if type(b2)=="string" then
|
|
b2=assert(minetest.string_to_pos(b2))
|
|
end
|
|
return lib.start(id,minetest.string_to_pos(blockpos),b2,...)
|
|
end)
|
|
})
|
|
|
|
minetest.register_chatcommand("afl:stop",{
|
|
params = "{...}",
|
|
privs = {privs=true},
|
|
func = chatify("stop",lib.stop)
|
|
})
|
|
|
|
minetest.register_chatcommand("afl:forceload_block",{
|
|
params = "{...}",
|
|
privs = {privs=true},
|
|
func = chatify("forceload_block",minetest.forceload_block)
|
|
})
|
|
|
|
minetest.register_chatcommand("afl:forceload_free_block",{
|
|
params = "{...}",
|
|
privs = {privs=true},
|
|
func = chatify("forceload_free_block",minetest.forceload_free_block)
|
|
})
|
|
|
|
minetest.register_chatcommand("afl:query",{
|
|
params = "{...}",
|
|
privs = {privs=true},
|
|
func = chatify("query",lib.query)
|
|
})
|
|
|
|
minetest.register_chatcommand("afl:query_raw",{
|
|
params = "{...}",
|
|
privs = {privs=true},
|
|
func = chatify("query_raw",query)
|
|
})
|
|
|
|
for k,v in ipairs{"unknown","emerging","loaded","active"} do
|
|
minetest.register_chatcommand("afl:check_"..v,{
|
|
params="{...}",
|
|
privs = {privs=true},
|
|
func = chatify("check_"..v,function(bp)
|
|
return minetest.compare_block_status(vector.multiply(minetest.string_to_pos(bp),16),v)
|
|
end)
|
|
})
|
|
end
|
|
end
|
|
--]]
|