-
Notifications
You must be signed in to change notification settings - Fork 3
/
init.lua
240 lines (186 loc) · 7.87 KB
/
init.lua
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
rspawn = {}
rspawn.playerspawns = {}
local mpath = minetest.get_modpath("rspawn")
-- Water level, plus one to ensure we are above the sea.
local water_level = tonumber(minetest.settings:get("water_level", "0") )
local radial_step = 16
-- Setting with no namespace for interoperability
local static_spawnpoint = minetest.setting_get_pos("static_spawnpoint") or {x=0, y=0, z=0}
rspawn.admin = minetest.settings:get("name") or "" -- For messaging only
-- Setting from beds mod
rspawn.bedspawn = minetest.setting_getbool("enable_bed_respawn") ~= false -- from beds mod
-- rSpawn specific settings
rspawn.debug_on = minetest.settings:get_bool("rspawn.debug")
rspawn.spawnanywhere = minetest.settings:get_bool("rspawn.spawn_anywhere") ~= false
rspawn.kick_on_fail = minetest.settings:get_bool("rspawn.kick_on_fail") == true
rspawn.max_pregen_spawns = tonumber(minetest.settings:get("rspawn.max_pregen") or 5)
rspawn.search_radius = tonumber(minetest.settings:get("rspawn.search_radius") or 32)
rspawn.gen_frequency = tonumber(minetest.settings:get("rspawn.gen_frequency") or 30)
rspawn.spawn_block = minetest.settings:get("rspawn.spawn_block") or "default:dirt_with_grass"
rspawn.min_x = tonumber(minetest.settings:get("rspawn.min_x") or -31000)
rspawn.max_x = tonumber(minetest.settings:get("rspawn.max_x") or 31000)
rspawn.min_z = tonumber(minetest.settings:get("rspawn.min_z") or -31000)
rspawn.max_z = tonumber(minetest.settings:get("rspawn.max_z") or 31000)
dofile(mpath.."/lua/data.lua")
dofile(mpath.."/lua/guestlists.lua")
dofile(mpath.."/lua/commands.lua")
dofile(mpath.."/lua/forceload.lua")
dofile(mpath.."/lua/debugging.lua")
minetest.after(0,function()
if not minetest.registered_items[rspawn.spawn_block] then
rspawn.spawn_block = "default:dirt_with_grass"
end
end)
rspawn:spawnload()
local function set_default_node(pos)
if rspawn.spawn_block then
minetest.set_node(pos, {name=rspawn.spawn_block})
end
end
local function daylight_above(min_daylight, pos)
local level = minetest.get_node_light(pos, 0.5)
return min_daylight <= level
end
function rspawn:get_positions_for(pos, radius)
local breadth = radius
local altitude = water_level + radius
if rspawn.spawnanywhere then
altitude = radius
end
local pos1 = {x=pos.x-breadth, y=pos.y, z=pos.z-breadth}
local pos2 = {x=pos.x+breadth, y=pos.y+altitude, z=pos.z+breadth}
return pos1,pos2
end
function rspawn:newspawn(pos, radius)
-- Given a seed position and a radius, find an exact spawn location
-- that is an air node, walkable under it, non-walkable over it
-- bright during the day, and not leaves
rspawn:debug("Trying somewhere around "..minetest.pos_to_string(pos))
local pos1,pos2 = rspawn:get_positions_for(pos, radius)
rspawn:debug("Searching "..minetest.pos_to_string(pos1).." to "..minetest.pos_to_string(pos2))
local airnodes = minetest.find_nodes_in_area(pos1, pos2, {"air"})
local validnodes = {}
rspawn:debug("Found "..tostring(#airnodes).." air nodes within "..tostring(radius))
for _,anode in pairs(airnodes) do
local under = minetest.get_node( {x=anode.x, y=anode.y-1, z=anode.z} ).name
local over = minetest.get_node( {x=anode.x, y=anode.y+1, z=anode.z} ).name
under = minetest.registered_nodes[under]
over = minetest.registered_nodes[over]
if under == nil or over == nil then
-- `under` or `over` could be nil if a mod that defined that node was removed.
-- Not something this mod can resolve, and so we just ignore it.
rspawn:debug("Found an undefined node around "..minetest.pos_to_string(anode))
else
if under.walkable
and not over.walkable
and not minetest.is_protected(anode, "")
and not (under.groups and under.groups.leaves ) -- no spawning on treetops!
and daylight_above(7, anode) then
if under.buildable_to then
validnodes[#validnodes+1] = {x=anode.x, y=anode.y-1, z=anode.z}
else
validnodes[#validnodes+1] = anode
end
end
end
end
if #validnodes > 0 then
rspawn:debug("Valid spawn points found with radius "..tostring(radius))
local newpos = validnodes[math.random(1,#validnodes)]
return newpos
else
rspawn:debug("No valid air nodes")
end
end
function rspawn:genpos()
-- Generate a random position, and derive a new spawn position
local pos = static_spawnpoint
if rspawn.spawnanywhere then
pos = {
x = math.random(rspawn.min_x,rspawn.max_x),
y = water_level, -- always start at waterlevel
z = math.random(rspawn.min_z,rspawn.max_z),
}
end
return pos
end
function rspawn:set_player_spawn(name, newpos)
local tplayer = minetest.get_player_by_name(name)
if not tplayer then
return false
end
local spos = minetest.pos_to_string(newpos)
rspawn.debug("Saving spawn for "..name, spos)
rspawn.playerspawns[name] = newpos
rspawn:spawnsave()
minetest.chat_send_player(name, "New spawn set at "..spos)
tplayer:setpos(rspawn.playerspawns[name])
minetest.after(0.5,function()
set_default_node({x=newpos.x,y=newpos.y-1,z=newpos.z})
end)
return true
end
function rspawn:set_newplayer_spawn(player, attempts)
-- only use for new players / players who have never had a randomized spawn
if not player then return end
local playername = player:get_player_name()
if playername == "" then return end
if not rspawn.playerspawns[playername] then
local newpos = rspawn:get_next_spawn()
if newpos then
rspawn:set_player_spawn(playername, newpos)
else
-- We did not get a new position
if rspawn.kick_on_fail or attempts <= 0 then
minetest.kick_player(playername, "No personalized spawn points available - please try again later.")
else
minetest.chat_send_player(playername, "Could not get custom spawn! Retrying in "..rspawn.gen_frequency.." seconds")
minetest.chat_send_player(rspawn.admin, "Exhausted spawns! Could not spawn "..playername)
minetest.log("warning", "rspawn -- Exhausted spawns! Could not spawn "..playername)
minetest.after(rspawn.gen_frequency, function()
rspawn:set_newplayer_spawn(player, attempts-1)
end)
end
end
end
end
function rspawn:renew_player_spawn(playername)
local player = minetest.get_player_by_name(playername)
if not player then
return false
end
local newpos = rspawn:get_next_spawn()
if newpos then
return rspawn:set_player_spawn(playername, newpos)
else
minetest.chat_send_player(playername, "Could not get custom spawn!")
return false
end
end
minetest.register_on_joinplayer(function(player)
rspawn:set_newplayer_spawn(player, 5)
end)
minetest.register_on_respawnplayer(function(player)
-- return true to disable further respawn placement
local name = player:get_player_name()
if rspawn.bedspawn == true and beds.spawn then
local pos = beds.spawn[name]
if pos then
minetest.log("action", name.." respawns at "..minetest.pos_to_string(pos))
player:setpos(pos)
return true
end
end
local pos = rspawn.playerspawns[name]
-- And if no bed, nor bed spwawning not active:
if pos then
minetest.log("action", name.." respawns at "..minetest.pos_to_string(pos))
player:setpos(pos)
return true
else
minetest.chat_send_player(name, "Failed to find your spawn point!")
minetest.log("warning", "rspawn -- Could not find spawn point for "..name)
return false
end
end)
dofile(mpath.."/lua/pregeneration.lua")