Merge branch 'develop' into feature/new-exception-coby

This commit is contained in:
Edo 2022-02-10 14:25:07 +00:00 committed by GitHub
commit 333e2eb024
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 892 additions and 1049 deletions

View File

@ -12,7 +12,7 @@ on:
jobs: jobs:
build: build:
name: Build binaries name: Build binaries
runs-on: windows-latest runs-on: windows-2022
strategy: strategy:
matrix: matrix:
configuration: configuration:
@ -36,11 +36,11 @@ jobs:
lfs: false lfs: false
- name: Add msbuild to PATH - name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.0.2 uses: microsoft/setup-msbuild@v1.1
- name: Generate project files - name: Generate project files
#run: tools/premake5 vs2019 --ci-build #run: tools/premake5 vs2022 --ci-build --ac-disable
run: tools/premake5 vs2019 --ac-disable run: tools/premake5 vs2022 --ac-disable
- name: Set up problem matching - name: Set up problem matching
uses: ammaraskar/msvc-problem-matcher@master uses: ammaraskar/msvc-problem-matcher@master

2
.gitmodules vendored
View File

@ -25,7 +25,7 @@
[submodule "deps/protobuf"] [submodule "deps/protobuf"]
path = deps/protobuf path = deps/protobuf
url = https://github.com/google/protobuf.git url = https://github.com/google/protobuf.git
branch = 3.11.x branch = 3.17.x
[submodule "deps/udis86"] [submodule "deps/udis86"]
path = deps/udis86 path = deps/udis86
url = https://github.com/vmt/udis86.git url = https://github.com/vmt/udis86.git

317
Jenkinsfile vendored
View File

@ -1,317 +0,0 @@
#!groovy
/*
This is our new pipeline script to do all of the building in, of and around IW4x.
Here's what it is supposed to do:
- Make sure Modern Warfare 2 is installed (CI should provide the folder like a custom tool)
- Check out code from iw4x-data
- Build the IW4x client library (this code repository)
- Use iw4x.exe from the iw4x-data repository in order to build the zone files in iw4x-data
- Package the IW4x client with the newly built data files
At this point it is done building everything, however afterwards we want the build server to
also push the newly built files to an update repository, depending on the branch we're on.
- For "develop", release to the "iw4x-dev" branch on the repository server.
- For "master", release to the "iw4x" branch on the repository server.
I'm looking into how the logic of pipelining works in detail before deciding on whether to
throw in the IW4x Updater and the IW4x Node binaries in as well or not.
*/
/*
Note that this is just a rewrite of the jobs as they are currently set up on the production
Jenkins server. This will allow every developer to tinker around with how the build process
is set up. For those who want to play around with this, here's a bit of information:
- This is a Groovy script. Essentially Java but with less bullshit (like brackets and verbose writing).
- This gets directly translated into a Jenkins pipeline.
- If you have no idea how to handle scripts, get your hands off this file.
- If you do not use Jenkins, get your hands off this file.
- If you fuck this script up, I will kill you.
*/
import groovy.transform.Field
@Field def configurations = [
"Debug": [
WorkspaceID: "build@debug",
StashName: "iw4x-debug",
MSBuildConfiguration: "Debug",
PremakeArgs: "",
Archive: true,
],
"Release": [
WorkspaceID: "build@release",
StashName: "iw4x-release",
MSBuildConfiguration: "Release",
PremakeArgs: "",
Archive: true,
],
"Release with unit tests": [
WorkspaceID: "build@release+unittests",
StashName: "iw4x-release-unittests",
MSBuildConfiguration: "Release",
PremakeArgs: "--force-unit-tests",
Archive: false,
],
].collect {k, v -> [k, v]}
@Field def testing = [
"Debug": [
WorkspaceID: "testing@debug",
StashName: "iw4x-debug",
],
"Release": [
WorkspaceID: "testing@release",
StashName: "iw4x-release-unittests",
],
].collect {k, v -> [k, v]}
def jobWorkspace(id, f) {
ws("workspace/${env.JOB_NAME.replaceAll(/[%$]/, "_")}@$id", f)
}
def useShippedPremake(f) {
def premakeHome = "${pwd()}\\tools"
withEnv(["PATH+=${premakeHome}"], f)
}
def getIW4xExecutable() {
step([
$class: 'CopyArtifact',
filter: '*',
fingerprintArtifacts: true,
projectName: 'iw4x/iw4x-executable/' + iw4xExecutableBranch(),
selector: [
$class: 'TriggeredBuildSelector',
allowUpstreamDependencies: false,
fallbackToLastSuccessful: true,
upstreamFilterStrategy: 'UseGlobalSetting'
]
])
}
// This will build the IW4x client.
// We need a Windows Server with Visual Studio 2015, Premake5 and Git on it.
def doBuild(cfg) {
retry(5) {
checkout scm
}
useShippedPremake {
def outputDir = pwd()
def msbuild = tool "Microsoft.NET MSBuild 15.0"
bat "premake5 vs2017 ${cfg.PremakeArgs}"
bat "\"${msbuild}\" build\\iw4x.sln \"/p:OutDir=$outputDir\\\\\" \"/p:Configuration=${cfg.MSBuildConfiguration}\""
}
stash name: "${cfg.StashName}", includes: "*.dll,*.pdb"
}
// This will run the unit tests for IW4x.
// We need a Windows Server with MW2 on it.
def doUnitTests(name) {
mw2dir = tool "Modern Warfare 2"
unstash "$name"
// Get installed localization for correct zonefiles directory junction
def localization = readFile("${tool "Modern Warfare 2"}/localization.txt").split("\r?\n")[0]
try {
timeout(time: 10, unit: "MINUTES") {
// Set up environment
if (isUnix()) {
def mw2dir = tool "Modern Warfare 2"
sh """
mkdir -p zone
for f in main zone/dlc \"zone/$localization\"; do
ln -sfv \"$mw2dir/\$f\" \"\$f\"
done
for f in \"$mw2dir\"/*.dll \"$mw2dir\"/*.txt \"$mw2dir\"/*.bmp; do
ln -sfv \"\$f\" \"\$(basename \"\$f\")\"
done
"""
} else {
def mw2dir = tool "Modern Warfare 2"
bat """
mklink /J \"main\" \"$mw2dir\\main\"
mkdir \"zone\"
mklink /J \"zone\\dlc\" \"$mw2dir\\zone\\dlc\"
mklink /J \"zone\\$localization\" \"$mw2dir\\zone\\$localization\"
copy /y \"$mw2dir\\*.dll\"
copy /y \"$mw2dir\\*.txt\"
copy /y \"$mw2dir\\*.bmp\"
"""
}
// Run tests
getIW4xExecutable()
retry(2) {
if (isUnix()) {
sh "WINEDEBUG=warn+all wine iw4x.exe -tests; wineserver -w"
} else {
bat "iw4x.exe -tests"
}
}
}
} catch (org.jenkinsci.plugins.workflow.steps.FlowInterruptedException e) {
currentBuild.result = 'UNSTABLE'
println("${name} unit test interrupted (ran too long?)")
} catch (Exception e) {
println("${name} unit test failed.")
if (isUnix()) {
currentBuild.result = 'UNSTABLE'
} else {
throw e
}
} finally {
// In all cases make sure to at least remove the directory junctions!
if (!isUnix()) {
bat """
rmdir \"main\"
rmdir \"zone\\dlc\"
rmdir \"zone\\$localization\"
"""
}
deleteDir()
}
}
// Returns the IW4x executable branch to use
def iw4xExecutableBranch() {
try {
return IW4X_EXECUTABLE_BRANCH;
} catch(MissingPropertyException) {
return "master";
}
}
// Job properties
properties([
buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '30')),
disableConcurrentBuilds(),
gitLabConnection('iw4x'),
[$class: 'LeastLoadDisabledProperty', leastLoadDisabled: false]
])
gitlabBuilds(builds: ["Checkout & Versioning", "Build", "Testing", "Archiving"]) {
// First though let's give this build a proper name
stage("Checkout & Versioning") {
gitlabCommitStatus(name: "Checkout & Versioning") {
node("windows") {
jobWorkspace("versioning") {
if (env.BRANCH_NAME == 'master')
{
echo 'Reset build environment'
deleteDir()
}
retry(5) {
checkout scm
}
useShippedPremake {
def version = bat(returnStdout: true, script: '@premake5 version').split("\r?\n")[2]
currentBuild.setDisplayName "$version (#${env.BUILD_NUMBER})"
}
stash name: "jenkins-files", includes: "jenkins/**"
}
}
}
}
// For each available configuration generate a normal build and a unit test build.
stage("Build") {
gitlabCommitStatus(name: "Build") {
def executions = [:]
for (int i = 0; i < configurations.size(); i++) {
def entry = configurations[i]
def configName = entry[0]
def config = entry[1]
executions[configName] = {
node("windows") {
jobWorkspace(config.WorkspaceID) {
doBuild(config)
}
}
}
}
parallel executions
}
}
// Run unit tests on each configuration.
stage("Testing") {
gitlabCommitStatus(name: "Testing") {
executions = [:]
for (int i = 0; i < testing.size(); i++) {
def entry = testing.get(i)
def testName = entry[0]
def test = entry[1]
executions["$testName on Windows"] = {
node("windows") {
jobWorkspace(test.WorkspaceID) {
doUnitTests(test.StashName)
}
}
}
executions["$testName on Linux"] = {
node("docker && linux && amd64") {
timeout(time: 10, unit: "MINUTES") {
wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
def image = null
dir("src") {
unstash "jenkins-files"
image = docker.build("github.com/IW4x/iw4x-client-testing-wine32", "--rm --force-rm -f jenkins/wine32.Dockerfile jenkins")
deleteDir()
}
image.inside {
doUnitTests(test.StashName)
}
}
}
}
}
parallel executions
}
}
}
// Collect all the binaries and give each configuration its own subfolder
stage("Archiving") {
gitlabCommitStatus(name: "Archiving") {
node("windows") { // any node will do
jobWorkspace("archiving") {
try {
for (int i = 0; i < configurations.size(); i++) {
def entry = configurations[i]
def configName = entry[0]
def config = entry[1]
if (config.Archive) {
dir(configName) {
unstash config.StashName
}
}
}
archiveArtifacts artifacts: "**/*.dll,**/*.pdb", fingerprint: true
} finally {
deleteDir()
}
}
}
}
}
}

View File

@ -11,8 +11,8 @@
## How to compile ## How to compile
- Run `premake5 vs2019` or use the delivered `generate.bat`. - Run `premake5 vs2022` or use the delivered `generate.bat`.
- Build via solution file in `build\iw4x.sln`. (You can use the `build.bat` script to do it quick and easy.) - Build via solution file in `build\iw4x.sln`.
## Premake arguments ## Premake arguments
@ -33,24 +33,22 @@
## Command line arguments ## Command line arguments
| Argument | Description | | Argument | Description |
|:----------------------------|:-----------------------------------------------| |:------------------------|:-----------------------------------------------|
| `-tests` | Perform unit tests. | | `-tests` | Perform unit tests. |
| `-entries` | Prints fast file info to the console. | | `-entries` | Print to the console a list of every asset as they are loaded from zonefiles. |
| `-stdout` | Redirect stdout to the external console. | | `-stdout` | Redirect all logging output to the terminal iw4x is started from, or if there is none, creates a new terminal window to write log information in. |
| `-console` | Enables external console. | | `-console` | Allow the game to display its own separate interactive console window. |
| `-dedicated` | Dedicated server. | | `-dedicated` | Starts the game as a headless dedicated server. |
| `-scriptablehttp` | Adds HTTP console commands. | | `-scriptablehttp` | Enable HTTP related gsc functions. |
| `-bigdumps` | Enables dumps. | | `-bigminidumps` | Include all code sections from loaded modules in the dump. |
| `-reallybigdumps` | Unused. | | `-reallybigminidumps` | Include data sections from all loaded modules in the dump. |
| `-bigminidumps` | Mini dumps. | | `-dump` | Write info of loaded assets to the raw folder as they are being loaded. |
| `-reallybigminidumps` | Big mini dumps. | | `-monitor` | This flag is for internal use and it is used to indicate if an external console is present. |
| `-dump` | Prints asset info to a .ents file. | | `-nointro` | Skip game's cinematic intro. |
| `-monitor` | Enables monitor. | | `-version` | Print IW4x build info on startup. |
| `-nointro` | Skips game's intro. | | `-zonebuilder` | Start the interactive zonebuilder tool console instead of starting the game. |
| `-version` | Prints IW4X version. | | `-nosteam` | Disable friends feature and do not update Steam about the game's current status just like an invisible mode. |
| `-zonebuilder` | Enables zone builder. |
| `-nosteam` | Disables Steam features. |
## Disclaimer ## Disclaimer

View File

@ -4,8 +4,8 @@ version: "#{build} ({branch})"
environment: environment:
matrix: matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
PREMAKE_ACTION: vs2019 PREMAKE_ACTION: vs2022
configuration: configuration:
- Debug - Debug

View File

@ -1,24 +0,0 @@
@echo off & setlocal
cd %~dp0
if exist "%PROGRAMFILES(x86)%\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsMSBuildCmd.bat" call "%PROGRAMFILES(x86)%\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsMSBuildCmd.bat"
call msbuild /version >NUL 2>NUL
if errorlevel 0 goto:build
if exist "%PROGRAMFILES(x86)%\Microsoft Visual Studio\2019\Enterprise\MSBuild\15.0\Bin\msbuild.exe" path %PROGRAMFILES(x86)%\Microsoft Visual Studio\2019\Enterprise\MSBuild\15.0\Bin;%PATH%
call msbuild /version >NUL 2>NUL
if errorlevel 0 goto:build
echo Couldn't find any MSBuild to build this project.
echo Make sure you have Visual C++ Build Tools 2019 or Visual Studio 2019 installed.
endlocal
exit /B 1
:build
call generate.bat
set PLATFORM=Win32
set CONFIGURATION=Release
call msbuild /nologo /m /v:m %* build\iw4x.sln
endlocal
exit /B %ERRORLEVEL%

2
deps/libtommath vendored

@ -1 +1 @@
Subproject commit 2cec6addaf4acbd8250611ec4cecadf1f4d655ee Subproject commit bea9270646303baf683f4ba2ddf0d70721f0e55d

2
deps/pdcurses vendored

@ -1 +1 @@
Subproject commit f1cd4f4569451a5028ddf3d3c202f0ad6b1ae446 Subproject commit 2fa0f10dd844da47ee83c05a40a1ec541ceb95e1

