misc.lua 8.0 KB

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