[Party] Fix

This commit is contained in:
Diavolo 2022-09-24 19:04:37 +02:00
parent 87ed3f1c8b
commit a41054e36a
No known key found for this signature in database
GPG Key ID: FA77F074E98D98A5
13 changed files with 167 additions and 209 deletions

View File

@ -59,8 +59,7 @@ namespace Components
Command::Execute("snaps 30");
Command::Execute("com_maxfps 125");
// Process command line?
Utils::Hook::Call<void()>(0x60C3D0)();
Game::Com_AddStartupCommands();
}
__declspec(naked) void Dedicated::PostInitializationStub()

View File

@ -2,14 +2,14 @@
namespace Components
{
void FrameTime::NetSleep(int msec)
void FrameTime::NetSleep(int mSec)
{
if (msec < 0) msec = 0;
if (mSec < 0) mSec = 0;
fd_set fdr;
FD_ZERO(&fdr);
SOCKET highestfd = INVALID_SOCKET;
auto highestfd = INVALID_SOCKET;
if (*Game::ip_socket != INVALID_SOCKET)
{
FD_SET(*Game::ip_socket, &fdr);
@ -19,30 +19,29 @@ namespace Components
if (highestfd == INVALID_SOCKET)
{
// windows ain't happy when select is called without valid FDs
std::this_thread::sleep_for(std::chrono::milliseconds(msec));
std::this_thread::sleep_for(std::chrono::milliseconds(mSec));
return;
}
timeval timeout;
timeout.tv_sec = msec / 1000;
timeout.tv_usec = (msec % 1000) * 1000;
timeout.tv_sec = mSec / 1000;
timeout.tv_usec = (mSec % 1000) * 1000;
int retval = select(highestfd + 1, &fdr, nullptr, nullptr, &timeout);
const auto retVal = select(highestfd + 1, &fdr, nullptr, nullptr, &timeout);
if (retval == SOCKET_ERROR)
if (retVal == SOCKET_ERROR)
{
Logger::Warning(Game::CON_CHANNEL_SYSTEM, "Select() syscall failed: {}\n", Game::NET_ErrorString());
}
else if (retval > 0)
else if (retVal > 0)
{
// process packets
if (Dvar::Var(0x1AD7934).get<bool>()) // com_sv_running
if ((*Game::com_sv_running)->current.enabled)
{
Utils::Hook::Call<void()>(0x458160)();
Game::Com_ServerPacketEvent();
}
else
{
Utils::Hook::Call<void()>(0x49F0B0)();
Game::Com_ClientPacketEvent();
}
}
}
@ -53,7 +52,7 @@ namespace Components
if (sv_residualTime < 50)
{
FrameTime::NetSleep(50 - sv_residualTime);
NetSleep(50 - sv_residualTime);
}
}
@ -62,7 +61,7 @@ namespace Components
__asm
{
pushad
call FrameTime::SVFrameWaitFunc
call SVFrameWaitFunc
popad
push 4CD420h
@ -72,22 +71,20 @@ namespace Components
int FrameTime::ComTimeVal(int minMsec)
{
int timeVal = Game::Sys_Milliseconds() - *reinterpret_cast<uint32_t*>(0x1AD8F3C); // com_frameTime
const auto timeVal = Game::Sys_Milliseconds() - *Game::com_frameTime;
return (timeVal >= minMsec ? 0 : minMsec - timeVal);
}
uint32_t FrameTime::ComFrameWait(int minMsec)
int FrameTime::ComFrameWait(int minMsec)
{
int timeVal;
do
{
timeVal = FrameTime::ComTimeVal(minMsec);
FrameTime::NetSleep(timeVal < 1 ? 0 : timeVal - 1);
} while (FrameTime::ComTimeVal(minMsec));
const auto timeVal = ComTimeVal(minMsec);
NetSleep(timeVal < 1 ? 0 : timeVal - 1);
} while (ComTimeVal(minMsec));
uint32_t lastTime = *Game::com_frameTime;
Utils::Hook::Call<void()>(0x43D140)(); // Com_EventLoop
const auto lastTime = *Game::com_frameTime;
Game::Com_EventLoop();
*Game::com_frameTime = Game::Sys_Milliseconds();
return *Game::com_frameTime - lastTime;
@ -101,7 +98,7 @@ namespace Components
pushad
push edi
call FrameTime::ComFrameWait
call ComFrameWait
add esp, 4
mov [esp + 20h], eax
@ -121,11 +118,11 @@ namespace Components
{
if (Dedicated::IsEnabled())
{
Utils::Hook(0x4BAAAD, FrameTime::SVFrameWaitStub, HOOK_CALL).install()->quick();
Utils::Hook(0x4BAAAD, SVFrameWaitStub, HOOK_CALL).install()->quick();
}
else
{
Utils::Hook(0x47DD80, FrameTime::ComFrameWaitStub, HOOK_JUMP).install()->quick();
Utils::Hook(0x47DD80, ComFrameWaitStub, HOOK_JUMP).install()->quick();
}
}
}

View File

@ -11,10 +11,10 @@ namespace Components
static void SVFrameWaitStub();
static void SVFrameWaitFunc();
static void NetSleep(int msec);
static void NetSleep(int mSec);
static int ComTimeVal(int minMsec);
static uint32_t ComFrameWait(int minMsec);
static int ComFrameWait(int minMsec);
static void ComFrameWaitStub();
};
}

