init.lua 25 KB

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