api.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. --[[
  2. stacked_realms, a API that allows modders to easily create stacked realms!
  3. Copyright (C) 2020 Genshin (emperor_genshin@hotmail.com)
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. --]]
  15. local update_interval = minetest.settings:get("realm_update_interval") or 1
  16. --Realm Registration Table
  17. realms = {
  18. locations = {},
  19. --[Method] Set String for Realm Context
  20. set_string = function(self, string, value)
  21. assert(string, "context string is missing."..dump(string))
  22. assert(type(string) == "string", "context string must be a string."..dump(string))
  23. assert(value or value == "", "context string\'s value is missing."..dump(value))
  24. if value == "" then --If the value is empty, nil it!
  25. value = nil
  26. end
  27. self[string] = value
  28. end,
  29. --[Method] Set String for Realm Context
  30. get_string = function(self, string)
  31. assert(string, "context string is missing."..dump(string))
  32. assert(type(string) == "string", "context string must be a string."..dump(string))
  33. return self[string]
  34. end
  35. }
  36. --[Global Function]: Register a new realm
  37. function realms.register_realm(realm, def)
  38. assert(realm, "realm requires a name.".. dump(realm))
  39. assert(def, "realm \""..realm.."\" does not have a definition table.".. dump(def))
  40. assert(type(def) == "table", "def is not a table.".. dump(def))
  41. assert(def.min_height and type(def.min_height) == "number", "min_height is not returning number or it\'s returning nil".. dump(def.min_height))
  42. assert(def.max_height and type(def.max_height) == "number", "max_height is not returning number or it\'s returning nil".. dump(def.max_height))
  43. assert(def.skybox_type and type(def.skybox_type) == "string"
  44. and (def.skybox_type == "default" or def.skybox_type == "colored" or def.skybox_type == "textured"),
  45. "skybox_type is not defined or it's not a string, must have a value of default, colored, or textured")
  46. assert(def.on_realm_entry and type(def.on_realm_entry) == "function", "def requires method \"on_realm_entry\" for realm \""..realm.."\".")
  47. assert(def.on_realm_entry and type(def.on_realm_leave) == "function", "def requires method \"on_realm_leave\" for realm \""..realm.."\".")
  48. assert(not realms.locations[realm], "attempt to register a realm that already exist." .. dump(realm))
  49. realms.locations[realm] = def
  50. def.in_realm = {} --Table array containing player names of players that are currently in the realm
  51. def.name = realm
  52. minetest.log("action", "Realm "..tostring(realm).." is now registered.")
  53. end
  54. --[Global Function]: Get the location name that the player is currently in (returns a string or nil)
  55. function get_player_realm_location(player)
  56. local name = player:get_player_name()
  57. local location = nil
  58. local list = realms.locations
  59. for k,v in pairs(list)do
  60. local realm = k
  61. if list[realm]["in_realm"][name] then
  62. location = realm
  63. break
  64. end
  65. end
  66. return location
  67. end
  68. --[Local Function]: Verify registered realms if they don't have matching minimum and max heights. If they do, purge them from their registration table
  69. local function verify_realms()
  70. local list = realms.locations
  71. local to_verify = {}
  72. local count = 0
  73. local verified_count = 0
  74. --First, store their names to a separate table and count them for verification
  75. for k,v in pairs(list)do
  76. local realm = k
  77. local def = v
  78. count = count + 1
  79. verified_count = count
  80. table.insert(to_verify, realm)
  81. end
  82. --then do another loop to go through all registered realms and verify conflicting height values
  83. for k,v in pairs(to_verify) do
  84. local realm = v
  85. local data = list[realm]
  86. local to_count = count
  87. minetest.log("action", "Verifing Realm "..tostring(realm).." for potential conflicting values before it becomes active.")
  88. while to_count > 0 do
  89. local to_compare = to_verify[to_count]
  90. local comparison = nil
  91. if list[realm] then
  92. comparison = list[realm]["name"]
  93. end
  94. if comparison and comparison ~= to_compare then --do not compare with yourself
  95. to_compare = list[to_compare]
  96. if to_compare == nil then else
  97. if data.min_height == to_compare.min_height then
  98. realms.locations[realm] = nil
  99. verified_count = verified_count - 1
  100. minetest.log("warning", "Realm with conflicting height values found, removing realm "..tostring(realm).." from the realms registration table to prevent potential errors.")
  101. elseif data.max_height == to_compare.max_height then
  102. realms.locations[realm] = nil
  103. verified_count = verified_count - 1
  104. minetest.log("warning", "Realm with conflicting height values found, removing realm "..tostring(realm).." from the realms registration table to prevent potential errors.")
  105. elseif data.min_height == to_compare.max_height or to_compare.max_height == data.min_height then
  106. realms.locations[realm] = nil
  107. verified_count = verified_count - 1
  108. minetest.log("warning", "Realm with conflicting height values found, removing realm "..tostring(realm).." from the realms registration table to prevent potential errors.")
  109. end
  110. end
  111. end
  112. to_count = to_count - 1
  113. end
  114. end
  115. minetest.log("action", tostring(verified_count).." out of "..tostring(count).." Realms have been verified and ready to activate.")
  116. end
  117. --[Local Function]: MArk the accurate current location by doing a checksum for each realm table
  118. local function in_realm_checksum(player, current_location)
  119. local name = player:get_player_name()
  120. local list = realms.locations
  121. for k,v in pairs(list) do
  122. local realm_to_check = k
  123. local to_compare = v["in_realm"]
  124. if realm_to_check ~= current_location then --Skip checking the current location to compare it's data with the other locations
  125. for a,b in pairs(to_compare) do
  126. local found_name = a
  127. if found_name == name then --If found, remove the player from the old location
  128. realms.locations[realm_to_check].in_realm[name] = nil
  129. list[realm_to_check]:on_realm_leave(player)
  130. break --Stop this loop after the job is completed
  131. end
  132. end
  133. end
  134. end
  135. list[current_location]:on_realm_entry(player)
  136. return
  137. end
  138. --[Local Function]: Realms Handler
  139. local function handle_realms()
  140. for _, player in ipairs(minetest.get_connected_players()) do
  141. local name = player:get_player_name()
  142. local list = realms.locations
  143. local pos = player:get_pos()
  144. local update_skybox = false
  145. local location_data = nil
  146. local old_location = ""
  147. local left_realm = false
  148. for k,v in pairs(list) do
  149. local realm = k
  150. local def = v
  151. local info = def["in_realm"]
  152. location_data = def
  153. if (pos.y < def.max_height and pos.y > def.min_height) or (pos.y == def.max_height or pos.y == def.min_height) then --Add player to the realm if the player is currently inside of it's bounds
  154. if info[name] == nil then
  155. update_skybox = true
  156. realms.locations[realm]["in_realm"][name] = true
  157. in_realm_checksum(player, realm)
  158. minetest.log("action", tostring(name).." is now in "..tostring(realm)..".")
  159. break
  160. end
  161. end
  162. end
  163. if update_skybox == true and location_data.skybox_type == "default" then
  164. player:set_sky(nil, "regular", {})
  165. player:set_clouds({
  166. density = 0.4,
  167. color = "#fff0f0e5",
  168. ambient = "#000000",
  169. height = 120,
  170. thickness = 16,
  171. speed = {x = 0, y = -2},
  172. })
  173. --If player exists, set his physics_override to 0 and notify him and the sender.
  174. player:set_physics_override({
  175. speed = minetest.settings:get("movement_speed_walk") or 1,
  176. jump = 1,
  177. gravity = 1.0,
  178. sneak = true,
  179. sneak_glitch = false,
  180. new_move = false,
  181. })
  182. elseif update_skybox == true and location_data.skybox_type == "colored" then
  183. player:set_sky(location_data.skybox_color, "plain", {})
  184. if location_data.enable_clouds == true then
  185. player:set_clouds({
  186. density = location_data.cloud_density or 0.4,
  187. color = location_data.cloud_color or "#fff0f0e5",
  188. ambient = location_data.cloud_ambient_color or "#000000",
  189. height = location_data.cloud_height or 120,
  190. thickness = location_data.cloud_thickness or 16,
  191. speed = location_data.cloud_speed or {x = 0, y = -2},
  192. })
  193. end
  194. if location_data.realm_physics == true then
  195. --If player exists, set his physics_override to 0 and notify him and the sender.
  196. player:set_physics_override({
  197. speed = minetest.settings:get("movement_speed_walk") or 1,
  198. jump = 1,
  199. gravity = location_data.realm_gravity or 1.0,
  200. sneak = true,
  201. sneak_glitch = false,
  202. new_move = false,
  203. })
  204. end
  205. elseif update_skybox == true and location_data.skybox_type == "textured" then
  206. player:set_sky({}, "skybox", location_data.skybox_textures)
  207. if location_data.enable_clouds == true then
  208. player:set_clouds({
  209. density = location_data.cloud_density or 0.4,
  210. color = location_data.cloud_color or "#fff0f0e5",
  211. ambient = location_data.cloud_ambient_color or "#000000",
  212. height = location_data.cloud_height or 120,
  213. thickness = location_data.cloud_thickness or 16,
  214. speed = location_data.cloud_speed or {x = 0, y = -2},
  215. })
  216. end
  217. if location_data.realm_physics == true then
  218. --If player exists, set his physics_override to 0 and notify him and the sender.
  219. player:set_physics_override({
  220. speed = minetest.settings:get("movement_speed_walk") or 1,
  221. jump = 1,
  222. gravity = location_data.realm_gravity or 1.0,
  223. sneak = true,
  224. sneak_glitch = false,
  225. new_move = false,
  226. })
  227. end
  228. end
  229. end
  230. minetest.after(update_interval, function()
  231. handle_realms()
  232. end)
  233. end
  234. --Activate cooldown handler
  235. minetest.register_on_mods_loaded(function()
  236. minetest.after(update_interval, function()
  237. verify_realms()
  238. handle_realms()
  239. end)
  240. end)
  241. minetest.register_on_leaveplayer(function(player)
  242. local name = player:get_player_name()
  243. if name then
  244. player:set_sky({}, "regular", {})
  245. for k,v in pairs(realms.locations)do
  246. local realm = k
  247. if realms.locations[realm]["in_realm"][name] == true then
  248. realms.locations[realm]["in_realm"][name] = nil
  249. end
  250. end
  251. end
  252. end)