2023-07-17 01:02:10 -07:00
-------------------------------------------------------------------------
-- ANTI PRIVS detects if a custom client without interacting "zips" around the map
-- Copyright (c) 2021 mt-mods/BuckarooBanzay MIT
-- Copyright 2020-2023 improvements and few fixes, mckaygerhard CC-BY-SA-NC 4.0
-------------------------------------------------------------------------
-- per-player data
local player_data = { }
2023-08-07 00:19:12 -07:00
local is_player = function ( player )
-- a table being a player is also supported because it quacks sufficiently
-- like a player if it has the is_player function
local t = type ( player )
return ( t == " userdata " or t == " table " ) and type ( player.is_player ) == " function "
end
2023-07-17 01:02:10 -07:00
-- obtain player data cheats, improved respect beowulf with missing checks and methods
local function get_player_data ( name )
local player = minetest.get_player_by_name ( name )
if not player_data [ name ] then
player_data [ name ] = {
fliyers = 0 , -- number of checks for fly ( added only with governing)
strikes = 0 , -- number of "strikes" (odd movements)
checked = 0 , -- number of checks
pre_pos = player : get_pos ( ) -- position track (missing in beowulf as bug)
}
end
return player_data [ name ]
-- WARNING pos its always current so checked must be after an interval using callbacks
end
-- clear player data for cheats
local function track_player_clear ( player )
2023-08-07 00:19:12 -07:00
if is_player ( player ) then
2023-07-17 01:02:10 -07:00
if player : get_player_name ( ) then
player_data [ player : get_player_name ( ) ] = nil
end
end
end
-- store player data cheats strikes and checks
local function track_player ( player )
2023-08-07 01:31:29 -07:00
if not is_player ( player ) then return end
2023-07-17 01:02:10 -07:00
local name = player : get_player_name ( )
local data = get_player_data ( name )
local pos = player : get_pos ( )
if data.pre_pos then
-- compare positions
local d = vector.distance ( pos , data.pre_pos )
if d > 200 then
data.strikes = data.strikes + 1
end
data.checked = data.checked + 1
end
if data.checked >= 10 then
-- check strike-count after 10 movement checks
if data.strikes > 8 then
-- suspicious movement, log it
-- TODO: if this doesn't yield any false-positives, add a kick/ban option
local msg = " suspicious movement detected for player: ' " .. name .. " ' "
minetest.log ( " action " , " [governing][beowulf] " .. msg )
end
-- reset counters
data.checked = 0
data.strikes = 0
end
-- store current position
data.pre_pos = pos
end
-------------------------------------------------------------------------
-- ANTI CHEAT for MT4 and old MT5 engines by rnd
-- Copyright 2016 rnd LGPL v3
-- Copyright 2020-2023 improvements and few fixes, mckaygerhard CC-BY-SA-NC 4.0
-------------------------------------------------------------------------
local cheat = { } ;
local version = " 09/08/2017 " ;
anticheatsettings = { } ;
anticheatsettings.moderators = { }
anticheatsettings.CHEAT_TIMESTEP = tonumber ( minetest.settings : get ( " governing.timestep " ) ) or 15 ; -- check timestep all players
anticheatsettings.CHECK_AGAIN = tonumber ( minetest.settings : get ( " governing.timeagain " ) ) or 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
anticheatsettings.STRING_MODERA = minetest.settings : get ( " governing.moderators " ) or " admin,singleplayer " ;
-- moderators list, those players can use cheat debug and will see full cheat message
for str in string.gmatch ( anticheatsettings.STRING_MODERA , " ([^,]+) " ) do table.insert ( anticheatsettings.moderators , str ) end
local CHEAT_TIMESTEP = anticheatsettings.CHEAT_TIMESTEP ;
local CHECK_AGAIN = anticheatsettings.CHECK_AGAIN ;
cheat.moderators = anticheatsettings.moderators ;
bonemod = minetest.get_modpath ( " boneworld " )
anticheatdb = { } ; -- data about detected cheaters
cheat.suspect = " " ;
cheat.players = { } ; -- temporary cheat detection db
cheat.message = " " ;
cheat.debuglist = { } ; -- [name]=true -- who gets to see debug msgs
cheat.scan_timer = 0 ; -- global scan of players
cheat.stat_timer = 0 ; -- used to collect stats
cheat.nodelist = { } ;
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 ) ;
local ip = tostring ( minetest.get_player_ip ( name ) ) ;
if not player then return end
local text = " " ; local logtext = " " ;
if cheat.players [ name ] . cheattype == 1 then
text = " #anticheat: " .. name .. " was caught walking inside wall " ;
logtext = os.date ( " %H:%M.%S " ) .. " #anticheat: " .. name .. " was caught walking inside wall at " .. minetest.pos_to_string ( cheat.players [ name ] . cheatpos ) ;
2023-08-07 01:31:29 -07:00
--player:set_hp(0);
2023-07-17 01:02:10 -07:00
elseif cheat.players [ name ] . cheattype == 2 then
local gravity = player : get_physics_override ( ) . gravity ; if gravity < 1 then return end
logtext = os.date ( " %H:%M.%S " ) .. " #anticheat: " .. name .. " was caught flying at " .. minetest.pos_to_string ( cheat.players [ name ] . cheatpos ) ;
if cheat.players [ name ] . cheatpos.y > 5 then -- only above height 5 it directly damages flyer
text = " #anticheat: " .. name .. " was caught flying " ;
--player:set_hp(0);
end
end
if text ~= " " then
minetest.chat_send_all ( text ) ;
end
if logtext ~= " " then
minetest.log ( " action " , logtext ) ;
cheat.message = logtext ;
anticheatdb [ ip ] = { name = name , msg = logtext } ;
cheat.players [ name ] . count = 0 ; -- reset counter
cheat.players [ name ] . cheattype = 0 ;
2023-08-07 01:31:29 -07:00
for namem , _ in pairs ( cheat.moderators ) do -- display full message to moderators
minetest.chat_send_player ( namem , logtext ) ;
2023-07-17 01:02:10 -07:00
end
end
end
-- CHECKS
-- DETAILED NOCLIP CHECK
local check_noclip = function ( pos )
local nodename = minetest.get_node ( pos ) . name ;
local clear = true ;
if nodename ~= " air " then -- check if forbidden material!
clear = cheat.nodelist [ nodename ] ; -- test clip violation
if clear == nil then clear = true end
end
if not clear then -- more detailed check
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 " } ) ;
if # anodes == 0 then return false end
clear = true ;
end
return clear ;
end
-- DETAILED FLY CHECK
local check_fly = function ( pos ) -- return true if player not flying
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
if not fly then return true end ;
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 " } ) ;
if # anodes == 18 then -- player standing on air?
return false
else
return true
end
end
local round = function ( x )
if x > 0 then
return math.floor ( x + 0.5 )
else
return - math.floor ( - x + 0.5 )
end
end
--main check routine
local check_player = function ( player )
local name = player : get_player_name ( ) ;
local privs = minetest.get_player_privs ( name ) . kick ; if privs then return end -- dont check moderators
local pos = player : getpos ( ) ; -- feet position
pos.x = round ( pos.x * 10 ) / 10 ; pos.z = round ( pos.z * 10 ) / 10 ; -- less useless clutter
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
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
local nodename = minetest.get_node ( pos ) . name ;
local clear = true ;
if nodename ~= " air " then -- check if forbidden material!
clear = cheat.nodelist [ nodename ] ; -- test clip violation
if clear == nil then clear = true end
end
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
if cheat.players [ name ] . count == 0 then -- player hasnt "cheated" yet, remember last clear position
cheat.players [ name ] . clearpos = cheat.players [ name ] . lastpos
end
-- manage noclip cheats
if not clear then -- player caught inside walls
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 ) ;
if moved then -- if player stands still whole time do nothing
if cheat.players [ name ] . count == 0 then cheat.players [ name ] . cheatpos = pos end -- remember first position where player found inside wall
if cheat.players [ name ] . count == 0 then
minetest.after ( CHECK_AGAIN + math.random ( 5 ) ,
function ( )
cheat.players [ name ] . count = 0 ;
if not check_noclip ( pos ) then
punish_cheat ( name ) -- we got a cheater!
else
cheat.players [ name ] . count = 0 ; -- reset
cheat.players [ name ] . cheattype = 0 ;
end
end
)
end
if cheat.players [ name ] . count == 0 then -- mark as suspect
cheat.players [ name ] . count = 1 ;
cheat.players [ name ] . cheattype = 1 ;
end
end
end
-- manage flyers
if fly then
local fpos ;
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
if fly then -- so we are in air, are we flying?
if player : get_player_control ( ) . sneak then -- player sneaks, maybe on border?
--search 18 nodes to find non air
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 " } ) ;
if # anodes < 18 then fly = false end
end -- if at this point fly = true means player is not standing on border
if pos.y >= cheat.players [ name ] . lastpos.y and fly then -- we actually didnt go down from last time and not on border
-- was lastpos in air too?
local lastpos = cheat.players [ name ] . lastpos ;
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 " } ) ;
if # anodes == 18 then fly = true else fly = false end
if fly then -- so now in air above previous position, which was in air too?
if cheat.players [ name ] . count == 0 then cheat.players [ name ] . cheatpos = pos end -- remember first position where player found "cheating"
if cheat.players [ name ] . count == 0 then
minetest.after ( CHECK_AGAIN ,
function ( )
cheat.players [ name ] . count = 0 ;
if not check_fly ( pos ) then
punish_cheat ( name ) -- we got a cheater!
else
cheat.players [ name ] . count = 0 ;
cheat.players [ name ] . cheattype = 0 ;
end
end
)
end
if cheat.players [ name ] . count == 0 then -- mark as suspect
cheat.players [ name ] . count = 1 ;
cheat.players [ name ] . cheattype = 2 ;
end
end
end
end
end
cheat.players [ name ] . lastpos = pos
end
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.stat_timer = cheat.stat_timer + cheat.timestep ;
-- dig xp stats every 2 minutes
if bonemod and cheat.stat_timer > 120 then
cheat.stat_timer = 0 ;
local players = minetest.get_connected_players ( ) ;
for _ , player in pairs ( players ) do
local pname = player : get_player_name ( ) ;
if cheat.players [ pname ] . stats.state == 1 then -- only if dig xp loaded to prevent anomalous stats
if boneworld.digxp [ pname ] then
local deltadig = cheat.players [ pname ] . stats.digxp ;
cheat.players [ pname ] . stats.digxp = boneworld.digxp [ pname ] ;
deltadig = boneworld.digxp [ pname ] - deltadig ;
cheat.players [ pname ] . stats.deltadig = deltadig ;
if deltadig > cheat.players [ pname ] . stats.maxdeltadig then
cheat.players [ pname ] . stats.maxdeltadig = deltadig ;
end
if deltadig > 2 then -- unnaturally high deltadig
local ip = tostring ( minetest.get_player_ip ( pname ) ) ;
local logtext = os.date ( " %H:%M.%S " ) .. " #anticheat: " .. pname .. " (ip " .. ip .. " ) is mining resources too fast, deltadig " .. deltadig ;
anticheatdb [ ip ] = { name = pname , msg = logtext } ;
minetest.log ( " action " , logtext ) ;
end
end
end
end
end
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
2023-08-07 01:31:29 -07:00
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 ) ;
2023-07-17 01:02:10 -07:00
end
end
end
end
end )
-- long range dig check
local check_can_dig = function ( pos , digger )
2024-04-07 20:07:15 -07:00
local cpos = minetest.pos_to_string ( pos ) or " missing pos seems nul returned "
local logtext = os.date ( " %H:%M.%S " ) .. " #anticheat: long range dig made by some entity, could be a player or huge tnt explotion at " .. cpos ;
if not digger then
minetest.log ( " warning " , " [governing] " .. logtext )
return
end
2023-07-17 01:02:10 -07:00
local p = digger : getpos ( ) ;
if p.y < 0 then p.y = p.y + 2 else p.y = p.y + 1 end -- head position
local dist = math.max ( math.abs ( p.x - pos.x ) , math.abs ( p.y - pos.y ) , math.abs ( p.z - pos.z ) ) ;
if dist > 6 then -- here 5
dist = math.floor ( dist * 100 ) / 100 ;
local pname = digger : get_player_name ( ) ;
2024-04-07 20:07:15 -07:00
logtext = os.date ( " %H:%M.%S " ) .. " #anticheat: long range dig " .. pname .. " , distance " .. dist .. " , pos " .. cpos ;
2023-07-17 01:02:10 -07:00
for name , _ in pairs ( cheat.debuglist ) do -- show to all watchers
minetest.chat_send_player ( name , logtext )
2023-08-07 00:19:12 -07:00
minetest.log ( " warning " , " [governing] " .. logtext )
2023-07-17 01:02:10 -07:00
end
local ip = tostring ( minetest.get_player_ip ( pname ) ) ;
anticheatdb [ ip ] = { name = pname , msg = logtext } ;
return false
end
return true
end
local set_check_can_dig = function ( name )
local tabl = minetest.registered_nodes [ name ] ;
if not tabl then return end
tabl.can_dig = check_can_dig ;
minetest.override_item ( name , { can_dig = check_can_dig } )
--minetest.register_node(":"..name, tabl);
end
2023-08-07 01:31:29 -07:00
local function is_player ( player )
if player then
if type ( player ) == " userdata " or type ( player ) == " table " then return true end
end
return false
end
minetest.register_on_joinplayer ( function ( player )
2023-07-17 01:02:10 -07:00
2023-08-07 01:31:29 -07:00
-- init stuff on player join
if not is_player ( player ) then return end
local name = player : get_player_name ( ) ;
if type ( name ) ~= " string " then return end
2023-07-17 01:02:10 -07:00
local pos = player : getpos ( ) ;
2023-08-07 01:31:29 -07:00
-- no matter if are incomplete info, save most possible of and start recolection of stats
2023-07-17 01:02:10 -07:00
if cheat.players [ name ] == nil then
cheat.players [ name ] = { count = 0 , cheatpos = pos , clearpos = pos , lastpos = pos , cheattype = 0 } ; -- type 0: none, 1 noclip, 2 fly
end
2023-08-07 01:31:29 -07:00
-- try to fill some stats or retrieve previously
2023-07-17 01:02:10 -07:00
if cheat.players [ name ] and cheat.players [ name ] . stats == nil then
cheat.players [ name ] . stats = { maxdeltadig = 0 , deltadig = 0 , digxp = 0 , state = 0 } ; -- various statistics about player: max dig xp increase in 2 minutes, current dig xp increase
2023-08-07 00:19:12 -07:00
if bonemod then
2023-08-07 01:31:29 -07:00
minetest.after ( 4 , -- load digxp after boneworld loads it
function ( )
if boneworld.xp then
cheat.players [ name ] . stats.digxp = boneworld.digxp [ name ] or 0 ;
cheat.players [ name ] . stats.state = 1 ;
end
2023-07-17 01:02:10 -07:00
end
2023-08-07 01:31:29 -07:00
) --state 0 = stats not loaded yet
2023-07-17 01:02:10 -07:00
end
end
2023-08-07 01:31:29 -07:00
-- check anticheat db for cheaters clients
-- ===================================
2023-07-17 01:02:10 -07:00
local ip = tostring ( minetest.get_player_ip ( name ) ) ;
2023-08-07 01:31:29 -07:00
local msgiplevelone = " " ;
local msgipleveltwo = " " ;
--check ip first try of info manually, later from player info
2023-07-17 01:02:10 -07:00
if anticheatdb [ ip ] then
2023-08-07 01:31:29 -07:00
msgiplevelone = " #anticheat: welcome back detected cheater, ip = " .. ip .. " , name " .. anticheatdb [ ip ] . name .. " , new name = " .. name ;
2023-07-17 01:02:10 -07:00
end ;
2023-08-07 01:31:29 -07:00
--check names from stats
2023-07-17 01:02:10 -07:00
for ip , v in pairs ( anticheatdb ) do
if v.name == name then
2023-08-07 01:31:29 -07:00
msgiplevelone = " #anticheat: welcome back detected cheater, ip = " .. ip .. " , name = newname = " .. v.name ;
2023-07-17 01:02:10 -07:00
break ;
end
end
2023-08-07 01:31:29 -07:00
-- send detection msg before try to check info (cos info may be incomplete detection)
if msgiplevelone ~= " " then
minetest.after ( 1 , function ( )
for namemd , _ in pairs ( cheat.moderators ) do
minetest.chat_send_player ( namemd , msgiplevelone ) ;
end
end )
end
2023-07-17 01:02:10 -07:00
end )
2023-08-07 01:31:29 -07:00
minetest.register_chatcommand ( " cchk " , {
2023-07-17 01:02:10 -07:00
privs = {
2024-03-31 21:14:38 -07:00
interact = true ,
server = true
2023-07-17 01:02:10 -07:00
} ,
description = " cchk NAME, checks if player is cheating in this moment " ,
func = function ( name , param )
local privs = minetest.get_player_privs ( name ) . privs ;
if not cheat.moderators [ name ] and not privs then return end
local player = minetest.get_player_by_name ( param ) ;
if not player then return end
check_player ( player ) ;
2023-08-07 01:31:29 -07:00
local ip = tostring ( minetest.get_player_ip ( param ) ) ;
local info = minetest.get_player_information ( param )
local msgm = " #anticheat detected a cheater with " .. ip .. " named " .. param
if info.version_string then
2024-03-31 21:14:38 -07:00
local dfv = gapi.isdf ( info.version_string )
2023-08-07 01:31:29 -07:00
if dfv then
msgm = msgm .. " using " .. info.version_string
minetest.chat_send_player ( param , " ..... cheat " ) ;
if minetest.settings : get_bool ( " beowulf.dfdetect.enable_kick " , false ) then
minetest.kick_player ( param , " Are you a cheater stupid user? change your cracked client for play " )
end
minetest.chat_send_player ( name , msgm ) ; -- advertise moderators
for namemd , _ in pairs ( cheat.moderators ) do
minetest.chat_send_player ( namemd , msgm ) ; -- advertise moderators
end
minetest.log ( msgm )
2024-03-31 21:14:38 -07:00
else
msgm = " Still just suspicius for " .. param .. " at " .. ip .. " using " .. info.version_string
minetest.chat_send_player ( name , msgm ) ; -- advertise command executor
2023-08-07 01:31:29 -07:00
end
else
for namemd , _ in pairs ( cheat.moderators ) do
minetest.chat_send_player ( namemd , msgm ) ; -- advertise moderators
end
minetest.log ( msgm )
end
2023-07-17 01:02:10 -07:00
2023-08-07 01:31:29 -07:00
local players = minetest.get_connected_players ( ) ;
2023-07-17 01:02:10 -07:00
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
} )
minetest.register_chatcommand ( " crep " , { -- see cheat report
privs = {
interact = true
} ,
description = " crep 0/1, 0 = default cheat report, 1 = connected player stats " ,
func = function ( name , param )
local privs = minetest.get_player_privs ( name ) . privs ;
if not cheat.moderators [ name ] and not privs then return end
if param == " " then
minetest.chat_send_player ( name , " use: crep type, types: 0(default) cheat report, 1 connected player stats ( " .. version .. " ) " ) ;
end
param = tonumber ( param ) or 0 ;
if param == 0 then -- show cheat report
local text = " " ;
for ip , v in pairs ( anticheatdb ) do
if v and v.name and v.msg then
text = text .. " ip " .. ip .. " " .. v.msg .. " \n " ;
end
end
if text ~= " " then
local form = " size [6,7] textarea[0,0;6.5,8.5;creport;CHEAT REPORT; " .. text .. " ] "
minetest.show_formspec ( name , " anticheatreport " , form )
end
elseif param == 1 then -- show cheat stats
local text = " " ;
local players = minetest.get_connected_players ( ) ;
for _ , player in pairs ( players ) do
local pname = player : get_player_name ( ) ;
local ip = tostring ( minetest.get_player_ip ( pname ) ) ;
text = text .. " \n name " .. pname .. " , digxp " .. math.floor ( 1000 * cheat.players [ pname ] . stats.digxp ) / 1000 ..
" , deltadigxp(2min) " .. math.floor ( 1000 * cheat.players [ pname ] . stats.deltadig ) / 1000 .. " , maxdeltadigxp " .. math.floor ( 1000 * cheat.players [ pname ] . stats.maxdeltadig ) / 1000 ; -- .. " ".. string.gsub(dump(cheat.players[pname].stats), "\n", " ");
if anticheatdb [ ip ] then text = text .. " (DETECTED) ip " .. ip .. " , name " .. anticheatdb [ ip ] . name end
end
if text ~= " " then
local form = " size [10,8] textarea[0,0;10.5,9.;creport;CHEAT STATISTICS; " .. text .. " ] "
minetest.show_formspec ( name , " anticheatreport " , form )
end
end
-- suspects info
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
} )
-------------------------------------------------------------------------
-- GOVERNONR custom action for features mod code improvements
-- minetest routines to implement the mod features
-- Copyright 2020-2023 mckaygerhard CC-BY-SA-NC 4.0
-------------------------------------------------------------------------
-- cleanup after the player leaves
minetest.register_on_leaveplayer ( function ( player )
2023-08-07 01:31:29 -07:00
if is_player ( player ) then
2023-07-17 01:02:10 -07:00
track_player_clear ( player )
end
end )
-- list of nodes to enable damage if noclip or to check if player its diggin too fast
local node_list_check = { }
-- TODO move this to a comma separted list in config, and adde the check below in node check for fly
if minetest.get_modpath ( " default " ) then
table.insert ( node_list_check , " default:stone " )
table.insert ( node_list_check , " default:stone_with_iron " )
table.insert ( node_list_check , " default:stone_with_copper " )
table.insert ( node_list_check , " default:stone_with_coal " )
table.insert ( node_list_check , " default:stone_with_gold " )
table.insert ( node_list_check , " default:stone_with_mese " )
table.insert ( node_list_check , " default:stone_with_diamond " )
end
local function interval_fn ( )
if not governing.modbeowulf then
for _ , player in ipairs ( minetest.get_connected_players ( ) ) do
track_player ( player )
end
end
if not governing.modanticheat then
for _ , nodename in ipairs ( node_list_check ) do
set_check_can_dig ( nodename ) ;
end
end
2023-08-07 01:31:29 -07:00
2023-07-17 01:02:10 -07:00
minetest.after ( 2 , interval_fn )
end
-- TODO: beowulf was pretty unneficient on large player servers.. cos only its made each 1 second and again each x inside function
if governing.is_50 then
minetest.register_on_mods_loaded ( interval_fn )
else
minetest.after ( 0.1 , interval_fn )
end
-- damaged if a player its making noclip, no matter if admin is doing
for _ , nodename in ipairs ( node_list_check ) do
minetest.override_item ( nodename , {
damage_per_second = 1
} )
end