about summary refs log tree commit diff
path: root/src/game
diff options
context:
space:
mode:
Diffstat (limited to 'src/game')
-rw-r--r--src/game/server/entities/character.cpp18
-rw-r--r--src/game/server/entities/character.h1
-rw-r--r--src/game/server/gamecontext.cpp105
-rw-r--r--src/game/server/gamecontext.h7
-rw-r--r--src/game/server/player.cpp13
-rw-r--r--src/game/server/player.h5
-rw-r--r--src/game/variables.h1
7 files changed, 114 insertions, 36 deletions
diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp
index 547eabce..27be82f8 100644
--- a/src/game/server/entities/character.cpp
+++ b/src/game/server/entities/character.cpp
@@ -158,6 +158,24 @@ bool CCharacter::AimedAtCharRecently(float aimX, float aimY, const CCharacter *c
 	return false;
 }
 
+float CCharacter::HowCloseToXRecently(vec2 x, const LastPosition *&pos, int firstTick)
+{
+	float lowest = 1000.0;
+	// start with the most recent position
+	firstTick = max(firstTick, Server()->Tick() - m_LastPositionsSize);
+	for(int lastTick = Server()->Tick(); lastTick > firstTick; --lastTick)
+	{
+		int i = lastTick % m_LastPositionsSize;
+		float d = distance(x, vec2(m_LastPositions[i].x, m_LastPositions[i].y));
+		if(d < lowest)
+		{
+			pos = &m_LastPositions[i];
+			lowest = d;
+		}
+	}
+	return lowest;
+}
+
 void CCharacter::SetWeapon(int W)
 {
 	if(W == m_ActiveWeapon)
diff --git a/src/game/server/entities/character.h b/src/game/server/entities/character.h
index 4d615324..7b949e29 100644
--- a/src/game/server/entities/character.h
+++ b/src/game/server/entities/character.h
@@ -75,6 +75,7 @@ public:
 	};
 	bool HasBeenThereRecently(float x, float y, const LastPosition *&pos, int firstTick, int lastTick) const;
 	bool AimedAtCharRecently(float aimX, float aimY, const CCharacter *c, const LastPosition *&pos, const LastPosition *&posVictim, int firstTick);
+	float HowCloseToXRecently(vec2 x, const LastPosition *&pos, int firstTick);
 
 private:
 	// player controlling this character
diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp
index 0ee29d40..5ad268cb 100644
--- a/src/game/server/gamecontext.cpp
+++ b/src/game/server/gamecontext.cpp
@@ -530,48 +530,85 @@ void CGameContext::OnTick()
 	// bot detection
 	// it is based on the behaviour of some bots to shoot at a player's _exact_ position
 	// check each player
