aboutsummaryrefslogtreecommitdiff
path: root/builtin/game/forceloading.lua
blob: e1e00920cf9eb891ae37526d22154e0c2415278d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
-- Prevent anyone else accessing those functions
local forceload_block = core.forceload_block
local forceload_free_block = core.forceload_free_block
core.forceload_block = nil
core.forceload_free_block = nil

local blocks_forceloaded
local blocks_temploaded = {}
local total_forceloaded = 0

-- true, if the forceloaded blocks got changed (flag for persistence on-disk)
local forceload_blocks_changed = false

local BLOCKSIZE = core.MAP_BLOCKSIZE
local function get_blockpos(pos)
	return {
		x = math.floor(pos.x/BLOCKSIZE),
		y = math.floor(pos.y/BLOCKSIZE),
		z = math.floor(pos.z/BLOCKSIZE)}
end

-- When we create/free a forceload, it's either transient or persistent. We want
-- to add to/remove from the table that corresponds to the type of forceload, but
-- we also need the other table because whether we forceload a block depends on
-- both tables.
-- This function returns the "primary" table we are adding to/removing from, and
-- the other table.
local function get_relevant_tables(transient)
	if transient then
		return blocks_temploaded, blocks_forceloaded
	else
		return blocks_forceloaded, blocks_temploaded
	end
end

function core.forceload_block(pos, transient)
	-- set changed flag
	forceload_blocks_changed = true

	local blockpos = get_blockpos(pos)
	local hash = core.hash_node_position(blockpos)
	local relevant_table, other_table = get_relevant_tables(transient)
	if relevant_table[hash] ~= nil then
		relevant_table[hash] = relevant_table[hash] + 1
		return true
	elseif other_table[hash] ~= nil then
		relevant_table[hash] = 1
	else
		if total_forceloaded >= (tonumber(core.settings:get("max_forceloaded_blocks")) or 16) then
			return false
		end
		total_forceloaded = total_forceloaded+1
		relevant_table[hash] = 1
		forceload_block(blockpos)
		return true
	end
end

function core.forceload_free_block(pos, transient)
	-- set changed flag
	forceload_blocks_changed = true

	local blockpos = get_blockpos(pos)
	local hash = core.hash_node_position(blockpos)
	local relevant_table, other_table = get_relevant_tables(transient)
	if relevant_table[hash] == nil then return end
	if relevant_table[hash] > 1 then
		relevant_table[hash] = relevant_table[hash] - 1
	elseif other_table[hash] ~= nil then
		relevant_table[hash] = nil
	else
		total_forceloaded = total_forceloaded-1
		relevant_table[hash] = nil
		forceload_free_block(blockpos)
	end
end

-- Keep the forceloaded areas after restart
local wpath = core.get_worldpath()
local function read_file(filename)
	local f = io.open(filename, "r")
	if f==nil then return {} end
	local t = f:read("*all")
	f:close()
	if t=="" or t==nil then return {} end
	return core.deserialize(t) or {}
end

local function write_file(filename, table)
	local f = io.open(filename, "w")
	f:write(core.serialize(table))
	f:close()
end

blocks_forceloaded = read_file(wpath.."/force_loaded.txt")
for _, __ in pairs(blocks_forceloaded) do
	total_forceloaded = total_forceloaded + 1
end

core.after(5, function()
	for hash, _ in pairs(blocks_forceloaded) do
		local blockpos = core.get_position_from_hash(hash)
		forceload_block(blockpos)
	end
end)

-- persists the currently forceloaded blocks to disk
local function persist_forceloaded_blocks()
	write_file(wpath.."/force_loaded.txt", blocks_forceloaded)
end

-- periodical forceload persistence
local function periodically_persist_forceloaded_blocks()

	-- only persist if the blocks actually changed
	if forceload_blocks_changed then
		persist_forceloaded_blocks()

		-- reset changed flag
		forceload_blocks_changed = false
	end

	-- recheck after some time
	core.after(10, periodically_persist_forceloaded_blocks)
end

-- persist periodically
core.after(5, periodically_persist_forceloaded_blocks)

-- persist on shutdown
core.register_on_shutdown(persist_forceloaded_blocks)