init.lua 30 KB

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