-	for(int i = 0; i < MAX_CLIENTS; ++i)
+	if(g_Config.m_SvBotDetection)
 	{
-		CCharacter *ci, *cj;
-		CPlayer *p;
-		// abort if player is not ingame or already detected as a bot
-		if(!(p = m_apPlayers[i]) || p->m_IsBot || !(ci = GetPlayerChar(i)))
-			continue;
-		
-		// check against every other player
-		for(int j = 0; j < MAX_CLIENTS; ++j)
+		for(int i = 0; i < MAX_CLIENTS; ++i)
 		{
-			const CCharacter::LastPosition *pos, *posVictim;
-
-			// other placer needs to be ingame and in sight. don't detect the same position again
-			if(j != i && (cj = GetPlayerChar(j)) && cj->NetworkClipped(i) == 0 && ci->AimedAtCharRecently(p->m_LatestActivity.m_TargetX, p->m_LatestActivity.m_TargetY, cj, pos, posVictim, p->m_AimBotLastDetection) && !(pos->x == p->m_AimBotLastDetectionPos.x && pos->y == p->m_AimBotLastDetectionPos.y && posVictim->x == p->m_AimBotLastDetectionPosVictim.x && posVictim->y == p->m_AimBotLastDetectionPosVictim.y))
+			CCharacter *ci, *cj;
+			CPlayer *p;
+			// abort if player is not ingame or already detected as a bot
+			if(!(p = m_apPlayers[i]) || p->m_IsAimBot || !(ci = GetPlayerChar(i)))
+				continue;
+			
+			// check against every other player
+			for(int j = 0; j < MAX_CLIENTS; ++j)
 			{
-				p->m_AimBotLastDetection = Server()->Tick();
-				p->m_AimBotLastDetectionPos = *pos;
-				p->m_AimBotLastDetectionPosVictim = *posVictim;
-				++p->m_AimBotIndex;
-				// write to console
-				char aBuf[128];
-				str_format(aBuf, sizeof(aBuf), "player=%d victim=%d a_index=%d", i, j, p->m_AimBotIndex);
-				Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "botdetect", aBuf);
-				// check if threshold is exceeded
-				if(p->m_AimBotIndex >= 10)
+
+				if(j != i && (cj = GetPlayerChar(j)))
 				{
-					p->m_IsBot = true;
-					// alert the chat
-					char aBuf[128];
-					str_format(aBuf, sizeof(aBuf), "'%s' might be botting", Server()->ClientName(i));
-					SendChat(-1, CGameContext::CHAT_ALL, aBuf);
+					const CCharacter::LastPosition *pos, *posVictim;
+					
+					// fast aiming bot detection
+					if(g_Config.m_SvBotDetection&BOT_DETECTION_FAST_AIM && p->m_AimBotTargetSpeed > 300.0 && !(p->m_AimBotLastDetectionPos.x == ci->m_Pos.x && p->m_AimBotLastDetectionPos.y == ci->m_Pos.y))
+					{
+						vec2 t(ci->m_Pos.x + p->m_LatestActivity.m_TargetX, ci->m_Pos.y + p->m_LatestActivity.m_TargetY);
+						float hc = cj->HowCloseToXRecently(t, posVictim, p->m_AimBotLastDetection);
+						float precision = p->m_AimBotTargetSpeed * (256.0 - hc * hc);
+						// don't detect same position twice
+						if(precision > 0 && !(posVictim->x == p->m_AimBotLastDetectionPosVictim.x && posVictim->y == p->m_AimBotLastDetectionPosVictim.y))
+						{
+							if(precision >= 50000.0)
+							{
+								p->m_AimBotLastDetection = Server()->Tick();
+								p->m_AimBotLastDetectionPos.x = ci->m_Pos.x;
+								p->m_AimBotLastDetectionPos.y = ci->m_Pos.y;
+								p->m_AimBotLastDetectionPosVictim = *posVictim;
+								p->m_AimBotIndex += 2 * min(3, (int)(precision / 50000));
+								// write to console
+								char aBuf[128];
+								str_format(aBuf, sizeof(aBuf), "player=%d victim=%d a_index=%d precision=%d speed=%d distance=%d", i, j, p->m_AimBotIndex, (int)precision, (int)p->m_AimBotTargetSpeed, (int)hc);
+								Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "botdetect", aBuf);
+								// don't check other players
+								break;
+							}
+						}
+					}
+					
+					// follow bot detection
+					// other placer needs to be ingame and in sight. don't detect the same position again
+					else if(g_Config.m_SvBotDetection&BOT_DETECTION_FOLLOW && cj->NetworkClipped(i) == 0 && ci->AimedAtCharRecently(p->m_LatestActivity.m_TargetX, p->m_LatestActivity.m_TargetY, cj, pos, posVictim, p->m_AimBotLastDetection) && !(pos->x == p->m_AimBotLastDetectionPos.x && pos->y == p->m_AimBotLastDetectionPos.y && posVictim->x == p->m_AimBotLastDetectionPosVictim.x && posVictim->y == p->m_AimBotLastDetectionPosVictim.y))
+					{
+						p->m_AimBotLastDetection = Server()->Tick();
+						p->m_AimBotLastDetectionPos = *pos;
+						p->m_AimBotLastDetectionPosVictim = *posVictim;
+						++p->m_AimBotIndex;
+						// write to console
+						char aBuf[128];
+						str_format(aBuf, sizeof(aBuf), "player=%d victim=%d a_index=%d", i, j, p->m_AimBotIndex);
+						Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "botdetect", aBuf);
+						// don't check other players
+						break;
+					}
+					
 				}
-				// don't check other players
-				break;
 			}
 			
