init.lua 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764
  1. -- Realm Control Mod.
  2. -- This mod manages realm boundaries and prevents players from moving freely
  3. -- between realms/dimensions without programmatic intervention.
  4. rc = rc or {}
  5. rc.players = rc.players or {}
  6. rc.modpath = minetest.get_modpath("rc")
  7. -- Localize for performance.
  8. local vector_round = vector.round
  9. local math_random = math.random
  10. local default_sky = {type="regular", clouds=true}
  11. local default_sun = {visible=true, sunrise_visible=true, scale=1}
  12. local default_moon = {visible=true, scale=1}
  13. local default_stars = {visible=true, count=1000}
  14. local default_clouds = {
  15. height = 120,
  16. density = 0.4,
  17. speed = {x = 0, z = -2},
  18. thickness = 16,
  19. }
  20. -- Known realms. Min/max area positions should not overlap!
  21. rc.realms = {
  22. {
  23. id = 1, -- REALM ID. Code relies on this.
  24. name = "overworld", -- Default/overworld realm.
  25. description = "Overworld",
  26. minp = {x=-30912, y=-30912, z=-30912},
  27. maxp = {x=30927, y=500, z=30927},
  28. gate_minp = {x=-30000, y=-30800, z=-30000},
  29. gate_maxp = {x=30000, y=-10, z=30000},
  30. orig = {x=0, y=-7, z=0}, -- Respawn point, if necessary.
  31. ground = -10,
  32. underground = -32, -- Affects sky color, see sky mod.
  33. sealevel = 0,
  34. windlevel = 20,
  35. realm_origin = {x=-1067, y=-10, z=8930},
  36. disabled = false, -- Whether realm can be "gated" to. Use when testing!
  37. cloud_data = {
  38. -- Overworld clouds change direction of travel on every restart.
  39. speed = {x = math_random(-200, 200)/200, z = math_random(-200, 200)/200},
  40. },
  41. },
  42. {
  43. id = 2, -- REALM ID. Code relies on this.
  44. name = "channelwood", -- Forest realm. 250 meters high.
  45. description = "Channelwood",
  46. minp = {x=-30912, y=3050, z=-30912},
  47. maxp = {x=30927, y=3300, z=30927},
  48. gate_minp = {x=-30000, y=3065, z=-30000},
  49. gate_maxp = {x=30000, y=3067, z=30000},
  50. orig = {x=0, y=-7, z=0}, -- Respawn point, if necessary.
  51. ground = 3066,
  52. underground = 3050,
  53. sealevel = 3066,
  54. windlevel = 3100,
  55. realm_origin = {x=2019, y=3066, z=-1992},
  56. disabled = false, -- Whether realm can be "gated" to.
  57. cloud_data={height=3112, density=0.6, speed={x=0.1, z=0.1}, thickness=4},
  58. moon_data={scale=2.5},
  59. },
  60. {
  61. id = 3, -- REALM ID. Code relies on this.
  62. name = "jarkati",
  63. description = "Jarkati",
  64. minp = {x=-30912, y=3600, z=-30912},
  65. maxp = {x=30927, y=3900, z=30927},
  66. gate_minp = {x=-30000, y=3620, z=-30000},
  67. gate_maxp = {x=30000, y=3640, z=30000},
  68. orig = {x=0, y=-7, z=0}, -- Respawn point, if necessary.
  69. ground = 3740,
  70. underground = 3730,
  71. sealevel = 3740,
  72. windlevel = 3750,
  73. realm_origin = {x=1986, y=3700, z=-1864},
  74. sky_data={clouds=true},
  75. cloud_data={height=3900, density=0.2, speed={x=5, z=2}},
  76. moon_data={scale=0.4},
  77. sun_data={scale=0.4},
  78. },
  79. {
  80. id = 4, -- REALM ID. Code relies on this.
  81. name = "abyss",
  82. description = "Outback",
  83. minp = vector.add({x=-9174, y=4100, z=5782}, {x=-100, y=-100, z=-100}),
  84. maxp = vector.add({x=-9174, y=4100, z=5782}, {x=100, y=100, z=100}),
  85. gate_minp = vector.add({x=-9174, y=4100, z=5782}, {x=-80, y=-80, z=-80}),
  86. gate_maxp = vector.add({x=-9174, y=4100, z=5782}, {x=80, y=80, z=80}),
  87. orig = {x=-9223, y=4169, z=5861}, -- Same as server's static spawnpoint!
  88. ground = 4200,
  89. underground = 4160, -- Affects sky color, see sky mod.
  90. sealevel = 4200,
  91. windlevel = 4200,
  92. realm_origin = {x=-9174, y=4100, z=5782},
  93. disabled = true, -- Realm cannot receive an incoming gate. OFFICIAL.
  94. sky_data = {clouds=false},
  95. sun_data = {},
  96. moon_data = {},
  97. star_data = {visible=true, count=50},
  98. },
  99. }
  100. -- Return true if a position is underground in some realm.
  101. -- False is returned if not underground.
  102. -- Returns nil if position isn't in any valid realm.
  103. function rc.position_underground(pos)
  104. local p = vector_round(pos)
  105. for k, v in ipairs(rc.realms) do
  106. local minp = v.minp
  107. local maxp = v.maxp
  108. -- Is position within realm boundaries?
  109. if p.x >= minp.x and p.x <= maxp.x and
  110. p.y >= minp.y and p.y <= maxp.y and
  111. p.z >= minp.z and p.z <= maxp.z then
  112. if p.y < v.underground then
  113. return true
  114. else
  115. return false
  116. end
  117. end
  118. end
  119. -- Not in any realm?
  120. return nil
  121. end
  122. -- Used by ice/ice-brick/snow nodes to determine if they should melt away.
  123. -- This is also used by the tree-snowdust code to determine whether a tree
  124. -- should spawn with snow on top.
  125. function rc.ice_melts_at_pos(pos)
  126. if pos.y < -26000 or pos.y > 1000 then
  127. return true
  128. end
  129. end
  130. -- Convert realm position to absolute coordinate. May return NIL if not valid!
  131. function rc.realmpos_to_pos(realm, pos)
  132. local data = rc.get_realm_data(realm:lower())
  133. if data then
  134. local origin = data.realm_origin
  135. return {
  136. x = origin.x + pos.x,
  137. y = origin.y + pos.y,
  138. z = origin.z + pos.z,
  139. }
  140. end
  141. -- Indicate failure.
  142. return nil
  143. end
  144. -- Convert absolute coordinate to realm position. May return NIL if not valid!
  145. function rc.pos_to_realmpos(pos)
  146. local origin = rc.get_realm_origin_at_pos(pos)
  147. if origin then
  148. return {
  149. x = pos.x - origin.x,
  150. y = pos.y - origin.y,
  151. z = pos.z - origin.z,
  152. }
  153. end
  154. -- Indicate failure.
  155. return nil
  156. end
  157. function rc.pos_to_namestr(pos)
  158. local name = rc.pos_to_name(pos)
  159. local str = rc.pos_to_string(pos)
  160. str = string.gsub(str, "[%(%)]", "")
  161. return "(" .. name .. ": " .. str .. ")"
  162. end
  163. function rc.pos_to_name(pos)
  164. return rc.realm_description_at_pos(pos)
  165. end
  166. function rc.get_realm_origin_at_pos(p)
  167. for k, v in ipairs(rc.realms) do
  168. local minp = v.minp
  169. local maxp = v.maxp
  170. -- Is position within realm boundaries?
  171. if p.x >= minp.x and p.x <= maxp.x and
  172. p.y >= minp.y and p.y <= maxp.y and
  173. p.z >= minp.z and p.z <= maxp.z then
  174. local o = table.copy(v.realm_origin)
  175. return o
  176. end
  177. end
  178. -- Not in any realm?
  179. return nil
  180. end
  181. -- Obtain a string in the format "(x,y,z)".
  182. function rc.pos_to_string(pos)
  183. local realpos = rc.pos_to_realmpos(pos)
  184. if realpos then
  185. return minetest.pos_to_string(realpos)
  186. end
  187. -- Indicate failure.
  188. return "(Nan,Nan,Nan)"
  189. end
  190. function rc.get_realm_sky(pos)
  191. local n = rc.get_realm_data(rc.current_realm_at_pos(pos))
  192. if n then
  193. local t = table.copy(default_sky)
  194. for k, v in pairs(n.sky_data or {}) do
  195. t[k] = v
  196. end
  197. --minetest.chat_send_all(dump(t))
  198. return t
  199. end
  200. return {}
  201. end
  202. function rc.get_realm_sun(pos)
  203. local n = rc.get_realm_data(rc.current_realm_at_pos(pos))
  204. if n then
  205. local t = table.copy(default_sun)
  206. for k, v in pairs(n.sun_data or {}) do
  207. t[k] = v
  208. end
  209. --minetest.chat_send_all(dump(t))
  210. return t
  211. end
  212. return {}
  213. end
  214. function rc.get_realm_moon(pos)
  215. local n = rc.get_realm_data(rc.current_realm_at_pos(pos))
  216. if n then
  217. local t = table.copy(default_moon)
  218. for k, v in pairs(n.moon_data or {}) do
  219. t[k] = v
  220. end
  221. --minetest.chat_send_all(dump(t))
  222. return t
  223. end
  224. return {}
  225. end
  226. function rc.get_realm_stars(pos)
  227. local n = rc.get_realm_data(rc.current_realm_at_pos(pos))
  228. if n then
  229. local t = table.copy(default_stars)
  230. for k, v in pairs(n.star_data or {}) do
  231. t[k] = v
  232. end
  233. --minetest.chat_send_all(dump(t))
  234. return t
  235. end
  236. return {}
  237. end
  238. function rc.get_realm_clouds(pos)
  239. local n = rc.get_realm_data(rc.current_realm_at_pos(pos))
  240. if n then
  241. local t = table.copy(default_clouds)
  242. for k, v in pairs(n.cloud_data or {}) do
  243. t[k] = v
  244. end
  245. --minetest.chat_send_all(dump(t))
  246. return t
  247. end
  248. return {}
  249. end
  250. function rc.get_realm_data(name)
  251. for k, v in ipairs(rc.realms) do
  252. if v.name == name then
  253. return v
  254. end
  255. if v.name == "overworld" then
  256. -- Alternate names.
  257. if name == "netherworld" or name == "nether" or name == "caverns" or name == "caves" then
  258. return v
  259. end
  260. end
  261. if v.name == "abyss" then
  262. -- Alternate names.
  263. if name == "outback" then
  264. return v
  265. end
  266. end
  267. end
  268. return nil
  269. end
  270. function rc.get_random_enabled_realm_data()
  271. if (#rc.realms) < 1 then
  272. return
  273. end
  274. local tries = 1
  275. local realm = rc.realms[math_random(1, #rc.realms)]
  276. while realm.disabled and tries < 10 do
  277. tries = tries + 1
  278. realm = rc.realms[math_random(1, #rc.realms)]
  279. end
  280. if realm.disabled then
  281. return
  282. end
  283. return realm
  284. end
  285. function rc.get_random_realm_gate_position(pname, origin)
  286. if rc.is_valid_realm_pos(origin) then
  287. if origin.y >= 128 and origin.y <= 1000 then
  288. -- If gateway is positioned in the Overworld mountains,
  289. -- permit easy realm hopping.
  290. local realm = rc.get_random_enabled_realm_data()
  291. if not realm then
  292. return nil
  293. end
  294. assert(realm)
  295. local pos = {
  296. x = math_random(realm.gate_minp.x, realm.gate_maxp.x),
  297. y = math_random(realm.gate_minp.y, realm.gate_maxp.y),
  298. z = math_random(realm.gate_minp.z, realm.gate_maxp.z),
  299. }
  300. local below = vector.add(origin, {x=0, y=-16, z=0})
  301. local minp = vector.add(below, {x=-16, y=-16, z=-16})
  302. local maxp = vector.add(below, {x=16, y=16, z=16})
  303. -- Should find 30K stone.
  304. local positions, counts = minetest.find_nodes_in_area(minp, maxp, {"default:stone"})
  305. --for k, v in pairs(counts) do
  306. -- minetest.chat_send_player(pname, "# Server: " .. k .. " = " .. v .. "!")
  307. --end
  308. if counts["default:stone"] > math_random(10000, 30000) then
  309. -- Search again, even deeper. The stone amount should be MUCH higher.
  310. below = vector.add(below, {x=0, y=-32, z=0})
  311. minp = vector.add(below, {x=-16, y=-16, z=-16})
  312. maxp = vector.add(below, {x=16, y=16, z=16})
  313. positions, counts = minetest.find_nodes_in_area(minp, maxp, {"default:stone"})
  314. --for k, v in pairs(counts) do
  315. -- minetest.chat_send_player(pname, "# Server: " .. k .. " = " .. v .. "!")
  316. --end
  317. if counts["default:stone"] > math_random(20000, 32000) then
  318. return pos
  319. end
  320. end
  321. return nil
  322. elseif origin.y > 1000 then
  323. -- The gateway is positioned in a realm somewhere.
  324. -- 9/10 times the exit point stays in the same realm.
  325. -- Sometimes a realm hop is possible.
  326. local realm
  327. if math_random(1, 10) == 1 then
  328. realm = rc.get_random_enabled_realm_data()
  329. else
  330. realm = rc.get_realm_data(rc.current_realm_at_pos(origin))
  331. end
  332. if not realm then
  333. return nil
  334. end
  335. assert(realm)
  336. -- Not more than 3000 meters away from origin!
  337. local pos = {
  338. x = math_random(-3000, 3000) + origin.x,
  339. y = math_random(-300, 300) + origin.y,
  340. z = math_random(-3000, 3000) + origin.z,
  341. }
  342. local min = math.min
  343. local max = math.max
  344. -- Clamp position to ensure we remain within realm boundaries.
  345. pos.x = max(realm.gate_minp.x, min(pos.x, realm.gate_maxp.x))
  346. pos.y = max(realm.gate_minp.y, min(pos.y, realm.gate_maxp.y))
  347. pos.z = max(realm.gate_minp.z, min(pos.z, realm.gate_maxp.z))
  348. return pos
  349. end
  350. end
  351. local realm = rc.get_realm_data("overworld")
  352. assert(realm)
  353. -- Player is in the Overworld or Nether. Use old Gateway behavior!
  354. -- Not more than 3000 meters in any direction, and MUST stay in the Overworld
  355. -- (or the Nether). Gate is more likely to go down rather than up.
  356. local pos = {
  357. x = math_random(-3000, 3000) + origin.x,
  358. y = math_random(-2000, 300) + origin.y,
  359. z = math_random(-3000, 3000) + origin.z,
  360. }
  361. local min = math.min
  362. local max = math.max
  363. -- Clamp position.
  364. pos.x = max(realm.gate_minp.x, min(pos.x, realm.gate_maxp.x))
  365. pos.y = max(realm.gate_minp.y, min(pos.y, realm.gate_maxp.y))
  366. pos.z = max(realm.gate_minp.z, min(pos.z, realm.gate_maxp.z))
  367. return pos
  368. end
  369. function rc.is_valid_gateway_region(pos)
  370. local p = vector_round(pos)
  371. for k, v in ipairs(rc.realms) do
  372. local gate_minp = v.gate_minp
  373. local gate_maxp = v.gate_maxp
  374. -- Is position within realm boundaries suitable for a gateway?
  375. if p.x >= gate_minp.x and p.x <= gate_maxp.x and
  376. p.y >= gate_minp.y and p.y <= gate_maxp.y and
  377. p.z >= gate_minp.z and p.z <= gate_maxp.z then
  378. return true
  379. end
  380. end
  381. -- Not in any realm?
  382. return false
  383. end
  384. function rc.is_valid_realm_pos(pos)
  385. local p = vector_round(pos)
  386. for i = 1, #rc.realms, 1 do
  387. local v = rc.realms[i]
  388. local minp = v.minp
  389. local maxp = v.maxp
  390. -- Is position within realm boundaries?
  391. if p.x >= minp.x and p.x <= maxp.x and
  392. p.y >= minp.y and p.y <= maxp.y and
  393. p.z >= minp.z and p.z <= maxp.z then
  394. return true
  395. end
  396. end
  397. -- Not in any realm?
  398. return false
  399. end
  400. function rc.get_ground_level_at_pos(pos)
  401. local p = vector_round(pos)
  402. for k, v in ipairs(rc.realms) do
  403. local minp = v.minp
  404. local maxp = v.maxp
  405. -- Is position within realm boundaries?
  406. if p.x >= minp.x and p.x <= maxp.x and
  407. p.y >= minp.y and p.y <= maxp.y and
  408. p.z >= minp.z and p.z <= maxp.z then
  409. return true, v.ground
  410. end
  411. end
  412. -- Not in any realm?
  413. return false, nil
  414. end
  415. function rc.get_sea_level_at_pos(pos)
  416. local p = vector_round(pos)
  417. for k, v in ipairs(rc.realms) do
  418. local minp = v.minp
  419. local maxp = v.maxp
  420. -- Is position within realm boundaries?
  421. if p.x >= minp.x and p.x <= maxp.x and
  422. p.y >= minp.y and p.y <= maxp.y and
  423. p.z >= minp.z and p.z <= maxp.z then
  424. return true, v.sealevel
  425. end
  426. end
  427. -- Not in any realm?
  428. return false, nil
  429. end
  430. function rc.get_wind_level_at_pos(pos)
  431. local p = vector_round(pos)
  432. for k, v in ipairs(rc.realms) do
  433. local minp = v.minp
  434. local maxp = v.maxp
  435. -- Is position within realm boundaries?
  436. if p.x >= minp.x and p.x <= maxp.x and
  437. p.y >= minp.y and p.y <= maxp.y and
  438. p.z >= minp.z and p.z <= maxp.z then
  439. return true, v.windlevel
  440. end
  441. end
  442. -- Not in any realm?
  443. return false, nil
  444. end
  445. -- API function. Get string name of the current realm the player is in.
  446. function rc.current_realm(player)
  447. local p = vector_round(player:get_pos())
  448. return rc.current_realm_at_pos(p)
  449. end
  450. -- API function. Get string name of the current realm at a position.
  451. function rc.current_realm_at_pos(p)
  452. for k, v in ipairs(rc.realms) do
  453. local minp = v.minp
  454. local maxp = v.maxp
  455. -- Is player within realm boundaries?
  456. if p.x >= minp.x and p.x <= maxp.x and
  457. p.y >= minp.y and p.y <= maxp.y and
  458. p.z >= minp.z and p.z <= maxp.z then
  459. return v.name
  460. end
  461. end
  462. -- Not in any realm?
  463. return ""
  464. end
  465. -- API function. Get static spawn point of a named realm.
  466. function rc.static_spawn(name)
  467. for k, v in ipairs(rc.realms) do
  468. if v.name == name then
  469. return table.copy(v.orig)
  470. end
  471. end
  472. -- Not in any realm?
  473. return {x=0, y=-7, z=0}
  474. end
  475. function rc.same_realm(p1, p2)
  476. return (rc.current_realm_at_pos(p1) == rc.current_realm_at_pos(p2))
  477. end
  478. function rc.realm_description_at_pos(p)
  479. -- Special realm name.
  480. if p.y < -25000 then
  481. if p.y < -30760 and p.y > -30800 then
  482. return "Abyssal Sea"
  483. end
  484. return "Nether"
  485. elseif p.y < -5000 then
  486. return "Caverns"
  487. end
  488. for k, v in ipairs(rc.realms) do
  489. local minp = v.minp
  490. local maxp = v.maxp
  491. -- Is player within realm boundaries?
  492. if p.x >= minp.x and p.x <= maxp.x and
  493. p.y >= minp.y and p.y <= maxp.y and
  494. p.z >= minp.z and p.z <= maxp.z then
  495. return v.description
  496. end
  497. end
  498. -- Not in any realm?
  499. return "Void"
  500. end
  501. -- API function.
  502. -- Check player position and current realm. If not valid, reset player to last
  503. -- valid location. If last valid location not found, reset them to 0,0,0.
  504. -- This function should be called from a global-step callback somewhere.
  505. function rc.check_position(player)
  506. local p = vector_round(player:get_pos())
  507. local n = player:get_player_name()
  508. local data = rc.players[n]
  509. -- Data not initialized yet.
  510. if not data then return end
  511. local reset -- Table set if player out-of-bounds.
  512. -- Bounds check to avoid an engine bug. These coordinates should be the last
  513. -- row of nodes at the map edge. This way, we never teleport the player to a
  514. -- location that is strictly outside the world boundaries, if they trigger it.
  515. if p.x < -30912 or p.x > 30927 or
  516. p.y < -30912 or p.y > 30927 or
  517. p.z < -30912 or p.z > 30927 then
  518. -- Some old clients, it seems, can randomly cause this problem.
  519. -- Or someone is deliberately triggering it.
  520. reset = {}
  521. reset.spawn = rc.static_spawn("abyss")
  522. end
  523. -- Check if player is currently in the void.
  524. if not reset then
  525. if data.realm == "" then
  526. reset = {}
  527. reset.spawn = rc.static_spawn("abyss")
  528. end
  529. end
  530. -- Do bounds checks for individual realms.
  531. if not reset then
  532. local lrem = #(rc.realms)
  533. for k = 1, lrem, 1 do
  534. local v = rc.realms[k]
  535. -- Is player within boundaries of the realm they are supposed to be in?
  536. if data.realm == v.name then
  537. local minp = v.minp
  538. local maxp = v.maxp
  539. if p.x < minp.x or p.x > maxp.x or
  540. p.y < minp.y or p.y > maxp.y or
  541. p.z < minp.z or p.z > maxp.z then
  542. reset = {}
  543. reset.spawn = v.orig -- Use current realm's respawn coordinates.
  544. break
  545. end
  546. end
  547. end
  548. end
  549. if reset then
  550. -- Player is out-of-bounds. Reset to last known good position.
  551. if not gdac.player_is_admin(n) and not data.new_arrival then
  552. minetest.chat_send_all("# Server: Player <" .. rename.gpn(n) ..
  553. "> was caught in the inter-dimensional void!")
  554. end
  555. -- Notify wield3d we're adjusting the player position.
  556. -- Wielded item entities don't like sudden movement.
  557. wield3d.on_teleport()
  558. if player:get_hp() > 0 then
  559. -- Return player to last known good position.
  560. player:set_pos(data.pos)
  561. else
  562. -- Return to realm's origin point.
  563. player:set_pos(reset.spawn)
  564. -- Update which realm the player is supposed to be in.
  565. -- (We might have crossed realms depending on what happened above.)
  566. rc.notify_realm_update(player, reset.spawn)
  567. end
  568. -- Damage player. Prevents them triggering this indefinitely.
  569. if player:get_hp() > 0 and not data.new_arrival then
  570. player:set_hp(player:get_hp() - 2)
  571. if player:get_hp() <= 0 then
  572. if not gdac.player_is_admin(n) then
  573. minetest.chat_send_all("# Server: <" .. rename.gpn(n) ..
  574. "> found death in the void.")
  575. end
  576. end
  577. end
  578. return
  579. end
  580. -- If we got this far, the player is not out of bounds.
  581. -- Record last known good position. Realm name should be same as before.
  582. do
  583. local ps = data.pos
  584. ps.x = p.x
  585. ps.y = p.y
  586. ps.z = p.z
  587. end
  588. end
  589. function rc.on_joinplayer(player)
  590. local n = player:get_player_name()
  591. local p = player:get_pos()
  592. -- Player's current dimension is determined from position on login.
  593. rc.players[n] = {
  594. pos = p,
  595. realm = rc.current_realm(player),
  596. new_arrival = true,
  597. }
  598. -- Remove the 'new_arrival' flag after a few seconds.
  599. minetest.after(10, function()
  600. local data = rc.players[n]
  601. if not data then return end
  602. data.new_arrival = nil
  603. end)
  604. end
  605. function rc.on_leaveplayer(player, timeout)
  606. local n = player:get_player_name()
  607. rc.players[n] = nil
  608. end
  609. -- API function. Call this whenever a player teleports,
  610. -- or lawfully changes realm. You can pass a player object or a name.
  611. -- Note: this must be called *before* you call :set_pos() on the player!
  612. function rc.notify_realm_update(player, pos)
  613. local p = vector_round(pos)
  614. local n = ""
  615. if type(player) == "string" then
  616. n = player
  617. else
  618. n = player:get_player_name()
  619. end
  620. local pref = minetest.get_player_by_name(n)
  621. local tb = rc.players[n]
  622. if not tb then
  623. minetest.log("action", "could not find data for player " .. n .. " when updating realm info")
  624. return
  625. end
  626. if pref and tb.realm then
  627. local pp = vector_round(pref:get_pos())
  628. local rr = rc.current_realm_at_pos(pp)
  629. local rr2 = rc.current_realm_at_pos(p)
  630. if rr ~= rr2 then
  631. if gdac_invis.is_invisible(n) or cloaking.is_cloaked(n) or player_labels.query_nametag_onoff(n) == false then
  632. if not gdac.player_is_admin(n) then
  633. minetest.chat_send_all("# Server: Someone has plane shifted.")
  634. end
  635. else
  636. local d = rc.get_realm_data(rr2)
  637. if d and d.description then
  638. local realm_name = d.description
  639. minetest.chat_send_all("# Server: <" .. rename.gpn(n) .. "> has plane shifted to " .. realm_name .. ".")
  640. end
  641. end
  642. end
  643. end
  644. tb.pos = p
  645. tb.realm = rc.current_realm_at_pos(p)
  646. sky.notify_sky_update_needed(n)
  647. end
  648. if not rc.registered then
  649. minetest.register_on_joinplayer(function(...)
  650. return rc.on_joinplayer(...)
  651. end)
  652. minetest.register_on_leaveplayer(function(...)
  653. return rc.on_leaveplayer(...)
  654. end)
  655. local c = "rc:core"
  656. local f = rc.modpath .. "/init.lua"
  657. reload.register_file(c, f, false)
  658. rc.registered = true
  659. end