init.lua 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. exile = exile or {}
  2. exile.modpath = minetest.get_modpath("exile")
  3. exile.eviction_notices = exile.eviction_notices or {}
  4. -- Localize for performance.
  5. local vector_distance = vector.distance
  6. local vector_normalize = vector.normalize
  7. local vector_subtract = vector.subtract
  8. local vector_multiply = vector.multiply
  9. local vector_round = vector.round
  10. local vector_add = vector.add
  11. local math_random = math.random
  12. -- Helper to query whether there is a nearby non-cheating player (also not self)
  13. -- within a certain range.
  14. local function nearby_noncheater(pname, pos, range)
  15. local players = minetest.get_connected_players()
  16. for i=1, #players, 1 do
  17. local pref = players[i]
  18. local pn = pref:get_player_name()
  19. if pn ~= pname then
  20. if vector_distance(pref:get_pos(), pos) < range then
  21. if not sheriff.is_suspected_cheater(pn) then
  22. if not gdac.player_is_admin(pn) then
  23. return true
  24. end
  25. end
  26. end
  27. end
  28. end
  29. end
  30. local function move_player_to_exile(pname, target)
  31. local minp = vector.add(target, {x=-8, y=-100, z=-8})
  32. local maxp = vector.add(target, {x=8, y=100, z=8})
  33. local function callback(blockpos, action, calls_remaining, param)
  34. --minetest.chat_send_player("MustTest", 'callback')
  35. -- We don't do anything until the last callback.
  36. if calls_remaining ~= 0 then
  37. return
  38. end
  39. -- Check if there was an error on the LAST call.
  40. -- Note: this will usually fail if the area to emerge intersects the map edge.
  41. -- But usually we don't try to do that, here.
  42. if action == core.EMERGE_CANCELLED or action == core.EMERGE_ERRORED then
  43. --minetest.chat_send_player("MustTest", "error")
  44. return
  45. end
  46. local pos = table.copy(param.target)
  47. local orig_y = pos.y
  48. local pname = param.pname
  49. local get_node = minetest.get_node
  50. -- Locate ground level, or some area where the player can fit in air.
  51. -- Start from sky and work downwards, to reduce chance of tp'ing to cave.
  52. for y = 90, -90, -1 do
  53. pos.y = orig_y + y - 1
  54. local n1 = get_node(pos)
  55. pos.y = orig_y + y
  56. local n2 = get_node(pos)
  57. pos.y = orig_y + y + 1
  58. local n3 = get_node(pos)
  59. --minetest.chat_send_player("MustTest", minetest.pos_to_string(pos))
  60. -- All 3 nodes must be loaded.
  61. if n1.name ~= "ignore" and n2.name ~= "ignore" and n3.name ~= "ignore" then
  62. local d1 = minetest.registered_nodes[n1.name]
  63. local d2 = minetest.registered_nodes[n2.name]
  64. local d3 = minetest.registered_nodes[n3.name]
  65. if d1 and d2 and d3 then
  66. if d1.walkable and not d2.walkable and not d3.walkable then
  67. --minetest.chat_send_player("MustTest", 'found ground')
  68. pos.y = orig_y + y
  69. local post_cb = function(param)
  70. -- Report publicly, while avoiding chat spam.
  71. local pname = param.pname
  72. if not exile.eviction_notices[pname] then
  73. minetest.chat_send_all("# Server: Law enforcement evicted <" .. rename.gpn(pname) .. "> from town.")
  74. exile.eviction_notices[pname] = true
  75. minetest.after(60, function()
  76. exile.eviction_notices[pname] = nil
  77. end)
  78. end
  79. end
  80. -- Wrapped in minetest.after() to avoid *potential* callstack issues.
  81. minetest.after(0, function()
  82. preload_tp.execute({
  83. player_name = pname,
  84. target_position = pos,
  85. emerge_radius = 8,
  86. post_teleport_callback = post_cb,
  87. callback_param = param,
  88. force_teleport = true,
  89. send_blocks = false,
  90. particle_effects = true,
  91. })
  92. end)
  93. return
  94. end
  95. end
  96. end
  97. end
  98. end
  99. minetest.emerge_area(minp, maxp, callback,
  100. {target=table.copy(target), pname=pname})
  101. end
  102. -- A player is violating their exile if ...
  103. function exile.player_in_violation(pname)
  104. -- They are a registered cheater ...
  105. if sheriff.is_cheater(pname) then
  106. local pref = minetest.get_player_by_name(pname)
  107. -- And they are logged in ...
  108. if pref then
  109. local pos = pref:get_pos()
  110. -- And there's a normal player near them ...
  111. if nearby_noncheater(pname, pos, 100) then
  112. -- And they are in a city area ...
  113. if city_block:in_city_suburbs(pos) then
  114. return true
  115. end
  116. end
  117. end
  118. end
  119. end
  120. function exile.send_to_exile(pname)
  121. local pref = minetest.get_player_by_name(pname)
  122. if pref then
  123. local pos = pref:get_pos()
  124. local rn1 = rc.current_realm_at_pos(pos)
  125. local cb = city_block:nearest_blocks_to_position(pos, 5, 100)
  126. -- No nearby city-blocks? Cannot move player (also avoid divide-by-zero).
  127. if #cb == 0 then
  128. return
  129. end
  130. -- Calculate the average postion of nearby city-blocks.
  131. local x, y, z, n = 0, 0, 0, #cb
  132. for i=1, n, 1 do
  133. local b = cb[i]
  134. local p = b.pos
  135. x = x + p.x
  136. y = y + p.y
  137. z = z + p.z
  138. end
  139. x = x / n
  140. y = y / n
  141. z = z / n
  142. -- Calculate a new position away from the city-blocks.
  143. pos.y = y -- Only move player in the X,Z dimensions.
  144. local center = {x=x, y=y, z=z}
  145. local dir = vector_normalize(vector_subtract(pos, center))
  146. local gpos = vector_round(vector_add(vector_multiply(dir, 100), center))
  147. gpos.y = y -- Only move player in the X,Z dimensions.
  148. local rn2 = rc.current_realm_at_pos(gpos)
  149. -- Only if we wouldn't cause player to change realms, or enter the void.
  150. if rn2 ~= "" and rn1 == rn2 then
  151. move_player_to_exile(pname, gpos)
  152. pref:set_hp(pref:get_hp() - 1)
  153. end
  154. end
  155. end
  156. -- Function shall return 'true' if player was confirmed to be in violation of their exile.
  157. function exile.check_player(pname)
  158. if exile.player_in_violation(pname) then
  159. exile.send_to_exile(pname)
  160. return true
  161. end
  162. end
  163. function exile.repeating_check(pname)
  164. --minetest.chat_send_player("MustTest", "repeating check")
  165. -- Only check confirmed cheaters, as no one else can be exiled.
  166. if sheriff.is_cheater(pname) then
  167. exile.check_player(pname)
  168. -- Schedule another check shortly.
  169. minetest.after(1, exile.repeating_check, pname)
  170. end
  171. end
  172. -- To be called if a cheater is confirmed/registered during game-play time.
  173. function exile.notify_new_exile(pname)
  174. -- Just call the function as if they've just joined.
  175. exile.on_joinplayer(minetest.get_player_by_name(pname))
  176. end
  177. function exile.on_joinplayer(pref)
  178. if pref and pref:is_player() then
  179. local pname = pref:get_player_name()
  180. minetest.after(math_random(1, 10), exile.repeating_check, pname)
  181. end
  182. end
  183. if not exile.registered then
  184. local c = "exile:core"
  185. local f = exile.modpath .. "/init.lua"
  186. reload.register_file(c, f, false)
  187. minetest.register_on_joinplayer(function(...)
  188. exile.on_joinplayer(...) end)
  189. exile.registered = true
  190. end