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

2
.gitmodules vendored
View File

@ -25,7 +25,7 @@
[submodule "deps/protobuf"]
path = deps/protobuf
url = https://github.com/google/protobuf.git
branch = 3.11.x
branch = 3.17.x
[submodule "deps/udis86"]
path = deps/udis86
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
- Run `premake5 vs2019` 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.)
- Run `premake5 vs2022` or use the delivered `generate.bat`.
- Build via solution file in `build\iw4x.sln`.
## Premake arguments
@ -33,24 +33,22 @@
## Command line arguments
| Argument | Description |
|:----------------------------|:-----------------------------------------------|
| `-tests` | Perform unit tests. |
| `-entries` | Prints fast file info to the console. |
| `-stdout` | Redirect stdout to the external console. |
| `-console` | Enables external console. |
| `-dedicated` | Dedicated server. |
| `-scriptablehttp` | Adds HTTP console commands. |
| `-bigdumps` | Enables dumps. |
| `-reallybigdumps` | Unused. |
| `-bigminidumps` | Mini dumps. |
| `-reallybigminidumps` | Big mini dumps. |
| `-dump` | Prints asset info to a .ents file. |
| `-monitor` | Enables monitor. |
| `-nointro` | Skips game's intro. |
| `-version` | Prints IW4X version. |
| `-zonebuilder` | Enables zone builder. |
| `-nosteam` | Disables Steam features. |
| Argument | Description |
|:------------------------|:-----------------------------------------------|
| `-tests` | Perform unit tests. |
| `-entries` | Print to the console a list of every asset as they are loaded from zonefiles. |
| `-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` | Allow the game to display its own separate interactive console window. |
| `-dedicated` | Starts the game as a headless dedicated server. |
| `-scriptablehttp` | Enable HTTP related gsc functions. |
| `-bigminidumps` | Include all code sections from loaded modules in the dump. |
| `-reallybigminidumps` | Include data sections from all loaded modules in the dump. |
| `-dump` | Write info of loaded assets to the raw folder as they are being loaded. |
| `-monitor` | This flag is for internal use and it is used to indicate if an external console is present. |
| `-nointro` | Skip game's cinematic intro. |
| `-version` | Print IW4x build info on startup. |
| `-zonebuilder` | Start the interactive zonebuilder tool console instead of starting the game. |
| `-nosteam` | Disable friends feature and do not update Steam about the game's current status just like an invisible mode. |
## Disclaimer

View File

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

View File

@ -48,9 +48,6 @@ namespace Components
Loader::Register(new Party());
Loader::Register(new Zones());
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 Script());
Loader::Register(new Weapon());

View File

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

View File

@ -634,7 +634,7 @@ namespace Components
LUID luid;
TOKEN_PRIVILEGES tp = { 0 };
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.Privileges[0].Luid = luid;

View File

@ -3,7 +3,7 @@
namespace Components
{
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, Utils::Slot<AssetHandler::Callback>> AssetHandler::TypeCallbacks;
Utils::Signal<AssetHandler::RestrictCallback> AssetHandler::RestrictSignal;
@ -70,20 +70,20 @@ namespace Components
return header;
}
Game::XAssetHeader AssetHandler::FindTemporaryAsset(Game::XAssetType type, const char* filename)
{
Game::XAssetHeader header = { nullptr };
if (type >= Game::XAssetType::ASSET_TYPE_COUNT) return header;
Game::XAssetHeader AssetHandler::FindTemporaryAsset(Game::XAssetType type, const char* filename)
{
Game::XAssetHeader header = { nullptr };
if (type >= Game::XAssetType::ASSET_TYPE_COUNT) return header;
auto tempPool = &AssetHandler::TemporaryAssets[type];
auto entry = tempPool->find(filename);
if (entry != tempPool->end())
{
header = { entry->second };
}
auto tempPool = &AssetHandler::TemporaryAssets[type];
auto entry = tempPool->find(filename);
if (entry != tempPool->end())
{
header = { entry->second };
}
return header;
}
return header;
}
int AssetHandler::HasThreadBypass()
{
@ -119,7 +119,6 @@ namespace Components
push esi
push edi
push eax
pushad
@ -130,14 +129,12 @@ namespace Components
popad
pop eax
test al, al
jnz checkTempAssets
mov ecx, [esp + 18h] // Asset type
mov ebx, [esp + 1Ch] // Filename
push eax
pushad
@ -152,31 +149,30 @@ namespace Components
popad
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
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
jnz finishFound
finishOriginal:
finishOriginal:
// 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 eax, 40793Bh
jmp eax
@ -546,10 +542,10 @@ namespace Components
for (int i = 0; i < vertexdecl->streamCount; i++)
{
routingData.push_back(json11::Json::object
{
{ "source", (int)vertexdecl->routing.data[i].source },
{ "dest", (int)vertexdecl->routing.data[i].dest },
});
{
{ "source", (int)vertexdecl->routing.data[i].source },
{ "dest", (int)vertexdecl->routing.data[i].dest },
});
}
std::vector<json11::Json> declData;
@ -770,7 +766,7 @@ namespace Components
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_LEADERBOARD, 500);
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::IFxWorld());
AssetHandler::RegisterInterface(new Assets::IMapEnts());
@ -781,9 +777,9 @@ namespace Components
AssetHandler::RegisterInterface(new Assets::ISndCurve());
AssetHandler::RegisterInterface(new Assets::IMaterial());
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::ITracerDef());
AssetHandler::RegisterInterface(new Assets::ITracerDef());
AssetHandler::RegisterInterface(new Assets::IPhysPreset());
AssetHandler::RegisterInterface(new Assets::IXAnimParts());
AssetHandler::RegisterInterface(new Assets::IFxEffectDef());

