1 -- Prevent anyone else accessing those functions
2 local forceload_block = core.forceload_block
3 local forceload_free_block = core.forceload_free_block
4 core.forceload_block = nil
5 core.forceload_free_block = nil
7 local blocks_forceloaded
8 local blocks_temploaded = {}
9 local total_forceloaded = 0
11 -- true, if the forceloaded blocks got changed (flag for persistence on-disk)
12 local forceload_blocks_changed = false
14 local BLOCKSIZE = core.MAP_BLOCKSIZE
15 local function get_blockpos(pos)
17 x = math.floor(pos.x/BLOCKSIZE),
18 y = math.floor(pos.y/BLOCKSIZE),
19 z = math.floor(pos.z/BLOCKSIZE)}
22 -- When we create/free a forceload, it's either transient or persistent. We want
23 -- to add to/remove from the table that corresponds to the type of forceload, but
24 -- we also need the other table because whether we forceload a block depends on
26 -- This function returns the "primary" table we are adding to/removing from, and
28 local function get_relevant_tables(transient)
30 return blocks_temploaded, blocks_forceloaded
32 return blocks_forceloaded, blocks_temploaded
36 function core.forceload_block(pos, transient)
38 forceload_blocks_changed = true
40 local blockpos = get_blockpos(pos)
41 local hash = core.hash_node_position(blockpos)
42 local relevant_table, other_table = get_relevant_tables(transient)
43 if relevant_table[hash] ~= nil then
44 relevant_table[hash] = relevant_table[hash] + 1
46 elseif other_table[hash] ~= nil then
47 relevant_table[hash] = 1
49 if total_forceloaded >= (tonumber(core.settings:get("max_forceloaded_blocks")) or 16) then
52 total_forceloaded = total_forceloaded+1
53 relevant_table[hash] = 1
54 forceload_block(blockpos)
59 function core.forceload_free_block(pos, transient)
61 forceload_blocks_changed = true
63 local blockpos = get_blockpos(pos)
64 local hash = core.hash_node_position(blockpos)
65 local relevant_table, other_table = get_relevant_tables(transient)
66 if relevant_table[hash] == nil then return end
67 if relevant_table[hash] > 1 then
68 relevant_table[hash] = relevant_table[hash] - 1
69 elseif other_table[hash] ~= nil then
70 relevant_table[hash] = nil
72 total_forceloaded = total_forceloaded-1
73 relevant_table[hash] = nil
74 forceload_free_block(blockpos)
78 -- Keep the forceloaded areas after restart
79 local wpath = core.get_worldpath()
80 local function read_file(filename)
81 local f = io.open(filename, "r")
82 if f==nil then return {} end
83 local t = f:read("*all")
85 if t=="" or t==nil then return {} end
86 return core.deserialize(t) or {}
89 blocks_forceloaded = read_file(wpath.."/force_loaded.txt")
90 for _, __ in pairs(blocks_forceloaded) do
91 total_forceloaded = total_forceloaded + 1
94 core.after(5, function()
95 for hash, _ in pairs(blocks_forceloaded) do
96 local blockpos = core.get_position_from_hash(hash)
97 forceload_block(blockpos)
101 -- persists the currently forceloaded blocks to disk
102 local function persist_forceloaded_blocks()
103 local data = core.serialize(blocks_forceloaded)
104 core.safe_file_write(wpath.."/force_loaded.txt", data)
107 -- periodical forceload persistence
108 local function periodically_persist_forceloaded_blocks()
110 -- only persist if the blocks actually changed
111 if forceload_blocks_changed then
112 persist_forceloaded_blocks()
114 -- reset changed flag
115 forceload_blocks_changed = false
118 -- recheck after some time
119 core.after(10, periodically_persist_forceloaded_blocks)
122 -- persist periodically
123 core.after(5, periodically_persist_forceloaded_blocks)
125 -- persist on shutdown
126 core.register_on_shutdown(persist_forceloaded_blocks)