init.lua 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911
  1. -- Realm Control Mod.
  2. -- This mod manages realm boundaries and prevents players from moving freely
  3. -- between realms/dimensions without programmatic intervention.
  4. if not minetest.global_exists("rc") then rc = {} end
  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, body_orbit_tilt=0.0}
  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, day_opacity=0.0}
  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. -- WARNING: ABSOLUTE MINIMUM GAP BETWEEN REALMS MUST BE 500 BLOCKS!
  22. rc.realms = {
  23. {
  24. id = 1, -- REALM ID. Code relies on this.
  25. name = "overworld", -- Default/overworld realm.
  26. description = "Overworld",
  27. minp = {x=-30912, y=-30912, z=-30912},
  28. maxp = {x=30927, y=500, z=30927},
  29. gate_minp = {x=-30000, y=-30800, z=-30000},
  30. gate_maxp = {x=30000, y=-10, z=30000},
  31. orig = {x=-9223, y=4169+400, z=5861}, -- Same as server's static spawnpoint!
  32. ground = -10,
  33. underground = -32, -- Affects sky color, see sky mod.
  34. sealevel = 0,
  35. windlevel = 20,
  36. realm_origin = {x=-1067, y=-10, z=8930},
  37. disabled = false, -- Whether realm can be "gated" to. Use when testing!
  38. cloud_data = {
  39. -- Overworld clouds change direction of travel on every restart.
  40. speed = {x = math_random(-200, 200)/200, z = math_random(-200, 200)/200},
  41. },
  42. sky_data = {body_orbit_tilt = snow.body_orbit_tilt()},
  43. star_data = {day_opacity = snow.star_opacity()},
  44. },
  45. -- Distance to channelwood: 2500
  46. {
  47. id = 2, -- REALM ID. Code relies on this.
  48. name = "channelwood", -- Forest realm. 250 meters high.
  49. description = "Channelwood",
  50. minp = {x=-30912, y=3050, z=-30912},
  51. maxp = {x=30927, y=3150, z=30927},
  52. gate_minp = {x=-30000, y=3065, z=-30000},
  53. gate_maxp = {x=30000, y=3067, z=30000},
  54. orig = {x=-9223, y=4169+400, z=5861}, -- Same as server's static spawnpoint!
  55. ground = 3066,
  56. underground = 3050,
  57. sealevel = 3066,
  58. windlevel = 3100,
  59. realm_origin = {x=2019, y=3066, z=-1992},
  60. disabled = false, -- Whether realm can be "gated" to.
  61. cloud_data={height=3112, density=0.6, speed={x=0.1, z=0.1}, thickness=4},
  62. moon_data={scale=2.5},
  63. sky_data = {body_orbit_tilt = -10.0},
  64. },
  65. -- Distance to jarkati: 450
  66. -- This breaks our minimum distance rule. Not much I can do about it now.
  67. {
  68. id = 3, -- REALM ID. Code relies on this.
  69. name = "jarkati",
  70. description = "Jarkati",
  71. minp = {x=-30912, y=3600, z=-30912},
  72. maxp = {x=30927, y=3800, z=30927},
  73. gate_minp = {x=-30000, y=3620, z=-30000},
  74. gate_maxp = {x=30000, y=3640, z=30000},
  75. orig = {x=-9223, y=4169+400, z=5861}, -- Same as server's static spawnpoint!
  76. ground = 3740,
  77. underground = 3730,
  78. sealevel = 3740,
  79. windlevel = 3750,
  80. realm_origin = {x=1986, y=3700, z=-1864},
  81. sky_data={clouds=true},
  82. cloud_data={height=3900, density=0.2, speed={x=5, z=2}},
  83. moon_data={scale=0.4},
  84. sun_data={scale=0.4},
  85. },
  86. -- Distance to utilities (pocket realms): 600
  87. {
  88. -- The OUTBACK. Starting realm for new players.
  89. id = 4, -- REALM ID. Code relies on this.
  90. name = "abyss",
  91. description = "Outback",
  92. minp = vector.add({x=-9174, y=4100+400, z=5782}, {x=-100, y=-100, z=-100}),
  93. maxp = vector.add({x=-9174, y=4100+400, z=5782}, {x=100, y=100, z=100}),
  94. gate_minp = vector.add({x=-9174, y=4100+400, z=5782}, {x=-80, y=-80, z=-80}),
  95. gate_maxp = vector.add({x=-9174, y=4100+400, z=5782}, {x=80, y=80, z=80}),
  96. orig = {x=-9223, y=4169+400, z=5861}, -- Same as server's static spawnpoint!
  97. ground = 4170+400,
  98. underground = 4160+400, -- Affects sky color, see sky mod.
  99. sealevel = 4160+400,
  100. windlevel = 4150+400,
  101. realm_origin = {x=-9174, y=4100+400, z=5782},
  102. disabled = true, -- Realm cannot receive an incoming gate. OFFICIAL.
  103. sky_data = {clouds=false},
  104. sun_data = {visible=true},
  105. moon_data = {visible=true},
  106. star_data = {visible=true, count=50},
  107. realm_resets = true,
  108. --[[
  109. -- Notes:
  110. --
  111. -- If you got into the Outback by dying in Midfeld (and your bed wasn't
  112. -- in Midfeld), then using the Outback portal shall always send you back
  113. -- to Midfeld, even if you subseqently died in the Outback as well.
  114. --
  115. -- Otherwise, using the Outback portal sends you to the Overworld.
  116. --]]
  117. },
  118. {
  119. -- The MIDFELD. In-between place; travel realm.
  120. id = 5, -- REALM ID. Code relies on this.
  121. name = "midfeld",
  122. description = "Midfeld",
  123. minp = vector.add({x=-12174, y=4100+400, z=5782}, {x=-132, y=-50, z=-132}),
  124. maxp = vector.add({x=-12174, y=4100+400, z=5782}, {x=132, y=150, z=132}),
  125. gate_minp = vector.add({x=-12174, y=4100+400, z=5782}, {x=-116, y=-34, z=-116}),
  126. gate_maxp = vector.add({x=-12174, y=4100+400, z=5782}, {x=116, y=-10, z=116}),
  127. orig = {x=-9223, y=4169+400, z=5861}, -- Same as server's static spawnpoint!
  128. ground = 4096+400,
  129. underground = 4085+400, -- Affects sky color, see sky mod.
  130. sealevel = 4095+400,
  131. windlevel = 4125+400,
  132. realm_origin = {x=-12174, y=4097+400, z=5782},
  133. disabled = true, -- Realm cannot receive an incoming gate. OFFICIAL.
  134. moon_data = {scale=2.5},
  135. sun_data = {scale=2.5},
  136. cloud_data = {height=4250+400, density=0.2, speed={x=-6, z=1}},
  137. protection_temporary = true,
  138. protection_time = 60*60*24*14,
  139. realm_resets = true,
  140. --[[
  141. //fixedpos set1 -11967 4450 5989
  142. //fixedpos set2 -12381 4650 5575
  143. -- Schempos: -12381,4450,5575
  144. -- Notes:
  145. --
  146. -- If you die in Midfeld for any reason,
  147. -- a) if you have a bed IN Midfeld, you respawn in your bed,
  148. -- b) otherwise (even if you have a bed elsewhere), you respawn in the Outback.
  149. --
  150. -- Dying in Midfeld is the canonical way to get into the Outback without
  151. -- needing to lose your bed respawn position (the dumb way, is to lose your
  152. -- bed's respawn position by dying multiple times).
  153. --]]
  154. },
  155. -- Distance to stoneworld: 500
  156. {
  157. -- Stoneworld. Tartarus. Place of evildoers. 3000 blocks high, all stone.
  158. id = 6, -- REALM ID. Code relies on this.
  159. name = "naraxen",
  160. description = "Naraxen",
  161. minp = {x=-30912, y=5150, z=-30912},
  162. maxp = {x=30927, y=8150, z=30927},
  163. gate_minp = {x=-30000, y=5350, z=-30000},
  164. gate_maxp = {x=30000, y=7950, z=30000},
  165. orig = {x=-9223, y=4169+400, z=5861}, -- Same as server's static spawnpoint!
  166. ground = 8150,
  167. underground = 8150, -- Affects sky color, see sky mod.
  168. sealevel = 8150,
  169. windlevel = 8150,
  170. realm_origin = {x=2382, y=6650, z=-3721},
  171. moon_data = {visible=false},
  172. sun_data = {visible=false},
  173. sky_data = {clouds=false},
  174. star_data = {visible=false},
  175. },
  176. }
  177. -- Return true if a position is underground in some realm.
  178. -- False is returned if not underground.
  179. -- Returns nil if position isn't in any valid realm.
  180. function rc.position_underground(pos)
  181. local p = vector_round(pos)
  182. for k, v in ipairs(rc.realms) do
  183. local minp = v.minp
  184. local maxp = v.maxp
  185. -- Is position within realm boundaries?
  186. if p.x >= minp.x and p.x <= maxp.x and
  187. p.y >= minp.y and p.y <= maxp.y and
  188. p.z >= minp.z and p.z <= maxp.z then
  189. if p.y < v.underground then
  190. return true
  191. else
  192. return false
  193. end
  194. end
  195. end
  196. -- Not in any realm?
  197. return nil
  198. end
  199. -- Used by ice/ice-brick/snow nodes to determine if they should melt away.
  200. -- This is also used by the tree-snowdust code to determine whether a tree
  201. -- should spawn with snow on top.
  202. function rc.ice_melts_at_pos(pos)
  203. if pos.y < -26000 or pos.y > 1000 then
  204. return true
  205. end
  206. end
  207. -- Convert realm position to absolute coordinate. May return NIL if not valid!
  208. function rc.realmpos_to_pos(realm, pos)
  209. local data = rc.get_realm_data(realm:lower())
  210. if data then
  211. local origin = data.realm_origin
  212. return {
  213. x = origin.x + pos.x,
  214. y = origin.y + pos.y,
  215. z = origin.z + pos.z,
  216. }
  217. end
  218. -- Indicate failure.
  219. return nil
  220. end
  221. -- Convert absolute coordinate to realm position. May return NIL if not valid!
  222. function rc.pos_to_realmpos(pos)
  223. local origin = rc.get_realm_origin_at_pos(pos)
  224. if origin then
  225. return {
  226. x = pos.x - origin.x,
  227. y = pos.y - origin.y,
  228. z = pos.z - origin.z,
  229. }
  230. end
  231. -- Indicate failure.
  232. return nil
  233. end
  234. function rc.pos_to_namestr(pos)
  235. local name = rc.pos_to_name(pos)
  236. local str = rc.pos_to_string(pos)
  237. str = string.gsub(str, "[%(%)]", "")
  238. return "(" .. name .. ": " .. str .. ")"
  239. end
  240. function rc.pos_to_name(pos)
  241. return rc.realm_description_at_pos(pos)
  242. end
  243. function rc.get_realm_origin_at_pos(p)
  244. for k, v in ipairs(rc.realms) do
  245. local minp = v.minp
  246. local maxp = v.maxp
  247. -- Is position within realm boundaries?
  248. if p.x >= minp.x and p.x <= maxp.x and
  249. p.y >= minp.y and p.y <= maxp.y and
  250. p.z >= minp.z and p.z <= maxp.z then
  251. local o = table.copy(v.realm_origin)
  252. return o
  253. end
  254. end
  255. -- Not in any realm?
  256. return nil
  257. end
  258. -- Obtain a string in the format "(x,y,z)".
  259. function rc.pos_to_string(pos)
  260. local realpos = rc.pos_to_realmpos(pos)
  261. if realpos then
  262. return minetest.pos_to_string(realpos)
  263. end
  264. -- Indicate failure.
  265. return "(Nan,Nan,Nan)"
  266. end
  267. function rc.get_realm_sky(pos)
  268. local n = rc.get_realm_data(rc.current_realm_at_pos(pos))
  269. if n then
  270. local t = table.copy(default_sky)
  271. for k, v in pairs(n.sky_data or {}) do
  272. t[k] = v
  273. end
  274. --minetest.chat_send_all(dump(t))
  275. return t
  276. end
  277. return {}
  278. end
  279. function rc.get_realm_sun(pos)
  280. local n = rc.get_realm_data(rc.current_realm_at_pos(pos))
  281. if n then
  282. local t = table.copy(default_sun)
  283. for k, v in pairs(n.sun_data or {}) do
  284. t[k] = v
  285. end
  286. --minetest.chat_send_all(dump(t))
  287. return t
  288. end
  289. return {}
  290. end
  291. function rc.get_realm_moon(pos)
  292. local n = rc.get_realm_data(rc.current_realm_at_pos(pos))
  293. if n then
  294. local t = table.copy(default_moon)
  295. for k, v in pairs(n.moon_data or {}) do
  296. t[k] = v
  297. end
  298. --minetest.chat_send_all(dump(t))
  299. return t
  300. end
  301. return {}
  302. end
  303. function rc.get_realm_stars(pos)
  304. local n = rc.get_realm_data(rc.current_realm_at_pos(pos))
  305. if n then
  306. local t = table.copy(default_stars)
  307. for k, v in pairs(n.star_data or {}) do
  308. t[k] = v
  309. end
  310. --minetest.chat_send_all(dump(t))
  311. return t
  312. end
  313. return {}
  314. end
  315. function rc.get_realm_clouds(pos)
  316. local n = rc.get_realm_data(rc.current_realm_at_pos(pos))
  317. if n then
  318. local t = table.copy(default_clouds)
  319. for k, v in pairs(n.cloud_data or {}) do
  320. t[k] = v
  321. end
  322. --minetest.chat_send_all(dump(t))
  323. return t
  324. end
  325. return {}
  326. end
  327. function rc.get_realm_data(name)
  328. for k, v in ipairs(rc.realms) do
  329. if v.name == name then
  330. return v
  331. end
  332. if v.name == "overworld" then
  333. -- Alternate names.
  334. if name == "netherworld" or name == "nether" or name == "caverns" or name == "caves" then
  335. return v
  336. end
  337. end
  338. if v.name == "abyss" then
  339. -- Alternate names.
  340. if name == "outback" then
  341. return v
  342. end
  343. end
  344. end
  345. return nil
  346. end
  347. function rc.get_random_enabled_realm_data()
  348. if (#rc.realms) < 1 then
  349. return
  350. end
  351. local tries = 1
  352. local realm = rc.realms[math_random(1, #rc.realms)]
  353. while realm.disabled and tries < 10 do
  354. tries = tries + 1
  355. realm = rc.realms[math_random(1, #rc.realms)]
  356. end
  357. if realm.disabled then
  358. return
  359. end
  360. return realm
  361. end
  362. function rc.get_random_realm_gate_position(pname, origin)
  363. if rc.is_valid_realm_pos(origin) then
  364. if origin.y >= 128 and origin.y <= 1000 then
  365. -- If gateway is positioned in the Overworld mountains,
  366. -- permit easy realm hopping.
  367. local realm = rc.get_random_enabled_realm_data()
  368. if not realm then
  369. return nil
  370. end
  371. assert(realm)
  372. local pos = {
  373. x = math_random(realm.gate_minp.x, realm.gate_maxp.x),
  374. y = math_random(realm.gate_minp.y, realm.gate_maxp.y),
  375. z = math_random(realm.gate_minp.z, realm.gate_maxp.z),
  376. }
  377. local below = vector.add(origin, {x=0, y=-16, z=0})
  378. local minp = vector.add(below, {x=-16, y=-16, z=-16})
  379. local maxp = vector.add(below, {x=16, y=16, z=16})
  380. -- Should find 30K stone.
  381. local positions, counts = minetest.find_nodes_in_area(minp, maxp, {"default:stone"})
  382. --for k, v in pairs(counts) do
  383. -- minetest.chat_send_player(pname, "# Server: " .. k .. " = " .. v .. "!")
  384. --end
  385. if counts["default:stone"] > math_random(10000, 30000) then
  386. -- Search again, even deeper. The stone amount should be MUCH higher.
  387. below = vector.add(below, {x=0, y=-32, z=0})
  388. minp = vector.add(below, {x=-16, y=-16, z=-16})
  389. maxp = vector.add(below, {x=16, y=16, z=16})
  390. positions, counts = minetest.find_nodes_in_area(minp, maxp, {"default:stone"})
  391. --for k, v in pairs(counts) do
  392. -- minetest.chat_send_player(pname, "# Server: " .. k .. " = " .. v .. "!")
  393. --end
  394. if counts["default:stone"] > math_random(20000, 32000) then
  395. return pos
  396. end
  397. end
  398. return nil
  399. elseif origin.y > 1000 then
  400. -- The gateway is positioned in a realm somewhere.
  401. -- 9/10 times the exit point stays in the same realm.
  402. -- Sometimes a realm hop is possible.
  403. local realm
  404. if math_random(1, 10) == 1 then
  405. realm = rc.get_random_enabled_realm_data()
  406. else
  407. realm = rc.get_realm_data(rc.current_realm_at_pos(origin))
  408. end
  409. if not realm then
  410. return nil
  411. end
  412. assert(realm)
  413. -- Not more than 3000 meters away from origin!
  414. local pos = {
  415. x = math_random(-3000, 3000) + origin.x,
  416. y = math_random(-300, 300) + origin.y,
  417. z = math_random(-3000, 3000) + origin.z,
  418. }
  419. local min = math.min
  420. local max = math.max
  421. -- Clamp position to ensure we remain within realm boundaries.
  422. pos.x = max(realm.gate_minp.x, min(pos.x, realm.gate_maxp.x))
  423. pos.y = max(realm.gate_minp.y, min(pos.y, realm.gate_maxp.y))
  424. pos.z = max(realm.gate_minp.z, min(pos.z, realm.gate_maxp.z))
  425. return pos
  426. end
  427. end
  428. local realm = rc.get_realm_data("overworld")
  429. assert(realm)
  430. -- Player is in the Overworld or Nether. Use old Gateway behavior!
  431. -- Not more than 3000 meters in any direction, and MUST stay in the Overworld
  432. -- (or the Nether). Gate is more likely to go down rather than up.
  433. local pos = {
  434. x = math_random(-3000, 3000) + origin.x,
  435. y = math_random(-2000, 300) + origin.y,
  436. z = math_random(-3000, 3000) + origin.z,
  437. }
  438. local min = math.min
  439. local max = math.max
  440. -- Clamp position.
  441. pos.x = max(realm.gate_minp.x, min(pos.x, realm.gate_maxp.x))
  442. pos.y = max(realm.gate_minp.y, min(pos.y, realm.gate_maxp.y))
  443. pos.z = max(realm.gate_minp.z, min(pos.z, realm.gate_maxp.z))
  444. return pos
  445. end
  446. function rc.is_valid_gateway_region(pos)
  447. local p = vector_round(pos)
  448. for k, v in ipairs(rc.realms) do
  449. local gate_minp = v.gate_minp
  450. local gate_maxp = v.gate_maxp
  451. -- Is position within realm boundaries suitable for a gateway?
  452. if p.x >= gate_minp.x and p.x <= gate_maxp.x and
  453. p.y >= gate_minp.y and p.y <= gate_maxp.y and
  454. p.z >= gate_minp.z and p.z <= gate_maxp.z then
  455. return true
  456. end
  457. end
  458. -- Not in any realm?
  459. return false
  460. end
  461. function rc.is_valid_realm_pos(pos)
  462. local p = vector_round(pos)
  463. for i = 1, #rc.realms, 1 do
  464. local v = rc.realms[i]
  465. local minp = v.minp
  466. local maxp = v.maxp
  467. -- Is position within realm boundaries?
  468. if p.x >= minp.x and p.x <= maxp.x and
  469. p.y >= minp.y and p.y <= maxp.y and
  470. p.z >= minp.z and p.z <= maxp.z then
  471. return true
  472. end
  473. end
  474. -- Not in any realm?
  475. return false
  476. end
  477. function rc.get_ground_level_at_pos(pos)
  478. local p = vector_round(pos)
  479. for k, v in ipairs(rc.realms) do
  480. local minp = v.minp
  481. local maxp = v.maxp
  482. -- Is position within realm boundaries?
  483. if p.x >= minp.x and p.x <= maxp.x and
  484. p.y >= minp.y and p.y <= maxp.y and
  485. p.z >= minp.z and p.z <= maxp.z then
  486. return true, v.ground
  487. end
  488. end
  489. -- Not in any realm?
  490. return false, nil
  491. end
  492. function rc.get_sea_level_at_pos(pos)
  493. local p = vector_round(pos)
  494. for k, v in ipairs(rc.realms) do
  495. local minp = v.minp
  496. local maxp = v.maxp
  497. -- Is position within realm boundaries?
  498. if p.x >= minp.x and p.x <= maxp.x and
  499. p.y >= minp.y and p.y <= maxp.y and
  500. p.z >= minp.z and p.z <= maxp.z then
  501. return true, v.sealevel
  502. end
  503. end
  504. -- Not in any realm?
  505. return false, nil
  506. end
  507. function rc.get_wind_level_at_pos(pos)
  508. local p = vector_round(pos)
  509. for k, v in ipairs(rc.realms) do
  510. local minp = v.minp
  511. local maxp = v.maxp
  512. -- Is position within realm boundaries?
  513. if p.x >= minp.x and p.x <= maxp.x and
  514. p.y >= minp.y and p.y <= maxp.y and
  515. p.z >= minp.z and p.z <= maxp.z then
  516. return true, v.windlevel
  517. end
  518. end
  519. -- Not in any realm?
  520. return false, nil
  521. end
  522. -- API function. Get string name of the current realm the player is in.
  523. function rc.current_realm(player)
  524. local p = vector_round(player:get_pos())
  525. return rc.current_realm_at_pos(p)
  526. end
  527. -- API function. Get string name of the current realm at a position.
  528. function rc.current_realm_at_pos(p)
  529. for k, v in ipairs(rc.realms) do
  530. local minp = v.minp
  531. local maxp = v.maxp
  532. -- Is pos within realm boundaries?
  533. if p.x >= minp.x and p.x <= maxp.x and
  534. p.y >= minp.y and p.y <= maxp.y and
  535. p.z >= minp.z and p.z <= maxp.z then
  536. return v.name
  537. end
  538. end
  539. -- Not in any realm?
  540. return ""
  541. end
  542. -- API function. Get static spawn point of a named realm.
  543. function rc.static_spawn(name)
  544. for k, v in ipairs(rc.realms) do
  545. if v.name == name then
  546. return table.copy(v.orig)
  547. end
  548. end
  549. -- Not in any realm?
  550. return {x=0, y=-7, z=0}
  551. end
  552. function rc.same_realm(p1, p2)
  553. return (rc.current_realm_at_pos(p1) == rc.current_realm_at_pos(p2))
  554. end
  555. function rc.realm_description_at_pos(p)
  556. -- Special realm name.
  557. if p.y < -25000 then
  558. if p.y < -30760 and p.y > -30800 then
  559. return "Abyssal Sea"
  560. end
  561. return "Nether"
  562. elseif p.y < -5000 then
  563. return "Caverns"
  564. end
  565. for k, v in ipairs(rc.realms) do
  566. local minp = v.minp
  567. local maxp = v.maxp
  568. -- Is player within realm boundaries?
  569. if p.x >= minp.x and p.x <= maxp.x and
  570. p.y >= minp.y and p.y <= maxp.y and
  571. p.z >= minp.z and p.z <= maxp.z then
  572. return v.description
  573. end
  574. end
  575. -- Not in any realm?
  576. return "Void"
  577. end
  578. -- API function.
  579. -- Check player position and current realm. If not valid, reset player to last
  580. -- valid location. If last valid location not found, reset them to 0,0,0.
  581. -- This function should be called from a global-step callback somewhere.
  582. function rc.check_position(player)
  583. local p = vector_round(player:get_pos())
  584. local n = player:get_player_name()
  585. local data = rc.players[n]
  586. -- Data not initialized yet.
  587. if not data then return end
  588. -- Admin may fly around in the void.
  589. -- WARNING: use only when needed!
  590. -- Careless flying+mapgen WILL cause lighting issues!
  591. --if gdac.player_is_admin(player) then return end
  592. local reset -- Table set if player out-of-bounds.
  593. -- Bounds check to avoid an engine bug. These coordinates should be the last
  594. -- row of nodes at the map edge. This way, we never teleport the player to a
  595. -- location that is strictly outside the world boundaries, if they trigger it.
  596. if p.x < -30912 or p.x > 30927 or
  597. p.y < -30912 or p.y > 30927 or
  598. p.z < -30912 or p.z > 30927 then
  599. -- Some old clients, it seems, can randomly cause this problem.
  600. -- Or someone is deliberately triggering it.
  601. reset = {}
  602. reset.spawn = rc.static_spawn("abyss")
  603. end
  604. -- Check if player is currently in the void.
  605. if not reset then
  606. if data.realm == "" then
  607. reset = {}
  608. reset.spawn = rc.static_spawn("abyss")
  609. end
  610. end
  611. -- Do bounds checks for individual realms.
  612. if not reset then
  613. local lrem = #(rc.realms)
  614. for k = 1, lrem, 1 do
  615. local v = rc.realms[k]
  616. -- Is player within boundaries of the realm they are supposed to be in?
  617. if data.realm == v.name then
  618. local minp = v.minp
  619. local maxp = v.maxp
  620. if p.x < minp.x or p.x > maxp.x or
  621. p.y < minp.y or p.y > maxp.y or
  622. p.z < minp.z or p.z > maxp.z then
  623. reset = {}
  624. reset.spawn = v.orig -- Use current realm's respawn coordinates.
  625. break
  626. end
  627. end
  628. end
  629. end
  630. if reset then
  631. -- Player is out-of-bounds. Reset to last known good position.
  632. if not gdac.player_is_admin(n) and not data.new_arrival then
  633. if not spam.test_key(58921) then
  634. spam.mark_key(58921, 30)
  635. minetest.chat_send_all("# Server: <" .. rename.gpn(n) ..
  636. "> strayed from the path ....")
  637. end
  638. end
  639. -- Notify wield3d we're adjusting the player position.
  640. -- Wielded item entities don't like sudden movement.
  641. wield3d.on_teleport()
  642. default.detach_player_if_attached(player)
  643. if player:get_hp() > 0 then
  644. -- Return player to last known good position.
  645. local nrealm = rc.current_realm_at_pos(data.pos)
  646. if nrealm ~= "" and nrealm == data.realm then
  647. player:set_pos(data.pos)
  648. else
  649. -- This can happen if player joins the server outside of any realm.
  650. rc.notify_realm_update(player, reset.spawn)
  651. player:set_pos(reset.spawn)
  652. end
  653. else
  654. -- Update which realm the player is supposed to be in.
  655. -- (We might have crossed realms depending on what happened above.)
  656. rc.notify_realm_update(player, reset.spawn)
  657. -- Return to realm's origin point.
  658. player:set_pos(reset.spawn)
  659. end
  660. -- Damage player. Prevents them triggering this indefinitely.
  661. if player:get_hp() > 0 and not data.new_arrival then
  662. -- Bypass armor code.
  663. player:set_hp(player:get_hp() - (2*500))
  664. -- Note: per bones code, if position is not within any valid realm, bones
  665. -- will not be spawned.
  666. if player:get_hp() <= 0 then
  667. if not gdac.player_is_admin(n) then
  668. minetest.chat_send_all("# Server: <" .. rename.gpn(n) ..
  669. "> took a flying leap off the Yggdrasill.")
  670. spam.block_playerjoin(n, 60*2)
  671. minetest.kick_player(n, "You found an end in the Void.")
  672. end
  673. end
  674. end
  675. return
  676. end
  677. -- If we got this far, the player is not out of bounds.
  678. -- Record last known good position. Realm name should be same as before.
  679. do
  680. local ps = data.pos
  681. ps.x = p.x
  682. ps.y = p.y
  683. ps.z = p.z
  684. end
  685. end
  686. function rc.on_joinplayer(player)
  687. local n = player:get_player_name()
  688. local p = player:get_pos()
  689. local crealm = rc.current_realm(player)
  690. -- Player's current dimension is determined from position on login.
  691. rc.players[n] = {
  692. pos = p,
  693. realm = crealm,
  694. new_arrival = true,
  695. }
  696. -- Remove the 'new_arrival' flag after a few seconds.
  697. minetest.after(10, function()
  698. local data = rc.players[n]
  699. if not data then return end
  700. data.new_arrival = nil
  701. end)
  702. end
  703. function rc.on_leaveplayer(player, timeout)
  704. local n = player:get_player_name()
  705. rc.players[n] = nil
  706. end
  707. function rc.plane_shift_message(pref, p, n)
  708. local pp = vector_round(pref:get_pos())
  709. local rr = rc.current_realm_at_pos(pp)
  710. local rr2 = rc.current_realm_at_pos(p)
  711. -- Only if new realm is different from old.
  712. if rr ~= rr2 then
  713. if gdac_invis.is_invisible(n) or cloaking.is_cloaked(n) or player_labels.query_nametag_onoff(n) == false then
  714. if not gdac.player_is_admin(n) then
  715. minetest.chat_send_all("# Server: Someone has plane shifted.")
  716. end
  717. else
  718. local d = rc.get_realm_data(rr2)
  719. if d and d.description then
  720. local realm_name = d.description
  721. local insult = ""
  722. if rr ~= "abyss" and rr2 == "abyss" then
  723. insult = " Noob!"
  724. end
  725. minetest.chat_send_all("# Server: <" .. rename.gpn(n) .. "> has plane shifted to " .. realm_name .. "." .. insult)
  726. end
  727. end
  728. end
  729. end
  730. -- API function. Call this whenever a player teleports,
  731. -- or lawfully changes realm. You can pass a player object or a name.
  732. -- Note: this must be called *before* you call :set_pos() on the player!
  733. -- Note #2: read the above note again! I really mean it!
  734. function rc.notify_realm_update(player, pos)
  735. local p = vector_round(pos)
  736. local n = ""
  737. if type(player) == "string" then
  738. n = player
  739. else
  740. n = player:get_player_name()
  741. end
  742. local pref = minetest.get_player_by_name(n)
  743. local tb = rc.players[n]
  744. if not tb then
  745. minetest.log("action", "could not find data for player " .. n .. " when updating realm info")
  746. return
  747. end
  748. -- Print plane-shift message.
  749. if pref and tb.realm then
  750. rc.plane_shift_message(pref, p, n)
  751. end
  752. -- Set 'new_arrival' flag to avoid complaining about them straying from the path ....
  753. tb.new_arrival = true
  754. tb.pos = p
  755. tb.realm = rc.current_realm_at_pos(p)
  756. sky.notify_sky_update_needed(n)
  757. -- Remove the 'new_arrival' flag after a few seconds.
  758. -- Note: this flag was set in order to suppress the 'strayed from path' message
  759. -- that gets printed if the player is caught outside the correct realm. This
  760. -- can happen because player position is controled by the client. Give the
  761. -- player's client a few seconds to get its feet in gear.
  762. minetest.after(5, function()
  763. local data = rc.players[n]
  764. if not data then return end
  765. data.new_arrival = nil
  766. end)
  767. end
  768. if not rc.registered then
  769. minetest.register_on_joinplayer(function(...)
  770. return rc.on_joinplayer(...)
  771. end)
  772. minetest.register_on_leaveplayer(function(...)
  773. return rc.on_leaveplayer(...)
  774. end)
  775. local c = "rc:core"
  776. local f = rc.modpath .. "/init.lua"
  777. reload.register_file(c, f, false)
  778. rc.registered = true
  779. end