r5sdk/r5dev/sdklauncher/sdklauncher.cpp

468 lines
15 KiB
C++
Raw Normal View History

//=============================================================================//
//
// Purpose: SDK launcher implementation.
//
//=============================================================================//
#include "core/logger.h"
#include "core/logdef.h"
#include "tier0/cpu.h"
#include "tier0/binstream.h"
#include "tier1/fmtstr.h"
#include "basepanel.h"
#include "sdklauncher.h"
#include "windows/console.h"
#include "vstdlib/keyvaluessystem.h"
#include "filesystem/filesystem_std.h"
static CKeyValuesSystem s_KeyValuesSystem;
static CFileSystem_Stdio s_FullFileSystem;
static CLauncher s_Launcher;
///////////////////////////////////////////////////////////////////////////////
// Purpose: keyvalues singleton accessor
///////////////////////////////////////////////////////////////////////////////
IKeyValuesSystem* KeyValuesSystem()
{
return &s_KeyValuesSystem;
}
///////////////////////////////////////////////////////////////////////////////
// Purpose: filesystem singleton accessor
///////////////////////////////////////////////////////////////////////////////
CFileSystem_Stdio* FileSystem()
{
return &s_FullFileSystem;
}
///////////////////////////////////////////////////////////////////////////////
// Purpose: launcher singleton accessor.
///////////////////////////////////////////////////////////////////////////////
CLauncher* SDKLauncher()
{
return &s_Launcher;
}
///////////////////////////////////////////////////////////////////////////////
// Purpose: launcher logger sink
///////////////////////////////////////////////////////////////////////////////
void LauncherLoggerSink(LogType_t logType, LogLevel_t logLevel, eDLL_T context,
const char* pszLogger, const char* pszFormat, va_list args,
const UINT exitCode /*= NO_ERROR*/, const char* pszUptimeOverride /*= nullptr*/)
{
const string buffer = FormatV(pszFormat, args);
SDKLauncher()->AddLog(logType, buffer.c_str());
EngineLoggerSink(logType, logLevel, context, pszLogger, pszFormat, args, exitCode, pszUptimeOverride);
}
///////////////////////////////////////////////////////////////////////////////
// Purpose: initializes and runs the user interface
///////////////////////////////////////////////////////////////////////////////
void CLauncher::RunSurface()
{
Forms::Application::EnableVisualStyles();
UIX::UIXTheme::InitializeRenderer(new Themes::KoreTheme());
m_pSurface = new CSurface();
m_pSurface->Init();
Forms::Application::Run(m_pSurface, true);
UIX::UIXTheme::ShutdownRenderer();
}
///////////////////////////////////////////////////////////////////////////////
// Purpose: initializes the launcher
///////////////////////////////////////////////////////////////////////////////
void CLauncher::Init()
{
g_CoreMsgVCallback = &LauncherLoggerSink; // Setup logger callback sink.
// Init time.
Plat_FloatTime();
SpdLog_Init(true);
}
///////////////////////////////////////////////////////////////////////////////
// Purpose: de-initializes the launcher
///////////////////////////////////////////////////////////////////////////////
void CLauncher::Shutdown()
{
SpdLog_Shutdown();
}
///////////////////////////////////////////////////////////////////////////////
// Purpose: adds a log to the surface console
///////////////////////////////////////////////////////////////////////////////
void CLauncher::AddLog(const LogType_t level, const char* szText)
{
if (m_pSurface)
{
m_pSurface->AddLog(level, szText);
}
}
///////////////////////////////////////////////////////////////////////////////
// Purpose: handles user input pre-init
// Input : argc -
// *argv[] -
// Output : exit_code (-1 if EntryPoint should continue to HandleInput)
///////////////////////////////////////////////////////////////////////////////
int CLauncher::HandleCommandLine(int argc, char* argv[])
{
for (int i = 1; i < argc; ++i)
{
string arg = argv[i];
eLaunchMode mode = eLaunchMode::LM_NONE;
if ((arg == "-developer") || (arg == "-dev"))
{
mode = eLaunchMode::LM_GAME_DEV;
}
else if ((arg == "-retail") || (arg == "-prod"))
{
mode = eLaunchMode::LM_GAME;
}
else if ((arg == "-server_dev") || (arg == "-svd"))
{
mode = eLaunchMode::LM_SERVER_DEV;
}
else if ((arg == "-server") || (arg == "-sv"))
{
mode = eLaunchMode::LM_SERVER;
}
else if ((arg == "-client_dev") || (arg == "-cld"))
{
mode = eLaunchMode::LM_CLIENT_DEV;
}
else if ((arg == "-client") || (arg == "-cl"))
{
mode = eLaunchMode::LM_CLIENT;
}
if (mode != eLaunchMode::LM_NONE)
{
if (CreateLaunchContext(mode) && LaunchProcess())
{
return EXIT_SUCCESS;
}
}
}
return -1;
}
///////////////////////////////////////////////////////////////////////////////
// Purpose: handles user input post-init
// Output : exit_code
///////////////////////////////////////////////////////////////////////////////
int CLauncher::HandleInput()
{
Msg(eDLL_T::NONE, "--------------------------------------------------------------------------------------------------------------\n");
Warning(eDLL_T::COMMON, "The '%s' options are for development purposes; use the '%s' options for default usage.\n", "DEV", "PROD");
Msg(eDLL_T::NONE, "--------------------------------------------------------------------------------------------------------------\n");
Msg(eDLL_T::COMMON, "%-6s ('0' = %s | '1' = %s).\n", "GAME", "DEV", "PROD");
Msg(eDLL_T::COMMON, "%-6s ('2' = %s | '3' = %s).\n", "SERVER", "DEV", "PROD");
Msg(eDLL_T::COMMON, "%-6s ('4' = %s | '5' = %s).\n", "CLIENT", "DEV", "PROD");
Msg(eDLL_T::NONE, "--------------------------------------------------------------------------------------------------------------\n");
std::cout << "User input: ";
std::string input;
if (std::cin >> input)
{
try
{
eLaunchMode mode = static_cast<eLaunchMode>(std::stoi(input));
if (CreateLaunchContext(mode) && LaunchProcess())
{
return EXIT_SUCCESS;
}
else
{
Error(eDLL_T::COMMON, 0, "Invalid mode (range 0-5).\n");
Sleep(2500);
return EXIT_FAILURE;
}
}
catch (const std::exception& e)
{
Error(eDLL_T::COMMON, 0, "SDK Launcher only takes numerical input (error = %s).\n", e.what());
Sleep(2500);
return EXIT_FAILURE;
}
}
Error(eDLL_T::COMMON, 0, "SDK Launcher requires numerical input.\n");
Sleep(2500);
return EXIT_FAILURE;
}
///////////////////////////////////////////////////////////////////////////////
// Purpose: create launch context.
// Input : lMode -
// lState -
// *szCommandLine -
// Output : true on success, false otherwise.
///////////////////////////////////////////////////////////////////////////////
bool CLauncher::CreateLaunchContext(eLaunchMode lMode, uint64_t nProcessorAffinity /*= NULL*/, const char* szCommandLine /*= nullptr*/, const char* szConfig /*= nullptr*/)
2021-04-13 04:45:22 -07:00
{
///////////////////////////////////////////////////////////////////////////
const char* szGameDLL = nullptr;
const char* szContext = nullptr;
const char* szLevel = nullptr;
std::function<void(const char*, const char*,
const char*, const char*)> fnSetup = [&](
const char* gameDLL, const char* context,
const char* level, const char* config)
{
szGameDLL = gameDLL;
szContext = context;
szLevel = level;
// Only set fall-back config
// if not already set.
if (!szConfig)
{
szConfig = config;
}
};
m_ProcessorAffinity = nProcessorAffinity;
2021-08-01 02:25:29 -07:00
switch (lMode)
{
case eLaunchMode::LM_GAME_DEV:
{
fnSetup(MAIN_GAME_DLL, "GAME",
"DEV", "startup_dev.cfg");
break;
}
case eLaunchMode::LM_GAME:
{
fnSetup(MAIN_GAME_DLL, "GAME",
"PROD", "startup_retail.cfg");
break;
}
case eLaunchMode::LM_SERVER_DEV:
{
fnSetup(SERVER_GAME_DLL, "SERVER",
"DEV", "startup_dedi_dev.cfg");
break;
}
case eLaunchMode::LM_SERVER:
{
fnSetup(SERVER_GAME_DLL, "SERVER",
"PROD", "startup_dedi_retail.cfg");
break;
}
case eLaunchMode::LM_CLIENT_DEV:
{
fnSetup(MAIN_GAME_DLL, "CLIENT",
"DEV", "startup_client_dev.cfg");
break;
}
case eLaunchMode::LM_CLIENT:
{
fnSetup(MAIN_GAME_DLL, "CLIENT",
"PROD", "startup_client_retail.cfg");
break;
}
default:
{
Error(eDLL_T::COMMON, 0, "No launch mode specified.\n");
return false;
}
}
SetupLaunchContext(szConfig, szGameDLL, szCommandLine);
Msg(eDLL_T::COMMON, "*** LAUNCHING %s [%s] ***\n", szContext, szLevel);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// Purpose: setup launch context.
// Input : *szConfig -
// *szWorkerDll -
// *szGameDll -
// *szCommandLine -
///////////////////////////////////////////////////////////////////////////////
void CLauncher::SetupLaunchContext(const char* szConfig, const char* szGameDll, const char* szCommandLine)
{
CIOStream cfgFile;
CFmtStrN<1024> cfgFileName;
CFmtStrMax commandLine;
if (szConfig && szConfig[0])
{
cfgFileName.Format(GAME_CFG_PATH"%s", szConfig);
if (cfgFile.Open(cfgFileName.String(), CIOStream::READ))
{
if (!cfgFile.ReadString(commandLine.Access(), commandLine.GetMaxLength()))
{
Error(eDLL_T::COMMON, 0, "Failed to read file '%s'!\n", szConfig);
}
else
{
commandLine.SetLength(strlen(commandLine.String()));
}
}
else // Failed to open config file.
{
Error(eDLL_T::COMMON, 0, "Failed to open file '%s'!\n", szConfig);
}
}
if (szCommandLine && szCommandLine[0])
{
commandLine.Append(szCommandLine);
}
m_svGameDll = Format("%s\\%s", m_svCurrentDir.c_str(), szGameDll);
m_svCmdLine = Format("%s\\%s %s", m_svCurrentDir.c_str(), szGameDll, commandLine.String());
///////////////////////////////////////////////////////////////////////////
// Print the file paths and arguments.
Msg(eDLL_T::NONE, "--------------------------------------------------------------------------------------------------------------\n");
Msg(eDLL_T::COMMON, "- CWD: %s\n", m_svCurrentDir.c_str());
Msg(eDLL_T::COMMON, "- EXE: %s\n", m_svGameDll.c_str());
Msg(eDLL_T::COMMON, "- CLI: %s\n", commandLine.String());
Msg(eDLL_T::NONE, "--------------------------------------------------------------------------------------------------------------\n");
}
///////////////////////////////////////////////////////////////////////////////
2022-05-28 16:12:56 +02:00
// Purpose: launches the game with results from the setup
// Output : true on success, false otherwise
///////////////////////////////////////////////////////////////////////////////
bool CLauncher::LaunchProcess() const
{
///////////////////////////////////////////////////////////////////////////
STARTUPINFOA StartupInfo = { 0 };
PROCESS_INFORMATION ProcInfo = { 0 };
// Initialize startup info struct.
StartupInfo.cb = sizeof(STARTUPINFOA);
///////////////////////////////////////////////////////////////////////////
2021-04-13 04:45:22 -07:00
// Create the game process in a suspended state with our dll.
BOOL createResult = CreateProcessA(
m_svGameDll.c_str(), // lpApplicationName
(LPSTR)m_svCmdLine.c_str(), // lpCommandLine
NULL, // lpProcessAttributes
NULL, // lpThreadAttributes
FALSE, // bInheritHandles
CREATE_SUSPENDED, // dwCreationFlags
NULL, // lpEnvironment
m_svCurrentDir.c_str(), // lpCurrentDirectory
&StartupInfo, // lpStartupInfo
&ProcInfo // lpProcessInformation
2021-04-13 04:45:22 -07:00
);
///////////////////////////////////////////////////////////////////////////
// Failed to create the process.
if (createResult)
{
if (m_ProcessorAffinity)
{
BOOL affinityResult = SetProcessAffinityMask(ProcInfo.hProcess, m_ProcessorAffinity);
if (!affinityResult)
{
// Just print the result, don't return, as
// the process was created successfully.
PrintLastError();
}
}
}
else
2021-04-13 04:45:22 -07:00
{
PrintLastError();
return false;
}
///////////////////////////////////////////////////////////////////////////
2021-04-13 04:45:22 -07:00
// Resume the process.
ResumeThread(ProcInfo.hThread);
///////////////////////////////////////////////////////////////////////////
2021-04-13 04:45:22 -07:00
// Close the process and thread handles.
CloseHandle(ProcInfo.hProcess);
CloseHandle(ProcInfo.hThread);
return true;
}
///////////////////////////////////////////////////////////////////////////////
// Purpose: Window enumerator callback.
// Input : hwnd -
// lParam -
// Output : TRUE on success, FALSE otherwise.
///////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
vector<HWND>* pHandles = reinterpret_cast<vector<HWND>*>(lParam);
if (!pHandles)
{
return FALSE;
}
char szClassName[256];
if (!GetClassNameA(hwnd, szClassName, 256))
{
return FALSE;
}
if (strcmp(szClassName, DEFAULT_WINDOW_CLASS_NAME) == 0)
{
pHandles->push_back(hwnd);
}
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
// EntryPoint.
2021-07-12 08:47:54 -07:00
///////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
2021-04-13 04:45:22 -07:00
{
CheckSystemCPUForSSE2();
if (__argc < 2)
2021-08-01 02:25:29 -07:00
{
#ifdef _DEBUG
Console_Init(true);
#endif // _DEBUG
SDKLauncher()->Init();
SDKLauncher()->RunSurface();
SDKLauncher()->Shutdown();
#ifdef _DEBUG
Console_Shutdown();
#endif // _DEBUG
}
else
Merge indev into master. (#44) * Added separate function to resolve relative addresses in address.h * Added new patterns to the print function. * Updated IsFlagSet hooks. * Cleaned up code to properly mask off Dev and Cheat flags. * Added separate define from _DEBUG so you can define it in release builds for people without C++ Debug Restributeables. * Removed un-used define in hooks.h * Fixed potential crashes in r5net and added debug prints. * Potential crashes were when in certain post functions the returned status wasn't 200. * Changed map-select drop down menu, now it displays 'Map Name + Season' instead of file-name. (#41) * Host Server shows normal map names * Changed a few stuff... * redid some stuff that isn't crucial * Update CCompanion.cpp * Update CCompanion.cpp * Updated mapname displaying. * Moved "ServerMap" as a static object into CCompanion::HostServerSection(). Co-authored-by: IcePixelx <41352111+PixieCore@users.noreply.github.com> * prevent squirrel compiler errors from killing game process (#43) * Read description for all changes. * Added ability to register custom ConVar. * Added 2 custom ConVars to open the CGameConsole and CCompanion Windows. * Changed ResolveRelativeAddress. * Added Config System for the Gui. * Added ImGui::Hotkey. * Added the ability to change 2 hotkeys for opening the window for CGameConsole and CCompanion. * Changed pattern for Squirrel_CompilerError to use a String. * Added IMemAlloc::AllocWrapper to patterns.h * Changes in description. * Added icon to launcher.exe * Launcher.exe gets remnamed to Run R5 Reloaded.exe in launcher release compilation configuration. * Extended argument buffer for starting the game in launcher.exe. * Added exception printing if in the custom ConVars an invalid value gets passed. * Wrong return. * Added shortcut with launch params. * Fixed prints. Co-authored-by: Marcii0 <58266292+Marcii0@users.noreply.github.com> Co-authored-by: BobTheBob <32057864+BobTheBob9@users.noreply.github.com>
2021-08-19 15:26:44 +02:00
{
if (!Console_Init(true))
return EXIT_FAILURE;
Merge indev into master. (#44) * Added separate function to resolve relative addresses in address.h * Added new patterns to the print function. * Updated IsFlagSet hooks. * Cleaned up code to properly mask off Dev and Cheat flags. * Added separate define from _DEBUG so you can define it in release builds for people without C++ Debug Restributeables. * Removed un-used define in hooks.h * Fixed potential crashes in r5net and added debug prints. * Potential crashes were when in certain post functions the returned status wasn't 200. * Changed map-select drop down menu, now it displays 'Map Name + Season' instead of file-name. (#41) * Host Server shows normal map names * Changed a few stuff... * redid some stuff that isn't crucial * Update CCompanion.cpp * Update CCompanion.cpp * Updated mapname displaying. * Moved "ServerMap" as a static object into CCompanion::HostServerSection(). Co-authored-by: IcePixelx <41352111+PixieCore@users.noreply.github.com> * prevent squirrel compiler errors from killing game process (#43) * Read description for all changes. * Added ability to register custom ConVar. * Added 2 custom ConVars to open the CGameConsole and CCompanion Windows. * Changed ResolveRelativeAddress. * Added Config System for the Gui. * Added ImGui::Hotkey. * Added the ability to change 2 hotkeys for opening the window for CGameConsole and CCompanion. * Changed pattern for Squirrel_CompilerError to use a String. * Added IMemAlloc::AllocWrapper to patterns.h * Changes in description. * Added icon to launcher.exe * Launcher.exe gets remnamed to Run R5 Reloaded.exe in launcher release compilation configuration. * Extended argument buffer for starting the game in launcher.exe. * Added exception printing if in the custom ConVars an invalid value gets passed. * Wrong return. * Added shortcut with launch params. * Fixed prints. Co-authored-by: Marcii0 <58266292+Marcii0@users.noreply.github.com> Co-authored-by: BobTheBob <32057864+BobTheBob9@users.noreply.github.com>
2021-08-19 15:26:44 +02:00
SDKLauncher()->Init();
int cmdRet = SDKLauncher()->HandleCommandLine(__argc, __argv);
if (cmdRet == -1)
cmdRet = SDKLauncher()->HandleInput();
SDKLauncher()->Shutdown();
if (!Console_Shutdown())
return EXIT_FAILURE;
return cmdRet;
}
return EXIT_SUCCESS;
Code base refactor + major performance and readability improvement. Read description for details. * Codebase restructured to SourceSDK codebase style and .cpp/.h assertion paths in the game executable. * Document most functions with valve style 'Purpose' blocks. * Rename variables to match the rest of the codebase and Valve's naming convention. * Dedicated DLL and the SDKLauncher now share the same codebase as the DevSDK. * Obtain globals or pointers directly instead of waiting for runtime initialized data. * Dynamically search for all functions and globals (this doesn't count for dedicated yet!). * Initialize most in-SDK variables. * Move certain prints and other utilities under ConVars to reduce verbosity and increase performance. * Print all pattern scan results through a virtual function to make it easier to add and debug new patterns in the future. * Type global var pointers appropriately if class or type is known and implemented. * Forward declare 'CClient' class to avoid having 2 'g_pClient' copies. * Add IDA's pseudo definitions for easier prototyping with decompiled assembly code. * RPAK decompress Command callback implementation. * Load decompressed RPaks from 'paks\Win32\' overriding the ones in 'paks\Win64\' (the decompress callback will automatically fix the header and write it to 'paks\Win32\'). * VPK decompress Command callback implementation. * Move CRC32 ands Adler32 to implementation files. * Server will print out more details about the connecting client. * Upgrade ImGui lib to v1.86. * Don't compile id3dx.h for dedicated. * Don't compile id3dx.cpp for dedicated * Implement DevMsg print function allowing to print information to the in-game VGUI/RUI console overlay, ImGui console overlay and the external windows console * Fixed bug where the Error function would not properly terminate the process when an error is called. This caused access violations for critical/non-recoverable errors. * Fixed bug where the game would crash if the console or server browser was enabled while the game was still starting up. * Several bug fixes for the dedicated server (warning: dedicated is still considered work-in-progress!).
2021-12-25 22:36:38 +01:00
}