+			// check if threshold is exceeded
+			if(p->m_AimBotIndex >= 10)
+			{
+				p->m_IsAimBot = Server()->Tick();
+				// alert the admins
+				char aBuf[128];
+				str_format(aBuf, sizeof(aBuf), "'%s' might be botting", Server()->ClientName(i));
+				for(int j = 0; j < MAX_CLIENTS; ++j)
+					if(Server()->IsAuthed(j))
+						SendChatTarget(j, aBuf);
+			}
+			
+			// reduce once every 2 seconds (tolerance)
+			if(((Server()->Tick() % (Server()->TickSpeed() * 2)) == 0) && p->m_AimBotIndex)
+				--p->m_AimBotIndex;
 		}
-		
-		// reduce once every 2 seconds (tolerance)
-		if(((Server()->Tick() % (Server()->TickSpeed() * 2)) == 0) && p->m_AimBotIndex)
-			--p->m_AimBotIndex;
 	}
 
 #ifdef CONF_DEBUG
diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h
index 27551ac8..eb73e019 100644
--- a/src/game/server/gamecontext.h
+++ b/src/game/server/gamecontext.h
@@ -195,6 +195,13 @@ public:
 	virtual const char *GameType();
 	virtual const char *Version();
 	virtual const char *NetVersion();
+	
+	// bot detection
+	enum
+	{
+		BOT_DETECTION_FAST_AIM=1,
+		BOT_DETECTION_FOLLOW=2,
+	};
 };
 
 inline int CmaskAll() { return -1; }
diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp
index 8fb62fa8..17fcd566 100644
--- a/src/game/server/player.cpp
+++ b/src/game/server/player.cpp
@@ -38,9 +38,14 @@ CPlayer::CPlayer(CGameContext *pGameServer, int ClientID, int Team)
 	m_zCatchNumKillsInARow = 0;
 	
 	// bot detection
-	m_IsBot = false;
+	m_IsAimBot = 0;
 	m_AimBotIndex = 0;
 	m_AimBotLastDetection = 0;
+	m_AimBotTargetSpeed = .0;
+	m_CurrentTarget.x = 0;
+	m_CurrentTarget.y = 0;
+	m_LastTarget.x = 0;
+	m_LastTarget.y = 0;
 }
 
 CPlayer::~CPlayer()
@@ -127,6 +132,12 @@ void CPlayer::Tick()
 		++m_LastActionTick;
 		++m_TeamChangeTick;
  	}
+	
+	// bot detection
+	m_LastTarget = m_CurrentTarget;
+	m_CurrentTarget.x = m_LatestActivity.m_TargetX;
+	m_CurrentTarget.y = m_LatestActivity.m_TargetY;
+	m_AimBotTargetSpeed = abs(distance(m_CurrentTarget, m_LastTarget));
 }
 
 void CPlayer::PostTick()
diff --git a/src/game/server/player.h b/src/game/server/player.h
index b3bf8c91..53466d0c 100644
--- a/src/game/server/player.h
+++ b/src/game/server/player.h
@@ -133,9 +133,12 @@ public:
 	int LastZCatchVictim() { return HasZCatchVictims() ? m_ZCatchVictims->ClientID : -1; }
 	
 	// bot detection
-	int m_IsBot;
+	int m_IsAimBot;
 	int m_AimBotIndex;
 	int m_AimBotLastDetection;
+	float m_AimBotTargetSpeed;
+	vec2 m_CurrentTarget;
+	vec2 m_LastTarget;
 	CCharacter::LastPosition m_AimBotLastDetectionPos;
 	CCharacter::LastPosition m_AimBotLastDetectionPosVictim;
 	
diff --git a/src/game/variables.h b/src/game/variables.h
index a588e6e6..8f247f25 100644
--- a/src/game/variables.h
+++ b/src/game/variables.h
@@ -118,4 +118,5 @@ MACRO_CONFIG_INT(SvKillPenalty, sv_kill_penalty, 5, 0, 50, CFGFLAG_SERVER, "The
 
 // zCatch/TeeVi
 MACRO_CONFIG_INT(SvLastStandingPlayers, sv_last_standing_players, 5, 2, 16, CFGFLAG_SERVER, "How many players are needed to have last standing rounds")
+MACRO_CONFIG_INT(SvBotDetection, sv_bot_detection, 0, 0, 3, CFGFLAG_SERVER, "Bot detection (0=off, 1=fast aim, 2=follow, 3=all)")
 #endif