commit c38698eb25c243b1ec10f5b880b73a44a680e2ba Author: rnd1 Date: Mon Aug 29 09:20:26 2016 +0200 init diff --git a/README b/README new file mode 100644 index 0000000..bd55387 --- /dev/null +++ b/README @@ -0,0 +1,37 @@ +-- ANTI CHEAT by rnd +-- Copyright 2016 rnd +-- includes spectator mod by jp, modified/bugfixed by rnd + +------------------------------------------------------------------------- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. + +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. + +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +------------------------------------------------------------------------- + +features: + +0. what it does: + - succesffuly detect noclip/fly. Its just a matter of time when someone noclipping/flying is detected. + - players cant know when they are being watch since intervals are randomized + - lag resistant (see CHECK_AGAIN in settings) + +1. moderators can: + -see full reports with coordinates of location as cheats occur + -use /cstats to see latest detected cheater + -use /cdebug to see even suspected cheats to be verified later + -use /watch NAME to spectate suspect/detected cheater, /unwatch to return to normal + +managing moderators: + -edit names inside anticheatsettings.moderators in settings.lua + -Any player with kick privileges is moderator and is additionaly ignored by cheat checks. Use this for admin only - cheaters can then see who moderators are. + +2. this mod works well with basic_vote mod. After cheater has been positively detected anyone can use /vote to kick, remove interact or kill cheater. Vote in this case is cast anonymously, under the name #anticheat. \ No newline at end of file diff --git a/anticheat_routines.bin b/anticheat_routines.bin new file mode 100644 index 0000000..0f7e35a Binary files /dev/null and b/anticheat_routines.bin differ diff --git a/depends.txt b/depends.txt new file mode 100644 index 0000000..331d858 --- /dev/null +++ b/depends.txt @@ -0,0 +1 @@ +default \ No newline at end of file diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..a258832 --- /dev/null +++ b/init.lua @@ -0,0 +1,306 @@ +-- ANTI CHEAT by rnd +-- Copyright 2016 rnd +-- includes modified/bugfixed spectator mod by jp + +------------------------------------------------------------------------- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. + +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. + +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +------------------------------------------------------------------------- +local cheat = {}; +local version = "08.28.2016a"; + +anticheatsettings = {}; +dofile(minetest.get_modpath("anticheat").."/settings.lua") + +local CHEAT_TIMESTEP = anticheatsettings.CHEAT_TIMESTEP; +local CHECK_AGAIN = anticheatsettings.CHECK_AGAIN; +cheat.moderators = anticheatsettings.moderators; +anticheatsettings= {}; + +anticheatNAME = ""; -- global used for detected cheater name +cheat.players = {}; +cheat.message = ""; +cheat.debuglist = {}; -- [name]=true +cheat.scan_timer = 0 +cheat.nodelist = {}; +cheat.watcher = {}; -- list of watchers + +cheat.timestep = CHEAT_TIMESTEP; +-- list of forbidden nodes +cheat.nodelist = {["default:stone"] = false, ["default:cobble"]= false, ["default:dirt"] = false, ["default:sand"]=false,["default:tree"]= false}; + + +local punish_cheat = function(name) + + local player = minetest.get_player_by_name(name); + if not player then return end + local text=""; local logtext = ""; + + if cheat.players[name].cheattype == 1 then + text = "#anticheat: Player ".. name .. " was caught walking inside wall"; + logtext = "#anticheat: Player ".. name .. " was caught walking inside wall at " .. minetest.pos_to_string(cheat.players[name].cheatpos); + player:set_hp(0); + elseif cheat.players[name].cheattype == 2 then + text = "#anticheat: Player ".. name .. " was caught flying"; + logtext="#anticheat: Player ".. name .. " was caught flying at " .. minetest.pos_to_string(cheat.players[name].cheatpos); + player:set_hp(0); + end + + + if text~="" then + minetest.chat_send_all(text); + minetest.log("action", logtext); + cheat.message = logtext; + anticheatNAME = name; + cheat.players[name].count=0; -- reset counter + cheat.players[name].cheattype = 0; + + for name,_ in pairs(cheat.moderators) do -- display full message to moderators + minetest.chat_send_player(name,logtext); + end + + end +end + + +-- check position more closely + +-- uncomment when obfuscating: +--dofile(minetest.get_modpath("anticheat").."/anticheat_source.lua") + +local anticheat_routines=loadfile(minetest.get_modpath("anticheat").."/anticheat_routines.bin") +check_noclip, check_fly, check_player = anticheat_routines(minetest,cheat); + + +minetest.register_globalstep(function(dtime) + + cheat.scan_timer = cheat.scan_timer + dtime + + -- GENERAL SCAN OF ALL PLAYERS + if cheat.scan_timer>cheat.timestep then + cheat.timestep = CHEAT_TIMESTEP + (2*math.random()-1)*2; -- randomize step so its unpredictable + cheat.scan_timer=0; + --local t = minetest.get_gametime(); + local players = minetest.get_connected_players(); + + for _,player in pairs(players) do + check_player(player); + end + + for name,_ in pairs(cheat.debuglist) do -- show suspects in debug + for _,player in pairs(players) do + local pname = player:get_player_name(); + if cheat.players[pname].count>0 then + minetest.chat_send_player(name, "name " .. pname .. ", cheat pos " .. minetest.pos_to_string(cheat.players[pname].cheatpos) .. " last clear pos " .. minetest.pos_to_string(cheat.players[pname].clearpos) .. " cheat type " .. cheat.players[pname].cheattype .. " cheatcount " .. cheat.players[pname].count ); + end + end + end + + + end +end) + + +local watchers = {}; -- for each player a list of watchers +minetest.register_on_joinplayer(function(player) -- init stuff on player join + local name = player:get_player_name(); if name == nil then return end + local pos = player:getpos(); + cheat.players[name]={count=0,cheatpos = pos, clearpos = pos, lastpos = pos, cheattype = 0}; -- type 0: none, 1 noclip, 2 fly + watchers[name] = {}; -- for spectator mod +end) + + +minetest.register_chatcommand("cstats", { -- see current stats + privs = { + interact = true + }, + func = function(name, param) + local privs = minetest.get_player_privs(name).privs; + if not cheat.moderators[name] and not privs then return end + + minetest.chat_send_player(name,cheat.message); -- displays last cheat message + + local players = minetest.get_connected_players(); + + for _,player in pairs(players) do + local pname = player:get_player_name(); + if cheat.players[pname].count>0 then + minetest.chat_send_player(name, "name " .. pname .. ", cheat pos " .. minetest.pos_to_string(cheat.players[pname].cheatpos) .. " last clear pos " .. minetest.pos_to_string(cheat.players[pname].lastpos) .. " cheat type " .. cheat.players[pname].cheattype .. " cheatcount " .. cheat.players[pname].count ); + end + end + + + end +}) + +minetest.register_chatcommand("cdebug", { -- toggle cdebug= display of stats on/off for this player + privs = { + interact = true + }, + func = function(name, param) + local privs = minetest.get_player_privs(name).privs; + if not cheat.moderators[name] and not privs then return end + + if cheat.debuglist[name] == nil then cheat.debuglist[name] = true else cheat.debuglist[name] = nil end; + + minetest.chat_send_player(name,"#anticheat: " .. version); + if cheat.debuglist[name]==true then + minetest.chat_send_player(name,"#anticheat: display of debug messages is ON"); + else + minetest.chat_send_player(name,"#anticheat: display of debug messages is OFF"); + end + end +}) + + +------------------------------------------------------ +-- [Mod] Spectator Mode [git] [spectator_mode] +-- https://github.com/minetest-mods/spectator_mode +-- by jp ยป Tue Dec 08, 2015 15:34 +-- modified/bugfixes by rnd +------------------------------------------------------ + + +local original_pos = {} + +local function unwatching(name) + local watcher = minetest.get_player_by_name(name) + local privs = minetest.get_player_privs(name) + + if watcher and default.player_attached[name] == true then + watcher:set_detach() + + + local pos = original_pos[watcher] + if pos then + -- setpos seems to be very unreliable + -- this workaround helps though + minetest.after(0.1, function() + watcher:setpos(pos) + end) + original_pos[watcher] = nil + end + cheat.watcher[name]=nil; + + minetest.after(5, + function() + default.player_attached[name] = false + watcher:set_eye_offset({x=0, y=0, z=0}, {x=0, y=0, z=0}) + watcher:set_nametag_attributes({color = {a=255, r=255, g=255, b=255}}) + + watcher:hud_set_flags({ + healthbar = true, + minimap = true, + breathbar = true, + hotbar = true, + wielditem = true, + crosshair = true + }) + + watcher:set_properties({ + visual_size = {x=1, y=1}, + makes_footstep_sound = true, + collisionbox = {-0.3, -1, -0.3, 0.3, 1, 0.3} + }) + end + ) + -- if not privs.interact and cheat.moderators[name] == true then + -- privs.interact = true + -- minetest.set_player_privs(name, privs) + -- end + + + + end +end + +minetest.register_chatcommand("watch", { + params = "", + description = "", + privs = {interact=true}, + func = function(name, param) + + local privs = minetest.get_player_privs(name) + if not cheat.moderators[name] and not privs.kick then return end + local watcher = minetest.get_player_by_name(name) + local target = minetest.get_player_by_name(param) + if not target then return end + if not cheat.players[param] then return end + + if anticheatNAME==param or cheat.players[param].count>0 or privs.kick then + else + minetest.chat_send_player(name, "ordinary watchers can only watch cheat suspects of detected cheaters"); + return + end + + if target and watcher ~= target then + if default.player_attached[name] == true then + unwatching(param) + else + original_pos[watcher] = watcher:getpos() + end + + default.player_attached[name] = true + watcher:set_attach(target, "", {x=0, y=-5, z=-20}, {x=0, y=0, z=0}) + watcher:set_eye_offset({x=0, y=-5, z=-20}, {x=0, y=0, z=0}) + watcher:set_nametag_attributes({color = {a=0}}) + + watcher:hud_set_flags({ + healthbar = false, + minimap = false, + breathbar = false, + hotbar = false, + wielditem = false, + crosshair = false + }) + + watcher:set_properties({ + visual_size = {x=0, y=0}, + makes_footstep_sound = false, + collisionbox = {0} + }) + + -- privs.interact = nil + -- minetest.set_player_privs(name, privs) + + cheat.watcher[name]=true; + watchers[param][name] = true; -- register name as watcher of param + + + return true, "Watching '"..param.."' at "..minetest.pos_to_string(vector.round(target:getpos())) + end + + return false, "Invalid parameter ('"..param.."')." + end +}) + +minetest.register_chatcommand("unwatch", { + description = "", + privs = {interact=true}, + func = function(name, param) + unwatching(name) + -- unregister name as watcher + for pname,val in pairs (watchers) do + if val[name] then watchers[pname][name] = nil; end + end + + end +}) + +minetest.register_on_leaveplayer(function(player) + local name = player:get_player_name() + for pname,_ in pairs (watchers[name]) do + unwatching(pname); -- all watchers do /unwatch + end + watchers[name] = nil; +end) \ No newline at end of file diff --git a/settings.lua b/settings.lua new file mode 100644 index 0000000..77cf38b --- /dev/null +++ b/settings.lua @@ -0,0 +1,34 @@ +-- ANTI CHEAT by rnd +-- Copyright 2016 rnd +-- includes modified/bugfixed spectator mod by jp + +------------------------------------------------------------------------- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. + +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. + +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see . +------------------------------------------------------------------------- + + +-- SETTINGS -------------------------------------------------------------- +anticheatsettings.CHEAT_TIMESTEP = 15; -- check all players +anticheatsettings.CHECK_AGAIN = 15; -- after player found in bad position check again after this to make sure its not lag, this should be larger than expected lag in seconds + +-- moderators list, those players can use cheat debug and will see full cheat message +anticheatsettings.moderators = { +["rnd"]=true, +["maikerumine"]=true, +["sorcerykid"]=true, +["Zorg"]=true, +["AspireMint"]=true, +["843jdc"]=true +} +-- END OF SETTINGS -------------------------------------------------------- \ No newline at end of file