init.lua 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754
  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. --minetest.chat_send_player("MustTest", "# Server: Success! " .. counts["default:stone"])
  319. return pos
  320. end
  321. end
  322. return nil
  323. elseif origin.y > 1000 then
  324. -- The gateway is positioned in a realm somewhere.
  325. -- 9/10 times the exit point stays in the same realm.
  326. -- Sometimes a realm hop is possible.
  327. local realm
  328. if math_random(1, 10) == 1 then
  329. realm = rc.get_random_enabled_realm_data()
  330. else
  331. realm = rc.get_realm_data(rc.current_realm_at_pos(origin))
  332. end
  333. if not realm then
  334. return nil
  335. end
  336. assert(realm)
  337. -- Not more than 1000 meters away from origin!
  338. local pos = {
  339. x = math_random(-1000, 1000) + origin.x,
  340. y = math_random(-1000, 1000) + origin.y,
  341. z = math_random(-1000, 1000) + origin.z,
  342. }
  343. local min = math.min
  344. local max = math.max
  345. -- Clamp position to ensure we remain within realm boundaries.
  346. pos.x = max(realm.gate_minp.x, min(pos.x, realm.gate_maxp.x))
  347. pos.y = max(realm.gate_minp.y, min(pos.y, realm.gate_maxp.y))
  348. pos.z = max(realm.gate_minp.z, min(pos.z, realm.gate_maxp.z))
  349. return pos
  350. end
  351. end
  352. local realm = rc.get_realm_data("overworld")
  353. assert(realm)
  354. -- Player is in the Overworld or Nether. Use old Gateway behavior!
  355. -- Not more than 1000 meters in any direction, and MUST stay in the Overworld
  356. -- (or the Nether).
  357. local pos = {
  358. x = math_random(-1000, 1000) + origin.x,
  359. y = math_random(-1000, 1000) + origin.y,
  360. z = math_random(-1000, 1000) + origin.z,
  361. }
  362. local min = math.min
  363. local max = math.max
  364. -- Clamp position.
  365. pos.x = max(realm.gate_minp.x, min(pos.x, realm.gate_maxp.x))
  366. pos.y = max(realm.gate_minp.y, min(pos.y, realm.gate_maxp.y))
  367. pos.z = max(realm.gate_minp.z, min(pos.z, realm.gate_maxp.z))
  368. return pos
  369. end
  370. function rc.is_valid_gateway_region(pos)
  371. local p = vector_round(pos)
  372. for k, v in ipairs(rc.realms) do
  373. local gate_minp = v.gate_minp
  374. local gate_maxp = v.gate_maxp
  375. -- Is position within realm boundaries suitable for a gateway?
  376. if p.x >= gate_minp.x and p.x <= gate_maxp.x and
  377. p.y >= gate_minp.y and p.y <= gate_maxp.y and
  378. p.z >= gate_minp.z and p.z <= gate_maxp.z then
  379. return true
  380. end
  381. end
  382. -- Not in any realm?
  383. return false
  384. end
  385. function rc.is_valid_realm_pos(pos)
  386. local p = vector_round(pos)
  387. for i = 1, #rc.realms, 1 do
  388. local v = rc.realms[i]
  389. local minp = v.minp
  390. local maxp = v.maxp
  391. -- Is position within realm boundaries?
  392. if p.x >= minp.x and p.x <= maxp.x and
  393. p.y >= minp.y and p.y <= maxp.y and
  394. p.z >= minp.z and p.z <= maxp.z then
  395. return true
  396. end
  397. end
  398. -- Not in any realm?
  399. return false
  400. end
  401. function rc.get_ground_level_at_pos(pos)
  402. local p = vector_round(pos)
  403. for k, v in ipairs(rc.realms) do
  404. local minp = v.minp
  405. local maxp = v.maxp
  406. -- Is position within realm boundaries?
  407. if p.x >= minp.x and p.x <= maxp.x and
  408. p.y >= minp.y and p.y <= maxp.y and
  409. p.z >= minp.z and p.z <= maxp.z then
  410. return true, v.ground
  411. end
  412. end
  413. -- Not in any realm?
  414. return false, nil
  415. end
  416. function rc.get_sea_level_at_pos(pos)
  417. local p = vector_round(pos)
  418. for k, v in ipairs(rc.realms) do
  419. local minp = v.minp
  420. local maxp = v.maxp
  421. -- Is position within realm boundaries?
  422. if p.x >= minp.x and p.x <= maxp.x and
  423. p.y >= minp.y and p.y <= maxp.y and
  424. p.z >= minp.z and p.z <= maxp.z then
  425. return true, v.sealevel
  426. end
  427. end
  428. -- Not in any realm?
  429. return false, nil
  430. end
  431. function rc.get_wind_level_at_pos(pos)
  432. local p = vector_round(pos)
  433. for k, v in ipairs(rc.realms) do
  434. local minp = v.minp
  435. local maxp = v.maxp
  436. -- Is position within realm boundaries?
  437. if p.x >= minp.x and p.x <= maxp.x and
  438. p.y >= minp.y and p.y <= maxp.y and
  439. p.z >= minp.z and p.z <= maxp.z then
  440. return true, v.windlevel
  441. end
  442. end
  443. -- Not in any realm?
  444. return false, nil
  445. end
  446. -- API function. Get string name of the current realm the player is in.
  447. function rc.current_realm(player)
  448. local p = vector_round(player:get_pos())
  449. return rc.current_realm_at_pos(p)
  450. end
  451. -- API function. Get string name of the current realm at a position.
  452. function rc.current_realm_at_pos(p)
  453. for k, v in ipairs(rc.realms) do
  454. local minp = v.minp
  455. local maxp = v.maxp
  456. -- Is player within realm boundaries?
  457. if p.x >= minp.x and p.x <= maxp.x and
  458. p.y >= minp.y and p.y <= maxp.y and
  459. p.z >= minp.z and p.z <= maxp.z then
  460. return v.name
  461. end
  462. end
  463. -- Not in any realm?
  464. return ""
  465. end
  466. -- API function. Get static spawn point of a named realm.
  467. function rc.static_spawn(name)
  468. for k, v in ipairs(rc.realms) do
  469. if v.name == name then
  470. return table.copy(v.orig)
  471. end
  472. end
  473. -- Not in any realm?
  474. return {x=0, y=-7, z=0}
  475. end
  476. function rc.same_realm(p1, p2)
  477. return (rc.current_realm_at_pos(p1) == rc.current_realm_at_pos(p2))
  478. end
  479. function rc.realm_description_at_pos(p)
  480. -- Special realm name.
  481. if p.y < -25000 then
  482. if p.y < -30760 and p.y > -30800 then
  483. return "Abyssal Plain"
  484. end
  485. return "Nether"
  486. elseif p.y < -5000 then
  487. return "Caverns"
  488. end
  489. for k, v in ipairs(rc.realms) do
  490. local minp = v.minp
  491. local maxp = v.maxp
  492. -- Is player within realm boundaries?
  493. if p.x >= minp.x and p.x <= maxp.x and
  494. p.y >= minp.y and p.y <= maxp.y and
  495. p.z >= minp.z and p.z <= maxp.z then
  496. return v.description
  497. end
  498. end
  499. -- Not in any realm?
  500. return "Void"
  501. end
  502. -- API function.
  503. -- Check player position and current realm. If not valid, reset player to last
  504. -- valid location. If last valid location not found, reset them to 0,0,0.
  505. -- This function should be called from a global-step callback somewhere.
  506. function rc.check_position(player)
  507. local p = vector_round(player:get_pos())
  508. local n = player:get_player_name()
  509. -- Data not initialized yet.
  510. if not rc.players[n] then
  511. return
  512. end
  513. local reset -- Table set if player out-of-bounds.
  514. -- Bounds check to avoid an engine bug. These coordinates should be the last
  515. -- row of nodes at the map edge. This way, we never teleport the player to a
  516. -- location that is strictly outside the world boundaries, if they trigger it.
  517. if p.x < -30912 or p.x > 30927 or
  518. p.y < -30912 or p.y > 30927 or
  519. p.z < -30912 or p.z > 30927 then
  520. -- Some old clients, it seems, can randomly cause this problem.
  521. -- Or someone is deliberately triggering it.
  522. reset = {}
  523. reset.spawn = rc.static_spawn("abyss")
  524. end
  525. -- Check if player is currently in the void.
  526. if not reset then
  527. if rc.players[n].realm == "" then
  528. reset = {}
  529. reset.spawn = rc.static_spawn("abyss")
  530. end
  531. end
  532. -- Do bounds checks for individual realms.
  533. if not reset then
  534. for k, v in ipairs(rc.realms) do
  535. -- Is player within boundaries of the realm they are supposed to be in?
  536. if rc.players[n].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) 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 and rc.players[n] then
  559. -- Return player to last known good position.
  560. player:set_pos(rc.players[n].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 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 = rc.players[n].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. }
  597. end
  598. function rc.on_leaveplayer(player, timeout)
  599. local n = player:get_player_name()
  600. rc.players[n] = nil
  601. end
  602. -- API function. Call this whenever a player teleports,
  603. -- or lawfully changes realm. You can pass a player object or a name.
  604. -- Note: this must be called *before* you call :set_pos() on the player!
  605. function rc.notify_realm_update(player, pos)
  606. local p = vector_round(pos)
  607. local n = ""
  608. if type(player) == "string" then
  609. n = player
  610. else
  611. n = player:get_player_name()
  612. end
  613. local pref = minetest.get_player_by_name(n)
  614. local tb = rc.players[n]
  615. if not tb then
  616. minetest.log("action", "could not find data for player " .. n .. " when updating realm info")
  617. return
  618. end
  619. if pref and tb.realm then
  620. local pp = vector_round(pref:get_pos())
  621. local rr = rc.current_realm_at_pos(pp)
  622. local rr2 = rc.current_realm_at_pos(p)
  623. if rr ~= rr2 then
  624. if gdac_invis.is_invisible(n) or cloaking.is_cloaked(n) or player_labels.query_nametag_onoff(n) == false then
  625. if not gdac.player_is_admin(n) then
  626. minetest.chat_send_all("# Server: Someone has plane shifted.")
  627. end
  628. else
  629. local d = rc.get_realm_data(rr2)
  630. if d and d.description then
  631. local realm_name = d.description
  632. minetest.chat_send_all("# Server: <" .. rename.gpn(n) .. "> has plane shifted to " .. realm_name .. ".")
  633. end
  634. end
  635. end
  636. end
  637. tb.pos = p
  638. tb.realm = rc.current_realm_at_pos(p)
  639. sky.notify_sky_update_needed(n)
  640. end
  641. if not rc.registered then
  642. minetest.register_on_joinplayer(function(...)
  643. return rc.on_joinplayer(...)
  644. end)
  645. minetest.register_on_leaveplayer(function(...)
  646. return rc.on_leaveplayer(...)
  647. end)
  648. local c = "rc:core"
  649. local f = rc.modpath .. "/init.lua"
  650. reload.register_file(c, f, false)
  651. rc.registered = true
  652. end