init.lua 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. -- multihome/init.lua
  2. multihome = {}
  3. -- Load settings from minetest.conf, else set to default values
  4. local max = tonumber(minetest.settings:get("multihome.max")) or 10
  5. local compat = minetest.settings:get("multihome.compatibility") or "none"
  6. local import = minetest.settings:get("multihome.import") or "false"
  7. -- Out of range is always bad, so log as error
  8. if max < 2 or max > 10000 then
  9. minetest.log("error", "multihome.max value of " .. dump(max) .. " in minetest.conf is outside of 2 - 10000 range. Resetting to 5.")
  10. max = 5
  11. end
  12. -- Any other value is always bad, so log as error
  13. if compat ~= "none" and compat ~= "deprecate" and compat ~= "override" then
  14. minetest.log("error", "multihome.compatibility value of '" .. compat .. "' in minetest.conf is invalid. Valid values are: none (default), deprecate, or override. Resetting to 'none'.")
  15. compat = "none"
  16. end
  17. -- The "sethome" mod can differ per world, so log as warning
  18. if compat ~= "none" and not minetest.get_modpath("sethome") then
  19. minetest.log("warning", "multihome.compatibility value of '" .. compat .. "' in minetest.conf is invalid when sethome mod not present. Resetting to 'none'.")
  20. compat = "none"
  21. end
  22. ---
  23. --- Load old homes from the homes file
  24. ---
  25. local homes_file = minetest.get_worldpath() .. "/homes"
  26. local oldhomes = {}
  27. local function old_loadhomes()
  28. local input = io.open(homes_file, "r")
  29. if not input then
  30. return -- no longer an error
  31. end
  32. -- Iterate over all stored positions in the format "x y z player" for each line
  33. for pos, name in input:read("*a"):gmatch("(%S+ %S+ %S+)%s([%w_-]+)[\r\n]") do
  34. oldhomes[name] = minetest.string_to_pos(pos)
  35. end
  36. input:close()
  37. end
  38. old_loadhomes()
  39. ---
  40. --- API
  41. ---
  42. -- [local function] Check attribute
  43. local function check_attr(player)
  44. if not player:get_attribute("multihome") then
  45. player:set_attribute("multihome", minetest.serialize({}))
  46. end
  47. end
  48. -- [local function] Count homes
  49. local function count_homes(list)
  50. local count = 0
  51. for _, h in pairs(list) do
  52. count = count + 1
  53. end
  54. return count
  55. end
  56. -- [function] Set home
  57. function multihome.set(player, name, pos)
  58. if type(player) == "string" then
  59. player = minetest.get_player_by_name(player)
  60. end
  61. local pos = pos or vector.round(player:getpos())
  62. local homes = minetest.deserialize(player:get_attribute("multihome"))
  63. local home_count = count_homes(homes)
  64. -- If home doesn't already exist (i.e. a new home is being created), check for space.
  65. -- Else, if count > max (should only happen if max gets lowered), indicate how many to remove.
  66. if not homes[name] and home_count == max then
  67. return false, "Error: too many homes. Replace one by reusing an existing name, or remove one with /multihome del <name> or /delhome <name>"
  68. elseif home_count > max then
  69. return false, "Error: too many homes. Remove at least " .. dump(home_count - max) .. " with /multihome del <name> or /delhome <name>"
  70. end
  71. homes[name] = pos
  72. player:set_attribute("multihome", minetest.serialize(homes))
  73. return true, "Set home \""..name.."\" to "..minetest.pos_to_string(pos)
  74. end
  75. -- [function] Remove home
  76. function multihome.remove(player, name)
  77. if type(player) == "string" then
  78. player = minetest.get_player_by_name(player)
  79. end
  80. local homes = minetest.deserialize(player:get_attribute("multihome"))
  81. if homes[name] then
  82. homes[name] = nil
  83. player:set_attribute("multihome", minetest.serialize(homes))
  84. return true, "Removed home \""..name.."\""
  85. else
  86. return false, "Home \""..name.."\" does not exist!"
  87. end
  88. end
  89. -- [function] Get home position
  90. function multihome.get(player, name)
  91. if type(player) == "string" then
  92. player = minetest.get_player_by_name(player)
  93. end
  94. local homes = minetest.deserialize(player:get_attribute("multihome"))
  95. return homes[name]
  96. end
  97. -- [function] Get player's default home
  98. function multihome.get_default(player)
  99. if type(player) == "string" then
  100. player = minetest.get_player_by_name(player)
  101. end
  102. local default
  103. local count = 0
  104. local homes = minetest.deserialize(player:get_attribute("multihome"))
  105. for home, pos in pairs(homes) do
  106. count = count + 1
  107. default = home
  108. end
  109. if count == 1 then
  110. return default
  111. end
  112. end
  113. -- [function] List homes
  114. function multihome.list(player)
  115. if type(player) == "string" then
  116. player = minetest.get_player_by_name(player)
  117. end
  118. local homes = minetest.deserialize(player:get_attribute("multihome"))
  119. if homes then
  120. local list = "None"
  121. for name, h in pairs(homes) do
  122. if list == "None" then
  123. list = name.." "..minetest.pos_to_string(h)
  124. else
  125. list = list..", "..name.." "..minetest.pos_to_string(h)
  126. end
  127. end
  128. return true, "Your Homes ("..count_homes(homes).."/"..max.."): "..list
  129. end
  130. end
  131. -- [function] Go to home
  132. function multihome.go(player, name)
  133. if type(player) == "string" then
  134. player = minetest.get_player_by_name(player)
  135. end
  136. local pos = multihome.get(player, name)
  137. if pos then
  138. player:setpos(pos)
  139. return true, "Teleported to home \""..name.."\""
  140. else
  141. local homes = minetest.deserialize(player:get_attribute("multihome"))
  142. if not homes then
  143. return false, "Set a home using /multihome set <name> or /sethome <name>"
  144. else
  145. return false, "Invalid home \""..name.."\""
  146. end
  147. end
  148. end
  149. ---
  150. --- Registrations
  151. ---
  152. -- [event] On join player
  153. minetest.register_on_joinplayer(function(player)
  154. -- Check attributes
  155. check_attr(player)
  156. -- Check if homes need to be imported
  157. if import == "true" and (compat == "deprecate" or compat == "override")
  158. and player:get_attribute("multihome:imported") ~= "true" then
  159. local name = player:get_player_name()
  160. local pos = minetest.string_to_pos(player:get_attribute("sethome:home")) or oldhomes[name]
  161. if pos then
  162. -- Set multihome entry
  163. multihome.set(player, "default", pos)
  164. -- Set imported attribute
  165. player:set_attribute("multihome:imported", "true")
  166. end
  167. end
  168. end)
  169. -- Compatibility mode: none or deprecate
  170. if compat == "none" or compat == "deprecate" then
  171. -- [privilege] Multihome
  172. minetest.register_privilege("multihome", {
  173. description = "Can use /multihome",
  174. give_to_singleplayer = false,
  175. })
  176. -- [chatcommand] /multihome
  177. minetest.register_chatcommand("multihome", {
  178. description = "Manage your home points",
  179. params = "<action> <home name> | <set, del, go, list>, <home name>",
  180. privs = {creative=true},
  181. func = function(name, params)
  182. local params = params:split(" ")
  183. if #params == 2 and params[1] == "set" then
  184. return multihome.set(name, params[2])
  185. elseif #params == 2 and params[1] == "del" then
  186. return multihome.remove(name, params[2])
  187. elseif params[1] == "go" then
  188. local home = params[2]
  189. if not home then
  190. home = multihome.get_default(name)
  191. if not home then
  192. return false, "Invalid parameters (see /help multihome)"
  193. end
  194. end
  195. return multihome.go(name, home)
  196. elseif params[1] == "list" then
  197. return multihome.list(name)
  198. else
  199. return false, "Invalid parameters (see /help multihome)"
  200. end
  201. end,
  202. })
  203. end
  204. -- Compatibility mode: deprecate
  205. if compat == "deprecate" then
  206. local msg = "Deprecated, use /multihome instead"
  207. local function deprecate()
  208. return false, msg
  209. end
  210. -- [override] /home
  211. minetest.override_chatcommand("home", {description = msg, func = deprecate})
  212. -- [override] /sethome
  213. minetest.override_chatcommand("sethome", {description = msg, func = deprecate})
  214. end
  215. -- Compatibility mode: override
  216. if compat == "override" then
  217. -- [override] /home
  218. minetest.override_chatcommand("home", {
  219. description = "Teleport you to one of your home points (related: /sethome, /delhome, /listhomes)",
  220. params = "<home name>",
  221. func = function(name, param)
  222. if param and param ~= "" then
  223. return multihome.go(name, param)
  224. else
  225. local home = multihome.get_default(name)
  226. if home then
  227. return multihome.go(name, home)
  228. end
  229. return false, "Invalid parameters (see /help home or /listhomes)"
  230. end
  231. end,
  232. })
  233. -- [override] /sethome
  234. minetest.override_chatcommand("sethome", {
  235. description = "Set or update one of your home points (related: /home, /delhome, /listhomes)",
  236. params = "<home name>",
  237. func = function(name, param)
  238. if param and param ~= "" then
  239. return multihome.set(name, param)
  240. else
  241. return false, "Invalid parameters (see /help sethome)"
  242. end
  243. end,
  244. })
  245. -- [chatcommand] /delhome
  246. minetest.register_chatcommand("delhome", {
  247. description = "Delete one of your home points (related: /home, /sethome, /listhomes)",
  248. params = "<home name>",
  249. privs = {creative=true},
  250. func = function(name, param)
  251. if param and param ~= "" then
  252. return multihome.remove(name, param)
  253. else
  254. return false, "Invalid parameters (see /help delhome or /listhomes)"
  255. end
  256. end,
  257. })
  258. -- [chatcommand] /listhomes
  259. minetest.register_chatcommand("listhomes", {
  260. description = "List all of your home points (related: /home, /sethome, /delhome)",
  261. privs = {creative=true},
  262. func = function(name)
  263. return multihome.list(name)
  264. end,
  265. })
  266. end