advfload-cd2025/init.lua
2022-12-03 13:49:21 +05:00

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
--]]