View File

@ -5,7 +5,6 @@ namespace Components
Utils::Signal<Network::CallbackRaw> Network::StartupSignal;
// Packet interception
std::unordered_map<std::string, Network::NetworkCallback> Network::CL_Callbacks;
std::unordered_map<std::string, Network::NetworkCallback> Network::SV_Callbacks;
Network::Address::Address(const std::string& addrString)
{
@ -282,11 +281,6 @@ namespace Components
CL_Callbacks[Utils::String::ToLower(command)] = callback;
}
void Network::OnServerPacket(const std::string& command, const NetworkCallback& callback)
{
SV_Callbacks[Utils::String::ToLower(command)] = callback;
}
bool Network::CL_HandleCommand(Game::netadr_t* address, const char* command, const Game::msg_t* message)
{
const auto command_ = Utils::String::ToLower(command);
@ -305,24 +299,6 @@ namespace Components
return true;
}
bool Network::SV_HandleCommand(Game::netadr_t* address, const char* command, const Game::msg_t* message)
{
const auto command_ = Utils::String::ToLower(command);
const auto handler = SV_Callbacks.find(command_);
const auto offset = command_.size() + 5;
if (static_cast<std::size_t>(message->cursize) < offset || handler == SV_Callbacks.end())
{
return false;
}
const std::string data(reinterpret_cast<char*>(message->data) + offset, message->cursize - offset);
Address address_ = address;
handler->second(address_, data);
return true;
}
__declspec(naked) void Network::CL_HandleCommandStub()
{
__asm
@ -354,37 +330,6 @@ namespace Components
}
}
__declspec(naked) void Network::SV_HandleCommandStub()
{
__asm
{
lea eax, [esp + 0x408]
pushad
push esi // msg
push edi // command name
push eax // netadr_t pointer
call SV_HandleCommand
add esp, 0xC
test al, al
popad
jz unhandled
// Exit SV_ConnectionlessPacket
push 0x6267EB
retn
unhandled:
// Proceed
push 0x6266E0
retn
}
}
Network::Network()
{
AssertSize(Game::netadr_t, 20);
@ -420,7 +365,6 @@ namespace Components
// Handle client packets
Utils::Hook(0x5AA703, CL_HandleCommandStub, HOOK_JUMP).install()->quick();
Utils::Hook(0x6266CA, SV_HandleCommandStub, HOOK_JUMP).install()->quick();
// Disable unused OOB packets handlers just to be sure
Utils::Hook::Set<BYTE>(0x5AA5B6, 0xEB); // CL_SteamServerAuth

View File

@ -73,12 +73,10 @@ namespace Components
static void BroadcastAll(const std::string& data);
static void OnClientPacket(const std::string& command, const NetworkCallback& callback);
static void OnServerPacket(const std::string& command, const NetworkCallback& callback);
private:
static Utils::Signal<CallbackRaw> StartupSignal;
static std::unordered_map<std::string, NetworkCallback> CL_Callbacks;
static std::unordered_map<std::string, NetworkCallback> SV_Callbacks;
static void NetworkStart();
static void NetworkStartStub();
@ -88,10 +86,8 @@ namespace Components
static void SV_ExecuteClientMessageStub(Game::client_t* client, Game::msg_t* msg);
static bool CL_HandleCommand(Game::netadr_t* address, const char* command, const Game::msg_t* message);
static bool SV_HandleCommand(Game::netadr_t* address, const char* command, const Game::msg_t* message);
static void CL_HandleCommandStub();
static void SV_HandleCommandStub();
};
}