2
deps/protobuf vendored

@ -1 +1 @@
Subproject commit df2bce345d4bc8cdc3eba2a866e11e79e1fff4df Subproject commit 5500c72c5b616da9f0125bcfab513987a1226e2b

2
deps/zlib vendored

@ -1 +1 @@
Subproject commit c3f3043f7aa80750245f8166a338c4877020b589 Subproject commit 2014a993addbc8f1b9785d97f55fd189792c2f78

View File

@ -1,4 +1,4 @@
@echo off @echo off
echo Updating submodules... echo Updating submodules...
call git submodule update --init --recursive call git submodule update --init --recursive
call tools\premake5 %* vs2019 --ac-disable call tools\premake5 %* vs2022 --ac-disable

View File

@ -1,46 +0,0 @@
# Requires a decent modern Docker version (v1.10.x at least ideally)
# Use semi-official Arch Linux image with fixed versioning
FROM archlinux/base
# Environment variables
ENV WINEPREFIX /wine32
ENV WINEARCH win32
ENV WINEDEBUG -all
# Install Wine (32-bit)
RUN \
echo -e "#!/bin/sh\nwine \$@\nretval=\$?\nwineserver -w\nexit \$retval" > /usr/local/bin/wine-wrapper &&\
chmod +x /usr/local/bin/wine-wrapper &&\
\
(\
echo '' &&\
echo '[multilib]' &&\
echo 'Include = /etc/pacman.d/mirrorlist'\
) >> /etc/pacman.conf &&\
pacman -Sy --noconfirm \
awk \
lib32-gnutls \
wine \
wget \
xorg-server-xvfb \
pacman-contrib \
awk \
&&\
\
wine-wrapper wineboot.exe -i &&\
wget -Ovcredist_x86.exe https://download.microsoft.com/download/d/d/9/dd9a82d0-52ef-40db-8dab-795376989c03/vcredist_x86.exe &&\
WINEDEBUG=+all-trace xvfb-run sh -c 'wine-wrapper vcredist_x86.exe /q' &&\
rm vcredist_x86.exe &&\
\
pacman -Rs --noconfirm \
xorg-server-xvfb \
wget \
&&\
\
find /. -name "*~" -type f -delete &&\
rm -rf /tmp/* /var/tmp/* /usr/share/man/* /usr/share/info/* /usr/share/doc/* &&\
pacman -Scc --noconfirm &&\
rm -rf /var/lib/pacman/sync/*
USER 0

View File

@ -1,54 +0,0 @@
iw4mvm = {
settings = nil
}
function iw4mvm.setup(settings)
if not settings.source then error("Missing source.") end
iw4mvm.settings = settings
if not iw4mvm.settings.defines then iw4mvm.settings.defines = {} end
end
function iw4mvm.import()
if not iw4mvm.settings then error("You need to call iw4mvm.setup first") end
links { "iw4mvm" }
iw4mvm.includes()
end
function iw4mvm.includes()
if not iw4mvm.settings then error("You need to call iw4mvm.setup first") end
includedirs { iw4mvm.settings.source }
libdirs { path.join(iw4mvm.settings.source, "IW4MVM") }
defines(iw4mvm.settings.defines)
end
function iw4mvm.project()
if not iw4mvm.settings then error("You need to call iw4mvm.setup first") end
project "iw4mvm"
language "C++"
characterset ("MBCS")
defines("_CRT_SECURE_NO_WARNINGS")
iw4mvm.includes()
files
{
path.join(iw4mvm.settings.source, "IW4MVM/*.h"),
path.join(iw4mvm.settings.source, "IW4MVM/*.cpp"),
}
removefiles
{
--path.join(iw4mvm.settings.source, "IW4MVM/detours.cpp"),
path.join(iw4mvm.settings.source, "IW4MVM/DllMain.cpp"),
}
-- not our code, ignore POSIX usage warnings for now
warnings "Off"
kind "StaticLib"
end

View File

@ -193,7 +193,6 @@ require "premake/pdcurses"
require "premake/protobuf" require "premake/protobuf"
require "premake/zlib" require "premake/zlib"
require "premake/udis86" require "premake/udis86"
require "premake/iw4mvm"
require "premake/dxsdk" require "premake/dxsdk"
json11.setup json11.setup
@ -240,15 +239,6 @@ udis86.setup
{ {
source = path.join(depsBasePath, "udis86"), source = path.join(depsBasePath, "udis86"),
} }
iw4mvm.setup
{
defines = {
"IW4X",
"DETOURS_X86",
"DETOURS_32BIT",
},
source = path.join(depsBasePath, "iw4mvm"),
}
dxsdk.setup dxsdk.setup
{ {
source = path.join(depsBasePath, "dxsdk"), source = path.join(depsBasePath, "dxsdk"),
@ -260,34 +250,44 @@ workspace "iw4x"
objdir "%{wks.location}/obj" objdir "%{wks.location}/obj"
targetdir "%{wks.location}/bin/%{cfg.buildcfg}" targetdir "%{wks.location}/bin/%{cfg.buildcfg}"
buildlog "%{wks.location}/obj/%{cfg.architecture}/%{cfg.buildcfg}/%{prj.name}/%{prj.name}.log" buildlog "%{wks.location}/obj/%{cfg.architecture}/%{cfg.buildcfg}/%{prj.name}/%{prj.name}.log"
configurations { "Debug", "Release" } configurations { "Debug", "Release" }
language "C++"
cppdialect "C++17"
architecture "x86" architecture "x86"
platforms "x86" platforms "x86"
--exceptionhandling ("SEH")
systemversion "latest"
symbols "On"
staticruntime "On" staticruntime "On"
editandcontinue "Off"
warnings "Extra"
characterset "ASCII"
configuration "windows" flags { "NoIncrementalLink", "NoMinimalRebuild", "MultiProcessorCompile", "No64BitChecks" }
defines { "_WINDOWS", "WIN32" }
configuration "Release*" filter "platforms:x86"
defines { "NDEBUG" } defines {"_WINDOWS", "WIN32"}
flags { "MultiProcessorCompile", "LinkTimeOptimization", "No64BitChecks" } filter {}
filter "configurations:Release"
optimize "On" optimize "On"
buildoptions { "/GL" }
linkoptions { "/IGNORE:4702", "/LTCG" }
defines { "NDEBUG" }
flags { "FatalCompileWarnings", "FatalLinkWarnings" }
if not _OPTIONS["force-unit-tests"] then if not _OPTIONS["force-unit-tests"] then
rtti ("Off") rtti ("Off")
end end
filter {}
configuration "Debug*" filter "configurations:Debug"
defines { "DEBUG", "_DEBUG" }
flags { "MultiProcessorCompile", "No64BitChecks" }
optimize "Debug" optimize "Debug"
if symbols ~= nil then defines { "DEBUG", "_DEBUG" }
symbols "On" filter {}
else
flags { "Symbols" }
end
project "iw4x" project "iw4x"
kind "SharedLib" kind "SharedLib"
@ -347,7 +347,6 @@ workspace "iw4x"
protobuf.import() protobuf.import()
zlib.import() zlib.import()
udis86.import() udis86.import()
--iw4mvm.import()
dxsdk.import() dxsdk.import()
-- fix vpaths for protobuf sources -- fix vpaths for protobuf sources
@ -401,23 +400,6 @@ workspace "iw4x"
} }
end end
-- Specific configurations
flags { "UndefinedIdentifiers" }
warnings "Extra"
if symbols ~= nil then
symbols "On"
else
flags { "Symbols" }
end
configuration "Release*"
flags {
"FatalCompileWarnings",
"FatalLinkWarnings",
}
configuration {}
--[[ --[[
-- Generate source code from protobuf definitions -- Generate source code from protobuf definitions
rules { "ProtobufCompiler" } rules { "ProtobufCompiler" }
@ -461,11 +443,6 @@ workspace "iw4x"
protobuf.project() protobuf.project()
zlib.project() zlib.project()
udis86.project() udis86.project()
--iw4mvm.project()
workspace "*"
cppdialect "C++17"
defines { "_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS" }
rule "ProtobufCompiler" rule "ProtobufCompiler"
display "Protobuf compiler" display "Protobuf compiler"

View File

@ -48,9 +48,6 @@ namespace Components
Loader::Register(new Party()); Loader::Register(new Party());
Loader::Register(new Zones()); Loader::Register(new Zones());
Loader::Register(new D3D9Ex()); Loader::Register(new D3D9Ex());
#if (!defined(VLD_RPTHOOK_INSTALL) || defined(VLDEnable)) && defined(COMPILE_IW4MVM) // IW4MVM uses detours which produces memory leaks, but those are not really relevant
Loader::Register(new IW4MVM());
#endif
Loader::Register(new Logger()); Loader::Register(new Logger());
Loader::Register(new Script()); Loader::Register(new Script());
Loader::Register(new Weapon()); Loader::Register(new Weapon());

View File

@ -88,7 +88,6 @@ namespace Components
#include "Modules/Node.hpp" #include "Modules/Node.hpp"
#include "Modules/RCon.hpp" #include "Modules/RCon.hpp"
#include "Modules/Party.hpp" // Destroys the order, but requires network classes :D #include "Modules/Party.hpp" // Destroys the order, but requires network classes :D
#include "Modules/IW4MVM.hpp"
#include "Modules/Logger.hpp" #include "Modules/Logger.hpp"
#include "Modules/Friends.hpp" #include "Modules/Friends.hpp"
#include "Modules/IPCPipe.hpp" #include "Modules/IPCPipe.hpp"

View File

@ -634,7 +634,7 @@ namespace Components
LUID luid; LUID luid;
TOKEN_PRIVILEGES tp = { 0 }; TOKEN_PRIVILEGES tp = { 0 };
DWORD cb = sizeof(TOKEN_PRIVILEGES); DWORD cb = sizeof(TOKEN_PRIVILEGES);
if (!LookupPrivilegeValueW(nullptr, SE_DEBUG_NAME, &luid)) return; if (!LookupPrivilegeValueA(nullptr, SE_DEBUG_NAME, &luid)) return;
tp.PrivilegeCount = 1; tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid; tp.Privileges[0].Luid = luid;

View File

@ -3,7 +3,7 @@
namespace Components namespace Components
{ {
thread_local int AssetHandler::BypassState = 0; thread_local int AssetHandler::BypassState = 0;
bool AssetHandler::ShouldSearchTempAssets = false; bool AssetHandler::ShouldSearchTempAssets = false;
std::map<Game::XAssetType, AssetHandler::IAsset*> AssetHandler::AssetInterfaces; std::map<Game::XAssetType, AssetHandler::IAsset*> AssetHandler::AssetInterfaces;
std::map<Game::XAssetType, Utils::Slot<AssetHandler::Callback>> AssetHandler::TypeCallbacks; std::map<Game::XAssetType, Utils::Slot<AssetHandler::Callback>> AssetHandler::TypeCallbacks;
Utils::Signal<AssetHandler::RestrictCallback> AssetHandler::RestrictSignal; Utils::Signal<AssetHandler::RestrictCallback> AssetHandler::RestrictSignal;
@ -70,20 +70,20 @@ namespace Components
return header; return header;
} }
Game::XAssetHeader AssetHandler::FindTemporaryAsset(Game::XAssetType type, const char* filename) Game::XAssetHeader AssetHandler::FindTemporaryAsset(Game::XAssetType type, const char* filename)
{ {
Game::XAssetHeader header = { nullptr }; Game::XAssetHeader header = { nullptr };
if (type >= Game::XAssetType::ASSET_TYPE_COUNT) return header; if (type >= Game::XAssetType::ASSET_TYPE_COUNT) return header;
auto tempPool = &AssetHandler::TemporaryAssets[type]; auto tempPool = &AssetHandler::TemporaryAssets[type];
auto entry = tempPool->find(filename); auto entry = tempPool->find(filename);
if (entry != tempPool->end()) if (entry != tempPool->end())
{ {
header = { entry->second }; header = { entry->second };
} }
return header; return header;
} }
int AssetHandler::HasThreadBypass() int AssetHandler::HasThreadBypass()
{ {
@ -119,7 +119,6 @@ namespace Components
push esi push esi
push edi push edi
push eax push eax
pushad pushad
@ -130,14 +129,12 @@ namespace Components
popad popad
pop eax pop eax
test al, al test al, al
jnz checkTempAssets jnz checkTempAssets
mov ecx, [esp + 18h] // Asset type mov ecx, [esp + 18h] // Asset type
mov ebx, [esp + 1Ch] // Filename mov ebx, [esp + 1Ch] // Filename
push eax push eax
pushad pushad
@ -152,31 +149,30 @@ namespace Components
popad popad
pop eax pop eax
test eax, eax
jnz finishFound
checkTempAssets:
mov al, AssetHandler::ShouldSearchTempAssets // check to see if enabled
test eax, eax
jz finishOriginal
mov ecx, [esp + 18h] // Asset type
mov ebx, [esp + 1Ch] // Filename
push ebx
push ecx
call AssetHandler::FindTemporaryAsset
add esp, 8h
test eax, eax test eax, eax
jnz finishFound jnz finishFound
checkTempAssets: finishOriginal:
mov al, AssetHandler::ShouldSearchTempAssets // check to see if enabled
test eax, eax
jz finishOriginal
mov ecx, [esp + 18h] // Asset type
mov ebx, [esp + 1Ch] // Filename
push ebx
push ecx
call AssetHandler::FindTemporaryAsset
add esp, 8h
test eax, eax
jnz finishFound
finishOriginal:
// Asset not found using custom handlers or in temp assets or bypasses were enabled // Asset not found using custom handlers or in temp assets or bypasses were enabled
// redirect to DB_FindXAssetHeader // redirect to DB_FindXAssetHeader
mov ebx, ds:6D7190h // InterlockedDecrement mov ebx, ds:6D7190h // InterlockedDecrement
mov eax, 40793Bh mov eax, 40793Bh
jmp eax jmp eax
@ -546,10 +542,10 @@ namespace Components
for (int i = 0; i < vertexdecl->streamCount; i++) for (int i = 0; i < vertexdecl->streamCount; i++)
{ {
routingData.push_back(json11::Json::object routingData.push_back(json11::Json::object
{ {
{ "source", (int)vertexdecl->routing.data[i].source }, { "source", (int)vertexdecl->routing.data[i].source },
{ "dest", (int)vertexdecl->routing.data[i].dest }, { "dest", (int)vertexdecl->routing.data[i].dest },
}); });
} }
std::vector<json11::Json> declData; std::vector<json11::Json> declData;
@ -770,7 +766,7 @@ namespace Components
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_LEADERBOARD, 500); Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_LEADERBOARD, 500);
AssetHandler::RegisterInterface(new Assets::IFont_s()); AssetHandler::RegisterInterface(new Assets::IFont_s());
AssetHandler::RegisterInterface(new Assets::IWeapon()); AssetHandler::RegisterInterface(new Assets::IWeapon());
AssetHandler::RegisterInterface(new Assets::IXModel()); AssetHandler::RegisterInterface(new Assets::IXModel());
AssetHandler::RegisterInterface(new Assets::IFxWorld()); AssetHandler::RegisterInterface(new Assets::IFxWorld());
AssetHandler::RegisterInterface(new Assets::IMapEnts()); AssetHandler::RegisterInterface(new Assets::IMapEnts());
@ -781,9 +777,9 @@ namespace Components
AssetHandler::RegisterInterface(new Assets::ISndCurve()); AssetHandler::RegisterInterface(new Assets::ISndCurve());
AssetHandler::RegisterInterface(new Assets::IMaterial()); AssetHandler::RegisterInterface(new Assets::IMaterial());
AssetHandler::RegisterInterface(new Assets::IMenuList()); AssetHandler::RegisterInterface(new Assets::IMenuList());
AssetHandler::RegisterInterface(new Assets::IclipMap_t()); AssetHandler::RegisterInterface(new Assets::IclipMap_t());
AssetHandler::RegisterInterface(new Assets::ImenuDef_t()); AssetHandler::RegisterInterface(new Assets::ImenuDef_t());
AssetHandler::RegisterInterface(new Assets::ITracerDef()); AssetHandler::RegisterInterface(new Assets::ITracerDef());
AssetHandler::RegisterInterface(new Assets::IPhysPreset()); AssetHandler::RegisterInterface(new Assets::IPhysPreset());
AssetHandler::RegisterInterface(new Assets::IXAnimParts()); AssetHandler::RegisterInterface(new Assets::IXAnimParts());
AssetHandler::RegisterInterface(new Assets::IFxEffectDef()); AssetHandler::RegisterInterface(new Assets::IFxEffectDef());

View File

@ -39,13 +39,13 @@ namespace Components
static void ResetBypassState(); static void ResetBypassState();
static void ExposeTemporaryAssets(bool expose); static void ExposeTemporaryAssets(bool expose);
static void OffsetToAlias(Utils::Stream::Offset* offset); static void OffsetToAlias(Utils::Stream::Offset* offset);
private: private:
static thread_local int BypassState; static thread_local int BypassState;
static bool ShouldSearchTempAssets; static bool ShouldSearchTempAssets;
static std::map<std::string, Game::XAssetHeader> TemporaryAssets[Game::XAssetType::ASSET_TYPE_COUNT]; static std::map<std::string, Game::XAssetHeader> TemporaryAssets[Game::XAssetType::ASSET_TYPE_COUNT];
@ -60,7 +60,7 @@ namespace Components
static void RegisterInterface(IAsset* iAsset); static void RegisterInterface(IAsset* iAsset);
static Game::XAssetHeader FindAsset(Game::XAssetType type, const char* filename); static Game::XAssetHeader FindAsset(Game::XAssetType type, const char* filename);
static Game::XAssetHeader FindTemporaryAsset(Game::XAssetType type, const char* filename); static Game::XAssetHeader FindTemporaryAsset(Game::XAssetType type, const char* filename);
static bool IsAssetEligible(Game::XAssetType type, Game::XAssetHeader* asset); static bool IsAssetEligible(Game::XAssetType type, Game::XAssetHeader* asset);
static void FindAssetStub(); static void FindAssetStub();
static void AddAssetStub(); static void AddAssetStub();

View File

@ -6,6 +6,8 @@ namespace Components
bool Bans::IsBanned(Bans::Entry entry) bool Bans::IsBanned(Bans::Entry entry)
{ {
std::lock_guard<std::recursive_mutex> _(Bans::AccessMutex);
Bans::BanList list; Bans::BanList list;
Bans::LoadBans(&list); Bans::LoadBans(&list);

View File

@ -136,6 +136,41 @@ namespace Components
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65, Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65,
(ent->flags & Game::FL_NOTARGET) ? "GAME_NOTARGETON" : "GAME_NOTARGETOFF")); (ent->flags & Game::FL_NOTARGET) ? "GAME_NOTARGETON" : "GAME_NOTARGETOFF"));
}); });
ClientCommand::Add("setviewpos", [](Game::gentity_s* ent)
{
assert(ent != nullptr);
if (!ClientCommand::CheatsOk(ent))
return;
Command::ServerParams params = {};
Game::vec3_t origin, angles{0.f, 0.f, 0.f};
if (params.length() < 4u || params.length() > 6u)
{
Game::SV_GameSendServerCommand(ent->s.number, 0,
Utils::String::VA("%c \"GAME_USAGE\x15: setviewpos x y z [yaw] [pitch]\n\"", 0x65));
return;
}
for (auto i = 0; i < 3; i++)
{
origin[i] = std::strtof(params.get(i + 1), nullptr);
}
if (params.length() >= 5u)
{
angles[1] = std::strtof(params.get(4), nullptr); // Yaw
}
if (params.length() == 6u)
{
angles[0] = std::strtof(params.get(5), nullptr); // Pitch
}
Game::TeleportPlayer(ent, origin, angles);
});
} }
void ClientCommand::AddScriptFunctions() void ClientCommand::AddScriptFunctions()

View File

@ -18,14 +18,14 @@ namespace Components
return result; return result;
} }
char* Command::Params::operator[](size_t index) const char* Command::Params::operator[](size_t index)
{ {
return this->get(index); return this->get(index);
} }
char* Command::ClientParams::get(size_t index) const char* Command::ClientParams::get(size_t index)
{ {
if (index >= this->length()) return const_cast<char*>(""); if (index >= this->length()) return "";
return Game::cmd_argv[this->commandId][index]; return Game::cmd_argv[this->commandId][index];
} }
@ -34,9 +34,9 @@ namespace Components
return Game::cmd_argc[this->commandId]; return Game::cmd_argc[this->commandId];
} }
char* Command::ServerParams::get(size_t index) const char* Command::ServerParams::get(size_t index)
{ {
if (index >= this->length()) return const_cast<char*>(""); if (index >= this->length()) return "";
return Game::cmd_argv_sv[this->commandId][index]; return Game::cmd_argv_sv[this->commandId][index];
} }
@ -160,52 +160,6 @@ namespace Components
{ {
AssertSize(Game::cmd_function_t, 24); AssertSize(Game::cmd_function_t, 24);
static int toastDurationShort = 1000;
static int toastDurationMedium = 2500;
static int toastDurationLong = 5000;
Command::Add("setviewpos", [](Command::Params* params)
{
int clientNum = Game::CG_GetClientNum();
if (!Game::CL_IsCgameInitialized() || clientNum >= 18 || clientNum < 0 || !Game::g_entities[clientNum].client)
{
Logger::Print("You are not hosting a match!\n");
Toast::Show("cardicon_stop", "Error", "You are not hosting a match!", toastDurationMedium);
return;
}
if (!Dvar::Var("sv_cheats").get<bool>())
{
Logger::Print("Cheats disabled!\n");
Toast::Show("cardicon_stop", "Error", "Cheats disabled!", toastDurationMedium);
return;
}
if (params->length() != 4 && params->length() != 6)
{
Logger::Print("Invalid coordinate specified!\n");
Toast::Show("cardicon_stop", "Error", "Invalid coordinate specified!", toastDurationMedium);
return;
}
float pos[3] = { 0.0f, 0.0f, 0.0f };
float orientation[3] = { 0.0f, 0.0f, 0.0f };
pos[0] = strtof(params->get(1), nullptr);
pos[1] = strtof(params->get(2), nullptr);
pos[2] = strtof(params->get(3), nullptr);
if (params->length() == 6)
{
orientation[0] = strtof(params->get(4), nullptr);
orientation[1] = strtof(params->get(5), nullptr);
}
Game::TeleportPlayer(&Game::g_entities[clientNum], pos, orientation);
// Logging will spam the console and screen if people use cinematics
});
Command::Add("openLink", [](Command::Params* params) Command::Add("openLink", [](Command::Params* params)
{ {
if (params->length() > 1) if (params->length() > 1)

View File

@ -10,11 +10,11 @@ namespace Components
public: public:
Params() {}; Params() {};
virtual ~Params() {}; virtual ~Params() {};
virtual char* get(size_t index) = 0; virtual const char* get(size_t index) = 0;
virtual size_t length() = 0; virtual size_t length() = 0;
virtual std::string join(size_t startIndex); virtual std::string join(size_t startIndex);
virtual char* operator[](size_t index); virtual const char* operator[](size_t index);
}; };
class ClientParams : public Params class ClientParams : public Params
@ -24,7 +24,7 @@ namespace Components
ClientParams(const ClientParams &obj) : commandId(obj.commandId) {}; ClientParams(const ClientParams &obj) : commandId(obj.commandId) {};
ClientParams() : ClientParams(*Game::cmd_id) {}; ClientParams() : ClientParams(*Game::cmd_id) {};
char* get(size_t index) override; const char* get(size_t index) override;
size_t length() override; size_t length() override;
private: private:
@ -38,7 +38,7 @@ namespace Components
ServerParams(const ServerParams &obj) : commandId(obj.commandId) {}; ServerParams(const ServerParams &obj) : commandId(obj.commandId) {};
ServerParams() : ServerParams(*Game::cmd_id_sv) {}; ServerParams() : ServerParams(*Game::cmd_id_sv) {};
char* get(size_t index) override; const char* get(size_t index) override;
size_t length() override; size_t length() override;
private: private:

View File

@ -273,6 +273,11 @@ namespace Components
} }
} }
Game::dvar_t* Dedicated::Dvar_RegisterSVNetworkFps(const char* dvarName, int, int min, int, int, const char* description)
{
return Game::Dvar_RegisterInt(dvarName, 1000, min, 1000, Game::dvar_flag::DVAR_FLAG_NONE, description);
}
Dedicated::Dedicated() Dedicated::Dedicated()
{ {
// Map rotation // Map rotation
@ -311,7 +316,7 @@ namespace Components
Utils::Hook::Nop(0x4DCEC9, 2); // some check preventing proper game functioning Utils::Hook::Nop(0x4DCEC9, 2); // some check preventing proper game functioning
Utils::Hook::Nop(0x507C79, 6); // another similar bsp check Utils::Hook::Nop(0x507C79, 6); // another similar bsp check
Utils::Hook::Nop(0x414E4D, 6); // unknown check in SV_ExecuteClientMessage (0x20F0890 == 0, related to client->f_40) Utils::Hook::Nop(0x414E4D, 6); // cl->messageAcknowledge > cl->gamestateMessageNum check in SV_ExecuteClientMessage
Utils::Hook::Nop(0x4DCEE9, 5); // some deinit renderer function Utils::Hook::Nop(0x4DCEE9, 5); // some deinit renderer function
Utils::Hook::Nop(0x59A896, 5); // warning message on a removed subsystem Utils::Hook::Nop(0x59A896, 5); // warning message on a removed subsystem
Utils::Hook::Nop(0x4B4EEF, 5); // same as above Utils::Hook::Nop(0x4B4EEF, 5); // same as above
@ -326,14 +331,8 @@ namespace Components
// isHost script call return 0 // isHost script call return 0
Utils::Hook::Set<DWORD>(0x5DEC04, 0); Utils::Hook::Set<DWORD>(0x5DEC04, 0);
// sv_network_fps max 1000, and uncheat
Utils::Hook::Set<BYTE>(0x4D3C67, 0); // ?
Utils::Hook::Set<DWORD>(0x4D3C69, 1000);
// Manually register sv_network_fps // Manually register sv_network_fps
Utils::Hook::Nop(0x4D3C7B, 5); Utils::Hook(0x4D3C7B, Dedicated::Dvar_RegisterSVNetworkFps, HOOK_CALL).install()->quick();
Utils::Hook::Nop(0x4D3C8E, 5);
*reinterpret_cast<Game::dvar_t**>(0x62C7C00) = Dvar::Register<int>("sv_network_fps", 1000, 20, 1000, Game::dvar_flag::DVAR_FLAG_NONE, "Number of times per second the server checks for net messages").get<Game::dvar_t*>();
// r_loadForRenderer default to 0 // r_loadForRenderer default to 0
Utils::Hook::Set<BYTE>(0x519DDF, 0); Utils::Hook::Set<BYTE>(0x519DDF, 0);

View File

@ -29,5 +29,7 @@ namespace Components
static void TransmitGuids(); static void TransmitGuids();
static void TimeWrapStub(Game::errorParm_t code, const char* message); static void TimeWrapStub(Game::errorParm_t code, const char* message);
static Game::dvar_t* Dvar_RegisterSVNetworkFps(const char* dvarName, int value, int min, int max, int flags, const char* description);
}; };
} }

View File

@ -22,24 +22,24 @@ namespace Components
return this->dvar; return this->dvar;
} }
template <> char* Dvar::Var::get()
{
if (this->dvar && this->dvar->type == Game::dvar_type::DVAR_TYPE_STRING && this->dvar->current.string)
{
return const_cast<char*>(this->dvar->current.string);
}
return const_cast<char*>("");
}
template <> const char* Dvar::Var::get() template <> const char* Dvar::Var::get()
{ {
return this->get<char*>(); if (this->dvar == nullptr)
return "";
if (this->dvar->type == Game::dvar_type::DVAR_TYPE_STRING
|| this->dvar->type == Game::dvar_type::DVAR_TYPE_ENUM)
{
if (this->dvar->current.string != nullptr)
return this->dvar->current.string;
}
return "";
} }
template <> int Dvar::Var::get() template <> int Dvar::Var::get()
{ {
if (this->dvar && this->dvar->type == Game::dvar_type::DVAR_TYPE_INT) if (this->dvar && this->dvar->type == Game::dvar_type::DVAR_TYPE_INT || this->dvar->type == Game::dvar_type::DVAR_TYPE_ENUM)
{ {
return this->dvar->current.integer; return this->dvar->current.integer;
} }
@ -193,7 +193,7 @@ namespace Components
void Dvar::ResetDvarsValue() void Dvar::ResetDvarsValue()
{ {
if (!Utils::IO::FileExists(Dvar::ArchiveDvarPath)) if (!Utils::IO::FileExists(Dvar::ArchiveDvarPath))
return return;
Command::Execute("exec archivedvars.cfg", true); Command::Execute("exec archivedvars.cfg", true);
// Clean up // Clean up
@ -209,7 +209,7 @@ namespace Components
Scheduler::OnFrame([]() Scheduler::OnFrame([]()
{ {
static std::string lastValidName = "Unknown Soldier"; static std::string lastValidName = "Unknown Soldier";
std::string name = Dvar::Var("name").get<char*>(); std::string name = Dvar::Var("name").get<const char*>();
// Don't perform any checks if name didn't change // Don't perform any checks if name didn't change
if (name == lastValidName) return; if (name == lastValidName) return;

View File

@ -2,38 +2,96 @@
namespace Components namespace Components
{ {
Game::dvar_t* Elevators::SV_DisableElevators; Dvar::Var Elevators::BG_Elevators;
__declspec(naked) void Elevators::PM_GroundTraceStub() int Elevators::PM_CorrectAllSolid(Game::pmove_s* pm, Game::pml_t* pml, Game::trace_t* trace)
{
assert(pm != nullptr);
assert(pm->ps != nullptr);
Game::vec3_t point;
auto* ps = pm->ps;
auto i = 0;
const auto elevatorSetting = Elevators::BG_Elevators.get<int>();
while (true)
{
point[0] = ps->origin[0] + Game::CorrectSolidDeltas[i][0];
point[1] = ps->origin[1] + Game::CorrectSolidDeltas[i][1];
point[2] = ps->origin[2] + Game::CorrectSolidDeltas[i][2];
Game::PM_playerTrace(pm, trace, point, point, &pm->bounds, ps->clientNum, pm->tracemask);
// If the player wishes to glitch without effort they can do so
if (!trace->startsolid || elevatorSetting == Elevators::EASY)
{
ps->origin[0] = point[0];
ps->origin[1] = point[1];
ps->origin[2] = point[2];
point[2] = (ps->origin[2] - 1.0f) - 0.25f;
Game::PM_playerTrace(pm, trace, ps->origin, point, &pm->bounds, ps->clientNum, pm->tracemask);
// If elevators are disabled we need to check that startsolid is false before proceeding
// like later versions of the game do
if (!trace->startsolid || elevatorSetting >= Elevators::ENABLED)
{
break;
}
}
i += 1;
if (i >= 26)
{
ps->groundEntityNum = Game::ENTITYNUM_NONE;
pml->groundPlane = 0;
pml->almostGroundPlane = 0;
pml->walking = 0;
Game::Jump_ClearState(ps);
return 0;
}
}
pml->groundTrace = *trace;
const auto fraction = trace->fraction;
ps->origin[0] += fraction * (point[0] - ps->origin[0]);
ps->origin[1] += fraction * (point[1] - ps->origin[1]);
ps->origin[2] += fraction * (point[2] - ps->origin[2]);
return 1;
}
void Elevators::PM_Trace_Hk(Game::pmove_s* pm, Game::trace_t* trace, const float* f3,
const float* f4, const Game::Bounds* bounds, int a6, int a7)
{
Game::PM_Trace(pm, trace, f3, f4, bounds, a6, a7);
// Allow the player to stand even when there is no headroom
if (Elevators::BG_Elevators.get<int>() == Elevators::EASY)
{
trace->allsolid = false;
}
}
__declspec(naked) void Elevators::PM_CorrectAllSolidStub()
{ {
__asm __asm
{ {
push eax push eax
mov eax, Elevators::SV_DisableElevators pushad
cmp byte ptr [eax + 16], 1
push [esp + 0x8 + 0x24]
push [esp + 0x8 + 0x24]
push eax
call Elevators::PM_CorrectAllSolid
add esp, 0xC
mov [esp + 0x20], eax
popad
pop eax pop eax
// Always skip PM_CorrectAllSolid if SV_DisableElevators is set to 1 ret
je noElevators
// Original code
cmp byte ptr [esp + 0x50], 0
rep movsd
mov esi, [esp + 0x58]
// Original code flow
push 0x573694
retn
noElevators:
// Original code
rep movsd
mov esi, [esp + 0x58]
// Jump over call to PM_CorrectAllSolid
push 0x5736AE
retn
} }
} }
@ -41,12 +99,26 @@ namespace Components
{ {
Dvar::OnInit([] Dvar::OnInit([]
{ {
Elevators::SV_DisableElevators = Game::Dvar_RegisterBool("sv_disableElevators", static const char* values[] =
false, Game::DVAR_FLAG_REPLICATED, "Disable elevators"); {
"off",
"normal",
"easy",
nullptr
};
Elevators::BG_Elevators = Game::Dvar_RegisterEnum("bg_elevators", values,
Elevators::ENABLED, Game::DVAR_FLAG_REPLICATED, "Elevators glitch settings");
}); });
// Hook PM_GroundTrace so me way skip PM_CorrectAllSolid (disable elevators) //Replace PM_CorrectAllSolid
Utils::Hook(0x573689, Elevators::PM_GroundTraceStub, HOOK_JUMP).install()->quick(); Utils::Hook(0x57369E, Elevators::PM_CorrectAllSolidStub, HOOK_CALL).install()->quick();
// Place hooks in PM_CheckDuck. If the elevators dvar is set to easy the
// flags for duck/prone will always be removed from the player state
Utils::Hook(0x570EC5, Elevators::PM_Trace_Hk, HOOK_CALL).install()->quick();
Utils::Hook(0x570E0B, Elevators::PM_Trace_Hk, HOOK_CALL).install()->quick();
Utils::Hook(0x570D70, Elevators::PM_Trace_Hk, HOOK_CALL).install()->quick();
} }
Elevators::~Elevators() Elevators::~Elevators()

View File

@ -9,8 +9,11 @@ namespace Components
~Elevators(); ~Elevators();
private: private:
static Game::dvar_t* SV_DisableElevators; enum ElevatorSettings { DISABLED, ENABLED, EASY };
static Dvar::Var BG_Elevators;
static void PM_GroundTraceStub(); static int PM_CorrectAllSolid(Game::pmove_s* move, Game::pml_t* pml, Game::trace_t* trace);
static void PM_CorrectAllSolidStub();
static void PM_Trace_Hk(Game::pmove_s*, Game::trace_t*, const float*, const float*, const Game::Bounds*, int, int);
}; };
} }

View File

@ -111,8 +111,8 @@ namespace Components
Friends::SortList(); Friends::SortList();
int notify = Dvar::Var("cl_notifyFriendState").get<int>(); const auto notify = Dvar::Var("cl_notifyFriendState").get<bool>();
if (gotOnline && (notify == -1 || (notify == 1 && !Game::CL_IsCgameInitialized())) && !Dvar::Var("ui_streamFriendly").get<bool>()) if (gotOnline && (!notify || (notify && !Game::CL_IsCgameInitialized())) && !Dvar::Var("ui_streamFriendly").get<bool>())
{ {
Game::Material* material = Friends::CreateAvatar(user); Game::Material* material = Friends::CreateAvatar(user);
Toast::Show(material, entry->name, "is playing IW4x", 3000, [material]() Toast::Show(material, entry->name, "is playing IW4x", 3000, [material]()
@ -186,7 +186,7 @@ namespace Components
{ {
std::lock_guard<std::recursive_mutex> _(Friends::Mutex); std::lock_guard<std::recursive_mutex> _(Friends::Mutex);
const unsigned int modId = *reinterpret_cast<unsigned int*>(const_cast<char*>("IW4x")) | 0x80000000; const unsigned int modId = *reinterpret_cast<unsigned int*>("IW4x") | 0x80000000;
// Split up the list // Split up the list
for (auto entry : Friends::FriendsList) for (auto entry : Friends::FriendsList)
@ -578,14 +578,19 @@ namespace Components
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled() || Monitor::IsEnabled()) return; if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled() || Monitor::IsEnabled()) return;
Dvar::Register<bool>("cl_anonymous", false, Game::DVAR_FLAG_SAVED, ""); Dvar::Register<bool>("cl_anonymous", false, Game::DVAR_FLAG_SAVED, "Enable invisible mode for Steam");
Dvar::Register<int>("cl_notifyFriendState", 1, -1, 1, Game::DVAR_FLAG_SAVED, ""); Dvar::Register<bool>("cl_notifyFriendState", true, Game::DVAR_FLAG_SAVED, "Update friends about current game status");
Command::Add("addFriend", [](Command::Params* params) Command::Add("addFriend", [](Command::Params* params)
{ {
if (params->length() <= 1) return; if (params->length() < 2u)
{
Logger::Print("Usage: %s <Steam ID in hexadecimal format>\n", params->get(0));
return;
}
SteamID id; SteamID id;
id.bits = atoll(params->get(1)); id.bits = std::strtoull(params->get(1), nullptr, 16);
Friends::AddFriend(id); Friends::AddFriend(id);
}); });

View File

@ -536,14 +536,13 @@ namespace Components
if (weaponDef->requireLockonToFire) if (weaponDef->requireLockonToFire)
return false; return false;
if (ps->linkFlags & 4) if (ps->linkFlags & Game::PLF_WEAPONVIEW_ONLY)
return false; return false;
if (ps->weaponState >= Game::WEAPON_STUNNED_START && ps->weaponState <= Game::WEAPON_STUNNED_END) if (ps->weaponState >= Game::WEAPON_STUNNED_START && ps->weaponState <= Game::WEAPON_STUNNED_END)
return false; return false;
// The game checks for these flags. Their meaning is to be researched if necessary. if (ps->eFlags & (Game::EF_VEHICLE_ACTIVE | Game::EF_TURRET_ACTIVE_DUCK | Game::EF_TURRET_ACTIVE_PRONE))
if (ps->eFlags & 0x100C00)
return false; return false;
if (!ps->hasAmmo) if (!ps->hasAmmo)
@ -1173,8 +1172,7 @@ namespace Components
} }
else else
{ {
Game::Cbuf_AddText(gamePadIndex, keyBinding); Game::Cbuf_InsertText(gamePadIndex, keyBinding);
Game::Cbuf_AddText(gamePadIndex, "\n");
} }
} }
} }

View File

@ -1,36 +0,0 @@
#include "STDInclude.hpp"
#ifdef COMPILE_IW4MVM
#include <IW4MVM/client_main.h>
#endif
namespace Components
{
IW4MVM::IW4MVM()
{
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled() || Monitor::IsEnabled() || Loader::IsPerformingUnitTests()) return;
DWORD oldProtect;
std::uint8_t* _module = reinterpret_cast<std::uint8_t*>(GetModuleHandle(nullptr));
VirtualProtect(_module + 0x1000, 0x2D6000, PAGE_EXECUTE_READWRITE, &oldProtect);
#ifdef COMPILE_IW4MVM
client_main::Init();
Scheduler::Once(client_main::PostInit);
#endif
Scheduler::OnFrame([]()
{
if (!Game::CL_IsCgameInitialized())
{
Dvar::Var("com_timescale").set(1.0f);
}
});
VirtualProtect(_module + 0x1000, 0x2D6000, PAGE_EXECUTE_READ, &oldProtect);
}
IW4MVM::~IW4MVM()
{
}
}

View File

@ -1,14 +0,0 @@
#pragma once
// Disabled for public release
//#define COMPILE_IW4MVM
namespace Components
{
class IW4MVM : public Component
{
public:
IW4MVM();
~IW4MVM();
};
}

View File

@ -206,8 +206,16 @@ namespace Components
if (std::find(Maps::CurrentDependencies.begin(), Maps::CurrentDependencies.end(), FastFiles::Current()) != Maps::CurrentDependencies.end() if (std::find(Maps::CurrentDependencies.begin(), Maps::CurrentDependencies.end(), FastFiles::Current()) != Maps::CurrentDependencies.end()
&& (FastFiles::Current() != "mp_shipment_long" || Maps::CurrentMainZone != "mp_shipment")) // Shipment is a special case && (FastFiles::Current() != "mp_shipment_long" || Maps::CurrentMainZone != "mp_shipment")) // Shipment is a special case
{ {
if (type == Game::XAssetType::ASSET_TYPE_CLIPMAP_MP || type == Game::XAssetType::ASSET_TYPE_CLIPMAP_SP || type == Game::XAssetType::ASSET_TYPE_GAMEWORLD_SP || type == Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP || type == Game::XAssetType::ASSET_TYPE_GFXWORLD || type == Game::XAssetType::ASSET_TYPE_MAP_ENTS || type == Game::XAssetType::ASSET_TYPE_COMWORLD || type == Game::XAssetType::ASSET_TYPE_FXWORLD) switch (type)
{ {
case Game::XAssetType::ASSET_TYPE_CLIPMAP_MP:
case Game::XAssetType::ASSET_TYPE_CLIPMAP_SP:
case Game::XAssetType::ASSET_TYPE_GAMEWORLD_SP:
case Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP:
case Game::XAssetType::ASSET_TYPE_GFXWORLD:
case Game::XAssetType::ASSET_TYPE_MAP_ENTS:
case Game::XAssetType::ASSET_TYPE_COMWORLD:
case Game::XAssetType::ASSET_TYPE_FXWORLD:
*restrict = true; *restrict = true;
return; return;
} }
@ -241,7 +249,7 @@ namespace Components
{ {
if (Flags::HasFlag("dump")) if (Flags::HasFlag("dump"))
{ {
Utils::IO::WriteFile(Utils::String::VA("raw/%s.ents", name.data()), asset.mapEnts->entityString); Utils::IO::WriteFile(Utils::String::VA("raw/%s.ents", name.data()), asset.mapEnts->entityString, true);
} }
static std::string mapEntities; static std::string mapEntities;
@ -249,7 +257,7 @@ namespace Components
if (ents.exists()) if (ents.exists())
{ {
mapEntities = ents.getBuffer(); mapEntities = ents.getBuffer();
asset.mapEnts->entityString = const_cast<char*>(mapEntities.data()); asset.mapEnts->entityString = mapEntities.data();
asset.mapEnts->numEntityChars = mapEntities.size() + 1; asset.mapEnts->numEntityChars = mapEntities.size() + 1;
} }
} }
@ -318,7 +326,7 @@ namespace Components
mapname = "mp_shipment_long"; mapname = "mp_shipment_long";
} }
_snprintf_s(buffer, size, size, format, mapname); _snprintf_s(buffer, size, _TRUNCATE, format, mapname);
} }
void Maps::HandleAsSPMap() void Maps::HandleAsSPMap()
@ -333,7 +341,7 @@ namespace Components
{ {
std::regex _(expression); std::regex _(expression);
} }
catch (const std::exception e) catch (const std::regex_error ex)
{ {
MessageBoxA(nullptr, Utils::String::VA("Invalid regular expression: %s", expression.data()), "Warning", MB_ICONEXCLAMATION); MessageBoxA(nullptr, Utils::String::VA("Invalid regular expression: %s", expression.data()), "Warning", MB_ICONEXCLAMATION);
return; return;
@ -439,7 +447,7 @@ namespace Components
void Maps::LoadNewMapCommand(char* buffer, size_t size, const char* /*format*/, const char* mapname, const char* gametype) void Maps::LoadNewMapCommand(char* buffer, size_t size, const char* /*format*/, const char* mapname, const char* gametype)
{ {
unsigned int hash = Maps::GetUsermapHash(mapname); unsigned int hash = Maps::GetUsermapHash(mapname);
_snprintf_s(buffer, size, size, "loadingnewmap\n%s\n%s\n%d", mapname, gametype, hash); _snprintf_s(buffer, size, _TRUNCATE, "loadingnewmap\n%s\n%s\n%d", mapname, gametype, hash);
} }
int Maps::TriggerReconnectForMap(Game::msg_t* msg, const char* mapname) int Maps::TriggerReconnectForMap(Game::msg_t* msg, const char* mapname)
@ -784,7 +792,7 @@ namespace Components
{ {
if (pack.index == dlc) if (pack.index == dlc)
{ {
ShellExecute(0, 0, L"https://xlabs.dev/support_iw4x_client.html", 0, 0, SW_SHOW); ShellExecuteW(0, 0, L"https://xlabs.dev/support_iw4x_client.html", 0, 0, SW_SHOW);
return; return;
} }
} }

