init.lua 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. if not minetest.global_exists("afk") then afk = {} end
  2. afk.players = afk.players or {}
  3. afk.modpath = minetest.get_modpath("afk")
  4. afk.steptime = 5
  5. afk.timeout = 60 * 10 -- 10 minutes.
  6. afk.warntime = 60 * 9
  7. afk.disable_kick = minetest.is_singleplayer()
  8. -- Localize vector.distance() for performance.
  9. local vector_distance = vector.distance
  10. local vector_round = vector.round
  11. local math_floor = math.floor
  12. -- Public API function.
  13. -- This should be called from any mod that wishes to reset the kick timeout for a player.
  14. -- For example, a chat mod may call this when a player chats.
  15. afk.reset_timeout = function(name)
  16. -- localize
  17. local players = afk.players
  18. -- Ensure an entry exists for this name.
  19. local data = players[name]
  20. if not data then
  21. players[name] = {time=0, pos={x=0, y=0, z=0}}
  22. data = players[name]
  23. end
  24. data.time = 0
  25. data.afk = nil
  26. end
  27. function afk.on_joinplayer(player)
  28. local name = player:get_player_name()
  29. afk.players[name] = {time=0, pos=player:get_pos()}
  30. end
  31. function afk.on_leaveplayer(player, timedout)
  32. local pname = player:get_player_name()
  33. afk.players[pname] = nil
  34. end
  35. -- API function to query whether a player is currently AFK.
  36. -- Note that this only has meaning for registered players.
  37. -- Unregistered players are kicked, so you generally won't encounter those.
  38. function afk.is_afk(pname)
  39. local p = afk.players
  40. local o = p[pname]
  41. if o then
  42. if o.afk then
  43. return true
  44. end
  45. end
  46. return false
  47. end
  48. -- Returns the number of seconds since player's last action.
  49. -- Returns -1 if player data is not available (wrong player name?).
  50. function afk.seconds_since_action(pname)
  51. local p = afk.players
  52. local o = p[pname]
  53. if o then
  54. return o.time
  55. end
  56. return -1
  57. end
  58. afk.update = function()
  59. local allplayers = minetest.get_connected_players()
  60. for k, player in ipairs(allplayers) do
  61. local name = player:get_player_name()
  62. local target = afk.players[name]
  63. local pos = vector_round(player:get_pos())
  64. local dist = vector_distance(pos, target.pos)
  65. local nokick = false
  66. if afk.disable_kick or minetest.check_player_privs(name, {allow_afk=true}) then
  67. nokick = true
  68. end
  69. if dist > 0.5 then
  70. target.pos = pos
  71. target.time = 0
  72. target.afk = nil
  73. else
  74. -- Increase time since AFK started.
  75. local time = target.time
  76. time = time + afk.steptime
  77. target.time = time
  78. if not nokick and time >= afk.warntime then
  79. -- Only ignore players who are registered and NOT dead.
  80. if player:get_hp() > 0 and passport.player_registered(name) then
  81. -- If player is registered and NOT dead, don't send message.
  82. nokick = true
  83. else
  84. local remain = afk.timeout - time
  85. minetest.chat_send_player(name, "# Server: You will be kicked for inactivity in " .. math_floor(remain) .. " seconds.")
  86. easyvend.sound_error(name)
  87. end
  88. end
  89. end
  90. -- Kick players who have done nothing for too long.
  91. if target.time >= afk.timeout then
  92. if nokick then
  93. -- If player is registered and NOT dead, then just mark them as AFK.
  94. -- If player is registered but dead, they'll be kicked anyway.
  95. target.afk = true
  96. else
  97. minetest.kick_player(name, "Kicked for inactivity.")
  98. local dname = rename.gpn(name)
  99. minetest.chat_send_all("# Server: <" .. dname .. "> was kicked for being AFK too long.")
  100. end
  101. end
  102. end
  103. end
  104. local timer = 0
  105. local delay = afk.steptime
  106. function afk.globalstep(dtime)
  107. timer = timer + dtime
  108. if timer < delay then return end
  109. timer = 0
  110. afk.update()
  111. end
  112. function afk.on_craft(itemstack, player, old_craft_grid, craft_inv)
  113. if not player then return end
  114. if not player:is_player() then return end
  115. -- Ensure this player has an entry in the table.
  116. local name = player:get_player_name()
  117. if not afk.players[name] then
  118. afk.players[name] = {time=0, pos={x=0, y=0, z=0}}
  119. end
  120. afk.players[name].time = 0
  121. end
  122. local function show_stats(name)
  123. local players = minetest.get_connected_players()
  124. local pc = 0
  125. local ac = 0
  126. -- Count AFK players, don't include invisible ones.
  127. for k, v in ipairs(players) do
  128. local pname = v:get_player_name()
  129. local invis = gdac_invis.is_invisible(pname)
  130. if not invis then
  131. if afk.is_afk(pname) then
  132. ac = ac + 1
  133. pc = pc + 1
  134. else
  135. pc = pc + 1
  136. end
  137. end
  138. end
  139. local ps = "players"
  140. if pc == 1 then
  141. ps = "player"
  142. end
  143. minetest.chat_send_player(name,
  144. "# Server: Currently " .. pc .. " " .. ps .. " logged in. " .. ac ..
  145. " AFK.")
  146. end
  147. local function show_player(name, param)
  148. local pname = rename.grn(param)
  149. local invis = gdac_invis.is_invisible(pname)
  150. if not invis then
  151. if afk.is_afk(pname) then
  152. minetest.chat_send_player(name, "# Server: <" .. rename.gpn(pname) .. "> is AFK!")
  153. return
  154. else
  155. -- If player logged in and not admin-invisible.
  156. if minetest.get_player_by_name(pname) then
  157. local time = afk.seconds_since_action(pname)
  158. if time < 60 then
  159. minetest.chat_send_player(name, "# Server: <" .. rename.gpn(pname) .. "> is active.")
  160. elseif time < 60*2 then
  161. minetest.chat_send_player(name, "# Server: <" .. rename.gpn(pname) .. "> might be AFK.")
  162. else
  163. minetest.chat_send_player(name, "# Server: <" .. rename.gpn(pname) .. "> is probably AFK.")
  164. end
  165. return
  166. end
  167. end
  168. end
  169. minetest.chat_send_player(name, "# Server: Status of <" .. param .. "> is unknown.")
  170. end
  171. function afk.do_afk(name, param)
  172. if param ~= "" then
  173. -- Show info for player.
  174. show_player(name, param)
  175. else
  176. -- Show AFK stats.
  177. show_stats(name)
  178. end
  179. end
  180. if not afk.registered then
  181. -- Crafting resets the player's AFK timeout.
  182. minetest.register_on_craft(function(...)
  183. return afk.on_craft(...)
  184. end)
  185. minetest.register_globalstep(function(...)
  186. return afk.globalstep(...)
  187. end)
  188. minetest.register_privilege("allow_afk", {
  189. description = "Player will not be kicked for being AFK.",
  190. give_to_singleplayer = false,
  191. })
  192. minetest.register_on_joinplayer(function(...)
  193. return afk.on_joinplayer(...)
  194. end)
  195. minetest.register_on_leaveplayer(function(...)
  196. return afk.on_leaveplayer(...)
  197. end)
  198. minetest.register_chatcommand("afk", {
  199. params = "[name]",
  200. description = "Query the AFK status of players.",
  201. func = function(...)
  202. afk.do_afk(...)
  203. return true
  204. end,
  205. })
  206. local c = "afk:core"
  207. local f = afk.modpath .. "/init.lua"
  208. reload.register_file(c, f, false)
  209. afk.registered = true
  210. end