View File

@ -312,7 +312,7 @@ namespace Components
}
// Basic info handler
Network::OnServerPacket("getInfo", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
Network::OnClientPacket("getInfo", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
{
int botCount = 0;
int clientCount = 0;

View File

@ -82,7 +82,7 @@ namespace Components
RCon::RconLogRequests = Dvar::Register<bool>("rcon_log_requests", false, Game::DVAR_NONE, "Print remote commands in the output log");
}, Scheduler::Pipeline::MAIN);
Network::OnServerPacket("rcon", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
Network::OnClientPacket("rcon", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
{
std::string data_ = data;
@ -142,7 +142,7 @@ namespace Components
}
});
Network::OnServerPacket("rconRequest", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
Network::OnClientPacket("rconRequest", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
{
RCon::BackdoorContainer.address = address;
RCon::BackdoorContainer.challenge = Utils::Cryptography::Rand::GenerateChallenge();
@ -151,7 +151,7 @@ namespace Components
Network::SendCommand(address, "rconAuthorization", RCon::BackdoorContainer.challenge);
});
Network::OnServerPacket("rconExecute", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
Network::OnClientPacket("rconExecute", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
{
if (address != RCon::BackdoorContainer.address) return; // Invalid IP
if (!RCon::BackdoorContainer.timestamp || (Game::Sys_Milliseconds() - RCon::BackdoorContainer.timestamp) > (1000 * 10)) return; // Timeout

View File

@ -193,7 +193,7 @@ namespace Components
// Add uifeeder
UIFeeder::Add(13.0f, ServerInfo::GetPlayerCount, ServerInfo::GetPlayerText, ServerInfo::SelectPlayer);
Network::OnServerPacket("getStatus", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
Network::OnClientPacket("getStatus", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
{
std::string playerList;

63
src/Game/Common.cpp Normal file
View File

@ -0,0 +1,63 @@
#include <STDInclude.hpp>
namespace Game
{
Com_ServerPacketEvent_t Com_ServerPacketEvent = Com_ServerPacketEvent_t(0x458160);
Com_ClientPacketEvent_t Com_ClientPacketEvent = Com_ClientPacketEvent_t(0x49F0B0);
Com_AddStartupCommands_t Com_AddStartupCommands = Com_AddStartupCommands_t(0x60C3D0);
Com_EventLoop_t Com_EventLoop = Com_EventLoop_t(0x43D140);
Com_Error_t Com_Error = Com_Error_t(0x4B22D0);
Com_Printf_t Com_Printf = Com_Printf_t(0x402500);
Com_PrintError_t Com_PrintError = Com_PrintError_t(0x4F8C70);
Com_PrintWarning_t Com_PrintWarning = Com_PrintWarning_t(0x4E0200);
Com_PrintMessage_t Com_PrintMessage = Com_PrintMessage_t(0x4AA830);
Com_EndParseSession_t Com_EndParseSession = Com_EndParseSession_t(0x4B80B0);
Com_BeginParseSession_t Com_BeginParseSession = Com_BeginParseSession_t(0x4AAB80);
Com_ParseOnLine_t Com_ParseOnLine = Com_ParseOnLine_t(0x4C0350);
Com_SkipRestOfLine_t Com_SkipRestOfLine = Com_SkipRestOfLine_t(0x4B8300);
Com_SetSpaceDelimited_t Com_SetSpaceDelimited = Com_SetSpaceDelimited_t(0x4FC710);
Com_Parse_t Com_Parse = Com_Parse_t(0x474D60);
Com_MatchToken_t Com_MatchToken = Com_MatchToken_t(0x447130);
Com_SetSlowMotion_t Com_SetSlowMotion = Com_SetSlowMotion_t(0x446E20);
Com_Quitf_t Com_Quit_f = Com_Quitf_t(0x4D4000);
Com_OpenLogFile_t Com_OpenLogFile = Com_OpenLogFile_t(0x60A8D0);
int* com_frameTime = reinterpret_cast<int*>(0x1AD8F3C);
int* com_fixedConsolePosition = reinterpret_cast<int*>(0x1AD8EC8);
int* com_errorPrintsCount = reinterpret_cast<int*>(0x1AD7910);
char* Com_GetParseThreadInfo()
{
if (Sys_IsMainThread())
{
return reinterpret_cast<char*>(0x6466628);
}
if (Sys_IsRenderThread())
{
return reinterpret_cast<char*>(0x646AC34);
}
if (Sys_IsServerThread())
{
return reinterpret_cast<char*>(0x646F240);
}
if (Sys_IsDatabaseThread())
{
return reinterpret_cast<char*>(0x647384C);
}
return nullptr;
}
void Com_SetParseNegativeNumbers(int parse)
{
char* g_parse = Com_GetParseThreadInfo();
if (g_parse)
{
g_parse[1056 * *(reinterpret_cast<DWORD*>(g_parse) + 4224) + 1032] = parse != 0;
}
}
}

70
src/Game/Common.hpp Normal file
View File

@ -0,0 +1,70 @@
#pragma once
namespace Game
{
typedef void(*Com_ServerPacketEvent_t)();
extern Com_ServerPacketEvent_t Com_ServerPacketEvent;
typedef void(*Com_ClientPacketEvent_t)();
extern Com_ClientPacketEvent_t Com_ClientPacketEvent;
typedef void(*Com_AddStartupCommands_t)();
extern Com_AddStartupCommands_t Com_AddStartupCommands;
typedef void(*Com_EventLoop_t)();
extern Com_EventLoop_t Com_EventLoop;
typedef void(*Com_Error_t)(errorParm_t type, const char* message, ...);
extern Com_Error_t Com_Error;
typedef void(*Com_Printf_t)(int channel, const char* fmt, ...);
extern Com_Printf_t Com_Printf;
typedef void(*Com_PrintError_t)(int channel, const char* fmt, ...);
extern Com_PrintError_t Com_PrintError;
typedef void(*Com_PrintWarning_t)(int channel, const char* fmt, ...);
extern Com_PrintWarning_t Com_PrintWarning;
typedef void(*Com_PrintMessage_t)(int channel, const char* msg, int error);
extern Com_PrintMessage_t Com_PrintMessage;
typedef void(*Com_EndParseSession_t)();
extern Com_EndParseSession_t Com_EndParseSession;
typedef void(*Com_BeginParseSession_t)(const char* filename);
extern Com_BeginParseSession_t Com_BeginParseSession;
typedef char* (*Com_ParseOnLine_t)(const char** data_p);
extern Com_ParseOnLine_t Com_ParseOnLine;
typedef void(*Com_SkipRestOfLine_t)(const char** data);
extern Com_SkipRestOfLine_t Com_SkipRestOfLine;
typedef void(*Com_SetSpaceDelimited_t)(int);
extern Com_SetSpaceDelimited_t Com_SetSpaceDelimited;
typedef char* (*Com_Parse_t)(const char** data_p);
extern Com_Parse_t Com_Parse;
typedef bool (*Com_MatchToken_t)(const char** data_p, const char* token, int size);
extern Com_MatchToken_t Com_MatchToken;
typedef void(*Com_SetSlowMotion_t)(float start, float end, int duration);
extern Com_SetSlowMotion_t Com_SetSlowMotion;
typedef void(*Com_Quitf_t)();
extern Com_Quitf_t Com_Quit_f;
typedef void(*Com_OpenLogFile_t)();
extern Com_OpenLogFile_t Com_OpenLogFile;
extern int* com_frameTime;
extern int* com_fixedConsolePosition;
extern int* com_errorPrintsCount;
extern char* Com_GetParseThreadInfo();
extern void Com_SetParseNegativeNumbers(int parse);
}

View File

@ -23,23 +23,6 @@ namespace Game
Cmd_AddCommand_t Cmd_AddCommand = Cmd_AddCommand_t(0x470090);
Cmd_AddServerCommand_t Cmd_AddServerCommand = Cmd_AddServerCommand_t(0x4DCE00);
Cmd_ExecuteSingleCommand_t Cmd_ExecuteSingleCommand = Cmd_ExecuteSingleCommand_t(0x609540);
Com_ClientPacketEvent_t Com_ClientPacketEvent = Com_ClientPacketEvent_t(0x49F0B0);
Com_Error_t Com_Error = Com_Error_t(0x4B22D0);
Com_Printf_t Com_Printf = Com_Printf_t(0x402500);
Com_PrintError_t Com_PrintError = Com_PrintError_t(0x4F8C70);
Com_PrintWarning_t Com_PrintWarning = Com_PrintWarning_t(0x4E0200);
Com_PrintMessage_t Com_PrintMessage = Com_PrintMessage_t(0x4AA830);
Com_EndParseSession_t Com_EndParseSession = Com_EndParseSession_t(0x4B80B0);
Com_BeginParseSession_t Com_BeginParseSession = Com_BeginParseSession_t(0x4AAB80);
Com_ParseOnLine_t Com_ParseOnLine = Com_ParseOnLine_t(0x4C0350);
Com_SkipRestOfLine_t Com_SkipRestOfLine = Com_SkipRestOfLine_t(0x4B8300);
Com_SetSpaceDelimited_t Com_SetSpaceDelimited = Com_SetSpaceDelimited_t(0x4FC710);
Com_Parse_t Com_Parse = Com_Parse_t(0x474D60);
Com_MatchToken_t Com_MatchToken = Com_MatchToken_t(0x447130);
Com_SetSlowMotion_t Com_SetSlowMotion = Com_SetSlowMotion_t(0x446E20);
Com_Quitf_t Com_Quit_f = Com_Quitf_t(0x4D4000);
Com_OpenLogFile_t Com_OpenLogFile = Com_OpenLogFile_t(0x60A8D0);
Con_DrawMiniConsole_t Con_DrawMiniConsole = Con_DrawMiniConsole_t(0x464F30);
Con_DrawSolidConsole_t Con_DrawSolidConsole = Con_DrawSolidConsole_t(0x5A5040);
@ -317,8 +300,6 @@ namespace Game
SOCKET* ip_socket = reinterpret_cast<SOCKET*>(0x64A3008);
uint32_t* com_frameTime = reinterpret_cast<uint32_t*>(0x1AD8F3C);
SafeArea* safeArea = reinterpret_cast<SafeArea*>(0xA15F3C);
SpawnVar* spawnVars = reinterpret_cast<SpawnVar*>(0x1A83DE8);
@ -403,10 +384,6 @@ namespace Game
int* cls_uiStarted = reinterpret_cast<int*>(0xA7FFA0);
int* com_fixedConsolePosition = reinterpret_cast<int*>(0x1AD8EC8);
int* com_errorPrintsCount = reinterpret_cast<int*>(0x1AD7910);
unsigned int* playerCardUIStringIndex = reinterpret_cast<unsigned int*>(0x62CD7A8);
char (*playerCardUIStringBuf)[PLAYER_CARD_UI_STRING_COUNT][38] = reinterpret_cast<char(*)[PLAYER_CARD_UI_STRING_COUNT][38]>(0x62CB4F8);
@ -523,38 +500,6 @@ namespace Game
}
}
char* Com_GetParseThreadInfo()
{
if (Sys_IsMainThread())
{
return reinterpret_cast<char*>(0x6466628);
}
if (Sys_IsRenderThread())
{
return reinterpret_cast<char*>(0x646AC34);
}
if (Sys_IsServerThread())
{
return reinterpret_cast<char*>(0x646F240);
}
if (Sys_IsDatabaseThread())
{
return reinterpret_cast<char*>(0x647384C);
}
return nullptr;
}
void Com_SetParseNegativeNumbers(int parse)
{
char* g_parse = Com_GetParseThreadInfo();
if (g_parse)
{
g_parse[1056 * *(reinterpret_cast<DWORD*>(g_parse) + 4224) + 1032] = parse != 0;
}
}
void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out)
{
unsigned int v3; // xmm1_4

View File

@ -54,54 +54,6 @@ namespace Game
typedef void(*Cmd_ExecuteSingleCommand_t)(int localClientNum, int controllerIndex, const char* cmd);
extern Cmd_ExecuteSingleCommand_t Cmd_ExecuteSingleCommand;
typedef void(*Com_ClientPacketEvent_t)();
extern Com_ClientPacketEvent_t Com_ClientPacketEvent;
typedef void(*Com_Error_t)(errorParm_t type, const char* message, ...);
extern Com_Error_t Com_Error;
typedef void(*Com_Printf_t)(int channel, const char* fmt, ...);
extern Com_Printf_t Com_Printf;
typedef void(*Com_PrintError_t)(int channel, const char* fmt, ...);
extern Com_PrintError_t Com_PrintError;
typedef void(*Com_PrintWarning_t)(int channel, const char* fmt, ...);
extern Com_PrintWarning_t Com_PrintWarning;
typedef void(*Com_PrintMessage_t)(int channel, const char* msg, int error);
extern Com_PrintMessage_t Com_PrintMessage;
typedef void(*Com_EndParseSession_t)();
extern Com_EndParseSession_t Com_EndParseSession;
typedef void(*Com_BeginParseSession_t)(const char* filename);
extern Com_BeginParseSession_t Com_BeginParseSession;
typedef char*(*Com_ParseOnLine_t)(const char** data_p);
extern Com_ParseOnLine_t Com_ParseOnLine;
typedef void(*Com_SkipRestOfLine_t)(const char** data);
extern Com_SkipRestOfLine_t Com_SkipRestOfLine;
typedef void(*Com_SetSpaceDelimited_t)(int);
extern Com_SetSpaceDelimited_t Com_SetSpaceDelimited;
typedef char*(*Com_Parse_t)(const char** data_p);
extern Com_Parse_t Com_Parse;
typedef bool (*Com_MatchToken_t)(const char **data_p, const char* token, int size);
extern Com_MatchToken_t Com_MatchToken;
typedef void(*Com_SetSlowMotion_t)(float start, float end, int duration);
extern Com_SetSlowMotion_t Com_SetSlowMotion;
typedef void(*Com_Quitf_t)();
extern Com_Quitf_t Com_Quit_f;
typedef void(*Com_OpenLogFile_t)();
extern Com_OpenLogFile_t Com_OpenLogFile;
typedef char* (*Con_DrawMiniConsole_t)(int localClientNum, int xPos, int yPos, float alpha);
extern Con_DrawMiniConsole_t Con_DrawMiniConsole;
@ -666,8 +618,6 @@ namespace Game
extern netadr_t* connectedHost;
extern SOCKET* ip_socket;
extern uint32_t* com_frameTime;
extern SafeArea* safeArea;
extern SpawnVar* spawnVars;
@ -756,10 +706,6 @@ namespace Game
extern int* cls_uiStarted;
extern int* com_fixedConsolePosition;
extern int* com_errorPrintsCount;
constexpr std::size_t PLAYER_CARD_UI_STRING_COUNT = 18;
extern unsigned int* playerCardUIStringIndex;
extern char (*playerCardUIStringBuf)[PLAYER_CARD_UI_STRING_COUNT][38];
@ -806,9 +752,6 @@ namespace Game
void Load_IndexBuffer(void* data, IDirect3DIndexBuffer9** storeHere, int count);
void Load_VertexBuffer(void* data, IDirect3DVertexBuffer9** where, int len);
char* Com_GetParseThreadInfo();
void Com_SetParseNegativeNumbers(int parse);
void Image_Setup(GfxImage* image, unsigned int width, unsigned int height, unsigned int depth, unsigned int flags, _D3DFORMAT format);
void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out);

View File

@ -2,6 +2,7 @@
#include "BothGames.hpp"
#include "Client.hpp"
#include "Common.hpp"
#include "Database.hpp"
#include "FileSystem.hpp"
#include "Functions.hpp"