View File

@ -21,7 +21,7 @@ namespace Components
Dvar::Var("xblive_privateserver").set(false); Dvar::Var("xblive_privateserver").set(false);
std::string playlistFilename = Dvar::Var("playlistFilename").get<char*>(); std::string playlistFilename = Dvar::Var("playlistFilename").get<const char*>();
FileSystem::File playlist(playlistFilename); FileSystem::File playlist(playlistFilename);
if (playlist.exists()) if (playlist.exists())

View File

@ -2,7 +2,6 @@
namespace Components namespace Components
{ {
int QuickPatch::FrameTime = 0;
Dvar::Var QuickPatch::r_customAspectRatio; Dvar::Var QuickPatch::r_customAspectRatio;
void QuickPatch::UnlockStats() void QuickPatch::UnlockStats()
@ -434,14 +433,14 @@ namespace Components
} }
} }
Game::dvar_t* QuickPatch::Dvar_RegisterUIBuildLocation(const char* dvarName,
float /*x*/, float /*y*/, float min, float max, int /*flags*/, const char* description)
{
return Game::Dvar_RegisterVec2(dvarName, -60.0f, 474.0f, min, max, Game::DVAR_FLAG_READONLY, description);
}
QuickPatch::QuickPatch() QuickPatch::QuickPatch()
{ {
QuickPatch::FrameTime = 0;
Scheduler::OnFrame([]()
{
QuickPatch::FrameTime = Game::Sys_Milliseconds();
});
// quit_hard // quit_hard
Command::Add("quit_hard", [](Command::Params*) Command::Add("quit_hard", [](Command::Params*)
{ {
@ -534,16 +533,11 @@ namespace Components
Utils::Hook::Set<const char*>(0x60BD56, "IW4x (" VERSION ")"); Utils::Hook::Set<const char*>(0x60BD56, "IW4x (" VERSION ")");
// version string color // version string color
static float buildLocColor[] = { 1.0f, 1.0f, 1.0f, 0.8f }; static Game::vec4_t buildLocColor = { 1.0f, 1.0f, 1.0f, 0.8f };
Utils::Hook::Set(0x43F710, buildLocColor); Utils::Hook::Set<float*>(0x43F710, buildLocColor);
// Shift ui version string to the left (ui_buildlocation) // Shift ui version string to the left (ui_buildlocation)
Utils::Hook::Nop(0x6310A0, 5); // Don't register the initial dvar Utils::Hook(0x6310A0, QuickPatch::Dvar_RegisterUIBuildLocation, HOOK_CALL).install()->quick();
Utils::Hook::Nop(0x6310B8, 5); // Don't write the result
Dvar::OnInit([]()
{
*reinterpret_cast<Game::dvar_t**>(0x62E4B64) = Game::Dvar_RegisterVec2("ui_buildLocation", -60.0f, 474.0f, -10000.0, 10000.0, Game::DVAR_FLAG_READONLY, "Where to draw the build number");
});
// console title // console title
if (ZoneBuilder::IsEnabled()) if (ZoneBuilder::IsEnabled())
@ -754,7 +748,7 @@ namespace Components
Utils::Hook(0x4A9F56, QuickPatch::MsgReadBitsCompressCheckCL, HOOK_CALL).install()->quick(); // CL_ParseServerMessage Utils::Hook(0x4A9F56, QuickPatch::MsgReadBitsCompressCheckCL, HOOK_CALL).install()->quick(); // CL_ParseServerMessage
Utils::Hook(0x407376, QuickPatch::SVCanReplaceServerCommand , HOOK_CALL).install()->quick(); // SV_CanReplaceServerCommand Utils::Hook(0x407376, QuickPatch::SVCanReplaceServerCommand , HOOK_CALL).install()->quick(); // SV_CanReplaceServerCommand
Utils::Hook(0x5B67ED, QuickPatch::AtolAdjustPlayerLimit , HOOK_CALL).install()->quick(); // PartyHost_HandleJoinPartyRequest Utils::Hook(0x5B67ED, QuickPatch::AtolAdjustPlayerLimit , HOOK_CALL).install()->quick(); // PartyHost_HandleJoinPartyRequest
Utils::Hook::Nop(0x41698E, 5); // Disable Svcmd_EntityList_f
// Patch selectStringTableEntryInDvar // Patch selectStringTableEntryInDvar
Utils::Hook::Set(0x405959, QuickPatch::SelectStringTableEntryInDvarStub); Utils::Hook::Set(0x405959, QuickPatch::SelectStringTableEntryInDvarStub);
@ -770,11 +764,6 @@ namespace Components
QuickPatch::UnlockStats(); QuickPatch::UnlockStats();
}); });
Command::Add("crash", [](Command::Params*)
{
throw new std::exception();
});
Command::Add("dumptechsets", [](Command::Params* param) Command::Add("dumptechsets", [](Command::Params* param)
{ {
if (param->length() != 2) if (param->length() != 2)
@ -964,204 +953,6 @@ namespace Components
} }
}); });
Scheduler::OnFrame([]()
{
if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawAabbTrees").get<bool>()) return;
float cyan[4] = { 0.0f, 0.5f, 0.5f, 1.0f };
float red[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
Game::clipMap_t* clipMap = *reinterpret_cast<Game::clipMap_t**>(0x7998E0);
//Game::GfxWorld* gameWorld = *reinterpret_cast<Game::GfxWorld**>(0x66DEE94);
if (!clipMap) return;
for (unsigned short i = 0; i < clipMap->smodelNodeCount; ++i)
{
Game::R_AddDebugBounds(cyan, &clipMap->smodelNodes[i].bounds);
}
for (unsigned int i = 0; i < clipMap->numStaticModels; i += 2)
{
Game::R_AddDebugBounds(red, &clipMap->staticModelList[i].absBounds);
}
});
Dvar::OnInit([]
{
Dvar::Register<bool>("r_drawSceneModelBoundingBoxes", false, Game::DVAR_FLAG_CHEAT, "Draw scene model bounding boxes");
Dvar::Register<bool>("r_drawSceneModelCollisions", false, Game::DVAR_FLAG_CHEAT, "Draw scene model collisions");
Dvar::Register<bool>("r_drawTriggers", false, Game::DVAR_FLAG_CHEAT, "Draw triggers");
Dvar::Register<bool>("r_drawModelNames", false, Game::DVAR_FLAG_CHEAT, "Draw all model names");
Dvar::Register<bool>("r_drawAabbTrees", false, Game::DVAR_FLAG_USERCREATED, "Draw aabb trees");
});
Scheduler::OnFrame([]()
{
if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawModelNames").get<bool>()) return;
float sceneModelsColor[4] = { 1.0f, 1.0f, 0.0f, 1.0f };
float dobjsColor[4] = { 0.0f, 1.0f, 1.0f, 1.0f };
float staticModelsColor[4] = { 1.0f, 0.0f, 1.0f, 1.0f };
auto mapName = Dvar::Var("mapname").get<const char*>();
auto* scene = Game::scene;
auto world = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_GFXWORLD, Utils::String::VA("maps/mp/%s.d3dbsp", mapName))->asset.header.gfxWorld;
for (auto i = 0; i < scene->sceneModelCount; i++)
{
if (!scene->sceneModel[i].model)
continue;
Game::R_AddDebugString(sceneModelsColor, scene->sceneModel[i].placement.base.origin, 1.0, scene->sceneModel[i].model->name);
}
for (auto i = 0; i < scene->sceneDObjCount; i++)
{
if (scene->sceneDObj[i].obj) {
for (int j = 0; j < scene->sceneDObj[i].obj->numModels; j++)
{
Game::R_AddDebugString(dobjsColor, scene->sceneDObj[i].placement.origin, 1.0, scene->sceneDObj[i].obj->models[j]->name);
}
}
}
// Static models
for (size_t i = 0; i < world->dpvs.smodelCount; i++)
{
auto staticModel = world->dpvs.smodelDrawInsts[i];
if (staticModel.model) {
Game::R_AddDebugString(staticModelsColor, staticModel.placement.origin, 1.0, staticModel.model->name);
}
}
});
Scheduler::OnFrame([]()
{
if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawSceneModelBoundingBoxes").get<bool>()) return;
float red[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
float blue[4] = { 0.0f, 0.0f, 1.0f, 1.0f };
auto* scene = Game::scene;
for(auto i = 0; i < scene->sceneModelCount; i++)
{
if(!scene->sceneModel[i].model)
continue;
auto b = scene->sceneModel[i].model->bounds;
b.midPoint[0] += scene->sceneModel[i].placement.base.origin[0];
b.midPoint[1] += scene->sceneModel[i].placement.base.origin[1];
b.midPoint[2] += scene->sceneModel[i].placement.base.origin[2];
b.halfSize[0] *= scene->sceneModel[i].placement.scale;
b.halfSize[1] *= scene->sceneModel[i].placement.scale;
b.halfSize[2] *= scene->sceneModel[i].placement.scale;
Game::R_AddDebugBounds(red, &b, &scene->sceneModel[i].placement.base.quat);
}
for(auto i = 0; i < scene->sceneDObjCount; i++)
{
scene->sceneDObj[i].cull.bounds.halfSize[0] = std::abs(scene->sceneDObj[i].cull.bounds.halfSize[0]);
scene->sceneDObj[i].cull.bounds.halfSize[1] = std::abs(scene->sceneDObj[i].cull.bounds.halfSize[1]);
scene->sceneDObj[i].cull.bounds.halfSize[2] = std::abs(scene->sceneDObj[i].cull.bounds.halfSize[2]);
if (scene->sceneDObj[i].cull.bounds.halfSize[0] < 0 ||
scene->sceneDObj[i].cull.bounds.halfSize[1] < 0 ||
scene->sceneDObj[i].cull.bounds.halfSize[2] < 0) {
Components::Logger::Print("WARNING: Negative half size for DOBJ %s, this will cause culling issues!", scene->sceneDObj[i].obj->models[0]->name);
}
Game::R_AddDebugBounds(blue, &scene->sceneDObj[i].cull.bounds);
}
});
Scheduler::OnFrame([]()
{
if (!Game::CL_IsCgameInitialized()) return;
if (!Dvar::Var("r_drawSceneModelCollisions").get<bool>()) return;
float green[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
auto* scene = Game::scene;
for (auto i = 0; i < scene->sceneModelCount; i++)
{
if (!scene->sceneModel[i].model)
continue;
for (auto j = 0; j < scene->sceneModel[i].model->numCollSurfs; j++) {
auto b = scene->sceneModel[i].model->collSurfs[j].bounds;
b.midPoint[0] += scene->sceneModel[i].placement.base.origin[0];
b.midPoint[1] += scene->sceneModel[i].placement.base.origin[1];
b.midPoint[2] += scene->sceneModel[i].placement.base.origin[2];
b.halfSize[0] *= scene->sceneModel[i].placement.scale;
b.halfSize[1] *= scene->sceneModel[i].placement.scale;
b.halfSize[2] *= scene->sceneModel[i].placement.scale;
Game::R_AddDebugBounds(green, &b, &scene->sceneModel[i].placement.base.quat);
}
}
});
Scheduler::OnFrame([]()
{
if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawTriggers").get<bool>()) return;
float hurt[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
float hurtTouch[4] = { 0.75f, 0.0f, 0.0f, 1.0f };
float damage[4] = { 0.0f, 0.0f, 1.0f, 1.0f };
float once[4] = { 0.0f, 1.0f, 1.0f, 1.0f };
float multiple[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
auto* entities = Game::g_entities;
for(auto i = 0u; i < 0x800; i++)
{
auto* ent = &entities[i];
if(ent->r.isInUse)
{
Game::Bounds b = ent->r.box;
b.midPoint[0] += ent->r.currentOrigin[0];
b.midPoint[1] += ent->r.currentOrigin[1];
b.midPoint[2] += ent->r.currentOrigin[2];
switch(ent->handler)
{
case Game::ENT_HANDLER_TRIGGER_HURT:
Game::R_AddDebugBounds(hurt, &b);
break;
case Game::ENT_HANDLER_TRIGGER_HURT_TOUCH:
Game::R_AddDebugBounds(hurtTouch, &b);
break;
case Game::ENT_HANDLER_TRIGGER_DAMAGE:
Game::R_AddDebugBounds(damage, &b);
break;
case Game::ENT_HANDLER_TRIGGER_MULTIPLE:
if(ent->spawnflags & 0x40)
Game::R_AddDebugBounds(once, &b);
else
Game::R_AddDebugBounds(multiple, &b);
break;
default:
float rv = std::min((float)ent->handler, (float)5) / 5;
float gv = std::clamp((float)ent->handler-5, (float)0, (float)5) / 5;
float bv = std::clamp((float)ent->handler - 10, (float)0, (float)5) / 5;
float color[4] = { rv, gv, bv, 1.0f };
Game::R_AddDebugBounds(color, &b);
break;
}
}
}
});
// Dvars // Dvars
Dvar::Register<bool>("ui_streamFriendly", false, Game::DVAR_FLAG_SAVED, "Stream friendly UI"); Dvar::Register<bool>("ui_streamFriendly", false, Game::DVAR_FLAG_SAVED, "Stream friendly UI");
@ -1205,11 +996,6 @@ namespace Components
} }
} }
QuickPatch::~QuickPatch()
{
}
bool QuickPatch::unitTest() bool QuickPatch::unitTest()
{ {
uint32_t randIntCount = 4'000'000; uint32_t randIntCount = 4'000'000;

View File

@ -6,16 +6,12 @@ namespace Components
{ {
public: public:
QuickPatch(); QuickPatch();
~QuickPatch();
bool unitTest() override; bool unitTest() override;
static void UnlockStats(); static void UnlockStats();
static int GetFrameTime() { return FrameTime; }
private: private:
static int FrameTime;
static void SelectStringTableEntryInDvarStub(); static void SelectStringTableEntryInDvarStub();
static int SVCanReplaceServerCommand(Game::client_t *client, const char *cmd); static int SVCanReplaceServerCommand(Game::client_t *client, const char *cmd);
@ -51,5 +47,7 @@ namespace Components
static void CL_KeyEvent_OnEscape(); static void CL_KeyEvent_OnEscape();
static void CL_KeyEvent_ConsoleEscape_Stub(); static void CL_KeyEvent_ConsoleEscape_Stub();
static Game::dvar_t* Dvar_RegisterUIBuildLocation(const char* dvarName, float x, float y, float min, float max, int flags, const char* description);
}; };
} }

