]> git.lizzy.rs Git - dragonfireclient.git/blob - builtin/game/misc.lua
9f5e3312bb42077a86d9579787162b09a23c93c7
[dragonfireclient.git] / builtin / game / misc.lua
1 -- Minetest: builtin/misc.lua
2
3 local S = core.get_translator("__builtin")
4
5 --
6 -- Misc. API functions
7 --
8
9 -- @spec core.kick_player(String, String) :: Boolean
10 function core.kick_player(player_name, reason)
11         if type(reason) == "string" then
12                 reason = "Kicked: " .. reason
13         else
14                 reason = "Kicked."
15         end
16         return core.disconnect_player(player_name, reason)
17 end
18
19 function core.check_player_privs(name, ...)
20         if core.is_player(name) then
21                 name = name:get_player_name()
22         elseif type(name) ~= "string" then
23                 error("core.check_player_privs expects a player or playername as " ..
24                         "argument.", 2)
25         end
26
27         local requested_privs = {...}
28         local player_privs = core.get_player_privs(name)
29         local missing_privileges = {}
30
31         if type(requested_privs[1]) == "table" then
32                 -- We were provided with a table like { privA = true, privB = true }.
33                 for priv, value in pairs(requested_privs[1]) do
34                         if value and not player_privs[priv] then
35                                 missing_privileges[#missing_privileges + 1] = priv
36                         end
37                 end
38         else
39                 -- Only a list, we can process it directly.
40                 for key, priv in pairs(requested_privs) do
41                         if not player_privs[priv] then
42                                 missing_privileges[#missing_privileges + 1] = priv
43                         end
44                 end
45         end
46
47         if #missing_privileges > 0 then
48                 return false, missing_privileges
49         end
50
51         return true, ""
52 end
53
54
55 function core.send_join_message(player_name)
56         if not core.is_singleplayer() then
57                 core.chat_send_all("*** " .. S("@1 joined the game.", player_name))
58         end
59 end
60
61
62 function core.send_leave_message(player_name, timed_out)
63         local announcement = "*** " .. S("@1 left the game.", player_name)
64         if timed_out then
65                 announcement = "*** " .. S("@1 left the game (timed out).", player_name)
66         end
67         core.chat_send_all(announcement)
68 end
69
70
71 core.register_on_joinplayer(function(player)
72         local player_name = player:get_player_name()
73         if not core.is_singleplayer() then
74                 local status = core.get_server_status(player_name, true)
75                 if status and status ~= "" then
76                         core.chat_send_player(player_name, status)
77                 end
78         end
79         core.send_join_message(player_name)
80 end)
81
82
83 core.register_on_leaveplayer(function(player, timed_out)
84         local player_name = player:get_player_name()
85         core.send_leave_message(player_name, timed_out)
86 end)
87
88
89 function core.is_player(player)
90         -- a table being a player is also supported because it quacks sufficiently
91         -- like a player if it has the is_player function
92         local t = type(player)
93         return (t == "userdata" or t == "table") and
94                 type(player.is_player) == "function" and player:is_player()
95 end
96
97
98 function core.player_exists(name)
99         return core.get_auth_handler().get_auth(name) ~= nil
100 end
101
102
103 -- Returns two position vectors representing a box of `radius` in each
104 -- direction centered around the player corresponding to `player_name`
105
106 function core.get_player_radius_area(player_name, radius)
107         local player = core.get_player_by_name(player_name)
108         if player == nil then
109                 return nil
110         end
111
112         local p1 = player:get_pos()
113         local p2 = p1
114
115         if radius then
116                 p1 = vector.subtract(p1, radius)
117                 p2 = vector.add(p2, radius)
118         end
119
120         return p1, p2
121 end
122
123
124 -- To be overriden by protection mods
125
126 function core.is_protected(pos, name)
127         return false
128 end
129
130
131 function core.record_protection_violation(pos, name)
132         for _, func in pairs(core.registered_on_protection_violation) do
133                 func(pos, name)
134         end
135 end
136
137 -- To be overridden by Creative mods
138
139 local creative_mode_cache = core.settings:get_bool("creative_mode")
140 function core.is_creative_enabled(name)
141         return creative_mode_cache
142 end
143
144 -- Checks if specified volume intersects a protected volume
145
146 function core.is_area_protected(minp, maxp, player_name, interval)
147         -- 'interval' is the largest allowed interval for the 3D lattice of checks.
148
149         -- Compute the optimal float step 'd' for each axis so that all corners and
150         -- borders are checked. 'd' will be smaller or equal to 'interval'.
151         -- Subtracting 1e-4 ensures that the max co-ordinate will be reached by the
152         -- for loop (which might otherwise not be the case due to rounding errors).
153
154         -- Default to 4
155         interval = interval or 4
156         local d = {}
157
158         for _, c in pairs({"x", "y", "z"}) do
159                 if minp[c] > maxp[c] then
160                         -- Repair positions: 'minp' > 'maxp'
161                         local tmp = maxp[c]
162                         maxp[c] = minp[c]
163                         minp[c] = tmp
164                 end
165
166                 if maxp[c] > minp[c] then
167                         d[c] = (maxp[c] - minp[c]) /
168                                 math.ceil((maxp[c] - minp[c]) / interval) - 1e-4
169                 else
170                         d[c] = 1 -- Any value larger than 0 to avoid division by zero
171                 end
172         end
173
174         for zf = minp.z, maxp.z, d.z do
175                 local z = math.floor(zf + 0.5)
176                 for yf = minp.y, maxp.y, d.y do
177                         local y = math.floor(yf + 0.5)
178                         for xf = minp.x, maxp.x, d.x do
179                                 local x = math.floor(xf + 0.5)
180                                 local pos = vector.new(x, y, z)
181                                 if core.is_protected(pos, player_name) then
182                                         return pos
183                                 end
184                         end
185                 end
186         end
187         return false
188 end
189
190
191 local raillike_ids = {}
192 local raillike_cur_id = 0
193 function core.raillike_group(name)
194         local id = raillike_ids[name]
195         if not id then
196                 raillike_cur_id = raillike_cur_id + 1
197                 raillike_ids[name] = raillike_cur_id
198                 id = raillike_cur_id
199         end
200         return id
201 end
202
203
204 -- HTTP callback interface
205
206 core.set_http_api_lua(function(httpenv)
207         httpenv.fetch = function(req, callback)
208                 local handle = httpenv.fetch_async(req)
209
210                 local function update_http_status()
211                         local res = httpenv.fetch_async_get(handle)
212                         if res.completed then
213                                 callback(res)
214                         else
215                                 core.after(0, update_http_status)
216                         end
217                 end
218                 core.after(0, update_http_status)
219         end
220
221         return httpenv
222 end)
223 core.set_http_api_lua = nil
224
225
226 function core.close_formspec(player_name, formname)
227         return core.show_formspec(player_name, formname, "")
228 end
229
230
231 function core.cancel_shutdown_requests()
232         core.request_shutdown("", false, -1)
233 end
234
235
236 -- Used for callback handling with dynamic_add_media
237 core.dynamic_media_callbacks = {}
238
239
240 -- Transfer of certain globals into async environment
241 -- see builtin/async/game.lua for the other side
242
243 local function copy_filtering(t, seen)
244         if type(t) == "userdata" or type(t) == "function" then
245                 return true -- don't use nil so presence can still be detected
246         elseif type(t) ~= "table" then
247                 return t
248         end
249         local n = {}
250         seen = seen or {}
251         seen[t] = n
252         for k, v in pairs(t) do
253                 local k_ = seen[k] or copy_filtering(k, seen)
254                 local v_ = seen[v] or copy_filtering(v, seen)
255                 n[k_] = v_
256         end
257         return n
258 end
259
260 function core.get_globals_to_transfer()
261         local all = {
262                 registered_items = copy_filtering(core.registered_items),
263                 registered_aliases = core.registered_aliases,
264         }
265         return core.serialize(all)
266 end