mirror of
https://github.com/rehlds/rehlds.git
synced 2024-12-29 08:05:50 +03:00
Merge pull request #410 from dreamstalker/launcher_refactoring
Launcher refactoring
This commit is contained in:
commit
97edca1d90
@ -322,7 +322,7 @@ const char *CCommandLine::CheckParm(const char *psz, char **ppszValue) const
|
|||||||
|
|
||||||
char *pret = strstr(m_pszCmdLine, psz);
|
char *pret = strstr(m_pszCmdLine, psz);
|
||||||
if (!pret || !ppszValue)
|
if (!pret || !ppszValue)
|
||||||
return nullptr;
|
return pret;
|
||||||
|
|
||||||
*ppszValue = nullptr;
|
*ppszValue = nullptr;
|
||||||
|
|
||||||
@ -340,11 +340,11 @@ const char *CCommandLine::CheckParm(const char *psz, char **ppszValue) const
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
sz[i++] = p2[i];
|
sz[i++] = p2[i];
|
||||||
}
|
} while (i < sizeof(sz));
|
||||||
while (i < sizeof(sz));
|
|
||||||
|
|
||||||
sz[i] = '\0';
|
sz[i] = '\0';
|
||||||
*ppszValue = sz;
|
*ppszValue = sz;
|
||||||
|
|
||||||
return pret;
|
return pret;
|
||||||
}
|
}
|
||||||
|
|
25
rehlds/common/icommandline.h
Normal file
25
rehlds/common/icommandline.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef ICOMMANDLINE_H
|
||||||
|
#define ICOMMANDLINE_H
|
||||||
|
#ifdef _WIN32
|
||||||
|
#pragma once
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Interface to engine command line
|
||||||
|
class ICommandLine {
|
||||||
|
public:
|
||||||
|
virtual void CreateCmdLine(const char *commandline) = 0;
|
||||||
|
virtual void CreateCmdLine(int argc, const char **argv) = 0;
|
||||||
|
virtual const char *GetCmdLine() const = 0;
|
||||||
|
|
||||||
|
// Check whether a particular parameter exists
|
||||||
|
virtual const char *CheckParm(const char *psz, char **ppszValue = nullptr) const = 0;
|
||||||
|
virtual void RemoveParm(const char *pszParm) = 0;
|
||||||
|
virtual void AppendParm(const char *pszParm, const char *pszValues) = 0;
|
||||||
|
|
||||||
|
virtual void SetParm(const char *pszParm, const char *pszValues) = 0;
|
||||||
|
virtual void SetParm(const char *pszParm, int iValue) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
ICommandLine *CommandLine();
|
||||||
|
|
||||||
|
#endif // ICOMMANDLINE_H
|
@ -114,6 +114,7 @@ model {
|
|||||||
include "ObjectList.cpp"
|
include "ObjectList.cpp"
|
||||||
include "SteamAppStartUp.cpp"
|
include "SteamAppStartUp.cpp"
|
||||||
include "textconsole.cpp"
|
include "textconsole.cpp"
|
||||||
|
include "commandline.cpp"
|
||||||
if (GradleCppUtils.windows) {
|
if (GradleCppUtils.windows) {
|
||||||
include "TextConsoleWin32.cpp"
|
include "TextConsoleWin32.cpp"
|
||||||
}
|
}
|
||||||
|
@ -154,6 +154,7 @@
|
|||||||
<ResourceCompile Include="dedicated.rc" />
|
<ResourceCompile Include="dedicated.rc" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ClCompile Include="..\..\common\commandline.cpp" />
|
||||||
<ClCompile Include="..\..\common\ObjectList.cpp" />
|
<ClCompile Include="..\..\common\ObjectList.cpp" />
|
||||||
<ClCompile Include="..\..\common\SteamAppStartUp.cpp" />
|
<ClCompile Include="..\..\common\SteamAppStartUp.cpp" />
|
||||||
<ClCompile Include="..\..\common\textconsole.cpp" />
|
<ClCompile Include="..\..\common\textconsole.cpp" />
|
||||||
@ -163,7 +164,6 @@
|
|||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\common\TextConsoleWin32.cpp" />
|
<ClCompile Include="..\..\common\TextConsoleWin32.cpp" />
|
||||||
<ClCompile Include="..\..\engine\mem.cpp" />
|
<ClCompile Include="..\..\engine\mem.cpp" />
|
||||||
<ClCompile Include="..\src\commandline.cpp" />
|
|
||||||
<ClCompile Include="..\src\conproc.cpp" />
|
<ClCompile Include="..\src\conproc.cpp" />
|
||||||
<ClCompile Include="..\src\dbg.cpp" />
|
<ClCompile Include="..\src\dbg.cpp" />
|
||||||
<ClCompile Include="..\src\dedicated_exports.cpp" />
|
<ClCompile Include="..\src\dedicated_exports.cpp" />
|
||||||
|
@ -37,9 +37,6 @@
|
|||||||
</ClInclude>
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="..\src\commandline.cpp">
|
|
||||||
<Filter>src</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="..\src\dbg.cpp">
|
<ClCompile Include="..\src\dbg.cpp">
|
||||||
<Filter>src</Filter>
|
<Filter>src</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@ -85,6 +82,9 @@
|
|||||||
<ClCompile Include="..\..\engine\mem.cpp">
|
<ClCompile Include="..\..\engine\mem.cpp">
|
||||||
<Filter>src\engine</Filter>
|
<Filter>src\engine</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\common\commandline.cpp">
|
||||||
|
<Filter>src</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Image Include="icon.ico" />
|
<Image Include="icon.ico" />
|
||||||
|
@ -1,16 +1,23 @@
|
|||||||
#ifndef DEDICATED_H
|
|
||||||
#define DEDICATED_H
|
|
||||||
#ifdef _WIN32
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define VGUI
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define LAUNCHER_ERROR -1
|
||||||
|
#define LAUNCHER_OK 0
|
||||||
|
|
||||||
typedef void (*NET_Sleep_t)();
|
typedef void (*NET_Sleep_t)();
|
||||||
typedef void (*SleepType)(int msec);
|
typedef void (*SleepType)(int msec);
|
||||||
|
|
||||||
extern bool g_bVGui;
|
extern bool g_bVGui;
|
||||||
extern IDedicatedServerAPI *engineAPI;
|
extern IDedicatedServerAPI *engineAPI;
|
||||||
|
|
||||||
#ifdef LAUNCHER_FIXES
|
#ifdef LAUNCHER_FIXES
|
||||||
extern IRehldsApi *rehldsApi;
|
extern IRehldsApi *rehldsApi;
|
||||||
extern const RehldsFuncs_t* rehldsFuncs;
|
extern const RehldsFuncs_t* rehldsFuncs;
|
||||||
#endif
|
#endif
|
||||||
#endif // DEDICATED_H
|
|
||||||
|
bool Sys_SetupConsole();
|
||||||
|
void Sys_PrepareConsoleInput();
|
||||||
|
void Sys_InitPingboost();
|
||||||
|
@ -11,6 +11,8 @@
|
|||||||
#include "engine_hlds_api.h"
|
#include "engine_hlds_api.h"
|
||||||
#include "idedicatedexports.h"
|
#include "idedicatedexports.h"
|
||||||
#include "icommandline.h"
|
#include "icommandline.h"
|
||||||
|
#include "isys.h"
|
||||||
|
#include "dll_state.h"
|
||||||
|
|
||||||
#ifdef LAUNCHER_FIXES
|
#ifdef LAUNCHER_FIXES
|
||||||
struct DLL_FUNCTIONS;
|
struct DLL_FUNCTIONS;
|
||||||
@ -18,6 +20,8 @@ struct DLL_FUNCTIONS;
|
|||||||
#include "rehlds_api.h"
|
#include "rehlds_api.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "dedicated.h"
|
||||||
|
|
||||||
#include "sys_ded.h"
|
#include "sys_ded.h"
|
||||||
#include "textconsole.h"
|
#include "textconsole.h"
|
||||||
#include "vgui/vguihelpers.h"
|
#include "vgui/vguihelpers.h"
|
||||||
@ -27,4 +31,6 @@ struct DLL_FUNCTIONS;
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include "conproc.h"
|
#include "conproc.h"
|
||||||
|
#else
|
||||||
|
#include <signal.h>
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
#include "precompiled.h"
|
#include "precompiled.h"
|
||||||
|
|
||||||
long hDLLThirdParty = 0L;
|
|
||||||
|
|
||||||
bool g_bVGui = false;
|
bool g_bVGui = false;
|
||||||
char *gpszCvars = nullptr;
|
char *gpszCvars = nullptr;
|
||||||
bool gbAppHasBeenTerminated = false;
|
bool g_bAppHasBeenTerminated = false;
|
||||||
|
|
||||||
CSysModule *g_pEngineModule = nullptr;
|
CSysModule *g_pEngineModule = nullptr;
|
||||||
IDedicatedServerAPI *engineAPI = nullptr;
|
IDedicatedServerAPI *engineAPI = nullptr;
|
||||||
@ -37,26 +35,18 @@ void Sys_Printf(char *fmt, ...)
|
|||||||
va_end(argptr);
|
va_end(argptr);
|
||||||
|
|
||||||
// Get Current text and append it.
|
// Get Current text and append it.
|
||||||
|
#ifdef VGUI
|
||||||
if (g_bVGui)
|
if (g_bVGui)
|
||||||
{
|
{
|
||||||
VGUIPrintf(szText);
|
VGUIPrintf(szText);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
console.Print(szText);
|
console.Print(szText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Load3rdParty()
|
|
||||||
{
|
|
||||||
// Only do this if the server operator wants the support.
|
|
||||||
// ( In case of malicious code, too )
|
|
||||||
if (CommandLine()->CheckParm("-usegh"))
|
|
||||||
{
|
|
||||||
hDLLThirdParty = sys->LoadLibrary("ghostinj.dll");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProcessConsoleInput()
|
void ProcessConsoleInput()
|
||||||
{
|
{
|
||||||
if (!engineAPI)
|
if (!engineAPI)
|
||||||
@ -76,10 +66,9 @@ char *UTIL_GetBaseDir()
|
|||||||
return ".";
|
return ".";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server Frame
|
int RunEngine()
|
||||||
int RunServer()
|
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef VGUI
|
||||||
if (g_bVGui)
|
if (g_bVGui)
|
||||||
{
|
{
|
||||||
// TODO: finish VGUI
|
// TODO: finish VGUI
|
||||||
@ -87,134 +76,204 @@ int RunServer()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int iret = 0;
|
CreateInterfaceFn engineFactory = Sys_GetFactory(g_pEngineModule);
|
||||||
while (true)
|
RunVGUIFrame();
|
||||||
|
|
||||||
|
if (engineFactory)
|
||||||
{
|
{
|
||||||
if (gbAppHasBeenTerminated)
|
engineAPI = (IDedicatedServerAPI *)engineFactory(VENGINE_HLDS_API_VERSION, nullptr);
|
||||||
return iret;
|
|
||||||
|
|
||||||
CreateInterfaceFn engineFactory = Sys_GetFactory(g_pEngineModule);
|
|
||||||
|
|
||||||
RunVGUIFrame();
|
|
||||||
|
|
||||||
if (engineFactory)
|
|
||||||
{
|
|
||||||
engineAPI = (IDedicatedServerAPI *)engineFactory(VENGINE_HLDS_API_VERSION, NULL);
|
|
||||||
#ifdef LAUNCHER_FIXES
|
#ifdef LAUNCHER_FIXES
|
||||||
rehldsApi = (IRehldsApi *)engineFactory(VREHLDS_HLDS_API_VERSION, NULL);
|
rehldsApi = (IRehldsApi *)engineFactory(VREHLDS_HLDS_API_VERSION, NULL);
|
||||||
if (rehldsApi)
|
if (rehldsApi)
|
||||||
{
|
|
||||||
if (rehldsApi->GetMajorVersion() != REHLDS_API_VERSION_MAJOR || rehldsApi->GetMinorVersion() < REHLDS_API_VERSION_MINOR)
|
|
||||||
{
|
|
||||||
rehldsApi = nullptr;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rehldsFuncs = rehldsApi->GetFuncs();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
RunVGUIFrame();
|
|
||||||
|
|
||||||
if (!engineAPI)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (!engineAPI->Init(UTIL_GetBaseDir(), (char *)CommandLine()->GetCmdLine(), Sys_GetFactoryThis(), g_FilesystemFactoryFn))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
RunVGUIFrame();
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
if (g_bVGui)
|
|
||||||
{
|
{
|
||||||
// TODO: finish VGUI
|
if (rehldsApi->GetMajorVersion() != REHLDS_API_VERSION_MAJOR || rehldsApi->GetMinorVersion() < REHLDS_API_VERSION_MINOR)
|
||||||
/*g_pFileSystemInterface->AddSearchPath("platform", "PLATFORM");
|
|
||||||
|
|
||||||
// find our configuration directory
|
|
||||||
char szConfigDir[512];
|
|
||||||
const char *steamPath = getenv("SteamInstallPath");
|
|
||||||
if (steamPath)
|
|
||||||
{
|
{
|
||||||
// put the config dir directly under steam
|
rehldsApi = nullptr;
|
||||||
_snprintf(szConfigDir, sizeof(szConfigDir), "%s/config", steamPath);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// we're not running steam, so just put the config dir under the platform
|
rehldsFuncs = rehldsApi->GetFuncs();
|
||||||
strncpy(szConfigDir, "platform/config", sizeof(szConfigDir));
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
#endif // _WIN32
|
|
||||||
|
|
||||||
RunVGUIFrame();
|
|
||||||
|
|
||||||
if (gpszCvars)
|
|
||||||
engineAPI->AddConsoleText(gpszCvars);
|
|
||||||
|
|
||||||
VGUIFinishedConfig();
|
|
||||||
RunVGUIFrame();
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
if (g_bVGui)
|
|
||||||
RunVGUIFrame();
|
|
||||||
#endif // _WIN32
|
|
||||||
|
|
||||||
// Running really fast, yield some time to other apps
|
|
||||||
sys->Sleep(1);
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
if (!g_bVGui)
|
|
||||||
{
|
|
||||||
bool bDone = false;
|
|
||||||
MSG msg;
|
|
||||||
while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
|
|
||||||
{
|
|
||||||
if (!GetMessage(&msg, NULL, 0, 0))
|
|
||||||
{
|
|
||||||
bDone = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
TranslateMessage(&msg);
|
|
||||||
DispatchMessage(&msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gbAppHasBeenTerminated || bDone)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!g_bVGui)
|
|
||||||
#endif // _WIN32
|
|
||||||
{
|
|
||||||
ProcessConsoleInput();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!engineAPI->RunFrame())
|
|
||||||
break;
|
|
||||||
|
|
||||||
sys->UpdateStatus(0 /* don't force */);
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
RunVGUIFrame();
|
||||||
|
if (!engineAPI || !engineAPI->Init(UTIL_GetBaseDir(), (char *)CommandLine()->GetCmdLine(), Sys_GetFactoryThis(), g_FilesystemFactoryFn)) {
|
||||||
|
return LAUNCHER_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
if (g_bVGui)
|
RunVGUIFrame();
|
||||||
{
|
|
||||||
|
#ifdef VGUI
|
||||||
|
// TODO: finish me!
|
||||||
|
/*if (g_bVGui) {
|
||||||
|
g_pFileSystemInterface->AddSearchPath("platform", "PLATFORM");
|
||||||
|
|
||||||
|
// find our configuration directory
|
||||||
|
char szConfigDir[512];
|
||||||
|
const char *steamPath = getenv("SteamInstallPath");
|
||||||
|
if (steamPath) {
|
||||||
|
// put the config dir directly under steam
|
||||||
|
_snprintf(szConfigDir, sizeof(szConfigDir), "%s/config", steamPath);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// we're not running steam, so just put the config dir under the platform
|
||||||
|
strncpy(szConfigDir, "platform/config", sizeof(szConfigDir));
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
#endif // VGUI
|
||||||
|
|
||||||
|
RunVGUIFrame();
|
||||||
|
|
||||||
|
if (gpszCvars) {
|
||||||
|
engineAPI->AddConsoleText(gpszCvars);
|
||||||
|
}
|
||||||
|
|
||||||
|
VGUIFinishedConfig();
|
||||||
|
RunVGUIFrame();
|
||||||
|
|
||||||
|
bool bDone = false;
|
||||||
|
while (!bDone) {
|
||||||
|
// Running really fast, yield some time to other apps
|
||||||
|
sys->Sleep(1);
|
||||||
|
|
||||||
|
Sys_PrepareConsoleInput();
|
||||||
|
|
||||||
|
if (g_bAppHasBeenTerminated)
|
||||||
|
break;
|
||||||
|
|
||||||
|
#ifdef VGUI
|
||||||
|
if (g_bVGui) {
|
||||||
RunVGUIFrame();
|
RunVGUIFrame();
|
||||||
StopVGUI();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
sys->DestroyConsoleWindow();
|
ProcessConsoleInput();
|
||||||
console.ShutDown();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
iret = engineAPI->Shutdown();
|
bDone = !engineAPI->RunFrame();
|
||||||
Sys_UnloadModule(g_pEngineModule);
|
sys->UpdateStatus(FALSE /* don't force */);
|
||||||
VGUIFinishedConfig();
|
|
||||||
|
|
||||||
if (iret == DLL_CLOSE)
|
|
||||||
return iret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef VGUI
|
||||||
|
if (g_bVGui) {
|
||||||
|
RunVGUIFrame();
|
||||||
|
StopVGUI();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
sys->DestroyConsoleWindow();
|
||||||
|
console.ShutDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
int iret = engineAPI->Shutdown();
|
||||||
|
VGUIFinishedConfig();
|
||||||
|
|
||||||
|
if (iret == DLL_CLOSE)
|
||||||
|
g_bAppHasBeenTerminated = true;
|
||||||
|
|
||||||
|
return LAUNCHER_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int StartServer(char* cmdline)
|
||||||
|
{
|
||||||
|
g_bAppHasBeenTerminated = false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
CommandLine()->CreateCmdLine(cmdline);
|
||||||
|
CommandLine()->AppendParm("-steam", nullptr);
|
||||||
|
|
||||||
|
Sys_InitPingboost();
|
||||||
|
|
||||||
|
// Load engine
|
||||||
|
g_pEngineModule = Sys_LoadModule(ENGINE_LIB);
|
||||||
|
if (!g_pEngineModule) {
|
||||||
|
sys->ErrorMessage(0, "Unable to load engine, image is corrupt.");
|
||||||
|
return LAUNCHER_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load filesystem
|
||||||
|
g_pFileSystemModule = Sys_LoadModule(STDIO_FILESYSTEM_LIB);
|
||||||
|
if (!g_pFileSystemModule) {
|
||||||
|
sys->ErrorMessage(0, "Unable to load filesystem, image is corrupt.");
|
||||||
|
return LAUNCHER_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get filesystem factory
|
||||||
|
g_FilesystemFactoryFn = Sys_GetFactory(g_pFileSystemModule);
|
||||||
|
if (!g_FilesystemFactoryFn) {
|
||||||
|
sys->ErrorMessage(0, "Unable to get filesystem factory.");
|
||||||
|
return LAUNCHER_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve filesystem interface
|
||||||
|
IFileSystem *pFullFileSystem = (IFileSystem *)g_FilesystemFactoryFn(FILESYSTEM_INTERFACE_VERSION, nullptr);
|
||||||
|
if (!pFullFileSystem) {
|
||||||
|
sys->ErrorMessage(0, "Can not retrive filesystem interface version '" FILESYSTEM_INTERFACE_VERSION "'.\n");
|
||||||
|
return LAUNCHER_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mount filesystem
|
||||||
|
pFullFileSystem->Mount();
|
||||||
|
|
||||||
|
// Init VGUI or Console mode
|
||||||
|
#ifdef VGUI
|
||||||
|
char *pszValue = nullptr;
|
||||||
|
if (!CommandLine()->CheckParm("-console", &pszValue)) {
|
||||||
|
g_bVGui = true;
|
||||||
|
StartVGUI();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
if (!console.Init()) {
|
||||||
|
sys->ErrorMessage(0, "Failed to initialize console."); // TODO: add more details
|
||||||
|
return LAUNCHER_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sys->CreateConsoleWindow()) {
|
||||||
|
sys->ErrorMessage(0, "Failed to create console window.");
|
||||||
|
return LAUNCHER_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Sys_SetupConsole())
|
||||||
|
return LAUNCHER_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef VGUI
|
||||||
|
// TODO: finish me!
|
||||||
|
/*// run vgui
|
||||||
|
if (g_bVGui)
|
||||||
|
{
|
||||||
|
while (VGUIIsInConfig() && VGUIIsRunning())
|
||||||
|
{
|
||||||
|
RunVGUIFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else*/
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
int ret = RunEngine();
|
||||||
|
|
||||||
|
if (ret == LAUNCHER_ERROR) {
|
||||||
|
sys->ErrorMessage(0, "Failed to launch engine.\n");
|
||||||
|
return LAUNCHER_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gpszCvars)
|
||||||
|
free(gpszCvars);
|
||||||
|
|
||||||
|
// Unmount filesystem
|
||||||
|
pFullFileSystem->Unmount();
|
||||||
|
|
||||||
|
// Unload modules
|
||||||
|
Sys_UnloadModule(g_pFileSystemModule);
|
||||||
|
Sys_UnloadModule(g_pEngineModule);
|
||||||
|
|
||||||
|
} while (!g_bAppHasBeenTerminated);
|
||||||
|
|
||||||
|
return LAUNCHER_OK;
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,12 @@
|
|||||||
#ifndef SYS_DED_H
|
|
||||||
#define SYS_DED_H
|
|
||||||
#ifdef _WIN32
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "isys.h"
|
|
||||||
#include "dedicated.h"
|
|
||||||
#include "dll_state.h"
|
|
||||||
|
|
||||||
#if !defined(_WIN32)
|
|
||||||
#include <signal.h>
|
|
||||||
#endif // !defined(_WIN32)
|
|
||||||
|
|
||||||
int RunServer();
|
|
||||||
void Load3rdParty();
|
void Load3rdParty();
|
||||||
void Sys_Printf_Safe(char *text);
|
void Sys_Printf_Safe(char *text);
|
||||||
void Sys_Printf(char *fmt, ...);
|
void Sys_Printf(char *fmt, ...);
|
||||||
void _log(const char *fmt, ...);
|
void _log(const char *fmt, ...);
|
||||||
|
|
||||||
extern long hDLLThirdParty;
|
|
||||||
extern char *gpszCvars;
|
extern char *gpszCvars;
|
||||||
extern bool gbAppHasBeenTerminated;
|
extern bool g_bAppHasBeenTerminated;
|
||||||
extern IFileSystem *g_pFileSystemInterface;
|
extern IFileSystem *g_pFileSystemInterface;
|
||||||
extern CSysModule *g_pEngineModule;
|
extern CSysModule *g_pEngineModule;
|
||||||
extern CSysModule *g_pFileSystemModule;
|
extern CSysModule *g_pFileSystemModule;
|
||||||
@ -30,4 +16,5 @@ extern CreateInterfaceFn g_FilesystemFactoryFn;
|
|||||||
BOOL WINAPI MyHandlerRoutine(DWORD CtrlType);
|
BOOL WINAPI MyHandlerRoutine(DWORD CtrlType);
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
#endif // SYS_DED_H
|
int RunEngine();
|
||||||
|
int StartServer(char* cmdline);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
class CSys: public ISys {
|
class CSys: public ISys {
|
||||||
public:
|
public:
|
||||||
|
CSys();
|
||||||
virtual ~CSys();
|
virtual ~CSys();
|
||||||
|
|
||||||
void Sleep(int msec);
|
void Sleep(int msec);
|
||||||
@ -29,6 +30,20 @@ char g_szEXEName[MAX_PATH];
|
|||||||
SleepType Sys_Sleep;
|
SleepType Sys_Sleep;
|
||||||
NET_Sleep_t NET_Sleep_Timeout = NULL;
|
NET_Sleep_t NET_Sleep_Timeout = NULL;
|
||||||
|
|
||||||
|
CSys::CSys()
|
||||||
|
{
|
||||||
|
char *fname;
|
||||||
|
if (CommandLine()->CheckParm("-pidfile", &fname) && fname) {
|
||||||
|
FILE *pidFile = fopen(fname, "w");
|
||||||
|
if (pidFile) {
|
||||||
|
fprintf(pidFile, "%i\n", getpid());
|
||||||
|
fclose(pidFile);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
printf("Warning: unable to open pidfile (%s)\n", fname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CSys::~CSys()
|
CSys::~CSys()
|
||||||
{
|
{
|
||||||
sys = nullptr;
|
sys = nullptr;
|
||||||
@ -61,8 +76,8 @@ void Sleep_Select(int msec)
|
|||||||
|
|
||||||
void Sleep_Net(int msec)
|
void Sleep_Net(int msec)
|
||||||
{
|
{
|
||||||
NET_Sleep_Timeout();
|
NET_Sleep_Timeout();
|
||||||
}
|
}
|
||||||
|
|
||||||
// linux runs on a 100Hz scheduling clock, so the minimum latency from
|
// linux runs on a 100Hz scheduling clock, so the minimum latency from
|
||||||
// usleep is 10msec. However, people want lower latency than this..
|
// usleep is 10msec. However, people want lower latency than this..
|
||||||
@ -113,6 +128,37 @@ void alarmFunc(int num)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Sys_InitPingboost()
|
||||||
|
{
|
||||||
|
Sys_Sleep = Sleep_Old;
|
||||||
|
|
||||||
|
char *pPingType;
|
||||||
|
int type;
|
||||||
|
if (CommandLine()->CheckParm("-pingboost", &pPingType) && pPingType) {
|
||||||
|
type = atoi(pPingType);
|
||||||
|
switch (type) {
|
||||||
|
case 1:
|
||||||
|
signal(SIGALRM, alarmFunc);
|
||||||
|
Sys_Sleep = Sleep_Timer;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
Sys_Sleep = Sleep_Select;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
Sys_Sleep = Sleep_Net;
|
||||||
|
|
||||||
|
// we Sys_GetProcAddress NET_Sleep() from
|
||||||
|
//engine_i486.so later in this function
|
||||||
|
NET_Sleep_Timeout = (NET_Sleep_t)Sys_GetProcAddress(g_pEngineModule, "NET_Sleep_Timeout");
|
||||||
|
break;
|
||||||
|
// just in case
|
||||||
|
default:
|
||||||
|
Sys_Sleep = Sleep_Old;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool CSys::GetExecutableName(char *out)
|
bool CSys::GetExecutableName(char *out)
|
||||||
{
|
{
|
||||||
strcpy(out, g_szEXEName);
|
strcpy(out, g_szEXEName);
|
||||||
@ -200,14 +246,13 @@ void CSys::Printf(char *fmt, ...)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const int MAX_LINUX_CMDLINE = 2048;
|
const int MAX_LINUX_CMDLINE = 2048;
|
||||||
static char linuxCmdline[ MAX_LINUX_CMDLINE ];
|
static char linuxCmdline[MAX_LINUX_CMDLINE];
|
||||||
|
|
||||||
void BuildCmdLine(int argc, char **argv)
|
char* BuildCmdLine(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int len;
|
int len = 0;
|
||||||
int i;
|
|
||||||
|
|
||||||
for (len = 0, i = 1; i < argc; i++)
|
for (int i = 1; i < argc; i++)
|
||||||
{
|
{
|
||||||
len += strlen(argv[i]) + 1;
|
len += strlen(argv[i]) + 1;
|
||||||
}
|
}
|
||||||
@ -216,112 +261,33 @@ void BuildCmdLine(int argc, char **argv)
|
|||||||
{
|
{
|
||||||
printf("command line too long, %i max\n", MAX_LINUX_CMDLINE);
|
printf("command line too long, %i max\n", MAX_LINUX_CMDLINE);
|
||||||
exit(-1);
|
exit(-1);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
linuxCmdline[0] = '\0';
|
linuxCmdline[0] = '\0';
|
||||||
for (i = 1; i < argc; i++)
|
for (int i = 1; i < argc; i++)
|
||||||
{
|
{
|
||||||
if (i > 1) {
|
if (i > 1) {
|
||||||
strcat(linuxCmdline, " ");
|
strcat(linuxCmdline, " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
strcat(linuxCmdline, argv[ i ]);
|
strcat(linuxCmdline, argv[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char *GetCommandLine()
|
bool Sys_SetupConsole()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sys_PrepareConsoleInput()
|
||||||
{
|
{
|
||||||
return linuxCmdline;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
_snprintf(g_szEXEName, sizeof(g_szEXEName), "%s", argv[0]);
|
_snprintf(g_szEXEName, sizeof(g_szEXEName), "%s", argv[0]);
|
||||||
BuildCmdLine(argc, argv);
|
char* cmdline = BuildCmdLine(argc, argv);
|
||||||
|
StartServer(cmdline);
|
||||||
|
|
||||||
CommandLine()->CreateCmdLine(::GetCommandLine());
|
|
||||||
CommandLine()->AppendParm("-steam", NULL);
|
|
||||||
|
|
||||||
// Load engine
|
|
||||||
g_pEngineModule = Sys_LoadModule(ENGINE_LIB);
|
|
||||||
if (!g_pEngineModule)
|
|
||||||
{
|
|
||||||
sys->ErrorMessage(1, "Unable to load engine, image is corrupt.");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Sys_Sleep = Sleep_Old;
|
|
||||||
|
|
||||||
char *pPingType;
|
|
||||||
int type;
|
|
||||||
if (CommandLine()->CheckParm("-pingboost", &pPingType) && pPingType)
|
|
||||||
{
|
|
||||||
type = atoi(pPingType);
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
signal(SIGALRM, alarmFunc);
|
|
||||||
Sys_Sleep = Sleep_Timer;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
Sys_Sleep = Sleep_Select;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
// we Sys_GetProcAddress NET_Sleep() from
|
|
||||||
//engine_i486.so later in this function
|
|
||||||
NET_Sleep_Timeout = (NET_Sleep_t)Sys_GetProcAddress(g_pEngineModule, "NET_Sleep_Timeout");
|
|
||||||
if (NET_Sleep_Timeout != nullptr)
|
|
||||||
Sys_Sleep = Sleep_Net;
|
|
||||||
break;
|
|
||||||
// just in case
|
|
||||||
default:
|
|
||||||
Sys_Sleep = Sleep_Old;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char *fsmodule;
|
|
||||||
if (CommandLine()->CheckParm("-pidfile", &fsmodule) && fsmodule)
|
|
||||||
{
|
|
||||||
FILE *pidFile = fopen(fsmodule, "w");
|
|
||||||
if (pidFile) {
|
|
||||||
fprintf(pidFile, "%i\n", getpid());
|
|
||||||
fclose(pidFile);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
printf("Warning: unable to open pidfile (%s)\n", pPingType);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_pFileSystemModule = Sys_LoadModule(STDIO_FILESYSTEM_LIB);
|
|
||||||
|
|
||||||
// Get FileSystem interface
|
|
||||||
g_FilesystemFactoryFn = Sys_GetFactory(g_pFileSystemModule);
|
|
||||||
if (!g_FilesystemFactoryFn)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
IFileSystem *pFullFileSystem = (IFileSystem *)g_FilesystemFactoryFn(FILESYSTEM_INTERFACE_VERSION, NULL);
|
|
||||||
if (!pFullFileSystem)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
pFullFileSystem->Mount();
|
|
||||||
|
|
||||||
if (!console.Init()) {
|
|
||||||
puts("Failed to initilise console.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
gbAppHasBeenTerminated = false;
|
|
||||||
RunServer();
|
|
||||||
|
|
||||||
if (gpszCvars)
|
|
||||||
free(gpszCvars);
|
|
||||||
|
|
||||||
if (pFullFileSystem)
|
|
||||||
pFullFileSystem->Unmount();
|
|
||||||
|
|
||||||
Sys_UnloadModule(g_pFileSystemModule);
|
|
||||||
|
|
||||||
exit(0);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2,31 +2,41 @@
|
|||||||
|
|
||||||
class CSys: public ISys {
|
class CSys: public ISys {
|
||||||
public:
|
public:
|
||||||
|
CSys();
|
||||||
virtual ~CSys();
|
virtual ~CSys();
|
||||||
|
|
||||||
void Sleep(int msec);
|
void Sleep(int msec) override;
|
||||||
bool GetExecutableName(char *out);
|
bool GetExecutableName(char *out) override;
|
||||||
void ErrorMessage(int level, const char *msg);
|
void ErrorMessage(int level, const char *msg) override;
|
||||||
|
|
||||||
void WriteStatusText(char *szText);
|
void WriteStatusText(char *szText) override;
|
||||||
void UpdateStatus(int force);
|
void UpdateStatus(int force) override;
|
||||||
|
|
||||||
long LoadLibrary(char *lib);
|
long LoadLibrary(char *lib) override;
|
||||||
void FreeLibrary(long library);
|
void FreeLibrary(long library) override;
|
||||||
|
|
||||||
bool CreateConsoleWindow();
|
bool CreateConsoleWindow() override;
|
||||||
void DestroyConsoleWindow();
|
void DestroyConsoleWindow() override;
|
||||||
|
|
||||||
void ConsoleOutput(char *string);
|
void ConsoleOutput(char *string) override;
|
||||||
char *ConsoleInput();
|
char *ConsoleInput() override;
|
||||||
void Printf(char *fmt, ...);
|
void Printf(char *fmt, ...) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
CSys g_Sys;
|
CSys g_Sys;
|
||||||
ISys *sys = &g_Sys;
|
ISys *sys = &g_Sys;
|
||||||
|
|
||||||
|
CSys::CSys()
|
||||||
|
{
|
||||||
|
// Startup winock
|
||||||
|
WORD version = MAKEWORD(2, 0);
|
||||||
|
WSADATA wsaData;
|
||||||
|
WSAStartup(version, &wsaData);
|
||||||
|
}
|
||||||
|
|
||||||
CSys::~CSys()
|
CSys::~CSys()
|
||||||
{
|
{
|
||||||
|
WSACleanup();
|
||||||
sys = nullptr;
|
sys = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,94 +158,51 @@ void CSys::Printf(char *fmt, ...)
|
|||||||
ConsoleOutput(szText);
|
ConsoleOutput(szText);
|
||||||
}
|
}
|
||||||
|
|
||||||
int StartServer()
|
BOOL WINAPI ConsoleCtrlHandler(DWORD CtrlType)
|
||||||
{
|
{
|
||||||
// Startup winock
|
switch (CtrlType) {
|
||||||
WORD version = MAKEWORD(2, 0);
|
case CTRL_C_EVENT:
|
||||||
WSADATA wsaData;
|
case CTRL_BREAK_EVENT:
|
||||||
WSAStartup(version, &wsaData);
|
case CTRL_CLOSE_EVENT:
|
||||||
|
case CTRL_LOGOFF_EVENT:
|
||||||
CommandLine()->CreateCmdLine(GetCommandLine());
|
case CTRL_SHUTDOWN_EVENT:
|
||||||
|
g_bAppHasBeenTerminated = true;
|
||||||
// Load engine
|
return TRUE;
|
||||||
g_pEngineModule = Sys_LoadModule(ENGINE_LIB);
|
default:
|
||||||
if (!g_pEngineModule)
|
break;
|
||||||
{
|
|
||||||
MessageBox(NULL, "Unable to load engine, image is corrupt.", "Half-Life Dedicated Server Error", MB_OK);
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
g_pFileSystemModule = Sys_LoadModule(STDIO_FILESYSTEM_LIB);
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
// Get FileSystem interface
|
bool Sys_SetupConsole()
|
||||||
g_FilesystemFactoryFn = Sys_GetFactory(g_pFileSystemModule);
|
{
|
||||||
if (!g_FilesystemFactoryFn)
|
console.SetColor(FOREGROUND_RED | FOREGROUND_INTENSITY);
|
||||||
return -1;
|
|
||||||
|
|
||||||
IFileSystem *pFullFileSystem = (IFileSystem *)g_FilesystemFactoryFn(FILESYSTEM_INTERFACE_VERSION, NULL);
|
if (!SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE)) {
|
||||||
if (!pFullFileSystem)
|
sys->ErrorMessage(0, "Unable to set control handler.");
|
||||||
return -1;
|
return false;
|
||||||
|
|
||||||
pFullFileSystem->Mount();
|
|
||||||
|
|
||||||
char *pszValue = nullptr;
|
|
||||||
if (CommandLine()->CheckParm("-steam")
|
|
||||||
|| (CommandLine()->CheckParm("-console", &pszValue) && !pszValue)) {
|
|
||||||
g_bVGui = true;
|
|
||||||
StartVGUI();
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
return true;
|
||||||
if (!console.Init()) {
|
}
|
||||||
MessageBox(NULL, "Failed to initialize console.", "Half-Life Dedicated Server Error", MB_OK);
|
|
||||||
return 0;
|
void Sys_PrepareConsoleInput()
|
||||||
|
{
|
||||||
|
MSG msg;
|
||||||
|
while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
|
||||||
|
if (!GetMessage(&msg, NULL, 0, 0)) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sys->CreateConsoleWindow()) {
|
TranslateMessage(&msg);
|
||||||
return 0;
|
DispatchMessage(&msg);
|
||||||
}
|
|
||||||
|
|
||||||
console.SetColor(FOREGROUND_RED | FOREGROUND_INTENSITY);
|
|
||||||
|
|
||||||
if (!SetConsoleCtrlHandler(MyHandlerRoutine, TRUE)) {
|
|
||||||
MessageBox(NULL, "Unable to set control handler", "Half-Life Dedicated Server Error", MB_OK);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gbAppHasBeenTerminated = false;
|
void Sys_InitPingboost()
|
||||||
Load3rdParty();
|
{
|
||||||
|
|
||||||
// TODO: finish me!
|
|
||||||
/*// run vgui
|
|
||||||
if (g_bVGui)
|
|
||||||
{
|
|
||||||
while (VGUIIsInConfig() && VGUIIsRunning())
|
|
||||||
{
|
|
||||||
RunVGUIFrame();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else*/
|
|
||||||
{
|
|
||||||
RunServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gpszCvars)
|
|
||||||
free(gpszCvars);
|
|
||||||
|
|
||||||
if (pFullFileSystem)
|
|
||||||
pFullFileSystem->Unmount();
|
|
||||||
|
|
||||||
Sys_UnloadModule(g_pFileSystemModule);
|
|
||||||
|
|
||||||
if (hDLLThirdParty)
|
|
||||||
{
|
|
||||||
Sys_UnloadModule((CSysModule *)hDLLThirdParty);
|
|
||||||
hDLLThirdParty = 0L;
|
|
||||||
}
|
|
||||||
|
|
||||||
WSACleanup();
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
|
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
|
||||||
@ -243,24 +210,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
if (ShouldLaunchAppViaSteam(lpCmdLine, STDIO_FILESYSTEM_LIB, STDIO_FILESYSTEM_LIB))
|
if (ShouldLaunchAppViaSteam(lpCmdLine, STDIO_FILESYSTEM_LIB, STDIO_FILESYSTEM_LIB))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
auto command = CommandLineToArgvW(GetCommandLineW(), (int *)&lpCmdLine);
|
//auto command = CommandLineToArgvW(GetCommandLineW(), (int *)&lpCmdLine);
|
||||||
auto ret = StartServer();
|
auto ret = StartServer(lpCmdLine);
|
||||||
LocalFree(command);
|
//LocalFree(command);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL WINAPI MyHandlerRoutine(DWORD CtrlType)
|
|
||||||
{
|
|
||||||
switch (CtrlType)
|
|
||||||
{
|
|
||||||
case CTRL_C_EVENT:
|
|
||||||
case CTRL_BREAK_EVENT:
|
|
||||||
case CTRL_CLOSE_EVENT:
|
|
||||||
case CTRL_LOGOFF_EVENT:
|
|
||||||
case CTRL_SHUTDOWN_EVENT:
|
|
||||||
gbAppHasBeenTerminated = true;
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user