View File

@ -8,6 +8,30 @@ namespace Components
Utils::Signal<Scheduler::Callback> Renderer::EndRecoverDeviceSignal; Utils::Signal<Scheduler::Callback> Renderer::EndRecoverDeviceSignal;
Utils::Signal<Scheduler::Callback> Renderer::BeginRecoverDeviceSignal; Utils::Signal<Scheduler::Callback> Renderer::BeginRecoverDeviceSignal;
Dvar::Var Renderer::r_drawTriggers;
Dvar::Var Renderer::r_drawSceneModelCollisions;
Dvar::Var Renderer::r_drawModelBoundingBoxes;
Dvar::Var Renderer::r_drawModelNames;
Dvar::Var Renderer::r_drawAABBTrees;
Dvar::Var Renderer::r_playerDrawDebugDistance;
float cyan[4] = { 0.0f, 0.5f, 0.5f, 1.0f };
float red[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
float green[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
// R_draw model names & collisions colors
float sceneModelsColor[4] = { 1.0f, 1.0f, 0.0f, 1.0f };
float dobjsColor[4] = { 0.0f, 1.0f, 1.0f, 1.0f };
float staticModelsColor[4] = { 1.0f, 0.0f, 1.0f, 1.0f };
float gentitiesColor[4] = { 1.0f, 0.5f, 0.5f, 1.0f };
// Trigger colors
float hurt[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
float hurtTouch[4] = { 0.75f, 0.0f, 0.0f, 1.0f };
float damage[4] = { 0.0f, 0.0f, 1.0f, 1.0f };
float once[4] = { 0.0f, 1.0f, 1.0f, 1.0f };
float multiple[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
__declspec(naked) void Renderer::FrameStub() __declspec(naked) void Renderer::FrameStub()
{ {
__asm __asm
@ -127,7 +151,7 @@ namespace Components
// show error // show error
pushad; pushad;
push [esp + 24h + 20h]; push[esp + 24h + 20h];
push eax; push eax;
call R_TextureFromCodeError; call R_TextureFromCodeError;
add esp, 8; add esp, 8;
@ -166,39 +190,319 @@ namespace Components
return Utils::Hook::Call<int(int, float, float, const char*, Game::vec4_t*, int)>(0x005033E0)(a1, a2, a3, Utils::String::VA("%s (^3%s^7)", mat->info.name, mat->techniqueSet->name), color, a6); return Utils::Hook::Call<int(int, float, float, const char*, Game::vec4_t*, int)>(0x005033E0)(a1, a2, a3, Utils::String::VA("%s (^3%s^7)", mat->info.name, mat->techniqueSet->name), color, a6);
} }
void Renderer::DebugDrawTriggers()
{
if (!r_drawTriggers.get<bool>()) return;
auto entities = Game::g_entities;
for (auto i = 0u; i < 0x800u; i++)
{
auto* ent = &entities[i];
if (ent->r.isInUse)
{
Game::Bounds b = ent->r.box;
b.midPoint[0] += ent->r.currentOrigin[0];
b.midPoint[1] += ent->r.currentOrigin[1];
b.midPoint[2] += ent->r.currentOrigin[2];
switch (ent->handler)
{
case Game::ENT_HANDLER_TRIGGER_HURT:
Game::R_AddDebugBounds(hurt, &b);
break;
case Game::ENT_HANDLER_TRIGGER_HURT_TOUCH:
Game::R_AddDebugBounds(hurtTouch, &b);
break;
case Game::ENT_HANDLER_TRIGGER_DAMAGE:
Game::R_AddDebugBounds(damage, &b);
break;
case Game::ENT_HANDLER_TRIGGER_MULTIPLE:
if (ent->spawnflags & 0x40)
Game::R_AddDebugBounds(once, &b);
else
Game::R_AddDebugBounds(multiple, &b);
break;
default:
auto rv = std::min(static_cast<float>(ent->handler), 5.0f) / 5.0f;
auto gv = std::clamp(static_cast<float>(ent->handler - 5), 0.f, 5.0f) / 5.0f;
auto bv = std::clamp(static_cast<float>(ent->handler - 10), 0.f, 5.0f) / 5.0f;
float color[4] = { rv, gv, bv, 1.0f };
Game::R_AddDebugBounds(color, &b);
break;
}
}
}
}
void Renderer::DebugDrawSceneModelCollisions()
{
if (!r_drawSceneModelCollisions.get<bool>()) return;
auto scene = Game::scene;
for (auto i = 0; i < scene->sceneModelCount; i++)
{
if (!scene->sceneModel[i].model)
continue;
for (auto j = 0; j < scene->sceneModel[i].model->numCollSurfs; j++)
{
auto b = scene->sceneModel[i].model->collSurfs[j].bounds;
b.midPoint[0] += scene->sceneModel[i].placement.base.origin[0];
b.midPoint[1] += scene->sceneModel[i].placement.base.origin[1];
b.midPoint[2] += scene->sceneModel[i].placement.base.origin[2];
b.halfSize[0] *= scene->sceneModel[i].placement.scale;
b.halfSize[1] *= scene->sceneModel[i].placement.scale;
b.halfSize[2] *= scene->sceneModel[i].placement.scale;
Game::R_AddDebugBounds(green, &b, &scene->sceneModel[i].placement.base.quat);
}
}
}
void Renderer::DebugDrawModelBoundingBoxes()
{
auto val = r_drawModelBoundingBoxes.get<int>();
if (!val) return;
// Ingame only
int clientNum = Game::CG_GetClientNum();
if (!Game::CL_IsCgameInitialized() ||
clientNum >= 18 ||
clientNum < 0 ||
Game::g_entities[clientNum].client == nullptr) {
return;
}
Game::gentity_t* clientEntity = &Game::g_entities[clientNum];
float playerPosition[3]{ clientEntity->r.currentOrigin[0], clientEntity->r.currentOrigin[1], clientEntity->r.currentOrigin[2] };
const auto mapName = Dvar::Var("mapname").get<const char*>();
auto scene = Game::scene;
auto world = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_GFXWORLD, Utils::String::VA("maps/mp/%s.d3dbsp", mapName))->asset.header.gfxWorld;
auto drawDistance = r_playerDrawDebugDistance.get<int>();
auto sqrDist = drawDistance * drawDistance;
switch (val)
{
case 1:
for (auto i = 0; i < scene->sceneModelCount; i++)
{
if (!scene->sceneModel[i].model)
continue;
if (Utils::Vec3SqrDistance(playerPosition, scene->sceneModel[i].placement.base.origin) < sqrDist)
{
auto b = scene->sceneModel[i].model->bounds;
b.midPoint[0] += scene->sceneModel[i].placement.base.origin[0];
b.midPoint[1] += scene->sceneModel[i].placement.base.origin[1];
b.midPoint[2] += scene->sceneModel[i].placement.base.origin[2];
b.halfSize[0] *= scene->sceneModel[i].placement.scale;
b.halfSize[1] *= scene->sceneModel[i].placement.scale;
b.halfSize[2] *= scene->sceneModel[i].placement.scale;
Game::R_AddDebugBounds(sceneModelsColor, &b, &scene->sceneModel[i].placement.base.quat);
}
}
break;
case 2:
for (auto i = 0; i < scene->sceneDObjCount; i++)
{
if (Utils::Vec3SqrDistance(playerPosition, scene->sceneDObj[i].cull.bounds.midPoint) < sqrDist)
{
scene->sceneDObj[i].cull.bounds.halfSize[0] = std::abs(scene->sceneDObj[i].cull.bounds.halfSize[0]);
scene->sceneDObj[i].cull.bounds.halfSize[1] = std::abs(scene->sceneDObj[i].cull.bounds.halfSize[1]);
scene->sceneDObj[i].cull.bounds.halfSize[2] = std::abs(scene->sceneDObj[i].cull.bounds.halfSize[2]);
if (scene->sceneDObj[i].cull.bounds.halfSize[0] < 0 ||
scene->sceneDObj[i].cull.bounds.halfSize[1] < 0 ||
scene->sceneDObj[i].cull.bounds.halfSize[2] < 0)
{
Components::Logger::Print("WARNING: Negative half size for DOBJ %s, this will cause culling issues!", scene->sceneDObj[i].obj->models[0]->name);
}
Game::R_AddDebugBounds(dobjsColor, &scene->sceneDObj[i].cull.bounds);
}
}
break;
case 3:
// Static models
for (size_t i = 0; i < world->dpvs.smodelCount; i++)
{
auto staticModel = world->dpvs.smodelDrawInsts[i];
if (Utils::Vec3SqrDistance(playerPosition, staticModel.placement.origin) < sqrDist)
{
if (staticModel.model)
{
Game::Bounds b = staticModel.model->bounds;
b.midPoint[0] += staticModel.placement.origin[0];
b.midPoint[1] += staticModel.placement.origin[1];
b.midPoint[2] += staticModel.placement.origin[2];
b.halfSize[0] *= staticModel.placement.scale;
b.halfSize[1] *= staticModel.placement.scale;
b.halfSize[2] *= staticModel.placement.scale;
Game::R_AddDebugBounds(staticModelsColor, &b);
}
}
}
break;
}
}
void Renderer::DebugDrawModelNames()
{
auto val = r_drawModelNames.get<int>();
if (!val) return;
// Ingame only
int clientNum = Game::CG_GetClientNum();
if (!Game::CL_IsCgameInitialized() ||
clientNum >= 18 ||
clientNum < 0 ||
Game::g_entities[clientNum].client == nullptr) {
return;
}
Game::gentity_t* clientEntity = &Game::g_entities[clientNum];
float playerPosition[3]{ clientEntity->r.currentOrigin[0], clientEntity->r.currentOrigin[1], clientEntity->r.currentOrigin[2] };
const auto mapName = Dvar::Var("mapname").get<const char*>();
auto scene = Game::scene;
auto world = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_GFXWORLD, Utils::String::VA("maps/mp/%s.d3dbsp", mapName))->asset.header.gfxWorld;
auto drawDistance = r_playerDrawDebugDistance.get<int>();
auto sqrDist = drawDistance * drawDistance;
switch (val) {
case 1:
for (auto i = 0; i < scene->sceneModelCount; i++)
{
if (!scene->sceneModel[i].model)
continue;
if (Utils::Vec3SqrDistance(playerPosition, scene->sceneModel[i].placement.base.origin) < sqrDist)
{
Game::R_AddDebugString(sceneModelsColor, scene->sceneModel[i].placement.base.origin, 1.0, scene->sceneModel[i].model->name);
}
}
break;
case 2:
for (auto i = 0; i < scene->sceneDObjCount; i++)
{
if (scene->sceneDObj[i].obj)
{
for (int j = 0; j < scene->sceneDObj[i].obj->numModels; j++)
{
if (Utils::Vec3SqrDistance(playerPosition, scene->sceneDObj[i].placement.origin) < sqrDist)
{
Game::R_AddDebugString(dobjsColor, scene->sceneDObj[i].placement.origin, 1.0, scene->sceneDObj[i].obj->models[j]->name);
}
}
}
}
break;
case 3:
// Static models
for (size_t i = 0; i < world->dpvs.smodelCount; i++)
{
auto staticModel = world->dpvs.smodelDrawInsts[i];
if (staticModel.model)
{
auto dist = Utils::Vec3SqrDistance(playerPosition, staticModel.placement.origin);
if (dist < sqrDist)
{
Game::R_AddDebugString(staticModelsColor, staticModel.placement.origin, 1.0, staticModel.model->name);
}
}
}
break;
}
}
void Renderer::DebugDrawAABBTrees()
{
if (!r_drawAABBTrees.get<bool>()) return;
Game::clipMap_t* clipMap = *reinterpret_cast<Game::clipMap_t**>(0x7998E0);
if (!clipMap) return;
for (unsigned short i = 0; i < clipMap->smodelNodeCount; ++i)
{
Game::R_AddDebugBounds(cyan, &clipMap->smodelNodes[i].bounds);
}
for (unsigned int i = 0; i < clipMap->numStaticModels; i += 2)
{
Game::R_AddDebugBounds(red, &clipMap->staticModelList[i].absBounds);
}
}
Renderer::Renderer() Renderer::Renderer()
{ {
if (Dedicated::IsEnabled()) return; if (Dedicated::IsEnabled()) return;
// Renderer::OnBackendFrame([] (IDirect3DDevice9* device) Scheduler::OnFrame([]() {
// { if (Game::CL_IsCgameInitialized())
// if (Game::Sys_Milliseconds() % 2) {
// { DebugDrawAABBTrees();
// device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0, 0, 0); DebugDrawModelNames();
// } DebugDrawModelBoundingBoxes();
// DebugDrawSceneModelCollisions();
// return; DebugDrawTriggers();
// }
// IDirect3DSurface9* buffer = nullptr; });
//
// device->CreateOffscreenPlainSurface(Renderer::Width(), Renderer::Height(), D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &buffer, nullptr);
// device->GetFrontBufferData(0, buffer);
//
// if (buffer)
// {
// D3DSURFACE_DESC desc;
// D3DLOCKED_RECT lockedRect;
//
// buffer->GetDesc(&desc);
//
// HRESULT res = buffer->LockRect(&lockedRect, NULL, D3DLOCK_READONLY);
//
//
// buffer->UnlockRect();
// }
// });
// Log broken materials // Renderer::OnBackendFrame([] (IDirect3DDevice9* device)
// {
// if (Game::Sys_Milliseconds() % 2)
// {
// device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0, 0, 0);
// }
//
// return;
//
// IDirect3DSurface9* buffer = nullptr;
//
// device->CreateOffscreenPlainSurface(Renderer::Width(), Renderer::Height(), D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &buffer, nullptr);
// device->GetFrontBufferData(0, buffer);
//
// if (buffer)
// {
// D3DSURFACE_DESC desc;
// D3DLOCKED_RECT lockedRect;
//
// buffer->GetDesc(&desc);
//
// HRESULT res = buffer->LockRect(&lockedRect, NULL, D3DLOCK_READONLY);
//
//
// buffer->UnlockRect();
// }
// });
// Log broken materials
Utils::Hook(0x0054CAAA, Renderer::StoreGfxBufContextPtrStub1, HOOK_JUMP).install()->quick(); Utils::Hook(0x0054CAAA, Renderer::StoreGfxBufContextPtrStub1, HOOK_JUMP).install()->quick();
Utils::Hook(0x0054CF8D, Renderer::StoreGfxBufContextPtrStub2, HOOK_JUMP).install()->quick(); Utils::Hook(0x0054CF8D, Renderer::StoreGfxBufContextPtrStub2, HOOK_JUMP).install()->quick();
@ -230,6 +534,25 @@ namespace Components
// End vid_restart // End vid_restart
Utils::Hook(0x4CA3A7, Renderer::PostVidRestartStub, HOOK_CALL).install()->quick(); Utils::Hook(0x4CA3A7, Renderer::PostVidRestartStub, HOOK_CALL).install()->quick();
Dvar::OnInit([]
{
static const char* values[] =
{
"Disabled",
"Scene Models",
"Scene Dynamic Objects",
"GfxWorld Static Models",
nullptr
};
Renderer::r_drawModelBoundingBoxes = Game::Dvar_RegisterEnum("r_drawModelBoundingBoxes", values, 0, Game::DVAR_FLAG_CHEAT, "Draw scene model bounding boxes");
Renderer::r_drawSceneModelCollisions = Game::Dvar_RegisterBool("r_drawSceneModelCollisions", false, Game::DVAR_FLAG_CHEAT, "Draw scene model collisions");
Renderer::r_drawTriggers = Game::Dvar_RegisterBool("r_drawTriggers", false, Game::DVAR_FLAG_CHEAT, "Draw triggers");
Renderer::r_drawModelNames = Game::Dvar_RegisterEnum("r_drawModelNames", values, 0, Game::DVAR_FLAG_CHEAT, "Draw all model names");
Renderer::r_drawAABBTrees = Game::Dvar_RegisterBool("r_drawAabbTrees", false, Game::DVAR_FLAG_CHEAT, "Draw aabb trees");
Renderer::r_playerDrawDebugDistance = Game::Dvar_RegisterInt("r_drawDebugDistance", 1000, 0, 50000, Game::DVAR_FLAG_SAVED, "r_draw debug functions draw distance, relative to the player");
});
} }
Renderer::~Renderer() Renderer::~Renderer()

View File

@ -35,10 +35,23 @@ namespace Components
static int DrawTechsetForMaterial(int a1, float a2, float a3, const char* material, Game::vec4_t* color, int a6); static int DrawTechsetForMaterial(int a1, float a2, float a3, const char* material, Game::vec4_t* color, int a6);
static void DebugDrawTriggers();
static void DebugDrawSceneModelCollisions();
static void DebugDrawModelBoundingBoxes();
static void DebugDrawModelNames();
static void DebugDrawAABBTrees();
static Utils::Signal<Scheduler::Callback> EndRecoverDeviceSignal; static Utils::Signal<Scheduler::Callback> EndRecoverDeviceSignal;
static Utils::Signal<Scheduler::Callback> BeginRecoverDeviceSignal; static Utils::Signal<Scheduler::Callback> BeginRecoverDeviceSignal;
static Utils::Signal<BackendCallback> BackendFrameSignal; static Utils::Signal<BackendCallback> BackendFrameSignal;
static Utils::Signal<BackendCallback> SingleBackendFrameSignal; static Utils::Signal<BackendCallback> SingleBackendFrameSignal;
static Dvar::Var r_drawTriggers;
static Dvar::Var r_drawSceneModelCollisions;
static Dvar::Var r_drawModelBoundingBoxes;
static Dvar::Var r_drawModelNames;
static Dvar::Var r_drawAABBTrees;
static Dvar::Var r_playerDrawDebugDistance;
}; };
} }