View File

@ -39,13 +39,13 @@ namespace Components
static void ResetBypassState();
static void ExposeTemporaryAssets(bool expose);
static void ExposeTemporaryAssets(bool expose);
static void OffsetToAlias(Utils::Stream::Offset* offset);
private:
static thread_local int BypassState;
static bool ShouldSearchTempAssets;
static bool ShouldSearchTempAssets;
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 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 void FindAssetStub();
static void AddAssetStub();

View File

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

View File

@ -136,6 +136,41 @@ namespace Components
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65,
(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()

View File

@ -18,14 +18,14 @@ namespace Components
return result;
}
char* Command::Params::operator[](size_t index)
const char* Command::Params::operator[](size_t 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];
}
@ -34,9 +34,9 @@ namespace Components
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];
}
@ -160,52 +160,6 @@ namespace Components
{
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)
{
if (params->length() > 1)

View File

@ -10,11 +10,11 @@ namespace Components
public:
Params() {};
virtual ~Params() {};
virtual char* get(size_t index) = 0;
virtual const char* get(size_t index) = 0;
virtual size_t length() = 0;
virtual std::string join(size_t startIndex);
virtual char* operator[](size_t index);
virtual const char* operator[](size_t index);
};
class ClientParams : public Params
@ -24,7 +24,7 @@ namespace Components
ClientParams(const ClientParams &obj) : commandId(obj.commandId) {};
ClientParams() : ClientParams(*Game::cmd_id) {};
char* get(size_t index) override;
const char* get(size_t index) override;
size_t length() override;
private:
@ -38,7 +38,7 @@ namespace Components
ServerParams(const ServerParams &obj) : commandId(obj.commandId) {};
ServerParams() : ServerParams(*Game::cmd_id_sv) {};
char* get(size_t index) override;
const char* get(size_t index) override;
size_t length() override;
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()
{
// Map rotation
@ -311,7 +316,7 @@ namespace Components
Utils::Hook::Nop(0x4DCEC9, 2); // some check preventing proper game functioning
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(0x59A896, 5); // warning message on a removed subsystem
Utils::Hook::Nop(0x4B4EEF, 5); // same as above
@ -326,14 +331,8 @@ namespace Components
// isHost script call return 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
Utils::Hook::Nop(0x4D3C7B, 5);
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*>();
Utils::Hook(0x4D3C7B, Dedicated::Dvar_RegisterSVNetworkFps, HOOK_CALL).install()->quick();
// r_loadForRenderer default to 0
Utils::Hook::Set<BYTE>(0x519DDF, 0);

View File

@ -29,5 +29,7 @@ namespace Components
static void TransmitGuids();
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;
}
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()
{
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()
{
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;
}
@ -193,7 +193,7 @@ namespace Components
void Dvar::ResetDvarsValue()
{
if (!Utils::IO::FileExists(Dvar::ArchiveDvarPath))
return
return;
Command::Execute("exec archivedvars.cfg", true);
// Clean up
@ -209,7 +209,7 @@ namespace Components
Scheduler::OnFrame([]()
{
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
if (name == lastValidName) return;

View File

@ -2,38 +2,96 @@
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
{
push eax
mov eax, Elevators::SV_DisableElevators
cmp byte ptr [eax + 16], 1
pushad
push [esp + 0x8 + 0x24]
push [esp + 0x8 + 0x24]
push eax
call Elevators::PM_CorrectAllSolid
add esp, 0xC
mov [esp + 0x20], eax
popad
pop eax
// Always skip PM_CorrectAllSolid if SV_DisableElevators is set to 1
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
ret
}
}
@ -41,12 +99,26 @@ namespace Components
{
Dvar::OnInit([]
{
Elevators::SV_DisableElevators = Game::Dvar_RegisterBool("sv_disableElevators",
false, Game::DVAR_FLAG_REPLICATED, "Disable elevators");
static const char* values[] =
{
"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)
Utils::Hook(0x573689, Elevators::PM_GroundTraceStub, HOOK_JUMP).install()->quick();
//Replace PM_CorrectAllSolid
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()

View File

@ -9,8 +9,11 @@ namespace Components
~Elevators();
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();
int notify = Dvar::Var("cl_notifyFriendState").get<int>();
if (gotOnline && (notify == -1 || (notify == 1 && !Game::CL_IsCgameInitialized())) && !Dvar::Var("ui_streamFriendly").get<bool>())
const auto notify = Dvar::Var("cl_notifyFriendState").get<bool>();
if (gotOnline && (!notify || (notify && !Game::CL_IsCgameInitialized())) && !Dvar::Var("ui_streamFriendly").get<bool>())
{
Game::Material* material = Friends::CreateAvatar(user);
Toast::Show(material, entry->name, "is playing IW4x", 3000, [material]()
@ -186,7 +186,7 @@ namespace Components
{
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
for (auto entry : Friends::FriendsList)
@ -578,14 +578,19 @@ namespace Components
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled() || Monitor::IsEnabled()) return;
Dvar::Register<bool>("cl_anonymous", false, Game::DVAR_FLAG_SAVED, "");
Dvar::Register<int>("cl_notifyFriendState", 1, -1, 1, Game::DVAR_FLAG_SAVED, "");
Dvar::Register<bool>("cl_anonymous", false, Game::DVAR_FLAG_SAVED, "Enable invisible mode for Steam");
Dvar::Register<bool>("cl_notifyFriendState", true, Game::DVAR_FLAG_SAVED, "Update friends about current game status");
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;
id.bits = atoll(params->get(1));
id.bits = std::strtoull(params->get(1), nullptr, 16);
Friends::AddFriend(id);
});

View File

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

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()
&& (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;
return;
}
@ -241,7 +249,7 @@ namespace Components
{
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;
@ -249,11 +257,11 @@ namespace Components
if (ents.exists())
{
mapEntities = ents.getBuffer();
asset.mapEnts->entityString = const_cast<char*>(mapEntities.data());
asset.mapEnts->entityString = mapEntities.data();
asset.mapEnts->numEntityChars = mapEntities.size() + 1;
}
}
// This is broken
if ((type == Game::XAssetType::ASSET_TYPE_MENU || type == Game::XAssetType::ASSET_TYPE_MENULIST) && Zones::Version() >= 359)
{
@ -318,7 +326,7 @@ namespace Components
mapname = "mp_shipment_long";
}
_snprintf_s(buffer, size, size, format, mapname);
_snprintf_s(buffer, size, _TRUNCATE, format, mapname);
}
void Maps::HandleAsSPMap()
@ -333,7 +341,7 @@ namespace Components
{
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);
return;
@ -439,7 +447,7 @@ namespace Components
void Maps::LoadNewMapCommand(char* buffer, size_t size, const char* /*format*/, const char* mapname, const char* gametype)
{
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)
@ -784,7 +792,7 @@ namespace Components
{
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;
}
}

View File

@ -21,7 +21,7 @@ namespace Components
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);
if (playlist.exists())

View File

@ -2,7 +2,6 @@
namespace Components
{
int QuickPatch::FrameTime = 0;
Dvar::Var QuickPatch::r_customAspectRatio;
void QuickPatch::UnlockStats()
@ -433,15 +432,15 @@ namespace Components
jmp ebx
}
}
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::FrameTime = 0;
Scheduler::OnFrame([]()
{
QuickPatch::FrameTime = Game::Sys_Milliseconds();
});
// quit_hard
Command::Add("quit_hard", [](Command::Params*)
{
@ -534,16 +533,11 @@ namespace Components
Utils::Hook::Set<const char*>(0x60BD56, "IW4x (" VERSION ")");
// version string color
static float buildLocColor[] = { 1.0f, 1.0f, 1.0f, 0.8f };
Utils::Hook::Set(0x43F710, buildLocColor);
static Game::vec4_t buildLocColor = { 1.0f, 1.0f, 1.0f, 0.8f };
Utils::Hook::Set<float*>(0x43F710, buildLocColor);
// Shift ui version string to the left (ui_buildlocation)
Utils::Hook::Nop(0x6310A0, 5); // Don't register the initial dvar
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");
});
Utils::Hook(0x6310A0, QuickPatch::Dvar_RegisterUIBuildLocation, HOOK_CALL).install()->quick();
// console title
if (ZoneBuilder::IsEnabled())
@ -754,7 +748,7 @@ namespace Components
Utils::Hook(0x4A9F56, QuickPatch::MsgReadBitsCompressCheckCL, HOOK_CALL).install()->quick(); // CL_ParseServerMessage
Utils::Hook(0x407376, QuickPatch::SVCanReplaceServerCommand , HOOK_CALL).install()->quick(); // SV_CanReplaceServerCommand
Utils::Hook(0x5B67ED, QuickPatch::AtolAdjustPlayerLimit , HOOK_CALL).install()->quick(); // PartyHost_HandleJoinPartyRequest
Utils::Hook::Nop(0x41698E, 5); // Disable Svcmd_EntityList_f
// Patch selectStringTableEntryInDvar
Utils::Hook::Set(0x405959, QuickPatch::SelectStringTableEntryInDvarStub);
@ -770,11 +764,6 @@ namespace Components
QuickPatch::UnlockStats();
});
Command::Add("crash", [](Command::Params*)
{
throw new std::exception();
});
Command::Add("dumptechsets", [](Command::Params* param)
{
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
Dvar::Register<bool>("ui_streamFriendly", false, Game::DVAR_FLAG_SAVED, "Stream friendly UI");
@ -1205,11 +996,6 @@ namespace Components
}
}
QuickPatch::~QuickPatch()
{
}
bool QuickPatch::unitTest()
{
uint32_t randIntCount = 4'000'000;

View File

@ -6,16 +6,12 @@ namespace Components
{
public:
QuickPatch();
~QuickPatch();
bool unitTest() override;
static void UnlockStats();
static int GetFrameTime() { return FrameTime; }
private:
static int FrameTime;
static void SelectStringTableEntryInDvarStub();
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_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::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()
{
__asm
@ -127,7 +151,7 @@ namespace Components
// show error
pushad;
push [esp + 24h + 20h];
push[esp + 24h + 20h];
push eax;
call R_TextureFromCodeError;
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);
}
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()
{
if (Dedicated::IsEnabled()) return;
// 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();
// }
// });
Scheduler::OnFrame([]() {
if (Game::CL_IsCgameInitialized())
{
DebugDrawAABBTrees();
DebugDrawModelNames();
DebugDrawModelBoundingBoxes();
DebugDrawSceneModelCollisions();
DebugDrawTriggers();
}
});
// 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(0x0054CF8D, Renderer::StoreGfxBufContextPtrStub2, HOOK_JUMP).install()->quick();
@ -230,6 +534,25 @@ namespace Components
// End vid_restart
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()

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 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> BeginRecoverDeviceSignal;
static Utils::Signal<BackendCallback> BackendFrameSignal;
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();
}
std::string message = StartupMessages::MessageList.front();
StartupMessages::MessageList.pop_front();
const auto& message = StartupMessages::MessageList.front();
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_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)
{
StartupMessages::MessageList.push_back(message);

View File

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

View File

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

View File

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

View File

@ -34,6 +34,7 @@ namespace Game
Cbuf_AddServerText_t Cbuf_AddServerText = Cbuf_AddServerText_t(0x4BB9B0);
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_GetClientNum_t CG_GetClientNum = CG_GetClientNum_t(0x433700);
@ -377,6 +378,10 @@ namespace Game
Field_AdjustScroll_t Field_AdjustScroll = Field_AdjustScroll_t(0x488C10);
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);
unsigned int* g_poolSize = reinterpret_cast<unsigned int*>(0x7995E8);
@ -493,6 +498,8 @@ namespace Game
GraphFloat* aaInputGraph = reinterpret_cast<GraphFloat*>(0x7A2FC0);
vec3_t* CorrectSolidDeltas = reinterpret_cast<vec3_t*>(0x739BB8); // Count 26
XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize)
{
int elSize = DB_GetXAssetSizeHandlers[type]();
@ -651,7 +658,7 @@ namespace Game
{
Dvar_SetStringByName("com_errorMessage", message.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)
{
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[1] /= length;
@ -789,9 +796,9 @@ namespace Game
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[1] /= length;

View File

@ -46,9 +46,12 @@ namespace Game
typedef void(__cdecl * Cbuf_AddServerText_t)();
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;
typedef void(__cdecl * Cbuf_InsertText_t)(int localClientNum, const char* text);
extern Cbuf_InsertText_t Cbuf_InsertText;
typedef int(__cdecl * CG_GetClientNum_t)();
extern CG_GetClientNum_t CG_GetClientNum;
@ -759,7 +762,7 @@ namespace Game
typedef void(__cdecl * SV_Cmd_EndTokenizedString_t)();
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;
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);
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 unsigned int* g_poolSize;
@ -944,6 +956,7 @@ namespace Game
extern int* serverMessageSequence;
constexpr auto MAX_GENTITIES = 2048u;
constexpr auto ENTITYNUM_NONE = MAX_GENTITIES - 1;
extern gentity_t* g_entities;
extern netadr_t* connectedHost;
@ -1013,6 +1026,8 @@ namespace Game
constexpr auto AIM_ASSIST_GRAPH_COUNT = 4u;
extern GraphFloat* aaInputGraph;
extern vec3_t* CorrectSolidDeltas;
XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize);
void Menu_FreeItemMemory(Game::itemDef_s* item);
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
};
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 pathnode_t;
struct pathnode_tree_t;
@ -1085,6 +1110,40 @@ namespace Game
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
{
int commandTime;
@ -1132,7 +1191,7 @@ namespace Game
int unpredictableEventSequenceOld;
int unpredictableEvents[4];
unsigned int unpredictableEventParms[4];
int clientNum;
int clientNum; // 260
int viewmodelIndex;
float viewangles[3];
int viewHeightTarget;
@ -1958,7 +2017,7 @@ namespace Game
struct __declspec(align(4)) MapEnts
{
const char *name;
char *entityString;
const char *entityString;
int numEntityChars;
MapTriggers trigger;
Stage *stages;
@ -5348,21 +5407,42 @@ namespace Game
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
{
unsigned char pad[12764];
unsigned int team;
playerState_s ps;
sessionState_t sessionState; // 12572
char pad0[40];
clientConnected_t connected; // 12616
char pad1[144];
unsigned int team; // 12764
char pad2[436];
int flags;
int flags; // 13204
int spectatorClient;
int lastCmdTime;
int buttons;
int oldbuttons;
int latched_buttons;
int buttonsSinceLastFrame;
char pad3[700];
int oldbuttons; // 13220
int latched_buttons; // 13224
int buttonsSinceLastFrame; // 13228
char pad3[700]; // 13232
} gclient_t;
static_assert(sizeof(gclient_t) == 13932);
struct EntHandle
{
unsigned __int16 number;
@ -5421,14 +5501,14 @@ namespace Game
void /*Vehicle*/* vehicle;
int physObjId;
unsigned __int16 model;
char physicsObject;
char takedamage;
char active;
char handler;
char team;
unsigned char physicsObject;
unsigned char takedamage;
unsigned char active;
unsigned char handler;
unsigned char team;
bool freeAfterEvent;
__int16 padding_short;
short classname;
unsigned __int16 classname;
unsigned __int16 script_classname;
unsigned __int16 script_linkName;
unsigned __int16 target;
@ -5456,7 +5536,10 @@ namespace Game
char pad[100];
} gentity_t;
static_assert(sizeof(gentity_s) == 0x274);
#pragma pack(push, 1)
typedef struct client_s
{
clientstate_t state; // 0
@ -5495,6 +5578,7 @@ namespace Game
unsigned __int64 steamID; // 278272
char __pad9[403592]; // 278280
} client_t;
#pragma pack(pop)
static_assert(sizeof(client_t) == 0xA6790);
@ -6930,15 +7014,43 @@ namespace Game
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
{
playerState_s* ps;
usercmd_s cmd;
usercmd_s oldcmd;
int tracemask;
int tracemask; // 84
int numtouch;
int touchents[32];
char __pad0[24];
Bounds bounds; // 220
float xyspeed;
int proneChange;
float maxSprintTimeMultiplier;
@ -6952,6 +7064,27 @@ namespace Game
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
{
PM_EFF_STANCE_DEFAULT = 0,

View File

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

View File

@ -107,12 +107,12 @@ namespace Utils
void SetEnvironment()
{
wchar_t exeName[512];
GetModuleFileName(GetModuleHandle(nullptr), exeName, sizeof(exeName) / 2);
GetModuleFileNameW(GetModuleHandle(nullptr), exeName, sizeof(exeName) / 2);
wchar_t* exeBaseName = wcsrchr(exeName, L'\\');
exeBaseName[0] = L'\0';
SetCurrentDirectory(exeName);
SetCurrentDirectoryW(exeName);
}
HMODULE GetNTDLL()
@ -149,4 +149,15 @@ namespace Utils
{
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);
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)
{

Binary file not shown.

Binary file not shown.