init.lua 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. if not minetest.global_exists("preload_tp") then preload_tp = {} end
  2. preload_tp.modpath = minetest.get_modpath("preload_tp")
  3. preload_tp.teleporting_players = preload_tp.teleporting_players or {}
  4. -- Localize for performance.
  5. local vector_distance = vector.distance
  6. local vector_round = vector.round
  7. local math_floor = math.floor
  8. function preload_tp.teleport_in_progress(pname)
  9. if preload_tp.teleporting_players[pname] then
  10. return true
  11. end
  12. end
  13. function preload_tp.finalize(parameters)
  14. local pname = parameters.player_name
  15. local force = parameters.force_teleport
  16. local pp = parameters.start_position
  17. local tp = parameters.target_position
  18. local pre_cb = parameters.pre_teleport_callback
  19. local post_cb = parameters.post_teleport_callback
  20. local cb_param = parameters.callback_param
  21. local tpsound = parameters.teleport_sound
  22. local pfx = parameters.particle_effects
  23. -- Regardless of success or failure, the player is no longer in progress.
  24. preload_tp.teleporting_players[pname] = nil
  25. -- Find the player.
  26. local player = minetest.get_player_by_name(pname)
  27. if not player or not player:is_player() then
  28. -- The player left, or something. Do not teleport them.
  29. minetest.log("action", pname .. " left the game while a teleport callback was in progress")
  30. return
  31. end
  32. -- The player may optionally be force-teleported.
  33. if not force then
  34. -- Did the player move?
  35. if vector_distance(pp, player:get_pos()) > 1.5 then
  36. minetest.chat_send_player(pname, "# Server: Transport error. You cannot move while a transport is in progress.")
  37. return
  38. end
  39. -- If the player killed themselves, do not teleport them.
  40. if player:get_hp() == 0 then
  41. minetest.chat_send_player(pname, "# Server: Transport error. You are dead.")
  42. return
  43. end
  44. end
  45. -- But we must never teleport a player who is attached.
  46. if default.player_attached[pname] or player:get_attach() then
  47. minetest.chat_send_player(pname, "# Server: Transport error. Player attached!")
  48. return
  49. end
  50. -- Execute the callback function, if everything else succeeded.
  51. if pre_cb then
  52. -- If the pre-teleport callback returns 'success' then that
  53. -- signals that the teleport must be aborted for some reason.
  54. if pre_cb(cb_param) then
  55. minetest.chat_send_player(pname, "# Server: Transport canceled.")
  56. return
  57. end
  58. end
  59. minetest.log("action", "executing teleport callback for " .. pname .. "!")
  60. -- Teleport player only if they didn't move (or teleporting is forced).
  61. wield3d.on_teleport()
  62. rc.notify_realm_update(player, tp)
  63. player:set_pos(tp)
  64. minetest.log("action", pname .. " actually teleports to " .. minetest.pos_to_string(tp))
  65. -- Execute the callback function, if everything else succeeded.
  66. if post_cb then
  67. post_cb(cb_param)
  68. end
  69. local thesound = "teleport"
  70. if type(tpsound) == "string" then
  71. thesound = tpsound
  72. end
  73. -- The teleport sound, played @ old & new locations.
  74. ambiance.sound_play(thesound, pp, 1.0, 50)
  75. ambiance.sound_play(thesound, tp, 1.0, 50)
  76. if pfx then
  77. preload_tp.spawn_particles(pp)
  78. preload_tp.spawn_particles(tp)
  79. preload_tp.spawn_spinup_particles(vector_round(tp), 3)
  80. end
  81. end
  82. function preload_tp.wait_for_timeout(parameters)
  83. local start_time = parameters.start_time
  84. local total_time = parameters.total_time
  85. local end_time = os.time()
  86. if (end_time - start_time) < total_time then
  87. minetest.after(1, function()
  88. preload_tp.wait_for_timeout(parameters)
  89. end)
  90. return
  91. end
  92. preload_tp.finalize(parameters)
  93. end
  94. function preload_tp.spawn_spinup_particles(pos, time)
  95. local xd = 1
  96. local zd = 1
  97. -- We used to use the coal & mese textures (ew).
  98. -- Now that we have better particles, we don't need multiple spawners.
  99. -- Can animate the particles, too.
  100. minetest.add_particlespawner({
  101. amount = 160,
  102. time = time,
  103. minpos = {x=pos.x-xd, y=pos.y-0, z=pos.z-zd},
  104. maxpos = {x=pos.x+xd, y=pos.y+2, z=pos.z+zd},
  105. minvel = {x=0, y=-1, z=0},
  106. maxvel = {x=0, y=1, z=0},
  107. minacc = {x=0, y=-1, z=0},
  108. maxacc = {x=0, y=1, z=0},
  109. minexptime = 1.0,
  110. maxexptime = 2.5,
  111. minsize = 1.0,
  112. maxsize = 1.0,
  113. collisiondetection = true,
  114. collision_removal = true,
  115. vertical = false,
  116. texture = "nether_particle_anim1.png",
  117. animation = {
  118. type = "vertical_frames",
  119. aspect_w = 7,
  120. aspect_h = 7,
  121. -- Disabled for now due to causing older clients to hang.
  122. --length = -1,
  123. length = 0.3,
  124. },
  125. glow = 14,
  126. })
  127. end
  128. -- API function. Preload the area, then teleport the player there
  129. -- only if they have not moved during the preload. After a successful
  130. -- teleport, execute the callback function if it's not nil.
  131. function preload_tp.execute(parameters)
  132. -- Copy table so we don't end up modifying the original.
  133. parameters = table.copy(parameters)
  134. -- Set default parameters.
  135. parameters.player_name = parameters.player_name or ""
  136. parameters.target_position = parameters.target_position or {x=0, y=0, z=0}
  137. parameters.emerge_radius = parameters.emerge_radius or 16
  138. parameters.pre_teleport_callback = parameters.pre_teleport_callback or nil
  139. parameters.post_teleport_callback = parameters.post_teleport_callback or nil
  140. parameters.callback_param = parameters.callback_param or nil
  141. parameters.force_teleport = parameters.force_teleport or false
  142. parameters.teleport_sound = parameters.teleport_sound or nil
  143. parameters.send_blocks = parameters.send_blocks or false
  144. parameters.particle_effects = parameters.particle_effects or false
  145. parameters.on_map_loaded = parameters.on_map_loaded or nil
  146. local pname = parameters.player_name
  147. local tpos = parameters.target_position
  148. local radius = parameters.emerge_radius
  149. local pre_cb = parameters.pre_teleport_callback
  150. local post_cb = parameters.post_teleport_callback
  151. local cb_param = parameters.callback_param
  152. local force = parameters.force_teleport
  153. local tpsound = parameters.teleport_sound
  154. local sendblocks = parameters.send_blocks
  155. local pfx = parameters.particle_effects
  156. local player = minetest.get_player_by_name(pname)
  157. if not player or not player:is_player() then
  158. return
  159. end
  160. local tp = table.copy(tpos)
  161. local pp = player:get_pos()
  162. local start_time = os.time()
  163. -- Time to teleport depends on distance.
  164. -- But allow calling code to override this for special cases.
  165. local total_time = math_floor(vector_distance(pp, tp) / 1000)
  166. if parameters.spinup_time then
  167. total_time = parameters.spinup_time
  168. end
  169. if total_time < 2 then
  170. total_time = 2
  171. end
  172. parameters.start_position = pp
  173. parameters.start_time = start_time
  174. parameters.total_time = total_time
  175. minetest.log("action", pname .. " initiates teleport to " .. minetest.pos_to_string(tp))
  176. local map_min, map_max = minetest.get_mapgen_edges()
  177. if tp.x < map_min.x or tp.y < map_min.y or tp.z < map_min.z or
  178. tp.x > map_max.x or tp.y > map_max.y or tp.z > map_max.z then
  179. minetest.chat_send_player(pname, "# Server: Internal error, void target destination.")
  180. return
  181. end
  182. if pfx then
  183. preload_tp.spawn_spinup_particles(vector_round(pp), total_time + 2)
  184. preload_tp.spawn_spinup_particles(vector_round(tp), total_time + 1)
  185. end
  186. -- Build callback function. When the map is loaded, we can teleport the player.
  187. local cb = function(blockpos, action, calls_remaining, parameters)
  188. -- Check if there was an error.
  189. if action == core.EMERGE_CANCELLED or action == core.EMERGE_ERRORED then
  190. minetest.chat_send_player(pname, "# Server: Internal error, block loading canceled.")
  191. preload_tp.teleporting_players[pname] = nil
  192. return
  193. end
  194. -- Send blocks to client as soon as they're available; looks better that way.
  195. if sendblocks then
  196. if blockpos then
  197. local pref = minetest.get_player_by_name(pname)
  198. if pref then
  199. pref:send_mapblock(blockpos)
  200. end
  201. end
  202. end
  203. -- We don't do anything until the last callback.
  204. if calls_remaining ~= 0 then
  205. return
  206. end
  207. if parameters.on_map_loaded then
  208. parameters.on_map_loaded()
  209. end
  210. if not force then
  211. preload_tp.wait_for_timeout(parameters)
  212. return
  213. end
  214. -- Forced teleport always teleports as soon as possible!
  215. preload_tp.finalize(parameters)
  216. end
  217. local minp = vector.add(tp, vector.new(-radius, -radius, -radius))
  218. local maxp = vector.add(tp, vector.new(radius, radius, radius))
  219. minp.x = math.max(minp.x, map_min.x)
  220. minp.y = math.max(minp.y, map_min.y)
  221. minp.z = math.max(minp.z, map_min.z)
  222. maxp.x = math.min(maxp.x, map_max.x)
  223. maxp.y = math.min(maxp.y, map_max.y)
  224. maxp.z = math.min(maxp.z, map_max.z)
  225. preload_tp.teleporting_players[pname] = true
  226. -- Emerge the target area. Once emergence is complete player can be teleported.
  227. minetest.chat_send_player(pname, "# Server: Spatially translating! Stand by.")
  228. minetest.emerge_area(minp, maxp, cb, parameters)
  229. end
  230. local particles = {
  231. amount = 20,
  232. time = 1,
  233. -- ^ If time is 0 has infinite lifespan and spawns the amount on a per-second base
  234. minpos = {x=0, y=0, z=0},
  235. maxpos = {x=0, y=0, z=0},
  236. minvel = {x=0, y=6, z=0},
  237. maxvel = {x=0, y=9, z=0},
  238. minacc = {x=0, y=2, z=0},
  239. maxacc = {x=0, y=4, z=0},
  240. minexptime = 0.5,
  241. maxexptime = 1,
  242. minsize = 1.0,
  243. maxsize = 1.0,
  244. -- ^ The particle's properties are random values in between the bounds:
  245. -- ^ minpos/maxpos, minvel/maxvel (velocity), minacc/maxacc (acceleration),
  246. -- ^ minsize/maxsize, minexptime/maxexptime (expirationtime)
  247. collisiondetection = false,
  248. -- ^ collisiondetection: if true uses collision detection
  249. collision_removal = false,
  250. -- ^ collision_removal: if true then particle is removed when it collides,
  251. -- ^ requires collisiondetection = true to have any effect
  252. --attached = ObjectRef,
  253. -- ^ attached: if defined, particle positions, velocities and accelerations
  254. -- ^ are relative to this object's position and yaw.
  255. vertical = false,
  256. -- ^ vertical: if true faces player using y axis only
  257. texture = "nether_particle_anim1.png",
  258. animation = {
  259. type = "vertical_frames",
  260. aspect_w = 7,
  261. aspect_h = 7,
  262. -- Disabled for now due to causing older clients to hang.
  263. --length = -1,
  264. length = 0.3,
  265. },
  266. glow = 14,
  267. }
  268. function preload_tp.spawn_particles(pos)
  269. particles.minpos = vector.add(pos, {x=-1.0, y=-1.0, z=-1.0})
  270. particles.maxpos = vector.add(pos, {x=1.0, y=-1.0, z=1.0})
  271. minetest.add_particlespawner(particles)
  272. end
  273. if not preload_tp.run_once then
  274. local c = "preload_tp:core"
  275. local f = preload_tp.modpath .. "/init.lua"
  276. reload.register_file(c, f, false)
  277. preload_tp.run_once = true
  278. end