anticheat_routines.lua 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. -- LGPL
  2. --[[
  3. -- DO NOT INCLUDE THIS WITH SOURCES
  4. -- THIS will produce anticheat_routines.bin.
  5. -- instructions:
  6. -- 1. uncomment in init.lua around line 80:
  7. -- dofile(minetest.get_modpath("anticheat").."/anticheat_source.lua")
  8. -- just run server and .bin is generated. Then comment it again.
  9. -- how to use in code(already done):
  10. -- local anticheat_routines=loadfile(minetest.get_modpath("anticheat").."/anticheat_routines.bin")
  11. -- check_noclip, check_fly, check_player = anticheat_routines(minetest,cheat);
  12. -- Localize for performance.
  13. local math_floor = math.floor
  14. local anticheat_routines = function(minetest,cheat, CHECK_AGAIN, punish_cheat)
  15. -- DETAILED NOCLIP CHECK
  16. local check_noclip = function(pos)
  17. local nodename = minetest.get_node(pos).name;
  18. local clear=true;
  19. if nodename ~= "air" then -- check if forbidden material!
  20. clear = cheat.nodelist[nodename]; -- test clip violation
  21. if clear == nil then clear = true end
  22. end
  23. if not clear then -- more detailed check
  24. local anodes = minetest.find_nodes_in_area({x=pos.x-1, y=pos.y-1, z=pos.z-1}, {x=pos.x+1, y=pos.y+1, z=pos.z+1}, {"air"});
  25. if #anodes == 0 then return false end
  26. clear=true;
  27. end
  28. return clear;
  29. end
  30. -- DETAILED FLY CHECK
  31. local check_fly = function(pos) -- return true if player not flying
  32. local fly = (minetest.get_node(pos).name=="air" and minetest.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name=="air"); -- prerequisite for flying is this to be "air", but not sufficient condition
  33. if not fly then return true end;
  34. local anodes = minetest.find_nodes_in_area({x=pos.x-1, y=pos.y-1, z=pos.z-1}, {x=pos.x+1, y=pos.y, z=pos.z+1}, {"air"});
  35. if #anodes == 18 then -- player standing on air?
  36. return false
  37. else
  38. return true
  39. end
  40. end
  41. local round = function (x)
  42. if x > 0 then
  43. return math_floor(x+0.5)
  44. else
  45. return -math_floor(-x+0.5)
  46. end
  47. end
  48. --main check routine
  49. local check_player = function(player)
  50. local name = player:get_player_name();
  51. local privs = minetest.get_player_privs(name).kick;if privs then return end -- dont check moderators
  52. if cheat.watcher[name] then return end -- skip checking watchers while they watch
  53. local pos = player:get_pos(); -- feet position
  54. pos.x = round(pos.x*10)/10;pos.z = round(pos.z*10)/10; -- less useless clutter
  55. pos.y = round(pos.y*10)/10; -- minetest buggy collision - need to do this or it returns wrong materials for feet position: aka magic number 0.498?????228
  56. if pos.y<0 then pos.y=pos.y+1 end -- weird, without this it fails to check feet block where y<0, it checks one below feet
  57. local nodename = minetest.get_node(pos).name;
  58. local clear=true;
  59. if nodename ~= "air" then -- check if forbidden material!
  60. clear = cheat.nodelist[nodename]; -- test clip violation
  61. if clear == nil then clear = true end
  62. end
  63. local fly = (nodename=="air" and minetest.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name=="air"); -- prerequisite for flying, but not sufficient condition
  64. if cheat.players[name].count == 0 then -- player hasnt "cheated" yet, remember last clear position
  65. cheat.players[name].clearpos = cheat.players[name].lastpos
  66. end
  67. -- manage noclip cheats
  68. if not clear then -- player caught inside walls
  69. local moved = (cheat.players[name].lastpos.x~=pos.x) or (cheat.players[name].lastpos.y~=pos.y) or (cheat.players[name].lastpos.z~=pos.z);
  70. if moved then -- if player stands still whole time do nothing
  71. if cheat.players[name].count == 0 then cheat.players[name].cheatpos = pos end -- remember first position where player found inside wall
  72. if cheat.players[name].count == 0 then
  73. minetest.after(CHECK_AGAIN,
  74. function()
  75. cheat.players[name].count = 0;
  76. if not check_noclip(pos) then
  77. punish_cheat(name)-- we got a cheater!
  78. else
  79. cheat.players[name].count = 0; -- reset
  80. cheat.players[name].cheattype = 0;
  81. end
  82. end
  83. )
  84. end
  85. if cheat.players[name].count == 0 then -- mark as suspect
  86. cheat.players[name].count = 1;
  87. cheat.players[name].cheattype = 1;
  88. end
  89. end
  90. end
  91. -- manage flyers
  92. if fly then
  93. local fpos;
  94. fly,fpos = minetest.line_of_sight(pos, {x = pos.x, y = pos.y - 4, z = pos.z}, 1); --checking node maximal jump height below feet
  95. if fly then -- so we are in air, are we flying?
  96. if player:get_player_control().sneak then -- player sneaks, maybe on border?
  97. --search 18 nodes to find non air
  98. local anodes = minetest.find_nodes_in_area({x=pos.x-1, y=pos.y-1, z=pos.z-1}, {x=pos.x+1, y=pos.y, z=pos.z+1}, {"air"});
  99. if #anodes < 18 then fly = false end
  100. end -- if at this point fly = true means player is not standing on border
  101. if pos.y>=cheat.players[name].lastpos.y and fly then -- we actually didnt go down from last time and not on border
  102. -- was lastpos in air too?
  103. local lastpos = cheat.players[name].lastpos;
  104. local anodes = minetest.find_nodes_in_area({x=lastpos.x-1, y=lastpos.y-1, z=lastpos.z-1}, {x=lastpos.x+1, y=lastpos.y, z=lastpos.z+1}, {"air"});
  105. if #anodes == 18 then fly = true else fly = false end
  106. if fly then -- so now in air above previous position, which was in air too?
  107. if cheat.players[name].count == 0 then cheat.players[name].cheatpos = pos end -- remember first position where player found "cheating"
  108. if cheat.players[name].count == 0 then
  109. minetest.after(CHECK_AGAIN,
  110. function()
  111. cheat.players[name].count = 0;
  112. if not check_fly(pos) then
  113. punish_cheat(name)-- we got a cheater!
  114. else
  115. cheat.players[name].count = 0;
  116. cheat.players[name].cheattype = 0;
  117. end
  118. end
  119. )
  120. end
  121. if cheat.players[name].count == 0 then -- mark as suspect
  122. cheat.players[name].count = 1;
  123. cheat.players[name].cheattype = 2;
  124. end
  125. end
  126. end
  127. end
  128. end
  129. cheat.players[name].lastpos = pos
  130. end
  131. return check_noclip, check_fly, check_player;
  132. end
  133. -- this produces compiled version of source
  134. local out = io.open(minetest.get_modpath("anticheat").."\\anticheat_routines.bin", "wb");
  135. local s = string.dump(anticheat_routines);
  136. out:write(s);
  137. out:close()
  138. --]]