aboutsummaryrefslogtreecommitdiff
path: root/NorthstarDLL
diff options
context:
space:
mode:
Diffstat (limited to 'NorthstarDLL')
-rw-r--r--NorthstarDLL/NorthstarDLL.vcxproj2
-rw-r--r--NorthstarDLL/NorthstarDLL.vcxproj.filters6
-rw-r--r--NorthstarDLL/bansystem.cpp2
-rw-r--r--NorthstarDLL/bots.cpp184
-rw-r--r--NorthstarDLL/bots.h26
-rw-r--r--NorthstarDLL/r2engine.h1
-rw-r--r--NorthstarDLL/r2server.h2
-rw-r--r--NorthstarDLL/serverauthentication.cpp13
8 files changed, 231 insertions, 5 deletions
diff --git a/NorthstarDLL/NorthstarDLL.vcxproj b/NorthstarDLL/NorthstarDLL.vcxproj
index 5e329f1f..7961ddf4 100644
--- a/NorthstarDLL/NorthstarDLL.vcxproj
+++ b/NorthstarDLL/NorthstarDLL.vcxproj
@@ -120,6 +120,7 @@
<ClInclude Include="bansystem.h" />
<ClInclude Include="bitbuf.h" />
<ClInclude Include="bits.h" />
+ <ClInclude Include="bots.h" />
<ClInclude Include="crashhandler.h" />
<ClInclude Include="keyvalues.h" />
<ClInclude Include="loghooks.h" />
@@ -569,6 +570,7 @@
<ClCompile Include="audio.cpp" />
<ClCompile Include="bansystem.cpp" />
<ClCompile Include="bits.cpp" />
+ <ClCompile Include="bots.cpp" />
<ClCompile Include="buildainfile.cpp" />
<ClCompile Include="chatcommand.cpp" />
<ClCompile Include="clientauthhooks.cpp" />
diff --git a/NorthstarDLL/NorthstarDLL.vcxproj.filters b/NorthstarDLL/NorthstarDLL.vcxproj.filters
index 14f8c265..4f5ccbc5 100644
--- a/NorthstarDLL/NorthstarDLL.vcxproj.filters
+++ b/NorthstarDLL/NorthstarDLL.vcxproj.filters
@@ -1503,6 +1503,9 @@
<ClInclude Include="squirrelclasstypes.h">
<Filter>Header Files\Squirrel</Filter>
</ClInclude>
+ <ClInclude Include="bots.h">
+ <Filter>Header Files\Server</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
@@ -1733,6 +1736,9 @@
<ClCompile Include="rejectconnectionfixes.cpp">
<Filter>Source Files\Client</Filter>
</ClCompile>
+ <ClCompile Include="bots.cpp">
+ <Filter>Source Files\Server</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<MASM Include="audio_asm.asm">
diff --git a/NorthstarDLL/bansystem.cpp b/NorthstarDLL/bansystem.cpp
index 25c0e6bf..5ba24cf1 100644
--- a/NorthstarDLL/bansystem.cpp
+++ b/NorthstarDLL/bansystem.cpp
@@ -188,7 +188,7 @@ void ConCommand_ban(const CCommand& args)
{
R2::CBaseClient* player = &R2::g_pClientArray[i];
- if (!strcmp(player->m_Name, args.Arg(1)) || !strcmp(player->m_UID, args.Arg(1)))
+ if (player->m_Signon >= R2::eSignonState::CONNECTED && !strcmp(player->m_Name, args.Arg(1)) || !strcmp(player->m_UID, args.Arg(1)))
{
g_pBanSystem->BanUID(strtoull(player->m_UID, nullptr, 10));
R2::CBaseClient__Disconnect(player, 1, "Banned from server");
diff --git a/NorthstarDLL/bots.cpp b/NorthstarDLL/bots.cpp
new file mode 100644
index 00000000..f8a37d26
--- /dev/null
+++ b/NorthstarDLL/bots.cpp
@@ -0,0 +1,184 @@
+#include "pch.h"
+#include "bots.h"
+
+#include "concommand.h"
+#include "r2engine.h"
+#include "r2server.h"
+#include "playlist.h"
+#include "maxplayers.h"
+#include "serverauthentication.h"
+
+AUTOHOOK_INIT()
+
+ServerBotManager* g_pBots;
+
+void* pServer;
+R2::CBaseClient* (*__fastcall CBaseServer__CreateFakeClient)(
+ void* self, const char* pName, const char* pUnk, const char* pDesiredPlaylist, int nDesiredTeam);
+
+void* pServerGameClients;
+void (*__fastcall ServerGameClients__ClientFullyConnect)(void* self, uint16_t edict, bool bRestore);
+
+void (*__fastcall CBasePlayer__RunNullCommand)(R2::CBasePlayer* self);
+
+const std::vector<const char*> BOT_NAMES = {
+ // normal titanfall names
+ "Jack",
+ "Cooper",
+ "BT-7274",
+ "FS-1041",
+ "Blisk",
+ "Marv",
+ "Marvin",
+ "Barker",
+ "Gates",
+ "Marder",
+ "Sarah",
+ "Briggs",
+ "Viper",
+ "Ash",
+ "Richter",
+ "Slone",
+ "Tai",
+ "Lastimosa",
+ "Davis",
+ "Droz",
+ "Bear",
+ "MacAllan",
+ "Bish",
+ "Spyglass",
+
+ // tf1 training/campaign npc names
+ "Rakowski",
+ "Alavi",
+ "Riggs",
+ "Bracken",
+ "Hainey",
+ "Vaughan",
+ "Dunnam",
+ "Riggins",
+ "Messerly",
+ "Heppe",
+ "Keating",
+ "Blanton",
+ "Emslie",
+ "Flagg",
+ "Hofner",
+
+ // deepcut references lol
+ "Iron",
+ "Starseeker",
+ "Hollow",
+ "Preacher",
+
+ // contributors/cool people
+ "BobTheBot",
+ "GeckoEidechsBot",
+ "TaskiBot",
+ "RoyalBot",
+ "EmmaB(ot)", // ok this one is shit to be honest but it's a hard name to put bot into
+ "bot0358",
+ // how the fuck do i turn amos into a bot name oh my god
+ "Botmos",
+ "Botscuit",
+ "Botty_RaVen",
+};
+
+// this is the root func from what usercmd running is called from
+AUTOHOOK(SomeRunUsercmdFunc, server.dll + 0x483A50,
+void, __fastcall, (char a1))
+{
+ g_pBots->SimulatePlayers();
+ SomeRunUsercmdFunc(a1);
+}
+
+void ConCommand_bot_add(const CCommand& arg)
+{
+ if (arg.ArgC() == 1)
+ g_pBots->AddBot();
+ else if (arg.ArgC() == 2)
+ g_pBots->AddBot(arg.Arg(1));
+ else if (arg.ArgC() == 2)
+ g_pBots->AddBot(arg.Arg(1), std::stoi(arg.Arg(2)));
+}
+
+ServerBotManager::ServerBotManager()
+{
+ //m_Cvar_bot_teams = new ConVar("")
+}
+
+
+// add a new bot player
+void ServerBotManager::AddBot(const char* pName, int iTeam)
+{
+ if (!*pName)
+ pName = BOT_NAMES[rand() % BOT_NAMES.size()];
+
+ if (iTeam == -1)
+ {
+ // pick team that needs players
+ // oops i cant be bothered just do imc for now
+ iTeam = 2;
+ }
+
+ const R2::CBaseClient* pPlayer = CBaseServer__CreateFakeClient(pServer, pName, "", "", iTeam);
+ ServerGameClients__ClientFullyConnect(pServerGameClients, pPlayer->edict, false); // this is normally done on eSignonState::FULL, but bots don't properly call it
+
+ spdlog::info("Created bot {}", pPlayer->m_Name);
+}
+
+
+// add bots at the start of a game
+void ServerBotManager::StartMatch()
+{
+ int nNumBots = Cvar_bot_quota->GetInt() - g_pServerAuthentication->m_PlayerAuthenticationData.size();
+ for (int i = nNumBots; i > 0; i--)
+ AddBot();
+}
+
+// setup bot after it connects
+void ServerBotManager::SetupPlayer(R2::CBaseClient* pPlayer)
+{
+ strncpy_s(pPlayer->m_ClanTag, Cvar_bot_clantag->GetString(), 15);
+}
+
+// every server frame, run bot usercmds
+void ServerBotManager::SimulatePlayers()
+{
+ for (int i = 0; i < R2::GetMaxPlayers(); i++)
+ {
+ R2::CBaseClient* pClient = &R2::g_pClientArray[i];
+ if (pClient->m_Signon == R2::eSignonState::FULL && pClient->m_bFakePlayer)
+ {
+ // todo: if we ever want actual moving bots, this would be where we'd nav, build usercmd, etc
+ CBasePlayer__RunNullCommand(R2::UTIL_PlayerByIndex(i + 1));
+ }
+ }
+}
+
+ON_DLL_LOAD_RELIESON("engine.dll", Bots, (ConVar, ConCommand), (CModule module))
+{
+ g_pBots = new ServerBotManager;
+ g_pBots->Cvar_bot_quota = new ConVar("bot_quota", "0", FCVAR_GAMEDLL, "Minimum number of players when filling game with bots");
+ g_pBots->Cvar_bot_clantag = new ConVar("bot_clantag", "BOT", FCVAR_GAMEDLL, "Clantag to give bots");
+
+ pServer = module.Offset(0x12A53D40).As<void*>();
+ pServerGameClients = module.Offset(0x13F0AAA8).As<void*>();
+
+ CBaseServer__CreateFakeClient =
+ module.Offset(0x114C60).As<R2::CBaseClient* (*__fastcall)(void*, const char*, const char*, const char*, int)>();
+
+ RegisterConCommand(
+ "bot_add",
+ ConCommand_bot_add,
+ "Add a bot, by default name and team are automatically chosen, they can be specified (bot_add name team)",
+ FCVAR_GAMEDLL);
+}
+
+ON_DLL_LOAD("server.dll", ServerBots, (CModule module))
+{
+ AUTOHOOK_DISPATCH_MODULE(server.dll)
+
+ ServerGameClients__ClientFullyConnect = module.Offset(0x153B70).As<void (*__fastcall)(void*, uint16_t, bool)>();
+ CBasePlayer__RunNullCommand = module.Offset(0x5A9FD0).As<void (*__fastcall)(R2::CBasePlayer*)>();
+}
diff --git a/NorthstarDLL/bots.h b/NorthstarDLL/bots.h
new file mode 100644
index 00000000..5973983a
--- /dev/null
+++ b/NorthstarDLL/bots.h
@@ -0,0 +1,26 @@
+#pragma once
+#include "convar.h"
+#include "r2engine.h"
+
+class ServerBotManager
+{
+
+ public:
+ ConVar* Cvar_bot_quota;
+ ConVar* Cvar_bot_teams;
+
+ ConVar* Cvar_bot_clantag;
+
+ public:
+ ServerBotManager();
+
+ void AddBot(const char* pName = "", int iTeam = -1);
+
+ // events we react to
+ void StartMatch();
+ void SetupPlayer(R2::CBaseClient* pPlayer);
+ void SimulatePlayers();
+
+};
+
+extern ServerBotManager* g_pBots;
diff --git a/NorthstarDLL/r2engine.h b/NorthstarDLL/r2engine.h
index 1b1fc081..c816ec86 100644
--- a/NorthstarDLL/r2engine.h
+++ b/NorthstarDLL/r2engine.h
@@ -175,6 +175,7 @@ namespace R2
OFFSET_STRUCT(CBaseClient)
{
STRUCT_SIZE(0x2D728)
+ FIELD(0x14, uint16_t edict)
FIELD(0x16, char m_Name[64])
FIELD(0x258, KeyValues* m_ConVars)
FIELD(0x2A0, eSignonState m_Signon)
diff --git a/NorthstarDLL/r2server.h b/NorthstarDLL/r2server.h
index aadfdefe..5d69f53e 100644
--- a/NorthstarDLL/r2server.h
+++ b/NorthstarDLL/r2server.h
@@ -15,6 +15,8 @@ namespace R2
STRUCT_SIZE(0x1D02);
FIELD(0x58, uint32_t m_nPlayerIndex)
+ FIELD(0x5E4, int m_nTeam)
+
FIELD(0x1C90, bool m_hasBadReputation)
FIELD(0x1C91, char m_communityName[64])
FIELD(0x1CD1, char m_communityClanTag[16])
diff --git a/NorthstarDLL/serverauthentication.cpp b/NorthstarDLL/serverauthentication.cpp
index 73b714ab..53892623 100644
--- a/NorthstarDLL/serverauthentication.cpp
+++ b/NorthstarDLL/serverauthentication.cpp
@@ -15,6 +15,7 @@
#include "r2engine.h"
#include "r2client.h"
#include "r2server.h"
+#include "bots.h"
#include "httplib.h"
@@ -188,10 +189,6 @@ bool ServerAuthenticationManager::CheckAuthentication(R2::CBaseClient* pPlayer,
void ServerAuthenticationManager::AuthenticatePlayer(R2::CBaseClient* pPlayer, uint64_t iUid, char* pAuthToken)
{
- // for bot players, generate a new uid
- if (pPlayer->m_bFakePlayer)
- iUid = 0; // is this a good way of doing things :clueless:
-
std::string sUid = std::to_string(iUid);
// copy uuid
@@ -316,7 +313,12 @@ bool,, (R2::CBaseClient* self, char* pName, void* pNetChannel, char bFakePlayer,
pAuthenticationFailure = "Authentication Failed.";
}
else // need to copy name for bots still
+ {
+ // generate uid and token for bot
+ iNextPlayerUid = 0;
+ pNextPlayerToken = (char*)"";
strncpy_s(pVerifiedName, pName, 63);
+ }
if (pAuthenticationFailure)
{
@@ -333,6 +335,9 @@ bool,, (R2::CBaseClient* self, char* pName, void* pNetChannel, char bFakePlayer,
// we already know this player's authentication data is legit, actually write it to them now
g_pServerAuthentication->AuthenticatePlayer(self, iNextPlayerUid, pNextPlayerToken);
+ if (bFakePlayer)
+ g_pBots->SetupPlayer(self);
+
g_pServerAuthentication->AddPlayer(self, pNextPlayerToken);
g_pServerLimits->AddPlayer(self);