init.lua 18 KB

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