diff --git a/publish.gradle b/publish.gradle index da624f1..ee78197 100644 --- a/publish.gradle +++ b/publish.gradle @@ -35,20 +35,32 @@ task publishPrepareFiles { } pubRootDir.mkdirs() + project.file('publish/publishRoot/bin/win32/valve/dlls').mkdirs() + project.file('publish/publishRoot/bin/linux32/valve/dlls').mkdirs() // bugfixed binaries - _copyFileToDir('publish/releaseRehldsFixes/swds.dll', 'publish/publishRoot/bin/bugfixed/') - _copyFileToDir('publish/releaseRehldsFixes/swds.pdb', 'publish/publishRoot/bin/bugfixed/') - _copyFile('publish/releaseRehldsFixes/libengine_i486.so', 'publish/publishRoot/bin/bugfixed/engine_i486.so') - - // pure binaries - _copyFileToDir('publish/releaseRehldsNofixes/swds.dll', 'publish/publishRoot/bin/pure/') - _copyFileToDir('publish/releaseRehldsNofixes/swds.pdb', 'publish/publishRoot/bin/pure/') - _copyFile('publish/releaseRehldsNofixes/libengine_i486.so', 'publish/publishRoot/bin/pure/engine_i486.so') + _copyFile('publish/releaseRehldsFixes/swds.dll', 'publish/publishRoot/bin/win32/swds.dll') + _copyFile('publish/releaseRehldsFixes/libengine_i486.so', 'publish/publishRoot/bin/linux32/engine_i486.so') // dedicated binaries - _copyFile('publish/hlds.exe', 'publish/publishRoot/bin/hlds.exe') - _copyFile('publish/hlds_linux', 'publish/publishRoot/bin/hlds_linux') + _copyFile('publish/hlds.exe', 'publish/publishRoot/bin/win32/hlds.exe') + _copyFile('publish/hlds_linux', 'publish/publishRoot/bin/linux32/hlds_linux') + + // HLTV binaries + _copyFile('publish/hltv.exe', 'publish/publishRoot/bin/win32/hltv.exe') + _copyFile('publish/hltv', 'publish/publishRoot/bin/linux32/hltv') + + _copyFile('publish/core.dll', 'publish/publishRoot/bin/win32/core.dll') + _copyFile('publish/libcore.so', 'publish/publishRoot/bin/linux32/core.so') + + _copyFile('publish/proxy.dll', 'publish/publishRoot/bin/win32/proxy.dll') + _copyFile('publish/libproxy.so', 'publish/publishRoot/bin/linux32/proxy.so') + + _copyFile('publish/demoplayer.dll', 'publish/publishRoot/bin/win32/demoplayer.dll') + _copyFile('publish/libdemoplayer.so', 'publish/publishRoot/bin/linux32/demoplayer.so') + + _copyFile('publish/director.dll', 'publish/publishRoot/bin/win32/valve/dlls/director.dll') + _copyFile('publish/libdirector.so', 'publish/publishRoot/bin/linux32/valve/dlls/director.so') // hlsdk project.file('publish/publishRoot/hlsdk').mkdirs() diff --git a/rehlds/HLTV/Console/build.gradle b/rehlds/HLTV/Console/build.gradle new file mode 100644 index 0000000..31d9a2a --- /dev/null +++ b/rehlds/HLTV/Console/build.gradle @@ -0,0 +1,153 @@ +import org.doomedsociety.gradlecpp.cfg.ToolchainConfigUtils +import org.doomedsociety.gradlecpp.msvc.MsvcToolchainConfig +import org.doomedsociety.gradlecpp.gcc.GccToolchainConfig +import org.doomedsociety.gradlecpp.toolchain.icc.Icc +import org.doomedsociety.gradlecpp.toolchain.icc.IccCompilerPlugin +import org.doomedsociety.gradlecpp.GradleCppUtils +import org.gradle.nativeplatform.NativeExecutableSpec +import org.gradle.nativeplatform.NativeExecutableBinarySpec + +apply plugin: 'cpp' +apply plugin: 'windows-resources' +apply plugin: IccCompilerPlugin +apply plugin: GccCompilerPlugin + +List getRcCompileTasks(NativeBinarySpec binary) +{ + def linkTask = GradleCppUtils.getLinkTask(binary) + def res = linkTask.taskDependencies.getDependencies(linkTask).findAll { Task t -> t instanceof WindowsResourceCompile } + return res as List +} + +void setupToolchain(NativeBinarySpec b) { + def cfg = rootProject.createToolchainConfig(b); + cfg.projectInclude(project, '/..', '/../..', '/src', '/../../common', '/../../engine', '/../../public', '/../../public/rehlds'); + cfg.singleDefines 'USE_BREAKPAD_HANDLER', 'HLTV', '_CONSOLE' + + if (cfg instanceof MsvcToolchainConfig) { + cfg.compilerOptions.pchConfig = new MsvcToolchainConfig.PrecompiledHeadersConfig( + enabled: true, + pchHeader: 'precompiled.h', + pchSourceSet: 'hltv_pch' + ); + + cfg.singleDefines('_CRT_SECURE_NO_WARNINGS') + cfg.compilerOptions.args '/Ob2', '/Oi', '/GF', '/GR-' + cfg.extraLibs "user32.lib" + } + else if (cfg instanceof GccToolchainConfig) { + cfg.compilerOptions.pchConfig = new GccToolchainConfig.PrecompilerHeaderOptions( + enabled: true, + pchSourceSet: 'hltv_pch' + ); + + cfg.compilerOptions.languageStandard = 'c++0x' + cfg.defines([ + '_strdup': 'strdup', + '_stricmp': 'strcasecmp', + '_strnicmp': 'strncasecmp', + '_vsnprintf': 'vsnprintf', + '_snprintf': 'snprintf', + ]); + + cfg.compilerOptions.args '-Qoption,cpp,--treat_func_as_string_literal_cpp', '-fno-rtti', '-fno-exceptions' + cfg.extraLibs 'dl' + } + + ToolchainConfigUtils.apply(project, cfg, b); +} + +model { + buildTypes { + release + } + + platforms { + x86 { + architecture "x86" + } + } + + toolChains { + visualCpp(VisualCpp) { + } + icc(Icc) { + } + } + + components { + hltv(NativeExecutableSpec) { + targetPlatform 'x86' + baseName 'hltv' + + sources { + hltv_main(CppSourceSet) { + source { + srcDir "src" + include "**/*.cpp" + exclude "precompiled.cpp" + } + } + hltv_common(CppSourceSet) { + source { + srcDirs "../../common", "../common" + + // common + include "BaseSystemModule.cpp" + include "ObjectList.cpp" + include "TokenLine.cpp" + include "textconsole.cpp" + if (GradleCppUtils.windows) { + include "TextConsoleWin32.cpp" + } + else { + include "TextConsoleUnix.cpp" + } + + // HLTV common + include "random.cpp" + include "common.cpp" + } + } + hltv_engine(CppSourceSet) { + source { + srcDir "../../engine" + include "mem.cpp" + } + } + hltv_pch(CppSourceSet) { + source { + srcDir "src" + include "precompiled.cpp" + } + } + + rc { + source { + srcDir "msvc" + include "hltv.rc" + } + exportedHeaders { + srcDirs "msvc" + } + } + } + + binaries.all { + NativeExecutableBinarySpec b -> project.setupToolchain(b) + } + } + } +} + +task buildFixes { + dependsOn binaries.withType(NativeExecutableBinarySpec).matching { NativeExecutableBinarySpec blib -> + blib.buildable && blib.buildType.name == 'release' + } +} + +task buildRelease { + dependsOn binaries.withType(NativeExecutableBinarySpec).matching { NativeExecutableBinarySpec blib -> + blib.buildable && blib.buildType.name == 'release' + } +} diff --git a/rehlds/HLTV/Console/msvc/Console.sln b/rehlds/HLTV/Console/msvc/Console.sln new file mode 100644 index 0000000..da920d1 --- /dev/null +++ b/rehlds/HLTV/Console/msvc/Console.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Console", "Console.vcxproj", "{D5CAB879-D54F-456F-8592-31D549CFD1D8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D5CAB879-D54F-456F-8592-31D549CFD1D8}.Debug|Win32.ActiveCfg = Debug|Win32 + {D5CAB879-D54F-456F-8592-31D549CFD1D8}.Debug|Win32.Build.0 = Debug|Win32 + {D5CAB879-D54F-456F-8592-31D549CFD1D8}.Release|Win32.ActiveCfg = Release|Win32 + {D5CAB879-D54F-456F-8592-31D549CFD1D8}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/rehlds/HLTV/Console/msvc/Console.vcxproj b/rehlds/HLTV/Console/msvc/Console.vcxproj new file mode 100644 index 0000000..c1bd1ca --- /dev/null +++ b/rehlds/HLTV/Console/msvc/Console.vcxproj @@ -0,0 +1,186 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + + + + + + + + + + + + + + + + + Use + Use + + + Use + Use + + + Use + Use + + + Use + Use + + + Use + Use + + + Use + Use + + + + Use + Use + + + Use + Use + + + Create + Create + + + Use + Use + + + Use + Use + + + + + + + + + + {D5CAB879-D54F-456F-8592-31D549CFD1D8} + Win32Proj + Console + 8.1 + Console + + + + Application + true + v120_xp + v140_xp + MultiByte + + + Application + false + v120_xp + v140_xp + true + MultiByte + + + + + + + + + + + + + + + true + hltv + + + false + hltv + + + + Use + Level3 + Disabled + HLTV;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + $(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;%(AdditionalIncludeDirectories) + precompiled.h + + + Windows + true + false + user32.lib;%(AdditionalDependencies) + + + IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)") + + + Automatic deployment script + + + echo Empty Action + Force build to run Pre-Build event + git.always.run + git.always.run + + + + + Level3 + Use + Disabled + true + true + HLTV;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + $(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;%(AdditionalIncludeDirectories) + precompiled.h + + + Windows + true + true + true + user32.lib;%(AdditionalDependencies) + + + IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)") + + + Automatic deployment script + + + echo Empty Action + Force build to run Pre-Build event + git.always.run + git.always.run + + + + + + \ No newline at end of file diff --git a/rehlds/HLTV/Console/msvc/Console.vcxproj.filters b/rehlds/HLTV/Console/msvc/Console.vcxproj.filters new file mode 100644 index 0000000..0bc41ab --- /dev/null +++ b/rehlds/HLTV/Console/msvc/Console.vcxproj.filters @@ -0,0 +1,106 @@ + + + + + {9111aa50-a4d2-45d9-8e74-ae8045065799} + + + {753dfdd8-0143-4b2f-8da1-0d60981a1388} + + + {2fbfe7d4-1a55-4e00-a1d0-7a0fea11bdc1} + + + {36d6fd93-a7f4-4f53-9a15-9983679abe00} + + + {26672827-46c9-4080-babb-f002c2e71cf8} + + + {6b303ee4-f4a5-4e82-8a4d-a374240de8d0} + + + + + src + + + src + + + game_shared + + + game_shared + + + game_shared + + + HLTV\common + + + + HLTV\common + + + common + + + common + + + common + + + common + + + engine + + + + + src + + + src + + + src + + + HLTV\common + + + HLTV\common + + + common + + + common + + + common + + + common + + + common + + + common + + + engine + + + + + + + + + \ No newline at end of file diff --git a/rehlds/HLTV/Console/msvc/PostBuild.bat b/rehlds/HLTV/Console/msvc/PostBuild.bat new file mode 100644 index 0000000..8583878 --- /dev/null +++ b/rehlds/HLTV/Console/msvc/PostBuild.bat @@ -0,0 +1,39 @@ +@echo OFF +:: +:: Post-build auto-deploy script +:: Create and fill PublishPath.txt file with path to deployment folder +:: I.e. PublishPath.txt should contain one line with a folder path +:: Call it so: +:: IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)") +:: + +SET targetDir=%~1 +SET targetName=%~2 +SET targetExt=%~3 +SET projectDir=%~4 +SET destination= + +IF NOT EXIST "%projectDir%\PublishPath.txt" ( + ECHO No deployment path specified. Create PublishPath.txt near PostBuild.bat with paths on separate lines for auto deployment. + exit /B 0 +) + +FOR /f "tokens=* delims= usebackq" %%a IN ("%projectDir%\PublishPath.txt") DO ( + ECHO Deploying to: %%a + IF NOT "%%a" == "" ( + copy /Y "%targetDir%%targetName%%targetExt%" "%%a" + IF NOT ERRORLEVEL 1 ( + IF EXIST "%targetDir%%targetName%.pdb" ( + copy /Y "%targetDir%%targetName%.pdb" "%%a" + ) + ) ELSE ( + ECHO PostBuild.bat ^(27^) : warning : Can't copy '%targetName%%targetExt%' to deploy path '%%a' + ) + ) +) + +IF "%%a" == "" ( + ECHO No deployment path specified. +) + +exit /B 0 \ No newline at end of file diff --git a/rehlds/HLTV/Console/msvc/hltv.rc b/rehlds/HLTV/Console/msvc/hltv.rc new file mode 100644 index 0000000..fe2b9fd Binary files /dev/null and b/rehlds/HLTV/Console/msvc/hltv.rc differ diff --git a/rehlds/HLTV/Console/msvc/icon.ico b/rehlds/HLTV/Console/msvc/icon.ico new file mode 100644 index 0000000..4abe090 Binary files /dev/null and b/rehlds/HLTV/Console/msvc/icon.ico differ diff --git a/rehlds/HLTV/Console/msvc/resource.h b/rehlds/HLTV/Console/msvc/resource.h new file mode 100644 index 0000000..26bff13 --- /dev/null +++ b/rehlds/HLTV/Console/msvc/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by resource.rc +// +#define IDI_ICON1 107 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 108 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/rehlds/HLTV/Console/src/System.cpp b/rehlds/HLTV/Console/src/System.cpp new file mode 100644 index 0000000..e263c13 --- /dev/null +++ b/rehlds/HLTV/Console/src/System.cpp @@ -0,0 +1,1147 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +System gSystem; + +#ifdef _WIN32 +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) +#else +int main(int argc, char *argv[]) +#endif // _WIN32 +{ +#ifdef _WIN32 + gSystem.BuildCommandLine(lpCmdLine); +#else + gSystem.BuildCommandLine(argc, argv); + _snprintf(g_szEXEName, sizeof(g_szEXEName), "%s", argv[0]); +#endif // _WIN32 + + if (!gSystem.Init(&gSystem, 0, "Console")) + { + gSystem.Sleep(2000); + return 0; + } + + gSystem.RunFrame(0); + gSystem.ShutDown(); + return 0; +} + +char *System::GetBaseDir() +{ + return COM_GetBaseDir(); +} + +void Sys_Printf(char *fmt, ...) +{ + // Dump text to debugging console. + va_list argptr; + static char string[8192]; + + va_start(argptr, fmt); + _vsnprintf(string, sizeof(string), fmt, argptr); + va_end(argptr); + + // Get Current text and append it. + gSystem.Printf("%s", string); +} + +double System::GetTime() +{ + return m_SystemTime; +} + +void System::SetFPS(float fps) +{ + m_MaxFPS = fps > 0 ? (1 / fps) : 0; +} + +void System::RedirectOutput(char *buffer, int maxSize) +{ + m_RedirectBuffer = buffer; + m_RedirectSize = maxSize; + + if (m_RedirectBuffer) { + memset(m_RedirectBuffer, 0, m_RedirectSize); + } +} + +void System::Printf(char *fmt, ...) +{ + va_list argptr; + static char string[8192]; + + va_start(argptr, fmt); + _vsnprintf(string, sizeof(string), fmt, argptr); + va_end(argptr); + + Log(string); + + if (m_RedirectBuffer && strlen(string) + strlen(m_RedirectBuffer) + 1 < m_RedirectSize) { + strcat(m_RedirectBuffer, string); + } + + m_Console.Print(string); +} + +void System::DPrintf(char *fmt, ...) +{ + va_list argptr; + static char string[8192]; + va_start(argptr, fmt); + if (m_Developer) + { + _vsnprintf(string, sizeof(string), fmt, argptr); + Log(string); + + if (m_RedirectBuffer && strlen(string) + strlen(m_RedirectBuffer) + 1 < m_RedirectSize) { + strcat(m_RedirectBuffer, string); + } + + m_Console.Print(string); + } + + va_end(argptr); +} + +void System::FreeFile(unsigned char *fileHandle) +{ + if (!fileHandle) + return; + + free(fileHandle); +} + +bool System::RegisterCommand(char *name, ISystemModule *module, int commandID) +{ + command_t *cmd = (command_t *)m_Commands.GetFirst(); + while (cmd) + { + if (_stricmp(cmd->name, name) == 0) { + Printf("WARNING! System::RegisterCommand: command \"%s\" already exists.\n", name); + return false; + } + + cmd = (command_t *)m_Commands.GetNext(); + } + + cmd = (command_t *)malloc(sizeof(command_t)); + + strcopy(cmd->name, name); + cmd->module = module; + cmd->commandID = commandID; + + m_Commands.Add(cmd); + return true; +} + +void System::ExecuteString(char *commands) +{ + if (!commands || !commands[0]) + return; + + int size = 0; + char singleCmd[256] = ""; + bool quotes = false; + char *p = singleCmd; + char *c = commands; + + COM_RemoveEvilChars(c); + while (true) + { + *p = *c; + + if (++size >= sizeof(singleCmd)) + { + DPrintf("WARNING! System::ExecuteString: Command token too long.\n"); + break; + } + + if (*c == '"') + quotes = !quotes; + + if ((*c != ';' || quotes) && *c) + { + ++p; + } + else + { + *p = '\0'; + + char *cmd = singleCmd; + while (*cmd == ' ') { cmd++; } + + DispatchCommand(cmd); + p = singleCmd; + size = 0; + } + + if (!*c++) + break; + } +} + +void System::Errorf(char *fmt, ...) +{ + va_list argptr; + static char string[1024]; + va_start(argptr, fmt); + _vsnprintf(string, sizeof(string), fmt, argptr); + va_end(argptr); + + Printf("***** FATAL ERROR *****\n"); + Printf("%s", string); + Printf("*** STOPPING SYSTEM ***\n"); + + Stop(); +} + +char *System::CheckParam(char *param) +{ + return m_Parameters.CheckToken(param); +} + +ISystemModule *System::FindModule(char *type, char *name) +{ + if (!type || !type[0]) + return nullptr; + + ISystemModule *module = (ISystemModule *)m_Modules.GetFirst(); + while (module) + { + if (_stricmp(type, module->GetType()) == 0 + && (!name || _stricmp(name, module->GetType()) == 0)) { + return module; + } + + module = (ISystemModule *)m_Modules.GetNext(); + } + + return nullptr; +} + +bool System::AddModule(ISystemModule *module, char *name) +{ + if (!module) + return false; + + if (!module->Init(this, m_SerialCounter, name)) + { + Printf("ERROR! System::AddModule: couldn't initialize module %s.\n", name); + return false; + } + + m_Modules.AddHead(module); + m_SerialCounter++; + + return true; +} + +ISystemModule *System::GetModule(char *interfacename, char *library, char *instancename) +{ + ISystemModule *module = FindModule(interfacename, instancename); + if (module) { + return module; + } + + library_t *lib = GetLibrary(library); + if (!lib) { + return nullptr; + } + + module = (ISystemModule *)lib->createInterfaceFn(interfacename, nullptr); + if (!module) { + DPrintf("ERROR! System::GetModule: interface \"%s\" not found in library %s.\n", interfacename, lib->name); + return nullptr; + } + + AddModule(module, instancename); + return module; +} + +void System::Stop() +{ + m_State = MODULE_DISCONNECTED; +} + +bool System::RemoveModule(ISystemModule *module) +{ + if (!module) { + return true; + } + + module->ShutDown(); + + command_t *cmd = (command_t *)m_Commands.GetFirst(); + while (cmd) + { + if (cmd->module->GetSerial() == module->GetSerial()) { + m_Commands.Remove(cmd); + free(cmd); + } + + cmd = (command_t *)m_Commands.GetNext(); + } + + ISystemModule *mod = (ISystemModule *)m_Modules.GetFirst(); + while (mod) + { + if (mod->GetSerial() == module->GetSerial()) { + m_Modules.Remove(mod); + return true; + } + + mod = (ISystemModule *)m_Modules.GetNext(); + } + + return false; +} + +void System::GetCommandMatches(char *string, ObjectList *pMatchList) +{ + int len = strlen(string); + command_t *cmd = (command_t *)m_Commands.GetFirst(); + while (cmd) + { + if (!strncmp(cmd->name, string, len)) { + pMatchList->AddTail(cmd); + } + + cmd = (command_t *)m_Commands.GetNext(); + } +} + +System::LocalCommandID_s System::m_LocalCmdReg[] = { + { "developer", CMD_ID_DEVELOPER, &System::CMD_Developer }, + { "exec", CMD_ID_EXEC, &System::CMD_Exec }, + { "modules", CMD_ID_MODULES, &System::CMD_Modules }, + { "cmdlist", CMD_ID_CMDLIST, &System::CMD_CmdList }, + { "help", CMD_ID_CMDLIST, &System::CMD_CmdList }, + { "logfile", CMD_ID_LOGFILE, &System::CMD_Logfile }, + { "quit", CMD_ID_QUIT, &System::CMD_Quit }, + { "exit", CMD_ID_QUIT, &System::CMD_Quit }, + { "loadmodule", CMD_ID_LOADMODULE, &System::CMD_LoadModule }, + { "unloadmodule", CMD_ID_UNLOADMODULE, &System::CMD_UnloadModule }, + { "title", CMD_ID_TITLE, &System::CMD_Title }, + { "showcon", CMD_ID_SHOWCON, &System::CMD_ShowCon }, + { "echo", CMD_ID_ECHO, &System::CMD_Echo }, +}; + +bool System::Init(IBaseSystem *system, int serial, char *name) +{ + if (!BaseSystemModule::Init(system, serial, name)) + return false; + + m_State = serial; + m_SerialCounter = serial + 1; + + if (!name) { + SetName("system"); + } + + strcopy(m_BaseDir, GetBaseDir()); + + m_Developer = CheckParam("-dev") ? true : false; + if (!m_Console.Init(this)) + { + Error(); + return false; + } + + m_SystemTime = 0; + m_LastTime = 0; + + if (!InitTimer() || !InitFileSystem()) + return false; + + m_Commands.Init(); + m_Modules.Init(); + m_Libraries.Init(); + + m_LogFile = nullptr; + memset(m_LogFileName, 0, sizeof(m_LogFileName)); + memset(m_Factorylist, 0, sizeof(m_Factorylist)); + + m_RedirectBuffer = nullptr; + m_RedirectSize = 0; + m_Tick = 0; + m_SleepMSecs = 1; + m_VGUIModule = nullptr; + m_VGUIStarted = false; + SeedRandomNumberGenerator(); + + if (CheckParam("-highpriority")) + { + m_SleepMSecs = 0; +#ifdef _WIN32 + SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); +#endif // _WIN32 + } + + auto pszValue = CheckParam("-maxfps"); + SetFPS(pszValue ? (float)atof(pszValue) : 100); + + for (auto& cmd : m_LocalCmdReg) { + RegisterCommand(cmd.name, this, cmd.id); + } + + SetTitle("Console"); + GetModule(PROXY_INTERFACE_VERSION, "proxy"); + SetTitle("HLTV"); + m_State = MODULE_RUNNING; + + DPrintf("System initialized.\n"); + ExecuteCommandLine(); + Printf("Type 'help' for a list of commands.\n"); + + return true; +} + +bool System::StartVGUI() +{ +#ifndef _WIN32 + return false; +#else + /*if (!m_FileSystemModule) { + Printf("System::StartVGUI: filesystem not loaded.\n"); + return false; + } + + if (CheckParam("-novgui")) { + return false; + } + + m_VGUIModule = Sys_LoadModule(VGUI2_LIB); + + m_Factorylist[0] = Sys_GetFactoryThis(); + m_Factorylist[1] = Sys_GetFactory(m_VGUIModule); + m_Factorylist[2] = Sys_GetFactory(m_FileSystemModule); + + if (vgui2::VGuiControls_Init("HLTV", m_Factorylist, ARRAYSIZE(m_Factorylist))) + { + vgui2::filesystem()->AddSearchPath("platform", "PLATFORM"); + vgui2::localize()->AddFile(vgui2::filesystem(), "Resource/platform_%language%.txt"); + vgui2::localize()->AddFile(vgui2::filesystem(), "Resource/vgui_%language%.txt"); + + m_MainPanel = new Panel(nullptr, "MainPanel"); + vgui2::surface()->SetEmbeddedPanel(m_MainPanel->GetVPanel()); + + if (!vgui2::scheme()->LoadSchemeFromFile("platform/Resource/TrackerScheme.res", "DEFAULT")) { + Printf("System::StartVGUI: Could not load TrackerScheme.res\n"); + return false; + } + + // Start vgui + vgui2::ivgui()->Start(); + m_VGUIStarted = true; + Printf("VGUI initialized.\n"); + return true; + } + else + { + Printf("System::StartVGUI: Could not initialize vgui.\n"); + return false; + }*/ + + return false; +#endif // _WIN32 +} + +void System::StopVGUI() +{ + if (!m_VGUIStarted) + return; + + /*vgui2::surface()->Shutdown(); + if (m_MainPanel) { + delete m_MainPanel; + m_MainPanel = nullptr; + } + + Sys_UnloadModule(m_VGUIModule);*/ + Printf("VGUI shutdown.\n"); +} + +void System::SetName(char *newName) +{ + strcopy(m_Name, newName); +} + +void System::RunFrame(double time) +{ + float fps = 0.0f; + float nextConsoleUpdate = 0.0f; + + BaseSystemModule::RunFrame(time); + while ((m_State == MODULE_RUNNING || m_State == MODULE_CONNECTING)) + { + UpdateTime(); + double timeDiff = m_SystemTime - m_LastTime; + if (m_LastTime > 0 && timeDiff <= 0) { + Printf("WARNING! System::RunFrame: system time difference <= 0.\n"); + timeDiff = 0.001; + } + + if (m_MaxFPS > timeDiff) { + Sleep(m_SleepMSecs); + continue; + } + + m_Tick++; + m_LastTime = m_SystemTime; + fps = (float)(0.1 / timeDiff) + fps * 0.9f; + + if (m_SystemTime > nextConsoleUpdate) + { + ExecuteString(GetInput()); + m_Console.SetStatusLine(m_StatusLine); + nextConsoleUpdate = (float)(m_SystemTime * 0.1); + } + + /*if (m_MainPanel) { + vgui2::ivgui()->RunFrame(); + }*/ + + if (m_State == MODULE_CONNECTING) { + Sleep(1); + continue; + } + + ISystemModule *module = (ISystemModule *)m_Modules.GetFirst(); + while (module) + { + if (m_State == MODULE_DISCONNECTED) + break; + + module->RunFrame(m_SystemTime); + module = (ISystemModule *)m_Modules.GetNext(); + } + } +} + +char *System::GetStatusLine() +{ + static char string[80]; + sprintf(string, " Libraries %i, Modules %i, Commands %i.\n", m_Libraries.CountElements(), m_Modules.CountElements(), m_Commands.CountElements()); + return string; +} + +char *System::GetType() +{ + return BASESYSTEM_INTERFACE_VERSION; +} + +IFileSystem *System::GetFileSystem() +{ + return m_FileSystem; +} + +void System::ShutDown() +{ + m_Commands.Clear(true); + + ISystemModule *module = (ISystemModule *)m_Modules.GetFirst(); + while (module) + { + module->ShutDown(); + module = (ISystemModule *)m_Modules.GetNext(); + } + + library_t *lib = (library_t *)m_Libraries.RemoveTail(); + while (lib) + { + if (lib->handle) { + Sys_UnloadModule(lib->handle); + } + free(lib); + lib = (library_t *)m_Libraries.RemoveTail(); + } + + CloseLogFile(); + if (m_FileSystem) { + m_FileSystem->Unmount(); + } + + Sys_UnloadModule(m_FileSystemModule); + StopVGUI(); + + if (m_Developer) { + Sleep(3000); + } + + m_Console.ShutDown(); + m_State = MODULE_DISCONNECTED; +} + +#ifdef _WIN32 + +void System::BuildCommandLine(char *argv) +{ + m_Parameters.SetLine(argv); +} + +#else // _WIN32 + +#define MAX_LINUX_CMDLINE 2048 + +void System::BuildCommandLine(int argc, char **argv) +{ + int len = 0; + char string[MAX_LINUX_CMDLINE]; + + for (int i = 1; i < argc && len < MAX_LINUX_CMDLINE; i++) + { + len += strlen(argv[i]) + 1; + + if (i > 1) { + strcat(string, " "); + } + + strcat(string, argv[i]); + } + + m_Parameters.SetLine(string); +} + +#endif // _WIN32 + +void System::Sleep(int msec) +{ +#ifdef _WIN32 + ::Sleep(msec); +#else + usleep(1000 * msec); +#endif // _WIN32 +} + +bool System::InitFileSystem() +{ + char *filesystemmodule = STDIO_FILESYSTEM_LIB; +#ifdef _WIN32 + if (CheckParam("-steam")) { + filesystemmodule = STEAM_FILESYSTEM_LIB; + } +#endif // _WIN32 + + m_FileSystemModule = Sys_LoadModule(filesystemmodule); + if (!m_FileSystemModule) + { + Errorf("Couldn't load %s.\n", filesystemmodule); + return false; + } + + CreateInterfaceFn filesystemFactory = Sys_GetFactory(m_FileSystemModule); + if (!filesystemFactory) + return false; + + m_FileSystem = (IFileSystem *)filesystemFactory(FILESYSTEM_INTERFACE_VERSION, nullptr); + if (!m_FileSystem) + { + Errorf("Couldn't get IFileSystem from %s.\n", filesystemmodule); + return false; + } + + m_FileSystem->Mount(); + m_FileSystem->AddSearchPath(m_BaseDir, "ROOT"); + + Printf("FileSystem initialized"); + DPrintf(" (%s, %s)", filesystemmodule, m_BaseDir); + Printf(".\n"); + + return true; +} + +bool System::InitTimer() +{ + if (!m_Counter.Init()) { + Errorf("Couldn't get system timer.\n"); + return false; + } + + m_SystemTime = 0; + m_LastTime = 0; + return true; +} + +void System::Error() +{ + Errorf("%s\n", strerror(errno)); +} + +void System::CMD_CmdList(char *cmdLine) +{ + Printf("Registerd console commands:"); + + command_t *cmd = (command_t *)m_Commands.GetFirst(); + while (cmd) + { + Printf("\n %s", cmd->name); + DPrintf(" (%s)", cmd->module->GetName()); + + cmd = (command_t *)m_Commands.GetNext(); + } + + Printf("\n"); +} + +void System::CMD_Title(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) { + Printf("Syntax: title \n"); + return; + } + + m_Console.SetTitle(params.GetToken(1)); +} + +void System::CMD_Echo(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() < 2) { + Printf("Syntax: title \n"); + return; + } + + Printf("%s\n", params.GetRestOfLine(1)); +} + +void System::CMD_Logfile(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + Printf("Syntax: logfile <0|1|filename>\n"); + if (m_LogFile) { + Printf("Console is logged to \"%s\".\n", m_LogFileName); + } + else { + Printf("Logging is off.\n"); + } + + return; + } + + char *param = params.GetToken(1); + switch (param[0]) { + // log enabled + case '1': + { + if (m_LogFile) { + Printf("Console is logged to \"%s\".\n", m_LogFileName); + return; + } + + char filename[MAX_PATH]; + sprintf(filename, "logfile%s.txt", COM_TimeString()); + OpenLogFile(filename); + break; + } + // log disabled + case '0': + CloseLogFile(); + break; + // log enabled with the provided file name. + default: + OpenLogFile(param); + break; + } +} + +void System::CMD_ShowCon(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) { + Printf("Syntax: showcon <0|1>\n"); + return; + } + + char *param = params.GetToken(1); + ShowConsole((param[0] != '0') ? true : false); +} + +void System::CMD_Modules(char *cmdLine) +{ + ISystemModule *module = (ISystemModule *)m_Modules.GetFirst(); + while (module) + { + Printf("Module %s, Interface %s, Version %i\n", + module->GetName(), + module->GetType(), + module->GetVersion()); + + module = (ISystemModule *)m_Modules.GetNext(); + } + + Printf("--- %i modules in total ---\n", m_Modules.CountElements()); +} + +void System::CMD_Exec(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) { + Printf("Syntax: exec \n"); + return; + } + + ExecuteFile(params.GetToken(1)); +} + +void System::CMD_Developer(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) { + Printf("Syntax: developer <0|1>\n"); + Printf("Developer mode is %s.\n", m_Developer ? "on" : "off"); + return; + } + + m_Developer = atoi(params.GetToken(1)) ? true : false; +} + +void System::CMD_Quit(char *cmdLine) +{ + Stop(); +} + +void System::OpenLogFile(char *filename) +{ + CloseLogFile(); + strcopy(m_LogFileName, filename); + + m_LogFile = m_FileSystem->Open(m_LogFileName, "wt"); + if (!m_LogFile) + { + memset(m_LogFileName, 0, sizeof(m_LogFileName)); + Printf("System::OpenLogFile: error while opening logfile.\n"); + } +} + +void System::CloseLogFile() +{ + if (!m_LogFile || !m_FileSystem) + return; + + m_FileSystem->Flush(m_LogFile); + m_FileSystem->Close(m_LogFile); + m_LogFile = nullptr; + + Printf("Console log file closed.\n"); + memset(m_LogFileName, 0, sizeof(m_LogFileName)); +} + +void System::Log(char *string) +{ + if (!m_LogFile) + return; + + m_FileSystem->Write(string, strlen(string), m_LogFile); +} + +void System::ExecuteFile(char *filename) +{ + char line[256]; + FileHandle_t cfgfile; + + cfgfile = m_FileSystem->Open(filename, "rt"); + if (!cfgfile) { + Printf("Couldn't open config file %s.\n", filename); + return; + } + + Printf("Executing file %s.\n", filename); + while ((m_FileSystem->ReadLine(line, sizeof(line) - 1, cfgfile))) + { + char *remark = strstr(line, "//"); + if (remark) { + *remark = '\0'; + } + + ExecuteString(line); + } + + m_FileSystem->Close(cfgfile); +} + +bool System::DispatchCommand(char *command) +{ + if (!command || !command[0]) + return true; + + TokenLine params(command); + command_t *cmd = (command_t *)m_Commands.GetFirst(); + while (cmd) + { + if (_stricmp(cmd->name, params.GetToken(0)) == 0) { + cmd->module->ExecuteCommand(cmd->commandID, command); + return true; + } + + cmd = (command_t *)m_Commands.GetNext(); + } + + Printf("WARNING! System::DispatchCommand: command \"%s\" not registered.\n", command); + return false; +} + +void System::ExecuteCommandLine() +{ + int i = 0; + char string[256]; + char *token = m_Parameters.GetToken(i); + while (token) + { + if (token[0] != '+') { + token = m_Parameters.GetToken(++i); + continue; + } + + memset(string, 0, sizeof(string)); + strcopy(string, token + 1); + + // next iteration + token = m_Parameters.GetToken(++i); + while (token && !(token[0] == '+' || token[0] == '-')) + { + strncat(string, " ", (sizeof(string) - 1) - strlen(string)); + strncat(string, token, (sizeof(string) - 1) - strlen(string)); + + token = m_Parameters.GetToken(++i); + } + + ExecuteString(string); + } +} + +void System::UpdateTime() +{ + m_SystemTime = m_Counter.GetCurTime(); +} + +char *System::GetInput() +{ + return m_Console.GetLine(); +} + +void System::CMD_LoadModule(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() < 2) { + Printf("Syntax: loadmodule [] []\n"); + return; + } + + switch (params.CountToken()) { + case 2: + GetModule(params.GetToken(1), params.GetToken(1)); + break; + case 3: + GetModule(params.GetToken(1), params.GetToken(2)); + break; + default: + GetModule(params.GetToken(1), params.GetToken(2), params.GetToken(3)); + break; + } +} + +void System::CMD_UnloadModule(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() < 2) { + Printf("Syntax: unloadmodule []\n"); + return; + } + + ISystemModule *module = nullptr; + switch (params.CountToken()) { + case 2: + module = FindModule(params.GetToken(1)); + break; + case 3: + module = FindModule(params.GetToken(1), params.GetToken(2)); + break; + } + + if (!module) { + Printf("Module not found.\n"); + return; + } + + RemoveModule(module); +} + +System::library_t *System::GetLibrary(char *name) +{ + char fixedname[MAX_PATH]; + strcopy(fixedname, name); + COM_FixSlashes(fixedname); + + library_t *lib = (library_t *)m_Libraries.GetFirst(); + while (lib) + { + if (_stricmp(lib->name, name) == 0) { + return lib; + } + + lib = (library_t *)m_Libraries.GetNext(); + } + + lib = (library_t *)malloc(sizeof(library_t)); + if (!lib) { + DPrintf("ERROR! System::GetLibrary: out of memory (%s).\n", name); + return nullptr; + } + + _snprintf(lib->name, sizeof(lib->name), "%s." LIBRARY_PREFIX, fixedname); + lib->handle = (CSysModule *)Sys_LoadModule(lib->name); + if (!lib->handle) { + DPrintf("ERROR! System::GetLibrary: coulnd't load library (%s).\n", lib->name); + free(lib); + return nullptr; + } + + lib->createInterfaceFn = (CreateInterfaceFn)Sys_GetFactory(lib->handle); + if (!lib->createInterfaceFn) { + DPrintf("ERROR! System::GetLibrary: coulnd't get object factory(%s).\n", lib->name); + free(lib); + return nullptr; + } + + m_Libraries.Add(lib); + DPrintf("Loaded library %s.\n", lib->name); + + return lib; +} + +unsigned int System::GetTick() +{ + return m_Tick; +} + +void System::ExecuteCommand(int commandID, char *commandLine) +{ + for (auto& cmd : m_LocalCmdReg) + { + if (cmd.pfnCmd && cmd.id == commandID) { + (this->*cmd.pfnCmd)(commandLine); + return; + } + } + + Printf("ERROR! System::ExecuteCommand: unknown command ID %i.\n", commandID); +} + +void System::SetTitle(char *text) +{ + m_Console.SetTitle(text); +} + +bool System::InitVGUI(IVGuiModule *module) +{ + if (!m_VGUIStarted && !StartVGUI()) { + return false; + } + +#ifdef _WIN32 + module->Initialize(m_Factorylist, ARRAYSIZE(m_Factorylist)); + module->PostInitialize(); + //module->SetParent(m_MainPanel->GetVPanel()); +#endif // _WIN32 + + return true; +} + +Panel *System::GetPanel() +{ +#ifdef _WIN32 + if (m_VGUIStarted || StartVGUI()) { + return m_MainPanel; + } +#endif // _WIN32 + + return nullptr; +} + +void System::SetStatusLine(char *text) +{ + strcopy(m_StatusLine, text); +} + +void System::ShowConsole(bool visible) +{ + m_Console.SetVisible(visible); +} + +void System::LogConsole(char *filename) +{ + if (filename) { + OpenLogFile(filename); + return; + } + + CloseLogFile(); +} + +unsigned char *System::LoadFile(const char *name, int *length) +{ + if (!m_FileSystem) { + return nullptr; + } + + if (length) { + *length = 0; + } + + FileHandle_t hFile = m_FileSystem->Open(name, "rb", 0); + if (!hFile) { + return nullptr; + } + + m_FileSystem->Seek(hFile, 0, FILESYSTEM_SEEK_TAIL); + + int len = m_FileSystem->Tell(hFile); + unsigned char *buf = (unsigned char *)malloc(len + 1); + if (!buf) + { + Printf("WARNING! System::LoadFile: not enough space for %s.\n", name); + return nullptr; + } + + buf[len] = '\0'; + m_FileSystem->Seek(hFile, 0, FILESYSTEM_SEEK_HEAD); + m_FileSystem->Read(buf, len, hFile); + m_FileSystem->Close(hFile); + + if (length) { + *length = len; + } + + return buf; +} diff --git a/rehlds/HLTV/Console/src/System.h b/rehlds/HLTV/Console/src/System.h new file mode 100644 index 0000000..75a589c --- /dev/null +++ b/rehlds/HLTV/Console/src/System.h @@ -0,0 +1,188 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "TokenLine.h" +#include "interface.h" +#include "textconsole.h" +#include "BaseSystemModule.h" +#include "ObjectList.h" + +class Panel; +class System: public IBaseSystem, public BaseSystemModule { +public: + System() {} + ~System() {} + + double GetTime(); + unsigned int GetTick(); + void SetFPS(float fps); + void Printf(char *fmt, ...); + void DPrintf(char *fmt, ...); + void RedirectOutput(char *buffer, int maxSize); + IFileSystem *GetFileSystem(); + unsigned char *LoadFile(const char *name, int *length); + void FreeFile(unsigned char *fileHandle); + void SetTitle(char *pszTitle); + void SetStatusLine(char *pszStatus); + void ShowConsole(bool visible); + void LogConsole(char *filename); + bool InitVGUI(IVGuiModule *module); + Panel *GetPanel(); + bool RegisterCommand(char *name, ISystemModule *module, int commandID); + void GetCommandMatches(char *string, ObjectList *pMatchList); + void ExecuteString(char *commands); + void ExecuteFile(char *filename); + void Errorf(char *fmt, ...); + char *CheckParam(char *param); + bool AddModule(ISystemModule *module, char *name); + ISystemModule *GetModule(char *interfacename, char *library, char *instancename = nullptr); + bool RemoveModule(ISystemModule *module); + void Stop(); + bool Init(IBaseSystem *system, int serial, char *name); + void RunFrame(double time); + void ExecuteCommand(int commandID, char *commandLine); + char *GetType(); + char *GetStatusLine(); + void ShutDown(); + char *GetBaseDir(); + +#ifdef _WIN32 + void BuildCommandLine(char *argv); +#else + void BuildCommandLine(int argc, char **argv); +#endif // _WIN32 + void Sleep(int msec); + +protected: + struct command_t { + char name[32]; + int commandID; + ISystemModule *module; + }; + + struct library_t { + char name[MAX_PATH]; + CSysModule *handle; + CreateInterfaceFn createInterfaceFn; + }; + + library_t *GetLibrary(char *name); + ISystemModule *FindModule(char *type, char *name = nullptr); + bool InitTimer(); + bool InitFileSystem(); + void Error(); + void OpenLogFile(char *filename); + void CloseLogFile(); + void Log(char *string); + bool DispatchCommand(char *command); + void ExecuteCommandLine(); + void UpdateTime(); + char *GetInput(); + bool StartVGUI(); + void StopVGUI(); + void SetName(char *newName); + +protected: + enum LocalCommandIDs { + CMD_ID_DEVELOPER = 1, + CMD_ID_EXEC, + CMD_ID_MODULES, + CMD_ID_CMDLIST, + CMD_ID_LOGFILE, + CMD_ID_QUIT, + CMD_ID_LOADMODULE, + CMD_ID_UNLOADMODULE, + CMD_ID_TITLE, + CMD_ID_SHOWCON, + CMD_ID_ECHO + }; + + void CMD_Developer(char *cmdLine); + void CMD_Exec(char *cmdLine); + void CMD_Title(char *cmdLine); + void CMD_Modules(char *cmdLine); + void CMD_CmdList(char *cmdLine); + void CMD_ShowCon(char *cmdLine); + void CMD_Logfile(char *cmdLine); + void CMD_Quit(char *cmdLine); + void CMD_UnloadModule(char *cmdLine); + void CMD_LoadModule(char *cmdLine); + void CMD_Echo(char *cmdLine); + + struct LocalCommandID_s { + char *name; + LocalCommandIDs id; + void (System::*pfnCmd)(char *cmdLine); + }; + static LocalCommandID_s m_LocalCmdReg[]; + +private: + double m_LastTime; + +#ifdef _WIN32 + CTextConsoleWin32 m_Console; +#else + CTextConsoleUnix m_Console; +#endif // _WIN32 + + CCounter m_Counter; + CSysModule *m_FileSystemModule; + IFileSystem *m_FileSystem; + CSysModule *m_VGUIModule; + TokenLine m_Parameters; + + char m_BaseDir[MAX_PATH]; + char m_StatusLine[80]; + + FileHandle_t m_LogFile; + char m_LogFileName[MAX_PATH]; + + ObjectList m_Modules; + ObjectList m_Libraries; + ObjectList m_Commands; + + float m_MaxFPS; + char *m_RedirectBuffer; + unsigned int m_RedirectSize; + bool m_Developer; + + unsigned int m_SerialCounter; + unsigned int m_Tick; + unsigned int m_SleepMSecs; + + CreateInterfaceFn m_Factorylist[3]; + + bool m_VGUIStarted; + Panel *m_MainPanel; +}; + +extern System gSystem; + +void Sys_Printf(char *fmt, ...); diff --git a/rehlds/HLTV/Console/src/precompiled.cpp b/rehlds/HLTV/Console/src/precompiled.cpp new file mode 100644 index 0000000..5f656a4 --- /dev/null +++ b/rehlds/HLTV/Console/src/precompiled.cpp @@ -0,0 +1 @@ +#include "precompiled.h" diff --git a/rehlds/HLTV/Console/src/precompiled.h b/rehlds/HLTV/Console/src/precompiled.h new file mode 100644 index 0000000..19ee736 --- /dev/null +++ b/rehlds/HLTV/Console/src/precompiled.h @@ -0,0 +1,19 @@ +#pragma once + +#include "osconfig.h" +#include "archtypes.h" +#include "mathlib.h" +#include "FileSystem.h" + +#include +#include + +#include "interface.h" + +#include "mem.h" +#include "common.h" +#include "common/common_hltv.h" + +// Console stuff +#include "System.h" +#include "common/random.h" diff --git a/rehlds/HLTV/Console/src/public_amalgamation.cpp b/rehlds/HLTV/Console/src/public_amalgamation.cpp new file mode 100644 index 0000000..1fc308c --- /dev/null +++ b/rehlds/HLTV/Console/src/public_amalgamation.cpp @@ -0,0 +1,3 @@ +#include "precompiled.h" + +#include "interface.cpp" diff --git a/rehlds/HLTV/Core/build.gradle b/rehlds/HLTV/Core/build.gradle new file mode 100644 index 0000000..3b137cc --- /dev/null +++ b/rehlds/HLTV/Core/build.gradle @@ -0,0 +1,154 @@ +import org.doomedsociety.gradlecpp.cfg.ToolchainConfigUtils +import org.doomedsociety.gradlecpp.msvc.MsvcToolchainConfig +import org.doomedsociety.gradlecpp.toolchain.icc.Icc +import org.doomedsociety.gradlecpp.toolchain.icc.IccCompilerPlugin +import org.doomedsociety.gradlecpp.gcc.GccToolchainConfig +import org.gradle.nativeplatform.NativeBinarySpec +import org.gradle.nativeplatform.NativeLibrarySpec +import org.gradle.nativeplatform.toolchain.VisualCpp + +apply plugin: 'cpp' +apply plugin: IccCompilerPlugin +apply plugin: GccCompilerPlugin + +project.ext.dep_bzip2 = project(':dep/bzip2') + +void setupToolchain(NativeBinarySpec b) { + def cfg = rootProject.createToolchainConfig(b); + cfg.projectInclude(project, '/..', '/../..', '/src', '/../../common', '/../../engine', '/../../public', '/../../public/rehlds', '/../../pm_shared'); + cfg.projectInclude(dep_bzip2, '/include') + + cfg.singleDefines 'USE_BREAKPAD_HANDLER', 'HLTV', 'CORE_MODULE' + + if (cfg instanceof MsvcToolchainConfig) { + cfg.compilerOptions.pchConfig = new MsvcToolchainConfig.PrecompiledHeadersConfig( + enabled: true, + pchHeader: 'precompiled.h', + pchSourceSet: 'core_pch' + ); + + cfg.singleDefines('_CRT_SECURE_NO_WARNINGS') + cfg.compilerOptions.args '/Ob2', '/Oi', '/GF' + cfg.extraLibs "ws2_32.lib", "psapi.lib" + } + else if (cfg instanceof GccToolchainConfig) { + cfg.compilerOptions.pchConfig = new GccToolchainConfig.PrecompilerHeaderOptions( + enabled: true, + pchSourceSet: 'core_pch' + ); + + cfg.compilerOptions.languageStandard = 'c++0x' + cfg.defines([ + '_strdup': 'strdup', + '_stricmp': 'strcasecmp', + '_strnicmp': 'strncasecmp', + '_vsnprintf': 'vsnprintf', + '_snprintf': 'snprintf', + ]); + + cfg.compilerOptions.args '-Qoption,cpp,--treat_func_as_string_literal_cpp', '-fno-exceptions' + } + + ToolchainConfigUtils.apply(project, cfg, b); +} + +model { + buildTypes { + release + } + + platforms { + x86 { + architecture "x86" + } + } + + toolChains { + visualCpp(VisualCpp) { + } + icc(Icc) { + } + } + + components { + core(NativeLibrarySpec) { + targetPlatform 'x86' + baseName 'core' + + sources { + core_main(CppSourceSet) { + source { + srcDir "src" + include "**/*.cpp" + exclude "precompiled.cpp" + } + + lib project: ':dep/bzip2', library: 'bzip2', linkage: 'static' + } + + core_common(CppSourceSet) { + source { + srcDirs "../../common", "../common" + + // common + include "BaseSystemModule.cpp" + include "ObjectDictionary.cpp" + include "ObjectList.cpp" + include "TokenLine.cpp" + + // HLTV common + include "BitBuffer.cpp" + include "byteorder.cpp" + include "common.cpp" + include "DemoFile.cpp" + include "DirectorCmd.cpp" + include "InfoString.cpp" + include "mathlib.cpp" + include "md5.cpp" + include "munge.cpp" + include "NetAddress.cpp" + include "NetChannel.cpp" + include "random.cpp" + } + } + + core_engine(CppSourceSet) { + source { + srcDir "../../engine" + include "mem.cpp" + } + } + + core_pch(CppSourceSet) { + source { + srcDir "src" + include "precompiled.cpp" + + lib project: ':dep/bzip2', library: 'bzip2', linkage: 'static' + } + } + } + + binaries.all { + NativeBinarySpec b -> project.setupToolchain(b) + } + } + } +} + +task buildFixes { + dependsOn binaries.withType(SharedLibraryBinarySpec).matching { SharedLibraryBinarySpec blib -> + blib.buildable && blib.buildType.name == 'release' + } +} + +task buildRelease { + dependsOn binaries.withType(SharedLibraryBinarySpec).matching { SharedLibraryBinarySpec blib -> + blib.buildable && blib.buildType.name == 'release' + } +} + +// prevent static lib building +binaries.withType(StaticLibraryBinarySpec) { binary -> + buildable = false +} diff --git a/rehlds/HLTV/Core/msvc/Core.sln b/rehlds/HLTV/Core/msvc/Core.sln new file mode 100644 index 0000000..bd7239b --- /dev/null +++ b/rehlds/HLTV/Core/msvc/Core.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Core", "Core.vcxproj", "{52F752EA-73D1-422D-B805-17EF1FB20E09}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bzip2", "..\..\..\..\dep\bzip2\msvc\bzip2.vcxproj", "{792DF067-9904-4579-99B9-46C17277ADE3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {52F752EA-73D1-422D-B805-17EF1FB20E09}.Debug|Win32.ActiveCfg = Debug|Win32 + {52F752EA-73D1-422D-B805-17EF1FB20E09}.Debug|Win32.Build.0 = Debug|Win32 + {52F752EA-73D1-422D-B805-17EF1FB20E09}.Release|Win32.ActiveCfg = Release|Win32 + {52F752EA-73D1-422D-B805-17EF1FB20E09}.Release|Win32.Build.0 = Release|Win32 + {792DF067-9904-4579-99B9-46C17277ADE3}.Debug|Win32.ActiveCfg = Debug|Win32 + {792DF067-9904-4579-99B9-46C17277ADE3}.Debug|Win32.Build.0 = Debug|Win32 + {792DF067-9904-4579-99B9-46C17277ADE3}.Release|Win32.ActiveCfg = Release|Win32 + {792DF067-9904-4579-99B9-46C17277ADE3}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/rehlds/HLTV/Core/msvc/Core.vcxproj b/rehlds/HLTV/Core/msvc/Core.vcxproj new file mode 100644 index 0000000..644508e --- /dev/null +++ b/rehlds/HLTV/Core/msvc/Core.vcxproj @@ -0,0 +1,251 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {52F752EA-73D1-422D-B805-17EF1FB20E09} + Win32Proj + Core + 8.1 + + + + DynamicLibrary + true + v120_xp + v140_xp + MultiByte + + + DynamicLibrary + false + v120_xp + v140_xp + true + MultiByte + + + + + + + + + + + + + + + true + core + + + false + + + + Use + Level3 + Disabled + HLTV;WIN32;_DEBUG;_WINDOWS;_USRDLL;CORE_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + $(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;$(ProjectDir)\..\..\..\..\dep\bzip2\include;%(AdditionalIncludeDirectories) + precompiled.h + MultiThreadedDebug + true + EnableFastChecks + false + + + Windows + true + psapi.lib;ws2_32.lib;%(AdditionalDependencies) + + + IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)") + + + Automatic deployment script + + + echo Empty Action + + + Force build to run Pre-Build event + + + git.always.run + + + git.always.run + + + + + Level3 + Use + MaxSpeed + true + true + HLTV;WIN32;NDEBUG;_WINDOWS;_USRDLL;CORE_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + $(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;$(ProjectDir)\..\..\..\..\dep\bzip2\include;%(AdditionalIncludeDirectories) + precompiled.h + MultiThreaded + true + + + Windows + true + true + true + psapi.lib;ws2_32.lib;%(AdditionalDependencies) + + + IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)") + + + Automatic deployment script + + + echo Empty Action + + + Force build to run Pre-Build event + + + git.always.run + + + git.always.run + + + + + + + + + + true + true + + + true + true + + + true + true + + + + + + + + + + + + + + + Use + precompiled.h + Use + precompiled.h + + + Use + precompiled.h + Use + precompiled.h + + + Use + precompiled.h + Use + precompiled.h + + + Use + precompiled.h + Use + precompiled.h + + + Create + precompiled.h + Create + precompiled.h + + + + Use + precompiled.h + Use + precompiled.h + + + Use + precompiled.h + Use + precompiled.h + + + + + + + + + + true + true + + + true + true + + + + + + + + + + + + + + + + + + + + + + + + + {792df067-9904-4579-99b9-46c17277ade3} + + + + + + \ No newline at end of file diff --git a/rehlds/HLTV/Core/msvc/Core.vcxproj.filters b/rehlds/HLTV/Core/msvc/Core.vcxproj.filters new file mode 100644 index 0000000..1a7689c --- /dev/null +++ b/rehlds/HLTV/Core/msvc/Core.vcxproj.filters @@ -0,0 +1,192 @@ + + + + + {a2b59fb8-08c7-4528-89af-ade37c2610c8} + + + {2c80c58c-56fb-4bd9-b106-8ed08029173e} + + + {664fe3c6-46ae-4fa5-bf0c-f70873228f5e} + + + {fb482bcd-a131-4e66-be87-c7b0ffe6a4cf} + + + {0997bc0d-a67f-47eb-abc1-3c7ebb128beb} + + + {3a1d44ef-34e6-4921-a2b7-ff9808b3cfad} + + + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + src + + + src\hookers + + + src\hookers + + + common + + + common + + + common + + + common + + + HLTV\common + + + src\hookers + + + engine + + + + + src + + + src + + + src + + + src + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + src + + + src + + + src + + + src\hookers + + + common + + + common + + + common + + + common + + + HLTV\common + + + src\hookers + + + engine + + + \ No newline at end of file diff --git a/rehlds/HLTV/Core/msvc/PostBuild.bat b/rehlds/HLTV/Core/msvc/PostBuild.bat new file mode 100644 index 0000000..8583878 --- /dev/null +++ b/rehlds/HLTV/Core/msvc/PostBuild.bat @@ -0,0 +1,39 @@ +@echo OFF +:: +:: Post-build auto-deploy script +:: Create and fill PublishPath.txt file with path to deployment folder +:: I.e. PublishPath.txt should contain one line with a folder path +:: Call it so: +:: IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)") +:: + +SET targetDir=%~1 +SET targetName=%~2 +SET targetExt=%~3 +SET projectDir=%~4 +SET destination= + +IF NOT EXIST "%projectDir%\PublishPath.txt" ( + ECHO No deployment path specified. Create PublishPath.txt near PostBuild.bat with paths on separate lines for auto deployment. + exit /B 0 +) + +FOR /f "tokens=* delims= usebackq" %%a IN ("%projectDir%\PublishPath.txt") DO ( + ECHO Deploying to: %%a + IF NOT "%%a" == "" ( + copy /Y "%targetDir%%targetName%%targetExt%" "%%a" + IF NOT ERRORLEVEL 1 ( + IF EXIST "%targetDir%%targetName%.pdb" ( + copy /Y "%targetDir%%targetName%.pdb" "%%a" + ) + ) ELSE ( + ECHO PostBuild.bat ^(27^) : warning : Can't copy '%targetName%%targetExt%' to deploy path '%%a' + ) + ) +) + +IF "%%a" == "" ( + ECHO No deployment path specified. +) + +exit /B 0 \ No newline at end of file diff --git a/rehlds/HLTV/Core/src/BSPModel.cpp b/rehlds/HLTV/Core/src/BSPModel.cpp new file mode 100644 index 0000000..8b27804 --- /dev/null +++ b/rehlds/HLTV/Core/src/BSPModel.cpp @@ -0,0 +1,1163 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +void BSPModel::Init(IBaseSystem *system) +{ + m_System = system; + m_visframecount = 0; + m_currentLeaf = nullptr; + m_base = nullptr; + m_wadpath = nullptr; + + memset(&m_model, 0, sizeof(m_model)); + memset(m_novis, 0xFF, sizeof(m_novis)); +} + +bool BSPModel::Load(const char *name, bool minimal) +{ + int length; + unsigned int *buffer = (unsigned int *)m_System->LoadFile(name, &length); + if (!buffer) { + return false; + } + + return LoadFromBuffer(buffer, length, COM_SkipPath((char *)name)); +} + +void BSPModel::LoadLeafs(lump_t *l) +{ + dleaf_t *in; + mleaf_t *out; + int i, j, count, p; + + in = (dleaf_t *)(m_base + l->fileofs); + if (l->filelen % sizeof(*in)) { + m_System->Errorf("BSPModel::LoadLeafs: funny lump size in %s\n", m_model.name); + } + + count = l->filelen / sizeof(*in); + out = (mleaf_t *)Mem_ZeroMalloc(count * sizeof(*out)); + + if (!out) { + m_System->Errorf("BSPModel::LoadLeafs: not enough memory to load leafs from %s\n", m_model.name); + } + + m_model.leafs = out; + m_model.numleafs = count; + + for (i = 0; i < count; i++, in++, out++) + { + for (j = 0; j < 3; j++) + { + out->minmaxs[j] = _LittleShort(in->mins[j]); + out->minmaxs[3 + j] = _LittleShort(in->maxs[j]); + } + + out->contents = _LittleLong(in->contents); + out->firstmarksurface = m_model.marksurfaces + _LittleShort(in->firstmarksurface); + out->nummarksurfaces = _LittleShort(in->nummarksurfaces); + + p = _LittleLong(in->visofs); + if (p == -1) + out->compressed_vis = nullptr; + else + out->compressed_vis = m_model.visdata + p; + + out->efrags = nullptr; + + for (j = 0; j < 4; j++) { + out->ambient_sound_level[j] = in->ambient_level[j]; + } + } +} + +void BSPModel::LoadNodes(lump_t *l) +{ + int i, j, count, p; + dnode_t *in; + mnode_t *out; + + in = (dnode_t *)(m_base + l->fileofs); + if (l->filelen % sizeof(*in)) { + m_System->Errorf("BSPModel::LoadNodes: funny lump size in %s\n", m_model.name); + } + + count = l->filelen / sizeof(*in); + out = (mnode_t *)Mem_ZeroMalloc(count * sizeof(*out)); + if (!out) { + m_System->Errorf("BSPModel::LoadNodes: not enough memory to load nodes from %s\n", m_model.name); + return; + } + + m_model.nodes = out; + m_model.numnodes = count; + + for (i = 0; i < count; i++, in++, out++) + { + for (j = 0; j < 3; j++) + { + out->minmaxs[j] = _LittleShort(in->mins[j]); + out->minmaxs[3 + j] = _LittleShort(in->maxs[j]); + } + + out->plane = m_model.planes + _LittleLong(in->planenum); + out->firstsurface = _LittleShort(in->firstface); + out->numsurfaces = _LittleShort(in->numfaces); + + for (j = 0; j < 2; j++) + { + p = _LittleShort(in->children[j]); + if (p >= 0) + out->children[j] = m_model.nodes + p; + else + out->children[j] = (mnode_t *)(m_model.leafs + (-1 - p)); + } + } + + if (count) { + // sets nodes and leafs + SetParent(m_model.nodes, nullptr); + } +} + +void BSPModel::SetParent(mnode_t *node, mnode_t *parent) +{ + node->parent = parent; + if (node->contents < 0) + return; + + SetParent(node->children[0], node); + SetParent(node->children[1], node); +} + +byte *BSPModel::LeafPVS(mleaf_t *leaf) +{ + if (leaf == m_model.leafs) { + return m_novis; + } + + return DecompressVis(leaf->compressed_vis); +} + +byte *BSPModel::DecompressVis(unsigned char *in) +{ + static unsigned char decompressed[MODEL_MAX_PVS]; + if (in == nullptr) { + return m_novis; + } + + int row = (m_model.numleafs + 7) / 8; + if (row < 0 || row > MODEL_MAX_PVS) { + m_System->Errorf("BSPModel::DecompressVis: oversized m_model.numleafs: %i\n", m_model.numleafs); + } + + DecompressPVS(in, decompressed, row); + return decompressed; +} + +void BSPModel::DecompressPVS(unsigned char *in, unsigned char *decompressed, int byteCount) +{ + int c; + unsigned char *out; + + if (in == nullptr) + { + // Make all visible + memcpy(decompressed, m_novis, byteCount); + return; + } + + out = decompressed; + while (out < decompressed + byteCount) + { + // Non zero is not copmpressed + if (*in) + { + *out++ = *in++; + continue; + } + + c = in[1]; // TODO: Check that input buffer is correct (last byte on the buffer could be zero and we will go out of the buffer - check the model) + in += 2; + + // Prevent buffer overrun + if (c > decompressed + byteCount - out) + { + c = decompressed + byteCount - out; + } + + // Unpack zeros + memset(out, 0, c); + out += c; + } +} + +mleaf_t *BSPModel::PointInLeaf(vec_t *point) +{ + mnode_t *node; + float d; + mplane_t *plane; + + if (!m_model.nodes) { + m_System->Errorf("BSPModel::PointInLeaf: bad model, no leafs.\n"); + return nullptr; + } + + node = m_model.nodes; + while (node->contents >= 0) + { + plane = node->plane; + if (plane->type >= 3u) + d = DotProduct(plane->normal, point) - plane->dist; + else + d = point[plane->type] - plane->dist; + + node = node->children[(d <= 0) ? 1 : 0]; + } + + return (mleaf_t *)node; +} + +void BSPModel::PVSMark(unsigned char *ppvs) +{ + m_visframecount++; + + for (int i = 0; i < m_model.numleafs - 1; i++) + { + if ((1 << (i & 7)) & ppvs[i >> 3]) + { + mnode_t *node = (mnode_t *)&m_model.leafs[i + 1]; + do + { + if (node->visframe == m_visframecount) + break; + + node->visframe = m_visframecount; + node = node->parent; + } + while (node); + } + } +} + +mnode_t *BSPModel::PVSNode(mnode_t *node, vec_t *emins, vec_t *emaxs) +{ + mplane_t *splitplane; + int sides; + mnode_t *splitNode; + + if (node->visframe != m_visframecount) { + return nullptr; + } + + if (node->contents < 0) { + return node->contents != CONTENTS_SOLID ? node : nullptr; + } + + splitplane = node->plane; + if (splitplane->type >= 3) + { + sides = BoxOnPlaneSide(emins, emaxs, splitplane); + } + else + { + if (splitplane->dist > emins[splitplane->type]) + { + if (splitplane->dist < emaxs[splitplane->type]) + sides = 3; + else + sides = 2; + } + else + { + sides = 1; + } + } + + if (sides & 1) + { + splitNode = PVSNode(node->children[0], emins, emaxs); + if (splitNode) + return splitNode; + } + + if (sides & 2) { + return PVSNode(node->children[1], emins, emaxs); + } + + return nullptr; +} + +void BSPModel::LoadPlanes(lump_t *l) +{ + int i, j; + mplane_t *out; + dplane_t *in; + int count; + int bits; + + in = (dplane_t *)(m_base + l->fileofs); + if (l->filelen % sizeof(*in)) { + m_System->Errorf("BSPModel::LoadPlanes: funny lump size in %s\n", m_model.name); + return; + } + + count = l->filelen / sizeof(*in); + out = (mplane_t *)Mem_ZeroMalloc(count * 2 * sizeof(*out)); + if (!out) { + m_System->Errorf("BSPModel::LoadPlanes: not enough memory to load planes from %s\n", m_model.name); + return; + } + + m_model.planes = out; + m_model.numplanes = count; + + for (i = 0; i < count; i++, in++, out++) + { + bits = 0; + for (j = 0; j < 3; j++) + { + out->normal[j] = _LittleFloat(in->normal[j]); + if (out->normal[j] < 0) { + bits |= (1 << j); + } + } + + out->dist = _LittleFloat(in->dist); + out->type = _LittleLong(in->type); + out->signbits = bits; + } +} + +void BSPModel::LoadVisibility(lump_t *l) +{ + if (!l->filelen) + { + m_model.visdata = nullptr; + return; + } + + m_model.visdata = (byte *)Mem_ZeroMalloc(l->filelen); + if (!m_model.visdata) { + m_System->Errorf("BSPModel::LoadPlanes: not enough memory to load planes from %s\n", m_model.name); + return; + } + + memcpy(m_model.visdata, m_base + l->fileofs, l->filelen); +} + +bool BSPModel::IsValid() +{ + return m_model.name[0] != '\0'; +} + +void BSPModel::SetPVS(vec_t *point) +{ + unsigned char *ppvs; + mleaf_t *pleaf = PointInLeaf(point); + + if (m_currentLeaf != pleaf) + { + ppvs = LeafPVS(pleaf); + + PVSMark(ppvs); + m_currentLeaf = pleaf; + } +} + +bool BSPModel::InPVS(vec_t *point) +{ + vec3_t mins, maxs; + for (int i = 0; i < 3; i++) + { + mins[i] = point[i] - 32; + maxs[i] = point[i] + 32; + } + + if (PVSNode(m_model.nodes, mins, maxs)) { + return true; + } + + return false; +} + +void BSPModel::Clear() +{ + #define FREE_FIELD(field) if (field) { free(field); } + + FREE_FIELD(m_model.leafs); + FREE_FIELD(m_model.nodes); + FREE_FIELD(m_model.planes); + FREE_FIELD(m_model.visdata); + FREE_FIELD(m_model.vertexes); + FREE_FIELD(m_model.entities); + FREE_FIELD(m_model.edges); + FREE_FIELD(m_model.lightdata); + FREE_FIELD(m_model.surfedges); + FREE_FIELD(m_model.surfaces); + FREE_FIELD(m_model.marksurfaces); + FREE_FIELD(m_model.clipnodes); + FREE_FIELD(m_model.hulls[0].clipnodes); + FREE_FIELD(m_model.texinfo); + + if (m_model.textures) + { + for (int i = 0; i < m_model.numtextures; ++i) { + FREE_FIELD(m_model.textures[i]); + } + + FREE_FIELD(m_model.textures); + } + + FREE_FIELD(m_wadpath); + + memset(&m_model, 0, sizeof(m_model)); + + m_visframecount = 0; + m_wadpath = nullptr; + m_currentLeaf = nullptr; +} + +void BSPModel::GetDimension(vec_t *min, vec_t *max) +{ + min[0] = MAX_WORLD_SIZE; + min[1] = MAX_WORLD_SIZE; + min[2] = MAX_WORLD_SIZE; + + max[0] = -MAX_WORLD_SIZE; + max[1] = -MAX_WORLD_SIZE; + max[2] = -MAX_WORLD_SIZE; + + for (int i = 0; i < m_model.numvertexes; i++) + { + vec3_t v; + VectorCopy(m_model.vertexes[i].position, v); + + if (v[0] > max[0]) max[0] = v[0]; + if (v[1] > max[1]) max[1] = v[1]; + if (v[2] > max[2]) max[2] = v[2]; + if (v[0] < min[0]) min[0] = v[0]; + if (v[1] < min[1]) min[1] = v[1]; + if (v[2] < min[2]) min[2] = v[2]; + } +} + +void BSPModel::LoadVertexes(lump_t *l) +{ + dvertex_t *in; + mvertex_t *out; + int i, count; + + in = (dvertex_t *)(m_base + l->fileofs); + if (l->filelen % sizeof(*in)) { + m_System->Errorf("BSPModel::LoadVertexes: funny lump size in %s\n", m_model.name); + } + + count = l->filelen / sizeof(*in); + out = (mvertex_t *)Mem_ZeroMalloc(count * sizeof(*out)); + + m_model.vertexes = out; + m_model.numvertexes = count; + + for (i = 0; i < count; i++, in++, out++) + { + out->position[0] = _LittleFloat(in->point[0]); + out->position[1] = _LittleFloat(in->point[1]); + out->position[2] = _LittleFloat(in->point[2]); + } +} + +void BSPModel::MakeHull0() +{ + mnode_t *in, *child; + dclipnode_t *out; + int i, j, count; + hull_t *hull; + + hull = &m_model.hulls[0]; + + in = m_model.nodes; + count = m_model.numnodes; + out = (dclipnode_t *)Mem_ZeroMalloc(count * sizeof(*out)); + + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count - 1; + hull->planes = m_model.planes; + + for (i = 0; i < count; i++, out++, in++) + { + out->planenum = in->plane - m_model.planes; + for (j = 0; j < 2; j++) + { + child = in->children[j]; + if (child->contents < 0) + out->children[j] = child->contents; + else + out->children[j] = child - m_model.nodes; + } + } +} + +bool BSPModel::TraceLine(vec_t *start, vec_t *end, vec_t *impact) +{ + trace_t trace; + memset(&trace, 0, sizeof(trace)); + auto res = RecursiveHullCheck(m_model.hulls, 0, 0, 1, start, end, &trace); + VectorCopy(trace.endpos, impact); + return res; +} + +bool BSPModel::RecursiveHullCheck(hull_t *hull, int num, float p1f, float p2f, vec_t *p1, vec_t *p2, trace_t *trace) +{ + int i; + dclipnode_t *node; + mplane_t *plane; + float t2; + vec3_t mid; + float frac; + float t1; + int side; + float midf; + float pdif = p2f - p1f; + + float DIST_EPSILON = 0.03125f; + + if (num >= 0) + { + if (num < hull->firstclipnode || num > hull->lastclipnode || !hull->planes) { + m_System->Errorf("BSPModel::RecursiveHullCheck: bad node number\n"); + } + + node = &hull->clipnodes[num]; + plane = &hull->planes[hull->clipnodes[num].planenum]; + if (plane->type >= 3) + { + t1 = DotProduct(p1, plane->normal) - plane->dist; + t2 = DotProduct(p2, plane->normal) - plane->dist; + } + else + { + t1 = p1[plane->type] - plane->dist; + t2 = p2[plane->type] - plane->dist; + } + + if (t1 >= 0.0f && t2 >= 0.0f) { + return RecursiveHullCheck(hull, node->children[0], p1f, p2f, p1, p2, trace); + } + + if (t1 >= 0.0f) + { + midf = t1 - DIST_EPSILON; + } + else + { + if (t2 < 0.0f) { + return RecursiveHullCheck(hull, node->children[1], p1f, p2f, p1, p2, trace); + } + + midf = t1 + DIST_EPSILON; + } + + midf = midf / (t1 - t2); + midf = clamp(midf, 0.0f, 1.0f); + + // not a number + if (!IS_NAN(midf)) + { + frac = pdif * midf + p1f; + for (i = 0; i < 3; i++) { + mid[i] = (p2[i] - p1[i]) * midf + p1[i]; + } + side = (t1 < 0.0f) ? 1 : 0; + + if (RecursiveHullCheck(hull, node->children[side], p1f, frac, p1, mid, trace)) + { + if (HullPointContents(hull, node->children[side ^ 1], mid) != CONTENTS_SOLID) { + return RecursiveHullCheck(hull, node->children[side ^ 1], frac, p2f, mid, p2, trace); + } + + if (!trace->allsolid) + { + if (side) + { + VectorSubtract(vec3_origin, plane->normal, trace->plane.normal); + trace->plane.dist = -plane->dist; + } + else + { + VectorCopy(plane->normal, trace->plane.normal); + trace->plane.dist = plane->dist; + } + + while (HullPointContents(hull, hull->firstclipnode, mid) == CONTENTS_SOLID) + { + midf -= 0.1f; + if (midf < 0.0f) + { + trace->fraction = frac; + VectorCopy(mid, trace->endpos); + m_System->Printf("BSPModel::RecursiveHullCheck: backup past 0\n"); + return false; + } + + frac = pdif * midf + p1f; + for (i = 0; i < 3; i++) { + mid[i] = (p2[i] - p1[i]) * midf + p1[i]; + } + } + + trace->fraction = frac; + VectorCopy(mid, trace->endpos); + m_System->Printf("BSPModel::RecursiveHullCheck: backup past 0\n"); + return false; + } + } + } + + return false; + } + + if (num == CONTENTS_SOLID) + { + trace->startsolid = TRUE; + } + else + { + trace->allsolid = FALSE; + if (num == CONTENTS_EMPTY) + { + trace->inopen = TRUE; + } + + else if (num != CONTENTS_TRANSLUCENT) + { + trace->inwater = TRUE; + } + } + + return true; +} + +int BSPModel::HullPointContents(hull_t *hull, int num, const vec_t *point) +{ + float d; + dclipnode_t *node; + mplane_t *plane; + + while (num >= 0) + { + if (num < hull->firstclipnode || num > hull->lastclipnode) { + m_System->Errorf("BSPModel::HullPointContents: bad node number\n"); + } + + node = &hull->clipnodes[num]; + plane = &hull->planes[node->planenum]; + + if (plane->type >= 3) + d = DotProduct(point, plane->normal) - plane->dist; + else + d = point[plane->type] - plane->dist; + + num = node->children[(d < 0) ? 1 : 0]; + } + + return num; +} + +bool BSPModel::LoadFromBuffer(unsigned int *buffer, int length, const char *name) +{ + dheader_t *header; + int i; + + Clear(); + strcopy(m_model.name, name); + + m_System->DPrintf("Loading model: %s (%i kB)\n", name, length / 1024); + m_model.needload = NL_PRESENT; + + // call the apropriate loader + switch (_LittleLong(*(uint32 *)buffer)) + { + case IDPOLYHEADER: // old-format of the model from the quake1 + case IDSPRITEHEADER: + case IDSTUDIOHEADER: + m_System->Printf("ERROR! BSPModel::Load: only .BSP models supported.\n"); + if (buffer) { + m_System->FreeFile((unsigned char *)buffer); + } + return false; + default: + break; + } + + m_model.type = mod_brush; + header = (dheader_t *)buffer; + + i = _LittleLong(header->version); + if (i != HLBSP_VERSION) { + m_System->Errorf("BSPModel::LoadFromBuffer: %s has wrong version number (%i should be %i)\n", m_model.name, i, HLBSP_VERSION); + } + + // swap all the lumps + m_base = (byte *)header; + + for (i = 0; i < sizeof(dheader_t) / 4; i++) { + ((int *)header)[i] = _LittleLong(((int *)header)[i]); + } + + // load into heap + if (!m_IsMinimal) + { + LoadVertexes(&header->lumps[LUMP_VERTEXES]); + LoadEdges(&header->lumps[LUMP_EDGES]); + LoadSurfedges(&header->lumps[LUMP_SURFEDGES]); + LoadEntities(&header->lumps[LUMP_ENTITIES]); + LoadTextures(&header->lumps[LUMP_TEXTURES]); + LoadLighting(&header->lumps[LUMP_LIGHTING]); + } + + LoadPlanes(&header->lumps[LUMP_PLANES]); + if (!m_IsMinimal) + { + LoadTexinfo(&header->lumps[LUMP_TEXINFO]); + LoadFaces(&header->lumps[LUMP_FACES]); + LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]); + } + + LoadVisibility(&header->lumps[LUMP_VISIBILITY]); + LoadLeafs(&header->lumps[LUMP_LEAFS]); + LoadNodes(&header->lumps[LUMP_NODES]); + + if (!m_IsMinimal) + { + LoadClipnodes(&header->lumps[LUMP_CLIPNODES]); + } + + MakeHull0(); + + // regular and alternate animation + m_model.numframes = 2; + m_model.flags = 0; + + m_System->FreeFile((unsigned char *)buffer); + return true; +} + +bool BSPModel::IsMinimal() +{ + return m_IsMinimal; +} + +void BSPModel::LoadEdges(lump_t *l) +{ + dedge_t *in; + medge_t *out; + int i, count; + + in = (dedge_t *)(m_base + l->fileofs); + if (l->filelen % sizeof(*in)) { + m_System->Errorf("BSPModel::LoadEdges funny lump size in %s\n", m_model.name); + } + + count = l->filelen / sizeof(*in); + out = (medge_t *)Mem_ZeroMalloc((count + 1) * sizeof(*out)); + + m_model.edges = out; + m_model.numedges = count; + + for (i = 0; i < count; i++, in++, out++) + { + out->v[0] = (unsigned short)_LittleShort(in->v[0]); + out->v[1] = (unsigned short)_LittleShort(in->v[1]); + } +} + +void BSPModel::LoadSurfedges(lump_t *l) +{ + int i, count; + int *in, *out; + + in = (int *)(m_base + l->fileofs); + if (l->filelen % sizeof(*in)) { + m_System->Errorf("BSPModel::LoadSurfedges; funny lump size in %s\n", m_model.name); + } + + count = l->filelen / sizeof(*in); + out = (int *)Mem_ZeroMalloc(count * sizeof(*out)); + + m_model.surfedges = out; + m_model.numsurfedges = count; + + for (i = 0; i < count; i++) { + out[i] = _LittleLong(in[i]); + } +} + +void BSPModel::LoadEntities(lump_t *l) +{ + if (!l->filelen) { + m_model.entities = nullptr; + return; + } + + m_model.entities = (char *)Mem_ZeroMalloc(l->filelen); + memcpy(m_model.entities, (const void *)(m_base + l->fileofs), l->filelen); + + if (m_model.entities) + { + char *pszInputStream = COM_Parse(m_model.entities); + if (*pszInputStream) + { + while (com_token[0] != '}') + { + if (!strcmp(com_token, "wad")) + { + COM_Parse(pszInputStream); + if (m_wadpath) { + free(m_wadpath); + } + + m_wadpath = _strdup(com_token); + return; + } + + pszInputStream = COM_Parse(pszInputStream); + if (!*pszInputStream) + return; + } + } + } +} + +void BSPModel::LoadLighting(lump_t *l) +{ + if (l->filelen) + { + m_model.lightdata = (color24 *)Mem_ZeroMalloc(l->filelen); + memcpy(m_model.lightdata, (const void *)(m_base + l->fileofs), l->filelen); + } + else + { + m_model.lightdata = nullptr; + } +} + +void BSPModel::LoadFaces(lump_t *l) +{ + dface_t *in; + msurface_t *out; + int i, count, surfnum; + int planenum, side; + + in = (dface_t *)(m_base + l->fileofs); + if (l->filelen % sizeof(*in)) { + m_System->Errorf("BSPModel::LoadFaces: funny lump size in %s\n", m_model.name); + } + + count = l->filelen / sizeof(*in); + out = (msurface_t *)Mem_ZeroMalloc(count * sizeof(*out)); + + m_model.surfaces = out; + m_model.numsurfaces = count; + + for (surfnum = 0; surfnum < count; surfnum++, in++, out++) + { + out->firstedge = _LittleLong(in->firstedge); + out->numedges = _LittleShort(in->numedges); + out->flags = 0; + out->pdecals = nullptr; + + planenum = _LittleShort(in->planenum); + side = _LittleShort(in->side); + if (side) { + out->flags |= SURF_PLANEBACK; + } + + out->plane = m_model.planes + planenum; + out->texinfo = m_model.texinfo + _LittleShort(in->texinfo); + + // lighting info + for (i = 0; i < MAXLIGHTMAPS; i++) { + out->styles[i] = in->styles[i]; + } + + i = _LittleLong(in->lightofs); + if (i == -1) + out->samples = nullptr; + else + out->samples = m_model.lightdata + i; + + if (out->texinfo->texture) + { + // set the drawing flags flag + const char *texName = out->texinfo->texture->name; + if (!strncmp(texName, "sky", 3)) { + out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); + continue; + } + + if (!strncmp(texName, "aaatrigger", 10)) { + out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); + continue; + } + } + } +} + +void BSPModel::LoadMarksurfaces(lump_t *l) +{ + int i, j, count; + short *in; + msurface_t **out; + + in = (short *)(m_base + l->fileofs); + if (l->filelen % sizeof(*in)) { + m_System->Errorf("BSPModel::LoadMarksurfaces: funny lump size in %s\n", m_model.name); + } + + count = l->filelen / sizeof(*in); + out = (msurface_t **)Mem_ZeroMalloc(count * sizeof(*out)); + + m_model.marksurfaces = out; + m_model.nummarksurfaces = count; + + for (i = 0; i < count; i++) + { + j = _LittleShort(in[i]); + if (j >= m_model.numsurfaces) { + m_System->Errorf("BSPModel::LoadMarksurfaces: bad surface number\n"); + } + + out[i] = m_model.surfaces + j; + } +} + +void BSPModel::LoadClipnodes(lump_t *l) +{ + dclipnode_t *in, *out; + int i, count; + hull_t *hull; + + in = (dclipnode_t *)(m_base + l->fileofs); + if (l->filelen % sizeof(*in)) { + m_System->Errorf("BSPModel::LoadClipnodes: funny lump size in %s\n", m_model.name); + } + + count = l->filelen / sizeof(*in); + out = (dclipnode_t *)Mem_ZeroMalloc(count * sizeof(*out)); + + m_model.clipnodes = out; + m_model.numclipnodes = count; + + hull = &m_model.hulls[1]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count - 1; + hull->planes = m_model.planes; + hull->clip_mins[0] = -16; + hull->clip_mins[1] = -16; + hull->clip_mins[2] = -36; + hull->clip_maxs[0] = 16; + hull->clip_maxs[1] = 16; + hull->clip_maxs[2] = 36; + + hull = &m_model.hulls[2]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count - 1; + hull->planes = m_model.planes; + hull->clip_mins[0] = -32; + hull->clip_mins[1] = -32; + hull->clip_mins[2] = -32; + hull->clip_maxs[0] = 32; + hull->clip_maxs[1] = 32; + hull->clip_maxs[2] = 32; + + hull = &m_model.hulls[3]; + hull->clipnodes = out; + hull->firstclipnode = 0; + hull->lastclipnode = count - 1; + hull->planes = m_model.planes; + hull->clip_mins[0] = -16; + hull->clip_mins[1] = -16; + hull->clip_mins[2] = -18; + hull->clip_maxs[0] = 16; + hull->clip_maxs[1] = 16; + hull->clip_maxs[2] = 18; + + for (i = 0; i < count; i++, out++, in++) + { + out->planenum = _LittleLong(in->planenum); + out->children[0] = _LittleShort(in->children[0]); + out->children[1] = _LittleShort(in->children[1]); + } +} + +int BSPModel::TruePointContents(vec_t *point) +{ + hull_t *hull = &m_model.hulls[0]; + if (hull->firstclipnode >= hull->lastclipnode) { + return CONTENTS_EMPTY; + } + + return HullPointContents(hull, hull->firstclipnode, point); +} + +void BSPModel::LoadTexinfo(lump_t *l) +{ + texinfo_t *in; + mtexinfo_t *out; + int i, j, count; + int miptex; + float len1, len2; + + in = (texinfo_t *)(m_base + l->fileofs); + if (l->filelen % sizeof(*in)) { + m_System->Errorf("BSPModel::LoadTexinfo: funny lump size in %s\n", m_model.name); + } + + count = l->filelen / sizeof(*in); + out = (mtexinfo_t *)Mem_ZeroMalloc(count * sizeof(*out)); + + if (!out) { + m_System->Errorf("BSPModel::LoadTexinfo: not enough memory to load planes from %s\n", m_model.name); + return; + } + + m_model.texinfo = out; + m_model.numtexinfo = count; + + for (i = 0; i < count; i++, in++, out++) + { + for (j = 0; j < 4; j++) + { + out->vecs[0][j] = _LittleFloat(in->vecs[0][j]); + out->vecs[1][j] = _LittleFloat(in->vecs[1][j]); + } + + len1 = Length(out->vecs[0]); + len2 = Length(out->vecs[1]); + len1 = (len1 + len2) / 2; + + if (len1 < 0.32) + out->mipadjust = 4; + else if (len1 < 0.49) + out->mipadjust = 3; + else if (len1 < 0.99) + out->mipadjust = 2; + else + out->mipadjust = 1; + + miptex = _LittleLong(in->_miptex); + out->flags = _LittleLong(in->flags); + + if (!m_model.textures) + { + out->texture = nullptr; + out->flags = 0; + } + else + { + if (miptex >= m_model.numtextures) { + m_System->Errorf("BSPModel::LoadTexinfo: miptex >= m_model.numtextures\n"); + return; + } + + out->texture = m_model.textures[miptex]; + if (!out->texture) + { + out->texture = nullptr; // texture not found + out->flags = 0; + } + } + } +} + +void BSPModel::LoadTextures(lump_t *l) +{ + int i, j; + int pixels; + + miptex_t *mt; + texture_t *tx; + dmiptexlump_t *m; + + if (!l->filelen) { + m_model.textures = nullptr; + return; + } + + m = (dmiptexlump_t *)(m_base + l->fileofs); + m->_nummiptex = _LittleLong(m->_nummiptex); + + m_model.numtextures = m->_nummiptex; + m_model.textures = (texture_t **)Mem_ZeroMalloc(4 * m->_nummiptex); + + if (!m_model.textures) { + m_System->Errorf("BSPModel::LoadTextures: not enough memory to load planes from %s\n", m_model.name); + return; + } + + for (i = 0; i < m->_nummiptex; i++) + { + m->dataofs[i] = _LittleLong(m->dataofs[i]); + if (m->dataofs[i] == -1) { + continue; + } + + mt = (miptex_t *)((char *)m + m->dataofs[i]); + for (j = 0; j < MIPLEVELS; j++) { + mt->offsets[j] = _LittleLong(mt->offsets[j]); + } + + mt->width = _LittleLong(mt->width); + mt->height = _LittleLong(mt->height); + if ((mt->width & 0xF) || (mt->height & 0xF)) { + m_System->Errorf("BSPModel::LoadTextures: Texture %s is not 16 aligned\n", mt->name); + return; + } + + pixels = (mt->width * mt->height) / 64; + tx = (texture_t *)Mem_ZeroMalloc(85 * pixels + 66); + + m_model.textures[i] = tx; + memcpy(tx->name, mt->name, sizeof(tx->name)); + + if (strchr(tx->name, '~')) { + tx->name[2] = ' '; + } + + tx->width = mt->width; + tx->height = mt->height; + + m_System->Printf("Texture: %s %i,%i\n", tx->name, tx->width, tx->height); + } +} diff --git a/rehlds/HLTV/Core/src/BSPModel.h b/rehlds/HLTV/Core/src/BSPModel.h new file mode 100644 index 0000000..45ea405 --- /dev/null +++ b/rehlds/HLTV/Core/src/BSPModel.h @@ -0,0 +1,99 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include + +#include "l_studio.h" +#include "edict.h" + +// values for model_t's needload +#define NL_PRESENT 0 +#define NL_NEEDS_LOADED 1 +#define NL_UNREFERENCED 2 + +const int MAX_WORLD_SIZE = 32000; + +class IBaseSystem; +class BSPModel: public IBSPModel { +public: + BSPModel() {} + virtual ~BSPModel() {} + + void Init(IBaseSystem *system); + void Clear(); + bool Load(const char *name, bool minimal); + bool IsValid(); + bool IsMinimal(); + void SetPVS(vec_t *point); + bool InPVS(vec_t *point); + bool TraceLine(vec_t *start, vec_t *end, vec_t *impact); + int TruePointContents(vec_t *point); + +private: + void LoadTextures(lump_t *l); + void LoadEdges(lump_t *l); + bool LoadFromBuffer(unsigned int *buffer, int length, const char *name); + void LoadLeafs(lump_t *l); + void LoadNodes(lump_t *l); + void LoadVisibility(lump_t *l); + void LoadPlanes(lump_t *l); + void LoadVertexes(lump_t *l); + void LoadTexinfo(lump_t *l); + void LoadClipnodes(lump_t *l); + void LoadMarksurfaces(lump_t *l); + void LoadFaces(lump_t *l); + void LoadLighting(lump_t *l); + void LoadEntities(lump_t *l); + void LoadSurfedges(lump_t *l); + int HullPointContents(hull_t *hull, int num, const vec_t *point); + bool RecursiveHullCheck(hull_t *hull, int num, float p1f, float p2f, vec_t *p1, vec_t *p2, trace_t *trace); + void MakeHull0(); + mleaf_t *PointInLeaf(vec_t *point); + void GetDimension(vec_t *min, vec_t *max); + mnode_t *PVSNode(mnode_t *node, vec_t *emins, vec_t *emaxs); + void PVSMark(unsigned char *ppvs); + unsigned char *LeafPVS(mleaf_t *leaf); + void DecompressPVS(unsigned char *in, unsigned char *decompressed, int byteCount); + unsigned char *DecompressVis(unsigned char *in); + void SetParent(mnode_t *node, mnode_t *parent); + +protected: + model_t m_model; + + enum { MODEL_MAX_PVS = 1024 }; + byte m_novis[MODEL_MAX_PVS]; + byte *m_base; + + int m_visframecount; + mleaf_t *m_currentLeaf; + bool m_IsMinimal; + char *m_wadpath; + IBaseSystem *m_System; +}; diff --git a/rehlds/HLTV/Core/src/Delta.cpp b/rehlds/HLTV/Core/src/Delta.cpp new file mode 100644 index 0000000..0c566d0 --- /dev/null +++ b/rehlds/HLTV/Core/src/Delta.cpp @@ -0,0 +1,1514 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +#ifndef HOOK_HLTV + +delta_t *Delta::m_EntityDelta = nullptr; +delta_t *Delta::m_PlayerDelta = nullptr; +delta_t *Delta::m_ClientDelta = nullptr; +delta_t *Delta::m_WeaponDelta = nullptr; +delta_t *Delta::m_EventDelta = nullptr; +delta_t *Delta::m_CustomentityDelta = nullptr; + +#define DELTA_D_DEF(member) #member, offsetof(delta_description_s, member) +#define DELTA_DEF(structname, member) { #member, offsetof(structname, member) } + +delta_description_t Delta::m_MetaDescription[] = +{ + { DT_INTEGER, DELTA_D_DEF(fieldType), 1, 32, 1.0, 1.0, 0, 0, 0 }, + { DT_STRING, DELTA_D_DEF(fieldName), 1, 1, 1.0, 1.0, 0, 0, 0 }, + { DT_INTEGER, DELTA_D_DEF(fieldOffset), 1, 16, 1.0, 1.0, 0, 0, 0 }, + { DT_INTEGER, DELTA_D_DEF(fieldSize), 1, 8, 1.0, 1.0, 0, 0, 0 }, + { DT_INTEGER, DELTA_D_DEF(significant_bits), 1, 8, 1.0, 1.0, 0, 0, 0 }, + { DT_FLOAT, DELTA_D_DEF(premultiply), 1, 32, 4000.0, 1.0, 0, 0, 0 }, + { DT_FLOAT, DELTA_D_DEF(postmultiply), 1, 32, 4000.0, 1.0, 0, 0, 0 }, +}; + +delta_t Delta::m_MetaDelta[] = +{ + { 0, ARRAYSIZE(Delta::m_MetaDescription), "", nullptr, Delta::m_MetaDescription }, +}; + +Delta::delta_definition_t Delta::m_DeltaDataDefinition[] = +{ + DELTA_DEF(delta_description_s, fieldType), + DELTA_DEF(delta_description_s, fieldName), + DELTA_DEF(delta_description_s, fieldOffset), + DELTA_DEF(delta_description_s, fieldSize), + DELTA_DEF(delta_description_s, significant_bits), + DELTA_DEF(delta_description_s, premultiply), + DELTA_DEF(delta_description_s, postmultiply), + DELTA_DEF(delta_description_s, flags), +}; + +Delta::delta_definition_t Delta::m_EventDataDefinition[] = +{ + DELTA_DEF(event_args_s, entindex), + DELTA_DEF(event_args_s, origin[0]), + DELTA_DEF(event_args_s, origin[1]), + DELTA_DEF(event_args_s, origin[2]), + DELTA_DEF(event_args_s, angles[0]), + DELTA_DEF(event_args_s, angles[1]), + DELTA_DEF(event_args_s, angles[2]), + DELTA_DEF(event_args_s, fparam1), + DELTA_DEF(event_args_s, fparam2), + DELTA_DEF(event_args_s, iparam1), + DELTA_DEF(event_args_s, iparam2), + DELTA_DEF(event_args_s, bparam1), + DELTA_DEF(event_args_s, bparam2), + DELTA_DEF(event_args_s, ducking), +}; + +Delta::delta_definition_t Delta::m_EntityDataDefinition[] = +{ + DELTA_DEF(entity_state_s, startpos[0]), + DELTA_DEF(entity_state_s, startpos[1]), + DELTA_DEF(entity_state_s, startpos[2]), + DELTA_DEF(entity_state_s, endpos[0]), + DELTA_DEF(entity_state_s, endpos[1]), + DELTA_DEF(entity_state_s, endpos[2]), + DELTA_DEF(entity_state_s, impacttime), + DELTA_DEF(entity_state_s, starttime), + DELTA_DEF(entity_state_s, origin[0]), + DELTA_DEF(entity_state_s, origin[1]), + DELTA_DEF(entity_state_s, origin[2]), + DELTA_DEF(entity_state_s, angles[0]), + DELTA_DEF(entity_state_s, angles[1]), + DELTA_DEF(entity_state_s, angles[2]), + DELTA_DEF(entity_state_s, modelindex), + DELTA_DEF(entity_state_s, frame), + DELTA_DEF(entity_state_s, movetype), + DELTA_DEF(entity_state_s, colormap), + DELTA_DEF(entity_state_s, skin), + DELTA_DEF(entity_state_s, solid), + DELTA_DEF(entity_state_s, scale), + DELTA_DEF(entity_state_s, effects), + DELTA_DEF(entity_state_s, sequence), + DELTA_DEF(entity_state_s, animtime), + DELTA_DEF(entity_state_s, framerate), + DELTA_DEF(entity_state_s, controller[0]), + DELTA_DEF(entity_state_s, controller[1]), + DELTA_DEF(entity_state_s, controller[2]), + DELTA_DEF(entity_state_s, controller[3]), + DELTA_DEF(entity_state_s, blending[0]), + DELTA_DEF(entity_state_s, blending[1]), + DELTA_DEF(entity_state_s, body), + DELTA_DEF(entity_state_s, owner), + DELTA_DEF(entity_state_s, rendermode), + DELTA_DEF(entity_state_s, renderamt), + DELTA_DEF(entity_state_s, renderfx), + DELTA_DEF(entity_state_s, rendercolor.r), + DELTA_DEF(entity_state_s, rendercolor.g), + DELTA_DEF(entity_state_s, rendercolor.b), + DELTA_DEF(entity_state_s, weaponmodel), + DELTA_DEF(entity_state_s, gaitsequence), + DELTA_DEF(entity_state_s, mins[0]), + DELTA_DEF(entity_state_s, mins[1]), + DELTA_DEF(entity_state_s, mins[2]), + DELTA_DEF(entity_state_s, maxs[0]), + DELTA_DEF(entity_state_s, maxs[1]), + DELTA_DEF(entity_state_s, maxs[2]), + DELTA_DEF(entity_state_s, aiment), + DELTA_DEF(entity_state_s, basevelocity[0]), + DELTA_DEF(entity_state_s, basevelocity[1]), + DELTA_DEF(entity_state_s, basevelocity[2]), + DELTA_DEF(entity_state_s, friction), + DELTA_DEF(entity_state_s, gravity), + DELTA_DEF(entity_state_s, spectator), + DELTA_DEF(entity_state_s, velocity[0]), + DELTA_DEF(entity_state_s, velocity[1]), + DELTA_DEF(entity_state_s, velocity[2]), + DELTA_DEF(entity_state_s, team), + DELTA_DEF(entity_state_s, playerclass), + DELTA_DEF(entity_state_s, health), + DELTA_DEF(entity_state_s, usehull), + DELTA_DEF(entity_state_s, oldbuttons), + DELTA_DEF(entity_state_s, onground), + DELTA_DEF(entity_state_s, iStepLeft), + DELTA_DEF(entity_state_s, flFallVelocity), + DELTA_DEF(entity_state_s, weaponanim), + DELTA_DEF(entity_state_s, eflags), + DELTA_DEF(entity_state_s, iuser1), + DELTA_DEF(entity_state_s, iuser2), + DELTA_DEF(entity_state_s, iuser3), + DELTA_DEF(entity_state_s, iuser4), + DELTA_DEF(entity_state_s, fuser1), + DELTA_DEF(entity_state_s, fuser2), + DELTA_DEF(entity_state_s, fuser3), + DELTA_DEF(entity_state_s, fuser4), + DELTA_DEF(entity_state_s, vuser1[0]), + DELTA_DEF(entity_state_s, vuser1[1]), + DELTA_DEF(entity_state_s, vuser1[2]), + DELTA_DEF(entity_state_s, vuser2[0]), + DELTA_DEF(entity_state_s, vuser2[1]), + DELTA_DEF(entity_state_s, vuser2[2]), + DELTA_DEF(entity_state_s, vuser3[0]), + DELTA_DEF(entity_state_s, vuser3[1]), + DELTA_DEF(entity_state_s, vuser3[2]), + DELTA_DEF(entity_state_s, vuser4[0]), + DELTA_DEF(entity_state_s, vuser4[1]), + DELTA_DEF(entity_state_s, vuser4[2]), +}; + +Delta::delta_definition_t Delta::m_UsercmdDataDefinition[] = +{ + DELTA_DEF(usercmd_s, lerp_msec), + DELTA_DEF(usercmd_s, msec), + DELTA_DEF(usercmd_s, lightlevel), + DELTA_DEF(usercmd_s, viewangles[0]), + DELTA_DEF(usercmd_s, viewangles[1]), + DELTA_DEF(usercmd_s, viewangles[2]), + DELTA_DEF(usercmd_s, buttons), + DELTA_DEF(usercmd_s, forwardmove), + DELTA_DEF(usercmd_s, sidemove), + DELTA_DEF(usercmd_s, upmove), + DELTA_DEF(usercmd_s, impulse), + DELTA_DEF(usercmd_s, weaponselect), + DELTA_DEF(usercmd_s, impact_index), + DELTA_DEF(usercmd_s, impact_position[0]), + DELTA_DEF(usercmd_s, impact_position[1]), + DELTA_DEF(usercmd_s, impact_position[2]), +}; + +Delta::delta_definition_t Delta::m_WeaponDataDefinition[] = +{ + DELTA_DEF(weapon_data_s, m_iId), + DELTA_DEF(weapon_data_s, m_iClip), + DELTA_DEF(weapon_data_s, m_flNextPrimaryAttack), + DELTA_DEF(weapon_data_s, m_flNextSecondaryAttack), + DELTA_DEF(weapon_data_s, m_flTimeWeaponIdle), + DELTA_DEF(weapon_data_s, m_fInReload), + DELTA_DEF(weapon_data_s, m_fInSpecialReload), + DELTA_DEF(weapon_data_s, m_flNextReload), + DELTA_DEF(weapon_data_s, m_flPumpTime), + DELTA_DEF(weapon_data_s, m_fReloadTime), + DELTA_DEF(weapon_data_s, m_fAimedDamage), + DELTA_DEF(weapon_data_s, m_fNextAimBonus), + DELTA_DEF(weapon_data_s, m_fInZoom), + DELTA_DEF(weapon_data_s, m_iWeaponState), + DELTA_DEF(weapon_data_s, iuser1), + DELTA_DEF(weapon_data_s, iuser2), + DELTA_DEF(weapon_data_s, iuser3), + DELTA_DEF(weapon_data_s, iuser4), + DELTA_DEF(weapon_data_s, fuser1), + DELTA_DEF(weapon_data_s, fuser2), + DELTA_DEF(weapon_data_s, fuser3), + DELTA_DEF(weapon_data_s, fuser4), +}; + +Delta::delta_definition_t Delta::m_ClientDataDefinition[] = +{ + DELTA_DEF(clientdata_s, origin[0]), + DELTA_DEF(clientdata_s, origin[1]), + DELTA_DEF(clientdata_s, origin[2]), + DELTA_DEF(clientdata_s, velocity[0]), + DELTA_DEF(clientdata_s, velocity[1]), + DELTA_DEF(clientdata_s, velocity[2]), + DELTA_DEF(clientdata_s, viewmodel), + DELTA_DEF(clientdata_s, punchangle[0]), + DELTA_DEF(clientdata_s, punchangle[1]), + DELTA_DEF(clientdata_s, punchangle[2]), + DELTA_DEF(clientdata_s, flags), + DELTA_DEF(clientdata_s, waterlevel), + DELTA_DEF(clientdata_s, watertype), + DELTA_DEF(clientdata_s, view_ofs[0]), + DELTA_DEF(clientdata_s, view_ofs[1]), + DELTA_DEF(clientdata_s, view_ofs[2]), + DELTA_DEF(clientdata_s, health), + DELTA_DEF(clientdata_s, bInDuck), + DELTA_DEF(clientdata_s, weapons), + DELTA_DEF(clientdata_s, flTimeStepSound), + DELTA_DEF(clientdata_s, flDuckTime), + DELTA_DEF(clientdata_s, flSwimTime), + DELTA_DEF(clientdata_s, waterjumptime), + DELTA_DEF(clientdata_s, maxspeed), + DELTA_DEF(clientdata_s, m_iId), + DELTA_DEF(clientdata_s, ammo_nails), + DELTA_DEF(clientdata_s, ammo_shells), + DELTA_DEF(clientdata_s, ammo_cells), + DELTA_DEF(clientdata_s, ammo_rockets), + DELTA_DEF(clientdata_s, m_flNextAttack), + DELTA_DEF(clientdata_s, physinfo), + DELTA_DEF(clientdata_s, fov), + DELTA_DEF(clientdata_s, weaponanim), + DELTA_DEF(clientdata_s, tfstate), + DELTA_DEF(clientdata_s, pushmsec), + DELTA_DEF(clientdata_s, deadflag), + DELTA_DEF(clientdata_s, iuser1), + DELTA_DEF(clientdata_s, iuser2), + DELTA_DEF(clientdata_s, iuser3), + DELTA_DEF(clientdata_s, iuser4), + DELTA_DEF(clientdata_s, fuser1), + DELTA_DEF(clientdata_s, fuser2), + DELTA_DEF(clientdata_s, fuser3), + DELTA_DEF(clientdata_s, fuser4), + DELTA_DEF(clientdata_s, vuser1[0]), + DELTA_DEF(clientdata_s, vuser1[1]), + DELTA_DEF(clientdata_s, vuser1[2]), + DELTA_DEF(clientdata_s, vuser2[0]), + DELTA_DEF(clientdata_s, vuser2[1]), + DELTA_DEF(clientdata_s, vuser2[2]), + DELTA_DEF(clientdata_s, vuser3[0]), + DELTA_DEF(clientdata_s, vuser3[1]), + DELTA_DEF(clientdata_s, vuser3[2]), + DELTA_DEF(clientdata_s, vuser4[0]), + DELTA_DEF(clientdata_s, vuser4[1]), + DELTA_DEF(clientdata_s, vuser4[2]), +}; + +void Delta::Init(IBaseSystem *system) +{ + m_Defs = nullptr; + m_Encoders = nullptr; + m_DeltaRegistry = nullptr; + + m_System = system; + + AddDefinition("clientdata_t", m_ClientDataDefinition, ARRAYSIZE(m_ClientDataDefinition)); + AddDefinition("weapon_data_t", m_WeaponDataDefinition, ARRAYSIZE(m_WeaponDataDefinition)); + AddDefinition("usercmd_t", m_UsercmdDataDefinition, ARRAYSIZE(m_UsercmdDataDefinition)); + AddDefinition("entity_state_t", m_EntityDataDefinition, ARRAYSIZE(m_EntityDataDefinition)); + AddDefinition("entity_state_player_t", m_EntityDataDefinition, ARRAYSIZE(m_EntityDataDefinition)); + AddDefinition("custom_entity_state_t", m_EntityDataDefinition, ARRAYSIZE(m_EntityDataDefinition)); + AddDefinition("event_t", m_EventDataDefinition, ARRAYSIZE(m_EventDataDefinition)); + + RegisterDescription("clientdata_t"); + RegisterDescription("entity_state_t"); + RegisterDescription("entity_state_player_t"); + RegisterDescription("custom_entity_state_t"); + RegisterDescription("usercmd_t"); + RegisterDescription("weapon_data_t"); + RegisterDescription("event_t"); + + m_LargeTime_Buffers = false; +} + +void Delta::Shutdown() +{ + ClearEncoders(); + ClearDefinitions(); + ClearRegistrations(); +} + +void Delta::UpdateDescriptions() +{ + m_PlayerDelta = *LookupRegistration("entity_state_player_t"); + m_EntityDelta = *LookupRegistration("entity_state_t"); + m_CustomentityDelta = *LookupRegistration("custom_entity_state_t"); + m_ClientDelta = *LookupRegistration("clientdata_t"); + m_WeaponDelta = *LookupRegistration("weapon_data_t"); + m_EventDelta = *LookupRegistration("event_t"); +} + +delta_description_t *Delta::FindField(delta_t *pFields, const char *pszField) +{ + for (int i = 0; i < pFields->fieldCount; i++) + { + if (_stricmp(pFields->pdd[i].fieldName, pszField) == 0) { + return &pFields->pdd[i]; + } + } + + m_System->Printf("Delta::FindField: Warning, couldn't find %s\n", pszField); + return nullptr; +} + +delta_t **Delta::LookupRegistration(char *name) +{ + delta_registry_t *delta = m_DeltaRegistry; + while (delta) + { + if (_stricmp(delta->name, name) == 0) { + return &delta->pdesc; + } + + delta = delta->next; + } + + return nullptr; +} + +int Delta::FindFieldIndex(delta_t *pFields, const char *fieldname) +{ + for (int i = 0; i < pFields->fieldCount; i++) + { + if (_stricmp(pFields->pdd[i].fieldName, fieldname) == 0) { + return i; + } + } + + m_System->Printf("Delta::FindFieldIndex: Warning, couldn't find %s\n", fieldname); + return -1; +} + +void Delta::SetField(delta_t *pFields, const char *fieldname) +{ + delta_description_t *pTest = FindField(pFields, fieldname); + if (pTest) { + pTest->flags |= FDT_MARK; + } +} + +void Delta::UnsetField(delta_t *pFields, const char *fieldname) +{ + delta_description_t *pTest = FindField(pFields, fieldname); + if (pTest) { + pTest->flags &= ~FDT_MARK; + } +} + +void Delta::SetFieldByIndex(delta_t *pFields, int fieldNumber) +{ + pFields->pdd[fieldNumber].flags |= FDT_MARK; +} + +void Delta::UnsetFieldByIndex(delta_t *pFields, int fieldNumber) +{ + pFields->pdd[fieldNumber].flags &= ~FDT_MARK; +} + +void Delta::ClearFlags(delta_t *pFields) +{ + for (int i = 0; i < pFields->fieldCount; i++) { + pFields->pdd[i].flags = 0; + } +} + +int Delta::CountSendFields(delta_t *pFields) +{ + int i, c; + for (i = 0, c = 0; i < pFields->fieldCount; i++) + { + auto pitem = &pFields->pdd[i]; + if (pitem->flags & FDT_MARK) { + pitem->stats.sendcount++; + c++; + } + } + + return c; +} + +void Delta::MarkSendFields(unsigned char *from, unsigned char *to, delta_t *pFields) +{ + int i; + char *st1, *st2; + delta_description_t *pTest; + int fieldType; + int fieldCount = pFields->fieldCount; + + for (i = 0, pTest = pFields->pdd; i < fieldCount; i++, pTest++) + { + fieldType = pTest->fieldType & ~DT_SIGNED; + switch (fieldType) + { + case DT_BYTE: + if (from[pTest->fieldOffset] != to[pTest->fieldOffset]) + pTest->flags |= FDT_MARK; + break; + case DT_SHORT: + if (*(uint16 *)&from[pTest->fieldOffset] != *(uint16 *)&to[pTest->fieldOffset]) + pTest->flags |= FDT_MARK; + break; + case DT_FLOAT: + case DT_INTEGER: + case DT_ANGLE: + if (*(uint32 *)&from[pTest->fieldOffset] != *(uint32 *)&to[pTest->fieldOffset]) + pTest->flags |= FDT_MARK; + break; + case DT_TIMEWINDOW_8: + case DT_TIMEWINDOW_BIG: + if (*(uint32 *)&from[pTest->fieldOffset] != *(uint32 *)&to[pTest->fieldOffset]) + pTest->flags |= FDT_MARK; + break; + case DT_STRING: + st1 = (char *)&from[pTest->fieldOffset]; + st2 = (char *)&to[pTest->fieldOffset]; + + // Not sure why it is case insensitive, but it looks so + if (!(!*st1 && !*st2 || *st1 && *st2 && !_stricmp(st1, st2))) { + pTest->flags |= FDT_MARK; + } + break; + default: + m_System->Printf("Delta::MarkSendFields: Bad field type %i\n", fieldType); + break; + } + } + + if (pFields->conditionalencode) { + pFields->conditionalencode(pFields, from, to); + } +} + +void Delta::SetSendFlagBits(delta_t *pFields, int *bits, int *bytecount) +{ + int i; + int lastbit = -1; + int fieldCount = pFields->fieldCount; + + memset(bits, 0, 8); + + for (i = fieldCount - 1; i >= 0; i--) + { + if (pFields->pdd[i].flags & FDT_MARK) + { + if (lastbit == -1) { + lastbit = i; + } + + bits[i > 31 ? 1 : 0] |= 1 << (i & 0x1f); + } + } + + // fix for bad bytecount when no fields are marked + if (lastbit == -1) { + *bytecount = 0; + return; + } + + *bytecount = (lastbit >> 3) + 1; +} + +void Delta::WriteMarkedFields(BitBuffer *stream, unsigned char *from, unsigned char *to, delta_t *pFields) +{ + int i; + delta_description_t *pTest; + int fieldSign; + int fieldType; + + float f2; + int fieldCount = pFields->fieldCount; + + for (i = 0, pTest = pFields->pdd; i < fieldCount; i++, pTest++) + { + if (!(pTest->flags & FDT_MARK)) + continue; + + fieldSign = pTest->fieldType & DT_SIGNED; + fieldType = pTest->fieldType & ~DT_SIGNED; + switch (fieldType) + { + case DT_BYTE: + if (fieldSign) + { + int8 si8 = *(int8 *)&to[pTest->fieldOffset]; + si8 = (int8)((double)si8 * pTest->premultiply); + stream->WriteSBits(si8, pTest->significant_bits); + } + else + { + uint8 i8 = *(uint8 *)&to[pTest->fieldOffset]; + i8 = (uint8)((double)i8 * pTest->premultiply); + stream->WriteBits(i8, pTest->significant_bits); + } + break; + case DT_SHORT: + if (fieldSign) + { + int16 si16 = *(int16 *)&to[pTest->fieldOffset]; + si16 = (int16)((double)si16 * pTest->premultiply); + stream->WriteSBits(si16, pTest->significant_bits); + } + else + { + uint16 i16 = *(uint16 *)&to[pTest->fieldOffset]; + i16 = (uint16)((double)i16 * pTest->premultiply); + stream->WriteBits(i16, pTest->significant_bits); + } + break; + case DT_FLOAT: + { + double val = (double)(*(float *)&to[pTest->fieldOffset]) * pTest->premultiply; + if (fieldSign) + { + stream->WriteSBits((int32)val, pTest->significant_bits); + } + else + { + stream->WriteBits((uint32)val, pTest->significant_bits); + } + break; + } + case DT_INTEGER: + { + if (fieldSign) + { + int32 signedInt = *(int32 *)&to[pTest->fieldOffset]; + if (pTest->premultiply < 0.9999 || pTest->premultiply > 1.0001) { + signedInt = (int32)((double)signedInt * pTest->premultiply); + } + + stream->WriteSBits(signedInt, pTest->significant_bits); + } + else + { + uint32 unsignedInt = *(uint32 *)&to[pTest->fieldOffset]; + if (pTest->premultiply < 0.9999 || pTest->premultiply > 1.0001) { + unsignedInt = (uint32)((double)unsignedInt * pTest->premultiply); + } + + stream->WriteBits(unsignedInt, pTest->significant_bits); + } + break; + } + case DT_ANGLE: + f2 = *(float *)&to[pTest->fieldOffset]; + stream->WriteBitAngle(f2, pTest->significant_bits); + break; + case DT_TIMEWINDOW_8: + { + f2 = *(float *)&to[pTest->fieldOffset]; + if (m_LargeTime_Buffers) { + stream->WriteFloat(f2); + } else { + int32 twVal = (int)(m_Time * 100.0) - (int)(f2 * 100.0); + stream->WriteSBits(twVal, 8); + } + break; + } + case DT_TIMEWINDOW_BIG: + { + f2 = *(float *)&to[pTest->fieldOffset]; + if (m_LargeTime_Buffers) { + stream->WriteFloat(f2); + } else { + int32 twVal = (int)(m_Time * pTest->premultiply) - (int)(f2 * pTest->premultiply); + stream->WriteSBits((int32)twVal, pTest->significant_bits); + } + break; + } + case DT_STRING: + stream->WriteBitString((const char *)&to[pTest->fieldOffset]); + break; + default: + m_System->Printf("Delta::WriteMarkedFields: unknown send field type\n"); + break; + } + } +} + +bool Delta::CheckDelta(unsigned char *from, unsigned char *to, delta_t *pFields) +{ + ClearFlags(pFields); + MarkSendFields(from, to, pFields); + + return CountSendFields(pFields) ? true : false; +} + +void Delta::WriteHeader(BitBuffer *stream, deltacallback_t *header) +{ + int delta = header->num - header->numbase; + if (header->full) + { + if (delta == 1) + { + stream->WriteBit(1); + } + else + { + stream->WriteBit(0); + if (delta <= 0 || delta >= 64) + { + stream->WriteBit(1); + stream->WriteBits(header->num, 11); + } + else + { + stream->WriteBit(0); + stream->WriteBits(delta, 6); + } + } + } + else + { + stream->WriteBit(header->remove != 0); + if (delta <= 0 || delta >= 64) + { + stream->WriteBit(1); + stream->WriteBits(header->num, 11); + } + else + { + stream->WriteBit(0); + stream->WriteBits(delta, 6); + } + } + + header->numbase = header->num; + if (!header->remove) + { + stream->WriteBit(header->custom != 0); + if (header->instanced_baseline) + { + if (header->newbl) + { + stream->WriteBit(1); + stream->WriteBits(header->newblindex, 6); + } + else + { + stream->WriteBit(0); + } + } + if (header->full && !header->newbl) + { + if (header->offset) + { + stream->WriteBit(1); + stream->WriteBits(header->offset, 6); + } + else + { + stream->WriteBit(0); + } + } + } +} + +bool Delta::WriteDelta(BitBuffer *stream, unsigned char *from, unsigned char *to, bool force, delta_t *pFields, deltacallback_t *header) +{ + int i; + int bytecount; + int bits[2]; + + if (!CheckDelta(from, to, pFields) && !force) { + return false; + } + + SetSendFlagBits(pFields, bits, &bytecount); + + if (header) { + WriteHeader(stream, header); + } + + stream->WriteBits(bytecount, 3); + for (i = 0; i < bytecount; i++) { + stream->WriteBits(((byte *)bits)[i], 8); + } + + WriteMarkedFields(stream, from, to, pFields); + return true; +} + +int Delta::ParseDelta(BitBuffer *stream, unsigned char *from, unsigned char *to, delta_t *pFields) +{ + delta_description_t *pTest; + int i; + int bits[2]; // this is a limit with 64 fields max in delta + int nbytes; + int bitfieldnumber; + int fieldCount = pFields->fieldCount; + int fieldType; + int fieldSign; + + double d2; + float t; + int addt; + char *st2; + char c; + int startbit; + + startbit = stream->CurrentBit(); + memset(bits, 0, sizeof(bits)); + + nbytes = stream->ReadBits(3); + for (i = 0; i < nbytes; i++) { + ((byte *)bits)[i] = stream->ReadBits(8); + } + + for (i = 0, pTest = pFields->pdd; i < fieldCount; i++, pTest++) + { + fieldType = pTest->fieldType & ~DT_SIGNED; + + bitfieldnumber = (1 << (i & 0x1F)); + if (!(bitfieldnumber & bits[i > 31])) + { + // Field was not sent to us, just transfer info from the "from" + switch (fieldType) + { + case DT_BYTE: + to[pTest->fieldOffset] = from[pTest->fieldOffset]; + break; + case DT_SHORT: + *(uint16 *)&to[pTest->fieldOffset] = *(uint16 *)&from[pTest->fieldOffset]; + break; + case DT_FLOAT: + case DT_INTEGER: + case DT_ANGLE: + case DT_TIMEWINDOW_8: + case DT_TIMEWINDOW_BIG: + *(uint32 *)&to[pTest->fieldOffset] = *(uint32 *)&from[pTest->fieldOffset]; + break; + case DT_STRING: + strcpy((char *)&to[pTest->fieldOffset], (char *)&from[pTest->fieldOffset]); + break; + default: + m_System->Printf("Delta::ParseDelta: unparseable field type %i\n", fieldType); + break; + } + continue; + } + + pTest->stats.receivedcount++; + fieldSign = pTest->fieldType & DT_SIGNED; + + switch (fieldType) + { + case DT_BYTE: + if (fieldSign) + { + d2 = (double)stream->ReadSBits(pTest->significant_bits); + if (pTest->premultiply <= 0.9999 || pTest->premultiply >= 1.0001) { + d2 = d2 / pTest->premultiply; + } +#ifndef HLTV + if (pTest->postmultiply <= 0.9999 || pTest->postmultiply >= 1.0001) { + d2 = d2 * pTest->postmultiply; + } +#endif + *(int8 *)&to[pTest->fieldOffset] = (int8)d2; + } + else + { + d2 = (double)stream->ReadBits(pTest->significant_bits); + if (pTest->premultiply <= 0.9999 || pTest->premultiply >= 1.0001) { + d2 = d2 / pTest->premultiply; + } +#ifndef HLTV + if (pTest->postmultiply <= 0.9999 || pTest->postmultiply >= 1.0001) { + d2 = d2 * pTest->postmultiply; + } +#endif + *(uint8 *)&to[pTest->fieldOffset] = (uint8)d2; + } + break; + case DT_SHORT: + if (fieldSign) + { + d2 = (double)stream->ReadSBits(pTest->significant_bits); + if (pTest->premultiply <= 0.9999 || pTest->premultiply >= 1.0001) { + d2 = d2 / pTest->premultiply; + } +#ifndef HLTV + if (pTest->postmultiply <= 0.9999 || pTest->postmultiply >= 1.0001) { + d2 = d2 * pTest->postmultiply; + } +#endif + *(int16 *)&to[pTest->fieldOffset] = (int16)d2; + } + else + { + d2 = (double)stream->ReadBits(pTest->significant_bits); + if (pTest->premultiply <= 0.9999 || pTest->premultiply >= 1.0001) { + d2 = d2 / pTest->premultiply; + } +#ifndef HLTV + if (pTest->postmultiply <= 0.9999 || pTest->postmultiply >= 1.0001) { + d2 = d2 * pTest->postmultiply; + } +#endif + *(uint16 *)&to[pTest->fieldOffset] = (uint16)d2; + } + break; + case DT_FLOAT: + if (fieldSign) + { + d2 = (double)stream->ReadSBits(pTest->significant_bits); + } + else + { + d2 = (double)stream->ReadBits(pTest->significant_bits); + } + if (pTest->premultiply <= 0.9999 || pTest->premultiply >= 1.0001) { + d2 = d2 / pTest->premultiply; + } +#ifndef HLTV + if (pTest->postmultiply <= 0.9999 || pTest->postmultiply >= 1.0001) { + d2 = d2 * pTest->postmultiply; + } +#endif + *(float *)&to[pTest->fieldOffset] = (float)d2; + break; + case DT_INTEGER: + if (fieldSign) + { + d2 = (double)stream->ReadSBits(pTest->significant_bits); + if (pTest->premultiply <= 0.9999 || pTest->premultiply >= 1.0001) { + d2 = d2 / pTest->premultiply; + } +#ifndef HLTV + if (pTest->postmultiply <= 0.9999 || pTest->postmultiply >= 1.0001) { + d2 = d2 * pTest->postmultiply; + } +#endif + *(int32 *)&to[pTest->fieldOffset] = (int32)d2; + } + else + { + d2 = (double)stream->ReadBits(pTest->significant_bits); + if (pTest->premultiply <= 0.9999 || pTest->premultiply >= 1.0001) { + d2 = d2 / pTest->premultiply; + } +#ifndef HLTV + if (pTest->postmultiply <= 0.9999 || pTest->postmultiply >= 1.0001) { + d2 = d2 * pTest->postmultiply; + } +#endif + *(uint32 *)&to[pTest->fieldOffset] = (uint32)d2; + } + break; + case DT_ANGLE: + *(float *)&to[pTest->fieldOffset] = stream->ReadBitAngle(pTest->significant_bits); + break; + case DT_TIMEWINDOW_8: + if (m_LargeTime_Buffers) { + t = stream->ReadFloat(); + } + else { + addt = stream->ReadSBits(8); + t = (float)((m_Time * 100.0 - addt) / 100.0); + } + *(float *)&to[pTest->fieldOffset] = t; + break; + case DT_TIMEWINDOW_BIG: + if (m_LargeTime_Buffers) { + t = stream->ReadFloat(); + } + else { + addt = stream->ReadSBits(pTest->significant_bits); + if (pTest->premultiply <= 0.9999 || pTest->premultiply >= 1.0001) { + t = (float)((m_Time * pTest->premultiply - addt) / pTest->premultiply); + } + else { + t = (float)(m_Time - addt); + } + } + + *(float *)&to[pTest->fieldOffset] = t; + break; + case DT_STRING: + st2 = (char *)&to[pTest->fieldOffset]; + do + { + c = stream->ReadBits(8); + *st2++ = c; + } while (c); + break; + default: + m_System->Printf("Delta::TestDelta: unparseable field type %i\n", fieldType); + break; + } + } + + return stream->CurrentBit() - startbit; +} + +int Delta::TestDelta(unsigned char *from, unsigned char *to, delta_t *pFields) +{ + int i; + char *st1, *st2; + delta_description_t *pTest; + int fieldType; + int fieldCount = pFields->fieldCount; + int length = 0; + bool different; + int neededBits = 0; + int highestBit = -1; + + for (i = 0, pTest = pFields->pdd; i < fieldCount; i++, pTest++) + { + different = false; + fieldType = pTest->fieldType & ~DT_SIGNED; + + switch (fieldType) + { + case DT_BYTE: + different = from[pTest->fieldOffset] != to[pTest->fieldOffset]; + break; + case DT_SHORT: + different = *(uint16 *)&from[pTest->fieldOffset] != *(uint16 *)&to[pTest->fieldOffset]; + break; + case DT_FLOAT: + case DT_INTEGER: + case DT_ANGLE: + different = *(uint32 *)&from[pTest->fieldOffset] != *(uint32 *)&to[pTest->fieldOffset]; + break; + // don't use multiplier when checking, to increase performance + // check values binary like it does in jit + case DT_TIMEWINDOW_8: + case DT_TIMEWINDOW_BIG: + different = (*(int32 *)&from[pTest->fieldOffset]) != (*(int32 *)&to[pTest->fieldOffset]); + break; + case DT_STRING: + st1 = (char *)&from[pTest->fieldOffset]; + st2 = (char *)&to[pTest->fieldOffset]; + + // Not sure why it is case insensitive, but it looks so + if (!(!*st1 && !*st2 || *st1 && *st2 && !_stricmp(st1, st2))) + { + different = true; + length = strlen(st2) * 8; + pTest->flags |= FDT_MARK; + } + break; + default: + m_System->Printf("Delta::TestDelta: Bad field type %i\n", fieldType); + break; + } + + if (different) + { + highestBit = i; + neededBits += (fieldType == DT_STRING) ? length + 8 : pTest->significant_bits; + } + } + + if (highestBit != -1) { + neededBits += highestBit / 8 * 8 + 8; + } + + return neededBits; +} + +void Delta::AddEncoder(char *name, encoder_t conditionalencode) +{ + delta_encoder_t *delta = (delta_encoder_t *)Mem_ZeroMalloc(sizeof(delta_encoder_t)); + delta->name = _strdup(name); + delta->conditionalencode = conditionalencode; + delta->next = m_Encoders; + m_Encoders = delta; +} + +void Delta::ClearEncoders() +{ + delta_encoder_t *n, *p = m_Encoders; + while (p) + { + n = p->next; + free(p->name); + free(p); + p = n; + } + + m_Encoders = nullptr; +} + +encoder_t Delta::LookupEncoder(char *name) +{ + delta_encoder_t *p = m_Encoders; + while (p) + { + if (_stricmp(name, p->name) == 0) { + return p->conditionalencode; + } + + p = p->next; + } + + return nullptr; +} + +int Delta::CountLinks(delta_link_t *plinks) +{ + delta_link_t *p = plinks; + + int c; + for (c = 0; p; c++) { + p = p->next; + } + + return c; +} + +void Delta::ReverseLinks(delta_link_t **plinks) +{ + delta_link_t *n, *p = *plinks; + delta_link_t *newlist = nullptr; + + while (p) + { + n = p->next; + p->next = newlist; + newlist = p; + p = n; + } + + *plinks = newlist; +} + +void Delta::ClearLinks(delta_link_t **plinks) +{ + delta_link_t *n, *p = *plinks; + while (p) + { + n = p->next; + free(p); + p = n; + } + *plinks = 0; +} + +delta_t *Delta::BuildFromLinks(delta_link_t **pplinks) +{ + delta_description_t *pdesc, *pcur; + delta_t *pdelta; + delta_link_t *p; + int count; + + pdelta = (delta_t *)Mem_ZeroMalloc(sizeof(delta_t)); + ReverseLinks(pplinks); + count = CountLinks(*pplinks); + + if (count > DELTA_MAX_FIELDS) { + m_System->Errorf("Delta::BuildFromLinks: Too many fields in delta description %i (MAX %i)\n", count, DELTA_MAX_FIELDS); + return nullptr; + } + + pdesc = (delta_description_t *)Mem_ZeroMalloc(sizeof(delta_description_t) * count); + + for (p = *pplinks, pcur = pdesc; p; p = p->next, pcur++) + { + memcpy(pcur, p->delta, sizeof(delta_description_t)); + free(p->delta); + p->delta = 0; + } + + ClearLinks(pplinks); + + pdelta->dynamic = 1; + pdelta->fieldCount = count; + pdelta->pdd = pdesc; + + return pdelta; +} + +int Delta::FindOffset(int count, delta_definition_t *pdef, char *fieldname) +{ + for (int i = 0; i < count; i++) + { + if (_stricmp(fieldname, pdef[i].fieldName) == 0) { + return pdef[i].fieldOffset; + } + } + + m_System->Errorf("Delta::FindOffset: Couldn't find offset for %s!!!\n", fieldname); + return 0; +} + +bool Delta::ParseType(delta_description_t *pdelta, char **pstream) +{ + // Read the stream till we hit the end + while (*pstream = COM_Parse(*pstream), com_token[0] != 0) + { + if (!_stricmp(com_token, ",")) + return true; // end of type description + + if (!_stricmp(com_token, "|")) + continue; // skip | token + + // Determine field type + if (!_stricmp(com_token, "DT_SIGNED")) + pdelta->fieldType |= DT_SIGNED; + else if (!_stricmp(com_token, "DT_BYTE")) + pdelta->fieldType |= DT_BYTE; + else if (!_stricmp(com_token, "DT_SHORT")) + pdelta->fieldType |= DT_SHORT; + else if (!_stricmp(com_token, "DT_FLOAT")) + pdelta->fieldType |= DT_FLOAT; + else if (!_stricmp(com_token, "DT_INTEGER")) + pdelta->fieldType |= DT_INTEGER; + else if (!_stricmp(com_token, "DT_ANGLE")) + pdelta->fieldType |= DT_ANGLE; + else if (!_stricmp(com_token, "DT_TIMEWINDOW_8")) + pdelta->fieldType |= DT_TIMEWINDOW_8; + else if (!_stricmp(com_token, "DT_TIMEWINDOW_BIG")) + pdelta->fieldType |= DT_TIMEWINDOW_BIG; + else if (!_stricmp(com_token, "DT_STRING")) + pdelta->fieldType |= DT_STRING; + else + { + m_System->Errorf("Delta::ParseType: Unknown type or type flag %s\n", com_token); + return false; + } + } + + // We are hit the end of the stream + m_System->Printf("Delta::ParseType: Expecting fieldtype info\n"); + return false; +} + +bool Delta::ParseField(int count, delta_definition_t *pdefinition, delta_link_t *pField, char **pstream) +{ + bool readpost = false; + if (_stricmp(com_token, "DEFINE_DELTA")) + { + if (_stricmp(com_token, "DEFINE_DELTA_POST") != 0) { + m_System->Errorf("Delta::ParseField: Expecting DEFINE_*, got %s\n", com_token); + return false; + } + + readpost = true; + } + + *pstream = COM_Parse(*pstream); + if (_stricmp(com_token, "(")) + { + m_System->Errorf("Delta::ParseField: Expecting (, got %s\n", com_token); + return false; + } + + *pstream = COM_Parse(*pstream); + if (com_token[0] == '\0') + { + m_System->Errorf("Delta::ParseField: Expecting fieldname\n"); + return false; + } + + strcopy(pField->delta->fieldName, com_token); + pField->delta->fieldOffset = FindOffset(count, pdefinition, com_token); + + *pstream = COM_Parse(*pstream); + if (!ParseType(pField->delta, pstream)) { + return false; + } + + *pstream = COM_Parse(*pstream); + pField->delta->fieldSize = 1; + pField->delta->significant_bits = atoi(com_token); + *pstream = COM_Parse(*pstream); + *pstream = COM_Parse(*pstream); + pField->delta->premultiply = (float)atof(com_token); + + if (readpost) + { + *pstream = COM_Parse(*pstream); + *pstream = COM_Parse(*pstream); + pField->delta->postmultiply = (float)atof(com_token); + } + else + { + pField->delta->postmultiply = 1.0; + } + + *pstream = COM_Parse(*pstream); + if (_stricmp(com_token, ")")) + { + m_System->Printf("Delta::ParseField: Expecting ), got %s\n", com_token); + return false; + } + + *pstream = COM_Parse(*pstream); + if (_stricmp(com_token, ",")) { + COM_UngetToken(); + } + + return true; +} + +void Delta::FreeDescription(delta_t **ppdesc) +{ + if (ppdesc && *ppdesc) + { + if ((*ppdesc)->dynamic) { + free((*ppdesc)->pdd); + } + + free(*ppdesc); + *ppdesc = nullptr; + } +} + +void Delta::AddDefinition(char *name, delta_definition_t *pdef, int numelements) +{ + delta_definition_list_t *p = m_Defs; + while (p) + { + if (_stricmp(name, p->ptypename) == 0) { + break; + } + + p = p->next; + } + + if (!p) + { + p = (delta_definition_list_t *)Mem_ZeroMalloc(sizeof(delta_definition_list_t)); + p->ptypename = _strdup(name); + p->next = m_Defs; + m_Defs = p; + } + + p->pdefinition = pdef; + p->numelements = numelements; +} + +void Delta::ClearDefinitions() +{ + delta_definition_list_t *n, *p = m_Defs; + while (p) + { + n = p->next; + free(p->ptypename); + free(p); + p = n; + } + + m_Defs = nullptr; +} + +Delta::delta_definition_t *Delta::FindDefinition(char *name, int *count) +{ + *count = 0; + + delta_definition_list_t *p = m_Defs; + while (p) + { + if (!_stricmp(name, p->ptypename)) + { + *count = p->numelements; + return p->pdefinition; + } + + p = p->next; + } + + return nullptr; +} + +void Delta::SkipDescription(char **pstream) +{ + *pstream = COM_Parse(*pstream); + do + { + *pstream = COM_Parse(*pstream); + if (com_token[0] == '\0') { + m_System->Errorf("Delta::SkipDescription: Error during description skip\n"); + return; + } + } + while (_stricmp(com_token, "}")); +} + +bool Delta::ParseOneField(char **ppstream, delta_link_t **pplist, int count, delta_definition_t *pdefinition) +{ + delta_link_t *newlink; + delta_link_t link; + + while (true) + { + if (!_stricmp(com_token, "}")) + { + COM_UngetToken(); + break; + } + + *ppstream = COM_Parse(*ppstream); + if (com_token[0] == '\0') { + break; + } + + memset(&link, 0, sizeof(link)); + link.delta = (delta_description_t *)Mem_ZeroMalloc(sizeof(delta_description_t)); + if (!ParseField(count, pdefinition, &link, ppstream)) { + return false; + } + + newlink = (delta_link_t *)Mem_ZeroMalloc(sizeof(delta_link_t)); + newlink->delta = link.delta; + newlink->next = *pplist; + *pplist = newlink; + } + + return true; +} + +bool Delta::ParseDescription(char *name, delta_t **ppdesc, char *pstream) +{ + delta_link_t *links = nullptr; + delta_definition_t *pdefinition; + char encoder[32] = ""; + char source[32]; + int count = 0; + + if (!ppdesc) { + m_System->Errorf("Delta::ParseDescription: called with no delta_description_t\n"); + return false; + } + + *ppdesc = nullptr; + + if (!pstream) { + m_System->Errorf("Delta::ParseDescription: called with no data stream\n"); + return false; + } + + while (true) + { + // Parse delta name + pstream = COM_Parse(pstream); + if (com_token[0] == '\0') { + break; + } + + if (_stricmp(com_token, name)) + { + SkipDescription(&pstream); + } + else + { + pdefinition = FindDefinition(com_token, &count); + if (!pdefinition) { + m_System->Errorf("Delta::ParseDescription: Unknown data type: %s\n", com_token); + return false; + } + + // Parse source of conditional encoder + pstream = COM_Parse(pstream); + if (com_token[0] == '\0') { + m_System->Errorf("Delta::ParseDescription: Unknown encoder: %s\nValid values:\nnone\ngamedll funcname\nclientdll funcname\n", com_token); + return false; + } + + if (_stricmp(com_token, "none")) + { + strcopy(source, com_token); + + // Parse custom encoder function name + pstream = COM_Parse(pstream); + if (com_token[0] == '\0') { + m_System->Errorf("Delta::ParseDescription: Expecting encoder\n"); + return false; + } + + strcopy(encoder, com_token); + } + + // Parse fields + while (true) + { + pstream = COM_Parse(pstream); + if (com_token[0] == '\0') { + break; + } + + if (!_stricmp(com_token, "}")) { + break; + } + + if (_stricmp(com_token, "{")) { + m_System->Printf("Delta::ParseDescription: Expecting {, got %s\n", com_token); + return false; + } + + if (!ParseOneField(&pstream, &links, count, pdefinition)) { + return false; + } + } + } + } + + *ppdesc = BuildFromLinks(&links); + + if (encoder[0]) + { + strcopy((*ppdesc)->conditionalencodename, encoder); + (*ppdesc)->conditionalencodename[sizeof((*ppdesc)->conditionalencodename) - 1] = '\0'; + (*ppdesc)->conditionalencode = nullptr; + } + + return true; +} + +bool Delta::Load(char *name, delta_t **ppdesc, char *pszFile) +{ + char *pbuf = (char *)m_System->LoadFile(pszFile); + if (!pbuf) { + m_System->Errorf("Delta::Load: Couldn't load file %s\n", pszFile); + return false; + } + + bool res = ParseDescription(name, ppdesc, pbuf); + free(pbuf); + return res; +} + +void Delta::RegisterDescription(char *name) +{ + delta_registry_t *p = (delta_registry_t *)Mem_ZeroMalloc(sizeof(delta_registry_t)); + p->next = m_DeltaRegistry; + m_DeltaRegistry = p; + p->name = _strdup(name); + p->pdesc = 0; +} + +void Delta::ClearRegistrations() +{ + delta_registry_t *n, *p = m_DeltaRegistry; + while (p) + { + n = p->next; + free(p->name); + + if (p->pdesc) { + FreeDescription(&p->pdesc); + } + + free(p); + p = n; + } + + m_DeltaRegistry = nullptr; +} + +void Delta::ClearStats(delta_t *p) +{ + if (!p) { + return; + } + + for (int i = 0; i < p->fieldCount; i++) + { + p->pdd[i].stats.sendcount = 0; + p->pdd[i].stats.receivedcount = 0; + } +} + +void Delta::SetTime(double time) +{ + m_Time = time; +} + +void Delta::SetLargeTimeBufferSize(bool bigBuffers) +{ + m_LargeTime_Buffers = bigBuffers; +} + +#endif // HOOK_HLTV diff --git a/rehlds/HLTV/Core/src/Delta.h b/rehlds/HLTV/Core/src/Delta.h new file mode 100644 index 0000000..17a540a --- /dev/null +++ b/rehlds/HLTV/Core/src/Delta.h @@ -0,0 +1,208 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "event_args.h" +#include "BaseSystemModule.h" + +#define DELTA_MAX_FIELDS 56 // 7*8 + +#define DT_BYTE BIT(0) // A byte +#define DT_SHORT BIT(1) // 2 byte field +#define DT_FLOAT BIT(2) // A floating point field +#define DT_INTEGER BIT(3) // 4 byte integer +#define DT_ANGLE BIT(4) // A floating point angle +#define DT_TIMEWINDOW_8 BIT(5) // A floating point timestamp relative to server time +#define DT_TIMEWINDOW_BIG BIT(6) // A floating point timestamp relative to server time (with more precision and custom multiplier) +#define DT_STRING BIT(7) // A null terminated string, sent as 8 byte chars +#define DT_SIGNED BIT(31) // sign modificator + +#define FDT_MARK BIT(0) // Delta mark for sending + +typedef void (*encoder_t)(struct delta_s *, const unsigned char *, const unsigned char *); + +typedef struct delta_encoder_s +{ + struct delta_encoder_s *next; + char *name; + encoder_t conditionalencode; +} delta_encoder_t; + +typedef struct delta_stats_s +{ + int sendcount; + int receivedcount; +} delta_stats_t; + +typedef struct delta_description_s +{ + int fieldType; + char fieldName[32]; + int fieldOffset; + short int fieldSize; + int significant_bits; + float premultiply; + float postmultiply; + short int flags; + delta_stats_t stats; +} delta_description_t; + +typedef struct deltacallback_s +{ + int numbase; + int num; + bool remove; + bool custom; + bool newbl; + int newblindex; + bool full; + int offset; + bool instanced_baseline; +} deltacallback_t; + +typedef struct delta_s +{ + int dynamic; + int fieldCount; + char conditionalencodename[32]; + encoder_t conditionalencode; + delta_description_t *pdd; +} delta_t; + +#include "hookers/HLTV/Core/DeltaEx.h" + +#ifndef HOOK_HLTV + +class Delta { +public: + void Init(IBaseSystem *system); + void Shutdown(); + void UpdateDescriptions(); + void WriteHeader(BitBuffer *stream, deltacallback_t *header); + bool WriteDelta(BitBuffer *stream, unsigned char *from, unsigned char *to, bool force, delta_t *pFields, deltacallback_t *header = nullptr); + int ParseDelta(BitBuffer *stream, unsigned char *from, unsigned char *to, delta_t *pFields); + void SetTime(double time); + void SetLargeTimeBufferSize(bool bigBuffers); + int TestDelta(unsigned char *from, unsigned char *to, delta_t *pFields); + delta_t **LookupRegistration(char *name); + void FreeDescription(delta_t **ppdesc); + +private: + typedef struct delta_link_s + { + struct delta_link_s *next; + delta_description_t *delta; + } delta_link_t; + + typedef struct delta_definition_s + { + char *fieldName; + size_t fieldOffset; + } delta_definition_t; + + typedef struct delta_definition_list_s + { + struct delta_definition_list_s *next; + char *ptypename; + int numelements; + delta_definition_t *pdefinition; + } delta_definition_list_t; + + typedef struct delta_registry_s + { + struct delta_registry_s *next; + char *name; + delta_t *pdesc; + } delta_registry_t; + + delta_registry_t *GetRegistry() const { return m_DeltaRegistry; } + delta_description_t *FindField(delta_t *pFields, const char *pszField); + int FindFieldIndex(delta_t *pFields, const char *fieldname); + void SetField(delta_t *pFields, const char *fieldname); + void UnsetField(delta_t *pFields, const char *fieldname); + void SetFieldByIndex(delta_t *pFields, int fieldNumber); + void UnsetFieldByIndex(delta_t *pFields, int fieldNumber); + void ClearFlags(delta_t *pFields); + int CountSendFields(delta_t *pFields); + void MarkSendFields(unsigned char *from, unsigned char *to, delta_t *pFields); + void SetSendFlagBits(delta_t *pFields, int *bits, int *bytecount); + void WriteMarkedFields(BitBuffer *stream, unsigned char *from, unsigned char *to, delta_t *pFields); + bool CheckDelta(unsigned char *from, unsigned char *to, delta_t *pFields); + void AddEncoder(char *name, encoder_t conditionalencode); + void ClearEncoders(); + encoder_t LookupEncoder(char *name); + int CountLinks(delta_link_t *plinks); + void ReverseLinks(delta_link_t **plinks); + void ClearLinks(delta_link_t **plinks); + delta_t *BuildFromLinks(delta_link_t **pplinks); + int FindOffset(int count, delta_definition_t *pdef, char *fieldname); + bool ParseType(delta_description_t *pdelta, char **pstream); + bool ParseField(int count, delta_definition_t *pdefinition, delta_link_t *pField, char **pstream); + void AddDefinition(char *name, delta_definition_t *pdef, int numelements); + void ClearDefinitions(); + delta_definition_t *FindDefinition(char *name, int *count); + void SkipDescription(char **pstream); + bool ParseOneField(char **ppstream, delta_link_t **pplist, int count, delta_definition_t *pdefinition); + bool ParseDescription(char *name, delta_t **ppdesc, char *pstream); + bool Load(char *name, delta_t **ppdesc, char *pszFile); + void RegisterDescription(char *name); + void ClearRegistrations(); + void ClearStats(delta_t *p); + +public: + static delta_t m_MetaDelta[]; + static delta_description_t m_MetaDescription[]; + static delta_definition_t m_DeltaDataDefinition[]; + static delta_definition_t m_EventDataDefinition[]; + static delta_definition_t m_EntityDataDefinition[]; + static delta_definition_t m_UsercmdDataDefinition[]; + static delta_definition_t m_WeaponDataDefinition[]; + static delta_definition_t m_ClientDataDefinition[]; + + static delta_t *m_EntityDelta; + static delta_t *m_PlayerDelta; + static delta_t *m_CustomentityDelta; + + static delta_t *m_ClientDelta; + static delta_t *m_WeaponDelta; + static delta_t *m_EventDelta; + +private: + friend class World; + IBaseSystem *m_System; + + delta_definition_list_t *m_Defs; + delta_encoder_t *m_Encoders; + delta_registry_t *m_DeltaRegistry; + + double m_Time; + bool m_LargeTime_Buffers; +}; + +#endif // HOOK_HLTV diff --git a/rehlds/HLTV/Core/src/NetSocket.cpp b/rehlds/HLTV/Core/src/NetSocket.cpp new file mode 100644 index 0000000..2ba4eed --- /dev/null +++ b/rehlds/HLTV/Core/src/NetSocket.cpp @@ -0,0 +1,555 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +bool NetSocket::AddChannel(INetChannel *channel) +{ + return m_Channels.AddTail(channel); +} + +bool NetSocket::RemoveChannel(INetChannel *channel) +{ + return m_Channels.Remove(channel); +} + +int NetSocket::DispatchIncoming() +{ + int length = 0; + int count = 0; + + NetAddress from; + while ((length = ReceivePacketIntern(&from))) + { + if (length == -1) + break; + + INetChannel *channel = (INetChannel *)m_Channels.GetFirst(); + while (channel) + { + if (from.Equal(channel->GetTargetAddress())) { + channel->ProcessIncoming(m_Buffer, length); + break; + } + + channel = (INetChannel *)m_Channels.GetNext(); + } + + // not found an existing channel for this address. + // create a new channel + if (!channel) + { + // do accept only connectionless (\xFF\xFF\xFF\xFF) incoming packet. + if (*(int *)m_Buffer == CONNECTIONLESS_HEADER) + { + NetPacket *p = new NetPacket; + p->time = m_System->GetTime(); + p->address.FromNetAddress(&from); + p->connectionless = true; + p->seqnr = -1; + + // cut connectionless data + p->data.Resize(length - 4); + p->data.WriteBuf(&m_Buffer[4], length - 4); + p->data.Reset(); + m_IncomingPackets.AddHead(p); + } + } + + count++; + } + + return count; +} + +bool NetSocket::SendPacket(NetPacket *packet) +{ + return SendPacket(&packet->address, packet->data.GetData(), packet->data.CurrentSize()); +} + +bool NetSocket::SendPacket(NetAddress *to, const void *data, int length) +{ + struct sockaddr addr; + int ret; + + if (m_Network->m_FakeLoss && RandomFloat(0, 1) < m_Network->m_FakeLoss) { + return true; + } + + if (length <= 0 || !data || !to) { + return true; + } + + to->ToSockadr(&addr); + + if (length > MAX_ROUTEABLE_PACKET) + { + ret = SendLong((const char *)data, length, 0, &addr, sizeof(addr)); + } + else + { + if (length < sizeof(addr)) + ret = SendShort((const char *)data, length, 0, &addr, sizeof(addr)); + else + ret = sendto(m_Socket, (const char *)data, length, 0, &addr, sizeof(addr)); + } + + if (ret == -1) + { + int err = m_Network->GetLastErrorCode(); + + // wouldblock is silent + if (err == WSAEWOULDBLOCK) + return true; + + if (err == WSAECONNREFUSED || err == WSAECONNRESET) + return true; + + // some PPP links dont allow broadcasts + if (err == WSAEADDRNOTAVAIL) + return true; + + m_System->Printf("WARNING! NetSocket::SendPacket: %s\n", m_Network->GetErrorText(err)); + return false; + } + + m_BytesOut += ret + UDP_HEADER_SIZE; + return true; +} + +INetwork *NetSocket::GetNetwork() +{ + return m_Network; +} + +int NetSocket::ReceivePacketIntern(NetAddress *fromHost) +{ + sockaddr from; + socklen_t fromlen = sizeof(from); + int size = recvfrom(m_Socket, (char *)m_Buffer, sizeof(m_Buffer), 0, &from, &fromlen); + if (size == -1) + { + int err = m_Network->GetLastErrorCode(); + if (err != WSAEWOULDBLOCK && err != WSAECONNRESET && err != WSAECONNREFUSED) + { + if (err == WSAEMSGSIZE) + { + m_System->DPrintf("WARNING! NetSocket::ReceivePacket: Ignoring oversized network message\n"); + } + else + { + m_System->DPrintf("WARNING! NetSocket::ReceivePacket: %s %d %i\n", m_Network->GetErrorText(err), err, errno); + perror("err"); + } + } + + return false; + } + + if (size == 0 || RandomFloat(0, 1) < m_Network->m_FakeLoss) { + return 0; + } + + if (size > 0 && size < 4) { + m_System->DPrintf("WARNING! NetSocket::ReceivePacket: Ignoring undersized network message\n"); + return 0; + } + + if (size >= MAX_UDP_PACKET) { + m_System->DPrintf("WARNING! NetSocket::ReceivePacket: Oversize packet from %s\n", fromHost->ToString()); + return 0; + } + + if (*(uint32 *)m_Buffer == NET_HEADER_FLAG_SPLITPACKET) + { + if (size < sizeof(SPLITPACKET)) + { + m_System->Printf("Invalid split packet length %i\n", size); + return 0; + } + + size = GetLong(m_Buffer, size); + } + + fromHost->FromSockadr(&from); + m_BytesIn += size + UDP_HEADER_SIZE; + return size; +} + +void NetSocket::Close() +{ + INetChannel *channel; + while ((channel = (INetChannel *)m_Channels.RemoveHead())) { + channel->Close(); + } + + Flush(); + m_Network->RemoveSocket(this); + + shutdown(m_Socket, 2); + SOCKET_CLOSE(m_Socket); +} + +bool NetSocket::JoinGroup(NetAddress *group) +{ + ip_mreq mreq; + SIN_SET_ADDR(&mreq.imr_multiaddr, *(unsigned int *)&group->m_IP[0]); + SIN_SET_ADDR(&mreq.imr_interface, 0); + + if (setsockopt(m_Socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mreq, sizeof(mreq)) == SOCKET_ERROR) + { + int err = m_Network->GetLastErrorCode(); + if (err != WSAEAFNOSUPPORT) { + m_System->DPrintf("WARNING! NetSocket::JoinGroup: IP_ADD_MEMBERSHIP: %s\n", m_Network->GetErrorText(err)); + } + + return false; + } + + return true; +} + +bool NetSocket::LeaveGroup(NetAddress *group) +{ + ip_mreq mreq; + SIN_SET_ADDR(&mreq.imr_multiaddr, *(unsigned int *)&group->m_IP[0]); + SIN_SET_ADDR(&mreq.imr_interface, 0); + + if (setsockopt(m_Socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *)&mreq, sizeof(mreq)) == SOCKET_ERROR) + { + int err = m_Network->GetLastErrorCode(); + if (err != WSAEAFNOSUPPORT) { + m_System->DPrintf("WARNING! NetSocket::LeaveGroup: IP_DROP_MEMBERSHIP: %s\n", m_Network->GetErrorText(err)); + } + + return false; + } + + return true; +} + +int NetSocket::DrainChannels() +{ + int count = 0; + INetChannel *channel = (INetChannel *)m_Channels.GetFirst(); + while (channel) + { + if (channel->IsReadyToSend() + && channel->KeepAlive() + && channel->IsConnected()) + { + count++; + channel->TransmitOutgoing(); + } + + channel = (INetChannel *)m_Channels.GetNext(); + } + + return count; +} + +int NetSocket::GetLong(unsigned char *pData, int size) +{ + unsigned int packetNumber; + unsigned int packetCount; + int sequenceNumber; + unsigned char packetID; + static int netSplitFlags[MAX_SPLIT_FRAGMENTS] = { -1, -1, -1, -1, -1 }; + SPLITPACKET *pHeader = (SPLITPACKET *)pData; + + sequenceNumber = pHeader->sequenceNumber; + packetID = pHeader->packetID; + packetCount = packetID & 0xF; + packetNumber = (unsigned int)packetID >> 4; + + if (packetNumber >= MAX_SPLIT_FRAGMENTS || packetCount > MAX_SPLIT_FRAGMENTS) + { + m_System->Printf("Malformed packet number (%i)\n", packetNumber); + return 0; + } + + if (m_NetSplitPacket.currentSequence == -1 || sequenceNumber != m_NetSplitPacket.currentSequence) + { + m_NetSplitPacket.currentSequence = sequenceNumber; + m_NetSplitPacket.splitCount = packetCount; + } + + unsigned int packetPayloadSize = size - sizeof(SPLITPACKET); + if (netSplitFlags[packetNumber] == sequenceNumber) + { + m_System->DPrintf("NetSocket::GetLong: Ignoring duplicated split packet %i of %i ( %i bytes )\n", packetNumber + 1, packetCount, packetPayloadSize); + } + else + { + if (packetNumber == packetCount - 1) { + m_NetSplitPacket.totalSize = packetPayloadSize + SPLIT_SIZE * (packetCount - 1); + } + + --m_NetSplitPacket.splitCount; + netSplitFlags[packetNumber] = sequenceNumber; + } + + memcpy(&m_NetSplitPacket.buffer[SPLIT_SIZE * packetNumber], pHeader + 1, packetPayloadSize); + + if (m_NetSplitPacket.splitCount > 0) { + return 0; + } + + m_NetSplitPacket.currentSequence = -1; + if (m_NetSplitPacket.totalSize > MAX_UDP_PACKET) + { + m_System->DPrintf("WARNING! NetSocket::GetLong: Split packet too large! %d bytes\n", m_NetSplitPacket.totalSize); + return -1; + + } + + memcpy(pData, m_NetSplitPacket.buffer, m_NetSplitPacket.totalSize); + return m_NetSplitPacket.totalSize; +} + +void NetSocket::OutOfBandPrintf(NetAddress *to, const char *format, ...) +{ + va_list argptr; + char string[NET_MAX_MESSAGE]; + + *(int *)string = CONNECTIONLESS_HEADER; + + va_start(argptr, format); + _vsnprintf(&string[4], sizeof(string) - 4, format, argptr); + va_end(argptr); + + SendPacket(to, string, strlen(string) + 1); +} + +void NetSocket::GetFlowStats(float *avgInKBSec, float *avgOutKBSec) +{ + *avgInKBSec = m_AvgBytesIn / 1024; + *avgOutKBSec = m_AvgBytesOut / 1024; +} + +NetPacket *NetSocket::ReceivePacket() +{ + return (NetPacket *)m_IncomingPackets.RemoveTail(); +} + +void NetSocket::FreePacket(NetPacket *packet) +{ + if (packet) { + delete packet; + } +} + +void NetSocket::AddPacket(NetPacket *packet) +{ + if (!packet->connectionless) { + m_System->DPrintf("WARNING! NetSocket::AddPacket: only connectionless packets accepted.\n"); + return; + } + + NetPacket *newpacket = new NetPacket; + + newpacket->time = packet->time; + newpacket->address.FromNetAddress(&packet->address); + newpacket->connectionless = true; + newpacket->seqnr = -1; + + newpacket->data.Resize(packet->data.GetMaxSize()); + newpacket->data.WriteBuf(packet->data.GetData(), packet->data.GetMaxSize()); + newpacket->data.Reset(); + + m_IncomingPackets.AddHead(newpacket); +} + +void NetSocket::Flush() +{ + NetPacket *in; + while ((in = (NetPacket *)m_IncomingPackets.RemoveTail())) { + FreePacket(in); + } + + if (m_Socket != INVALID_SOCKET) + { + sockaddr from; + socklen_t fromlen = sizeof(from); + while (recvfrom(m_Socket, (char *)m_Buffer, sizeof(m_Buffer), 0, &from, &fromlen) > 0) + ; + } +} + +bool NetSocket::Create(Network *network, int port, bool reuse, bool loopback) +{ + sockaddr_in address; + char _true = 1; + uint32 i = 1; + + m_Network = network; + m_System = network->GetSystem(); + + m_Channels.Init(); + + memset(m_Buffer, 0, sizeof(m_Buffer)); + memset(&m_NetSplitPacket, 0, sizeof(m_NetSplitPacket)); + + if ((m_Socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) { + return false; + } + + if (SOCKET_FIONBIO(m_Socket, i) == SOCKET_ERROR) { + return false; + } + + if (setsockopt(m_Socket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) == SOCKET_ERROR) { + return false; + } + + // resue + if (reuse && setsockopt(m_Socket, SOL_SOCKET, SO_REUSEADDR, &_true, sizeof(_true)) == SOCKET_ERROR) { + return false; + } + + if (m_Network->m_IsMultihomed) + { + m_Network->GetLocalAddress()->ToSockadr((sockaddr *)&address); + } + else + { + address.sin_addr.s_addr = 0; + } + + m_Port = port; + address.sin_port = htons(port); + address.sin_family = AF_INET; + + if (bind(m_Socket, (struct sockaddr *)&address, sizeof(address)) == SOCKET_ERROR) { + SOCKET_CLOSE(m_Socket); + return false; + } + + // Time-To-Live (TTL) for Multicast Packets + // As the values of the TTL field increase, routers will expand the number of hops they will forward a multicast packet. + // To provide meaningful scope control, multicast routers enforce the following + // "thresholds" on forwarding based on the TTL field: + // + // 0: restricted to the same host + // 1: restricted to the same subnet + // 32: restricted to the same site + // 64: restricted to the same region + // 128: restricted to the same continent + // 255: unrestricted + + uint32 ttl = 32; + char *ttlparam = m_System->CheckParam("-multicastttl"); + if (ttlparam) { + ttl = atoi(ttlparam); + } + + if (setsockopt(m_Socket, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&ttl, sizeof(ttl)) == SOCKET_ERROR) { + return false; + } + + m_BytesOut = 0; + m_BytesIn = 0; + m_LastUpdateTime = 0; + + m_AvgBytesOut = 0; + m_AvgBytesIn = 0; + + return true; +} + +int NetSocket::GetPort() +{ + return m_Port; +} + +void NetSocket::UpdateStats(double time) +{ + float timeDiff = time - m_LastUpdateTime; + if (timeDiff > 0) + { + m_AvgBytesIn = m_BytesIn / timeDiff * 0.3 + m_AvgBytesIn * 0.6; + m_AvgBytesOut = m_BytesOut / timeDiff * 0.3 + m_AvgBytesOut * 0.6; + + m_BytesIn = 0; + m_BytesOut = 0; + m_LastUpdateTime = time; + } +} + +int NetSocket::SendLong(const char *pData, int len, int flags, const sockaddr *to, int tolen) +{ + char packet[MAX_ROUTEABLE_PACKET]; + int totalSent, ret, size, packetCount, packetNumber; + SPLITPACKET *pPacket; + + if (++m_netSplitSequenceNumber < 0) { + m_netSplitSequenceNumber = 1; + } + + pPacket = (SPLITPACKET *)packet; + pPacket->netID = -2; + pPacket->sequenceNumber = m_netSplitSequenceNumber; + packetNumber = 0; + totalSent = 0; + packetCount = (len + SPLIT_SIZE - 1) / SPLIT_SIZE; + + while (len > 0) + { + size = min(SPLIT_SIZE, (unsigned)len); + pPacket->packetID = (packetNumber << 4) + packetCount; + memcpy(packet + sizeof(SPLITPACKET), pData + (packetNumber * SPLIT_SIZE), size); + + ret = sendto(m_Socket, packet, size + sizeof(SPLITPACKET), flags, to, tolen); + if (ret < 0) { + return ret; + } + + if (ret >= size) { + totalSent += size; + } + + len -= size; + packetNumber++; + } + + return totalSent; +} + +int NetSocket::SendShort(const char *pData, int len, int flags, const sockaddr *to, int tolen) +{ + if (len > MIN_ROUTEABLE_PACKET) { + m_System->DPrintf("WARNING! NetSocket::SendShort: length > MIN_ROUTEABLE_PACKET.\n"); + return 0; + } + + char packet[MAX_ROUTEABLE_PACKET]; + memcpy(packet, pData, len); + memset(&packet[len], 0, sizeof(packet) - len); + + return sendto(m_Socket, packet, sizeof(packet), flags, to, tolen); +} diff --git a/rehlds/HLTV/Core/src/NetSocket.h b/rehlds/HLTV/Core/src/NetSocket.h new file mode 100644 index 0000000..b14a047 --- /dev/null +++ b/rehlds/HLTV/Core/src/NetSocket.h @@ -0,0 +1,108 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include + +class Network; +class IBaseSytem; + +class NetSocket: public INetSocket { +public: + NetSocket() : m_netSplitSequenceNumber(0) {} + virtual ~NetSocket() {} + + NetPacket *ReceivePacket(); + void FreePacket(NetPacket *packet); + bool SendPacket(NetPacket *packet); + bool SendPacket(NetAddress *packet, const void *data, int length); + void AddPacket(NetPacket *packet); + bool AddChannel(INetChannel *channel); + bool RemoveChannel(INetChannel *channel); + INetwork *GetNetwork(); + void OutOfBandPrintf(NetAddress *to, const char *format, ...); + void Flush(); + void GetFlowStats(float *avgInKBSec, float *avgOutKBSec); + bool LeaveGroup(NetAddress *group); + bool JoinGroup(NetAddress *group); + void Close(); + int GetPort(); + + bool Create(Network *network, int port, bool reuse, bool loopback); + void UpdateStats(double time); + int DrainChannels(); + int DispatchIncoming(); + +private: + int ReceivePacketIntern(NetAddress *fromHost); + int SendLong(const char *pData, int len, int flags, const sockaddr *to, int tolen); + int SendShort(const char *pData, int len, int flags, const sockaddr *to, int tolen); + int GetLong(unsigned char *pData, int size); + +protected: + int m_Port; + unsigned char m_Buffer[MAX_UDP_PACKET]; + + ObjectList m_IncomingPackets; + ObjectList m_Channels; + SOCKET m_Socket; + Network *m_Network; + IBaseSystem *m_System; + + enum { MAX_SPLIT_FRAGMENTS = 5 }; + // Use this to pick apart the network stream, must be packed + #pragma pack(push, 1) + typedef struct SPLITPACKET_t + { + int netID; + int sequenceNumber; + unsigned char packetID; + } SPLITPACKET; + #pragma pack(pop) + + // Split long packets. Anything over 1460 is failing on some routers. + typedef struct LONGPACKET_t + { + int currentSequence; + int splitCount; + int totalSize; + // TODO: It should be NET_MAX_MESSAGE, but value differs + char buffer[MAX_UDP_PACKET]; // This has to be big enough to hold the largest message + } LONGPACKET; + + LONGPACKET m_NetSplitPacket; + + int m_netSplitSequenceNumber; + int m_netSplitFlags[MAX_SPLIT_FRAGMENTS]; + int m_BytesIn; + int m_BytesOut; + double m_LastUpdateTime; + float m_AvgBytesIn; + float m_AvgBytesOut; +}; diff --git a/rehlds/HLTV/Core/src/Network.cpp b/rehlds/HLTV/Core/src/Network.cpp new file mode 100644 index 0000000..91a732b --- /dev/null +++ b/rehlds/HLTV/Core/src/Network.cpp @@ -0,0 +1,445 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +bool Network::Init(IBaseSystem *system, int serial, char *name) +{ + BaseSystemModule::Init(system, serial, name); + + if (!name) { + SetName(NETWORK_INTERFACE_VERSION); + } + + m_FakeLoss = 0; + m_IsMultihomed = false; + m_NoDNS = false; + + m_Sockets.Init(); + m_System->RegisterCommand("fakeloss", this, CMD_ID_FAKELOSS); + +#ifdef _WIN32 + // Startup winock + WORD version = MAKEWORD(2, 2); // for use Winsock 2.2, version should be 514 + WSADATA wsaData; + int err = WSAStartup(version, &wsaData); + if (err != NO_ERROR) + { + m_System->Printf("Network Error WSAStartup:%s\n", GetErrorText(GetLastErrorCode())); + return false; + } +#endif // _WIN32 + + if (m_System->CheckParam("-nodns")) { + m_NoDNS = true; + } + + if (ResolveAddress(m_System->CheckParam("-ip"), &m_LocalAddress)) + { + m_IsMultihomed = true; + m_System->Printf("Network uses %s as host IP.\n", m_LocalAddress.ToBaseString()); + } + else + { + char hostname[512]; + gethostname(hostname, sizeof(hostname)); + ResolveAddress(hostname, &m_LocalAddress); + } + + char *portparam = m_System->CheckParam("-port"); + if (!portparam) { + portparam = "27020"; + } + + m_LocalAddress.m_Port = htons(atoi(portparam)); + m_LastStatsUpdateTime = 0; + + m_State = MODULE_RUNNING; + m_System->Printf("Network initialized.\n"); + return true; +} + +void Network::ExecuteCommand(int commandID, char *commandLine) +{ + if (commandID == CMD_ID_FAKELOSS) { + CMD_FakeLoss(commandLine); + return; + } + + m_System->Printf("ERROR! Network::ExecuteCommand: unknown command ID %i.\n", commandID); +} + +void Network::ShutDown() +{ + if (m_State == MODULE_DISCONNECTED) { + return; + } + + NetSocket *sock; + while ((sock = (NetSocket *)m_Sockets.RemoveTail())) + { + sock->Close(); + delete sock; + } + +#ifdef _WIN32 + if (WSACleanup() == SOCKET_ERROR) { + m_System->Printf("Network Error WSACleanup:%s\n", GetErrorText(GetLastErrorCode())); + } +#endif // _WIN32 + + m_System->Printf("Network module shutdown.\n"); + BaseSystemModule::ShutDown(); +} + +INetSocket *Network::CreateSocket(int port, bool reuse, bool loopback) +{ + NetSocket *socket = GetSocket(port); + if (socket) { + return socket; + } + + socket = new NetSocket; + if (!socket) { + m_System->Printf("ERROR! Network::CreateSocket: out of memory.\n"); + return nullptr; + } + + if (!socket->Create(this, port, reuse, loopback)) + { + m_System->Printf("ERROR! Network::CreateSocket: %s.\n", GetErrorText(GetLastErrorCode())); + + delete socket; + return nullptr; + } + + m_Sockets.Add(socket); + return socket; +} + +bool Network::RemoveSocket(INetSocket *netsocket) +{ + return m_Sockets.Remove(netsocket); +} + +char *Network::GetType() +{ + return NETWORK_INTERFACE_VERSION; +} + +int Network::ReceiveData() +{ + int countPackets = 0; + NetSocket *sock = (NetSocket *)m_Sockets.GetFirst(); + while (sock) + { + countPackets += sock->DispatchIncoming(); + sock = (NetSocket *)m_Sockets.GetNext(); + } + + return countPackets; +} + +int Network::SendData() +{ + int countPackets = 0; + NetSocket *sock = (NetSocket *)m_Sockets.GetFirst(); + while (sock) + { + countPackets += sock->DrainChannels(); + sock = (NetSocket *)m_Sockets.GetNext(); + } + + return countPackets; +} + +NetSocket *Network::GetSocket(int port) +{ + NetSocket *sock = (NetSocket *)m_Sockets.GetFirst(); + if (!sock) { + return nullptr; + } + + while (sock) + { + if (sock->GetPort() == port || !port) { + return sock; + } + + sock = (NetSocket *)m_Sockets.GetNext(); + } + + return nullptr; +} + +void Network::RunFrame(double time) +{ + BaseSystemModule::RunFrame(time); + + SendData(); + ReceiveData(); + UpdateStats(); +} + +NetAddress *Network::GetLocalAddress() +{ + return &m_LocalAddress; +} + +char *Network::GetStatusLine() +{ + static char string[256]; + float in, out; + + GetFlowStats(&in, &out); + _snprintf(string, sizeof(string), "Local IP %s, Sockets %i, In %.2f, Out %.2f.\n", m_LocalAddress.ToBaseString(), m_Sockets.CountElements(), in, out); + return string; +} + +void Network::GetFlowStats(float *totalIn, float *totalOut) +{ + *totalIn = 0; + *totalOut = 0; + + NetSocket *sock = (NetSocket *)m_Sockets.GetFirst(); + while (sock) + { + float in, out; + sock->GetFlowStats(&in, &out); + + *totalIn += in; + *totalOut += out; + + sock = (NetSocket *)m_Sockets.GetNext(); + } +} + +void Network::CMD_FakeLoss(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) { + m_System->Printf("Syntax: fakeloss \n"); + m_System->Printf("Current fakeloss is %.2f\n", m_FakeLoss); + return; + } + + m_FakeLoss = atof(params.GetToken(1)); +} + +int Network::GetLastErrorCode() +{ + return WSAGetLastError(); +} + +bool Network::ResolveAddress(char *string, NetAddress *address) +{ + sockaddr sadr; + hostent *h; + char copy[128]; + + address->Clear(); + + if (!string || !strlen(string)) { + return 0; + } + + memset(&sadr, 0, sizeof(sadr)); + ((sockaddr_in *)&sadr)->sin_family = AF_INET; + ((sockaddr_in *)&sadr)->sin_port = 0; + + strcopy(copy, string); + + // Parse port + char *colon = copy; + while (*colon != '\0') + { + if (*colon == ':') + { + *colon = '\0'; + int val = atoi(colon + 1); + ((sockaddr_in *)&sadr)->sin_port = htons(val); + } + colon++; + } + + // Parse address + // Validate IPv4 + if (copy[0] >= '0' && copy[0] <= '9' && strstr(copy, ".")) + { + uint32 ret = inet_addr(copy); + if (ret == INADDR_NONE) { + return false; + } + + ((sockaddr_in *)&sadr)->sin_addr.s_addr = ret; + } + else if (m_NoDNS) + { + // do not resolve address via DNS. + return false; + } + // If we allow the use of DNS, + // let's get the address by the hostname. + else + { + h = gethostbyname(copy); + if (!h || !h->h_addr) { + return false; + } + + ((sockaddr_in *)&sadr)->sin_addr.s_addr = *(uint32 *)h->h_addr; + } + + return address->FromSockadr(&sadr); +} + +char *Network::GetErrorText(int code) +{ + switch (code) + { + case WSAEINTR: return "WSAEINTR"; + case WSAEBADF: return "WSAEBADF"; + case WSAEACCES: return "WSAEACCES"; + case WSAEFAULT: return "WSAEFAULT"; + case WSAEINVAL: return "WSAEINVAL"; + case WSAEMFILE: return "WSAEMFILE"; + case WSAEWOULDBLOCK: return "WSAEWOULDBLOCK"; + case WSAEINPROGRESS: return "WSAEINPROGRESS"; + case WSAEALREADY: return "WSAEALREADY"; + case WSAENOTSOCK: return "WSAENOTSOCK"; + case WSAEDESTADDRREQ: return "WSAEDESTADDRREQ"; + case WSAEMSGSIZE: return "WSAEMSGSIZE"; + case WSAEPROTOTYPE: return "WSAEPROTOTYPE"; + case WSAENOPROTOOPT: return "WSAENOPROTOOPT"; + case WSAEPROTONOSUPPORT: return "WSAEPROTONOSUPPORT"; + case WSAESOCKTNOSUPPORT: return "WSAESOCKTNOSUPPORT"; + case WSAEOPNOTSUPP: return "WSAEOPNOTSUPP"; + case WSAEPFNOSUPPORT: return "WSAEPFNOSUPPORT"; + case WSAEAFNOSUPPORT: return "WSAEAFNOSUPPORT"; + case WSAEADDRINUSE: return "WSAEADDRINUSE"; + case WSAEADDRNOTAVAIL: return "WSAEADDRNOTAVAIL"; + case WSAENETDOWN: return "WSAENETDOWN"; + case WSAENETUNREACH: return "WSAENETUNREACH"; + case WSAENETRESET: return "WSAENETRESET"; + case WSAECONNABORTED: return "WSWSAECONNABORTEDAEINTR"; + case WSAECONNRESET: return "WSAECONNRESET"; + case WSAENOBUFS: return "WSAENOBUFS"; + case WSAEISCONN: return "WSAEISCONN"; + case WSAENOTCONN: return "WSAENOTCONN"; + case WSAESHUTDOWN: return "WSAESHUTDOWN"; + case WSAETOOMANYREFS: return "WSAETOOMANYREFS"; + case WSAETIMEDOUT: return "WSAETIMEDOUT"; + case WSAECONNREFUSED: return "WSAECONNREFUSED"; + case WSAELOOP: return "WSAELOOP"; + case WSAENAMETOOLONG: return "WSAENAMETOOLONG"; + case WSAEHOSTDOWN: return "WSAEHOSTDOWN"; + default: return "UNKNOWN ERROR"; + +#ifdef _WIN32 + case WSASYSNOTREADY: return "WSASYSNOTREADY"; + case WSAVERNOTSUPPORTED: return "WSAVERNOTSUPPORTED"; + case WSANOTINITIALISED: return "WSANOTINITIALISED"; + case WSAEDISCON: return "WSAEDISCON"; + case WSAHOST_NOT_FOUND: return "WSAHOST_NOT_FOUND"; + case WSATRY_AGAIN: return "WSATRY_AGAIN"; + case WSANO_RECOVERY: return "WSANO_RECOVERY"; + case WSANO_DATA: return "WSANO_DATA"; +#endif // _WIN32 + } +} + +void Network::SetName(char *newName) +{ + strcopy(m_Name, newName); +} + +void Network::UpdateStats() +{ + if (m_LastStatsUpdateTime + 0.5 > m_SystemTime) { + return; + } + + NetSocket *sock = (NetSocket *)m_Sockets.GetFirst(); + while (sock) + { + sock->UpdateStats(m_SystemTime); + sock = (NetSocket *)m_Sockets.GetNext(); + } + + m_LastStatsUpdateTime = m_SystemTime; +} + +void Network::ReceiveSignal(ISystemModule *module, unsigned int signal, void *data) +{ + BaseSystemModule::ReceiveSignal(module, signal, data); +} + +void Network::RegisterListener(ISystemModule *module) +{ + BaseSystemModule::RegisterListener(module); +} + +void Network::RemoveListener(ISystemModule *module) +{ + BaseSystemModule::RemoveListener(module); +} + +IBaseSystem *Network::GetSystem() +{ + return BaseSystemModule::GetSystem(); +} + +int Network::GetSerial() +{ + return BaseSystemModule::GetSerial(); +} + +char *Network::GetName() +{ + return BaseSystemModule::GetName(); +} + +int Network::GetState() +{ + return BaseSystemModule::GetState(); +} + +int Network::GetVersion() +{ + return BaseSystemModule::GetVersion(); +} + +IBaseInterface *CreateNetwork() +{ + INetwork *pNetwork = new Network; + return (IBaseInterface *)pNetwork; +} + +#ifndef HOOK_HLTV +EXPOSE_INTERFACE_FN(CreateNetwork, Network, NETWORK_INTERFACE_VERSION); +#endif // HOOK_HLTV diff --git a/rehlds/HLTV/Core/src/Network.h b/rehlds/HLTV/Core/src/Network.h new file mode 100644 index 0000000..c2f68f7 --- /dev/null +++ b/rehlds/HLTV/Core/src/Network.h @@ -0,0 +1,130 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include +#include "BaseSystemModule.h" + +#ifndef _WIN32 +#define WSAEWOULDBLOCK EWOULDBLOCK // Operation would block EAGAIN (11) +#define WSAEMSGSIZE EMSGSIZE // Message too long (90) +#define WSAEADDRNOTAVAIL EADDRNOTAVAIL // Cannot assign requested address (99) +#define WSAEAFNOSUPPORT EAFNOSUPPORT // Address family not supported by protocol (97) +#define WSAECONNRESET ECONNRESET // Connection reset by peer (104) +#define WSAECONNREFUSED ECONNREFUSED // Connection refused (111) +#define WSAEADDRINUSE EADDRINUSE // Address already in use (98) +#define WSAENOBUFS ENOBUFS // No buffer space available (105) + +#define WSAEINTR EINTR // Interrupted system call +#define WSAEBADF EBADF // Bad file number +#define WSAEACCES EACCES // Permission denied +#define WSAEFAULT EFAULT // Bad address +#define WSAEINVAL EINVAL // Invalid argument +#define WSAEMFILE EMFILE // Too many open files +#define WSAEWOULDBLOCK EWOULDBLOCK // Operation would block +#define WSAEINPROGRESS EINPROGRESS // Operation now in progress +#define WSAEALREADY EALREADY // Operation already in progress +#define WSAENOTSOCK ENOTSOCK // Socket operation on non-socket +#define WSAEDESTADDRREQ EDESTADDRREQ // Destination address required +#define WSAEMSGSIZE EMSGSIZE // Message too long +#define WSAEPROTOTYPE EPROTOTYPE // Protocol wrong type for socket +#define WSAENOPROTOOPT ENOPROTOOPT // Protocol not available +#define WSAEPROTONOSUPPORT EPROTONOSUPPORT // Protocol not supported +#define WSAESOCKTNOSUPPORT ESOCKTNOSUPPORT // Socket type not supported +#define WSAEOPNOTSUPP EOPNOTSUPP // Operation not supported on transport endpoint +#define WSAEPFNOSUPPORT EPFNOSUPPORT // Protocol family not supported +#define WSAEAFNOSUPPORT EAFNOSUPPORT // Address family not supported by protocol +#define WSAEADDRINUSE EADDRINUSE // Address already in use +#define WSAEADDRNOTAVAIL EADDRNOTAVAIL // Cannot assign requested address +#define WSAENETDOWN ENETDOWN // Network is down +#define WSAENETUNREACH ENETUNREACH // Network is unreachable +#define WSAENETRESET ENETRESET // Network dropped connection because of reset +#define WSAECONNABORTED ECONNABORTED // Software caused connection abort +#define WSAECONNRESET ECONNRESET // Connection reset by peer +#define WSAENOBUFS ENOBUFS // No buffer space available +#define WSAEISCONN EISCONN // Transport endpoint is already connected +#define WSAENOTCONN ENOTCONN // Transport endpoint is not connected +#define WSAESHUTDOWN ESHUTDOWN // Cannot send after transport endpoint shutdown +#define WSAETOOMANYREFS ETOOMANYREFS // Too many references: cannot splice +#define WSAETIMEDOUT ETIMEDOUT // Connection timed out +#define WSAECONNREFUSED ECONNREFUSED // Connection refused +#define WSAELOOP ELOOP // Too many symbolic links encountered +#define WSAENAMETOOLONG ENAMETOOLONG // File name too long +#define WSAEHOSTDOWN EHOSTDOWN // Host is down +#endif // _WIN32 + +class NetSocket; +class Network: public BaseSystemModule, public INetwork { +public: + Network() {} + virtual ~Network() {} + + bool Init(IBaseSystem *system, int serial, char *name); + void RunFrame(double time); + void ReceiveSignal(ISystemModule *module, unsigned int signal, void *data); + void ExecuteCommand(int commandID, char *commandLine); + void RegisterListener(ISystemModule *module); + void RemoveListener(ISystemModule *module); + IBaseSystem *GetSystem(); + int GetSerial(); + char *GetStatusLine(); + char *GetType(); + char *GetName(); + int GetState(); + int GetVersion(); + void ShutDown(); + + INetSocket *CreateSocket(int port, bool reuse, bool loopback); + bool RemoveSocket(INetSocket *netsocket); + NetAddress *GetLocalAddress(); + bool ResolveAddress(char *string, NetAddress *address); + void GetFlowStats(float *totalIn, float *totalOut); + int GetLastErrorCode(); + char *GetErrorText(int code); + +protected: + enum LocalCommandIDs { CMD_ID_FAKELOSS = 1 }; + void CMD_FakeLoss(char *cmdLine); + + void SetName(char *newName); + void UpdateStats(); + NetSocket *GetSocket(int port); + int SendData(); + int ReceiveData(); + +protected: + friend class NetSocket; + + float m_FakeLoss; + NetAddress m_LocalAddress; + bool m_IsMultihomed; + bool m_NoDNS; + double m_LastStatsUpdateTime; + ObjectList m_Sockets; +}; diff --git a/rehlds/HLTV/Core/src/Server.cpp b/rehlds/HLTV/Core/src/Server.cpp new file mode 100644 index 0000000..24f257d --- /dev/null +++ b/rehlds/HLTV/Core/src/Server.cpp @@ -0,0 +1,2388 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +Server::svc_func_s Server::m_ClientFuncs[] +{ + { svc_bad, "svc_bad", &Server::ParseBad }, + { svc_nop, "svc_nop", &Server::ParseNop }, + { svc_disconnect, "svc_disconnect", &Server::ParseDisconnect }, + { svc_event, "svc_event", &Server::ParseEvent }, + { svc_version, "svc_version", &Server::ParseVersion }, + { svc_setview, "svc_setview", &Server::ParseSetView }, + { svc_sound, "svc_sound", &Server::ParseSound }, + { svc_time, "svc_time", &Server::ParseTime }, + { svc_print, "svc_print", &Server::ParsePrint }, + { svc_stufftext, "svc_stufftext", &Server::ParseStuffText }, + { svc_setangle, "svc_setangle", &Server::ParseSetAngle }, + { svc_serverinfo, "svc_serverinfo", &Server::ParseServerinfo }, + { svc_lightstyle, "svc_lightstyle", &Server::ParseLightStyle }, + { svc_updateuserinfo, "svc_updateuserinfo", &Server::ParseUpdateUserInfo }, + { svc_deltadescription, "svc_deltadescription", &Server::ParseDeltaDescription }, + { svc_clientdata, "svc_clientdata", &Server::ParseClientData }, + { svc_stopsound, "svc_stopsound", &Server::ParseStopSound }, + { svc_pings, "svc_pings", &Server::ParsePings }, + { svc_particle, "svc_particle", &Server::ParseParticle }, + { svc_damage, "svc_damage", &Server::ParseNop }, + { svc_spawnstatic, "svc_spawnstatic", &Server::ParseNop }, + { svc_event_reliable, "svc_event_reliable", &Server::ParseEventReliable }, + { svc_spawnbaseline, "svc_spawnbaseline", &Server::ParseBaseline }, + { svc_temp_entity, "svc_temp_entity", &Server::ParseTempEntity }, + { svc_setpause, "svc_setpause", &Server::ParseSetPause }, + { svc_signonnum, "svc_signonnum", &Server::ParseSignonNum }, + { svc_centerprint, "svc_centerprint", &Server::ParseCenterPrint }, + { svc_killedmonster, "svc_killedmonster", &Server::ParseNop }, + { svc_foundsecret, "svc_foundsecret", &Server::ParseNop }, + { svc_spawnstaticsound, "svc_spawnstaticsound", &Server::ParseSpawnStaticSound }, + { svc_intermission, "svc_intermission", &Server::ParseIntermission }, + { svc_finale, "svc_finale", &Server::ParseFinale }, + { svc_cdtrack, "svc_cdtrack", &Server::ParseCDTrack }, + { svc_restore, "svc_restore", &Server::ParseRestore }, + { svc_cutscene, "svc_cutscene", &Server::ParseCutscene }, + { svc_weaponanim, "svc_weaponanim", &Server::ParseWeaponAnim }, + { svc_decalname, "svc_decalname", &Server::ParseDecalName }, + { svc_roomtype, "svc_roomtype", &Server::ParseRoomType }, + { svc_addangle, "svc_addangle", &Server::ParseAddAngle }, + { svc_newusermsg, "svc_newusermsg", &Server::ParseNewUserMsg }, + { svc_packetentities, "svc_packetentities", &Server::ParsePacketEntities }, + { svc_deltapacketentities, "svc_deltapacketentities", &Server::ParseDeltaPacketEntities }, + { svc_choke, "svc_choke", &Server::ParseChoke }, + { svc_resourcelist, "svc_resourcelist", &Server::ParseResourceList }, + { svc_newmovevars, "svc_newmovevars", &Server::ParseMoveVars }, + { svc_resourcerequest, "svc_resourcerequest", &Server::ParseResourceRequest }, + { svc_customization, "svc_customization", &Server::ParseCustomization }, + { svc_crosshairangle, "svc_crosshairangle", &Server::ParseCrosshairAngle }, + { svc_soundfade, "svc_soundfade", &Server::ParseSoundFade }, + { svc_filetxferfailed, "svc_filetxferfailed", &Server::ParseFileTransferFailed }, + { svc_hltv, "svc_hltv", &Server::ParseHLTV }, + { svc_director, "svc_director", &Server::ParseDirector }, + { svc_voiceinit, "svc_voiceinit", &Server::ParseVoiceInit }, + { svc_voicedata, "svc_voicedata", &Server::ParseVoiceData }, + { svc_sendextrainfo, "svc_sendextrainfo", &Server::ParseSendExtraInfo }, + { svc_timescale, "svc_timescale", &Server::ParseTimeScale }, + { svc_resourcelocation, "svc_resourcelocation", &Server::ParseResourceLocation }, + { svc_sendcvarvalue, "svc_sendcvarvalue", &Server::ParseSendCvarValue }, + { svc_sendcvarvalue2, "svc_sendcvarvalue2", &Server::ParseSendCvarValue2 }, + { svc_endoflist, "End of List", nullptr } +}; + +bool Server::Init(IBaseSystem *system, int serial, char *name) +{ + SetState(SERVER_INITIALIZING); + BaseSystemModule::Init(system, serial, name); + + if (!name) { + SetName(SERVER_INTERFACE_VERSION); + } + + m_FileSystem = m_System->GetFileSystem(); + m_Rate = 10000; + m_UpdateRate = 20; + m_NextAutoRetry = 0; + m_DelayReconnect = true; + m_Protocol = PROTOCOL_VERSION; + + m_UserInfo.SetMaxSize(256); + m_UserInfo.SetValueForKey("name", "HLTV Proxy"); + m_UserInfo.SetValueForKey("cl_lw", "1"); + m_UserInfo.SetValueForKey("cl_lc", "1"); + m_UserInfo.SetValueForKey("*hltv", "1"); + m_UserInfo.SetValueForKey("rate", COM_VarArgs("%i", m_Rate)); + m_UserInfo.SetValueForKey("cl_updaterate", COM_VarArgs("%i", m_UpdateRate)); + + m_IsGameServer = false; + m_IsVoiceBlocking = false; + m_ServerSocket = nullptr; + m_ServerChannel.Create(system); + m_ServerInfo.SetMaxSize(512); + + SetState(SERVER_DISCONNECTED); + + m_ReliableData.Resize(MAX_UDP_PACKET); + m_UnreliableData.Resize(MAX_UDP_PACKET); + m_VoiceData.Resize(MAX_UDP_PACKET); + m_UserMessages.Resize(MAX_UDP_PACKET); + m_ClientData.Resize(MAX_UDP_PACKET); + m_DemoData.Resize(MAX_UDP_PACKET); + + memset(&m_Frame, 0, sizeof(m_Frame)); + + m_Frame.reliableData = m_ReliableData.GetData(); + m_Frame.unreliableData = m_UnreliableData.GetData(); + m_Frame.voiceData = m_VoiceData.GetData(); + m_Frame.userMessages = m_UserMessages.GetData(); + m_Frame.clientData = m_ClientData.GetData(); + m_Frame.demoData = m_DemoData.GetData(); + + m_UnreliableData.Clear(); + m_ReliableData.Clear(); + m_VoiceData.Clear(); + m_UserMessages.Clear(); + m_ClientData.Clear(); + m_DemoData.Clear(); + + strcopy(m_CDKey, "2123437429222"); + strcopy(m_HostName, "Unkown Host"); + + memset(m_SeqNrMap, 0, sizeof(m_SeqNrMap)); + m_validSequence = 0; + m_AutoRetry = true; + m_IsHLTV = true; + m_ForceHLTV = false; + m_IsPaused = false; + + m_World = nullptr; + m_Proxy = nullptr; + m_Director = nullptr; + + m_DemoFile.Reset(); + m_State = MODULE_RUNNING; + m_System->Printf("Server module initialized.\n"); + + return true; +} + +void Server::SetGameDirectory(const char *defaultDir, const char *gameDir) +{ + char temp[MAX_PATH]; + m_FileSystem->RemoveAllSearchPaths(); + + if (gameDir && _stricmp(gameDir, defaultDir) != 0) + { + sprintf(temp, "%s/%s", GetBaseDir(), gameDir); + m_FileSystem->AddSearchPath(temp, "GAME"); + } + + sprintf(temp, "%s/%s", GetBaseDir(), defaultDir); + m_FileSystem->AddSearchPath(temp, "DEFAULTGAME"); + m_FileSystem->AddSearchPath(GetBaseDir(), "ROOT"); +} + +void Server::ExecuteCommand(int commandID, char *commandLine) +{ + m_System->Printf("ERROR! Server::ExecuteCommand: unknown command ID %i.\n", commandID); +} + +void Server::Challenge() +{ + const int MAX_CONNECT_RETRIES = 3; + const float CONNECT_RETRY_INTERVAL = 4.0; + + static float nextRetry = 0; + if (!m_CurrentRetry) + { + nextRetry = 0; + m_CurrentRetry++; + } + + if (nextRetry >= m_SystemTime) { + return; + } + + if (m_CurrentRetry > MAX_CONNECT_RETRIES) + { + m_System->Printf("WARNING! Server::Challenge: Timeout after %i retries\n", MAX_CONNECT_RETRIES); + SetState(SERVER_DISCONNECTED); + ScheduleAutoRetry(); + return; + } + + m_ServerChannel.OutOfBandPrintf("%s\n", "getchallenge"); + m_System->Printf("Challenging %s (%i/%i).\n", m_ServerChannel.GetTargetAddress()->ToString(), m_CurrentRetry, MAX_CONNECT_RETRIES); + nextRetry = m_SystemTime + CONNECT_RETRY_INTERVAL; + m_CurrentRetry++; +} + +void Server::ShutDown() +{ + if (m_State == MODULE_DISCONNECTED) { + return; + } + + Disconnect(); + m_ServerChannel.Close(); + m_ReliableData.Free(); + m_UnreliableData.Free(); + m_VoiceData.Free(); + m_UserMessages.Free(); + m_ClientData.Free(); + m_DemoData.Free(); + m_Listener.Clear(); + m_System->Printf("Server module shutdown.\n"); + + BaseSystemModule::ShutDown(); +} + +void Server::RunFrame(double time) +{ + BaseSystemModule::RunFrame(time); + CheckConnection(); + + if (IsDemoFile()) { + m_DemoFile.ReadDemoPacket(&m_DemoData, &m_DemoInfo); + } + + int packets = 0; + NetPacket *packet; + while ((packet = m_ServerChannel.GetPacket())) + { + if (packet->connectionless) + { + if (!ProcessConnectionlessMessage(&packet->address, &packet->data) && m_Proxy) + { + packet->data.Reset(); + m_Proxy->ProcessConnectionlessMessage(&packet->address, &packet->data); + } + } + else + { + m_Instream = &packet->data; + ProcessMessage(packet->seqnr); + } + + m_ServerChannel.FreePacket(packet); + + if (++packets > 32) { + break; + } + + if (IsDemoFile()) { + m_DemoFile.ReadDemoPacket(&m_DemoData, &m_DemoInfo); + } + } + + switch (m_ServerState) + { + case SERVER_CONNECTED: + break; + case SERVER_DISCONNECTED: + CheckAutoRetry(); + break; + case SERVER_CHALLENGING: + Challenge(); + break; + case SERVER_CONNECTING: + SendConnectPacket(); + break; + case SERVER_RUNNING: + case SERVER_INTERMISSION: + if (m_ServerChannel.IsReadyToSend()) { + SendServerCommands(); + } + break; + default: + m_System->Errorf("Server::RunFrame: not valid state.\n"); + break; + } +} + +bool Server::ProcessConnectionlessMessage(NetAddress *from, BitBuffer *stream) +{ + TokenLine params; + + char *firstToken = stream->ReadString(); + if (!params.SetLine(firstToken)) { + m_System->Printf("WARNING! Server::ProcessConnectionlessMessage: message too long.\n"); + return true; + } + + if (!params.CountToken()) { + m_System->Printf("WARNING! Invalid packet from %s.\n", from->ToString()); + return true; + } + + char *c = params.GetToken(0); + switch (c[0]) + { + case S2C_CONNECTION: + AcceptConnection(); + break; + case S2C_CHALLENGE: + AcceptChallenge(params.GetLine()); + break; + case A2A_PRINT: + m_System->Printf(">%s\n", params.GetLine() + 1); + break; + case S2C_REJECT_BADPASSWORD: + AcceptBadPassword(); + break; + case S2C_REJECT: + AcceptRejection(params.GetLine() + 1); + break; + case S2A_PROXY_LISTEN: + AcceptChallenge(params.GetLine()); + break; + case S2A_PROXY_REDIRECT: + AcceptRedirect(params.GetLine() + 1); + break; + case S2A_INFO: + ParseInfo(stream, false); + break; + case S2A_INFO_DETAILED: + ParseInfo(stream, true); + break; + default: + m_System->DPrintf("Server::ProcessConnectionlessServerMessage: unknown command \"%c\"\n", *c); + return false; + } + + return true; +} + +void Server::ProcessMessage(unsigned int seqNr) +{ + if (!IsConnected()) { + return; + } + + m_Frame.seqnr = seqNr; + + while (m_ServerState != SERVER_DISCONNECTED) + { + if (m_Instream->IsOverflowed()) { + m_System->Printf("WARNING! Server::ProcessMessage: packet read overflow.\n"); + return; + } + + int cmd = m_Instream->ReadByte(); + if (cmd == -1) { + break; + } + + if (cmd > svc_startofusermessages) + { + if (!ParseUserMessage(cmd)) { + break; + } + + continue; + } + + if (!m_ClientFuncs[cmd].func) { + m_System->Printf("TODO! Server::ProcessMessage: missing parsing routine for %s.\n", m_ClientFuncs[cmd].pszname); + return; + } + + (this->*m_ClientFuncs[cmd].func)(); + } + + if (m_Frame.entitynum) + { + if (!m_ReliableData.IsOverflowed()) { + m_Frame.reliableDataSize = m_ReliableData.CurrentSize(); + } + + if (!m_UnreliableData.IsOverflowed()) { + m_Frame.unreliableDataSize = m_UnreliableData.CurrentSize(); + } + + if (!m_VoiceData.IsOverflowed()) { + m_Frame.voiceDataSize = m_VoiceData.CurrentSize(); + } + + if (!m_ClientData.IsOverflowed()) { + m_Frame.clientDataSize = m_ClientData.CurrentSize(); + } + + if (!m_UserMessages.IsOverflowed()) { + m_Frame.userMessagesSize = m_UserMessages.CurrentSize(); + } + + if (!m_DemoData.IsOverflowed() && !m_ForceHLTV) { + m_Frame.demoDataSize = m_DemoData.CurrentSize(); + } + + if (!m_IsHLTV && !m_ForceHLTV) { + m_Frame.demoInfo = &m_DemoInfo; + } + + if (m_ForceHLTV) { + ProcessEntityUpdate(); + } + + m_SeqNrMap[seqNr & 0xFF] = m_World->AddFrame(&m_Frame); + m_validSequence = seqNr; + ClearFrame(true); + } + else + { + ClearFrame(m_World->IsActive() ? false : true); + } +} + +void Server::AcceptChallenge(char *cmdLine) +{ + TokenLine params(cmdLine); + if (m_ServerState != SERVER_CHALLENGING) { + m_System->Printf("WARNING! Server::AcceptChallenge: ignoring unwanted challenge return.\n"); + return; + } + + if (params.CountToken() < 3) { + m_System->Printf("WARNING! Server::AcceptChallenge: invalid challenge format.\n"); + return; + } + + m_ChallengeNumber = atoi(params.GetToken(1)); + m_AuthProtocol = atoi(params.GetToken(2)); + m_AuthProtocol = 2; + + if (m_AuthProtocol == 2) + { + m_System->Printf("Get challenge (HASHEDCDKEY)\n"); + SetState(SERVER_CONNECTING); + } + else + { + m_System->Printf("Invalid auth type\n"); + } + + m_CurrentRetry = 0; +} + +bool Server::IsVoiceBlocking() +{ + return m_IsVoiceBlocking; +} + +void Server::SetVoiceBlocking(bool state) +{ + m_IsVoiceBlocking = state; +} + +void Server::SetRate(int rate) +{ + m_Rate = clamp(rate, 1000, MAX_SERVER_RATE); + SetUserInfo("rate", COM_VarArgs("%i", m_Rate)); +} + +void Server::SetUpdateRate(int updaterate) +{ + m_UpdateRate = clamp(updaterate, 1, MAX_SERVER_UPDATERATE); + SetUserInfo("cl_updaterate", COM_VarArgs("%i", m_UpdateRate)); +} + +void Server::SetState(ServerState newState) +{ + if (newState == m_ServerState) + return; + + bool stateError = false; + switch (newState) + { + case SERVER_INITIALIZING: + break; + case SERVER_DISCONNECTED: + { + if (m_ServerState != SERVER_INITIALIZING) { + m_ServerChannel.Close(); + } + + break; + } + case SERVER_CHALLENGING: + { + if (m_ServerState != SERVER_DISCONNECTED) { + stateError = true; + } + m_ServerChannel.SetUpdateRate(1); + break; + } + case SERVER_AUTHENTICATING: + { + if (m_ServerState != SERVER_CHALLENGING) { + stateError = true; + } + break; + } + case SERVER_CONNECTING: + { + if (m_ServerState != SERVER_AUTHENTICATING + && m_ServerState != SERVER_CHALLENGING) { + stateError = true; + } + break; + } + case SERVER_CONNECTED: + { + if (m_ServerState != SERVER_CONNECTING + && m_ServerState != SERVER_RUNNING + && m_ServerState != SERVER_INTERMISSION && !IsDemoFile()) { + stateError = true; + } + + m_ServerChannel.SetTimeOut(60); + m_ServerChannel.SetUpdateRate(30); + m_ServerChannel.SetKeepAlive(true); + break; + } + case SERVER_RUNNING: + { + if (m_ServerState != SERVER_CONNECTED + && m_ServerState != SERVER_INTERMISSION) { + stateError = true; + } + + m_ServerChannel.SetKeepAlive(false); + m_ServerChannel.SetTimeOut(30); + break; + } + case SERVER_INTERMISSION: + { + if (m_ServerState != SERVER_RUNNING) { + stateError = true; + } + break; + } + default: + stateError = true; + break; + } + + if (stateError) + { + m_System->Errorf("Server::SetState: not valid m_ServerState (%i -> %i).\n", m_ServerState, newState); + return; + } + + m_ServerState = newState; +} + +void Server::SendConnectPacket() +{ + const int MAX_CONNECT_RETRIES = 3; + const float CONNECT_RETRY_INTERVAL = 4.0; + + char data[2048]; + char buffer[1024]; + InfoString protinfo(1024); + + static float nextRetry = 0; + if (!m_CurrentRetry) { + nextRetry = 0; + m_CurrentRetry++; + } + + if (nextRetry >= m_SystemTime) { + return; + } + + if (m_CurrentRetry > MAX_CONNECT_RETRIES) + { + m_System->Printf("WARNING! Server::SendConnectPacket: Timeout \n"); + SetState(SERVER_DISCONNECTED); + ScheduleAutoRetry(); + return; + } + + if (m_AuthProtocol == 2) + { + if (!m_CDKey[0]) + { + m_System->Printf("Invalid CD Key\n"); + SetState(SERVER_DISCONNECTED); + return; + } + + strcopy(buffer, MD5_GetCDKeyHash(m_CDKey)); + } + + protinfo.SetValueForKey("prot", COM_VarArgs("%i", m_AuthProtocol)); + protinfo.SetValueForKey("unique", COM_VarArgs("%i", -1)); + protinfo.SetValueForKey("raw", buffer); + + if (m_AuthProtocol != 2) { + protinfo.SetValueForKey("cdkey", MD5_GetCDKeyHash(m_CDKey)); + } + + _snprintf(data, sizeof(data), "%c%c%c%cconnect %i %i \"%s\" \"%s\"\n", 0xFF, 0xFF, 0xFF, 0xFF, m_Protocol, m_ChallengeNumber, protinfo.GetString(), m_UserInfo.GetString()); + m_ServerSocket->SendPacket(m_ServerChannel.GetTargetAddress(), data, strlen(data)); + + m_System->Printf("Connecting to %s (%i/%i).\n", m_ServerChannel.GetTargetAddress()->ToString(), m_CurrentRetry, MAX_CONNECT_RETRIES); + nextRetry = m_SystemTime + CONNECT_RETRY_INTERVAL; + m_CurrentRetry++; +} + +void Server::AcceptBadPassword() +{ + if (m_ServerState != SERVER_CONNECTING) { + m_System->Printf("WARNING! Server::AcceptBadPassword: ignoring unwanted bad password return.\n"); + return; + } + + m_System->Printf("Bad server password.\n"); + SetState(SERVER_DISCONNECTED); +} + +void Server::AcceptRejection(char *reason) +{ + if (m_ServerState != SERVER_CONNECTING) { + m_System->Printf("WARNING! Server::AcceptRejection: ignoring unwanted rejection message.\n"); + return; + } + + m_System->Printf("Connection rejected: %s\n", reason); + SetState(SERVER_DISCONNECTED); + ScheduleAutoRetry(); +} + +void Server::AcceptConnection() +{ + if (m_ServerState != SERVER_CONNECTING) { + m_System->Printf("WARNING! Server::AcceptConnection: ignoring unwanted connection return.\n"); + return; + } + + SetState(SERVER_CONNECTED); + m_ServerChannel.SetConnected(true); + + m_System->DPrintf("Connection accepted by %s\n", m_ServerAddress.ToString()); + SendStringCommand("new"); + + BaseSystemModule::FireSignal(1, &m_ServerAddress); +} + +void Server::AcceptRedirect(char *toAddress) +{ + if (m_ServerState != SERVER_CONNECTING) { + m_System->Printf("WARNING! Server::AcceptRedirect: ignoring unwanted redirect message.\n"); + return; + } + + INetwork *newtwork = m_ServerSocket->GetNetwork(); + newtwork->ResolveAddress(toAddress, &m_ServerAddress); + m_System->Printf("Redirected to %s\n", m_ServerAddress.ToString()); + + SetState(SERVER_DISCONNECTED); + BaseSystemModule::FireSignal(3, &m_ServerAddress); + Retry(); +} + +void Server::ParsePrint() +{ + char *string = m_Instream->ReadString(); + if (*string < 32) { + string++; + } + + m_System->Printf(">%s\n", string); +} + +void Server::ParseVoiceInit() +{ + unsigned char *start = m_Instream->CurrentByte(); + char *codec = m_Instream->ReadString(); + unsigned char quality = m_Instream->ReadByte(); + + int length = strlen(codec); + if (m_ServerState == SERVER_CONNECTED) { + m_World->AddSignonData(svc_voiceinit, start, length + 2); + } + else if (m_ServerState == SERVER_RUNNING || m_ServerState == SERVER_INTERMISSION) + { + m_ReliableData.WriteByte(svc_voiceinit); + m_ReliableData.WriteString(codec); + m_ReliableData.WriteByte(quality); + } + else { + m_System->Errorf("Server::ParseVoiceInit: unexpected server state.\n"); + } + + m_World->SetVoiceEnabled(codec[0] ? true : false); +} + +void Server::ParseVoiceData() +{ + int index = m_Instream->ReadByte(); + int dataLength = m_Instream->ReadShort(); + + if (!m_IsVoiceBlocking && m_ServerState >= SERVER_RUNNING) + { + m_VoiceData.WriteByte(svc_voicedata); + m_VoiceData.WriteByte(index); + m_VoiceData.WriteShort(dataLength); + m_VoiceData.WriteBuf(m_Instream->CurrentByte(), dataLength); + } + + m_Instream->SkipBytes(dataLength); +} + +void Server::ParseNop() +{ +} + +void Server::ParseBad() +{ + m_System->Printf("WARNING! Server::ParseBad: illegal server message.\n"); + m_Instream->m_Overflowed = true; +} + +void Server::ParseServerinfo() +{ + if (IsDemoFile() && m_ServerState == SERVER_RUNNING) { + Disconnect(); + return; + } + + m_System->DPrintf("Serverinfo packet received.\n"); + + int protocol = m_Instream->ReadLong(); + if (protocol != m_Protocol) + { + m_System->Printf("WARNING! Server returned protocol version %i, not %i\n", protocol, m_Protocol); + Disconnect(); + return; + } + + m_ServerCount = m_Instream->ReadLong(); + m_World->NewGame(m_ServerCount); + m_ServerCRC = m_Instream->ReadLong(); + + unsigned char clientdllmd5[16]; + m_Instream->ReadBuf(sizeof(clientdllmd5), clientdllmd5); + + int maxclients = m_Instream->ReadByte(); + int playernum = m_Instream->ReadByte(); + m_ClientPlayerNum = playernum; + + COM_UnMunge3((byte *)&m_ServerCRC, sizeof(m_ServerCRC), (-1 - playernum) & 0xFF); + if (m_ForceHLTV) + { + if (maxclients == 1) { + maxclients = 2; + } + + playernum = maxclients - 1; + } + + char gamedir[MAX_PATH]; + char levelname[MAX_PATH]; + int gametype = m_Instream->ReadByte(); + + strcopy(gamedir, m_Instream->ReadString()); + strcopy(m_HostName, m_Instream->ReadString()); + strcopy(levelname, m_Instream->ReadString()); + + m_Instream->SkipString(); + + if (m_Instream->ReadByte()) + { + int length = m_Instream->ReadByte(); + m_Instream->SkipBytes(length); + m_Instream->SkipBytes(16); + } + + m_World->SetServerInfo(protocol, m_ServerCRC, clientdllmd5, maxclients, playernum, gametype, gamedir, m_HostName, levelname); + + if (!IsDemoFile()) { + SetGameDirectory("valve", gamedir); + } + + m_World->SetHLTV((m_IsHLTV || m_ForceHLTV) ? true : false); + + SetState(SERVER_CONNECTED); + SendStringCommand("sendres"); +} + +void Server::ParseDeltaDescription() +{ + m_World->ParseDeltaDescription(m_Instream); +} + +void Server::ParseDecalName() +{ + m_ReliableData.WriteByte(svc_decalname); + m_ReliableData.WriteByte(m_Instream->ReadByte()); + m_ReliableData.WriteString(m_Instream->ReadString()); +} + +void Server::ParseMoveVars() +{ + movevars_t movevars; + movevars.gravity = m_Instream->ReadFloat(); + movevars.stopspeed = m_Instream->ReadFloat(); + movevars.maxspeed = m_Instream->ReadFloat(); + movevars.spectatormaxspeed = m_Instream->ReadFloat(); + movevars.accelerate = m_Instream->ReadFloat(); + movevars.airaccelerate = m_Instream->ReadFloat(); + movevars.wateraccelerate = m_Instream->ReadFloat(); + movevars.friction = m_Instream->ReadFloat(); + movevars.edgefriction = m_Instream->ReadFloat(); + movevars.waterfriction = m_Instream->ReadFloat(); + movevars.entgravity = m_Instream->ReadFloat(); + movevars.bounce = m_Instream->ReadFloat(); + movevars.stepsize = m_Instream->ReadFloat(); + movevars.maxvelocity = m_Instream->ReadFloat(); + movevars.zmax = m_Instream->ReadFloat(); + movevars.waveHeight = m_Instream->ReadFloat(); + movevars.footsteps = m_Instream->ReadByte (); + movevars.rollangle = m_Instream->ReadFloat(); + movevars.rollspeed = m_Instream->ReadFloat(); + movevars.skycolor_r = m_Instream->ReadFloat(); + movevars.skycolor_g = m_Instream->ReadFloat(); + movevars.skycolor_b = m_Instream->ReadFloat(); + movevars.skyvec_x = m_Instream->ReadFloat(); + movevars.skyvec_y = m_Instream->ReadFloat(); + movevars.skyvec_z = m_Instream->ReadFloat(); + + strcopy(movevars.skyName, m_Instream->ReadString()); + m_World->SetMoveVars(&movevars); +} + +void Server::ParseCDTrack() +{ + int cdtrack = m_Instream->ReadByte(); + int looptrack = m_Instream->ReadByte(); + + m_World->SetCDInfo(cdtrack, looptrack); +} + +void Server::ParseRestore() +{ + m_Instream->SkipString(); + + int mapCount = m_Instream->ReadByte(); + for (int i = 0; i < mapCount; i++) { + m_Instream->SkipString(); + } +} + +void Server::ParseSetView() +{ + if (m_ServerState == SERVER_CONNECTED) + { + m_World->SetViewEntity(m_Instream->ReadShort()); + } + else if (m_ServerState == SERVER_RUNNING || m_ServerState == SERVER_INTERMISSION) + { + m_ReliableData.WriteByte(svc_setview); + m_ReliableData.WriteBuf(m_Instream->CurrentByte(), 2); + m_ReliableData.SkipBytes(2); + } + else + { + m_System->Errorf("Server::ParseAddAngle: unexpected server state.\n"); + } +} + +void Server::ParseResourceRequest() +{ + if (m_ServerState != SERVER_CONNECTED) { + m_System->Printf("WARNING! Server::ParseResourceRequest: custom resource request not valid - not connected\n"); + return; + } + + int arg = m_Instream->ReadLong(); + if (arg != m_ServerCount) { + m_System->Printf("WARNING! Server::ParseResourceRequest: from old level\n"); + return; + } + + int nStartIndex = m_Instream->ReadLong(); + if (nStartIndex) { + m_System->Printf("WARNING! Server::ParseResourceRequest: custom resource list request out of range\n"); + return; + } + + m_ServerChannel.m_reliableStream.WriteByte(clc_resourcelist); + m_ServerChannel.m_reliableStream.WriteShort(0); +} + +void Server::ParseCrosshairAngle() +{ + m_ReliableData.WriteByte(svc_crosshairangle); + m_ReliableData.WriteChar(m_Instream->ReadChar()); + m_ReliableData.WriteChar(m_Instream->ReadChar()); +} + +void Server::ParseSoundFade() +{ + m_ReliableData.WriteByte(svc_soundfade); + m_ReliableData.WriteByte(m_Instream->ReadByte()); + m_ReliableData.WriteByte(m_Instream->ReadByte()); + m_ReliableData.WriteByte(m_Instream->ReadByte()); + m_ReliableData.WriteByte(m_Instream->ReadByte()); +} + +void Server::ParseNewUserMsg() +{ + int iMsg = m_Instream->ReadByte(); + int iSize = m_Instream->ReadByte(); + if (iSize == 255) { + iSize = -1; + } + + char name[16]; + m_Instream->ReadBuf(sizeof(name), name); + + m_System->DPrintf("Adding user message:%s(%i).\n", name, iMsg); + m_World->AddUserMessage(iMsg, iSize, name); +} + +void Server::ParseWeaponAnim() +{ + m_Instream->SkipBytes(2); +} + +void Server::ParseStuffText() +{ + TokenLine cmdLine; + if (!cmdLine.SetLine(m_Instream->ReadString())) { + m_System->Printf("WARNING! Server::ParseStuffText: command too long.\n"); + return; + } + + char *cmd = cmdLine.GetToken(0); + if (!cmd) { + m_System->Printf("WARNING! Server::ParseStuffText: command is NULL.\n"); + return; + } + + if (!_stricmp(cmd, "fullserverinfo")) + { + char *infostring = cmdLine.GetToken(1); + m_ServerInfo.SetString(infostring); + m_World->SetServerInfoString(infostring); + + if (IsDemoFile()) + { + m_IsGameServer = false; + } + else + { + NetAddress gameAddr; + m_ServerSocket->GetNetwork()->ResolveAddress(m_ServerInfo.ValueForKey("proxy"), &gameAddr); + + if (gameAddr.IsValid()) + { + m_IsGameServer = false; + m_World->SetGameServerAddress(&gameAddr); + } + else + { + m_IsGameServer = true; + m_World->SetGameServerAddress(&m_ServerAddress); + m_World->GetServerInfoString()->SetValueForKey("proxy", m_ServerAddress.ToString()); + } + } + + BaseSystemModule::FireSignal(4); + return; + } + else if (!_stricmp(cmd, "reconnect")) + { + if (IsDemoFile()) + { + Disconnect(); + } + else if (m_DelayReconnect) + { + BaseSystemModule::FireSignal(5); + m_System->DPrintf("Delaying reconnect to broadcast complete game.\n"); + } + else + { + Reconnect(); + } + + m_IsPaused = false; + return; + } + else if (!_stricmp(cmd, "connect")) + { + if (m_ServerSocket && m_World) + { + NetAddress to; + m_ServerSocket->GetNetwork()->ResolveAddress(cmdLine.GetToken(1), &to); + + Connect(m_World, &to, m_ServerSocket); + BaseSystemModule::FireSignal(3); + } + else + { + m_System->Printf("ERROR! Server::ParseStuffText: received redirect while offline.\n"); + Disconnect(); + } + + return; + } + else if (!_stricmp(cmd, "rate") + || !_stricmp(cmd, "cl_updaterate") + || !_stricmp(cmd, "ex_interp") + || !_stricmp(cmd, "cl_cmdrate") + || !_stricmp(cmd, "cl_cmdbackup")) + { + return; + } + + if (m_ServerState == SERVER_CONNECTED) + { + m_World->AddSignonData(svc_stufftext, (unsigned char *)cmdLine.GetLine(), strlen(cmdLine.GetLine()) + 1); + } + else if (m_ServerState == SERVER_RUNNING || m_ServerState == SERVER_INTERMISSION) + { + m_UnreliableData.WriteByte(svc_stufftext); + m_UnreliableData.WriteString(cmdLine.GetLine()); + } + else + { + m_System->Errorf("Server::ParseStuffText: unexpected state.\n"); + } +} + +void Server::ParseUpdateUserInfo() +{ + unsigned char *start = m_Instream->CurrentByte(); + int playernumber = m_Instream->ReadByte(); + int userID = m_Instream->ReadLong(); + char *infostring = m_Instream->ReadString(); + + char hashedcdkey[16]; + m_Instream->ReadBuf(sizeof(hashedcdkey), hashedcdkey); + m_World->UpdatePlayer(playernumber, userID, infostring, hashedcdkey); + + if (m_ServerState == SERVER_CONNECTED) + { + /* do nothing */ + } + else if (m_ServerState == SERVER_RUNNING || m_ServerState == SERVER_INTERMISSION) + { + m_ReliableData.WriteByte(svc_updateuserinfo); + m_ReliableData.WriteBuf(start, m_Instream->CurrentByte() - start); + } + else + { + m_System->Errorf("Server::ParseUpdateUserInfo: unexpected state.\n"); + } +} + +void Server::ParseResourceList() +{ + m_Instream->StartBitMode(); + int total = m_Instream->ReadBits(12); + for (int i = 0; i < total; i++) + { + resource_t resource; + memset(&resource, 0, sizeof(resource)); + + resource.type = (resourcetype_t)m_Instream->ReadBits(4); + + strcopy(resource.szFileName, m_Instream->ReadBitString()); + + resource.nIndex = m_Instream->ReadBits(12); + resource.nDownloadSize = m_Instream->ReadBits(24); + resource.ucFlags = m_Instream->ReadBits(3); + + if (resource.ucFlags & RES_CUSTOM) { + m_Instream->ReadBitData(resource.rgucMD5_hash, sizeof(resource.rgucMD5_hash)); + } + + if (m_Instream->ReadBit()) { + m_Instream->ReadBitData(resource.rguc_reserved, sizeof(resource.rguc_reserved)); + } + + if (strncmp(resource.szFileName, "gfx/temp/", 9) != 0) { + m_World->AddResource(&resource); + } + } + + m_System->Printf("Added %i resources.\n", total); + + if (m_Instream->ReadBit()) + { + if (!IsDemoFile()) { + m_System->Printf("WARNING: HLTV proxy doesn't support file consistency check.\nWARNING: Disable mp_consistency on server."); + } + + while (m_Instream->ReadBit()) + { + if (m_Instream->ReadBit()) + m_Instream->ReadBits(5); + else + m_Instream->ReadBits(10); + } + } + + m_Instream->EndBitMode(); + + CRC32_t mungebuffer = m_ServerCRC; + COM_Munge2((byte *)&mungebuffer, sizeof(mungebuffer), (-1 - m_ServerCount) & 0xFF); + SendStringCommand(COM_VarArgs("spawn %i %i", m_ServerCount, mungebuffer)); +} + +static const int TE_LENGTH[TE_MAX] = +{ + 24, 20, 6, 11, 6, 10, 12, 17, 16, 6, 6, 6, 8, -1, 9, 19, + -2, 10, 16, 24, 24, 24, 10, 11, 16, 19, -2, 12, 16, -1, 19, 17, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, + -2, -2, -2, 2, 10, 14, 12, 14, 9, 5, 17, 13, 24, 9, 17, 7, + 10, 19, 19, 12, 7, 7, 9, 16, 18, 6, 10, 13, 7, 1, 18, 15, +}; + +void Server::ParseTempEntity() +{ + unsigned char *start = m_Instream->CurrentByte(); + int type = m_Instream->ReadByte(); + + if (type >= TE_MAX) { + m_System->Errorf("Server::ParseTempEntity:type >= TE_MAX.\n"); + return; + } + + int length = TE_LENGTH[type]; + if (length == -2) { + m_System->Errorf("Server::ParseTempEntity:invalid type %i.\n", type); + return; + } + + if (length == -1) + { + if (type == TE_BSPDECAL) + { + m_Instream->SkipBytes(8); + if (m_Instream->ReadWord()) + { + m_Instream->SkipBytes(2); + length = 12; + } + else + length = 10; + } + else if (type == TE_TEXTMESSAGE) + { + m_Instream->SkipBytes(5); + if (m_Instream->ReadByte() == 2) + { + m_Instream->SkipBytes(2); + length = 8; + } + else + length = 6; + + m_Instream->SkipBytes(14); + length += m_Instream->SkipString() + 14; + } + else + { + m_System->Printf("ERROR! Server::ParseTempEntity: unknown type with dynamic length.\n"); + } + } + else + { + m_Instream->SkipBytes(length); + } + + if (m_ServerState == SERVER_CONNECTED) + { + m_World->AddSignonData(svc_temp_entity, start, length + 1); + } + else if (m_ServerState == SERVER_RUNNING || m_ServerState == SERVER_INTERMISSION) + { + m_UnreliableData.WriteByte(svc_temp_entity); + m_UnreliableData.WriteBuf(start, length + 1); + } + else { + m_System->Errorf("Server::ParseTempEntity: unexpected server state.\n"); + } +} + +void Server::ParseBaseline() +{ + m_World->ParseBaseline(m_Instream); +} + +void Server::ParseVersion() +{ + int protocol = m_Instream->ReadLong(); + if (protocol != m_Protocol) { + m_System->Printf("Server::ParseVersion: Server is protocol %i instead of %i\n", protocol, PROTOCOL_VERSION); + } +} + +void Server::ParseTime() +{ + m_Time = m_Instream->ReadFloat(); + m_Frame.time = m_Time; +} + +void Server::ParseLightStyle() +{ + int index = m_Instream->ReadByte(); + char *style = m_Instream->ReadString(); + + if (m_ServerState == SERVER_CONNECTED) + { + m_World->AddLightStyle(index, style); + } + else if (m_ServerState == SERVER_RUNNING || m_ServerState == SERVER_INTERMISSION) + { + m_UnreliableData.WriteByte(svc_lightstyle); + m_UnreliableData.WriteByte(index); + m_UnreliableData.WriteString(style); + } + else + { + m_System->Errorf("Server::ParseLightStyle: unexpected server state.\n"); + } +} + +void Server::ParseSetAngle() +{ + if (m_ServerState == SERVER_CONNECTED) + { + m_World->AddSignonData(svc_setangle, m_Instream->CurrentByte(), 6); + } + else if (m_ServerState == SERVER_RUNNING || m_ServerState == SERVER_INTERMISSION) + { + m_UnreliableData.WriteByte(svc_setangle); + m_UnreliableData.WriteBuf(m_Instream->CurrentByte(), 6); + } + else + { + m_System->Errorf("Server::ParseSetAngle: unexpected server state.\n"); + } + + m_Instream->SkipBytes(6); +} + +void Server::ParseAddAngle() +{ + + if (m_ServerState == SERVER_CONNECTED) + { + m_World->AddSignonData(svc_addangle, m_Instream->CurrentByte(), 2); + } + else if (m_ServerState == SERVER_RUNNING || m_ServerState == SERVER_INTERMISSION) + { + m_UnreliableData.WriteByte(svc_addangle); + m_UnreliableData.WriteBuf(m_Instream->CurrentByte(), 2); + } + else + { + m_System->Errorf("Server::ParseAddAngle: unexpected server state.\n"); + } + + m_Instream->SkipBytes(2); +} + +void Server::ParseChoke() +{ +} + +void Server::ParseDisconnect() +{ + char *msg = m_Instream->ReadString(); + if (msg && *msg) { + m_System->Printf("Dropped from %s (%s).\n", m_ServerChannel.GetTargetAddress()->ToString(), msg); + } + else { + m_System->Printf("Dropped from %s.\n", m_ServerChannel.GetTargetAddress()->ToString()); + } + + if (IsConnected()) { + ScheduleAutoRetry(); + } + + Disconnect(); +} + +void Server::ParseFileTransferFailed() +{ + char *name = m_Instream->ReadString(); + if (!name || !name[0]) { + m_System->Printf("WARNING! Server::ParseFileTransferFailed: empty filename.\n"); + return; + } + + m_System->Printf("WARNING! Downloading \"%s\" failed.\n", name); +} + +void Server::ParseSignonNum() +{ + int num = m_Instream->ReadByte(); + m_System->DPrintf("Received signon (%i).\n", num); + + if (IsDemoFile()) + { + m_World->SetServerInfo(&m_DemoFile.m_ServerInfo); + } + else + { + m_ServerChannel.OutOfBandPrintf("details"); + + m_World->UpdateServerInfo(); + SendStringCommand("sendents"); + SendStringCommand("spectate"); + SendStringCommand(COM_VarArgs("VModEnable %d", m_IsVoiceBlocking ? 0 : 1)); + + char string[128]; + strcopy(string, "vban"); + + for (int i = 0; i < MAX_CLIENTS; i++) { + strcat(string, " 0"); + } + + SendStringCommand(string); + } + + SetState(SERVER_RUNNING); +} + +void Server::ParseCustomization() +{ + int index = m_Instream->ReadByte(); + if (index < 0 || index >= MAX_CLIENTS) + { + m_System->DPrintf("Bogus player index (%i) during customization parsing.\n", index); + m_Instream->m_Overflowed = true; + return; + } + + resource_t *resource = (resource_t *)Mem_ZeroMalloc(sizeof(resource_t)); + resource->type = (resourcetype_t)m_Instream->ReadByte(); + + strcopy(resource->szFileName, m_Instream->ReadString()); + + resource->nIndex = m_Instream->ReadShort(); + resource->nDownloadSize = m_Instream->ReadLong(); + resource->ucFlags = m_Instream->ReadByte() & (~RES_WASMISSING); + resource->pNext = nullptr; + + if (resource->ucFlags & RES_CUSTOM) { + m_Instream->ReadBuf(16, resource->rgucMD5_hash); + } + + resource->playernum = index; + m_System->DPrintf("Ignoring player customization (%s).\n", resource->szFileName); + free(resource); +} + +void Server::ClearFrame(bool completely) +{ + if (completely) + { + m_UserMessages.FastClear(); + m_ReliableData.FastClear(); + + m_Frame.reliableDataSize = 0; + m_Frame.userMessagesSize = 0; + } + + m_UnreliableData.FastClear(); + m_VoiceData.FastClear(); + m_ClientData.FastClear(); + m_DemoData.FastClear(); + + m_Frame.time = 0; + m_Frame.entities = 0; + m_Frame.entitynum = 0; + m_Frame.entitiesSize = 0; + m_Frame.events = 0; + m_Frame.eventnum = 0; + m_Frame.eventsSize = 0; + m_Frame.demoInfo = 0; + + memset(&m_DemoInfo, 0, sizeof(m_DemoInfo)); + m_Frame.unreliableDataSize = 0; + m_Frame.voiceDataSize = 0; + m_Frame.clientDataSize = 0; + m_Frame.demoDataSize = 0; +} + +bool Server::ParseUserMessage(int cmd) +{ + UserMsg *usermsg = m_World->GetUserMsg(cmd); + if (!usermsg) { + m_System->Printf("WARNING! Server::ProcessMessage: unknown user message (%i).\n", cmd); + return false; + } + + unsigned char *start = m_Instream->CurrentByte(); + + int length = 0; + int msgSize = usermsg->iSize; + + if (msgSize == -1) { + msgSize = m_Instream->ReadByte(); + length = msgSize + 1; + } + else + length = msgSize; + + if (msgSize > MAX_USER_MSG_DATA) { + m_System->Printf("WARNING! Server::ParseUserMessage: User Msg %d sent too much data (%i bytes)\n", cmd, msgSize); + return false; + } + + m_Instream->SkipBytes(msgSize); + + if (m_ServerState == SERVER_CONNECTED) + { + m_World->AddSignonData(cmd, start, length); + } + else if (m_ServerState == SERVER_RUNNING || m_ServerState == SERVER_INTERMISSION) + { + m_UserMessages.WriteByte(cmd); + m_UserMessages.WriteBuf(start, length); + } + else + { + m_System->Printf("WARNING! Server::ParseUserMessage: unexpected server state.\n"); + } + + if (!strcmp(usermsg->szName, "SayText")) + { + m_System->Printf("%s\n", start + 2); + } + else if (!strcmp(usermsg->szName, "TextMsg")) + { + m_System->DPrintf("%s\n", start + 2); + } + else if (!strcmp(usermsg->szName, "ReqState")) + { + char cmdString[32]; + _snprintf(cmdString, sizeof(cmdString), "VModEnable %d", m_IsVoiceBlocking == 0); + SendStringCommand(cmdString); + + char string[128]; + strcopy(string, "vban"); + + for (int i = 0; i < MAX_CLIENTS; i++) { + strcat(string, " 0"); + } + + SendStringCommand(string); + } + + return true; +} + +void Server::ParsePacketEntities() +{ + int entnum = m_Instream->ReadShort(); + if (entnum > MAX_PACKET_ENTITIES) { + m_System->Errorf("ERROR! Server::ParsePacketEntities: entnum > MAX_PACKET_ENTITIES.\n"); + m_Instream->m_Overflowed = true; + return; + } + + m_Frame.delta = 0; + m_Frame.entitynum = entnum; + m_Frame.entitiesSize = sizeof(entity_state_t) * entnum; + m_Frame.entities = m_EntityBuffer; + + memset(m_EntityBuffer, 0, m_Frame.entitiesSize); + m_World->UncompressEntitiesFromStream(&m_Frame, m_Instream); +} + +void Server::ParseDeltaPacketEntities() +{ + int entnum = m_Instream->ReadShort(); + if (entnum > MAX_PACKET_ENTITIES) { + m_System->Errorf("Server::ParsePacketEntities: entnum > MAX_PACKET_ENTITIES.\n"); + } + + m_Frame.delta = 0; + m_Frame.entitynum = entnum; + m_Frame.entitiesSize = sizeof(entity_state_t) * entnum; + m_Frame.entities = m_EntityBuffer; + memset(m_EntityBuffer, 0, m_Frame.entitiesSize); + + int from = m_Instream->ReadByte(); + if (!m_World->UncompressEntitiesFromStream(&m_Frame, m_Instream, m_SeqNrMap[from])) { + m_Instream->m_Overflowed = true; + } +} + +void Server::SendServerCommands() +{ + if (m_validSequence && m_ServerState >= SERVER_RUNNING) + { + m_ServerChannel.m_unreliableStream.WriteByte(clc_delta); + m_ServerChannel.m_unreliableStream.WriteByte(m_validSequence & 0xFF); + } + + m_ServerChannel.TransmitOutgoing(); +} + +void Server::ParseSound() +{ + m_Instream->StartBitMode(); + + vec3_t pos; + unsigned char *start = this->m_Instream->m_CurByte; + int field_mask = m_Instream->ReadBits(9); + + if (field_mask & SND_FL_VOLUME) { + m_Instream->ReadBits(8); + } + + if (field_mask & SND_FL_ATTENUATION) { + m_Instream->ReadBits(8); + } + + m_Instream->ReadBits(22); + + if (field_mask & SND_FL_LARGE_INDEX) { + m_Instream->ReadBits(8); + } + + m_Instream->ReadBitVec3Coord(pos); + + if (field_mask & SND_FL_PITCH) { + m_Instream->ReadBits(8); + } + + m_Instream->EndBitMode(); + + m_UnreliableData.WriteByte(6); + m_UnreliableData.WriteBuf(start, m_Instream->m_CurByte - start); +} + +void Server::ParseEvent() +{ + m_Instream->StartBitMode(); + + m_Frame.events = m_Instream->m_CurByte; + m_Frame.eventnum = m_Instream->ReadBits(5); + + for (unsigned int i = 0; i < m_Frame.eventnum; i++) + { + m_Instream->SkipBits(10); + if (m_Instream->ReadBit()) + m_Instream->SkipBits(11); + + if (m_Instream->ReadBit()) + m_World->ParseEvent(m_Instream); + + if (m_Instream->ReadBit()) + m_Instream->SkipBits(16); + } + + m_Instream->EndBitMode(); + m_Frame.eventsSize = m_Instream->CurrentByte() - m_Frame.events; +} + +void Server::ParseStopSound() +{ + int i = m_Instream->ReadShort(); + + m_UnreliableData.WriteByte(svc_stopsound); + m_UnreliableData.WriteShort(i); +} + +void Server::ParsePings() +{ + m_Instream->StartBitMode(); + + unsigned char *start = m_Instream->CurrentByte(); + while (m_Instream->ReadBit()) { + m_Instream->ReadBits(24); + } + + m_Instream->EndBitMode(); + + m_UnreliableData.WriteByte(svc_pings); + m_UnreliableData.WriteBuf(start, m_Instream->CurrentByte() - start); +} + +void Server::ParseEventReliable() +{ + m_Instream->StartBitMode(); + + unsigned char *start = m_Instream->CurrentByte(); + + m_Instream->SkipBits(10); + m_World->ParseEvent(m_Instream); + + if (m_Instream->ReadBit()) { + m_Instream->SkipBits(16); + } + + m_Instream->EndBitMode(); + + m_ReliableData.WriteByte(svc_event_reliable); + m_ReliableData.WriteBuf(start, m_Instream->CurrentByte() - start); +} + +void Server::ParseSpawnStaticSound() +{ + if (m_ServerState == SERVER_CONNECTED) { + m_World->AddSignonData(svc_spawnstaticsound, m_Instream->CurrentByte(), 14); + } + else if (m_ServerState == SERVER_RUNNING || m_ServerState == SERVER_INTERMISSION) + { + m_UnreliableData.WriteByte(svc_spawnstaticsound); + m_UnreliableData.WriteBuf(m_Instream->CurrentByte(), 14); + } + else { + m_System->Errorf("Server::ParseSpawnStaticSound: unexpected server state.\n"); + } + + m_Instream->SkipBytes(14); +} + +void Server::ParseRoomType() +{ + m_ReliableData.WriteByte(svc_roomtype); + m_ReliableData.WriteShort(m_Instream->ReadShort()); +} + +void Server::ParseParticle() +{ + m_UnreliableData.WriteByte(svc_particle); + m_UnreliableData.WriteBuf(m_Instream->CurrentByte(), 11); + + m_Instream->SkipBytes(11); +} + +InfoString *Server::GetServerInfoString() +{ + return &m_ServerInfo; +} + +int Server::GetRate() +{ + return m_Rate; +} + +int Server::GetUpdateRate() +{ + return m_UpdateRate; +} + +char *Server::GetHostName() +{ + return m_HostName; +} + +int Server::GetProtocol() +{ + return m_Protocol; +} + +bool Server::SetProtocol(int version) +{ + if (version != 46 + && version != 47 + && version != PROTOCOL_VERSION) { + return false; + } + + m_Protocol = version; + return true; +} + +void Server::SetPlayerName(char *newName) +{ + COM_RemoveEvilChars(newName); + SetUserInfo("name", newName); +} + +char *Server::GetPlayerName() +{ + return m_UserInfo.ValueForKey("name"); +} + +char *Server::GetType() +{ + return SERVER_INTERFACE_VERSION; +} + +IWorld *Server::GetWorld() +{ + return m_World; +} + +NetAddress *Server::GetAddress() +{ + if (IsDemoFile()) { + return nullptr; + } + + return &m_ServerAddress; +} + +char *Server::GetDemoFileName() +{ + if (!IsDemoFile()) { + return nullptr; + } + + return m_DemoFile.GetFileName(); +} + +bool Server::GetAutoRetry() +{ + return m_AutoRetry; +} + +float Server::GetPacketLoss() +{ + return m_ServerChannel.GetLoss(); +} + +char *Server::GetStatusLine() +{ + float in, out; + static char string[256]; + + switch (m_ServerState) + { + case SERVER_INITIALIZING: + _snprintf(string, sizeof(string), "Initializing.\n"); + break; + case SERVER_DISCONNECTED: + _snprintf(string, sizeof(string), "Disconnected.\n"); + break; + case SERVER_CHALLENGING: + _snprintf(string, sizeof(string), "challenging %s.\n", m_ServerChannel.GetTargetAddress()->ToString()); + break; + case SERVER_AUTHENTICATING: + _snprintf(string, sizeof(string), "Authenticating.\n"); + break; + case SERVER_CONNECTING: + _snprintf(string, sizeof(string), "Connecting to %s.\n", m_ServerChannel.GetTargetAddress()->ToString()); + break; + case SERVER_CONNECTED: + case SERVER_RUNNING: + m_ServerChannel.GetFlowStats(&in, &out); + _snprintf(string, sizeof(string), "Connected to %s, Time %.0f, In %.2f, Out %.2f.\n", m_ServerChannel.GetTargetAddress()->ToString(), m_SystemTime - m_ServerChannel.m_connect_time, in, out); + break; + case SERVER_INTERMISSION: + _snprintf(string, sizeof(string), "Intermission (%s).\n", m_ServerChannel.GetTargetAddress()->ToString()); + break; + default: + m_System->Errorf("Server::GetStatusLine: not valid state.\n"); + break; + } + + return string; +} + +void Server::ParseHLTV() +{ + unsigned char *start = m_Instream->CurrentByte(); + unsigned char cmd = m_Instream->ReadByte(); + + switch (cmd) + { + case HLTV_ACTIVE: + m_IsHLTV = true; + m_ForceHLTV = false; + break; + case HLTV_STATUS: + { + if (m_Proxy) { + m_Proxy->ParseStatusMsg(m_Instream); + return; + } + + m_Instream->SkipBytes(10); + break; + } + case HLTV_LISTEN: + m_System->Printf("WARNING! Server::ParseHLTV: unexpected \"listen\" command.\n"); + break; + default: + m_System->Printf("WARNING! Server::ParseHLTV: unknown director command.\n"); + m_Instream->m_Overflowed = true; + break; + } +} + +void Server::SetProxy(IProxy *proxy) +{ + m_Proxy = proxy; + m_Director = proxy->GetDirector(); +} + +void Server::SetDirector(IDirector *director) +{ + m_Director = director; +} + +void Server::ParseDirector() +{ + unsigned char *start = m_Instream->CurrentByte(); + int length = m_Instream->ReadByte(); + + if (m_Director) + { + DirectorCmd cmd; + cmd.ReadFromStream(m_Instream); + cmd.SetTime(m_Time); + + m_Director->AddCommand(&cmd); + return; + } + + m_Instream->SkipBytes(length); + + if (m_ServerState == SERVER_CONNECTED) { + m_World->AddSignonData(svc_director, start, length + 1); + } + else if (m_ServerState == SERVER_RUNNING || m_ServerState == SERVER_INTERMISSION) + { + m_ReliableData.WriteByte(svc_director); + m_ReliableData.WriteBuf(start, length + 1); + } + else { + m_System->Errorf("Server::ParseDirector: unexpected server state.\n"); + } +} + +void Server::ParseInfo(BitBuffer *stream, bool detailed) +{ + serverinfo_t si; + + strcopy(si.address, m_ServerChannel.GetTargetAddress()->ToBaseString()); + strcopy(si.name, stream->ReadString()); + strcopy(si.map, stream->ReadString()); + strcopy(si.gamedir, stream->ReadString()); + strcopy(si.description, stream->ReadString()); + + si.activePlayers = stream->ReadByte(); + si.maxPlayers = stream->ReadByte(); + si.protocol = stream->ReadByte(); + + if (detailed) + { + si.type = stream->ReadByte(); + si.os = stream->ReadByte(); + si.pw = stream->ReadByte(); + si.mod = stream->ReadByte() != 0; + + if (si.mod) + { + strcopy(si.url_info, stream->ReadString()); + strcopy(si.url_dl, stream->ReadString()); + strcopy(si.hlversion, stream->ReadString()); + + si.ver = stream->ReadLong(); + si.size = stream->ReadLong(); + si.svonly = stream->ReadByte() != 0; + si.cldll = stream->ReadByte() != 0; + } + else + { + si.url_info[0] = '\0'; + si.url_dl[0] = '\0'; + + si.ver = 0; + si.size = 0; + } + + stream->ReadByte(); + } + else + { + si.type = '?'; + si.os = '?'; + si.pw = '?'; + si.mod = 0; + } + + if (si.activePlayers >= MAX_PROXY_CLIENTS || si.maxPlayers >= MAX_PROXY_CLIENTS) + { + si.activePlayers = stream->ReadLong(); + si.maxPlayers = stream->ReadLong(); + } + + if (m_World) { + m_World->SetServerInfo(&si); + } +} + +void Server::Reconnect() +{ + if (IsConnected()) + { + m_System->DPrintf("Reconnecting...\n"); + SetState(SERVER_CONNECTED); + + m_ServerChannel.Clear(); + m_validSequence = 0; + + BaseSystemModule::FireSignal(2); + SendStringCommand("new"); + } +} + +void Server::Disconnect() +{ + if (m_ServerState == SERVER_DISCONNECTED) { + return; + } + + BaseSystemModule::FireSignal(6); + m_ServerChannel.ClearFragments(); + + if (IsDemoFile()) + { + m_DemoFile.StopPlayBack(); + m_System->Printf("Demo playback stopped.\n"); + } + else + { + for (int i = 0; i < 3; i++) + { + SendStringCommand("dropclient\n"); + m_ServerChannel.TransmitOutgoing(); + } + + m_System->Printf("Disconnected.\n"); + } + + m_World->FinishGame(); + Reset(); + ScheduleAutoRetry(); + m_ServerChannel.Close(); +} + +void Server::ParseClientData() +{ + if (m_IsHLTV) { + return; + } + + unsigned int fromSeqNr = 0; + m_Instream->StartBitMode(); + + if (m_Instream->ReadBit()) { + fromSeqNr = m_SeqNrMap[m_Instream->ReadByte()]; + } + + m_World->ParseClientData(m_Instream, fromSeqNr, &m_ClientData, &m_ClientDataStruct); + m_Instream->EndBitMode(); +} + +void Server::ParseIntermission() +{ + SetState(SERVER_INTERMISSION); + + if (!IsDemoFile()) { + m_ReliableData.WriteByte(svc_intermission); + } +} + +void Server::ParseFinale() +{ + m_ReliableData.WriteByte(svc_finale); + m_ReliableData.WriteString(m_Instream->ReadString()); +} + +void Server::ParseCutscene() +{ + m_ReliableData.WriteByte(svc_cutscene); + m_ReliableData.WriteString(m_Instream->ReadString()); +} + +void Server::SendUserVar(char *key, char *value) +{ + char cmdString[1024]; + + if (!IsConnected()) { + return; + } + + if (*value) + { + _snprintf(cmdString, sizeof(cmdString), "setinfo \"%s\" \"%s\"\n", key, value); + SendStringCommand(cmdString); + } +} + +bool Server::IsConnected() +{ + if (m_ServerState >= SERVER_CONNECTED) { + return true; + } + + return false; +} + +bool Server::IsDemoFile() +{ + return m_DemoFile.IsPlaying(); +} + +bool Server::IsGameServer() +{ + return m_IsGameServer; +} + +bool Server::IsRelayProxy() +{ + return !m_IsGameServer; +} + +void Server::ParseCenterPrint() +{ + char *string = m_Instream->ReadString(); + if (m_ServerState == SERVER_CONNECTED) + { + m_World->AddSignonData(svc_centerprint, (unsigned char *)string, strlen(string) + 1); + } + else if (m_ServerState == SERVER_RUNNING || m_ServerState == SERVER_INTERMISSION) + { + m_UnreliableData.WriteByte(svc_centerprint); + m_UnreliableData.WriteString(string); + } + else { + m_System->Errorf("Server::ParseStuffText: unexpected state.\n"); + } +} + +void Server::CheckAutoRetry() +{ + if (m_NextAutoRetry > 0 && m_NextAutoRetry < m_SystemTime) + { + m_NextAutoRetry = 0; + + Retry(); + m_System->Printf("Automatic connection retry...\n"); + } +} + +void Server::ScheduleAutoRetry() +{ + if (m_AutoRetry) { + m_NextAutoRetry = m_SystemTime + 4; + return; + } + + m_NextAutoRetry = 0; +} + +bool Server::Connect(IWorld *world, NetAddress *address, INetSocket *socket) +{ + if (!socket || !world || !address) { + m_System->Printf("ERROR! Server::Connect: invalid parameters.\n"); + return false; + } + + INetwork *network = socket->GetNetwork(); + if (address->Equal(network->GetLocalAddress())) { + m_System->Printf("ERROR! Server::Connect: can't connect to myself.\n"); + return false; + } + + Reset(); + m_ServerAddress.FromNetAddress(address); + + m_ServerSocket = socket; + m_ServerChannel.Create(m_System, m_ServerSocket, &m_ServerAddress); + m_World = world; + + SetState(SERVER_CHALLENGING); + return true; +} + +bool Server::LoadDemo(IWorld *world, char *filename, bool forceHLTV, bool continuous) +{ + Reset(); + m_World = world; + m_ServerSocket = nullptr; + + m_ServerChannel.Create(m_System); + m_DemoFile.Init(m_World, this, &m_ServerChannel); + + if (!m_DemoFile.LoadDemo(filename)) { + return false; + } + + m_DemoFile.SetContinuous(continuous); + + m_IsHLTV = false; + m_ForceHLTV = forceHLTV; + + SetState(SERVER_CONNECTED); + m_ServerChannel.SetConnected(true); + + return true; +} + +void Server::SetUserInfo(char *key, char *value) +{ + if (!key || !value) { + return; + } + + m_UserInfo.SetValueForKey(key, value); + SendUserVar(key, value); +} + +void Server::ParseSetPause() +{ + m_IsPaused = m_Instream->ReadByte() != 0; + if (!m_IsPaused) { + m_validSequence = 0; + } + + m_World->SetPaused(m_IsPaused); +} + +void Server::SendStringCommand(char *command) +{ + if (!m_ServerChannel.IsFakeChannel()) + { + m_ServerChannel.m_reliableStream.WriteByte(clc_stringcmd); + m_ServerChannel.m_reliableStream.WriteString(command); + } +} + +void Server::SendHLTVCommand(BitBuffer *msg) +{ + if (!m_ServerChannel.IsFakeChannel()) + { + m_ServerChannel.m_reliableStream.WriteByte(clc_hltv); + m_ServerChannel.m_reliableStream.WriteBuf(msg->GetData(), msg->CurrentSize()); + } +} + +void Server::ParseSendExtraInfo() +{ + char *clientfallback = m_Instream->ReadString(); + int allowCheats = m_Instream->ReadByte(); + + m_World->SetExtraInfo(clientfallback, allowCheats); +} + +void Server::ParseTimeScale() +{ + float timescale = m_Instream->ReadFloat(); + if (m_Director) { + return; + } + + if (m_ServerState == SERVER_CONNECTED) + { + m_System->Printf("Server::ParseTimeScale: invalid during signon.\n"); + } + else if (m_ServerState == SERVER_RUNNING && m_ServerState == SERVER_INTERMISSION) + { + m_ReliableData.WriteByte(svc_timescale); + m_ReliableData.WriteFloat(timescale); + } + else { + m_System->Errorf("Server::ParseTimeScale: unexpected server state.\n"); + } +} + +void Server::ParseResourceLocation() +{ + char *url = m_Instream->ReadString(); + if (url) { + strcopy(g_DownloadURL, url); + } +} + +float Server::GetTime() +{ + return m_Time; +} + +void Server::CheckConnection() +{ + if (m_ServerState == SERVER_DISCONNECTED) { + return; + } + + if (!m_ServerChannel.IsTimedOut() && !m_ServerChannel.IsCrashed()) { + return; + } + + m_System->Printf("Server connection lost (%s).\n", m_ServerChannel.IsCrashed() ? "data mismatch" : "time out"); + + Disconnect(); + ScheduleAutoRetry(); +} + +void Server::StopRetry() +{ + m_NextAutoRetry = 0; +} + +void Server::Retry() +{ + if (m_ServerAddress.IsValid() && m_ServerSocket && m_World) + { + Connect(m_World, &m_ServerAddress, m_ServerSocket); + BaseSystemModule::FireSignal(7); + } + else + { + m_System->DPrintf("HLTV proxy wasn't connected before.\n"); + } +} + +void Server::SetAutoRetry(bool state) +{ + if (!state) { + m_NextAutoRetry = 0; + } + + m_AutoRetry = state; +} + +void Server::SetDelayReconnect(bool state) +{ + m_DelayReconnect = state; +} + +void Server::Reset() +{ + m_ServerChannel.ClearFragments(); + m_ServerChannel.Close(); + + ClearFrame(true); + memset(m_SeqNrMap, 0, sizeof(m_SeqNrMap)); + + m_validSequence = 0; + m_CurrentRetry = 0; + m_IsHLTV = true; + m_ForceHLTV = false; + m_IsPaused = false; + m_IsGameServer = false; + m_Time = 0; + + m_DemoFile.Reset(); + SetState(SERVER_DISCONNECTED); +} + +char *Server::GetCmdName(int cmd) +{ + static char description[64]; + if (cmd > svc_startofusermessages && m_World) + { + UserMsg *usermsg = m_World->GetUserMsg(cmd); + if (usermsg) + { + _snprintf(description, sizeof(description), "UserMsg:%s", usermsg->szName); + } + else + { + _snprintf(description, sizeof(description), "Invalid UserMsg"); + } + } + else + { + _snprintf(description, sizeof(description), "EngMsg:%s", m_ClientFuncs[cmd].pszname); + } + + return description; +} + +void Server::SetName(char *newName) +{ + strcopy(m_Name, newName); +} + +void Server::ProcessEntityUpdate() +{ + if (!m_Frame.entities) { + return; + } + + for (unsigned int num = 0; num < m_Frame.entitynum; num++) + { + entity_state_t *state = &((entity_state_t *)m_Frame.entities)[num]; + if (m_ClientPlayerNum == state->number - 1) + { + state->origin[0] = m_ClientDataStruct.origin[0]; + state->origin[1] = m_ClientDataStruct.origin[1]; + state->origin[2] = m_ClientDataStruct.origin[2]; + } + } +} + +void Server::ParseSendCvarValue() +{ + char *name = m_Instream->ReadString(); +} + +void Server::ParseSendCvarValue2() +{ + int requestID = m_Instream->ReadLong(); + char *name = m_Instream->ReadString(); +} + +void Server::ReceiveSignal(ISystemModule *module, unsigned int signal, void *data) +{ + BaseSystemModule::ReceiveSignal(module, signal, data); +} + +void Server::RegisterListener(ISystemModule *module) +{ + BaseSystemModule::RegisterListener(module); +} + +void Server::RemoveListener(ISystemModule *module) +{ + BaseSystemModule::RemoveListener(module); +} + +IBaseSystem *Server::GetSystem() +{ + return BaseSystemModule::GetSystem(); +} + +int Server::GetSerial() +{ + return BaseSystemModule::GetSerial(); +} + +char *Server::GetName() +{ + return BaseSystemModule::GetName(); +} + +int Server::GetState() +{ + return BaseSystemModule::GetState(); +} + +int Server::GetVersion() +{ + return BaseSystemModule::GetVersion(); +} + +IBaseInterface *CreateServer() +{ + IServer *pServer = new Server; + return (IBaseInterface *)pServer; +} + +#ifndef HOOK_HLTV +EXPOSE_INTERFACE_FN(CreateServer, Server, SERVER_INTERFACE_VERSION); +#endif // HOOK_HLTV diff --git a/rehlds/HLTV/Core/src/Server.h b/rehlds/HLTV/Core/src/Server.h new file mode 100644 index 0000000..f238424 --- /dev/null +++ b/rehlds/HLTV/Core/src/Server.h @@ -0,0 +1,266 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include +#include "BaseSystemModule.h" +#include "TokenLine.h" + +class IWorld; +class IProxy; +class IDirector; +class IFileSystem; +class NetPacket; +class INetSocket; + +#define MAX_SERVER_RATE 20000 +#define MAX_SERVER_UPDATERATE 100 + +#define TE_MAX 128 + +// Sound flags +enum +{ + SND_FL_VOLUME = BIT(0), // send volume + SND_FL_ATTENUATION = BIT(1), // send attenuation + SND_FL_LARGE_INDEX = BIT(2), // send sound number as short instead of byte + SND_FL_PITCH = BIT(3), // send pitch + SND_FL_SENTENCE = BIT(4), // set if sound num is actually a sentence num + SND_FL_STOP = BIT(5), // stop the sound + SND_FL_CHANGE_VOL = BIT(6), // change sound vol + SND_FL_CHANGE_PITCH = BIT(7), // change sound pitch + SND_FL_SPAWNING = BIT(8) // we're spawning, used in some cases for ambients (not sent across network) +}; + +class Server: public IServer, public BaseSystemModule { +public: + Server() {} + virtual ~Server() {} + + bool Init(IBaseSystem *system, int serial, char *name); + void RunFrame(double time); + void ReceiveSignal(ISystemModule *module, unsigned int signal, void *data); + void ExecuteCommand(int commandID, char *commandLine); + void RegisterListener(ISystemModule *module); + void RemoveListener(ISystemModule *module); + IBaseSystem *GetSystem(); + int GetSerial(); + char *GetStatusLine(); + char *GetType(); + char *GetName(); + int GetState(); + int GetVersion(); + void ShutDown(); + + bool Connect(IWorld *world, NetAddress *address, INetSocket *socket); + bool LoadDemo(IWorld *world, char *filename, bool forceHLTV, bool continuous); + void Reconnect(); + void Disconnect(); + void Retry(); + void StopRetry(); + void SendStringCommand(char *command); + void SendHLTVCommand(BitBuffer *msg); + void SetPlayerName(char *newName); + void SetProxy(IProxy *proxy); + void SetDirector(IDirector *director); + void SetDelayReconnect(bool state); + void SetAutoRetry(bool state); + void SetVoiceBlocking(bool state); + void SetRate(int rate); + void SetUpdateRate(int updaterate); + void SetUserInfo(char *key, char *value); + bool SetProtocol(int version); + void SetGameDirectory(const char *defaultDir, const char *gameDir); + bool IsConnected(); + bool IsDemoFile(); + bool IsGameServer(); + bool IsRelayProxy(); + bool IsVoiceBlocking(); + int GetRate(); + int GetUpdateRate(); + char *GetPlayerName(); + InfoString *GetServerInfoString(); + + float GetTime(); + IWorld *GetWorld(); + char *GetDemoFileName(); + NetAddress *GetAddress(); + bool GetAutoRetry(); + char *GetHostName(); + float GetPacketLoss(); + int GetProtocol(); + +private: +public: + void CheckAutoRetry(); + void CheckConnection(); + void ScheduleAutoRetry(); + void AcceptConnection(); + void AcceptBadPassword(); + void AcceptRejection(char *reason); + void AcceptRedirect(char *toAddress); + void SendConnectPacket(); + void SendServerCommands(); + + enum ServerState { + SERVER_UNDEFINED, + SERVER_INITIALIZING, + SERVER_DISCONNECTED, + SERVER_CHALLENGING, + SERVER_AUTHENTICATING, + SERVER_CONNECTING, + SERVER_CONNECTED, + SERVER_RUNNING, + SERVER_INTERMISSION, + }; + + void SetState(ServerState newState); + void Challenge(); + void Reset(); + void AcceptChallenge(char *cmdLine); + void SendUserVar(char *key, char *value); + char *GetCmdName(int cmd); + void SetName(char *newName); + void ProcessMessage(unsigned int seqNr); + void ProcessEntityUpdate(); + bool ProcessConnectionlessMessage(NetAddress *from, BitBuffer *stream); + void ClearFrame(bool completely); + void ParseHLTV(); + void ParseDirector(); + void ParseFileTransferFailed(); + void ParseInfo(BitBuffer *stream, bool detailed); + void ParseParticle(); + void ParseRoomType(); + void ParseSpawnStaticSound(); + void ParseEventReliable(); + void ParsePings(); + void ParseStopSound(); + void ParseEvent(); + void ParseSound(); + void ParseDeltaPacketEntities(); + void ParsePacketEntities(); + bool ParseUserMessage(int cmd); + void ParseCustomization(); + void ParseCrosshairAngle(); + void ParseSoundFade(); + void ParseSignonNum(); + void ParseDisconnect(); + void ParseChoke(); + void ParseSetAngle(); + void ParseAddAngle(); + void ParseLightStyle(); + void ParseTime(); + void ParseVersion(); + void ParseBaseline(); + void ParseTempEntity(); + void ParseResourceList(); + void ParseUpdateUserInfo(); + void ParseStuffText(); + void ParseNewUserMsg(); + void ParseResourceRequest(); + void ParseSetView(); + void ParseCDTrack(); + void ParseRestore(); + void ParseMoveVars(); + void ParseDeltaDescription(); + void ParseServerinfo(); + void ParseBad(); + void ParseNop(); + void ParsePrint(); + void ParseVoiceInit(); + void ParseVoiceData(); + void ParseTimeScale(); + void ParseSendExtraInfo(); + void ParseCenterPrint(); + void ParseSetPause(); + void ParseCutscene(); + void ParseWeaponAnim(); + void ParseDecalName(); + void ParseFinale(); + void ParseIntermission(); + void ParseClientData(); + void ParseResourceLocation(); + void ParseSendCvarValue(); + void ParseSendCvarValue2(); + +protected: + struct svc_func_s { + svc_commands_e opcode; + char *pszname; + void (Server::*func)(); + }; + static svc_func_s m_ClientFuncs[]; + + IWorld *m_World; + IProxy *m_Proxy; + IDirector *m_Director; + IFileSystem *m_FileSystem; + int m_ChallengeNumber; + + INetSocket *m_ServerSocket; + NetChannel m_ServerChannel; + NetAddress m_ServerAddress; + + int m_ServerState; + char m_HostName[MAX_PATH]; + int m_CurrentRetry; + InfoString m_UserInfo; + char m_CDKey[32]; + int m_AuthProtocol; + frame_t m_Frame; + unsigned char m_EntityBuffer[87040]; + int m_Rate; + int m_UpdateRate; + float m_Time; + BitBuffer m_ReliableData; + BitBuffer m_UnreliableData; + BitBuffer m_VoiceData; + BitBuffer m_UserMessages; + BitBuffer m_ClientData; + BitBuffer *m_Instream; + int m_validSequence; + bool m_AutoRetry; + double m_NextAutoRetry; + bool m_IsHLTV; + bool m_ForceHLTV; + bool m_IsGameServer; + bool m_IsPaused; + bool m_IsVoiceBlocking; + DemoFile m_DemoFile; + demo_info_t m_DemoInfo; + BitBuffer m_DemoData; + CRC32_t m_ServerCRC; + int m_ServerCount; + int m_ClientPlayerNum; + int m_Protocol; + clientdata_t m_ClientDataStruct; + InfoString m_ServerInfo; + bool m_DelayReconnect; + unsigned int m_SeqNrMap[256]; +}; diff --git a/rehlds/HLTV/Core/src/World.cpp b/rehlds/HLTV/Core/src/World.cpp new file mode 100644 index 0000000..54e0669 --- /dev/null +++ b/rehlds/HLTV/Core/src/World.cpp @@ -0,0 +1,2380 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +char g_DownloadURL[128]; + +bool World::Init(IBaseSystem *system, int serial, char *name) +{ + BaseSystemModule::Init(system, serial, name); + SetState(WORLD_INITIALIZING); + + if (!name) { + SetName(WORLD_INTERFACE_VERSION); + } + + m_ClientUserMsgs = 0; + m_ResourcesList = 0; + m_ResourcesNum = 0; + m_Protocol = PROTOCOL_VERSION; + + m_SignonData.Resize(MAX_BUFFER_SIGNONDATA); + m_ServerInfo.SetMaxSize(MAX_SERVERINFO_STRING); + + memset(m_HostName, 0, sizeof(m_HostName)); + strcopy(m_ServerName, "Unnamed HLTV"); + + m_Frames.Init(); + m_FramesByTime.Init(); + + m_WorldModel.Init(system); + m_Delta.Init(system); + + m_MaxBufferLength = -1.0f; + m_MaxCacheIndex = MAX_FRAME_CACHE; + + char *maxCacheparam = m_System->CheckParam("-cachesize"); + if (maxCacheparam) { + m_MaxCacheIndex = atoi(maxCacheparam); + } + + if (m_MaxCacheIndex <= 0) { + m_MaxCacheIndex = MAX_FRAME_CACHE; + } + + m_FrameCache = (frameCache_t *)Mem_ZeroMalloc(sizeof(frameCache_t) * m_MaxCacheIndex); + m_DeltaCache = (deltaCache_t *)Mem_ZeroMalloc(sizeof(deltaCache_t) * m_MaxCacheIndex); + + if (!m_FrameCache || !m_DeltaCache) { + m_System->Errorf("World::Init: Not enough memory for caches. Reduce -cachesize.\n"); + return false; + } + + Reset(); + SetState(WORLD_DISCONNECTED); + m_State = MODULE_RUNNING; + m_System->Printf("World module initialized.\n"); + + return true; +} + +void World::ShutDown() +{ + if (m_State == MODULE_DISCONNECTED) { + return; + } + + Reset(); + SetState(WORLD_DISCONNECTED); + BaseSystemModule::FireSignal(8); + + m_Delta.Shutdown(); + m_Listener.Clear(); + + if (m_DeltaCache) + { + free(m_DeltaCache); + m_DeltaCache = nullptr; + } + + if (m_FrameCache) + { + free(m_FrameCache); + m_FrameCache = nullptr; + } + + BaseSystemModule::ShutDown(); + m_System->Printf("World module shutdown.\n"); +} + +void World::Reset() +{ + ClearUserMessages(); + ClearResources(); + ClearBaseline(); + ClearInstancedBaseline(); + ClearLightStyles(); + ClearPlayers(); + ClearEntityCache(); + ClearServerInfo(); + ClearFrames(); + + m_SignonData.Clear(); + m_WorldModel.Clear(); + + m_SequenceNr = 0; + m_ViewEntity = 0; + m_WorldTime = 0; + m_StartTime = 0; + m_VoiceEnabled = false; + + memset(m_ClientFallback, 0, sizeof(m_ClientFallback)); + + m_AllowCheats = false; + m_IsHLTV = false; + m_IsPaused = false; + + m_GameServerAddress.Clear(); + g_DownloadURL[0] = '\0'; +} + +bool World::SetMaxClients(int max) +{ + if (max <= 0 || max > MAX_CLIENTS) { + m_System->Printf("WARNING! World::SetMaxClients: Bad maxclients (%u)\n", max); + return false; + } + + m_Maxclients = max; + return true; +} + +void World::ClearUserMessages() +{ + UserMsg *pList, *pNext; + for (pList = m_ClientUserMsgs; pList; pList = pNext) + { + pNext = pList->next; + free(pList); + } + + m_ClientUserMsgs = nullptr; +} + +bool World::AddUserMessage(int msgNumber, int size, char *name) +{ + UserMsg umsg; + memset(&umsg, 0, sizeof(umsg)); + + umsg.iMsg = msgNumber; + umsg.iSize = size; + + if (size == 255) { + umsg.iSize = -1; + } + + strcopy(umsg.szName, name); + + bool bFound = false; + for (UserMsg *pList = m_ClientUserMsgs; pList; pList = pList->next) + { + if (!_stricmp(pList->szName, umsg.szName)) { + bFound = true; + pList->iMsg = umsg.iMsg; + pList->iSize = umsg.iSize; + } + } + + if (!bFound) + { + UserMsg *pumsg = (UserMsg *)malloc(sizeof(UserMsg)); + memcpy(pumsg, &umsg, sizeof(*pumsg)); + pumsg->next = m_ClientUserMsgs; + m_ClientUserMsgs = pumsg; + return true; + } + + return false; +} + +void World::UpdatePlayer(int playerNum, int userId, char *infostring, char *hashedcdkey) +{ + InfoString infostr(infostring); + if (playerNum >= MAX_CLIENTS) { + m_System->Errorf("World::UpdatePlayer: player number %i >= MAX_CLIENTS\n", playerNum); + return; + } + + player_info_t *player = &m_Players[playerNum]; + if (!infostring[0]) { + return; + } + + player->active = true; + player->userid = userId; + memcpy(player->hashedcdkey, hashedcdkey, sizeof(player->hashedcdkey)); + + m_System->DPrintf("Player update(%i:%s)\n", playerNum, player->name); + infostr.RemovePrefixedKeys('_'); + + strcopy(player->userinfo, infostr.GetString()); + strcopy(player->name, infostr.ValueForKey("name")); + strcopy(player->model, infostr.ValueForKey("model")); + + player->topcolor = atoi(infostr.ValueForKey("topcolor")); + player->bottomcolor = atoi(infostr.ValueForKey("bottomcolor")); + player->spectator = atoi(infostr.ValueForKey("*hltv")); + player->trackerID = atoi(infostr.ValueForKey("*fid")); + + BaseSystemModule::FireSignal(4, &playerNum); +} + +bool World::AddResource(resource_t *resource) +{ + resource_t *newresource = (resource_t *)Mem_ZeroMalloc(sizeof(resource_t)); + if (!newresource) { + return false; + } + + memcpy(newresource, resource, sizeof(*newresource)); + + newresource->data = nullptr; + newresource->pNext = m_ResourcesList; + + m_ResourcesList = newresource; + m_ResourcesNum++; + + return true; +} + +void World::ClearResources() +{ + resource_t *res, *next; + for (res = m_ResourcesList; res; res = next) { + next = res->pNext; + free(res); + } + + m_ResourcesList = nullptr; + m_ResourcesNum = 0; +} + +UserMsg *World::GetUserMsg(int msgNumber) +{ + for (UserMsg *pList = m_ClientUserMsgs; pList; pList = pList->next) + { + if (pList->iMsg == msgNumber) { + return pList; + } + } + + return nullptr; +} + +void World::AddBaselineEntity(int index, entity_state_t *ent) +{ + if (index < 0 || index >= MAX_ENTITIES) { + m_System->Printf("WARNING! World::SetBaselineEntity: index (%i) out of bounds.\n", index); + return; + } + + memcpy(&m_BaseLines[index], ent, sizeof(m_BaseLines[index])); +} + +bool World::IsPlayerIndex(int index) +{ + if (index <= 0 || index > m_Maxclients) { + return false; + } + + return true; +} + +void World::ClearBaseline() +{ + memset(m_BaseLines, 0, sizeof(m_BaseLines)); + + for (auto& base : m_BaseLines) { + base.entityType = ENTITY_UNINITIALIZED; + } +} + +void World::ClearInstancedBaseline() +{ + memset(m_Instanced_BaseLines, 0, sizeof(m_Instanced_BaseLines)); +} + +void World::AddInstancedBaselineEntity(int index, entity_state_t *ent) +{ + if (index < 0 || index >= MAX_INSTANCED_BASELINES) { + m_System->Printf("WARNING! World::SetInstancedBaselineEntity: index (%i) out of bounds.\n", index); + return; + } + + memcpy(&m_Instanced_BaseLines[index], ent, sizeof(m_Instanced_BaseLines[index])); +} + +void World::SetTime(double newTime) +{ + m_System->DPrintf("Synchronizing time %.2f ( %.2f)\n", newTime, newTime - m_WorldTime); + m_WorldTime = newTime; +} + +void World::SetHostName(char *name) +{ + if (!name || !name[0]) { + memset(m_HostName, 0, sizeof(m_HostName)); + return; + } + + strcopy(m_HostName, name); +} + +void World::ClearLightStyles() +{ + memset(m_Lightstyles, 0, sizeof(m_Lightstyles)); +} + +void World::AddLightStyle(int index, char *style) +{ + if (index < 0 || index >= MAX_LIGHTSTYLES) { + m_System->Printf("WARNING! World::SetLightStyle: index (%i) out of bounds.\n", index); + return; + } + + int length = strlen(style); + if (length >= sizeof(m_Lightstyles[0])) { + m_System->Printf("WARNING! World::SetLightStyle: style too long (%i).\n", length); + } + + strcopy(m_Lightstyles[index], style); +} + +void World::NewGame(int newServerCount) +{ + BaseSystemModule::FireSignal(1); + + Reset(); + m_ServerCount = newServerCount; + SetState(WORLD_CONNECTING); +} + +void World::SetState(WorldState newState) +{ + if (newState == m_WorldState) + return; + + bool stateError = false; + switch (newState) + { + case WORLD_INITIALIZING: + case WORLD_DISCONNECTED: + break; + case WORLD_CONNECTING: + { + if (m_WorldState != WORLD_DISCONNECTED + && m_WorldState != WORLD_RUNNING + && m_WorldState != WORLD_COMPLETE) { + stateError = true; + } + break; + } + case WORLD_RUNNING: + { + if (m_WorldState != WORLD_CONNECTING) { + stateError = true; + } + break; + } + case WORLD_COMPLETE: + { + if (m_WorldState != WORLD_RUNNING) { + stateError = true; + } + break; + } + default: + stateError = true; + break; + } + + if (stateError) + { + m_System->Errorf("World::SetState: not valid m_WorldState (%i -> %i).\n", m_WorldState, newState); + return; + } + + m_WorldState = newState; +} + +void World::ConnectionComplete() +{ + WriteBaseline(&m_SignonData); + m_StartTime = m_SystemTime; + + SetState(WORLD_RUNNING); + BaseSystemModule::FireSignal(2); +} + +int World::GetNumPlayers() +{ + int count = 0; + for (int i = 0; i < m_Maxclients; i++) + { + if (m_Players[i].active) { + count++; + } + } + + return count; +} + +bool World::IsActive() +{ + return (m_WorldState == WORLD_RUNNING + || m_WorldState == WORLD_COMPLETE); +} + +void World::ClearPlayers() +{ + memset(m_Players, 0, sizeof(m_Players)); +} + +void World::SetServerInfoString(char *infostring) +{ + m_ServerInfo.SetString(infostring); +} + +void World::WriteBaseline(BitBuffer *stream) +{ + if (!IsDeltaEncoder()) { + m_System->Errorf("World::WriteBaseline: delta encoder not found.\n"); + return; + } + + stream->WriteByte(svc_spawnbaseline); + stream->StartBitMode(); + + bool custom = false; + int entnum = 0; + entity_state_t nullstate; + memset(&nullstate, 0, sizeof(entity_state_t)); + + for (auto& base : m_BaseLines) + { + // ignore uninitialized entity, not passed parse process World::ParseBaseline + if (base.entityType != ENTITY_UNINITIALIZED) + { + stream->WriteBits(entnum, 11); + stream->WriteBits(m_BaseLines[entnum].entityType, 2); + + custom = ~m_BaseLines[entnum].entityType & ENTITY_NORMAL; + + delta_t *delta = GetDeltaEncoder(entnum, custom); + m_Delta.WriteDelta(stream, (byte *)&nullstate, (byte *)&base, true, delta); + } + + entnum++; + } + + stream->WriteBits(0xFFFF, 16); + stream->WriteBits(m_MaxInstanced_BaseLine, 6); + + for (entnum = 0; entnum < m_MaxInstanced_BaseLine; entnum++) { + m_Delta.WriteDelta(stream, (byte *)&nullstate, (byte *)&m_Instanced_BaseLines[entnum], true, GetEntityDelta()); + } + + stream->EndBitMode(); +} + +void World::WriteLightStyles(BitBuffer *stream) +{ + for (int i = 0; i < MAX_LIGHTSTYLES; i++) + { + stream->WriteByte(svc_lightstyle); + stream->WriteByte(i); + stream->WriteString(m_Lightstyles[i]); + } +} + +char *World::GetHostName() +{ + if (m_HostName[0]) { + return m_HostName; + } + + return m_ServerName; +} + +void World::WriteServerinfo(BitBuffer *stream) +{ + char message[2048]; + _snprintf(message, sizeof(message), "Protocol Version %i, Spawn count %i %s\n", m_Protocol, m_ServerCount, m_IsHLTV ? "(HLTV)" : ""); + + stream->WriteByte(svc_print); + stream->WriteString(message); + + if (m_IsHLTV) + { + stream->WriteByte(svc_hltv); + stream->WriteByte(HLTV_ACTIVE); + } + + stream->WriteByte(svc_serverinfo); + stream->WriteLong(m_Protocol); + stream->WriteLong(m_ServerCount); + + CRC32_t mungebuffer = m_ServerCRC; + COM_Munge3((byte *)&mungebuffer, sizeof(mungebuffer), (-1 - m_PlayerNum) & 0xFF); + + stream->WriteLong(mungebuffer); + stream->WriteBuf(m_ClientdllMD5, sizeof(m_ClientdllMD5)); + stream->WriteByte(m_Maxclients); + stream->WriteByte(m_PlayerNum); + stream->WriteByte(m_GameType); // deathmatch, coop etc + stream->WriteString(m_GameDir); + stream->WriteString(GetHostName()); + stream->WriteString(m_LevelName); + stream->WriteString("mapcycle failure"); + stream->WriteByte(0); + + stream->WriteByte(svc_sendextrainfo); + stream->WriteString(m_ClientFallback); + stream->WriteByte(m_AllowCheats); + + WriteDeltaDescriptions(stream); + WriteMovevars(stream); + + stream->WriteByte(svc_cdtrack); + stream->WriteByte(m_CDTrack); + stream->WriteByte(m_CDTrack); + + stream->WriteByte(svc_setview); + stream->WriteShort(m_ViewEntity); +} + +void World::WriteDeltaDescriptions(BitBuffer *stream) +{ + int i, c; + + delta_description_t nulldesc; + memset(&nulldesc, 0, sizeof(nulldesc)); + + for (auto p = m_Delta.GetRegistry(); p; p = p->next) + { + stream->WriteByte(svc_deltadescription); + stream->WriteString(p->name); + + if (!p->pdesc) + { + m_System->Errorf("World::WriteDeltaDescriptions : missing delta for %s\n", p->name); + return; + } + + c = p->pdesc->fieldCount; + stream->StartBitMode(); + stream->WriteBits(c, 16); + + for (i = 0; i < c; i++) + { + delta_description_t *from = &nulldesc; + delta_description_t *to = &p->pdesc->pdd[i]; + m_Delta.WriteDelta(stream, (byte *)from, (byte *)to, true, (delta_t *)&Delta::m_MetaDelta); + } + + stream->EndBitMode(); + } +} + +void World::WriteMovevars(BitBuffer *stream) +{ + stream->WriteByte (svc_newmovevars); + stream->WriteFloat(m_MoveVars.gravity); + stream->WriteFloat(m_MoveVars.stopspeed); + stream->WriteFloat(m_MoveVars.maxspeed); + stream->WriteFloat(m_MoveVars.spectatormaxspeed); + stream->WriteFloat(m_MoveVars.accelerate); + stream->WriteFloat(m_MoveVars.airaccelerate); + stream->WriteFloat(m_MoveVars.wateraccelerate); + stream->WriteFloat(m_MoveVars.friction); + stream->WriteFloat(m_MoveVars.edgefriction); + stream->WriteFloat(m_MoveVars.waterfriction); + stream->WriteFloat(m_MoveVars.entgravity); + stream->WriteFloat(m_MoveVars.bounce); + stream->WriteFloat(m_MoveVars.stepsize); + stream->WriteFloat(m_MoveVars.maxvelocity); + stream->WriteFloat(m_MoveVars.zmax); + stream->WriteFloat(m_MoveVars.waveHeight); + stream->WriteByte (m_MoveVars.footsteps != 0); + stream->WriteFloat(m_MoveVars.rollangle); + stream->WriteFloat(m_MoveVars.rollspeed); + stream->WriteFloat(m_MoveVars.skycolor_r); + stream->WriteFloat(m_MoveVars.skycolor_g); + stream->WriteFloat(m_MoveVars.skycolor_b); + stream->WriteFloat(m_MoveVars.skyvec_x); + stream->WriteFloat(m_MoveVars.skyvec_y); + stream->WriteFloat(m_MoveVars.skyvec_z); + stream->WriteString(m_MoveVars.skyName); +} + +void World::WriteRegisteredUserMessages(BitBuffer *stream) +{ + for (UserMsg *pMsg = m_ClientUserMsgs; pMsg; pMsg = pMsg->next) + { + stream->WriteByte(svc_newusermsg); + stream->WriteByte(pMsg->iMsg); + stream->WriteByte(pMsg->iSize); + stream->WriteBuf(pMsg->szName, sizeof(pMsg->szName)); + } +} + +void World::WriteResources(BitBuffer *stream) +{ + unsigned char nullbuffer[32]; + memset(nullbuffer, 0, sizeof(nullbuffer)); + + if (g_DownloadURL[0]) + { + stream->WriteByte(svc_resourcelocation); + stream->WriteString(g_DownloadURL); + } + + stream->WriteByte(svc_resourcelist); + stream->StartBitMode(); + stream->WriteBits(m_ResourcesNum, RESOURCE_INDEX_BITS); + + for (resource_t *resource = m_ResourcesList; resource; resource = resource->pNext) + { + stream->WriteBits(resource->type, 4); + stream->WriteBitString(resource->szFileName); + stream->WriteBits(resource->nIndex, RESOURCE_INDEX_BITS); + stream->WriteBits(resource->nDownloadSize, 24); + stream->WriteBits(resource->ucFlags & (RES_WASMISSING | RES_FATALIFMISSING), 3); + + if (resource->ucFlags & RES_CUSTOM) + { + stream->WriteBitData(resource->rgucMD5_hash, sizeof(resource->rgucMD5_hash)); + } + + if (!memcmp(resource->rguc_reserved, nullbuffer, sizeof(resource->rguc_reserved))) + { + stream->WriteBit(0); + } + else + { + stream->WriteBit(1); + stream->WriteBitData(resource->rguc_reserved, sizeof(resource->rguc_reserved)); + } + } + + stream->WriteBit(0); + stream->EndBitMode(); +} + +void World::WriteClientUpdate(BitBuffer *stream, int playerIndex) +{ + if (m_Players[playerIndex].active) + { + stream->WriteByte(svc_updateuserinfo); + stream->WriteByte(playerIndex); + stream->WriteLong(m_Players[playerIndex].userid); + stream->WriteString(m_Players[playerIndex].userinfo); + stream->WriteBuf(m_Players[playerIndex].hashedcdkey, sizeof(m_Players[playerIndex].hashedcdkey)); + } +} + +int World::AddFrame(frame_t *newFrame) +{ + if (!newFrame) + { + m_System->Errorf("World::AddFrame: newFrame == NULL.\n"); + return 0; + } + + m_SequenceNr++; + + frame_t *currentFrame; + currentFrame = (frame_t *)Mem_ZeroMalloc(sizeof(frame_t)); + currentFrame->seqnr = m_SequenceNr; + currentFrame->time = newFrame->time; + + if (newFrame->time < m_WorldTime) + { + m_System->DPrintf("Fixing frame time, delta %.3f\n", m_WorldTime - currentFrame->time); + ReorderFrameTimes(currentFrame->time - 0.05f); + } + + m_WorldTime = currentFrame->time; + + if (m_WorldState == WORLD_CONNECTING) { + ConnectionComplete(); + } + + int compressedEntitiesSize = 0; + if (newFrame->entitiesSize) + { + if (newFrame->delta) + { + m_System->Errorf("World::AddFrame: only uncompressed frames accepted.\n"); + return 0; + } + + m_Delta.SetLargeTimeBufferSize(true); + BitBuffer tempStream(m_EntityBuffer, sizeof(m_EntityBuffer)); + memset(m_EntityBuffer, 0, sizeof(m_EntityBuffer)); + + compressedEntitiesSize = CompressFrame(newFrame, &tempStream); + m_Delta.SetLargeTimeBufferSize(false); + + if (tempStream.IsOverflowed()) + { + m_System->Printf("WARNING! World::AddFrame: couldn't compress frame entities.\n"); + return 0; + } + } + + unsigned char *pdata; + int maxFrameSize = newFrame->demoDataSize + + newFrame->userMessagesSize + + newFrame->voiceDataSize + + newFrame->unreliableDataSize + + newFrame->reliableDataSize + + newFrame->clientDataSize + + newFrame->eventsSize + + compressedEntitiesSize + + 4; + + if (newFrame->demoInfo) + { + maxFrameSize += sizeof(demo_info_t); + } + + pdata = (unsigned char *)malloc(maxFrameSize); + + currentFrame->data = pdata; + currentFrame->delta = -1; + + if (newFrame->entitiesSize) + { + memcpy(pdata, m_EntityBuffer, compressedEntitiesSize); + currentFrame->entities = pdata; + currentFrame->entitiesSize = compressedEntitiesSize; + currentFrame->entitynum = newFrame->entitynum; + pdata += compressedEntitiesSize; + } + + if (newFrame->clientDataSize) + { + memcpy(pdata, newFrame->clientData, newFrame->clientDataSize); + currentFrame->clientData = pdata; + currentFrame->clientDataSize = newFrame->clientDataSize; + pdata += currentFrame->clientDataSize; + } + + if (newFrame->eventsSize) + { + memcpy(pdata, newFrame->events, newFrame->eventsSize); + currentFrame->events = pdata; + currentFrame->eventsSize = newFrame->eventsSize; + currentFrame->eventnum = newFrame->eventnum; + pdata += currentFrame->eventsSize; + } + + if (newFrame->reliableDataSize) + { + memcpy(pdata, newFrame->reliableData, newFrame->reliableDataSize); + currentFrame->reliableData = pdata; + currentFrame->reliableDataSize = newFrame->reliableDataSize; + pdata += currentFrame->reliableDataSize; + } + + if (newFrame->unreliableDataSize) + { + memcpy(pdata, newFrame->unreliableData, newFrame->unreliableDataSize); + currentFrame->unreliableData = pdata; + currentFrame->unreliableDataSize = newFrame->unreliableDataSize; + pdata += currentFrame->unreliableDataSize; + } + + if (newFrame->voiceDataSize) + { + memcpy(pdata, newFrame->voiceData, newFrame->voiceDataSize); + currentFrame->voiceData = pdata; + currentFrame->voiceDataSize = newFrame->voiceDataSize; + pdata += currentFrame->voiceDataSize; + } + + if (newFrame->userMessagesSize) + { + memcpy(pdata, newFrame->userMessages, newFrame->userMessagesSize); + currentFrame->userMessages = pdata; + currentFrame->userMessagesSize = newFrame->userMessagesSize; + pdata += currentFrame->userMessagesSize; + } + + if (newFrame->demoDataSize) + { + memcpy(pdata, newFrame->demoData, newFrame->demoDataSize); + currentFrame->demoData = pdata; + currentFrame->demoDataSize = newFrame->demoDataSize; + pdata += currentFrame->demoDataSize; + } + + if (newFrame->demoInfo) + { + memcpy(pdata, newFrame->demoInfo, sizeof(demo_info_t)); + currentFrame->demoInfo = pdata; + } + + m_Frames.Add(currentFrame, currentFrame->seqnr); + m_FramesByTime.Add(currentFrame, m_WorldTime); + + CheckFrameBufferSize(); + BaseSystemModule::FireSignal(3, &m_SequenceNr); + + return m_SequenceNr; +} + +frame_t *World::GetFrameBySeqNr(unsigned int seqnr) +{ + return (frame_t *)m_Frames.FindExactKey(seqnr); +} + +frame_t *World::GetLastFrame() +{ + return (frame_t *)m_Frames.GetLast(); +} + +frame_t *World::GetFirstFrame() +{ + return (frame_t *)m_Frames.GetFirst(); +} + +frame_t *World::GetFrameByTime(double time) +{ + frame_t *lastFrame = (frame_t *)m_FramesByTime.FindClosestKey(time); + if (!lastFrame) { + return nullptr; + } + + if (lastFrame->time < time) + { + frame_t *nextFrame = (frame_t *)m_FramesByTime.GetNext(); + if (nextFrame) { + lastFrame = nextFrame; + } + } + + return lastFrame; +} + +void World::WriteFrame(frame_t *frame, unsigned int lastFrameSeqnr, BitBuffer *reliableStream, BitBuffer *unreliableStream, unsigned int deltaSeqNr, unsigned int clientDelta, bool addVoice) +{ + frame_t fullFrame; + if (!GetUncompressedFrame(frame->seqnr, &fullFrame)) { + m_System->DPrintf("World::WriteFrame: couldn't uncompress frame.\n"); + return; + } + + if (m_IsHLTV) + { + unreliableStream->WriteByte(svc_clientdata); + } + else + { + clientdata_t clientData; + if (GetClientData(frame, &clientData)) + { + clientdata_t nullClientData; + memset(&nullClientData, 0, sizeof(nullClientData)); + + unreliableStream->WriteByte(svc_clientdata); + unreliableStream->StartBitMode(); + unreliableStream->WriteBit(0); + + m_Delta.WriteDelta(unreliableStream, (byte *)&nullClientData, (byte *)&clientData, true, GetClientDelta()); + + unreliableStream->WriteBit(0); + unreliableStream->EndBitMode(); + } + } + + bool validEntities = false; + if (deltaSeqNr && WriteDeltaEntities(unreliableStream, &fullFrame, deltaSeqNr, clientDelta)) { + validEntities = true; + } + else + { + unreliableStream->WriteByte(svc_packetentities); + unreliableStream->WriteShort(frame->entitynum); + + if (CompressFrame(&fullFrame, unreliableStream) > 0) { + validEntities = true; + } + else { + m_System->DPrintf("WARNING! World::WriteFrame: could write entities!\n"); + } + } + + if (frame->eventsSize && validEntities) + { + unreliableStream->WriteByte(svc_event); + unreliableStream->WriteBuf(frame->events, frame->eventsSize); + } + + frame_t *lastFrame = (frame_t *)m_Frames.FindExactKey(lastFrameSeqnr + 1); + while (lastFrame) + { + if (lastFrame->seqnr > frame->seqnr + || reliableStream->IsOverflowed()) { + break; + } + + if (lastFrame->reliableDataSize && (unsigned)reliableStream->SpaceLeft() > lastFrame->reliableDataSize) { + reliableStream->WriteBuf(lastFrame->reliableData, lastFrame->reliableDataSize); + } + + if (lastFrame->userMessagesSize && (unsigned)reliableStream->SpaceLeft() > lastFrame->userMessagesSize) { + reliableStream->WriteBuf(lastFrame->userMessages, lastFrame->userMessagesSize); + } + + if (lastFrame->seqnr + 8 > frame->seqnr) + { + if (lastFrame->unreliableDataSize && (unsigned)unreliableStream->SpaceLeft() > lastFrame->unreliableDataSize) { + unreliableStream->WriteBuf(lastFrame->unreliableData, lastFrame->unreliableDataSize); + } + + if (lastFrame->voiceDataSize) + { + if ((unsigned)unreliableStream->SpaceLeft() > lastFrame->voiceDataSize) { + unreliableStream->WriteBuf(lastFrame->voiceData, lastFrame->voiceDataSize); + } + } + } + + lastFrame = (frame_t *)m_Frames.GetNext(); + } +} + +bool World::GetUncompressedFrame(unsigned int seqNr, frame_t *frame) +{ + frame_t *deltaFrame = (frame_t *)m_Frames.FindExactKey(seqNr); + if (!deltaFrame) { + return false; + } + + if (deltaFrame->seqnr != seqNr) { + m_System->DPrintf("WARNING! World::GetUncompressedFrame: frame not found %i.\n", seqNr); + return false; + } + + return GetUncompressedFrame(deltaFrame, frame); +} + +bool World::GetClientData(unsigned int SeqNr, clientdata_t *clientData) +{ + frame_t *frame = (frame_t *)m_Frames.FindExactKey(SeqNr); + return GetClientData(frame, clientData); +} + +bool World::GetClientData(frame_t *frame, clientdata_t *clientData) +{ + if (!frame || !frame->clientData || !frame->clientDataSize) { + return false; + } + + clientdata_t BaseLineClientData; + memset(&BaseLineClientData, 0, sizeof(BaseLineClientData)); + + BitBuffer stream(frame->clientData, frame->clientDataSize); + stream.StartBitMode(); + m_Delta.ParseDelta(&stream, (byte *)&BaseLineClientData, (byte *)clientData, GetClientDelta()); + + return true; +} + +bool World::GetUncompressedFrame(frame_t *deltaFrame, frame_t *frame) +{ + BitBuffer frameStream; + if (!deltaFrame) { + return false; + } + + if (deltaFrame->delta != -1) { + m_System->DPrintf("World::GetUncompressedFrame: unexpected non baseline delta frame.\n"); + return false; + } + + memcpy(frame, deltaFrame, sizeof(*frame)); + + frame->delta = 0; + frame->entitiesSize = deltaFrame->entitynum * sizeof(entity_state_t); + + if (frame->entitynum > MAX_PACKET_ENTITIES) { + m_System->Errorf("World::GetUncompressedFrame: entnum >= MAX_PACKET_ENTITIES.\n"); + } + + if (frame->entitynum) + { + if (!GetFrameFromCache(deltaFrame->seqnr, (entity_state_t **)&frame->entities)) + { + m_Delta.SetLargeTimeBufferSize(true); + frameStream.SetBuffer(deltaFrame->entities, deltaFrame->entitiesSize); + UncompressEntitiesFromStream(frame, &frameStream); + m_Delta.SetLargeTimeBufferSize(false); + } + } + + return true; +} + +void World::ParseClientData(BitBuffer *stream, unsigned int deltaSeqNr, BitBuffer *to, clientdata_t *clientData) +{ + weapon_data_t nullWeaponData; + clientdata_t fromClientData, nullClientData; + + memset(&nullWeaponData, 0, sizeof(nullWeaponData)); + memset(&fromClientData, 0, sizeof(fromClientData)); + memset(&nullClientData, 0, sizeof(nullClientData)); + memset(clientData, 0, sizeof(*clientData)); + + if (deltaSeqNr && !GetClientData(deltaSeqNr, &fromClientData)) { + m_System->Printf("WARNING! World::ParseClientData: couldn't uncompress delta frame %i\n", deltaSeqNr); + } + + m_Delta.ParseDelta(stream, (byte *)&fromClientData, (byte *)clientData, GetClientDelta()); + to->StartBitMode(); + m_Delta.WriteDelta(to, (byte *)&nullClientData, (byte *)clientData, true, GetClientDelta()); + to->EndBitMode(); + + while (stream->ReadBit()) + { + stream->ReadBits(6); + m_Delta.ParseDelta(stream, (byte *)&nullWeaponData, (byte *)&nullWeaponData, GetWeaponDelta()); + } +} + +int World::ParseDeltaHeader(BitBuffer *stream, bool *remove, bool *custom, int *numbase, bool *newbl, int *newblindex, bool full, int *offset) +{ + int num; + + *custom = false; + *newbl = false; + + if (full) + { + *remove = false; + + if (stream->ReadBit() != 0) + { + num = *numbase + 1; + } + else if (stream->ReadBit() == 0) + { + num = stream->ReadBits(6) + *numbase; + } + else + { + num = stream->ReadBits(11); + } + } + else + { + *remove = stream->ReadBit() != 0; + + if (stream->ReadBit() == 0) + num = stream->ReadBits(6) + *numbase; + else + num = stream->ReadBits(11); + } + + *numbase = num; + + if (!*remove) + { + *custom = stream->ReadBit() != 0; + if (m_MaxInstanced_BaseLine) + { + *newbl = stream->ReadBit() != 0; + if (*newbl) + *newblindex = stream->ReadBits(6); + } + else + { + *newbl = false; + } + + if (!full || *newbl) + { + *offset = 0; + } + else + { + *offset = stream->ReadBit(); + if (*offset) { + *offset = stream->ReadBits(6); + } + } + } + + return num; +} + +int World::CompressFrame(frame_t *from, BitBuffer *stream) +{ + deltacallback_t header; + header.instanced_baseline = (m_MaxInstanced_BaseLine > 0) ? true : false; + header.num = 0; + header.offset = 0; + header.numbase = 0; + header.newblindex = 0; + header.full = true; + header.newbl = false; + header.remove = false; + header.custom = false; + + if (from->delta) + { + m_System->Errorf("World::CompressFrame: frame was not uncompressed.\n"); + return 0; + } + + m_Delta.SetTime(from->time); + stream->StartBitMode(); + + entity_state_t *entities = (entity_state_t *)from->entities; + unsigned char *start = stream->CurrentByte(); + for (auto entnum = 0u; entnum < from->entitynum; entnum++) + { + header.num = entities[entnum].number; + header.custom = (entities[entnum].entityType & ENTITY_BEAM) == ENTITY_BEAM; + header.newblindex = 0; + header.newbl = false; + + entity_state_t *baseline = &m_BaseLines[header.num]; + header.offset = FindBestBaseline(entnum, &baseline, entities, header.num, header.custom); + + delta_t *delta = GetDeltaEncoder(header.num, header.custom); + m_Delta.WriteDelta(stream, (byte *)baseline, (byte *)&entities[entnum], true, delta, &header); + } + + stream->WriteBits(0, 16); + stream->EndBitMode(); + + return stream->CurrentByte() - start; +} + +int World::FindBestBaseline(int index, entity_state_t **baseline, entity_state_t *to, int num, bool custom) +{ + int bestbitnumber; + delta_t *delta; + + delta = GetDeltaEncoder(num, custom); + bestbitnumber = m_Delta.TestDelta((byte *)*baseline, (byte *)&to[index], delta); + bestbitnumber -= 6; + + int i = 0; + int bitnumber = 0; + int bestfound = index; + + for (i = index - 1; bestbitnumber > 0 && i >= 0 && (index - i) <= 64; i--) + { + if (to[index].modelindex == to[i].modelindex) + { + bitnumber = m_Delta.TestDelta((byte *)&to[i], (byte *)&to[index], delta); + + if (bitnumber < bestbitnumber) + { + bestbitnumber = bitnumber; + bestfound = i; + } + } + } + + if (index != bestfound) { + *baseline = &to[bestfound]; + } + + return index - bestfound; +} + +void World::ClearEntityCache() +{ + if (m_DeltaCache) + { + for (int i = 0; i < m_MaxCacheIndex; i++) { + m_DeltaCache[i].buffer.Free(); + } + + memset(m_DeltaCache, 0, sizeof(deltaCache_t) * m_MaxCacheIndex); + } + + if (m_FrameCache) { + memset(m_FrameCache, 0, sizeof(frameCache_t) * m_MaxCacheIndex); + } + + m_CacheHits = 1; + m_CacheFaults = 1; +} + +bool World::GetFrameFromCache(unsigned int seqNr, entity_state_t **entities) +{ + for (int i = 0; i < m_MaxCacheIndex; i++) + { + if (m_FrameCache[i].seqNr == seqNr) + { + *entities = m_FrameCache[i].entities; + m_CacheHits++; + return true; + } + } + + static int next = 0; + m_FrameCache[next].seqNr = seqNr; + *entities = m_FrameCache[next].entities; + + if (++next == m_MaxCacheIndex) { + next = 0; + } + + m_CacheFaults++; + return false; +} + +bool World::GetDeltaFromCache(unsigned int seqNr, unsigned int deltaNr, BitBuffer **buffer) +{ + for (int i = 0; i < m_MaxCacheIndex; i++) + { + if (m_DeltaCache[i].seqNr == seqNr && m_DeltaCache[i].deltaNr == deltaNr) + { + *buffer = &m_DeltaCache[i].buffer; + m_CacheHits++; + return true; + } + } + + static int next = 0; + m_DeltaCache[next].seqNr = seqNr; + m_DeltaCache[next].deltaNr = deltaNr; + *buffer = &m_DeltaCache[next].buffer; + + if (++next == m_MaxCacheIndex) { + next = 0; + } + + m_CacheFaults++; + return 0; +} + +void World::WritePacketEntities(BitBuffer *stream, frame_t *frame, frame_t *deltaframe) +{ + int oldmax; + int oldindex; + int newindex; + int newnum; + int oldnum; + entity_state_t *frameEntities; + entity_state_t *deltaEntities; + + deltacallback_t header; + header.instanced_baseline = (m_MaxInstanced_BaseLine > 0) ? true : false; + header.num = 0; + header.offset = 0; + header.numbase = 0; + header.newblindex = 0; + header.full = false; + header.newbl = false; + header.remove = false; + header.custom = false; + + if (frame->delta || deltaframe->delta) { + m_System->Errorf("World::WritePacketEntities: frame and delta frame must be uncompressed.\n"); + return; + } + + m_Delta.SetTime(frame->time); + + oldmax = deltaframe->entitynum; + newnum = 0; // index in frame->entities + oldnum = 0; // index in deltaframe->entities + + frameEntities = (entity_state_t *)frame->entities; + deltaEntities = (entity_state_t *)deltaframe->entities; + + stream->StartBitMode(); + while (true) + { + if ((unsigned)newnum < frame->entitynum) + { + newindex = frameEntities[newnum].number; + } + else + { + if (oldnum >= oldmax) + break; + + // TODO: Unreachable code + if ((unsigned)newnum < frame->entitynum) + newindex = frameEntities[newnum].number; + else + newindex = 9999; + } + + if (oldnum < oldmax) + oldindex = deltaEntities[oldnum].number; + else + oldindex = 9999; + + if (newindex == oldindex) + { + header.custom = (frameEntities[newnum].entityType & ENTITY_BEAM) == ENTITY_BEAM; + header.num = newindex; + header.newblindex = 0; + header.newbl = false; + header.remove = false; + + delta_t *delta = GetDeltaEncoder(newindex, header.custom); + m_Delta.WriteDelta(stream, (byte *)&deltaEntities[oldnum], (byte *)&frameEntities[newnum], true, delta, &header); + + oldnum++; + newnum++; + continue; + } + + if (newindex >= oldindex) + { + if (newindex > oldindex) + { + header.num = oldindex; + header.remove = true; + header.newbl = false; + header.newblindex = 0; + + m_Delta.WriteHeader(stream, &header); + ++oldnum; + } + continue; + } + + header.custom = (frameEntities[newnum].entityType & ENTITY_BEAM) == ENTITY_BEAM; + header.newblindex = 0; + header.num = newindex; + header.remove = false; + header.newbl = false; + + delta_t *delta = GetDeltaEncoder(newindex, header.custom); + m_Delta.WriteDelta(stream, (byte *)&m_BaseLines[oldnum], (byte *)&frameEntities[newnum], true, delta, &header); + newnum++; + } + + stream->WriteBits(0, 16); + stream->EndBitMode(); +} + +char *World::GetType() +{ + return WORLD_INTERFACE_VERSION; +} + +char *World::GetStatusLine() +{ + static char string[256]; + if (IsActive()) + { + _snprintf(string, sizeof(string), + "Game \"%s\", Map \"%s\", Time %s, Players %i\nFrame cache use %.1f, Buffered time %.0f .\n", + m_GameDir, + m_LevelName, + COM_FormatTime(m_WorldTime), + GetNumPlayers(), + (float)m_CacheHits / (float)(m_CacheHits + m_CacheFaults), + GetBufferedGameTime()); + } + else + { + _snprintf(string, sizeof(string), "World not active.\n"); + } + + return string; +} + +void World::ClearServerInfo() +{ + m_ServerInfo.Clear(); + + memset(&m_DetailedServerInfo, 0, sizeof(m_DetailedServerInfo)); + m_DetailedServerInfo.type = '?'; + m_DetailedServerInfo.os = '?'; + m_DetailedServerInfo.pw = '?'; +} + +void World::SetServerInfo(serverinfo_t *serverinfo) +{ + memcpy(&m_DetailedServerInfo, serverinfo, sizeof(m_DetailedServerInfo)); +} + +void World::FinishGame() +{ + if (m_WorldState != WORLD_RUNNING) { + return; + } + + SetState(WORLD_COMPLETE); + BaseSystemModule::FireSignal(7); +} + +void World::StopGame() +{ + BaseSystemModule::FireSignal(1); + Reset(); + SetState(WORLD_DISCONNECTED); +} + +void World::WriteSigonData(BitBuffer *stream) +{ + stream->WriteBuf(m_SignonData.GetData(), m_SignonData.CurrentSize()); +} + +bool World::AddSignonData(unsigned char type, unsigned char *data, int size) +{ + m_SignonData.WriteByte(type); + m_SignonData.WriteBuf(data, size); + + return m_SignonData.m_Overflowed; +} + +int World::FindUserMsgByName(char *name) +{ + for (UserMsg *pList = m_ClientUserMsgs; pList; pList = pList->next) + { + if (!strcmp(pList->szName, name)) { + return pList->iMsg; + } + } + + return 0; +} + +double World::GetTime() +{ + return m_WorldTime; +} + +bool World::UncompressEntitiesFromStream(frame_t *frame, BitBuffer *stream, unsigned int from) +{ + int newnum, oldnum; + int oldindex, newindex; + + bool remove, custom, newbl; + int newblindex, numbase, offset; + + frame_t deltaFrame; + if (!GetUncompressedFrame(from, &deltaFrame)) { + m_System->DPrintf("WARNING! World::UncompressEntitiesFromStream: delta frame too old.\n"); + stream->m_Overflowed = true; + return false; + } + + oldindex = 0; + newindex = 0; + + remove = false; + custom = false; + newbl = false; + newblindex = 0; + numbase = 0; + + m_Delta.SetTime(frame->time); + stream->StartBitMode(); + + entity_state_t *entity = (entity_state_t *)frame->entities; + entity_state_t *deltaEntity = (entity_state_t *)deltaFrame.entities; + + while (stream->PeekBits(16)) + { + newnum = ParseDeltaHeader(stream, &remove, &custom, &numbase, &newbl, &newblindex, false, &offset); + + if ((unsigned)oldindex < deltaFrame.entitynum) + oldnum = deltaEntity[oldindex].number; + else + oldnum = 9999; + + while (newnum > oldnum) + { + if (newindex >= MAX_PACKET_ENTITIES) + { + m_System->DPrintf("WARNING!World::UncompressEntitiesFromStream: newindex >= MAX_PACKET_ENTITIES.\n"); + stream->m_Overflowed = true; + } + + memcpy(&entity[newindex], &deltaEntity[oldindex], sizeof(entity[newindex])); + + newindex++; + oldindex++; + + if ((unsigned)oldindex < deltaFrame.entitynum) + oldnum = deltaEntity[oldindex].number; + else + oldnum = 9999; + } + + if (newnum >= oldnum) + { + if (newnum == oldnum) + { + if (remove) + { + ++oldindex; + } + else + { + entity[newindex].entityType = custom ? ENTITY_BEAM : ENTITY_NORMAL; + + delta_t *delta = GetDeltaEncoder(newnum, custom); + m_Delta.ParseDelta(stream, (byte *)&deltaEntity[oldindex], (byte *)&entity[newindex], delta); + + entity[newindex].number = newnum; + ++newindex; + ++oldindex; + } + } + } + else if (!remove) + { + if (newindex >= MAX_PACKET_ENTITIES) + { + m_System->DPrintf("World::UncompressEntitiesFromStream: newindex >= MAX_PACKET_ENTITIES.\n"); + stream->m_Overflowed = true; + } + + entity_state_t *baseline; + if (newbl) + { + baseline = &m_Instanced_BaseLines[newblindex]; + } + else if (offset) + { + baseline = &entity[newindex - offset]; + } + else + { + baseline = &m_BaseLines[newnum]; + } + + entity[newindex].entityType = custom ? ENTITY_BEAM : ENTITY_NORMAL; + + delta_t *delta = GetDeltaEncoder(newnum, custom); + m_Delta.ParseDelta(stream, (byte *)baseline, (byte *)&entity[newindex], delta); + entity[newindex].number = newnum; + newindex++; + } + } + + if (stream->ReadShort()) + { + m_System->DPrintf("WARNING! World::UncompressEntitiesFromStream: missing end tag.\n"); + return false; + } + + stream->EndBitMode(); + + while ((unsigned)oldindex < deltaFrame.entitynum) + { + if (newindex >= MAX_PACKET_ENTITIES) + { + m_System->DPrintf("WARNING! World::UncompressEntitiesFromStream: newindex >= MAX_PACKET_ENTITIES.\n"); + stream->m_Overflowed = true; + } + + memcpy(&entity[newindex], &deltaEntity[oldindex], sizeof(entity[newindex])); + newindex++; + oldindex++; + } + + if (newindex != frame->entitynum) { + m_System->DPrintf("WARNING! World::UncompressEntitiesFromStream: newindex != frame->entitynum.\n"); + } + + return true; +} + +bool World::UncompressEntitiesFromStream(frame_t *frame, BitBuffer *stream) +{ + int num; + int newindex = 0; + int entnum = frame->entitynum; + entity_state_t *baseline; + bool remove, custom, newbl; + int newblindex, numbase, offset; + + newblindex = 0; + numbase = 0; + + remove = false; + custom = false; + newbl = false; + + entity_state_t *entities = (entity_state_t *)frame->entities; + m_Delta.SetTime(frame->time); + stream->StartBitMode(); + while (true) + { + if (stream->IsOverflowed()) { + m_System->Printf("WARNING! World::UncompressEntitiesFromStream: incoming entities are corrupt.\n"); + break; + } + + if (!stream->PeekBits(16)) { + break; + } + + num = ParseDeltaHeader(stream, &remove, &custom, &numbase, &newbl, &newblindex, true, &offset); + if (num >= MAX_ENTITIES) + { + m_System->Errorf("World::GetUncompressedFrame: entity number %i >= MAX_ENTITIES\n", num); + memset(frame, 0, sizeof(*frame)); + return false; + } + + if (remove) + { + m_System->Errorf("World::GetUncompressedFrame: remove invalid on non-delta compressed frames\n"); + memset(frame, 0, sizeof(*frame)); + return false; + } + + if (newbl) + { + baseline = &m_Instanced_BaseLines[newblindex]; + } + else if (offset) + { + baseline = &entities[newindex - offset]; + } + else + { + baseline = &m_BaseLines[num]; + } + + entities[num].entityType = custom ? ENTITY_BEAM : ENTITY_NORMAL; + + delta_t *delta = GetDeltaEncoder(num, custom); + m_Delta.ParseDelta(stream, (byte *)baseline, (byte *)&entities[newindex], delta); + entities[newindex].number = num; + newindex++; + } + + if (stream->ReadShort()) { + m_System->Printf("WARNING! World::UncompressEntitiesFromStream: missing end tag.\n"); + return false; + } + + stream->EndBitMode(); + + if (newindex != entnum) { + m_System->DPrintf("WARNING! World::UncompressEntitiesFromStream: newindex != entnum.\n"); + return false; + } + + return true; +} + +void World::WriteNewData(BitBuffer *stream) +{ + WriteServerinfo(stream); + WriteRegisteredUserMessages(stream); + + stream->WriteByte(svc_stufftext); + stream->WriteString(COM_VarArgs("fullserverinfo \"%s\"\n", m_ServerInfo.GetString())); + + WriteResources(stream); +} + +void World::SetName(char *newName) +{ + strcopy(m_Name, newName); +} + +float World::GetBufferedGameTime() +{ + if (!IsActive() || m_Frames.IsEmpty()) { + return 0; + } + + frame_t *firstFrame = (frame_t *)m_Frames.GetFirst(); + frame_t *lastFrame = (frame_t *)m_Frames.GetLast(); + + return lastFrame->time - firstFrame->time; +} + +int World::RemoveFrames(unsigned int startSeqNr, unsigned int endSeqNr) +{ + if (startSeqNr > endSeqNr) { + return 0; + } + + frame_t *startFrame = (frame_t *)m_Frames.FindExactKey(startSeqNr); + frame_t *endFrame = (frame_t *)m_Frames.FindExactKey(endSeqNr); + if (!startFrame || !endFrame) { + return 0; + } + + unsigned int nextseqnr = startFrame->seqnr; + unsigned int lastseqnr = endFrame->seqnr; + + int seqNrOffset = lastseqnr - nextseqnr + 1; + float timeOffset = endFrame->time - startFrame->time; + + frame_t *frame = startFrame; + while (frame) + { + if (frame->seqnr > lastseqnr) { + break; + } + + m_Frames.Remove(frame); + m_FramesByTime.Remove(frame); + + if (frame->data) { + free(frame->data); + } + + free(frame); + frame = (frame_t *)m_Frames.FindExactKey(++nextseqnr); + } + + if (frame != m_Frames.GetFirst()) + { + ClearEntityCache(); + while (frame) + { + RearrangeFrame(frame, seqNrOffset, timeOffset); + frame = (frame_t *)m_Frames.FindExactKey(++nextseqnr); + } + ClearEntityCache(); + } + + return seqNrOffset; +} + +int World::DuplicateFrames(unsigned int startSeqNr, unsigned int endSeqNr) +{ + m_System->Printf("TODO World::DuplicateFrames\n"); + return 0; +} + +int World::MoveFrames(unsigned int startSeqNr, unsigned int endSeqNr, double destSeqnr) +{ + m_System->Printf("TODO World::MoveFrames\n"); + return 0; +} + +int World::RevertFrames(unsigned int startSeqNr, unsigned int endSeqNr) +{ + m_System->Printf("TODO World::RevertFrames\n"); + return 0; +} + +void World::ClearFrames() +{ + frame_t *frame = (frame_t *)m_Frames.GetFirst(); + while (frame) + { + if (frame->data) { + free(frame->data); + } + + free(frame); + frame = (frame_t *)m_Frames.GetNext(); + } + + m_Frames.Clear(); + m_FramesByTime.Clear(); +} + +void World::CheckFrameBufferSize() +{ + if (m_MaxBufferLength <= 0) { + return; + } + + frame_t *frame = (frame_t *)m_Frames.GetLast(); + if (!frame) { + return; + } + + frame_t *firstFrame = (frame_t *)m_Frames.GetFirst(); + if (!firstFrame) { + return; + } + + frame_t *newfirstFrame = (frame_t *)m_FramesByTime.FindClosestKey(frame->time - m_MaxBufferLength); + if (newfirstFrame) { + RemoveFrames(firstFrame->seqnr, newfirstFrame->seqnr - 1); + } +} + +void World::ReorderFrameTimes(float newLastTime) +{ + frame_t *fprev = (frame_t *)m_Frames.GetLast(); + if (!fprev) { + return; + } + + frame_t *f; + float offset = newLastTime; + int fseqnr = fprev->seqnr - 1; + while ((f = (frame_t *)m_Frames.FindExactKey(fseqnr))) + { + float timediff = fprev->time - f->time; + + fseqnr--; + fprev->time = offset; + fprev = f; + + offset -= timediff; + f = (frame_t *)m_Frames.FindExactKey(fseqnr); + } + + fprev->time = offset; +} + +void World::SetServerInfo(int protocol, CRC32_t nserverCRC, unsigned char *nclientdllmd5, int nmaxclients, int nplayernum, int ngametype, char *ngamedir, char *nservername, char *nlevelname) +{ + m_Protocol = protocol; + m_ServerCRC = nserverCRC; + memcpy(m_ClientdllMD5, nclientdllmd5, sizeof(m_ClientdllMD5)); + + m_Maxclients = nmaxclients; + m_PlayerNum = nplayernum; + m_GameType = ngametype; + + strcopy(m_GameDir, ngamedir); + strcopy(m_LevelName, nlevelname); + + _snprintf(m_ServerName, sizeof(m_ServerName), "%s:%i", nservername, m_PlayerNum); +} + +void World::SetMoveVars(movevars_t *nmovevars) +{ + memcpy(&m_MoveVars, nmovevars, sizeof(m_MoveVars)); +} + +void World::SetPaused(bool state) +{ + if (m_IsPaused == state) { + return; + } + + m_IsPaused = state; + BaseSystemModule::FireSignal(m_IsPaused ? 5 : 6); +} + +bool World::IsPaused() +{ + return m_IsPaused; +} + +bool World::IsComplete() +{ + return m_WorldState == WORLD_COMPLETE; +} + +void World::SetBufferSize(float seconds) +{ + if (seconds <= 0) { + m_MaxBufferLength = -1; + return; + } + + m_MaxBufferLength = seconds + 10; +} + +void World::ParseDeltaDescription(BitBuffer *stream) +{ + delta_description_t *pdesc; + delta_description_t nulldesc; + memset(&nulldesc, 0, sizeof(nulldesc)); + + char *s = stream->ReadString(); + if (!s || !s[0]) { + m_System->Errorf("ParseDeltaDescription: Illegible description name\n"); + return; + } + + char szDesc[256]; + strcopy(szDesc, s); + + m_System->DPrintf("Reading delta description for: %s.\n", s); + + delta_t **ppdelta = m_Delta.LookupRegistration(szDesc); + if (ppdelta && *ppdelta) { + m_Delta.FreeDescription(ppdelta); + } + + stream->StartBitMode(); + + int c = stream->ReadShort(); + *ppdelta = (delta_t *)Mem_ZeroMalloc(sizeof(delta_t)); + pdesc = (delta_description_t *)Mem_ZeroMalloc(sizeof(delta_description_t) * c); + + (*ppdelta)->dynamic = 1; + (*ppdelta)->fieldCount = c; + + for (int i = 0; i < c; i++) { + m_Delta.ParseDelta(stream, (byte *)&nulldesc, (byte *)&pdesc[i], Delta::m_MetaDelta); + } + + (*ppdelta)->pdd = pdesc; + stream->EndBitMode(); + m_Delta.UpdateDescriptions(); +} + +void World::SetCDInfo(int ncdtrack, int nlooptrack) +{ + m_CDTrack = ncdtrack; + m_LoopTrack = nlooptrack; +} + +void World::SetGameServerAddress(NetAddress *address) +{ + m_GameServerAddress.FromNetAddress(address); +} + +InfoString *World::GetServerInfoString() +{ + return &m_ServerInfo; +} + +bool World::GetPlayerInfoString(int playerNum, InfoString *infoString) +{ + if (!infoString || playerNum >= MAX_CLIENTS || playerNum < 0) { + return false; + } + + if (!m_Players[playerNum].userinfo[0] + || !m_Players[playerNum].active) { + return false; + } + + return infoString->SetString(m_Players[playerNum].userinfo); +} + +void World::SetExtraInfo(char *nclientfallback, int nallowCheats) +{ + strcopy(m_ClientFallback, nclientfallback); + m_AllowCheats = nallowCheats ? true : false; +} + +void World::ParseEvent(BitBuffer *stream) +{ + event_args_t nullargs; + memset(&nullargs, 0, sizeof(nullargs)); + m_Delta.ParseDelta(stream, (byte *)&nullargs, (byte *)&nullargs, GetEventDelta()); +} + +void World::ParseBaseline(BitBuffer *stream) +{ + bool custom; + unsigned char type; + int count = 0; + + entity_state_t nullstate; + entity_state_t entstate; + delta_t **ppentity, **ppcustom, **ppplayer; + + ppentity = m_Delta.LookupRegistration("entity_state_t"); + ppplayer = m_Delta.LookupRegistration("entity_state_player_t"); + ppcustom = m_Delta.LookupRegistration("custom_entity_state_t"); + + if (!(ppentity && *ppentity && ppcustom && *ppcustom && ppplayer && *ppplayer)) { + m_System->Errorf("Server::ParseBaseline: needed delta encoder missing.\n"); + return; + } + + memset(&nullstate, 0, sizeof(nullstate)); + stream->StartBitMode(); + m_MaxBaseLines = 0; + ClearBaseline(); + + while (stream->PeekBits(16) != 0xFFFF) + { + int index = stream->ReadBits(11); + m_MaxBaseLines = max(index, m_MaxBaseLines); + + memset(&entstate, 0, sizeof(nullstate)); + + type = stream->ReadBits(2); + custom = (type & ENTITY_BEAM) == ENTITY_BEAM; + + delta_t *delta; + if (custom) + { + delta = *ppcustom; + } + else + { + delta = IsPlayerIndex(index) ? *ppplayer : *ppentity; + } + + m_Delta.ParseDelta(stream, (byte *)&nullstate, (byte *)&entstate, delta); + + entstate.entityType = type; + AddBaselineEntity(index, &entstate); + count++; + } + + m_System->Printf("Received baseline with %i entities.\n", count); + + stream->ReadBits(16); + ClearInstancedBaseline(); + m_MaxInstanced_BaseLine = stream->ReadBits(6); + for (int i = 0; i < m_MaxInstanced_BaseLine; i++) + { + memset(&entstate, 0, sizeof(entstate)); + m_Delta.ParseDelta(stream, (byte *)&nullstate, (byte *)&entstate, *ppentity); + AddInstancedBaselineEntity(i, &entstate); + } + + if (m_MaxInstanced_BaseLine) { + m_System->Printf("Received instanced baseline with %i entities.\n", m_MaxInstanced_BaseLine); + } + + stream->EndBitMode(); +} + +bool World::IsVoiceEnabled() +{ + return m_VoiceEnabled; +} + +int World::GetSlotNumber() +{ + return m_PlayerNum; +} + +void World::SetViewEntity(int nviewentity) +{ + m_ViewEntity = nviewentity; +} + +void World::SetVoiceEnabled(bool state) +{ + m_VoiceEnabled = state; +} + +NetAddress *World::GetGameServerAddress() +{ + return &m_GameServerAddress; +} + +char *World::GetLevelName() +{ + return m_LevelName; +} + +IBSPModel *World::GetWorldModel() +{ + if (!m_WorldModel.IsValid()) { + m_WorldModel.Load(m_LevelName, true); + } + + return &m_WorldModel; +} + +char *World::GetGameDir() +{ + return m_GameDir; +} + +int World::GetServerCount() +{ + return m_ServerCount; +} + +int World::GetMaxClients() +{ + return m_Maxclients; +} + +bool World::SaveAsDemo(char *filename, IDirector *director) +{ + DemoFile demoFile; + NetChannel demoChannel; + + client_data_t cdata; + double lastFrameTime = 0; + double originalWorldTime = m_WorldTime; + unsigned int clientDelta = 0; + unsigned int lastFrameSeqNr = 0; + + memset(&cdata, 0, sizeof(cdata)); + + frame_t *frame = (frame_t *)m_Frames.GetFirst(); + if (!frame) { + return false; + } + + m_WorldTime = frame->time; + demoChannel.Create(m_System, 0, 0); + demoFile.Init(this, 0, &demoChannel); + + if (!demoFile.StartRecording(filename)) { + return false; + } + + m_System->Printf("Saving game to \"%s\". This can take some time...\n", filename); + + while (frame) + { + m_WorldTime = frame->time; + + if (!lastFrameSeqNr || lastFrameSeqNr > frame->seqnr) { + lastFrameSeqNr = frame->seqnr - 1; + } + + if (lastFrameSeqNr < frame->seqnr) + { + demoChannel.m_unreliableStream.WriteByte(svc_time); + demoChannel.m_unreliableStream.WriteByte(frame->time); + + WriteFrame(frame, lastFrameSeqNr, &demoChannel.m_reliableStream, &demoChannel.m_unreliableStream, lastFrameSeqNr, clientDelta, true); + + if (director) { + director->WriteCommands(&demoChannel.m_reliableStream, lastFrameTime, frame->time); + } + + lastFrameSeqNr = frame->seqnr; + clientDelta = (demoChannel.m_outgoing_sequence & 0xFF); + + if (demoChannel.m_reliableStream.IsOverflowed()) { + m_System->Printf("WARNING! World::SaveAsDemo: reliable data overflow.\n"); + return false; + } + + if (demoChannel.m_unreliableStream.IsOverflowed()) { + demoChannel.m_unreliableStream.Clear(); + } + + demoFile.WriteDemoMessage(&demoChannel.m_unreliableStream, &demoChannel.m_reliableStream); + demoChannel.TransmitOutgoing(); + demoFile.WriteUpdateClientData(&cdata); + + lastFrameTime = frame->time; + frame = (frame_t *)m_Frames.FindExactKey(lastFrameSeqNr + 1); + } + } + + return true; +} + +serverinfo_t *World::GetServerInfo() +{ + return &m_DetailedServerInfo; +} + +bool World::WriteDeltaEntities(BitBuffer *stream, frame_t *fullFrame, unsigned int deltaSeqNr, unsigned int clientDelta) +{ + BitBuffer *buffer; + if (!GetDeltaFromCache(fullFrame->seqnr, deltaSeqNr, &buffer)) + { + frame_t deltaFrame; + if (!GetUncompressedFrame(deltaSeqNr, &deltaFrame)) + { + m_System->DPrintf("WARNING! World::WriteFrame: delta frame too old (%i).\n", deltaSeqNr); + return false; + } + + buffer->Resize(fullFrame->entitiesSize); + WritePacketEntities(buffer, fullFrame, &deltaFrame); + } + + stream->WriteByte(svc_deltapacketentities); + stream->WriteShort(fullFrame->entitynum); + stream->WriteByte(clientDelta); + stream->ConcatBuffer(buffer); + return true; +} + +void World::RearrangeFrame(frame_t *frame, int seqNrOffset, float timeOffset) +{ + frame_t fullFrame; + if (!GetUncompressedFrame(frame, &fullFrame)) { + return; + } + + m_FramesByTime.ChangeKey(frame, frame->time - timeOffset); + m_Frames.ChangeKey(frame, frame->seqnr - seqNrOffset); + + frame->time -= timeOffset; + frame->seqnr -= seqNrOffset; + + m_Delta.SetLargeTimeBufferSize(true); + + if (frame->entitynum) + { + for (unsigned int i = 0; i < fullFrame.entitynum; i++) + { + entity_state_t *entity = &((entity_state_t *)fullFrame.entities)[i]; + if (entity->animtime != m_BaseLines[entity->number].animtime) { + entity->animtime -= timeOffset; + } + + if (entity->impacttime != m_BaseLines[entity->number].impacttime) { + entity->impacttime -= timeOffset; + } + + if (entity->starttime != m_BaseLines[entity->number].starttime) { + entity->starttime -= timeOffset; + } + } + + BitBuffer tempStream(frame->entities, frame->entitiesSize); + memset(frame->entities, 0, frame->entitiesSize); + + int newsize = CompressFrame(&fullFrame, &tempStream); + if ((unsigned)newsize > frame->entitiesSize || tempStream.IsOverflowed()) { + m_System->Printf("WARNING! World::RearrangeFrame: wrong entities size (%i != %i).\n", frame->entitiesSize, newsize); + return; + } + + frame->entitiesSize = newsize; + } + + m_Delta.SetLargeTimeBufferSize(false); +} + +void World::UpdateServerInfo() +{ + strcopy(m_DetailedServerInfo.address, ""); + strcopy(m_DetailedServerInfo.name, GetHostName()); + + COM_FileBase(m_LevelName, m_DetailedServerInfo.map); + m_DetailedServerInfo.map[sizeof(m_DetailedServerInfo.map) - 1] = '\0'; + + strcopy(m_DetailedServerInfo.gamedir, m_GameDir); + strcopy(m_DetailedServerInfo.description, "HLTV"); + + m_DetailedServerInfo.activePlayers = m_PlayerNum; + m_DetailedServerInfo.maxPlayers = m_Maxclients; + m_DetailedServerInfo.protocol = m_Protocol; + m_DetailedServerInfo.type = GetServerType(HLST_Dedicated)[0]; + m_DetailedServerInfo.os = GetServerOS()[0]; + m_DetailedServerInfo.pw = PROTOCOL_VERSION; + m_DetailedServerInfo.mod = false; +} + +void World::SetHLTV(bool state) +{ + m_IsHLTV = state; +} + +bool World::IsHLTV() +{ + return m_IsHLTV; +} + +void World::RunFrame(double time) +{ + BaseSystemModule::RunFrame(time); +} + +void World::ReceiveSignal(ISystemModule *module, unsigned int signal, void *data) +{ + BaseSystemModule::ReceiveSignal(module, signal, data); +} + +void World::ExecuteCommand(int commandID, char *commandLine) +{ + BaseSystemModule::ExecuteCommand(commandID, commandLine); +} + +void World::RegisterListener(ISystemModule *module) +{ + BaseSystemModule::RegisterListener(module); +} + +void World::RemoveListener(ISystemModule *module) +{ + BaseSystemModule::RemoveListener(module); +} + +IBaseSystem *World::GetSystem() +{ + return BaseSystemModule::GetSystem(); +} + +int World::GetSerial() +{ + return BaseSystemModule::GetSerial(); +} + +char *World::GetName() +{ + return BaseSystemModule::GetName(); +} + +int World::GetState() +{ + return BaseSystemModule::GetState(); +} + +int World::GetVersion() +{ + return BaseSystemModule::GetVersion(); +} + +IBaseInterface *CreateWorld() +{ + IWorld *pWorld = new World; + return (IBaseInterface *)pWorld; +} + +#ifndef HOOK_HLTV +bool World::IsDeltaEncoder() const +{ + if (Delta::m_CustomentityDelta + && Delta::m_EntityDelta + && Delta::m_PlayerDelta) { + return true; + } + + return false; +} + +delta_t *World::GetDeltaEncoder(int index, bool custom) +{ + if (custom) { + return Delta::m_CustomentityDelta; + } + else if (IsPlayerIndex(index)) { + return Delta::m_PlayerDelta; + } + + return Delta::m_EntityDelta; +} + +delta_t *World::GetEventDelta() const { + return Delta::m_EventDelta; +} + +delta_t *World::GetClientDelta() const { + return Delta::m_ClientDelta; +} + +delta_t *World::GetEntityDelta() const { + return Delta::m_EntityDelta; +} + +delta_t *World::GetWeaponDelta() const { + return Delta::m_WeaponDelta; +} + +EXPOSE_INTERFACE_FN(CreateWorld, World, WORLD_INTERFACE_VERSION); +#endif // HOOK_HLTV diff --git a/rehlds/HLTV/Core/src/World.h b/rehlds/HLTV/Core/src/World.h new file mode 100644 index 0000000..057c2c8 --- /dev/null +++ b/rehlds/HLTV/Core/src/World.h @@ -0,0 +1,310 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "info.h" // MAX_INFO_STRING +#include "qlimits.h" // MAX_PACKET_ENTITIES + +#include "Delta.h" +#include "ObjectDictionary.h" +#include "BSPModel.h" + +// TODO: move to qlimits.h +#define RESOURCE_INDEX_BITS 12 +#define RESOURCE_MAX_COUNT (1 << RESOURCE_INDEX_BITS) + +class World: public IWorld, public BaseSystemModule { +public: + World() {} + virtual ~World() {} + + bool Init(IBaseSystem *system, int serial, char *name); + void RunFrame(double time); + void ReceiveSignal(ISystemModule *module, unsigned int signal, void *data); + void ExecuteCommand(int commandID, char *commandLine); + void RegisterListener(ISystemModule *module); + void RemoveListener(ISystemModule *module); + IBaseSystem *GetSystem(); + int GetSerial(); + char *GetStatusLine(); + char *GetType(); + char *GetName(); + int GetState(); + int GetVersion(); + void ShutDown(); + + double GetTime(); + NetAddress *GetGameServerAddress(); + char *GetLevelName(); + char *GetGameDir(); + frame_t *GetFrameByTime(double time); + frame_t *GetFrameBySeqNr(unsigned int seqnr); + frame_t *GetLastFrame(); + frame_t *GetFirstFrame(); + int GetServerCount(); + int GetSlotNumber(); + int GetMaxClients(); + int GetNumPlayers(); + IBSPModel *GetWorldModel(); + InfoString *GetServerInfoString(); + bool GetPlayerInfoString(int playerNum, InfoString *infoString); + UserMsg *GetUserMsg(int msgNumber); + char *GetHostName(); + serverinfo_t *GetServerInfo(); + + bool IsPlayerIndex(int index); + bool IsVoiceEnabled(); + bool IsActive(); + bool IsPaused(); + bool IsComplete(); + bool IsHLTV(); + void Reset(); + + void SetServerInfo(int protocol, CRC32_t nserverCRC, unsigned char *nclientdllmd5, int nmaxclients, int nplayernum, int ngametype, char *ngamedir, char *nservername, char *nlevelname); + void SetServerInfoString(char *infostring); + void SetServerInfo(serverinfo_t *serverinfo); + + void UpdateServerInfo(); + void SetPaused(bool state); + void SetTime(double newTime); + void SetBufferSize(float seconds); + void SetVoiceEnabled(bool state); + void SetMoveVars(movevars_t *nmovevars); + void SetCDInfo(int ncdtrack, int nlooptrack); + void SetHLTV(bool state); + void SetExtraInfo(char *nclientfallback, int nallowCheats); + void SetViewEntity(int nviewentity); + void SetGameServerAddress(NetAddress *address); + void SetHostName(char *name); + void NewGame(int newServerCount); + void FinishGame(); + bool SaveAsDemo(char *filename, IDirector *director); + void StopGame(); + int FindUserMsgByName(char *name); + void ParseDeltaDescription(BitBuffer *stream); + void ParseBaseline(BitBuffer *stream); + void ParseEvent(BitBuffer *stream); + void ParseClientData(BitBuffer *stream, unsigned int deltaSeqNr, BitBuffer *to, clientdata_t *clientData); + bool GetUncompressedFrame(unsigned int seqNr, frame_t *frame); + bool UncompressEntitiesFromStream(frame_t *frame, BitBuffer *stream); + bool UncompressEntitiesFromStream(frame_t *frame, BitBuffer *stream, unsigned int from); + bool GetClientData(unsigned int SeqNr, clientdata_t *clientData); + bool GetClientData(frame_t *frame, clientdata_t *clientData); + int AddFrame(frame_t *newFrame); + bool AddResource(resource_t *resource); + void AddLightStyle(int index, char *style); + bool AddSignonData(unsigned char type, unsigned char *data, int size); + bool AddUserMessage(int msgNumber, int size, char *name); + void AddBaselineEntity(int index, entity_state_t *ent); + void AddInstancedBaselineEntity(int index, entity_state_t *ent); + void UpdatePlayer(int playerNum, int userId, char *infostring, char *hashedcdkey); + + void WriteFrame(frame_t *frame, unsigned int lastFrameSeqnr, BitBuffer *reliableStream, BitBuffer *unreliableStream, unsigned int deltaSeqNr, unsigned int clientDelta, bool addVoice); + void WriteNewData(BitBuffer *stream); + void WriteClientUpdate(BitBuffer *stream, int playerIndex); + void WriteMovevars(BitBuffer *stream); + void WriteSigonData(BitBuffer *stream); + void WriteLightStyles(BitBuffer *stream); + + int RemoveFrames(unsigned int startSeqNr, unsigned int endSeqNr); + int DuplicateFrames(unsigned int startSeqNr, unsigned int endSeqNr); + int MoveFrames(unsigned int startSeqNr, unsigned int endSeqNr, double destSeqnr); + int RevertFrames(unsigned int startSeqNr, unsigned int endSeqNr); + +private: + int CompressFrame(frame_t *from, BitBuffer *stream); + int ParseDeltaHeader(BitBuffer *stream, bool *remove, bool *custom, int *numbase, bool *newbl, int *newblindex, bool full, int *offset); + void SetDirector(IDirector *director); + void SetTimeScale(float scale); + void SetGameGroupAddress(NetAddress *addr); + bool SetMaxClients(int max); + void SetName(char *newName); + IDirector *GetDirector(); + float GetBufferedGameTime(); + void ConnectionComplete(); + + void WriteResources(BitBuffer *stream); + void WriteDeltaDescriptions(BitBuffer *stream); + void WriteRegisteredUserMessages(BitBuffer *stream); + void WriteBaseline(BitBuffer *stream); + void WriteServerinfo(BitBuffer *stream); + void WriteCustomDecals(BitBuffer *stream); + void WritePacketEntities(BitBuffer *stream, frame_t *frame, frame_t *deltaframe); + bool WriteDeltaEntities(BitBuffer *stream, frame_t *fullFrame, unsigned int deltaSeqNr, unsigned int clientDelta); + + enum WorldState { + WORLD_UNDEFINED, + WORLD_INITIALIZING, + WORLD_DISCONNECTED, + WORLD_CONNECTING, + WORLD_RUNNING, + WORLD_COMPLETE + }; + + void SetState(WorldState newState); + void ClearUserMessages(); + void ClearServerInfo(); + void ClearResources(); + void ClearInstancedBaseline(); + void ClearBaseline(); + void ClearLightStyles(); + void ClearPlayers(); + void ClearFrames(); + void ClearEntityCache(); + bool GetFrameFromCache(unsigned int seqNr, entity_state_t **entities); + bool GetUncompressedFrame(frame_t *deltaFrame, frame_t *frame); + bool GetDeltaFromCache(unsigned int seqNr, unsigned int deltaNr, BitBuffer **buffer); + void CheckFrameBufferSize(); + void ReorderFrameTimes(float newLastTime); + int FindBestBaseline(int index, entity_state_t **baseline, entity_state_t *to, int num, bool custom); + void RearrangeFrame(frame_t *frame, int seqNrOffset, float timeOffset); + + delta_t *GetEventDelta() const; + delta_t *GetClientDelta() const; + delta_t *GetEntityDelta() const; + delta_t *GetWeaponDelta() const; + delta_t *GetDeltaEncoder(int index, bool custom); + bool IsDeltaEncoder() const; + +protected: + bool m_IsPaused; + int m_WorldState; + int m_Protocol; + int m_ServerCount; + CRC32_t m_ServerCRC; + unsigned char m_ClientdllMD5[16]; + int m_Maxclients; + int m_PlayerNum; + int m_GameType; + char m_GameDir[MAX_PATH]; + char m_LevelName[40]; + char m_ServerName[255]; + + int m_ViewEntity; + serverinfo_t m_DetailedServerInfo; + + enum { + MAX_ENTITIES = 1380, + MAX_INSTANCED_BASELINES = 64, + MAX_FRAME_CACHE = 32, + MAX_SCOREBOARDNAME = 32, + + MAX_SERVERINFO_STRING = 512, + MAX_BUFFER_SIGNONDATA = 32768 + }; + + // Defined in client.h differently + typedef struct player_info_s + { + int userid; // User id on server + + // User info string + char userinfo[MAX_INFO_STRING]; // User info string + char name[MAX_SCOREBOARDNAME]; // Name + int spectator; // Spectator or not, unused + int ping; + int packet_loss; + + // skin information + char model[MAX_PATH]; + int topcolor; + int bottomcolor; + + bool active; + char hashedcdkey[16]; + + int trackerID; + + } player_info_t; + + player_info_t m_Players[MAX_CLIENTS]; + unsigned char m_EntityBuffer[87040]; + + entity_state_t m_BaseLines[MAX_ENTITIES]; + int m_MaxBaseLines; + entity_state_t m_Instanced_BaseLines[MAX_INSTANCED_BASELINES]; + + int m_MaxInstanced_BaseLine; + char m_Lightstyles[MAX_LIGHTSTYLES][65]; + + movevars_t m_MoveVars; + BSPModel m_WorldModel; + InfoString m_ServerInfo; + UserMsg *m_ClientUserMsgs; + resource_t *m_ResourcesList; + int m_ResourcesNum; + + BitBuffer m_SignonData; + int m_CDTrack; + int m_LoopTrack; + + ObjectDictionary m_Frames; + ObjectDictionary m_FramesByTime; + ObjectDictionary m_CamCommands; + + unsigned int m_SequenceNr; + float m_MaxBufferLength; + + typedef struct frameCache_s { + unsigned int seqNr; + entity_state_t entities[MAX_PACKET_ENTITIES]; + } frameCache_t; + + typedef struct deltaCache_s { + unsigned int seqNr; + unsigned int deltaNr; + BitBuffer buffer; + } deltaCache_t; + + int m_MaxCacheIndex; + frameCache_t *m_FrameCache; + deltaCache_t *m_DeltaCache; + int m_CacheHits; + int m_CacheFaults; + + double m_WorldTime; + double m_StartTime; + + bool m_VoiceEnabled; + char m_ClientFallback[MAX_PATH]; + bool m_AllowCheats; + bool m_IsHLTV; + char m_HostName[255]; + + NetAddress m_GameServerAddress; + +#ifdef HOOK_HLTV + static DeltaWrapper m_Delta; +#else + Delta m_Delta; +#endif // HOOK_HLTV + +}; + +extern char g_DownloadURL[128]; diff --git a/rehlds/HLTV/Core/src/precompiled.cpp b/rehlds/HLTV/Core/src/precompiled.cpp new file mode 100644 index 0000000..5f656a4 --- /dev/null +++ b/rehlds/HLTV/Core/src/precompiled.cpp @@ -0,0 +1 @@ +#include "precompiled.h" diff --git a/rehlds/HLTV/Core/src/precompiled.h b/rehlds/HLTV/Core/src/precompiled.h new file mode 100644 index 0000000..b17cb08 --- /dev/null +++ b/rehlds/HLTV/Core/src/precompiled.h @@ -0,0 +1,44 @@ +#pragma once + +#include "basetypes.h" +#include "archtypes.h" +#include "mathlib.h" +#include "FileSystem.h" + +#include "interface.h" +#include "IBaseSystem.h" + +#include "mem.h" +#include "common.h" + +// Hooks stuff +#include "hookers/HLTV/Core/hooklist.h" + +#include "common/md5.h" +#include "common/random.h" +#include "common/byteorder.h" +#include "common/ServerInfo.h" +#include "common/common_hltv.h" +#include "common/net_internal.h" +#include "common/mathlib_internal.h" + +#include "common/DirectorCmd.h" +#include "common/NetAddress.h" +#include "common/NetChannel.h" +#include "common/BaseClient.h" + +#include "common/DemoFile.h" +#include "common/munge.h" + +#include +#include +#include +#include +#include +#include + +// Core module stuff +#include "World.h" +#include "Network.h" +#include "NetSocket.h" +#include "Server.h" diff --git a/rehlds/HLTV/Core/src/public_amalgamation.cpp b/rehlds/HLTV/Core/src/public_amalgamation.cpp new file mode 100644 index 0000000..1fc308c --- /dev/null +++ b/rehlds/HLTV/Core/src/public_amalgamation.cpp @@ -0,0 +1,3 @@ +#include "precompiled.h" + +#include "interface.cpp" diff --git a/rehlds/HLTV/DemoPlayer/build.gradle b/rehlds/HLTV/DemoPlayer/build.gradle new file mode 100644 index 0000000..11ae425 --- /dev/null +++ b/rehlds/HLTV/DemoPlayer/build.gradle @@ -0,0 +1,139 @@ +import org.doomedsociety.gradlecpp.cfg.ToolchainConfigUtils +import org.doomedsociety.gradlecpp.msvc.MsvcToolchainConfig +import org.doomedsociety.gradlecpp.toolchain.icc.Icc +import org.doomedsociety.gradlecpp.toolchain.icc.IccCompilerPlugin +import org.doomedsociety.gradlecpp.gcc.GccToolchainConfig +import org.gradle.nativeplatform.NativeBinarySpec +import org.gradle.nativeplatform.NativeLibrarySpec +import org.gradle.nativeplatform.toolchain.VisualCpp + +apply plugin: 'cpp' +apply plugin: IccCompilerPlugin +apply plugin: GccCompilerPlugin + +void setupToolchain(NativeBinarySpec b) { + def cfg = rootProject.createToolchainConfig(b); + cfg.projectInclude(project, '/..', '/../..', '/src', '/../common', '/../../common', '/../../engine', '/../../public', '/../../public/rehlds', '/../../pm_shared'); + cfg.singleDefines 'USE_BREAKPAD_HANDLER', 'HLTV', 'DEMOPLAYER_MODULE' + + if (cfg instanceof MsvcToolchainConfig) { + cfg.compilerOptions.pchConfig = new MsvcToolchainConfig.PrecompiledHeadersConfig( + enabled: true, + pchHeader: 'precompiled.h', + pchSourceSet: 'demoplayer_pch' + ); + + cfg.singleDefines('_CRT_SECURE_NO_WARNINGS') + cfg.compilerOptions.args '/Ob2', '/Oi', '/GF' + cfg.extraLibs "psapi.lib" + } + else if (cfg instanceof GccToolchainConfig) { + cfg.compilerOptions.pchConfig = new GccToolchainConfig.PrecompilerHeaderOptions( + enabled: true, + pchSourceSet: 'demoplayer_pch' + ); + + cfg.compilerOptions.languageStandard = 'c++0x' + cfg.defines([ + '_strdup': 'strdup', + '_stricmp': 'strcasecmp', + '_strnicmp': 'strncasecmp', + '_vsnprintf': 'vsnprintf', + '_snprintf': 'snprintf', + ]); + + cfg.compilerOptions.args '-Qoption,cpp,--treat_func_as_string_literal_cpp', '-fno-exceptions' + } + + ToolchainConfigUtils.apply(project, cfg, b); +} + +model { + buildTypes { + release + } + + platforms { + x86 { + architecture "x86" + } + } + + toolChains { + visualCpp(VisualCpp) { + } + icc(Icc) { + } + } + + components { + demoplayer(NativeLibrarySpec) { + targetPlatform 'x86' + baseName 'demoplayer' + + sources { + demoplayer_main(CppSourceSet) { + source { + srcDir "src" + include "**/*.cpp" + exclude "precompiled.cpp" + } + } + + demoplayer_common(CppSourceSet) { + source { + srcDirs "/../../common", "/../common" + + // common + include "BaseSystemModule.cpp" + include "ObjectDictionary.cpp" + include "ObjectList.cpp" + include "TokenLine.cpp" + + // HLTV common + include "BitBuffer.cpp" + include "byteorder.cpp" + include "common.cpp" + include "DirectorCmd.cpp" + include "mathlib.cpp" + } + } + + demoplayer_engine(CppSourceSet) { + source { + srcDir "../../engine" + include "mem.cpp" + } + } + + demoplayer_pch(CppSourceSet) { + source { + srcDir "src" + include "precompiled.cpp" + } + } + } + + binaries.all { + NativeBinarySpec b -> project.setupToolchain(b) + } + } + } +} + +task buildFixes { + dependsOn binaries.withType(SharedLibraryBinarySpec).matching { SharedLibraryBinarySpec blib -> + blib.buildable && blib.buildType.name == 'release' + } +} + +task buildRelease { + dependsOn binaries.withType(SharedLibraryBinarySpec).matching { SharedLibraryBinarySpec blib -> + blib.buildable && blib.buildType.name == 'release' + } +} + +// prevent static lib building +binaries.withType(StaticLibraryBinarySpec) { binary -> + buildable = false +} diff --git a/rehlds/HLTV/DemoPlayer/msvc/DemoPlayer.sln b/rehlds/HLTV/DemoPlayer/msvc/DemoPlayer.sln new file mode 100644 index 0000000..317caa2 --- /dev/null +++ b/rehlds/HLTV/DemoPlayer/msvc/DemoPlayer.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DemoPlayer", "DemoPlayer.vcxproj", "{05292761-0847-4A68-BA10-9D384DC0D3EE}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {05292761-0847-4A68-BA10-9D384DC0D3EE}.Debug|Win32.ActiveCfg = Debug|Win32 + {05292761-0847-4A68-BA10-9D384DC0D3EE}.Debug|Win32.Build.0 = Debug|Win32 + {05292761-0847-4A68-BA10-9D384DC0D3EE}.Release|Win32.ActiveCfg = Release|Win32 + {05292761-0847-4A68-BA10-9D384DC0D3EE}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/rehlds/HLTV/DemoPlayer/msvc/DemoPlayer.vcxproj b/rehlds/HLTV/DemoPlayer/msvc/DemoPlayer.vcxproj new file mode 100644 index 0000000..339ad05 --- /dev/null +++ b/rehlds/HLTV/DemoPlayer/msvc/DemoPlayer.vcxproj @@ -0,0 +1,175 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + + + + + + + true + true + + + true + true + + + + + + + + + Create + Create + + + + + + + + + + + true + true + + + + + + + + + + + + {05292761-0847-4A68-BA10-9D384DC0D3EE} + Win32Proj + DemoPlayer + 8.1 + + + + DynamicLibrary + true + v120_xp + v140_xp + MultiByte + + + DynamicLibrary + false + v120_xp + v140_xp + true + MultiByte + + + + + + + + + + + + + + + true + + + false + + + + Use + Level3 + Disabled + HLTV;WIN32;_DEBUG;_WINDOWS;_USRDLL;DEMOPLAYER_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + precompiled.h + $(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;$(ProjectDir)\..\..\..\game_shared;$(ProjectDir)\..\..\..\..\dep\bzip2\include;%(AdditionalIncludeDirectories) + MultiThreadedDebug + + + Windows + true + psapi.lib;%(AdditionalDependencies) + + + IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)") + + + Automatic deployment script + + + echo Empty Action + + + Force build to run Pre-Build event + + + git.always.run + + + git.always.run + + + + + Level3 + Use + MaxSpeed + true + true + HOOK_HLTV;HLTV;WIN32;NDEBUG;_WINDOWS;_USRDLL;DEMOPLAYER_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitiHOOons) + precompiled.h + $(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;%(AdditionalIncludeDirectories) + MultiThreaded + true + + + Windows + true + true + true + psapi.lib;%(AdditionalDependencies) + false + + + IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)") + + + Automatic deployment script + + + echo Empty Action + + + Force build to run Pre-Build event + + + git.always.run + + + git.always.run + + + + + + \ No newline at end of file diff --git a/rehlds/HLTV/DemoPlayer/msvc/DemoPlayer.vcxproj.filters b/rehlds/HLTV/DemoPlayer/msvc/DemoPlayer.vcxproj.filters new file mode 100644 index 0000000..9d7f4cd --- /dev/null +++ b/rehlds/HLTV/DemoPlayer/msvc/DemoPlayer.vcxproj.filters @@ -0,0 +1,114 @@ + + + + + {4d99bbd6-4658-4bed-a29e-9322ec16db5a} + + + {c58ccefe-767c-4f0a-b075-c55e24d15263} + + + {ca1c7722-8e65-480d-9e3c-34f7f2524372} + + + {1f62aa80-c3f7-4f97-a93e-5b67b153a751} + + + {8712d29d-6fec-42fb-9f6e-5618302f3203} + + + {fab89dcf-8536-490e-81b3-d9bb29fce8c6} + + + + + src + + + src + + + src + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + src\hookers + + + src\hookers + + + common + + + common + + + common + + + common + + + engine + + + + + src + + + src + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + src\hookers + + + common + + + common + + + common + + + common + + + engine + + + HLTV\common + + + \ No newline at end of file diff --git a/rehlds/HLTV/DemoPlayer/msvc/PostBuild.bat b/rehlds/HLTV/DemoPlayer/msvc/PostBuild.bat new file mode 100644 index 0000000..8583878 --- /dev/null +++ b/rehlds/HLTV/DemoPlayer/msvc/PostBuild.bat @@ -0,0 +1,39 @@ +@echo OFF +:: +:: Post-build auto-deploy script +:: Create and fill PublishPath.txt file with path to deployment folder +:: I.e. PublishPath.txt should contain one line with a folder path +:: Call it so: +:: IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)") +:: + +SET targetDir=%~1 +SET targetName=%~2 +SET targetExt=%~3 +SET projectDir=%~4 +SET destination= + +IF NOT EXIST "%projectDir%\PublishPath.txt" ( + ECHO No deployment path specified. Create PublishPath.txt near PostBuild.bat with paths on separate lines for auto deployment. + exit /B 0 +) + +FOR /f "tokens=* delims= usebackq" %%a IN ("%projectDir%\PublishPath.txt") DO ( + ECHO Deploying to: %%a + IF NOT "%%a" == "" ( + copy /Y "%targetDir%%targetName%%targetExt%" "%%a" + IF NOT ERRORLEVEL 1 ( + IF EXIST "%targetDir%%targetName%.pdb" ( + copy /Y "%targetDir%%targetName%.pdb" "%%a" + ) + ) ELSE ( + ECHO PostBuild.bat ^(27^) : warning : Can't copy '%targetName%%targetExt%' to deploy path '%%a' + ) + ) +) + +IF "%%a" == "" ( + ECHO No deployment path specified. +) + +exit /B 0 \ No newline at end of file diff --git a/rehlds/HLTV/DemoPlayer/src/DemoPlayer.cpp b/rehlds/HLTV/DemoPlayer/src/DemoPlayer.cpp new file mode 100644 index 0000000..ff12b72 --- /dev/null +++ b/rehlds/HLTV/DemoPlayer/src/DemoPlayer.cpp @@ -0,0 +1,1060 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +DemoPlayer::DemoPlayer() +{ + m_World = nullptr; + m_System = nullptr; + m_Engine = nullptr; + m_Server = nullptr; +} + +bool DemoPlayer::Init(IBaseSystem *system, int serial, char *name) +{ + if (!BaseSystemModule::Init(system, serial, name)) { + return false; + } + + if (!name) { + SetName(DEMOPLAYER_INTERFACE_VERSION); + } + + m_Engine = (IEngineWrapper *)m_System->GetModule(ENGINEWRAPPER_INTERFACE_VERSION, ""); + if (!m_Engine) { + m_System->Printf("DemoPlayer::Init: couldn't get engine interface.\n"); + return false; + } + + m_Server = (IServer *)m_System->GetModule(SERVER_INTERFACE_VERSION, "core", "DemoServer"); + if (!m_Server) { + m_System->Printf("DemoPlayer::Init: couldn't load server module.\n"); + return false; + } + + m_Server->RegisterListener(this); + m_Server->SetDirector(GetDirector()); + m_Server->SetDelayReconnect(false); + + m_World = (IWorld *)m_System->GetModule(WORLD_INTERFACE_VERSION, "core", "DemoWorld"); + if (!m_World) { + m_System->Printf("DemoPlayer::Init: couldn't load world module.\n"); + return false; + } + + m_System->RegisterCommand("dem_jump", this, CMD_ID_JUMP); + m_System->RegisterCommand("dem_forcehltv", this, CMD_ID_FORCEHLTV); + m_System->RegisterCommand("dem_pause", this, CMD_ID_PAUSE); + m_System->RegisterCommand("dem_speed", this, CMD_ID_SPEED); + m_System->RegisterCommand("dem_start", this, CMD_ID_START); + m_System->RegisterCommand("dem_save", this, CMD_ID_SAVE); + + m_World->RegisterListener(this); + m_DemoStream.Resize(65536); + + SetTimeScale(1); + SetPaused(false); + + m_Outgoing_sequence = 0; + m_DeltaFrameSeqNr = 0; + m_LastFrameSeqNr = 0; + m_LastCmd = nullptr; + + m_LastFrameTime = 0; + m_CommandCounter = 0; + m_PlayerState = DEMOPLAYER_UNDEFINED; + m_EditorMode = false; + m_IsSaving = false; + m_MasterMode = true; + + memset(m_DemoFileName, 0, sizeof(m_DemoFileName)); + + m_State = MODULE_RUNNING; + m_System->DPrintf("DemoPlayer module initialized.\n"); + return true; +} + +void DemoPlayer::RunFrame(double time) +{ + BaseSystemModule::RunFrame(time); + + if (m_PlayerState != DEMOPLAYER_UNDEFINED) { + RunClocks(); + } +} + +void DemoPlayer::ReceiveSignal(ISystemModule *module, unsigned int signal, void *data) +{ + int from = module->GetSerial(); + + // Server module + if (from == m_Server->GetSerial()) + { + if (signal == 6) { + m_System->Printf("Demo file completely loaded.\n"); + return; + } + } + + // World module + if (from == m_World->GetSerial()) + { + BitBuffer buf(32); + switch (signal) + { + case 1: + m_StartTime = 9999; + break; + case 2: + NewGame(m_World); + break; + case 5: + case 6: + buf.WriteByte(svc_setpause); + buf.WriteByte(signal == 5 ? true : false); + break; + } + } +} + +bool DemoPlayer::SaveGame(char *filename) +{ + if (IsLoading()) { + return false; + } + + SetPaused(true); + + m_IsSaving = true; + auto ret = m_World->SaveAsDemo(filename ? filename : m_DemoFileName, this); + m_IsSaving = false; + + return ret; +} + +double DemoPlayer::GetWorldTime() +{ + return m_WorldTime; +} + +double DemoPlayer::GetStartTime() +{ + frame_t *frame = m_World->GetFirstFrame(); + if (!frame) { + return 0; + } + + return frame->time; +} + +double DemoPlayer::GetEndTime() +{ + frame_t *frame = m_World->GetLastFrame(); + if (!frame) { + return 0; + } + + return frame->time; +} + +double DemoPlayer::GetPlayerTime() +{ + return m_PlayerTime; +} + +bool DemoPlayer::IsLoading() +{ + if (!m_Server) { + return false; + } + + return m_Server->IsConnected(); +} + +bool DemoPlayer::IsActive() +{ + return m_PlayerState != 0; +} + +char *DemoPlayer::GetType() +{ + return DEMOPLAYER_INTERFACE_VERSION; +} + +char *DemoPlayer::GetStatusLine() +{ + return "No status available.\n"; +} + +IWorld *DemoPlayer::GetWorld() +{ + return m_World; +} + +IDirector *DemoPlayer::GetDirector() +{ + return this; +} + +void DemoPlayer::ExecuteCommand(int commandID, char *commandLine) +{ + switch (commandID) + { + case CMD_ID_JUMP: + CMD_Jump(commandLine); + break; + case CMD_ID_FORCEHLTV: + CMD_ForceHLTV(commandLine); + break; + case CMD_ID_PAUSE: + CMD_Pause(commandLine); + break; + case CMD_ID_SPEED: + CMD_Speed(commandLine); + break; + case CMD_ID_START: + CMD_Start(commandLine); + break; + case CMD_ID_SAVE: + CMD_Save(commandLine); + break; + default: + m_System->Printf("ERROR! DemoPlayer::ExecuteCommand: unknown command ID %i.\n", commandID); + break; + } +} + +void DemoPlayer::CMD_Jump(char *cmdLine) +{ + if (!IsActive()) { + m_System->Printf("Not viewing a demo.\n"); + return; + } + + TokenLine params(cmdLine); + if (params.CountToken() != 2) { + m_System->Printf("dem_jump \n"); + return; + } + + SetWorldTime(atof(params.GetToken(1)), true); + SetPaused(true); +} + +void DemoPlayer::CMD_ForceHLTV(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) { + m_System->Printf("dem_forcehltv <0|1>\n"); + return; + } + + char *val = params.GetToken(1); + ForceHLTV(atoi(val) ? true : false); +} + +void DemoPlayer::CMD_Save(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) { + m_System->Printf("dem_save \n"); + return; + } + + if (!SaveGame(params.GetToken(1))) { + m_System->Printf("Warning! Could save game as demo file.\n"); + return; + } +} + +void DemoPlayer::CMD_Pause(char *cmdLine) +{ + if (!IsActive()) { + m_System->Printf("Not viewing a demo.\n"); + return; + } + + TokenLine params(cmdLine); + if (params.CountToken() != 2) { + m_System->Printf("dem_pause <0|1>\n"); + return; + } + + char *val = params.GetToken(1); + SetPaused(atoi(val) ? true : false); +} + +void DemoPlayer::CMD_Speed(char *cmdLine) +{ + if (!IsActive()) { + m_System->Printf("Not viewing a demo.\n"); + return; + } + + TokenLine params(cmdLine); + if (params.CountToken() != 2) { + m_System->Printf("dem_speed \n"); + return; + } + + float timescale = (float)atof(params.GetToken(1)); + SetTimeScale(timescale); +} + +void DemoPlayer::CMD_Start(char *cmdLine) +{ + if (!IsActive()) { + m_System->Printf("Not viewing a demo.\n"); + return; + } + + SetWorldTime(GetStartTime(), false); +} + +float DemoPlayer::GetTimeScale() +{ + return m_TimeScale; +} + +char *DemoPlayer::GetModName() +{ + return "valve"; +} + +void DemoPlayer::WriteCommands(BitBuffer *stream, float startTime, float endTime) +{ + DirectorCmd *cmd = (DirectorCmd *)m_Commands.FindClosestKey(startTime); + while (cmd && cmd->GetTime() <= endTime) + { + if (cmd->GetTime() > startTime) + { + if (m_IsSaving) + { + cmd->WriteToStream(stream); + } + else + { + if (cmd->GetType() == DRC_CMD_TIMESCALE) + { + float timescale; + cmd->GetTimeScaleData(timescale); + + stream->WriteByte(svc_timescale); + stream->WriteFloat(timescale); + m_TimeScale = timescale; + } + else if (cmd->GetType() == DRC_CMD_CAMPATH) + { + float fov; + vec3_t v; + int flags; + cmd->GetCamPathData(v, v, fov, flags); + + if (flags & DRC_FLAG_STARTPATH) { + WriteCameraPath(cmd, stream); + } + } + else + { + cmd->WriteToStream(stream); + } + + m_LastCmd = cmd; + BaseSystemModule::FireSignal(2); + m_System->DPrintf("Director Cmd %s, Time %.2f\n", cmd->GetName(), cmd->GetTime()); + } + } + + cmd = (DirectorCmd *)m_Commands.GetNext(); + } +} + +int DemoPlayer::AddCommand(DirectorCmd *cmd) +{ + int type = cmd->GetType(); + if (type == DRC_CMD_WAYPOINTS) { + return 0; + } + + DirectorCmd *newcmd = new DirectorCmd; + newcmd->Copy(cmd); + + if (!m_Commands.Add(newcmd, newcmd->GetTime())) { + delete newcmd; + return 0; + } + + ReindexCommands(); + return newcmd->m_Index; +} + +bool DemoPlayer::RemoveCommand(int index) +{ + DirectorCmd *cmd = (DirectorCmd *)m_Commands.GetFirst(); + while (cmd) + { + if (cmd->m_Index == index) { + break; + } + + cmd = (DirectorCmd *)m_Commands.GetNext(); + } + + if (cmd) + { + m_Commands.Remove(cmd); + if (m_LastCmd == cmd) { + m_LastCmd = nullptr; + } + + delete cmd; + ReindexCommands(); + return true; + } + + return false; +} + +IObjectContainer *DemoPlayer::GetCommands() +{ + return &m_Commands; +} + +void DemoPlayer::SetWorldTime(double time, bool relative) +{ + if (relative) + m_WorldTime += time; + else + m_WorldTime = time; +} + +void DemoPlayer::SetTimeScale(float scale) +{ + m_TimeScale = clamp(scale, 0.05f, 4.0f); + + m_DemoStream.WriteByte(svc_timescale); + m_DemoStream.WriteFloat(m_TimeScale); +} + +void DemoPlayer::SetPaused(bool state) +{ + m_IsPaused = state; +} + +void DemoPlayer::SetEditMode(bool state) +{ + m_EditorMode = state; +} + +bool DemoPlayer::IsEditMode() +{ + return m_EditorMode; +} + +void DemoPlayer::Stop() +{ + m_Server->Disconnect(); + m_World->Reset(); + m_PlayerState = DEMOPLAYER_UNDEFINED; +} + +void DemoPlayer::ForceHLTV(bool state) +{ + m_ForceHLTV = state; +} + +void DemoPlayer::SetMasterMode(bool state) +{ + m_MasterMode = state; +} + +bool DemoPlayer::IsMasterMode() +{ + return m_MasterMode; +} + +bool DemoPlayer::IsPaused() +{ + return m_IsPaused; +} + +void DemoPlayer::GetDemoViewInfo(ref_params_t *rp, float *view, int *viewmodel) +{ + int i; + usercmd_t *oldcmd; + movevars_t *oldmv; + int oldviewport[4]; + + frame_t *frame = m_World->GetFrameBySeqNr(m_LastFrameSeqNr); + frame_t *fromFrame = m_World->GetFrameBySeqNr(m_LastFrameSeqNr - 1); + + if (!frame || !frame->demoInfo) { + return; + } + + oldviewport[0] = rp->viewport[0]; + oldviewport[1] = rp->viewport[1]; + oldviewport[2] = rp->viewport[2]; + oldviewport[3] = rp->viewport[3]; + + oldmv = rp->movevars; + oldcmd = rp->cmd; + + demo_info_t *demoInfo = (demo_info_t *)frame->demoInfo; + memcpy(rp, &demoInfo->rp, sizeof(*rp)); + + rp->viewport[0] = oldviewport[0]; + rp->viewport[1] = oldviewport[1]; + rp->viewport[2] = oldviewport[2]; + rp->viewport[3] = oldviewport[3]; + rp->cmd = oldcmd; + rp->movevars = oldmv; + + view[0] = demoInfo->view[0]; + view[1] = demoInfo->view[1]; + view[2] = demoInfo->view[2]; + + *viewmodel = demoInfo->viewmodel; + + if (fromFrame && fromFrame->demoInfo) + { + demo_info_t *fromDemoInfo = (demo_info_t *)fromFrame->demoInfo; + + if (fromFrame->time < frame->time) + { + float frac = float(m_WorldTime - fromFrame->time) / (frame->time - fromFrame->time); + for (i = 0; i < 3; i++) { + rp->vieworg[i] = (demoInfo->rp.vieworg[i] - fromDemoInfo->rp.vieworg[i]) * frac + fromDemoInfo->rp.vieworg[i]; + } + + for (i = 0; i < 3; i++) + { + float d = demoInfo->rp.viewangles[i] - fromDemoInfo->rp.viewangles[i]; + if (d > 180) { + d -= 360; + } + else if (d < -180) { + d += 360; + } + + rp->viewangles[i] = fromDemoInfo->rp.viewangles[i] + d * frac; + } + + NormalizeAngles(rp->viewangles); + + for (i = 0; i < 3; i++) { + rp->simvel[i] = (demoInfo->rp.simvel[i] - fromDemoInfo->rp.simvel[i]) * frac + fromDemoInfo->rp.simvel[i]; + } + + for (i = 0; i < 3; i++) { + rp->simorg[i] = (demoInfo->rp.simorg[i] - fromDemoInfo->rp.simorg[i]) * frac + fromDemoInfo->rp.simorg[i]; + } + + for (i = 0; i < 3; i++) { + rp->viewheight[i] = (demoInfo->rp.viewheight[i] - fromDemoInfo->rp.viewheight[i]) * frac + fromDemoInfo->rp.viewheight[i]; + } + + for (i = 0; i < 3; i++) { + view[i] = (demoInfo->view[i] - fromDemoInfo->view[i]) * frac + fromDemoInfo->view[i]; + } + } + } +} + +void DemoPlayer::WriteSpawn(BitBuffer *stream) +{ + m_World->WriteSigonData(stream); + + stream->WriteByte(svc_time); + stream->WriteFloat(1); + + for (int i = 0; i < m_World->GetMaxClients(); i++) { + m_World->WriteClientUpdate(stream, i); + } + + m_World->WriteLightStyles(stream); + + stream->WriteByte(svc_signonnum); + stream->WriteByte(1); +} + +void DemoPlayer::WriteCameraPath(DirectorCmd *cmd, BitBuffer *stream) +{ + ObjectList path; + path.Init(); + + float startTime = cmd->GetTime(); + bool firstCmd = true; + + DirectorCmd *command = (DirectorCmd *)m_Commands.FindExactKey(cmd->GetTime()); + while (command && command->GetType() == DRC_CMD_CAMPATH) + { + vec3_t v; + float fov; + int flags; + command->GetCamPathData(v, v, fov, flags); + + if (flags & DRC_FLAG_STARTPATH) + { + if (!firstCmd) { + break; + } + + firstCmd = false; + } + + path.AddTail(command); + command = (DirectorCmd *)m_Commands.GetNext(); + } + + command = (DirectorCmd *)path.GetFirst(); + if (!command) { + m_System->Printf("Warning! No waypoints in camera path!\n"); + return; + } + + int count = path.CountElements(); + int length = count * (command->m_Size + 2) + 2; + if (length > 250) { + m_System->Printf("Warning! Too many waypoints in a camera path!\n"); + return; + } + + // tell director about it + // send director message, that something important happed here + stream->WriteByte(svc_director); + stream->WriteByte(length); // command length in bytes + stream->WriteByte(DRC_CMD_WAYPOINTS); // event waypoints + stream->WriteByte(count); // count waypoints + + while (command) + { + float time = (command->GetTime() - startTime) * 100; + stream->WriteShort((int)time); + stream->WriteBuf(command->m_Data.GetData(), command->m_Size); + + command = (DirectorCmd *)path.GetNext(); + } +} + +void DemoPlayer::ReindexCommands() +{ + int index = 1; + DirectorCmd *cmd = (DirectorCmd *)m_Commands.GetFirst(); + while (cmd) + { + cmd->m_Index = index++; + cmd = (DirectorCmd *)m_Commands.GetNext(); + } + + BaseSystemModule::FireSignal(1); +} + +int DemoPlayer::ReadDemoMessage(unsigned char *buffer, int size) +{ + int lastSeqNr = m_LastFrameSeqNr; + switch (m_PlayerState) + { + case DEMOPLAYER_UNDEFINED: + case DEMOPLAYER_INITIALIZING: + return 0; + case DEMOPLAYER_CONNECTING: + { + m_World->WriteNewData(&m_DemoStream); + m_PlayerState = DEMOPLAYER_CONNECTED; + break; + } + case DEMOPLAYER_CONNECTED: + { + m_LastFrameSeqNr = 0; + m_DeltaFrameSeqNr = 0; + WriteSpawn(&m_DemoStream); + m_Engine->SetCvar("spec_pip", "0"); + + m_WorldTime = 0; + m_StartTime = m_PlayerTime; + + SetTimeScale(1); + SetPaused(false); + m_PlayerState = DEMOPLAYER_RUNNING; + break; + } + case DEMOPLAYER_RUNNING: + { + WriteDatagram(&m_DemoStream); + + int newSeqNr = m_LastFrameSeqNr; + while (lastSeqNr < newSeqNr) + { + frame_t *frame = m_World->GetFrameBySeqNr(++lastSeqNr); + if (frame && frame->demoData && frame->demoDataSize) { + BitBuffer stream(frame->demoData, frame->demoDataSize); + ExecuteDemoFileCommands(&stream); + } + } + break; + } + default: + break; + } + + int msgsize = m_DemoStream.CurrentSize(); + if (msgsize <= 0) { + return 0; + } + + if (msgsize > size) { + m_System->Printf("ERROR! DemoPlayer::ReadDemoMessage: data overflow (%i bytes).\n", msgsize); + return 0; + } + + memcpy(buffer, m_DemoStream.GetData(), msgsize); + m_DemoStream.FastClear(); + return msgsize; +} + +void DemoPlayer::ReadNetchanState(int *incoming_sequence, int *incoming_acknowledged, int *incoming_reliable_acknowledged, int *incoming_reliable_sequence, int *outgoing_sequence, int *reliable_sequence, int *last_reliable_sequence) +{ + *incoming_sequence = m_Outgoing_sequence; + *incoming_acknowledged = m_Outgoing_sequence; + *incoming_reliable_acknowledged = 0; + *incoming_reliable_sequence = 0; + *outgoing_sequence = m_Outgoing_sequence; + *reliable_sequence = 0; + *last_reliable_sequence = 0; +} + +void DemoPlayer::SetName(char *newName) +{ + strcopy(m_Name, newName); +} + +void DemoPlayer::RunClocks() +{ + float realTimeDiff = float(m_SystemTime - m_LastClockUpdateTime); + + m_LastClockUpdateTime = m_SystemTime; + m_PlayerTime = realTimeDiff + m_PlayerTime; + + if (!m_World->IsActive() || m_IsPaused) { + return; + } + + m_WorldTime = realTimeDiff + m_WorldTime; + + frame_t *lastFrame = m_World->GetFrameBySeqNr(m_LastFrameSeqNr); + frame_t *nextFrame = m_World->GetFrameBySeqNr(m_LastFrameSeqNr + 1); + if (nextFrame && lastFrame) + { + if ((nextFrame->time - lastFrame->time) > 2) { + m_WorldTime = nextFrame->time - 0.01f; + } + } + + lastFrame = m_World->GetLastFrame(); + frame_t *firstFrame = m_World->GetFirstFrame(); + if (firstFrame && lastFrame) + { + if (m_WorldTime > lastFrame->time) { + m_WorldTime = lastFrame->time; + } + else if (firstFrame->time > m_WorldTime) { + m_WorldTime = firstFrame->time - 0.01f; + } + } +} + +void DemoPlayer::NewGame(IWorld *world, IProxy *proxy) +{ + m_World = world; + m_PlayerTime = 1; + m_PlayerState = DEMOPLAYER_CONNECTING; + + m_Commands.Clear(true); + m_LastCmd = nullptr; + + BaseSystemModule::FireSignal(1); + + if (m_World->IsHLTV() || m_ForceHLTV) + { + static unsigned char cmd[] = { + 1, // command length in bytes + DRC_CMD_START // tell them for start director + }; + + m_World->AddSignonData(svc_director, cmd, sizeof(cmd)); + } +} + +void DemoPlayer::ShutDown() +{ + if (m_State == MODULE_DISCONNECTED) { + return; + } + + if (m_World) { + m_World->ShutDown(); + } + + if (m_Server) { + m_Server->ShutDown(); + } + + m_DemoStream.Free(); + m_Commands.Clear(true); + + BaseSystemModule::ShutDown(); + m_System->DPrintf("DemoPlayer module Shutdown.\n"); +} + +char *DemoPlayer::FormatTime(float time) +{ + static char timeCode[16]; + _snprintf(timeCode, sizeof(timeCode), "%02u:%02u:%02u", (int)time / 60, (int)time % 60, (int)(time * 100) % 100); + return timeCode; +} + +void DemoPlayer::RemoveFrames(double starttime, double endtime) +{ + ; +} + +void DemoPlayer::ExecuteDirectorCmd(DirectorCmd *cmd) +{ + switch (cmd->GetType()) + { + case DRC_CMD_CAMERA: + { + float fov; + int entity; + vec3_t position, angles; + cmd->GetCameraData(position, angles, fov, entity); + cmd->SetCameraData(position, angles, fov, 0); + cmd->WriteToStream(&m_DemoStream); + cmd->SetCameraData(position, angles, fov, entity); + break; + } + case DRC_CMD_TIMESCALE: + float timescale; + cmd->GetTimeScaleData(timescale); + SetTimeScale(timescale); + break; + default: + cmd->WriteToStream(&m_DemoStream); + break; + } +} + +char *DemoPlayer::GetFileName() +{ + return m_DemoFileName; +} + +DirectorCmd *DemoPlayer::GetLastCommand() +{ + return m_LastCmd; +} + +bool DemoPlayer::LoadGame(char *filename) +{ + if (!m_Server->LoadDemo(m_World, filename, m_ForceHLTV, false)) { + return false; + } + + strcopy(m_DemoFileName, filename); + + m_World->SetBufferSize(-1); + m_Outgoing_sequence = 0; + m_LastClockUpdateTime = 0; + m_LastFrameTime = 0; + m_PlayerState = DEMOPLAYER_INITIALIZING; + m_LastFrameTime = 0; + m_MasterMode = true; + + return true; +} + +void DemoPlayer::WriteDatagram(BitBuffer *stream) +{ + frame_t *frame = m_World->GetFrameByTime(m_WorldTime); + if (!frame) { + return; + } + + if (m_LastFrameSeqNr && m_LastFrameSeqNr <= frame->seqnr) + { + if (m_LastFrameSeqNr >= frame->seqnr) { + return; + } + } + else + { + m_LastFrameSeqNr = frame->seqnr - 1; + if (frame->seqnr - 1 >= frame->seqnr) { + return; + } + } + + double time = m_PlayerTime - (m_WorldTime - frame->time); + stream->WriteByte(svc_time); + stream->WriteFloat(float(time)); + + m_World->WriteFrame(frame, m_LastFrameSeqNr, stream, stream, m_DeltaFrameSeqNr, m_Outgoing_sequence, true); + + if (m_MasterMode) { + WriteCommands(stream, float(m_LastFrameTime), float(m_WorldTime)); + } + + m_LastFrameTime = m_WorldTime; + if (stream->IsOverflowed()) + { + m_System->Printf("Demo data stream overflow.\n"); + stream->Clear(); + + m_DeltaFrameSeqNr = 0; + m_LastFrameSeqNr = 0; + } + else + { + m_Outgoing_sequence++; + m_DeltaFrameSeqNr = frame->seqnr; + m_LastFrameSeqNr = frame->seqnr; + } +} + +void DemoPlayer::ExecuteDemoFileCommands(BitBuffer *stream) +{ + unsigned int cmd; + while ((cmd = stream->ReadByte()) != -1) + { + switch (cmd) + { + case DEMO_STRINCMD: + { + char szCmdName[64]; + stream->ReadBuf(sizeof(szCmdName), szCmdName); + m_Engine->Cbuf_AddText(szCmdName); + m_Engine->Cbuf_AddText("\n"); + break; + } + case DEMO_CLIENTDATA: + { + client_data_t cdat; + stream->ReadBuf(sizeof(cdat), &cdat); + m_Engine->DemoUpdateClientData(&cdat); + break; + } + case DEMO_EVENT: + { + int flags = _LittleLong(stream->ReadLong()); + int idx = _LittleLong(stream->ReadLong()); + float delay = _LittleFloat(stream->ReadFloat()); + + event_args_t eargs; + stream->ReadBuf(sizeof(eargs), &eargs); + + m_Engine->CL_QueueEvent(flags, idx, delay, &eargs); + break; + } + case DEMO_WEAPONANIM: + { + int anim = _LittleLong(stream->ReadLong()); + int body = _LittleLong(stream->ReadLong()); + + m_Engine->HudWeaponAnim(anim, body); + break; + } + case DEMO_PLAYSOUND: + { + int channel = stream->ReadLong(); + int sampleSize = stream->ReadLong(); + + char sample[256]; + stream->ReadBuf(sampleSize, sample); + sample[sampleSize] = '\0'; + + float attenuation = _LittleFloat(stream->ReadFloat()); + float volume = _LittleFloat(stream->ReadFloat()); + int flags = _LittleLong(stream->ReadLong()); + int pitch = _LittleLong(stream->ReadLong()); + + m_Engine->CL_DemoPlaySound(channel, sample, attenuation, volume, flags, pitch); + break; + } + case DEMO_DATA: + { + unsigned char data[32768]; + memset(data, 0, sizeof(data)); + + int length = stream->ReadLong(); + stream->ReadBuf(length, data); + m_Engine->ClientDLL_ReadDemoBuffer(length, data); + break; + } + default: + m_System->Printf("WARNING! DemoPlayer::ExecuteDemoFileCommands: unexpected demo file command %i\n", cmd); + return; + } + } +} + +void DemoPlayer::RegisterListener(ISystemModule *module) +{ + BaseSystemModule::RegisterListener(module); +} + +void DemoPlayer::RemoveListener(ISystemModule *module) +{ + BaseSystemModule::RemoveListener(module); +} + +IBaseSystem *DemoPlayer::GetSystem() +{ + return BaseSystemModule::GetSystem(); +} + +int DemoPlayer::GetSerial() +{ + return BaseSystemModule::GetSerial(); +} + +char *DemoPlayer::GetName() +{ + return BaseSystemModule::GetName(); +} + +int DemoPlayer::GetState() +{ + return BaseSystemModule::GetState(); +} + +int DemoPlayer::GetVersion() +{ + return BaseSystemModule::GetVersion(); +} diff --git a/rehlds/HLTV/DemoPlayer/src/DemoPlayer.h b/rehlds/HLTV/DemoPlayer/src/DemoPlayer.h new file mode 100644 index 0000000..4f54f0e --- /dev/null +++ b/rehlds/HLTV/DemoPlayer/src/DemoPlayer.h @@ -0,0 +1,193 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include +#include +#include + +#include "vmodes.h" +#include "cdll_int.h" +#include "IDemoPlayer.h" +#include "IEngineWrapper.h" + +#include "BaseSystemModule.h" +#include "ObjectDictionary.h" + +class IProxy; +class DirectorCmd; +class IBaseSystem; +class IObjectContainer; + +class DemoPlayer: public IDemoPlayer, public BaseSystemModule, public IDirector { +public: + DemoPlayer(); + virtual ~DemoPlayer() {} + + bool Init(IBaseSystem *system, int serial, char *name); + void RunFrame(double time); + void ReceiveSignal(ISystemModule *module, unsigned int signal, void *data); + void ExecuteCommand(int commandID, char *commandLine); + void RegisterListener(ISystemModule *module); + void RemoveListener(ISystemModule *module); + IBaseSystem *GetSystem(); + int GetSerial(); + char *GetStatusLine(); + char *GetType(); + char *GetName(); + int GetState(); + int GetVersion(); + void ShutDown(); + + void NewGame(IWorld *world, IProxy *proxy = nullptr); + char *GetModName(); + void WriteCommands(BitBuffer *stream, float startTime, float endTime); + int AddCommand(DirectorCmd *cmd); + bool RemoveCommand(int index); + DirectorCmd *GetLastCommand(); + IObjectContainer *GetCommands(); + IDirector *GetDirector(); + void SetWorldTime(double time, bool relative); + void SetTimeScale(float scale); + void SetPaused(bool state); + void SetEditMode(bool state); + void SetMasterMode(bool state); + bool IsPaused(); + bool IsLoading(); + bool IsActive(); + bool IsEditMode(); + bool IsMasterMode(); + void RemoveFrames(double starttime, double endtime); + void ExecuteDirectorCmd(DirectorCmd *cmd); + double GetWorldTime(); + double GetStartTime(); + double GetEndTime(); + float GetTimeScale(); + IWorld *GetWorld(); + char *GetFileName(); + bool SaveGame(char *filename); + bool LoadGame(char *filename); + void Stop(); + void ForceHLTV(bool state); + void GetDemoViewInfo(ref_params_t *rp, float *view, int *viewmodel); + int ReadDemoMessage(unsigned char *buffer, int size); + void ReadNetchanState(int *incoming_sequence, int *incoming_acknowledged, int *incoming_reliable_acknowledged, int *incoming_reliable_sequence, int *outgoing_sequence, int *reliable_sequence, int *last_reliable_sequence); + void SetName(char *newName); + +private: + enum LocalCommandIDs { + CMD_ID_JUMP = 1, + CMD_ID_FORCEHLTV, + CMD_ID_PAUSE, + CMD_ID_SPEED, + CMD_ID_START, + CMD_ID_SAVE, + }; + + void CMD_Jump(char *cmdLine); + void CMD_ForceHLTV(char *cmdLine); + void CMD_Pause(char *cmdLine); + void CMD_Speed(char *cmdLine); + void CMD_Start(char *cmdLine); + void CMD_Save(char *cmdLine); + + struct LocalCommandID_s { + char *name; + LocalCommandIDs id; + void (DemoPlayer::*pfnCmd)(char *cmdLine); + }; + static LocalCommandID_s m_LocalCmdReg[]; + +protected: + double GetPlayerTime(); + char *FormatTime(float time); + + void RunClocks(); + void WriteDatagram(BitBuffer *stream); + void WriteSpawn(BitBuffer *stream); + void ReindexCommands(); + void WriteCameraPath(DirectorCmd *, BitBuffer *stream); + + enum DemoCmds { + DEMO_STRINCMD = 3, + DEMO_CLIENTDATA = 4, + DEMO_EVENT = 6, + DEMO_WEAPONANIM = 7, + DEMO_PLAYSOUND = 8, + DEMO_DATA = 9 + }; + + void ExecuteDemoFileCommands(BitBuffer *stream); + +private: + IEngineWrapper *m_Engine; + IWorld *m_World; + IServer *m_Server; + ObjectDictionary m_Commands; + DirectorCmd *m_LastCmd; + unsigned int m_CommandCounter; + char m_DemoFileName[MAX_PATH]; + + enum PlayerState { + DEMOPLAYER_UNDEFINED, + DEMOPLAYER_INITIALIZING, + DEMOPLAYER_CONNECTING, + DEMOPLAYER_CONNECTED, + DEMOPLAYER_RUNNING + }; + + int m_PlayerState; + BitBuffer m_DemoStream; + bool m_EditorMode; + bool m_MasterMode; + bool m_ForceHLTV; + bool m_IsSaving; + float m_TimeScale; + double m_WorldTime; + double m_PlayerTime; + double m_StartTime; + + double m_LastFrameTime; + bool m_IsPaused; + double m_LastClockUpdateTime; + + unsigned int m_LastFrameSeqNr; + unsigned int m_DeltaFrameSeqNr; + unsigned int m_Outgoing_sequence; +}; + +#ifndef HOOK_HLTV +// Use this to expose a singleton interface. This creates the global variable for you automatically. +#define EXPOSE_SINGLE_INTERFACE2(className, interfaceName, versionName) \ + static className __g_##className##_singleton;\ + static IBaseInterface *__Create##className##interfaceName##_interface() {return (IBaseInterface *)((interfaceName *)&__g_##className##_singleton);}\ + static InterfaceReg __g_Create##className##interfaceName##_reg(__Create##className##interfaceName##_interface, versionName); + +EXPOSE_SINGLE_INTERFACE2(DemoPlayer, IDemoPlayer, DEMOPLAYER_INTERFACE_VERSION); +#endif // HOOK_HLTV diff --git a/rehlds/HLTV/DemoPlayer/src/precompiled.cpp b/rehlds/HLTV/DemoPlayer/src/precompiled.cpp new file mode 100644 index 0000000..5f656a4 --- /dev/null +++ b/rehlds/HLTV/DemoPlayer/src/precompiled.cpp @@ -0,0 +1 @@ +#include "precompiled.h" diff --git a/rehlds/HLTV/DemoPlayer/src/precompiled.h b/rehlds/HLTV/DemoPlayer/src/precompiled.h new file mode 100644 index 0000000..7f1d66c --- /dev/null +++ b/rehlds/HLTV/DemoPlayer/src/precompiled.h @@ -0,0 +1,23 @@ +#pragma once + +#include "archtypes.h" +#include "maintypes.h" + +#include "TokenLine.h" +#include "interface.h" + +#include "mem.h" +#include "common.h" + +#include "common/common_hltv.h" +#include "common/net_internal.h" +#include "common/mathlib_internal.h" +#include "common/byteorder.h" +#include "common/BitBuffer.h" +#include "common/DirectorCmd.h" +#include "common/DemoFile.h" + +#include "DemoPlayer.h" + +// Hooks stuff +#include "hookers/HLTV/DemoPlayer/hooklist.h" diff --git a/rehlds/HLTV/DemoPlayer/src/public_amalgamation.cpp b/rehlds/HLTV/DemoPlayer/src/public_amalgamation.cpp new file mode 100644 index 0000000..1fc308c --- /dev/null +++ b/rehlds/HLTV/DemoPlayer/src/public_amalgamation.cpp @@ -0,0 +1,3 @@ +#include "precompiled.h" + +#include "interface.cpp" diff --git a/rehlds/HLTV/Director/build.gradle b/rehlds/HLTV/Director/build.gradle new file mode 100644 index 0000000..23790c3 --- /dev/null +++ b/rehlds/HLTV/Director/build.gradle @@ -0,0 +1,139 @@ +import org.doomedsociety.gradlecpp.cfg.ToolchainConfigUtils +import org.doomedsociety.gradlecpp.msvc.MsvcToolchainConfig +import org.doomedsociety.gradlecpp.toolchain.icc.Icc +import org.doomedsociety.gradlecpp.toolchain.icc.IccCompilerPlugin +import org.doomedsociety.gradlecpp.gcc.GccToolchainConfig +import org.gradle.nativeplatform.NativeBinarySpec +import org.gradle.nativeplatform.NativeLibrarySpec +import org.gradle.nativeplatform.toolchain.VisualCpp + +apply plugin: 'cpp' +apply plugin: IccCompilerPlugin +apply plugin: GccCompilerPlugin + +void setupToolchain(NativeBinarySpec b) { + def cfg = rootProject.createToolchainConfig(b); + cfg.projectInclude(project, '/..', '/../..', '/src', '/../../common', '/../../engine', '/../../public', '/../../public/rehlds', '/../../pm_shared'); + cfg.singleDefines 'USE_BREAKPAD_HANDLER', 'HLTV', 'DIRECTOR_MODULE' + + if (cfg instanceof MsvcToolchainConfig) { + cfg.compilerOptions.pchConfig = new MsvcToolchainConfig.PrecompiledHeadersConfig( + enabled: true, + pchHeader: 'precompiled.h', + pchSourceSet: 'director_pch' + ); + + cfg.singleDefines('_CRT_SECURE_NO_WARNINGS') + cfg.compilerOptions.args '/Ob2', '/Oi', '/GF' + } + else if (cfg instanceof GccToolchainConfig) { + cfg.compilerOptions.pchConfig = new GccToolchainConfig.PrecompilerHeaderOptions( + enabled: true, + pchSourceSet: 'director_pch' + ); + + cfg.compilerOptions.languageStandard = 'c++0x' + cfg.defines([ + '_strdup': 'strdup', + '_stricmp': 'strcasecmp', + '_strnicmp': 'strncasecmp', + '_vsnprintf': 'vsnprintf', + '_snprintf': 'snprintf', + ]); + + cfg.compilerOptions.args '-Qoption,cpp,--treat_func_as_string_literal_cpp', '-fno-exceptions' + } + + ToolchainConfigUtils.apply(project, cfg, b); +} + +model { + buildTypes { + release + } + + platforms { + x86 { + architecture "x86" + } + } + + toolChains { + visualCpp(VisualCpp) { + } + icc(Icc) { + } + } + + components { + director(NativeLibrarySpec) { + targetPlatform 'x86' + baseName 'director' + + sources { + director_main(CppSourceSet) { + source { + srcDir "src" + include "**/*.cpp" + exclude "precompiled.cpp" + } + } + + director_common(CppSourceSet) { + source { + srcDirs "../../common", "../common" + + // common + include "BaseSystemModule.cpp" + include "ObjectDictionary.cpp" + include "ObjectList.cpp" + include "TokenLine.cpp" + + // HLTV common + include "BitBuffer.cpp" + include "byteorder.cpp" + include "common.cpp" + include "DirectorCmd.cpp" + include "mathlib.cpp" + include "random.cpp" + } + } + + director_engine(CppSourceSet) { + source { + srcDir "../../engine" + include "mem.cpp" + } + } + + director_pch(CppSourceSet) { + source { + srcDir "src" + include "precompiled.cpp" + } + } + } + + binaries.all { + NativeBinarySpec b -> project.setupToolchain(b) + } + } + } +} + +task buildFixes { + dependsOn binaries.withType(SharedLibraryBinarySpec).matching { SharedLibraryBinarySpec blib -> + blib.buildable && blib.buildType.name == 'release' + } +} + +task buildRelease { + dependsOn binaries.withType(SharedLibraryBinarySpec).matching { SharedLibraryBinarySpec blib -> + blib.buildable && blib.buildType.name == 'release' + } +} + +// prevent static lib building +binaries.withType(StaticLibraryBinarySpec) { binary -> + buildable = false +} diff --git a/rehlds/HLTV/Director/msvc/Director.sln b/rehlds/HLTV/Director/msvc/Director.sln new file mode 100644 index 0000000..3cc33f7 --- /dev/null +++ b/rehlds/HLTV/Director/msvc/Director.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Director", "Director.vcxproj", "{04D0594C-57F5-4277-AF1B-EAE90AED0C3C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {04D0594C-57F5-4277-AF1B-EAE90AED0C3C}.Debug|Win32.ActiveCfg = Debug|Win32 + {04D0594C-57F5-4277-AF1B-EAE90AED0C3C}.Debug|Win32.Build.0 = Debug|Win32 + {04D0594C-57F5-4277-AF1B-EAE90AED0C3C}.Release|Win32.ActiveCfg = Release|Win32 + {04D0594C-57F5-4277-AF1B-EAE90AED0C3C}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/rehlds/HLTV/Director/msvc/Director.vcxproj b/rehlds/HLTV/Director/msvc/Director.vcxproj new file mode 100644 index 0000000..965c934 --- /dev/null +++ b/rehlds/HLTV/Director/msvc/Director.vcxproj @@ -0,0 +1,157 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {04D0594C-57F5-4277-AF1B-EAE90AED0C3C} + Win32Proj + Director + 8.1 + + + + DynamicLibrary + true + v120_xp + v140_xp + MultiByte + + + DynamicLibrary + false + v120_xp + v140_xp + true + MultiByte + + + + + + + + + + + + + + + true + + + false + + + + Use + Level3 + Disabled + HLTV;WIN32;_DEBUG;_WINDOWS;_USRDLL;DIRECTOR_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + $(ProjectDir)\..\..\;$(ProjectDir)\..\src;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;%(AdditionalIncludeDirectories) + precompiled.h + MultiThreadedDebug + true + + + Windows + true + %(AdditionalDependencies) + + + IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)") + + + Automatic deployment script + + + echo Empty Action + + + Force build to run Pre-Build event + git.always.run + git.always.run + + + + + Level3 + Use + MaxSpeed + true + true + HLTV;WIN32;NDEBUG;_WINDOWS;_USRDLL;DIRECTOR_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + $(ProjectDir)\..\..\;$(ProjectDir)\..\src;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;%(AdditionalIncludeDirectories) + precompiled.h + MultiThreaded + true + + + Windows + true + true + true + %(AdditionalDependencies) + + + echo Empty Action + + + Force build to run Pre-Build event + git.always.run + git.always.run + + + IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)") + + + Automatic deployment script + + + + + + + + + + + + + + + + + Create + Create + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/rehlds/HLTV/Director/msvc/Director.vcxproj.filters b/rehlds/HLTV/Director/msvc/Director.vcxproj.filters new file mode 100644 index 0000000..53672cc --- /dev/null +++ b/rehlds/HLTV/Director/msvc/Director.vcxproj.filters @@ -0,0 +1,108 @@ + + + + + {e86d9738-6b9c-4a2d-ba74-454526be3b1c} + + + {b2fdbdd6-d164-4470-97e9-b454d0dc7b0f} + + + {722c9b02-95dc-410f-a5b4-955878646049} + + + {33779299-5f45-402f-a52f-888505614cbe} + + + {188c9b98-4976-4512-9313-8c3ffe3d0249} + + + + + src + + + src + + + src + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + common + + + common + + + common + + + common + + + engine + + + + + src + + + src + + + src + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + common + + + common + + + common + + + common + + + engine + + + \ No newline at end of file diff --git a/rehlds/HLTV/Director/msvc/PostBuild.bat b/rehlds/HLTV/Director/msvc/PostBuild.bat new file mode 100644 index 0000000..8583878 --- /dev/null +++ b/rehlds/HLTV/Director/msvc/PostBuild.bat @@ -0,0 +1,39 @@ +@echo OFF +:: +:: Post-build auto-deploy script +:: Create and fill PublishPath.txt file with path to deployment folder +:: I.e. PublishPath.txt should contain one line with a folder path +:: Call it so: +:: IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)") +:: + +SET targetDir=%~1 +SET targetName=%~2 +SET targetExt=%~3 +SET projectDir=%~4 +SET destination= + +IF NOT EXIST "%projectDir%\PublishPath.txt" ( + ECHO No deployment path specified. Create PublishPath.txt near PostBuild.bat with paths on separate lines for auto deployment. + exit /B 0 +) + +FOR /f "tokens=* delims= usebackq" %%a IN ("%projectDir%\PublishPath.txt") DO ( + ECHO Deploying to: %%a + IF NOT "%%a" == "" ( + copy /Y "%targetDir%%targetName%%targetExt%" "%%a" + IF NOT ERRORLEVEL 1 ( + IF EXIST "%targetDir%%targetName%.pdb" ( + copy /Y "%targetDir%%targetName%.pdb" "%%a" + ) + ) ELSE ( + ECHO PostBuild.bat ^(27^) : warning : Can't copy '%targetName%%targetExt%' to deploy path '%%a' + ) + ) +) + +IF "%%a" == "" ( + ECHO No deployment path specified. +) + +exit /B 0 \ No newline at end of file diff --git a/rehlds/HLTV/Director/src/Director.cpp b/rehlds/HLTV/Director/src/Director.cpp new file mode 100644 index 0000000..e44fa5c --- /dev/null +++ b/rehlds/HLTV/Director/src/Director.cpp @@ -0,0 +1,771 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +#ifdef DIRECTOR_MODULE +EXPOSE_SINGLE_INTERFACE(Director, IDirector, DIRECTOR_INTERFACE_VERSION); +#endif // DIRECTOR_MODULE + +bool Director::Init(IBaseSystem *system, int serial, char *name) +{ + BaseSystemModule::Init(system, serial, name); + m_System->RegisterCommand("slowmotion", this, CMD_ID_SLOWMOTION); + + m_historyLength = MAX_WORLD_HISTORY; + m_history = (worldHistory_t *)malloc(sizeof(worldHistory_t) * m_historyLength); + if (!m_history) { + m_System->Printf("ERROR!Director::Init: not enough memory for world history.\n"); + return false; + } + + m_LastExecTime = 0; + m_currentTime = 0; + m_slowMotion = 0.5f; + + for (int i = 0; i < MAX_WORLD_HISTORY; i++) { + m_gaussFilter[i] = 1.0f / exp((i * i) / 10000.0f); + } + + m_World = nullptr; + m_Active = false; + m_System->ExecuteFile("director.cfg"); + m_System->Printf("Director module initialized.\n"); + return true; +} + +float WeightedAngle(vec_t *vec1, vec_t *vec2) +{ + float a = AngleBetweenVectors(vec1, vec2); + if (a < 11.25f) { + return 1; + } + else if (a < 22.5f) { + return 0.5f; + } + else if (a < 45.0f) { + return 0.25f; + } + else if (a < 90.0f) { + return 0.125f; + } + + return 0.05f; +} + +void Director::WriteProxyStatus(BitBuffer *stream) +{ + ; +} + +void Director::ShutDown() +{ + if (m_State == MODULE_DISCONNECTED) { + return; + } + + if (m_history) { + free(m_history); + } + + if (m_World) { + m_World->RemoveListener(this); + } + + ClearDirectorCommands(); + BaseSystemModule::ShutDown(); + + m_System->Printf("Director module shutdown.\n"); +} + +void Director::RunFrame(double time) +{ + BaseSystemModule::RunFrame(time); + + if (!m_World || !m_World->IsActive()) { + return; + } + + if (m_nextCutTime && m_Active) + { + ExecuteDirectorCommands(); + if ((m_currentTime - m_nextCutTime) > 10) + { + float duration = AddBestMODCut(); + if (!duration) { + duration = AddBestGenericCut(); + } + + m_nextCutTime += duration; + + while (m_nextCutTime > m_history[m_nextCutSeqnr % m_historyLength].time) { + m_nextCutSeqnr++; + } + } + } +} + +void Director::ReceiveSignal(ISystemModule *module, unsigned int signal, void *data) +{ + int from = module->GetSerial(); + if (m_World->GetSerial() != from) { + return; + } + + if (signal != 3) { + return; + } + + if (!m_World) { + m_System->Printf("Director::ReceiveSignal: world == NULL\n"); + return; + } + + frame_t frame; + m_World->GetUncompressedFrame(m_World->GetLastFrame()->seqnr, &frame); + AnalyseFrame(&frame); +} + +char *Director::GetModName() +{ + return "valve"; +} + +void Director::NewGame(IWorld *world, IProxy *proxy) +{ + m_World = world; + m_Proxy = proxy; + + world->RegisterListener(this); + m_WorldModel = m_World->GetWorldModel(); + + memset(m_history, 0, sizeof(*m_history) * m_historyLength); + memset(&m_frameEvent, 0, sizeof(m_frameEvent)); + + ClearDirectorCommands(); + + m_lastCut = nullptr; + m_LastExecTime = 0; + m_nextCutTime = 0; + m_currentTime = 0; + m_nextCutSeqnr = 0; + m_maxRank = 0; + + WriteSignonData(); + m_Active = true; +} + +void Director::AnalyseFrame(frame_t *frame) +{ + unsigned int i; + unsigned int num; + float ranks[MAX_CLIENTS]; + unsigned int maxclients = m_World->GetMaxClients(); + unsigned int seqnr = frame->seqnr; + worldHistory_t *now = &m_history[seqnr % m_historyLength]; + + for (i = 0; i < MAX_CLIENTS; i++) { + ranks[i] = now->players[i].rank; + } + + memset(now, 0, sizeof(*now)); + for (i = 0; i < MAX_CLIENTS; i++) { + now->players[i].rank = ranks[i]; + } + + if (frame->delta || !m_Active) { + m_System->DPrintf("Director::AnalyseFrame: frame must be uncompressed.\n"); + return; + } + + if (m_currentTime) + { + m_nextCutTime = frame->time; + m_nextCutSeqnr = seqnr; + } + + m_currentSeqnr = seqnr; + now->seqNr = seqnr; + + m_currentTime = frame->time; + now->time = frame->time; + + unsigned int count = min(maxclients, frame->entitynum); + for (num = 0; num < count; num++) + { + entity_state_t *ent = &((entity_state_t *)frame->entities)[num]; + unsigned int index = ent->number - 1; + if (index >= maxclients) { + continue; + } + + playerData_t *player = &now->players[i]; + player->active = (ent->solid != SOLID_NOT); + if (player->active) + { + player->origin[0] = ent->origin[0]; + player->origin[1] = ent->origin[1]; + player->origin[2] = ent->origin[2]; + + vec3_t angles; + AngleVectors(ent->angles, angles); + VectorCopy(angles, player->angles); + } + } + + for (i = 0; i < MAX_CLIENTS; i++) { + AnalysePlayer(i); + } + + now->event.entity1 = m_frameEvent.entity1; + now->event.entity2 = m_frameEvent.entity2; + now->event.flags = m_frameEvent.flags; + + memset(&m_frameEvent, 0, sizeof(m_frameEvent)); +} + +void Director::SmoothRank(int playerNum, float rank) +{ + const unsigned int smoothRange = 40; + m_history[(m_currentSeqnr + smoothRange) % m_historyLength].players[playerNum].rank = 0; + + unsigned int steps; + for (unsigned int i = 0; i < smoothRange; i++) + { + float addition = rank * m_gaussFilter[i * (MAX_WORLD_HISTORY / smoothRange)]; + + steps = (m_currentSeqnr - i) % m_historyLength; + m_history[steps].players[playerNum].rank += addition; + + steps = (i + m_currentSeqnr) % m_historyLength; + m_history[steps].players[playerNum].rank += addition; + } +} + +void Director::AnalysePlayer(int playerNum) +{ + int secondTarget = 0; + float rank = RandomFloat(0.0f, 0.01f) + 1; + float targetRank, bestTargetRank = 0; + + worldHistory_t *now = &m_history[m_currentSeqnr % m_historyLength]; + playerData_t *player = &now->players[playerNum]; + + if (player->active) + { + if (m_WorldModel->IsValid()) + { + m_WorldModel->SetPVS(player->origin); + for (int i = 0; i < MAX_CLIENTS; i++) + { + playerData_t *target = &now->players[i]; + if (!target->active || !m_WorldModel->InPVS(target->origin)) { + continue; + } + + vec3_t offset; + VectorSubtract(target->origin, player->origin, offset); + + float distance = Length(offset); + if (distance >= 1.0f) + { + targetRank = WeightedAngle(target->angles, offset) + 1 / distance; + + // inverted + VectorScale(offset, -1.0f, offset); + + targetRank = WeightedAngle(target->angles, offset) * targetRank; + rank += targetRank; + + // remember closest player + if (targetRank > bestTargetRank) + { + bestTargetRank = targetRank; + secondTarget = i + 1; + } + } + } + } + } + else + { + rank = 0; + } + + player->target = secondTarget; + player->rank = player->rank + rank; + SmoothRank(playerNum, rank); +} + +void Director::WriteCommands(BitBuffer *stream, float startTime, float endTime) +{ + DirectorCmd *cmd = (DirectorCmd *)m_Commands.FindClosestKey(startTime); + while (cmd && cmd->GetTime() <= endTime) + { + if (cmd->GetTime() > startTime) + { + if (cmd->GetType() == DRC_CMD_TIMESCALE) + { + float timescale; + cmd->GetTimeScaleData(timescale); + + stream->WriteByte(svc_timescale); + stream->WriteFloat(timescale); + } + + cmd->WriteToStream(stream); + } + + cmd = (DirectorCmd *)m_Commands.GetNext(); + } +} + +void Director::WriteSignonData() +{ + static unsigned char cmd[] = { + 1, // command length in bytes + DRC_CMD_START // tell them for start director + }; + + m_World->AddSignonData(svc_director, cmd, sizeof(cmd)); +} + +void Director::AddEvent(int entity1, int entity2, unsigned int flags) +{ + if ((flags & DRC_FLAG_PRIO_MASK) > (m_frameEvent.flags & DRC_FLAG_PRIO_MASK)) + { + m_frameEvent.flags = flags; + m_frameEvent.entity1 = entity1; + m_frameEvent.entity2 = entity2; + } +} + +float Director::AddBestGenericCut() +{ + int seqNrMod = m_nextCutSeqnr % m_historyLength; + + float sumTarget2Rank[MAX_CLIENTS]; + float bestTarget2Rank, bestRank = 0; + float targetRankSum = 0; + int newTarget, newTarget2; + int bestTarget2; + + for (int i = 0; i < MAX_CLIENTS; i++) + { + memset(sumTarget2Rank, 0, sizeof(sumTarget2Rank)); + + float tillTime = m_nextCutTime + 4; + while (tillTime > m_history[seqNrMod].time) + { + playerData_t *player = &m_history[seqNrMod].players[i]; + + targetRankSum += player->rank; + if (player->target) { + sumTarget2Rank[player->target - 1] += player->rank; + } + + if (++seqNrMod == m_historyLength) { + seqNrMod = 0; + } + } + + bestTarget2 = 0; + bestTarget2Rank = 0; + + for (int j = 0; j < MAX_CLIENTS; j++) + { + if (sumTarget2Rank[j] > bestTarget2Rank) + { + bestTarget2 = j + 1; + bestTarget2Rank = sumTarget2Rank[j]; + } + } + + if (targetRankSum > bestRank) + { + bestRank = targetRankSum; + newTarget = i + 1; + newTarget2 = bestTarget2; + } + } + + if (bestRank > m_maxRank) { + m_maxRank = bestRank; + } + + float duration = 1.0f; + if (newTarget > 0) + { + if (m_lastCut && m_lastCut->GetType() == TYPE_DIRECTOR) + { + int lastTarget1, lastTarget2,flags; + m_lastCut->GetEventData(lastTarget1, lastTarget2, flags); + + if (newTarget == lastTarget1 && newTarget2 == lastTarget2) { + return 1.0f; + } + + if (newTarget == lastTarget1) { + duration = 2.0f; + } + } + + DirectorCmd *cmd = new DirectorCmd; + cmd->SetEventData(newTarget, newTarget2, 0); + cmd->SetTime(m_nextCutTime); + + RandomizeCommand(cmd); + m_Commands.Add(cmd, m_nextCutTime); + m_lastCut = cmd; + } + + return duration; +} + +float Director::AddBestMODCut() +{ + worldHistory_t *timepoint = FindBestEvent(); + if (!timepoint) { + return 0; + } + + DirectorCmd *cmd = new DirectorCmd; + cmd->SetEventData(timepoint->event.entity1, timepoint->event.entity2, timepoint->event.flags); + cmd->SetTime(m_nextCutTime); + + RandomizeCommand(cmd); + m_Commands.Add(cmd, cmd->GetTime()); + m_lastCut = cmd; + + int lastTarget1, lastTarget2, lastFlags; + cmd->GetEventData(lastTarget1, lastTarget2, lastFlags); + + float trailTime = 2.0f; + if ((lastFlags & DRC_FLAG_SLOWMOTION) && m_slowMotion) + { + trailTime = 1.5f; + + cmd = new DirectorCmd; + cmd->SetTimeScaleData(m_slowMotion); + cmd->SetTime(timepoint->time - 0.25f); + m_Commands.Add(cmd, cmd->GetTime()); + + cmd = new DirectorCmd; + cmd->SetTimeScaleData(1.0); + cmd->SetTime(timepoint->time + 1.5f); + m_Commands.Add(cmd, cmd->GetTime()); + } + + return trailTime + timepoint->time - m_nextCutTime; +} + +int Director::GetClosestPlayer(frame_t *frame, int entityIndex) +{ + unsigned int i; + unsigned int bestPlayer = 0; + unsigned int maxclients = m_World->GetMaxClients(); + + if (!m_WorldModel->IsValid()) { + return 0; + } + + vec3_t origin; + for (i = 0; i < frame->entitynum; i++) + { + entity_state_t *ent = &((entity_state_t *)frame->entities)[i]; + if (ent->number == entityIndex) { + VectorCopy(origin, ent->origin); + break; + } + } + + // entityIndex not found + if (i == frame->entitynum) { + return 0; + } + + m_WorldModel->SetPVS(origin); + + float minDistance = 32000.0f; + unsigned int count = min(maxclients, frame->entitynum); + + for (i = 0; i < count; i++) + { + entity_state_t *ent = &((entity_state_t *)frame->entities)[i]; + if ((unsigned)(ent->number - 1) < maxclients && m_WorldModel->InPVS(ent->origin)) + { + vec3_t offset; + VectorSubtract(ent->origin, origin, offset); + + float distance = Length(offset); + if (distance >= 1 && distance < minDistance) + { + minDistance = distance; + bestPlayer = ent->number; + } + } + } + + return bestPlayer; +} + +void Director::ClearDirectorCommands() +{ + DirectorCmd *cmd = (DirectorCmd *)m_Commands.GetFirst(); + while (cmd) + { + delete cmd; + cmd = (DirectorCmd *)m_Commands.GetNext(); + } + + m_Commands.Clear(); +} + +void Director::RandomizeCommand(DirectorCmd *cmd) +{ + if ((cmd->GetType() & DRC_FLAG_NO_RANDOM) || cmd->GetType() != DRC_CMD_EVENT) { + return; + } + + int target1, target2, flags; + cmd->GetEventData(target1, target2, flags); + if ((flags & DRC_FLAG_SLOWMOTION) && RandomFloat(0, 1) > 0.25f) + { + flags &= ~DRC_FLAG_SLOWMOTION; + } + // check DRC_CMD_SOUND, DRC_CMD_STATUS, DRC_CMD_BANNER + else if ((flags & DRC_FLAG_PRIO_MASK) > DRC_CMD_MESSAGE + && (flags & DRC_FLAG_PRIO_MASK) < DRC_CMD_STUFFTEXT && RandomFloat(0, 1) < 0.15f) + { + flags |= DRC_FLAG_SLOWMOTION; + } + + // toggle flag DRC_FLAG_DRAMATIC + if (RandomFloat(0, 1) < 0.33f) { + flags ^= DRC_FLAG_DRAMATIC; + } + + if (target1 && target2 && !(flags & DRC_FLAG_SLOWMOTION) && RandomFloat(0, 1) < 0.33f) + { + int swap = target1; + target1 = target2; + target2 = swap; + } + + worldHistory_t *now = &m_history[m_nextCutSeqnr % m_historyLength]; + + vec3_t v1, v2; + VectorSubtract(now->players[target2 % MAX_CLIENTS].origin, now->players[target1 % MAX_CLIENTS].origin, v1); + + v1[2] = 0; + VectorAngles(v1, v1); + + VectorCopy(now->players[target1 % MAX_CLIENTS].angles, v2); + if (AngleLeftOfOther(v1, v2)) + flags &= ~DRC_FLAG_SIDE; + else + flags |= DRC_FLAG_SIDE; + + if (target1 == target2) { + target2 = 0; + } + + cmd->SetEventData(target1, target2, flags); +} + +void Director::ExecuteCommand(int commandID, char *commandLine) +{ + if (commandID == CMD_ID_SLOWMOTION) { + CMD_SlowMotion(commandLine); + return; + } + + m_System->Printf("ERROR! Director::ExecuteCommand: unknown command ID %i.\n", commandID); +} + +char *Director::GetStatusLine() +{ + return "Default Director"; +} + +char *Director::GetType() +{ + return DIRECTOR_INTERFACE_VERSION; +} + +void Director::ExecuteDirectorCommands() +{ + unsigned char bufferdata[4096]; + BitBuffer buffer(bufferdata, sizeof(bufferdata)); + buffer.Clear(); + + float newTime = (float)m_Proxy->GetSpectatorTime(); + DirectorCmd *cmd = (DirectorCmd *)m_Commands.FindClosestKey(m_LastExecTime); + if (!cmd) { + return; + } + + while (cmd && cmd->GetTime() <= newTime) + { + if (cmd->GetTime() > m_LastExecTime) + { + if (cmd->GetType() == DRC_CMD_TIMESCALE) + { + float timescale; + cmd->GetTimeScaleData(timescale); + m_Proxy->SetClientTimeScale(timescale); + + if (timescale < 1.0) + { + vec3_t pos = { 0.02f, 0.85f, 0.f }; + + DirectorCmd slowmo; + slowmo.SetMessageData(0, COM_PackRGBA(255, 160, 0, 255), pos, 0.3f, 0.1f, 2, 0, "Slow Motion"); + slowmo.WriteToStream(&buffer); + } + } + else + { + cmd->WriteToStream(&buffer); + } + } + + cmd = (DirectorCmd *)m_Commands.GetNext(); + } + + if (buffer.IsOverflowed()) + { + m_System->Printf("Director::ExecuteDirectorCommands: command overflow.\n"); + } + else + { + m_Proxy->Broadcast(buffer.GetData(), buffer.CurrentSize(), GROUP_CLIENT_ALL, true); + } + + m_LastExecTime = newTime; +} + +void Director::CMD_SlowMotion(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) { + m_System->Printf("Syntax: slowmotion \n"); + m_System->Printf("Currently slowmotion factor is %.2f\n", m_slowMotion); + return; + } + + float val = (float)atof(params.GetToken(1)); + m_slowMotion = clamp(val, 0.05f, 4.0f); +} + +Director::worldHistory_t *Director::FindBestEvent() +{ + int i; + int nseqMod = m_nextCutSeqnr % m_historyLength; + int bestEvent[] = { 0, 0, 0, 0 }; + int bestEventPrio[] = { 0, 0, 0, 0 }; + + for (i = 0; i < 4; i++) + { + bestEventPrio[i] = 0; + bestEvent[i] = 0; + + float tillTime = (i + 1) * 2 + m_nextCutTime; + while (tillTime > m_history[nseqMod].time) + { + if ((m_history[nseqMod].event.flags & DRC_FLAG_PRIO_MASK) > (unsigned int)bestEventPrio[i]) { + bestEventPrio[i] = (m_history[nseqMod].event.flags & DRC_FLAG_PRIO_MASK); + bestEvent[i] = nseqMod; + } + + if (++nseqMod == m_historyLength) { + nseqMod = 0; + } + } + } + + if (bestEventPrio[0] || bestEventPrio[1] || bestEventPrio[2]) + { + if (bestEventPrio[1] >= bestEventPrio[0] + && bestEventPrio[1] >= bestEventPrio[2] + && bestEventPrio[1] >= bestEventPrio[3]) { + return &m_history[ bestEvent[1] ]; + } + + else if (bestEventPrio[0] > bestEventPrio[1] + && bestEventPrio[0] > bestEventPrio[2]) { + return &m_history[ bestEvent[0] ]; + } + + else if (bestEventPrio[2] > bestEventPrio[3]) { + return &m_history[ bestEvent[2] ]; + } + + if (bestEventPrio[0]) { + return &m_history[ bestEvent[0] ]; + } + } + + return nullptr; +} + +int Director::AddCommand(DirectorCmd *cmd) +{ + if (cmd->GetType() > DRC_CMD_LAST) { + return 0; + } + + if (cmd->GetType() == DRC_CMD_EVENT) + { + int target1, target2, flags; + cmd->GetEventData(target1, target2, flags); + AddEvent(target1, target2, flags); + return 0; + } + + DirectorCmd *newcmd = new DirectorCmd; + newcmd->Copy(cmd); + + m_Commands.Add(newcmd, newcmd->GetTime()); + return 1; +} + +IObjectContainer *Director::GetCommands() +{ + return &m_Commands; +} + +bool Director::RemoveCommand(int index) +{ + return false; +} + +DirectorCmd *Director::GetLastCommand() +{ + return nullptr; +} diff --git a/rehlds/HLTV/Director/src/Director.h b/rehlds/HLTV/Director/src/Director.h new file mode 100644 index 0000000..63def9e --- /dev/null +++ b/rehlds/HLTV/Director/src/Director.h @@ -0,0 +1,126 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include +#include +#include + +#include "BaseSystemModule.h" +#include "ObjectDictionary.h" + +class Director: public IDirector, public BaseSystemModule { +public: + Director() {} + virtual ~Director() {} + + virtual bool Init(IBaseSystem *system, int serial, char *name); + virtual void RunFrame(double time); + virtual void ReceiveSignal(ISystemModule *module, unsigned int signal, void *data); + virtual void ExecuteCommand(int commandID, char *commandLine); + virtual char *GetStatusLine(); + virtual char *GetType(); + virtual void ShutDown(); + + virtual void NewGame(IWorld *world, IProxy *proxy); + virtual int AddCommand(DirectorCmd *cmd); + virtual void WriteCommands(BitBuffer *stream, float startTime, float endTime); + virtual char *GetModName(); + virtual bool RemoveCommand(int index); + virtual DirectorCmd *GetLastCommand(); + virtual IObjectContainer *GetCommands(); + + typedef struct playerData_s { + vec3_t origin; + vec3_t angles; + int active; + int target; + float rank; + } playerData_t; + + typedef struct worldHistory_s + { + float time; + unsigned int seqNr; + + typedef struct worldEvent_s { + int entity1; + int entity2; + unsigned int flags; + } worldEvent_t; + + worldEvent_t event; + playerData_t players[MAX_CLIENTS]; + + } worldHistory_t; + + worldHistory_t *FindBestEvent(); + + void ExecuteDirectorCommands(); + void RandomizeCommand(DirectorCmd *cmd); + int GetClosestPlayer(frame_t *frame, int entityIndex); + void AddEvent(int entity1, int entity2, unsigned int flags); + void SmoothRank(int playerNum, float rank); + void AnalysePlayer(int playerNum); + void AnalyseFrame(frame_t *frame); + void ClearDirectorCommands(); + float AddBestMODCut(); + float AddBestGenericCut(); + void WriteSignonData(); + void WriteProxyStatus(BitBuffer *stream); + +private: + enum LocalCommandIDs { CMD_ID_SLOWMOTION = 1 }; + void CMD_SlowMotion(char *cmdLine); + +protected: + ObjectDictionary m_Commands; + + worldHistory_t *m_history; + float m_maxRank; + + enum { MAX_WORLD_HISTORY = 200 }; + float m_gaussFilter[MAX_WORLD_HISTORY]; + + int m_historyLength; + unsigned int m_nextCutSeqnr; + unsigned int m_currentSeqnr; + + float m_nextCutTime; + float m_currentTime; + float m_LastExecTime; + float m_slowMotion; + + worldHistory_t::worldEvent_t m_frameEvent; + DirectorCmd *m_lastCut; + IBSPModel *m_WorldModel; + bool m_Active; + IWorld *m_World; + IProxy *m_Proxy; +}; diff --git a/rehlds/HLTV/Director/src/DirectorNull.h b/rehlds/HLTV/Director/src/DirectorNull.h new file mode 100644 index 0000000..9412494 --- /dev/null +++ b/rehlds/HLTV/Director/src/DirectorNull.h @@ -0,0 +1,57 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include +#include +#include "BaseSystemModule.h" + +class IObjectContainer; +class DirectorNull: public IDirector, public BaseSystemModule { +public: + void NewGame(IWorld *world, IProxy *proxy) + { + static unsigned char cmd[] = { + 1, // command length in bytes + DRC_CMD_START // tell them for start director + }; + + world->AddSignonData(svc_director, cmd, sizeof(cmd)); + } + + int AddCommand(DirectorCmd *cmd) { return 0; } + void WriteCommands(BitBuffer *stream, float startTime, float endTime) {} + char *GetModName() { return "NULL"; } + bool RemoveCommand(int index) { return false; } + + DirectorCmd *GetLastCommand() { return nullptr; } + IObjectContainer *GetCommands() { return nullptr; } + + virtual ~DirectorNull() {} +}; diff --git a/rehlds/HLTV/Director/src/precompiled.cpp b/rehlds/HLTV/Director/src/precompiled.cpp new file mode 100644 index 0000000..5f656a4 --- /dev/null +++ b/rehlds/HLTV/Director/src/precompiled.cpp @@ -0,0 +1 @@ +#include "precompiled.h" diff --git a/rehlds/HLTV/Director/src/precompiled.h b/rehlds/HLTV/Director/src/precompiled.h new file mode 100644 index 0000000..d191f95 --- /dev/null +++ b/rehlds/HLTV/Director/src/precompiled.h @@ -0,0 +1,24 @@ +#pragma once + +#include "osconfig.h" +#include "archtypes.h" +#include "mathlib.h" +#include "FileSystem.h" + +#include "TokenLine.h" +#include "interface.h" + +#include "hltv.h" +#include "mem.h" +#include "common.h" + +#include "common/random.h" +#include "common/byteorder.h" +#include "common/BitBuffer.h" +#include "common/DirectorCmd.h" +#include "common/mathlib_internal.h" +#include "common/net_internal.h" +#include "common/common_hltv.h" + +#include "Director.h" +#include "DirectorNull.h" diff --git a/rehlds/HLTV/Director/src/public_amalgamation.cpp b/rehlds/HLTV/Director/src/public_amalgamation.cpp new file mode 100644 index 0000000..1fc308c --- /dev/null +++ b/rehlds/HLTV/Director/src/public_amalgamation.cpp @@ -0,0 +1,3 @@ +#include "precompiled.h" + +#include "interface.cpp" diff --git a/rehlds/HLTV/Proxy/build.gradle b/rehlds/HLTV/Proxy/build.gradle new file mode 100644 index 0000000..53e85cf --- /dev/null +++ b/rehlds/HLTV/Proxy/build.gradle @@ -0,0 +1,165 @@ +import org.doomedsociety.gradlecpp.cfg.ToolchainConfigUtils +import org.doomedsociety.gradlecpp.msvc.MsvcToolchainConfig +import org.doomedsociety.gradlecpp.toolchain.icc.Icc +import org.doomedsociety.gradlecpp.toolchain.icc.IccCompilerPlugin +import org.doomedsociety.gradlecpp.gcc.GccToolchainConfig +import org.gradle.nativeplatform.NativeBinarySpec +import org.gradle.nativeplatform.NativeLibrarySpec +import org.gradle.nativeplatform.toolchain.VisualCpp + +apply plugin: 'cpp' +apply plugin: IccCompilerPlugin +apply plugin: GccCompilerPlugin + +project.ext.dep_bzip2 = project(':dep/bzip2') + +void setupToolchain(NativeBinarySpec b) { + def cfg = rootProject.createToolchainConfig(b); + cfg.projectInclude(project, '/..', '/../..', '/src', '/../Director/src', '/../../common', '/../../engine', '/../../public', '/../../public/rehlds', '/../../pm_shared'); + cfg.projectInclude(dep_bzip2, '/include') + + cfg.singleDefines 'USE_BREAKPAD_HANDLER', 'HLTV', 'CORE_MODULE' + + if (cfg instanceof MsvcToolchainConfig) { + cfg.compilerOptions.pchConfig = new MsvcToolchainConfig.PrecompiledHeadersConfig( + enabled: true, + pchHeader: 'precompiled.h', + pchSourceSet: 'proxy_pch' + ); + + cfg.singleDefines('_CRT_SECURE_NO_WARNINGS') + cfg.compilerOptions.args '/Ob2', '/Oi', '/GF' + cfg.projectLibpath(project, '/../../lib') + cfg.extraLibs "steam_api.lib", "psapi.lib", "ws2_32.lib" + } + else if (cfg instanceof GccToolchainConfig) { + cfg.compilerOptions.pchConfig = new GccToolchainConfig.PrecompilerHeaderOptions( + enabled: true, + pchSourceSet: 'proxy_pch' + ); + + cfg.compilerOptions.languageStandard = 'c++0x' + cfg.defines([ + '_strdup': 'strdup', + '_stricmp': 'strcasecmp', + '_strnicmp': 'strncasecmp', + '_vsnprintf': 'vsnprintf', + '_snprintf': 'snprintf', + ]); + + cfg.compilerOptions.args '-Qoption,cpp,--treat_func_as_string_literal_cpp', '-fno-exceptions' + cfg.projectLibpath(project, '/../../lib/linux32') + cfg.extraLibs "steam_api" + } + + ToolchainConfigUtils.apply(project, cfg, b); +} + +model { + buildTypes { + release + } + + platforms { + x86 { + architecture "x86" + } + } + + toolChains { + visualCpp(VisualCpp) { + } + icc(Icc) { + } + } + + components { + proxy(NativeLibrarySpec) { + targetPlatform 'x86' + baseName 'proxy' + + sources { + proxy_main(CppSourceSet) { + source { + srcDir "src" + include "**/*.cpp" + exclude "precompiled.cpp" + } + + lib project: ':dep/bzip2', library: 'bzip2', linkage: 'static' + } + + proxy_common(CppSourceSet) { + source { + srcDirs "../../common", "../common" + + // common + include "BaseSystemModule.cpp" + include "ObjectDictionary.cpp" + include "ObjectList.cpp" + include "TokenLine.cpp" + + // HLTV common + include "BaseClient.cpp" + include "BitBuffer.cpp" + include "byteorder.cpp" + include "common.cpp" + include "DemoFile.cpp" + include "DirectorCmd.cpp" + include "InfoString.cpp" + include "mathlib.cpp" + include "md5.cpp" + include "munge.cpp" + include "NetAddress.cpp" + include "NetChannel.cpp" + include "random.cpp" + } + } + + proxy_engine(CppSourceSet) { + source { + srcDir "../../engine" + include "mem.cpp" + } + } + + proxy_director(CppSourceSet) { + source { + srcDir "../Director/src" + include "Director.cpp" + } + } + + proxy_pch(CppSourceSet) { + source { + srcDir "src" + include "precompiled.cpp" + + lib project: ':dep/bzip2', library: 'bzip2', linkage: 'static' + } + } + } + + binaries.all { + NativeBinarySpec b -> project.setupToolchain(b) + } + } + } +} + +task buildFixes { + dependsOn binaries.withType(SharedLibraryBinarySpec).matching { SharedLibraryBinarySpec blib -> + blib.buildable && blib.buildType.name == 'release' + } +} + +task buildRelease { + dependsOn binaries.withType(SharedLibraryBinarySpec).matching { SharedLibraryBinarySpec blib -> + blib.buildable && blib.buildType.name == 'release' + } +} + +// prevent static lib building +binaries.withType(StaticLibraryBinarySpec) { binary -> + buildable = false +} diff --git a/rehlds/HLTV/Proxy/msvc/PostBuild.bat b/rehlds/HLTV/Proxy/msvc/PostBuild.bat new file mode 100644 index 0000000..8583878 --- /dev/null +++ b/rehlds/HLTV/Proxy/msvc/PostBuild.bat @@ -0,0 +1,39 @@ +@echo OFF +:: +:: Post-build auto-deploy script +:: Create and fill PublishPath.txt file with path to deployment folder +:: I.e. PublishPath.txt should contain one line with a folder path +:: Call it so: +:: IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)") +:: + +SET targetDir=%~1 +SET targetName=%~2 +SET targetExt=%~3 +SET projectDir=%~4 +SET destination= + +IF NOT EXIST "%projectDir%\PublishPath.txt" ( + ECHO No deployment path specified. Create PublishPath.txt near PostBuild.bat with paths on separate lines for auto deployment. + exit /B 0 +) + +FOR /f "tokens=* delims= usebackq" %%a IN ("%projectDir%\PublishPath.txt") DO ( + ECHO Deploying to: %%a + IF NOT "%%a" == "" ( + copy /Y "%targetDir%%targetName%%targetExt%" "%%a" + IF NOT ERRORLEVEL 1 ( + IF EXIST "%targetDir%%targetName%.pdb" ( + copy /Y "%targetDir%%targetName%.pdb" "%%a" + ) + ) ELSE ( + ECHO PostBuild.bat ^(27^) : warning : Can't copy '%targetName%%targetExt%' to deploy path '%%a' + ) + ) +) + +IF "%%a" == "" ( + ECHO No deployment path specified. +) + +exit /B 0 \ No newline at end of file diff --git a/rehlds/HLTV/Proxy/msvc/Proxy.sln b/rehlds/HLTV/Proxy/msvc/Proxy.sln new file mode 100644 index 0000000..099d4da --- /dev/null +++ b/rehlds/HLTV/Proxy/msvc/Proxy.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Proxy", "Proxy.vcxproj", "{ADDFF069-D39D-4A1B-87C9-85A62B980547}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bzip2", "..\..\..\..\dep\bzip2\msvc\bzip2.vcxproj", "{792DF067-9904-4579-99B9-46C17277ADE3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {ADDFF069-D39D-4A1B-87C9-85A62B980547}.Debug|Win32.ActiveCfg = Debug|Win32 + {ADDFF069-D39D-4A1B-87C9-85A62B980547}.Debug|Win32.Build.0 = Debug|Win32 + {ADDFF069-D39D-4A1B-87C9-85A62B980547}.Release|Win32.ActiveCfg = Release|Win32 + {ADDFF069-D39D-4A1B-87C9-85A62B980547}.Release|Win32.Build.0 = Release|Win32 + {792DF067-9904-4579-99B9-46C17277ADE3}.Debug|Win32.ActiveCfg = Debug|Win32 + {792DF067-9904-4579-99B9-46C17277ADE3}.Debug|Win32.Build.0 = Debug|Win32 + {792DF067-9904-4579-99B9-46C17277ADE3}.Release|Win32.ActiveCfg = Release|Win32 + {792DF067-9904-4579-99B9-46C17277ADE3}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/rehlds/HLTV/Proxy/msvc/Proxy.vcxproj b/rehlds/HLTV/Proxy/msvc/Proxy.vcxproj new file mode 100644 index 0000000..625d946 --- /dev/null +++ b/rehlds/HLTV/Proxy/msvc/Proxy.vcxproj @@ -0,0 +1,377 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + + + true + true + + + Use + Use + + + + + + + Use + Use + + + + + + + + + + + + + + + + + + + Use + Use + + + + + + + Use + Use + + + + + + + Use + Use + + + + + + + Use + Use + + + + + + + Use + Use + + + + + + + Use + Use + + + + + + + Use + Use + + + + + + + Use + Use + + + + + + + + + + + + + + + + + + + Use + Use + + + + + + + Use + Use + + + + + + + + + + + + + Create + Create + + + Use + Use + + + + + + + Use + Use + + + + + + + Use + Use + + + + + + + Use + Use + + + + + + + + + + + + + + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + {792df067-9904-4579-99b9-46c17277ade3} + + + + {ADDFF069-D39D-4A1B-87C9-85A62B980547} + Win32Proj + Proxy + 8.1 + + + + DynamicLibrary + true + v120_xp + v140_xp + MultiByte + + + DynamicLibrary + false + v120_xp + v140_xp + true + MultiByte + + + + + + + + + + + + + + + true + proxy + + + false + proxy + + + + Use + Level3 + Disabled + HLTV;WIN32;_DEBUG;_WINDOWS;_USRDLL;PROXY_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + $(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\Director\src;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;$(ProjectDir)\..\..\..\..\dep\bzip2\include;%(AdditionalIncludeDirectories) + precompiled.h + MultiThreadedDebug + true + EnableFastChecks + false + + + Windows + true + psapi.lib;ws2_32.lib;steam_api.lib;%(AdditionalDependencies) + $(ProjectDir)../../../lib + + + Force build to run Pre-Build event + + + git.always.run + + + git.always.run + echo Empty Action + + + IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)") + + + Automatic deployment script + + + + + Level3 + Use + MaxSpeed + true + true + HLTV;WIN32;NDEBUG;_WINDOWS;_USRDLL;PROXY_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + $(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\Director\src;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;$(ProjectDir)\..\..\..\..\dep\bzip2\include;%(AdditionalIncludeDirectories) + precompiled.h + MultiThreaded + true + false + + + Windows + true + true + true + psapi.lib;ws2_32.lib;steam_api.lib;%(AdditionalDependencies) + $(ProjectDir)../../../lib + + + + + + + + + + + echo Empty Action + + + Force build to run Pre-Build event + + + git.always.run + + + git.always.run + + + IF EXIST "$(ProjectDir)PostBuild.bat" (CALL "$(ProjectDir)PostBuild.bat" "$(TargetDir)" "$(TargetName)" "$(TargetExt)" "$(ProjectDir)") + + + Automatic deployment script + + + + + + \ No newline at end of file diff --git a/rehlds/HLTV/Proxy/msvc/Proxy.vcxproj.filters b/rehlds/HLTV/Proxy/msvc/Proxy.vcxproj.filters new file mode 100644 index 0000000..d86b875 --- /dev/null +++ b/rehlds/HLTV/Proxy/msvc/Proxy.vcxproj.filters @@ -0,0 +1,195 @@ + + + + + {e272c141-8f15-473d-9ea7-8c46b9f2db19} + + + {b7066def-60e7-4352-8787-a31a8f9dec0d} + + + {feda00f0-7c09-4c63-b5a1-e4fbcdfc6b69} + + + {199d99ef-8924-48fc-8c8a-2859f8d40c82} + + + {1777708a-8593-40c0-8caa-9154180854dc} + + + {ee194364-02f4-428c-a25f-0a60a7d20263} + + + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + src + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + src\hookers + + + src\hookers + + + src + + + common + + + common + + + common + + + common + + + engine + + + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + src + + + src + + + src + + + HLTV\common + + + src + + + src + + + src + + + src + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + HLTV\common + + + src\hookers + + + common + + + common + + + common + + + common + + + engine + + + \ No newline at end of file diff --git a/rehlds/HLTV/Proxy/src/DemoClient.cpp b/rehlds/HLTV/Proxy/src/DemoClient.cpp new file mode 100644 index 0000000..b4cef3d --- /dev/null +++ b/rehlds/HLTV/Proxy/src/DemoClient.cpp @@ -0,0 +1,294 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +bool DemoClient::Init(IBaseSystem *system, int serial, char *name) +{ + BaseSystemModule::Init(system, serial, name); + + m_Proxy = nullptr; + m_LastFrameSeqNr = 0; + m_ClientDelta = 0; + m_IsActive = false; + + memset(m_BaseFileName, 0, sizeof(m_BaseFileName)); + m_DemoInfo.SetMaxSize(MAX_DEMO_INFO); + + m_State = MODULE_RUNNING; + m_System->Printf("Demo client initialized.\n"); + + return true; +} + +char *DemoClient::GetStatusLine() +{ + static char string[256]; + _snprintf(string, sizeof(string), "Recording to %s (%.1f seconds)\n", m_DemoFile.GetFileName(), m_DemoFile.GetDemoTime()); + return string; +} + +bool DemoClient::IsHearingVoices() +{ + return true; +} + +bool DemoClient::HasChatEnabled() +{ + return false; +} + +char *DemoClient::GetType() +{ + return DEMOCLIENT_INTERFACE_VERSION; +} + +int DemoClient::GetClientType() +{ + return TYPE_DEMO; +} + +void DemoClient::Reconnect() +{ + FinishDemo(); +} + +bool DemoClient::Connect(INetSocket *socket, NetAddress *adr, char *userinfo) +{ + if (IsActive()) + { + if (m_DemoFile.IsRecording()) { + m_System->Printf("Already recording to %s.\n", m_DemoFile.GetFileName()); + } else { + m_System->Printf("Already recording initialized for %s.\n", m_BaseFileName); + } + + return false; + } + + m_DemoChannel.Create(m_System); + m_DemoChannel.SetUpdateRate(40); + m_DemoChannel.SetRate(20000); + + m_LastFrameSeqNr = 0; + m_ClientDelta = 0; + + m_DemoChannel.SetKeepAlive(false); + m_DemoFile.Init(m_World, nullptr, &m_DemoChannel); + + m_IsActive = true; + m_System->Printf("Recording initialized.\n"); + + return true; +} + +void DemoClient::RunFrame(double time) +{ + BaseSystemModule::RunFrame(time); + + NetPacket *packet = m_DemoChannel.GetPacket(); + while (packet) + { + m_System->DPrintf("WARNING! Incoming data in demo game channel.Ignored.\n"); + m_DemoChannel.FreePacket(packet); + + packet = m_DemoChannel.GetPacket(); + } + + if (IsActive() && m_World->IsActive() && m_DemoChannel.IsReadyToSend()) { + SendDatagram(); + } +} + +void DemoClient::SendDatagram() +{ + if (m_Proxy->GetDelay() > 0) + { + double worldTime = m_Proxy->GetSpectatorTime(); + double proxyTime = m_Proxy->GetProxyTime(); + + frame_t *frame = m_World->GetFrameByTime(worldTime); + if (!frame) { + return; + } + + if (frame->seqnr > 1) { + WriteDatagram(proxyTime - (worldTime - frame->time), frame); + } + + return; + } + + frame_t *frame = m_World->GetLastFrame(); + if (frame) { + WriteDatagram(frame->time, frame); + } +} + +void DemoClient::WriteDatagram(double time, frame_t *frame) +{ + unsigned int deltaFrameSeqNr = m_LastFrameSeqNr; + if (deltaFrameSeqNr > 0) + { + if (deltaFrameSeqNr == frame->seqnr) + return; + + if (deltaFrameSeqNr > frame->seqnr) + m_LastFrameSeqNr = frame->seqnr - 1; + } + else + { + char mapname[MAX_PATH]; + COM_FileBase(m_World->GetLevelName(), mapname); + + char fileName[MAX_PATH]; + _snprintf(fileName, sizeof(fileName), "%s-%s-%s.dem", m_BaseFileName, COM_TimeString(), mapname); + + m_DemoFile.StartRecording(fileName); + m_Proxy->WriteSignonData(TYPE_DEMO, &m_DemoChannel.m_reliableStream); + } + + m_DemoChannel.m_unreliableStream.WriteByte(svc_time); + m_DemoChannel.m_unreliableStream.WriteFloat((float)time); + + m_World->WriteFrame(frame, m_LastFrameSeqNr, &m_DemoChannel.m_reliableStream, &m_DemoChannel.m_unreliableStream, deltaFrameSeqNr, m_ClientDelta, true); + m_LastFrameSeqNr = frame->seqnr; + m_ClientDelta = (m_DemoChannel.m_outgoing_sequence & 0xFF); + + // reliable stream has overflowed + if (m_DemoChannel.m_reliableStream.IsOverflowed()) + { + m_System->Printf("WARNING! DemoClient::WriteDatagram: reliable data overflow.\n"); + Disconnect("reliable data overflow"); + return; + } + + if (m_DemoChannel.m_unreliableStream.IsOverflowed()) { + m_DemoChannel.m_unreliableStream.Clear(); + } + + m_DemoFile.WriteDemoMessage(&m_DemoChannel.m_unreliableStream, &m_DemoChannel.m_reliableStream); + m_DemoChannel.TransmitOutgoing(); + + client_data_t cdata; + memset(&cdata, 0, sizeof(cdata)); + m_DemoFile.WriteUpdateClientData(&cdata); +} + +bool DemoClient::IsActive() +{ + return m_IsActive; +} + +void DemoClient::Disconnect(const char *reason) +{ + if (!IsActive()) + return; + + if (reason) + { + m_DemoChannel.m_reliableStream.WriteByte(svc_print); + m_DemoChannel.m_reliableStream.WriteString(reason); + } + + FinishDemo(); + m_IsActive = false; +} + +void DemoClient::ShutDown() +{ + if (m_State == MODULE_DISCONNECTED) + return; + + FinishDemo(); + m_IsActive = false; + + BaseSystemModule::ShutDown(); + m_System->Printf("Demo module shutdown.\n"); +} + +void DemoClient::FinishDemo() +{ + if (!IsActive()) + return; + + m_DemoChannel.m_reliableStream.WriteByte(svc_print); + m_DemoChannel.m_reliableStream.WriteString("HLTV Demo finished.\n"); + + m_DemoFile.WriteDemoMessage(&m_DemoChannel.m_unreliableStream, &m_DemoChannel.m_reliableStream); + m_DemoFile.CloseFile(); + m_DemoChannel.Clear(); + + m_LastFrameSeqNr = 0; + m_ClientDelta = 0; +} + +NetAddress *DemoClient::GetAddress() +{ + static NetAddress fakeaddress; + fakeaddress.Clear(); + return &fakeaddress; +} + +void DemoClient::Send(unsigned char *data, int length, bool isReliable) +{ + if (isReliable) + m_DemoChannel.m_reliableStream.WriteBuf(data, length); + else + m_DemoChannel.m_unreliableStream.WriteBuf(data, length); +} + +void DemoClient::SetProxy(IProxy *proxy) +{ + m_Proxy = proxy; +} + +InfoString *DemoClient::GetUserInfo() +{ + return &m_DemoInfo; +} + +char *DemoClient::GetClientName() +{ + return m_Name; +} + +void DemoClient::SetWorld(IWorld *world) +{ + m_World = world; +} + +void DemoClient::SetFileName(char *fileName) +{ + strcopy(m_BaseFileName, fileName); +} + +DemoFile *DemoClient::GetDemoFile() +{ + return &m_DemoFile; +} diff --git a/rehlds/HLTV/Proxy/src/DemoClient.h b/rehlds/HLTV/Proxy/src/DemoClient.h new file mode 100644 index 0000000..94cfe26 --- /dev/null +++ b/rehlds/HLTV/Proxy/src/DemoClient.h @@ -0,0 +1,78 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "common/InfoString.h" + +class DemoClient: public IClient, public BaseSystemModule { +public: + DemoClient() {} + virtual ~DemoClient() {} + + bool Init(IBaseSystem *system, int serial, char *name); + void RunFrame(double time); + char *GetStatusLine(); + char *GetType(); + void ShutDown(); + bool Connect(INetSocket *socket = nullptr, NetAddress *adr = nullptr, char *userinfo = ""); + void Disconnect(const char *reason); + void Reconnect(); + void SetWorld(IWorld *world); + bool IsHearingVoices(); + bool HasChatEnabled(); + NetAddress *GetAddress(); + int GetClientType(); + char *GetClientName(); + InfoString *GetUserInfo(); + bool IsActive(); + void Send(unsigned char *data, int length, bool isReliable); + + void SetProxy(IProxy *proxy); + void SetFileName(char *fileName); + DemoFile *GetDemoFile(); + void FinishDemo(); + void SendDatagram(); + void WriteDatagram(double time, frame_t *frame); + +protected: + IProxy *m_Proxy; + IWorld *m_World; + bool m_IsActive; + NetChannel m_DemoChannel; + + enum { MAX_DEMO_INFO = 256 }; + DemoFile m_DemoFile; + char m_BaseFileName[MAX_PATH]; + + InfoString m_DemoInfo; + unsigned int m_LastFrameSeqNr; + unsigned int m_ClientDelta; +}; + +#define DEMOCLIENT_INTERFACE_VERSION "democlient000" diff --git a/rehlds/HLTV/Proxy/src/FakeClient.cpp b/rehlds/HLTV/Proxy/src/FakeClient.cpp new file mode 100644 index 0000000..b014489 --- /dev/null +++ b/rehlds/HLTV/Proxy/src/FakeClient.cpp @@ -0,0 +1,156 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +bool FakeClient::Init(IBaseSystem *system, int serial, char *name) +{ + BaseSystemModule::Init(system, serial, name); + + _snprintf(m_Name, sizeof(m_Name), "fakeclient%i", serial); + + m_Network = dynamic_cast(m_System->GetModule(NETWORK_INTERFACE_VERSION, "core")); + if (!m_Network) + { + m_System->Errorf("FakeClient::Init: couldn't load network module.\n"); + return false; + } + + if (!(m_Socket = m_Network->CreateSocket(serial + 2048))) + { + m_System->Errorf("FakeClient::Init: Could not create port %i.\n", serial + 2048); + return false; + } + + char temp[256]; + sprintf(temp, "fakeserver%i", serial); + + m_Server = dynamic_cast(m_System->GetModule(SERVER_INTERFACE_VERSION, "core", temp)); + if (!m_Server) + { + m_System->Errorf("FakeClient::Init: couldn't load server module.\n"); + return false; + } + + m_Server->RegisterListener(this); + m_Server->SetUserInfo("hspecs", "0"); + m_Server->SetUserInfo("hslots", "0"); + m_Server->SetDelayReconnect(false); + m_Server->SetPlayerName(m_Name); + m_Server->SetAutoRetry(false); + + sprintf(temp, "fakeworld%i", serial); + + m_World = dynamic_cast(m_System->GetModule(WORLD_INTERFACE_VERSION, "core", temp)); + if (!m_World) + { + m_System->Errorf("FakeClient::Init: couldn't load world module.\n"); + return false; + } + + m_World->RegisterListener(this); + + m_State = MODULE_RUNNING; + m_System->Printf("Fake client module initialized (%i).\n", serial); + return true; +} + +void FakeClient::RunFrame(double time) +{ + BaseSystemModule::RunFrame(time); + + NetPacket *packet; + while ((packet = m_Socket->ReceivePacket())) + { + m_System->DPrintf("FakeClient: WARNING! Packet from %s with invalid sequence number.\n", packet->address.ToString()); + m_Socket->FreePacket(packet); + } +} + +void FakeClient::SetRate(int rate) +{ + m_Server->SetRate(rate); +} + +void FakeClient::Connect(NetAddress *adr) +{ + m_Server->Connect(m_World, adr, m_Socket); +} + +void FakeClient::Retry() +{ + m_Server->Retry(); +} + +void FakeClient::Say(char *text) +{ + if (!m_Server->IsConnected()) + return; + + char string[1024]; + _snprintf(string, sizeof(string), "say \"%s\"", text); + m_Server->SendStringCommand(text); +} + +void FakeClient::Disconnect() +{ + m_Server->Disconnect(); +} + +void FakeClient::ShutDown() +{ + if (m_State == MODULE_DISCONNECTED) + return; + + if (m_World) { + m_World->RemoveListener(this); + } + + BaseSystemModule::ShutDown(); + m_System->Printf("FakeClient module shutdown.\n"); +} + +void FakeClient::ReceiveSignal(ISystemModule *module, unsigned int signal, void *data) +{ + if (module->GetSerial() != m_World->GetSerial()) + return; + + if (signal == 8 /* signal to shutdown*/) { + ShutDown(); + } +} + +char *FakeClient::GetType() +{ + return FAKECLIENT_INTERFACE_VERSION; +} + +char *FakeClient::GetStatusLine() +{ + return "I am a fake client.\n"; +} diff --git a/rehlds/HLTV/Proxy/src/FakeClient.h b/rehlds/HLTV/Proxy/src/FakeClient.h new file mode 100644 index 0000000..48f0966 --- /dev/null +++ b/rehlds/HLTV/Proxy/src/FakeClient.h @@ -0,0 +1,63 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "BaseSystemModule.h" + +class IWorld; +class IServer; +class INetwork; +class INetSocket; + +class FakeClient: public BaseSystemModule { +public: + FakeClient() {} + virtual ~FakeClient() {} + + bool Init(IBaseSystem *system, int serial, char *name); + void RunFrame(double time); + void ReceiveSignal(ISystemModule *module, unsigned int signal, void *data); + char *GetStatusLine(); + char *GetType(); + void ShutDown(); + + void SetRate(int rate); + void Connect(NetAddress *adr); + void Retry(); + void Say(char *text); + void Disconnect(); + +protected: + INetwork *m_Network; + IWorld *m_World; + IServer *m_Server; + INetSocket *m_Socket; +}; + +#define FAKECLIENT_INTERFACE_VERSION "fakeclient000" diff --git a/rehlds/HLTV/Proxy/src/Master.cpp b/rehlds/HLTV/Proxy/src/Master.cpp new file mode 100644 index 0000000..44aea36 --- /dev/null +++ b/rehlds/HLTV/Proxy/src/Master.cpp @@ -0,0 +1,293 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +Master::GameToAppIDMapItem_t Master::m_GameToAppIDMap[] = +{ + { GAME_APPID_CSTRIKE, "cstrike" }, + { GAME_APPID_TFC, "tfc" }, + { GAME_APPID_DOD, "dod" }, + { GAME_APPID_DMC, "dmc" }, + { GAME_APPID_GEARBOX, "gearbox" }, + { GAME_APPID_RICOCHET, "ricochet" }, + { GAME_APPID_VALVE, "valve" }, + { GAME_APPID_CZERO, "czero" }, + { GAME_APPID_CZEROR, "czeror" }, + { GAME_APPID_BSHIFT, "bshift" }, + { GAME_APPID_CSTRIKE_BETA, "cstrike_beta" } +}; + +Master::Master() : + m_flMasterUpdateTime(0), + m_bSteamInitialized(false) +{ +} + +int Master::GetGameAppID(const char *gamedir) const +{ + for (auto& game : m_GameToAppIDMap) { + if (!_stricmp(game.dir, gamedir)) { + return game.appID; + } + } + + return GAME_APPID_VALVE; +} + +bool Master::Init(IBaseSystem *system, int serial, char *name) +{ + BaseSystemModule::Init(system, serial, name); + + m_MasterSocket = m_Proxy->GetSocket(); + + m_System->RegisterCommand("heartbeat", this, CMD_ID_HEARTBEAT); + m_System->RegisterCommand("nomaster", this, CMD_ID_NOMASTER); + m_System->RegisterCommand("listmaster", this, CMD_ID_LISTMASTER); + + m_State = MODULE_RUNNING; + m_NoMaster = false; + m_bMasterLoaded = false; + + INetwork *network = m_MasterSocket->GetNetwork(); + if (!network->GetLocalAddress()) { + m_System->Printf("Master module failed to initialize (no net).\n"); + return false; + } + + m_bSteamInitialized = false; + m_System->Printf("Master module initialized.\n"); + + return true; +} + +void Master::SetProxy(IProxy *proxy) +{ + m_Proxy = proxy; +} + +void Master::RunFrame(double time) +{ + BaseSystemModule::RunFrame(time); + + static double s_flLastRunCallbacks = 0.0f; + if ((time - s_flLastRunCallbacks) > 0.1) + { + SteamGameServer_RunCallbacks(); + s_flLastRunCallbacks = time; + } + + if (m_NoMaster) + return; + + if (m_Proxy->IsActive()) + { + if (!m_bSteamInitialized) + { + IWorld *world = m_Proxy->GetWorld(); + if (world) + { + int nAppID = GetGameAppID(world->GetGameDir()); + if (nAppID > 0) + { + FILE *f = fopen("steam_appid.txt", "w+"); + if (f) + { + fprintf(f, "%d\n", nAppID); + fclose(f); + } + } + + INetwork *network = m_MasterSocket->GetNetwork(); + NetAddress *netAdr = network->GetLocalAddress(); + + if (SteamGameServer_Init(ntohl(*(u_long *)&netAdr->m_IP[0]), 0, 0, 0xFFFFu, eServerModeNoAuthentication, MASTER_VERSION)) + { + SteamGameServer()->SetProduct("hltv"); + SteamGameServer()->SetGameDescription("hltv"); + SteamGameServer()->SetModDir(world->GetGameDir()); + SteamGameServer()->SetDedicatedServer(true); + SteamGameServer()->SetSpectatorPort(netAdr->m_Port); + SteamGameServer()->LogOnAnonymous(); + + SteamGameServer()->EnableHeartbeats(true); + } + else + { + // NOTE: Actually here call via Printf, but we will be use DPrintf to avoid useless print into console + // or at least make only print once. + m_System->DPrintf("Master module failed to initialize. (init failed)\n"); + } + + m_bSteamInitialized = SteamGameServer() ? true : false; + } + } + + if (m_bSteamInitialized) + { + IWorld *world = m_Proxy->GetWorld(); + if (m_flMasterUpdateTime < m_System->GetTime()) + { + char mapName[MAX_PATH]; + COM_FileBase(world->GetLevelName(), mapName); + + char szHostName[MAX_PATH]; + strcopy(szHostName, world->GetHostName()); + + int slots, proxies, spectators; + m_Proxy->GetStatistics(proxies, slots, spectators); + + SteamGameServer()->SetMaxPlayerCount(min(slots, 127)); // max slots + SteamGameServer()->SetServerName(szHostName); + SteamGameServer()->SetMapName(mapName); + SteamGameServer()->SetPasswordProtected(m_Proxy->IsPasswordProtected()); + SteamGameServer()->SetModDir(world->GetGameDir()); + + m_flMasterUpdateTime = m_System->GetTime() + 5.0f; + } + } + } + + if (m_bSteamInitialized) + { + uint32 ip; + uint16 port; + char szOutBuf[4096]; + + int iLen = SteamGameServer()->GetNextOutgoingPacket(szOutBuf, sizeof(szOutBuf), &ip, &port); + while (iLen > 0) + { + NetAddress netAdr; + *((uint32 *)&netAdr.m_IP[0]) = htonl(ip); + netAdr.m_Port = htons(port); + + m_MasterSocket->SendPacket(&netAdr, szOutBuf, iLen); + + iLen = SteamGameServer()->GetNextOutgoingPacket(szOutBuf, sizeof(szOutBuf), &ip, &port); + } + } +} + +void Master::ShutDown() +{ + if (m_State == MODULE_DISCONNECTED) { + return; + } + + if (SteamGameServer()) { + SteamGameServer()->LogOff(); + } + + BaseSystemModule::ShutDown(); + SteamGameServer_Shutdown(); + m_System->Printf("Master module shutdown.\n"); +} + +void Master::CMD_Heartbeat(char *cmdLine) +{ + if (m_State == MODULE_DISCONNECTED) { + return; + } + + if (SteamGameServer()) { + SteamGameServer()->ForceHeartbeat(); + } +} + +void Master::CMD_NoMaster(char *cmdLine) +{ + if (m_State == MODULE_DISCONNECTED || !SteamGameServer()) { + return; + } + + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: nomaster <0|1>\n"); + m_System->Printf("Master server notification is %s.\n", m_NoMaster ? "disabled" : "enabled"); + return; + } + + bool bOldMasterState = m_NoMaster; + m_NoMaster = atoi(params.GetToken(1)) ? true : false; + + if (bOldMasterState != m_NoMaster) { + SteamGameServer()->EnableHeartbeats(m_NoMaster); + } +} + +void Master::CMD_ListMaster(char *pchCmdLine) +{ + if (m_State == MODULE_DISCONNECTED) { + return; + } + + m_System->Printf("No longer used\n"); +} + +void Master::ExecuteCommand(int commandID, char *commandLine) +{ + switch (commandID) + { + case CMD_ID_HEARTBEAT: + CMD_Heartbeat(commandLine); + break; + case CMD_ID_NOMASTER: + CMD_NoMaster(commandLine); + break; + case CMD_ID_LISTMASTER: + CMD_ListMaster(commandLine); + break; + default: + m_System->Printf("ERROR! Master::ExecuteCommand: unknown command ID %i.\n", commandID); + break; + } +} + +char *Master::GetType() +{ + return MASTER_INTERFACE_VERSION; +} + +void Master::SendShutdown() +{ + if (m_State == MODULE_DISCONNECTED) { + return; + } + + if (SteamGameServer()) { + SteamGameServer()->EnableHeartbeats(false); + } +} + +char *Master::GetStatusLine() +{ + static char string[256]; + _snprintf(string, sizeof(string), "Master servers: 0\n"); + return string; +} diff --git a/rehlds/HLTV/Proxy/src/Master.h b/rehlds/HLTV/Proxy/src/Master.h new file mode 100644 index 0000000..a3115ae --- /dev/null +++ b/rehlds/HLTV/Proxy/src/Master.h @@ -0,0 +1,115 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "BaseSystemModule.h" +#include "TokenLine.h" + +#define MASTER_VERSION "1.1.2.6" + +enum GameType_e +{ + GT_CSTRIKE, + GT_TFC, + GT_DOD, + GT_DMC, + GT_GEARBOX, + GT_RICOCHET, + GT_VALVE, + GT_CZERO, + GT_CZEROR, + GT_BSHIFT, + GT_CSTRIKE_BETA, +}; + +class Proxy; +class INetSocket; + +class Master: public BaseSystemModule { +public: + Master(); + virtual ~Master() {} + + bool Init(IBaseSystem *system, int serial, char *name); + void ExecuteCommand(int commandID, char *commandLine); + void RunFrame(double time); + char *GetStatusLine(); + char *GetType(); + void ShutDown(); + + void InitializeSteam(); + void SendShutdown(); + void SetProxy(IProxy *proxy); + +private: + enum LocalCommandIDs { + CMD_ID_HEARTBEAT = 1, + CMD_ID_NOMASTER, + CMD_ID_LISTMASTER, + }; + + void CMD_Heartbeat(char *cmdLine); + void CMD_NoMaster(char *cmdLine); + void CMD_ListMaster(char *cmdLine); + + enum { + GAME_APPID_CSTRIKE = 10, + GAME_APPID_TFC = 20, + GAME_APPID_DOD = 30, + GAME_APPID_DMC = 40, + GAME_APPID_GEARBOX = 50, + GAME_APPID_RICOCHET = 60, + GAME_APPID_VALVE = 70, + GAME_APPID_CZERO = 80, + GAME_APPID_CZEROR = 100, + GAME_APPID_BSHIFT = 130, + GAME_APPID_CSTRIKE_BETA = 150, + }; + + typedef struct GameToAppIDMapItem_s + { + size_t appID; + const char *dir; + } GameToAppIDMapItem_t; + + static GameToAppIDMapItem_t m_GameToAppIDMap[]; + int GetGameAppID(const char *gamedir) const; + +protected: + friend class Proxy; + + bool m_NoMaster; + bool m_bMasterLoaded; + INetSocket *m_MasterSocket; + IProxy *m_Proxy; + double m_flMasterUpdateTime; + bool m_bSteamInitialized; +}; + +#define MASTER_INTERFACE_VERSION "master000" diff --git a/rehlds/HLTV/Proxy/src/Proxy.cpp b/rehlds/HLTV/Proxy/src/Proxy.cpp new file mode 100644 index 0000000..617a104 --- /dev/null +++ b/rehlds/HLTV/Proxy/src/Proxy.cpp @@ -0,0 +1,2770 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +Proxy::LocalCommandID_s Proxy::m_LocalCmdReg[] = { + { "rcon", CMD_ID_RCON, &Proxy::CMD_Rcon }, + { "rconpassword", CMD_ID_RCONPASSWORD, &Proxy::CMD_RconPassword }, + { "rconaddress", CMD_ID_RCONADDRESS, &Proxy::CMD_RconAddress }, + { "say", CMD_ID_SAY, &Proxy::CMD_Say }, + { "msg", CMD_ID_MSG, &Proxy::CMD_Msg }, + { "clients", CMD_ID_CLIENTS, &Proxy::CMD_Clients }, + { "kick", CMD_ID_KICK, &Proxy::CMD_Kick }, + { "chatmode", CMD_ID_CHATMODE, &Proxy::CMD_ChatMode }, + { "publicgame", CMD_ID_PUBLICGAME, &Proxy::CMD_PublicGame }, + { "offlinetext", CMD_ID_OFFLINETEXT, &Proxy::CMD_OffLineText }, + { "adminpassword", CMD_ID_ADMINPASSWORD, &Proxy::CMD_AdminPassword }, + { "signoncommands", CMD_ID_SIGNONCOMMANDS, &Proxy::CMD_SignOnCommands }, + { "spectatorpassword", CMD_ID_SPECTATORPASSWORD, &Proxy::CMD_SpectatorPassword }, + { "dispatchmode", CMD_ID_DISPATCHMODE, &Proxy::CMD_DispatchMode }, + { "cheeringthreshold", CMD_ID_CHEERINGTHRESHOLD, &Proxy::CMD_CheeringThreshold }, +// { "informplayers", CMD_ID_INFORMPLAYERS, nullptr }, + { "ping", CMD_ID_PING, &Proxy::CMD_Ping }, + { "proxypassword", CMD_ID_PROXYPASSWORD, &Proxy::CMD_ProxyPassword }, + { "maxrate", CMD_ID_MAXRATE, &Proxy::CMD_MaxRate }, + { "loopcmd", CMD_ID_LOOPCMD, &Proxy::CMD_LoopCmd }, + { "maxclients", CMD_ID_MAXCLIENTS, &Proxy::CMD_MaxClients }, + { "disconnect", CMD_ID_DISCONNECT, &Proxy::CMD_Disconnect }, + { "localmsg", CMD_ID_LOCALMSG, &Proxy::CMD_LocalMsg }, + { "connect", CMD_ID_CONNECT, &Proxy::CMD_Connect }, + { "playdemo", CMD_ID_PLAYDEMO, &Proxy::CMD_PlayDemo }, + { "delay", CMD_ID_DELAY, &Proxy::CMD_Delay }, + { "stop", CMD_ID_STOP, &Proxy::CMD_Stop }, + { "record", CMD_ID_RECORD, &Proxy::CMD_Record }, + { "stoprecording", CMD_ID_STOPRECORDING, &Proxy::CMD_StopRecording }, + { "servercmd", CMD_ID_SERVERCMD, &Proxy::CMD_ServerCmd }, + { "clientcmd", CMD_ID_CLIENTCMD, &Proxy::CMD_ClientCmd }, + { "blockvoice", CMD_ID_BLOCKVOICE, &Proxy::CMD_BlockVoice }, + { "name", CMD_ID_NAME, &Proxy::CMD_Name }, + { "updaterate", CMD_ID_UPDATERATE, &Proxy::CMD_Updaterate }, + { "rate", CMD_ID_RATE, &Proxy::CMD_Rate }, + { "addresource", CMD_ID_ADDRESOURCE, &Proxy::CMD_AddResource }, + { "resources", CMD_ID_RESOURCES, &Proxy::CMD_Resources }, + { "bannerfile", CMD_ID_BANNERFILE, &Proxy::CMD_BannerFile }, + { "bann", CMD_ID_BANN, &Proxy::CMD_Bann }, + { "addfakeclients", CMD_ID_ADDFAKECLIENTS, &Proxy::CMD_AddFakeClients }, + { "retry", CMD_ID_RETRY, &Proxy::CMD_Retry }, + { "players", CMD_ID_PLAYERS, &Proxy::CMD_Players }, + { "autoretry", CMD_ID_AUTORETRY, &Proxy::CMD_AutoRetry }, + { "serverpassword", CMD_ID_SERVERPASSWORD, &Proxy::CMD_ServerPassword }, + { "status", CMD_ID_STATUS, &Proxy::CMD_Status }, + { "hostname", CMD_ID_HOSTNAME, &Proxy::CMD_HostName }, + { "maxqueries", CMD_ID_MAXQUERIES, &Proxy::CMD_MaxQueries }, + { "clearbanns", CMD_ID_CLEARBANNS, &Proxy::CMD_ClearBanns }, + { "maxloss", CMD_ID_MAXLOSS, &Proxy::CMD_MaxLoss }, + { "protocol", CMD_ID_PROTOCOL, &Proxy::CMD_Protocol }, + { "region", CMD_ID_REGION, &Proxy::CMD_Region }, +}; + +#ifndef HOOK_HLTV +EXPOSE_SINGLE_INTERFACE(Proxy, IProxy, PROXY_INTERFACE_VERSION); +#endif // HOOK_HLTV + +bool Proxy::Init(IBaseSystem *system, int serial, char *name) +{ + BaseSystemModule::Init(system, serial, name); + + if (!name) { + SetName(PROXY_INTERFACE_VERSION); + } + + m_MaxRate = 20000; + m_MaxUpdateRate = 20; + m_IsMaster = false; + + for (auto& cmd : m_LocalCmdReg) { + m_System->RegisterCommand(cmd.name, this, cmd.id); + } + + m_Network = (INetwork *)m_System->GetModule(NETWORK_INTERFACE_VERSION, "core"); + if (!m_Network) + { + m_System->Errorf("Proxy::Init: couldn't load network module.\n"); + return false; + } + + char *portparam = m_System->CheckParam("-port"); + int proxyport = atoi(portparam ? portparam : PROXY_DEFAULT_PORT); + + if (!(m_Socket = (INetSocket *)m_Network->CreateSocket(proxyport))) + { + m_System->Errorf("Proxy::Init: Could not create proxy port %i.\n", proxyport); + return false; + } + + m_Status.SetProxy(this); + if (!m_System->AddModule(&m_Status, "status")) + { + m_System->Errorf("Proxy::Init: add status module.\n"); + return false; + } + + m_Master.SetProxy(this); + if (!m_System->AddModule(&m_Master, "master")) + { + m_System->Errorf("Proxy::Init: add master module.\n"); + return false; + } + + m_Server = (IServer *)m_System->GetModule(SERVER_INTERFACE_VERSION, "core"); + if (!m_Server) + { + m_System->Errorf("Proxy::Init: couldn't load server module.\n"); + return false; + } + + m_Server->RegisterListener(this); + m_Server->SetProxy(this); + m_Server->SetUserInfo("hspecs", "0"); + m_Server->SetUserInfo("hslots", "0"); + m_Server->SetGameDirectory("valve"); + + m_World = (IWorld *)m_System->GetModule(WORLD_INTERFACE_VERSION, "core"); + if (!m_World) + { + m_System->Errorf("Proxy::Init: couldn't load world module.\n"); + return false; + } + + m_World->RegisterListener(this); + + if (!m_System->AddModule(&m_DemoClient, "demo")) { + m_System->Printf("Proxy::Init: Couldn't create demo client.\n"); + } + + m_DemoClient.SetProxy(this); + m_DemoClient.SetWorld(m_World); + + SetDelay(30); + SetClientTimeScale(1); + + m_Director = nullptr; + m_DispatchMode = DISPATCH_BALANCE; + m_ChatMode = CHAT_OFF; + + m_LastClockUpdateTime = 0; + m_NextStatusUpdateTime = 0; + + m_CheeringPlayers = 0; + m_BannerTGA = nullptr; + + m_PublicGame = true; + m_IsReconnectRequested = false; + m_IsFinishingBroadcast = false; + + m_LastCheeringUpdate = 0; + m_CheeringThreshold = 0.25f; + m_MaxLoss = 0.05f; + m_CurrentLoss = 0; + m_FPS = 1; + + m_MaxSeenClients = 0; + m_MaxClients = 128; + m_MaxQueries = 100; + m_Region = 255; + + const int maxRouteAblePacketSize = 1400; + m_InfoInfo.Resize(maxRouteAblePacketSize); + m_InfoRules.Resize(maxRouteAblePacketSize); + m_InfoPlayers.Resize(maxRouteAblePacketSize); + m_InfoDetails.Resize(maxRouteAblePacketSize); + + m_InfoString.Resize(2080); + m_NextInfoMessagesUpdate = 0; + + // Clear buffers + memset(&m_RconAddress, 0, sizeof(m_RconAddress)); + memset(m_RconPassword, 0, sizeof(m_RconPassword)); + memset(m_AdminPassword, 0, sizeof(m_AdminPassword)); + memset(m_ProxyPassword, 0, sizeof(m_ProxyPassword)); + memset(m_SpectatorPassword, 0, sizeof(m_SpectatorPassword)); + memset(m_LastRconCommand, 0, sizeof(m_LastRconCommand)); + memset(m_OffLineText, 0, sizeof(m_OffLineText)); + memset(m_SignonCommands, 0, sizeof(m_SignonCommands)); + memset(m_Challenges, 0, sizeof(m_Challenges)); + + m_LoopCommands.Init(); + m_BannList.Init(); + m_Resources.Init(); + + memset(&m_LocalMessage, 0, sizeof(m_LocalMessage)); + m_LocalMessage = { + 0, // effect + 255, 160, 0, 255, // r1, g1, b1, a1 + 255, 255, 255, 255, // r2, g2, b2, a2 + -1.f, -1.f, // x, y + 0.5f, 2.f, // fadein, fadeout + 5.f, 0.f, // holdtime, fxtime + "" // text + }; + + memset(&m_CommentatorMessage, 0, sizeof(m_CommentatorMessage)); + m_CommentatorMessage = { + 0, // effect + 255, 160, 0, 255, // r1, g1, b1, a1 + 255, 160, 0, 255, // r2, g2, b2, a2 + -1.f, -1.f, // x, y + 0.3f, 1.f, // fadein, fadeout + 5.f, 0.f, // holdtime, fxtime + "" // text + }; + + strcopy(m_OffLineText, "Game is delayed. Please try again later."); + + m_System->SetTitle("HLTV - offline"); + m_System->ExecuteFile("hltv.cfg"); + m_System->Printf("Proxy module initialized.\n"); + m_State = MODULE_RUNNING; + + return true; +} + +void Proxy::ExecuteCommand(int commandID, char *commandLine) +{ + for (auto& cmd : m_LocalCmdReg) + { + if (cmd.pfnCmd && cmd.id == commandID) { + (this->*cmd.pfnCmd)(commandLine); + return; + } + } + + m_System->Printf("ERROR! Proxy::ExecuteCommand: unknown command ID %i.\n", commandID); +} + +void Proxy::RunFrame(double time) +{ + float frameTime = float(time - m_SystemTime); + BaseSystemModule::RunFrame(time); + + if (m_MaxQueries > 0) + { + m_MaxFrameQueries = int(m_MaxQueries * frameTime); + if (m_MaxFrameQueries <= 0) { + m_MaxFrameQueries = 1; + } + } + else + m_MaxFrameQueries = 0; + + if (frameTime > 0) { + m_FPS = 0.99f * m_FPS + 0.01f / frameTime; + } + + if (m_SystemTime > m_NextStatusUpdateTime) { + UpdateStatusLine(); + } + + if (m_SystemTime > m_NextInfoMessagesUpdate) { + UpdateInfoMessages(); + } + + int numPackets = 0; + NetPacket *packet; + while (numPackets < 32 && (packet = m_Socket->ReceivePacket())) + { + if (packet->connectionless && !packet->data.IsOverflowed()) + { + if (ProcessConnectionlessMessage(&packet->address, &packet->data)) { + numPackets++; + } + } + else + { + m_System->DPrintf("WARNING! Packet from %s with invalid sequence number.\n", packet->address.ToString()); + } + + m_Socket->FreePacket(packet); + } + + ExecuteLoopCommands(); + + if (m_World->IsActive()) + { + if (GetDelay() > 0) + { + RunClocks(); + if (m_IsFinishingBroadcast && m_ClientWorldTime > m_World->GetTime() && !m_IsReconnectRequested) + { + if (m_Server->IsConnected()) { + m_Server->Reconnect(); + } + m_IsReconnectRequested = true; + } + } + + float lastTime = m_LastCheeringUpdate + 8; + if (lastTime < m_SystemTime) + { + m_CheeringPlayers /= 2; + m_LastCheeringUpdate = float(m_SystemTime); + } + } +} + +void Proxy::ShutDown() +{ + if (m_State == MODULE_DISCONNECTED) { + return; + } + + StopBroadcast("HLTV Shutdown."); + + m_Master.ShutDown(); + m_Status.ShutDown(); + m_DemoClient.ShutDown(); + + // Director module shutdown + if (m_Director) { + m_Director->ShutDown(); + } + + // World module shutdown + if (m_World) { + m_World->ShutDown(); + } + + // Server module shutdown + if (m_Server) { + m_Server->ShutDown(); + } + + // Network module shutdown + if (m_Network) { + m_Network->ShutDown(); + } + + m_LoopCommands.Clear(true); + m_BannList.Clear(true); + + m_InfoRules.Free(); + m_InfoPlayers.Free(); + m_InfoDetails.Free(); + m_InfoInfo.Free(); + m_InfoString.Free(); + + ClearResources(); + BaseSystemModule::ShutDown(); + m_System->Printf("Proxy module shutdown.\n"); +} + +void Proxy::ReplyPing(NetAddress *to) +{ + m_Socket->OutOfBandPrintf(to, "%c", A2A_ACK); +} + +void Proxy::CMD_Ping(char *cmdLine) +{ + NetAddress to; + TokenLine params(cmdLine); + if (!m_Network->ResolveAddress(params.GetToken(1), &to)) { + m_System->Printf("Error! HLTV Proxy::CMD_Ping: IP address not valid.\n"); + return; + } + + if (!to.m_Port) { + to.SetPort_(atoi("27015")); + } + + m_Socket->OutOfBandPrintf(&to, "ping"); +} + +void Proxy::ReplyChallenge(NetAddress *to) +{ + unsigned int challengenr = GetChallengeNumber(to); + + m_Socket->OutOfBandPrintf(to, "%c00000000 %u %i\n", S2C_CHALLENGE, challengenr, 2); + m_System->DPrintf("Received challenge from %s.\n", to->ToString()); +} + +void Proxy::ReplyInfoString(NetAddress *to) +{ + m_System->DPrintf("Info string request from %s.\n", to->ToString()); + m_Socket->SendPacket(to, m_InfoString.GetData(), m_InfoString.CurrentSize()); +} + +void Proxy::ReplyInfo(NetAddress *to, bool detailed) +{ + m_System->DPrintf("General information%srequest from %s.\n", detailed ? " (detail) " : " ", to->ToString()); + + if (m_World->IsActive()) + { + BitBuffer *buf = &(detailed ? m_InfoDetails : m_InfoInfo); + m_Socket->SendPacket(to, buf->GetData(), buf->CurrentSize()); + } +} + +void Proxy::ReplyPlayers(NetAddress *to) +{ + m_System->DPrintf("Player information request from %s.\n", to->ToString()); + + if (m_World->IsActive()) { + m_Socket->SendPacket(to, m_InfoPlayers.GetData(), m_InfoPlayers.CurrentSize()); + } +} + +void Proxy::ReplyRules(NetAddress *to) +{ + m_System->DPrintf("Rules information request from %s.\n", to->ToString()); + + if (m_World->IsActive()) { + m_Socket->SendPacket(to, m_InfoRules.GetData(), m_InfoRules.CurrentSize()); + } +} + +void Proxy::ReplyConnect(NetAddress *to, int protocol, int challenge, char *protinfo, char *userinfo) +{ + InfoString info(userinfo); + NetAddress relayProxy; + + int type = atoi(info.ValueForKey("*hltv")); + char *name = info.ValueForKey("name"); + + if (protocol != PROTOCOL_VERSION) { + RejectConnection(to, false, "This HLTV proxy is using protocol %i which is incompatible with yours (%i).\n", PROTOCOL_VERSION, protocol); + return; + } + + if (!CheckChallenge(to, challenge)) { + RejectConnection(to, false, "Challenge number invalid.\n"); + return; + } + + if (!m_World->IsActive()) { + RejectConnection(to, false, "HLTV not started. %s\n", m_OffLineText); + return; + } + + if (m_MaxClients <= 0) { + RejectConnection(to, false, "HLTV proxy disabled.\n"); + return; + } + + if (type == TYPE_CLIENT && m_DispatchMode != DISPATCH_OFF) + { + float ratio = m_Status.GetBestRelayProxy(&relayProxy); + float myRatio = m_Clients.CountElements() / m_MaxClients * 1.25f; + if (myRatio > 1) { + myRatio = 1; + } + + if (ratio < 0) + { + if (m_DispatchMode == DISPATCH_ALL) { + RejectConnection(to, false, "HLTV network is full.\n"); + return; + } + } + else if (myRatio >= ratio || m_DispatchMode == DISPATCH_ALL) + { + DispatchClient(to, &relayProxy); + return; + } + } + + if (!IsValidPassword(type, info.ValueForKey("password"))) { + RejectConnection(to, true, "Bad password"); + return; + } + + if (IsStressed() && type == TYPE_CLIENT) { + RejectConnection(to, false, "Workload limit exceeded."); + return; + } + + char *clientTypeString[] = { "Spectator", "Relay Proxy", "Director", "Commentator", "Fake Client" }; + IClient *client = (IClient *)m_Clients.GetFirst(); + while (client) + { + if (to->Equal(client->GetAddress())) { + m_System->Printf("%s reconnected (%s at %s).\n", clientTypeString[type], name, to->ToString()); + break; + } + + client = (IClient *)m_Clients.GetNext(); + } + + // Duplicate client IP is not found. + if (!client) + { + if (m_Clients.CountElements() >= m_MaxClients) { + RejectConnection(to, false, (m_MaxClients > 0) ? "HLTV proxy is full.\n" : "HLTV proxy is disabled.\n"); + return; + } + + client = new ProxyClient(this); + + if (!m_System->AddModule(client, to->ToString())) + { + RejectConnection(to, false, "HLTV proxy is overloaded.\n"); + delete client; + return; + } + + client->SetWorld(m_World); + m_Clients.AddHead(client); + m_System->Printf("%s connected (%s at %s).\n", clientTypeString[type], name, to->ToString()); + } + + if (!client->Connect(m_Socket, to, userinfo)) { + RejectConnection(to, false, "Connection rejected.\n"); + m_System->RemoveModule(client); + m_System->Printf("Refused director connection for %s.\n", to->ToString()); + return; + } + + int specs, proxies; + CountLocalClients(specs, proxies); + if (m_MaxSeenClients < specs) { + m_MaxSeenClients = specs; + } +} + +void Proxy::CMD_Clients(char *cmdLine) +{ + int count = 0; + TokenLine params(cmdLine); + IClient *client = (IClient *)m_Clients.GetFirst(); + while (client) + { + count++; + m_System->Printf("%s", client->GetStatusLine()); + client = (IClient *)m_Clients.GetNext(); + } + + m_System->Printf("--- Total %i Clients ---\n", count); +} + +void Proxy::RejectConnection(NetAddress *adr, bool badPassword, const char *fmt, ...) +{ + va_list argptr; + char text[1024] = ""; + + va_start(argptr, fmt); + _vsnprintf(text, sizeof(text), fmt, argptr); + va_end(argptr); + + if (badPassword) { + m_Socket->OutOfBandPrintf(adr, "%cBADPASSWORD", S2C_REJECT_BADPASSWORD); + } + else { + m_Socket->OutOfBandPrintf(adr, "%c%s", S2C_REJECT, text); + } + + m_System->DPrintf("Rejected connection: %s (%s)\n", text, adr->ToString()); +} + +char *Proxy::GetStatusLine() +{ + static char string[256]; + _snprintf(string, sizeof(string), "Proxy name \"%s\", %s, Port %i, Clients %i/%i.\n", m_World->GetName(), m_IsMaster ? "Master" : "Relay", m_Socket->GetPort(), m_Clients.CountElements(), m_MaxClients); + return string; +} + +IObjectContainer *Proxy::GetClients() +{ + return &m_Clients; +} + +IWorld *Proxy::GetWorld() +{ + return m_World; +} + +IServer *Proxy::GetServer() +{ + return m_Server; +} + +IDirector *Proxy::GetDirector() +{ + return m_Director; +} + +INetSocket *Proxy::GetSocket() +{ + return m_Socket; +} + +ChatMode_e Proxy::GetChatMode() +{ + return m_ChatMode; +} + +void Proxy::UpdateStatusLine() +{ + float in, out; + char text[128]; + char activeTime[32]; + + strcopy(activeTime, COM_FormatTime((float)m_World->GetTime())); + + m_Network->GetFlowStats(&in, &out); + m_CurrentLoss = m_Server->GetPacketLoss(); + + _snprintf(text, sizeof(text), "%s, Time %s, Delay %.0f, FPS %.0f, Clients %i, In %.1f, Out %.1f, Loss %.2f", + IsMaster() ? "Master" : "Relay", activeTime, m_ClientDelay, m_FPS, m_Clients.CountElements(), in, out, m_CurrentLoss); + + m_System->SetStatusLine(text); + m_NextStatusUpdateTime = m_SystemTime + 0.25; +} + +void Proxy::CMD_Status(char *cmdLine) +{ + float in, out, loss; + m_Network->GetFlowStats(&in, &out); + loss = m_Server->GetPacketLoss(); + + m_System->Printf("--- HLTV Status ---\n"); + m_System->Printf("Online %s, FPS %.1f, Version %i (%s)\n", COM_FormatTime((float)m_System->GetTime()), m_FPS, COM_BuildNumber(), __isWindows ? "Win32" : "Linux"); + m_System->Printf("Local IP %s, Network In %.1f, Out %.1f, Loss %.2f\n", m_Network->GetLocalAddress()->ToString(), in, out, loss); + + int spectators, proxies; + CountLocalClients(spectators, proxies); + m_System->Printf("Local Slots %i, Spectators %i (max %i), Proxies %i\n", GetMaxClients(), spectators, m_MaxSeenClients, proxies); + + if (m_Server->IsConnected()) + { + int slots, maxspecs; + m_Status.GetGlobalStats(proxies, slots, spectators); + maxspecs = m_Status.GetMaxSpectatorNumber(); + m_System->Printf("Total Slots %i, Spectators %i (max %i), Proxies %i\n", slots, spectators, maxspecs, proxies); + + if (m_Server->IsDemoFile()) + { + m_System->Printf("Playing Demo File \"%s\"\n", m_Server->GetDemoFileName()); + } + else if (m_Server->IsGameServer()) + { + m_System->Printf("Connected to Game Server %s, Delay %.0f\n", m_Server->GetAddress()->ToString(), m_ClientDelay); + m_System->Printf("Server Name \"%s\"\n", m_Server->GetHostName()); + } + else if (m_Server->IsRelayProxy()) + { + m_System->Printf("Connected to HLTV Proxy %s\n", m_Server->GetAddress()->ToString()); + m_System->Printf("Proxy Name \"%s\"\n", m_Server->GetHostName()); + } + else + { + m_System->Printf("Not connected.\n"); + } + } + else + { + m_System->Printf("Not connected.\n"); + } + + if (m_World->IsActive()) + { + char activeTime[32]; + strcopy(activeTime, COM_FormatTime((float)m_World->GetTime())); + + const char *mapname = m_World->GetLevelName() + sizeof("maps/") - 1; // skip 'maps/' + m_System->Printf("Game Time %s, Mod \"%s\", Map \"%s\", Players %i\n", activeTime, m_World->GetGameDir(), mapname, m_World->GetNumPlayers()); + } + + if (m_DemoClient.IsActive()) + { + DemoFile *df = m_DemoClient.GetDemoFile(); + m_System->Printf("Recording to %s, Length %.1f sec.\n", df->GetFileName(), df->GetDemoTime()); + } +} + +void Proxy::CountLocalClients(int &spectators, int &proxies) +{ + spectators = 0; + proxies = 0; + + IClient *client = (IClient *)m_Clients.GetFirst(); + while (client) + { + if (client->GetClientType() == TYPE_PROXY) + proxies++; + else + spectators++; + + client = (IClient *)m_Clients.GetNext(); + } +} + +void Proxy::GetStatistics(int &proxies, int &slots, int &spectators) +{ + m_Status.GetGlobalStats(proxies, slots, spectators); +} + +int Proxy::GetMaxUpdateRate() +{ + return m_MaxUpdateRate; +} + +int Proxy::GetMaxRate() +{ + return m_MaxRate; +} + +bool Proxy::WriteSignonData(int type, BitBuffer *stream) +{ + if (type != TYPE_CLIENT && type != TYPE_DEMO) { + return false; + } + + if (m_SignonCommands[0]) { + stream->WriteByte(svc_stufftext); + stream->WriteString(COM_VarArgs("%s\n", m_SignonCommands)); + } + + float ex_interp = (1 / GetMaxUpdateRate()) + 0.05f; + stream->WriteByte(svc_stufftext); + stream->WriteString(COM_VarArgs("ex_interp %.2f\n", ex_interp)); + + stream->WriteByte(svc_timescale); + stream->WriteFloat(1); + + if (m_BannerTGA) { + DirectorCmd cmd; + cmd.SetBannerData(m_BannerTGA->szFileName); + cmd.WriteToStream(stream); + } + + return true; +} + +bool Proxy::IsPublicGame() +{ + return m_PublicGame; +} + +bool Proxy::IsPasswordProtected() +{ + return m_SpectatorPassword[0] ? true : false; +} + +bool Proxy::IsStressed() +{ + if (m_CurrentLoss > m_MaxLoss || m_FPS < 30) { + return true; + } + + return false; +} + +int Proxy::GetDispatchMode() +{ + return m_DispatchMode; +} + +char *Proxy::GetType() +{ + return PROXY_INTERFACE_VERSION; +} + +bool Proxy::IsLanOnly() +{ + return m_Master.m_NoMaster; +} + +bool Proxy::IsActive() +{ + if (!m_World) { + return false; + } + + return m_World->IsActive(); +} + +unsigned char Proxy::GetRegion() +{ + return m_Region; +} + +void Proxy::ReplyListen(NetAddress *to) +{ + m_Socket->OutOfBandPrintf(to, "%c 0.0.0.0\n", S2A_PROXY_LISTEN); +} + +void Proxy::Reset() +{ + m_Status.Reset(); + + m_MaxSeenClients = 0; + m_LastCheeringUpdate = 0; + m_CheeringPlayers = 0; +} + +void Proxy::ParseStatusMsg(BitBuffer *stream) +{ + m_Status.ParseStatusMsg(stream); +} + +void Proxy::ParseStatusReport(NetAddress *from, BitBuffer *stream) +{ + m_Status.ParseStatusReport(from, stream); +} + +void Proxy::Broadcast(byte *data, int length, int groupType, bool isReliable) +{ + IClient *client = (IClient *)m_Clients.GetFirst(); + while (client) + { + if (((groupType & GROUP_CLIENT) && client->GetClientType() == TYPE_CLIENT) + || ((groupType & GROUP_PROXY) && client->GetClientType() == TYPE_PROXY) + || ((groupType & GROUP_VOICE) && client->IsHearingVoices()) + || ((groupType & GROUP_CHAT) && client->HasChatEnabled())) + { + client->Send(data, length, isReliable); + } + + client = (IClient *)m_Clients.GetNext(); + } + + if (m_DemoClient.IsActive()) + { + if (groupType & GROUP_DEMO) { + m_DemoClient.Send(data, length, isReliable); + } + } +} + +void Proxy::CMD_Say(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() < 2) + { + m_System->Printf("Syntax: say \n"); + return; + } + + if (m_Server->IsConnected()) + { + char string[1024]; + _snprintf(string, sizeof(string), "say \"%s\"", params.GetRestOfLine(1)); + m_Server->SendStringCommand(string); + } +} + +void Proxy::CMD_MaxClients(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: maxclients \n"); + m_System->Printf("Current number of maximal clients is %i.\n", GetMaxClients()); + return; + } + + if (!SetMaxClients(atoi(params.GetToken(1)))) { + m_System->Printf("Allowed maximum number of local clients is %i.\n", MAX_PROXY_CLIENTS); + return; + } +} + +bool Proxy::SetMaxClients(int number) +{ + if (number < 0) { + m_MaxClients = 0; + return false; + } + + if (number > MAX_PROXY_CLIENTS) { + m_MaxClients = MAX_PROXY_CLIENTS; + return false; + } + + m_MaxClients = number; + return true; +} + +void Proxy::SetMaxLoss(float maxloss) +{ + m_MaxLoss = clamp(maxloss, 0.0f, 1.0f); +} + +int Proxy::GetMaxClients() +{ + return m_MaxClients; +} + +void Proxy::SetRegion(unsigned char region) +{ + m_Region = region; +} + +void Proxy::CMD_Delay(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: delay \n"); + m_System->Printf("Current spectator delay is %.1f seconds.\n", m_ClientDelay); + return; + } + + SetDelay(float(atof(params.GetToken(1)))); +} + +void Proxy::CMD_Stop(char *cmdLine) +{ + TokenLine params(cmdLine); + StopBroadcast(params.CountToken() > 1 ? params.GetRestOfLine(1) : "HLTV stopped."); + m_System->Printf("Disconnected and stoppted.\n"); +} + +void Proxy::CMD_Connect(char *cmdLine) +{ + NetAddress address; + TokenLine params(cmdLine); + if (!m_Network->ResolveAddress(params.GetToken(1), &address)) { + m_System->Printf("Error! HLTV Proxy::ConnectToServer: couldn't resolve server address.\n"); + return; + } + + if (!address.m_Port) { + address.SetPort_(atoi("27015")); + } + + Reset(); + m_Server->Connect(m_World, &address, m_Socket); +} + +void Proxy::CMD_Name(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() < 2) + { + m_System->Printf("Current name is \"%s\".\n", m_Server->GetPlayerName()); + return; + } + + char name[MAX_NAME]; + int len = strlen(params.GetToken(1)); + if (len > sizeof(name) - 1) { + m_System->Printf("Invalid name length.\n"); + return; + } + + strcopy(name, params.GetToken(1)); + m_Server->SetPlayerName(name); +} + +void Proxy::CMD_Msg(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() < 2) + { + m_System->Printf("Syntax: msg [ ]\n", m_Server->GetPlayerName()); + return; + } + + strcopy(m_LocalMessage.text, params.GetToken(1)); + + if (params.CountToken() == 6) + { + m_LocalMessage.holdtime = float(atof(params.GetToken(2))); + m_LocalMessage.x = float(atof(params.GetToken(3))); + m_LocalMessage.y = float(atof(params.GetToken(4))); + + sscanf(params.GetToken(5), "%2hhx%2hhx%2hhx%2hhx", &m_LocalMessage.r1, &m_LocalMessage.g1, &m_LocalMessage.b1, &m_LocalMessage.a1); + } + + BitBuffer buffer(144); + WriteHUDMsg(&m_LocalMessage, &buffer); + + Broadcast(buffer.GetData(), buffer.CurrentSize(), GROUP_CLIENT_ALL, false); +} + +void Proxy::CMD_Protocol(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: protocol <46|47>\n"); + m_System->Printf("Current protcol version is %i\n", m_Server->GetProtocol()); + return; + } + + if (!m_Server->SetProtocol(atoi(params.GetToken(1)))) { + m_System->Printf("Protocol version not supported!\n"); + return; + } +} + +void Proxy::CMD_MaxRate(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: maxrate \n"); + m_System->Printf("Current maximal client rate is %i bytes/sec.\n", m_MaxRate); + return; + } + + SetMaxRate(atoi(params.GetToken(1))); +} + +void Proxy::CMD_ServerCmd(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() < 2) + { + m_System->Printf("Syntax: servercmd \n"); + return; + } + + if (m_Server->IsConnected()) { + m_Server->SendStringCommand(params.GetRestOfLine(1)); + } +} + +void Proxy::CMD_ClientCmd(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() < 3) + { + m_System->Printf("Syntax: clientcmd \n"); + m_System->Printf("groups: 1 = spectators only, 2 = proxies only, 3 = all\n"); + return; + } + + int group = atoi(params.GetToken(1)); + char *cmdstring = params.GetRestOfLine(2); + if (strlen(cmdstring) > 100) { + m_System->Printf("ERROR! Command string too long.\n"); + return; + } + + switch (group) + { + case 1: // spectators + group = GROUP_CLIENT | GROUP_DEMO | GROUP_UNKNOWN; + break; + case 2: // proxies + group = GROUP_PROXY; + break; + default: // all + group = GROUP_CLIENT_ALL; + break; + } + + BitBuffer cmdbuf(128); + cmdbuf.WriteByte(svc_stufftext); + cmdbuf.WriteString(COM_VarArgs("%s\n", cmdstring)); + + Broadcast(cmdbuf.GetData(), cmdbuf.CurrentSize(), group, true); +} + +void Proxy::CMD_Rate(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: rate \n"); + m_System->Printf("Current maximal server to HLTV proxy rate is %i bytes/sec.\n", m_Server->GetRate()); + return; + } + + m_Server->SetRate(atoi(params.GetToken(1))); +} + +void Proxy::CMD_Players(char *cmdLine) +{ + int count = 0; + InfoString playerInfo(MAX_INFO_STRING); + for (int i = 0; i < m_World->GetMaxClients(); i++) + { + if (m_World->GetPlayerInfoString(i, &playerInfo)) { + m_System->Printf("#%2i \"%s\" %s\n", i + 1, playerInfo.ValueForKey("name"), playerInfo.ValueForKey("model")); + count++; + } + } + + m_System->Printf("--- Total %i Players ---\n", count); +} + +void Proxy::CMD_HostName(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Host name is \"%s\"\n", m_World->GetHostName()); + return; + } + + if (!_stricmp(params.GetToken(1), "none")) { + m_World->SetHostName(nullptr); + return; + } + + m_World->SetHostName(params.GetToken(1)); +} + +void Proxy::CMD_Updaterate(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: updaterate \n"); + m_System->Printf("Current update rate is %i packets/sec.\n", m_Server->GetUpdateRate()); + return; + } + + SetMaxUpdateRate(atoi(params.GetToken(1))); + m_Server->SetUpdateRate(m_MaxUpdateRate); +} + +void Proxy::CMD_BlockVoice(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: blockvoice <0|1>\n"); + m_System->Printf("Currently voice data is %s.\n", m_Server->IsVoiceBlocking() ? "blocked" : "relayed"); + return; + } + + m_Server->SetVoiceBlocking(atoi(params.GetToken(1)) ? true : false); +} + +void Proxy::CMD_Record(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: record \n"); + m_System->Printf("Output format is \"filename-date-map.dem\".\n"); + return; + } + + if (m_DemoClient.Connect()) { + m_DemoClient.SetFileName(params.GetToken(1)); + } +} + +void Proxy::CMD_StopRecording(char *cmdLine) +{ + if (m_DemoClient.IsActive()) { + m_DemoClient.Disconnect("End of Record"); + return; + } + + m_System->Printf("Not recording.\n"); +} + +void Proxy::CMD_LoopCmd(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() < 4) + { + m_System->Printf("Syntax: loopcmd \n"); + m_System->Printf("Currently %i looping commands in list.\n", m_LoopCommands.CountElements()); + return; + } + + int id = atoi(params.GetToken(1)); + float seconds = float(atof(params.GetToken(2))); + char *cmds = params.GetRestOfLine(3); + + loopcmd_t *lcmd = (loopcmd_t *)m_LoopCommands.GetFirst(); + while (lcmd) + { + if (lcmd->id == id) { + break; + } + + lcmd = (loopcmd_t *)m_LoopCommands.GetNext(); + } + + if (!_stricmp(cmds, "none") || !seconds) + { + if (lcmd) { + m_LoopCommands.Remove(lcmd); + return; + } + + m_System->Printf("Couldn't remove loop command %i\n", id); + return; + } + + if (!lcmd) + { + lcmd = (loopcmd_t *)Mem_ZeroMalloc(sizeof(loopcmd_t)); + lcmd->id = id; + + m_LoopCommands.Add(lcmd); + } + + lcmd->interval = seconds; + lcmd->lastTime = 0; + + strcopy(lcmd->command, cmds); +} + +void Proxy::CMD_RconAddress(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: rconaddress \n"); + m_System->Printf("Current remote console address: %s\n", m_RconAddress.ToString()); + return; + } + + m_Network->ResolveAddress(params.GetToken(1), &m_RconAddress); + if (!m_RconAddress.m_Port) { + m_RconAddress.SetPort_(atoi("27015")); + } +} + +void Proxy::CMD_RconPassword(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: rconpassword \n"); + return; + } + + strcopy(m_RconPassword, params.GetToken(1)); +} + +void Proxy::CMD_Rcon(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() < 2) + { + m_System->Printf("Syntax: rcon \n"); + return; + } + + if (!m_RconPassword[0]) { + m_System->Printf("Set 'rconpassword' before issuing a rcon command.\n"); + return; + } + + if (!m_RconAddress.IsValid() && m_Server->IsConnected()) { + m_RconAddress.FromNetAddress(m_Server->GetAddress()); + } + + if (!m_RconAddress.IsValid()) { + m_System->Printf("rconaddress not valid.\n"); + return; + } + + strcopy(m_LastRconCommand, params.GetRestOfLine(1)); + m_Socket->OutOfBandPrintf(&m_RconAddress, "challenge rcon\n"); +} + +void Proxy::SendRcon(NetAddress *to, unsigned int challenge) +{ + if (!m_LastRconCommand[0]) { + m_System->Printf("HLTV Proxy::SendRcon: Empty rcon string\n"); + return; + } + + if (!to->Equal(&m_RconAddress)) { + m_System->Printf("Unwanted rcon challenge reply from %s\n", to->ToString()); + return; + } + + m_Socket->OutOfBandPrintf(to, "rcon %u \"%s\" %s", challenge, m_RconPassword, m_LastRconCommand); +} + +unsigned int Proxy::GetChallengeNumber(NetAddress *host) +{ + int i; + int oldest = 0; + float oldestTime = 9.9999997e37f; + const float challengeLife = 40.0f; + + for (i = 0; i < MAX_CHALLENGES; i++) + { + if (m_Challenges[i].adr.Equal(host)) { + break; + } + + if (m_Challenges[i].time < oldestTime) + { + oldestTime = m_Challenges[i].time; + oldest = i; + } + } + + if (i == MAX_CHALLENGES) + { + m_Challenges[oldest].adr.FromNetAddress(host); + + // generate new challenge number + m_Challenges[oldest].challenge = (RandomLong(0, 0xFFFF) | RandomLong(0, 0xFFFF) << 16); + m_Challenges[oldest].time = float(m_SystemTime); + + i = oldest; + } + + if (m_SystemTime > m_Challenges[i].time + PROXY_CHALLENGE_LIFE) + { + // generate new challenge number + m_Challenges[i].challenge = (RandomLong(0, 0xFFFF) | RandomLong(0, 0xFFFF) << 16); + m_Challenges[i].time = float(m_SystemTime); + } + + return m_Challenges[i].challenge; +} + +bool Proxy::CheckChallenge(NetAddress *from, unsigned int challengeNumber) +{ + for (auto& it : m_Challenges) + { + if (it.adr.Equal(from)) { + if (it.challenge == challengeNumber) { + return (m_SystemTime - it.time <= 40); + } + + break; + } + } + + return false; +} + +void Proxy::ReplyServiceChallenge(NetAddress *to, char *type) +{ + unsigned int challengeNumber = GetChallengeNumber(to); + m_Socket->OutOfBandPrintf(to, "challenge %s %u\n", type, challengeNumber); +} + +void Proxy::CMD_ProxyPassword(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: proxypassword \n"); + return; + } + + if (!_stricmp(params.GetToken(1), "none")) { + m_ProxyPassword[0] = '\0'; + return; + } + + strcopy(m_ProxyPassword, params.GetToken(1)); +} + +void Proxy::NewServerConnection() +{ + m_IsMaster = m_Server->IsGameServer(); + + if (m_IsMaster && m_ClientDelay > 0) + { + m_World->SetBufferSize(m_ClientDelay + m_ClientDelay); + m_Server->SetDelayReconnect(true); + } + else + { + m_World->SetBufferSize(10); + m_Server->SetDelayReconnect(false); + } + + resource_t *resource = (resource_t *)m_Resources.GetFirst(); + while (resource) + { + m_World->AddResource(resource); + resource = (resource_t *)m_Resources.GetNext(); + } + + m_IsReconnectRequested = false; +} + +void Proxy::ReceiveSignal(ISystemModule *module, unsigned int signal, void *data) +{ + int from = module->GetSerial(); + + // Server module + if (from == m_Server->GetSerial()) + { + switch (signal) + { + case 4: + NewServerConnection(); + break; + case 5: + case 6: + m_IsFinishingBroadcast = true; + break; + case 7: + BroadcastRetryMessage(); + break; + default: + break; + } + + return; + } + + // World module + if (from == m_World->GetSerial()) + { + switch (signal) + { + case 2: + NewGameStarted(); + ReconnectClients(); + break; + case 5: + case 6: + BroadcastPaused(signal == 5 ? true : false); + break; + case 8: + StopBroadcast("HLTV shutddown."); + break; + default: + break; + } + + return; + } +} + +char *Proxy::GetModVersion(char *gamedir) +{ + static char version[] = PROXY_VERSION; + return version; +} + +void Proxy::BroadcastRetryMessage() +{ + BitBuffer msg(32); + msg.WriteByte(svc_centerprint); + msg.WriteString("Retrying HLTV connection ..."); + + Broadcast(msg.GetData(), msg.CurrentSize(), GROUP_CLIENT | GROUP_PROXY | GROUP_UNKNOWN, false); +} + +void Proxy::StopBroadcast(const char *message) +{ + DisconnectClients(message ? COM_VarArgs("%s\n", message) : "HLTV stopped.\n"); + + m_Server->Disconnect(); + m_Server->StopRetry(); + m_World->StopGame(); + + m_Master.SendShutdown(); + m_System->SetTitle("HLTV - offline"); +} + +void Proxy::ExecuteRcon(NetAddress *from, char *command) +{ + char outputbuf[1024]; + m_System->Printf("Executing rcon \"%s\" from %s.\n", command, from->ToString()); + + m_System->RedirectOutput(outputbuf + 1, sizeof(outputbuf) - 1); + m_System->ExecuteString(command); + m_System->RedirectOutput(); + + if (outputbuf[1]) { + m_Socket->OutOfBandPrintf(from, "%c%s", A2A_PRINT, outputbuf); + } +} + +bool Proxy::ProcessConnectionlessMessage(NetAddress *from, BitBuffer *stream) +{ + if (IsBanned(from)) { + return false; + } + + TokenLine cmdLine; + if (!cmdLine.SetLine(stream->ReadStringLine())) { + m_System->DPrintf("WARNING! HLTV Proxy::ProcessConnectionlessMessage: message too long.\n"); + return false; + } + + if (!cmdLine.CountToken()) { + m_System->DPrintf("WARNING! Invalid packet from %s.\n", from->ToString()); + return false; + } + + if (SteamGameServer()) + { + const int maxBuffer = 4096; + unsigned char data[maxBuffer]; + int maxSize = min(stream->m_MaxSize, maxBuffer); + + memcpy((char *)&data[4], stream->m_Data, maxSize); + *(uint32 *)data = CONNECTIONLESS_HEADER; // connectionless packet + + SteamGameServer()->HandleIncomingPacket(data, maxSize + 4, ntohl(*(u_long *)&from->m_IP[0]), htons(from->m_Port)); + } + + char *c = cmdLine.GetToken(0); + if (c[0] == S2C_CHALLENGE) + { + m_System->DPrintf("Unwanted challenge response from %s.\n", from->ToString()); + return false; + } + + if (!strcmp(c, "challenge")) + { + char *type = cmdLine.GetToken(1); + switch (cmdLine.CountToken()) + { + case 3: + { + if (type && type[0] && !_stricmp(type, "rcon")) + { + unsigned int challengeNr = strtoul(cmdLine.GetToken(2), 0, 10); + SendRcon(from, challengeNr); + break; + } + + m_System->Printf("Invalid challenge type (%s) from: %s\n", type ? type : "NULL", from->ToString()); + return false; + } + case 2: + ReplyServiceChallenge(from, type); + break; + default: + m_System->Printf("Invalid challenge request from: %s\n", from->ToString()); + return false; + } + } + else if (!strcmp(c, "rcon")) + { + if (cmdLine.CountToken() < 4) { + return false; + } + + unsigned int challenge = strtoul(cmdLine.GetToken(1), 0, 10); + char *password = cmdLine.GetToken(2); + char *command = cmdLine.GetRestOfLine(3); + + if (!CheckChallenge(from, challenge)) + { + m_System->Printf("Invalid rcon challenge from: %s\n", from->ToString()); + return false; + } + + if (!m_AdminPassword[0] || strcmp(m_AdminPassword, password) != 0) + { + m_System->Printf("Invalid rcon password from: %s\n", from->ToString()); + return false; + } + + ExecuteRcon(from, command); + } + else if (!strcmp(c, "getchallenge")) + { + ReplyChallenge(from); + } + else if (!strcmp(c, "connect")) + { + if (cmdLine.CountToken() == 5) + { + int protocol = atoi(cmdLine.GetToken(1)); + int challenge = atoi(cmdLine.GetToken(2)); + + char *protinfo = cmdLine.GetToken(3); + char *userinfo = cmdLine.GetToken(4); + + ReplyConnect(from, protocol, challenge, protinfo, userinfo); + } + else + { + RejectConnection(from, false, "Insufficient connection info\n"); + } + } + else if (c[0] == A2A_ACK && (c[1] == 0 || c[1] == '\n')) + { + m_System->DPrintf("Acknowledgment from %s.\n", from->ToString()); + } + else if (c[0] == A2S_INFO) + { + ReplyInfo(from, true); + } + else if (c[0] == A2S_PLAYER) + { + ReplyPlayers(from); + } + else if (c[0] == A2S_RULES) + { + ReplyRules(from); + } + else if (!strcmp(c, "listen")) + { + ReplyListen(from); + } + // guard to frequency queries + else if (m_MaxFrameQueries > 0) + { + if (!strcmp(c, "ping") || (c[0] == A2A_PING && (c[1] == 0 || c[1] == '\n'))) + { + ReplyPing(from); + } + else if (!_stricmp(c, "infostring")) + { + ReplyInfoString(from); + } + else if (!_stricmp(c, "info")) + { + ReplyInfo(from, false); + } + else if (!_stricmp(c, "details")) + { + ReplyInfo(from, true); + } + else if (!_stricmp(c, "players")) + { + ReplyPlayers(from); + } + else if (!_stricmp(c, "rules")) + { + ReplyRules(from); + } + else if (!_stricmp(c, "log")) + { + m_System->Printf("Ignoring log from %s.\n", from->ToString()); + return false; + } + else if (c[0] == A2A_PRINT) + { + stream->Reset(); + stream->SkipBytes(1); + + m_System->Printf(">%s\n", stream->ReadString()); + } + else + { + m_System->DPrintf("Proxy::ProcessConnectionlessMessage: unknown \"%s\" from %s \n", c, from->ToString()); + return false; + } + + m_MaxFrameQueries--; + } + + return true; +} + +void Proxy::CMD_ChatMode(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + static const char *chatModeString[] = { "OFF", "LOCAL", "GLOBAL" }; + + m_System->Printf("Syntax: chatmode <0|1|2>\n"); + m_System->Printf("Currently chat mode is %s.\n", chatModeString[ m_ChatMode ]); + return; + } + + m_ChatMode = clamp((ChatMode_e)atoi(params.GetToken(1)), CHAT_OFF, CHAT_GLOBAL); +} + +void Proxy::CMD_MaxQueries(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: maxqueries \n"); + m_System->Printf("Currently maximum %.0f queries per seconds accepted.\n", m_MaxQueries); + return; + } + + m_MaxQueries = float(atof(params.GetToken(1))); +} + +void Proxy::CMD_Kick(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: kick \n"); + return; + } + + int id = atoi(params.GetToken(1)); + + IClient *client = (IClient *)m_Clients.GetFirst(); + while (client) + { + if (client->GetSerial() == id) { + client->Disconnect("You have been kicked.\n"); + return; + } + + client = (IClient *)m_Clients.GetNext(); + } + + m_System->Printf("%i is not a valid client ID.\n", id); +} + +void Proxy::CMD_Disconnect(char *cmdLine) +{ + m_Server->Disconnect(); +} + +void Proxy::ChatSpectator(char *nick, char *text) +{ + if (!text || m_ChatMode == CHAT_OFF) { + return; + } + + int sayMsgNum = m_World->FindUserMsgByName("SayText"); + if (!sayMsgNum) { + return; + } + + char string[512] = ""; + COM_RemoveEvilChars(text); + + // TODO: too harsh, let more? maxlen: 190 + const int maxLengthOfString = 64; + int len = strlen(text); + if (len <= 0) { + return; + } + + if (len > maxLengthOfString) { + text[maxLengthOfString] = '\0'; + } + + if (m_ChatMode != CHAT_LOCAL && !m_IsMaster) + { + _snprintf(string, sizeof(string), "say \"%s\"", text); + if (m_Server->IsConnected()) { + m_Server->SendStringCommand(string); + } + + return; + } + + const int extraBytesOfEnd = 2; // extra of ends bytes \n + \0 + const int headerBytes = 3; // reserve a header of 3 bytes. + const int maxSizeOfMessage = MAX_USER_MSG_DATA - headerBytes; // user message size limit is 192 bytes + + _snprintf(&string[ headerBytes ], sizeof(string) - headerBytes, "<%s> %s", nick, text); + + int curLen = strlen(&string[ headerBytes ]); + if (curLen > maxSizeOfMessage) { + curLen = maxSizeOfMessage; + } + + // fill up to 3+ remaining bytes + string[0] = sayMsgNum; // unique id usermsg SayText. + string[1] = curLen + headerBytes; // the length of message. + string[2] = '\0'; // terminal null + + string[curLen + 3] = '\n'; + string[curLen + 4] = '\0'; + + int groupType; + if (m_ChatMode == CHAT_LOCAL) { + groupType = GROUP_CHAT; + } else { + groupType = GROUP_CHAT | GROUP_PROXY; + } + + Broadcast((byte *)string, curLen + headerBytes + extraBytesOfEnd, groupType, false); +} + +bool Proxy::IsMaster() +{ + return m_IsMaster; +} + +bool Proxy::CheckDirectorModule() +{ + char szAbsoluteLibFilename[MAX_PATH]; + if (m_Director && !strcmp(m_Director->GetModName(), m_World->GetGameDir())) { + return true; + } + + _snprintf(szAbsoluteLibFilename, sizeof(szAbsoluteLibFilename), "%s/dlls/director", m_World->GetGameDir()); + if (m_Director) { + m_System->RemoveModule(m_Director); + } + + m_Director = dynamic_cast(m_System->GetModule(DIRECTOR_INTERFACE_VERSION, szAbsoluteLibFilename, "director")); + if (m_Director) + { + m_System->Printf("Using extern director module (%s).\n", szAbsoluteLibFilename); + return true; + } + + // If we don't have director module, + // to set up on internal default director. + if (!m_System->AddModule(&m_DefaultDirector, "director")) { + m_System->Errorf("Proxy::CheckDirectorModule: failed to add internal director module.\n"); + return false; + } + + m_Director = &m_DefaultDirector; + m_System->DPrintf("Using internal default director.\n"); + + return true; +} + +void Proxy::ReconnectClients() +{ + IClient *client = (IClient *)m_Clients.GetFirst(); + while (client) + { + client->Reconnect(); + client = (IClient *)m_Clients.GetNext(); + } + + m_DemoClient.Reconnect(); +} + +void Proxy::CMD_OffLineText(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: offlinetext \n"); + m_System->Printf("Offline info text is \"%s\"\n", m_OffLineText); + return; + } + + strcopy(m_OffLineText, params.GetToken(1)); + COM_RemoveEvilChars(m_OffLineText); +} + +void Proxy::CMD_Resources(char *cmdLine) +{ + resource_t *resource = (resource_t *)m_Resources.GetFirst(); + while (resource) + { + m_System->Printf("File: \"%s\", Size: %i bytes.\n", resource->szFileName, resource->nDownloadSize); + resource = (resource_t *)m_Resources.GetNext(); + } + + m_System->Printf("--- Total %i Resources ---\n", m_Resources.CountElements()); +} + +void Proxy::CMD_AddResource(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() < 3) + { + m_System->Printf("Syntax: addresource []\n"); + return; + } + + if (!AddResource(params.GetToken(1), (resourcetype_t)atoi(params.GetToken(2)), params.GetToken(3))) + { + m_System->Printf("Error! Failed to load resource %s.\n", params.GetToken(1)); + return; + } +} + +void Proxy::CMD_PublicGame(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: publicgame <0|1>\n"); + m_System->Printf("Joingame is %s.\n", m_PublicGame ? "enabled" : "disabled"); + return; + } + + m_PublicGame = atoi(params.GetToken(1)) ? true : false; +} + +void Proxy::WriteHUDMsg(textmessage_t *msg, BitBuffer *stream) +{ + DirectorCmd cmd; + + vec3_t position; + position[0] = msg->x; + position[1] = msg->y; + position[2] = 0; + + cmd.SetMessageData( + msg->effect, + COM_PackRGB(msg->r1, msg->g1, msg->b1), + position, + msg->fadein, + msg->fadeout, + msg->holdtime, + msg->fxtime, + msg->text + ); + + cmd.WriteToStream(stream); +} + +void Proxy::CMD_SpectatorPassword(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: spectatorpassword \n"); + return; + } + + if (!_stricmp(params.GetToken(1), "none")) { + m_SpectatorPassword[0] = '\0'; + return; + } + + strcopy(m_SpectatorPassword, params.GetToken(1)); +} + +void Proxy::CMD_DispatchMode(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + static const char *dispatchModeString[] = { "OFF", "AUTO", "ALWAYS" }; + + m_System->Printf("Syntax: dispatchmode <0|1|2>\n"); + m_System->Printf("Spectator dispatch mode is: %s.\n", dispatchModeString[ m_DispatchMode ]); + return; + } + + m_DispatchMode = clamp((DispatchMode_e)atoi(params.GetToken(1)), DISPATCH_OFF, DISPATCH_ALL); +} + +bool Proxy::IsValidPassword(int type, char *pw) +{ + switch (type) + { + // spec password + case TYPE_CLIENT: + { + if (m_SpectatorPassword[0]) { + return strcmp(m_SpectatorPassword, pw) == 0; + } + + return true; + } + // proxy password + case TYPE_PROXY: + { + if (m_ProxyPassword[0]) { + return strcmp(m_ProxyPassword, pw) == 0; + } + + // password is not set + return true; + } + // admin password + case TYPE_COMMENTATOR: + { + if (m_AdminPassword[0]) { + return strcmp(m_AdminPassword, pw) == 0; + } + + break; + } + default: + break; + } + + return false; +} + +void Proxy::CMD_PlayDemo(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() < 2) + { + m_System->Printf("Sytax: playdemo \n"); + return; + } + + m_Server->LoadDemo(m_World, params.GetToken(1), true, true); + m_World->SetBufferSize(10); +} + +void Proxy::DispatchClient(NetAddress *client, NetAddress *proxy) +{ + BitBuffer buf(128); + + buf.WriteLong(CONNECTIONLESS_HEADER); // connectionless header + buf.WriteByte(S2A_PROXY_REDIRECT); // data header + buf.WriteString(proxy->ToString()); // ip proxy destination + + m_Socket->SendPacket(client, buf.GetData(), buf.CurrentSize()); + m_System->DPrintf("Dispatched client %s to proxy %s.\n", client->ToString(), proxy->ToString()); +} + +resource_t *Proxy::LoadResourceFromFile(char *fileName, resourcetype_t type) +{ + resource_t *newresource = (resource_t *)Mem_ZeroMalloc(sizeof(resource_t)); + + strcopy(newresource->szFileName, fileName); + + newresource->type = type; + newresource->data = m_System->LoadFile(newresource->szFileName, &newresource->nDownloadSize); + if (newresource->data && newresource->nDownloadSize >= 0) { + MD5_Hash_Mem(newresource->rgucMD5_hash, newresource->data, newresource->nDownloadSize); + return newresource; + } + + m_System->Printf("WARNING! Failed to load resource file %s.\n", fileName); + free(newresource); + return nullptr; +} + +void Proxy::FreeResource(resource_t *resource) +{ + if (!resource) { + return; + } + + if (resource->data) { + m_System->FreeFile(resource->data); + } + + free(resource); +} + +void Proxy::ClearResources() +{ + resource_t *resource; + while ((resource = (resource_t *)m_Resources.RemoveHead())) { + FreeResource(resource); + } +} + +resource_t *Proxy::AddResource(char *fileName, resourcetype_t type, char *asFileName) +{ + resource_t *resource = GetResource(fileName); + if (resource) { + FreeResource(resource); + m_Resources.Remove(resource); + } + + resource = LoadResourceFromFile(fileName, t_generic /* type */); // TODO: why we use only t_generic? + if (resource) + { + if (asFileName) { + strcopy(resource->szFileName, asFileName); + } + + m_Resources.Add(resource); + return resource; + } + + return nullptr; +} + +resource_t *Proxy::GetResource(char *fileName) +{ + resource_t *resource = (resource_t *)m_Resources.GetFirst(); + while (resource) + { + if (!_strnicmp(fileName, resource->szFileName, sizeof(resource->szFileName))) { + return resource; + } + + resource = (resource_t *)m_Resources.GetNext(); + } + + return nullptr; +} + +void Proxy::CMD_SignOnCommands(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: signoncommands \n"); + m_System->Printf("Current sign on commands: \"%s\"\n", m_SignonCommands); + return; + } + + if (strlen(params.GetToken(1)) > 250) + { + m_System->Printf("Error! String too long (>250)!\n"); + return; + } + + strcopy(m_SignonCommands, params.GetToken(1)); +} + +void Proxy::CMD_AdminPassword(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: adminpassword \n"); + return; + } + + if (!_stricmp(params.GetToken(1), "none")) { + m_AdminPassword[0] = '\0'; + return; + } + + strcopy(m_AdminPassword, params.GetToken(1)); +} + +void Proxy::CMD_LocalMsg(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() < 2) + { + m_System->Printf("Syntax: localmsg [ ]\n"); + return; + } + + strcopy(m_LocalMessage.text, params.GetToken(1)); + + if (params.CountToken() == 6) + { + m_LocalMessage.holdtime = float(atof(params.GetToken(2))); + m_LocalMessage.x = float(atof(params.GetToken(3))); + m_LocalMessage.y = float(atof(params.GetToken(4))); + + sscanf(params.GetToken(5), "%2hhx%2hhx%2hhx%2hhx", &m_LocalMessage.r1, &m_LocalMessage.g1, &m_LocalMessage.b1, &m_LocalMessage.a1); + } + + BitBuffer buffer(144); + WriteHUDMsg(&m_LocalMessage, &buffer); + + Broadcast(buffer.GetData(), buffer.CurrentSize(), GROUP_CLIENT | GROUP_DEMO, false); +} + +void Proxy::ChatCommentator(char *nick, char *text) +{ + BitBuffer buffer(144); + + strcopy(m_CommentatorMessage.text, text); + + COM_RemoveEvilChars(m_CommentatorMessage.text); + WriteHUDMsg(&m_CommentatorMessage, &buffer); + + Broadcast(buffer.GetData(), buffer.CurrentSize(), GROUP_CLIENT_ALL, true); +} + +bool Proxy::IsBanned(NetAddress *adr) +{ + NetAddress *bannedAdr = (NetAddress *)m_BannList.GetFirst(); + while (bannedAdr) + { + if (adr->EqualBase(bannedAdr)) { + return bannedAdr != nullptr; + } + + bannedAdr = (NetAddress *)m_BannList.GetNext(); + } + + return false; +} + +void Proxy::CMD_AddFakeClients(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 5) + { + m_System->Printf("Syntax: addfakeclients \n"); + return; + } + + int number = atoi(params.GetToken(1)); + + NetAddress adr; + m_Network->ResolveAddress(params.GetToken(2), &adr); + + int rate = atoi(params.GetToken(3)); + float activity = float(atof(params.GetToken(4))); + + for (int i = 0; i < number; i++) + { + FakeClient *fakeclient = new FakeClient; + if (!m_System->AddModule(fakeclient, "")) { + m_System->Errorf("AddFakeClients: failed to add module.\n"); + delete fakeclient; + break; + } + + fakeclient->SetRate(rate); + fakeclient->Connect(&adr); + } +} + +void Proxy::CMD_MaxLoss(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: maxloss \n"); + m_System->Printf("Current server packet loss limit is %.2f.\n", m_MaxLoss); + return; + } + + SetMaxLoss(float(atof(params.GetToken(1)))); +} + +void Proxy::CMD_Region(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: region \n"); + m_System->Printf("Current server region is %i.\n", m_Region); + return; + } + + SetRegion(atoi(params.GetToken(1))); +} + +void Proxy::CMD_Bann(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: bann \n"); + m_System->Printf("Currently %i IPs banned\n", m_BannList.CountElements()); + return; + } + + NetAddress *adr = (NetAddress *)Mem_ZeroMalloc(sizeof(NetAddress)); + if (!(m_Network->ResolveAddress(params.GetToken(1), adr))) + { + m_System->Printf("Couldn't resolve IP \x02%s\"\n", params.GetToken(1)); + free(adr); + return; + } + + if (IsBanned(adr)) { + m_System->Printf("IP already banned.\n"); + free(adr); + return; + } + + m_BannList.Add(adr); +} + +void Proxy::CMD_ClearBanns(char *cmdLine) +{ + m_System->Printf("Clearing IP bann list (%i entries).\n", m_BannList.CountElements()); + m_BannList.Clear(true); +} + +void Proxy::CMD_ServerPassword(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() < 2) + { + m_System->Printf("Sytax: serverpassword \n"); + return; + } + + m_Server->SetUserInfo("password", params.GetToken(1)); +} + +void Proxy::CMD_Retry(char *cmdLine) +{ + m_System->Printf("Retrying connection...\n"); + m_Server->Retry(); +} + +void Proxy::CMD_AutoRetry(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: autoretry <0|1>\n"); + m_System->Printf("Automatic connection retry is %s.\n", m_Server->GetAutoRetry() ? "enabled" : "disabled"); + return; + } + + m_Server->SetAutoRetry(atoi(params.GetToken(1)) ? true : false); +} + +void Proxy::CMD_BannerFile(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: bannerfile \n"); + if (m_BannerTGA) { + m_System->Printf("Current HLTV banner file is %s\n", m_BannerTGA->szFileName); + return; + } + + m_System->Printf("No HLTV banner file specified.\n"); + return; + } + + if (!_stricmp(params.GetToken(1), "none")) { + m_BannerTGA = nullptr; + return; + } + + if (!(m_BannerTGA = AddResource(params.GetToken(1), t_generic))) { + m_System->Printf("ERROR! Couldn't load banner file.\n"); + return; + } + + _snprintf(m_BannerTGA->szFileName, sizeof(m_BannerTGA->szFileName), "gfx/temp/%s.tga", COM_BinPrintf(m_BannerTGA->rgucMD5_hash, sizeof(m_BannerTGA->rgucMD5_hash))); +} + +void Proxy::CMD_CheeringThreshold(char *cmdLine) +{ + TokenLine params(cmdLine); + if (params.CountToken() != 2) + { + m_System->Printf("Syntax: cheeringthreshold \n"); + m_System->Printf("Cheering threshold is %.2f.\n", m_CheeringThreshold); + return; + } + + m_CheeringThreshold = float(atof(params.GetToken(1))); +} + +float Proxy::GetDelay() +{ + if (m_IsMaster) { + return m_ClientDelay; + } + + return 0; +} + +double Proxy::GetSpectatorTime() +{ + return m_ClientWorldTime; +} + +double Proxy::GetProxyTime() +{ + return m_ClientProxyTime; +} + +void Proxy::IncreaseCheering(int votes) +{ + m_CheeringPlayers += votes; + float fraction = (float)(m_CheeringPlayers / m_Clients.CountElements()); + if (fraction > 1) { + fraction = 1; + } + + if (fraction > m_CheeringThreshold) + { + DirectorCmd cmd; + BitBuffer buf(64); + + cmd.SetSoundData("ambience/goal_1.wav", fraction); + cmd.WriteToStream(&buf); + + Broadcast(buf.GetData(), buf.CurrentSize(), GROUP_CLIENT | GROUP_DEMO, true); + m_CheeringPlayers = 0; + } +} + +void Proxy::ExecuteLoopCommands() +{ + static float nextCheck = 0.0f; + if (nextCheck > m_SystemTime) { + return; + } + + loopcmd_t *lcmd = (loopcmd_t *)m_LoopCommands.GetFirst(); + while (lcmd) + { + if (m_SystemTime > (lcmd->lastTime + lcmd->interval)) { + m_System->ExecuteString(lcmd->command); + lcmd->lastTime = float(m_SystemTime); + } + + lcmd = (loopcmd_t *)m_LoopCommands.GetNext(); + } + + nextCheck = float(m_SystemTime + 0.5f); +} + +void Proxy::CreateServerInfoString(InfoString *info) +{ + int proxies, slots, spectators; + m_Status.GetLocalStats(proxies, slots, spectators); + + char address[256]; + _snprintf(address, sizeof(address), "%s", m_Network->GetLocalAddress()->ToString()); + + info->SetValueForKey("protocol", COM_VarArgs("%i", PROTOCOL_VERSION)); + info->SetValueForKey("address", address); + info->SetValueForKey("players", COM_VarArgs("%i", spectators)); + info->SetValueForKey("proxy", COM_VarArgs("%i", IsMaster() ? 1 : 2)); + + if (IsPublicGame()) { + info->SetValueForKey("proxyaddress", m_World->GetGameServerAddress()->ToString()); + } + + info->SetValueForKey("lan", COM_VarArgs("%i", IsLanOnly())); + info->SetValueForKey("max", COM_VarArgs("%i", slots)); + if (m_World->IsActive()) + { + char gameDir[MAX_PATH], mapName[MAX_PATH]; + COM_FileBase(m_World->GetLevelName(), mapName); + COM_FileBase(m_World->GetGameDir(), gameDir); + + serverinfo_t *serverInfo = m_World->GetServerInfo(); + info->SetValueForKey("gamedir", gameDir); + info->SetValueForKey("description", serverInfo->description); + info->SetValueForKey("hostname", m_World->GetHostName()); + info->SetValueForKey("map", mapName); + + if (serverInfo->mod) + { + info->SetValueForKey("mod", "1"); + info->SetValueForKey("modversion", COM_VarArgs("%i", serverInfo->ver)); + } + } + else + { + info->SetValueForKey("gamedir", "valve"); + info->SetValueForKey("description", "HLTV"); + info->SetValueForKey("hostname", m_OffLineText); + info->SetValueForKey("map", "none"); + } + + info->SetValueForKey("type", GetServerType(HLST_TV)); + info->SetValueForKey("password", COM_VarArgs("%i", IsPasswordProtected())); + info->SetValueForKey("os", GetServerOS()); + info->SetValueForKey("secure", "0"); +} + +void Proxy::SetMaxRate(int rate) +{ + // maxrate: 1.000 - 20.000 + m_MaxRate = clamp(rate, 1000, MAX_PROXY_RATE); +} + +void Proxy::SetMaxUpdateRate(int updaterate) +{ + // maxupdaterate: 1.0 - 40.0 + m_MaxUpdateRate = clamp(updaterate, 1, MAX_PROXY_UPDATERATE); +} + +void Proxy::SetDelay(float seconds) +{ + m_ClientDelay = seconds; + + if (seconds < 10) + { + m_ClientDelay = 0; + m_World->SetBufferSize(10); + } + else + { + m_World->SetBufferSize(seconds + seconds); + m_ClientWorldTime = m_World->GetTime() - m_ClientDelay; + } + + m_Server->SetUserInfo("hdelay", COM_VarArgs("%u", (int)m_ClientDelay)); +} + +void Proxy::SetClientTime(double time, bool relative) +{ + if (relative) { + m_ClientWorldTime += time; + return; + } + + m_ClientWorldTime = time; +} + +void Proxy::SetClientTimeScale(float scale) +{ + BitBuffer buf(32); + m_ClientTimeScale = clamp(scale, 4.0f, 0.5f); + + buf.WriteByte(svc_timescale); + buf.WriteFloat(m_ClientTimeScale); + + Broadcast(buf.GetData(), buf.CurrentSize(), GROUP_CLIENT_ALL, true); +} + +void Proxy::BroadcastPaused(bool paused) +{ + BitBuffer buf(32); + buf.WriteByte(svc_centerprint); + buf.WriteString(paused ? "Game was paused" : "Game was unpaused"); + + buf.WriteByte(svc_setpause); + buf.WriteByte(paused ? 1 : 0); + + Broadcast(buf.GetData(), buf.CurrentSize(), GROUP_CLIENT_ALL, true); +} + +void Proxy::NewGameStarted() +{ + m_LastClockUpdateTime = m_SystemTime; + m_IsFinishingBroadcast = false; + + m_ClientWorldTime = 0; + m_ClientProxyTime = 0; + m_ClientTimeScale = 1; + + m_CurrentLoss = 0; + + if (m_Server->IsDemoFile()) + { + if (m_Director) { + m_System->RemoveModule(m_Director); + m_Director = nullptr; + } + } + else if (GetDelay() > 0) + { + if (CheckDirectorModule()) { + m_Director->NewGame(m_World, this); + } + + m_ClientWorldTime = m_World->GetTime() - m_ClientDelay; + } + else + { + if (m_Director) { + m_System->RemoveModule(m_Director); + m_Director = nullptr; + } + + if (m_IsMaster) + { + if (m_System->AddModule(&m_NullDirector, "nulldirector")) { + m_Director = &m_NullDirector; + m_NullDirector.NewGame(m_World, this); + m_System->DPrintf("Using zero delay director.\n"); + } + else { + m_System->Errorf("Proxy::NewGameStarted: failed to add null director module.\n"); + } + } + } + + m_Server->SetDirector(m_Director); + + char newTitle[4096]; + _snprintf(newTitle, sizeof(newTitle), "HLTV - %s %s", + m_Server->IsDemoFile() ? m_Server->GetDemoFileName() : m_Server->GetAddress()->ToString(), + m_World->GetLevelName()); + + m_System->SetTitle(newTitle); +} + +void Proxy::RunClocks() +{ + double lastClockTime = m_LastClockUpdateTime; + m_LastClockUpdateTime = m_SystemTime; + + if (m_World->IsPaused()) { + return; + } + + double realTimeDiff = (m_SystemTime - lastClockTime) * m_ClientTimeScale; + m_ClientProxyTime += realTimeDiff; + m_ClientWorldTime += realTimeDiff; + + if (!m_IsFinishingBroadcast) + { + frame_t *start = m_World->GetFirstFrame(); + if (m_ClientWorldTime > m_World->GetTime()) { + m_ClientWorldTime = m_World->GetTime() - m_ClientDelay; + return; + } + + if (start->time > (m_ClientWorldTime + m_ClientDelay)) + { + m_System->DPrintf("Proxy::RunClocks: forcing client delay (1).\n"); + m_ClientWorldTime = start->time - m_ClientDelay; + return; + } + + if (m_World->GetTime() - m_ClientDelay > (m_ClientWorldTime + 10)) { + m_System->DPrintf("Proxy::RunClocks: forcing client delay (2).\n"); + m_ClientWorldTime = m_World->GetTime() - m_ClientDelay; + return; + } + } +} + +void Proxy::DisconnectClients(const char *reason) +{ + IClient *client = (IClient *)m_Clients.GetFirst(); + while (client) + { + client->Disconnect(reason); + client = (IClient *)m_Clients.GetNext(); + } + + m_DemoClient.Disconnect(reason); +} + +void Proxy::UpdateInfoMessages() +{ + char ipport[32]; + int proxies, slots, spectators; + serverinfo_t *serverInfo = m_World->GetServerInfo(); + + m_Status.GetLocalStats(proxies, slots, spectators); + _snprintf(ipport, sizeof(ipport), "%s:%i", m_Network->GetLocalAddress()->ToBaseString(), m_Socket->GetPort()); + + m_InfoInfo.Clear(); + if (m_World->IsActive()) + { + // deprecated goldsrc response + m_InfoInfo.WriteLong(CONNECTIONLESS_HEADER); // connectionless header + m_InfoInfo.WriteByte(S2A_INFO); // data header + m_InfoInfo.WriteString(ipport); // server address + m_InfoInfo.WriteString(m_World->GetHostName()); // hostname + m_InfoInfo.WriteString(serverInfo->map); // mapname + m_InfoInfo.WriteString(serverInfo->gamedir); // gamedir + m_InfoInfo.WriteString(serverInfo->description); // gamename + m_InfoInfo.WriteByte(min(spectators, MAX_PROXY_CLIENTS)); // players + m_InfoInfo.WriteByte(min(slots, MAX_PROXY_CLIENTS)); // maxplayers + m_InfoInfo.WriteByte(PROTOCOL_VERSION); // protocol + + if (spectators >= MAX_PROXY_CLIENTS || slots >= MAX_PROXY_CLIENTS) + { + m_InfoInfo.WriteLong(spectators); + m_InfoInfo.WriteLong(slots); + } + } + + m_InfoDetails.Clear(); + if (m_World->IsActive()) + { + m_InfoDetails.WriteLong(CONNECTIONLESS_HEADER); // connectionless header + m_InfoDetails.WriteByte(S2A_INFO_DETAILED); // data header (detailed mode) + m_InfoDetails.WriteString(ipport); // server address + m_InfoDetails.WriteString(m_World->GetHostName()); // hostname + m_InfoDetails.WriteString(serverInfo->map); // mapname + m_InfoDetails.WriteString(serverInfo->gamedir); // gamedir + m_InfoDetails.WriteString(serverInfo->description); // gamename + m_InfoDetails.WriteByte(min(spectators, MAX_PROXY_CLIENTS)); // players + m_InfoDetails.WriteByte(min(slots, MAX_PROXY_CLIENTS)); // maxplayers + m_InfoDetails.WriteByte(PROTOCOL_VERSION); // protocol + + m_InfoDetails.WriteByte(GetServerType(HLST_TV)[0]); // server type (Indicates the type of server: HLTV Server) + m_InfoDetails.WriteByte(GetServerOS()[0]); // server os (Indicates the operating system of the server) + m_InfoDetails.WriteByte(IsPasswordProtected()); // password + + // whether the game is a mod + // 0: Half-Life + // 1: Half-Life mod + m_InfoDetails.WriteByte(serverInfo->mod); // game is mod + + if (serverInfo->mod) + { + m_InfoDetails.WriteString(serverInfo->url_info); // url to mod website + m_InfoDetails.WriteString(serverInfo->url_dl); // url to download the mod + m_InfoDetails.WriteString(serverInfo->hlversion); + m_InfoDetails.WriteLong(serverInfo->ver); // version of mod installed on server + m_InfoDetails.WriteLong(serverInfo->size); // space (in bytes) the mod takes up + m_InfoDetails.WriteByte(serverInfo->svonly); // the type of mod + m_InfoDetails.WriteByte(serverInfo->cldll); // whether mod uses its own DLL + } + + m_InfoDetails.WriteByte(0); // specifies whether the server uses VAC + + if (spectators >= MAX_PROXY_CLIENTS || slots >= MAX_PROXY_CLIENTS) + { + m_InfoDetails.WriteLong(spectators); + m_InfoDetails.WriteLong(slots); + } + } + + m_InfoRules.Clear(); + if (m_World->IsActive()) + { + m_InfoRules.WriteLong(CONNECTIONLESS_HEADER); // connectionless header + m_InfoRules.WriteByte(S2A_RULES); // rules query are multi-packeted + m_InfoRules.WriteShort(5); // number of rules in the response + + // HLTV Rules + m_InfoRules.WriteString("GameServer"); + m_InfoRules.WriteString(GetDescription()); + + m_InfoRules.WriteString("HLTVProxy"); + m_InfoRules.WriteString(COM_VarArgs("%i", m_IsMaster ? 1 : 2)); + + m_InfoRules.WriteString("HLTVDelay"); + m_InfoRules.WriteString(COM_VarArgs("%.1f", m_ClientDelay)); + + m_InfoRules.WriteString("HLTVChat"); + m_InfoRules.WriteString(COM_VarArgs("%i", m_ChatMode)); + + m_InfoRules.WriteString("HLTVDemo"); + m_InfoRules.WriteString(m_DemoClient.IsActive() ? m_DemoClient.GetDemoFile()->GetFileName() : "none"); + } + + m_InfoPlayers.Clear(); + + InfoString info(2048); + if (m_World->GetNumPlayers() > 0) + { + m_InfoPlayers.WriteLong(CONNECTIONLESS_HEADER); // connectionless header + m_InfoPlayers.WriteByte(S2A_PLAYERS); // data header (players info) + m_InfoPlayers.WriteByte(0); // number of players whose information was gathered + + int count = 0; + for (int i = 0; i < m_World->GetMaxClients(); i++) + { + if (!m_World->GetPlayerInfoString(i, &info)) { + continue; + } + + m_InfoPlayers.WriteByte(++count); // index of player chunk starting from 0 + m_InfoPlayers.WriteString(info.ValueForKey("name")); // name of the player. + m_InfoPlayers.WriteLong(0); // player's score (usually "frags" or "kills") + m_InfoPlayers.WriteFloat(1); // time (in seconds) player has been connected to the server + } + + m_InfoPlayers.m_Data[5] = count; + } + + CreateServerInfoString(&info); + + m_InfoString.Clear(); + m_InfoString.WriteLong(CONNECTIONLESS_HEADER); // connectionless header + m_InfoString.WriteString("infostringresponse"); // data header (response on infostring) + m_InfoString.WriteString(info.GetString()); // info + + m_NextInfoMessagesUpdate = m_SystemTime + 1; +} + +void Proxy::SetName(char *newName) +{ + strcopy(m_Name, newName); +} + +const char *Proxy::GetDescription() +{ + if (m_Server->IsDemoFile()) { + return m_Server->GetDemoFileName(); + } + + if (IsPublicGame()) { + return m_World->GetGameServerAddress()->ToString(); + } + + return "Private Server"; +} diff --git a/rehlds/HLTV/Proxy/src/Proxy.h b/rehlds/HLTV/Proxy/src/Proxy.h new file mode 100644 index 0000000..2471bec --- /dev/null +++ b/rehlds/HLTV/Proxy/src/Proxy.h @@ -0,0 +1,377 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "BaseSystemModule.h" +#include "common/NetAddress.h" + +#include "ObjectList.h" +#include "DemoClient.h" +#include "Status.h" +#include "Master.h" +#include "Director.h" +#include "DirectorNull.h" + +class IWorld; +class IServer; +class INetwork; +class IDirector; +class IBaseSystem; + +#define MAX_NAME 32 +#define MAX_PROXY_RATE 20000 +#define MAX_PROXY_UPDATERATE 40 + +#define PROXY_CHALLENGE_LIFE 40.0f +#define PROXY_PRIVATE 0x8000 +#define PROXY_VERSION "1.1.2.0" +#define PROXY_DEFAULT_PORT "27020" + +// TODO: it is necessary to unify with rehlsdk +typedef struct textmessage_s { + int effect; + byte r1, g1, b1, a1; // 2 colors for effects + byte r2, g2, b2, a2; + float x; + float y; + float fadein; + float fadeout; + float holdtime; + float fxtime; + char text[80]; +} textmessage_t; + +class Proxy: public IProxy, public BaseSystemModule { +public: + Proxy() {} + virtual ~Proxy() {} + + bool Init(IBaseSystem *system, int serial, char *name); + void RunFrame(double time); + void ReceiveSignal(ISystemModule *module, unsigned int signal, void *data); + void ExecuteCommand(int commandID, char *commandLine); + char *GetStatusLine(); + char *GetType(); + void ShutDown(); + + void Reset(); + void Broadcast(byte *data, int length, int groupType, bool isReliable); + void IncreaseCheering(int votes); + void ChatCommentator(char *nick, char *text); + void ChatSpectator(char *nick, char *text); + void CountLocalClients(int &spectators, int &proxies); + void ParseStatusMsg(BitBuffer *stream); + void ParseStatusReport(NetAddress *from, BitBuffer *stream); + bool ProcessConnectionlessMessage(NetAddress *from, BitBuffer *stream); + resource_t *AddResource(char *fileName, resourcetype_t type, char *asFileName = nullptr); + bool IsMaster(); + bool IsLanOnly(); + bool IsActive(); + bool IsPublicGame(); + bool IsPasswordProtected(); + bool IsStressed(); + void SetDelay(float seconds); + void SetClientTime(double time, bool relative); + void SetClientTimeScale(float scale); + void SetMaxRate(int rate); + void SetMaxLoss(float maxloss); + void SetMaxUpdateRate(int updaterate); + bool SetMaxClients(int number); + void SetRegion(unsigned char region); + float GetDelay(); + double GetSpectatorTime(); + double GetProxyTime(); + IObjectContainer *GetClients(); + IWorld *GetWorld(); + IServer *GetServer(); + IDirector *GetDirector(); + INetSocket *GetSocket(); + ChatMode_e GetChatMode(); + void GetStatistics(int &proxies, int &slots, int &spectators); + int GetMaxRate(); + int GetMaxClients(); + int GetMaxUpdateRate(); + resource_t *GetResource(char *fileName); + int GetDispatchMode(); + unsigned char GetRegion(); + bool WriteSignonData(int type, BitBuffer *stream); + + void ReconnectClients(); + void ExecuteRcon(NetAddress *from, char *command); + void SendRcon(NetAddress *to, unsigned int challenge); + void ReplyServiceChallenge(NetAddress *to, char *type); + void ReplyListen(NetAddress *to); + void RejectConnection(NetAddress *adr, bool badPassword, const char *fmt, ...); + void ReplyConnect(NetAddress *to, int protocol, int challenge, char *protinfo, char *userinfo); + void ReplyRules(NetAddress *to); + void ReplyPlayers(NetAddress *to); + void ReplyInfo(NetAddress *to, bool detailed); + void ReplyInfoString(NetAddress *to); + void ReplyChallenge(NetAddress *to); + void ReplyPing(NetAddress *to); + + void UpdateStatusLine(); + void DispatchClient(NetAddress *client, NetAddress *proxy); + bool IsValidPassword(int type, char *pw); + void WriteHUDMsg(textmessage_t *msg, BitBuffer *stream); + + typedef struct loopcmd_s { + int id; + float interval; + float lastTime; + char command[255]; + } loopcmd_t; + + void ExecuteLoopCommands(); + unsigned int GetChallengeNumber(NetAddress *host); + bool CheckChallenge(NetAddress *from, unsigned int challengeNumber); + void CreateServerInfoString(InfoString *info); + bool CheckDirectorModule(); + void RunClocks(); + void NewGameStarted(); + void NewServerConnection(); + void BroadcastPaused(bool paused); + void BroadcastRetryMessage(); + void StopBroadcast(const char *message); + char *GetModVersion(char *gamedir); + void DisconnectClients(const char *reason); + void FreeResource(resource_t *resource); + void ClearResources(); + resource_t *LoadResourceFromFile(char *fileName, resourcetype_t type); + bool IsBanned(NetAddress *adr); + void UpdateInfoMessages(); + void SetName(char *newName); + +private: + enum LocalCommandIDs { + CMD_ID_RCON = 1, + CMD_ID_RCONPASSWORD, + CMD_ID_RCONADDRESS, + CMD_ID_SAY, + CMD_ID_MSG, + CMD_ID_CLIENTS, + CMD_ID_KICK, + CMD_ID_CHATMODE, + CMD_ID_PUBLICGAME, + CMD_ID_OFFLINETEXT, + CMD_ID_ADMINPASSWORD, + CMD_ID_SIGNONCOMMANDS, + CMD_ID_SPECTATORPASSWORD, + CMD_ID_DISPATCHMODE, + CMD_ID_CHEERINGTHRESHOLD, + CMD_ID_INFORMPLAYERS, + CMD_ID_PING, + CMD_ID_PROXYPASSWORD, + CMD_ID_MAXRATE, + CMD_ID_LOOPCMD, + CMD_ID_MAXCLIENTS, + CMD_ID_DISCONNECT, + CMD_ID_LOCALMSG, + CMD_ID_CONNECT, + CMD_ID_PLAYDEMO, + CMD_ID_DELAY, + CMD_ID_STOP, + CMD_ID_RECORD, + CMD_ID_STOPRECORDING, + CMD_ID_SERVERCMD, + CMD_ID_CLIENTCMD, + CMD_ID_BLOCKVOICE, + CMD_ID_NAME, + CMD_ID_UPDATERATE, + CMD_ID_RATE, + CMD_ID_ADDRESOURCE, + CMD_ID_RESOURCES, + CMD_ID_BANNERFILE, + CMD_ID_BANN, + CMD_ID_ADDFAKECLIENTS, + CMD_ID_RETRY, + CMD_ID_PLAYERS, + CMD_ID_AUTORETRY, + CMD_ID_SERVERPASSWORD, + CMD_ID_STATUS, + CMD_ID_HOSTNAME, + CMD_ID_MAXQUERIES, + CMD_ID_CLEARBANNS, + CMD_ID_MAXLOSS, + CMD_ID_PROTOCOL, + CMD_ID_REGION + }; + + void CMD_Rcon(char *cmdLine); + void CMD_ServerCmd(char *cmdLine); + void CMD_ClientCmd(char *cmdLine); + void CMD_RconPassword(char *cmdLine); + void CMD_RconAddress(char *cmdLine); + void CMD_Say(char *cmdLine); + void CMD_Msg(char *cmdLine); + void CMD_Clients(char *cmdLine); + void CMD_Kick(char *cmdLine); + void CMD_ChatMode(char *cmdLine); + void CMD_PublicGame(char *cmdLine); + void CMD_OffLineText(char *cmdLine); + void CMD_AdminPassword(char *cmdLine); + void CMD_SignOnCommands(char *cmdLine); + void CMD_SpectatorPassword(char *cmdLine); + void CMD_DispatchMode(char *cmdLine); + void CMD_CheeringThreshold(char *cmdLine); + void CMD_InformPlayers(char *cmdLine); + void CMD_Ping(char *cmdLine); + void CMD_ProxyPassword(char *cmdLine); + void CMD_MaxRate(char *cmdLine); + void CMD_MaxUpdateRate(char *cmdLine); + void CMD_LoopCmd(char *cmdLine); + void CMD_MaxClients(char *cmdLine); + void CMD_LocalMsg(char *cmdLine); + void CMD_Connect(char *cmdLine); + void CMD_Disconnect(char *cmdLine); + void CMD_PlayDemo(char *cmdLine); + void CMD_Delay(char *cmdLine); + void CMD_Stop(char *cmdLine); + void CMD_Record(char *cmdLine); + void CMD_StopRecording(char *cmdLine); + void CMD_BlockVoice(char *cmdLine); + void CMD_Name(char *cmdLine); + void CMD_Rate(char *cmdLine); + void CMD_Updaterate(char *cmdLine); + void CMD_HostName(char *cmdLine); + void CMD_AddResource(char *cmdLine); + void CMD_Resources(char *cmdLine); + void CMD_BannerFile(char *cmdLine); + void CMD_Bann(char *cmdLine); + void CMD_AddFakeClients(char *cmdLine); + void CMD_Retry(char *cmdLine); + void CMD_AutoRetry(char *cmdLine); + void CMD_ServerPassword(char *cmdLine); + void CMD_Status(char *cmdLine); + void CMD_MaxQueries(char *cmdLine); + void CMD_Players(char *cmdLine); + void CMD_ClearBanns(char *cmdLine); + void CMD_MaxLoss(char *cmdLine); + void CMD_Protocol(char *cmdLine); + void CMD_Region(char *cmdLine); + + struct LocalCommandID_s { + char *name; + LocalCommandIDs id; + void (Proxy::*pfnCmd)(char *cmdLine); + }; + static LocalCommandID_s m_LocalCmdReg[]; + const char *GetDescription(); + +protected: + friend class DemoClient; + friend class Status; + friend class Master; + friend class Director; + + INetwork *m_Network; + IWorld *m_World; + IDirector *m_Director; + IServer *m_Server; + ObjectList m_Clients; + + DemoClient m_DemoClient; + Status m_Status; + Master m_Master; + Director m_DefaultDirector; + DirectorNull m_NullDirector; + INetSocket *m_Socket; + + typedef struct challenge_s { + NetAddress adr; + unsigned int challenge; + float time; + } challenge_t; + + // MAX_CHALLENGES is made large to prevent a denial + // of service attack that could cycle all of them + // out before legitimate users connected + enum { MAX_CHALLENGES = 1024 }; + challenge_t m_Challenges[MAX_CHALLENGES]; + + bool m_IsMaster; + bool m_IsFinishingBroadcast; + bool m_IsReconnectRequested; + + int m_MaxRate; + int m_MaxUpdateRate; + + ChatMode_e m_ChatMode; + int m_MaxClients; + float m_MaxQueries; + int m_MaxFrameQueries; + int m_MaxSeenClients; + + NetAddress m_RconAddress; + char m_RconPassword[128]; + char m_AdminPassword[128]; + char m_ProxyPassword[128]; + char m_SpectatorPassword[128]; + char m_LastRconCommand[1024]; + char m_OffLineText[128]; + char m_SignonCommands[256]; + + ObjectList m_LoopCommands; + bool m_PublicGame; + + enum DispatchMode_e : int { + DISPATCH_OFF, // Won't redirect any clients. + DISPATCH_BALANCE, // Will redirect connecting clients to other proxies balancing work load between all proxies. + DISPATCH_ALL, // Any spectator clients will be redirected, so this proxy serves only as dispatcher. + }; + DispatchMode_e m_DispatchMode; + + float m_ClientDelay; + float m_ClientTimeScale; + double m_ClientWorldTime; + double m_ClientProxyTime; + + double m_LastClockUpdateTime; + float m_CheeringThreshold; + float m_LastCheeringUpdate; + int m_CheeringPlayers; + resource_t *m_BannerTGA; + ObjectList m_Resources; + double m_NextStatusUpdateTime; + float m_FPS; + float m_MaxLoss; + float m_CurrentLoss; + ObjectList m_BannList; + unsigned char m_Region; + + textmessage_t m_LocalMessage; + textmessage_t m_CommentatorMessage; + + double m_NextInfoMessagesUpdate; + BitBuffer m_InfoRules; + BitBuffer m_InfoPlayers; + BitBuffer m_InfoDetails; + BitBuffer m_InfoInfo; + BitBuffer m_InfoString; +}; diff --git a/rehlds/HLTV/Proxy/src/ProxyClient.cpp b/rehlds/HLTV/Proxy/src/ProxyClient.cpp new file mode 100644 index 0000000..da20dd7 --- /dev/null +++ b/rehlds/HLTV/Proxy/src/ProxyClient.cpp @@ -0,0 +1,317 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +void ProxyClient::ShutDown() +{ + char *clientTypeString[] = { "Spectator", "Relay Proxy", "Director", "Commentator", "Fake Client" }; + if (m_State == MODULE_DISCONNECTED) { + return; + } + + m_System->Printf("%s disconnected (%s)\n", clientTypeString[m_ClientType], m_ClientChannel.m_remote_address.ToString()); + m_Proxy->GetClients()->Remove(this); + + BaseClient::ShutDown(); + delete this; +} + +bool ProxyClient::Init(IBaseSystem *system, int serial, char *name) +{ + BaseClient::Init(system, serial, name); + + m_ClientType = TYPE_CLIENT; + m_ChatEnabled = true; + + m_LastChatTime = 0; + m_LastCheerTime = 0; + m_NextDecalTime = 0; + + return true; +} + +ProxyClient::LocalCommandID_s ProxyClient::m_LocalCmdReg[] = { + { "cheer", CMD_ID_CHEER, &ProxyClient::CMD_Cheer }, + { "say", CMD_ID_SAY, &ProxyClient::CMD_Say }, + { "joingame", CMD_ID_JOINGAME, &ProxyClient::CMD_JoinGame }, + { "status", CMD_ID_STATUS, &ProxyClient::CMD_Status }, + { "ignoremsg", CMD_ID_IGNOREMSG, &ProxyClient::CMD_IgnoreMsg }, +}; + +bool ProxyClient::ProcessStringCmd(char *string) +{ + if (BaseClient::ProcessStringCmd(string)) { + return true; + } + + TokenLine cmdLine; + if (!cmdLine.SetLine(string)) { + m_System->Printf("WARNING! ProxyClient::ProcessStringCmd: string command too long.\n"); + return true; + } + + char *cmd = cmdLine.GetToken(0); + for (auto& local_cmd : m_LocalCmdReg) + { + if (!_stricmp(local_cmd.name, cmd)) { + (this->*local_cmd.pfnCmd)(&cmdLine); + return true; + } + } + + if (m_ClientType < TYPE_COMMENTATOR) { + m_System->DPrintf("Unkown client command: \"%s\"\n", cmd); + } + + m_System->DPrintf("Unkown director command: \"%s\"\n", cmd); + return false; +} + +void ProxyClient::CMD_Cheer(TokenLine *cmd) +{ + if (m_SystemTime <= m_LastCheerTime + 6) { + return; + } + + m_LastCheerTime = float(m_SystemTime); + m_Proxy->IncreaseCheering(1); +} + +void ProxyClient::CMD_Say(TokenLine *cmd) +{ + char *chatText = (cmd->CountToken() > 2) ? cmd->GetRestOfLine(1) : cmd->GetToken(1); + if (m_ClientType == TYPE_COMMENTATOR) { + m_Proxy->ChatCommentator(nullptr, chatText); + return; + } + + if (m_ClientType == TYPE_PROXY && m_Proxy->GetChatMode() == CHAT_GLOBAL) { + m_Proxy->ChatSpectator("Unknown", chatText); + return; + } + + if (m_SystemTime >= m_LastChatTime + 6) { + m_Proxy->ChatSpectator(m_ClientName, chatText); + m_LastChatTime = float(m_SystemTime); + return; + } +} + +void ProxyClient::CMD_JoinGame(TokenLine *cmd) +{ + if (m_Proxy->GetServer()->IsDemoFile()) { + PrintfToClient("Proxy is replaying demo.\n"); + return; + } + + if (m_Proxy->IsPublicGame()) + { + char string[64]; + _snprintf(string, sizeof(string), "connect %s\n", m_World->GetGameServerAddress()->ToString()); + + m_ClientChannel.m_reliableStream.WriteByte(svc_stufftext); + m_ClientChannel.m_reliableStream.WriteString(string); + } + + PrintfToClient("Joining game is not allowed.\n"); +} + +void ProxyClient::CMD_Status(TokenLine *cmd) +{ + PrintfToClient("--- HLTV Status ---\n"); + + if (m_Proxy->GetServer()->IsDemoFile()) { + PrintfToClient("Replay demo file %s\n", m_Proxy->GetServer()->GetDemoFileName()); + } + else if (m_Proxy->IsPublicGame()) { + PrintfToClient("Game Server: %s\n", m_World->GetGameServerAddress()->ToString()); + } + else { + PrintfToClient("Private game server\n"); + } + + int proxies, spectators, slots; + m_Proxy->GetStatistics(proxies, slots, spectators); + PrintfToClient("Global HLTV stats: spectators %i, slots %i, proxies %i\n", spectators, slots, proxies); +} + +void ProxyClient::CMD_IgnoreMsg(TokenLine *cmd) +{ + if (cmd->CountToken() != 2) { + return; + } + + m_ChatEnabled = !atoi(cmd->GetToken(1)); + PrintfToClient("Global HLTV stats: spectators %i, slots %i, proxies %i\n", m_ChatEnabled ? "Spectator chat enabled.\n" : "Spectator chat disabled.\n"); +} + +void ProxyClient::UpdateUserInfo(char *userinfostring) +{ + BaseClient::UpdateUserInfo(userinfostring); + + if (m_ClientType > TYPE_DEMO) { + m_System->DPrintf("WARNING! Client::UpdateUserInfo: invalid client ype %i\n", m_ClientType); + m_ClientType = TYPE_CLIENT; + } + + if (m_ClientType) { + return; + } + + // clamp rate + if (m_ClientChannel.GetRate() > m_Proxy->GetMaxRate()) { + m_ClientChannel.SetRate(m_Proxy->GetMaxRate()); + } + + if (m_ClientType == TYPE_CLIENT) + { + if (m_ClientChannel.GetUpdateRate() > m_Proxy->GetMaxUpdateRate()) { + m_ClientChannel.SetUpdateRate(m_Proxy->GetMaxUpdateRate()); + } + } +} + +void ProxyClient::ParseHLTV(NetPacket *packet) +{ + unsigned char cmd = packet->data.ReadByte(); + if (cmd != HLTV_STATUS) { + m_System->Printf("WARNING! unknown HLTV client msg %i\n", cmd); + return; + } + + if (m_ClientType != TYPE_PROXY) { + m_System->DPrintf("WARNING! HLTV status data from spectator client\n"); + packet->data.SkipBytes(12); + return; + } + + m_Proxy->ParseStatusReport(GetAddress(), &packet->data); +} + +void ProxyClient::ReplySpawn(int spawncount, int crcMap) +{ + BaseClient::ReplySpawn(spawncount, crcMap); + + if (m_ClientType == TYPE_PROXY) { + m_VoiceQuery = false; + } + + m_Proxy->WriteSignonData(m_ClientType, &m_ClientChannel.m_reliableStream); +} + +void ProxyClient::SendDatagram() +{ + if (m_Proxy->GetDelay() > 0) + { + double worldTime = m_Proxy->GetSpectatorTime(); + double proxyTime = m_Proxy->GetProxyTime(); + + frame_t *frame = m_World->GetFrameByTime(worldTime); + if (!frame) { + return; + } + + if (m_ClientChannel.GetIdleTime() > 2) { + m_ClientChannel.m_unreliableStream.WriteByte(svc_centerprint); + m_ClientChannel.m_unreliableStream.WriteString((frame->seqnr > 1) ? "Game pending..." : "Buffering game..."); + } + + double time = proxyTime - (worldTime - frame->time); + WriteDatagram(time, frame); + return; + } + + frame_t *frame = m_World->GetLastFrame(); + if (frame) + { + if (m_ClientChannel.GetIdleTime() > 2) { + m_ClientChannel.m_unreliableStream.WriteByte(svc_centerprint); + m_ClientChannel.m_unreliableStream.WriteString("Game pending..."); + } + + WriteDatagram(frame->time, frame); + } +} + +bool ProxyClient::HasChatEnabled() +{ + return m_ChatEnabled; +} + +void ProxyClient::ParseVoiceData(NetPacket *packet) +{ + int nDataLength = packet->data.ReadShort(); + BitBuffer voiceMsg(nDataLength + 8); + + if (m_ClientType != TYPE_COMMENTATOR) { + packet->data.SkipBytes(nDataLength); + return; + } + + if (nDataLength > MAX_VOICEDATA_LEN) { + Disconnect("Invalid voice message.\n"); + return; + } + + voiceMsg.WriteByte(svc_voicedata); + voiceMsg.WriteByte(m_World->GetSlotNumber()); + voiceMsg.WriteShort(nDataLength); + voiceMsg.WriteBuf(packet->data.CurrentByte(), nDataLength); + + m_Proxy->Broadcast(voiceMsg.GetData(), voiceMsg.CurrentSize(), GROUP_PROXY | GROUP_DEMO | GROUP_UNKNOWN | GROUP_VOICE, false); + packet->data.SkipBytes(nDataLength); +} + +void ProxyClient::DownloadFile(char *fileName) +{ + if (!fileName || !fileName[0]) + return; + + const char szMD5[] = "!MD5"; + if (strstr(fileName, "..") || + // ignore customization's + (strlen(fileName) == 36 && !_strnicmp(fileName, szMD5, sizeof(szMD5) - 1))) + { + DownloadFailed(fileName); + return; + } + + resource_t *resource = m_Proxy->GetResource(fileName); + if (resource) { + m_ClientChannel.CreateFragmentsFromBuffer(resource->data, resource->nDownloadSize, FRAG_FILE_STREAM, fileName); + return; + } + + if (m_ClientChannel.CreateFragmentsFromFile(fileName)) { + m_ClientChannel.FragSend(); + return; + } + + DownloadFailed(fileName); +} diff --git a/rehlds/HLTV/Proxy/src/ProxyClient.h b/rehlds/HLTV/Proxy/src/ProxyClient.h new file mode 100644 index 0000000..7f1bfcf --- /dev/null +++ b/rehlds/HLTV/Proxy/src/ProxyClient.h @@ -0,0 +1,82 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "common/BaseClient.h" + +class IProxy; +class NetPacket; +class IBaseSystem; + +class ProxyClient: public BaseClient { +public: + ProxyClient(IProxy *proxy) : m_Proxy(proxy) {} + virtual ~ProxyClient() {} + + bool Init(IBaseSystem *system, int serial, char *name); + void ShutDown(); + bool HasChatEnabled(); + bool ProcessStringCmd(char *string); + void ReplySpawn(int spawncount, int crcMap); + void UpdateUserInfo(char *userinfostring = nullptr); + void ParseVoiceData(NetPacket *packet); + void DownloadFile(char *fileName); + void SendDatagram(); + void ParseHLTV(NetPacket *packet); + +private: + enum LocalCommandIDs { + CMD_ID_CHEER = 1, + CMD_ID_SAY, + CMD_ID_JOINGAME, + CMD_ID_STATUS, + CMD_ID_IGNOREMSG + }; + + void CMD_Cheer(TokenLine *cmd); + void CMD_Say(TokenLine *cmd); + void CMD_JoinGame(TokenLine *cmd); + void CMD_Status(TokenLine *cmd); + void CMD_IgnoreMsg(TokenLine *cmd); + + struct LocalCommandID_s { + char *name; + LocalCommandIDs id; + void (ProxyClient::*pfnCmd)(TokenLine *cmd); + }; + static LocalCommandID_s m_LocalCmdReg[]; + +protected: + IProxy *m_Proxy; + + float m_LastChatTime; + float m_LastCheerTime; + float m_NextDecalTime; + bool m_ChatEnabled; +}; diff --git a/rehlds/HLTV/Proxy/src/Status.cpp b/rehlds/HLTV/Proxy/src/Status.cpp new file mode 100644 index 0000000..0336c0b --- /dev/null +++ b/rehlds/HLTV/Proxy/src/Status.cpp @@ -0,0 +1,318 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +bool Status::Init(IBaseSystem *system, int serial, char *name) +{ + BaseSystemModule::Init(system, serial, name); + + SetName("status"); + + m_Proxies.Init(); + m_System->RegisterCommand("proxies", this, CMD_ID_PROXIES); + + Reset(); + + m_State = MODULE_RUNNING; + m_System->DPrintf("Status module initialized.\n"); + + return true; +} + +void Status::RunFrame(double time) +{ + BaseSystemModule::RunFrame(time); + + if (m_MaxNumberOfSpectators < m_NumberOfSpectators) { + m_MaxNumberOfSpectators = m_NumberOfSpectators; + } + + if (!m_Proxy->IsMaster() || !m_Proxy->IsActive()) + return; + + if (m_SystemTime > m_NextUpdateTime) + { + GetLocalStats(m_NumberOfProxies, m_NumberOfSlots, m_NumberOfSpectators); + SendStatusUpdate(); + + m_Proxy->GetServer()->SetUserInfo("hspecs", COM_VarArgs("%u", m_NumberOfSpectators)); + m_Proxy->GetServer()->SetUserInfo("hslots", COM_VarArgs("%u", m_NumberOfSlots)); + + m_NextUpdateTime = float(m_SystemTime) + 16.f; + } +} + +void Status::ExecuteCommand(int commandID, char *commandLine) +{ + if (commandID == CMD_ID_PROXIES) { + CMD_Proxies(commandLine); + return; + } + + m_System->Printf("ERROR! Status::ExecuteCommand: unknown command ID %i.\n", commandID); +} + +void Status::ShutDown() +{ + if (m_State == MODULE_DISCONNECTED) + return; + + m_Proxies.Clear(true); + m_System->Printf("Status module shutdown.\n"); + + BaseSystemModule::ShutDown(); +} + +void Status::GetGlobalStats(int &proxies, int &slots, int &spectators) +{ + proxies = m_NumberOfProxies; + slots = m_NumberOfSlots; + spectators = m_NumberOfSpectators; +} + +void Status::SetProxy(IProxy *proxy) +{ + m_Proxy = proxy; +} + +void Status::SendStatusUpdate() +{ + BitBuffer buffer(128); + + buffer.WriteByte(svc_hltv); + buffer.WriteByte(HLTV_STATUS); + + buffer.WriteWord(m_NumberOfProxies); + buffer.WriteLong(m_NumberOfSlots); + buffer.WriteLong(m_NumberOfSpectators); + + m_Proxy->Broadcast(buffer.GetData(), buffer.CurrentSize(), GROUP_PROXY, true); + buffer.FastClear(); + + DirectorCmd cmd; + cmd.SetStatusData(m_NumberOfSlots, m_NumberOfSpectators, m_NumberOfProxies); + cmd.WriteToStream(&buffer); + + // group spectators + m_Proxy->Broadcast(buffer.GetData(), buffer.CurrentSize(), GROUP_CLIENT | GROUP_DEMO | GROUP_UNKNOWN, false); + buffer.FastClear(); +} + +void Status::SetName(char *newName) +{ + strcopy(m_Name, newName); +} + +char *Status::GetType() +{ + return STATUS_INTERFACE_VERSION; +} + +char *Status::GetStatusLine() +{ + static char string[256]; + _snprintf(string, sizeof(string), "Global Status: Proxies %i, Slots %i, Spectators %i (max %i)\n", + m_NumberOfProxies, m_NumberOfSlots, + m_NumberOfSpectators, m_MaxNumberOfSpectators); + + return string; +} + +void Status::Reset() +{ + m_Proxies.Clear(true); + + m_NumberOfProxies = 0; + m_NumberOfSlots = 0; + m_NumberOfSpectators = 0; + m_MaxNumberOfSpectators = 0; + m_NextUpdateTime = 0; +} + +void Status::ParseStatusMsg(BitBuffer *stream) +{ + if (m_Proxy->IsMaster()) { + m_System->Printf("WARNING! Status::ParseStatusMsg: unexpected HLTV_STATUS message as Master.\n"); + stream->SkipBytes(10); + return; + } + + m_NumberOfProxies = stream->ReadWord(); + m_NumberOfSlots = stream->ReadLong(); + m_NumberOfSpectators = stream->ReadLong(); + + ReplyStatusReport(); + SendStatusUpdate(); +} + +void Status::ReplyStatusReport() +{ + BitBuffer buf(128); + + int slots, proxies, spectators; + GetLocalStats(proxies, slots, spectators); + + if (!m_Proxy->IsPasswordProtected()) + { + if (!m_Proxy->GetDispatchMode()) { + slots = m_Proxy->GetMaxClients(); + } + else if (m_Proxy->IsStressed()) { + proxies |= PROXY_PRIVATE; + } + } + + buf.WriteByte(1); + buf.WriteWord(proxies); + buf.WriteLong(slots); + buf.WriteLong(spectators); + + m_Proxy->GetServer()->SendHLTVCommand(&buf); +} + +int Status::GetMaxSpectatorNumber() +{ + return m_MaxNumberOfSpectators; +} + +void Status::ParseStatusReport(NetAddress *from, BitBuffer *stream) +{ + int proxies = stream->ReadWord(); + int slots = stream->ReadLong(); + int spectators = stream->ReadLong(); + + bool isPrivate = false; + if (proxies & PROXY_PRIVATE) { + proxies &= ~PROXY_PRIVATE; + isPrivate = true; + } + + proxyInfo_t *proxy = (proxyInfo_t *)m_Proxies.GetFirst(); + while (proxy) + { + if (from->Equal(&proxy->address)) { + break; + } + + proxy = (proxyInfo_t *)m_Proxies.GetNext(); + } + + // couldn't find it? + // allocate for + if (!proxy) + { + proxy = (proxyInfo_t *)Mem_ZeroMalloc(sizeof(proxyInfo_t)); + if (!proxy) { + m_System->Printf("WARNING! Status::ParseStatusReport: not enough memory to increase proxy list.\n"); + return; + } + + proxy->address.FromNetAddress(from); + m_Proxies.Add(proxy); + } + + proxy->slots = slots; + proxy->spectators = spectators; + proxy->proxies = proxies; + proxy->time = float(m_SystemTime); + proxy->isPrivate = isPrivate; + + float ratio = (slots > 0 && !isPrivate) ? float(spectators / slots) : 1; + m_Proxies.ChangeKey(proxy, ratio); +} + +void Status::GetLocalStats(int &proxies, int &slots, int &spectators) +{ + slots = 0; + spectators = 0; + proxies = 0; + + proxyInfo_t *proxy = (proxyInfo_t *)m_Proxies.GetFirst(); + while (proxy) + { + if (m_SystemTime > proxy->time + 64) + { + m_Proxies.Remove(proxy); + free(proxy); + } + else + { + proxies += proxy->proxies; + slots += proxy->slots; + spectators += proxy->spectators; + } + + proxy = (proxyInfo_t *)m_Proxies.GetNext(); + } + + int mySpecs, myProxies; + m_Proxy->CountLocalClients(mySpecs, myProxies); + + spectators += mySpecs; + slots += m_Proxy->GetMaxClients(); + + proxies++; +} + +void Status::CMD_Proxies(char *cmdLine) +{ + int nCount = 0; + proxyInfo_t *proxy = (proxyInfo_t *)m_Proxies.GetFirst(); + while (proxy) + { + nCount++; + m_System->Printf("IP %s, Clients %i, MaxClients %i%s\n", proxy->address.ToString(), proxy->spectators, proxy->slots, proxy->isPrivate ? " (excluded)" : ""); + + proxy = (proxyInfo_t *)m_Proxies.GetNext(); + } + + m_System->Printf("--- Total %i relay proxies ---\n", nCount); +} + +float Status::GetBestRelayProxy(NetAddress *addr) +{ + proxyInfo_t *proxy = (proxyInfo_t *)m_Proxies.GetFirst(); + while (proxy) + { + if (proxy->slots > 0 && proxy->slots >= proxy->spectators && !proxy->isPrivate) + { + float ratio = float(++proxy->spectators / proxy->slots); + + m_Proxies.ChangeKey(proxy, ratio); + addr->FromNetAddress(&proxy->address); + return ratio; + } + + m_Proxies.ChangeKey(proxy, 1); + proxy = (proxyInfo_t *)m_Proxies.GetNext(); + } + + addr->Clear(); + return -1; +} diff --git a/rehlds/HLTV/Proxy/src/Status.h b/rehlds/HLTV/Proxy/src/Status.h new file mode 100644 index 0000000..6edd6d5 --- /dev/null +++ b/rehlds/HLTV/Proxy/src/Status.h @@ -0,0 +1,85 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "common/NetAddress.h" +#include "ObjectDictionary.h" +#include "hltv.h" + +typedef struct proxyInfo_s { + NetAddress address; + int slots; + int proxies; + int spectators; + int isPrivate; + float time; +} proxyInfo_t; + +class IProxy; +class Status: public BaseSystemModule { +public: + Status() {} + virtual ~Status() {} + + bool Init(IBaseSystem *system, int serial, char *name); + void ExecuteCommand(int commandID, char *commandLine); + void RunFrame(double time); + char *GetStatusLine(); + char *GetType(); + void ShutDown(); + + void GetGlobalStats(int &proxies, int &slots, int &spectators); + void GetLocalStats(int &proxies, int &slots, int &spectators); + void SetProxy(IProxy *proxy); + float GetBestRelayProxy(NetAddress *addr); + void ParseStatusMsg(BitBuffer *stream); + void ParseStatusReport(NetAddress *from, BitBuffer *stream); + void Reset(); + int GetMaxSpectatorNumber(); + void RemoveProxy(NetAddress *addr); + void ClearProxyList(); + void ReplyStatusReport(); + void SendStatusUpdate(); + void SetName(char *newName); + +protected: + enum LocalCommandIDs { CMD_ID_PROXIES = 1 }; + void CMD_Proxies(char *cmdLine); + +private: + IProxy *m_Proxy; + float m_NextUpdateTime; + ObjectDictionary m_Proxies; + int m_NumberOfProxies; + int m_NumberOfSlots; + int m_NumberOfSpectators; + int m_MaxNumberOfSpectators; +}; + +#define STATUS_INTERFACE_VERSION "status000" diff --git a/rehlds/HLTV/Proxy/src/precompiled.cpp b/rehlds/HLTV/Proxy/src/precompiled.cpp new file mode 100644 index 0000000..5f656a4 --- /dev/null +++ b/rehlds/HLTV/Proxy/src/precompiled.cpp @@ -0,0 +1 @@ +#include "precompiled.h" diff --git a/rehlds/HLTV/Proxy/src/precompiled.h b/rehlds/HLTV/Proxy/src/precompiled.h new file mode 100644 index 0000000..d722eb0 --- /dev/null +++ b/rehlds/HLTV/Proxy/src/precompiled.h @@ -0,0 +1,49 @@ +#pragma once + +#include "basetypes.h" +#include "archtypes.h" +#include "mathlib.h" +#include "FileSystem.h" + +#include "interface.h" +#include "IBaseSystem.h" + +#include "mem.h" +#include "common.h" + +#include "common/md5.h" +#include "common/random.h" +#include "common/byteorder.h" +#include "common/ServerInfo.h" +#include "common/common_hltv.h" +#include "common/net_internal.h" +#include "common/mathlib_internal.h" + +// Hooks stuff +#include "hookers/HLTV/Proxy/hooklist.h" + +#include "common/DirectorCmd.h" +#include "common/NetAddress.h" +#include "common/NetChannel.h" +#include "common/BaseClient.h" + +#include "common/DemoFile.h" +#include "common/munge.h" + +#include + +#include +#include +#include +#include +#include +#include + +// Proxy module stuff +#include "Proxy.h" +#include "ProxyClient.h" +#include "DemoClient.h" +#include "FakeClient.h" + +#include "Master.h" +#include "Status.h" diff --git a/rehlds/HLTV/Proxy/src/public_amalgamation.cpp b/rehlds/HLTV/Proxy/src/public_amalgamation.cpp new file mode 100644 index 0000000..1fc308c --- /dev/null +++ b/rehlds/HLTV/Proxy/src/public_amalgamation.cpp @@ -0,0 +1,3 @@ +#include "precompiled.h" + +#include "interface.cpp" diff --git a/rehlds/HLTV/README.md b/rehlds/HLTV/README.md new file mode 100644 index 0000000..c774022 --- /dev/null +++ b/rehlds/HLTV/README.md @@ -0,0 +1,333 @@ +## Half-Life TV for Goldsrc based games HLTV Launcher + +### Building +On Windows: +
gradlew --max-workers=1 clean rehlds/HLTV:build
+ +On Linux (ICC): +
./gradlew --max-workers=1 clean rehlds/HLTV:build
+ +On Linux (GCC): +
./gradlew --max-workers=1 -PuseGcc clean rehlds/HLTV:build
+ +Compiled binaries will be placed in each project rehlds/HLTV/**/binaries/ directory + +### Overview +Half-Life TV offers the ability to have an unlimited number of spectators watching online games. +They can follow the game just like they would as a spectator on the game server. +Spectators are invisible to players and can't interact with the running game in any way. +Each spectator can choose any view position or choose any player to track individually. + +To have the most enjoyable spectating experience, spectators can enable the Auto-Director mode. +Then the camera is changed automatically so that only interesting scenes are shown from a suitable viewpoint. +Thus the spectator can lean back and won't miss any relevant action. +All the time, spectators may communicate between each other using the standard HL chat system. + +Most popular Half-Life MODs are supported like `Counter-Strike`, `Team Fortress Classic`, `Day of Defeat` and many others. +HLTV providers have full control over their HLTV system, may change number of spectator slots, add text messages or change the HLTV logo. + +The broadcast is delayed by a customizable amount of time, by default 30 seconds. This ensures that the playing teams can't use +HLTV to get any usable information about their opponents. Providing a single HLTV server for up to 100 spectators is an easy task +and doesn't need any changes in default configurations. + +Installing a larger HLTV network for thousands of spectators needs some more planning time and experience about required bandwidth +and CPU/RAM demands (see [Larger Broadcasts](#larger-broadcasts)). + +### Spectating Games +To watch a HLTV game, start Half-Life, open the Multiplayer menu and select 'Find Servers'. +To search for currently broadcasted games, choose the 'Spectate' section and hit 'Refresh All'. +After the list has been updated, double click on the server you want to spectate and you'll be connected. + +If computer game leagues announce important matches to be broadcasted via HLTV, they often provide IP:Port addresses of their HLTV servers. +Instead of searching them via the server browser, you can also go to the 'Favorites' folder and add the HLTV address to your server +list by pressing the right mouse button. Otherwise, you can also open the console window and use the 'connect' command to spectate a certain game. + +For example: +
+connect 192.168.130.42:27020
+
+ +The default HLTV port number is 27020, but may be changed. +It should always be included in the given address, since commonly this port number is different from the default port number 27015. + +You can spectate the game in different modes: `Chase Cam`, `First Person`, `Free Look`, `Map Overview` and `Map Chase`.
+The easiest way to change modes is to press the JUMP key (default `SPACE`). Alternatively you can use the spectator menu, which can be enabled by pressing the `DUCK` key (default `CTRL`). +Here you can customize your personal view style and enable the Auto-Director Mode. Press `USE` (default `E`) to cycle through the different `Picture-In-Picture` modes. + +The following HL console commands can be used to customize spectator settings: + +| Console commands | Default | Min | Max | Description | +| :---------------------------- | :-----: | :-: | :----------: | :--------------------------------------------- | +| spec_autodirector | 1 | 0 | 1 | Turns Auto Director mode on or off | +| spec_drawcone | 1 | 0 | 1 | Shows your view cone in map overview mode | +| spec_drawnames | 1 | 0 | 1 | Shows player names under their icons | +| spec_drawstatus | 1 | 0 | 1 | Shows game information (time, map etc) | +| spec_pip | 0 | 0 | 1 | Turns Picture-In-Picture mode on or off | +| spec_mode | 1 | 1 | 6 | Set the main view mode, seconds parameter is the PIP mode.
Not all combinations are valid.
Main modes are:
`1` Locked Chase
`2` Free Chase
`3` Free Roaming
`4` First Person
`5` Map Overview
`6` Chase Map Overview

PIP modes are:
`0` PIP off
`1` Free Chase
`2` First Person
`3` Map Overview
`4` Chase Map Overview
| +| spec_menu | - | - | - | Opens the spectator menu | +| spec_help | - | - | - | Shows a help screen | + +### HLTV Basics +The core of the HLTV broadcasting system is the HLTV server, also called HLTV proxy. +The HLTV executable is a console application that works much like a HL dedicated server. +To broadcast a game running on a certain game server, the HLTV proxy connects to this server just like a normal player. +Spectators connect themselves to the HLTV proxy and the game data stream is relayed through the HLTV proxy to all connected spectator clients. + +The next figure shows a basic HLTV configuration: +
+HL Game Server -> HLTV Proxy => Spectator Clients
+
+ +The number of clients that one HLTV proxy can serve depends on available hardware and network resources. +Theoretically, a single proxy can hold a maximum of 255 spectator clients. +But be careful, even a proxy with 100 spectator clients needs a full 2 MBit line to run smoothly. +If more spectator slots are needed, the required network load must be distributed over multiple HLTV proxies. + +The first HLTV proxy connected to the game server is called the Master proxy, which sets the general broadcast settings like +game stream delay or packet rate. All other HLTV proxies linked to this proxy are the Relay proxies. Their total number and +link order is not restricted, they may form a chain or tree of proxies. +Most important is that their location is in different networks to ensure a balanced bandwidth usage. + +
+                              -> HLTV Relay Proxy 1 => Spectator Clients
+HL Game Server -> HLTV Master -> HLTV Relay Proxy 2 => Spectator Clients
+                              -> HLTV Relay Proxy 3 => Spectator Clients
+
+ +### Broadcasting Games +Let's assume the most simple configuration, a single HLTV proxy in a LAN environment. +This is a very common situation and the default HLTV settings doesn't need to be changed. +Choose a dedicated computer as your HLTV proxy and install the Half-Life Dedicated Server, which also includes all files needed by a HLTV proxy. +This isn't needed if Half-Life is already installed. + +Start the HLTV application (HL icon with a small camera) and the HLTV console will open, showing some initialization messages +(if that takes a long time, HLTV maybe can't resolve some IP addresses, then start HLTV with the '-nodns' command line option). +Then the console is ready to accept your commands, here we use '>' as the console prompt. + +First give your HLTV proxy an unique name: +
+>name "My HLTV Proxy"
+
+ +Let's assume you have started the proxy on host `192.168.1.2` and the game server, +you want to spectate is running on host `192.168.1.3:27015`. + +Then connect the HLTV proxy to this game server by typing: +
+>connect 192.168.1.3:27015
+
+ +After a few seconds HLTV will be fully connected and ready to serve spectator clients. +Use the 'status' command to verify that the HLTV proxy has connected properly: +
+>status
+--- HLTV Status ---
+Online 00:23, FPS 79.0, Version 2435 (Win32)
+Local IP 192.168.1.2:27020, Network In 1.7, Out 1.0, Loss 0.00
+Local Slots 128, Spectators 0, Proxies 0
+Total Slots 128, Spectators 0, Proxies 1
+Source Game Server 192.168.1.3:27015, Delay 30
+Server Name "Half-Life dedicated server"
+Time 01:35, Game "valve", Map "maps/rapidcore.bsp", Players 1
+
+ +The 'status' command shows your own IP address, HLTV system cycles per second, total incoming and outgoing network traffic in `kB/sec`. +Local slot and spectator numbers your HLTV proxy is providing, total numbers are the sum of all slots & spectators on all proxies broadcasting this game. +The game source can be a game server, another HLTV proxy or a demo file. + +### Recording HLTV demos +HLTV demo files are like normal recorded games in Half-Life, but you can choose any view point, +view mode or player to chase during replay. To play back a HLTV demo, a HLTV proxy is not needed. +Just start Half-Life and type in console `playdemo ` or `viewdemo ` +(viewdemo offers more options during playback like fast forward/backward, pause & slowmotion). +To record a HLTV demo, connect the proxy to a game server (see [Broadcasting Games](#broadcasting-games)) and type in console: + +
+>record 
+
+ +All games will be recorded after issuing this command. The demo files will be saved in the current Mod directory, e.g. `\cstrike`. +All demo files have a special naming convention `--.dem`, including the given name, date/time and map name. +Demo files record the same data as send to spectator clients. That means also, the demo file records the game with the same delay as used for spectators. +To verify, that a demo file is recorded use the `status` command. The recording may be stopped with `stoprecording`. + +Sometimes a HLTV broadcast is not wanted and the HLTV proxy is only used to record a demo file. +In this case, some HLTV settings should be made to gain optimal recording results: +
+>maxclients 0   // don't allow any spectator clients
+>delay 0        // no game stream delay
+>rate 10000     // maximum data rate
+>updaterate 20  // standard update rate
+>nomaster 1     // don't register at master servers
+
+ +### Larger Broadcasts +Setting up a HLTV network that can handle a larger number of spectators (>1000) is a difficult and time consuming task. +The following guide should help to configure and run such a HLTV network. One of the most important rule should be `Quality, not Quantity`. +It's better to offer a smaller number of spectator slots, than operating at the maximum bandwidth limit, thus all spectators would suffer from lags and timeouts. +Check carefully your available bandwidth capacity and calculate how many spectators can be handled by your HLTV servers. +The average bandwidth demand per spectator is between 2 and 3 KB/sec and depends on the current mod, map and number of players. +CPU and RAM shouldn't be a bottleneck on modern PC systems. + + +This list for common Internet connection types gives a feeling, how bandwidth demanding HLTV can be: +
+-   ISDN         64 Kbps            :      2 spectators
+-   DSL         128 Kbps (upstream) :      5 spectators
+-   T1          1.5 Mbps            :     75 spectators
+-   LAN          10 Mbps            :    500 spectators
+-   T3           75 Mbps            :   4000 spectators
+
+ +Use the `maxclients` command to set how many clients should be accepted by a HLTV proxy. +Make sure that the `maxrate` variable is set too a reasonable value, e.g. `3500 kB/sec`. Lower values are possible, +but make sure spectators don't get too much `choke` during a running game. The `maxrate` command doesn't effect +the bandwidth limit between HLTV proxies, only for spectator clients. +To lower the general bandwidth demand, you can turn off the internal HLTV chat (`chatmode 0`) or decrease the game +update rate from the default value 20 to 10 (`updaterate 10`). A lower update rate may save up to 25% network traffic +and is an acceptable tradeoff in this case since spectators doesn't need a high update rate like real players does. + +A very common setup for large broadcasts is to use 2 dedicated HLTV servers to create a private and a public HLTV segment. +Let's assume the game server is in a closed LAN and not accessible from outside. +This ensures a maximum security against attacks (DOS etc) from outside. The HLTV master server is started within the +LAN and it's IP address should be kept secret. The second HLTV server is started outside the LAN with a global IP +and is connected to the HLTV master server. This second HLTV server is the public HLTV dispatcher, which IP address is given to the audience. +Any relay proxies are connected to this HLTV dispatcher. Thus the HLTV master server is in a secure LAN environment +and can be used for demo recording or for HL clients serving video projectors. +Spectators connect to the HLTV dispatcher and are relayed through the HLTV network to a relay proxy with a low usage. +Thus the total network load is balanced between all connected HLTV proxies. + +
+       Private LAN                 Public Internet
+                                            -> HLTV Proxy 1
+HL Server -> HLTV Master -> HLTV Dispatcher -> HLTV Proxy 2
+                                            -> HLTV Proxy 3
+
+ +The configuration files of HLTV master and HLTV dispatcher are different: + +**master.cfg:** +
+nomaster      1        // don't register at WON master servers
+proxypassword MyPWD    // protect HLTV server
+publicgame    0        // don't show game server IP
+dispatchmode  0        // don't dispatch spectators
+
+ +**dispatcher.cfg:** +
+forcemaster  1         // register at WON master servers
+publicgame   0         // don't show game server IP
+dispatchmode 2         // dispatch all clients to other proxies
+hostname     MyGame    // public HLTV server name
+
+ +If you're running 3 or more HLTV servers in total, it's a good idea to use RCON to manage them via a single server admin tool. +To enable RCON on a HLTV server an `adminpassword` must been set. +Also `proxypassword` should be set to ensure only known HLTV providers can connect to your HLTV network. +Otherwise anybody can connect with slow HLTV proxies and disturb your network load balancing. + +### HLTV Configuration + +A short note about console command syntax. +A command may have one or more parameters. Parameters in brackets `[]` are optional. + +Common used parameters are: +
+   : text, must be in quotes if text contains spaces "My Name"
+   : a whole number, e.g. 42
+   : a floating point number, e.g. 4.2
+   : an IP address, e.g. 192.168.130.42:27020
+   : a set of options, a or b or c
+
+ +`NOTE:` Any of these special characters <, >, |, [ or ] are not part of the final command as typed in the console. +Lots of these commands are boolean switches, were 1 is meaning ON and 0 is respectively OFF. + + +| HLTV Commands | Arguments | Description | +| :---------------------------- | :--------------------------------------------------------- | :------------------------------------------------------------------------------------------------------ | +| connect | IP:Port | Connect HLTV proxy to game server. (default port `27015`) | +| disconnect | - | Disconnects proxy from server, but doesn't stop the broadcast.
All spectator clients stay connected. | +| stop | [ `text` ] | Disconnects from server, disconnects all clients and stops demo recording.
Optional goodbye message. | +| quit | 1 | Quits the HLTV process. | +| retry | 0 | Retries the last server connection. | +| autoretry | `0/1` | If enabled, proxy will retry connection to server.
If connection was interrupted for any reason. | +| name | `text` | Sets the HLTV proxy scoreboard name. | +| hostname | `text` | Sets the HLTV host name for game browser list. | +| serverpassword | `text` | Sets the game server password. | +| adminpassword | `text` | Sets password for RCON & commentator. | +| proxypassword | `text` | Sets password for other relay proxies. | +| spectatorpassword | `text` | Sets spectator password. Will also exclude proxy from global load balancing. | +| clients | - | Lists connected spectator clients. | +| proxies | - | Lists connected relay proxies. | +| players | - | Lists players on game server. | +| kick | `ID` | Kicks a spectator client from proxy. | +| bann | `IP` | Banns an IP address. (completely ignored) | +| clearbanns | - | Removes all IPs from bann list. | +| say | `text` | Sends a text message to game server. (chat with players) | +| msg | `text` [ `duration` `pos x` `pos y` `rgba` ] | Sends a text message to all spectators as big HUD text. | +| localmsg | `text` [ `duration` `pos x` `pos y` `rgba` ] | Same as msg, but only seen by local clients. | +| servercmd | `string` | Forwards console command to game server. | +| clientcmd | `group` `string` | Forwards a console command to all clients of given group:
`1` SPECTATORS
`2` PROXIES
`3` ALL | +| loopcmd | `id` `n` `string` | Loopcmd will execute `string` every `n` seconds.
`` is a number between `1` and `64` to identify this loopcmd.
`loopcmd none` will disable a looping command again.
loopcmd without any parameter will list any command currently in the list. | +| signoncommands | `string` | Console commands that will be executed by local
spectator clients after connection is established.
Commands may be separated by semicolons. | +| maxclients | `n` | Set spectator number limit for this proxy. (default `128`) | +| delay | `n` | Delays the game stream for n seconds on the Master Proxy.
The default value is 30 seconds to avoid cheating.
If the delay is set to a value below 10 seconds (e.g. 0),
the auto director function will be disabled. | +| rate | `n` | Bandwidth rate the game server sends data to the proxy. | +| updaterate | `n` | Game updates per seconds send from server to proxy. | +| maxrate | `n` | Sets the maximum bandwidth rate for spectator clients. | +| maxloss | `f` | Sets the acceptable packet loss rate, default value is 0.05 (5%).
If packet loss is higher, new spectator clients will be rejected. | +| maxqueries | `n` | Maximum of status queries per second requested by server browsers. | +| dispatchmode | `0/1/2` | `0` (OFF) Won't redirect any clients.
`1` (AUTO) Will redirect connecting clients to other
proxies balancing work load between all proxies.
`2` (ALWAYS) Any spectator clients will be redirected,
so this proxy serves only as dispatcher.
| +| publicgame | `0/1` | `0` Private.
`1` Game server IP will be visible to spectators and 'joingame' is allowed. | +| offlinetext | `string` | Info text clients will see as reject reason if HLTV isn't broadcasting yet. | +| chatmode | `0/1/2` | `0` Spectators can't chat.
`1` Only spectators connected to the same proxy can see their chat messages.
`2` All spectators can chat between each other (then Master and all Relay proxies must have set chatmode 2).
| +| bannerfile | `file` | Specifies a TGA file (`RGBA`) that will be shown as logo in spectator GUI. | +| ping | `host:port` | Pings a HL server on the given port. (default `27015`) | +| nomaster | `0/1` | `0` Disabled.
`1` Proxy won't register at WON master servers. | +| forcemaster | `0/1` | `0` Disabled.
`1` Proxy will register at WON master server. | +| heartbeat | - | Sends manually a status packet to WON master servers. | +| region | `n` | Set the region your HLTV proxy is located in. | +| rcon | `string` | Sends a remote control command to other servers. | +| rconaddress | `IP:Port` | Sets the remote control target address. | +| rconpassword | `string` | Sets the password for the remote controlled host. | +| cheeringthreshold | `f` | Number of cheering players must be above this threshold to play
the cheering sound. (by default `0.25`). | +| blockvoice | `0/1` | `0` Disabled.
`1` All incoming voice data is blocked.
This is useful to override incoming voice commentators
or player voice with own commentators voice. | +| cmdlist | - | Shows all registered proxy commands. | +| logfile | `0/1` | Starts/stops console logging in `logfile.log` | +| status | - | Shows proxy status information. | +| modules | - | Shows all loaded HLTV modules and versions. | +| exec | `filename` | Executes a `.cfg` file. | +| echo | `string` | Prints a text to HLTV console. | +| developer | `0/1` | `0` Disabled.
`1` Additional debug messages are shown in developer mode. | +| record | `filename` | Records all following games to demo files
using name syntax `filename--.dem` | +| stoprecording | - | Stops recording a demo file. | +| playdemo | `filename` | Starts broadcasting a demo file. | + +The console does auto-completion by hitting `TAB`. +All commands in the config file `hltv.cfg` are executed during startup. + +Some parameters can only be set in the command line: +
+-port            - sets the HLTV proxy port that spectators connect to (default "27020" )
+-ip              - forces the proxy to use this IP on a multihomed host
+-nodns           - disables any DNS resolving (useful for LAN proxies)
+-maxfps          - sets maximum system cycles per seconds (default "100")
+-highpriority    - starts the HLTV proxy as high priority process
+-multicastttl    - sets the Time-To-Live value for multicast packets
+-steam           - proxy enables special Steam support
+-dev             - developer mode
+
+ +These parameters cannot be changed during runtime, thus they can't be used in config files.
+All console commands can be used in the command line, if a `+` is prepended to them, for example: +
+hltv.exe +connect localhost:27015 -port 27021
+
+ +A Half-Life server can set sv_proxies `n`, to determine how many proxies are allowed to connect.
+If HLTV proxies should be forbidden, set it to `0`, otherwise `1` to allow for a Master Proxy.
+Other values are experimental. + +Copyright © 2003, `Valve LLC`, All rights reserved. diff --git a/rehlds/HLTV/build.gradle b/rehlds/HLTV/build.gradle new file mode 100644 index 0000000..8f08d31 --- /dev/null +++ b/rehlds/HLTV/build.gradle @@ -0,0 +1,13 @@ +evaluationDependsOn(':rehlds/HLTV/Console'); +evaluationDependsOn(':rehlds/HLTV/Core'); +evaluationDependsOn(':rehlds/HLTV/DemoPlayer'); +evaluationDependsOn(':rehlds/HLTV/Director'); +evaluationDependsOn(':rehlds/HLTV/Proxy'); + +task build { + dependsOn project(':rehlds/HLTV/Console').tasks.build, + project(':rehlds/HLTV/Core').tasks.build, + project(':rehlds/HLTV/DemoPlayer').tasks.build, + project(':rehlds/HLTV/Director').tasks.build, + project(':rehlds/HLTV/Proxy').tasks.build +} diff --git a/rehlds/HLTV/common/BaseClient.cpp b/rehlds/HLTV/common/BaseClient.cpp new file mode 100644 index 0000000..dd33e9d --- /dev/null +++ b/rehlds/HLTV/common/BaseClient.cpp @@ -0,0 +1,776 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +bool BaseClient::Init(IBaseSystem *system, int serial, char *name) +{ + if (!BaseSystemModule::Init(system, serial, name)) { + return false; + } + + if (!name) { + strcopy(m_Name, CLIENT_INTERFACE_VERSION); + } + + SetState(CLIENT_INITIALIZING); + + m_ClientType = TYPE_CLIENT; + m_World = nullptr; + m_Socket = nullptr; + + m_VoiceEnabled = false; + m_VoiceQuery = false; + m_Userinfo.SetMaxSize(MAX_USER_INFO); + + return false; +} + +void BaseClient::RunFrame(double time) +{ + BaseSystemModule::RunFrame(time); + + if (m_ClientState != CLIENT_DISCONNECTED && m_ClientChannel.IsTimedOut()) { + m_System->Printf("Client %s timed out.\n", m_ClientChannel.GetTargetAddress()->ToString()); + Disconnect("Timed out.\n"); + } + + if (m_ClientState != CLIENT_DISCONNECTED && m_ClientChannel.IsCrashed()) { + m_System->Printf("Client %s netchannel crashed.\n", m_ClientChannel.GetTargetAddress()->ToString()); + Disconnect("Netchannel crashed.\n"); + } + + int numPackets = 0; + NetPacket *packet; + while ((packet = m_ClientChannel.GetPacket())) + { + if (packet->connectionless) + { + m_Socket->AddPacket(packet); + } + else if (numPackets < 32) + { + numPackets++; + ProcessMessage(packet); + } + + m_ClientChannel.FreePacket(packet); + } + + // process state + switch (m_ClientState) + { + case CLIENT_RUNNING: + { + if (m_World->IsActive() && m_ClientChannel.IsReadyToSend()) { + SendDatagram(); + } + break; + } + case CLIENT_DISCONNECTED: + ShutDown(); + break; + case CLIENT_INITIALIZING: + case CLIENT_CONNECTING: + /* do nothing */ + break; + default: + case CLIENT_UNDEFINED: + m_System->Errorf("Client::RunFrame: not valid state.\n"); + break; + } +} + +void BaseClient::ShutDown() +{ + if (m_State == MODULE_DISCONNECTED) { + return; + } + + if (m_ClientState != CLIENT_DISCONNECTED) { + Disconnect("Connection dropped (hard).\n"); + } + + m_ClientChannel.Close(); + BaseSystemModule::ShutDown(); +} + +bool BaseClient::Connect(INetSocket *socket, NetAddress *adr, char *userinfo) +{ + if (!userinfo || !adr) { + return false; + } + + m_ClientType = TYPE_CLIENT; + m_Socket = socket; + + if (!adr->Equal(m_ClientChannel.GetTargetAddress())) { + m_ClientChannel.Create(m_System, m_Socket, adr); + } else { + m_ClientChannel.Clear(); + m_ClientChannel.Reset(); + } + + SetState(CLIENT_CONNECTING); + m_ClientChannel.SetUpdateRate(20); + m_ClientChannel.SetRate(20000); + m_ClientChannel.SetConnected(1); + + UpdateUserInfo(userinfo); + m_ClientChannel.OutOfBandPrintf("%c0000000000000000", S2C_CONNECTION); + + return true; +} + +BaseClient::clc_func_s BaseClient::m_ClientFuncs[] = { + { clc_bad, "clc_bad", &BaseClient::ParseBad }, + { clc_nop, "clc_nop", &BaseClient::ParseNop }, + { clc_move, "clc_move", &BaseClient::ParseMove }, + { clc_stringcmd, "clc_stringcmd", &BaseClient::ParseStringCmd }, + { clc_delta, "clc_delta", &BaseClient::ParseDelta }, + { clc_resourcelist, "clc_resourcelist", nullptr }, + { clc_tmove, "clc_tmove", nullptr }, + { clc_fileconsistency, "clc_fileconsistency", nullptr }, + { clc_voicedata, "clc_voicedata", &BaseClient::ParseVoiceData }, + { clc_hltv, "clc_hltv", &BaseClient::ParseHLTV }, + { clc_cvarvalue, "clc_cvarvalue", &BaseClient::ParseCvarValue }, + { clc_cvarvalue2, "clc_cvarvalue2", &BaseClient::ParseCvarValue2 }, + { clc_endoflist, "End of List", nullptr }, +}; + +void BaseClient::ProcessMessage(NetPacket *packet) +{ + m_ClientDelta = 0; + m_DeltaFrameSeqNr = 0; + + while (true) + { + if (packet->data.IsOverflowed()) { + m_System->Printf("Client::ProcessMessage: packet read overflow\n"); + Disconnect("Dropped due to bad message format!\n"); + break; + } + + int cmd = packet->data.ReadByte(); + if (cmd == -1) + break; + + if (cmd < clc_bad || cmd > clc_cvarvalue2) + { + m_System->Printf("Client::ProcessMessage: unknown command char\n"); + Disconnect("Bad command character in client command"); + return; + } + + if (!m_ClientFuncs[cmd].func) { + m_System->Printf("TODO! Client::ProcessMessage: missing parsing routine for %s.\n", m_ClientFuncs[cmd].pszname); + return; + } + + (this->*m_ClientFuncs[cmd].func)(packet); + } +} + +char *BaseClient::GetClientName() +{ + return m_ClientName; +} + +InfoString *BaseClient::GetUserInfo() +{ + return &m_Userinfo; +} + +void BaseClient::Disconnect(const char *reason) +{ + SetState(CLIENT_DISCONNECTED); + + m_ClientChannel.m_reliableStream.WriteByte(svc_disconnect); + m_ClientChannel.m_reliableStream.WriteString(reason ? reason : ""); + + m_ClientChannel.m_unreliableStream.WriteByte(svc_disconnect); + m_ClientChannel.m_unreliableStream.WriteString(reason ? reason : ""); + m_ClientChannel.TransmitOutgoing(); +} + +void BaseClient::ParseNop(NetPacket *packet) +{ + ; +} + +void BaseClient::ParseBad(NetPacket *packet) +{ + m_System->Printf("Client::ParseBad: bad command char\n"); + Disconnect("Bad command character in client command"); +} + +void BaseClient::ParseStringCmd(NetPacket *packet) +{ + char *string = packet->data.ReadString(); + ProcessStringCmd(string); +} + +BaseClient::LocalCommandID_s BaseClient::m_LocalCmdReg[] = { + { "name", CMD_ID_NAME, &BaseClient::CMD_Name }, + { "spawn", CMD_ID_SPAWN, &BaseClient::CMD_Spawn }, + { "new", CMD_ID_NEW, &BaseClient::CMD_New }, + { "fullupdate", CMD_ID_FULLUPDATE, &BaseClient::CMD_FullUpdate }, + { "dropclient", CMD_ID_DROPCLIENT, &BaseClient::CMD_DropClient }, + { "dlfile", CMD_ID_DLFILE, &BaseClient::CMD_DownloadFile }, + { "setinfo", CMD_ID_SETINFO, &BaseClient::CMD_SetInfo }, + { "showinfo", CMD_ID_SHOWINFO, &BaseClient::CMD_ShowInfo }, + { "sendents", CMD_ID_SENDENTS, &BaseClient::CMD_SendEntities }, + { "VModEnable", CMD_ID_VMODENABLE, &BaseClient::CMD_VoiceModEnable }, + { "vban", CMD_ID_VBAN, &BaseClient::CMD_VoiceBan }, + + // TODO: Pure function's, finish them. + { "sendres", CMD_ID_SENDERS, &BaseClient::CMD_SendResources }, + { "ping", CMD_ID_PING, &BaseClient::CMD_RequestPing }, + { "spectate", CMD_ID_SPECTATE, &BaseClient::CMD_Spectate }, + { "spk", CMD_ID_SPK, &BaseClient::CMD_Spk }, + { "pause", CMD_ID_PAUSE, &BaseClient::CMD_Pause }, + { "unpause", CMD_ID_UNPAUSE, &BaseClient::CMD_UnPause }, + { "setpause", CMD_ID_SETPAUSE, &BaseClient::CMD_SetPause } +}; + +bool BaseClient::ProcessStringCmd(char *string) +{ + TokenLine cmdLine; + if (!cmdLine.SetLine(string)) { + m_System->Printf("WARNING! BaseClient::ProcessStringCmd: string command too long.\n"); + return false; + } + + char *cmd = cmdLine.GetToken(0); + for (auto& local_cmd : m_LocalCmdReg) + { + if (!_stricmp(local_cmd.name, cmd)) { + (this->*local_cmd.pfnCmd)(&cmdLine); + return true; + } + } + + return false; +} + +void BaseClient::CMD_Name(TokenLine *cmd) +{ + SetName(cmd->GetRestOfLine(1)); +} + +void BaseClient::CMD_Spawn(TokenLine *cmd) +{ + if (cmd->CountToken() != 3) { + m_System->Printf("Client::ParseStringCmd: spawn is not valid\n"); + Disconnect("Spawn is not valid."); + return; + } + + ReplySpawn(atoi(cmd->GetToken(1)), atoi(cmd->GetToken(2))); +} + +void BaseClient::CMD_New(TokenLine *cmd) +{ + ReplyNew(); +} + +void BaseClient::CMD_FullUpdate(TokenLine *cmd) +{ + ReplyFullUpdate(); +} + +void BaseClient::CMD_DropClient(TokenLine *cmd) +{ + Disconnect(); +} + +void BaseClient::CMD_DownloadFile(TokenLine *cmd) +{ + DownloadFile(cmd->GetToken(1)); +} + +void BaseClient::CMD_SetInfo(TokenLine *cmd) +{ + if (cmd->CountToken() != 3) { + return; + } + + m_Userinfo.SetValueForKey(cmd->GetToken(1), cmd->GetToken(2)); + UpdateUserInfo(); +} + +void BaseClient::CMD_ShowInfo(TokenLine *cmd) +{ + InfoString info(MAX_INFO_STRING); + + info.SetString(m_World->GetServerInfoString()->GetString()); + info.RemoveKey("proxy"); // remove proxy ip + PrintfToClient("%s\n", info.GetString()); +} + +void BaseClient::CMD_SendEntities(TokenLine *cmd) +{ + m_System->DPrintf("Client fully connected.\n"); + SetState(CLIENT_RUNNING); +} + +void BaseClient::CMD_VoiceModEnable(TokenLine *cmd) +{ + if (cmd->CountToken() != 2) { + return; + } + + m_VoiceQuery = false; + m_VoiceEnabled = atoi(cmd->GetToken(1)) ? true : false; + UpdateVoiceMask(&m_ClientChannel.m_reliableStream); +} + +void BaseClient::CMD_VoiceBan(TokenLine *cmd) +{ + if (cmd->CountToken() < 2) { + return; + } + + for (int i = 1; i < cmd->CountToken(); i++) + { + uint32 mask = 0; + sscanf(cmd->GetToken(i), "%x", &mask); + + if (i <= VOICE_MAX_PLAYERS_DW) { + m_SentBanMask.SetDWord(i - 1, mask); + } + } + + UpdateVoiceMask(&m_ClientChannel.m_reliableStream); +} + +void BaseClient::CMD_SendResources(TokenLine *cmd) { /* do nothing */ } +void BaseClient::CMD_RequestPing(TokenLine *cmd) { /* do nothing */ } +void BaseClient::CMD_Spectate(TokenLine *cmd) { /* do nothing */ } +void BaseClient::CMD_Spk(TokenLine *cmd) { /* do nothing */ } +void BaseClient::CMD_Pause(TokenLine *cmd) { /* do nothing */ } +void BaseClient::CMD_UnPause(TokenLine *cmd) { /* do nothing */ } +void BaseClient::CMD_SetPause(TokenLine *cmd) { /* do nothing */ } + +void BaseClient::UpdateUserInfo(char *userinfostring) +{ + char buffer[1024]; + char *string; + + if (userinfostring) { + m_Userinfo.SetString(userinfostring); + } + + strcopy(buffer, m_Userinfo.ValueForKey("name")); + + SetName(buffer); + m_ClientType = atoi(m_Userinfo.ValueForKey("*hltv")); + + if (m_ClientType < TYPE_CLIENT) { + m_System->DPrintf("WARNING! BaseClient::UpdateUserInfo: invalid client ype %i\n", m_ClientType); + m_ClientType = TYPE_CLIENT; + } + + string = m_Userinfo.ValueForKey("rate"); + if (*string) { + m_ClientChannel.SetRate(atoi(string)); + } + + string = m_Userinfo.ValueForKey("cl_updaterate"); + if (*string) { + m_ClientChannel.SetUpdateRate(atoi(string)); + } +} + +void BaseClient::PrintfToClient(char *fmt, ...) +{ + va_list argptr; + static char string[1024]; + + va_start(argptr, fmt); + _vsnprintf(string, sizeof(string), fmt, argptr); + va_end(argptr); + + m_ClientChannel.m_reliableStream.WriteByte(svc_print); + m_ClientChannel.m_reliableStream.WriteString(string); +} + +void BaseClient::ReplySpawn(int spawncount, int crcMap) +{ + BitBuffer msg(MAX_POSSIBLE_MSG); + + m_CRC_Value = crcMap; + COM_UnMunge2((unsigned char *)&m_CRC_Value, 4, (-1 - m_World->GetServerCount()) & 0xFF); + + if (m_World->GetServerCount() != spawncount) { + ReplyNew(); + return; + } + + m_World->WriteSigonData(&msg); + WriteSpawn(&msg); + + m_ClientChannel.CreateFragmentsFromBuffer(msg.GetData(), msg.CurrentSize(), FRAG_NORMAL_STREAM, nullptr); + m_ClientChannel.FragSend(); + m_LastFrameSeqNr = 0; +} + +void BaseClient::ReplyNew() +{ + BitBuffer msg(MAX_POSSIBLE_MSG); + + Reset(); + m_World->WriteNewData(&msg); + + m_ClientChannel.CreateFragmentsFromBuffer(msg.GetData(), msg.CurrentSize(), FRAG_NORMAL_STREAM, nullptr); + m_ClientChannel.FragSend(); + msg.Free(); +} + +void BaseClient::ReplyFullUpdate() +{ + BitBuffer msg(1024 * 9); + for (int i = 0; i < m_World->GetMaxClients(); i++) { + m_World->WriteClientUpdate(&msg, i); + } + + m_ClientChannel.CreateFragmentsFromBuffer(msg.GetData(), msg.CurrentSize(), FRAG_NORMAL_STREAM, nullptr); + m_ClientChannel.FragSend(); +} + +void BaseClient::SetState(ClientState newState) +{ + if (newState == m_ClientState) + return; + + bool stateError = false; + switch (newState) + { + case CLIENT_INITIALIZING: + break; + case CLIENT_CONNECTING: + { + if (m_ClientState != CLIENT_INITIALIZING + && m_ClientState != CLIENT_RUNNING) { + stateError = true; + } + + m_ClientChannel.SetKeepAlive(true); + m_ClientChannel.SetTimeOut(60); + break; + } + case CLIENT_RUNNING: + { + if (m_ClientState != CLIENT_CONNECTING) { + stateError = true; + } + + m_ClientChannel.SetKeepAlive(false); + m_ClientChannel.SetTimeOut(30); + break; + } + case CLIENT_DISCONNECTED: + { + m_ClientChannel.SetKeepAlive(true); + break; + } + default: + stateError = true; + break; + } + + if (stateError) + { + m_System->Errorf("Client::SetState: not valid m_ClientState (%i -> %i).\n", m_ClientState, newState); + return; + } + + m_ClientState = newState; +} + +void BaseClient::WriteSpawn(BitBuffer *stream) +{ + stream->WriteByte(svc_time); + stream->WriteFloat(1); + + for (int i = 0; i < m_World->GetMaxClients(); i++) { + m_World->WriteClientUpdate(stream, i); + } + + m_World->WriteLightStyles(stream); + + stream->WriteByte(svc_signonnum); + stream->WriteByte(1); +} + +void BaseClient::WriteDatagram(double time, frame_t *frame) +{ + if (!frame) { + return; + } + + if (!m_LastFrameSeqNr || m_LastFrameSeqNr > frame->seqnr) { + m_LastFrameSeqNr = frame->seqnr - 1; + m_ClientDelta = 0; + m_DeltaFrameSeqNr = 0; + } + + if (m_LastFrameSeqNr >= frame->seqnr && m_ClientChannel.GetIdleTime() <= 2) { + return; + } + + m_ClientChannel.m_unreliableStream.WriteByte(svc_time); + m_ClientChannel.m_unreliableStream.WriteFloat(float(time)); + m_World->WriteFrame(frame, m_LastFrameSeqNr, &m_ClientChannel.m_reliableStream, &m_ClientChannel.m_unreliableStream, m_DeltaFrameSeqNr, m_ClientDelta, IsHearingVoices()); + + if (m_VoiceQuery) { + QueryVoiceEnabled(&m_ClientChannel.m_unreliableStream); + } + + if (m_ClientChannel.m_reliableStream.IsOverflowed()) { + Disconnect("Reliable data stream overflow.\n"); + return; + } + + if (m_ClientChannel.m_unreliableStream.IsOverflowed()) { + m_System->DPrintf("Unreliable data stream overflow.\n"); + m_ClientChannel.m_unreliableStream.Clear(); + m_LastFrameSeqNr = 0; + } + + m_LastFrameSeqNr = frame->seqnr; + m_SeqNrMap[m_ClientChannel.m_outgoing_sequence & 0xFF] = frame->seqnr; + m_ClientChannel.TransmitOutgoing(); +} + +void BaseClient::ParseDelta(NetPacket *packet) +{ + if (m_ClientState != CLIENT_RUNNING) { + packet->data.SkipBytes(1); + return; + } + + m_ClientDelta = packet->data.ReadByte(); + m_DeltaFrameSeqNr = m_SeqNrMap[m_ClientDelta]; +} + +char *BaseClient::GetStatusLine() +{ + float in, out; + static char string[256]; + + m_ClientChannel.GetFlowStats(&in, &out); + _snprintf(string, sizeof(string), + "ID: %i, Name \"%s\", Time %s, IP %s, In %.2f, Out %.2f.\n", + GetSerial(), + m_ClientName, + COM_FormatTime(float(m_SystemTime - m_ClientChannel.m_connect_time)), + m_ClientChannel.GetTargetAddress()->ToString(), + in, + out + ); + + return string; +} + +void BaseClient::SetWorld(IWorld *world) +{ + if (m_World && m_World != world) { + Reconnect(); + } + + m_World = world; +} + +void BaseClient::Reconnect() +{ + Reset(); + m_ClientChannel.m_reliableStream.WriteByte(svc_stufftext); + m_ClientChannel.m_reliableStream.WriteString("reconnect\n"); +} + +bool BaseClient::IsActive() +{ + return (m_ClientState == CLIENT_RUNNING); +} + +void BaseClient::ParseVoiceData(NetPacket *packet) +{ + int nDataLength = packet->data.ReadShort(); + packet->data.SkipBytes(nDataLength); +} + +void BaseClient::ParseMove(NetPacket *packet) +{ + int nDataLength = packet->data.ReadByte(); + packet->data.SkipBytes(nDataLength + 1); +} + +void BaseClient::SetName(char *newName) +{ + char temp[32]; + COM_RemoveEvilChars(newName); + COM_TrimSpace(newName, temp); + + if (strlen(temp) > sizeof(temp) - 1) { + temp[sizeof(temp) - 1] = '\0'; + } + + if (!temp[0] || !_stricmp(temp, "console")) { + strcpy(temp, "unnamed"); + } + + strcopy(m_ClientName, temp); + m_Userinfo.SetValueForKey("name", m_ClientName); +} + +void BaseClient::ParseHLTV(NetPacket *packet) +{ + Disconnect("TODO Client::ParseHLTV: Invalid client command.\n"); +} + +void BaseClient::ParseCvarValue(NetPacket *packet) +{ + packet->data.ReadString(); +} + +void BaseClient::ParseCvarValue2(NetPacket *packet) +{ + packet->data.ReadWord(); + packet->data.ReadString(); + packet->data.ReadString(); +} + +void BaseClient::QueryVoiceEnabled(BitBuffer *stream) +{ + int reqState = m_World->FindUserMsgByName("ReqState"); + if (!reqState || !m_World->IsVoiceEnabled()) + return; + + stream->WriteByte(reqState); +} + +void BaseClient::UpdateVoiceMask(BitBuffer *stream) +{ + int voiceMask = m_World->FindUserMsgByName("VoiceMask"); + if (!voiceMask) { + m_System->DPrintf("WARNING! Client::UpdateVoiceMask(): could find User Msg VoiceMaks.\n"); + return; + } + + if (!m_World->IsVoiceEnabled()) { + return; + } + + CPlayerBitVec gameRulesMask; + gameRulesMask[m_World->GetSlotNumber()] = !!m_VoiceEnabled; + + if (gameRulesMask != m_SentGameRulesMask || m_BanMask != m_SentBanMask) + { + m_SentGameRulesMask = gameRulesMask; + m_SentBanMask = m_BanMask; + + stream->WriteByte(voiceMask); + for (int dw = 0; dw < VOICE_MAX_PLAYERS_DW; dw++) + { + stream->WriteLong(gameRulesMask.GetDWord(dw)); + stream->WriteLong(m_BanMask.GetDWord(dw)); + } + } +} + +bool BaseClient::IsHearingVoices() +{ + return m_BanMask[m_World->GetSlotNumber()] ? true : false; +} + +bool BaseClient::HasChatEnabled() +{ + return false; +} + +void BaseClient::DownloadFile(char *fileName) +{ + if (!fileName || !fileName[0]) + return; + + DownloadFailed(fileName); +} + +void BaseClient::DownloadFailed(char *fileName) +{ + if (!fileName || !fileName[0]) + return; + + m_ClientChannel.m_reliableStream.WriteByte(svc_filetxferfailed); + m_ClientChannel.m_reliableStream.WriteString(fileName); +} + +void BaseClient::Send(unsigned char *data, int length, bool isReliable) +{ + if (isReliable) + m_ClientChannel.m_reliableStream.WriteBuf(data, length); + else + m_ClientChannel.m_unreliableStream.WriteBuf(data, length); +} + +NetAddress *BaseClient::GetAddress() +{ + return m_ClientChannel.GetTargetAddress(); +} + +int BaseClient::GetClientType() +{ + return m_ClientType; +} + +char *BaseClient::GetType() +{ + return CLIENT_INTERFACE_VERSION; +} + +void BaseClient::SendDatagram() +{ + frame_t *frame = m_World->GetLastFrame(); + if (frame) { + WriteDatagram(frame->time, frame); + } +} + +void BaseClient::Reset() +{ + SetState(CLIENT_CONNECTING); + + m_LastFrameSeqNr = 0; + m_DeltaFrameSeqNr = 0; + m_ClientDelta = 0; + + memset(m_SeqNrMap, 0, sizeof(m_SeqNrMap)); + m_VoiceQuery = true; + m_ClientChannel.Clear(); +} diff --git a/rehlds/HLTV/common/BaseClient.h b/rehlds/HLTV/common/BaseClient.h new file mode 100644 index 0000000..7ef1c50 --- /dev/null +++ b/rehlds/HLTV/common/BaseClient.h @@ -0,0 +1,176 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include +#include +#include + +#include "InfoString.h" +#include "NetChannel.h" +#include "BaseSystemModule.h" + +class TokenLine; +class BaseClient: public IClient, public BaseSystemModule { +public: + BaseClient() {} + virtual ~BaseClient() {} + + virtual bool Init(IBaseSystem *system, int serial, char *name); + virtual void RunFrame(double time); + virtual char *GetStatusLine(); + virtual char *GetType(); + virtual void ShutDown(); + virtual bool Connect(INetSocket *socket, NetAddress *adr, char *userinfo); + virtual void Disconnect(const char *reason = nullptr); + virtual void Reconnect(); + virtual void SetWorld(IWorld *world); + virtual bool IsHearingVoices(); + virtual bool HasChatEnabled(); + virtual NetAddress *GetAddress(); + virtual InfoString *GetUserInfo(); + virtual int GetClientType(); + virtual char *GetClientName(); + virtual bool IsActive(); + virtual void Send(unsigned char *data, int length, bool isReliable); + virtual void DownloadFailed(char *fileName); + virtual void DownloadFile(char *fileName); + virtual void UpdateVoiceMask(BitBuffer *stream); + virtual void QueryVoiceEnabled(BitBuffer *stream); + virtual void SetName(char *newName); + virtual void WriteSpawn(BitBuffer *stream); + virtual void WriteDatagram(double time, frame_t *frame); + virtual void SendDatagram(); + virtual void Reset(); + + enum ClientState { + CLIENT_UNDEFINED = 0, + CLIENT_INITIALIZING, + CLIENT_CONNECTING, + CLIENT_RUNNING, + CLIENT_DISCONNECTED + }; + + virtual void SetState(ClientState newState); + virtual void ReplyNew(); + virtual void ReplySpawn(int spawncount, int crcMap); + virtual void ReplyFullUpdate(); + virtual void PrintfToClient(char *fmt, ...); + virtual void UpdateUserInfo(char *userinfostring = nullptr); + virtual void ParseStringCmd(NetPacket *packet); + virtual void ParseNop(NetPacket *packet); + virtual void ParseBad(NetPacket *packet); + virtual void ParseMove(NetPacket *packet); + virtual void ParseVoiceData(NetPacket *packet); + virtual void ParseHLTV(NetPacket *packet); + virtual void ParseDelta(NetPacket *packet); + virtual void ParseCvarValue(NetPacket *packet); + virtual void ParseCvarValue2(NetPacket *packet); + virtual void ProcessMessage(NetPacket *packet); + virtual bool ProcessStringCmd(char *string); + +private: + enum LocalCommandIDs { + CMD_ID_NAME = 1, + CMD_ID_SPAWN, + CMD_ID_NEW, + CMD_ID_FULLUPDATE, + CMD_ID_DROPCLIENT, + CMD_ID_DLFILE, + CMD_ID_SETINFO, + CMD_ID_SHOWINFO, + CMD_ID_SENDENTS, + CMD_ID_VMODENABLE, + CMD_ID_VBAN, + CMD_ID_SENDERS, + CMD_ID_PING, + CMD_ID_SPECTATE, + CMD_ID_SPK, + CMD_ID_PAUSE, + CMD_ID_UNPAUSE, + CMD_ID_SETPAUSE + }; + + void CMD_Name(TokenLine *cmd); + void CMD_Spawn(TokenLine *cmd); + void CMD_New(TokenLine *cmd); + void CMD_FullUpdate(TokenLine *cmd); + void CMD_DropClient(TokenLine *cmd); + void CMD_DownloadFile(TokenLine *cmd); + void CMD_SetInfo(TokenLine *cmd); + void CMD_ShowInfo(TokenLine *cmd); + void CMD_SendEntities(TokenLine *cmd); + void CMD_VoiceModEnable(TokenLine *cmd); + void CMD_VoiceBan(TokenLine *cmd); + void CMD_SendResources(TokenLine *cmd); + void CMD_RequestPing(TokenLine *cmd); + void CMD_Spectate(TokenLine *cmd); + void CMD_Spk(TokenLine *cmd); + void CMD_Pause(TokenLine *cmd); + void CMD_UnPause(TokenLine *cmd); + void CMD_SetPause(TokenLine *cmd); + + struct LocalCommandID_s { + char *name; + LocalCommandIDs id; + void (BaseClient::*pfnCmd)(TokenLine *cmd); + }; + static LocalCommandID_s m_LocalCmdReg[]; + +protected: + struct clc_func_s { + clc_commands opcode; + char *pszname; + void (BaseClient::*func)(NetPacket *packet); + }; + static clc_func_s m_ClientFuncs[]; + + IWorld *m_World; + INetSocket *m_Socket; + NetChannel m_ClientChannel; + + int m_ClientType; + int m_ClientState; + char m_ClientName[32]; + + enum { MAX_USER_INFO = 256 }; + InfoString m_Userinfo; + unsigned int m_LastFrameSeqNr; + unsigned int m_DeltaFrameSeqNr; + unsigned int m_ClientDelta; + unsigned int m_SeqNrMap[256]; + + int m_CRC_Value; + bool m_VoiceEnabled; + bool m_VoiceQuery; + + CPlayerBitVec m_SentGameRulesMask; + CPlayerBitVec m_SentBanMask; + CPlayerBitVec m_BanMask; +}; diff --git a/rehlds/HLTV/common/BitBuffer.cpp b/rehlds/HLTV/common/BitBuffer.cpp new file mode 100644 index 0000000..4c9f872 --- /dev/null +++ b/rehlds/HLTV/common/BitBuffer.cpp @@ -0,0 +1,887 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +const uint32 BITTABLE[] = +{ + 0x00000001, 0x00000002, 0x00000004, 0x00000008, + 0x00000010, 0x00000020, 0x00000040, 0x00000080, + 0x00000100, 0x00000200, 0x00000400, 0x00000800, + 0x00001000, 0x00002000, 0x00004000, 0x00008000, + 0x00010000, 0x00020000, 0x00040000, 0x00080000, + 0x00100000, 0x00200000, 0x00400000, 0x00800000, + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x00000000, +}; + +const uint32 ROWBITTABLE[] = +{ + 0x00000000, 0x00000001, 0x00000003, 0x00000007, + 0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F, + 0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF, + 0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF, + 0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF, + 0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF, + 0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, + 0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, + 0xFFFFFFFF, +}; + +const uint32 INVBITTABLE[] = +{ + 0xFFFFFFFE, 0xFFFFFFFD, 0xFFFFFFFB, 0xFFFFFFF7, + 0xFFFFFFEF, 0xFFFFFFDF, 0xFFFFFFBF, 0xFFFFFF7F, + 0xFFFFFEFF, 0xFFFFFDFF, 0xFFFFFBFF, 0xFFFFF7FF, + 0xFFFFEFFF, 0xFFFFDFFF, 0xFFFFBFFF, 0xFFFF7FFF, + 0xFFFEFFFF, 0xFFFDFFFF, 0xFFFBFFFF, 0xFFF7FFFF, + 0xFFEFFFFF, 0xFFDFFFFF, 0xFFBFFFFF, 0xFF7FFFFF, + 0xFEFFFFFF, 0xFDFFFFFF, 0xFBFFFFFF, 0xF7FFFFFF, + 0xEFFFFFFF, 0xDFFFFFFF, 0xBFFFFFFF, 0x7FFFFFFF, + 0xFFFFFFFF, +}; + +BitBuffer::BitBuffer() : m_Data(nullptr), + m_CurByte(nullptr), + m_CurSize(0), + m_MaxSize(0), + m_Overflowed(false), + m_LittleEndian(false), + m_OwnData(false) +{ + ; +} + +BitBuffer::BitBuffer(void *newData, unsigned int size) +{ + m_Data = (unsigned char *)newData; + m_CurByte = m_Data; + + m_CurSize = 0; + m_MaxSize = size; + m_Overflowed = false; + m_LittleEndian = true; + m_OwnData = false; +} + +BitBuffer::~BitBuffer() +{ + Free(); +} + +BitBuffer::BitBuffer(unsigned int size) +{ + m_Data = nullptr; + m_CurByte = nullptr; + + m_CurSize = 0; + m_MaxSize = size; + m_Overflowed = false; + m_LittleEndian = false; + m_OwnData = false; + + Resize(size); +} + +bool BitBuffer::Resize(unsigned int size) +{ + Free(); + + m_Data = (unsigned char *)Mem_ZeroMalloc(size + 4); + m_CurSize = 0; + m_Overflowed = false; + + if (m_Data) + { + m_CurByte = m_Data; + m_MaxSize = size; + + m_OwnData = true; + m_LittleEndian = true; + return true; + } + + m_MaxSize = 0; + m_OwnData = false; + m_CurByte = nullptr; + + return false; +} + +void BitBuffer::Clear() +{ + memset(m_Data, 0, m_MaxSize); + + m_CurByte = m_Data; + m_CurSize = 0; + + m_Overflowed = false; + m_LittleEndian = true; +} + +int BitBuffer::CurrentBit() +{ + return m_CurSize + 8 * (m_CurByte - m_Data); +} + +void BitBuffer::Reset() +{ + m_CurByte = m_Data; + m_CurSize = 0; + + m_Overflowed = false; + m_LittleEndian = true; +} + +void BitBuffer::Free() +{ + if (m_Data && m_OwnData) { + free(m_Data); + } + + m_Data = nullptr; + m_CurByte = nullptr; + + m_CurSize = 0; + m_MaxSize = 0; + + m_OwnData = false; + m_Overflowed = false; + m_LittleEndian = true; +} + +unsigned int BitBuffer::ReadBits(int numbits) +{ + unsigned int result = 0; + if (m_LittleEndian) + { + if (m_CurByte - m_Data >= m_MaxSize) + { + m_Overflowed = true; + return -1; + } + + int bits = m_CurSize + numbits; + if (bits <= 32) + { + result = (*(unsigned int *)m_CurByte >> m_CurSize) & ROWBITTABLE[numbits]; + + m_CurByte += numbits >> 3; + m_CurSize += numbits & 7; + + if (m_CurSize > 7) + { + m_CurSize &= 7; + m_CurByte++; + } + } + else + { + unsigned int data = *(unsigned int *)m_CurByte >> m_CurSize; + m_CurByte += 4; + result = ((ROWBITTABLE[bits & 7] & *(unsigned int *)m_CurByte) << (32 - m_CurSize)) | data; + m_CurSize = bits & 7; + } + } + else + { + int d = numbits; + while (d > 0) + { + --d; + if (ReadBit()) { + result |= (1 << d); + } + } + } + + return result; +} + +int BitBuffer::ReadBit() +{ + int result; + if (m_CurByte - m_Data >= m_MaxSize) { + m_Overflowed = true; + result = -1; + } + else + { + if (m_LittleEndian) + { + if (m_CurSize == 7) + { + m_CurSize = 0; + result = (*m_CurByte++ >> 7) & 1; + } + else + { + result = ((unsigned int)*m_CurByte >> m_CurSize++) & 1; + } + } + else + { + if (m_CurSize == 7) + { + m_CurSize = 0; + result = *m_CurByte++ & 1; + } + else + { + result = ((unsigned int)*m_CurByte >> (7 - m_CurSize++)) & 1; + } + } + } + + return result; +} + +unsigned int BitBuffer::PeekBits(int numbits) +{ + int oldcurrentBit = m_CurSize; + unsigned char *oldcurrentByte = m_CurByte; + unsigned int data = ReadBits(numbits); + + m_CurSize = oldcurrentBit; + m_CurByte = oldcurrentByte; + return data; +} + +int BitBuffer::ReadChar() +{ + return ReadBits(8); +} + +int BitBuffer::ReadByte() +{ + return ReadBits(8); +} + +int BitBuffer::ReadShort() +{ + return ReadBits(16); +} + +int BitBuffer::ReadWord() +{ + return ReadBits(16); +} + +unsigned int BitBuffer::ReadLong() +{ + return ReadBits(32); +} + +float BitBuffer::ReadFloat() +{ + union { + float f; + int i; + } dat; + + dat.i = _LittleLong(ReadLong()); + return dat.f; +} + +bool BitBuffer::ReadBuf(int iSize, void *pbuf) +{ + if (m_CurByte - m_Data + iSize > m_MaxSize) { + m_Overflowed = true; + return false; + } + + if (m_CurSize) + { + int i, j; + unsigned int *p = (unsigned int *)pbuf; + for (i = 4; i < iSize; i += 4) { + *p++ = ReadLong(); + } + + for (j = 0; j < iSize - (i - 4); ++j) { + *((unsigned char *)p + j) = ReadByte(); + } + } + else + { + memcpy(pbuf, m_CurByte, iSize); + m_CurByte += iSize; + } + + return true; +} + +void BitBuffer::WriteBuf(BitBuffer *buf, int length) +{ + WriteBuf(buf->m_CurByte, length); + buf->SkipBytes(length); +} + +char *BitBuffer::ReadString() +{ + int c = 0, l = 0; + static char string[8192]; + + while ((c = ReadChar(), c) && c != -1 && l < sizeof(string) - 1) { + string[l++] = c; + } + + string[l] = '\0'; + return string; +} + +char *BitBuffer::ReadStringLine() +{ + int c = 0, l = 0; + static char string[2048]; + + while ((c = ReadChar(), c) && c != '\n' && c != -1 && l < sizeof(string) - 1) { + string[l++] = c; + } + + string[l] = '\0'; + return string; +} + +float BitBuffer::ReadAngle() +{ + int c = ReadChar(); + if (c == -1) + { + // FIXED: Added check for wrong value, but just return 0 instead of -1 * (360.0 / 256) + return 0; + } + + return (float)(c * (360.0 / 256)); +} + +float BitBuffer::ReadHiresAngle() +{ + int c = ReadShort(); + if (c == -1) + { + // FIXED: Added check for wrong value, but just return 0 instead of -1 * (360.0 / 65536) + return 0; + } + + return (float)(c * (360.0 / 65536)); +} + +void BitBuffer::WriteBit(int c) +{ + if (m_CurByte - m_Data >= m_MaxSize) { + m_Overflowed = true; + return; + } + + if (m_LittleEndian) + { + if (m_CurSize == 7) + { + if (c) + { + m_CurByte[0] |= 0x80u; + } + else + { + m_CurByte[0] &= 0x7Fu; + } + + m_CurByte++; + m_CurSize = 0; + } + else + { + if (c) + { + m_CurByte[0] |= BITTABLE[ m_CurSize ]; + } + else + { + m_CurByte[0] &= INVBITTABLE[ m_CurSize ]; + } + + m_CurSize++; + } + } + else + { + static unsigned char masks[] = { 0x80u, 0x40u, 0x20u, 0x10u, 0x8u, 0x4u, 0x2u, 0x1u }; + static unsigned char inv_masks[] = { 0x7Fu, 0xBFu, 0xDFu, 0xEFu, 0xF7u, 0xFBu, 0xFDu, 0xFEu }; + + if (c) + m_CurByte[0] |= masks[ m_CurSize ]; + else + m_CurByte[0] &= inv_masks[ m_CurSize ]; + + if (++m_CurSize == 8) + { + m_CurSize = 0; + m_CurByte++; + } + } +} + +void BitBuffer::WriteBits(unsigned int data, int numbits) +{ + if (m_Overflowed) { + return; + } + + if (m_LittleEndian) + { + if (m_CurByte - m_Data + (numbits >> 8) > m_MaxSize) { + m_Overflowed = true; + return; + } + + int bits = numbits + m_CurSize; + if (bits <= 32) + { + *(uint32 *)m_CurByte |= (ROWBITTABLE[numbits] & data) << m_CurSize; + + m_CurByte = &m_CurByte[numbits >> 3]; + m_CurSize = m_CurSize + (numbits & 7); + + if (m_CurSize > 7) + { + m_CurSize = m_CurSize & 7; + m_CurByte = m_CurByte + 1; + } + } + else + { + *(uint32 *)m_CurByte |= (ROWBITTABLE[numbits] & data) << m_CurSize; + + int leftBits = (32 - m_CurSize); + m_CurSize = (m_CurSize + numbits) & 7; + + m_CurByte += 4; + *(uint32 *)m_CurByte |= (ROWBITTABLE[numbits] & data) >> leftBits; + } + + return; + } + + int nBitValue = data; + if (numbits <= 31 && nBitValue >= (1 << numbits) && nBitValue != -1) + nBitValue = (1 << numbits) - 1; + + while (--numbits > 0) + { + if (m_CurByte - m_Data >= m_MaxSize) { + m_Overflowed = true; + break; + } + + WriteBit(nBitValue & (1 << numbits)); + } +} + +void BitBuffer::WriteSBits(int data, int numbits) +{ + int idata = data; + if (numbits < 32) + { + int maxnum = (1 << (numbits - 1)) - 1; + if (data > maxnum || (maxnum = -maxnum, data < maxnum)) + { + idata = maxnum; + } + } + + int sigbits = idata < 0; + + WriteBit(sigbits); + WriteBits(abs(idata), numbits - 1); +} + +void BitBuffer::WriteChar(int c) +{ + WriteBits(c, 8 * sizeof(char)); +} + +void BitBuffer::WriteByte(int c) +{ + WriteBits(c, 8 * sizeof(uint8)); +} + +void BitBuffer::WriteShort(int c) +{ + WriteBits(c, 8 * sizeof(int16)); +} + +void BitBuffer::WriteWord(int c) +{ + WriteBits(c, 8 * sizeof(uint16)); +} + +void BitBuffer::WriteLong(unsigned int c) +{ + WriteBits(c, 8 * sizeof(uint32)); +} + +void BitBuffer::WriteFloat(float f) +{ + union { + float f; + int i; + } dat; + + dat.f = f; + WriteBits(_LittleLong(dat.i), 8 * sizeof(float)); +} + +void BitBuffer::WriteString(const char *p) +{ + if (p) + { + WriteBuf(p, strlen(p) + 1); + } + else + { + WriteChar('\0'); + } +} + +void BitBuffer::WriteBuf(const void *buf, int iSize) +{ + if (!buf || m_Overflowed || !iSize) { + return; + } + + if (m_CurByte - m_Data + iSize > m_MaxSize) { + m_Overflowed = true; + return; + } + + if (m_CurSize) + { + int i, j; + unsigned int *p = (unsigned int *)buf; + + for (i = 4; i < iSize; i += 4) { + WriteLong(*p++); + } + + for (j = 0; j < (iSize - (i - 4)); j++) { + WriteChar(*((unsigned char *)p + j)); + } + } + else + { + memcpy(m_CurByte, buf, iSize); + m_CurByte += iSize; + } +} + +void BitBuffer::WriteBitData(void *src, int length) +{ + int i; + unsigned char *p = (unsigned char *)src; + for (i = 0; i < length; i++, p++) + { + WriteChar(*p); + } +} + +void BitBuffer::WriteAngle(float f) +{ + WriteByte((int64)(fmod((double)f, 360.0) * 256.0 / 360.0) & 0xFF); +} + +void BitBuffer::WriteHiresAngle(float f) +{ + WriteShort((int64)(fmod((double)f, 360.0) * 65536.0 / 360.0) & 0xFFFF); +} + +int BitBuffer::CurrentSize() +{ + return (m_CurSize != 0) + m_CurByte - m_Data; +} + +unsigned char *BitBuffer::CurrentByte() +{ + return m_CurByte; +} + +int BitBuffer::SpaceLeft() +{ + return m_MaxSize + m_Data - m_CurByte; +} + +void BitBuffer::AlignByte() +{ + if (m_CurSize) + { + m_CurByte++; + m_CurSize = 0; + } +} + +int BitBuffer::ReadSBits(int numbits) +{ + int nSignBit = ReadBit(); + int result = ReadBits(numbits - 1); + + if (nSignBit) + { + result = -result; + } + + return result; +} + +float BitBuffer::ReadBitAngle(int numbits) +{ + return (float)(ReadBits(numbits) * (360.0 / (1 << numbits))); +} + +void BitBuffer::WriteBitAngle(float fAngle, int numbits) +{ + if (numbits >= 32) { + m_Overflowed = true; + return; + } + + unsigned int shift = (1 << numbits); + unsigned int mask = shift - 1; + + int d = (int)(shift * fmod((double)fAngle, 360.0)) / 360; + d &= mask; + + WriteBits(d, numbits); +} + +char *BitBuffer::ReadBitString() +{ + static char buf[8192]; + buf[0] = '\0'; + + char *p = &buf[0]; + for (unsigned int c = ReadChar(); c; c = ReadChar()) + { + // Prevent infinite cycle if m_Overflowed + if (m_Overflowed) { + break; + } + + *p++ = c; + } + + *p = '\0'; + return buf; +} + +void BitBuffer::WriteBitString(const char *p) +{ + const unsigned char *pch = (unsigned char *)p; + while (*pch) + { + WriteChar(*pch++); + } + + WriteChar('\0'); +} + +void BitBuffer::StartBitMode() +{ + if (m_CurSize) { + m_Overflowed = true; + } +} + +void BitBuffer::EndBitMode() +{ + AlignByte(); +} + +int BitBuffer::ReadBitData(void *dest, int length) +{ + unsigned char *p = (unsigned char *)dest; + for (int i = 0; i < length; i++) { + *p++ = ReadByte(); + } + + return length; +} + +void BitBuffer::SetBuffer(void *buffer, int size) +{ + Free(); + + m_Data = (unsigned char *)buffer; + m_CurByte = (unsigned char *)buffer; + m_MaxSize = size; + m_CurSize = 0; + + m_OwnData = false; + m_Overflowed = false; + m_LittleEndian = true; +} + +void BitBuffer::ReadBitVec3Coord(float *fa) +{ + int xflag = ReadBit(); + int yflag = ReadBit(); + int zflag = ReadBit(); + + if (xflag) + fa[0] = ReadBitCoord(); + if (yflag) + fa[1] = ReadBitCoord(); + if (zflag) + fa[2] = ReadBitCoord(); +} + +float BitBuffer::ReadBitCoord() +{ + float value = 0; + + int intval = ReadBit(); + int fractval = ReadBit(); + + if (intval || fractval) + { + int signbit = ReadBit(); + if (intval) { + intval = ReadBits(12); + } + + if (fractval) { + fractval = ReadBits(3); + } + + value = (float)(fractval / 8.0 + intval); + if (signbit) { + value = -value; + } + } + + return value; +} + +float BitBuffer::ReadCoord() +{ + return (float)(ReadShort() * (1.0 / 8)); +} + +void BitBuffer::SkipBytes(int numbits) +{ + if (numbits + m_CurByte - m_Data > m_MaxSize) { + m_Overflowed = true; + } + + m_CurByte += numbits; +} + +void BitBuffer::SkipBits(int numbits) +{ + if (m_LittleEndian) + { + if (m_CurByte - m_Data >= m_MaxSize) + { + m_Overflowed = true; + return; + } + + int bits = m_CurSize + numbits; + if (bits <= 32) + { + m_CurByte += numbits >> 3; + m_CurSize += numbits & 7; + + if (m_CurSize > 7) + { + m_CurSize &= 7; + m_CurByte++; + } + } + else + { + m_CurByte += 4; + m_CurSize = bits & 7; + } + } + else + { + int d = numbits; + while (d > 0) + { + --d; + if (m_CurSize == 7) + { + m_CurByte++; + m_CurSize = 0; + } + else + { + m_CurSize++; + } + } + } +} + +int BitBuffer::SkipString() +{ + int c = 0, l = 1; + const int maxString = 8192; + + while ((c = ReadChar(), c) && c != -1 && l < maxString) { + l++; + } + + return l; +} + +void BitBuffer::FastClear() +{ + int iSize = CurrentSize() + 4; + if (iSize > m_MaxSize) { + iSize = m_MaxSize; + } + + memset(m_Data, 0, iSize); + + m_CurByte = m_Data; + m_CurSize = 0; + + m_Overflowed = false; + m_LittleEndian = true; +} + +void BitBuffer::ConcatBuffer(BitBuffer *buffer) +{ + WriteBuf(buffer->m_Data, buffer->CurrentSize()); +} + +void BitBuffer::WriteCoord(float f) +{ + WriteShort((int)(f * 8.0)); +} diff --git a/rehlds/HLTV/common/BitBuffer.h b/rehlds/HLTV/common/BitBuffer.h new file mode 100644 index 0000000..119e6b6 --- /dev/null +++ b/rehlds/HLTV/common/BitBuffer.h @@ -0,0 +1,117 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +class BitBuffer { +public: + BitBuffer(); + BitBuffer(unsigned int size); + BitBuffer(void *newData, unsigned int size); + virtual ~BitBuffer(); + + unsigned int PeekBits(int numbits); + int CurrentBit(); + int CurrentSize(); + unsigned char *CurrentByte(); + + int GetMaxSize() const { return m_MaxSize; } + unsigned int GetCurSize() const { return m_CurSize; } + unsigned char *GetData() const { return m_Data; } + bool IsOverflowed() const { return m_Overflowed; } + + void SetBuffer(void *buffer, int size); + void Free(); + void Reset(); + void Clear(); + void FastClear(); + int SpaceLeft(); + void EndBitMode(); + void StartBitMode(); + bool Resize(unsigned int size); + void ConcatBuffer(BitBuffer *buffer); + int SkipString(); + + void SkipBits(int numbits); + void SkipBytes(int numbits); + + void AlignByte(); + + // function's read stuff + unsigned int ReadBits(int numbits); + int ReadBit(); + int ReadChar(); + int ReadByte(); + int ReadShort(); + int ReadWord(); + + unsigned int ReadLong(); + float ReadFloat(); + char *ReadString(); + char *ReadStringLine(); + char *ReadBitString(); + int ReadSBits(int numbits); + float ReadBitAngle(int numbits); + int ReadBitData(void *dest, int length); + bool ReadBuf(int iSize, void *pbuf); + float ReadAngle(); + float ReadHiresAngle(); + float ReadCoord(); + float ReadBitCoord(); + void ReadBitVec3Coord(float *fa); + + // function's write stuff + void WriteBits(unsigned int data, int numbits); + void WriteBit(int c); + void WriteChar(int c); + void WriteByte(int c); + void WriteShort(int c); + void WriteWord(int c); + void WriteLong(unsigned int c); + void WriteFloat(float f); + void WriteString(const char *p); + void WriteBitString(const char *p); + void WriteSBits(int data, int numbits); + void WriteBitAngle(float fAngle, int numbits); + void WriteBitData(void *src, int length); + void WriteBuf(const void *buf, int iSize); + void WriteBuf(BitBuffer *buf, int length); + void WriteAngle(float f); + void WriteHiresAngle(float f); + void WriteCoord(float f); + + bool m_Overflowed; + unsigned char *m_Data; + unsigned char *m_CurByte; + int m_CurSize; + int m_MaxSize; + +protected: + bool m_LittleEndian; + bool m_OwnData; +}; diff --git a/rehlds/HLTV/common/DemoFile.cpp b/rehlds/HLTV/common/DemoFile.cpp new file mode 100644 index 0000000..f676d8e --- /dev/null +++ b/rehlds/HLTV/common/DemoFile.cpp @@ -0,0 +1,604 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +DemoFile::DemoFile() : + m_FileSystem(nullptr), + m_FileHandle(FILESYSTEM_INVALID_HANDLE), + m_DemoChannel(nullptr), + m_Entries(nullptr) +{ + Reset(); +} + +void DemoFile::Init(IWorld *world, IServer *server, NetChannel *channel) +{ + m_World = world; + m_Server = server; + m_System = world->GetSystem(); + m_FileSystem = m_System->GetFileSystem(); + + m_DemoChannel = channel; + m_Entries = nullptr; + + Reset(); +} + +void DemoFile::Reset() +{ + memset(m_FileName, 0, sizeof(m_FileName)); + memset(&m_zeroDemoInfo, 0, sizeof(m_zeroDemoInfo)); + + CloseFile(); + m_Continuous = true; +} + +bool DemoFile::IsRecording() +{ + return m_DemoState == DEMO_RECORDING; +} + +void DemoFile::CloseFile() +{ + unsigned char c; + float f; + int curpos; + int i; + + if (m_FileSystem && m_FileHandle != FILESYSTEM_INVALID_HANDLE) + { + if (m_DemoState == DEMO_RECORDING) + { + c = 5; + m_FileSystem->Write(&c, sizeof(unsigned char), m_FileHandle); + + f = _LittleFloat(GetDemoTime()); + m_FileSystem->Write(&f, sizeof(float), m_FileHandle); + + i = _LittleLong(m_frameCount); + m_FileSystem->Write(&i, sizeof(int), m_FileHandle); + + curpos = m_FileSystem->Tell(m_FileHandle); + + m_gameEntry.nFileLength = curpos - m_gameEntry.nOffset; + m_gameEntry.fTrackTime = GetDemoTime(); + m_gameEntry.nFrames = m_frameCount; + + i = 2; + m_FileSystem->Write(&i, sizeof(int), m_FileHandle); + m_FileSystem->Write(&m_loadEntry, sizeof(m_loadEntry), m_FileHandle); + m_FileSystem->Write(&m_gameEntry, sizeof(m_gameEntry), m_FileHandle); + + m_demoHeader.nDirectoryOffset = curpos; + m_FileSystem->Seek(m_FileHandle, 0, FILESYSTEM_SEEK_HEAD); + m_FileSystem->Write(&m_demoHeader, sizeof(m_demoHeader), m_FileHandle); + m_System->Printf("Completed demo %s.\n", m_FileName); + } + + m_FileSystem->Close(m_FileHandle); + } + + if (m_Entries) + { + free(m_Entries); + m_Entries = nullptr; + } + + m_FileHandle = FILESYSTEM_INVALID_HANDLE; + m_DemoState = DEMO_IDLE; +} + +void DemoFile::WriteDemoInfo(demo_info_t *demoInfo) +{ + m_FileSystem->Write(demoInfo, sizeof(*demoInfo), m_FileHandle); +} + +void DemoFile::WriteSequenceInfo() +{ + m_FileSystem->Write(&m_DemoChannel->m_outgoing_sequence, sizeof(int), m_FileHandle); + m_FileSystem->Write(&m_DemoChannel->m_incoming_sequence, sizeof(int), m_FileHandle); + m_FileSystem->Write(&m_DemoChannel->m_last_reliable_sequence, sizeof(int), m_FileHandle); + m_FileSystem->Write(&m_DemoChannel->m_reliable_sequence, sizeof(int), m_FileHandle); + m_FileSystem->Write(&m_DemoChannel->m_incoming_acknowledged, sizeof(int), m_FileHandle); + m_FileSystem->Write(&m_DemoChannel->m_incoming_reliable_sequence, sizeof(int), m_FileHandle); + m_FileSystem->Write(&m_DemoChannel->m_incoming_reliable_acknowledged, sizeof(int), m_FileHandle); +} + +void DemoFile::WriteDemoStartup(BitBuffer *buffer) +{ + unsigned char c; + int i; + float f; + + int len = buffer->CurrentSize(); + if (len <= 0) { + return; + } + + c = 0; + m_FileSystem->Write(&c, sizeof(unsigned char), m_FileHandle); + + f = _LittleFloat(GetDemoTime()); + m_FileSystem->Write(&f, sizeof(float), m_FileHandle); + + i = _LittleLong(m_frameCount); + m_FileSystem->Write(&i, sizeof(int), m_FileHandle); + + WriteDemoInfo(&m_zeroDemoInfo); + WriteSequenceInfo(); + + i = _LittleLong(len); + m_FileSystem->Write(&i, sizeof(int), m_FileHandle); + m_FileSystem->Write(buffer->GetData(), len, m_FileHandle); +} + +void DemoFile::WriteDemoMessage(BitBuffer *unreliableData, BitBuffer *reliableData) +{ + int len; + int i; + float f; + unsigned char c; + + len = reliableData->CurrentSize() + unreliableData->CurrentSize(); + if (len <= 0 || m_FileHandle == FILESYSTEM_INVALID_HANDLE) { + return; + } + + if (!m_FileSystem) { + return; + } + + c = 1; + m_frameCount++; + + m_FileSystem->Write(&c, sizeof(unsigned char), m_FileHandle); + + f = _LittleFloat(GetDemoTime()); + m_FileSystem->Write(&f, sizeof(float), m_FileHandle); + + i = _LittleLong(m_frameCount); + m_FileSystem->Write(&i, sizeof(int), m_FileHandle); + + WriteDemoInfo(&m_zeroDemoInfo); + WriteSequenceInfo(); + + i = _LittleLong(len); + m_FileSystem->Write(&i, sizeof(int), m_FileHandle); + m_FileSystem->Write(unreliableData->GetData(), unreliableData->CurrentSize(), m_FileHandle); + m_FileSystem->Write(reliableData->GetData(), reliableData->CurrentSize(), m_FileHandle); +} + +void DemoFile::WriteUpdateClientData(client_data_t *cdata) +{ + if (!m_FileSystem || m_FileHandle == FILESYSTEM_INVALID_HANDLE) { + return; + } + + unsigned char cmd = 4; + m_FileSystem->Write(&cmd, sizeof(unsigned char), m_FileHandle); + + float f = _LittleFloat(GetDemoTime()); + m_FileSystem->Write(&f, sizeof(float), m_FileHandle); + + int i = _LittleLong(m_frameCount); + m_FileSystem->Write(&i, sizeof(int), m_FileHandle); + m_FileSystem->Write(cdata, sizeof(*cdata), m_FileHandle); +} + +float DemoFile::GetDemoTime() +{ + return m_System->GetTime() - m_startTime; +} + +void DemoFile::WriteSignonData() +{ + BitBuffer buffer(MAX_POSSIBLE_MSG * 2); + m_World->WriteNewData(&buffer); + m_World->WriteSigonData(&buffer); + + buffer.WriteByte(svc_time); + buffer.WriteFloat(GetDemoTime()); + + for (int i = 0; i < m_World->GetMaxClients(); i++) { + m_World->WriteClientUpdate(&buffer, i); + } + + m_World->WriteLightStyles(&buffer); + + buffer.WriteByte(svc_signonnum); + buffer.WriteByte(1); + + WriteDemoStartup(&buffer); +} + +bool DemoFile::StartRecording(char *newName) +{ + unsigned char b; + int i; + float f; + + if (IsPlaying() || !m_FileSystem) { + return false; + } + + if (m_FileHandle != FILESYSTEM_INVALID_HANDLE) { + CloseFile(); + } + + strcopy(m_FileName, newName); + + m_FileHandle = m_FileSystem->Open(m_FileName, "wb"); + if (!m_FileHandle) { + m_System->Printf("WARNING! DemoFile::StartRecording: coudn't open demo file %s.\n", m_FileName); + return false; + } + + memset(&m_demoHeader, 0, sizeof(m_demoHeader)); + strcopy(m_demoHeader.szFileStamp, "HLDEMO"); + + COM_FileBase(m_World->GetLevelName(), m_demoHeader.szMapName); + COM_FileBase(m_World->GetGameDir(), m_demoHeader.szDllDir); + + m_demoHeader.mapCRC = 0; + m_demoHeader.nDemoProtocol = DEMO_VERSION; + m_demoHeader.nNetProtocolVersion = PROTOCOL_VERSION; + m_demoHeader.nDirectoryOffset = 0; + m_FileSystem->Write(&m_demoHeader, sizeof(m_demoHeader), m_FileHandle); + + memset(&m_loadEntry, 0, sizeof(m_loadEntry)); + strcopy(m_loadEntry.szDescription, "LOADING"); + + m_loadEntry.nEntryType = 0; + m_loadEntry.nOffset = m_FileSystem->Tell(m_FileHandle); + + m_frameCount = 0; + m_startTime = m_System->GetTime(); + + WriteSignonData(); + + b = 5; + m_FileSystem->Write(&b, sizeof(unsigned char), m_FileHandle); + + f = _LittleFloat(GetDemoTime()); + m_FileSystem->Write(&f, sizeof(float), m_FileHandle); + + i = _LittleLong(m_frameCount); + m_FileSystem->Write(&i, sizeof(int), m_FileHandle); + + m_loadEntry.nFileLength = m_FileSystem->Tell(m_FileHandle) - m_loadEntry.nOffset; + + memset(&m_gameEntry, 0, sizeof(m_gameEntry)); + _snprintf(m_gameEntry.szDescription, sizeof(m_gameEntry.szDescription), "Playback"); + m_gameEntry.nEntryType = 1; + m_gameEntry.nOffset = m_FileSystem->Tell(m_FileHandle); + + b = 2; + m_FileSystem->Write(&b, sizeof(unsigned char), m_FileHandle); + + f = _LittleFloat(GetDemoTime()); + m_FileSystem->Write(&f, sizeof(float), m_FileHandle); + + i = _LittleLong(m_frameCount); + m_FileSystem->Write(&i, sizeof(int), m_FileHandle); + + m_DemoState = DEMO_RECORDING; + m_System->Printf("Start recording to %s.\n", m_FileName); + + return true; +} + +bool DemoFile::IsPlaying() +{ + return m_DemoState == DEMO_PLAYING; +} + +bool DemoFile::LoadDemo(char *demoname) +{ + if (IsRecording()) { + m_System->Printf("Cannot load demo, still recording.\n"); + return false; + } + + if (!m_FileSystem) { + return false; + } + + CloseFile(); + + strcopy(m_FileName, demoname); + _strlwr(m_FileName); + + if (!strstr(m_FileName, ".dem")) { + strcat(m_FileName, ".dem"); + } + + m_FileHandle = m_FileSystem->Open(m_FileName, "rb"); + if (!m_FileHandle) { + m_System->Printf("Coudn't open demo file %s.\n", m_FileName); + return false; + } + + memset(&m_demoHeader, 0, sizeof(m_demoHeader)); + m_FileSystem->Read(&m_demoHeader, sizeof(m_demoHeader), m_FileHandle); + + if (strcmp(m_demoHeader.szFileStamp, "HLDEMO") != 0) { + m_System->Printf("%s is not a HL demo file.\n", m_FileName); + m_FileSystem->Close(m_FileHandle); + return false; + } + + if (m_demoHeader.nNetProtocolVersion != PROTOCOL_VERSION || m_demoHeader.nDemoProtocol != DEMO_VERSION) { + m_System->Printf("WARNING! %s has an outdated demo format.\n", m_FileName); + } + + int fileMarker = m_FileSystem->Tell(m_FileHandle); + m_FileSystem->Seek(m_FileHandle, m_demoHeader.nDirectoryOffset, FILESYSTEM_SEEK_HEAD); + m_FileSystem->Read(&m_EntryNumber, sizeof(int), m_FileHandle); + m_CurrentEntry = 0; + + if (m_EntryNumber > 0 && m_EntryNumber <= 1024) + { + m_Entries = (demoentry_t *)Mem_ZeroMalloc(m_EntryNumber * MAX_DEMO_ENTRY); + m_FileSystem->Read(m_Entries, m_EntryNumber * MAX_DEMO_ENTRY, m_FileHandle); + m_FileSystem->Seek(m_FileHandle, m_Entries[m_CurrentEntry].nOffset, FILESYSTEM_SEEK_HEAD); + } + else + { + m_EntryNumber = 2; + m_Entries = nullptr; + + m_System->Printf("WARNING! Demo had bogus number of directory entries!\n"); + m_FileSystem->Seek(m_FileHandle, fileMarker, FILESYSTEM_SEEK_HEAD); + } + + m_startTime = m_System->GetTime(); + m_nextReadTime = m_System->GetTime(); + + m_Continuous = true; + memset(&m_ServerInfo, 0, sizeof(m_ServerInfo)); + + strcopy(m_ServerInfo.address, m_DemoChannel->m_remote_address.ToBaseString()); + strcopy(m_ServerInfo.name, m_FileName); + strcopy(m_ServerInfo.map, m_demoHeader.szMapName); + strcopy(m_ServerInfo.gamedir, m_demoHeader.szDllDir); + strcopy(m_ServerInfo.description, "Demo Playback"); + + m_ServerInfo.activePlayers = 0; + m_ServerInfo.maxPlayers = MAX_CLIENTS; + m_ServerInfo.protocol = m_demoHeader.nNetProtocolVersion; + m_ServerInfo.type = GetServerType(HLST_Dedicated)[0]; + m_ServerInfo.os = GetServerOS()[0]; + m_ServerInfo.pw = '\0'; + m_ServerInfo.mod = false; + m_DemoState = DEMO_PLAYING; + + return true; +} + +void DemoFile::StopPlayBack() +{ + if (m_DemoState != DEMO_PLAYING) { + return; + } + + CloseFile(); + if (m_Server) { + m_Server->Disconnect(); + } +} + +void DemoFile::ReadDemoPacket(BitBuffer *demoData, demo_info_t *demoInfo) +{ + if (!m_FileHandle || (m_nextReadTime > m_System->GetTime() && m_Continuous)) { + return; + } + + int msglen; + unsigned char msgbuf[MAX_POSSIBLE_MSG]; + float time; + unsigned char cmd; + int frame; + int channel; + int sampleSize; + + bool readNextCmd = true; + while (readNextCmd) + { + unsigned int curpos = m_FileSystem->Tell(m_FileHandle); + if (m_FileSystem->Read(&cmd, sizeof(unsigned char), m_FileHandle) != 1) { + StopPlayBack(); + return; + } + + m_FileSystem->Read(&time, sizeof(float), m_FileHandle); + time = _LittleFloat(time); + + m_FileSystem->Read(&frame, sizeof(int), m_FileHandle); + frame = _LittleLong(frame); + + if (cmd && cmd != 5) { + m_nextReadTime = m_startTime + time; + } + + if (m_nextReadTime > m_System->GetTime() && m_Continuous) { + m_FileSystem->Seek(m_FileHandle, curpos, FILESYSTEM_SEEK_HEAD); + return; + } + + msglen = 0; + + // TODO: Find out or guess the names of the opcode + switch (cmd) + { + case 2: + m_startTime = (float)m_System->GetTime(); + break; + case 3: + msglen = 64; + break; + case 4: + msglen = 32; + break; + case 5: + { + if (++m_CurrentEntry >= m_EntryNumber) { + StopPlayBack(); + return; + } + + if (m_Entries) { + m_FileSystem->Seek(m_FileHandle, m_Entries[m_CurrentEntry].nOffset, FILESYSTEM_SEEK_HEAD); + } + break; + } + case 6: + msglen = 84; + break; + case 7: + msglen = 8; + break; + case 8: + { + m_FileSystem->Read(&channel, sizeof(int), m_FileHandle); + channel = _LittleLong(channel); + + m_FileSystem->Read(&sampleSize, sizeof(int), m_FileHandle); + sampleSize = _LittleLong(sampleSize); + msglen = sampleSize + 16; + break; + } + case 9: + { + m_FileSystem->Read(&msglen, sizeof(int), m_FileHandle); + msglen = _LittleLong(msglen); + break; + } + default: + readNextCmd = false; + break; + } + + if (msglen) + { + m_FileSystem->Read(msgbuf, msglen, m_FileHandle); + + demoData->WriteByte(cmd); + + switch (cmd) + { + case 8: + demoData->WriteLong(channel); + demoData->WriteLong(sampleSize); + break; + case 9: + demoData->WriteLong(msglen); + break; + } + + demoData->WriteBuf(msgbuf, msglen); + } + } + + ReadDemoInfo(demoInfo); + ReadSequenceInfo(); + + int length; + if (m_FileSystem->Read(&length, sizeof(int), m_FileHandle) != 4) { + m_System->DPrintf("WARNING! DemoFile::ReadDemoPacket: Bad demo length.\n"); + StopPlayBack(); + return; + } + + length = _LittleLong(length); + if (length < 0) { + m_System->DPrintf("WARNING! DemoFile::ReadDemoPacket: Demo message length < 0.\n"); + StopPlayBack(); + return; + } + + if (length > MAX_POSSIBLE_MSG) { + m_System->DPrintf("WARNING! DemoFile::ReadDemoPacket: message length > MAX_POSSIBLE_MSG\n"); + StopPlayBack(); + return; + } + + if (length > 0) + { + if (m_FileSystem->Read(msgbuf, length, m_FileHandle) != length) { + m_System->DPrintf("WARNING! DemoFile::ReadDemoPacket: Error reading demo message data.\n"); + StopPlayBack(); + return; + } + + NetPacket *p = new NetPacket; + + p->connectionless = false; + p->time = m_System->GetTime(); + p->seqnr = m_DemoChannel->m_incoming_sequence; + p->data.Resize(length); + p->data.WriteBuf(msgbuf, length); + p->data.Reset(); + + m_DemoChannel->m_incomingPackets.AddHead(p); + } +} + +void DemoFile::ReadDemoInfo(demo_info_t *demoInfo) +{ + m_FileSystem->Read(demoInfo, sizeof(*demoInfo), m_FileHandle); + + demoInfo->rp.cmd = &demoInfo->cmd; + demoInfo->rp.movevars = &demoInfo->movevars; +} + +void DemoFile::ReadSequenceInfo() +{ + m_FileSystem->Read(&m_DemoChannel->m_incoming_sequence, sizeof(int), m_FileHandle); + m_FileSystem->Read(&m_DemoChannel->m_incoming_acknowledged, sizeof(int), m_FileHandle); + m_FileSystem->Read(&m_DemoChannel->m_incoming_reliable_acknowledged, sizeof(int), m_FileHandle); + m_FileSystem->Read(&m_DemoChannel->m_incoming_reliable_sequence, sizeof(int), m_FileHandle); + m_FileSystem->Read(&m_DemoChannel->m_outgoing_sequence, sizeof(int), m_FileHandle); + m_FileSystem->Read(&m_DemoChannel->m_reliable_sequence, sizeof(int), m_FileHandle); + m_FileSystem->Read(&m_DemoChannel->m_last_reliable_sequence, sizeof(int), m_FileHandle); +} + +char *DemoFile::GetFileName() +{ + return m_FileName; +} + +bool DemoFile::IsContinuous() +{ + return m_Continuous; +} + +void DemoFile::SetContinuous(bool state) +{ + m_Continuous = state; +} diff --git a/rehlds/HLTV/common/DemoFile.h b/rehlds/HLTV/common/DemoFile.h new file mode 100644 index 0000000..9fc46df --- /dev/null +++ b/rehlds/HLTV/common/DemoFile.h @@ -0,0 +1,135 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include +#include + +#include "usercmd.h" +#include "ref_params.h" +#include "common/ServerInfo.h" + +#include "vmodes.h" +#include "cdll_int.h" + +#define DEMO_VERSION 5 +#define MAX_DEMO_ENTRY 92 + +typedef struct demoheader_s { + char szFileStamp[6]; + int nDemoProtocol; + int nNetProtocolVersion; + char szMapName[260]; + char szDllDir[260]; + CRC32_t mapCRC; + int nDirectoryOffset; + +} demoheader_t; + +typedef struct demoentry_s { + int nEntryType; + char szDescription[64]; + int nFlags; + int nCDTrack; + float fTrackTime; + int nFrames; + int nOffset; + int nFileLength; +} demoentry_t; + +typedef struct demo_info_s { + float timestamp; + ref_params_t rp; + usercmd_t cmd; + movevars_t movevars; + vec3_t view; + int viewmodel; +} demo_info_t; + +class NetChannel; +class DemoFile { +public: + DemoFile(); + virtual ~DemoFile() {} + + void Init(IWorld *world, IServer *server, NetChannel *channel); + bool LoadDemo(char *demoname); + void StopPlayBack(); + bool StartRecording(char *newName); + void CloseFile(); + void Reset(); + void SetContinuous(bool state); + bool IsContinuous(); + bool IsPlaying(); + bool IsRecording(); + char *GetFileName(); + void ReadDemoPacket(BitBuffer *demoData, demo_info_t *demoInfo); + void WriteDemoMessage(BitBuffer *unreliableData, BitBuffer *reliableData); + void WriteUpdateClientData(client_data_t *cdata); + float GetDemoTime(); + void ReadSequenceInfo(); + void ReadDemoInfo(demo_info_t *demoInfo); + void WriteDemoStartup(BitBuffer *buffer); + void WriteSequenceInfo(); + void WriteDemoInfo(demo_info_t *demoInfo); + void WriteSignonData(); + + serverinfo_t m_ServerInfo; + +private: + char m_FileName[MAX_PATH]; + + enum DemoState { + DEMO_IDLE, + DEMO_PLAYING, + DEMO_RECORDING + }; + int m_DemoState; + unsigned int m_frameCount; + FileHandle_t m_FileHandle; + demoheader_t m_demoHeader; + demoentry_t m_loadEntry; + demoentry_t m_gameEntry; + demo_info_t m_zeroDemoInfo; + + float m_startTime; + float m_nextReadTime; + + NetChannel *m_DemoChannel; + int m_StartPos; + int m_EntryNumber; + int m_CurrentEntry; + demoentry_t *m_Entries; + bool m_Continuous; + + IBaseSystem *m_System; + IWorld *m_World; + IServer *m_Server; + IFileSystem *m_FileSystem; +}; diff --git a/rehlds/HLTV/common/DirectorCmd.cpp b/rehlds/HLTV/common/DirectorCmd.cpp new file mode 100644 index 0000000..0ec044d --- /dev/null +++ b/rehlds/HLTV/common/DirectorCmd.cpp @@ -0,0 +1,683 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +// define DRC_CMD_* +char *DirectorCmd::m_CMD_Name[] = +{ + "NONE", + "START", + "EVENT", + "MODE", + "CAMERA", + "TIMESCALE", + "MESSAGE", + "SOUND", + "STATUS", + "BANNER", + "STUFFTEXT", + "CHASE", + "INEYE", + "MAP", + "CAMPATH", + "WAYPOINTS" +}; + +DirectorCmd::DirectorCmd() +{ + Clear(); +} + +DirectorCmd::~DirectorCmd() +{ + Clear(); +} + +bool DirectorCmd::GetEventData(int &entity1, int &entity2, int &flags) +{ + if (m_Type != DRC_CMD_EVENT) { + return false; + } + + m_Data.Reset(); + entity1 = m_Data.ReadWord(); + entity2 = m_Data.ReadWord(); + flags = m_Data.ReadLong(); + + return true; +} + +bool DirectorCmd::GetModeData(int &mode) +{ + if (m_Type != DRC_CMD_MODE) { + return false; + } + + m_Data.Reset(); + mode = m_Data.ReadByte(); + + return true; +} + +bool DirectorCmd::GetChaseData(int &entity1, int &entity2, float &distance, int &flags) +{ + if (m_Type != DRC_CMD_CHASE) { + return false; + } + + m_Data.Reset(); + entity1 = m_Data.ReadByte(); + entity2 = m_Data.ReadByte(); + distance = m_Data.ReadFloat(); + flags = m_Data.ReadByte(); + + return true; +} + +bool DirectorCmd::GetInEyeData(int &player) +{ + if (m_Type != DRC_CMD_INEYE) { + return false; + } + + m_Data.Reset(); + player = m_Data.ReadByte(); + + return true; +} + +bool DirectorCmd::GetMapData(int &entity, float &angle, float &distance) +{ + if (m_Type != DRC_CMD_MAP) { + return false; + } + + m_Data.Reset(); + entity = m_Data.ReadByte(); + angle = m_Data.ReadFloat(); + distance = m_Data.ReadFloat(); + + return true; +} + +bool DirectorCmd::GetCameraData(vec_t *position, vec_t *angles, float &fov, int &entity) +{ + if (m_Type != DRC_CMD_CAMERA) { + return false; + } + + m_Data.Reset(); + + position[0] = m_Data.ReadCoord(); + position[1] = m_Data.ReadCoord(); + position[2] = m_Data.ReadCoord(); + + angles[0] = m_Data.ReadCoord(); + angles[1] = m_Data.ReadCoord(); + angles[2] = m_Data.ReadCoord(); + + fov = (float)m_Data.ReadByte(); + entity = m_Data.ReadWord(); + + return true; +} + +bool DirectorCmd::GetCamPathData(vec_t *position, vec_t *angles, float &fov, int &flags) +{ + if (m_Type != DRC_CMD_CAMPATH) { + return false; + } + + m_Data.Reset(); + + position[0] = m_Data.ReadCoord(); + position[1] = m_Data.ReadCoord(); + position[2] = m_Data.ReadCoord(); + + angles[0] = m_Data.ReadCoord(); + angles[1] = m_Data.ReadCoord(); + angles[2] = m_Data.ReadCoord(); + + fov = (float)m_Data.ReadByte(); + flags = m_Data.ReadByte(); + + return true; +} + +bool DirectorCmd::GetSoundData(char *name, float &volume) +{ + if (m_Type != DRC_CMD_SOUND) { + return false; + } + + m_Data.Reset(); + strcpy(name, m_Data.ReadString()); + volume = m_Data.ReadFloat(); + + return true; +} + +float DirectorCmd::GetTime() +{ + return m_Time; +} + +int DirectorCmd::GetType() +{ + return m_Type; +} + +char *DirectorCmd::GetName() +{ + return m_CMD_Name[m_Type]; +} + +bool DirectorCmd::GetTimeScaleData(float &factor) +{ + if (m_Type != DRC_CMD_TIMESCALE) { + return false; + } + + m_Data.Reset(); + factor = m_Data.ReadFloat(); + + return true; +} + +bool DirectorCmd::GetWayPointsData(int &number) +{ + if (m_Type != DRC_CMD_WAYPOINTS) { + return false; + } + + m_Data.Reset(); + number = m_Data.ReadByte(); + + return true; +} + +bool DirectorCmd::GetMessageData(int &effect, int &color, vec_t *position, float &fadein, float &fadeout, float &holdtime, float &fxtime, char *text) +{ + if (m_Type != DRC_CMD_MESSAGE) { + return false; + } + + m_Data.Reset(); + + effect = m_Data.ReadByte(); + color = m_Data.ReadLong(); + + position[0] = m_Data.ReadFloat(); + position[1] = m_Data.ReadFloat(); + + fadein = m_Data.ReadFloat(); + fadeout = m_Data.ReadFloat(); + holdtime = m_Data.ReadFloat(); + fxtime = m_Data.ReadFloat(); + strcpy(text, m_Data.ReadString()); + + return true; +} + +bool DirectorCmd::GetStatusData(int &slots, int &spectators, int &proxies) +{ + if (m_Type != DRC_CMD_STATUS) { + return false; + } + + m_Data.Reset(); + slots = m_Data.ReadLong(); + spectators = m_Data.ReadLong(); + proxies = m_Data.ReadWord(); + + return true; +} + +bool DirectorCmd::GetBannerData(char *filename) +{ + if (m_Type != DRC_CMD_BANNER) { + return false; + } + + m_Data.Reset(); + strcpy(filename, m_Data.ReadString()); + + return true; +} + +bool DirectorCmd::GetStuffTextData(char *commands) +{ + if (m_Type != DRC_CMD_STUFFTEXT) { + return false; + } + + m_Data.Reset(); + strcpy(commands, m_Data.ReadString()); + + return true; +} + +void DirectorCmd::SetEventData(int entity1, int entity2, int flags) +{ + m_Type = DRC_CMD_EVENT; + Resize(8); + + m_Data.WriteWord(entity1); + m_Data.WriteWord(entity2); + m_Data.WriteLong(flags); +} + +void DirectorCmd::SetChaseData(int entity1, int entity2, float distance, int flags) +{ + m_Type = DRC_CMD_CHASE; + Resize(9); + + m_Data.WriteWord(entity1); + m_Data.WriteWord(entity2); + m_Data.WriteFloat(distance); + m_Data.WriteByte(flags); +} + +void DirectorCmd::SetInEyeData(int entity) +{ + m_Type = DRC_CMD_INEYE; + Resize(2); + + m_Data.WriteWord(entity); +} + +void DirectorCmd::SetMapData(int entity, float angle, float distance) +{ + m_Type = DRC_CMD_MAP; + Resize(10); + + m_Data.WriteWord(entity); + m_Data.WriteFloat(angle); + m_Data.WriteFloat(distance); +} + +void DirectorCmd::SetStartData() +{ + m_Type = DRC_CMD_START; + Resize(0); +} + +void DirectorCmd::SetModeData(int mode) +{ + m_Type = DRC_CMD_MODE; + Resize(1); + + m_Data.WriteByte(mode); +} + +void DirectorCmd::SetCameraData(vec_t *position, vec_t *angles, float fov, int entity) +{ + m_Type = DRC_CMD_CAMERA; + Resize(15); + + m_Data.WriteCoord(position[0]); + m_Data.WriteCoord(position[1]); + m_Data.WriteCoord(position[2]); + m_Data.WriteCoord(angles[0]); + m_Data.WriteCoord(angles[1]); + m_Data.WriteCoord(angles[2]); + m_Data.WriteByte((int)fov); + m_Data.WriteWord(entity); +} + +void DirectorCmd::SetCamPathData(vec_t *position, vec_t *angles, float fov, int flags) +{ + m_Type = DRC_CMD_CAMPATH; + Resize(14); + + m_Data.WriteCoord(position[0]); + m_Data.WriteCoord(position[1]); + m_Data.WriteCoord(position[2]); + m_Data.WriteCoord(angles[0]); + m_Data.WriteCoord(angles[1]); + m_Data.WriteCoord(angles[2]); + m_Data.WriteByte((int)fov); + m_Data.WriteByte(flags); +} + +void DirectorCmd::SetSoundData(char *name, float volume) +{ + int len = strlen(name); + m_Type = DRC_CMD_SOUND; + Resize(len + 5); + + m_Data.WriteString(name); + m_Data.WriteFloat(volume); +} + +void DirectorCmd::SetTimeScaleData(float factor) +{ + m_Type = DRC_CMD_TIMESCALE; + + Resize(sizeof(float)); + m_Data.WriteFloat(factor); +} + +void DirectorCmd::SetTime(float time) +{ + m_Time = time; +} + +void DirectorCmd::SetMessageData(int effect, unsigned int color, vec_t *position, float fadein, float fadeout, float holdtime, float fxtime, char *text) +{ + int len = strlen(text); + m_Type = DRC_CMD_MESSAGE; + Resize(len + 30); + + m_Data.WriteByte(effect); + m_Data.WriteLong(color); + m_Data.WriteFloat(position[0]); + m_Data.WriteFloat(position[1]); + m_Data.WriteFloat(fadein); + m_Data.WriteFloat(fadeout); + m_Data.WriteFloat(holdtime); + m_Data.WriteFloat(fxtime); + m_Data.WriteString(text); +} + +void DirectorCmd::Copy(DirectorCmd *cmd) +{ + Clear(); + + m_Time = cmd->m_Time; + m_Type = cmd->m_Type; + m_Size = cmd->m_Size; + m_Index = cmd->m_Index; + + m_Data.Resize(m_Size); + m_Data.WriteBuf(cmd->m_Data.GetData(), m_Size); +} + +void DirectorCmd::SetStatusData(int slots, int spectators, int proxies) +{ + m_Type = DRC_CMD_STATUS; + Resize(10); + + m_Data.WriteLong(slots); + m_Data.WriteLong(spectators); + m_Data.WriteWord(proxies); +} + +void DirectorCmd::SetBannerData(char *filename) +{ + m_Type = DRC_CMD_BANNER; + + int len = strlen(filename); + Resize(len + 1); + m_Data.WriteString(filename); +} + +void DirectorCmd::SetStuffTextData(char *commands) +{ + m_Type = DRC_CMD_STUFFTEXT; + + int len = strlen(commands); + Resize(len + 1); + m_Data.WriteString(commands); +} + +void DirectorCmd::SetWayPoints(int number) +{ + m_Type = DRC_CMD_WAYPOINTS; + + Resize(1); + m_Data.WriteByte(number); +} + +bool DirectorCmd::ReadFromStream(BitBuffer *stream) +{ + char *string; + unsigned char *start; + int length; + + if (!stream) { + return false; + } + + Clear(); + m_Type = stream->ReadByte(); + + switch (m_Type) + { + case DRC_CMD_START: + Resize(0); + break; + case DRC_CMD_EVENT: + Resize(8); + m_Data.WriteBuf(stream, 8); + break; + case DRC_CMD_MODE: + Resize(1); + m_Data.WriteBuf(stream, 1); + break; + case DRC_CMD_CAMERA: + Resize(15); + m_Data.WriteBuf(stream, 15); + break; + case DRC_CMD_TIMESCALE: + Resize(4); + m_Data.WriteBuf(stream, 4); + break; + case DRC_CMD_MESSAGE: + { + start = stream->m_CurByte; + stream->SkipBytes(29); + + string = stream->ReadString(); + length = strlen(string); + Resize(length + 30); + + m_Data.WriteBuf(start, 29); + m_Data.WriteBuf(string, length + 1); + break; + } + case DRC_CMD_SOUND: + { + string = stream->ReadString(); + length = strlen(string); + Resize(length + 5); + + m_Data.WriteBuf(string, length + 1); + m_Data.WriteFloat(stream->ReadFloat()); + + break; + } + case DRC_CMD_STATUS: + Resize(10); + m_Data.WriteBuf(stream, 10); + break; + case DRC_CMD_BANNER: + { + string = stream->ReadString(); + length = strlen(string) + 1; + + Resize(length); + m_Data.WriteBuf(string, length); + break; + } + case DRC_CMD_STUFFTEXT: + { + string = stream->ReadString(); + length = strlen(string) + 1; + + Resize(length); + m_Data.WriteBuf(string, length); + break; + } + case DRC_CMD_CHASE: + Resize(7); + m_Data.WriteBuf(stream, 7); + break; + case DRC_CMD_INEYE: + Resize(1); + m_Data.WriteBuf(stream, 1); + break; + case DRC_CMD_MAP: + Resize(10); + m_Data.WriteBuf(stream, 15); + break; + case DRC_CMD_CAMPATH: + Resize(14); + m_Data.WriteBuf(stream, 14); + break; + case DRC_CMD_WAYPOINTS: + { + Resize(1); + length = stream->ReadByte(); + m_Data.WriteByte(length); + stream->SkipBytes(length * 14); + break; + } + default: + case DRC_CMD_NONE: + return false; + } + + return true; +} + +void DirectorCmd::WriteToStream(BitBuffer *stream) +{ + if (!stream) { + return; + } + + if ((m_Type > DRC_CMD_NONE && m_Type <= DRC_CMD_LAST) && m_Size < 255) + { + stream->WriteByte(svc_director); + stream->WriteByte(m_Size + 1); + stream->WriteByte(m_Type); + stream->WriteBuf(m_Data.GetData(), m_Size); + } +} + +char *DirectorCmd::ToString() +{ + int i1, i2, i3; + float f1, f2, f3, f4; + vec3_t v1, v2; + + char *t1 = m_CMD_Name[m_Type]; + char t2[1024]; + + static char s[1024]; + memset(s, 0, sizeof(s)); + + switch (m_Type) + { + case DRC_CMD_START: + sprintf(s, "%s", t1); + break; + case DRC_CMD_EVENT: + GetEventData(i1, i2, i3); + sprintf(s, "%s %i %i %i", t1, i1, i2, i3); + break; + case DRC_CMD_MODE: + GetModeData(i1); + sprintf(s, "%s %i", t1, i1); + break; + case DRC_CMD_CAMERA: + GetCameraData(v1, v2, f1, i1); + sprintf(s, "%s (%.1f %.1f %.1f) (%.1f %.1f %.1f) %.1f %i", t1, v1[0], v1[1], v1[2], v2[0], v2[1], v2[2], f1, i1); + break; + case DRC_CMD_TIMESCALE: + GetTimeScaleData(f1); + sprintf(s, "%s %.2f", t1, f1); + break; + case DRC_CMD_MESSAGE: + GetMessageData(i1, i2, v1, f1, f2, f3, f4, t2); + sprintf(s, "%s \"%s\" %i %x (%.2f %.2f) %.1f, %.1f %.1f %.1f", t1, t2, i1, i2, v1[0], v1[1], f1, f2, f3, f4); + break; + case DRC_CMD_SOUND: + GetSoundData(t2, f1); + sprintf(s, "%s \"%s\" %.2f", t1, t2, f1); + break; + case DRC_CMD_STATUS: + GetStatusData(i1, i2, i3); + sprintf(s, "%s %i %i %i", t1, i1, i2, i3); + break; + case DRC_CMD_BANNER: + GetBannerData(t2); + sprintf(s, "%s \"%s\"", t1, t2); + break; + case DRC_CMD_STUFFTEXT: + GetStuffTextData(t2); + sprintf(s, "%s \"%s\"", t1, t2); + break; + case DRC_CMD_CHASE: + GetChaseData(i1, i2, f1, i3); + sprintf(s, "%s %i %i %.1f %i", t1, i1, i2, f1, i3); + break; + case DRC_CMD_INEYE: + GetInEyeData(i1); + sprintf(s, "%s %i", t1, i1); + break; + case DRC_CMD_MAP: + GetMapData(i1, f1, f2); + sprintf(s, "%s %i %.1f %.1f", t1, i1, f1, f2); + break; + case DRC_CMD_CAMPATH: + GetCamPathData(v1, v2, f1, i1); + sprintf(s, "%s (%.1f %.1f %.1f) (%.1f %.1f %.1f) %.1f %i", t1, v1[0], v1[1], v1[2], v2[0], v2[1], v2[2], f1, i1); + break; + case DRC_CMD_WAYPOINTS: + GetWayPointsData(i1); + sprintf(s, "%s %i", t1, i1); + break; + default: + case DRC_CMD_NONE: + return nullptr; + } + + return s; +} + +void DirectorCmd::FromString(char *string) +{ + ; +} + +void DirectorCmd::Clear() +{ + m_Type = 0; + m_Time = 0; + + m_Data.Free(); +} + +void DirectorCmd::Resize(int size) +{ + m_Data.Resize(size); + m_Size = size; +} diff --git a/rehlds/HLTV/common/DirectorCmd.h b/rehlds/HLTV/common/DirectorCmd.h new file mode 100644 index 0000000..88e5101 --- /dev/null +++ b/rehlds/HLTV/common/DirectorCmd.h @@ -0,0 +1,89 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "hltv.h" +#include "BitBuffer.h" + +class DirectorCmd { +public: + DirectorCmd(); + virtual ~DirectorCmd(); + + void Clear(); + void Copy(DirectorCmd *cmd); + void Resize(int size); + void FromString(char *string); + char *ToString(); + void WriteToStream(BitBuffer *stream); + bool ReadFromStream(BitBuffer *stream); + int GetType(); + char *GetName(); + float GetTime(); + void SetTime(float time); + void SetStartData(); + void SetEventData(int entity1, int entity2, int flags); + void SetChaseData(int entity1, int entity2, float distance, int flags); + void SetModeData(int mode); + void SetInEyeData(int entity); + void SetMapData(int entity, float angle, float distance); + void SetCameraData(vec_t *position, vec_t *angles, float fov, int entity); + void SetCamPathData(vec_t *position, vec_t *angles, float fov, int flags); + void SetTimeScaleData(float factor); + void SetMessageData(int effect, unsigned int color, vec_t *position, float fadein, float fadeout, float holdtime, float fxtime, char *text); + void SetSoundData(char *name, float volume); + void SetStatusData(int slots, int spectators, int proxies); + void SetBannerData(char *filename); + void SetStuffTextData(char *commands); + void SetWayPoints(int number); + + bool GetEventData(int &entity1, int &entity2, int &flags); + bool GetChaseData(int &entity1, int &entity2, float &distance, int &flags); + bool GetInEyeData(int &player); + bool GetMapData(int &entity, float &angle, float &distance); + bool GetCameraData(vec_t *position, vec_t *angles, float &fov, int &entity); + bool GetCamPathData(vec_t *position, vec_t *angles, float &fov, int &flags); + bool GetTimeScaleData(float &factor); + bool GetMessageData(int &effect, int &color, vec_t *position, float &fadein, float &fadeout, float &holdtime, float &fxtime, char *text); + bool GetSoundData(char *name, float &volume); + bool GetModeData(int &mode); + bool GetStatusData(int &slots, int &spectators, int &proxies); + bool GetBannerData(char *filename); + bool GetStuffTextData(char *commands); + bool GetWayPointsData(int &number); + +public: + static char *m_CMD_Name[]; + + float m_Time; + int m_Type; + int m_Size; + BitBuffer m_Data; + int m_Index; +}; diff --git a/rehlds/HLTV/common/InfoString.cpp b/rehlds/HLTV/common/InfoString.cpp new file mode 100644 index 0000000..4cebd69 --- /dev/null +++ b/rehlds/HLTV/common/InfoString.cpp @@ -0,0 +1,431 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +InfoString::InfoString(char *string, unsigned int maxSize) + : m_String(nullptr), m_MaxSize(0) +{ + unsigned int len = strlen(string) + 1; + if (len < maxSize) { + len = maxSize; + } + + SetMaxSize(len); + SetString(string); +} + +InfoString::InfoString() + : m_String(nullptr), m_MaxSize(0) +{ +} + +InfoString::InfoString(unsigned int maxSize) + : m_String(nullptr), m_MaxSize(0) +{ + SetMaxSize(maxSize); +} + +InfoString::InfoString(char *string) + : m_String(nullptr), m_MaxSize(0) +{ + unsigned int len = strlen(string) + 1; + if (len < MAX_INFO_LEN) { + len = MAX_INFO_LEN; + } + + SetMaxSize(len); + SetString(string); +} + +InfoString::~InfoString() +{ + if (m_String) { + free(m_String); + m_String = nullptr; + } +} + +bool InfoString::SetString(char *string) +{ + if (!m_String || !string) { + return false; + } + + if (strlen(string) >= m_MaxSize) { + return false; + } + + strncpy(m_String, string, m_MaxSize - 1); + m_String[m_MaxSize - 1] = '\0'; + return true; +} + +void InfoString::SetMaxSize(unsigned int maxSize) +{ + char *newBuffer = (char *)Mem_ZeroMalloc(maxSize); + if (!newBuffer) { + return; + } + + if (m_String) + { + if (maxSize > strlen(m_String)) { + strncpy(newBuffer, m_String, maxSize - 1); + newBuffer[maxSize - 1] = '\0'; + } + + free(m_String); + } + + m_MaxSize = maxSize; + m_String = newBuffer; +} + +int InfoString::GetMaxSize() +{ + return m_MaxSize; +} + +int InfoString::GetCurrentSize() +{ + return strlen(m_String); +} + +void InfoString::Clear() +{ + if (m_String) { + memset(m_String, 0, m_MaxSize); + } +} + +char *InfoString::GetString() +{ + return m_String; +} + +// Searches the string for the given key and returns the associated value, or an empty string. +char *InfoString::ValueForKey(const char *key) +{ + // use two buffers so compares work without stomping on each other + static char value[MAX_INFO_VALUES][MAX_INFO_VALUE]; + static int valueindex; + char pkey[MAX_INFO_KEY]; + char *c; + char *s = m_String; + int nCount; + + while (*s) + { + if (*s == '\\') { + // skip the slash + s++; + } + + // Copy a key + nCount = 0; + c = pkey; + + while (*s != '\\') + { + if (!*s) + { + // key should end with a \, not a nullptr, but suppose its value as absent + return ""; + } + + if (nCount >= MAX_INFO_KEY) + { + s++; + // skip oversized key chars till the slash or EOL + continue; + } + + *c++ = *s++; + nCount++; + } + + *c = '\0'; + + // skip the slash + s++; + + // Copy a value + nCount = 0; + c = value[valueindex]; + + while (*s != '\\') + { + if (!*s) + { + // allow value to be ended with nullptr + break; + } + + if (nCount >= MAX_INFO_VALUE) + { + s++; + // skip oversized value chars till the slash or EOL + continue; + } + + *c++ = *s++; + nCount++; + } + + *c = '\0'; + + if (!strcmp(key, pkey)) + { + c = value[valueindex]; + valueindex = (valueindex + 1) % MAX_INFO_VALUES; + return c; + } + } + + return ""; +} + +bool InfoString::RemoveKey(const char *key) +{ + char *start; + char pkey[MAX_INFO_KEY]; + char value[MAX_INFO_VALUE]; + char *c; + char *s; + int cmpsize; + int nCount; + + s = m_String; + + if (strchr(key, '\\')) { + return false; + } + + cmpsize = strlen(key); + if (cmpsize > MAX_INFO_LEN - 1) { + cmpsize = MAX_INFO_LEN - 1; + } + + bool found = false; + while (*s) + { + start = s; + if (*s == '\\') { + // skip the slash + s++; + } + + // Copy a key + nCount = 0; + c = pkey; + while (*s != '\\') + { + if (!*s) { + // key should end with a \, not a nullptr, but allow to remove it + break; + } + + if (nCount >= MAX_INFO_KEY) + { + s++; + // skip oversized key chars till the slash or EOL + continue; + } + + *c++ = *s++; + nCount++; + } + + *c = '\0'; + if (*s) { + // skip the slash + s++; + } + + // Copy a value + nCount = 0; + c = value; + while (*s != '\\') + { + if (!*s) { + // allow value to be ended with nullptr + break; + } + + if (nCount >= MAX_INFO_VALUE) + { + s++; + // skip oversized value chars till the slash or EOL + continue; + } + + *c++ = *s++; + nCount++; + } + + *c = '\0'; + + // Compare keys + if (!strncmp(key, pkey, cmpsize) ) + { + found = true; + strcpy_safe(start, s); // remove this part + s = start; // continue searching + } + } + + return found; +} + +void InfoString::RemovePrefixedKeys(char prefix) +{ + char pkey[MAX_INFO_KEY]; + char value[MAX_INFO_VALUE]; + char *start; + char *c; + char *s; + int nCount; + + s = m_String; + while (*s) + { + start = s; + + if (*s == '\\') { + // skip the slash + s++; + } + + // Copy a key + nCount = 0; + c = pkey; + while (*s != '\\') + { + if (!*s) { + // key should end with a \, not a nullptr, but allow to remove it + break; + } + + if (nCount >= MAX_INFO_KEY) + { + s++; + // skip oversized key chars till the slash or EOL + continue; + } + + *c++ = *s++; + nCount++; + } + + *c = '\0'; + if (*s) { + // skip the slash + s++; + } + + // Copy a value + nCount = 0; + c = value; + while (*s != '\\') + { + if (!*s) { + // allow value to be ended with nullptr + break; + } + + if (nCount >= MAX_INFO_VALUE) { + s++; + // skip oversized value chars till the slash or EOL + continue; + } + + *c++ = *s++; + nCount++; + } + + *c = '\0'; + + // Compare prefix + if (pkey[0] == prefix) + { + strcpy_safe(start, s); // remove this part + s = start; // continue searching + } + } +} + +bool InfoString::SetValueForStarKey(const char *key, const char *value) +{ + char newtoken[MAX_INFO_LEN + 4]; + if (strstr(key, "\\") || strstr(value, "\\")) { + return false; + } + + if (strstr(key, "\"") || strstr(value, "\"")) { + return false; + } + + if (strlen(key) > MAX_INFO_KEY || strlen(value) > MAX_INFO_VALUE) { + return false; + } + + // Remove current key/value and return if we doesn't specified to set a value + RemoveKey(key); + + if (!value || !strlen(value)) { + return true; + } + + // Create key/value pair + _snprintf(newtoken, sizeof(newtoken), "\\%s\\%s", key, value); + + int length = strlen(m_String); + if ((length + strlen(newtoken)) < m_MaxSize) + { + char *v = newtoken; + char *s = m_String + length; + unsigned char c; + + while (*v) + { + c = (unsigned char)*v++; + *s++ = c; + } + + *s = '\0'; + return true; + } + + return false; +} + +bool InfoString::SetValueForKey(const char *key, const char *value) +{ + return SetValueForStarKey(key, value); +} diff --git a/rehlds/HLTV/common/InfoString.h b/rehlds/HLTV/common/InfoString.h new file mode 100644 index 0000000..732b16d --- /dev/null +++ b/rehlds/HLTV/common/InfoString.h @@ -0,0 +1,65 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +class InfoString { +public: + InfoString(); + InfoString(unsigned int maxSize); + InfoString(char *string); + InfoString(char *string, unsigned int maxSize); + + virtual ~InfoString(); + + void SetMaxSize(unsigned int maxSize); + bool SetString(char *string); + char *GetString(); + + void Clear(); + int GetMaxSize(); + int GetCurrentSize(); + + bool SetValueForStarKey(const char *key, const char *value); + char *ValueForKey(const char *key); + bool RemoveKey(const char *key); + void RemovePrefixedKeys(char prefix); + bool SetValueForKey(const char *key, const char *value); + +protected: + enum { + MAX_INFO_KEY = 512, + MAX_INFO_VALUE = 512, + + MAX_INFO_LEN = 256, + MAX_INFO_VALUES = 4 + }; + + unsigned int m_MaxSize; + char *m_String; +}; diff --git a/rehlds/HLTV/common/NetAddress.cpp b/rehlds/HLTV/common/NetAddress.cpp new file mode 100644 index 0000000..fd708d3 --- /dev/null +++ b/rehlds/HLTV/common/NetAddress.cpp @@ -0,0 +1,170 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +NetAddress::NetAddress() +{ + memset(this, 0, sizeof(NetAddress)); +} + +void NetAddress::SetPort_(int16 port) +{ + m_Port = htons(port); +} + +void NetAddress::ToSockadr(sockaddr *s) +{ + s->sa_family = AF_INET; + ((struct sockaddr_in *)s)->sin_addr.s_addr = *(int *)&m_IP; + ((struct sockaddr_in *)s)->sin_port = m_Port; +} + +bool NetAddress::FromSockadr(sockaddr *s) +{ + if (s->sa_family != AF_INET) { + return false; + } + + *(int *)&m_IP = ((struct sockaddr_in *)s)->sin_addr.s_addr; + m_Port = ((struct sockaddr_in *)s)->sin_port; + + return true; +} + +bool NetAddress::Equal(NetAddress *a) +{ + if (*(int *)&m_IP == *(int *)&a->m_IP && m_Port == a->m_Port) { + return true; + } + + return false; +} + +bool NetAddress::EqualBase(NetAddress *a) +{ + if (*(int *)&m_IP == *(int *)&a->m_IP) { + return true; + } + + return false; +} + +void NetAddress::ToStream(BitBuffer *stream) +{ + stream->WriteByte(m_IP[0]); + stream->WriteByte(m_IP[1]); + stream->WriteByte(m_IP[2]); + stream->WriteByte(m_IP[3]); + stream->WriteShort(m_Port); +} + +void NetAddress::FromStream(BitBuffer *stream) +{ + m_IP[0] = stream->ReadByte(); + m_IP[1] = stream->ReadByte(); + m_IP[2] = stream->ReadByte(); + m_IP[3] = stream->ReadByte(); + m_Port = stream->ReadShort(); +} + +char *NetAddress::ToString() +{ + _snprintf(m_String, sizeof(m_String), "%i.%i.%i.%i:%i", m_IP[0], m_IP[1], m_IP[2], m_IP[3], ntohs(m_Port)); + return m_String; +} + +char *NetAddress::ToBaseString() +{ + _snprintf(m_String, sizeof(m_String), "%i.%i.%i.%i", m_IP[0], m_IP[1], m_IP[2], m_IP[3]); + return m_String; +} + +void NetAddress::FromNetAddress(NetAddress *adr) +{ + if (!adr) { + memset(m_IP, 0, sizeof(m_IP)); + m_Port = 0; + return; + } + + *(int *)&m_IP[0] = *(int *)&adr->m_IP[0]; + m_Port = adr->m_Port; +} + +netadr_t *NetAddress::ToOldNetAdr() +{ + static netadr_t adr; + + adr.type = NA_IP; + adr.port = m_Port; + *(int *)&adr.ip[0] = *(int *)&m_IP[0]; + + return &adr; +} + +bool NetAddress::FromOldNetAdr(netadr_t *adr) +{ + if (adr->type != NA_IP) { + return false; + } + + *(int *)&m_IP[0] = *(int *)&adr->ip[0]; + m_Port = adr->port; + return true; +} + +bool NetAddress::IsValid() +{ + return m_Port && (m_IP[0] || m_IP[1] || m_IP[2] || m_IP[3]); +} + +void NetAddress::Clear() +{ + m_IP[3] = 0; + m_IP[2] = 0; + m_IP[1] = 0; + m_IP[0] = 0; + m_Port = 0; + + memset(m_String, 0, sizeof(m_String)); +} + +bool NetAddress::IsSubAdress(NetAddress *adr) +{ + if ((!m_IP[0] || m_IP[0] == adr->m_IP[0]) + && (!m_IP[1] || m_IP[1] == adr->m_IP[1]) + && (!m_IP[2] || m_IP[2] == adr->m_IP[2]) + && (!m_IP[3] || m_IP[3] == adr->m_IP[3]) + && (m_IP[0] || m_IP[1] || m_IP[2] || m_IP[3])) + { + return true; + } + + return false; +} diff --git a/rehlds/HLTV/common/NetAddress.h b/rehlds/HLTV/common/NetAddress.h new file mode 100644 index 0000000..0004554 --- /dev/null +++ b/rehlds/HLTV/common/NetAddress.h @@ -0,0 +1,74 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "netadr.h" +#include "BitBuffer.h" + +class NetAddress { +public: + NetAddress(); + + bool IsValid(); + bool IsSubAdress(NetAddress *adr); + + void Clear(); + + bool FromOldNetAdr(netadr_t *adr); + netadr_t *ToOldNetAdr(); + + void FromNetAddress(NetAddress *adr); + char *ToBaseString(); + char *ToString(); + + bool EqualBase(NetAddress *a); + bool Equal(NetAddress *a); + bool FromSockadr(struct sockaddr *s); + void ToSockadr(struct sockaddr *s); + void SetPort_(int16 port); + void ToStream(BitBuffer *stream); + void FromStream(BitBuffer *stream); + + virtual ~NetAddress() {} + +public: + unsigned char m_IP[4]; + short unsigned int m_Port; + char m_String[32]; +}; + +class NetPacket { +public: + NetAddress address; + BitBuffer data; + double time; + bool connectionless; + unsigned int seqnr; + bool hasReliableData; +}; diff --git a/rehlds/HLTV/common/NetChannel.cpp b/rehlds/HLTV/common/NetChannel.cpp new file mode 100644 index 0000000..4c041eb --- /dev/null +++ b/rehlds/HLTV/common/NetChannel.cpp @@ -0,0 +1,1286 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +NetChannel::NetChannel() +{ + for (int i = 0; i < MAX_STREAMS; i++) + { + m_waitlist[i] = nullptr; + m_fragbufs[i] = nullptr; + m_incomingbufs[i] = nullptr; + + m_frag_length[i] = 0; + m_frag_startpos[i] = 0; + m_fragbufcount[i] = 0; + + m_reliable_fragid[i] = 0; + m_reliable_fragment[i] = 0; + } + + m_Socket = nullptr; + m_tempBuffer = nullptr; +} + +void NetChannel::UnlinkFragment(fragbuf_t *buf, int stream) +{ + fragbuf_t *search; + fragbuf_t **list = &m_fragbufs[stream]; + + if (list == nullptr) + { + m_System->DPrintf("Netchan_UnlinkFragment: Asked to unlink fragment from empty list, ignored\n"); + return; + } + + if (*list == buf) + { + *list = buf->next; + free(buf); + return; + } + + search = *list; + while (search->next) + { + if (search->next == buf) + { + search->next = buf->next; + free(buf); + return; + } + + search = search->next; + } + + m_System->DPrintf("Netchan_UnlinkFragment: Couldn't find fragment\n"); +} + +void NetChannel::OutOfBandPrintf(const char *format, ...) +{ + va_list argptr; + char string[NET_MAX_MESSAGE]; + BitBuffer data(string, sizeof(string)); + + if (m_Socket) + { + *(int *)string = CONNECTIONLESS_HEADER; + + va_start(argptr, format); + _vsnprintf(&string[4], sizeof(string) - 4, format, argptr); + va_end(argptr); + + data.SkipBytes(strlen(string)); + m_Socket->SendPacket(&m_remote_address, data.GetData(), data.CurrentSize()); + } +} + +void NetChannel::ClearFragbufs(fragbuf_t **ppbuf) +{ + fragbuf_t *buf, *n; + + if (!ppbuf) + { + return; + } + + // Throw away any that are sitting around + buf = *ppbuf; + while (buf) + { + n = buf->next; + free(buf); + buf = n; + } + + *ppbuf = nullptr; +} + +void NetChannel::ClearFragments() +{ + fragbufwaiting_t *wait, *next; + for (int i = 0; i < MAX_STREAMS; i++) + { + wait = m_waitlist[i]; + while (wait) + { + next = wait->next; + ClearFragbufs(&wait->fragbufs); + free(wait); + wait = next; + } + m_waitlist[i] = nullptr; + + ClearFragbufs(&m_fragbufs[i]); + FlushIncoming(i); + } +} + +void NetChannel::FlushIncoming(int stream) +{ + fragbuf_t *p, *n; + + NetPacket *in; + while ((in = (NetPacket *)m_incomingPackets.RemoveTail())) { + FreePacket(in); + } + + p = m_incomingbufs[stream]; + while (p) + { + n = p->next; + free(p); + p = n; + } + + m_incomingbufs[stream] = nullptr; +} + +void NetChannel::Reset() +{ + m_keep_alive = true; + m_crashed = false; + m_connected = false; + + m_connect_time = m_System->GetTime(); + + SetTimeOut(30); + SetRate(10000); + SetUpdateRate(20); + + m_incoming_sequence = 0; + m_incoming_acknowledged = 0; + m_incoming_reliable_acknowledged = 0; + m_incoming_reliable_sequence = 0; + m_outgoing_sequence = 1; + m_reliable_sequence = 0; + m_last_reliable_sequence = 0; +} + +void NetChannel::Clear() +{ + NetPacket *packet; + while ((packet = (NetPacket *)m_incomingPackets.RemoveHead())) { + FreePacket(packet); + } + + ClearFragments(); + + for (int i = 0; i < MAX_STREAMS; i++) + { + m_reliable_fragid[i] = 0; + m_reliable_fragment[i] = 0; + m_fragbufcount[i] = 0; + m_frag_startpos[i] = 0; + m_frag_length[i] = 0; + } + + if (m_tempBuffer) + { + free(m_tempBuffer); + m_tempBuffer = nullptr; + } + + m_tempBufferSize = 0; + m_reliableOutSize = 0; + + memset(m_reliableOutBuffer, 0, sizeof(m_reliableOutBuffer)); + memset(m_flow, 0, sizeof(m_flow)); + + m_reliableStream.Clear(); + m_unreliableStream.Clear(); + + m_last_send = + m_last_received = + m_cleartime = m_System->GetTime(); + + m_loss = 0; +} + +bool NetChannel::Create(IBaseSystem *system, INetSocket *netsocket, NetAddress *adr) +{ + m_System = system; + m_incomingPackets.Init(); + m_blocksize = FRAGMENT_S2C_MAX_SIZE; + + if (!m_reliableStream.Resize(MAX_MSGLEN)) + { + m_System->Errorf("NetChannel::Create: m_reliableStream out of memory.\n"); + return false; + } + + if (!m_unreliableStream.Resize(MAX_MSGLEN)) + { + m_System->Errorf("NetChannel::Create: m_unreliableStream out of memory.\n"); + return false; + } + + m_remote_address.FromNetAddress(adr); + m_Socket = netsocket; + if (m_Socket) + { + if (!m_Socket->AddChannel(this)) + { + m_System->Errorf("NetChannel::Create: could not bound to NetSocket\n"); + return false; + } + } + else + { + m_System->DPrintf("Creating fake network channel.\n"); + } + + Clear(); + Reset(); + + return true; +} + +void NetChannel::Close() +{ + if (m_Socket) { + m_Socket->RemoveChannel(this); + } + + Clear(); + m_reliableStream.Free(); + m_unreliableStream.Free(); +} + +bool NetChannel::IsReadyToSend() +{ + if (m_System->GetTime() > m_cleartime) { + return true; + } + + return false; +} + +void NetChannel::UpdateFlow(int stream) +{ + int bytes = 0; + float faccumulatedtime = 0.0f; + flow_t *pflow = &m_flow[stream]; + + if (pflow->nextcompute > m_System->GetTime()) { + return; + } + + pflow->nextcompute = m_System->GetTime() + 0.5f; + + flowstats_t *pstat; + int start = pflow->current - 1; + for (int i = 1; i < (MAX_LATENT / 2); i++) + { + pstat = &pflow->stats[(start - i) & 0x1f]; + bytes += pstat->size; + } + + faccumulatedtime = m_System->GetTime() - pstat->time; + + pflow->kbytespersec = (faccumulatedtime == 0.0f) ? 0.0f : bytes / faccumulatedtime / 1024.0f; + pflow->avgkbytespersec = pflow->avgkbytespersec * (2.0 / 3) + pflow->kbytespersec * (1.0 / 3); +} + +void NetChannel::TransmitOutgoing() +{ + byte send_buf[NET_MAX_MESSAGE]; + BitBuffer data(send_buf, sizeof(send_buf)); + + bool send_reliable; + bool send_reliable_fragment; + bool send_resending = false; + unsigned w1, w2; + int i, j; + + if (IsFakeChannel()) + { + m_outgoing_sequence++; + + m_last_send = m_System->GetTime(); + m_cleartime = m_last_send + m_send_interval; + + m_reliableStream.FastClear(); + m_unreliableStream.FastClear(); + + FakeAcknowledgement(); + return; + } + + // check for reliable message overflow + if (m_reliableStream.IsOverflowed()) + { + m_System->DPrintf("Transmit:Outgoing m_reliableStream overflow (%s)\n", m_remote_address.ToString()); + m_reliableStream.Clear(); + return; + } + + // check for unreliable message overflow + if (m_unreliableStream.IsOverflowed()) + { + m_System->DPrintf("Transmit:Outgoing m_unreliableStream overflow (%s)\n", m_remote_address.ToString()); + m_unreliableStream.Clear(); + } + + // if the remote side dropped the last reliable message, resend it + send_reliable = false; + + if (m_incoming_acknowledged > m_last_reliable_sequence && m_incoming_reliable_acknowledged != m_reliable_sequence) + { + send_reliable = true; + send_resending = true; + } + + // A packet can have "reliable payload + frag payload + unreliable payload + // frag payload can be a file chunk, if so, it needs to be parsed on the receiving end and reliable payload + unreliable payload need + // to be passed on to the message queue. The processing routine needs to be able to handle the case where a message comes in and a file + // transfer completes + // + // if the reliable transmit buffer is empty, copy the current message out + if (!m_reliableOutSize) + { + bool send_frag = false; + fragbuf_t *pbuf; + + // Will be true if we are active and should let chan->message get some bandwidth + int send_from_frag[MAX_STREAMS] = { 0, 0 }; + int send_from_regular = 0; + + // If we have data in the waiting list(s) and we have cleared the current queue(s), then + // push the m_waitlist(s) into the current queue(s) + FragSend(); + + // Sending regular payload + send_from_regular = m_reliableStream.CurrentSize() ? 1 : 0; + + // Check to see if we are sending a frag payload + for (i = 0; i < MAX_STREAMS; i++) + { + if (m_fragbufs[i]) + { + send_from_frag[i] = 1; + } + } + + /*if (m_reliableStream.CurrentSize() > sizeof(send_buf)) + { + CreateFragmentsFromBuffer(m_reliableStream.GetData(), m_reliableStream.CurrentSize(), FRAG_NORMAL_STREAM); + m_reliableStream.FastClear(); + }*/ + + // Stall reliable payloads if sending from frag buffer + if (send_from_regular && (send_from_frag[FRAG_NORMAL_STREAM])) + { + send_from_regular = false; + + // If the reliable buffer has gotten too big, queue it at the end of everything and clear out buffer + if (m_reliableStream.CurrentSize() > MAX_RELIABLE_PAYLOAD) + { + CreateFragmentsFromBuffer(m_reliableStream.GetData(), m_reliableStream.CurrentSize(), FRAG_NORMAL_STREAM); + m_reliableStream.FastClear(); + } + } + + // Startpos will be zero if there is no regular payload + for (i = 0; i < MAX_STREAMS; i++) + { + m_frag_startpos[i] = 0; + + // Assume no fragment is being sent + m_reliable_fragment[i] = 0; + m_reliable_fragid[i] = 0; + m_frag_length[i] = 0; + + if (send_from_frag[i]) + { + send_frag = true; + } + } + + if (send_from_regular || send_frag) + { + m_reliable_sequence ^= 1u; + send_reliable = true; + } + + if (send_from_regular) + { + memcpy(m_reliableOutBuffer, m_reliableStream.GetData(), m_reliableStream.CurrentSize()); + + m_reliableOutSize = m_reliableStream.CurrentSize(); + m_reliableStream.FastClear(); + + // If we send fragments, this is where they'll start + for (i = 0; i < MAX_STREAMS; i++) { + m_frag_startpos[i] = m_reliableOutSize; + } + } + + for (i = 0; i < MAX_STREAMS; i++) + { + int fragment_size = 0; + + // Is there something in the fragbuf? + pbuf = m_fragbufs[i]; + if (pbuf) + { + fragment_size = pbuf->size; + + // Files set size a bit differently. + if (pbuf->isfile && !pbuf->isbuffer) + { + fragment_size = pbuf->size; + } + } + + // Make sure we have enought space left + if (send_from_frag[i] && pbuf && (m_reliableOutSize + fragment_size) < MAX_RELIABLE_PAYLOAD) + { + m_reliable_fragid[i] = MAKE_FRAGID(pbuf->bufferId, m_fragbufcount[i]); // Which buffer are we sending? + + // If it's not in-memory, then we'll need to copy it in frame the file handle. + if (pbuf->isfile && !pbuf->isbuffer) { + m_System->Printf("TODO! NetChannel::Transmit: system file support\n"); + } + + memcpy(m_reliableOutBuffer + m_reliableOutSize, pbuf->data, pbuf->size); + + m_reliableOutSize += pbuf->size; + m_frag_length[i] = pbuf->size; + + // Unlink pbuf + UnlinkFragment(pbuf, i); + m_reliable_fragment[i] = 1; + + // Offset the rest of the starting positions + for (j = i + 1; j < MAX_STREAMS; j++) + { + m_frag_startpos[j] += m_frag_length[i]; + } + } + } + } + + // Prepare the packet header + w1 = m_outgoing_sequence | (send_reliable << 31); + w2 = m_incoming_sequence | (m_incoming_reliable_sequence << 31); + + send_reliable_fragment = false; + + for (i = 0; i < MAX_STREAMS; i++) + { + if (m_reliable_fragment[i]) + { + send_reliable_fragment = true; + break; + } + } + + if (send_reliable && send_reliable_fragment) { + w1 |= (1 << 30); + } + + m_outgoing_sequence++; + + data.Clear(); + data.WriteLong(w1); + data.WriteLong(w2); + + if (send_reliable && send_reliable_fragment) + { + for (i = 0; i < MAX_STREAMS; i++) + { + if (m_reliable_fragment[i]) + { + data.WriteByte(1); + data.WriteLong(m_reliable_fragid[i]); + data.WriteShort(m_frag_startpos[i]); + data.WriteShort(m_frag_length[i]); + } + else + { + data.WriteByte(0); + } + } + } + + // Copy the reliable message to the packet first + if (send_reliable) { + data.WriteBuf(m_reliableOutBuffer, m_reliableOutSize); + m_last_reliable_sequence = m_outgoing_sequence - 1; + } + + // Is there room for the unreliable payload? + int max_send_size = send_resending ? MAX_ROUTEABLE_PACKET : NET_MAX_MESSAGE; + if ((max_send_size - data.CurrentSize()) >= m_unreliableStream.CurrentSize()) { + data.ConcatBuffer(&m_unreliableStream); + } + else { + m_System->DPrintf("WARNING! TransmitOutgoing: Unreliable would overfow, ignoring.\n"); + } + + m_unreliableStream.FastClear(); + + // Deal with packets that are too small for some networks + // Packet too small for some networks + if (data.CurrentSize() < 16) + { + // Go ahead and pad a full 16 extra bytes -- this only happens during authentication / signon + for (int i = data.CurrentSize(); i < 16; i++) + { + // Note that the server can parse svc_nop, too. + data.WriteByte(svc_nop); + } + } + + int statId = m_flow[FLOW_OUTGOING].current & 0x1f; + m_flow[FLOW_OUTGOING].stats[statId].size = data.CurrentSize() + UDP_HEADER_SIZE; + m_flow[FLOW_OUTGOING].stats[statId].time = m_System->GetTime(); + m_flow[FLOW_OUTGOING].current++; + + COM_Munge2(data.GetData() + 8, data.CurrentSize() - 8, (unsigned char)(m_outgoing_sequence - 1)); + + if (m_Socket) { + m_Socket->SendPacket(&m_remote_address, data.GetData(), data.CurrentSize()); + } + + m_last_send = m_System->GetTime(); + m_cleartime = max(m_send_interval, (data.CurrentSize() + UDP_HEADER_SIZE) * (1.0 / m_max_bandwidth_rate)) + m_last_send; +} + +NetChannel::fragbuf_t *NetChannel::FindBufferById(fragbuf_t **pplist, int id, bool allocate) +{ + fragbuf_t *list = *pplist; + fragbuf_t *pnewbuf; + + while (list) + { + if (list->bufferId == id) + return list; + + list = list->next; + } + + if (!allocate) + return nullptr; + + // Create new entry + pnewbuf = (fragbuf_t *)Mem_ZeroMalloc(sizeof(fragbuf_t)); + pnewbuf->bufferId = id; + AddBufferToList(pplist, pnewbuf); + + return pnewbuf; +} + +bool NetChannel::CheckForCompletion(int stream, int intotalbuffers) +{ + int c; + int size; + int id; + fragbuf_t *p; + + size = 0; + c = 0; + + if (stream != FRAG_NORMAL_STREAM && stream != FRAG_FILE_STREAM) { + m_System->DPrintf("ERROR! NetChannel::CheckForCompletion: invalid stream number %i.\n"); + return false; + } + + p = m_incomingbufs[stream]; + if (!p) { + return false; + } + + while (p) + { + size += p->size; + c++; + + id = FRAG_GETID(p->bufferId); + if (id != c) + { + m_System->DPrintf("WARNING! NetChannel::CheckForCompletion: lost/dropped fragment Lost/dropped fragment would cause stall, retrying connection\n"); + m_crashed = true; + return false; + } + + p = p->next; + } + + // Received final message + if (c == intotalbuffers) + { + switch (stream) + { + case FRAG_NORMAL_STREAM: + CopyNormalFragments(); + break; + case FRAG_FILE_STREAM: + m_System->Printf("TODO! NetChannel::CheckForCompletion: create file from fragments.\n"); + break; + } + + return true; + } + + return false; +} + +void NetChannel::ProcessIncoming(unsigned char *data, int size) +{ + BitBuffer message(data, size); + + int i; + unsigned int sequence, sequence_ack; + unsigned int reliable_ack, reliable_message; + unsigned int fragid[MAX_STREAMS] = { 0, 0 }; + + bool frag_message[MAX_STREAMS] = { false, false }; + int frag_offset[MAX_STREAMS] = { 0, 0 }; + int m_frag_length[MAX_STREAMS] = { 0, 0 }; + + bool message_contains_fragments; + int net_drop; + float newLoss; + float weight; + + // get sequence numbers + sequence = message.ReadLong(); + if (sequence == CONNECTIONLESS_HEADER) + { + NetPacket *p = new NetPacket; + + p->connectionless = true; + p->time = m_System->GetTime(); + p->seqnr = -1; + + p->address.FromNetAddress(&m_remote_address); + p->data.Resize(size - 4); + p->data.WriteBuf(data + 4, size - 4); + p->data.Reset(); + + m_incomingPackets.AddHead(p); + return; + } + + if (!m_connected) { + return; + } + + sequence_ack = message.ReadLong(); + + COM_UnMunge2(message.GetData() + 8, size - 8, sequence & 0xFF); + + reliable_message = sequence >> 31; + reliable_ack = sequence_ack >> 31; + message_contains_fragments = sequence & (1 << 30) ? true : false; + + // TODO: Looks like need to move it above COM_UnMunge2 + if (sequence_ack & 0x40000000) + { + m_crashed = true; + return; + } + + if (message_contains_fragments) + { + for (i = 0; i < MAX_STREAMS; i++) + { + if (message.ReadByte()) + { + frag_message[i] = true; + fragid[i] = message.ReadLong(); + frag_offset[i] = message.ReadShort(); + m_frag_length[i] = message.ReadShort(); + } + } + } + + sequence &= ~(1 << 31); + sequence &= ~(1 << 30); + + sequence_ack &= ~(1 << 31); + sequence_ack &= ~(1 << 30); + + if (sequence <= (unsigned int)m_incoming_sequence) + { + if (sequence == (unsigned int)m_incoming_sequence) + m_System->DPrintf("NetChannel::ProcessIncoming: duplicate packet %i at %i from %s\n", sequence, m_incoming_sequence, m_remote_address.ToString()); + else + m_System->DPrintf("NetChannel::ProcessIncoming: out of order packet %i at %i from %s\n", sequence, m_incoming_sequence, m_remote_address.ToString()); + + return; + } + + // dropped packets don't keep the message from being used + net_drop = sequence - (m_incoming_sequence + 1); + if (net_drop < 0) { + net_drop = 0; + } + + newLoss = (net_drop + 1) * (1.0f / 200.0f); + if (newLoss < 1.0f) + { + weight = (float)net_drop / (float)(net_drop + 1); + m_loss = (1.0 - newLoss) * m_loss + weight * newLoss; + } + else + m_loss = 1; + + // if the current outgoing reliable message has been acknowledged + // clear the buffer to make way for the next + if (reliable_ack == (unsigned int)m_reliable_sequence) + { + if (m_incoming_acknowledged + 1 >= m_last_reliable_sequence) + { + // it has been received + m_reliableOutSize = 0; + } + } + + // if this message contains a reliable message, bump incoming_reliable_sequence + m_incoming_sequence = sequence; + m_incoming_acknowledged = sequence_ack; + m_incoming_reliable_acknowledged = reliable_ack; + + if (reliable_message) { + m_incoming_reliable_sequence ^= 1u; + } + + int statId = m_flow[FLOW_INCOMING].current & 0x1f; + m_flow[FLOW_INCOMING].stats[statId].size = size + UDP_HEADER_SIZE; + m_flow[FLOW_INCOMING].stats[statId].time = m_System->GetTime(); + m_flow[FLOW_INCOMING].current++; + + m_last_received = m_System->GetTime(); + + if (message_contains_fragments) + { + for (i = 0; i <= 1; ++i) + { + int j; + fragbuf_t *pbuf; + int inbufferid; + int intotalbuffers; + + if (!frag_message[i]) + continue; + + inbufferid = FRAG_GETID(fragid[i]); + intotalbuffers = FRAG_GETCOUNT(fragid[i]); + + if (fragid[i]) + { + pbuf = FindBufferById(&m_incomingbufs[i], fragid[i], true); + if (pbuf) + { + memcpy(pbuf->data, message.GetData() + message.CurrentSize() + frag_offset[i], m_frag_length[i]); + pbuf->size = m_frag_length[i]; + } + else + { + m_System->Printf("NetChannel::ProcessIncoming: couldn't allocate or find buffer %i\n", inbufferid); + } + + // Count # of incoming bufs we've queued? are we done? + CheckForCompletion(i, intotalbuffers); + } + + // Rearrange incoming data to not have the frag stuff in the middle of it + int wpos = message.CurrentSize() + frag_offset[i]; + int rpos = wpos + m_frag_length[i]; + + memmove(message.GetData() + wpos, message.GetData() + rpos, message.GetMaxSize() - rpos); + message.m_MaxSize -= m_frag_length[i]; + + for (j = i + 1; j < MAX_STREAMS; j++) + { + // fragments order already validated + frag_offset[j] -= m_frag_length[i]; + } + } + } + + int curLen = message.GetMaxSize() - message.CurrentSize(); + if (curLen > 0) + { + NetPacket *p = new NetPacket; + p->connectionless = 0; + p->hasReliableData = reliable_message != 0; + p->time = m_System->GetTime(); + p->seqnr = m_incoming_sequence; + p->address.FromNetAddress(&m_remote_address); + p->data.Resize(curLen); + p->data.WriteBuf(message.m_CurByte, curLen); + p->data.Reset(); + m_incomingPackets.AddHead(p); + } +} + +void NetChannel::FragSend() +{ + fragbufwaiting_t *wait; + int i; + + for (i = 0; i < MAX_STREAMS; i++) + { + // Already something queued up, just leave in waitlist + if (m_fragbufs[i]) { + continue; + } + + wait = m_waitlist[i]; + + // Nothing to queue? + if (!wait) { + continue; + } + + m_waitlist[i] = wait->next; + wait->next = nullptr; + + // Copy in to fragbuf + m_fragbufs[i] = wait->fragbufs; + m_fragbufcount[i] = wait->fragbufcount; + + // Throw away wait list + free(wait); + } +} + +void NetChannel::AddBufferToList(fragbuf_t **pplist, fragbuf_t *pbuf) +{ + // Find best slot + fragbuf_t *pprev, *n; + int id1, id2; + + pbuf->next = nullptr; + + if (!pplist) + return; + + if (!*pplist) + { + pbuf->next = *pplist; + *pplist = pbuf; + return; + } + + pprev = *pplist; + while (pprev->next) + { + n = pprev->next; // Next item in list + id1 = FRAG_GETID(n->bufferId); + id2 = FRAG_GETID(pbuf->bufferId); + + if (id1 > id2) + { + // Insert here + pbuf->next = n->next; + pprev->next = pbuf; + return; + } + + pprev = pprev->next; + } + + // Insert at end + pprev->next = pbuf; +} + +bool NetChannel::CreateFragmentsFromBuffer(void *buffer, int size, int streamtype, char *filename) +{ + fragbuf_t *buf; + int chunksize; + int sendsize; + int remaining; + int pos; + int bufferid = 1; + bool firstfragment = true; + fragbufwaiting_t *wait, *p; + + if (IsFakeChannel()) + { + m_System->Printf("NetChannel::CreateFragmentsFromBuffer: IsFakeChannel()\n"); + return true; + } + + if (size == 0) { + return true; + } + + unsigned char compressed[65536]; + char hdr[4] = "BZ2"; + + unsigned int header_size = sizeof(hdr); + unsigned int compressedSize = size - sizeof(hdr); // we should fit in same data buffer minus 4 bytes for a header + + if (streamtype == FRAG_FILE_STREAM) { + compressedSize -= 4; + } + + if (!BZ2_bzBuffToBuffCompress((char *)compressed, &compressedSize, (char *)buffer, size, 9, 0, 30)) + { + m_System->DPrintf("Compressing split packet (%d -> %d bytes)\n", size, compressedSize); + memcpy(buffer, hdr, sizeof(hdr)); + + if (streamtype == FRAG_FILE_STREAM) { + memcpy((char *)buffer + 4, &size, sizeof(int)); + header_size = 8; + } + + memcpy((char *)buffer + header_size, compressed, compressedSize); + size = header_size + compressedSize; + } + + chunksize = FRAGMENT_S2C_MIN_SIZE; + wait = (fragbufwaiting_t *)Mem_ZeroMalloc(sizeof(fragbufwaiting_t)); + + remaining = size; + pos = 0; + while (remaining > 0) + { + sendsize = min(remaining, chunksize); + remaining -= sendsize; + + buf = (fragbuf_t *)Mem_ZeroMalloc(sizeof(fragbuf_t)); + if (!buf) + { + m_System->Printf("NetChannel::CreateFragmentsFromBuffer:Couldn't allocate fragbuf_t\n"); + free(wait); + return false; + } + + if (firstfragment && filename) + { + firstfragment = false; + + unsigned int len = strlen(filename) + 1; + memcpy(buf->data, filename, len); + sendsize -= len; + + memcpy(&buf->data[len], (char *)buffer + pos, sendsize); + buf->size = len + sendsize; + } + else + { + memcpy(buf->data, (char *)buffer + pos, sendsize); + buf->size = sendsize; + } + + buf->bufferId = bufferid++; + buf->isfile = filename ? true : false; + buf->isbuffer = true; + + // Copy in data + pos += sendsize; + + AddFragbufToTail(wait, buf); + } + + // Now add waiting list item to the end of buffer queue + if (!m_waitlist[streamtype]) + { + m_waitlist[streamtype] = wait; + } + else + { + p = m_waitlist[streamtype]; + while (p->next) { + p = p->next; + } + + p->next = wait; + } + + return true; +} + +void NetChannel::AddFragbufToTail(fragbufwaiting_t *wait, fragbuf_t *buf) +{ + fragbuf_t *p; + + buf->next = nullptr; + wait->fragbufcount++; + + p = wait->fragbufs; + if (p) + { + while (p->next) { + p = p->next; + } + + p->next = buf; + } + else + { + wait->fragbufs = buf; + } +} + +NetPacket *NetChannel::GetPacket() +{ + return (NetPacket *)m_incomingPackets.RemoveTail(); +} + +void NetChannel::FreePacket(NetPacket *packet) +{ + if (packet) { + delete packet; + } +} + +void NetChannel::SetUpdateRate(int newupdaterate) +{ + m_updaterate = newupdaterate; + if (newupdaterate > 100) { + m_updaterate = 100; + } else if (newupdaterate < 0) { + m_updaterate = 1; + } + + m_send_interval = (1.0f / m_updaterate); +} + +void NetChannel::CopyNormalFragments() +{ + fragbuf_t *p, *n; + int totalSize; + + if (!m_incomingbufs[FRAG_NORMAL_STREAM]) { + m_System->DPrintf("WARNING! NetChannel::CopyNormalFragments: called with no fragments readied.\n"); + return; + } + + totalSize = 0; + p = m_incomingbufs[FRAG_NORMAL_STREAM]; + while (p) + { + totalSize += p->size; + p = p->next; + } + + NetPacket *packet = new NetPacket; + packet->seqnr = m_incoming_sequence; + packet->connectionless = false; + packet->time = m_System->GetTime(); + packet->address.FromNetAddress(&m_remote_address); + packet->data.Resize(totalSize); + + p = m_incomingbufs[FRAG_NORMAL_STREAM]; + while (p) + { + n = p->next; + packet->data.WriteBuf(p->data, p->size); + + free(p); + p = n; + } + + if (*(uint32 *)packet->data.GetData() == MAKEID('B', 'Z', '2', '\0')) + { + char uncompressed[65536]; + unsigned int uncompressedSize = 65536; + + BZ2_bzBuffToBuffDecompress(uncompressed, &uncompressedSize, (char *)packet->data.GetData() + 4, totalSize - 4, 1, 0); + + packet->data.Resize(uncompressedSize); + packet->data.WriteBuf(uncompressed, uncompressedSize); + } + + packet->data.Reset(); + + m_incomingPackets.AddHead(packet); + m_incomingbufs[FRAG_NORMAL_STREAM] = nullptr; +} + +void NetChannel::SetConnected(bool flag) +{ + m_connected = flag; +} + +void NetChannel::SetRate(int newRate) +{ + m_max_bandwidth_rate = clamp(newRate, 1000, 20000); +} + +void NetChannel::GetFlowStats(float *avgInKBSec, float *avgOutKBSec) +{ + UpdateFlow(FLOW_OUTGOING); + UpdateFlow(FLOW_INCOMING); + + *avgInKBSec = m_flow[FLOW_OUTGOING].avgkbytespersec; + *avgOutKBSec = m_flow[FLOW_INCOMING].avgkbytespersec; +} + +void NetChannel::SetKeepAlive(bool flag) +{ + m_keep_alive = flag; +} + +bool NetChannel::KeepAlive() +{ + return m_keep_alive; +} + +NetAddress *NetChannel::GetTargetAddress() +{ + return &m_remote_address; +} + +bool NetChannel::IsFakeChannel() +{ + if (m_Socket) { + return false; + } + + return true; +} + +bool NetChannel::IsConnected() +{ + return m_connected; +} + +void NetChannel::SetTimeOut(float time) +{ + if (time > 0) { + m_timeout = time; + } +} + +bool NetChannel::IsTimedOut() +{ + if (m_Socket && m_System->GetTime() > m_timeout + m_last_received) { + return true; + } + + return false; +} + +float NetChannel::GetIdleTime() +{ + return m_System->GetTime() - m_last_send; +} + +int NetChannel::GetRate() +{ + return m_max_bandwidth_rate; +} + +int NetChannel::GetUpdateRate() +{ + return m_updaterate; +} + +float NetChannel::GetLoss() +{ + return m_loss; +} + +void NetChannel::FakeAcknowledgement() +{ + m_incoming_sequence = 0; + m_incoming_acknowledged = m_outgoing_sequence - 1; + m_incoming_reliable_acknowledged = m_reliable_sequence; + m_reliableOutSize = 0; + + m_last_received = m_System->GetTime(); +} + +bool NetChannel::CreateFragmentsFromFile(char *fileName) +{ + if (IsFakeChannel()) { + m_System->Printf("NetChannel::CreateFragmentsFromBuffer: IsFakeChannel()\n"); + return true; + } + + m_System->Printf("WARNING! Ignoring file request %s.\n", fileName); + return false; +} + +bool NetChannel::IsCrashed() +{ + return m_crashed; +} + +bool NetChannel::CopyFileFragments() +{ + fragbuf_t *p; + fragbuf_s *n; + char filename[MAX_PATH]; + int totalSize = 0; + + if (!m_incomingbufs[FRAG_FILE_STREAM]) { + m_System->DPrintf("WARNING! NetChannel::CopyFileFragments: called with no fragments readied.\n"); + return false; + } + + totalSize = 0; + p = m_incomingbufs[FRAG_FILE_STREAM]; + while (p) + { + totalSize += p->size; + p = p->next; + } + + BitBuffer filecontent(totalSize); + p = m_incomingbufs[FRAG_FILE_STREAM]; + while (p) + { + n = p->next; + filecontent.WriteBuf(p->data, p->size); + free(p); + p = n; + } + + filecontent.Reset(); + strcopy(filename, filecontent.ReadString()); + + if (!strlen(filename)) { + m_System->Printf("File fragment received with no filename\n"); + FlushIncoming(FRAG_FILE_STREAM); + return false; + } + + if (strstr(filename, "..")) { + m_System->Printf("File fragment received with relative path, ignoring\n"); + FlushIncoming(FRAG_FILE_STREAM); + return false; + } + + // TODO: Here is the missing code. + // TODO: Check me, value of return function only false. + + totalSize -= strlen(filename) - 1; + m_incomingbufs[FRAG_FILE_STREAM] = nullptr; + + return false; +} diff --git a/rehlds/HLTV/common/NetChannel.h b/rehlds/HLTV/common/NetChannel.h new file mode 100644 index 0000000..8c396e3 --- /dev/null +++ b/rehlds/HLTV/common/NetChannel.h @@ -0,0 +1,237 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include +#include + +#include "bzip2/bzlib.h" +#include "ObjectList.h" + +// 0 == regular, 1 == file stream +enum +{ + FRAG_NORMAL_STREAM = 0, + FRAG_FILE_STREAM, + + MAX_STREAMS +}; + +enum +{ + FLOW_OUTGOING = 0, + FLOW_INCOMING, + + MAX_FLOWS +}; + +#define MAX_LATENT 32 +#define FRAGMENT_MAX_SIZE 1400 // Size of fragmentation buffer internal buffers + +#define UDP_HEADER_SIZE 28 +#define MAX_RELIABLE_PAYLOAD 1200 + +#define MAKE_FRAGID(id, count) (((id & 0xffff) << 16) | (count & 0xffff)) +#define FRAG_GETID(fragid) ((fragid >> 16) & 0xffff) +#define FRAG_GETCOUNT(fragid) (fragid & 0xffff) + +// Max length of a reliable message +#define MAX_MSGLEN 3990 // 10 reserved for fragheader? +#define MAX_POSSIBLE_MSG 65536 + +#define MAX_ROUTEABLE_PACKET 1400 +#define MIN_ROUTEABLE_PACKET 16 + +#define SPLIT_SIZE (MAX_ROUTEABLE_PACKET - sizeof(SPLITPACKET)) + +// Pad this to next higher 16 byte boundary +// This is the largest packet that can come in/out over the wire, before processing the header +// bytes will be stripped by the networking channel layer +// #define NET_MAX_MESSAGE PAD_NUMBER( ( MAX_MSGLEN + HEADER_BYTES ), 16 ) +// This is currently used value in the engine. TODO: define above gives 4016, check it why. +#define NET_MAX_MESSAGE 4037 +#define NET_HEADER_FLAG_SPLITPACKET -2 + +class IBaseSystem; + +// Network Connection Channel +class NetChannel: public INetChannel { +public: + NetChannel(); + virtual ~NetChannel() {} + + virtual bool Create(IBaseSystem *system, INetSocket *netsocket = nullptr, NetAddress *adr = nullptr); + virtual bool IsConnected(); + virtual bool IsReadyToSend(); + virtual bool IsCrashed(); + virtual bool IsTimedOut(); + virtual bool IsFakeChannel(); + virtual bool KeepAlive(); + virtual NetAddress *GetTargetAddress(); + virtual void Close(); + virtual void Clear(); + virtual void Reset(); + virtual void TransmitOutgoing(); + virtual void ProcessIncoming(unsigned char *data, int size); + virtual void OutOfBandPrintf(const char *format, ...); + virtual void FakeAcknowledgement(); + virtual void SetUpdateRate(int newupdaterate); + virtual void SetRate(int newRate); + virtual void SetKeepAlive(bool flag); + virtual void SetTimeOut(float time); + virtual float GetIdleTime(); + virtual int GetRate(); + virtual int GetUpdateRate(); + virtual float GetLoss(); + + enum { FRAGMENT_C2S_MIN_SIZE = 16, FRAGMENT_S2C_MIN_SIZE = 256, FRAGMENT_S2C_MAX_SIZE = 1024 }; + + // Message data + typedef struct flowstats_s + { + int size; // Size of message sent/received + double time; // Time that message was sent/received + } flowstats_t; + + typedef struct flow_s + { + flowstats_t stats[MAX_LATENT]; // Data for last MAX_LATENT messages + int current; // Current message position + double nextcompute; // Time when we should recompute k/sec data + + // Average data + float kbytespersec; + float avgkbytespersec; + } flow_t; + + // Generic fragment structure + typedef struct fragbuf_s + { + struct fragbuf_s *next; // Next buffer in chain + int bufferId; // Id of this buffer + byte data[FRAGMENT_MAX_SIZE]; // The actual data sits here + + int size; // Size of data to read at that offset + bool isfile; // Is this a file buffer? + bool isbuffer; // Is this file buffer from memory ( custom decal, etc. ). + char fileName[MAX_PATH]; // Name of the file to save out on remote host + int fOffset; // Offset in file from which to read data + } fragbuf_t; + + // Waiting list of fragbuf chains + typedef struct fragbufwaiting_s + { + struct fragbufwaiting_s *next; // Next chain in waiting list + int fragbufcount; // Number of buffers in this chain + fragbuf_t *fragbufs; // The actual buffers + } fragbufwaiting_t; + + bool CreateFragmentsFromFile(char *fileName); + bool CopyFileFragments(); + void GetFlowStats(float *avgInKBSec, float *avgOutKBSec); + void SetConnected(bool flag); + void FlushOutgoing(); + void CopyNormalFragments(); + void UpdateFlow(int stream); + void FlushIncoming(int stream); + void ClearFragments(); + void ClearFragbufs(fragbuf_t **ppbuf); + void UnlinkFragment(fragbuf_t *buf, int stream); + NetPacket *GetPacket(); + void FreePacket(NetPacket *packet); + void AddFragbufToTail(fragbufwaiting_t *wait, fragbuf_t *buf); + bool CreateFragmentsFromBuffer(void *buffer, int size, int streamtype, char *filename = nullptr); + void AddBufferToList(fragbuf_t ** pplist, fragbuf_t *pbuf); + void FragSend(); + bool CheckForCompletion(int stream, int intotalbuffers); + fragbuf_t *FindBufferById(fragbuf_t **pplist, int id, bool allocate); + +public: + IBaseSystem *m_System; + INetSocket *m_Socket; + + NetAddress m_remote_address; // Address this channel is talking to. + double m_last_received; + double m_last_send; + double m_connect_time; // Time when channel was connected. + float m_timeout; + int m_max_bandwidth_rate; + double m_send_interval; + int m_updaterate; // Bandwidth choke, bytes per second + double m_cleartime; // If realtime > cleartime, free to send next packet + + bool m_keep_alive; + bool m_crashed; + bool m_connected; + + // Sequencing variables + int m_incoming_sequence; // Increasing count of sequence numbers + int m_incoming_acknowledged; // # of last outgoing message that has been ack'd. + int m_incoming_reliable_acknowledged; // Toggles T/F as reliable messages are received. + int m_incoming_reliable_sequence; // single bit, maintained local + int m_outgoing_sequence; // Message we are sending to remote + int m_reliable_sequence; // Whether the message contains reliable payload, single bit + int m_last_reliable_sequence; // Outgoing sequence number of last send that had reliable data + + void *m_connection_status; + + int m_blocksize; + BitBuffer m_reliableStream; + BitBuffer m_unreliableStream; + ObjectList m_incomingPackets; + + // Reliable message buffer. + // We keep adding to it until reliable is acknowledged. Then we clear it. + int m_reliableOutSize; + unsigned char m_reliableOutBuffer[MAX_MSGLEN + 20]; + + fragbufwaiting_t *m_waitlist[MAX_STREAMS]; // Waiting list of buffered fragments to go onto queue. Multiple outgoing buffers can be queued in succession. + + int m_reliable_fragment[MAX_STREAMS]; // Is reliable waiting buf a fragment? + size_t m_reliable_fragid[MAX_STREAMS]; // Buffer id for each waiting fragment + + fragbuf_t *m_fragbufs[MAX_STREAMS]; // The current fragment being set + int m_fragbufcount[MAX_STREAMS]; // The total number of fragments in this stream + + int16 m_frag_startpos[MAX_STREAMS]; // Position in outgoing buffer where frag data starts + int16 m_frag_length[MAX_STREAMS]; // Length of frag data in the buffer + + fragbuf_t *m_incomingbufs[MAX_STREAMS]; // Incoming fragments are stored here + + // Only referenced by the FRAG_FILE_STREAM component + // Name of file being downloaded + char m_incomingfilename[MAX_PATH]; + + void *m_tempBuffer; + int m_tempBufferSize; + + // Incoming and outgoing flow metrics + flow_t m_flow[MAX_FLOWS]; + float m_loss; +}; diff --git a/rehlds/HLTV/common/ServerInfo.h b/rehlds/HLTV/common/ServerInfo.h new file mode 100644 index 0000000..dde21e5 --- /dev/null +++ b/rehlds/HLTV/common/ServerInfo.h @@ -0,0 +1,85 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +typedef struct serverinfo_s +{ + char address[64]; + char name[80]; + char map[16]; + char gamedir[256]; + char description[256]; + + int activePlayers; + int maxPlayers; + + char type; // HLServerType + char os; + char pw; + bool mod; + + char url_info[256]; + char url_dl[256]; + char hlversion[256]; + + int ver; + int size; + + bool svonly; + bool cldll; + + unsigned char protocol; +} serverinfo_t; + +enum HLServerType { + HLST_Dedicated, + HLST_NonDedicated, + HLST_TV, +}; + +inline const char *GetServerType(HLServerType type) +{ + switch (type) + { + case HLST_Dedicated: return "d"; + case HLST_NonDedicated: return "l"; + case HLST_TV: return "p"; + } + + return ""; +} + +inline const char *GetServerOS() +{ +#ifdef _WIN32 + return "w"; +#else + return "l"; +#endif +} diff --git a/rehlds/HLTV/common/byteorder.cpp b/rehlds/HLTV/common/byteorder.cpp new file mode 100644 index 0000000..b5d340e --- /dev/null +++ b/rehlds/HLTV/common/byteorder.cpp @@ -0,0 +1,89 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +short _BigShort(short l) +{ + return bswap(l); +} + +short _LittleShort(short l) +{ + return l; +} + +int _BigLong(int l) +{ + return bswap(l); +} + +int _LittleLong(int l) +{ + return l; +} + +float _BigFloat(float l) +{ + return bswap(l); +} + +float _LittleFloat(float l) +{ + return l; +} + +int _LongSwap(int l) +{ + return bswap(l); +} + +int _LongNoSwap(int l) +{ + return l; +} + +short _ShortSwap(short l) +{ + return bswap(l); +} + +short _ShortNoSwap(short l) +{ + return l; +} + +float _FloatSwap(float f) +{ + return bswap(f); +} + +float _FloatNoSwap(float f) +{ + return f; +} diff --git a/rehlds/HLTV/common/byteorder.h b/rehlds/HLTV/common/byteorder.h new file mode 100644 index 0000000..2364874 --- /dev/null +++ b/rehlds/HLTV/common/byteorder.h @@ -0,0 +1,42 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +short _BigShort(short l); +short _LittleShort(short l); +int _BigLong(int l); +int _LittleLong(int l); +float _BigFloat(float l); +float _LittleFloat(float l); +int _LongSwap(int l); +int _LongNoSwap(int l); +short _ShortSwap(short l); +short _ShortNoSwap(short l); +float _FloatSwap(float f); +float _FloatNoSwap(float f); diff --git a/rehlds/HLTV/common/common.cpp b/rehlds/HLTV/common/common.cpp new file mode 100644 index 0000000..9e4bf33 --- /dev/null +++ b/rehlds/HLTV/common/common.cpp @@ -0,0 +1,468 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +static char *date = __DATE__; +static char *mon[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; +static char mond[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +int COM_BuildNumber() +{ + int m = 0; + int d = 0; + int y = 0; + static int b = 0; + + if (b != 0) + return b; + + for (m = 0; m < 11; m++) + { + if (_strnicmp(&date[0], mon[m], 3) == 0) + break; + d += mond[m]; + } + + d += atoi(&date[4]) - 1; + y = atoi(&date[7]) - 1900; + b = d + (int)((y - 1) * 365.25); + + if (((y % 4) == 0) && m > 1) + { + b += 1; + } + + // return days since initial commit on Apr 12 2014 (Happy Cosmonautics Day!) + b -= 41374; + return b; +} + +char *COM_TimeString() +{ + static char timedate[16]; + time_t ltime = time(nullptr); + tm *now = localtime(<ime); + + strftime(timedate, sizeof(timedate), "%y%m%d%H%M", now); + return timedate; +} + +char *COM_SkipPath(char *pathname) +{ + char *last = pathname; + while (*pathname) + { + if (*pathname == '/' || *pathname == '\\') { + last = pathname + 1; + } + + pathname++; + } + + return last; +} + +#ifdef _WIN32 +char *COM_GetBaseDir() +{ + static char basedir[MAX_PATH]; + basedir[0] = '\0'; + + if (GetModuleFileName((HMODULE)nullptr, basedir, sizeof(basedir))) + { + char *pBuffer = strrchr(basedir, '\\'); + + if (pBuffer && *pBuffer) + pBuffer[1] = '\0'; + + int j = strlen(basedir); + if (j > 0 && (basedir[j - 1] == '\\' || basedir[j - 1] == '/')) + basedir[j - 1] = '\0'; + } + + return basedir; +} + +#else // _WIN32 + +char g_szEXEName[256]; +char *COM_GetBaseDir() +{ + static char basedir[MAX_PATH]; + basedir[0] = '\0'; + + strcpy(basedir, g_szEXEName); + char *pBuffer = strrchr(basedir, '/'); + + if (pBuffer && *pBuffer) + pBuffer[1] = '\0'; + + int j = strlen(basedir); + if (j > 0 && (basedir[j - 1] == '\\' || basedir[j - 1] == '/')) + basedir[j - 1] = '\0'; + + return basedir; +} + +#endif // _WIN32 + +void COM_FixSlashes(char *pname) +{ + while (*pname) + { +#ifdef _WIN32 + if (*pname == '/') + { + *pname = '\\'; + } +#else + if (*pname == '\\') + { + *pname = '/'; + } +#endif + + pname++; + } +} + +// Fills "out" with the file name without path and extension. +void COM_FileBase(char *in, char *out) +{ + *out = '\0'; + + int len = strlen(in); + if (len <= 0) { + return; + } + + const char *start = in + len - 1; + const char *end = in + len; + + while (start >= in && *start != '/' && *start != '\\') + { + if (*start == '.') { + end = start; + } + + start--; + } + start++; + + len = end - start; + strncpy(out, start, len); + out[len] = '\0'; +} + +char com_token[COM_TOKEN_LEN]; +qboolean s_com_token_unget = FALSE; + +void COM_UngetToken() +{ + s_com_token_unget = TRUE; +} + +char *COM_Parse(char *data) +{ + int c; + int len; + + if (s_com_token_unget) + { + s_com_token_unget = FALSE; + return data; + } + + len = 0; + com_token[0] = '\0'; + + if (!data) { + return nullptr; + } + +skipwhite: + while (*data <= 32) + { + if (!*data) { + return nullptr; + } + + data++; + } + + c = *data; + + // skip // comments till the next line + if (c == '/' && data[1] == '/') + { + while (*data && *data != '\n') + data++; + + // start over new line + goto skipwhite; + } + + // handle quoted strings specially: copy till the end or another quote + if (c == '\"') + { + // skip starting quote + data++; + while (true) + { + c = *data++; // get char and advance + if (c == '\"') // closing quote + { + com_token[len] = '\0'; + return data; + } + + if (!c || len == COM_TOKEN_LEN - 1) // check if buffer is full + { + com_token[len] = 0; + return data; + } + + com_token[len++] = c; + } + } + else + { + // parse single characters + if (c == '{' || c == '}' || c == ')' || c == '(' || c == '\'' || c == ',') + { + com_token[len++] = c; + com_token[len] = '\0'; + return data + 1; + } + + // parse a regular word + do + { + com_token[len] = c; + data++; + len++; + c = *data; + if (c == '{' || c == '}' || c == ')' || c == '(' || c == '\'' || c == ',') + break; + } + while (len < COM_TOKEN_LEN - 1 && (c < 0 || c > 32)); + } + + com_token[len] = '\0'; + return data; +} + +char *COM_VarArgs(char *format, ...) +{ + va_list argptr; + static char string[1024]; + + va_start(argptr, format); + _vsnprintf(string, sizeof(string), format, argptr); + va_end(argptr); + + return string; +} + +unsigned char COM_Nibble(char c) +{ + if (c >= '0' && c <= '9') + { + return (unsigned char)(c - '0'); + } + + if (c >= 'A' && c <= 'F') + { + return (unsigned char)(c - 'A' + 0x0A); + } + + if (c >= 'a' && c <= 'f') + { + return (unsigned char)(c - 'a' + 0x0A); + } + + return '0'; +} + +void COM_HexConvert(const char *pszInput, int nInputLength, unsigned char *pOutput) +{ + const char *pIn; + unsigned char *p = pOutput; + for (int i = 0; i < nInputLength - 1; i += 2) + { + pIn = &pszInput[i]; + if (!pIn[0] || !pIn[1]) + break; + + *p++ = ((COM_Nibble(pIn[0]) << 4) | COM_Nibble(pIn[1])); + } +} + +char *COM_BinPrintf(unsigned char *buf, int length) +{ + char szChunk[10]; + static char szReturn[4096]; + memset(szReturn, 0, sizeof(szReturn)); + + for (int i = 0; i < length; i++) + { + _snprintf(szChunk, sizeof(szChunk), "%02x", buf[i]); + strncat(szReturn, szChunk, sizeof(szReturn) - strlen(szReturn) - 1); + } + + return szReturn; +} + +char *COM_FormatTime(float seconds) +{ + + static char time[32]; + int hours = (int)seconds / 3600; + if (hours > 0) + { + _snprintf(time, sizeof(time), "%02i:%2i:%02i", hours, (int)seconds / 60, (int)seconds % 60); + } + else + { + _snprintf(time, sizeof(time), "%02i:%02i", (int)seconds / 60, (int)seconds % 60); + } + + return time; +} + +void COM_RemoveEvilChars(char *string) +{ + char *c = string; + if (!c) { + return; + } + + while (*c) + { + if (*c < ' ' || *c > '~' || *c == '%' || *c == ';') { + *c = ' '; + } + + c++; + } +} + +int COM_FileNameCmp(const char *file1, const char *file2) +{ + while (*file1 && *file2) + { + if ((*file1 != '/' || *file2 != '\\') && (*file2 != '/' || *file1 != '\\')) + { + if (tolower(*file1) != tolower(*file2)) + return -1; + + if (!*file1) + return 0; + } + + file1++; + file2++; + } + + return 0; +} + +bool COM_IsWhiteSpace(char space) +{ + switch (space) { + case '\t': + case '\r': + case '\n': + case ' ': + return true; + } + + return false; +} + +void COM_TrimSpace(const char *source, char *dest) +{ + int start = 0; + while (source[start] && COM_IsWhiteSpace(source[start])) { + start++; + } + + int end = strlen(source) - 1; + while (end > 0 && COM_IsWhiteSpace(source[end])) { + end--; + } + + end++; + + int length = end - start; + if (length > 0) { + strncpy(dest, &source[start], length); + } + else + length = 0; + + dest[length] = '\0'; +} + +void COM_UnpackRGB(unsigned char &r, unsigned char &g, unsigned char &b, unsigned int color) +{ + r = (color >> 16) & 0xFF; + g = (color >> 8) & 0xFF; + b = (color) & 0xFF; +} + +unsigned int COM_PackRGB(unsigned char r, unsigned char g, unsigned char b) +{ + return (r << 16) | (g << 8) | b; +} + +unsigned int COM_PackRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + return (a << 24) | (r << 16) | (g << 8) | b; +} + +void NORETURN HLTV_SysError(const char *fmt, ...) +{ + va_list argptr; + static char string[8192]; + + va_start(argptr, fmt); + vsnprintf(string, sizeof(string), fmt, argptr); + va_end(argptr); + + printf("%s\n", string); + + FILE* fl = fopen("hltv_error.txt", "w"); + fprintf(fl, "%s\n", string); + fclose(fl); + + int *null = 0; + *null = 0; + exit(-1); +} diff --git a/rehlds/HLTV/common/common_hltv.h b/rehlds/HLTV/common/common_hltv.h new file mode 100644 index 0000000..8af86ed --- /dev/null +++ b/rehlds/HLTV/common/common_hltv.h @@ -0,0 +1,55 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +int COM_BuildNumber(); +char *COM_TimeString(); +char *COM_SkipPath(char *pathname); +char *COM_GetBaseDir(); +void COM_FixSlashes(char *pname); +void COM_FileBase(char *in, char *out); +void COM_UngetToken(); +char *COM_Parse(char *data); +char *COM_VarArgs(char *format, ...); +unsigned char COM_Nibble(char c); +void COM_HexConvert(const char *pszInput, int nInputLength, unsigned char *pOutput); +char *COM_BinPrintf(unsigned char *buf, int length); +char *COM_FormatTime(float seconds); +void COM_RemoveEvilChars(char *string); +int COM_FileNameCmp(const char *file1, const char *file2); +bool COM_IsWhiteSpace(char space); +void COM_TrimSpace(const char *source, char *dest); +void COM_UnpackRGB(unsigned char &r, unsigned char &g, unsigned char &b, unsigned int color); +unsigned int COM_PackRGB(unsigned char r, unsigned char g, unsigned char b); +unsigned int COM_PackRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a); +void NORETURN HLTV_SysError(const char *fmt, ...); + +#ifdef LINUX +extern char g_szEXEName[256]; +#endif // LINUX diff --git a/rehlds/HLTV/common/mathlib.cpp b/rehlds/HLTV/common/mathlib.cpp new file mode 100644 index 0000000..8b3f96f --- /dev/null +++ b/rehlds/HLTV/common/mathlib.cpp @@ -0,0 +1,390 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +vec3_t vec3_origin; + +float TriangleDiameter(const vec_t *v1, vec_t *v2, vec_t *v3) +{ + vec3_t e1, e2, e3; + float l1, l2, l3; + int i; + + l1 = 0; + l2 = 0; + l3 = 0; + + VectorSubtract(v2, v1, e1); + VectorSubtract(v3, v2, e2); + VectorSubtract(v1, v3, e3); + + for (i = 0; i < 3; i++) { + l3 = e1[i] * e1[i] + l3; + } + + for (i = 0; i < 3; i++) { + l2 = e2[i] * e2[i] + l2; + } + + for (i = 0; i < 3; i++) { + l1 = e3[i] * e3[i] + l1; + } + + if (l3 > l2 && l3 > l1) { + return sqrt(l3); + } + + if (l2 > l3 && l2 > l1) { + return sqrt(l2); + } + + return sqrt(l1); +} + +float VectorNormalize(vec_t *v) +{ + float length = Length(v); + if (length) + { + float ilength = 1 / length; + + v[0] *= ilength; + v[1] *= ilength; + v[2] *= ilength; + } + + return length; +} + +float VectorDistance(const vec_t *v1, const vec_t *v2) +{ + vec3_t t; + VectorSubtract(v1, v2, t); + return Length(t); +} + +void VectorAngles(const float *forward, float *angles) +{ + float tmp, yaw, pitch; + if (forward[1] == 0 && forward[0] == 0) + { + yaw = 0; + if (forward[2] > 0) + pitch = 90; + else + pitch = 270; + } + else + { + yaw = float(atan2(forward[1], forward[0]) * 180 / M_PI); + if (yaw < 0) + yaw += 360; + + tmp = sqrt(forward[0] * forward[0] + forward[1] * forward[1]); + pitch = float(atan2(forward[2], tmp) * 180 / M_PI); + if (pitch < 0) + pitch += 360; + } + + angles[0] = pitch; + angles[1] = yaw; + angles[2] = 0; +} + +void VectorRAngles(const float *v, float *a) +{ + a[0] = atan2(v[2], sqrt(v[0] * v[0] + v[1] * v[1])); + a[1] = atan2(v[1], v[0]); + NormalizeRAngles(a); +} + +int BoxOnPlaneSide(vec_t *emins, vec_t *emaxs, mplane_t *p) +{ + // From sources + float dist1, dist2; + int sides = 0; + + // general case + switch (p->signbits) + { + case 0: + dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]; + dist2 = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]; + break; + case 1: + dist1 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]; + dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]; + break; + case 2: + dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]; + dist2 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]; + break; + case 3: + dist1 = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]; + dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]; + break; + case 4: + dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]; + dist2 = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]; + break; + case 5: + dist1 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emins[2]; + dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emaxs[2]; + break; + case 6: + dist1 = p->normal[0] * emaxs[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]; + dist2 = p->normal[0] * emins[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]; + break; + case 7: + dist1 = p->normal[0] * emins[0] + p->normal[1] * emins[1] + p->normal[2] * emins[2]; + dist2 = p->normal[0] * emaxs[0] + p->normal[1] * emaxs[1] + p->normal[2] * emaxs[2]; + break; + default: + dist1 = dist2 = 0.0; + break; + } + + if (dist1 >= p->dist) + sides = 1; + if (dist2 < p->dist) + sides |= 2; + + return sides; +} + +float AngleBetweenVectors(const vec_t *angle1, const vec_t *angle2) +{ + float angle; + float d1 = Length(angle1); + float d2 = Length(angle2); + + if (!d1 || !d2) { + return 0; + } + + angle = acos(DotProduct(angle1, angle2) / (d1 * d2)); + angle = angle * float(180.0f / M_PI); + + return angle; +} + +float RAngleBetweenVectors(const vec_t *angle1, const vec_t *angle2) +{ + float d1, d2, d3; + + d1 = Length(angle1); + d2 = Length(angle2); + + if (!d1 || !d2) { + return 0; + } + + d3 = DotProduct(angle1, angle2) / (d1 * d2); + if (d3 > 1) { + return 0; + } + + if (d3 < -1) { + return (float)M_PI; + } + + return acosf(d3); +} + +void NormalizeAngles(vec_t *angles) +{ + // Normalize angles + for (int i = 0; i < 3; ++i) + { + if (angles[i] > 180.0) + { + angles[i] -= 360.0; + } + else if (angles[i] < -180.0) + { + angles[i] += 360.0; + } + } +} + +void NormalizeRAngles(vec_t *a) +{ + if (a[0] > M_PI) { + a[0] -= float(M_PI * 2); + } + else if (a[0] < -M_PI) { + a[0] += float(M_PI * 2); + } + + if (a[1] > M_PI) { + a[1] -= float(M_PI * 2); + } + else if (a[1] < -M_PI) { + a[1] += float(M_PI * 2); + } + + a[2] = 0; +} + +void AngleVectors(const vec_t *angles, vec_t *forward, vec_t *right, vec_t *up) +{ + float sr, sp, sy, cr, cp, cy; + + float angle; + angle = (float)(angles[YAW] * (M_PI * 2 / 360)); + sy = sin(angle); + cy = cos(angle); + angle = (float)(angles[PITCH] * (M_PI * 2 / 360)); + sp = sin(angle); + cp = cos(angle); + angle = (float)(angles[ROLL] * (M_PI * 2 / 360)); + sr = sin(angle); + cr = cos(angle); + + if (forward) + { + forward[0] = cp * cy; + forward[1] = cp * sy; + forward[2] = -sp; + } + + if (right) + { + right[0] = (-1 * sr * sp * cy + -1 * cr * -sy); + right[1] = (-1 * sr * sp * sy + -1 * cr * cy); + right[2] = -1 * sr * cp; + } + + if (up) + { + up[0] = (cr * sp * cy + -sr * -sy); + up[1] = (cr * sp * sy + -sr * cy); + up[2] = cr * cp; + } +} + +int AngleLeftOfOther(const vec_t *v1, const vec_t *v2) +{ + float a = v1[1]; + float b = v2[1]; + + if (a >= 180) + return (b >= a) || ((a - 180) > b); + else + return (b > a) && ((a + 180) >= b); +} + +float RadiusFromBounds(const vec_t *mins, const vec_t *maxs) +{ + vec3_t corner; + for (int i = 0; i < 3; i++) + { + float fmin = fabs(mins[i]); + float fmax = fabs(mins[i]); + + if (fmin > fmax) + corner[i] = fmin; + else + corner[i] = fmax; + } + + return Length(corner); +} + +bool SolveLSE(vec_t *v0, vec_t *v1, vec_t *v2, vec_t *v3, float *x, float *y, float *z) +{ + float d = v1[2] * (v2[0] * v3[1] - v2[1] * v3[0]) + v1[1] * (v3[0] * v2[2] - v2[0] * v3[2]) + v1[0] * (v2[1] * v3[2] - v3[1] * v2[2]); + if (!d) { + return false; + } + + if (x) *x = ((v2[0] * v3[1] - v2[1] * v3[0]) * v0[2] + (v3[0] * v2[2] - v2[0] * v3[2]) * v0[1] + (v2[1] * v3[2] - v3[1] * v2[2]) * v0[0]) / d; + if (y) *y = ((v0[0] * v3[1] - v0[1] * v3[0]) * v1[2] + (v3[0] * v0[2] - v0[0] * v3[2]) * v1[1] + (v0[1] * v3[2] - v3[1] * v0[2]) * v1[0]) / d; + if (z) *z = ((v2[0] * v0[1] - v2[1] * v0[0]) * v1[2] + (v0[0] * v2[2] - v2[0] * v0[2]) * v1[1] + (v2[1] * v0[2] - v0[1] * v2[2]) * v1[0]) / d; + + return true; +} + +void NormalizePoints(vec_t *v0, vec_t *v1) +{ + vec3_t a; + VectorSubtract(v1, v0, a); + + if (Length(a)) + { + VectorNormalize(a); + VectorAdd(a, v0, v1); + } +} + +void Normal2Plane(vec_t *v1, vec_t *v2, vec_t *v3) +{ + if (v1[0]) + { + v2[0] = -v1[1] / v1[0]; + v2[1] = 1; + v2[2] = 0; + + v3[0] = -v1[2] / v1[0]; + v3[1] = 0; + v3[2] = 1; + return; + } + + if (v1[1]) + { + v2[0] = 1; + v2[1] = -v1[0] / v1[1]; + v2[2] = 0; + + v3[0] = 0; + v3[1] = -v1[2] / v1[1]; + v3[2] = 1; + + return; + } + + v2[0] = 1; + v2[1] = 0; + v2[2] = -v1[0] / v1[2]; + + v3[0] = 0; + v3[1] = 1; + v3[2] = -v1[1] / v1[2]; +} + +float Length(const vec_t *v) +{ + return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); +} + +float DotProduct(const vec_t *v1, const vec_t *v2) +{ + return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; +} diff --git a/rehlds/HLTV/common/mathlib_internal.h b/rehlds/HLTV/common/mathlib_internal.h new file mode 100644 index 0000000..2fa959f --- /dev/null +++ b/rehlds/HLTV/common/mathlib_internal.h @@ -0,0 +1,65 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "model.h" + +#define PITCH 0 // up / down +#define YAW 1 // left / right +#define ROLL 2 // fall over + +extern vec3_t vec3_origin; + +static const int nanmask = 0x7F800000; +#define IS_NAN(fvar) ((*reinterpret_cast(&(fvar)) & nanmask) == nanmask) + +#define VectorScale(a,b,c) {(c)[0]=(b)*(a)[0];(c)[1]=(b)*(a)[1];(c)[2]=(b)*(a)[2];} +#define VectorSubtract(a,b,c) {(c)[0]=(a)[0]-(b)[0];(c)[1]=(a)[1]-(b)[1];(c)[2]=(a)[2]-(b)[2];} +#define VectorAdd(a,b,c) {(c)[0]=(a)[0]+(b)[0];(c)[1]=(a)[1]+(b)[1];(c)[2]=(a)[2]+(b)[2];} +#define VectorCopy(a,b) {(b)[0]=(a)[0];(b)[1]=(a)[1];(b)[2]=(a)[2];} +#define VectorClear(a) {(a)[0]=0.0;(a)[1]=0.0;(a)[2]=0.0;} + +float DotProduct(const vec_t *v1, const vec_t *v2); +float TriangleDiameter(const vec_t *v1, vec_t *v2, vec_t *v3); +float VectorNormalize(vec_t *v); +float VectorDistance(const vec_t *v1, const vec_t *v2); +void VectorAngles(const float *forward, float *angles); +void VectorRAngles(const float *v, float *a); +int BoxOnPlaneSide(vec_t *emins, vec_t *emaxs, struct mplane_s *p); +float AngleBetweenVectors(const vec_t *angle1, const vec_t *angle2); +float RAngleBetweenVectors(const vec_t *angle1, const vec_t *angle2); +void NormalizeAngles(vec_t *angles); +void NormalizeRAngles(vec_t *a); +void AngleVectors(const vec_t *angles, vec_t *forward, vec_t *right = nullptr, vec_t *up = nullptr); +int AngleLeftOfOther(const vec_t *v1, const vec_t *v2); +float RadiusFromBounds(const vec_t *mins, const vec_t *maxs); +bool SolveLSE(vec_t *v0, vec_t *v1, vec_t *v2, vec_t *v3, float *x, float *y, float *z); +void NormalizePoints(vec_t *v0, vec_t *v1); +void Normal2Plane(vec_t *v1, vec_t *v2, vec_t *v3); +float Length(const vec_t *v); diff --git a/rehlds/HLTV/common/md5.cpp b/rehlds/HLTV/common/md5.cpp new file mode 100644 index 0000000..801b933 --- /dev/null +++ b/rehlds/HLTV/common/md5.cpp @@ -0,0 +1,317 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +static unsigned char PADDING[64] = +{ + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* F, G and H are basic MD5 functions: selection, majority, parity */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ +/* Rotation is separate from addition to prevent recomputation */ +#define FF(a, b, c, d, x, s, ac) \ + { \ + (a) += F ((b), (c), (d)) + (x) + (uint32)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) \ + { \ + (a) += G ((b), (c), (d)) + (x) + (uint32)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) \ + { \ + (a) += H ((b), (c), (d)) + (x) + (uint32)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) \ + { \ + (a) += I ((b), (c), (d)) + (x) + (uint32)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +void MD5_Init(MD5Context_t *ctx) +{ + ctx->bits[0] = ctx->bits[1] = 0; + + // Load magic initialization constants. + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; +} + +void MD5_Transform(unsigned int *buf, const unsigned int *in) +{ + uint32 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + // Round 1 +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + FF(a, b, c, d, in[0], S11, 3614090360); /* 1 */ + FF(d, a, b, c, in[1], S12, 3905402710); /* 2 */ + FF(c, d, a, b, in[2], S13, 606105819); /* 3 */ + FF(b, c, d, a, in[3], S14, 3250441966); /* 4 */ + FF(a, b, c, d, in[4], S11, 4118548399); /* 5 */ + FF(d, a, b, c, in[5], S12, 1200080426); /* 6 */ + FF(c, d, a, b, in[6], S13, 2821735955); /* 7 */ + FF(b, c, d, a, in[7], S14, 4249261313); /* 8 */ + FF(a, b, c, d, in[8], S11, 1770035416); /* 9 */ + FF(d, a, b, c, in[9], S12, 2336552879); /* 10 */ + FF(c, d, a, b, in[10], S13, 4294925233); /* 11 */ + FF(b, c, d, a, in[11], S14, 2304563134); /* 12 */ + FF(a, b, c, d, in[12], S11, 1804603682); /* 13 */ + FF(d, a, b, c, in[13], S12, 4254626195); /* 14 */ + FF(c, d, a, b, in[14], S13, 2792965006); /* 15 */ + FF(b, c, d, a, in[15], S14, 1236535329); /* 16 */ + + // Round 2 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + GG(a, b, c, d, in[1], S21, 4129170786); /* 17 */ + GG(d, a, b, c, in[6], S22, 3225465664); /* 18 */ + GG(c, d, a, b, in[11], S23, 643717713); /* 19 */ + GG(b, c, d, a, in[0], S24, 3921069994); /* 20 */ + GG(a, b, c, d, in[5], S21, 3593408605); /* 21 */ + GG(d, a, b, c, in[10], S22, 38016083); /* 22 */ + GG(c, d, a, b, in[15], S23, 3634488961); /* 23 */ + GG(b, c, d, a, in[4], S24, 3889429448); /* 24 */ + GG(a, b, c, d, in[9], S21, 568446438); /* 25 */ + GG(d, a, b, c, in[14], S22, 3275163606); /* 26 */ + GG(c, d, a, b, in[3], S23, 4107603335); /* 27 */ + GG(b, c, d, a, in[8], S24, 1163531501); /* 28 */ + GG(a, b, c, d, in[13], S21, 2850285829); /* 29 */ + GG(d, a, b, c, in[2], S22, 4243563512); /* 30 */ + GG(c, d, a, b, in[7], S23, 1735328473); /* 31 */ + GG(b, c, d, a, in[12], S24, 2368359562); /* 32 */ + + // Round 3 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + HH(a, b, c, d, in[5], S31, 4294588738); /* 33 */ + HH(d, a, b, c, in[8], S32, 2272392833); /* 34 */ + HH(c, d, a, b, in[11], S33, 1839030562); /* 35 */ + HH(b, c, d, a, in[14], S34, 4259657740); /* 36 */ + HH(a, b, c, d, in[1], S31, 2763975236); /* 37 */ + HH(d, a, b, c, in[4], S32, 1272893353); /* 38 */ + HH(c, d, a, b, in[7], S33, 4139469664); /* 39 */ + HH(b, c, d, a, in[10], S34, 3200236656); /* 40 */ + HH(a, b, c, d, in[13], S31, 681279174); /* 41 */ + HH(d, a, b, c, in[0], S32, 3936430074); /* 42 */ + HH(c, d, a, b, in[3], S33, 3572445317); /* 43 */ + HH(b, c, d, a, in[6], S34, 76029189); /* 44 */ + HH(a, b, c, d, in[9], S31, 3654602809); /* 45 */ + HH(d, a, b, c, in[12], S32, 3873151461); /* 46 */ + HH(c, d, a, b, in[15], S33, 530742520); /* 47 */ + HH(b, c, d, a, in[2], S34, 3299628645); /* 48 */ + + // Round 4 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + II(a, b, c, d, in[0], S41, 4096336452); /* 49 */ + II(d, a, b, c, in[7], S42, 1126891415); /* 50 */ + II(c, d, a, b, in[14], S43, 2878612391); /* 51 */ + II(b, c, d, a, in[5], S44, 4237533241); /* 52 */ + II(a, b, c, d, in[12], S41, 1700485571); /* 53 */ + II(d, a, b, c, in[3], S42, 2399980690); /* 54 */ + II(c, d, a, b, in[10], S43, 4293915773); /* 55 */ + II(b, c, d, a, in[1], S44, 2240044497); /* 56 */ + II(a, b, c, d, in[8], S41, 1873313359); /* 57 */ + II(d, a, b, c, in[15], S42, 4264355552); /* 58 */ + II(c, d, a, b, in[6], S43, 2734768916); /* 59 */ + II(b, c, d, a, in[13], S44, 1309151649); /* 60 */ + II(a, b, c, d, in[4], S41, 4149444226); /* 61 */ + II(d, a, b, c, in[11], S42, 3174756917); /* 62 */ + II(c, d, a, b, in[2], S43, 718787259); /* 63 */ + II(b, c, d, a, in[9], S44, 3951481745); /* 64 */ + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +void MD5_Update(MD5Context_t *ctx, const unsigned char *buf, unsigned int len) +{ + uint32 in[16]; + int mdi; + unsigned int i, ii; + + // Compute number of bytes mod 64 + mdi = (int)((ctx->bits[0] >> 3) & 0x3F); + + // Update number of bits + if ((ctx->bits[0] + ((uint32)len << 3)) < ctx->bits[0]) + { + ctx->bits[1]++; + } + + ctx->bits[0] += ((uint32)len << 3); + ctx->bits[1] += ((uint32)len >> 29); + + while (len--) + { + // Add new character to buffer, increment mdi + ctx->in[mdi++] = *buf++; + + // Transform if necessary + if (mdi == 0x40) + { + for (i = 0, ii = 0; i < 16; i++, ii += 4) + in[i] = (((uint32)ctx->in[ii + 3]) << 24) | + (((uint32)ctx->in[ii + 2]) << 16) | + (((uint32)ctx->in[ii + 1]) << 8) | + ((uint32)ctx->in[ii]); + MD5_Transform(ctx->buf, in); + mdi = 0; + } + } +} + +void MD5_Final(unsigned char *digest, MD5Context_t *ctx) +{ + uint32 in[16]; + int mdi; + unsigned int i, ii; + unsigned int padLen; + + // save number of bits + in[14] = ctx->bits[0]; + in[15] = ctx->bits[1]; + + // Compute number of bytes mod 64 + mdi = (int)((ctx->bits[0] >> 3) & 0x3F); + + // Pad out to 56 mod 64 + padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); + MD5_Update(ctx, PADDING, padLen); + + // Append length in bits and transform + for (i = 0, ii = 0; i < 14; i++, ii += 4) + in[i] = (((uint32)ctx->in[ii + 3]) << 24) | + (((uint32)ctx->in[ii + 2]) << 16) | + (((uint32)ctx->in[ii + 1]) << 8) | + ((uint32)ctx->in[ii]); + MD5_Transform(ctx->buf, in); + + // Store buffer in digest + for (i = 0, ii = 0; i < 4; i++, ii += 4) + { + digest[ii] = (unsigned char)(ctx->buf[i] & 0xFF); + digest[ii + 1] = (unsigned char)((ctx->buf[i] >> 8) & 0xFF); + digest[ii + 2] = (unsigned char)((ctx->buf[i] >> 16) & 0xFF); + digest[ii + 3] = (unsigned char)((ctx->buf[i] >> 24) & 0xFF); + } +} + +char *MD5_Print(unsigned char *hash) +{ + static char szReturn[64]; + char szChunk[10]; + int i; + + memset(szReturn, 0, sizeof(szReturn)); + + for (i = 0; i < 16; i++) + { + _snprintf(szChunk, sizeof(szChunk), "%02x", hash[i]); + strncat(szReturn, szChunk, sizeof(szReturn) - strlen(szReturn) - 1); + } + + return szReturn; +} + +char *MD5_GetCDKeyHash(const char *key) +{ + static char szHashedKeyBuffer[256]; + + char szKeyBuffer[256]; + int nKeyLength = strlen(key); + if (nKeyLength <= 0 || nKeyLength > 255) { + return nullptr; + } + + strncpy(szKeyBuffer, key, sizeof(szKeyBuffer) - 1); + szKeyBuffer[sizeof(szKeyBuffer) - 1] = '\0'; + szKeyBuffer[nKeyLength] = '\0'; + + MD5Context_t ctx; + unsigned char digest[16]; + + memset(&ctx, 0, sizeof(ctx)); + memset(digest, 0, sizeof(digest)); + memset(szHashedKeyBuffer, 0, sizeof(szHashedKeyBuffer)); + + MD5_Init(&ctx); + MD5_Update(&ctx, (unsigned char *)szKeyBuffer, nKeyLength); + MD5_Final(digest, &ctx); + + strncpy(szHashedKeyBuffer, MD5_Print(digest), sizeof(szHashedKeyBuffer) - 1); + szHashedKeyBuffer[sizeof(szHashedKeyBuffer) - 1] = '\0'; + + return szHashedKeyBuffer; +} + +void MD5_Hash_Mem(unsigned char *digest, unsigned char *mem, int size) +{ + MD5Context_t ctx; + memset(&ctx, 0, sizeof(ctx)); + + MD5_Init(&ctx); + MD5_Update(&ctx, mem, size); + MD5_Final(digest, &ctx); +} diff --git a/rehlds/HLTV/common/md5.h b/rehlds/HLTV/common/md5.h new file mode 100644 index 0000000..22b7945 --- /dev/null +++ b/rehlds/HLTV/common/md5.h @@ -0,0 +1,45 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +// MD5 Hash +typedef struct +{ + unsigned int buf[4]; + unsigned int bits[2]; + unsigned char in[64]; +} MD5Context_t; + +void MD5_Init(MD5Context_t *ctx); +void MD5_Transform(unsigned int *buf, const unsigned int *in); +void MD5_Update(MD5Context_t *ctx, const unsigned char *buf, unsigned int len); +void MD5_Final(unsigned char *digest, MD5Context_t *ctx); +char *MD5_Print(unsigned char *hash); +char *MD5_GetCDKeyHash(const char *key); +void MD5_Hash_Mem(unsigned char *digest, unsigned char *mem, int size); diff --git a/rehlds/HLTV/common/munge.cpp b/rehlds/HLTV/common/munge.cpp new file mode 100644 index 0000000..0a0395e --- /dev/null +++ b/rehlds/HLTV/common/munge.cpp @@ -0,0 +1,235 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +const unsigned char mungify_table[] = +{ + 0x7A, 0x64, 0x05, 0xF1, + 0x1B, 0x9B, 0xA0, 0xB5, + 0xCA, 0xED, 0x61, 0x0D, + 0x4A, 0xDF, 0x8E, 0xC7 +}; + +const unsigned char mungify_table2[] = +{ + 0x05, 0x61, 0x7A, 0xED, + 0x1B, 0xCA, 0x0D, 0x9B, + 0x4A, 0xF1, 0x64, 0xC7, + 0xB5, 0x8E, 0xDF, 0xA0 +}; + +unsigned char mungify_table3[] = +{ + 0x20, 0x07, 0x13, 0x61, + 0x03, 0x45, 0x17, 0x72, + 0x0A, 0x2D, 0x48, 0x0C, + 0x4A, 0x12, 0xA9, 0xB5 +}; + +// Anti-proxy/aimbot obfuscation code +// COM_UnMunge should reversably fixup the data +void COM_Munge(unsigned char *data, int len, int seq) +{ + int i; + int mungelen; + int c; + int *pc; + unsigned char *p; + int j; + + mungelen = len & ~3; + mungelen /= 4; + + for (i = 0; i < mungelen; i++) + { + pc = (int *)&data[i * 4]; + c = *pc; + c ^= ~seq; + c = _LongSwap(c); + + p = (unsigned char *)&c; + for (j = 0; j < 4; j++) + { + *p++ ^= (0xa5 | (j << j) | j | mungify_table[(i + j) & 0x0f]); + } + + c ^= seq; + *pc = c; + } +} + +void COM_UnMunge(unsigned char *data, int len, int seq) +{ + int i; + int mungelen; + int c; + int *pc; + unsigned char *p; + int j; + + mungelen = len & ~3; + mungelen /= 4; + + for (i = 0; i < mungelen; i++) + { + pc = (int *)&data[i * 4]; + c = *pc; + c ^= seq; + + p = (unsigned char *)&c; + for (j = 0; j < 4; j++) + { + *p++ ^= (0xa5 | (j << j) | j | mungify_table[(i + j) & 0x0f]); + } + + c = _LongSwap(c); + c ^= ~seq; + *pc = c; + } +} + +void COM_Munge2(unsigned char *data, int len, int seq) +{ + int i; + int mungelen; + int c; + int *pc; + unsigned char *p; + int j; + + mungelen = len & ~3; + mungelen /= 4; + + for (i = 0; i < mungelen; i++) + { + pc = (int *)&data[i * 4]; + c = *pc; + c ^= ~seq; + c = _LongSwap(c); + + p = (unsigned char *)&c; + for (j = 0; j < 4; j++) + { + *p++ ^= (0xa5 | (j << j) | j | mungify_table2[(i + j) & 0x0f]); + } + + c ^= seq; + *pc = c; + } +} + +void COM_UnMunge2(unsigned char *data, int len, int seq) +{ + int i; + int mungelen; + int c; + int *pc; + unsigned char *p; + int j; + + mungelen = len & ~3; + mungelen /= 4; + + for (i = 0; i < mungelen; i++) + { + pc = (int *)&data[i * 4]; + c = *pc; + c ^= seq; + + p = (unsigned char *)&c; + for (j = 0; j < 4; j++) + { + *p++ ^= (0xa5 | (j << j) | j | mungify_table2[(i + j) & 0x0f]); + } + + c = _LongSwap(c); + c ^= ~seq; + *pc = c; + } +} + +void COM_Munge3(unsigned char *data, int len, int seq) +{ + int i; + int mungelen; + int c; + int *pc; + unsigned char *p; + int j; + + mungelen = len & ~3; + mungelen /= 4; + + for (i = 0; i < mungelen; i++) + { + pc = (int *)&data[i * 4]; + c = *pc; + c ^= ~seq; + c = _LongSwap(c); + + p = (unsigned char *)&c; + for (j = 0; j < 4; j++) + { + *p++ ^= (0xa5 | (j << j) | j | mungify_table3[(i + j) & 0x0f]); + } + + c ^= seq; + *pc = c; + } +} + +void COM_UnMunge3(unsigned char *data, int len, int seq) +{ + int i; + int mungelen; + int c; + int *pc; + unsigned char *p; + int j; + + mungelen = len & ~3; + mungelen /= 4; + + for (i = 0; i < mungelen; i++) + { + pc = (int *)&data[i * 4]; + c = *pc; + c ^= seq; + + p = (unsigned char *)&c; + for (j = 0; j < 4; j++) + { + *p++ ^= (0xa5 | (j << j) | j | mungify_table3[(i + j) & 0x0f]); + } + + c = _LongSwap(c); + c ^= ~seq; + *pc = c; + } +} diff --git a/rehlds/HLTV/common/munge.h b/rehlds/HLTV/common/munge.h new file mode 100644 index 0000000..d68b498 --- /dev/null +++ b/rehlds/HLTV/common/munge.h @@ -0,0 +1,36 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +void COM_Munge(unsigned char *data, int len, int seq); +void COM_UnMunge(unsigned char *data, int len, int seq); +void COM_UnMunge2(unsigned char *data, int len, int seq); +void COM_Munge2(unsigned char *data, int len, int seq); +void COM_Munge3(unsigned char *data, int len, int seq); +void COM_UnMunge3(unsigned char *data, int len, int seq); diff --git a/rehlds/HLTV/common/net_internal.h b/rehlds/HLTV/common/net_internal.h new file mode 100644 index 0000000..bb304d7 --- /dev/null +++ b/rehlds/HLTV/common/net_internal.h @@ -0,0 +1,181 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#define MAX_VOICEDATA_LEN 4096 +#define PROTOCOL_VERSION 48 + +// all OOB packet start with this sequence +#define CONNECTIONLESS_HEADER 0xFFFFFFFF + +// info request +#define S2A_INFO 'C' // deprecated goldsrc response +#define S2A_INFO_DETAILED 'm' // New Query protocol, returns dedicated or not, + other performance info. + +#define S2A_PROXY_LISTEN 'G' +#define S2A_PROXY_REDIRECT 'L' + +// Response details about each player on the server +#define S2A_PLAYERS 'D' + +// Response as multi-packeted the rules the server is using +#define S2A_RULES 'E' + +/* ------ S2C_* - Server to client ------ */ +// Rejected the connection because the password is invalid +#define S2C_REJECT_BADPASSWORD '8' + +// Rejected the connection by explain the reason +#define S2C_REJECT '9' + +// Client connection is initiated by requesting a challenge value +// the server sends this value back +#define S2C_CHALLENGE 'A' // + challenge value + +// Send a userid, client remote address, is this server secure and engine build number +#define S2C_CONNECTION 'B' + +// HLMaster rejected a server's connection because the server needs to be updated +#define M2S_REQUESTRESTART 'O' + +// Send a log event as key value +#define S2A_LOGSTRING 'R' + +// Send a log string +#define S2A_LOGKEY 'S' + +// Basic information about the server +#define A2S_INFO 'T' + +// Details about each player on the server +#define A2S_PLAYER 'U' + +// The rules the server is using +#define A2S_RULES 'V' + +/* ------ A2A_* - Another Answer to ------ */ +// Another user is requesting a challenge value from this machine +#define A2A_GETCHALLENGE 'W' // Request challenge # from another machine + +// Generic Ping Request +#define A2A_PING 'i' // respond with an A2A_ACK + +// Generic Ack +#define A2A_ACK 'j' // general acknowledgement without info + +// Print to client console. +#define A2A_PRINT 'l' // print a message on client + +// Challenge response from master +#define M2A_CHALLENGE 's' // + challenge value + +// Max size of udp packet payload +#define MAX_UDP_PACKET 4010 // 9 bytes SPLITHEADER + 4000 payload? + +enum svc_commands_e +{ + svc_bad, + svc_nop, + svc_disconnect, + svc_event, + svc_version, + svc_setview, + svc_sound, + svc_time, + svc_print, + svc_stufftext, + svc_setangle, + svc_serverinfo, + svc_lightstyle, + svc_updateuserinfo, + svc_deltadescription, + svc_clientdata, + svc_stopsound, + svc_pings, + svc_particle, + svc_damage, + svc_spawnstatic, + svc_event_reliable, + svc_spawnbaseline, + svc_temp_entity, + svc_setpause, + svc_signonnum, + svc_centerprint, + svc_killedmonster, + svc_foundsecret, + svc_spawnstaticsound, + svc_intermission, + svc_finale, + svc_cdtrack, + svc_restore, + svc_cutscene, + svc_weaponanim, + svc_decalname, + svc_roomtype, + svc_addangle, + svc_newusermsg, + svc_packetentities, + svc_deltapacketentities, + svc_choke, + svc_resourcelist, + svc_newmovevars, + svc_resourcerequest, + svc_customization, + svc_crosshairangle, + svc_soundfade, + svc_filetxferfailed, + svc_hltv, + svc_director, + svc_voiceinit, + svc_voicedata, + svc_sendextrainfo, + svc_timescale, + svc_resourcelocation, + svc_sendcvarvalue, + svc_sendcvarvalue2, + svc_startofusermessages = svc_sendcvarvalue2, + svc_endoflist = 255, +}; + +enum clc_commands : byte +{ + clc_bad, + clc_nop, + clc_move, + clc_stringcmd, + clc_delta, + clc_resourcelist, + clc_tmove, + clc_fileconsistency, + clc_voicedata, + clc_hltv, + clc_cvarvalue, + clc_cvarvalue2, + clc_endoflist = 255, +}; diff --git a/rehlds/HLTV/common/random.cpp b/rehlds/HLTV/common/random.cpp new file mode 100644 index 0000000..8d4910f --- /dev/null +++ b/rehlds/HLTV/common/random.cpp @@ -0,0 +1,144 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +int idum; + +void SeedRandomNumberGenerator() +{ + idum = -(int)time(nullptr); + if (idum > 1000) + { + idum = -idum; + } + else if (idum > -1000) + { + idum -= 22261048; + } +} + +#define IA 16807 +#define IM 2147483647 +#define IQ 127773 +#define IR 2836 +#define NTAB 32 +#define NDIV (1 + (IM - 1) / NTAB) + +int ran1() +{ + int j; + long k; + static long iy = 0; + static long iv[NTAB]; + + if (idum <= 0 || !iy) + { + if (-(idum) < 1) + idum = 1; + else + idum = -(idum); + + for (j = NTAB + 7; j >= 0; j--) + { + k = (idum) / IQ; + idum = IA * (idum - k * IQ) - IR * k; + + if (idum < 0) + idum += IM; + + if (j < NTAB) + iv[j] = idum; + } + iy = iv[0]; + } + + k = (idum) / IQ; + idum = IA * (idum - k * IQ) - IR * k; + if (idum < 0) + idum += IM; + + j = iy / NDIV; + iy = iv[j]; + iv[j] = idum; + + return iy; +} + +#define AM (1.0 / IM) +#define EPS 1.2e-7 +#define RNMX (1.0 - EPS) + +float fran1() +{ + float temp = (float)AM * ran1(); + if (temp > RNMX) { + return (float)RNMX; + } + else + return temp; +} + +float RandomFloat(float flLow, float flHigh) +{ + SeedRandomNumberGenerator(); + + float fl = fran1(); // float in (0, 1) + return (fl * (flHigh - flLow)) + flLow; // float in (low, high) +} + +#define MAX_RANDOM_RANGE 0x7FFFFFFFUL + +int RandomLong(int lLow, int lHigh) +{ + SeedRandomNumberGenerator(); + + unsigned long maxAcceptable; + unsigned long x = lHigh - lLow + 1; + unsigned long n; + if (x <= 0 || MAX_RANDOM_RANGE < x - 1) + { + return lLow; + } + + // The following maps a uniform distribution on the interval [0,MAX_RANDOM_RANGE] + // to a smaller, client-specified range of [0,x-1] in a way that doesn't bias + // the uniform distribution unfavorably. Even for a worst case x, the loop is + // guaranteed to be taken no more than half the time, so for that worst case x, + // the average number of times through the loop is 2. For cases where x is + // much smaller than MAX_RANDOM_RANGE, the average number of times through the + // loop is very close to 1. + maxAcceptable = MAX_RANDOM_RANGE - ((MAX_RANDOM_RANGE + 1) % x); + do + { + n = ran1(); + } + while (n > maxAcceptable); + + return lLow + (n % x); +} diff --git a/rehlds/HLTV/common/random.h b/rehlds/HLTV/common/random.h new file mode 100644 index 0000000..b2b42da --- /dev/null +++ b/rehlds/HLTV/common/random.h @@ -0,0 +1,33 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +void SeedRandomNumberGenerator(); +float RandomFloat(float flLow, float flHigh); +int RandomLong(int lLow, int lHigh); diff --git a/rehlds/HLTV/msvc/hltv.sln b/rehlds/HLTV/msvc/hltv.sln new file mode 100644 index 0000000..e2dd3a3 --- /dev/null +++ b/rehlds/HLTV/msvc/hltv.sln @@ -0,0 +1,52 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Console", "..\Console\msvc\Console.vcxproj", "{D5CAB879-D54F-456F-8592-31D549CFD1D8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Core", "..\Core\msvc\Core.vcxproj", "{52F752EA-73D1-422D-B805-17EF1FB20E09}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Director", "..\Director\msvc\Director.vcxproj", "{04D0594C-57F5-4277-AF1B-EAE90AED0C3C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DemoPlayer", "..\DemoPlayer\msvc\DemoPlayer.vcxproj", "{05292761-0847-4A68-BA10-9D384DC0D3EE}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bzip2", "..\..\..\dep\bzip2\msvc\bzip2.vcxproj", "{792DF067-9904-4579-99B9-46C17277ADE3}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Proxy", "..\Proxy\msvc\Proxy.vcxproj", "{ADDFF069-D39D-4A1B-87C9-85A62B980547}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D5CAB879-D54F-456F-8592-31D549CFD1D8}.Debug|Win32.ActiveCfg = Debug|Win32 + {D5CAB879-D54F-456F-8592-31D549CFD1D8}.Debug|Win32.Build.0 = Debug|Win32 + {D5CAB879-D54F-456F-8592-31D549CFD1D8}.Release|Win32.ActiveCfg = Release|Win32 + {D5CAB879-D54F-456F-8592-31D549CFD1D8}.Release|Win32.Build.0 = Release|Win32 + {52F752EA-73D1-422D-B805-17EF1FB20E09}.Debug|Win32.ActiveCfg = Debug|Win32 + {52F752EA-73D1-422D-B805-17EF1FB20E09}.Debug|Win32.Build.0 = Debug|Win32 + {52F752EA-73D1-422D-B805-17EF1FB20E09}.Release|Win32.ActiveCfg = Release|Win32 + {52F752EA-73D1-422D-B805-17EF1FB20E09}.Release|Win32.Build.0 = Release|Win32 + {04D0594C-57F5-4277-AF1B-EAE90AED0C3C}.Debug|Win32.ActiveCfg = Debug|Win32 + {04D0594C-57F5-4277-AF1B-EAE90AED0C3C}.Debug|Win32.Build.0 = Debug|Win32 + {04D0594C-57F5-4277-AF1B-EAE90AED0C3C}.Release|Win32.ActiveCfg = Release|Win32 + {04D0594C-57F5-4277-AF1B-EAE90AED0C3C}.Release|Win32.Build.0 = Release|Win32 + {05292761-0847-4A68-BA10-9D384DC0D3EE}.Debug|Win32.ActiveCfg = Debug|Win32 + {05292761-0847-4A68-BA10-9D384DC0D3EE}.Debug|Win32.Build.0 = Debug|Win32 + {05292761-0847-4A68-BA10-9D384DC0D3EE}.Release|Win32.ActiveCfg = Release|Win32 + {05292761-0847-4A68-BA10-9D384DC0D3EE}.Release|Win32.Build.0 = Release|Win32 + {792DF067-9904-4579-99B9-46C17277ADE3}.Debug|Win32.ActiveCfg = Debug|Win32 + {792DF067-9904-4579-99B9-46C17277ADE3}.Debug|Win32.Build.0 = Debug|Win32 + {792DF067-9904-4579-99B9-46C17277ADE3}.Release|Win32.ActiveCfg = Release|Win32 + {792DF067-9904-4579-99B9-46C17277ADE3}.Release|Win32.Build.0 = Release|Win32 + {ADDFF069-D39D-4A1B-87C9-85A62B980547}.Debug|Win32.ActiveCfg = Debug|Win32 + {ADDFF069-D39D-4A1B-87C9-85A62B980547}.Debug|Win32.Build.0 = Debug|Win32 + {ADDFF069-D39D-4A1B-87C9-85A62B980547}.Release|Win32.ActiveCfg = Release|Win32 + {ADDFF069-D39D-4A1B-87C9-85A62B980547}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/rehlds/common/BaseSystemModule.h b/rehlds/common/BaseSystemModule.h index 0a15382..385c418 100644 --- a/rehlds/common/BaseSystemModule.h +++ b/rehlds/common/BaseSystemModule.h @@ -62,7 +62,7 @@ public: virtual int GetState(); virtual int GetVersion(); virtual void ShutDown(); - virtual char *COM_GetBaseDir() { return ""; } + virtual char *GetBaseDir() { return ""; } void FireSignal(unsigned int signal, void *data = nullptr); protected: diff --git a/rehlds/common/IBaseSystem.h b/rehlds/common/IBaseSystem.h index 209950b..9f50fe3 100644 --- a/rehlds/common/IBaseSystem.h +++ b/rehlds/common/IBaseSystem.h @@ -28,6 +28,14 @@ #pragma once +#if defined(_WIN32) + #define LIBRARY_PREFIX "dll" +#elif defined(OSX) + #define LIBRARY_PREFIX "dylib" +#else + #define LIBRARY_PREFIX "so" +#endif + #include "ISystemModule.h" #include "IVGuiModule.h" @@ -77,5 +85,7 @@ public: virtual bool RemoveModule(ISystemModule *module) = 0; virtual void Stop() = 0; - virtual char *COM_GetBaseDir() = 0; + virtual char *GetBaseDir() = 0; }; + +#define BASESYSTEM_INTERFACE_VERSION "basesystem002" diff --git a/rehlds/common/IDemoPlayer.h b/rehlds/common/IDemoPlayer.h new file mode 100644 index 0000000..2431b54 --- /dev/null +++ b/rehlds/common/IDemoPlayer.h @@ -0,0 +1,93 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "ref_params.h" + +class IWorld; +class IProxy; +class DirectorCmd; +class IBaseSystem; +class ISystemModule; +class IObjectContainer; + +class IDemoPlayer { +public: + virtual ~IDemoPlayer() {} + + virtual bool Init(IBaseSystem *system, int serial, char *name) = 0; + virtual void RunFrame(double time) = 0; + virtual void ReceiveSignal(ISystemModule *module, unsigned int signal, void *data) = 0; + virtual void ExecuteCommand(int commandID, char *commandLine) = 0; + virtual void RegisterListener(ISystemModule *module) = 0; + virtual void RemoveListener(ISystemModule *module) = 0; + virtual IBaseSystem *GetSystem() = 0; + virtual int GetSerial() = 0; + virtual char *GetStatusLine() = 0; + virtual char *GetType() = 0; + virtual char *GetName() = 0; + virtual int GetState() = 0; + virtual int GetVersion() = 0; + virtual void ShutDown() = 0; + + virtual void NewGame(IWorld *world, IProxy *proxy = nullptr) = 0; + virtual char *GetModName() = 0; + virtual void WriteCommands(BitBuffer *stream, float startTime, float endTime) = 0; + virtual int AddCommand(DirectorCmd *cmd) = 0; + virtual bool RemoveCommand(int index) = 0; + virtual DirectorCmd *GetLastCommand() = 0; + virtual IObjectContainer *GetCommands() = 0; + virtual void SetWorldTime(double time, bool relative) = 0; + virtual void SetTimeScale(float scale) = 0; + virtual void SetPaused(bool state) = 0; + virtual void SetEditMode(bool state) = 0; + virtual void SetMasterMode(bool state) = 0; + virtual bool IsPaused() = 0; + virtual bool IsLoading() = 0; + virtual bool IsActive() = 0; + virtual bool IsEditMode() = 0; + virtual bool IsMasterMode() = 0; + virtual void RemoveFrames(double starttime, double endtime) = 0; + virtual void ExecuteDirectorCmd(DirectorCmd *cmd) = 0; + virtual double GetWorldTime() = 0; + virtual double GetStartTime() = 0; + virtual double GetEndTime() = 0; + virtual float GetTimeScale() = 0; + virtual IWorld *GetWorld() = 0; + virtual char *GetFileName() = 0; + virtual bool SaveGame(char *filename) = 0; + virtual bool LoadGame(char *filename) = 0; + virtual void Stop() = 0; + virtual void ForceHLTV(bool state) = 0; + virtual void GetDemoViewInfo(ref_params_t *rp, float *view, int *viewmodel) = 0; + virtual int ReadDemoMessage(unsigned char *buffer, int size) = 0; + virtual void ReadNetchanState(int *incoming_sequence, int *incoming_acknowledged, int *incoming_reliable_acknowledged, int *incoming_reliable_sequence, int *outgoing_sequence, int *reliable_sequence, int *last_reliable_sequence) = 0; +}; + +#define DEMOPLAYER_INTERFACE_VERSION "demoplayer001" diff --git a/rehlds/common/IEngineWrapper.h b/rehlds/common/IEngineWrapper.h new file mode 100644 index 0000000..7162d5b --- /dev/null +++ b/rehlds/common/IEngineWrapper.h @@ -0,0 +1,71 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "event_args.h" +#include "cdll_int.h" + +class IBaseSystem; +class IEngineWrapper { +public: + virtual ~IEngineWrapper() {} + + virtual bool Init(IBaseSystem *system, int serial, char *name) = 0; + virtual void RunFrame(double time) = 0; + virtual void ReceiveSignal(ISystemModule *module, unsigned int signal, void *data) = 0; + virtual void ExecuteCommand(int commandID, char *commandLine) = 0; + virtual void RegisterListener(ISystemModule *module) = 0; + virtual void RemoveListener(ISystemModule *module) = 0; + + virtual IBaseSystem *GetSystem() = 0; + + virtual int GetSerial() = 0; + virtual char *GetStatusLine() = 0; + virtual char *GetType() = 0; + virtual char *GetName() = 0; + + virtual int GetState() = 0; + virtual int GetVersion() = 0; + virtual void ShutDown() = 0; + + virtual bool GetViewOrigin(float *origin) = 0; + virtual bool GetViewAngles(float *angles) = 0; + virtual int GetTraceEntity() = 0; + virtual float GetCvarFloat(char *szName) = 0; + virtual char *GetCvarString(char *szName) = 0; + virtual void SetCvar(char *szName, char *szValue) = 0; + virtual void Cbuf_AddText(char *text) = 0; + virtual void DemoUpdateClientData(client_data_t *cdat) = 0; + virtual void CL_QueueEvent(int flags, int index, float delay, event_args_t *pargs) = 0; + virtual void HudWeaponAnim(int iAnim, int body) = 0; + virtual void CL_DemoPlaySound(int channel, char* sample, float attenuation, float volume, int flags, int pitch) = 0; + virtual void ClientDLL_ReadDemoBuffer(int size, unsigned char *buffer) = 0; +}; + +#define ENGINEWRAPPER_INTERFACE_VERSION "enginewrapper001" diff --git a/rehlds/common/IObjectContainer.h b/rehlds/common/IObjectContainer.h index de08bbf..333e9d0 100644 --- a/rehlds/common/IObjectContainer.h +++ b/rehlds/common/IObjectContainer.h @@ -41,7 +41,7 @@ public: virtual void *GetFirst() = 0; virtual void *GetNext() = 0; - virtual int CountElements() = 0;; + virtual int CountElements() = 0; virtual bool Contains(void *object) = 0; virtual bool IsEmpty() = 0; }; diff --git a/rehlds/common/IVGuiModule.h b/rehlds/common/IVGuiModule.h index 4af7658..e6864c8 100644 --- a/rehlds/common/IVGuiModule.h +++ b/rehlds/common/IVGuiModule.h @@ -41,7 +41,7 @@ public: // called after all the modules have been initialized // modules should use this time to link to all the other module interfaces - virtual bool PostInitialize(CreateInterfaceFn *modules, int factoryCount) = 0; + virtual bool PostInitialize(CreateInterfaceFn *modules = nullptr, int factoryCount = 0) = 0; // called when the module is selected from the menu or otherwise activated virtual bool Activate() = 0; diff --git a/rehlds/common/entity_state.h b/rehlds/common/entity_state.h index 4bd381e..91fe2ed 100644 --- a/rehlds/common/entity_state.h +++ b/rehlds/common/entity_state.h @@ -23,8 +23,9 @@ // For entityType below -#define ENTITY_NORMAL (1<<0) -#define ENTITY_BEAM (1<<1) +#define ENTITY_NORMAL (1<<0) +#define ENTITY_BEAM (1<<1) +#define ENTITY_UNINITIALIZED (1<<30) // Entity state is used for the baseline and for delta compression of a packet of // entities that is sent to a client. diff --git a/rehlds/common/hltv.h b/rehlds/common/hltv.h index 556ef32..635c8c3 100644 --- a/rehlds/common/hltv.h +++ b/rehlds/common/hltv.h @@ -42,7 +42,6 @@ #define DRC_CMD_LAST 15 - // DRC_CMD_EVENT event flags #define DRC_FLAG_PRIO_MASK 0x0F // priorities between 0 and 15 (15 most important) #define DRC_FLAG_SIDE (1<<4) // @@ -53,7 +52,6 @@ #define DRC_FLAG_FINAL (1<<9) // is a final scene #define DRC_FLAG_NO_RANDOM (1<<10) // don't randomize event data - // DRC_CMD_WAYPOINT flags #define DRC_FLAG_STARTPATH 1 // end with speed 0.0 #define DRC_FLAG_SLOWSTART 2 // start with speed 0.0 diff --git a/rehlds/dedicated/README.md b/rehlds/dedicated/README.md new file mode 100644 index 0000000..63a472a --- /dev/null +++ b/rehlds/dedicated/README.md @@ -0,0 +1,16 @@ +## HLDS Launcher HLDS Launcher + +## What is this? +Dedicated Server Launcher for Goldsrc based games + +### Building +On Windows: +
gradlew --max-workers=1 clean rehlds/dedicated:build
+ +On Linux (ICC): +
./gradlew --max-workers=1 clean rehlds/dedicated:build
+ +On Linux (GCC): +
./gradlew --max-workers=1 -PuseGcc clean rehlds/dedicated:build
+ +Compiled binaries will be placed in the rehlds/dedicated/build/binaries/ directory diff --git a/rehlds/dedicated/src/dedicated_exports.cpp b/rehlds/dedicated/src/dedicated_exports.cpp index 956fb15..6db5576 100644 --- a/rehlds/dedicated/src/dedicated_exports.cpp +++ b/rehlds/dedicated/src/dedicated_exports.cpp @@ -1,6 +1,6 @@ #include "precompiled.h" -class CDedicatedExports: IDedicatedExports { +class CDedicatedExports: public IDedicatedExports { public: void Sys_Printf(char *text); }; diff --git a/rehlds/engine/mathlib.cpp b/rehlds/engine/mathlib.cpp index fe2c7db..7f44e63 100644 --- a/rehlds/engine/mathlib.cpp +++ b/rehlds/engine/mathlib.cpp @@ -126,7 +126,7 @@ int BoxOnPlaneSide(vec_t *emins, vec_t *emaxs, mplane_t *p) int sides = 0; __m128 emin = _mm_loadu_ps(emins); - __m128 emax = _mm_loadu_ps(emaxs);; + __m128 emax = _mm_loadu_ps(emaxs); avec4_t d1, d2; // general case diff --git a/rehlds/engine/textures.cpp b/rehlds/engine/textures.cpp index 2f160a1..3c8708d 100644 --- a/rehlds/engine/textures.cpp +++ b/rehlds/engine/textures.cpp @@ -139,7 +139,7 @@ qboolean TEX_InitFromWad(char *path) SafeRead(texfile, &lumpinfo[nTexLumps], sizeof(lumpinfo_t)); CleanupName(lumpinfo[nTexLumps].lump.name, lumpinfo[nTexLumps].lump.name); lumpinfo[nTexLumps].lump.filepos = LittleLong(lumpinfo[nTexLumps].lump.filepos); - lumpinfo[nTexLumps].lump.disksize = LittleLong(lumpinfo[nTexLumps].lump.disksize);; + lumpinfo[nTexLumps].lump.disksize = LittleLong(lumpinfo[nTexLumps].lump.disksize); lumpinfo[nTexLumps].iTexFile = nTexFiles - 1; } diff --git a/rehlds/engine/unicode_strtools.h b/rehlds/engine/unicode_strtools.h index 9b319c5..7a14e08 100644 --- a/rehlds/engine/unicode_strtools.h +++ b/rehlds/engine/unicode_strtools.h @@ -57,6 +57,6 @@ int Q_UChar32ToUTF8(uchar32 uVal, char * pUTF8Out); int Q_UChar32ToUTF8Len(uchar32 uVal); qboolean Q_UnicodeValidate(const char *pUTF8); char *Q_UnicodeAdvance(char *pUTF8, int nChars); -qboolean Q_StripUnprintableAndSpace(char *pch);; +qboolean Q_StripUnprintableAndSpace(char *pch); qboolean V_UTF8ToUChar32(const char *pUTF8_, uchar32 *uValueOut); int Q_UnicodeRepair(char *pUTF8); diff --git a/rehlds/engine/zone.cpp b/rehlds/engine/zone.cpp index 2f67b78..da6f3ab 100644 --- a/rehlds/engine/zone.cpp +++ b/rehlds/engine/zone.cpp @@ -227,7 +227,7 @@ NOXREF void Z_Print(memzone_t *zone) memblock_t *block; Con_Printf("zone size: %i location: %p\n", mainzone->size, mainzone); - for (block = zone->blocklist.next;; block = block->next) + for (block = zone->blocklist.next; ; block = block->next) { Con_Printf("block:%p size:%7i tag:%3i\n", block, block->size, block->tag); @@ -257,7 +257,7 @@ void Z_CheckHeap(void) { memblock_t *block; - for (block = mainzone->blocklist.next;; block = block->next) + for (block = mainzone->blocklist.next; ; block = block->next) { if (block->next == &mainzone->blocklist) { diff --git a/rehlds/game_shared/bitvec.h b/rehlds/game_shared/bitvec.h new file mode 100644 index 0000000..a8e352f --- /dev/null +++ b/rehlds/game_shared/bitvec.h @@ -0,0 +1,164 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +class CBitVecAccessor +{ +public: + CBitVecAccessor(uint32 *pDWords, int iBit); + + void operator=(int val); + operator uint32(); + +private: + uint32 *m_pDWords; + int m_iBit; +}; + +// CBitVec allows you to store a list of bits and do operations on them like they were +// an atomic type +template +class CBitVec +{ +public: + CBitVec(); + + // Set all values to the specified value (0 or 1..) + void Init(int val = 0); + + // Access the bits like an array. + CBitVecAccessor operator[](int i); + + // Operations on other bit vectors. + CBitVec &operator=(CBitVec const &other); + bool operator==(CBitVec const &other); + bool operator!=(CBitVec const &other); + + // Get underlying dword representations of the bits. + int GetNumDWords() { return NUM_DWORDS; } + uint32 GetDWord(int i); + void SetDWord(int i, uint32 val); + int GetNumBits(); + +private: + + enum + { + NUM_DWORDS = NUM_BITS / 32 + !!(NUM_BITS & 31) + }; + + unsigned int m_DWords[ NUM_DWORDS ]; +}; + +inline CBitVecAccessor::CBitVecAccessor(uint32 *pDWords, int iBit) +{ + m_pDWords = pDWords; + m_iBit = iBit; +} + +inline void CBitVecAccessor::operator=(int val) +{ + if (val) + m_pDWords[m_iBit >> 5] |= (1 << (m_iBit & 31)); + else + m_pDWords[m_iBit >> 5] &= ~(uint32)(1 << (m_iBit & 31)); +} + +inline CBitVecAccessor::operator uint32() +{ + return m_pDWords[m_iBit >> 5] & (1 << (m_iBit & 31)); +} + +template +inline int CBitVec::GetNumBits() +{ + return NUM_BITS; +} + +template +inline CBitVec::CBitVec() +{ + for (int i = 0; i < NUM_DWORDS; ++i) + m_DWords[i] = 0; +} + +template +inline void CBitVec::Init(int val) +{ + for (int i = 0; i < GetNumBits(); ++i) + { + (*this)[i] = val; + } +} + +template +inline CBitVec &CBitVec::operator=(CBitVec const &other) +{ + Q_memcpy(m_DWords, other.m_DWords, sizeof(m_DWords)); + return *this; +} + +template +inline CBitVecAccessor CBitVec::operator[](int i) +{ + assert(i >= 0 && i < GetNumBits()); + return CBitVecAccessor(m_DWords, i); +} + +template +inline bool CBitVec::operator==(CBitVec const &other) +{ + for (int i = 0; i < NUM_DWORDS; ++i) + { + if (m_DWords[i] != other.m_DWords[i]) + return false; + } + + return true; +} + +template +inline bool CBitVec::operator!=(CBitVec const &other) +{ + return !(*this == other); +} + +template +inline uint32 CBitVec::GetDWord(int i) +{ + assert(i >= 0 && i < NUM_DWORDS); + return m_DWords[i]; +} + +template +inline void CBitVec::SetDWord(int i, uint32 val) +{ + assert(i >= 0 && i < NUM_DWORDS); + m_DWords[i] = val; +} diff --git a/rehlds/game_shared/counter.h b/rehlds/game_shared/counter.h new file mode 100644 index 0000000..aedaf43 --- /dev/null +++ b/rehlds/game_shared/counter.h @@ -0,0 +1,187 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#ifdef _WIN32 + #include + #include + #include +#else + #include + #include + #include + #include + #ifdef OSX + #include + #else + #include + #endif + #include +#endif + +#include +#include +#include +#include +#include + +class CCounter +{ +public: + CCounter(); + + bool Init(); + double GetCurTime(); + +private: + int m_iLowShift; + double m_flPerfCounterFreq; + double m_flCurrentTime; + double m_flLastCurrentTime; +}; + +inline CCounter::CCounter() : + m_iLowShift(0), + m_flPerfCounterFreq(0), + m_flCurrentTime(0), + m_flLastCurrentTime(0) +{ + Init(); +} + +inline bool CCounter::Init() +{ +#ifdef _WIN32 + + LARGE_INTEGER performanceFreq; + if (!QueryPerformanceFrequency(&performanceFreq)) + return false; + + // get 32 out of the 64 time bits such that we have around + // 1 microsecond resolution + unsigned int lowpart, highpart; + lowpart = (unsigned int)performanceFreq.LowPart; + highpart = (unsigned int)performanceFreq.HighPart; + m_iLowShift = 0; + + while (highpart || (lowpart > 2000000.0)) + { + m_iLowShift++; + lowpart >>= 1; + lowpart |= (highpart & 1) << 31; + highpart >>= 1; + } + + m_flPerfCounterFreq = 1.0 / (double)lowpart; + +#endif // _WIN32 + + return true; +} + +inline double CCounter::GetCurTime() +{ +#ifdef _WIN32 + + static int sametimecount; + static unsigned int oldtime; + static int first = 1; + LARGE_INTEGER PerformanceCount; + unsigned int temp, t2; + double time; + + QueryPerformanceCounter(&PerformanceCount); + if (m_iLowShift == 0) + { + temp = (unsigned int)PerformanceCount.LowPart; + } + else + { + temp = ((unsigned int)PerformanceCount.LowPart >> m_iLowShift) | + ((unsigned int)PerformanceCount.HighPart << (32 - m_iLowShift)); + } + + if (first) + { + oldtime = temp; + first = 0; + } + else + { + // check for turnover or backward time + if ((temp <= oldtime) && ((oldtime - temp) < 0x10000000)) + { + // so we can't get stuck + oldtime = temp; + } + else + { + t2 = temp - oldtime; + + time = (double)t2 * m_flPerfCounterFreq; + oldtime = temp; + + m_flCurrentTime += time; + + if (m_flCurrentTime == m_flLastCurrentTime) + { + if (++sametimecount > 100000) + { + m_flCurrentTime += 1.0; + sametimecount = 0; + } + } + else + { + sametimecount = 0; + } + + m_flLastCurrentTime = m_flCurrentTime; + } + } + + return m_flCurrentTime; + +#else // _WIN32 + + struct timeval tp; + static int secbase = 0; + + gettimeofday(&tp, NULL); + + if (!secbase) + { + secbase = tp.tv_sec; + return (tp.tv_usec / 1000000.0); + } + + return ((tp.tv_sec - secbase) + tp.tv_usec / 1000000.0); + +#endif // _WIN32 +} diff --git a/rehlds/game_shared/voice_common.h b/rehlds/game_shared/voice_common.h new file mode 100644 index 0000000..32e1ed9 --- /dev/null +++ b/rehlds/game_shared/voice_common.h @@ -0,0 +1,43 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#ifndef VOICE_COMMON_H +#define VOICE_COMMON_H +#ifdef _WIN32 +#pragma once +#endif + +#include "bitvec.h" + +// TODO: this should just be set to MAX_CLIENTS +#define VOICE_MAX_PLAYERS 32 +#define VOICE_MAX_PLAYERS_DW ((VOICE_MAX_PLAYERS / 32) + !!(VOICE_MAX_PLAYERS & 31)) + +typedef CBitVec< VOICE_MAX_PLAYERS > CPlayerBitVec; + +#endif // VOICE_COMMON_H diff --git a/rehlds/hookers/HLTV/Core/DeltaEx.cpp b/rehlds/hookers/HLTV/Core/DeltaEx.cpp new file mode 100644 index 0000000..99c5784 --- /dev/null +++ b/rehlds/hookers/HLTV/Core/DeltaEx.cpp @@ -0,0 +1,1537 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +#ifdef HOOK_HLTV + +DeltaWrapper World::m_Delta; + +delta_definition_list_t *g_defs; +delta_encoder_t *g_encoders; +delta_registry_t *g_deltaregistry; + +double g_delta_Time; +bool g_large_Time_Buffers; + +delta_t *g_pentitydelta; +delta_t *g_pplayerdelta; +delta_t *g_pcustomentitydelta; +delta_t *g_pclientdelta; +delta_t *g_pweapondelta; +delta_t *g_peventdelta; + +#define DELTA_D_DEF(member) #member, offsetof(delta_description_s, member) +#define DELTA_DEF(structname, member) { #member, offsetof(structname, member) } + +delta_definition_t g_DeltaDataDefinition[] = +{ + DELTA_DEF(delta_description_s, fieldType), + DELTA_DEF(delta_description_s, fieldName), + DELTA_DEF(delta_description_s, fieldOffset), + DELTA_DEF(delta_description_s, fieldSize), + DELTA_DEF(delta_description_s, significant_bits), + DELTA_DEF(delta_description_s, premultiply), + DELTA_DEF(delta_description_s, postmultiply), + DELTA_DEF(delta_description_s, flags) +}; + +delta_description_t g_MetaDescription[] = +{ + { DT_INTEGER, DELTA_D_DEF(fieldType), 1, 32, 1.0, 1.0, 0, 0, 0 }, + { DT_STRING, DELTA_D_DEF(fieldName), 1, 1, 1.0, 1.0, 0, 0, 0 }, + { DT_INTEGER, DELTA_D_DEF(fieldOffset), 1, 16, 1.0, 1.0, 0, 0, 0 }, + { DT_INTEGER, DELTA_D_DEF(fieldSize), 1, 8, 1.0, 1.0, 0, 0, 0 }, + { DT_INTEGER, DELTA_D_DEF(significant_bits), 1, 8, 1.0, 1.0, 0, 0, 0 }, + { DT_FLOAT, DELTA_D_DEF(premultiply), 1, 32, 4000.0, 1.0, 0, 0, 0 }, + { DT_FLOAT, DELTA_D_DEF(postmultiply), 1, 32, 4000.0, 1.0, 0, 0, 0 } +}; + +namespace Delta { + delta_t g_MetaDelta[] = + { + { 0, ARRAYSIZE(g_MetaDescription), "", NULL, g_MetaDescription }, + }; + +}; // namespace Delta + +delta_definition_t g_EventDataDefinition[] = +{ + DELTA_DEF(event_args_s, entindex), + DELTA_DEF(event_args_s, origin[0]), + DELTA_DEF(event_args_s, origin[1]), + DELTA_DEF(event_args_s, origin[2]), + DELTA_DEF(event_args_s, angles[0]), + DELTA_DEF(event_args_s, angles[1]), + DELTA_DEF(event_args_s, angles[2]), + DELTA_DEF(event_args_s, fparam1), + DELTA_DEF(event_args_s, fparam2), + DELTA_DEF(event_args_s, iparam1), + DELTA_DEF(event_args_s, iparam2), + DELTA_DEF(event_args_s, bparam1), + DELTA_DEF(event_args_s, bparam2), + DELTA_DEF(event_args_s, ducking) +}; + +delta_definition_t g_EntityDataDefinition[] = +{ + DELTA_DEF(entity_state_s, startpos[0]), + DELTA_DEF(entity_state_s, startpos[1]), + DELTA_DEF(entity_state_s, startpos[2]), + DELTA_DEF(entity_state_s, endpos[0]), + DELTA_DEF(entity_state_s, endpos[1]), + DELTA_DEF(entity_state_s, endpos[2]), + DELTA_DEF(entity_state_s, impacttime), + DELTA_DEF(entity_state_s, starttime), + DELTA_DEF(entity_state_s, origin[0]), + DELTA_DEF(entity_state_s, origin[1]), + DELTA_DEF(entity_state_s, origin[2]), + DELTA_DEF(entity_state_s, angles[0]), + DELTA_DEF(entity_state_s, angles[1]), + DELTA_DEF(entity_state_s, angles[2]), + DELTA_DEF(entity_state_s, modelindex), + DELTA_DEF(entity_state_s, frame), + DELTA_DEF(entity_state_s, movetype), + DELTA_DEF(entity_state_s, colormap), + DELTA_DEF(entity_state_s, skin), + DELTA_DEF(entity_state_s, solid), + DELTA_DEF(entity_state_s, scale), + DELTA_DEF(entity_state_s, effects), + DELTA_DEF(entity_state_s, sequence), + DELTA_DEF(entity_state_s, animtime), + DELTA_DEF(entity_state_s, framerate), + DELTA_DEF(entity_state_s, controller[0]), + DELTA_DEF(entity_state_s, controller[1]), + DELTA_DEF(entity_state_s, controller[2]), + DELTA_DEF(entity_state_s, controller[3]), + DELTA_DEF(entity_state_s, blending[0]), + DELTA_DEF(entity_state_s, blending[1]), + DELTA_DEF(entity_state_s, body), + DELTA_DEF(entity_state_s, owner), + DELTA_DEF(entity_state_s, rendermode), + DELTA_DEF(entity_state_s, renderamt), + DELTA_DEF(entity_state_s, renderfx), + DELTA_DEF(entity_state_s, rendercolor.r), + DELTA_DEF(entity_state_s, rendercolor.g), + DELTA_DEF(entity_state_s, rendercolor.b), + DELTA_DEF(entity_state_s, weaponmodel), + DELTA_DEF(entity_state_s, gaitsequence), + DELTA_DEF(entity_state_s, mins[0]), + DELTA_DEF(entity_state_s, mins[1]), + DELTA_DEF(entity_state_s, mins[2]), + DELTA_DEF(entity_state_s, maxs[0]), + DELTA_DEF(entity_state_s, maxs[1]), + DELTA_DEF(entity_state_s, maxs[2]), + DELTA_DEF(entity_state_s, aiment), + DELTA_DEF(entity_state_s, basevelocity[0]), + DELTA_DEF(entity_state_s, basevelocity[1]), + DELTA_DEF(entity_state_s, basevelocity[2]), + DELTA_DEF(entity_state_s, friction), + DELTA_DEF(entity_state_s, gravity), + DELTA_DEF(entity_state_s, spectator), + DELTA_DEF(entity_state_s, velocity[0]), + DELTA_DEF(entity_state_s, velocity[1]), + DELTA_DEF(entity_state_s, velocity[2]), + DELTA_DEF(entity_state_s, team), + DELTA_DEF(entity_state_s, playerclass), + DELTA_DEF(entity_state_s, health), + DELTA_DEF(entity_state_s, usehull), + DELTA_DEF(entity_state_s, oldbuttons), + DELTA_DEF(entity_state_s, onground), + DELTA_DEF(entity_state_s, iStepLeft), + DELTA_DEF(entity_state_s, flFallVelocity), + DELTA_DEF(entity_state_s, weaponanim), + DELTA_DEF(entity_state_s, eflags), + DELTA_DEF(entity_state_s, iuser1), + DELTA_DEF(entity_state_s, iuser2), + DELTA_DEF(entity_state_s, iuser3), + DELTA_DEF(entity_state_s, iuser4), + DELTA_DEF(entity_state_s, fuser1), + DELTA_DEF(entity_state_s, fuser2), + DELTA_DEF(entity_state_s, fuser3), + DELTA_DEF(entity_state_s, fuser4), + DELTA_DEF(entity_state_s, vuser1[0]), + DELTA_DEF(entity_state_s, vuser1[1]), + DELTA_DEF(entity_state_s, vuser1[2]), + DELTA_DEF(entity_state_s, vuser2[0]), + DELTA_DEF(entity_state_s, vuser2[1]), + DELTA_DEF(entity_state_s, vuser2[2]), + DELTA_DEF(entity_state_s, vuser3[0]), + DELTA_DEF(entity_state_s, vuser3[1]), + DELTA_DEF(entity_state_s, vuser3[2]), + DELTA_DEF(entity_state_s, vuser4[0]), + DELTA_DEF(entity_state_s, vuser4[1]), + DELTA_DEF(entity_state_s, vuser4[2]) +}; + +delta_definition_t g_UsercmdDataDefinition[] = +{ + DELTA_DEF(usercmd_s, lerp_msec), + DELTA_DEF(usercmd_s, msec), + DELTA_DEF(usercmd_s, lightlevel), + DELTA_DEF(usercmd_s, viewangles[0]), + DELTA_DEF(usercmd_s, viewangles[1]), + DELTA_DEF(usercmd_s, viewangles[2]), + DELTA_DEF(usercmd_s, buttons), + DELTA_DEF(usercmd_s, forwardmove), + DELTA_DEF(usercmd_s, sidemove), + DELTA_DEF(usercmd_s, upmove), + DELTA_DEF(usercmd_s, impulse), + DELTA_DEF(usercmd_s, weaponselect), + DELTA_DEF(usercmd_s, impact_index), + DELTA_DEF(usercmd_s, impact_position[0]), + DELTA_DEF(usercmd_s, impact_position[1]), + DELTA_DEF(usercmd_s, impact_position[2]) +}; + +delta_definition_t g_WeaponDataDefinition[] = +{ + DELTA_DEF(weapon_data_s, m_iId), + DELTA_DEF(weapon_data_s, m_iClip), + DELTA_DEF(weapon_data_s, m_flNextPrimaryAttack), + DELTA_DEF(weapon_data_s, m_flNextSecondaryAttack), + DELTA_DEF(weapon_data_s, m_flTimeWeaponIdle), + DELTA_DEF(weapon_data_s, m_fInReload), + DELTA_DEF(weapon_data_s, m_fInSpecialReload), + DELTA_DEF(weapon_data_s, m_flNextReload), + DELTA_DEF(weapon_data_s, m_flPumpTime), + DELTA_DEF(weapon_data_s, m_fReloadTime), + DELTA_DEF(weapon_data_s, m_fAimedDamage), + DELTA_DEF(weapon_data_s, m_fNextAimBonus), + DELTA_DEF(weapon_data_s, m_fInZoom), + DELTA_DEF(weapon_data_s, m_iWeaponState), + DELTA_DEF(weapon_data_s, iuser1), + DELTA_DEF(weapon_data_s, iuser2), + DELTA_DEF(weapon_data_s, iuser3), + DELTA_DEF(weapon_data_s, iuser4), + DELTA_DEF(weapon_data_s, fuser1), + DELTA_DEF(weapon_data_s, fuser2), + DELTA_DEF(weapon_data_s, fuser3), + DELTA_DEF(weapon_data_s, fuser4) +}; + +delta_definition_t g_ClientDataDefinition[] = +{ + DELTA_DEF(clientdata_s, origin[0]), + DELTA_DEF(clientdata_s, origin[1]), + DELTA_DEF(clientdata_s, origin[2]), + DELTA_DEF(clientdata_s, velocity[0]), + DELTA_DEF(clientdata_s, velocity[1]), + DELTA_DEF(clientdata_s, velocity[2]), + DELTA_DEF(clientdata_s, viewmodel), + DELTA_DEF(clientdata_s, punchangle[0]), + DELTA_DEF(clientdata_s, punchangle[1]), + DELTA_DEF(clientdata_s, punchangle[2]), + DELTA_DEF(clientdata_s, flags), + DELTA_DEF(clientdata_s, waterlevel), + DELTA_DEF(clientdata_s, watertype), + DELTA_DEF(clientdata_s, view_ofs[0]), + DELTA_DEF(clientdata_s, view_ofs[1]), + DELTA_DEF(clientdata_s, view_ofs[2]), + DELTA_DEF(clientdata_s, health), + DELTA_DEF(clientdata_s, bInDuck), + DELTA_DEF(clientdata_s, weapons), + DELTA_DEF(clientdata_s, flTimeStepSound), + DELTA_DEF(clientdata_s, flDuckTime), + DELTA_DEF(clientdata_s, flSwimTime), + DELTA_DEF(clientdata_s, waterjumptime), + DELTA_DEF(clientdata_s, maxspeed), + DELTA_DEF(clientdata_s, m_iId), + DELTA_DEF(clientdata_s, ammo_nails), + DELTA_DEF(clientdata_s, ammo_shells), + DELTA_DEF(clientdata_s, ammo_cells), + DELTA_DEF(clientdata_s, ammo_rockets), + DELTA_DEF(clientdata_s, m_flNextAttack), + DELTA_DEF(clientdata_s, physinfo), + DELTA_DEF(clientdata_s, fov), + DELTA_DEF(clientdata_s, weaponanim), + DELTA_DEF(clientdata_s, tfstate), + DELTA_DEF(clientdata_s, pushmsec), + DELTA_DEF(clientdata_s, deadflag), + DELTA_DEF(clientdata_s, iuser1), + DELTA_DEF(clientdata_s, iuser2), + DELTA_DEF(clientdata_s, iuser3), + DELTA_DEF(clientdata_s, iuser4), + DELTA_DEF(clientdata_s, fuser1), + DELTA_DEF(clientdata_s, fuser2), + DELTA_DEF(clientdata_s, fuser3), + DELTA_DEF(clientdata_s, fuser4), + DELTA_DEF(clientdata_s, vuser1[0]), + DELTA_DEF(clientdata_s, vuser1[1]), + DELTA_DEF(clientdata_s, vuser1[2]), + DELTA_DEF(clientdata_s, vuser2[0]), + DELTA_DEF(clientdata_s, vuser2[1]), + DELTA_DEF(clientdata_s, vuser2[2]), + DELTA_DEF(clientdata_s, vuser3[0]), + DELTA_DEF(clientdata_s, vuser3[1]), + DELTA_DEF(clientdata_s, vuser3[2]), + DELTA_DEF(clientdata_s, vuser4[0]), + DELTA_DEF(clientdata_s, vuser4[1]), + DELTA_DEF(clientdata_s, vuser4[2]) +}; + +delta_description_t *DELTA_FindField(delta_t *pFields, const char *pszField) +{ + for (int i = 0; i < pFields->fieldCount; i++) + { + if (_stricmp(pFields->pdd[i].fieldName, pszField) == 0) { + return &pFields->pdd[i]; + } + } + + return nullptr; +} + +int DELTA_FindFieldIndex(delta_t *pFields, const char *fieldname) +{ + for (int i = 0; i < pFields->fieldCount; i++) + { + if (_stricmp(pFields->pdd[i].fieldName, fieldname) == 0) { + return i; + } + } + + return -1; +} + +void DELTA_SetField(delta_t *pFields, const char *fieldname) +{ + delta_description_t *pTest = DELTA_FindField(pFields, fieldname); + if (pTest) { + pTest->flags |= FDT_MARK; + } +} + +void DELTA_UnsetField(delta_t *pFields, const char *fieldname) +{ + delta_description_t *pTest = DELTA_FindField(pFields, fieldname); + if (pTest) { + pTest->flags &= ~FDT_MARK; + } +} + +void DELTA_SetFieldByIndex(delta_t *pFields, int fieldNumber) +{ + pFields->pdd[fieldNumber].flags |= FDT_MARK; +} + +void DELTA_UnsetFieldByIndex(delta_t *pFields, int fieldNumber) +{ + pFields->pdd[fieldNumber].flags &= ~FDT_MARK; +} + +void DELTA_ClearFlags(delta_t *pFields) +{ + for (int i = 0; i < pFields->fieldCount; i++) { + pFields->pdd[i].flags = 0; + } +} + +int DELTA_CountSendFields(delta_t *pFields) +{ + int i, c; + for (i = 0, c = 0; i < pFields->fieldCount; i++) + { + auto pitem = &pFields->pdd[i]; + if (pitem->flags & FDT_MARK) { + pitem->stats.sendcount++; + c++; + } + } + + return c; +} + +void DELTA_MarkSendFields(unsigned char *from, unsigned char *to, delta_t *pFields) +{ + int i; + char *st1, *st2; + delta_description_t *pTest; + int fieldType; + int fieldCount = pFields->fieldCount; + + for (i = 0, pTest = pFields->pdd; i < fieldCount; i++, pTest++) + { + fieldType = pTest->fieldType & ~DT_SIGNED; + switch (fieldType) + { + case DT_BYTE: + if (from[pTest->fieldOffset] != to[pTest->fieldOffset]) + pTest->flags |= FDT_MARK; + break; + case DT_SHORT: + if (*(uint16 *)&from[pTest->fieldOffset] != *(uint16 *)&to[pTest->fieldOffset]) + pTest->flags |= FDT_MARK; + break; + case DT_FLOAT: + case DT_INTEGER: + case DT_ANGLE: + if (*(uint32 *)&from[pTest->fieldOffset] != *(uint32 *)&to[pTest->fieldOffset]) + pTest->flags |= FDT_MARK; + break; + case DT_TIMEWINDOW_8: + case DT_TIMEWINDOW_BIG: + if (*(uint32 *)&from[pTest->fieldOffset] != *(uint32 *)&to[pTest->fieldOffset]) + pTest->flags |= FDT_MARK; + break; + case DT_STRING: + st1 = (char *)&from[pTest->fieldOffset]; + st2 = (char *)&to[pTest->fieldOffset]; + + // Not sure why it is case insensitive, but it looks so + if (!(!*st1 && !*st2 || *st1 && *st2 && !_stricmp(st1, st2))) { + pTest->flags |= FDT_MARK; + } + break; + default: + break; + } + } + + if (pFields->conditionalencode) { + pFields->conditionalencode(pFields, from, to); + } +} + +void DELTA_SetSendFlagBits(delta_t *pFields, int *bits, int *bytecount) +{ + int i; + int lastbit = -1; + int fieldCount = pFields->fieldCount; + + memset(bits, 0, 8); + + for (i = fieldCount - 1; i >= 0; i--) + { + if (pFields->pdd[i].flags & FDT_MARK) + { + if (lastbit == -1) { + lastbit = i; + } + + bits[i > 31 ? 1 : 0] |= 1 << (i & 0x1f); + } + } + + // fix for bad bytecount when no fields are marked + if (lastbit == -1) { + *bytecount = 0; + return; + } + + *bytecount = (lastbit >> 3) + 1; +} + +void DELTA_WriteMarkedFields(BitBuffer *stream, unsigned char *from, unsigned char *to, delta_t *pFields) +{ + int i; + delta_description_t *pTest; + int fieldSign; + int fieldType; + + float f2; + int fieldCount = pFields->fieldCount; + + for (i = 0, pTest = pFields->pdd; i < fieldCount; i++, pTest++) + { + if (!(pTest->flags & FDT_MARK)) + continue; + + fieldSign = pTest->fieldType & DT_SIGNED; + fieldType = pTest->fieldType & ~DT_SIGNED; + switch (fieldType) + { + case DT_BYTE: + if (fieldSign) + { + int8 si8 = *(int8 *)&to[pTest->fieldOffset]; + si8 = (int8)((double)si8 * pTest->premultiply); + stream->WriteSBits(si8, pTest->significant_bits); + } + else + { + uint8 i8 = *(uint8 *)&to[pTest->fieldOffset]; + i8 = (uint8)((double)i8 * pTest->premultiply); + stream->WriteBits(i8, pTest->significant_bits); + } + break; + case DT_SHORT: + if (fieldSign) + { + int16 si16 = *(int16 *)&to[pTest->fieldOffset]; + si16 = (int16)((double)si16 * pTest->premultiply); + stream->WriteSBits(si16, pTest->significant_bits); + } + else + { + uint16 i16 = *(uint16 *)&to[pTest->fieldOffset]; + i16 = (uint16)((double)i16 * pTest->premultiply); + stream->WriteBits(i16, pTest->significant_bits); + } + break; + case DT_FLOAT: + { + double val = (double)(*(float *)&to[pTest->fieldOffset]) * pTest->premultiply; + if (fieldSign) + { + stream->WriteSBits((int32)val, pTest->significant_bits); + } + else + { + stream->WriteBits((uint32)val, pTest->significant_bits); + } + break; + } + case DT_INTEGER: + { + if (fieldSign) + { + int32 signedInt = *(int32 *)&to[pTest->fieldOffset]; + if (pTest->premultiply < 0.9999 || pTest->premultiply > 1.0001) { + signedInt = (int32)((double)signedInt * pTest->premultiply); + } + + stream->WriteSBits(signedInt, pTest->significant_bits); + } + else + { + uint32 unsignedInt = *(uint32 *)&to[pTest->fieldOffset]; + if (pTest->premultiply < 0.9999 || pTest->premultiply > 1.0001) { + unsignedInt = (uint32)((double)unsignedInt * pTest->premultiply); + } + + stream->WriteBits(unsignedInt, pTest->significant_bits); + } + break; + } + case DT_ANGLE: + f2 = *(float *)&to[pTest->fieldOffset]; + stream->WriteBitAngle(f2, pTest->significant_bits); + break; + case DT_TIMEWINDOW_8: + { + f2 = *(float *)&to[pTest->fieldOffset]; + if (g_large_Time_Buffers) { + stream->WriteFloat(f2); + } else { + int32 twVal = (int)(g_delta_Time * 100.0) - (int)(f2 * 100.0); + stream->WriteSBits(twVal, 8); + } + break; + } + case DT_TIMEWINDOW_BIG: + { + f2 = *(float *)&to[pTest->fieldOffset]; + if (g_large_Time_Buffers) { + stream->WriteFloat(f2); + } else { + int32 twVal = (int)(g_delta_Time * pTest->premultiply) - (int)(f2 * pTest->premultiply); + stream->WriteSBits((int32)twVal, pTest->significant_bits); + } + break; + } + case DT_STRING: + stream->WriteBitString((const char *)&to[pTest->fieldOffset]); + break; + default: + break; + } + } +} + +int DELTA_CheckDelta(unsigned char *from, unsigned char *to, delta_t *pFields) +{ + DELTA_ClearFlags(pFields); + DELTA_MarkSendFields(from, to, pFields); + + return DELTA_CountSendFields(pFields); +} + +void DELTA_WriteHeader(BitBuffer *stream, deltacallback_t *header) +{ + int delta = header->num - header->numbase; + if (header->full) + { + if (delta == 1) + { + stream->WriteBit(1); + } + else + { + stream->WriteBit(0); + if (delta <= 0 || delta >= 64) + { + stream->WriteBit(1); + stream->WriteBits(header->num, 11); + } + else + { + stream->WriteBit(0); + stream->WriteBits(delta, 6); + } + } + } + else + { + stream->WriteBit(header->remove != 0); + if (delta <= 0 || delta >= 64) + { + stream->WriteBit(1); + stream->WriteBits(header->num, 11); + } + else + { + stream->WriteBit(0); + stream->WriteBits(delta, 6); + } + } + + header->numbase = header->num; + if (!header->remove) + { + stream->WriteBit(header->custom != 0); + if (header->instanced_baseline) + { + if (header->newbl) + { + stream->WriteBit(1); + stream->WriteBits(header->newblindex, 6); + } + else + { + stream->WriteBit(0); + } + } + if (header->full && !header->newbl) + { + if (header->offset) + { + stream->WriteBit(1); + stream->WriteBits(header->offset, 6); + } + else + { + stream->WriteBit(0); + } + } + } +} + +qboolean DELTA_WriteDelta(BitBuffer *stream, unsigned char *from, unsigned char *to, bool force, delta_t *pFields, deltacallback_t *header) +{ + int i; + int bytecount; + int bits[2]; + + if (!DELTA_CheckDelta(from, to, pFields) && !force) { + return FALSE; + } + + DELTA_SetSendFlagBits(pFields, bits, &bytecount); + + if (header) { + DELTA_WriteHeader(stream, header); + } + + stream->WriteBits(bytecount, 3); + for (i = 0; i < bytecount; i++) { + stream->WriteBits(((byte *)bits)[i], 8); + } + + DELTA_WriteMarkedFields(stream, from, to, pFields); + return TRUE; +} + +int DELTA_ParseDelta(BitBuffer *stream, unsigned char *from, unsigned char *to, delta_t *pFields) +{ + delta_description_t *pTest; + int i; + int bits[2]; // this is a limit with 64 fields max in delta + int nbytes; + int bitfieldnumber; + int fieldCount = pFields->fieldCount; + int fieldType; + int fieldSign; + + double d2; + float t; + int addt; + char *st2; + char c; + int startbit; + + startbit = stream->CurrentBit(); + memset(bits, 0, sizeof(bits)); + + nbytes = stream->ReadBits(3); + for (i = 0; i < nbytes; i++) { + ((byte *)bits)[i] = stream->ReadBits(8); + } + + for (i = 0, pTest = pFields->pdd; i < fieldCount; i++, pTest++) + { + fieldType = pTest->fieldType & ~DT_SIGNED; + + bitfieldnumber = (1 << (i & 0x1F)); + if (!(bitfieldnumber & bits[i > 31])) + { + // Field was not sent to us, just transfer info from the "from" + switch (fieldType) + { + case DT_BYTE: + to[pTest->fieldOffset] = from[pTest->fieldOffset]; + break; + case DT_SHORT: + *(uint16 *)&to[pTest->fieldOffset] = *(uint16 *)&from[pTest->fieldOffset]; + break; + case DT_FLOAT: + case DT_INTEGER: + case DT_ANGLE: + case DT_TIMEWINDOW_8: + case DT_TIMEWINDOW_BIG: + *(uint32 *)&to[pTest->fieldOffset] = *(uint32 *)&from[pTest->fieldOffset]; + break; + case DT_STRING: + strcpy((char *)&to[pTest->fieldOffset], (char *)&from[pTest->fieldOffset]); + break; + default: + break; + } + continue; + } + + pTest->stats.receivedcount++; + fieldSign = pTest->fieldType & DT_SIGNED; + + switch (fieldType) + { + case DT_BYTE: + if (fieldSign) + { + d2 = (double)stream->ReadSBits(pTest->significant_bits); + if (pTest->premultiply <= 0.9999 || pTest->premultiply >= 1.0001) { + d2 = d2 / pTest->premultiply; + } +#if !defined(HLTV) + if (pTest->postmultiply <= 0.9999 || pTest->postmultiply >= 1.0001) { + d2 = d2 * pTest->postmultiply; + } +#endif + *(int8 *)&to[pTest->fieldOffset] = (int8)d2; + } + else + { + d2 = (double)stream->ReadBits(pTest->significant_bits); + if (pTest->premultiply <= 0.9999 || pTest->premultiply >= 1.0001) { + d2 = d2 / pTest->premultiply; + } +#if !defined(HLTV) + if (pTest->postmultiply <= 0.9999 || pTest->postmultiply >= 1.0001) { + d2 = d2 * pTest->postmultiply; + } +#endif + *(uint8 *)&to[pTest->fieldOffset] = (uint8)d2; + } + break; + case DT_SHORT: + if (fieldSign) + { + d2 = (double)stream->ReadSBits(pTest->significant_bits); + if (pTest->premultiply <= 0.9999 || pTest->premultiply >= 1.0001) { + d2 = d2 / pTest->premultiply; + } +#if !defined(HLTV) + if (pTest->postmultiply <= 0.9999 || pTest->postmultiply >= 1.0001) { + d2 = d2 * pTest->postmultiply; + } +#endif + *(int16 *)&to[pTest->fieldOffset] = (int16)d2; + } + else + { + d2 = (double)stream->ReadBits(pTest->significant_bits); + if (pTest->premultiply <= 0.9999 || pTest->premultiply >= 1.0001) { + d2 = d2 / pTest->premultiply; + } +#if !defined(HLTV) + if (pTest->postmultiply <= 0.9999 || pTest->postmultiply >= 1.0001) { + d2 = d2 * pTest->postmultiply; + } +#endif + *(uint16 *)&to[pTest->fieldOffset] = (uint16)d2; + } + break; + case DT_FLOAT: + if (fieldSign) + { + d2 = (double)stream->ReadSBits(pTest->significant_bits); + } + else + { + d2 = (double)stream->ReadBits(pTest->significant_bits); + } + if (pTest->premultiply <= 0.9999 || pTest->premultiply >= 1.0001) { + d2 = d2 / pTest->premultiply; + } +#if !defined(HLTV) + if (pTest->postmultiply <= 0.9999 || pTest->postmultiply >= 1.0001) { + d2 = d2 * pTest->postmultiply; + } +#endif + *(float *)&to[pTest->fieldOffset] = (float)d2; + break; + case DT_INTEGER: + if (fieldSign) + { + d2 = (double)stream->ReadSBits(pTest->significant_bits); + if (pTest->premultiply <= 0.9999 || pTest->premultiply >= 1.0001) { + d2 = d2 / pTest->premultiply; + } +#if !defined(HLTV) + if (pTest->postmultiply <= 0.9999 || pTest->postmultiply >= 1.0001) { + d2 = d2 * pTest->postmultiply; + } +#endif + *(int32 *)&to[pTest->fieldOffset] = (int32)d2; + } + else + { + d2 = (double)stream->ReadBits(pTest->significant_bits); + if (pTest->premultiply <= 0.9999 || pTest->premultiply >= 1.0001) { + d2 = d2 / pTest->premultiply; + } +#if !defined(HLTV) + if (pTest->postmultiply <= 0.9999 || pTest->postmultiply >= 1.0001) { + d2 = d2 * pTest->postmultiply; + } +#endif + *(uint32 *)&to[pTest->fieldOffset] = (uint32)d2; + } + break; + case DT_ANGLE: + *(float *)&to[pTest->fieldOffset] = stream->ReadBitAngle(pTest->significant_bits); + break; + case DT_TIMEWINDOW_8: + if (g_large_Time_Buffers) { + t = stream->ReadFloat(); + } + else { + addt = stream->ReadSBits(8); + t = (float)((g_delta_Time * 100.0 - addt) / 100.0); + } + *(float *)&to[pTest->fieldOffset] = t; + break; + case DT_TIMEWINDOW_BIG: + if (g_large_Time_Buffers) { + t = stream->ReadFloat(); + } + else { + addt = stream->ReadSBits(pTest->significant_bits); + if (pTest->premultiply <= 0.9999 || pTest->premultiply >= 1.0001) { + t = (float)((g_delta_Time * pTest->premultiply - addt) / pTest->premultiply); + } + else { + t = (float)(g_delta_Time - addt); + } + } + + *(float *)&to[pTest->fieldOffset] = t; + break; + case DT_STRING: + st2 = (char *)&to[pTest->fieldOffset]; + do + { + c = stream->ReadBits(8); + *st2++ = c; + } while (c); + break; + default: + break; + } + } + + return stream->CurrentBit() - startbit; +} + +int DELTA_TestDelta(unsigned char *from, unsigned char *to, delta_t *pFields) +{ + int i; + char *st1, *st2; + delta_description_t *pTest; + int fieldType; + int fieldCount = pFields->fieldCount; + int length = 0; + bool different; + int neededBits = 0; + int highestBit = -1; + + for (i = 0, pTest = pFields->pdd; i < fieldCount; i++, pTest++) + { + different = false; + fieldType = pTest->fieldType & ~DT_SIGNED; + + switch (fieldType) + { + case DT_BYTE: + different = from[pTest->fieldOffset] != to[pTest->fieldOffset]; + break; + case DT_SHORT: + different = *(uint16 *)&from[pTest->fieldOffset] != *(uint16 *)&to[pTest->fieldOffset]; + break; + case DT_FLOAT: + case DT_INTEGER: + case DT_ANGLE: + different = *(uint32 *)&from[pTest->fieldOffset] != *(uint32 *)&to[pTest->fieldOffset]; + break; + // don't use multiplier when checking, to increase performance + // check values binary like it does in jit + case DT_TIMEWINDOW_8: + case DT_TIMEWINDOW_BIG: + different = (*(int32 *)&from[pTest->fieldOffset]) != (*(int32 *)&to[pTest->fieldOffset]); + break; + case DT_STRING: + st1 = (char *)&from[pTest->fieldOffset]; + st2 = (char *)&to[pTest->fieldOffset]; + + // Not sure why it is case insensitive, but it looks so + if (!(!*st1 && !*st2 || *st1 && *st2 && !_stricmp(st1, st2))) + { + different = true; + length = strlen(st2) * 8; + pTest->flags |= FDT_MARK; + } + break; + default: + break; + } + + if (different) + { + highestBit = i; + neededBits += (fieldType == DT_STRING) ? length + 8 : pTest->significant_bits; + } + } + + if (highestBit != -1) { + neededBits += highestBit / 8 * 8 + 8; + } + + return neededBits; +} + +void DELTA_AddEncoder(char *name, encoder_t conditionalencode) +{ + delta_encoder_t *delta = (delta_encoder_t *)Mem_ZeroMalloc(sizeof(delta_encoder_t)); + delta->name = _strdup(name); + delta->conditionalencode = conditionalencode; + delta->next = g_encoders; + g_encoders = delta; +} + +void DELTA_ClearEncoders() +{ + delta_encoder_t *n, *p = g_encoders; + while (p) + { + n = p->next; + free(p->name); + free(p); + p = n; + } + + g_encoders = nullptr; +} + +encoder_t DELTA_LookupEncoder(char *name) +{ + delta_encoder_t *p = g_encoders; + while (p) + { + if (_stricmp(name, p->name) == 0) { + return p->conditionalencode; + } + + p = p->next; + } + + return nullptr; +} + +int DELTA_CountLinks(delta_link_t *plinks) +{ + delta_link_t *p = plinks; + + int c; + for (c = 0; p; c++) { + p = p->next; + } + + return c; +} + +void DELTA_ReverseLinks(delta_link_t **plinks) +{ + delta_link_t *n, *p = *plinks; + delta_link_t *newlist = nullptr; + + while (p) + { + n = p->next; + p->next = newlist; + newlist = p; + p = n; + } + + *plinks = newlist; +} + +void DELTA_ClearLinks(delta_link_t **plinks) +{ + delta_link_t *n, *p = *plinks; + while (p) + { + n = p->next; + free(p); + p = n; + } + *plinks = 0; +} + +delta_t *DELTA_BuildFromLinks(delta_link_t **pplinks) +{ + delta_description_t *pdesc, *pcur; + delta_t *pdelta; + delta_link_t *p; + int count; + + pdelta = (delta_t *)Mem_ZeroMalloc(sizeof(delta_t)); + DELTA_ReverseLinks(pplinks); + count = DELTA_CountLinks(*pplinks); + + if (count > DELTA_MAX_FIELDS) { + return nullptr; + } + + pdesc = (delta_description_t *)Mem_ZeroMalloc(sizeof(delta_description_t) * count); + + for (p = *pplinks, pcur = pdesc; p; p = p->next, pcur++) + { + memcpy(pcur, p->delta, sizeof(delta_description_t)); + free(p->delta); + p->delta = nullptr; + } + + DELTA_ClearLinks(pplinks); + + pdelta->dynamic = 1; + pdelta->fieldCount = count; + pdelta->pdd = pdesc; + + return pdelta; +} + +int DELTA_FindOffset(int count, delta_definition_t *pdef, char *fieldname) +{ + for (int i = 0; i < count; i++) + { + if (_stricmp(fieldname, pdef[i].fieldName) == 0) { + return pdef[i].fieldOffset; + } + } + + return 0; +} + +bool DELTA_ParseType(delta_description_t *pdelta, char **pstream) +{ + // Read the stream till we hit the end + while (*pstream = COM_Parse(*pstream), com_token[0] != 0) + { + if (!_stricmp(com_token, ",")) + return true; // end of type description + + if (!_stricmp(com_token, "|")) + continue; // skip | token + + // Determine field type + if (!_stricmp(com_token, "DT_SIGNED")) + pdelta->fieldType |= DT_SIGNED; + else if (!_stricmp(com_token, "DT_BYTE")) + pdelta->fieldType |= DT_BYTE; + else if (!_stricmp(com_token, "DT_SHORT")) + pdelta->fieldType |= DT_SHORT; + else if (!_stricmp(com_token, "DT_FLOAT")) + pdelta->fieldType |= DT_FLOAT; + else if (!_stricmp(com_token, "DT_INTEGER")) + pdelta->fieldType |= DT_INTEGER; + else if (!_stricmp(com_token, "DT_ANGLE")) + pdelta->fieldType |= DT_ANGLE; + else if (!_stricmp(com_token, "DT_TIMEWINDOW_8")) + pdelta->fieldType |= DT_TIMEWINDOW_8; + else if (!_stricmp(com_token, "DT_TIMEWINDOW_BIG")) + pdelta->fieldType |= DT_TIMEWINDOW_BIG; + else if (!_stricmp(com_token, "DT_STRING")) + pdelta->fieldType |= DT_STRING; + else + { + return false; + } + } + + // We are hit the end of the stream + return false; +} + +bool DELTA_ParseField(int count, delta_definition_t *pdefinition, delta_link_t *pField, char **pstream) +{ + bool readpost = false; + if (_stricmp(com_token, "DEFINE_DELTA")) + { + if (_stricmp(com_token, "DEFINE_DELTA_POST") != 0) { + return false; + } + + readpost = true; + } + + *pstream = COM_Parse(*pstream); + if (_stricmp(com_token, "(")) + { + return false; + } + + *pstream = COM_Parse(*pstream); + if (com_token[0] == '\0') + { + return false; + } + + strncpy(pField->delta->fieldName, com_token, sizeof(pField->delta->fieldName) - 1); + pField->delta->fieldName[sizeof(pField->delta->fieldName) - 1] = '\0'; + + pField->delta->fieldOffset = DELTA_FindOffset(count, pdefinition, com_token); + + *pstream = COM_Parse(*pstream); + if (!DELTA_ParseType(pField->delta, pstream)) { + return false; + } + + *pstream = COM_Parse(*pstream); + pField->delta->fieldSize = 1; + pField->delta->significant_bits = atoi(com_token); + *pstream = COM_Parse(*pstream); + *pstream = COM_Parse(*pstream); + pField->delta->premultiply = (float)atof(com_token); + + if (readpost) + { + *pstream = COM_Parse(*pstream); + *pstream = COM_Parse(*pstream); + pField->delta->postmultiply = (float)atof(com_token); + } + else + { + pField->delta->postmultiply = 1.0; + } + + *pstream = COM_Parse(*pstream); + if (_stricmp(com_token, ")")) + { + return false; + } + + *pstream = COM_Parse(*pstream); + if (_stricmp(com_token, ",")) { + COM_UngetToken(); + } + + return true; +} + +void DELTA_FreeDescription(delta_t **ppdesc) +{ + if (ppdesc && *ppdesc) + { + if ((*ppdesc)->dynamic) { + free((*ppdesc)->pdd); + } + + free(*ppdesc); + *ppdesc = nullptr; + } +} + +void DELTA_AddDefinition(char *name, delta_definition_t *pdef, int numelements) +{ + delta_definition_list_t *p = g_defs; + while (p) + { + if (_stricmp(name, p->ptypename) == 0) { + break; + } + + p = p->next; + } + + if (!p) + { + p = (delta_definition_list_t *)Mem_ZeroMalloc(sizeof(delta_definition_list_t)); + p->ptypename = _strdup(name); + p->next = g_defs; + g_defs = p; + } + + p->pdefinition = pdef; + p->numelements = numelements; +} + +void DELTA_ClearDefinitions() +{ + delta_definition_list_t *n, *p = g_defs; + while (p) + { + n = p->next; + free(p->ptypename); + free(p); + p = n; + } + + g_defs = nullptr; +} + +delta_definition_t *DELTA_FindDefinition(char *name, int *count) +{ + *count = 0; + + delta_definition_list_t *p = g_defs; + while (p) + { + if (!_stricmp(name, p->ptypename)) + { + *count = p->numelements; + return p->pdefinition; + } + + p = p->next; + } + + return nullptr; +} + +void DELTA_SkipDescription(char **pstream) +{ + *pstream = COM_Parse(*pstream); + do + { + *pstream = COM_Parse(*pstream); + if (com_token[0] == '\0') { + return; + } + } + while (_stricmp(com_token, "}")); +} + +bool DELTA_ParseOneField(char **ppstream, delta_link_t **pplist, int count, delta_definition_t *pdefinition) +{ + delta_link_t *newlink; + delta_link_t link; + + while (true) + { + if (!_stricmp(com_token, "}")) + { + COM_UngetToken(); + break; + } + + *ppstream = COM_Parse(*ppstream); + if (com_token[0] == '\0') { + break; + } + + memset(&link, 0, sizeof(link)); + link.delta = (delta_description_t *)Mem_ZeroMalloc(sizeof(delta_description_t)); + if (!DELTA_ParseField(count, pdefinition, &link, ppstream)) { + return false; + } + + newlink = (delta_link_t *)Mem_ZeroMalloc(sizeof(delta_link_t)); + newlink->delta = link.delta; + newlink->next = *pplist; + *pplist = newlink; + } + + return true; +} + +bool DELTA_ParseDescription(char *name, delta_t **ppdesc, char *pstream) +{ + delta_link_t *links = nullptr; + delta_definition_t *pdefinition; + char encoder[32] = ""; + char source[32]; + int count = 0; + + if (!ppdesc) { + return false; + } + + *ppdesc = nullptr; + + if (!pstream) { + return false; + } + + while (true) + { + // Parse delta name + pstream = COM_Parse(pstream); + if (com_token[0] == '\0') { + break; + } + + if (_stricmp(com_token, name)) + { + DELTA_SkipDescription(&pstream); + } + else + { + pdefinition = DELTA_FindDefinition(com_token, &count); + if (!pdefinition) { + return false; + } + + // Parse source of conditional encoder + pstream = COM_Parse(pstream); + if (com_token[0] == '\0') { + return false; + } + + if (_stricmp(com_token, "none")) + { + strncpy(source, com_token, sizeof(source) - 1); + source[sizeof(source) - 1] = '\0'; + + // Parse custom encoder function name + pstream = COM_Parse(pstream); + if (com_token[0] == '\0') { + return false; + } + + strncpy(encoder, com_token, sizeof(encoder) - 1); + encoder[sizeof(encoder) - 1] = '\0'; + } + + // Parse fields + while (true) + { + pstream = COM_Parse(pstream); + if (com_token[0] == '\0') { + break; + } + + if (!_stricmp(com_token, "}")) { + break; + } + + if (_stricmp(com_token, "{")) { + return false; + } + + if (!DELTA_ParseOneField(&pstream, &links, count, pdefinition)) { + return false; + } + } + } + } + + *ppdesc = DELTA_BuildFromLinks(&links); + + if (encoder[0]) + { + strncpy((*ppdesc)->conditionalencodename, encoder, sizeof((*ppdesc)->conditionalencodename) - 1); + (*ppdesc)->conditionalencodename[sizeof((*ppdesc)->conditionalencodename) - 1] = '\0'; + (*ppdesc)->conditionalencode = nullptr; + } + + return true; +} + +bool DELTA_Load(char *name, delta_t **ppdesc, char *pszFile) +{ + return false; +} + +void DELTA_RegisterDescription(char *name) +{ + delta_registry_t *p = (delta_registry_t *)Mem_ZeroMalloc(sizeof(delta_registry_t)); + p->next = g_deltaregistry; + g_deltaregistry = p; + p->name = _strdup(name); + p->pdesc = 0; +} + +void DELTA_ClearRegistrations() +{ + delta_registry_t *n, *p = g_deltaregistry; + while (p) + { + n = p->next; + free(p->name); + + if (p->pdesc) { + DELTA_FreeDescription(&p->pdesc); + } + + free(p); + p = n; + } + + g_deltaregistry = nullptr; +} + +delta_t **DELTA_LookupRegistration(char *name) +{ + delta_registry_t *delta = g_deltaregistry; + while (delta) + { + if (_stricmp(delta->name, name) == 0) { + return &delta->pdesc; + } + + delta = delta->next; + } + + return nullptr; +} + +void DELTA_ClearStats(delta_t *p) +{ + if (!p) { + return; + } + + for (int i = 0; i < p->fieldCount; i++) + { + p->pdd[i].stats.sendcount = 0; + p->pdd[i].stats.receivedcount = 0; + } +} + +void DELTA_Init() +{ + g_defs = nullptr; + g_encoders = nullptr; + g_deltaregistry = nullptr; + + DELTA_AddDefinition("clientdata_t", g_ClientDataDefinition, ARRAYSIZE(g_ClientDataDefinition)); + DELTA_AddDefinition("weapon_data_t", g_WeaponDataDefinition, ARRAYSIZE(g_WeaponDataDefinition)); + DELTA_AddDefinition("usercmd_t", g_UsercmdDataDefinition, ARRAYSIZE(g_UsercmdDataDefinition)); + DELTA_AddDefinition("entity_state_t", g_EntityDataDefinition, ARRAYSIZE(g_EntityDataDefinition)); + DELTA_AddDefinition("entity_state_player_t", g_EntityDataDefinition, ARRAYSIZE(g_EntityDataDefinition)); + DELTA_AddDefinition("custom_entity_state_t", g_EntityDataDefinition, ARRAYSIZE(g_EntityDataDefinition)); + DELTA_AddDefinition("event_t", g_EventDataDefinition, ARRAYSIZE(g_EventDataDefinition)); + + DELTA_RegisterDescription("clientdata_t"); + DELTA_RegisterDescription("entity_state_t"); + DELTA_RegisterDescription("entity_state_player_t"); + DELTA_RegisterDescription("custom_entity_state_t"); + DELTA_RegisterDescription("usercmd_t"); + DELTA_RegisterDescription("weapon_data_t"); + DELTA_RegisterDescription("event_t"); + + g_large_Time_Buffers = false; +} + +void DELTA_UpdateDescriptions() +{ + g_pplayerdelta = *DELTA_LookupRegistration("entity_state_player_t"); + g_pentitydelta = *DELTA_LookupRegistration("entity_state_t"); + g_pcustomentitydelta = *DELTA_LookupRegistration("custom_entity_state_t"); + g_pclientdelta = *DELTA_LookupRegistration("clientdata_t"); + g_pweapondelta = *DELTA_LookupRegistration("weapon_data_t"); + g_peventdelta = *DELTA_LookupRegistration("event_t"); +} + +void DELTA_Shutdown() +{ + DELTA_ClearEncoders(); + DELTA_ClearDefinitions(); + DELTA_ClearRegistrations(); +} + +void DELTA_SetTime(double time) +{ + g_delta_Time = time; +} + +void DELTA_SetLargeTimeBufferSize(bool bigBuffers) +{ + g_large_Time_Buffers = bigBuffers; +} + +bool World::IsDeltaEncoder() const +{ + if (g_pcustomentitydelta + && g_pentitydelta + && g_pplayerdelta) { + return true; + } + + return false; +} + +delta_t *World::GetDeltaEncoder(int index, bool custom) +{ + if (custom) { + return (delta_t *)g_pcustomentitydelta; + } + else if (IsPlayerIndex(index)) { + return (delta_t *)g_pplayerdelta; + } + + return (delta_t *)g_pentitydelta; +} + +delta_t *World::GetEventDelta() const { + return g_peventdelta; +} + +delta_t *World::GetClientDelta() const { + return g_pclientdelta; +} + +delta_t *World::GetEntityDelta() const { + return g_pentitydelta; +} + +delta_t *World::GetWeaponDelta() const { + return g_pweapondelta; +} + +#endif // HOOK_HLTV diff --git a/rehlds/hookers/HLTV/Core/DeltaEx.h b/rehlds/hookers/HLTV/Core/DeltaEx.h new file mode 100644 index 0000000..06db8ed --- /dev/null +++ b/rehlds/hookers/HLTV/Core/DeltaEx.h @@ -0,0 +1,163 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#ifdef HOOK_HLTV + +#include "event_args.h" + +#define g_defs (*pg_defs) +#define g_encoders (*pg_encoders) +#define g_deltaregistry (*pg_deltaregistry) + +#define g_pplayerdelta (*pg_pplayerdelta) +#define g_pentitydelta (*pg_pentitydelta) +#define g_pcustomentitydelta (*pg_pcustomentitydelta) +#define g_pclientdelta (*pg_pclientdelta) +#define g_pweapondelta (*pg_pweapondelta) +#define g_peventdelta (*pg_peventdelta) + +#define g_delta_Time (*pg_delta_Time) +#define g_large_Time_Buffers (*pg_large_Time_Buffers) + +typedef struct delta_link_s +{ + struct delta_link_s *next; + delta_description_t *delta; +} delta_link_t; + +typedef struct delta_definition_s +{ + char *fieldName; + size_t fieldOffset; +} delta_definition_t; + +typedef struct delta_definition_list_s +{ + struct delta_definition_list_s *next; + char *ptypename; + int numelements; + delta_definition_t *pdefinition; +} delta_definition_list_t; + +typedef struct delta_registry_s +{ + struct delta_registry_s *next; + char *name; + delta_t *pdesc; +} delta_registry_t; + +namespace Delta { + #define m_MetaDelta g_MetaDelta + extern delta_t g_MetaDelta[]; +}; + +#define m_EntityDelta g_pentitydelta +#define m_PlayerDelta g_pplayerdelta +#define m_CustomentityDelta g_pcustomentitydelta + +#define m_ClientDelta g_pclientdelta +#define m_WeaponDelta g_pweapondelta +#define m_EventDelta g_peventdelta + +extern delta_definition_list_t *g_defs; +extern delta_encoder_t *g_encoders; +extern delta_registry_t *g_deltaregistry; + +extern delta_t *g_pentitydelta; +extern delta_t *g_pplayerdelta; +extern delta_t *g_pcustomentitydelta; +extern delta_t *g_pclientdelta; +extern delta_t *g_pweapondelta; +extern delta_t *g_peventdelta; + +extern double g_delta_Time; +extern bool g_large_Time_Buffers; + +delta_description_t *DELTA_FindField(delta_t *pFields, const char *pszField); +int DELTA_FindFieldIndex(delta_t *pFields, const char *fieldname); +void DELTA_SetField(delta_t *pFields, const char *fieldname); +void DELTA_UnsetField(delta_t *pFields, const char *fieldname); +void DELTA_SetFieldByIndex(delta_t *pFields, int fieldNumber); +void DELTA_UnsetFieldByIndex(delta_t *pFields, int fieldNumber); +void DELTA_ClearFlags(delta_t *pFields); +int DELTA_CountSendFields(delta_t *pFields); +void DELTA_MarkSendFields(unsigned char *from, unsigned char *to, delta_t *pFields); +void DELTA_SetSendFlagBits(delta_t *pFields, int *bits, int *bytecount); +void DELTA_WriteMarkedFields(BitBuffer *stream, unsigned char *from, unsigned char *to, delta_t *pFields); +int DELTA_CheckDelta(unsigned char *from, unsigned char *to, delta_t *pFields); +void DELTA_WriteHeader(BitBuffer *stream, deltacallback_t *header); +qboolean DELTA_WriteDelta(BitBuffer *stream, unsigned char *from, unsigned char *to, bool force, delta_t *pFields, deltacallback_t *header = nullptr); +int DELTA_ParseDelta(BitBuffer *stream, unsigned char *from, unsigned char *to, delta_t *pFields); +int DELTA_TestDelta(unsigned char *from, unsigned char *to, delta_t *pFields); +void DELTA_AddEncoder(char *name, encoder_t conditionalencode); +void DELTA_ClearEncoders(); +encoder_t DELTA_LookupEncoder(char *name); +int DELTA_CountLinks(delta_link_t *plinks); +void DELTA_ReverseLinks(delta_link_t **plinks); +void DELTA_ClearLinks(delta_link_t **plinks); +delta_t *DELTA_BuildFromLinks(delta_link_t **pplinks); +int DELTA_FindOffset(int count, delta_definition_t *pdef, char *fieldname); +bool DELTA_ParseType(delta_description_t *pdelta, char **pstream); +bool DELTA_ParseField(int count, delta_definition_t *pdefinition, delta_link_t *pField, char **pstream); +void DELTA_FreeDescription(delta_t **ppdesc); +void DELTA_AddDefinition(char *name, delta_definition_t *pdef, int numelements); +void DELTA_ClearDefinitions(); +delta_definition_t *DELTA_FindDefinition(char *name, int *count); +void DELTA_SkipDescription(char **pstream); +bool DELTA_ParseOneField(char **ppstream, delta_link_t **pplist, int count, delta_definition_t *pdefinition); +bool DELTA_ParseDescription(char *name, delta_t **ppdesc, char *pstream); +bool DELTA_Load(char *name, delta_t **ppdesc, char *pszFile); +void DELTA_RegisterDescription(char *name); +void DELTA_ClearRegistrations(); +delta_t **DELTA_LookupRegistration(char *name); +void DELTA_ClearStats(delta_t *p); +void DELTA_Init(); +void DELTA_UpdateDescriptions(); +void DELTA_Shutdown(); +void DELTA_SetTime(double time); +void DELTA_SetLargeTimeBufferSize(bool bigBuffers); + +class DeltaWrapper { +public: + void Init(IBaseSystem *system) { DELTA_Init(); } + void Shutdown() { DELTA_Shutdown(); } + void UpdateDescriptions() { DELTA_UpdateDescriptions(); } + void WriteHeader(BitBuffer *stream, deltacallback_t *header) { DELTA_WriteHeader(stream, header); } + bool WriteDelta(BitBuffer *stream, unsigned char *from, unsigned char *to, bool force, delta_t *pFields, deltacallback_t *header = nullptr) { return DELTA_WriteDelta(stream, from, to, force, pFields, header) ? true : false; } + int ParseDelta(BitBuffer *stream, unsigned char *from, unsigned char *to, delta_t *pFields) { return DELTA_ParseDelta(stream, from, to, pFields); } + void SetTime(double time) { DELTA_SetTime(time); } + void SetLargeTimeBufferSize(bool bigBuffers) { DELTA_SetLargeTimeBufferSize(bigBuffers); } + int TestDelta(unsigned char *from, unsigned char *to, delta_t *pFields) { return DELTA_TestDelta(from, to, pFields); } + delta_t **LookupRegistration(char *name) { return DELTA_LookupRegistration(name); } + void FreeDescription(delta_t **ppdesc) { DELTA_FreeDescription(ppdesc); } + delta_registry_t *GetRegistry() const { return g_deltaregistry; } +}; + +#endif // HOOK_HLTV diff --git a/rehlds/hookers/HLTV/Core/hooklist.cpp b/rehlds/hookers/HLTV/Core/hooklist.cpp new file mode 100644 index 0000000..d306a03 --- /dev/null +++ b/rehlds/hookers/HLTV/Core/hooklist.cpp @@ -0,0 +1,617 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +#ifdef HOOK_HLTV + +// Hooks stuff +#include "hookers/memory.cpp" +#include "hookers/hooker.cpp" + +//#define Mem_region +//#define World_region +//#define Server_region +//#define Network_region +//#define NetSocket_region +//#define BSPModel_region +//#define Delta_region +//#define ObjectDictionary_region +//#define ObjectList_region +//#define BitBuffer_region +//#define Function_References_region +//#define Data_References_region + +FunctionHook g_FunctionHooks[] = +{ + // DO NOT DISABLE, other functions depends on memory allocation routines +#ifndef Mem_region + + HOOK_DEF(0x01D21F5F, malloc_wrapper), + HOOK_DEF(0x01D21E4E, free_wrapper), + HOOK_DEF(0x01D30145, strdup_wrapper), + HOOK_DEF(0x01D21F71, __nh_malloc_wrapper), + + HOOK_DEF(0x01D043C0, Mem_ZeroMalloc), + //HOOK_DEF(0x0, Mem_Malloc), + //HOOK_DEF(0x0, Mem_Realloc), + //HOOK_DEF(0x0, Mem_Calloc), + //HOOK_DEF(0x0, Mem_Strdup), + //HOOK_DEF(0x0, Mem_Free), + + //HOOK_DEF(0x0, realloc_wrapper), + //HOOK_DEF(0x0, calloc_wrapper), + +#endif // Mem_region + +#ifndef World_region + + // World virtual functions + HOOK_VIRTUAL_DEF(0x01D14DA0, World::Init), + HOOK_VIRTUAL_DEF(0x01D15470, World::RunFrame), + HOOK_VIRTUAL_DEF(0x01D154F0, World::ReceiveSignal), + HOOK_VIRTUAL_DEF(0x01D15490, World::ExecuteCommand), + HOOK_VIRTUAL_DEF(0x01D15500, World::RegisterListener), + HOOK_VIRTUAL_DEF(0x01D15530, World::RemoveListener), + HOOK_VIRTUAL_DEF(0x01D15570, World::GetSystem), + HOOK_VIRTUAL_DEF(0x01D15580, World::GetSerial), + HOOK_VIRTUAL_DEF(0x01D17380, World::GetStatusLine), + HOOK_VIRTUAL_DEF(0x01D17370, World::GetType), + HOOK_VIRTUAL_DEF(0x01D15590, World::GetName), + HOOK_VIRTUAL_DEF(0x01D155C0, World::GetState), + HOOK_VIRTUAL_DEF(0x01D15620, World::GetVersion), + HOOK_VIRTUAL_DEF(0x01D14F70, World::ShutDown), + HOOK_VIRTUAL_DEF(0x01D175D0, World::GetTime), + HOOK_VIRTUAL_DEF(0x01D186A0, World::GetGameServerAddress), + HOOK_VIRTUAL_DEF(0x01D186B0, World::GetLevelName), + HOOK_VIRTUAL_DEF(0x01D186F0, World::GetGameDir), + HOOK_VIRTUAL_DEF(0x01D16470, World::GetFrameByTime), + HOOK_VIRTUAL_DEF(0x01D16420, World::GetFrameBySeqNr), + HOOK_VIRTUAL_DEF(0x01D16450, World::GetLastFrame), + HOOK_VIRTUAL_DEF(0x01D16460, World::GetFirstFrame), + HOOK_VIRTUAL_DEF(0x01D18700, World::GetServerCount), + HOOK_VIRTUAL_DEF(0x01D18670, World::GetSlotNumber), + HOOK_VIRTUAL_DEF(0x01D18710, World::GetMaxClients), + HOOK_VIRTUAL_DEF(0x01D15840, World::GetNumPlayers), + HOOK_VIRTUAL_DEF(0x01D186C0, World::GetWorldModel), + HOOK_VIRTUAL_DEF(0x01D18350, World::GetServerInfoString), + HOOK_VIRTUAL_DEF(0x01D18360, World::GetPlayerInfoString), + HOOK_VIRTUAL_DEF(0x01D15450, World::GetUserMsg), + HOOK_VIRTUAL_DEF(0x01D15A60, World::GetHostName), + HOOK_VIRTUAL_DEF(0x01D189A0, World::GetServerInfo), + HOOK_VIRTUAL_DEF(0x01D15510, World::IsPlayerIndex), + HOOK_VIRTUAL_DEF(0x01D18660, World::IsVoiceEnabled), + HOOK_VIRTUAL_DEF(0x01D15870, World::IsActive), + HOOK_VIRTUAL_DEF(0x01D18190, World::IsPaused), + HOOK_VIRTUAL_DEF(0x01D181A0, World::IsComplete), + HOOK_VIRTUAL_DEF(0x01D18D20, World::IsHLTV), + HOOK_VIRTUAL_DEF(0x01D15010, World::Reset), + HOOK_VIRTUAL_DEF(0x01D158A0, World::SetServerInfoString), + HOOK_VIRTUAL_DEF(0x01D18C30, World::UpdateServerInfo), + HOOK_VIRTUAL_DEF(0x01D18150, World::SetPaused), + HOOK_VIRTUAL_DEF(0x01D15630, World::SetTime), + HOOK_VIRTUAL_DEF(0x01D181B0, World::SetBufferSize), + HOOK_VIRTUAL_DEF(0x01D18690, World::SetVoiceEnabled), + HOOK_VIRTUAL_DEF(0x01D18130, World::SetMoveVars), + HOOK_VIRTUAL_DEF(0x01D18320, World::SetCDInfo), + HOOK_VIRTUAL_DEF(0x01D18D10, World::SetHLTV), + HOOK_VIRTUAL_DEF(0x01D183B0, World::SetExtraInfo), + HOOK_VIRTUAL_DEF(0x01D18680, World::SetViewEntity), + HOOK_VIRTUAL_DEF(0x01D18340, World::SetGameServerAddress), + HOOK_VIRTUAL_DEF(0x01D15680, World::SetHostName), + HOOK_VIRTUAL_DEF(0x01D15760, World::NewGame), + HOOK_VIRTUAL_DEF(0x01D17490, World::FinishGame), + HOOK_VIRTUAL_DEF(0x01D18720, World::SaveAsDemo), + HOOK_VIRTUAL_DEF(0x01D174C0, World::StopGame), + HOOK_VIRTUAL_DEF(0x01D17560, World::FindUserMsgByName), + HOOK_VIRTUAL_DEF(0x01D181F0, World::ParseDeltaDescription), + HOOK_VIRTUAL_DEF(0x01D18420, World::ParseBaseline), + HOOK_VIRTUAL_DEF(0x01D183F0, World::ParseEvent), + HOOK_VIRTUAL_DEF(0x01D16990, World::ParseClientData), + HOOK_VIRTUAL_DEF(0x01D18070, World::SetServerInfo, void(int, CRC32_t, unsigned char *, int, int, int, char *, char *, char *)), + HOOK_VIRTUAL_DEF(0x01D17470, World::SetServerInfo, void(serverinfo_t *)), + HOOK_VIRTUAL_DEF(0x01D16740, World::GetUncompressedFrame, bool(unsigned int, frame_t *)), + HOOK_VIRTUAL_DEF(0x01D17A60, World::UncompressEntitiesFromStream, bool(frame_t *, BitBuffer *)), + HOOK_VIRTUAL_DEF(0x01D175E0, World::UncompressEntitiesFromStream, bool(frame_t *, BitBuffer *, unsigned int)), + HOOK_VIRTUAL_DEF(0x01D167F0, World::GetClientData, bool(frame_t *, clientdata_t *)), + HOOK_VIRTUAL_DEF(0x01D167B0, World::GetClientData, bool(unsigned int, clientdata_t *)), + HOOK_VIRTUAL_DEF(0x01D160A0, World::AddFrame), + HOOK_VIRTUAL_DEF(0x01D153B0, World::AddResource), + HOOK_VIRTUAL_DEF(0x01D156E0, World::AddLightStyle), + HOOK_VIRTUAL_DEF(0x01D17520, World::AddSignonData), + HOOK_VIRTUAL_DEF(0x01D15130, World::AddUserMessage), + HOOK_VIRTUAL_DEF(0x01D154A0, World::AddBaselineEntity), + HOOK_VIRTUAL_DEF(0x01D155D0, World::AddInstancedBaselineEntity), + HOOK_VIRTUAL_DEF(0x01D151F0, World::UpdatePlayer), + HOOK_VIRTUAL_DEF(0x01D164C0, World::WriteFrame), + HOOK_VIRTUAL_DEF(0x01D17CB0, World::WriteNewData), + HOOK_VIRTUAL_DEF(0x01D16030, World::WriteClientUpdate), + HOOK_VIRTUAL_DEF(0x01D15D20, World::WriteMovevars), + HOOK_VIRTUAL_DEF(0x01D174F0, World::WriteSigonData), + HOOK_VIRTUAL_DEF(0x01D15A20, World::WriteLightStyles), + HOOK_VIRTUAL_DEF(0x01D17D80, World::RemoveFrames), + HOOK_VIRTUAL_DEF(0x01D17ED0, World::DuplicateFrames), + HOOK_VIRTUAL_DEF(0x01D17EF0, World::MoveFrames), + HOOK_VIRTUAL_DEF(0x01D17F10, World::RevertFrames), + + // World non-virtual functions + HOOK_DEF(0x01D16BA0, World::CompressFrame), + HOOK_DEF(0x01D16A80, World::ParseDeltaHeader), + HOOK_DEF(0x01D150D0, World::SetMaxClients), + HOOK_DEF(0x01D17D30, World::GetBufferedGameTime), + HOOK_DEF(0x01D15800, World::ConnectionComplete), + HOOK_DEF(0x01D15F00, World::WriteResources), + HOOK_DEF(0x01D15C60, World::WriteDeltaDescriptions), + HOOK_DEF(0x01D15EB0, World::WriteRegisteredUserMessages), + HOOK_DEF(0x01D158B0, World::WriteBaseline), + HOOK_DEF(0x01D15A80, World::WriteServerinfo), + HOOK_DEF(0x01D170E0, World::WritePacketEntities), + HOOK_DEF(0x01D189B0, World::WriteDeltaEntities), + HOOK_DEF(0x01D15790, World::SetState), + HOOK_DEF(0x01D15100, World::ClearUserMessages), + HOOK_DEF(0x01D17430, World::ClearServerInfo), + HOOK_DEF(0x01D15410, World::ClearResources), + HOOK_DEF(0x01D155A0, World::ClearInstancedBaseline), + HOOK_DEF(0x01D15540, World::ClearBaseline), + HOOK_DEF(0x01D156C0, World::ClearLightStyles), + HOOK_DEF(0x01D15880, World::ClearPlayers), + HOOK_DEF(0x01D17F30, World::ClearFrames), + HOOK_DEF(0x01D16E80, World::ClearEntityCache), + HOOK_DEF(0x01D16F30, World::GetFrameFromCache), + HOOK_DEF(0x01D17010, World::GetDeltaFromCache), + HOOK_DEF(0x01D17F90, World::CheckFrameBufferSize), + HOOK_DEF(0x01D18000, World::ReorderFrameTimes), + HOOK_DEF(0x01D16D20, World::FindBestBaseline), + HOOK_DEF(0x01D18A60, World::RearrangeFrame), + HOOK_DEF(0x01D16880, World::GetUncompressedFrame, bool(frame_t *deltaFrame, frame_t *frame)), + + //HOOK_DEF(0x01D17D00, World::SetName), // NOXREF + //HOOK_DEF(0x0, World::SetDirector), // NOXREF + //HOOK_DEF(0x0, World::SetTimeScale), // NOXREF + //HOOK_DEF(0x0, World::SetGameGroupAddress), // NOXREF + //HOOK_DEF(0x0, World::GetDirector), // NOXREF + //HOOK_DEF(0x0, World::WriteCustomDecals), // NOXREF + +#endif // World_region + +#ifndef Server_region + + // IServer virtual functions + HOOK_VIRTUAL_DEF(0x01D106B0, Server::Init), + HOOK_VIRTUAL_DEF(0x01D10C10, Server::RunFrame), + HOOK_VIRTUAL_DEF(0x01D10A90, Server::ExecuteCommand), + HOOK_VIRTUAL_DEF(0x01D13870, Server::GetStatusLine), + HOOK_VIRTUAL_DEF(0x01D137F0, Server::GetType), + HOOK_VIRTUAL_DEF(0x01D10B80, Server::ShutDown), + HOOK_VIRTUAL_DEF(0x01D14220, Server::Connect), + HOOK_VIRTUAL_DEF(0x01D142F0, Server::LoadDemo), + HOOK_VIRTUAL_DEF(0x01D13E40, Server::Reconnect), + HOOK_VIRTUAL_DEF(0x01D13EA0, Server::Disconnect), + HOOK_VIRTUAL_DEF(0x01D14620, Server::Retry), + HOOK_VIRTUAL_DEF(0x01D14680, Server::StopRetry), + HOOK_VIRTUAL_DEF(0x01D14430, Server::SendStringCommand), + HOOK_VIRTUAL_DEF(0x01D14470, Server::SendHLTVCommand), + HOOK_VIRTUAL_DEF(0x01D140C0, Server::IsConnected), + HOOK_VIRTUAL_DEF(0x01D140D0, Server::IsDemoFile), + HOOK_VIRTUAL_DEF(0x01D140E0, Server::IsGameServer), + HOOK_VIRTUAL_DEF(0x01D140F0, Server::IsRelayProxy), + HOOK_VIRTUAL_DEF(0x01D11260, Server::IsVoiceBlocking), + HOOK_VIRTUAL_DEF(0x01D13A70, Server::SetProxy), + HOOK_VIRTUAL_DEF(0x01D13A90, Server::SetDirector), + HOOK_VIRTUAL_DEF(0x01D137A0, Server::SetPlayerName), + HOOK_VIRTUAL_DEF(0x01D146B0, Server::SetDelayReconnect), + HOOK_VIRTUAL_DEF(0x01D14690, Server::SetAutoRetry), + HOOK_VIRTUAL_DEF(0x01D11270, Server::SetVoiceBlocking), + HOOK_VIRTUAL_DEF(0x01D11280, Server::SetRate), + HOOK_VIRTUAL_DEF(0x01D112D0, Server::SetUpdateRate), + HOOK_VIRTUAL_DEF(0x01D14390, Server::SetUserInfo), + HOOK_VIRTUAL_DEF(0x01D13770, Server::SetProtocol), + HOOK_VIRTUAL_DEF(0x01D109C0, Server::SetGameDirectory), + HOOK_VIRTUAL_DEF(0x01D13730, Server::GetRate), + HOOK_VIRTUAL_DEF(0x01D13740, Server::GetUpdateRate), + HOOK_VIRTUAL_DEF(0x01D13720, Server::GetServerInfoString), + HOOK_VIRTUAL_DEF(0x01D137D0, Server::GetPlayerName), + + // Server virtual functions + HOOK_VIRTUAL_DEF(0x01D145A0, Server::GetTime), + HOOK_VIRTUAL_DEF(0x01D13800, Server::GetWorld), + HOOK_VIRTUAL_DEF(0x01D13830, Server::GetDemoFileName), + HOOK_VIRTUAL_DEF(0x01D13810, Server::GetAddress), + HOOK_VIRTUAL_DEF(0x01D13750, Server::GetAutoRetry), + HOOK_VIRTUAL_DEF(0x01D13850, Server::GetHostName), + HOOK_VIRTUAL_DEF(0x01D13860, Server::GetPacketLoss), + HOOK_VIRTUAL_DEF(0x01D13760, Server::GetProtocol), + + // Server non-virtual functions + HOOK_DEF(0x01D14190, Server::CheckAutoRetry), + HOOK_DEF(0x01D145B0, Server::CheckConnection), + HOOK_DEF(0x01D141F0, Server::ScheduleAutoRetry), + HOOK_DEF(0x01D11750, Server::AcceptConnection), + HOOK_DEF(0x01D116C0, Server::AcceptBadPassword), + HOOK_DEF(0x01D11700, Server::AcceptRejection), + HOOK_DEF(0x01D117D0, Server::AcceptRedirect), + HOOK_DEF(0x01D11460, Server::SendConnectPacket), + HOOK_DEF(0x01D132F0, Server::SendServerCommands), + HOOK_DEF(0x01D11320, Server::SetState), + HOOK_DEF(0x01D10AB0, Server::Challenge), + HOOK_DEF(0x01D146C0, Server::Reset), + HOOK_DEF(0x01D11170, Server::AcceptChallenge), + HOOK_DEF(0x01D14050, Server::SendUserVar), + HOOK_DEF(0x01D10F70, Server::ProcessMessage), + HOOK_DEF(0x01D147B0, Server::ProcessEntityUpdate), + HOOK_DEF(0x01D10DA0, Server::ProcessConnectionlessMessage), + HOOK_DEF(0x01D12EF0, Server::ClearFrame), + HOOK_DEF(0x01D139E0, Server::ParseHLTV), + HOOK_DEF(0x01D13AA0, Server::ParseDirector), + HOOK_DEF(0x01D12CA0, Server::ParseFileTransferFailed), + HOOK_DEF(0x01D13B80, Server::ParseInfo), + HOOK_DEF(0x01D136E0, Server::ParseParticle), + HOOK_DEF(0x01D136B0, Server::ParseRoomType), + HOOK_DEF(0x01D13610, Server::ParseSpawnStaticSound), + HOOK_DEF(0x01D13580, Server::ParseEventReliable), + HOOK_DEF(0x01D13510, Server::ParsePings), + HOOK_DEF(0x01D12CE0, Server::ParseSignonNum), + HOOK_DEF(0x01D12FB0, Server::ParseUserMessage), + HOOK_DEF(0x01D134E0, Server::ParseStopSound), + HOOK_DEF(0x01D13410, Server::ParseEvent), + HOOK_DEF(0x01D13340, Server::ParseSound), + HOOK_DEF(0x01D13240, Server::ParseDeltaPacketEntities), + HOOK_DEF(0x01D131A0, Server::ParsePacketEntities), + HOOK_DEF(0x01D12DF0, Server::ParseCustomization), + HOOK_DEF(0x01D12010, Server::ParseCrosshairAngle), + HOOK_DEF(0x01D12050, Server::ParseSoundFade), + HOOK_DEF(0x01D12C10, Server::ParseDisconnect), + HOOK_DEF(0x01D12C00, Server::ParseChoke), + HOOK_DEF(0x01D12AC0, Server::ParseSetAngle), + HOOK_DEF(0x01D12B60, Server::ParseAddAngle), + HOOK_DEF(0x01D12A40, Server::ParseLightStyle), + HOOK_DEF(0x01D12A20, Server::ParseTime), + HOOK_DEF(0x01D129F0, Server::ParseVersion), + HOOK_DEF(0x01D129D0, Server::ParseBaseline), + HOOK_DEF(0x01D12860, Server::ParseTempEntity), + HOOK_DEF(0x01D12650, Server::ParseResourceList), + HOOK_DEF(0x01D12590, Server::ParseUpdateUserInfo), + HOOK_DEF(0x01D12150, Server::ParseStuffText), + HOOK_DEF(0x01D120C0, Server::ParseNewUserMsg), + HOOK_DEF(0x01D11F80, Server::ParseResourceRequest), + HOOK_DEF(0x01D11F00, Server::ParseSetView), + HOOK_DEF(0x01D11E90, Server::ParseCDTrack), + HOOK_DEF(0x01D11EC0, Server::ParseRestore), + HOOK_DEF(0x01D11CC0, Server::ParseMoveVars), + HOOK_DEF(0x01D11C60, Server::ParseDeltaDescription), + HOOK_DEF(0x01D119F0, Server::ParseServerinfo), + HOOK_DEF(0x01D119D0, Server::ParseBad), + HOOK_DEF(0x01D119C0, Server::ParseNop), + HOOK_DEF(0x01D11850, Server::ParsePrint), + HOOK_DEF(0x01D11880, Server::ParseVoiceInit), + HOOK_DEF(0x01D11940, Server::ParseVoiceData), + HOOK_DEF(0x01D144F0, Server::ParseTimeScale), + HOOK_DEF(0x01D144C0, Server::ParseSendExtraInfo), + HOOK_DEF(0x01D14100, Server::ParseCenterPrint), + HOOK_DEF(0x01D143D0, Server::ParseSetPause), + HOOK_DEF(0x01D14020, Server::ParseCutscene), + HOOK_DEF(0x01D12140, Server::ParseWeaponAnim), + HOOK_DEF(0x01D11C80, Server::ParseDecalName), + HOOK_DEF(0x01D13FF0, Server::ParseFinale), + HOOK_DEF(0x01D13FC0, Server::ParseIntermission), + HOOK_DEF(0x01D13F50, Server::ParseClientData), + HOOK_DEF(0x01D14570, Server::ParseResourceLocation), + HOOK_DEF(0x01D14820, Server::ParseSendCvarValue), + HOOK_DEF(0x01D14830, Server::ParseSendCvarValue2), + //HOOK_DEF(0x01D14740, Server::GetCmdName), // NOXREF + +#endif // Server_region + +#ifndef Network_region + + // Network virtual functions + HOOK_VIRTUAL_DEF(0x01D0E040, Network::Init), + HOOK_VIRTUAL_DEF(0x01D0E480, Network::RunFrame), + HOOK_VIRTUAL_DEF(0x01D0E5B0, Network::ReceiveSignal), + HOOK_VIRTUAL_DEF(0x01D0E210, Network::ExecuteCommand), + HOOK_VIRTUAL_DEF(0x01D0E5C0, Network::RegisterListener), + HOOK_VIRTUAL_DEF(0x01D0E670, Network::RemoveListener), + HOOK_VIRTUAL_DEF(0x01D0E680, Network::GetSystem), + HOOK_VIRTUAL_DEF(0x01D0E690, Network::GetSerial), + HOOK_VIRTUAL_DEF(0x01D0E4D0, Network::GetStatusLine), + HOOK_VIRTUAL_DEF(0x01D0E3B0, Network::GetType), + HOOK_VIRTUAL_DEF(0x01D0E6B0, Network::GetName), + HOOK_VIRTUAL_DEF(0x01D0E810, Network::GetState), + HOOK_VIRTUAL_DEF(0x01D0E820, Network::GetVersion), + HOOK_VIRTUAL_DEF(0x01D0E250, Network::ShutDown), + HOOK_VIRTUAL_DEF(0x01D0E2E0, Network::CreateSocket), + HOOK_VIRTUAL_DEF(0x01D0E3A0, Network::RemoveSocket), + HOOK_VIRTUAL_DEF(0x01D0E4C0, Network::GetLocalAddress), + HOOK_VIRTUAL_DEF(0x01D0E6C0, Network::ResolveAddress), + HOOK_VIRTUAL_DEF(0x01D0E530, Network::GetFlowStats), + HOOK_VIRTUAL_DEF(0x01D0E6A0, Network::GetLastErrorCode), + HOOK_VIRTUAL_DEF(0x01D0E830, Network::GetErrorText), + HOOK_VIRTUAL_DEF(0x01D0E2E0, Network::CreateSocket), + HOOK_VIRTUAL_DEF(0x01D0E3A0, Network::RemoveSocket), + HOOK_VIRTUAL_DEF(0x01D0E4C0, Network::GetLocalAddress), + HOOK_VIRTUAL_DEF(0x01D0E6C0, Network::ResolveAddress), + HOOK_VIRTUAL_DEF(0x01D0E530, Network::GetFlowStats), + HOOK_VIRTUAL_DEF(0x01D0E6A0, Network::GetLastErrorCode), + HOOK_VIRTUAL_DEF(0x01D0E830, Network::GetErrorText), + + // Network non-virtual functions + HOOK_DEF(0x01D0EAE0, Network::UpdateStats), + HOOK_DEF(0x01D0E440, Network::GetSocket), + HOOK_DEF(0x01D0E400, Network::SendData), + HOOK_DEF(0x01D0E3C0, Network::ReceiveData), + +#endif // Network_region + +#ifndef NetSocket_region + + // Network virtual functions + HOOK_VIRTUAL_DEF(0x01D0D940, NetSocket::ReceivePacket), + HOOK_VIRTUAL_DEF(0x01D0D950, NetSocket::FreePacket), + HOOK_VIRTUAL_DEF(0x01D0D980, NetSocket::AddPacket), + HOOK_VIRTUAL_DEF(0x01D0D000, NetSocket::AddChannel), + HOOK_VIRTUAL_DEF(0x01D0D010, NetSocket::RemoveChannel), + HOOK_VIRTUAL_DEF(0x01D0D310, NetSocket::GetNetwork), + HOOK_VIRTUAL_DEF(0x01D0D8D0, NetSocket::OutOfBandPrintf), + HOOK_VIRTUAL_DEF(0x01D0DA30, NetSocket::Flush), + HOOK_VIRTUAL_DEF(0x01D0D8A0, NetSocket::GetFlowStats), + HOOK_VIRTUAL_DEF(0x01D0D610, NetSocket::LeaveGroup), + HOOK_VIRTUAL_DEF(0x01D0D570, NetSocket::JoinGroup), + HOOK_VIRTUAL_DEF(0x01D0D500, NetSocket::Close), + HOOK_VIRTUAL_DEF(0x01D0DCB0, NetSocket::GetPort), + + HOOK_VIRTUAL_DEF(0x01D0D160, NetSocket::SendPacket, bool(NetPacket *)), + HOOK_VIRTUAL_DEF(0x01D0D190, NetSocket::SendPacket, bool(NetAddress *, const void *, int)), + + // Network non-virtual functions + HOOK_DEF(0x01D0DAC0, NetSocket::Create), + HOOK_DEF(0x01D0DCC0, NetSocket::UpdateStats), + HOOK_DEF(0x01D0D6B0, NetSocket::DrainChannels), + HOOK_DEF(0x01D0D020, NetSocket::DispatchIncoming), + HOOK_DEF(0x01D0D330, NetSocket::ReceivePacketIntern), + HOOK_DEF(0x01D0DD50, NetSocket::SendLong), + HOOK_DEF(0x01D0DE70, NetSocket::SendShort), + HOOK_DEF(0x01D0D730, NetSocket::GetLong), + +#endif // NetSocket_region + +#ifndef Delta_region + + HOOK_DEF(0x01D06740, DELTA_Init), + HOOK_DEF(0x01D06810, DELTA_UpdateDescriptions), + HOOK_DEF(0x01D06880, DELTA_Shutdown), + HOOK_DEF(0x01D06890, DELTA_SetTime), + HOOK_DEF(0x01D068B0, DELTA_SetLargeTimeBufferSize), + HOOK_DEF(0x01D04AF0, DELTA_FindField), + HOOK_DEF(0x01D04C10, DELTA_ClearFlags), + HOOK_DEF(0x01D04C40, DELTA_CountSendFields), + HOOK_DEF(0x01D04C80, DELTA_MarkSendFields), + HOOK_DEF(0x01D04E80, DELTA_SetSendFlagBits), + HOOK_DEF(0x01D04EF0, DELTA_WriteMarkedFields), + HOOK_DEF(0x01D052A0, DELTA_WriteHeader), + HOOK_DEF(0x01D053C0, DELTA_WriteDelta), + HOOK_DEF(0x01D05470, DELTA_ParseDelta), + HOOK_DEF(0x01D05A30, DELTA_TestDelta), + HOOK_DEF(0x01D05CA0, DELTA_AddEncoder), + HOOK_DEF(0x01D05CE0, DELTA_ClearEncoders), + HOOK_DEF(0x01D05D60, DELTA_CountLinks), + HOOK_DEF(0x01D05D80, DELTA_ReverseLinks), + HOOK_DEF(0x01D05DA0, DELTA_ClearLinks), + HOOK_DEF(0x01D05DD0, DELTA_BuildFromLinks), + HOOK_DEF(0x01D05E80, DELTA_FindOffset), + HOOK_DEF(0x01D05EC0, DELTA_ParseType), + HOOK_DEF(0x01D06050, DELTA_ParseField), + HOOK_DEF(0x01D06200, DELTA_FreeDescription), + HOOK_DEF(0x01D06240, DELTA_AddDefinition), + HOOK_DEF(0x01D062A0, DELTA_ClearDefinitions), + HOOK_DEF(0x01D062E0, DELTA_FindDefinition), + HOOK_DEF(0x01D06330, DELTA_SkipDescription), + HOOK_DEF(0x01D06380, DELTA_ParseOneField), + HOOK_DEF(0x01D06640, DELTA_RegisterDescription), + HOOK_DEF(0x01D06670, DELTA_ClearRegistrations), + HOOK_DEF(0x01D066C0, DELTA_LookupRegistration), + + //HOOK_DEF(0x01D06700, DELTA_ClearStats), // NOXREF + //HOOK_DEF(0x01D06430, DELTA_ParseDescription), // NOXREF + //HOOK_DEF(0x01D06630, DELTA_Load), // NOXREF + //HOOK_DEF(0x01D04B40, DELTA_FindFieldIndex), // NOXREF + //HOOK_DEF(0x01D04B90, DELTA_SetField), // NOXREF + //HOOK_DEF(0x01D04BB0, DELTA_UnsetField), // NOXREF + //HOOK_DEF(0x01D04BD0, DELTA_SetFieldByIndex), // NOXREF + //HOOK_DEF(0x01D04BF0, DELTA_UnsetFieldByIndex), // NOXREF + //HOOK_DEF(0x01D05270, DELTA_CheckDelta), // NOXREF + //HOOK_DEF(0x01D05D20, DELTA_LookupEncoder), // NOXREF + +#endif // Delta_region + +#ifndef ObjectDictionary_region + + HOOK_DEF(0x01D0ECA0, MethodThunk::Constructor), + HOOK_DEF(0x01D0ECE0, MethodThunk::Destructor), + + HOOK_VIRTUAL_DEF(0x01D0F220, ObjectDictionary::Init, void()), + HOOK_VIRTUAL_DEF(0x01D0F290, ObjectDictionary::Add, bool(void *)), + HOOK_VIRTUAL_DEF(0x01D0F050, ObjectDictionary::Remove), + HOOK_VIRTUAL_DEF(0x01D0ED10, ObjectDictionary::Clear), + HOOK_VIRTUAL_DEF(0x01D0F310, ObjectDictionary::GetFirst), + HOOK_VIRTUAL_DEF(0x01D0F5B0, ObjectDictionary::GetNext), + HOOK_VIRTUAL_DEF(0x01D0F2A0, ObjectDictionary::CountElements), + HOOK_VIRTUAL_DEF(0x01D0F2C0, ObjectDictionary::Contains), + HOOK_VIRTUAL_DEF(0x01D0F2B0, ObjectDictionary::IsEmpty), + + HOOK_DEF(0x01D0F250, ObjectDictionary::Init, void(int)), // NOXREF + HOOK_DEF(0x01D0ED90, ObjectDictionary::Add, bool(void *, float)), + HOOK_DEF(0x01D0F4D0, ObjectDictionary::AddToCache, void(ObjectDictionary::entry_t *, float)), + HOOK_DEF(0x01D0F490, ObjectDictionary::AddToCache, void(ObjectDictionary::entry_t *)), + HOOK_DEF(0x01D0F340, ObjectDictionary::ChangeKey), + HOOK_DEF(0x01D0F0D0, ObjectDictionary::RemoveKey), + HOOK_DEF(0x01D0F590, ObjectDictionary::FindClosestKey), + HOOK_DEF(0x01D0F5D0, ObjectDictionary::FindExactKey), + HOOK_DEF(0x01D0F320, ObjectDictionary::GetLast), + HOOK_DEF(0x01D0F510, ObjectDictionary::FindKeyInCache), + HOOK_DEF(0x01D0F550, ObjectDictionary::FindObjectInCache), + HOOK_DEF(0x01D0EF60, ObjectDictionary::ClearCache), + HOOK_DEF(0x01D0F130, ObjectDictionary::CheckSize), + HOOK_DEF(0x01D0EF80, ObjectDictionary::RemoveIndex), + HOOK_DEF(0x01D0F010, ObjectDictionary::RemoveIndexRange), + HOOK_DEF(0x01D0EE60, ObjectDictionary::FindClosestAsIndex), + //HOOK_DEF(0x0, ObjectDictionary::RemoveRange), // NOXREF + //HOOK_DEF(0x01D0F440, ObjectDictionary::UnsafeChangeKey), // NOXREF + //HOOK_DEF(0x01D0F090, ObjectDictionary::RemoveSingle), // NOXREF + +#endif // ObjectDictionary_region + +#ifndef ObjectList_region + + HOOK_DEF(0x01D0F610, MethodThunk::Constructor), + HOOK_DEF(0x01D0F650, MethodThunk::Destructor), + + HOOK_VIRTUAL_DEF(0x01D0F8D0, ObjectList::Init), + HOOK_VIRTUAL_DEF(0x01D0F920, ObjectList::Add), + HOOK_VIRTUAL_DEF(0x01D0F850, ObjectList::Remove), + HOOK_VIRTUAL_DEF(0x01D0F800, ObjectList::Clear), + HOOK_VIRTUAL_DEF(0x01D0F8E0, ObjectList::GetFirst), + HOOK_VIRTUAL_DEF(0x01D0F900, ObjectList::GetNext), + HOOK_VIRTUAL_DEF(0x01D0F7C0, ObjectList::CountElements), + HOOK_VIRTUAL_DEF(0x01D0F7D0, ObjectList::Contains), + HOOK_VIRTUAL_DEF(0x01D0F7B0, ObjectList::IsEmpty), + + HOOK_DEF(0x01D0F760, ObjectList::RemoveTail), + HOOK_DEF(0x01D0F6C0, ObjectList::RemoveHead), + HOOK_DEF(0x01D0F710, ObjectList::AddTail), + HOOK_DEF(0x01D0F670, ObjectList::AddHead), + +#endif // ObjectList_region + +#ifndef BitBuffer_region + + HOOK_DEF(0x01D015A0, MethodThunk::Destructor), + HOOK_DEF(0x01D01530, (MethodThunk::Constructor), void()), + HOOK_DEF(0x01D015B0, (MethodThunk::Constructor), void(unsigned int)), + HOOK_DEF(0x01D01570, (MethodThunk::Constructor), void(void *, unsigned int)), + + HOOK_DEF(0x01D015E0, BitBuffer::Resize), + HOOK_DEF(0x01D01630, BitBuffer::Clear), + HOOK_DEF(0x01D01670, BitBuffer::Reset), + HOOK_DEF(0x01D01690, BitBuffer::Free), + HOOK_DEF(0x01D01840, BitBuffer::PeekBits), + HOOK_DEF(0x01D01EF0, BitBuffer::CurrentSize), + HOOK_DEF(0x01D02300, BitBuffer::FastClear), + HOOK_DEF(0x01D02350, BitBuffer::ConcatBuffer), + HOOK_DEF(0x01D02210, BitBuffer::SkipBytes), + HOOK_DEF(0x01D01660, BitBuffer::CurrentBit), + HOOK_DEF(0x01D01F10, BitBuffer::SpaceLeft), + HOOK_DEF(0x01D01F20, BitBuffer::AlignByte), + HOOK_DEF(0x01D02090, BitBuffer::StartBitMode), + HOOK_DEF(0x01D020A0, BitBuffer::EndBitMode), + HOOK_DEF(0x01D020E0, BitBuffer::SetBuffer), + HOOK_DEF(0x01D02240, BitBuffer::SkipBits), + HOOK_DEF(0x01D022D0, BitBuffer::SkipString), + + // Read + HOOK_DEF(0x01D016D0, BitBuffer::ReadBits), + HOOK_DEF(0x01D017B0, BitBuffer::ReadBit), + HOOK_DEF(0x01D01870, BitBuffer::ReadChar), + HOOK_DEF(0x01D01880, BitBuffer::ReadByte), + HOOK_DEF(0x01D01890, BitBuffer::ReadShort), + HOOK_DEF(0x01D018A0, BitBuffer::ReadWord), + HOOK_DEF(0x01D018B0, BitBuffer::ReadLong), + HOOK_DEF(0x01D018C0, BitBuffer::ReadFloat), + HOOK_DEF(0x01D018E0, BitBuffer::ReadBuf), + HOOK_DEF(0x01D019C0, BitBuffer::ReadString), + HOOK_DEF(0x01D01A00, BitBuffer::ReadStringLine), // NOXREF + HOOK_DEF(0x01D02020, BitBuffer::ReadBitString), + HOOK_DEF(0x01D020B0, BitBuffer::ReadBitData), + HOOK_DEF(0x01D02110, BitBuffer::ReadBitVec3Coord), + HOOK_DEF(0x01D02170, BitBuffer::ReadBitCoord), + HOOK_DEF(0x01D021F0, BitBuffer::ReadCoord), + HOOK_DEF(0x01D01A40, BitBuffer::ReadAngle), // NOXREF + HOOK_DEF(0x01D01A60, BitBuffer::ReadHiresAngle), // NOXREF + HOOK_DEF(0x01D01F40, BitBuffer::ReadSBits), + HOOK_DEF(0x01D01F70, BitBuffer::ReadBitAngle), + + // Write + HOOK_DEF(0x01D01D90, BitBuffer::WriteBuf, void(const void *, int)), + HOOK_DEF(0x01D01990, BitBuffer::WriteBuf, void(BitBuffer *, int)), + + HOOK_DEF(0x01D01A80, BitBuffer::WriteBit), + HOOK_DEF(0x01D01B50, BitBuffer::WriteBits), + HOOK_DEF(0x01D01C70, BitBuffer::WriteSBits), + HOOK_DEF(0x01D01CD0, BitBuffer::WriteChar), + HOOK_DEF(0x01D01CE0, BitBuffer::WriteByte), + HOOK_DEF(0x01D01CF0, BitBuffer::WriteShort), + HOOK_DEF(0x01D01D00, BitBuffer::WriteWord), + HOOK_DEF(0x01D01D10, BitBuffer::WriteLong), + HOOK_DEF(0x01D01D20, BitBuffer::WriteFloat), + HOOK_DEF(0x01D01D50, BitBuffer::WriteString), + HOOK_DEF(0x01D02370, BitBuffer::WriteCoord), + HOOK_DEF(0x01D01E60, BitBuffer::WriteBitData), + HOOK_DEF(0x01D01E90, BitBuffer::WriteAngle), // NOXREF + HOOK_DEF(0x01D01EC0, BitBuffer::WriteHiresAngle), // NOXREF + HOOK_DEF(0x01D01FB0, BitBuffer::WriteBitAngle), + HOOK_DEF(0x01D02050, BitBuffer::WriteBitString), + +#endif // BitBuffer_region + + { NULL, NULL, NULL }, +}; + +AddressRef g_FunctionRefs[] = +{ +#ifndef Function_References_region + +#endif // Function_References_region + + { NULL, NULL, NULL }, +}; + +AddressRef g_DataRefs[] = +{ +#ifndef Data_References_region + + GLOBALVAR_LINK(0x01D45708, "_ZL6g_defs", pg_defs), + GLOBALVAR_LINK(0x01D4570C, "_ZL10g_encoders", pg_encoders), + GLOBALVAR_LINK(0x01D45710, "g_deltaregistry", pg_deltaregistry), + GLOBALVAR_LINK(0x01D456F0, "g_pplayerdelta", pg_pplayerdelta), + GLOBALVAR_LINK(0x01D456F4, "g_pentitydelta", pg_pentitydelta), + GLOBALVAR_LINK(0x01D456F8, "g_pcustomentitydelta", pg_pcustomentitydelta), + GLOBALVAR_LINK(0x01D456FC, "g_pclientdelta", pg_pclientdelta), + GLOBALVAR_LINK(0x01D45700, "g_pweapondelta", pg_pweapondelta), + GLOBALVAR_LINK(0x01D45704, "g_peventdelta", pg_peventdelta), + GLOBALVAR_LINK(0x01D456E8, "g_delta_Time", pg_delta_Time), + GLOBALVAR_LINK(0x01D456E0, "g_large_Time_Buffers", pg_large_Time_Buffers), + GLOBALVAR_LINK(0x01D46870, "g_DownloadURL", pg_DownloadURL), + + GLOBALVAR_LINK(0x01D442C8, "com_token", pcom_token), + GLOBALVAR_LINK(0x01D456C8, "s_com_token_unget", ps_com_token_unget), + +#endif // Data_References_region + + { NULL, NULL, NULL }, +}; + +#endif // HOOK_HLTV diff --git a/rehlds/hookers/HLTV/Core/hooklist.h b/rehlds/hookers/HLTV/Core/hooklist.h new file mode 100644 index 0000000..fcdf32b --- /dev/null +++ b/rehlds/hookers/HLTV/Core/hooklist.h @@ -0,0 +1,18 @@ +#pragma once + +#ifdef HOOK_HLTV + +#include "hookers/memory.h" +#include "hookers/helper.h" +#include "hookers/hooker.h" + +#define rehlds_syserror HLTV_SysError + +#define com_token (*pcom_token) +#define s_com_token_unget (*ps_com_token_unget) +#define g_DownloadURL (*pg_DownloadURL) + +extern char com_token[COM_TOKEN_LEN]; +extern qboolean s_com_token_unget; + +#endif // HOOK_HLTV diff --git a/rehlds/hookers/HLTV/Core/main.cpp b/rehlds/hookers/HLTV/Core/main.cpp new file mode 100644 index 0000000..7c4b3a8 --- /dev/null +++ b/rehlds/hookers/HLTV/Core/main.cpp @@ -0,0 +1,102 @@ +#include "precompiled.h" + +#if defined(HOOK_HLTV) +#define ORIGINAL_CORE_DLL_NAME "core2.dll" + +CSysModule *g_pOriginalCoreModule = NULL; +CreateInterfaceFn g_OriginalCoreFactory = NULL; +ISystemModule *g_pOriginalServer = NULL; +ISystemModule *g_pOriginalWorld = NULL; +ISystemModule *g_pOriginalNetwork = NULL; + +IBaseInterface *CreateCoreInterface() +{ + if (g_pOriginalServer) { + return g_pOriginalServer; + } + + if (g_pOriginalCoreModule) + { + g_OriginalCoreFactory = Sys_GetFactory(g_pOriginalCoreModule); + if (g_OriginalCoreFactory) + { + int returnCode = 0; + g_pOriginalServer = reinterpret_cast(g_OriginalCoreFactory(SERVER_INTERFACE_VERSION, &returnCode)); + return g_pOriginalServer; + } + } + + return NULL; +} + +IBaseInterface *CreateWorldInterface() +{ + if (g_pOriginalWorld) { + return g_pOriginalWorld; + } + + if (g_pOriginalCoreModule) + { + g_OriginalCoreFactory = Sys_GetFactory(g_pOriginalCoreModule); + if (g_OriginalCoreFactory) + { + int returnCode = 0; + g_pOriginalWorld = reinterpret_cast(g_OriginalCoreFactory(WORLD_INTERFACE_VERSION, &returnCode)); + return g_pOriginalWorld; + } + } + + return NULL; +} + +IBaseInterface *CreateNetworkInterface() +{ + if (g_pOriginalNetwork) { + return g_pOriginalNetwork; + } + + if (g_pOriginalCoreModule) + { + g_OriginalCoreFactory = Sys_GetFactory(g_pOriginalCoreModule); + if (g_OriginalCoreFactory) + { + int returnCode = 0; + g_pOriginalNetwork = reinterpret_cast(g_OriginalCoreFactory(NETWORK_INTERFACE_VERSION, &returnCode)); + return g_pOriginalNetwork; + } + } + + return NULL; +} + +InterfaceReg iface_Server = InterfaceReg(CreateCoreInterface, SERVER_INTERFACE_VERSION); +InterfaceReg iface_World = InterfaceReg(CreateWorldInterface, WORLD_INTERFACE_VERSION); +InterfaceReg iface_Network= InterfaceReg(CreateNetworkInterface, NETWORK_INTERFACE_VERSION); + +// DLL entry point +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + if (fdwReason == DLL_PROCESS_ATTACH) + { + g_pOriginalCoreModule = Sys_LoadModule(ORIGINAL_CORE_DLL_NAME); + size_t addr = (size_t)Sys_GetProcAddress(ORIGINAL_CORE_DLL_NAME, CREATEINTERFACE_PROCNAME); + HookModule("hltv.exe", addr); + } + else if (fdwReason == DLL_PROCESS_DETACH) + { + if (g_pOriginalCoreModule) + { + Sys_UnloadModule(g_pOriginalCoreModule); + g_pOriginalCoreModule = NULL; + g_OriginalCoreFactory = NULL; + + g_pOriginalServer = NULL; + g_pOriginalWorld = NULL; + g_pOriginalNetwork = NULL; + } + } + + return TRUE; +} + +#endif // #if defined(HOOK_HLTV) diff --git a/rehlds/hookers/HLTV/DemoPlayer/hooklist.cpp b/rehlds/hookers/HLTV/DemoPlayer/hooklist.cpp new file mode 100644 index 0000000..ad0011e --- /dev/null +++ b/rehlds/hookers/HLTV/DemoPlayer/hooklist.cpp @@ -0,0 +1,152 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +// Hooks stuff +#include "hookers/memory.cpp" +#include "hookers/hooker.cpp" + +//#define Mem_region +//#define Function_References_region +//#define Data_References_region + +FunctionHook g_FunctionHooks[] = +{ + // DO NOT DISABLE, other functions depends on memory allocation routines +#ifndef Mem_region + + HOOK_DEF(0x01D07C0F, malloc_wrapper), + HOOK_DEF(0x01D07AFE, free_wrapper), + HOOK_DEF(0x01D14F4D, strdup_wrapper), + HOOK_DEF(0x01D07C21, __nh_malloc_wrapper), + + HOOK_DEF(0x01D02510, Mem_ZeroMalloc), + //HOOK_DEF(0x0, Mem_Malloc), + //HOOK_DEF(0x0, Mem_Realloc), + //HOOK_DEF(0x0, Mem_Calloc), + //HOOK_DEF(0x0, Mem_Strdup), + //HOOK_DEF(0x0, Mem_Free), + + //HOOK_DEF(0x0, realloc_wrapper), + //HOOK_DEF(0x0, calloc_wrapper), + +#endif // Mem_region + +#ifndef DemoPlayer_Region + + // BaseSystemModule virtual function + //HOOK_VIRTUAL_DEF(0x01D032A0, DemoPlayer::RegisterListener), + //HOOK_VIRTUAL_DEF(0x01D03360, DemoPlayer::RemoveListener), + //HOOK_VIRTUAL_DEF(0x1D033700, DemoPlayer::GetSystem), + //HOOK_VIRTUAL_DEF(0x01D03380, DemoPlayer::GetSerial), + //HOOK_VIRTUAL_DEF(0x01D03390, DemoPlayer::GetName), + //HOOK_VIRTUAL_DEF(0x01D033A0, DemoPlayer::GetState), + //HOOK_VIRTUAL_DEF(0x01D033F0, DemoPlayer::GetVersion), + + // DemoPlayer virtual function + HOOK_VIRTUAL_DEF(0x01D03670, DemoPlayer::Init), + HOOK_VIRTUAL_DEF(0x01D04180, DemoPlayer::RunFrame), + HOOK_VIRTUAL_DEF(0x01D042E0, DemoPlayer::ReceiveSignal), + HOOK_VIRTUAL_DEF(0x01D02FA0, DemoPlayer::ExecuteCommand), + HOOK_VIRTUAL_DEF(0x01D02F70, DemoPlayer::GetStatusLine), + HOOK_VIRTUAL_DEF(0x01D02F60, DemoPlayer::GetType), + HOOK_VIRTUAL_DEF(0x01D04490, DemoPlayer::ShutDown), + HOOK_VIRTUAL_DEF(0x01D04400, DemoPlayer::NewGame), + HOOK_VIRTUAL_DEF(0x01D03410, DemoPlayer::GetModName), + HOOK_VIRTUAL_DEF(0x01D03420, DemoPlayer::WriteCommands), + HOOK_VIRTUAL_DEF(0x01D03570, DemoPlayer::AddCommand), + HOOK_VIRTUAL_DEF(0x01D035F0, DemoPlayer::RemoveCommand), + HOOK_VIRTUAL_DEF(0x01D04640, DemoPlayer::GetLastCommand), + HOOK_VIRTUAL_DEF(0x01D03660, DemoPlayer::GetCommands), + HOOK_VIRTUAL_DEF(0x01D03900, DemoPlayer::SetWorldTime), + HOOK_VIRTUAL_DEF(0x01D03940, DemoPlayer::SetTimeScale), + HOOK_VIRTUAL_DEF(0x01D039B0, DemoPlayer::SetPaused), + HOOK_VIRTUAL_DEF(0x01D039C0, DemoPlayer::SetEditMode), + HOOK_VIRTUAL_DEF(0x01D03A20, DemoPlayer::SetMasterMode), + HOOK_VIRTUAL_DEF(0x01D03A40, DemoPlayer::IsPaused), + HOOK_VIRTUAL_DEF(0x01D02F30, DemoPlayer::IsLoading), + HOOK_VIRTUAL_DEF(0x01D02F50, DemoPlayer::IsActive), + HOOK_VIRTUAL_DEF(0x01D039D0, DemoPlayer::IsEditMode), + HOOK_VIRTUAL_DEF(0x01D03A30, DemoPlayer::IsMasterMode), + HOOK_VIRTUAL_DEF(0x01D04560, DemoPlayer::RemoveFrames), + HOOK_VIRTUAL_DEF(0x01D04570, DemoPlayer::ExecuteDirectorCmd), + HOOK_VIRTUAL_DEF(0x01D02ED0, DemoPlayer::GetWorldTime), + HOOK_VIRTUAL_DEF(0x01D02EE0, DemoPlayer::GetStartTime), + HOOK_VIRTUAL_DEF(0x01D02F00, DemoPlayer::GetEndTime), + HOOK_VIRTUAL_DEF(0x01D03400, DemoPlayer::GetTimeScale), + HOOK_VIRTUAL_DEF(0x01D02F80, DemoPlayer::GetWorld), + HOOK_VIRTUAL_DEF(0x01D04630, DemoPlayer::GetFileName), + HOOK_VIRTUAL_DEF(0x01D02E60, DemoPlayer::SaveGame), + HOOK_VIRTUAL_DEF(0x01D04650, DemoPlayer::LoadGame), + HOOK_VIRTUAL_DEF(0x01D039E0, DemoPlayer::Stop), + HOOK_VIRTUAL_DEF(0x01D03A10, DemoPlayer::ForceHLTV), + HOOK_VIRTUAL_DEF(0x01D03A50, DemoPlayer::GetDemoViewInfo), + HOOK_VIRTUAL_DEF(0x01D03F90, DemoPlayer::ReadDemoMessage), + HOOK_VIRTUAL_DEF(0x01D04130, DemoPlayer::ReadNetchanState), + HOOK_VIRTUAL_DEF(0x01D02F90, DemoPlayer::GetDirector), // NOXREF + + // DemoPlayer non-virtual function + //HOOK_DEF(0x01D02F20, DemoPlayer::GetPlayerTime), // NOXREF + //HOOK_DEF(0x01D04500, DemoPlayer::FormatTime), // NOXREF + HOOK_DEF(0x01D041B0, DemoPlayer::RunClocks), + HOOK_DEF(0x01D046F0, DemoPlayer::WriteDatagram), + HOOK_DEF(0x01D03D30, DemoPlayer::WriteSpawn), + HOOK_DEF(0x01D03F50, DemoPlayer::ReindexCommands), + HOOK_DEF(0x01D03DC0, DemoPlayer::WriteCameraPath), + HOOK_DEF(0x01D04840, DemoPlayer::ExecuteDemoFileCommands), + HOOK_DEF(0x01D03030, DemoPlayer::CMD_Jump), + HOOK_DEF(0x01D030E0, DemoPlayer::CMD_ForceHLTV), + HOOK_DEF(0x01D031F0, DemoPlayer::CMD_Pause), + HOOK_DEF(0x01D032B0, DemoPlayer::CMD_Speed), + HOOK_DEF(0x01D033B0, DemoPlayer::CMD_Start), + HOOK_DEF(0x01D03170, DemoPlayer::CMD_Save), + +#endif // DemoPlayer_Region + + { NULL, NULL, NULL }, +}; + +AddressRef g_FunctionRefs[] = +{ +#ifndef Function_References_region + +#endif // Function_References_region + + { NULL, NULL, NULL }, +}; + +AddressRef g_DataRefs[] = +{ +#ifndef Data_References_region + + +#endif // Data_References_region + + { NULL, NULL, NULL }, +}; diff --git a/rehlds/hookers/HLTV/DemoPlayer/hooklist.h b/rehlds/hookers/HLTV/DemoPlayer/hooklist.h new file mode 100644 index 0000000..d030ec9 --- /dev/null +++ b/rehlds/hookers/HLTV/DemoPlayer/hooklist.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef HOOK_HLTV + +#include "hookers/memory.h" +#include "hookers/helper.h" +#include "hookers/hooker.h" + +#define rehlds_syserror HLTV_SysError + +#endif // HOOK_HLTV diff --git a/rehlds/hookers/HLTV/DemoPlayer/main.cpp b/rehlds/hookers/HLTV/DemoPlayer/main.cpp new file mode 100644 index 0000000..0837c3c --- /dev/null +++ b/rehlds/hookers/HLTV/DemoPlayer/main.cpp @@ -0,0 +1,55 @@ +#include "precompiled.h" + +#if defined(HOOK_HLTV) +#define ORIGINAL_DEMOPLAYER_DLL_NAME "DemoPlayer2.dll" + +CSysModule *g_pOriginalDemoPlayerModule = NULL; +CreateInterfaceFn g_OriginalDemoPlayerFactory = NULL; +ISystemModule *g_pOriginalDemoPlayer = NULL; + +IBaseInterface *CreateDemoPlayerInterface() +{ + if (g_pOriginalDemoPlayer) { + return g_pOriginalDemoPlayer; + } + + if (g_pOriginalDemoPlayerModule) + { + g_OriginalDemoPlayerFactory = Sys_GetFactory(g_pOriginalDemoPlayerModule); + if (g_OriginalDemoPlayerFactory) + { + int returnCode = 0; + g_pOriginalDemoPlayer = reinterpret_cast(g_OriginalDemoPlayerFactory(DEMOPLAYER_INTERFACE_VERSION, &returnCode)); + return g_pOriginalDemoPlayer; + } + } + + return NULL; +} + +InterfaceReg iface = InterfaceReg(CreateDemoPlayerInterface, DEMOPLAYER_INTERFACE_VERSION); + +// DLL entry point +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + if (fdwReason == DLL_PROCESS_ATTACH) + { + g_pOriginalDemoPlayerModule = Sys_LoadModule(ORIGINAL_DEMOPLAYER_DLL_NAME); + size_t addr = (size_t)Sys_GetProcAddress(ORIGINAL_DEMOPLAYER_DLL_NAME, CREATEINTERFACE_PROCNAME); + HookModule("hltv.exe", addr); + } + else if (fdwReason == DLL_PROCESS_DETACH) + { + if (g_pOriginalDemoPlayerModule) + { + Sys_UnloadModule(g_pOriginalDemoPlayerModule); + g_pOriginalDemoPlayerModule = NULL; + g_OriginalDemoPlayerFactory = NULL; + g_pOriginalDemoPlayer = NULL; + } + } + + return TRUE; +} + +#endif // #if defined(HOOK_HLTV) diff --git a/rehlds/hookers/HLTV/Proxy/hooklist.cpp b/rehlds/hookers/HLTV/Proxy/hooklist.cpp new file mode 100644 index 0000000..2fb1ca5 --- /dev/null +++ b/rehlds/hookers/HLTV/Proxy/hooklist.cpp @@ -0,0 +1,427 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#include "precompiled.h" + +#ifdef HOOK_HLTV + +// Hooks stuff +#include "hookers/memory.cpp" +#include "hookers/hooker.cpp" + +//#define Mem_region +//#define Proxy_region +//#define BaseClient_region +//#define BitBuffer_region +//#define NetChannel_region +//#define Master_region +//#define Function_References_region +//#define Data_References_region + +FunctionHook g_FunctionHooks[] = +{ + // DO NOT DISABLE, other functions depends on memory allocation routines +#ifndef Mem_region + + HOOK_DEF(0x01D20F3F, malloc_wrapper), + HOOK_DEF(0x01D20E2E, free_wrapper), + HOOK_DEF(0x01D3078C, strdup_wrapper), + HOOK_DEF(0x01D20F51, __nh_malloc_wrapper), + + HOOK_DEF(0x01D03EF0, Mem_ZeroMalloc), + //HOOK_DEF(0x0, Mem_Malloc), + //HOOK_DEF(0x0, Mem_Realloc), + //HOOK_DEF(0x0, Mem_Calloc), + //HOOK_DEF(0x0, Mem_Strdup), + //HOOK_DEF(0x0, Mem_Free), + + //HOOK_DEF(0x0, realloc_wrapper), + //HOOK_DEF(0x0, calloc_wrapper), + +#endif // Mem_region + +#ifndef Proxy_region + + // virtual functions - BaseSystemModule + HOOK_VIRTUAL_DEF(0x01D16010, Proxy::Init), + HOOK_VIRTUAL_DEF(0x01D16020, Proxy::RunFrame), + HOOK_VIRTUAL_DEF(0x01D16030, Proxy::ReceiveSignal), + HOOK_VIRTUAL_DEF(0x01D16040, Proxy::ExecuteCommand), + HOOK_VIRTUAL_DEF(0x01D16090, Proxy::GetStatusLine), + HOOK_VIRTUAL_DEF(0x01D160A0, Proxy::GetType), + HOOK_VIRTUAL_DEF(0x01D160E0, Proxy::ShutDown), + + // virtual functions - Proxy + HOOK_VIRTUAL_DEF(0x01D115D0, Proxy::Reset), + HOOK_VIRTUAL_DEF(0x01D11620, Proxy::Broadcast), + HOOK_VIRTUAL_DEF(0x01D14FE0, Proxy::IncreaseCheering), + HOOK_VIRTUAL_DEF(0x01D11600, Proxy::ParseStatusMsg), + HOOK_VIRTUAL_DEF(0x01D11610, Proxy::ParseStatusReport), + HOOK_VIRTUAL_DEF(0x01D12FF0, Proxy::ProcessConnectionlessMessage), + HOOK_VIRTUAL_DEF(0x01D14780, Proxy::ChatCommentator), + HOOK_VIRTUAL_DEF(0x01D137B0, Proxy::ChatSpectator), + HOOK_VIRTUAL_DEF(0x01D11370, Proxy::CountLocalClients), + HOOK_VIRTUAL_DEF(0x01D143B0, Proxy::AddResource), + HOOK_VIRTUAL_DEF(0x01D11570, Proxy::IsLanOnly), + HOOK_VIRTUAL_DEF(0x01D13A00, Proxy::IsMaster), + HOOK_VIRTUAL_DEF(0x01D11580, Proxy::IsActive), + HOOK_VIRTUAL_DEF(0x01D114F0, Proxy::IsPublicGame), + HOOK_VIRTUAL_DEF(0x01D11500, Proxy::IsPasswordProtected), + HOOK_VIRTUAL_DEF(0x01D11510, Proxy::IsStressed), + HOOK_VIRTUAL_DEF(0x01D154A0, Proxy::SetDelay), + HOOK_VIRTUAL_DEF(0x01D15540, Proxy::SetClientTime), + HOOK_VIRTUAL_DEF(0x01D15580, Proxy::SetClientTimeScale), + HOOK_VIRTUAL_DEF(0x01D15430, Proxy::SetMaxRate), + HOOK_VIRTUAL_DEF(0x01D118D0, Proxy::SetMaxLoss), + HOOK_VIRTUAL_DEF(0x01D15470, Proxy::SetMaxUpdateRate), + HOOK_VIRTUAL_DEF(0x01D11870, Proxy::SetMaxClients), + HOOK_VIRTUAL_DEF(0x01D11930, Proxy::SetRegion), + HOOK_VIRTUAL_DEF(0x01D14FA0, Proxy::GetDelay), + HOOK_VIRTUAL_DEF(0x01D14FC0, Proxy::GetSpectatorTime), + HOOK_VIRTUAL_DEF(0x01D14FD0, Proxy::GetProxyTime), + HOOK_VIRTUAL_DEF(0x01D11920, Proxy::GetMaxClients), + HOOK_VIRTUAL_DEF(0x01D10EF0, Proxy::GetWorld), + HOOK_VIRTUAL_DEF(0x01D10F00, Proxy::GetServer), + HOOK_VIRTUAL_DEF(0x01D10F10, Proxy::GetDirector), + HOOK_VIRTUAL_DEF(0x01D10F20, Proxy::GetSocket), + HOOK_VIRTUAL_DEF(0x01D10F30, Proxy::GetChatMode), + HOOK_VIRTUAL_DEF(0x01D113C0, Proxy::GetStatistics), + HOOK_VIRTUAL_DEF(0x01D113D0, Proxy::GetMaxRate), + HOOK_VIRTUAL_DEF(0x01D113E0, Proxy::GetMaxUpdateRate), + HOOK_VIRTUAL_DEF(0x01D14420, Proxy::GetResource), + HOOK_VIRTUAL_DEF(0x01D11550, Proxy::GetDispatchMode), + HOOK_VIRTUAL_DEF(0x01D115A0, Proxy::GetRegion), + HOOK_VIRTUAL_DEF(0x01D10EE0, Proxy::GetClients), + HOOK_VIRTUAL_DEF(0x01D113F0, Proxy::WriteSignonData), + + // non-virtual functions - Proxy + HOOK_DEF(0x01D12BD0, Proxy::ReplyServiceChallenge), + HOOK_DEF(0x01D115B0, Proxy::ReplyListen), + HOOK_DEF(0x01D10900, Proxy::ReplyConnect), + HOOK_DEF(0x01D10890, Proxy::ReplyRules), + HOOK_DEF(0x01D10820, Proxy::ReplyPlayers), + HOOK_DEF(0x01D10770, Proxy::ReplyInfo), + HOOK_DEF(0x01D10720, Proxy::ReplyInfoString), + HOOK_DEF(0x01D106D0, Proxy::ReplyChallenge), + HOOK_DEF(0x01D105F0, Proxy::ReplyPing), + HOOK_DEF(0x01D12F50, Proxy::ExecuteRcon), + HOOK_DEF(0x01D13B70, Proxy::ReconnectClients), + HOOK_DEF(0x01D129A0, Proxy::SendRcon), + HOOK_DEF(0x01D10DD0, Proxy::RejectConnection), + HOOK_DEF(0x01D10F40, Proxy::UpdateStatusLine), + HOOK_DEF(0x01D14220, Proxy::DispatchClient), + HOOK_DEF(0x01D13FA0, Proxy::IsValidPassword), + HOOK_DEF(0x01D13E50, Proxy::WriteHUDMsg), + HOOK_DEF(0x01D150D0, Proxy::ExecuteLoopCommands), + HOOK_DEF(0x01D12A30, Proxy::GetChallengeNumber), + HOOK_DEF(0x01D12B60, Proxy::CheckChallenge), + HOOK_DEF(0x01D15150, Proxy::CreateServerInfoString), + HOOK_DEF(0x01D13A10, Proxy::CheckDirectorModule), + HOOK_DEF(0x01D158B0, Proxy::RunClocks), + HOOK_DEF(0x01D156B0, Proxy::NewGameStarted), + HOOK_DEF(0x01D12CC0, Proxy::NewServerConnection), + HOOK_DEF(0x01D15620, Proxy::BroadcastPaused), + HOOK_DEF(0x01D12E80, Proxy::BroadcastRetryMessage), + HOOK_DEF(0x01D12EE0, Proxy::StopBroadcast), + HOOK_DEF(0x01D15A10, Proxy::DisconnectClients), + HOOK_DEF(0x01D14350, Proxy::FreeResource), + HOOK_DEF(0x01D14380, Proxy::ClearResources), + HOOK_DEF(0x01D142C0, Proxy::LoadResourceFromFile), + HOOK_DEF(0x01D14800, Proxy::IsBanned), + HOOK_DEF(0x01D15A60, Proxy::UpdateInfoMessages), + + HOOK_DEF(0x01D12860, Proxy::CMD_Rcon), + HOOK_DEF(0x01D11EF0, Proxy::CMD_ServerCmd), + HOOK_DEF(0x01D11F90, Proxy::CMD_ClientCmd), + HOOK_DEF(0x01D127D0, Proxy::CMD_RconPassword), + HOOK_DEF(0x01D12700, Proxy::CMD_RconAddress), + HOOK_DEF(0x01D11700, Proxy::CMD_Say), + HOOK_DEF(0x01D11C10, Proxy::CMD_Msg), + HOOK_DEF(0x01D10D40, Proxy::CMD_Clients), + HOOK_DEF(0x01D13920, Proxy::CMD_Kick), + HOOK_DEF(0x01D13650, Proxy::CMD_ChatMode), + HOOK_DEF(0x01D13DA0, Proxy::CMD_PublicGame), + HOOK_DEF(0x01D13BB0, Proxy::CMD_OffLineText), + HOOK_DEF(0x01D14550, Proxy::CMD_AdminPassword), + HOOK_DEF(0x01D14470, Proxy::CMD_SignOnCommands), + HOOK_DEF(0x01D13EE0, Proxy::CMD_SpectatorPassword), + HOOK_DEF(0x01D140B0, Proxy::CMD_DispatchMode), + HOOK_DEF(0x01D14F00, Proxy::CMD_CheeringThreshold), + HOOK_DEF(0x01D10610, Proxy::CMD_Ping), + HOOK_DEF(0x01D12C00, Proxy::CMD_ProxyPassword), + HOOK_DEF(0x01D11E50, Proxy::CMD_MaxRate), + HOOK_DEF(0x01D12570, Proxy::CMD_LoopCmd), + HOOK_DEF(0x01D117C0, Proxy::CMD_MaxClients), + HOOK_DEF(0x01D14610, Proxy::CMD_LocalMsg), + HOOK_DEF(0x01D11A50, Proxy::CMD_Connect), + HOOK_DEF(0x01D139F0, Proxy::CMD_Disconnect), + HOOK_DEF(0x01D14180, Proxy::CMD_PlayDemo), + HOOK_DEF(0x01D11940, Proxy::CMD_Delay), + HOOK_DEF(0x01D119E0, Proxy::CMD_Stop), + HOOK_DEF(0x01D12480, Proxy::CMD_Record), + HOOK_DEF(0x01D12530, Proxy::CMD_StopRecording), + HOOK_DEF(0x01D123C0, Proxy::CMD_BlockVoice), + HOOK_DEF(0x01D11B20, Proxy::CMD_Name), + HOOK_DEF(0x01D120B0, Proxy::CMD_Rate), + HOOK_DEF(0x01D12300, Proxy::CMD_Updaterate), + HOOK_DEF(0x01D12220, Proxy::CMD_HostName), + HOOK_DEF(0x01D13CC0, Proxy::CMD_AddResource), + HOOK_DEF(0x01D13C60, Proxy::CMD_Resources), + HOOK_DEF(0x01D14DD0, Proxy::CMD_BannerFile), + HOOK_DEF(0x01D14AF0, Proxy::CMD_Bann), + HOOK_DEF(0x01D14850, Proxy::CMD_AddFakeClients), + HOOK_DEF(0x01D14CD0, Proxy::CMD_Retry), + HOOK_DEF(0x01D14D00, Proxy::CMD_AutoRetry), + HOOK_DEF(0x01D14C40, Proxy::CMD_ServerPassword), + HOOK_DEF(0x01D11040, Proxy::CMD_Status), + HOOK_DEF(0x01D13710, Proxy::CMD_MaxQueries), + HOOK_DEF(0x01D12170, Proxy::CMD_Players), + HOOK_DEF(0x01D14C00, Proxy::CMD_ClearBanns), + HOOK_DEF(0x01D149B0, Proxy::CMD_MaxLoss), + HOOK_DEF(0x01D11D80, Proxy::CMD_Protocol), + HOOK_DEF(0x01D14A50, Proxy::CMD_Region), + + //HOOK_DEF(0x01D12E70, Proxy::GetModVersion), // NOXREF + //HOOK_DEF(0x0, Proxy::CMD_InformPlayers), // NOXREF + //HOOK_DEF(0x0, Proxy::CMD_MaxUpdateRate), // NOXREF + +#endif // Proxy_region + +#ifndef BaseClient_region + + // IClient + HOOK_VIRTUAL_DEF(0x01D01670, BaseClient::Connect), + HOOK_VIRTUAL_DEF(0x01D02790, BaseClient::Send), + HOOK_VIRTUAL_DEF(0x01D01830, BaseClient::Disconnect), + HOOK_VIRTUAL_DEF(0x01D023F0, BaseClient::Reconnect), + HOOK_VIRTUAL_DEF(0x01D023C0, BaseClient::SetWorld), + HOOK_VIRTUAL_DEF(0x01D027D0, BaseClient::GetClientType), + HOOK_VIRTUAL_DEF(0x01D01810, BaseClient::GetClientName), + HOOK_VIRTUAL_DEF(0x01D01820, BaseClient::GetUserInfo), + HOOK_VIRTUAL_DEF(0x01D027C0, BaseClient::GetAddress), + HOOK_VIRTUAL_DEF(0x01D02420, BaseClient::IsActive), + HOOK_VIRTUAL_DEF(0x01D026F0, BaseClient::IsHearingVoices), + HOOK_VIRTUAL_DEF(0x01D02730, BaseClient::HasChatEnabled), + HOOK_VIRTUAL_DEF(0x01D02760, BaseClient::DownloadFailed), + HOOK_VIRTUAL_DEF(0x01D02740, BaseClient::DownloadFile), + HOOK_VIRTUAL_DEF(0x01D025C0, BaseClient::UpdateVoiceMask), + HOOK_VIRTUAL_DEF(0x01D02580, BaseClient::QueryVoiceEnabled), + HOOK_VIRTUAL_DEF(0x01D02470, BaseClient::SetName), + HOOK_VIRTUAL_DEF(0x01D02100, BaseClient::WriteSpawn), + HOOK_VIRTUAL_DEF(0x01D02180, BaseClient::WriteDatagram), + HOOK_VIRTUAL_DEF(0x01D027F0, BaseClient::SendDatagram), + HOOK_VIRTUAL_DEF(0x01D02820, BaseClient::Reset), + HOOK_VIRTUAL_DEF(0x01D02020, BaseClient::SetState), + HOOK_VIRTUAL_DEF(0x01D01F20, BaseClient::ReplyNew), + HOOK_VIRTUAL_DEF(0x01D01E50, BaseClient::ReplySpawn), + HOOK_VIRTUAL_DEF(0x01D01F90, BaseClient::ReplyFullUpdate), + HOOK_VIRTUAL_DEF(0x01D01E10, BaseClient::PrintfToClient), + HOOK_VIRTUAL_DEF(0x01D01D00, BaseClient::UpdateUserInfo), + HOOK_VIRTUAL_DEF(0x01D01900, BaseClient::ParseStringCmd), + HOOK_VIRTUAL_DEF(0x01D018C0, BaseClient::ParseNop), + HOOK_VIRTUAL_DEF(0x01D018D0, BaseClient::ParseBad), + HOOK_VIRTUAL_DEF(0x01D02450, BaseClient::ParseMove), + HOOK_VIRTUAL_DEF(0x01D02430, BaseClient::ParseVoiceData), + HOOK_VIRTUAL_DEF(0x01D02530, BaseClient::ParseHLTV), + HOOK_VIRTUAL_DEF(0x01D022E0, BaseClient::ParseDelta), + HOOK_VIRTUAL_DEF(0x01D02540, BaseClient::ParseCvarValue), + HOOK_VIRTUAL_DEF(0x01D02550, BaseClient::ParseCvarValue2), + HOOK_VIRTUAL_DEF(0x01D01740, BaseClient::ProcessMessage), + HOOK_VIRTUAL_DEF(0x01D01920, BaseClient::ProcessStringCmd), + + // BaseSystemModule + HOOK_VIRTUAL_DEF(0x01D02950, BaseClient::Init), + HOOK_VIRTUAL_DEF(0x01D02960, BaseClient::RunFrame), + HOOK_VIRTUAL_DEF(0x01D02330, BaseClient::GetStatusLine), + HOOK_VIRTUAL_DEF(0x01D16CE0, BaseClient::GetType), + HOOK_VIRTUAL_DEF(0x01D02A20, BaseClient::ShutDown), + +#endif // BaseClient_region + +#ifndef BitBuffer_region + + HOOK_DEF(0x01D02F80, MethodThunk::Destructor), + HOOK_DEF(0x01D02F10, (MethodThunk::Constructor), void()), + HOOK_DEF(0x01D02F90, (MethodThunk::Constructor), void(unsigned int)), + HOOK_DEF(0x01D02F50, (MethodThunk::Constructor), void(void *, unsigned int)), + + HOOK_DEF(0x01D02FC0, BitBuffer::Resize), + HOOK_DEF(0x01D03010, BitBuffer::Clear), + HOOK_DEF(0x01D03050, BitBuffer::Reset), + HOOK_DEF(0x01D03070, BitBuffer::Free), + HOOK_DEF(0x01D03220, BitBuffer::PeekBits), + HOOK_DEF(0x01D038D0, BitBuffer::CurrentSize), + HOOK_DEF(0x01D03CE0, BitBuffer::FastClear), + HOOK_DEF(0x01D03D30, BitBuffer::ConcatBuffer), + HOOK_DEF(0x01D03BF0, BitBuffer::SkipBytes), + HOOK_DEF(0x01D03040, BitBuffer::CurrentBit), // NOXREF + HOOK_DEF(0x01D038F0, BitBuffer::SpaceLeft), // NOXREF + HOOK_DEF(0x01D03900, BitBuffer::AlignByte), // NOXREF + HOOK_DEF(0x01D03A70, BitBuffer::StartBitMode), // NOXREF + HOOK_DEF(0x01D03A80, BitBuffer::EndBitMode), // NOXREF + HOOK_DEF(0x01D03AC0, BitBuffer::SetBuffer), // NOXREF + HOOK_DEF(0x01D03C20, BitBuffer::SkipBits), // NOXREF + HOOK_DEF(0x01D03CB0, BitBuffer::SkipString), // NOXREF + + // Read + HOOK_DEF(0x01D030B0, BitBuffer::ReadBits), + HOOK_DEF(0x01D03190, BitBuffer::ReadBit), + HOOK_DEF(0x01D03250, BitBuffer::ReadChar), + HOOK_DEF(0x01D03260, BitBuffer::ReadByte), + HOOK_DEF(0x01D03270, BitBuffer::ReadShort), + HOOK_DEF(0x01D03280, BitBuffer::ReadWord), + HOOK_DEF(0x01D03290, BitBuffer::ReadLong), + HOOK_DEF(0x01D032A0, BitBuffer::ReadFloat), + HOOK_DEF(0x01D032C0, BitBuffer::ReadBuf), + HOOK_DEF(0x01D033A0, BitBuffer::ReadString), + HOOK_DEF(0x01D033E0, BitBuffer::ReadStringLine), + HOOK_DEF(0x01D03A00, BitBuffer::ReadBitString), + HOOK_DEF(0x01D03A90, BitBuffer::ReadBitData), + HOOK_DEF(0x01D03AF0, BitBuffer::ReadBitVec3Coord), + HOOK_DEF(0x01D03B50, BitBuffer::ReadBitCoord), + HOOK_DEF(0x01D03BD0, BitBuffer::ReadCoord), + HOOK_DEF(0x01D03420, BitBuffer::ReadAngle), // NOXREF + HOOK_DEF(0x01D03440, BitBuffer::ReadHiresAngle), // NOXREF + HOOK_DEF(0x01D03920, BitBuffer::ReadSBits), // NOXREF + HOOK_DEF(0x01D03950, BitBuffer::ReadBitAngle), // NOXREF + + // Write + HOOK_DEF(0x01D03770, BitBuffer::WriteBuf, void(const void *, int)), + HOOK_DEF(0x01D03370, BitBuffer::WriteBuf, void(BitBuffer *, int)), + + HOOK_DEF(0x01D03460, BitBuffer::WriteBit), + HOOK_DEF(0x01D03530, BitBuffer::WriteBits), + HOOK_DEF(0x01D03650, BitBuffer::WriteSBits), // NOXREF + HOOK_DEF(0x01D036B0, BitBuffer::WriteChar), + HOOK_DEF(0x01D036C0, BitBuffer::WriteByte), + HOOK_DEF(0x01D036D0, BitBuffer::WriteShort), + HOOK_DEF(0x01D036E0, BitBuffer::WriteWord), + HOOK_DEF(0x01D036F0, BitBuffer::WriteLong), + HOOK_DEF(0x01D03700, BitBuffer::WriteFloat), + HOOK_DEF(0x01D03730, BitBuffer::WriteString), + HOOK_DEF(0x01D03D50, BitBuffer::WriteCoord), + HOOK_DEF(0x01D03840, BitBuffer::WriteBitData), // NOXREF + HOOK_DEF(0x01D03870, BitBuffer::WriteAngle), // NOXREF + HOOK_DEF(0x01D038A0, BitBuffer::WriteHiresAngle), // NOXREF + HOOK_DEF(0x01D03990, BitBuffer::WriteBitAngle), // NOXREF + HOOK_DEF(0x01D03A30, BitBuffer::WriteBitString), // NOXREF + +#endif // BitBuffer_region + +#ifndef NetChannel_region + + // virtual functions + HOOK_VIRTUAL_DEF(0x01D0C7E0, NetChannel::Create), + HOOK_VIRTUAL_DEF(0x01D0DC20, NetChannel::GetTargetAddress), + HOOK_VIRTUAL_DEF(0x01D0C8C0, NetChannel::Close), + HOOK_VIRTUAL_DEF(0x01D0C6E0, NetChannel::Clear), + HOOK_VIRTUAL_DEF(0x01D0C650, NetChannel::Reset), + HOOK_VIRTUAL_DEF(0x01D0DC40, NetChannel::IsConnected), + HOOK_VIRTUAL_DEF(0x01D0C8F0, NetChannel::IsReadyToSend), + HOOK_VIRTUAL_DEF(0x01D0DD60, NetChannel::IsCrashed), + HOOK_VIRTUAL_DEF(0x01D0DC70, NetChannel::IsTimedOut), + HOOK_VIRTUAL_DEF(0x01D0DC30, NetChannel::IsFakeChannel), + HOOK_VIRTUAL_DEF(0x01D0DC10, NetChannel::KeepAlive), + HOOK_VIRTUAL_DEF(0x01D0DB90, NetChannel::SetRate), + + HOOK_VIRTUAL_DEF(0x01D0D9F0, NetChannel::SetUpdateRate), + HOOK_VIRTUAL_DEF(0x01D0DC50, NetChannel::SetTimeOut), + HOOK_VIRTUAL_DEF(0x01D0DC00, NetChannel::SetKeepAlive), + HOOK_VIRTUAL_DEF(0x01D0DCB0, NetChannel::GetIdleTime), + HOOK_VIRTUAL_DEF(0x01D0DCC0, NetChannel::GetRate), + HOOK_VIRTUAL_DEF(0x01D0DCD0, NetChannel::GetUpdateRate), + HOOK_VIRTUAL_DEF(0x01D0DCE0, NetChannel::GetLoss), + + HOOK_VIRTUAL_DEF(0x01D0C9E0, NetChannel::TransmitOutgoing), + HOOK_VIRTUAL_DEF(0x01D0D130, NetChannel::ProcessIncoming), + HOOK_VIRTUAL_DEF(0x01D0DCF0, NetChannel::FakeAcknowledgement), + HOOK_VIRTUAL_DEF(0x01D0C480, NetChannel::OutOfBandPrintf), + + // non-virtual functions + HOOK_DEF(0x01D0C400, NetChannel::UnlinkFragment), + HOOK_DEF(0x01D0C530, NetChannel::ClearFragbufs), + HOOK_DEF(0x01D0C560, NetChannel::ClearFragments), + HOOK_DEF(0x01D0C5D0, NetChannel::FlushIncoming), + HOOK_DEF(0x01D0C910, NetChannel::UpdateFlow), + HOOK_DEF(0x01D0D030, NetChannel::FindBufferById), + HOOK_DEF(0x01D0D090, NetChannel::CheckForCompletion), + HOOK_DEF(0x01D0D620, NetChannel::FragSend), + HOOK_DEF(0x01D0D670, NetChannel::AddBufferToList), + HOOK_DEF(0x01D0D6C0, NetChannel::CreateFragmentsFromBuffer), + HOOK_DEF(0x01D0DD20, NetChannel::CreateFragmentsFromFile), + HOOK_DEF(0x01D0D970, NetChannel::AddFragbufToTail), + HOOK_DEF(0x01D0D9B0, NetChannel::GetPacket), + HOOK_DEF(0x01D0D9C0, NetChannel::FreePacket), + HOOK_DEF(0x01D0DA30, NetChannel::CopyNormalFragments), + HOOK_DEF(0x01D0DBD0, NetChannel::GetFlowStats), + HOOK_DEF(0x01D0DBC0, NetChannel::SetConnected), // NOXREF + HOOK_DEF(0x01D0DD70, NetChannel::CopyFileFragments), // NOXREF + +#endif // NetChannel_region + +#ifndef Master_region + + // virtual functions + HOOK_VIRTUAL_DEF(0x01D0A480, Master::Init), + HOOK_VIRTUAL_DEF(0x01D0A490, Master::RunFrame), + HOOK_VIRTUAL_DEF(0x01D0A4B0, Master::ExecuteCommand), + HOOK_VIRTUAL_DEF(0x01D0A500, Master::GetStatusLine), + HOOK_VIRTUAL_DEF(0x01D0A510, Master::GetType), + HOOK_VIRTUAL_DEF(0x01D0A550, Master::ShutDown), + + //HOOK_DEF(0x0, Master::InitializeSteam), // NOXREF + HOOK_DEF(0x01D0A230, Master::CMD_Heartbeat), + HOOK_DEF(0x01D0A260, Master::CMD_NoMaster), + HOOK_DEF(0x01D0A350, Master::CMD_ListMaster), + HOOK_DEF(0x01D0A3E0, Master::SendShutdown), + +#endif // Master_region + + { NULL, NULL, NULL }, +}; + +AddressRef g_FunctionRefs[] = +{ +#ifndef Function_References_region + +#endif // Function_References_region + + { NULL, NULL, NULL }, +}; + +AddressRef g_DataRefs[] = +{ +#ifndef Data_References_region + + +#endif // Data_References_region + + { NULL, NULL, NULL }, +}; + +#endif // HOOK_HLTV diff --git a/rehlds/hookers/HLTV/Proxy/hooklist.h b/rehlds/hookers/HLTV/Proxy/hooklist.h new file mode 100644 index 0000000..d030ec9 --- /dev/null +++ b/rehlds/hookers/HLTV/Proxy/hooklist.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef HOOK_HLTV + +#include "hookers/memory.h" +#include "hookers/helper.h" +#include "hookers/hooker.h" + +#define rehlds_syserror HLTV_SysError + +#endif // HOOK_HLTV diff --git a/rehlds/hookers/HLTV/Proxy/main.cpp b/rehlds/hookers/HLTV/Proxy/main.cpp new file mode 100644 index 0000000..a1528c3 --- /dev/null +++ b/rehlds/hookers/HLTV/Proxy/main.cpp @@ -0,0 +1,55 @@ +#include "precompiled.h" + +#if defined(HOOK_HLTV) +#define ORIGINAL_PROXY_DLL_NAME "proxy2.dll" + +CSysModule *g_pOriginalProxyModule = NULL; +CreateInterfaceFn g_OriginalProxyFactory = NULL; +ISystemModule *g_pOriginalProxy = NULL; + +IBaseInterface *CreateProxyInterface() +{ + if (g_pOriginalProxy) { + return g_pOriginalProxy; + } + + if (g_pOriginalProxyModule) + { + g_OriginalProxyFactory = Sys_GetFactory(g_pOriginalProxyModule); + if (g_OriginalProxyFactory) + { + int returnCode = 0; + g_pOriginalProxy = reinterpret_cast(g_OriginalProxyFactory(PROXY_INTERFACE_VERSION, &returnCode)); + return g_pOriginalProxy; + } + } + + return NULL; +} + +InterfaceReg iface = InterfaceReg(CreateProxyInterface, PROXY_INTERFACE_VERSION); + +// DLL entry point +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + if (fdwReason == DLL_PROCESS_ATTACH) + { + g_pOriginalProxyModule = Sys_LoadModule(ORIGINAL_PROXY_DLL_NAME); + size_t addr = (size_t)Sys_GetProcAddress(ORIGINAL_PROXY_DLL_NAME, CREATEINTERFACE_PROCNAME); + HookModule("hltv.exe", addr); + } + else if (fdwReason == DLL_PROCESS_DETACH) + { + if (g_pOriginalProxyModule) + { + Sys_UnloadModule(g_pOriginalProxyModule); + g_pOriginalProxyModule = NULL; + g_OriginalProxyFactory = NULL; + g_pOriginalProxy = NULL; + } + } + + return TRUE; +} + +#endif // #if defined(HOOK_HLTV) diff --git a/rehlds/public/FileSystem.h b/rehlds/public/FileSystem.h index 41d26b5..8e202ba 100644 --- a/rehlds/public/FileSystem.h +++ b/rehlds/public/FileSystem.h @@ -1,20 +1,39 @@ -//========= Copyright � 1996-2001, Valve LLC, All rights reserved. ============ -// -// Purpose: -// -// $NoKeywords: $ -//============================================================================= +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ -#ifndef FILESYSTEM_H -#define FILESYSTEM_H -#ifdef _WIN32 #pragma once -#endif #include "interface.h" #include #include +// There is only one instance of the IFileSystem interface, +// located in the filesystem_stdio library (filesystem_steam is obsolete). #ifdef _WIN32 #define STDIO_FILESYSTEM_LIB "filesystem_stdio.dll" #define STEAM_FILESYSTEM_LIB "filesystem_steam.dll" @@ -23,17 +42,13 @@ #define STEAM_FILESYSTEM_LIB "filesystem_steam.so" #endif // _WIN32 -//----------------------------------------------------------------------------- // Forward declarations -//----------------------------------------------------------------------------- -typedef FILE * FileHandle_t; +typedef FILE *FileHandle_t; typedef int FileFindHandle_t; typedef int WaitForResourcesHandle_t; +typedef void (*WarningFunc_t)(const char *fmt, ...); - -//----------------------------------------------------------------------------- // Enums used by the interface -//----------------------------------------------------------------------------- #ifndef FILESYSTEM_INTERNAL_H typedef enum { @@ -49,148 +64,138 @@ enum typedef enum { - // Don't print anything - FILESYSTEM_WARNING_QUIET = 0, - - // On shutdown, report names of files left unclosed - FILESYSTEM_WARNING_REPORTUNCLOSED, - - // Report number of times a file was opened, closed - FILESYSTEM_WARNING_REPORTUSAGE, - - // Report all open/close events to console ( !slow! ) - FILESYSTEM_WARNING_REPORTALLACCESSES + FILESYSTEM_WARNING_QUIET = 0, // Don't print anything + FILESYSTEM_WARNING_REPORTUNCLOSED, // On shutdown, report names of files left unclosed + FILESYSTEM_WARNING_REPORTUSAGE, // Report number of times a file was opened, closed + FILESYSTEM_WARNING_REPORTALLACCESSES // Report all open/close events to console (!slow!) } FileWarningLevel_t; -#define FILESYSTEM_INVALID_HANDLE ( FileHandle_t )0 -#endif +#define FILESYSTEM_INVALID_HANDLE (FileHandle_t)0 +#endif // FILESYSTEM_INTERNAL_H // turn off any windows defines #undef GetCurrentDirectory -//----------------------------------------------------------------------------- // Purpose: Main file system interface -//----------------------------------------------------------------------------- class IFileSystem : public IBaseInterface { public: // Mount and unmount the filesystem - virtual void Mount( void ) = 0; - virtual void Unmount( void ) = 0; + virtual void Mount() = 0; + virtual void Unmount() = 0; // Remove all search paths (including write path?) - virtual void RemoveAllSearchPaths( void ) = 0; + virtual void RemoveAllSearchPaths() = 0; // Add paths in priority order (mod dir, game dir, ....) // If one or more .pak files are in the specified directory, then they are // added after the file system path // If the path is the relative path to a .bsp file, then any previous .bsp file // override is cleared and the current .bsp is searched for an embedded PAK file - // and this file becomes the highest priority search path ( i.e., it's looked at first - // even before the mod's file system path ). - virtual void AddSearchPath( const char *pPath, const char *pathID ) = 0; - virtual bool RemoveSearchPath( const char *pPath ) = 0; + // and this file becomes the highest priority search path (i.e., it's looked at first + // even before the mod's file system path). + virtual void AddSearchPath(const char *pPath, const char *pathID) = 0; + virtual bool RemoveSearchPath(const char *pPath) = 0; // Deletes a file - virtual void RemoveFile( const char *pRelativePath, const char *pathID ) = 0; + virtual void RemoveFile(const char *pRelativePath, const char *pathID) = 0; // this isn't implementable on STEAM as is. - virtual void CreateDirHierarchy( const char *path, const char *pathID ) = 0; + virtual void CreateDirHierarchy(const char *path, const char *pathID) = 0; // File I/O and info - virtual bool FileExists( const char *pFileName ) = 0; - virtual bool IsDirectory( const char *pFileName ) = 0; + virtual bool FileExists(const char *pFileName) = 0; + virtual bool IsDirectory(const char *pFileName) = 0; // opens a file // if pathID is NULL, all paths will be searched for the file - virtual FileHandle_t Open( const char *pFileName, const char *pOptions, const char *pathID = 0L ) = 0; + virtual FileHandle_t Open(const char *pFileName, const char *pOptions, const char *pathID = 0L) = 0; - virtual void Close( FileHandle_t file ) = 0; + virtual void Close(FileHandle_t file) = 0; - virtual void Seek( FileHandle_t file, int pos, FileSystemSeek_t seekType ) = 0; - virtual unsigned int Tell( FileHandle_t file ) = 0; + virtual void Seek(FileHandle_t file, int pos, FileSystemSeek_t seekType) = 0; + virtual unsigned int Tell(FileHandle_t file) = 0; - virtual unsigned int Size( FileHandle_t file ) = 0; - virtual unsigned int Size( const char *pFileName ) = 0; + virtual unsigned int Size(FileHandle_t file) = 0; + virtual unsigned int Size(const char *pFileName) = 0; - virtual long GetFileTime( const char *pFileName ) = 0; - virtual void FileTimeToString( char* pStrip, int maxCharsIncludingTerminator, long fileTime ) = 0; + virtual long GetFileTime(const char *pFileName) = 0; + virtual void FileTimeToString(char *pStrip, int maxCharsIncludingTerminator, long fileTime) = 0; - virtual bool IsOk( FileHandle_t file ) = 0; + virtual bool IsOk(FileHandle_t file) = 0; - virtual void Flush( FileHandle_t file ) = 0; - virtual bool EndOfFile( FileHandle_t file ) = 0; + virtual void Flush(FileHandle_t file) = 0; + virtual bool EndOfFile(FileHandle_t file) = 0; - virtual int Read( void* pOutput, int size, FileHandle_t file ) = 0; - virtual int Write( void const* pInput, int size, FileHandle_t file ) = 0; - virtual char *ReadLine( char *pOutput, int maxChars, FileHandle_t file ) = 0; - virtual int FPrintf( FileHandle_t file, char *pFormat, ... ) = 0; + virtual int Read(void *pOutput, int size, FileHandle_t file) = 0; + virtual int Write(void const *pInput, int size, FileHandle_t file) = 0; + virtual char *ReadLine(char *pOutput, int maxChars, FileHandle_t file) = 0; + virtual int FPrintf(FileHandle_t file, char *pFormat, ...) = 0; // direct filesystem buffer access // returns a handle to a buffer containing the file data // this is the optimal way to access the complete data for a file, // since the file preloader has probably already got it in memory - virtual void *GetReadBuffer( FileHandle_t file, int *outBufferSize, bool failIfNotInCache ) = 0; - virtual void ReleaseReadBuffer( FileHandle_t file, void *readBuffer ) = 0; + virtual void *GetReadBuffer(FileHandle_t file, int *outBufferSize, bool failIfNotInCache) = 0; + virtual void ReleaseReadBuffer(FileHandle_t file, void *readBuffer) = 0; // FindFirst/FindNext - virtual const char *FindFirst( const char *pWildCard, FileFindHandle_t *pHandle, const char *pathID = 0L ) = 0; - virtual const char *FindNext( FileFindHandle_t handle ) = 0; - virtual bool FindIsDirectory( FileFindHandle_t handle ) = 0; - virtual void FindClose( FileFindHandle_t handle ) = 0; + virtual const char *FindFirst(const char *pWildCard, FileFindHandle_t *pHandle, const char *pathID = 0L) = 0; + virtual const char *FindNext(FileFindHandle_t handle) = 0; + virtual bool FindIsDirectory(FileFindHandle_t handle) = 0; + virtual void FindClose(FileFindHandle_t handle) = 0; - virtual void GetLocalCopy( const char *pFileName ) = 0; + virtual void GetLocalCopy(const char *pFileName) = 0; - virtual const char *GetLocalPath( const char *pFileName, char *pLocalPath, int localPathBufferSize ) = 0; + virtual const char *GetLocalPath(const char *pFileName, char *pLocalPath, int localPathBufferSize) = 0; // Note: This is sort of a secondary feature; but it's really useful to have it here - virtual char *ParseFile( char* pFileBytes, char* pToken, bool* pWasQuoted ) = 0; + virtual char *ParseFile(char *pFileBytes, char *pToken, bool *pWasQuoted) = 0; - // Returns true on success ( based on current list of search paths, otherwise false if it can't be resolved ) - virtual bool FullPathToRelativePath( const char *pFullpath, char *pRelative ) = 0; + // Returns true on success (based on current list of search paths, otherwise false if it can't be resolved) + virtual bool FullPathToRelativePath(const char *pFullpath, char *pRelative) = 0; // Gets the current working directory - virtual bool GetCurrentDirectory( char* pDirectory, int maxlen ) = 0; + virtual bool GetCurrentDirectory(char *pDirectory, int maxlen) = 0; // Dump to printf/OutputDebugString the list of files that have not been closed - virtual void PrintOpenedFiles( void ) = 0; + virtual void PrintOpenedFiles() = 0; - virtual void SetWarningFunc( void (*pfnWarning)( const char *fmt, ... ) ) = 0; - virtual void SetWarningLevel( FileWarningLevel_t level ) = 0; + virtual void SetWarningFunc(WarningFunc_t pfnWarning) = 0; + virtual void SetWarningLevel(FileWarningLevel_t level) = 0; - virtual void LogLevelLoadStarted( const char *name ) = 0; - virtual void LogLevelLoadFinished( const char *name ) = 0; - virtual int HintResourceNeed( const char *hintlist, int forgetEverything ) = 0; - virtual int PauseResourcePreloading( void ) = 0; - virtual int ResumeResourcePreloading( void ) = 0; - virtual int SetVBuf( FileHandle_t stream, char *buffer, int mode, long size ) = 0; - virtual void GetInterfaceVersion( char *p, int maxlen ) = 0; + virtual void LogLevelLoadStarted(const char *name) = 0; + virtual void LogLevelLoadFinished(const char *name) = 0; + virtual int HintResourceNeed(const char *hintlist, int forgetEverything) = 0; + virtual int PauseResourcePreloading() = 0; + virtual int ResumeResourcePreloading() = 0; + virtual int SetVBuf(FileHandle_t stream, char *buffer, int mode, long size) = 0; + virtual void GetInterfaceVersion(char *p, int maxlen) = 0; virtual bool IsFileImmediatelyAvailable(const char *pFileName) = 0; // starts waiting for resources to be available // returns FILESYSTEM_INVALID_HANDLE if there is nothing to wait on - virtual WaitForResourcesHandle_t WaitForResources( const char *resourcelist ) = 0; + virtual WaitForResourcesHandle_t WaitForResources(const char *resourcelist) = 0; + // get progress on waiting for resources; progress is a float [0, 1], complete is true on the waiting being done // returns false if no progress is available // any calls after complete is true or on an invalid handle will return false, 0.0f, true - virtual bool GetWaitForResourcesProgress( WaitForResourcesHandle_t handle, float *progress /* out */ , bool *complete /* out */ ) = 0; + virtual bool GetWaitForResourcesProgress(WaitForResourcesHandle_t handle, float *progress /* out */ , bool *complete /* out */) = 0; + // cancels a progress call - virtual void CancelWaitForResources( WaitForResourcesHandle_t handle ) = 0; + virtual void CancelWaitForResources(WaitForResourcesHandle_t handle) = 0; // returns true if the appID has all its caches fully preloaded - virtual bool IsAppReadyForOfflinePlay( int appID ) = 0; + virtual bool IsAppReadyForOfflinePlay(int appID) = 0; // interface for custom pack files > 4Gb - virtual bool AddPackFile( const char *fullpath, const char *pathID ) = 0; + virtual bool AddPackFile(const char *fullpath, const char *pathID) = 0; // open a file but force the data to come from the steam cache, NOT from disk - virtual FileHandle_t OpenFromCacheForRead( const char *pFileName, const char *pOptions, const char *pathID = 0L ) = 0; - - virtual void AddSearchPathNoWrite( const char *pPath, const char *pathID ) = 0; + virtual FileHandle_t OpenFromCacheForRead(const char *pFileName, const char *pOptions, const char *pathID = 0L) = 0; + virtual void AddSearchPathNoWrite(const char *pPath, const char *pathID) = 0; }; // Steam3/Src compat #define IBaseFileSystem IFileSystem #define FILESYSTEM_INTERFACE_VERSION "VFileSystem009" - -#endif // FILESYSTEM_H diff --git a/rehlds/public/HLTV/IBSPModel.h b/rehlds/public/HLTV/IBSPModel.h new file mode 100644 index 0000000..22a6438 --- /dev/null +++ b/rehlds/public/HLTV/IBSPModel.h @@ -0,0 +1,45 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +class IBaseSystem; +class IBSPModel { +public: + virtual ~IBSPModel() {}; + + virtual void Init(IBaseSystem *system) = 0; + virtual void Clear() = 0; + virtual bool Load(const char *name, bool minimal) = 0; + virtual bool IsValid() = 0; + virtual bool IsMinimal() = 0; + virtual void SetPVS(float *point) = 0; + virtual bool InPVS(float *point) = 0; + virtual bool TraceLine(float *start, float *end, float *impact) = 0; + virtual int TruePointContents(float *point) = 0; +}; diff --git a/rehlds/public/HLTV/IClient.h b/rehlds/public/HLTV/IClient.h new file mode 100644 index 0000000..fe51503 --- /dev/null +++ b/rehlds/public/HLTV/IClient.h @@ -0,0 +1,51 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +class IWorld; +class InfoString; +class IClient: virtual public ISystemModule { +public: + virtual ~IClient() {} + + virtual bool Connect(INetSocket *socket, NetAddress *adr, char *userinfo) = 0; + virtual void Send(unsigned char *data, int length, bool isReliable) = 0; + virtual void Disconnect(const char *reason = nullptr) = 0; + virtual void Reconnect() = 0; + virtual void SetWorld(IWorld *world) = 0; + virtual int GetClientType() = 0; + virtual char *GetClientName() = 0; + virtual InfoString *GetUserInfo() = 0; + virtual NetAddress *GetAddress() = 0; + virtual bool IsActive() = 0; + virtual bool IsHearingVoices() = 0; + virtual bool HasChatEnabled() = 0; +}; + +#define CLIENT_INTERFACE_VERSION "client001" diff --git a/rehlds/public/HLTV/IDirector.h b/rehlds/public/HLTV/IDirector.h new file mode 100644 index 0000000..b840662 --- /dev/null +++ b/rehlds/public/HLTV/IDirector.h @@ -0,0 +1,52 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "ISystemModule.h" + +class IWorld; +class IProxy; +class BitBuffer; +class DirectorCmd; +class IObjectContainer; + +class IDirector: virtual public ISystemModule { +public: + virtual ~IDirector() {} + + virtual void NewGame(IWorld *world, IProxy *proxy) = 0; + virtual char *GetModName() = 0; + virtual void WriteCommands(BitBuffer *stream, float startTime, float endTime) = 0; + virtual int AddCommand(DirectorCmd *cmd) = 0; + virtual bool RemoveCommand(int index) = 0; + virtual DirectorCmd *GetLastCommand() = 0; + virtual IObjectContainer *GetCommands() = 0; +}; + +#define DIRECTOR_INTERFACE_VERSION "director001" diff --git a/rehlds/public/HLTV/INetChannel.h b/rehlds/public/HLTV/INetChannel.h new file mode 100644 index 0000000..dad99f6 --- /dev/null +++ b/rehlds/public/HLTV/INetChannel.h @@ -0,0 +1,60 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +class INetSocket; +class IBaseSystem; +class INetChannel { +public: + virtual ~INetChannel() {} + + virtual bool Create(IBaseSystem *system, INetSocket *netsocket = nullptr, NetAddress *adr = nullptr) = 0; + virtual NetAddress *GetTargetAddress() = 0; + virtual void Close() = 0; + virtual void Clear() = 0; + virtual void Reset() = 0; + virtual bool IsConnected() = 0; + virtual bool IsReadyToSend() = 0; + virtual bool IsCrashed() = 0; + virtual bool IsTimedOut() = 0; + virtual bool IsFakeChannel() = 0; + virtual bool KeepAlive() = 0; + virtual void SetRate(int newRate) = 0; + virtual void SetUpdateRate(int newupdaterate) = 0; + virtual void SetTimeOut(float time) = 0; + virtual void SetKeepAlive(bool flag) = 0; + virtual float GetIdleTime() = 0; + virtual int GetRate() = 0; + virtual int GetUpdateRate() = 0; + virtual float GetLoss() = 0; + virtual void TransmitOutgoing() = 0; + virtual void ProcessIncoming(unsigned char *data, int size) = 0; + virtual void OutOfBandPrintf(const char *format, ...) = 0; + virtual void FakeAcknowledgement() = 0; +}; diff --git a/rehlds/public/HLTV/INetSocket.h b/rehlds/public/HLTV/INetSocket.h new file mode 100644 index 0000000..972d441 --- /dev/null +++ b/rehlds/public/HLTV/INetSocket.h @@ -0,0 +1,55 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +class INetwork; +class INetChannel; +class NetPacket; + +class INetSocket { +public: + virtual ~INetSocket() {}; + + virtual NetPacket *ReceivePacket() = 0; + virtual void FreePacket(NetPacket *packet) = 0; + virtual bool SendPacket(NetPacket *packet) = 0; + virtual bool SendPacket(NetAddress *to, const void *data, int length) = 0; + virtual void AddPacket(NetPacket *packet) = 0; + virtual bool AddChannel(INetChannel *channel) = 0; + virtual bool RemoveChannel(INetChannel *channel) = 0; + + virtual INetwork *GetNetwork() = 0; + virtual void OutOfBandPrintf(NetAddress *to, const char *format, ...) = 0; + virtual void Flush() = 0; + virtual void GetFlowStats(float *totalIn, float *totalOut) = 0; + virtual bool LeaveGroup(NetAddress *group) = 0; + virtual bool JoinGroup(NetAddress *group) = 0; + virtual void Close() = 0; + virtual int GetPort() = 0; +}; diff --git a/rehlds/public/HLTV/INetwork.h b/rehlds/public/HLTV/INetwork.h new file mode 100644 index 0000000..05c31a0 --- /dev/null +++ b/rehlds/public/HLTV/INetwork.h @@ -0,0 +1,62 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "ISystemModule.h" + +class INetSocket; +class INetwork { +public: + virtual ~INetwork() {}; + + virtual bool Init(IBaseSystem *system, int serial, char *name) = 0; + virtual void RunFrame(double time) = 0; + virtual void ReceiveSignal(ISystemModule *module, unsigned int signal, void *data) = 0; + virtual void ExecuteCommand(int commandID, char *commandLine) = 0; + virtual void RegisterListener(ISystemModule *module) = 0; + virtual void RemoveListener(ISystemModule *module) = 0; + virtual IBaseSystem *GetSystem() = 0; + virtual int GetSerial() = 0; + virtual char *GetStatusLine() = 0; + virtual char *GetType() = 0; + virtual char *GetName() = 0; + virtual int GetState() = 0; + virtual int GetVersion() = 0; + virtual void ShutDown() = 0; + + virtual INetSocket *CreateSocket(int port, bool reuse = false, bool loopback = false) = 0; + virtual bool RemoveSocket(INetSocket *netsocket) = 0; + virtual NetAddress *GetLocalAddress() = 0; + virtual bool ResolveAddress(char *string, NetAddress *address) = 0; + virtual void GetFlowStats(float *totalIn, float *totalOut) = 0; + virtual int GetLastErrorCode() = 0; + virtual char *GetErrorText(int code) = 0; +}; + +#define NETWORK_INTERFACE_VERSION "network001" diff --git a/rehlds/public/HLTV/IProxy.h b/rehlds/public/HLTV/IProxy.h new file mode 100644 index 0000000..6724897 --- /dev/null +++ b/rehlds/public/HLTV/IProxy.h @@ -0,0 +1,107 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "ISystemModule.h" +#include "custom.h" + +class IWorld; +class IServer; +class IDirector; +class INetSocket; +class BitBuffer; +class NetAddress; +class IObjectContainer; + +#define MAX_PROXY_CLIENTS 255 + +#define GROUP_CLIENT 0x00001 // Broadcast to client +#define GROUP_PROXY 0x00002 // Broadcast to proxy +#define GROUP_DEMO 0x00004 // Broadcast to demo file +#define GROUP_UNKNOWN 0x00008 // Broadcast to UNKNOWN: unused +#define GROUP_VOICE 0x00010 // Broadcast to voice enabled clients +#define GROUP_CHAT 0x00020 // Broadcast to chat enabled clients + +#define GROUP_CLIENT_ALL GROUP_CLIENT | GROUP_PROXY | GROUP_DEMO | GROUP_UNKNOWN + +enum ChatMode_e : int +{ + CHAT_OFF, // Spectators can't chat. + CHAT_LOCAL, // Only spectators connected to the same proxy can see their chat messages. + CHAT_GLOBAL, // All spectators can chat between each other (then Master and all Relay proxies must have set chatmode 2). +}; + +class IProxy: virtual public ISystemModule { +public: + virtual ~IProxy() {} + + virtual void Reset() = 0; + virtual void Broadcast(byte *data, int length, int groupType, bool isReliable) = 0; + virtual void IncreaseCheering(int votes) = 0; + virtual void ParseStatusMsg(BitBuffer *stream) = 0; + virtual void ParseStatusReport(NetAddress *from, BitBuffer *stream) = 0; + virtual bool ProcessConnectionlessMessage(NetAddress *from, BitBuffer *stream) = 0; + virtual void ChatCommentator(char *nick, char *text) = 0; + virtual void ChatSpectator(char *nick, char *text) = 0; + virtual void CountLocalClients(int &spectators, int &proxies) = 0; + virtual struct resource_s *AddResource(char *fileName, resourcetype_t type, char *asFileName = nullptr) = 0; + virtual bool IsLanOnly() = 0; + virtual bool IsMaster() = 0; + virtual bool IsActive() = 0; + virtual bool IsPublicGame() = 0; + virtual bool IsPasswordProtected() = 0; + virtual bool IsStressed() = 0; + virtual void SetDelay(float seconds) = 0; + virtual void SetClientTime(double time, bool relative) = 0; + virtual void SetClientTimeScale(float scale) = 0; + virtual void SetMaxRate(int rate) = 0; + virtual void SetMaxLoss(float maxloss) = 0; + virtual void SetMaxUpdateRate(int updaterate) = 0; + virtual bool SetMaxClients(int number) = 0; + virtual void SetRegion(unsigned char region) = 0; + virtual float GetDelay() = 0; + virtual double GetSpectatorTime() = 0; + virtual double GetProxyTime() = 0; + virtual int GetMaxClients() = 0; + virtual IWorld *GetWorld() = 0; + virtual IServer *GetServer() = 0; + virtual IDirector *GetDirector() = 0; + virtual INetSocket *GetSocket() = 0; + virtual ChatMode_e GetChatMode() = 0; + virtual void GetStatistics(int &proxies, int &slots, int &spectators) = 0; + virtual int GetMaxRate() = 0; + virtual int GetMaxUpdateRate() = 0; + virtual struct resource_s *GetResource(char *fileName) = 0; + virtual int GetDispatchMode() = 0; + virtual unsigned char GetRegion() = 0; + virtual IObjectContainer *GetClients() = 0; + virtual bool WriteSignonData(int type, BitBuffer *stream) = 0; +}; + +#define PROXY_INTERFACE_VERSION "proxy001" diff --git a/rehlds/public/HLTV/IServer.h b/rehlds/public/HLTV/IServer.h new file mode 100644 index 0000000..37640cf --- /dev/null +++ b/rehlds/public/HLTV/IServer.h @@ -0,0 +1,101 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "ISystemModule.h" + +class IWorld; +class IProxy; +class IDirector; +class INetSocket; +class ISystemModule; +class IBaseSystem; + +class NetAddress; +class InfoString; +class BitBuffer; + +class IServer { +public: + virtual ~IServer() {} + + virtual bool Init(IBaseSystem *system, int serial, char *name) = 0; + virtual void RunFrame(double time) = 0; + virtual void ReceiveSignal(ISystemModule *module, unsigned int signal, void *data) = 0; + virtual void ExecuteCommand(int commandID, char *commandLine) = 0; + virtual void RegisterListener(ISystemModule *module) = 0; + virtual void RemoveListener(ISystemModule *module) = 0; + virtual IBaseSystem *GetSystem() = 0; + virtual int GetSerial() = 0; + virtual char *GetStatusLine() = 0; + virtual char *GetType() = 0; + virtual char *GetName() = 0; + virtual int GetState() = 0; + virtual int GetVersion() = 0; + virtual void ShutDown() = 0; + + virtual bool Connect(IWorld *world, NetAddress *adr, INetSocket *socket) = 0; + virtual bool LoadDemo(IWorld *world, char *filename, bool forceHLTV, bool continuous) = 0; + virtual void Reconnect() = 0; + virtual void Disconnect() = 0; + virtual void Retry() = 0; + virtual void StopRetry() = 0; + virtual void SendStringCommand(char *command) = 0; + virtual void SendHLTVCommand(BitBuffer *msg) = 0; + virtual bool IsConnected() = 0; + virtual bool IsDemoFile() = 0; + virtual bool IsGameServer() = 0; + virtual bool IsRelayProxy() = 0; + virtual bool IsVoiceBlocking() = 0; + virtual void SetProxy(IProxy *proxy) = 0; + virtual void SetDirector(IDirector *director) = 0; + virtual void SetPlayerName(char *newName) = 0; + virtual void SetDelayReconnect(bool state) = 0; + virtual void SetAutoRetry(bool state) = 0; + virtual void SetVoiceBlocking(bool state) = 0; + virtual void SetRate(int rate) = 0; + virtual void SetUpdateRate(int updaterate) = 0; + virtual void SetUserInfo(char *key, char *value) = 0; + virtual bool SetProtocol(int version) = 0; + virtual void SetGameDirectory(const char *defaultDir, const char *gameDir = nullptr) = 0; + virtual int GetRate() = 0; + virtual int GetUpdateRate() = 0; + virtual InfoString *GetServerInfoString() = 0; + virtual char *GetPlayerName() = 0; + virtual float GetTime() = 0; + virtual IWorld *GetWorld() = 0; + virtual char *GetDemoFileName() = 0; + virtual NetAddress *GetAddress() = 0; + virtual char *GetHostName() = 0; + virtual bool GetAutoRetry() = 0; + virtual float GetPacketLoss() = 0; + virtual int GetProtocol() = 0; +}; + +#define SERVER_INTERFACE_VERSION "server001" diff --git a/rehlds/public/HLTV/IWorld.h b/rehlds/public/HLTV/IWorld.h new file mode 100644 index 0000000..8e24c26 --- /dev/null +++ b/rehlds/public/HLTV/IWorld.h @@ -0,0 +1,163 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + +#pragma once + +#include "IBSPModel.h" +#include "IDirector.h" +#include "ISystemModule.h" +#include "common/ServerInfo.h" + +#include "pm_movevars.h" +#include "usermsg.h" +#include "entity_state.h" + +typedef struct frame_s +{ + float time; + unsigned int seqnr; + unsigned char *data; + void *entities; + unsigned int entitiesSize; + unsigned int entitynum; + void *clientData; + unsigned int clientDataSize; + unsigned char *events; + unsigned int eventsSize; + unsigned int eventnum; + unsigned char *reliableData; + unsigned int reliableDataSize; + unsigned char *unreliableData; + unsigned int unreliableDataSize; + unsigned char *userMessages; + unsigned int userMessagesSize; + unsigned char *voiceData; + unsigned int voiceDataSize; + unsigned char *demoData; + unsigned int demoDataSize; + void *demoInfo; + unsigned int delta; +} frame_t; + +class InfoString; +class NetAddress; + +class IWorld { +public: + virtual ~IWorld() {} + + virtual bool Init(IBaseSystem *system, int serial, char *name) = 0; + virtual void RunFrame(double time) = 0; + virtual void ReceiveSignal(ISystemModule *module, unsigned int signal, void *data) = 0; + virtual void ExecuteCommand(int commandID, char *commandLine) = 0; + virtual void RegisterListener(ISystemModule *module) = 0; + virtual void RemoveListener(ISystemModule *module) = 0; + virtual IBaseSystem *GetSystem() = 0; + virtual int GetSerial() = 0; + virtual char *GetStatusLine() = 0; + virtual char *GetType() = 0; + virtual char *GetName() = 0; + virtual int GetState() = 0; + virtual int GetVersion() = 0; + virtual void ShutDown() = 0; + + virtual double GetTime() = 0; + virtual NetAddress *GetGameServerAddress() = 0; + virtual char *GetLevelName() = 0; + virtual char *GetGameDir() = 0; + virtual frame_t *GetFrameByTime(double time) = 0; + virtual frame_t *GetFrameBySeqNr(unsigned int seqnr) = 0; + virtual frame_t *GetLastFrame() = 0; + virtual frame_t *GetFirstFrame() = 0; + virtual int GetServerCount() = 0; + virtual int GetSlotNumber() = 0; + virtual int GetMaxClients() = 0; + virtual int GetNumPlayers() = 0; + virtual IBSPModel *GetWorldModel() = 0; + virtual InfoString *GetServerInfoString() = 0; + virtual bool GetPlayerInfoString(int playerNum, InfoString *infoString) = 0; + virtual UserMsg *GetUserMsg(int msgNumber) = 0; + virtual char *GetHostName() = 0; + virtual serverinfo_t *GetServerInfo() = 0; + virtual bool IsPlayerIndex(int index) = 0; + virtual bool IsVoiceEnabled() = 0; + virtual bool IsActive() = 0; + virtual bool IsPaused() = 0; + virtual bool IsComplete() = 0; + virtual bool IsHLTV() = 0; + virtual void Reset() = 0; + virtual void SetServerInfo(int protocol, CRC32_t nserverCRC, byte *nclientdllmd5, int nmaxclients, int nplayernum, int ngametype, char *ngamedir, char *nservername, char *nlevelname) = 0; + virtual void SetServerInfoString(char *infostring) = 0; + virtual void SetServerInfo(serverinfo_t *serverinfo) = 0; + virtual void UpdateServerInfo() = 0; + virtual void SetPaused(bool state) = 0; + virtual void SetTime(double newTime) = 0; + virtual void SetBufferSize(float seconds) = 0; + virtual void SetVoiceEnabled(bool state) = 0; + virtual void SetMoveVars(movevars_t *nmovevars) = 0; + virtual void SetCDInfo(int ncdtrack, int nlooptrack) = 0; + virtual void SetHLTV(bool state) = 0; + virtual void SetExtraInfo(char *nclientfallback, int nallowCheats) = 0; + virtual void SetViewEntity(int nviewentity) = 0; + virtual void SetGameServerAddress(NetAddress *address) = 0; + virtual void SetHostName(char *name) = 0; + virtual void NewGame(int newServerCount) = 0; + virtual void FinishGame() = 0; + virtual bool SaveAsDemo(char *filename, IDirector *director) = 0; + virtual void StopGame() = 0; + virtual int FindUserMsgByName(char *name) = 0; + virtual void ParseDeltaDescription(BitBuffer *stream) = 0; + virtual void ParseBaseline(BitBuffer *stream) = 0; + virtual void ParseEvent(BitBuffer *stream) = 0; + virtual void ParseClientData(BitBuffer *stream, unsigned int deltaSeqNr, BitBuffer *to, clientdata_t *clientData) = 0; + virtual bool GetUncompressedFrame(unsigned int seqNr, frame_t *frame) = 0; + virtual bool UncompressEntitiesFromStream(frame_t *frame, BitBuffer *stream) = 0; + virtual bool UncompressEntitiesFromStream(frame_t *frame, BitBuffer *stream, unsigned int from) = 0; + virtual bool GetClientData(unsigned int SeqNr, clientdata_t *clientData) = 0; + virtual bool GetClientData(frame_t *frame, clientdata_t *clientData) = 0; + virtual int AddFrame(frame_t *newFrame) = 0; + virtual bool AddResource(resource_t *resource) = 0; + virtual void AddLightStyle(int index, char *style) = 0; + virtual bool AddSignonData(unsigned char type, unsigned char *data, int size) = 0; + virtual bool AddUserMessage(int msgNumber, int size, char *name) = 0; + virtual void AddBaselineEntity(int index, entity_state_t *ent) = 0; + virtual void AddInstancedBaselineEntity(int index, entity_state_t *ent) = 0; + virtual void UpdatePlayer(int playerNum, int userId, char *infostring, char *hashedcdkey) = 0; + virtual void WriteFrame(frame_t *frame, unsigned int lastFrameSeqnr, BitBuffer *reliableStream, BitBuffer *unreliableStream, unsigned int deltaSeqNr, unsigned int clientDelta, bool addVoice) = 0; + virtual void WriteNewData(BitBuffer *stream) = 0; + virtual void WriteClientUpdate(BitBuffer *stream, int playerIndex) = 0; + virtual void WriteMovevars(BitBuffer *stream) = 0; + virtual void WriteSigonData(BitBuffer *stream) = 0; + virtual void WriteLightStyles(BitBuffer *stream) = 0; + virtual int RemoveFrames(unsigned int startSeqNr, unsigned int endSeqNr) = 0; + virtual int DuplicateFrames(unsigned int startSeqNr, unsigned int endSeqNr) = 0; + virtual int MoveFrames(unsigned int startSeqNr, unsigned int endSeqNr, double destSeqnr) = 0; + virtual int RevertFrames(unsigned int startSeqNr, unsigned int endSeqNr) = 0; +}; + +#define WORLD_INTERFACE_VERSION "world001" diff --git a/rehlds/public/engine_hlds_api.h b/rehlds/public/engine_hlds_api.h index 9613a0b..007237e 100644 --- a/rehlds/public/engine_hlds_api.h +++ b/rehlds/public/engine_hlds_api.h @@ -39,10 +39,9 @@ class IDedicatedServerAPI : public IBaseInterface { public: - virtual bool Init(char *basedir, char *cmdline, CreateInterfaceFn launcherFactory, CreateInterfaceFn filesystemFactory) = 0; - virtual int Shutdown(void) = 0; - virtual bool RunFrame(void) = 0; + virtual int Shutdown() = 0; + virtual bool RunFrame() = 0; virtual void AddConsoleText(char *text) = 0; virtual void UpdateStatus(float *fps, int *nActive, int *nMaxPlayers, char *pszMap) = 0; }; diff --git a/rehlds/public/idedicatedexports.h b/rehlds/public/idedicatedexports.h index 509cbbd..2444e22 100644 --- a/rehlds/public/idedicatedexports.h +++ b/rehlds/public/idedicatedexports.h @@ -1,25 +1,40 @@ -//========= Copyright 1996-2001, Valve LLC, All rights reserved. ============ -// -// Purpose: -// -// $NoKeywords: $ -//============================================================================= +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ -#ifndef IDEDICATEDEXPORTS_H -#define IDEDICATEDEXPORTS_H -#ifdef _WIN32 #pragma once -#endif #include "interface.h" -class IDedicatedExports : IBaseInterface +class IDedicatedExports : public IBaseInterface { public: - virtual ~IDedicatedExports() { }; + virtual ~IDedicatedExports() {}; virtual void Sys_Printf(char *text) = 0; }; #define VENGINE_DEDICATEDEXPORTS_API_VERSION "VENGINE_DEDICATEDEXPORTS_API_VERSION001" - -#endif // IDEDICATEDEXPORTS_H diff --git a/rehlds/public/interface.cpp b/rehlds/public/interface.cpp index 2b62260..d5a35ad 100644 --- a/rehlds/public/interface.cpp +++ b/rehlds/public/interface.cpp @@ -1,26 +1,98 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ + #include "interface.h" +#ifdef _WIN32 + #define WIN32_LEAN_AND_MEAN + #include "windows.h" +#endif // _WIN32 -#if !defined ( _WIN32 ) +// InterfaceReg +InterfaceReg *InterfaceReg::s_pInterfaceRegs = nullptr; + +InterfaceReg::InterfaceReg(InstantiateInterfaceFn fn, const char *pName) : m_pName(pName) +{ + m_CreateFn = fn; + m_pNext = s_pInterfaceRegs; + s_pInterfaceRegs = this; +} + +// This is the primary exported function by a dll, referenced by name via dynamic binding +// that exposes an opqaue function pointer to the interface. +// +// We have the Internal variant so Sys_GetFactoryThis() returns the correct internal +// symbol under GCC/Linux/Mac as CreateInterface is DLL_EXPORT so its global so the loaders +// on those OS's pick exactly 1 of the CreateInterface symbols to be the one that is process wide and +// all Sys_GetFactoryThis() calls find that one, which doesn't work. Using the internal walkthrough here +// makes sure Sys_GetFactoryThis() has the dll specific symbol and GetProcAddress() returns the module specific +// function for CreateInterface again getting the dll specific symbol we need. +EXPORT_FUNCTION IBaseInterface *CreateInterface(const char *pName, int *pReturnCode) +{ + InterfaceReg *pCur; + for (pCur = InterfaceReg::s_pInterfaceRegs; pCur; pCur = pCur->m_pNext) + { + if (strcmp(pCur->m_pName, pName) == 0) + { + if (pReturnCode) + { + *pReturnCode = IFACE_OK; + } + + return pCur->m_CreateFn(); + } + } + + if (pReturnCode) + { + *pReturnCode = IFACE_FAILED; + } + + return nullptr; +} + +#ifndef _WIN32 // Linux doesn't have this function so this emulates its functionality -// -// void *GetModuleHandle(const char *name) { void *handle; - - if (name == NULL) + if (name == nullptr) { // hmm, how can this be handled under linux.... // is it even needed? - return NULL; + return nullptr; } - if ((handle=dlopen(name, RTLD_NOW)) == NULL) + if ((handle = dlopen(name, RTLD_NOW)) == nullptr) { //printf("Error:%s\n",dlerror()); // couldn't open this file - return NULL; + return nullptr; } // read "man dlopen" for details @@ -29,121 +101,39 @@ void *GetModuleHandle(const char *name) dlclose(handle); return handle; } -#endif +#endif // _WIN32 -// ------------------------------------------------------------------------------------ // -// InterfaceReg. -// ------------------------------------------------------------------------------------ // -InterfaceReg *InterfaceReg::s_pInterfaceRegs = NULL; - - -InterfaceReg::InterfaceReg( InstantiateInterfaceFn fn, const char *pName ) : m_pName(pName) -{ - m_CreateFn = fn; - m_pNext = s_pInterfaceRegs; - s_pInterfaceRegs = this; -} - - - -// ------------------------------------------------------------------------------------ // -// CreateInterface. -// ------------------------------------------------------------------------------------ // -EXPORT_FUNCTION IBaseInterface *CreateInterface( const char *pName, int *pReturnCode ) -{ - InterfaceReg *pCur; - - for(pCur=InterfaceReg::s_pInterfaceRegs; pCur; pCur=pCur->m_pNext) - { - if(strcmp(pCur->m_pName, pName) == 0) - { - if ( pReturnCode ) - { - *pReturnCode = IFACE_OK; - } - return pCur->m_CreateFn(); - } - } - - if ( pReturnCode ) - { - *pReturnCode = IFACE_FAILED; - } - return NULL; -} - -#ifdef LINUX -static IBaseInterface *CreateInterfaceLocal( const char *pName, int *pReturnCode ) -{ - InterfaceReg *pCur; - - for(pCur=InterfaceReg::s_pInterfaceRegs; pCur; pCur=pCur->m_pNext) - { - if(strcmp(pCur->m_pName, pName) == 0) - { - if ( pReturnCode ) - { - *pReturnCode = IFACE_OK; - } - return pCur->m_CreateFn(); - } - } - - if ( pReturnCode ) - { - *pReturnCode = IFACE_FAILED; - } - return NULL; -} -#endif - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include "windows.h" -#endif - -//----------------------------------------------------------------------------- // Purpose: returns a pointer to a function, given a module // Input : pModuleName - module name // *pName - proc name -//----------------------------------------------------------------------------- -//static hlds_run wants to use this function -void *Sys_GetProcAddress( const char *pModuleName, const char *pName ) +//static hlds_run wants to use this function +void *Sys_GetProcAddress(const char *pModuleName, const char *pName) { - return GetProcAddress( GetModuleHandle(pModuleName), pName ); + return GetProcAddress(GetModuleHandle(pModuleName), pName); } -//----------------------------------------------------------------------------- // Purpose: returns a pointer to a function, given a module // Input : pModuleName - module name // *pName - proc name -//----------------------------------------------------------------------------- -// hlds_run wants to use this function -void *Sys_GetProcAddress( void *pModuleHandle, const char *pName ) +// hlds_run wants to use this function +void *Sys_GetProcAddress(void *pModuleHandle, const char *pName) { -#if defined ( _WIN32 ) - return GetProcAddress( (HINSTANCE)pModuleHandle, pName ); -#else - return GetProcAddress( pModuleHandle, pName ); -#endif + return GetProcAddress((HMODULE)pModuleHandle, pName); } -//----------------------------------------------------------------------------- // Purpose: Loads a DLL/component from disk and returns a handle to it // Input : *pModuleName - filename of the component // Output : opaque handle to the module (hides system dependency) -//----------------------------------------------------------------------------- CSysModule *Sys_LoadModule(const char *pModuleName) { -#if defined (_WIN32) +#ifdef _WIN32 HMODULE hDLL = LoadLibrary(pModuleName); #else - HMODULE hDLL = NULL; + HMODULE hDLL = nullptr; + char szAbsoluteModuleName[1024]; if (pModuleName[0] != '/') { char szCwd[1024]; - char szAbsoluteModuleName[1024]; - getcwd(szCwd, sizeof(szCwd)); if (szCwd[strlen(szCwd) - 1] == '/') szCwd[strlen(szCwd) - 1] = '\0'; @@ -153,118 +143,94 @@ CSysModule *Sys_LoadModule(const char *pModuleName) } else { + _snprintf(szAbsoluteModuleName, sizeof(szAbsoluteModuleName), "%s", pModuleName); hDLL = dlopen(pModuleName, RTLD_NOW); } -#endif +#endif // _WIN32 - if(!hDLL) + if (!hDLL) { - char szPathModule[MAX_PATH]; - char szModuleName[64]; + char str[512]; - strncpy(szModuleName, pModuleName, sizeof szModuleName - 1); - szModuleName[sizeof szModuleName - 1] = '\0'; - - // remove extension if provided. - char *ext = strrchr(szModuleName, '.'); - if (ext) { - *ext = '\0'; - } - -#if defined (_WIN32) - _snprintf(szPathModule, sizeof(szPathModule), "%s.dll", szModuleName); - hDLL = LoadLibrary(szPathModule); +#if defined(_WIN32) + _snprintf(str, sizeof(str), "%s.dll", pModuleName); + hDLL = LoadLibrary(str); #elif defined(OSX) printf("Error: %s\n", dlerror()); - _snprintf(szPathModule, sizeof(szPathModule), "%s.dylib", szModuleName); - hDLL = dlopen(szPathModule, RTLD_NOW); + _snprintf(str, sizeof(str), "%s.dylib", szAbsoluteModuleName); + hDLL = dlopen(str, RTLD_NOW); #else printf("Error: %s\n", dlerror()); - _snprintf(szPathModule, sizeof(szPathModule), "%s.so", szModuleName); - hDLL = dlopen(szPathModule, RTLD_NOW); + _snprintf(str, sizeof(str), "%s.so", szAbsoluteModuleName); + hDLL = dlopen(str, RTLD_NOW); #endif } return reinterpret_cast(hDLL); } -//----------------------------------------------------------------------------- // Purpose: Unloads a DLL/component from // Input : *pModuleName - filename of the component // Output : opaque handle to the module (hides system dependency) -//----------------------------------------------------------------------------- -void Sys_UnloadModule( CSysModule *pModule ) +void Sys_UnloadModule(CSysModule *pModule) { - if ( !pModule ) + if (!pModule) return; HMODULE hDLL = reinterpret_cast(pModule); -#if defined ( _WIN32 ) - FreeLibrary( hDLL ); -#else - dlclose((void *)hDLL); -#endif +#ifdef _WIN32 + FreeLibrary(hDLL); +#else + dlclose(hDLL); +#endif // _WIN32 } -//----------------------------------------------------------------------------- // Purpose: returns a pointer to a function, given a module -// Input : module - windows HMODULE from Sys_LoadModule() +// Input : module - windows HMODULE from Sys_LoadModule() // *pName - proc name // Output : factory for this module -//----------------------------------------------------------------------------- -CreateInterfaceFn Sys_GetFactory( CSysModule *pModule ) +CreateInterfaceFn Sys_GetFactory(CSysModule *pModule) { - if ( !pModule ) - return NULL; + if (!pModule) + return nullptr; - HMODULE hDLL = reinterpret_cast(pModule); -#if defined ( _WIN32 ) - return reinterpret_cast(GetProcAddress( hDLL, CREATEINTERFACE_PROCNAME )); -#else -// Linux gives this error: -//../public/interface.cpp: In function `IBaseInterface *(*Sys_GetFactory -//(CSysModule *)) (const char *, int *)': -//../public/interface.cpp:154: ISO C++ forbids casting between -//pointer-to-function and pointer-to-object -// -// so lets get around it :) - return (CreateInterfaceFn)(GetProcAddress( hDLL, CREATEINTERFACE_PROCNAME )); -#endif + return reinterpret_cast(Sys_GetProcAddress(pModule, CREATEINTERFACE_PROCNAME)); } - - -//----------------------------------------------------------------------------- // Purpose: returns the instance of this module -// Output : interface_instance_t -//----------------------------------------------------------------------------- -CreateInterfaceFn Sys_GetFactoryThis( void ) +// Output : CreateInterfaceFn +CreateInterfaceFn Sys_GetFactoryThis() { -#ifdef LINUX - return CreateInterfaceLocal; -#else return CreateInterface; -#endif } -//----------------------------------------------------------------------------- // Purpose: returns the instance of the named module // Input : *pModuleName - name of the module -// Output : interface_instance_t - instance of that module -//----------------------------------------------------------------------------- -CreateInterfaceFn Sys_GetFactory( const char *pModuleName ) +// Output : CreateInterfaceFn - instance of that module +CreateInterfaceFn Sys_GetFactory(const char *pModuleName) { -#if defined ( _WIN32 ) - return static_cast( Sys_GetProcAddress( pModuleName, CREATEINTERFACE_PROCNAME ) ); -#else -// Linux gives this error: -//../public/interface.cpp: In function `IBaseInterface *(*Sys_GetFactory -//(const char *)) (const char *, int *)': -//../public/interface.cpp:186: invalid static_cast from type `void *' to -//type `IBaseInterface *(*) (const char *, int *)' -// -// so lets use the old style cast. - return (CreateInterfaceFn)( Sys_GetProcAddress( pModuleName, CREATEINTERFACE_PROCNAME ) ); -#endif + return reinterpret_cast(Sys_GetProcAddress(pModuleName, CREATEINTERFACE_PROCNAME)); +} + +// Purpose: finds a particular interface in the factory set +void *InitializeInterface(char const *interfaceName, CreateInterfaceFn *factoryList, int numFactories) +{ + void *retval; + + for (int i = 0; i < numFactories; i++) + { + CreateInterfaceFn factory = factoryList[ i ]; + if (!factory) + continue; + + retval = factory(interfaceName, nullptr); + if (retval) + return retval; + } + + // No provider for requested interface!!! + // assert(!"No provider for requested interface!!!"); + + return nullptr; } diff --git a/rehlds/public/interface.h b/rehlds/public/interface.h index cdd361e..dc424be 100644 --- a/rehlds/public/interface.h +++ b/rehlds/public/interface.h @@ -1,4 +1,3 @@ - // This header defines the interface convention used in the valve engine. // To make an interface and expose it: // 1. Derive from IBaseInterface. @@ -14,16 +13,11 @@ // for legacy code). In this case, you need to make a new version name for your new interface, and make a wrapper interface and // expose it for the old interface. -//#if _MSC_VER >= 1300 // VC7 -//#include "tier1/interface.h" -//#else +#pragma once -#ifndef INTERFACE_H -#define INTERFACE_H +#ifndef _WIN32 -#if !defined ( _WIN32 ) - -#include // dlopen,dlclose, et al +#include // dlopen, dlclose, et al #include #define HMODULE void * @@ -31,7 +25,7 @@ #define _snprintf snprintf -#endif +#endif // _WIN32 void *Sys_GetProcAddress(const char *pModuleName, const char *pName); void *Sys_GetProcAddress(void *pModuleHandle, const char *pName); @@ -40,17 +34,13 @@ void *Sys_GetProcAddress(void *pModuleHandle, const char *pName); class IBaseInterface { public: - - virtual ~IBaseInterface() {} + virtual ~IBaseInterface() {} }; +#define CREATEINTERFACE_PROCNAME "CreateInterface" -#define CREATEINTERFACE_PROCNAME "CreateInterface" -typedef IBaseInterface* (*CreateInterfaceFn)(const char *pName, int *pReturnCode); - - -typedef IBaseInterface* (*InstantiateInterfaceFn)(); - +typedef IBaseInterface *(*CreateInterfaceFn)(const char *pName, int *pReturnCode); +typedef IBaseInterface *(*InstantiateInterfaceFn)(); // Used internally to register classes. class InterfaceReg @@ -60,19 +50,18 @@ public: public: - InstantiateInterfaceFn m_CreateFn; - const char *m_pName; + InstantiateInterfaceFn m_CreateFn; + const char *m_pName; - InterfaceReg *m_pNext; // For the global list. - static InterfaceReg *s_pInterfaceRegs; + InterfaceReg *m_pNext; // For the global list. + static InterfaceReg *s_pInterfaceRegs; }; - // Use this to expose an interface that can have multiple instances. // e.g.: -// EXPOSE_INTERFACE( CInterfaceImp, IInterface, "MyInterface001" ) +// EXPOSE_INTERFACE(CInterfaceImp, IInterface, "MyInterface001") // This will expose a class called CInterfaceImp that implements IInterface (a pure class) -// clients can receive a pointer to this class by calling CreateInterface( "MyInterface001" ) +// clients can receive a pointer to this class by calling CreateInterface("MyInterface001") // // In practice, the shared header file defines the interface (IInterface) and version name ("MyInterface001") // so that each component can use these names/vtables to communicate @@ -80,30 +69,28 @@ public: // A single class can support multiple interfaces through multiple inheritance // // Use this if you want to write the factory function. -#define EXPOSE_INTERFACE_FN(functionName, interfaceName, versionName) \ +#define EXPOSE_INTERFACE_FN(functionName, interfaceName, versionName)\ static InterfaceReg __g_Create##className##_reg(functionName, versionName); -#define EXPOSE_INTERFACE(className, interfaceName, versionName) \ - static IBaseInterface* __Create##className##_interface() {return (interfaceName *)new className;}\ - static InterfaceReg __g_Create##className##_reg(__Create##className##_interface, versionName ); +#define EXPOSE_INTERFACE(className, interfaceName, versionName)\ + static IBaseInterface *__Create##className##_interface() {return (interfaceName *)new className;}\ + static InterfaceReg __g_Create##className##_reg(__Create##className##_interface, versionName); // Use this to expose a singleton interface with a global variable you've created. -#define EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, globalVarName) \ - static IBaseInterface* __Create##className##interfaceName##_interface() {return (IBaseInterface *)&globalVarName;}\ +#define EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, globalVarName)\ + static IBaseInterface *__Create##className##interfaceName##_interface() {return (IBaseInterface *)&globalVarName;}\ static InterfaceReg __g_Create##className##interfaceName##_reg(__Create##className##interfaceName##_interface, versionName); // Use this to expose a singleton interface. This creates the global variable for you automatically. -#define EXPOSE_SINGLE_INTERFACE(className, interfaceName, versionName) \ +#define EXPOSE_SINGLE_INTERFACE(className, interfaceName, versionName)\ static className __g_##className##_singleton;\ EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, __g_##className##_singleton) - #ifdef _WIN32 #define EXPORT_FUNCTION __declspec(dllexport) #else - #define EXPORT_FUNCTION __attribute__ ((visibility("default"))) -#endif - + #define EXPORT_FUNCTION __attribute__((visibility("default"))) +#endif // _WIN32 // This function is automatically exported and allows you to access any interfaces exposed with the above macros. // if pReturnCode is set, it will return one of the following values @@ -114,37 +101,23 @@ enum IFACE_FAILED }; - extern "C" { - EXPORT_FUNCTION IBaseInterface* CreateInterface(const char *pName, int *pReturnCode); + EXPORT_FUNCTION IBaseInterface *CreateInterface(const char *pName, int *pReturnCode); }; +extern CreateInterfaceFn Sys_GetFactoryThis(); -extern CreateInterfaceFn Sys_GetFactoryThis( void ); - - -//----------------------------------------------------------------------------- // UNDONE: This is obsolete, use the module load/unload/get instead!!! -//----------------------------------------------------------------------------- -extern CreateInterfaceFn Sys_GetFactory( const char *pModuleName ); - +extern CreateInterfaceFn Sys_GetFactory(const char *pModuleName); // load/unload components class CSysModule; -//----------------------------------------------------------------------------- // Load & Unload should be called in exactly one place for each module // The factory for that module should be passed on to dependent components for // proper versioning. -//----------------------------------------------------------------------------- -extern CSysModule *Sys_LoadModule( const char *pModuleName ); -extern void Sys_UnloadModule( CSysModule *pModule ); - -extern CreateInterfaceFn Sys_GetFactory( CSysModule *pModule ); - - -#endif -//#endif // MSVC 6.0 - - +extern CSysModule *Sys_LoadModule(const char *pModuleName); +extern void Sys_UnloadModule(CSysModule *pModule); +extern CreateInterfaceFn Sys_GetFactory(CSysModule *pModule); +extern void *InitializeInterface(char const *interfaceName, CreateInterfaceFn *factoryList, int numFactories); diff --git a/rehlds/public/rehlds/custom.h b/rehlds/public/rehlds/custom.h index 7422638..d2ebd48 100644 --- a/rehlds/public/rehlds/custom.h +++ b/rehlds/public/rehlds/custom.h @@ -63,7 +63,14 @@ typedef struct resourceinfo_s typedef struct resource_s { +#ifdef HOOK_HLTV + // NOTE HLTV: array szFileName declared on 260 cell, + // this changes necessary for compatibility hookers. + char szFileName[MAX_PATH]; +#else char szFileName[MAX_QPATH]; // File name to download/precache. +#endif // HOOK_HLTV + resourcetype_t type; // t_sound, t_skin, t_model, t_decal. int nIndex; // For t_decals int nDownloadSize; // Size in Bytes if this must be downloaded. diff --git a/rehlds/public/savegame_version.h b/rehlds/public/savegame_version.h index 51eb768..9cbbd91 100644 --- a/rehlds/public/savegame_version.h +++ b/rehlds/public/savegame_version.h @@ -1,20 +1,35 @@ -//========= Copyright 1996-2001, Valve LLC, All rights reserved. ============ -// -// Purpose: -// -// $NoKeywords: $ -//============================================================================= +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* In addition, as a special exception, the author gives permission to +* link the code of this program with the Half-Life Game Engine ("HL +* Engine") and Modified Game Libraries ("MODs") developed by Valve, +* L.L.C ("Valve"). You must obey the GNU General Public License in all +* respects for all of the code used other than the HL Engine and MODs +* from Valve. If you modify this file, you may extend this exception +* to your version of the file, but you are not obligated to do so. If +* you do not wish to do so, delete this exception statement from your +* version. +* +*/ -#if !defined( SAVEGAME_VERSION_H ) -#define SAVEGAME_VERSION_H -#ifdef _WIN32 #pragma once -#endif #include "commonmacros.h" #define SAVEFILE_HEADER MAKEID('V','A','L','V') // little-endian "VALV" #define SAVEGAME_HEADER MAKEID('J','S','A','V') // little-endian "JSAV" -#define SAVEGAME_VERSION 0x0071 // Version 0.71 - -#endif // SAVEGAME_VERSION_H +#define SAVEGAME_VERSION 0x0071 // Version 0.71 diff --git a/rehlds/public/vgui/VGUI.h b/rehlds/public/vgui/VGUI.h index 50cf372..cdaed0d 100644 --- a/rehlds/public/vgui/VGUI.h +++ b/rehlds/public/vgui/VGUI.h @@ -1,12 +1,13 @@ -#ifndef VGUI_H -#define VGUI_H -#ifdef _WIN32 #pragma once -#endif + +#ifdef _WIN32 + #define VGUI2_LIB "vgui2.dll" +#else + #define VGUI2_LIB "vgui2.so" +#endif // _WIN32 namespace vgui2 { - // handle to an internal vgui panel // this is the only handle to a panel that is valid across dll boundaries typedef unsigned int VPANEL; @@ -26,6 +27,4 @@ const VPANEL NULL_PANEL = 0; const HFont INVALID_FONT = 0; const HPanel INVALID_PANEL = 0xffffffff; -} // namespace vgui - -#endif // VGUI_H +} // namespace vgui2 diff --git a/settings.gradle b/settings.gradle index 4879f1d..e8ab98c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -3,4 +3,5 @@ include 'dep/cppunitlite' include 'dep/bzip2' include 'rehlds' include 'rehlds/dedicated' +include 'rehlds/HLTV', 'rehlds/HLTV/Core', 'rehlds/HLTV/Proxy', 'rehlds/HLTV/Console', 'rehlds/HLTV/Director', 'rehlds/HLTV/DemoPlayer' include 'flightrec/decoder_api', 'flightrec/decoder' diff --git a/shared_gcc.gradle b/shared_gcc.gradle index 31159d8..2426a19 100644 --- a/shared_gcc.gradle +++ b/shared_gcc.gradle @@ -58,5 +58,6 @@ rootProject.ext.createGccConfig = { boolean release, BinaryKind binKind -> ) } + cfg.singleDefines('LINUX') return cfg } diff --git a/shared_icc.gradle b/shared_icc.gradle index 894a63c..036fa00 100644 --- a/shared_icc.gradle +++ b/shared_icc.gradle @@ -58,5 +58,6 @@ rootProject.ext.createIccConfig = { boolean release, BinaryKind binKind -> ) } + cfg.singleDefines('LINUX') return cfg }