View File

@ -23,16 +23,22 @@ namespace Components
StartupMessages::TotalMessages = StartupMessages::MessageList.size(); StartupMessages::TotalMessages = StartupMessages::MessageList.size();
} }
std::string message = StartupMessages::MessageList.front(); const auto& message = StartupMessages::MessageList.front();
StartupMessages::MessageList.pop_front();
Game::Dvar_SetStringByName("ui_startupMessage", message.data()); Game::Dvar_SetStringByName("ui_startupMessage", message.data());
Game::Dvar_SetStringByName("ui_startupMessageTitle", Utils::String::VA("Messages (%d/%d)", StartupMessages::TotalMessages - StartupMessages::MessageList.size(), StartupMessages::TotalMessages)); Game::Dvar_SetStringByName("ui_startupMessageTitle", Utils::String::VA("Messages (%d/%d)", StartupMessages::TotalMessages - StartupMessages::MessageList.size(), StartupMessages::TotalMessages));
Game::Dvar_SetStringByName("ui_startupNextButtonText", StartupMessages::MessageList.size() ? "Next" : "Close"); Game::Dvar_SetStringByName("ui_startupNextButtonText", StartupMessages::MessageList.size() ? "Next" : "Close");
Game::Cbuf_AddText(0, "openmenu startup_messages"); Game::Cbuf_AddText(0, "openmenu startup_messages\n");
StartupMessages::MessageList.pop_front();
}); });
} }
StartupMessages::~StartupMessages()
{
StartupMessages::MessageList.clear();
}
void StartupMessages::AddMessage(const std::string& message) void StartupMessages::AddMessage(const std::string& message)
{ {
StartupMessages::MessageList.push_back(message); StartupMessages::MessageList.push_back(message);

View File

@ -6,6 +6,7 @@ namespace Components
{ {
public: public:
StartupMessages(); StartupMessages();
~StartupMessages();
static void AddMessage(const std::string& message); static void AddMessage(const std::string& message);

View File

@ -15,19 +15,14 @@ namespace Components
return 0; return 0;
} }
template<> char* UIScript::Token::get() template<> const char* UIScript::Token::get()
{ {
if (this->isValid()) if (this->isValid())
{ {
return this->token; return this->token;
} }
return const_cast<char*>(""); return "";
}
template<> const char* UIScript::Token::get()
{
return this->get<char*>();
} }
template<> std::string UIScript::Token::get() template<> std::string UIScript::Token::get()

View File

@ -1300,7 +1300,7 @@ namespace Components
}, nullptr, false); }, nullptr, false);
// HACK: set language to 'techsets' to load from that dir // HACK: set language to 'techsets' to load from that dir
char* language = Utils::Hook::Get<char*>(0x649E740); const char* language = Utils::Hook::Get<const char*>(0x649E740);
Utils::Hook::Set<const char*>(0x649E740, "techsets"); Utils::Hook::Set<const char*>(0x649E740, "techsets");
// load generated techset fastfiles // load generated techset fastfiles
@ -1447,7 +1447,7 @@ namespace Components
Utils::IO::WriteFile("zone_source/techsets/techsets.csv", csvStr.data()); Utils::IO::WriteFile("zone_source/techsets/techsets.csv", csvStr.data());
// set language back // set language back
Utils::Hook::Set<char*>(0x649E740, language); Utils::Hook::Set<const char*>(0x649E740, language);
Logger::Print("Building zone 'techsets/techsets'...\n"); Logger::Print("Building zone 'techsets/techsets'...\n");
Zone("techsets/techsets").build(); Zone("techsets/techsets").build();

View File

@ -34,6 +34,7 @@ namespace Game
Cbuf_AddServerText_t Cbuf_AddServerText = Cbuf_AddServerText_t(0x4BB9B0); Cbuf_AddServerText_t Cbuf_AddServerText = Cbuf_AddServerText_t(0x4BB9B0);
Cbuf_AddText_t Cbuf_AddText = Cbuf_AddText_t(0x404B20); Cbuf_AddText_t Cbuf_AddText = Cbuf_AddText_t(0x404B20);
Cbuf_InsertText_t Cbuf_InsertText = Cbuf_InsertText_t(0x4940B0);
CG_NextWeapon_f_t CG_NextWeapon_f = CG_NextWeapon_f_t(0x449DE0); CG_NextWeapon_f_t CG_NextWeapon_f = CG_NextWeapon_f_t(0x449DE0);
CG_GetClientNum_t CG_GetClientNum = CG_GetClientNum_t(0x433700); CG_GetClientNum_t CG_GetClientNum = CG_GetClientNum_t(0x433700);
@ -377,6 +378,10 @@ namespace Game
Field_AdjustScroll_t Field_AdjustScroll = Field_AdjustScroll_t(0x488C10); Field_AdjustScroll_t Field_AdjustScroll = Field_AdjustScroll_t(0x488C10);
AimAssist_ApplyAutoMelee_t AimAssist_ApplyAutoMelee = AimAssist_ApplyAutoMelee_t(0x56A360); AimAssist_ApplyAutoMelee_t AimAssist_ApplyAutoMelee = AimAssist_ApplyAutoMelee_t(0x56A360);
Jump_ClearState_t Jump_ClearState = Jump_ClearState_t(0x04B3890);
PM_playerTrace_t PM_playerTrace = PM_playerTrace_t(0x458980);
PM_Trace_t PM_Trace = PM_Trace_t(0x441F60);
XAssetHeader* DB_XAssetPool = reinterpret_cast<XAssetHeader*>(0x7998A8); XAssetHeader* DB_XAssetPool = reinterpret_cast<XAssetHeader*>(0x7998A8);
unsigned int* g_poolSize = reinterpret_cast<unsigned int*>(0x7995E8); unsigned int* g_poolSize = reinterpret_cast<unsigned int*>(0x7995E8);
@ -493,6 +498,8 @@ namespace Game
GraphFloat* aaInputGraph = reinterpret_cast<GraphFloat*>(0x7A2FC0); GraphFloat* aaInputGraph = reinterpret_cast<GraphFloat*>(0x7A2FC0);
vec3_t* CorrectSolidDeltas = reinterpret_cast<vec3_t*>(0x739BB8); // Count 26
XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize) XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize)
{ {
int elSize = DB_GetXAssetSizeHandlers[type](); int elSize = DB_GetXAssetSizeHandlers[type]();
@ -651,7 +658,7 @@ namespace Game
{ {
Dvar_SetStringByName("com_errorMessage", message.data()); Dvar_SetStringByName("com_errorMessage", message.data());
Dvar_SetStringByName("com_errorTitle", title.data()); Dvar_SetStringByName("com_errorTitle", title.data());
Cbuf_AddText(0, "openmenu error_popmenu_lobby"); Cbuf_AddText(0, "openmenu error_popmenu_lobby\n");
} }
} }
@ -776,9 +783,9 @@ namespace Game
float Vec2Normalize(vec2_t& vec) float Vec2Normalize(vec2_t& vec)
{ {
const float length = std::sqrt((vec[0] * vec[0]) + (vec[1] * vec[1])); const auto length = std::sqrt(vec[0] * vec[0] + vec[1] * vec[1]);
if(length > 0.0f) if (length > 0.0f)
{ {
vec[0] /= length; vec[0] /= length;
vec[1] /= length; vec[1] /= length;
@ -789,9 +796,9 @@ namespace Game
float Vec3Normalize(vec3_t& vec) float Vec3Normalize(vec3_t& vec)
{ {
const float length = std::sqrt(std::pow(vec[0], 2.0f) + std::pow(vec[1], 2.0f) + std::pow(vec[2], 2.0f)); const auto length = std::sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]);
if(length > 0.0f) if (length > 0.0f)
{ {
vec[0] /= length; vec[0] /= length;
vec[1] /= length; vec[1] /= length;

View File

@ -46,9 +46,12 @@ namespace Game
typedef void(__cdecl * Cbuf_AddServerText_t)(); typedef void(__cdecl * Cbuf_AddServerText_t)();
extern Cbuf_AddServerText_t Cbuf_AddServerText; extern Cbuf_AddServerText_t Cbuf_AddServerText;
typedef void(__cdecl * Cbuf_AddText_t)(int localClientNum, const char *text); typedef void(__cdecl * Cbuf_AddText_t)(int localClientNum, const char* text);
extern Cbuf_AddText_t Cbuf_AddText; extern Cbuf_AddText_t Cbuf_AddText;
typedef void(__cdecl * Cbuf_InsertText_t)(int localClientNum, const char* text);
extern Cbuf_InsertText_t Cbuf_InsertText;
typedef int(__cdecl * CG_GetClientNum_t)(); typedef int(__cdecl * CG_GetClientNum_t)();
extern CG_GetClientNum_t CG_GetClientNum; extern CG_GetClientNum_t CG_GetClientNum;
@ -759,7 +762,7 @@ namespace Game
typedef void(__cdecl * SV_Cmd_EndTokenizedString_t)(); typedef void(__cdecl * SV_Cmd_EndTokenizedString_t)();
extern SV_Cmd_EndTokenizedString_t SV_Cmd_EndTokenizedString; extern SV_Cmd_EndTokenizedString_t SV_Cmd_EndTokenizedString;
typedef void(__cdecl* SV_Cmd_ArgvBuffer_t)(int arg, char* buf, int size); typedef void(__cdecl * SV_Cmd_ArgvBuffer_t)(int arg, char* buf, int size);
extern SV_Cmd_ArgvBuffer_t SV_Cmd_ArgvBuffer; extern SV_Cmd_ArgvBuffer_t SV_Cmd_ArgvBuffer;
typedef void(__cdecl * SV_SetConfigstring_t)(int index, const char* string); typedef void(__cdecl * SV_SetConfigstring_t)(int index, const char* string);
@ -894,6 +897,15 @@ namespace Game
typedef void(__cdecl * AimAssist_ApplyAutoMelee_t)(const AimInput* input, AimOutput* output); typedef void(__cdecl * AimAssist_ApplyAutoMelee_t)(const AimInput* input, AimOutput* output);
extern AimAssist_ApplyAutoMelee_t AimAssist_ApplyAutoMelee; extern AimAssist_ApplyAutoMelee_t AimAssist_ApplyAutoMelee;
typedef void(__cdecl * Jump_ClearState_t)(playerState_s* ps);
extern Jump_ClearState_t Jump_ClearState;
typedef void(__cdecl * PM_playerTrace_t)(pmove_s*, trace_t*, const float*, const float*, const Bounds*, int, int);
extern PM_playerTrace_t PM_playerTrace;
typedef void(__cdecl * PM_Trace_t)(pmove_s*, trace_t*, const float*, const float*, const Bounds*, int, int);
extern PM_Trace_t PM_Trace;
extern XAssetHeader* DB_XAssetPool; extern XAssetHeader* DB_XAssetPool;
extern unsigned int* g_poolSize; extern unsigned int* g_poolSize;
@ -944,6 +956,7 @@ namespace Game
extern int* serverMessageSequence; extern int* serverMessageSequence;
constexpr auto MAX_GENTITIES = 2048u; constexpr auto MAX_GENTITIES = 2048u;
constexpr auto ENTITYNUM_NONE = MAX_GENTITIES - 1;
extern gentity_t* g_entities; extern gentity_t* g_entities;
extern netadr_t* connectedHost; extern netadr_t* connectedHost;
@ -1013,6 +1026,8 @@ namespace Game
constexpr auto AIM_ASSIST_GRAPH_COUNT = 4u; constexpr auto AIM_ASSIST_GRAPH_COUNT = 4u;
extern GraphFloat* aaInputGraph; extern GraphFloat* aaInputGraph;
extern vec3_t* CorrectSolidDeltas;
XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize); XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize);
void Menu_FreeItemMemory(Game::itemDef_s* item); void Menu_FreeItemMemory(Game::itemDef_s* item);
void Menu_SetNextCursorItem(Game::UiContext* ctx, Game::menuDef_t* currentMenu, int unk = 1); void Menu_SetNextCursorItem(Game::UiContext* ctx, Game::menuDef_t* currentMenu, int unk = 1);

View File

@ -211,6 +211,31 @@ namespace Game
FL_MOVER_SLIDE = 0x8000000 FL_MOVER_SLIDE = 0x8000000
}; };
typedef enum
{
HITLOC_NONE,
HITLOC_HELMET,
HITLOC_HEAD,
HITLOC_NECK,
HITLOC_TORSO_UPR,
HITLOC_TORSO_LWR,
HITLOC_R_ARM_UPR,
HITLOC_L_ARM_UPR,
HITLOC_R_ARM_LWR,
HITLOC_L_ARM_LWR,
HITLOC_R_HAND,
HITLOC_L_HAND,
HITLOC_R_LEG_UPR,
HITLOC_L_LEG_UPR,
HITLOC_R_LEG_LWR,
HITLOC_L_LEG_LWR,
HITLOC_R_FOOT,
HITLOC_L_FOOT,
HITLOC_GUN,
HITLOC_SHIELD,
HITLOC_NUM
} hitLocation_t;
struct FxEffectDef; struct FxEffectDef;
struct pathnode_t; struct pathnode_t;
struct pathnode_tree_t; struct pathnode_tree_t;
@ -1085,6 +1110,40 @@ namespace Game
PM_DEAD_LINKED = 0x9, PM_DEAD_LINKED = 0x9,
}; };
enum playerEFlag
{
EF_NONSOLID_BMODEL = 0x1,
EF_TELEPORT_BIT = 0x2,
EF_CROUCHING = 0x4,
EF_PRONE = 0x8,
EF_NODRAW = 0x20,
EF_TIMED_OBJECT = 0x40,
EF_VOTED = 0x80,
EF_TALK = 0x100,
EF_FIRING = 0x200,
EF_TURRET_ACTIVE_PRONE = 0x400,
EF_TURRET_ACTIVE_DUCK = 0x800,
EF_LOCK_LIGHT_VIS = 0x1000,
EF_AIM_ASSIST = 0x2000,
EF_LOOP_RUMBLE = 0x4000,
EF_LASER_SIGHT = 0x8000,
EF_MANTLE = 0x10000,
EF_DEAD = 0x20000,
EF_ADS = 0x40000,
EF_NEW = 0x80000,
EF_VEHICLE_ACTIVE = 0x100000,
EF_JAMMING = 0x200000,
EF_COMPASS_PING = 0x400000,
EF_SOFT = 0x800000
};
enum playerLinkFlag
{
PLF_ANGLES_LOCKED = 0x1,
PLF_USES_OFFSET = 0x2,
PLF_WEAPONVIEW_ONLY = 0x4
};
struct playerState_s struct playerState_s
{ {
int commandTime; int commandTime;
@ -1132,7 +1191,7 @@ namespace Game
int unpredictableEventSequenceOld; int unpredictableEventSequenceOld;
int unpredictableEvents[4]; int unpredictableEvents[4];
unsigned int unpredictableEventParms[4]; unsigned int unpredictableEventParms[4];
int clientNum; int clientNum; // 260
int viewmodelIndex; int viewmodelIndex;
float viewangles[3]; float viewangles[3];
int viewHeightTarget; int viewHeightTarget;
@ -1958,7 +2017,7 @@ namespace Game
struct __declspec(align(4)) MapEnts struct __declspec(align(4)) MapEnts
{ {
const char *name; const char *name;
char *entityString; const char *entityString;
int numEntityChars; int numEntityChars;
MapTriggers trigger; MapTriggers trigger;
Stage *stages; Stage *stages;
@ -5348,21 +5407,42 @@ namespace Game
PLAYER_FLAG_FROZEN = 1 << 2, PLAYER_FLAG_FROZEN = 1 << 2,
}; };
typedef enum
{
SESS_STATE_PLAYING = 0x0,
SESS_STATE_DEAD = 0x1,
SESS_STATE_SPECTATOR = 0x2,
SESS_STATE_INTERMISSION = 0x3
} sessionState_t;
typedef enum
{
CON_DISCONNECTED = 0x0,
CON_CONNECTING = 0x1,
CON_CONNECTED = 0x2
} clientConnected_t;
typedef struct gclient_s typedef struct gclient_s
{ {
unsigned char pad[12764]; playerState_s ps;
unsigned int team; sessionState_t sessionState; // 12572
char pad0[40];
clientConnected_t connected; // 12616
char pad1[144];
unsigned int team; // 12764
char pad2[436]; char pad2[436];
int flags; int flags; // 13204
int spectatorClient; int spectatorClient;
int lastCmdTime; int lastCmdTime;
int buttons; int buttons;
int oldbuttons; int oldbuttons; // 13220
int latched_buttons; int latched_buttons; // 13224
int buttonsSinceLastFrame; int buttonsSinceLastFrame; // 13228
char pad3[700]; char pad3[700]; // 13232
} gclient_t; } gclient_t;
static_assert(sizeof(gclient_t) == 13932);
struct EntHandle struct EntHandle
{ {
unsigned __int16 number; unsigned __int16 number;
@ -5421,14 +5501,14 @@ namespace Game
void /*Vehicle*/* vehicle; void /*Vehicle*/* vehicle;
int physObjId; int physObjId;
unsigned __int16 model; unsigned __int16 model;
char physicsObject; unsigned char physicsObject;
char takedamage; unsigned char takedamage;
char active; unsigned char active;
char handler; unsigned char handler;
char team; unsigned char team;
bool freeAfterEvent; bool freeAfterEvent;
__int16 padding_short; __int16 padding_short;
short classname; unsigned __int16 classname;
unsigned __int16 script_classname; unsigned __int16 script_classname;
unsigned __int16 script_linkName; unsigned __int16 script_linkName;
unsigned __int16 target; unsigned __int16 target;
@ -5456,7 +5536,10 @@ namespace Game
char pad[100]; char pad[100];
} gentity_t; } gentity_t;
static_assert(sizeof(gentity_s) == 0x274);
#pragma pack(push, 1) #pragma pack(push, 1)
typedef struct client_s typedef struct client_s
{ {
clientstate_t state; // 0 clientstate_t state; // 0
@ -5495,6 +5578,7 @@ namespace Game
unsigned __int64 steamID; // 278272 unsigned __int64 steamID; // 278272
char __pad9[403592]; // 278280 char __pad9[403592]; // 278280
} client_t; } client_t;
#pragma pack(pop) #pragma pack(pop)
static_assert(sizeof(client_t) == 0xA6790); static_assert(sizeof(client_t) == 0xA6790);
@ -6930,15 +7014,43 @@ namespace Game
const char* args[9]; const char* args[9];
}; };
enum TraceHitType
{
TRACE_HITTYPE_NONE = 0,
TRACE_HITTYPE_ENTITY = 1,
TRACE_HITTYPE_DYNENT_MODEL = 2,
TRACE_HITTYPE_DYNENT_BRUSH = 3,
TRACE_HITTYPE_GLASS = 4
};
struct trace_t
{
float fraction;
float normal[3];
int surfaceFlags;
int contents;
const char* material;
TraceHitType hitType;
unsigned __int16 hitId;
unsigned __int16 modelIndex;
unsigned __int16 partName;
unsigned __int16 partGroup;
bool allsolid;
bool startsolid;
bool walkable;
};
static_assert(sizeof(trace_t) == 0x2C);
struct pmove_s struct pmove_s
{ {
playerState_s* ps; playerState_s* ps;
usercmd_s cmd; usercmd_s cmd;
usercmd_s oldcmd; usercmd_s oldcmd;
int tracemask; int tracemask; // 84
int numtouch; int numtouch;
int touchents[32]; int touchents[32];
char __pad0[24]; Bounds bounds; // 220
float xyspeed; float xyspeed;
int proneChange; int proneChange;
float maxSprintTimeMultiplier; float maxSprintTimeMultiplier;
@ -6952,6 +7064,27 @@ namespace Game
unsigned char handler; unsigned char handler;
}; };
static_assert(sizeof(pmove_s) == 296);
struct pml_t
{
float forward[3];
float right[3];
float up[3];
float frametime;
int msec;
int walking;
int groundPlane;
int almostGroundPlane;
trace_t groundTrace;
float impactSpeed;
float previous_origin[3];
float previous_velocity[3];
int holdrand;
};
static_assert(sizeof(pml_t) == 0x84);
enum EffectiveStance enum EffectiveStance
{ {
PM_EFF_STANCE_DEFAULT = 0, PM_EFF_STANCE_DEFAULT = 0,

View File

@ -156,13 +156,13 @@ namespace Steam
gameID.type = 1; // k_EGameIDTypeGameMod gameID.type = 1; // k_EGameIDTypeGameMod
gameID.appID = Proxy::AppId & 0xFFFFFF; gameID.appID = Proxy::AppId & 0xFFFFFF;
char* modId = const_cast<char*>("IW4x"); const char* modId = "IW4x";
gameID.modID = *reinterpret_cast<unsigned int*>(modId) | 0x80000000; gameID.modID = *reinterpret_cast<const unsigned int*>(modId) | 0x80000000;
Interface clientUtils(Proxy::ClientEngine->GetIClientUtils(Proxy::SteamPipe)); Interface clientUtils(Proxy::ClientEngine->GetIClientUtils(Proxy::SteamPipe));
clientUtils.invoke<void>("SetAppIDForCurrentPipe", Proxy::AppId, false); clientUtils.invoke<void>("SetAppIDForCurrentPipe", Proxy::AppId, false);
char ourPath[MAX_PATH] = { 0 }; char ourPath[MAX_PATH] = {0};
GetModuleFileNameA(GetModuleHandle(nullptr), ourPath, sizeof(ourPath)); GetModuleFileNameA(GetModuleHandle(nullptr), ourPath, sizeof(ourPath));
char ourDirectory[MAX_PATH] = { 0 }; char ourDirectory[MAX_PATH] = { 0 };

View File

@ -107,12 +107,12 @@ namespace Utils
void SetEnvironment() void SetEnvironment()
{ {
wchar_t exeName[512]; wchar_t exeName[512];
GetModuleFileName(GetModuleHandle(nullptr), exeName, sizeof(exeName) / 2); GetModuleFileNameW(GetModuleHandle(nullptr), exeName, sizeof(exeName) / 2);
wchar_t* exeBaseName = wcsrchr(exeName, L'\\'); wchar_t* exeBaseName = wcsrchr(exeName, L'\\');
exeBaseName[0] = L'\0'; exeBaseName[0] = L'\0';
SetCurrentDirectory(exeName); SetCurrentDirectoryW(exeName);
} }
HMODULE GetNTDLL() HMODULE GetNTDLL()
@ -149,4 +149,15 @@ namespace Utils
{ {
return !(base1 + len1 <= base2 || base2 + len2 <= base1); return !(base1 + len1 <= base2 || base2 + len2 <= base1);
} }
float Vec3SqrDistance(const float v1[3], const float v2[3])
{
auto x = v2[0] - v1[0];
auto y = v2[1] - v1[1];
auto z = v2[2] - v1[2];
return x * x + y * y + z * z;
}
} }

View File

@ -24,6 +24,7 @@ namespace Utils
void OpenUrl(const std::string& url); void OpenUrl(const std::string& url);
bool HasIntercection(unsigned int base1, unsigned int len1, unsigned int base2, unsigned int len2); bool HasIntercection(unsigned int base1, unsigned int len1, unsigned int base2, unsigned int len2);
float Vec3SqrDistance(const float v1[3], const float v2[3]);
template <typename T> inline void RotLeft(T& object, size_t bits) template <typename T> inline void RotLeft(T& object, size_t bits)
{ {

Binary file not shown.

Binary file not shown.