commit
dfd7f84ebc
19
.github/workflows/build.yml
vendored
19
.github/workflows/build.yml
vendored
@ -9,6 +9,10 @@ on:
|
|||||||
- "*"
|
- "*"
|
||||||
types: [opened, synchronize, reopened]
|
types: [opened, synchronize, reopened]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build binaries
|
name: Build binaries
|
||||||
@ -19,14 +23,6 @@ jobs:
|
|||||||
- Debug
|
- Debug
|
||||||
- Release
|
- Release
|
||||||
steps:
|
steps:
|
||||||
- name: Wait for previous workflows
|
|
||||||
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
|
||||||
uses: softprops/turnstyle@v1
|
|
||||||
with:
|
|
||||||
poll-interval-seconds: 10
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Check out files
|
- name: Check out files
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
@ -84,13 +80,6 @@ jobs:
|
|||||||
- name: Add known hosts
|
- name: Add known hosts
|
||||||
run: ssh-keyscan -H ${{ secrets.XLABS_MASTER_SSH_ADDRESS }} >> ~/.ssh/known_hosts
|
run: ssh-keyscan -H ${{ secrets.XLABS_MASTER_SSH_ADDRESS }} >> ~/.ssh/known_hosts
|
||||||
|
|
||||||
- name: Wait for previous workflows
|
|
||||||
uses: softprops/turnstyle@v1
|
|
||||||
with:
|
|
||||||
poll-interval-seconds: 10
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
# - name: Remove old data files
|
# - name: Remove old data files
|
||||||
# run: ssh ${{ secrets.XLABS_MASTER_SSH_USER }}@${{ secrets.XLABS_MASTER_SSH_ADDRESS }} rm -rf ${{ env.XLABS_MASTER_PATH }}/iw4x/data/*
|
# run: ssh ${{ secrets.XLABS_MASTER_SSH_USER }}@${{ secrets.XLABS_MASTER_SSH_ADDRESS }} rm -rf ${{ env.XLABS_MASTER_PATH }}/iw4x/data/*
|
||||||
|
|
||||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -32,5 +32,6 @@
|
|||||||
path = deps/GSL
|
path = deps/GSL
|
||||||
url = https://github.com/microsoft/GSL.git
|
url = https://github.com/microsoft/GSL.git
|
||||||
[submodule "deps/nlohmannjson"]
|
[submodule "deps/nlohmannjson"]
|
||||||
path = deps/nlohmannjson
|
path = deps/json
|
||||||
url = https://github.com/nlohmann/json.git
|
url = https://github.com/nlohmann/json.git
|
||||||
|
branch = v3.11.2
|
||||||
|
45
CHANGELOG.md
45
CHANGELOG.md
@ -4,6 +4,51 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.3.0/) and this project adheres to [Semantic Versioning](http://semver.org/).
|
The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.3.0/) and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
## [0.7.7] - 2022-12-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add `r_forceTechnique` Dvar to debug techniques on materials.
|
||||||
|
- Add `IsSprinting` GSC method (#587)
|
||||||
|
- Add `StorageLoad` GSC function (#595)
|
||||||
|
- Add `bg_climbAnything` Dvar (#663)
|
||||||
|
- Add ClanTag support for bots (#645)
|
||||||
|
- Add `sv_randomBotNames` Dvar (#665)
|
||||||
|
- Add support for parsing localized strings files (.str & .json) (#621)
|
||||||
|
- Add `callvote` menus (#613)
|
||||||
|
- IW3x-port converts Technique Sets between CoD4 and CoD6 properly.
|
||||||
|
- Add new map porting utility tool that makes the map porting process between CoD4 to CoD6 easy.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- `r_drawModelNames` now uses ground lighting when available.
|
||||||
|
- Speculars are now enabled on custom maps.
|
||||||
|
- Techset(s) get loaded from disk first rather than memory.
|
||||||
|
- Use CSO format for Vertex Shaders & Pixel Shaders on ZoneBuilder to allow replacement/swapping.
|
||||||
|
- New map porting utility tool builds teams directly into the map.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix bug where ZoneBuilder would not build loaded sounds correctly.
|
||||||
|
- Fix bug where ZoneBuilder no longer differentiates assets depending on their name.
|
||||||
|
- Fix building FX with ZoneBuilder.
|
||||||
|
- Fix branding in ZoneBuilder generated zones.
|
||||||
|
- Fix Script String crash when building zones.
|
||||||
|
- Fix the changelog menu (#583)
|
||||||
|
- Fix bug when adding commands (#609)
|
||||||
|
- Fix bug where some ported maps would either crash or lag (mp_zavod, mp_kowloon, ...)
|
||||||
|
- Fix GSC conversion from CoD4 to CoD6 (Specular Scale, Create Exp Fog, Create FX, ...)
|
||||||
|
- The map porting process from CoD4 to IW4x has improved.
|
||||||
|
- Ported map zones are about 40% lighter than before.
|
||||||
|
- Static models are now lit correctly depending on their position and ground lighting.
|
||||||
|
- New map porting utility ports sounds and effects properly.
|
||||||
|
|
||||||
|
### Known issues
|
||||||
|
|
||||||
|
- HTTPS is not supported for fast downloads at the moment.
|
||||||
|
- Sound issue fix is experimental as the bug is not fully understood.
|
||||||
|
- `reloadmenus` command does not free resources used by custom menus.
|
||||||
|
|
||||||
## [0.7.6] - 2022-11-22
|
## [0.7.6] - 2022-11-22
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
| `--copy-to=PATH` | Optional, copy the DLL to a custom folder after build, define the path here if wanted. |
|
| `--copy-to=PATH` | Optional, copy the DLL to a custom folder after build, define the path here if wanted. |
|
||||||
| `--copy-pdb` | Copy debug information for binaries as well to the path given via --copy-to. |
|
| `--copy-pdb` | Copy debug information for binaries as well to the path given via --copy-to. |
|
||||||
| `--force-unit-tests` | Always compile unit tests. |
|
| `--force-unit-tests` | Always compile unit tests. |
|
||||||
| `--force-exception-handler` | Install custom unhandled exception handler even for Debug builds. |
|
|
||||||
| `--disable-binary-check` | Do not perform integrity checks on the exe. |
|
| `--disable-binary-check` | Do not perform integrity checks on the exe. |
|
||||||
|
|
||||||
## Command line arguments
|
## Command line arguments
|
||||||
|
2
deps/GSL
vendored
2
deps/GSL
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 517ed29228d18cf2c5004d10826090108e06f049
|
Subproject commit f94c1f6f2b5e141d5f6eb3d284cd4a8cf9a81aac
|
1
deps/json
vendored
Submodule
1
deps/json
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit bc889afb4c5bf1c0d8ee29ef35eaaf4c8bef8a5d
|
2
deps/mongoose
vendored
2
deps/mongoose
vendored
@ -1 +1 @@
|
|||||||
Subproject commit db81c30d24df98031e41e33423a5eef0e89ba8c6
|
Subproject commit 73813a838386f6ebca447eb54c626803163ee257
|
1
deps/nlohmannjson
vendored
1
deps/nlohmannjson
vendored
@ -1 +0,0 @@
|
|||||||
Subproject commit a3e6e26dc83a726b292f5be0492fcc408663ce55
|
|
19
deps/premake/json.lua
vendored
Normal file
19
deps/premake/json.lua
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
json = {
|
||||||
|
source = path.join(dependencies.basePath, "json"),
|
||||||
|
}
|
||||||
|
|
||||||
|
function json.import()
|
||||||
|
json.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function json.includes()
|
||||||
|
includedirs {
|
||||||
|
path.join(json.source, "single_include/nlohmann")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function json.project()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, json)
|
3
deps/premake/libtomcrypt.lua
vendored
3
deps/premake/libtomcrypt.lua
vendored
@ -17,6 +17,8 @@ function libtomcrypt.includes()
|
|||||||
"LTC_NO_FAST",
|
"LTC_NO_FAST",
|
||||||
"LTC_NO_PROTOTYPES",
|
"LTC_NO_PROTOTYPES",
|
||||||
"LTC_NO_RSA_BLINDING",
|
"LTC_NO_RSA_BLINDING",
|
||||||
|
"LTC_NO_FILE",
|
||||||
|
"ARGTYPE=4",
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -32,6 +34,7 @@ function libtomcrypt.project()
|
|||||||
}
|
}
|
||||||
|
|
||||||
removefiles {
|
removefiles {
|
||||||
|
path.join(libtomcrypt.source, "src/**/*_test.c"),
|
||||||
path.join(libtomcrypt.source, "src/**/*tab.c"),
|
path.join(libtomcrypt.source, "src/**/*tab.c"),
|
||||||
path.join(libtomcrypt.source, "src/encauth/ocb3/**.c"),
|
path.join(libtomcrypt.source, "src/encauth/ocb3/**.c"),
|
||||||
}
|
}
|
||||||
|
18
deps/premake/nlohmannjson.lua
vendored
18
deps/premake/nlohmannjson.lua
vendored
@ -1,18 +0,0 @@
|
|||||||
nlohmannjson = {
|
|
||||||
source = path.join(dependencies.basePath, "nlohmannjson"),
|
|
||||||
}
|
|
||||||
|
|
||||||
function nlohmannjson.import()
|
|
||||||
nlohmannjson.includes()
|
|
||||||
end
|
|
||||||
|
|
||||||
function nlohmannjson.includes()
|
|
||||||
includedirs {
|
|
||||||
path.join(nlohmannjson.source, "single_include/nlohmann")
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function nlohmannjson.project()
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(dependencies, nlohmannjson)
|
|
2
deps/protobuf
vendored
2
deps/protobuf
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 57786d126249b5ed4f42b579047941805e742949
|
Subproject commit 8d5fdedd42ef361dcfc1531fba4f33470273f375
|
2
deps/zlib
vendored
2
deps/zlib
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 04f42ceca40f73e2978b50e93806c2a18c1281fc
|
Subproject commit 02a6049eb3884c430268bb0fe3296d597a03174c
|
@ -76,11 +76,6 @@ newoption {
|
|||||||
description = "Always compile unit tests."
|
description = "Always compile unit tests."
|
||||||
}
|
}
|
||||||
|
|
||||||
newoption {
|
|
||||||
trigger = "force-exception-handler",
|
|
||||||
description = "Install custom unhandled exception handler even for Debug builds."
|
|
||||||
}
|
|
||||||
|
|
||||||
newoption {
|
newoption {
|
||||||
trigger = "disable-binary-check",
|
trigger = "disable-binary-check",
|
||||||
description = "Do not perform integrity checks on the exe."
|
description = "Do not perform integrity checks on the exe."
|
||||||
@ -257,9 +252,6 @@ workspace "iw4x"
|
|||||||
if _OPTIONS["force-unit-tests"] then
|
if _OPTIONS["force-unit-tests"] then
|
||||||
defines {"FORCE_UNIT_TESTS"}
|
defines {"FORCE_UNIT_TESTS"}
|
||||||
end
|
end
|
||||||
if _OPTIONS["force-exception-handler"] then
|
|
||||||
defines {"FORCE_EXCEPTION_HANDLER"}
|
|
||||||
end
|
|
||||||
if _OPTIONS["disable-binary-check"] then
|
if _OPTIONS["disable-binary-check"] then
|
||||||
defines {"DISABLE_BINARY_CHECK"}
|
defines {"DISABLE_BINARY_CHECK"}
|
||||||
end
|
end
|
||||||
|
@ -1,5 +1,55 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
|
#include "Modules/Bans.hpp"
|
||||||
|
#include "Modules/Bots.hpp"
|
||||||
|
#include "Modules/Branding.hpp"
|
||||||
|
#include "Modules/Bullet.hpp"
|
||||||
|
#include "Modules/CardTitles.hpp"
|
||||||
|
#include "Modules/Ceg.hpp"
|
||||||
|
#include "Modules/Changelog.hpp"
|
||||||
|
#include "Modules/Chat.hpp"
|
||||||
|
#include "Modules/ClanTags.hpp"
|
||||||
|
#include "Modules/ClientCommand.hpp"
|
||||||
|
#include "Modules/ConnectProtocol.hpp"
|
||||||
|
#include "Modules/Console.hpp"
|
||||||
|
#include "Modules/D3D9Ex.hpp"
|
||||||
|
#include "Modules/Debug.hpp"
|
||||||
|
#include "Modules/Discovery.hpp"
|
||||||
|
#include "Modules/Download.hpp"
|
||||||
|
#include "Modules/Elevators.hpp"
|
||||||
|
#include "Modules/Gamepad.hpp"
|
||||||
|
#include "Modules/Lean.hpp"
|
||||||
|
#include "Modules/MapDump.hpp"
|
||||||
|
#include "Modules/MapRotation.hpp"
|
||||||
|
#include "Modules/NetworkDebug.hpp"
|
||||||
|
#include "Modules/News.hpp"
|
||||||
|
#include "Modules/PlayerMovement.hpp"
|
||||||
|
#include "Modules/PlayerName.hpp"
|
||||||
|
#include "Modules/Playlist.hpp"
|
||||||
|
#include "Modules/QuickPatch.hpp"
|
||||||
|
#include "Modules/RawFiles.hpp"
|
||||||
|
#include "Modules/RawMouse.hpp"
|
||||||
|
#include "Modules/RCon.hpp"
|
||||||
|
#include "Modules/Security.hpp"
|
||||||
|
#include "Modules/ServerCommands.hpp"
|
||||||
|
#include "Modules/ServerInfo.hpp"
|
||||||
|
#include "Modules/ServerList.hpp"
|
||||||
|
#include "Modules/Session.hpp"
|
||||||
|
#include "Modules/SlowMotion.hpp"
|
||||||
|
#include "Modules/SoundMutexFix.hpp"
|
||||||
|
#include "Modules/StartupMessages.hpp"
|
||||||
|
#include "Modules/Stats.hpp"
|
||||||
|
#include "Modules/StringTable.hpp"
|
||||||
|
#include "Modules/StructuredData.hpp"
|
||||||
|
#include "Modules/Theatre.hpp"
|
||||||
|
#include "Modules/Threading.hpp"
|
||||||
|
#include "Modules/UIFeeder.hpp"
|
||||||
|
#include "Modules/UserInfo.hpp"
|
||||||
|
#include "Modules/VisionFile.hpp"
|
||||||
|
#include "Modules/Voice.hpp"
|
||||||
|
#include "Modules/Vote.hpp"
|
||||||
|
#include "Modules/Weapon.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
bool Loader::Pregame = true;
|
bool Loader::Pregame = true;
|
||||||
@ -9,126 +59,127 @@ namespace Components
|
|||||||
|
|
||||||
bool Loader::IsPregame()
|
bool Loader::IsPregame()
|
||||||
{
|
{
|
||||||
return Loader::Pregame;
|
return Pregame;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Loader::IsPostgame()
|
bool Loader::IsPostgame()
|
||||||
{
|
{
|
||||||
return Loader::Postgame;
|
return Postgame;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Loader::IsUninitializing()
|
bool Loader::IsUninitializing()
|
||||||
{
|
{
|
||||||
return Loader::Uninitializing;
|
return Uninitializing;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loader::Initialize()
|
void Loader::Initialize()
|
||||||
{
|
{
|
||||||
Loader::Pregame = true;
|
Pregame = true;
|
||||||
Loader::Postgame = false;
|
Postgame = false;
|
||||||
Loader::Uninitializing = false;
|
Uninitializing = false;
|
||||||
Utils::Memory::GetAllocator()->clear();
|
Utils::Memory::GetAllocator()->clear();
|
||||||
|
|
||||||
Loader::Register(new Flags());
|
Register(new Auth());
|
||||||
Loader::Register(new Singleton());
|
Register(new Command());
|
||||||
// Install our exception handler as early as posssible to get better debug dumps from startup crashes
|
Register(new Dvar());
|
||||||
Loader::Register(new Exception());
|
Register(new Exception()); // Install our exception handler as early as posssible to get better debug dumps from startup crashes
|
||||||
Loader::Register(new Auth());
|
Register(new Flags());
|
||||||
Loader::Register(new Bans());
|
Register(new Network());
|
||||||
Loader::Register(new Bots());
|
Register(new Logger());
|
||||||
Loader::Register(new Dvar());
|
Register(new Singleton());
|
||||||
Loader::Register(new Lean());
|
Register(new UIScript());
|
||||||
Loader::Register(new Maps());
|
Register(new ZoneBuilder());
|
||||||
Loader::Register(new News());
|
|
||||||
Loader::Register(new Node());
|
|
||||||
Loader::Register(new RCon());
|
|
||||||
Loader::Register(new Stats());
|
|
||||||
Loader::Register(new Menus());
|
|
||||||
Loader::Register(new Toast());
|
|
||||||
Loader::Register(new Party());
|
|
||||||
Loader::Register(new Zones());
|
|
||||||
Loader::Register(new D3D9Ex());
|
|
||||||
Loader::Register(new Logger());
|
|
||||||
Loader::Register(new Weapon());
|
|
||||||
Loader::Register(new Window());
|
|
||||||
Loader::Register(new Command());
|
|
||||||
Loader::Register(new Console());
|
|
||||||
Loader::Register(new Friends());
|
|
||||||
Loader::Register(new IPCPipe());
|
|
||||||
Loader::Register(new MapDump());
|
|
||||||
Loader::Register(new ModList());
|
|
||||||
Loader::Register(new Network());
|
|
||||||
Loader::Register(new Session());
|
|
||||||
Loader::Register(new Theatre());
|
|
||||||
Loader::Register(new ClanTags());
|
|
||||||
Loader::Register(new Download());
|
|
||||||
Loader::Register(new Playlist());
|
|
||||||
Loader::Register(new RawFiles());
|
|
||||||
Loader::Register(new Renderer());
|
|
||||||
Loader::Register(new UIFeeder());
|
|
||||||
Loader::Register(new UIScript());
|
|
||||||
Loader::Register(new Changelog());
|
|
||||||
Loader::Register(new Dedicated());
|
|
||||||
Loader::Register(new Discovery());
|
|
||||||
Loader::Register(new FastFiles());
|
|
||||||
Loader::Register(new Gametypes());
|
|
||||||
Loader::Register(new Materials());
|
|
||||||
Loader::Register(new Scheduler());
|
|
||||||
Loader::Register(new CardTitles());
|
|
||||||
Loader::Register(new FileSystem());
|
|
||||||
Loader::Register(new ModelSurfs());
|
|
||||||
Loader::Register(new PlayerName());
|
|
||||||
Loader::Register(new QuickPatch());
|
|
||||||
Loader::Register(new Security());
|
|
||||||
Loader::Register(new ServerInfo());
|
|
||||||
Loader::Register(new ServerList());
|
|
||||||
Loader::Register(new SlowMotion());
|
|
||||||
Loader::Register(new ArenaLength());
|
|
||||||
Loader::Register(new StringTable());
|
|
||||||
Loader::Register(new ZoneBuilder());
|
|
||||||
Loader::Register(new AssetHandler());
|
|
||||||
Loader::Register(new Localization());
|
|
||||||
Loader::Register(new ServerCommands());
|
|
||||||
Loader::Register(new StructuredData());
|
|
||||||
Loader::Register(new ConnectProtocol());
|
|
||||||
Loader::Register(new StartupMessages());
|
|
||||||
Loader::Register(new SoundMutexFix());
|
|
||||||
Loader::Register(new Gamepad());
|
|
||||||
Loader::Register(new Chat());
|
|
||||||
Loader::Register(new TextRenderer());
|
|
||||||
Loader::Register(new Movement());
|
|
||||||
Loader::Register(new Elevators());
|
|
||||||
Loader::Register(new ClientCommand());
|
|
||||||
Loader::Register(new VisionFile());
|
|
||||||
Loader::Register(new Branding());
|
|
||||||
Loader::Register(new Debug());
|
|
||||||
Loader::Register(new RawMouse());
|
|
||||||
Loader::Register(new Bullet());
|
|
||||||
Loader::Register(new MapRotation());
|
|
||||||
Loader::Register(new Ceg());
|
|
||||||
Loader::Register(new UserInfo());
|
|
||||||
Loader::Register(new Events());
|
|
||||||
Loader::Register(new Voice());
|
|
||||||
Loader::Register(new Vote());
|
|
||||||
|
|
||||||
Loader::Register(new GSC());
|
Register(new ArenaLength());
|
||||||
|
Register(new AssetHandler());
|
||||||
|
Register(new Bans());
|
||||||
|
Register(new Bots());
|
||||||
|
Register(new Branding());
|
||||||
|
Register(new Bullet());
|
||||||
|
Register(new CardTitles());
|
||||||
|
Register(new Ceg());
|
||||||
|
Register(new Changelog());
|
||||||
|
Register(new Chat());
|
||||||
|
Register(new ClanTags());
|
||||||
|
Register(new ClientCommand());
|
||||||
|
Register(new ConnectProtocol());
|
||||||
|
Register(new Console());
|
||||||
|
Register(new D3D9Ex());
|
||||||
|
Register(new Debug());
|
||||||
|
Register(new Dedicated());
|
||||||
|
Register(new Discovery());
|
||||||
|
Register(new Download());
|
||||||
|
Register(new Elevators());
|
||||||
|
Register(new Events());
|
||||||
|
Register(new FastFiles());
|
||||||
|
Register(new FileSystem());
|
||||||
|
Register(new Friends());
|
||||||
|
Register(new Gamepad());
|
||||||
|
Register(new IPCPipe());
|
||||||
|
Register(new Lean());
|
||||||
|
Register(new Localization());
|
||||||
|
Register(new MapDump());
|
||||||
|
Register(new MapRotation());
|
||||||
|
Register(new Maps());
|
||||||
|
Register(new Materials());
|
||||||
|
Register(new Menus());
|
||||||
|
Register(new ModList());
|
||||||
|
Register(new ModelSurfs());
|
||||||
|
Register(new NetworkDebug());
|
||||||
|
Register(new News());
|
||||||
|
Register(new Node());
|
||||||
|
Register(new Party());
|
||||||
|
Register(new PlayerMovement());
|
||||||
|
Register(new PlayerName());
|
||||||
|
Register(new Playlist());
|
||||||
|
Register(new QuickPatch());
|
||||||
|
Register(new RawFiles());
|
||||||
|
Register(new RawMouse());
|
||||||
|
Register(new RCon());
|
||||||
|
Register(new Renderer());
|
||||||
|
Register(new Scheduler());
|
||||||
|
Register(new Security());
|
||||||
|
Register(new ServerCommands());
|
||||||
|
Register(new ServerInfo());
|
||||||
|
Register(new ServerList());
|
||||||
|
Register(new Session());
|
||||||
|
Register(new SlowMotion());
|
||||||
|
Register(new SoundMutexFix());
|
||||||
|
Register(new StartupMessages());
|
||||||
|
Register(new Stats());
|
||||||
|
Register(new StringTable());
|
||||||
|
Register(new StructuredData());
|
||||||
|
Register(new TextRenderer());
|
||||||
|
Register(new Theatre());
|
||||||
|
Register(new Threading());
|
||||||
|
Register(new Toast());
|
||||||
|
Register(new UIFeeder());
|
||||||
|
Register(new UserInfo());
|
||||||
|
Register(new VisionFile());
|
||||||
|
Register(new Voice());
|
||||||
|
Register(new Vote());
|
||||||
|
Register(new Weapon());
|
||||||
|
Register(new Window());
|
||||||
|
Register(new Zones());
|
||||||
|
|
||||||
Loader::Pregame = false;
|
Register(new GSC());
|
||||||
|
|
||||||
|
Pregame = false;
|
||||||
|
|
||||||
// Make sure preDestroy is called when the game shuts down
|
// Make sure preDestroy is called when the game shuts down
|
||||||
Scheduler::OnGameShutdown(Loader::PreDestroy);
|
Scheduler::OnGameShutdown(PreDestroy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loader::Uninitialize()
|
void Loader::Uninitialize()
|
||||||
{
|
{
|
||||||
Loader::Uninitializing = true;
|
Uninitializing = true;
|
||||||
Loader::PreDestroyNoPostGame();
|
PreDestroyNoPostGame();
|
||||||
|
|
||||||
std::reverse(Loader::Components.begin(), Loader::Components.end());
|
std::reverse(Components.begin(), Components.end());
|
||||||
for (auto component : Loader::Components)
|
for (auto& component : Components)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if (!Loader::IsPerformingUnitTests())
|
if (!IsPerformingUnitTests())
|
||||||
{
|
{
|
||||||
Logger::Print("Unregister component: {}\n", component->getName());
|
Logger::Print("Unregister component: {}\n", component->getName());
|
||||||
}
|
}
|
||||||
@ -136,21 +187,21 @@ namespace Components
|
|||||||
delete component;
|
delete component;
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader::Components.clear();
|
Components.clear();
|
||||||
Utils::Memory::GetAllocator()->clear();
|
Utils::Memory::GetAllocator()->clear();
|
||||||
Loader::Uninitializing = false;
|
Uninitializing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loader::PreDestroy()
|
void Loader::PreDestroy()
|
||||||
{
|
{
|
||||||
if (!Loader::Postgame)
|
if (!Postgame)
|
||||||
{
|
{
|
||||||
Loader::Postgame = true;
|
Postgame = true;
|
||||||
|
|
||||||
auto components = Loader::Components;
|
auto components = Components;
|
||||||
|
|
||||||
std::reverse(components.begin(), components.end());
|
std::reverse(components.begin(), components.end());
|
||||||
for (auto component : components)
|
for (auto& component : components)
|
||||||
{
|
{
|
||||||
component->preDestroy();
|
component->preDestroy();
|
||||||
}
|
}
|
||||||
@ -159,17 +210,17 @@ namespace Components
|
|||||||
|
|
||||||
void Loader::PreDestroyNoPostGame()
|
void Loader::PreDestroyNoPostGame()
|
||||||
{
|
{
|
||||||
if (!Loader::Postgame)
|
if (!Postgame)
|
||||||
{
|
{
|
||||||
auto components = Loader::Components;
|
auto components = Components;
|
||||||
|
|
||||||
std::reverse(components.begin(), components.end());
|
std::reverse(components.begin(), components.end());
|
||||||
for (auto component : components)
|
for (auto& component : components)
|
||||||
{
|
{
|
||||||
component->preDestroy();
|
component->preDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader::Postgame = true;
|
Postgame = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +230,7 @@ namespace Components
|
|||||||
|
|
||||||
Logger::Print("Performing unit tests for components:\n");
|
Logger::Print("Performing unit tests for components:\n");
|
||||||
|
|
||||||
for (const auto component : Loader::Components)
|
for (const auto& component : Components)
|
||||||
{
|
{
|
||||||
#if defined(FORCE_UNIT_TESTS)
|
#if defined(FORCE_UNIT_TESTS)
|
||||||
Logger::Debug("Testing '{}'...\n", component->getName());
|
Logger::Debug("Testing '{}'...\n", component->getName());
|
||||||
@ -208,12 +259,12 @@ namespace Components
|
|||||||
if (component)
|
if (component)
|
||||||
{
|
{
|
||||||
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
|
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
|
||||||
if (!Loader::IsPerformingUnitTests())
|
if (!IsPerformingUnitTests())
|
||||||
{
|
{
|
||||||
Logger::Print("Component registered: {}\n", component->getName());
|
Logger::Print("Component registered: {}\n", component->getName());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
Loader::Components.push_back(component);
|
Components.push_back(component);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ namespace Components
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
static T* GetInstance()
|
static T* GetInstance()
|
||||||
{
|
{
|
||||||
for (auto& component : Loader::Components)
|
for (auto& component : Components)
|
||||||
{
|
{
|
||||||
if (typeid(*component) == typeid(T))
|
if (typeid(*component) == typeid(T))
|
||||||
{
|
{
|
||||||
@ -61,85 +61,39 @@ namespace Components
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "Modules/Scheduler.hpp"
|
// Priority
|
||||||
#include "Modules/Auth.hpp"
|
#include "Modules/Auth.hpp"
|
||||||
#include "Modules/Bans.hpp"
|
|
||||||
#include "Modules/Bots.hpp"
|
|
||||||
#include "Modules/Dvar.hpp"
|
|
||||||
#include "Modules/Lean.hpp"
|
|
||||||
#include "Modules/Maps.hpp"
|
|
||||||
#include "Modules/News.hpp"
|
|
||||||
#include "Modules/Flags.hpp"
|
|
||||||
#include "Modules/Menus.hpp"
|
|
||||||
#include "Modules/Toast.hpp"
|
|
||||||
#include "Modules/Zones.hpp"
|
|
||||||
#include "Modules/D3D9Ex.hpp"
|
|
||||||
#include "Modules/Weapon.hpp"
|
|
||||||
#include "Modules/Window.hpp"
|
|
||||||
#include "Modules/Command.hpp"
|
#include "Modules/Command.hpp"
|
||||||
#include "Modules/Console.hpp"
|
#include "Modules/Dvar.hpp"
|
||||||
#include "Modules/UIScript.hpp"
|
#include "Modules/Exception.hpp"
|
||||||
#include "Modules/ModList.hpp"
|
#include "Modules/Flags.hpp"
|
||||||
#include "Modules/Network.hpp"
|
#include "Modules/Network.hpp"
|
||||||
#include "Modules/Theatre.hpp"
|
|
||||||
#include "Modules/QuickPatch.hpp"
|
|
||||||
#include "Modules/Security.hpp"
|
|
||||||
#include "Modules/Node.hpp"
|
|
||||||
#include "Modules/RCon.hpp"
|
|
||||||
#include "Modules/Party.hpp" // Destroys the order, but requires network classes :D
|
|
||||||
#include "Modules/Logger.hpp"
|
#include "Modules/Logger.hpp"
|
||||||
|
#include "Modules/Singleton.hpp"
|
||||||
|
#include "Modules/UIScript.hpp"
|
||||||
|
#include "Modules/ZoneBuilder.hpp"
|
||||||
|
|
||||||
|
#include "Modules/ArenaLength.hpp"
|
||||||
|
#include "Modules/AssetHandler.hpp"
|
||||||
|
#include "Modules/Dedicated.hpp"
|
||||||
|
#include "Modules/Events.hpp"
|
||||||
|
#include "Modules/FastFiles.hpp"
|
||||||
|
#include "Modules/FileSystem.hpp"
|
||||||
#include "Modules/Friends.hpp"
|
#include "Modules/Friends.hpp"
|
||||||
#include "Modules/IPCPipe.hpp"
|
#include "Modules/IPCPipe.hpp"
|
||||||
#include "Modules/MapDump.hpp"
|
|
||||||
#include "Modules/Session.hpp"
|
|
||||||
#include "Modules/ClanTags.hpp"
|
|
||||||
#include "Modules/Download.hpp"
|
|
||||||
#include "Modules/Playlist.hpp"
|
|
||||||
#include "Modules/RawFiles.hpp"
|
|
||||||
#include "Modules/Renderer.hpp"
|
|
||||||
#include "Modules/UIFeeder.hpp"
|
|
||||||
#include "Modules/Changelog.hpp"
|
|
||||||
#include "Modules/Dedicated.hpp"
|
|
||||||
#include "Modules/Discovery.hpp"
|
|
||||||
#include "Modules/Exception.hpp"
|
|
||||||
#include "Modules/FastFiles.hpp"
|
|
||||||
#include "Modules/Gametypes.hpp"
|
|
||||||
#include "Modules/Materials.hpp"
|
|
||||||
#include "Modules/Singleton.hpp"
|
|
||||||
#include "Modules/CardTitles.hpp"
|
|
||||||
#include "Modules/FileSystem.hpp"
|
|
||||||
#include "Modules/ModelSurfs.hpp"
|
|
||||||
#include "Modules/PlayerName.hpp"
|
|
||||||
#include "Modules/ServerInfo.hpp"
|
|
||||||
#include "Modules/ServerList.hpp"
|
|
||||||
#include "Modules/SlowMotion.hpp"
|
|
||||||
#include "Modules/ArenaLength.hpp"
|
|
||||||
#include "Modules/StringTable.hpp"
|
|
||||||
#include "Modules/ZoneBuilder.hpp"
|
|
||||||
#include "Modules/AssetHandler.hpp"
|
|
||||||
#include "Modules/Localization.hpp"
|
#include "Modules/Localization.hpp"
|
||||||
#include "Modules/ServerCommands.hpp"
|
#include "Modules/Maps.hpp"
|
||||||
#include "Modules/StructuredData.hpp"
|
#include "Modules/Materials.hpp"
|
||||||
#include "Modules/ConnectProtocol.hpp"
|
#include "Modules/Menus.hpp"
|
||||||
#include "Modules/StartupMessages.hpp"
|
#include "Modules/ModList.hpp"
|
||||||
#include "Modules/Stats.hpp"
|
#include "Modules/ModelSurfs.hpp"
|
||||||
#include "Modules/SoundMutexFix.hpp"
|
#include "Modules/Node.hpp"
|
||||||
#include "Modules/Chat.hpp"
|
#include "Modules/Party.hpp"
|
||||||
|
#include "Modules/Renderer.hpp"
|
||||||
|
#include "Modules/Scheduler.hpp"
|
||||||
#include "Modules/TextRenderer.hpp"
|
#include "Modules/TextRenderer.hpp"
|
||||||
#include "Modules/Movement.hpp"
|
#include "Modules/Toast.hpp"
|
||||||
#include "Modules/Elevators.hpp"
|
#include "Modules/Window.hpp"
|
||||||
#include "Modules/ClientCommand.hpp"
|
#include "Modules/Zones.hpp"
|
||||||
#include "Modules/VisionFile.hpp"
|
|
||||||
#include "Modules/Gamepad.hpp"
|
|
||||||
#include "Modules/Branding.hpp"
|
|
||||||
#include "Modules/Debug.hpp"
|
|
||||||
#include "Modules/RawMouse.hpp"
|
|
||||||
#include "Modules/Bullet.hpp"
|
|
||||||
#include "Modules/MapRotation.hpp"
|
|
||||||
#include "Modules/Ceg.hpp"
|
|
||||||
#include "Modules/UserInfo.hpp"
|
|
||||||
#include "Modules/Events.hpp"
|
|
||||||
#include "Modules/Voice.hpp"
|
|
||||||
#include "Modules/Vote.hpp"
|
|
||||||
|
|
||||||
#include "Modules/GSC/GSC.hpp"
|
#include "Modules/GSC/GSC.hpp"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "Weapon.hpp"
|
||||||
|
|
||||||
#include "AssetInterfaces/IFont_s.hpp"
|
#include "AssetInterfaces/IFont_s.hpp"
|
||||||
#include "AssetInterfaces/IWeapon.hpp"
|
#include "AssetInterfaces/IWeapon.hpp"
|
||||||
@ -257,7 +258,7 @@ namespace Components
|
|||||||
|
|
||||||
for (int k = 0; k < (pass->perPrimArgCount + pass->perObjArgCount + pass->stableArgCount); ++k)
|
for (int k = 0; k < (pass->perPrimArgCount + pass->perObjArgCount + pass->stableArgCount); ++k)
|
||||||
{
|
{
|
||||||
if (pass->args[k].type == D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_CONSTINT)
|
if (pass->args[k].type == Game::MaterialShaderArgumentType::MTL_ARG_LITERAL_PIXEL_CONST)
|
||||||
{
|
{
|
||||||
if (pass->args[k].u.codeConst.index == -28132)
|
if (pass->args[k].u.codeConst.index == -28132)
|
||||||
{
|
{
|
||||||
|
@ -11,7 +11,7 @@ namespace Assets
|
|||||||
Utils::String::Replace(name, "maps/mp/", "");
|
Utils::String::Replace(name, "maps/mp/", "");
|
||||||
Utils::String::Replace(name, ".d3dbsp", "");
|
Utils::String::Replace(name, ".d3dbsp", "");
|
||||||
|
|
||||||
Components::FileSystem::File mapFile(Utils::String::VA("comworld/%s.iw4xComWorld", name.data()));
|
Components::FileSystem::File mapFile(std::format("comworld/{}.iw4xComWorld", name));
|
||||||
|
|
||||||
if (mapFile.exists())
|
if (mapFile.exists())
|
||||||
{
|
{
|
||||||
|
@ -95,22 +95,22 @@ namespace Assets
|
|||||||
|
|
||||||
void IFont_s::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
void IFont_s::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
Components::FileSystem::File fontDefFile(Utils::String::VA("%s.json", name.data()));
|
Components::FileSystem::File fontDefFile(std::format("{}.json", name));
|
||||||
Components::FileSystem::File fontFile(Utils::String::VA("%s.ttf", name.data()));
|
Components::FileSystem::File fontFile(std::format("{}.ttf", name));
|
||||||
|
|
||||||
if (!fontDefFile.exists() || !fontFile.exists())
|
if (!fontDefFile.exists() || !fontFile.exists())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
nlohmann::json fontDef = nlohmann::json::parse(fontDefFile.getBuffer());
|
nlohmann::json fontDef;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
fontDef = nlohmann::json::parse(fontDefFile.getBuffer());
|
fontDef = nlohmann::json::parse(fontDefFile.getBuffer());
|
||||||
}
|
}
|
||||||
catch (const nlohmann::json::parse_error& ex)
|
catch (const nlohmann::json::parse_error& ex)
|
||||||
{
|
{
|
||||||
Components::Logger::Error(Game::ERR_FATAL, "Json Parse Error: {}. Font {} is invalid", ex.what(), name);
|
Components::Logger::Error(Game::ERR_FATAL, "Json Parse Error: {}. Font {} is invalid\n", ex.what(), name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,7 +254,7 @@ namespace Assets
|
|||||||
rgbaPixels[i + 3] = static_cast<char>(pixels[i / 4]);
|
rgbaPixels[i + 3] = static_cast<char>(pixels[i / 4]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::IO::WriteFile(Utils::String::VA("userraw\\images\\%s.iwi", texName), outIwi);
|
Utils::IO::WriteFile(std::format("userraw\\images\\{}.iwi", texName), outIwi);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IFont_s::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
void IFont_s::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||||
|
@ -64,7 +64,7 @@ namespace Assets
|
|||||||
|
|
||||||
void IFxEffectDef::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
void IFxEffectDef::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
Components::FileSystem::File fxFile(Utils::String::VA("fx/%s.iw4xFx", name.data()));
|
Components::FileSystem::File fxFile(std::format("fx/{}.iw4xFx", name));
|
||||||
|
|
||||||
if (fxFile.exists())
|
if (fxFile.exists())
|
||||||
{
|
{
|
||||||
@ -391,7 +391,7 @@ namespace Assets
|
|||||||
|
|
||||||
switch (elemType)
|
switch (elemType)
|
||||||
{
|
{
|
||||||
case 7:
|
case Game::FX_ELEM_TYPE_MODEL:
|
||||||
{
|
{
|
||||||
if (visuals->model)
|
if (visuals->model)
|
||||||
{
|
{
|
||||||
@ -401,11 +401,11 @@ namespace Assets
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 8:
|
case Game::FX_ELEM_TYPE_OMNI_LIGHT:
|
||||||
case 9:
|
case Game::FX_ELEM_TYPE_SPOT_LIGHT:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xA:
|
case Game::FX_ELEM_TYPE_SOUND:
|
||||||
{
|
{
|
||||||
if (visuals->soundName)
|
if (visuals->soundName)
|
||||||
{
|
{
|
||||||
@ -416,7 +416,7 @@ namespace Assets
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 0xC:
|
case Game::FX_ELEM_TYPE_RUNNER:
|
||||||
{
|
{
|
||||||
if (visuals->effectDef.handle)
|
if (visuals->effectDef.handle)
|
||||||
{
|
{
|
||||||
@ -491,7 +491,7 @@ namespace Assets
|
|||||||
|
|
||||||
// Save_FxElemDefVisuals
|
// Save_FxElemDefVisuals
|
||||||
{
|
{
|
||||||
if (elemDef->elemType == 11)
|
if (elemDef->elemType == Game::FX_ELEM_TYPE_DECAL)
|
||||||
{
|
{
|
||||||
if (elemDef->visuals.markArray)
|
if (elemDef->visuals.markArray)
|
||||||
{
|
{
|
||||||
@ -501,7 +501,7 @@ namespace Assets
|
|||||||
Game::FxElemMarkVisuals* destMarkArray = buffer->dest<Game::FxElemMarkVisuals>();
|
Game::FxElemMarkVisuals* destMarkArray = buffer->dest<Game::FxElemMarkVisuals>();
|
||||||
buffer->saveArray(elemDef->visuals.markArray, elemDef->visualCount);
|
buffer->saveArray(elemDef->visuals.markArray, elemDef->visualCount);
|
||||||
|
|
||||||
for (char j = 0; j < elemDef->visualCount; ++j)
|
for (auto j = 0; j < elemDef->visualCount; ++j)
|
||||||
{
|
{
|
||||||
if (elemDef->visuals.markArray[j].materials[0])
|
if (elemDef->visuals.markArray[j].materials[0])
|
||||||
{
|
{
|
||||||
@ -563,7 +563,7 @@ namespace Assets
|
|||||||
{
|
{
|
||||||
AssertSize(Game::FxElemExtendedDefPtr, 4);
|
AssertSize(Game::FxElemExtendedDefPtr, 4);
|
||||||
|
|
||||||
if (elemDef->elemType == 3)
|
if (elemDef->elemType == Game::FX_ELEM_TYPE_TRAIL)
|
||||||
{
|
{
|
||||||
// Save_FxTrailDef
|
// Save_FxTrailDef
|
||||||
{
|
{
|
||||||
@ -597,7 +597,7 @@ namespace Assets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (elemDef->elemType == 6)
|
else if (elemDef->elemType == Game::FX_ELEM_TYPE_SPARK_FOUNTAIN)
|
||||||
{
|
{
|
||||||
if (elemDef->extended.sparkFountainDef)
|
if (elemDef->extended.sparkFountainDef)
|
||||||
{
|
{
|
||||||
|
@ -186,11 +186,20 @@ namespace Assets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IFxWorld::load(Game::XAssetHeader* /*header*/, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
void IFxWorld::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
Game::FxWorld* map = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FXWORLD, name.data()).fxWorld;
|
Game::FxWorld* map = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FXWORLD, name.data()).fxWorld;
|
||||||
if (map) return;
|
if (map) return;
|
||||||
|
|
||||||
Components::Logger::Error(Game::ERR_FATAL, "Missing fx_map {}... you can't make them yet you idiot.", name);
|
// Generate
|
||||||
|
map = builder->getAllocator()->allocate<Game::FxWorld>();
|
||||||
|
map->name = builder->getAllocator()->duplicateString(name);
|
||||||
|
|
||||||
|
// No glass for you!
|
||||||
|
ZeroMemory(&map->glassSys, sizeof(map->glassSys));
|
||||||
|
|
||||||
|
map->glassSys.firstFreePiece = 0xFFFF; // That's how rust has it (no glass)
|
||||||
|
|
||||||
|
header->fxWorld = map;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
#include "IGameWorldMp.hpp"
|
#include "IGameWorldMp.hpp"
|
||||||
|
|
||||||
|
#define IW4X_GAMEWORLD_VERSION 1
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
void IGameWorldMp::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
void IGameWorldMp::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||||
@ -75,4 +77,118 @@ namespace Assets
|
|||||||
|
|
||||||
buffer->popBlock();
|
buffer->popBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IGameWorldMp::load(Game::XAssetHeader* header, const std::string& _name, Components::ZoneBuilder::Zone* builder)
|
||||||
|
{
|
||||||
|
std::string name = _name;
|
||||||
|
Utils::String::Replace(name, "maps/mp/", "");
|
||||||
|
Utils::String::Replace(name, ".d3dbsp", "");
|
||||||
|
|
||||||
|
Components::FileSystem::File gameWorld(std::format("gameworld/{}.iw4x.json", name));
|
||||||
|
|
||||||
|
if (gameWorld.exists())
|
||||||
|
{
|
||||||
|
nlohmann::json gameWorldJson;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
gameWorldJson = nlohmann::json::parse(gameWorld.getBuffer());
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid JSON for gameworld {}! {}", name, e.what());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* asset = builder->getAllocator()->allocate<Game::GameWorldMp>();
|
||||||
|
|
||||||
|
if (!gameWorldJson.is_object())
|
||||||
|
{
|
||||||
|
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid GameWorldMp json for {}\n", name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto version = gameWorldJson["version"].is_number() ? gameWorldJson["version"].get<int>() : 0;
|
||||||
|
if (version != IW4X_GAMEWORLD_VERSION)
|
||||||
|
{
|
||||||
|
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid GameWorld json version for {}, expected {} and got {}\n", name, IW4X_GAMEWORLD_VERSION, version);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gameWorldJson["name"].is_string())
|
||||||
|
{
|
||||||
|
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Missing gameworld name! on {}\n", name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
asset->name = builder->getAllocator()->duplicateString(gameWorldJson["name"].get<std::string>());
|
||||||
|
auto glassData = builder->getAllocator()->allocate<Game::G_GlassData>();
|
||||||
|
|
||||||
|
if (gameWorldJson["glassData"].is_object())
|
||||||
|
{
|
||||||
|
auto jsonGlassData = gameWorldJson["glassData"];
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
glassData->damageToDestroy = jsonGlassData["damageToDestroy"].get<unsigned short>();
|
||||||
|
glassData->damageToWeaken = jsonGlassData["damageToWeaken"].get<unsigned short>();
|
||||||
|
|
||||||
|
if (jsonGlassData["glassNames"].is_array())
|
||||||
|
{
|
||||||
|
nlohmann::json::array_t glassNames = jsonGlassData["glassNames"];
|
||||||
|
glassData->glassNameCount = glassNames.size();
|
||||||
|
glassData->glassNames = builder->getAllocator()->allocateArray<Game::G_GlassName>(glassData->glassNameCount);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < glassData->glassNameCount; i++)
|
||||||
|
{
|
||||||
|
auto jsonGlassName = glassNames[i];
|
||||||
|
glassData->glassNames[i].nameStr = builder->getAllocator()->duplicateString(jsonGlassName["nameStr"]);
|
||||||
|
|
||||||
|
glassData->glassNames[i].name = jsonGlassName["name"].get<unsigned short>();
|
||||||
|
|
||||||
|
if (jsonGlassName["piecesIndices"].is_array())
|
||||||
|
{
|
||||||
|
nlohmann::json::array_t jsonPiecesIndices = jsonGlassName["piecesIndices"];
|
||||||
|
glassData->glassNames[i].pieceCount = static_cast<unsigned short>(jsonPiecesIndices.size());
|
||||||
|
|
||||||
|
for (size_t j = 0; j < glassData->glassNames[i].pieceCount; j++)
|
||||||
|
{
|
||||||
|
glassData->glassNames[i].pieceIndices[j] = jsonPiecesIndices[j].get<unsigned short>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gameWorldJson["glassPieces"].is_array())
|
||||||
|
{
|
||||||
|
nlohmann::json::array_t glassPieces = gameWorldJson["glassPieces"];
|
||||||
|
glassData->pieceCount = glassPieces.size();
|
||||||
|
glassData->glassPieces = builder->getAllocator()->allocateArray<Game::G_GlassPiece>(glassData->pieceCount);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < glassData->pieceCount; i++)
|
||||||
|
{
|
||||||
|
glassData->glassPieces[i].collapseTime = glassPieces[i]["collapseTime"].get<unsigned short>();
|
||||||
|
glassData->glassPieces[i].damageTaken = glassPieces[i]["damageTaken"].get<unsigned short>();
|
||||||
|
glassData->glassPieces[i].lastStateChangeTime = glassPieces[i]["lastStateChangeTime"].get<int>();
|
||||||
|
glassData->glassPieces[i].impactDir = glassPieces[i]["impactDir"].get<char>();
|
||||||
|
|
||||||
|
nlohmann::json::array_t jsonPos = glassPieces[i]["impactPos"];
|
||||||
|
glassData->glassPieces[i].impactPos[0] = jsonPos[0].get<char>();
|
||||||
|
glassData->glassPieces[i].impactPos[1] = jsonPos[1].get<char>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const nlohmann::json::exception& e)
|
||||||
|
{
|
||||||
|
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Malformed GameWorldMp json for {} ({})\n", name, e.what());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
asset->g_glassData = glassData;
|
||||||
|
|
||||||
|
header->gameWorldMp = asset;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,5 +8,6 @@ namespace Assets
|
|||||||
Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP; }
|
Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP; }
|
||||||
|
|
||||||
void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||||
|
void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -151,11 +151,11 @@ namespace Assets
|
|||||||
|
|
||||||
void IGameWorldSp::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
void IGameWorldSp::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
AssertSize(Game::GameWorldMp, 8);
|
AssertSize(Game::GameWorldSp, 0x38);
|
||||||
|
|
||||||
Utils::Stream* buffer = builder->getBuffer();
|
Utils::Stream* buffer = builder->getBuffer();
|
||||||
Game::GameWorldSp* asset = header.gameWorldSp;
|
auto* asset = header.gameWorldSp;
|
||||||
Game::GameWorldSp* dest = buffer->dest<Game::GameWorldSp>();
|
auto* dest = buffer->dest<Game::GameWorldSp>();
|
||||||
buffer->save(asset);
|
buffer->save(asset);
|
||||||
|
|
||||||
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
|
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
|
||||||
@ -187,7 +187,7 @@ namespace Assets
|
|||||||
|
|
||||||
for (char j = 0; j < 5; ++j)
|
for (char j = 0; j < 5; ++j)
|
||||||
{
|
{
|
||||||
builder->mapScriptString(&(&node->constant.targetname)[j]);
|
builder->mapScriptString((&node->constant.targetname)[j]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node->constant.Links)
|
if (node->constant.Links)
|
||||||
|
@ -18,12 +18,12 @@ namespace Assets
|
|||||||
}
|
}
|
||||||
|
|
||||||
image->name = builder->getAllocator()->duplicateString(name);
|
image->name = builder->getAllocator()->duplicateString(name);
|
||||||
image->semantic = 2;
|
image->semantic = Game::TextureSemantic::TS_COLOR_MAP;
|
||||||
|
|
||||||
const char* tempName = image->name;
|
const char* tempName = image->name;
|
||||||
if (tempName[0] == '*') tempName++;
|
if (tempName[0] == '*') tempName++;
|
||||||
|
|
||||||
Components::FileSystem::File imageFile(Utils::String::VA("images/%s.iw4xImage", tempName));
|
Components::FileSystem::File imageFile(std::format("images/{}.iw4xImage", tempName));
|
||||||
if (imageFile.exists())
|
if (imageFile.exists())
|
||||||
{
|
{
|
||||||
Utils::Stream::Reader reader(builder->getAllocator(), imageFile.getBuffer());
|
Utils::Stream::Reader reader(builder->getAllocator(), imageFile.getBuffer());
|
||||||
@ -35,7 +35,7 @@ namespace Assets
|
|||||||
}
|
}
|
||||||
|
|
||||||
image->mapType = reader.read<char>();
|
image->mapType = reader.read<char>();
|
||||||
image->semantic = reader.read<char>();
|
image->semantic = reader.read<Game::TextureSemantic>();
|
||||||
image->category = reader.read<char>();
|
image->category = reader.read<char>();
|
||||||
|
|
||||||
int dataLength = reader.read<int>();
|
int dataLength = reader.read<int>();
|
||||||
|
@ -7,7 +7,7 @@ namespace Assets
|
|||||||
{
|
{
|
||||||
void IGfxLightDef::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
void IGfxLightDef::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
Components::FileSystem::File mapFile(Utils::String::VA("lights/%s.iw4xLight", name.data()));
|
Components::FileSystem::File mapFile(std::format("lights/{}.iw4xLight", name));
|
||||||
|
|
||||||
if (mapFile.exists())
|
if (mapFile.exists())
|
||||||
{
|
{
|
||||||
|
@ -3,6 +3,27 @@
|
|||||||
|
|
||||||
#define IW4X_GFXMAP_VERSION 1
|
#define IW4X_GFXMAP_VERSION 1
|
||||||
|
|
||||||
|
// The xmodel vehicle_small_hatch_green_destructible_mp causes EXTREME lag
|
||||||
|
// when placed in the world, for reasons unknown.
|
||||||
|
//
|
||||||
|
// Something happens with the SModelSurfIterator which makes it load garbage
|
||||||
|
// as an XSurface in the middle of otherwise valid surfaces. This bug is very
|
||||||
|
// easy to reproduce with an empty map and just this car in the middle
|
||||||
|
//
|
||||||
|
// As of know we do not know why the iterator corruption occurs or what causes
|
||||||
|
// it. It doesn't seem linked to the SModel, nor to the materials or techsets,
|
||||||
|
// nor to the sortkeys, nor to the tilemode, boneinfo, and so on. So for now
|
||||||
|
// and to make it work for majority of users, we just swap the car. (no, using
|
||||||
|
// the identical car from iw4's favela_escape doesn't work either!)
|
||||||
|
//
|
||||||
|
// Two other models have this problem: ch_apartment_9story_noentry_02 and
|
||||||
|
// ch_apartment_5story_noentry_01
|
||||||
|
// But these exist in mp_vacant in slightly different versions, and can be
|
||||||
|
// swapped safely by deleting the two .iw4XModel files and requiring mp_vacant
|
||||||
|
// or a minimal zone containing just these two models.
|
||||||
|
//
|
||||||
|
#define SWAP_GREEN_VEHICLE_XMODEL 1
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
void IGfxWorld::loadGfxWorldDpvsStatic(Game::GfxWorld* world, Game::GfxWorldDpvsStatic* asset, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader)
|
void IGfxWorld::loadGfxWorldDpvsStatic(Game::GfxWorld* world, Game::GfxWorldDpvsStatic* asset, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader)
|
||||||
@ -47,7 +68,11 @@ namespace Assets
|
|||||||
|
|
||||||
if (model->model)
|
if (model->model)
|
||||||
{
|
{
|
||||||
model->model = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_XMODEL, reader->readString().data(), builder).model;
|
auto name = reader->readString();
|
||||||
|
|
||||||
|
model->model = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_XMODEL, name.data(), builder).model;
|
||||||
|
|
||||||
|
assert(model->model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -129,7 +154,7 @@ namespace Assets
|
|||||||
Utils::String::Replace(name, "maps/mp/", "");
|
Utils::String::Replace(name, "maps/mp/", "");
|
||||||
Utils::String::Replace(name, ".d3dbsp", "");
|
Utils::String::Replace(name, ".d3dbsp", "");
|
||||||
|
|
||||||
Components::FileSystem::File mapFile(Utils::String::VA("gfxworld/%s.iw4xGfxWorld", name.data()));
|
Components::FileSystem::File mapFile(std::format("gfxworld/{}.iw4xGfxWorld", name));
|
||||||
|
|
||||||
if (mapFile.exists())
|
if (mapFile.exists())
|
||||||
{
|
{
|
||||||
|
@ -5,7 +5,7 @@ namespace Assets
|
|||||||
{
|
{
|
||||||
void ILoadedSound::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
void ILoadedSound::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
Components::FileSystem::File soundFile(Utils::String::VA("loaded_sound/%s", name.data()));
|
Components::FileSystem::File soundFile(std::format("loaded_sound/{}", name));
|
||||||
if (!soundFile.exists())
|
if (!soundFile.exists())
|
||||||
{
|
{
|
||||||
header->loadSnd = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).loadSnd;
|
header->loadSnd = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).loadSnd;
|
||||||
@ -120,18 +120,9 @@ namespace Assets
|
|||||||
|
|
||||||
if (asset->sound.data)
|
if (asset->sound.data)
|
||||||
{
|
{
|
||||||
if (builder->hasPointer(asset->sound.data))
|
|
||||||
{
|
|
||||||
dest->sound.data = builder->getPointer(asset->sound.data);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
builder->storePointer(asset->sound.data);
|
|
||||||
|
|
||||||
buffer->saveArray(asset->sound.data, asset->sound.info.data_len);
|
buffer->saveArray(asset->sound.data, asset->sound.info.data_len);
|
||||||
Utils::Stream::ClearPointer(&dest->sound.data);
|
Utils::Stream::ClearPointer(&dest->sound.data);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
buffer->popBlock();
|
buffer->popBlock();
|
||||||
}
|
}
|
||||||
|
@ -81,4 +81,51 @@ namespace Assets
|
|||||||
builder->addRawAsset(type, entry);
|
builder->addRawAsset(type, entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ILocalizeEntry::ParseLocalizedStringsJson(Components::ZoneBuilder::Zone* builder, Components::FileSystem::File& file)
|
||||||
|
{
|
||||||
|
nlohmann::json localize;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Components::Logger::Debug("Parsing localized string \"{}\"...", file.getName());
|
||||||
|
localize = nlohmann::json::parse(file.getBuffer());
|
||||||
|
}
|
||||||
|
catch (const std::exception& ex)
|
||||||
|
{
|
||||||
|
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}\n", ex.what());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!localize.is_object())
|
||||||
|
{
|
||||||
|
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Localized strings json file '{}' should be an object!", file.getName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Game::LocalizeEntry*> assets;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (const auto& [key, value] : localize.items())
|
||||||
|
{
|
||||||
|
const auto valueStr = value.get<std::string>();
|
||||||
|
|
||||||
|
auto* entry = builder->getAllocator()->allocate<Game::LocalizeEntry>();
|
||||||
|
entry->name = builder->getAllocator()->duplicateString(key);
|
||||||
|
entry->value = builder->getAllocator()->duplicateString(valueStr);
|
||||||
|
|
||||||
|
assets.emplace_back(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& ex)
|
||||||
|
{
|
||||||
|
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}: Localized strings json file '{}' contains invalid data!", ex.what(), file.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto type = Game::DB_GetXAssetNameType("localize");
|
||||||
|
for (const auto& entry : assets)
|
||||||
|
{
|
||||||
|
builder->addRawAsset(type, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,5 +11,6 @@ namespace Assets
|
|||||||
void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||||
|
|
||||||
static void ParseLocalizedStringsFile(Components::ZoneBuilder::Zone* builder, const std::string& name, const std::string& filename);
|
static void ParseLocalizedStringsFile(Components::ZoneBuilder::Zone* builder, const std::string& name, const std::string& filename);
|
||||||
|
static void ParseLocalizedStringsJson(Components::ZoneBuilder::Zone* builder, Components::FileSystem::File& file);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ namespace Assets
|
|||||||
Utils::String::Replace(name, "mp/", "");
|
Utils::String::Replace(name, "mp/", "");
|
||||||
Utils::String::Replace(name, ".d3dbsp", "");
|
Utils::String::Replace(name, ".d3dbsp", "");
|
||||||
|
|
||||||
Components::FileSystem::File ents(Utils::String::VA("mapents/%s.ents", name.data()));
|
Components::FileSystem::File ents(std::format("mapents/{}.ents", name));
|
||||||
if (ents.exists())
|
if (ents.exists())
|
||||||
{
|
{
|
||||||
Game::MapEnts* entites = builder->getAllocator()->allocate<Game::MapEnts>();
|
Game::MapEnts* entites = builder->getAllocator()->allocate<Game::MapEnts>();
|
||||||
@ -48,7 +48,7 @@ namespace Assets
|
|||||||
|
|
||||||
std::string entityString = ents.getBuffer();
|
std::string entityString = ents.getBuffer();
|
||||||
|
|
||||||
entites->name = builder->getAllocator()->duplicateString(Utils::String::VA("maps/mp/%s.d3dbsp", name.data()));
|
entites->name = builder->getAllocator()->duplicateString(std::format("maps/mp/{}.d3dbsp", name));
|
||||||
entites->entityString = builder->getAllocator()->duplicateString(entityString);
|
entites->entityString = builder->getAllocator()->duplicateString(entityString);
|
||||||
entites->numEntityChars = entityString.size() + 1;
|
entites->numEntityChars = entityString.size() + 1;
|
||||||
|
|
||||||
|
@ -1,15 +1,439 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
#include "IMaterial.hpp"
|
#include "IMaterial.hpp"
|
||||||
|
|
||||||
#define IW4X_MAT_VERSION "1"
|
#define IW4X_MAT_BIN_VERSION "1"
|
||||||
|
#define IW4X_MAT_JSON_VERSION 1
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
|
const std::unordered_map<std::string, std::string> techSetCorrespondance =
|
||||||
|
{
|
||||||
|
{"effect", "effect_blend"},
|
||||||
|
{"effect", "effect_blend"},
|
||||||
|
{"effect_nofog", "effect_blend_nofog"},
|
||||||
|
{"effect_zfeather", "effect_zfeather_blend"},
|
||||||
|
{"effect_zfeather_falloff", "effect_zfeather_falloff_add"},
|
||||||
|
{"effect_zfeather_nofog", "effect_zfeather_add_nofog"},
|
||||||
|
|
||||||
|
{"wc_unlit_add", "wc_unlit_add_lin"},
|
||||||
|
{"wc_unlit_distfalloff", "wc_unlit_distfalloff_replace"},
|
||||||
|
{"wc_unlit_multiply", "wc_unlit_multiply_lin"},
|
||||||
|
{"wc_unlit_falloff_add", "wc_unlit_falloff_add_lin"},
|
||||||
|
{"wc_unlit", "wc_unlit_replace_lin"},
|
||||||
|
{"wc_unlit_alphatest", "wc_unlit_blend_lin"},
|
||||||
|
{"wc_unlit_blend", "wc_unlit_blend_lin"},
|
||||||
|
{"wc_unlit_replace", "wc_unlit_replace_lin"},
|
||||||
|
{"wc_unlit_nofog", "wc_unlit_replace_lin_nofog_nocast" },
|
||||||
|
|
||||||
|
{"mc_unlit_replace", "mc_unlit_replace_lin"},
|
||||||
|
{"mc_unlit_nofog", "mc_unlit_blend_nofog_ua"},
|
||||||
|
{"mc_unlit", "mc_unlit_replace_lin_nocast"},
|
||||||
|
{"mc_unlit_alphatest", "mc_unlit_blend_lin"},
|
||||||
|
{"mc_effect_nofog", "mc_effect_blend_nofog"},
|
||||||
|
{"mc_effect_falloff_add_nofog", "mc_effect_falloff_add_nofog_eyeoffset"},
|
||||||
|
};
|
||||||
|
|
||||||
void IMaterial::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
void IMaterial::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
if (!header->data) this->loadJson(header, name, builder); // Check if we want to override materials
|
if (!header->data) this->loadJson(header, name, builder); // Check if we want to load a material from disk
|
||||||
|
if (!header->data) this->loadBinary(header, name, builder); // Check if we want to load a material from disk (binary format)
|
||||||
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
|
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
|
||||||
if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void IMaterial::loadJson(Game::XAssetHeader* header, const std::string& name, [[maybe_unused]] Components::ZoneBuilder::Zone* builder)
|
||||||
|
{
|
||||||
|
Components::FileSystem::File materialInfo(std::format("materials/{}.iw4x.json", name));
|
||||||
|
|
||||||
|
if (!materialInfo.exists()) return;
|
||||||
|
|
||||||
|
Game::Material* asset = builder->getAllocator()->allocate<Game::Material>();
|
||||||
|
|
||||||
|
|
||||||
|
nlohmann::json materialJson;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
materialJson = nlohmann::json::parse(materialInfo.getBuffer());
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
Components::Logger::Print("Invalid material json for {} (broken json {})\n", name, e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!materialJson.is_object())
|
||||||
|
{
|
||||||
|
Components::Logger::Print("Invalid material json for {} (Is it zonebuilder format?)\n", name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (materialJson["version"].get<int>() != IW4X_MAT_JSON_VERSION)
|
||||||
|
{
|
||||||
|
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid material json version for {}, expected {} and got {}\n", name, IW4X_MAT_JSON_VERSION, materialJson["version"].get<std::string>());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
asset->info.name = builder->getAllocator()->duplicateString(materialJson["name"].get<std::string>());
|
||||||
|
asset->info.gameFlags = static_cast<char>(Utils::Json::ReadFlags(materialJson["gameFlags"].get<std::string>(), sizeof(char)));
|
||||||
|
|
||||||
|
asset->info.sortKey = materialJson["sortKey"].get<char>();
|
||||||
|
// * We do techset later * //
|
||||||
|
asset->info.textureAtlasRowCount = materialJson["textureAtlasRowCount"].get<unsigned char>();
|
||||||
|
asset->info.textureAtlasColumnCount = materialJson["textureAtlasColumnCount"].get<unsigned char>();
|
||||||
|
asset->info.surfaceTypeBits = static_cast<unsigned int>(Utils::Json::ReadFlags(materialJson["surfaceTypeBits"].get<std::string>(), sizeof(int)));
|
||||||
|
asset->info.hashIndex = materialJson["hashIndex"].get<unsigned short>();
|
||||||
|
asset->cameraRegion = materialJson["cameraRegion"].get<char>();
|
||||||
|
}
|
||||||
|
catch (const nlohmann::json::exception& e)
|
||||||
|
{
|
||||||
|
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid material json for {} (broken json {})\n", name, e.what());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (materialJson["gfxDrawSurface"].is_object())
|
||||||
|
{
|
||||||
|
asset->info.drawSurf.fields.customIndex = materialJson["gfxDrawSurface"]["customIndex"].get<long long>();
|
||||||
|
asset->info.drawSurf.fields.hasGfxEntIndex = materialJson["gfxDrawSurface"]["hasGfxEntIndex"].get<long long>();
|
||||||
|
asset->info.drawSurf.fields.materialSortedIndex = materialJson["gfxDrawSurface"]["materialSortedIndex"].get<long long>();
|
||||||
|
asset->info.drawSurf.fields.objectId = materialJson["gfxDrawSurface"]["objectId"].get<long long>();
|
||||||
|
asset->info.drawSurf.fields.prepass = materialJson["gfxDrawSurface"]["prepass"].get<long long>();
|
||||||
|
asset->info.drawSurf.fields.primarySortKey = materialJson["gfxDrawSurface"]["primarySortKey"].get<long long>();
|
||||||
|
asset->info.drawSurf.fields.reflectionProbeIndex = materialJson["gfxDrawSurface"]["reflectionProbeIndex"].get<long long>();
|
||||||
|
asset->info.drawSurf.fields.sceneLightIndex = materialJson["gfxDrawSurface"]["sceneLightIndex"].get<long long>();
|
||||||
|
asset->info.drawSurf.fields.surfType = materialJson["gfxDrawSurface"]["surfType"].get<long long>();
|
||||||
|
asset->info.drawSurf.fields.unused = materialJson["gfxDrawSurface"]["unused"].get<long long>();
|
||||||
|
asset->info.drawSurf.fields.useHeroLighting = materialJson["gfxDrawSurface"]["useHeroLighting"].get<long long>();
|
||||||
|
}
|
||||||
|
|
||||||
|
asset->stateFlags = static_cast<char>(Utils::Json::ReadFlags(materialJson["stateFlags"].get<std::string>(), sizeof(char)));
|
||||||
|
|
||||||
|
if (materialJson["textureTable"].is_array())
|
||||||
|
{
|
||||||
|
nlohmann::json::array_t textureTable = materialJson["textureTable"];
|
||||||
|
asset->textureCount = static_cast<unsigned char>(textureTable.size());
|
||||||
|
asset->textureTable = builder->getAllocator()->allocateArray<Game::MaterialTextureDef>(asset->textureCount);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < textureTable.size(); i++)
|
||||||
|
{
|
||||||
|
auto& textureJson = textureTable[i];
|
||||||
|
if (textureJson.is_object())
|
||||||
|
{
|
||||||
|
Game::MaterialTextureDef* textureDef = &asset->textureTable[i];
|
||||||
|
textureDef->semantic = textureJson["semantic"].get<Game::TextureSemantic>();
|
||||||
|
textureDef->samplerState = textureJson["samplerState"].get<char>();
|
||||||
|
textureDef->nameStart = textureJson["nameStart"].get<char>();
|
||||||
|
textureDef->nameEnd = textureJson["nameEnd"].get<char>();
|
||||||
|
textureDef->nameHash = textureJson["nameHash"].get<unsigned int>();
|
||||||
|
|
||||||
|
if (textureDef->semantic == Game::TextureSemantic::TS_WATER_MAP)
|
||||||
|
{
|
||||||
|
Game::water_t* water = builder->getAllocator()->allocate<Game::water_t>();
|
||||||
|
|
||||||
|
if (textureJson["water"].is_object())
|
||||||
|
{
|
||||||
|
auto& waterJson = textureJson["water"];
|
||||||
|
|
||||||
|
if (waterJson["image"].is_string())
|
||||||
|
{
|
||||||
|
auto imageName = waterJson["image"].get<std::string>();
|
||||||
|
|
||||||
|
water->image = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, imageName.data(), builder).image;
|
||||||
|
}
|
||||||
|
|
||||||
|
water->amplitude = waterJson["amplitude"].get<float>();
|
||||||
|
water->M = waterJson["M"].get<int>();
|
||||||
|
water->N = waterJson["N"].get<int>();
|
||||||
|
water->Lx = waterJson["Lx"].get<float>();
|
||||||
|
water->Lz = waterJson["Lz"].get<float>();
|
||||||
|
water->gravity = waterJson["gravity"].get<float>();
|
||||||
|
water->windvel = waterJson["windvel"].get<float>();
|
||||||
|
|
||||||
|
auto winddir = waterJson["winddir"].get<std::vector<float>>();
|
||||||
|
if (winddir.size() == 2)
|
||||||
|
{
|
||||||
|
std::copy(winddir.begin(), winddir.end(), water->winddir);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto codeConstant = waterJson["codeConstant"].get<std::vector<float>>();
|
||||||
|
|
||||||
|
if (codeConstant.size() == 4)
|
||||||
|
{
|
||||||
|
std::copy(codeConstant.begin(), codeConstant.end(), water->codeConstant);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// H0
|
||||||
|
[[maybe_unused]] auto idealSize = water->M * water->N * sizeof(Game::complex_s);
|
||||||
|
auto h064 = waterJson["H0"].get<std::string>();
|
||||||
|
auto predictedSize = static_cast<unsigned long>(std::ceilf((h064.size() / 4.f) * 3.f));
|
||||||
|
assert(predictedSize >= idealSize);
|
||||||
|
|
||||||
|
auto h0 = reinterpret_cast<Game::complex_s*>(builder->getAllocator()->allocate(predictedSize));
|
||||||
|
|
||||||
|
[[maybe_unused]] auto h0Result = base64_decode(
|
||||||
|
h064.data(),
|
||||||
|
h064.size(),
|
||||||
|
reinterpret_cast<unsigned char*>(h0),
|
||||||
|
&predictedSize
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(h0Result == CRYPT_OK);
|
||||||
|
water->H0 = h0;
|
||||||
|
|
||||||
|
/// WTerm
|
||||||
|
auto wTerm64 = waterJson["wTerm"].get<std::string>();
|
||||||
|
auto predictedWTermSize = static_cast<unsigned long>(std::ceilf((wTerm64.size() / 4.f) * 3.f));
|
||||||
|
|
||||||
|
auto wTerm = reinterpret_cast<float*>(builder->getAllocator()->allocate(predictedWTermSize));
|
||||||
|
|
||||||
|
[[maybe_unused]] auto wTermResult = base64_decode(
|
||||||
|
wTerm64.data(),
|
||||||
|
wTerm64.size(),
|
||||||
|
reinterpret_cast<unsigned char*>(wTerm),
|
||||||
|
&predictedWTermSize
|
||||||
|
);
|
||||||
|
|
||||||
|
assert(wTermResult == CRYPT_OK);
|
||||||
|
water->wTerm = wTerm;
|
||||||
|
}
|
||||||
|
|
||||||
|
textureDef->u.water = water;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
textureDef->u.image = nullptr;
|
||||||
|
if (textureJson["image"].is_string())
|
||||||
|
{
|
||||||
|
textureDef->u.image = Components::AssetHandler::FindAssetForZone
|
||||||
|
(
|
||||||
|
Game::XAssetType::ASSET_TYPE_IMAGE,
|
||||||
|
textureJson["image"].get<std::string>(),
|
||||||
|
builder
|
||||||
|
).image;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Statebits
|
||||||
|
if (materialJson["stateBitsEntry"].is_array())
|
||||||
|
{
|
||||||
|
nlohmann::json::array_t stateBitsEntry = materialJson["stateBitsEntry"];
|
||||||
|
|
||||||
|
for (size_t i = 0; i < std::min(stateBitsEntry.size(), 48u); i++)
|
||||||
|
{
|
||||||
|
asset->stateBitsEntry[i] = stateBitsEntry[i].get<char>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (materialJson["stateBitsTable"].is_array())
|
||||||
|
{
|
||||||
|
nlohmann::json::array_t array = materialJson["stateBitsTable"];
|
||||||
|
asset->stateBitsCount = static_cast<unsigned char>(array.size());
|
||||||
|
|
||||||
|
asset->stateBitsTable = builder->getAllocator()->allocateArray<Game::GfxStateBits>(array.size());
|
||||||
|
|
||||||
|
size_t statebitTableIndex = 0;
|
||||||
|
for (auto& jsonStateBitEntry : array)
|
||||||
|
{
|
||||||
|
auto stateBit = &asset->stateBitsTable[statebitTableIndex++];
|
||||||
|
|
||||||
|
unsigned int loadbits0 = 0;
|
||||||
|
unsigned int loadbits1 = 0;
|
||||||
|
|
||||||
|
#define READ_INT_LB_FROM_JSON(x) unsigned int x = jsonStateBitEntry[#x].get<unsigned int>()
|
||||||
|
#define READ_BOOL_LB_FROM_JSON(x) bool x = jsonStateBitEntry[#x].get<bool>()
|
||||||
|
|
||||||
|
READ_INT_LB_FROM_JSON(srcBlendRgb);
|
||||||
|
READ_INT_LB_FROM_JSON(dstBlendRgb);
|
||||||
|
READ_INT_LB_FROM_JSON(blendOpRgb);
|
||||||
|
READ_INT_LB_FROM_JSON(srcBlendAlpha);
|
||||||
|
READ_INT_LB_FROM_JSON(dstBlendAlpha);
|
||||||
|
READ_INT_LB_FROM_JSON(blendOpAlpha);
|
||||||
|
READ_INT_LB_FROM_JSON(depthTest);
|
||||||
|
READ_INT_LB_FROM_JSON(polygonOffset);
|
||||||
|
|
||||||
|
const auto alphaTest = jsonStateBitEntry["alphaTest"].get<std::string>();
|
||||||
|
const auto cullFace = jsonStateBitEntry["cullFace"].get<std::string>();
|
||||||
|
|
||||||
|
READ_BOOL_LB_FROM_JSON(colorWriteRgb);
|
||||||
|
READ_BOOL_LB_FROM_JSON(colorWriteAlpha);
|
||||||
|
READ_BOOL_LB_FROM_JSON(polymodeLine);
|
||||||
|
|
||||||
|
READ_BOOL_LB_FROM_JSON(gammaWrite);
|
||||||
|
READ_BOOL_LB_FROM_JSON(depthWrite);
|
||||||
|
READ_BOOL_LB_FROM_JSON(stencilFrontEnabled);
|
||||||
|
READ_BOOL_LB_FROM_JSON(stencilBackEnabled);
|
||||||
|
|
||||||
|
READ_INT_LB_FROM_JSON(stencilFrontPass);
|
||||||
|
READ_INT_LB_FROM_JSON(stencilFrontFail);
|
||||||
|
READ_INT_LB_FROM_JSON(stencilFrontZFail);
|
||||||
|
READ_INT_LB_FROM_JSON(stencilFrontFunc);
|
||||||
|
READ_INT_LB_FROM_JSON(stencilBackPass);
|
||||||
|
READ_INT_LB_FROM_JSON(stencilBackFail);
|
||||||
|
READ_INT_LB_FROM_JSON(stencilBackZFail);
|
||||||
|
READ_INT_LB_FROM_JSON(stencilBackFunc);
|
||||||
|
|
||||||
|
loadbits0 |= srcBlendRgb << Game::GFXS0_SRCBLEND_RGB_SHIFT;
|
||||||
|
loadbits0 |= dstBlendRgb << Game::GFXS0_DSTBLEND_RGB_SHIFT;
|
||||||
|
loadbits0 |= blendOpRgb << Game::GFXS0_BLENDOP_RGB_SHIFT;
|
||||||
|
loadbits0 |= srcBlendAlpha << Game::GFXS0_SRCBLEND_ALPHA_SHIFT;
|
||||||
|
loadbits0 |= dstBlendAlpha << Game::GFXS0_DSTBLEND_ALPHA_SHIFT;
|
||||||
|
loadbits0 |= blendOpAlpha << Game::GFXS0_BLENDOP_ALPHA_SHIFT;
|
||||||
|
|
||||||
|
if (depthTest == -1)
|
||||||
|
{
|
||||||
|
loadbits1 |= Game::GFXS1_DEPTHTEST_DISABLE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
loadbits1 |= depthTest << Game::GFXS1_DEPTHTEST_SHIFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadbits1 |= polygonOffset << Game::GFXS1_POLYGON_OFFSET_SHIFT;
|
||||||
|
|
||||||
|
if (alphaTest == "disable")
|
||||||
|
{
|
||||||
|
loadbits0 |= Game::GFXS0_ATEST_DISABLE;
|
||||||
|
}
|
||||||
|
else if (alphaTest == ">0")
|
||||||
|
{
|
||||||
|
loadbits0 |= Game::GFXS0_ATEST_GT_0;
|
||||||
|
}
|
||||||
|
else if (alphaTest == "<128")
|
||||||
|
{
|
||||||
|
loadbits0 |= Game::GFXS0_ATEST_LT_128;
|
||||||
|
}
|
||||||
|
else if (alphaTest == ">=128")
|
||||||
|
{
|
||||||
|
loadbits0 |= Game::GFXS0_ATEST_GE_128;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid alphatest loadbit0 '{}' in material {}\n", alphaTest, name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cullFace == "none")
|
||||||
|
{
|
||||||
|
loadbits0 |= Game::GFXS0_CULL_NONE;
|
||||||
|
}
|
||||||
|
else if (cullFace == "back")
|
||||||
|
{
|
||||||
|
loadbits0 |= Game::GFXS0_CULL_BACK;
|
||||||
|
}
|
||||||
|
else if (cullFace == "front")
|
||||||
|
{
|
||||||
|
loadbits0 |= Game::GFXS0_CULL_FRONT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid cullFace loadbit0 '{}' in material {}\n", cullFace, name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gammaWrite)
|
||||||
|
{
|
||||||
|
loadbits0 |= Game::GFXS0_GAMMAWRITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colorWriteAlpha)
|
||||||
|
{
|
||||||
|
loadbits0 |= Game::GFXS0_COLORWRITE_ALPHA;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colorWriteRgb)
|
||||||
|
{
|
||||||
|
loadbits0 |= Game::GFXS0_COLORWRITE_RGB;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (polymodeLine)
|
||||||
|
{
|
||||||
|
loadbits0 |= Game::GFXS0_POLYMODE_LINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (depthWrite)
|
||||||
|
{
|
||||||
|
loadbits1 |= Game::GFXS1_DEPTHWRITE;
|
||||||
|
}
|
||||||
|
if (stencilFrontEnabled)
|
||||||
|
{
|
||||||
|
loadbits1 |= Game::GFXS1_STENCIL_FRONT_ENABLE;
|
||||||
|
}
|
||||||
|
if (stencilBackEnabled)
|
||||||
|
{
|
||||||
|
loadbits1 |= Game::GFXS1_STENCIL_BACK_ENABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadbits1 |= stencilFrontPass << Game::GFXS1_STENCIL_FRONT_PASS_SHIFT;
|
||||||
|
loadbits1 |= stencilFrontFail << Game::GFXS1_STENCIL_FRONT_FAIL_SHIFT;
|
||||||
|
loadbits1 |= stencilFrontZFail << Game::GFXS1_STENCIL_FRONT_ZFAIL_SHIFT;
|
||||||
|
loadbits1 |= stencilFrontFunc << Game::GFXS1_STENCIL_FRONT_FUNC_SHIFT;
|
||||||
|
loadbits1 |= stencilBackPass << Game::GFXS1_STENCIL_BACK_PASS_SHIFT;
|
||||||
|
loadbits1 |= stencilBackFail << Game::GFXS1_STENCIL_BACK_FAIL_SHIFT;
|
||||||
|
loadbits1 |= stencilBackZFail << Game::GFXS1_STENCIL_BACK_ZFAIL_SHIFT;
|
||||||
|
loadbits1 |= stencilBackFunc << Game::GFXS1_STENCIL_BACK_FUNC_SHIFT;
|
||||||
|
|
||||||
|
stateBit->loadBits[0] = loadbits0;
|
||||||
|
stateBit->loadBits[1] = loadbits1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constant table
|
||||||
|
if (materialJson["constantTable"].is_array())
|
||||||
|
{
|
||||||
|
|
||||||
|
nlohmann::json::array_t constants = materialJson["constantTable"];
|
||||||
|
asset->constantCount = static_cast<char>(constants.size());
|
||||||
|
auto table = builder->getAllocator()->allocateArray<Game::MaterialConstantDef>(asset->constantCount);
|
||||||
|
|
||||||
|
for (size_t constantIndex = 0; constantIndex < asset->constantCount; constantIndex++)
|
||||||
|
{
|
||||||
|
auto& constant = constants[constantIndex];
|
||||||
|
auto entry = &table[constantIndex];
|
||||||
|
|
||||||
|
auto litVec = constant["literal"].get<std::vector<float>>();
|
||||||
|
std::copy(litVec.begin(), litVec.end(), entry->literal);
|
||||||
|
|
||||||
|
auto constantName = constant["name"].get<std::string>();
|
||||||
|
std::copy(constantName.begin(), constantName.end(), entry->name);
|
||||||
|
|
||||||
|
entry->nameHash = constant["nameHash"].get<unsigned int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
asset->constantTable = table;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (materialJson["techniqueSet"].is_string())
|
||||||
|
{
|
||||||
|
const std::string techsetName = materialJson["techniqueSet"].get<std::string>();
|
||||||
|
asset->techniqueSet = findWorkingTechset(techsetName, asset, builder);
|
||||||
|
|
||||||
|
if (asset->techniqueSet == nullptr)
|
||||||
|
{
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header->material = asset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::MaterialTechniqueSet* IMaterial::findWorkingTechset(std::string techsetName, [[maybe_unused]] Game::Material* material, Components::ZoneBuilder::Zone* builder) const
|
||||||
|
{
|
||||||
|
Game::MaterialTechniqueSet* techset;
|
||||||
|
|
||||||
|
// Pass 1: Identical techset (1:1)
|
||||||
|
techset = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, techsetName.data(), builder).techniqueSet;
|
||||||
|
if (techset != nullptr)
|
||||||
|
{
|
||||||
|
return techset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We do no more cause we use CoD4 techset and they should always be present
|
||||||
|
// If one day we want to go back to mw2 fallback we can add extra steps here!
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IMaterial::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
void IMaterial::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
@ -30,30 +454,7 @@ namespace Assets
|
|||||||
"_add_lin_nofog",
|
"_add_lin_nofog",
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::unordered_map<std::string, std::string> techSetCorrespondance =
|
Components::FileSystem::File materialFile(std::format("materials/{}.iw4xMaterial", name));
|
||||||
{
|
|
||||||
{"effect", "effect_blend"},
|
|
||||||
{"effect", "effect_blend"},
|
|
||||||
{"effect_nofog", "effect_blend_nofog"},
|
|
||||||
{"effect_zfeather", "effect_zfeather_blend"},
|
|
||||||
|
|
||||||
{"wc_unlit_add", "wc_unlit_add_lin"},
|
|
||||||
{"wc_unlit_distfalloff", "wc_unlit_distfalloff_replace"},
|
|
||||||
{"wc_unlit_multiply", "wc_unlit_multiply_lin"},
|
|
||||||
{"wc_unlit_falloff_add", "wc_unlit_falloff_add_lin_ua"},
|
|
||||||
{"wc_unlit", "wc_unlit_replace_lin"},
|
|
||||||
{"wc_unlit_alphatest", "wc_unlit_blend_lin"},
|
|
||||||
{"wc_unlit_blend", "wc_unlit_blend_lin_ua"},
|
|
||||||
{"wc_unlit_replace", "wc_unlit_replace_lin"},
|
|
||||||
{"wc_unlit_nofog", "wc_unlit_replace_lin_nofog_nocast" },
|
|
||||||
|
|
||||||
{"mc_unlit_replace", "mc_unlit_replace_lin"},
|
|
||||||
{"mc_unlit_nofog", "mc_unlit_blend_nofog_ua"},
|
|
||||||
{"mc_unlit", "mc_unlit_replace_lin_nocast"},
|
|
||||||
{"mc_unlit_alphatest", "mc_unlit_blend_lin"}
|
|
||||||
};
|
|
||||||
|
|
||||||
Components::FileSystem::File materialFile(Utils::String::VA("materials/%s.iw4xMaterial", name.data()));
|
|
||||||
if (!materialFile.exists()) return;
|
if (!materialFile.exists()) return;
|
||||||
|
|
||||||
Utils::Stream::Reader reader(builder->getAllocator(), materialFile.getBuffer());
|
Utils::Stream::Reader reader(builder->getAllocator(), materialFile.getBuffer());
|
||||||
@ -66,9 +467,9 @@ namespace Assets
|
|||||||
|
|
||||||
std::string version;
|
std::string version;
|
||||||
version.push_back(reader.read<char>());
|
version.push_back(reader.read<char>());
|
||||||
if (version != IW4X_MAT_VERSION)
|
if (version != IW4X_MAT_BIN_VERSION)
|
||||||
{
|
{
|
||||||
Components::Logger::Error(Game::ERR_FATAL, "Reading material '{}' failed, expected version is {}, but it was {}!", name, IW4X_MAT_VERSION, version);
|
Components::Logger::Error(Game::ERR_FATAL, "Reading material '{}' failed, expected version is {}, but it was {}!", name, IW4X_MAT_BIN_VERSION, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* asset = reader.readObject<Game::Material>();
|
auto* asset = reader.readObject<Game::Material>();
|
||||||
@ -127,7 +528,7 @@ namespace Assets
|
|||||||
{
|
{
|
||||||
Game::MaterialTextureDef* textureDef = &asset->textureTable[i];
|
Game::MaterialTextureDef* textureDef = &asset->textureTable[i];
|
||||||
|
|
||||||
if (textureDef->semantic == SEMANTIC_WATER_MAP)
|
if (textureDef->semantic == Game::TextureSemantic::TS_WATER_MAP)
|
||||||
{
|
{
|
||||||
if (textureDef->u.water)
|
if (textureDef->u.water)
|
||||||
{
|
{
|
||||||
@ -190,7 +591,7 @@ namespace Assets
|
|||||||
// This is temp, as nobody has time to fix materials
|
// This is temp, as nobody has time to fix materials
|
||||||
asset->stateBitsCount = header.material->stateBitsCount;
|
asset->stateBitsCount = header.material->stateBitsCount;
|
||||||
asset->stateBitsTable = header.material->stateBitsTable;
|
asset->stateBitsTable = header.material->stateBitsTable;
|
||||||
std::memcpy(asset->stateBitsEntry, header.material->stateBitsEntry, 48);
|
std::memcpy(asset->stateBitsEntry, header.material->stateBitsEntry, ARRAYSIZE(asset->stateBitsEntry));
|
||||||
asset->constantCount = header.material->constantCount;
|
asset->constantCount = header.material->constantCount;
|
||||||
asset->constantTable = header.material->constantTable;
|
asset->constantTable = header.material->constantTable;
|
||||||
Components::Logger::Print("For {}, copied constants & statebits from {}\n", asset->info.name, header.material->info.name);
|
Components::Logger::Print("For {}, copied constants & statebits from {}\n", asset->info.name, header.material->info.name);
|
||||||
@ -241,10 +642,10 @@ namespace Assets
|
|||||||
{
|
{
|
||||||
Components::Logger::Print("No replacement found for material {} with techset {}\n", asset->info.name, asset->techniqueSet->name);
|
Components::Logger::Print("No replacement found for material {} with techset {}\n", asset->info.name, asset->techniqueSet->name);
|
||||||
std::string techName = asset->techniqueSet->name;
|
std::string techName = asset->techniqueSet->name;
|
||||||
if (techSetCorrespondance.contains(techName))
|
if (const auto itr = techSetCorrespondance.find(techName); itr != techSetCorrespondance.end())
|
||||||
{
|
{
|
||||||
auto iw4TechSetName = techSetCorrespondance[techName];
|
auto& iw4TechSetName = itr->second;
|
||||||
Game::XAssetEntry* iw4TechSet = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, iw4TechSetName.data());
|
auto* iw4TechSet = Game::DB_FindXAssetEntry(Game::ASSET_TYPE_TECHNIQUE_SET, iw4TechSetName.data());
|
||||||
|
|
||||||
if (iw4TechSet)
|
if (iw4TechSet)
|
||||||
{
|
{
|
||||||
@ -253,9 +654,11 @@ namespace Assets
|
|||||||
if (!replacementFound)
|
if (!replacementFound)
|
||||||
{
|
{
|
||||||
Game::XAssetHeader header = entry->asset.header;
|
Game::XAssetHeader header = entry->asset.header;
|
||||||
|
Components::Logger::Print("Material {} with techset {} has been mapped to {} (last chance!), taking the sort key of material {}\n",
|
||||||
|
asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name, header.material->info.name);
|
||||||
|
|
||||||
if (header.material->techniqueSet == iw4TechSet->asset.header.techniqueSet
|
// Yeah this has a tendency to fuck up a LOT of transparent materials
|
||||||
&& std::string(header.material->info.name).find("icon") == std::string::npos) // Yeah this has a tendency to fuck up a LOT of transparent materials
|
if (header.material->techniqueSet == iw4TechSet->asset.header.techniqueSet && std::string(header.material->info.name).find("icon") != std::string::npos)
|
||||||
{
|
{
|
||||||
Components::Logger::Print("Material {} with techset {} has been mapped to {} (last chance!), taking the sort key of material {}\n",
|
Components::Logger::Print("Material {} with techset {} has been mapped to {} (last chance!), taking the sort key of material {}\n",
|
||||||
asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name, header.material->info.name);
|
asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name, header.material->info.name);
|
||||||
@ -327,15 +730,6 @@ namespace Assets
|
|||||||
header->material = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).material;
|
header->material = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).material;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IMaterial::loadJson(Game::XAssetHeader* header, const std::string& name, [[maybe_unused]] Components::ZoneBuilder::Zone* builder)
|
|
||||||
{
|
|
||||||
Components::FileSystem::File materialInfo(Utils::String::VA("materials/%s.json", name.data()));
|
|
||||||
|
|
||||||
if (!materialInfo.exists()) return;
|
|
||||||
|
|
||||||
header->material = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IMaterial::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
void IMaterial::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
Game::Material* asset = header.material;
|
Game::Material* asset = header.material;
|
||||||
@ -351,7 +745,7 @@ namespace Assets
|
|||||||
{
|
{
|
||||||
if (asset->textureTable[i].u.image)
|
if (asset->textureTable[i].u.image)
|
||||||
{
|
{
|
||||||
if (asset->textureTable[i].semantic == SEMANTIC_WATER_MAP)
|
if (asset->textureTable[i].semantic == Game::TextureSemantic::TS_WATER_MAP)
|
||||||
{
|
{
|
||||||
if (asset->textureTable[i].u.water->image)
|
if (asset->textureTable[i].u.water->image)
|
||||||
{
|
{
|
||||||
@ -411,7 +805,7 @@ namespace Assets
|
|||||||
auto* destTextureDef = &destTextureTable[i];
|
auto* destTextureDef = &destTextureTable[i];
|
||||||
auto* textureDef = &asset->textureTable[i];
|
auto* textureDef = &asset->textureTable[i];
|
||||||
|
|
||||||
if (textureDef->semantic == SEMANTIC_WATER_MAP)
|
if (textureDef->semantic == Game::TextureSemantic::TS_WATER_MAP)
|
||||||
{
|
{
|
||||||
AssertSize(Game::water_t, 68);
|
AssertSize(Game::water_t, 68);
|
||||||
|
|
||||||
|
@ -13,5 +13,8 @@ namespace Assets
|
|||||||
void loadJson(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
void loadJson(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||||
void loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
void loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||||
void loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
void loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Game::MaterialTechniqueSet* findWorkingTechset(const std::string techsetName, Game::Material* material, Components::ZoneBuilder::Zone* builder) const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
#include "IMaterialPixelShader.hpp"
|
#include "IMaterialPixelShader.hpp"
|
||||||
|
|
||||||
#define IW4X_TECHSET_VERSION "0"
|
#define GFX_RENDERER_SHADER_SM3 0
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
|
|
||||||
void IMaterialPixelShader::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
void IMaterialPixelShader::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
|
|
||||||
if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game
|
if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game
|
||||||
|
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
|
||||||
}
|
}
|
||||||
|
|
||||||
void IMaterialPixelShader::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
void IMaterialPixelShader::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
||||||
@ -19,36 +19,19 @@ namespace Assets
|
|||||||
|
|
||||||
void IMaterialPixelShader::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
void IMaterialPixelShader::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
Components::FileSystem::File psFile(Utils::String::VA("ps/%s.iw4xPS", name.data()));
|
Components::FileSystem::File psFile(std::format("ps/{}.cso", name));
|
||||||
if (!psFile.exists()) return;
|
if (!psFile.exists()) return;
|
||||||
|
|
||||||
Utils::Stream::Reader reader(builder->getAllocator(), psFile.getBuffer());
|
auto buff = psFile.getBuffer();
|
||||||
|
auto programSize = buff.size() / 4;
|
||||||
|
Game::MaterialPixelShader* asset = builder->getAllocator()->allocate<Game::MaterialPixelShader>();
|
||||||
|
|
||||||
char* magic = reader.readArray<char>(8);
|
asset->name = builder->getAllocator()->duplicateString(name);
|
||||||
if (std::memcmp(magic, "IW4xPIXL", 8))
|
asset->prog.loadDef.loadForRenderer = GFX_RENDERER_SHADER_SM3;
|
||||||
{
|
asset->prog.loadDef.programSize = static_cast<unsigned short>(programSize);
|
||||||
Components::Logger::Error(Game::ERR_FATAL, "Reading pixel shader '{}' failed, header is invalid!", name);
|
asset->prog.loadDef.program = builder->getAllocator()->allocateArray<unsigned int>(programSize);
|
||||||
}
|
memcpy_s(asset->prog.loadDef.program, buff.size(), buff.data(), buff.size());
|
||||||
|
|
||||||
std::string version;
|
|
||||||
version.push_back(reader.read<char>());
|
|
||||||
if (version != IW4X_TECHSET_VERSION)
|
|
||||||
{
|
|
||||||
Components::Logger::Error(Game::ERR_FATAL,
|
|
||||||
"Reading pixel shader '{}' failed, expected version is {}, but it was {}!", name, IW4X_TECHSET_VERSION, version);
|
|
||||||
}
|
|
||||||
|
|
||||||
Game::MaterialPixelShader* asset = reader.readObject<Game::MaterialPixelShader>();
|
|
||||||
|
|
||||||
if (asset->name)
|
|
||||||
{
|
|
||||||
asset->name = reader.readCString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (asset->prog.loadDef.program)
|
|
||||||
{
|
|
||||||
asset->prog.loadDef.program = reader.readArray<unsigned int>(asset->prog.loadDef.programSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
header->pixelShader = asset;
|
header->pixelShader = asset;
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
#include "IMaterialTechniqueSet.hpp"
|
#include "IMaterialTechniqueSet.hpp"
|
||||||
|
|
||||||
#define IW4X_TECHSET_VERSION "0"
|
#define IW4X_TECHSET_VERSION 1
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
void IMaterialTechniqueSet::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
void IMaterialTechniqueSet::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
|
if (!header->data) this->loadFromDisk(header, name, builder); // Check if we need to import a new one into the game
|
||||||
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
|
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
|
||||||
if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IMaterialTechniqueSet::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
void IMaterialTechniqueSet::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
||||||
@ -16,126 +16,196 @@ namespace Assets
|
|||||||
header->techniqueSet = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).techniqueSet;
|
header->techniqueSet = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).techniqueSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IMaterialTechniqueSet::loadBinaryTechnique(Game::MaterialTechnique** tech, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
void IMaterialTechniqueSet::loadTechniqueFromDisk(Game::MaterialTechnique** tech, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
AssertSize(Game::MaterialPass, 20);
|
AssertSize(Game::MaterialPass, 20);
|
||||||
|
|
||||||
Components::FileSystem::File techFile(Utils::String::VA("techniques/%s.iw4xTech", name.data()));
|
Components::FileSystem::File techFile(std::format("techniques/{}.iw4x.json", name));
|
||||||
if (!techFile.exists()) {
|
if (!techFile.exists())
|
||||||
|
{
|
||||||
*tech = nullptr;
|
*tech = nullptr;
|
||||||
|
|
||||||
Components::Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "Missing technique '{}'\n", name);
|
Components::Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "Missing technique '{}'\n", name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::Stream::Reader reader(builder->getAllocator(), techFile.getBuffer());
|
nlohmann::json technique;
|
||||||
|
|
||||||
char* magic = reader.readArray<char>(8);
|
try
|
||||||
if (std::memcmp(magic, "IW4xTECH", 8))
|
|
||||||
{
|
{
|
||||||
Components::Logger::Error(Game::ERR_FATAL, "Reading technique '{}' failed, header is invalid!", name);
|
technique = nlohmann::json::parse(techFile.getBuffer());
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, file is messed up! {}", name, e.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string version;
|
int version = technique["version"].get<int>();
|
||||||
version.push_back(reader.read<char>());
|
|
||||||
if (version != IW4X_TECHSET_VERSION)
|
if (version != IW4X_TECHSET_VERSION)
|
||||||
{
|
{
|
||||||
Components::Logger::Error(Game::ERR_FATAL,
|
Components::Logger::Error(Game::ERR_FATAL,
|
||||||
"Reading technique '{}' failed, expected version is {}, but it was {}!", name, IW4X_TECHSET_VERSION, version.data());
|
"Reading technique '{}' failed, expected version is {}, but it was {}!", name, IW4X_TECHSET_VERSION, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned short flags = reader.read<unsigned short>();
|
unsigned short flags = static_cast<unsigned short>(Utils::Json::ReadFlags(technique["flags"].get<std::string>(), sizeof(short)));
|
||||||
unsigned short passCount = reader.read<unsigned short>();
|
|
||||||
|
|
||||||
Game::MaterialTechnique* asset = (Game::MaterialTechnique*)builder->getAllocator()->allocateArray<unsigned char>(sizeof(Game::MaterialTechnique) + (sizeof(Game::MaterialPass) * (passCount - 1)));
|
if (technique["passArray"].is_array())
|
||||||
|
{
|
||||||
|
nlohmann::json::array_t passArray = technique["passArray"];
|
||||||
|
|
||||||
|
Game::MaterialTechnique* asset = (Game::MaterialTechnique*)builder->getAllocator()->allocateArray<unsigned char>(sizeof(Game::MaterialTechnique) + (sizeof(Game::MaterialPass) * (passArray.size() - 1)));
|
||||||
|
|
||||||
asset->name = builder->getAllocator()->duplicateString(name);
|
asset->name = builder->getAllocator()->duplicateString(name);
|
||||||
asset->flags = flags;
|
asset->flags = flags;
|
||||||
asset->passCount = passCount;
|
asset->passCount = static_cast<unsigned short>(passArray.size());
|
||||||
|
|
||||||
Game::MaterialPass* passes = reader.readArray<Game::MaterialPass>(passCount);
|
Game::MaterialPass* passes = builder->getAllocator()->allocateArray<Game::MaterialPass>(asset->passCount);
|
||||||
std::memcpy(asset->passArray, passes, sizeof(Game::MaterialPass) * passCount);
|
std::memcpy(asset->passArray, passes, sizeof(Game::MaterialPass) * asset->passCount);
|
||||||
|
|
||||||
for (unsigned short i = 0; i < asset->passCount; i++)
|
for (unsigned short i = 0; i < asset->passCount; i++)
|
||||||
{
|
{
|
||||||
Game::MaterialPass* pass = &asset->passArray[i];
|
Game::MaterialPass* pass = &asset->passArray[i];
|
||||||
|
auto jsonPass = passArray[i];
|
||||||
|
|
||||||
if (pass->vertexDecl)
|
if (jsonPass["vertexDeclaration"].is_string())
|
||||||
{
|
{
|
||||||
const char* declName = reader.readCString();
|
auto declName = jsonPass["vertexDeclaration"].get<std::string>();
|
||||||
pass->vertexDecl = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_VERTEXDECL, declName, builder).vertexDecl;
|
pass->vertexDecl = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_VERTEXDECL, declName, builder).vertexDecl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pass->vertexShader)
|
if (jsonPass["vertexShader"].is_string())
|
||||||
{
|
{
|
||||||
const char* vsName = reader.readCString();
|
auto vsName = jsonPass["vertexShader"].get<std::string>();
|
||||||
pass->vertexShader = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_VERTEXSHADER, vsName, builder).vertexShader;
|
pass->vertexShader = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_VERTEXSHADER, vsName, builder).vertexShader;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pass->pixelShader)
|
if (jsonPass["pixelShader"].is_string())
|
||||||
{
|
{
|
||||||
const char* psName = reader.readCString();
|
auto psName = jsonPass["pixelShader"].get<std::string>();
|
||||||
pass->pixelShader = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_PIXELSHADER, psName, builder).pixelShader;
|
pass->pixelShader = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_PIXELSHADER, psName, builder).pixelShader;
|
||||||
}
|
}
|
||||||
|
|
||||||
pass->args = reader.readArray<Game::MaterialShaderArgument>(pass->perPrimArgCount + pass->perObjArgCount + pass->stableArgCount);
|
pass->perPrimArgCount = jsonPass["perPrimArgCount"].get<char>();
|
||||||
|
pass->perObjArgCount = jsonPass["perObjArgCount"].get<char>();
|
||||||
|
pass->stableArgCount = jsonPass["stableArgCount"].get<char>();
|
||||||
|
pass->customSamplerFlags = jsonPass["customSamplerFlags"].get<char>();
|
||||||
|
|
||||||
for (int j = 0; j < pass->perPrimArgCount + pass->perObjArgCount + pass->stableArgCount; j++)
|
|
||||||
|
if (jsonPass["arguments"].is_array())
|
||||||
{
|
{
|
||||||
if (pass->args[j].type == 1 || pass->args[j].type == 7)
|
nlohmann::json::array_t jsonAguments = jsonPass["arguments"];
|
||||||
|
|
||||||
|
pass->args = builder->getAllocator()->allocateArray<Game::MaterialShaderArgument>(jsonAguments.size());
|
||||||
|
|
||||||
|
for (size_t j = 0; j < jsonAguments.size(); j++)
|
||||||
{
|
{
|
||||||
pass->args[j].u.literalConst = reader.readArray<float>(4);
|
auto jsonArgument = jsonAguments[j];
|
||||||
|
Game::MaterialShaderArgument* argument = &pass->args[j];
|
||||||
|
|
||||||
|
argument->type = jsonArgument["type"].get<Game::MaterialShaderArgumentType>();
|
||||||
|
argument->dest = jsonArgument["dest"].get<unsigned short>();
|
||||||
|
|
||||||
|
if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_LITERAL_VERTEX_CONST ||
|
||||||
|
argument->type == Game::MaterialShaderArgumentType::MTL_ARG_LITERAL_PIXEL_CONST)
|
||||||
|
{
|
||||||
|
argument->u.literalConst = builder->getAllocator()->allocateArray<float>(4);
|
||||||
|
|
||||||
|
auto literals = jsonArgument["literals"].get<std::vector<float>>();
|
||||||
|
std::copy(literals.begin(), literals.end(), argument->u.literalConst);
|
||||||
}
|
}
|
||||||
|
else if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_CODE_VERTEX_CONST ||
|
||||||
if (pass->args[j].type == 3 || pass->args[j].type == 5)
|
argument->type == Game::MaterialShaderArgumentType::MTL_ARG_CODE_PIXEL_CONST)
|
||||||
{
|
{
|
||||||
pass->args[j].u.codeConst.index = *reader.readObject<unsigned short>();
|
if (jsonArgument["codeConst"].is_object())
|
||||||
pass->args[j].u.codeConst.firstRow = *reader.readObject<unsigned char>();
|
{
|
||||||
pass->args[j].u.codeConst.rowCount = *reader.readObject<unsigned char>();
|
auto codeConst = jsonArgument["codeConst"];
|
||||||
|
|
||||||
|
argument->u.codeConst.index = codeConst["index"].get<unsigned short>();
|
||||||
|
argument->u.codeConst.firstRow = codeConst["firstRow"].get<unsigned char>();
|
||||||
|
argument->u.codeConst.rowCount = codeConst["rowCount"].get<unsigned char>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_MATERIAL_PIXEL_SAMPLER ||
|
||||||
|
argument->type == Game::MaterialShaderArgumentType::MTL_ARG_MATERIAL_VERTEX_CONST ||
|
||||||
|
argument->type == Game::MaterialShaderArgumentType::MTL_ARG_MATERIAL_PIXEL_CONST)
|
||||||
|
{
|
||||||
|
argument->u.nameHash = jsonArgument["nameHash"].get<unsigned int>();
|
||||||
|
}
|
||||||
|
else if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_CODE_PIXEL_SAMPLER)
|
||||||
|
{
|
||||||
|
argument->u.codeSampler = jsonArgument["codeSampler"].get<unsigned int>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*tech = asset;
|
*tech = asset;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IMaterialTechniqueSet::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
|
||||||
{
|
|
||||||
Components::FileSystem::File tsFile(Utils::String::VA("techsets/%s.iw4xTS", name.data()));
|
|
||||||
if (!tsFile.exists()) return;
|
|
||||||
|
|
||||||
Utils::Stream::Reader reader(builder->getAllocator(), tsFile.getBuffer());
|
|
||||||
|
|
||||||
char* magic = reader.readArray<char>(8);
|
|
||||||
if (std::memcmp(magic, "IW4xTSET", 8))
|
|
||||||
{
|
|
||||||
Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, header is invalid!", name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string version;
|
void IMaterialTechniqueSet::loadFromDisk(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
version.push_back(reader.read<char>());
|
{
|
||||||
|
Components::FileSystem::File tsFile(std::format("techsets/{}.iw4x.json", name));
|
||||||
|
if (!tsFile.exists()) return;
|
||||||
|
|
||||||
|
nlohmann::json techset;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
techset = nlohmann::json::parse(tsFile.getBuffer());
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, file is messed up! {}", name, e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto version = techset["version"].get<int>();
|
||||||
if (version != IW4X_TECHSET_VERSION)
|
if (version != IW4X_TECHSET_VERSION)
|
||||||
{
|
{
|
||||||
Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, expected version is {}, but it was {}!",
|
Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, expected version is {}, but it was {}!",
|
||||||
name, IW4X_TECHSET_VERSION, version);
|
name, IW4X_TECHSET_VERSION, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::MaterialTechniqueSet* asset = reader.readObject<Game::MaterialTechniqueSet>();
|
Game::MaterialTechniqueSet* asset = builder->getAllocator()->allocate<Game::MaterialTechniqueSet>();
|
||||||
|
|
||||||
if (asset->name)
|
if (asset == nullptr)
|
||||||
{
|
{
|
||||||
asset->name = reader.readCString();
|
Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, allocation failed!", name);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 48; i++)
|
if (techset["name"].is_string())
|
||||||
{
|
{
|
||||||
if (asset->techniques[i])
|
asset->name = builder->getAllocator()->duplicateString(techset["name"].get<std::string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
asset->hasBeenUploaded = techset["hasBeenUploaded"].get<bool>();
|
||||||
|
asset->worldVertFormat = techset["worldVertFormat"].get<char>();
|
||||||
|
|
||||||
|
|
||||||
|
if (techset["remappedTechniqueSet"].is_string())
|
||||||
{
|
{
|
||||||
const char* techName = reader.readCString();
|
auto remapped = techset["remappedTechniqueSet"].get<std::string>();
|
||||||
this->loadBinaryTechnique(&asset->techniques[i], techName, builder);
|
|
||||||
|
if (remapped != asset->name)
|
||||||
|
{
|
||||||
|
builder->loadAssetByName(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, remapped, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (techset["techniques"].is_object())
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Game::TECHNIQUE_COUNT; i++)
|
||||||
|
{
|
||||||
|
auto technique = techset["techniques"].at(std::to_string(i));
|
||||||
|
|
||||||
|
if (technique.is_string())
|
||||||
|
{
|
||||||
|
this->loadTechniqueFromDisk(&asset->techniques[i], technique.get<std::string>(), builder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
header->techniqueSet = asset;
|
header->techniqueSet = asset;
|
||||||
}
|
}
|
||||||
@ -177,8 +247,10 @@ namespace Assets
|
|||||||
AssertSize(Game::MaterialTechniqueSet, 204);
|
AssertSize(Game::MaterialTechniqueSet, 204);
|
||||||
|
|
||||||
Utils::Stream* buffer = builder->getBuffer();
|
Utils::Stream* buffer = builder->getBuffer();
|
||||||
|
|
||||||
Game::MaterialTechniqueSet* asset = header.techniqueSet;
|
Game::MaterialTechniqueSet* asset = header.techniqueSet;
|
||||||
Game::MaterialTechniqueSet* dest = buffer->dest<Game::MaterialTechniqueSet>();
|
Game::MaterialTechniqueSet* dest = buffer->dest<Game::MaterialTechniqueSet>();
|
||||||
|
|
||||||
buffer->save(asset);
|
buffer->save(asset);
|
||||||
|
|
||||||
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
|
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
|
||||||
|
@ -12,8 +12,8 @@ namespace Assets
|
|||||||
void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
|
void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
|
||||||
|
|
||||||
void loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
void loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||||
void loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
void loadFromDisk(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||||
|
|
||||||
void loadBinaryTechnique(Game::MaterialTechnique** tech, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
void loadTechniqueFromDisk(Game::MaterialTechnique** tech, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
#include "IMaterialVertexDeclaration.hpp"
|
#include "IMaterialVertexDeclaration.hpp"
|
||||||
|
|
||||||
#define IW4X_TECHSET_VERSION "0"
|
#define IW4X_TECHSET_VERSION 1
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
void IMaterialVertexDeclaration::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
void IMaterialVertexDeclaration::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
|
|
||||||
if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game
|
if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game
|
||||||
|
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
|
||||||
}
|
}
|
||||||
|
|
||||||
void IMaterialVertexDeclaration::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
void IMaterialVertexDeclaration::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
||||||
@ -18,7 +18,7 @@ namespace Assets
|
|||||||
|
|
||||||
void IMaterialVertexDeclaration::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
void IMaterialVertexDeclaration::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
Components::FileSystem::File declFile(Utils::String::VA("decl/%s.iw4xDECL", name.data()));
|
Components::FileSystem::File declFile(std::format("decl/{}.iw4xDECL", name));
|
||||||
if (!declFile.exists()) return;
|
if (!declFile.exists()) return;
|
||||||
|
|
||||||
Utils::Stream::Reader reader(builder->getAllocator(), declFile.getBuffer());
|
Utils::Stream::Reader reader(builder->getAllocator(), declFile.getBuffer());
|
||||||
@ -29,12 +29,11 @@ namespace Assets
|
|||||||
Components::Logger::Error(Game::ERR_FATAL, "Reading vertex declaration '{}' failed, header is invalid!", name);
|
Components::Logger::Error(Game::ERR_FATAL, "Reading vertex declaration '{}' failed, header is invalid!", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string version;
|
auto version = reader.read<char>();
|
||||||
version.push_back(reader.read<char>());
|
|
||||||
if (version != IW4X_TECHSET_VERSION)
|
if (version != IW4X_TECHSET_VERSION)
|
||||||
{
|
{
|
||||||
Components::Logger::Error(Game::ERR_FATAL, "Reading vertex declaration '{}' failed, expected version is {}, but it was {}!",
|
Components::Logger::Error(Game::ERR_FATAL, "Reading vertex declaration '{}' failed, expected version is {}, but it was {:d}!",
|
||||||
name, IW4X_TECHSET_VERSION, version.data());
|
name, IW4X_TECHSET_VERSION, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::MaterialVertexDeclaration* asset = reader.readObject<Game::MaterialVertexDeclaration>();
|
Game::MaterialVertexDeclaration* asset = reader.readObject<Game::MaterialVertexDeclaration>();
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
#include "IMaterialVertexShader.hpp"
|
#include "IMaterialVertexShader.hpp"
|
||||||
|
|
||||||
#define IW4X_TECHSET_VERSION "0"
|
#define GFX_RENDERER_SHADER_SM3 0
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
void IMaterialVertexShader::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
void IMaterialVertexShader::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
|
|
||||||
if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game
|
if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game
|
||||||
|
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
|
||||||
}
|
}
|
||||||
|
|
||||||
void IMaterialVertexShader::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
void IMaterialVertexShader::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
||||||
@ -18,36 +18,18 @@ namespace Assets
|
|||||||
|
|
||||||
void IMaterialVertexShader::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
void IMaterialVertexShader::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
Components::FileSystem::File vsFile(Utils::String::VA("vs/%s.iw4xVS", name.data()));
|
Components::FileSystem::File vsFile(std::format("vs/{}.cso", name));
|
||||||
if (!vsFile.exists()) return;
|
if (!vsFile.exists()) return;
|
||||||
|
|
||||||
Utils::Stream::Reader reader(builder->getAllocator(), vsFile.getBuffer());
|
auto buff = vsFile.getBuffer();
|
||||||
|
auto programSize = buff.size() / 4;
|
||||||
|
Game::MaterialVertexShader* asset = builder->getAllocator()->allocate<Game::MaterialVertexShader>();
|
||||||
|
|
||||||
char* magic = reader.readArray<char>(8);
|
asset->name = builder->getAllocator()->duplicateString(name);
|
||||||
if (std::memcmp(magic, "IW4xVERT", 8))
|
asset->prog.loadDef.loadForRenderer = GFX_RENDERER_SHADER_SM3;
|
||||||
{
|
asset->prog.loadDef.programSize = static_cast<unsigned short>(programSize);
|
||||||
Components::Logger::Error(Game::ERR_FATAL, "Reading vertex shader '{}' failed, header is invalid!", name);
|
asset->prog.loadDef.program = builder->getAllocator()->allocateArray<unsigned int>(programSize);
|
||||||
}
|
memcpy_s(asset->prog.loadDef.program, buff.size(), buff.data(), buff.size());
|
||||||
|
|
||||||
std::string version;
|
|
||||||
version.push_back(reader.read<char>());
|
|
||||||
if (version != IW4X_TECHSET_VERSION)
|
|
||||||
{
|
|
||||||
Components::Logger::Error(Game::ERR_FATAL, "Reading vertex shader '{}' failed, expected version is {}, but it was {}!",
|
|
||||||
name, IW4X_TECHSET_VERSION, version);
|
|
||||||
}
|
|
||||||
|
|
||||||
Game::MaterialVertexShader* asset = reader.readObject<Game::MaterialVertexShader>();
|
|
||||||
|
|
||||||
if (asset->name)
|
|
||||||
{
|
|
||||||
asset->name = reader.readCString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (asset->prog.loadDef.program)
|
|
||||||
{
|
|
||||||
asset->prog.loadDef.program = reader.readArray<unsigned int>(asset->prog.loadDef.programSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
header->vertexShader = asset;
|
header->vertexShader = asset;
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ namespace Assets
|
|||||||
if (menus.empty()) return;
|
if (menus.empty()) return;
|
||||||
|
|
||||||
// Allocate new menu list
|
// Allocate new menu list
|
||||||
Game::MenuList* newList = allocator->allocate<Game::MenuList>();
|
auto* newList = allocator->allocate<Game::MenuList>();
|
||||||
if (!newList) return;
|
if (!newList) return;
|
||||||
|
|
||||||
newList->menus = allocator->allocateArray<Game::menuDef_t*>(menus.size());
|
newList->menus = allocator->allocateArray<Game::menuDef_t*>(menus.size());
|
||||||
@ -35,7 +35,7 @@ namespace Assets
|
|||||||
}
|
}
|
||||||
void IMenuList::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
void IMenuList::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
Game::MenuList *asset = header.menuList;
|
auto* asset = header.menuList;
|
||||||
|
|
||||||
for (int i = 0; i < asset->menuCount; ++i)
|
for (int i = 0; i < asset->menuCount; ++i)
|
||||||
{
|
{
|
||||||
@ -51,7 +51,7 @@ namespace Assets
|
|||||||
|
|
||||||
Utils::Stream* buffer = builder->getBuffer();
|
Utils::Stream* buffer = builder->getBuffer();
|
||||||
Game::MenuList* asset = header.menuList;
|
Game::MenuList* asset = header.menuList;
|
||||||
Game::MenuList* dest = buffer->dest<Game::MenuList>();
|
auto* dest = buffer->dest<Game::MenuList>();
|
||||||
|
|
||||||
buffer->save(asset);
|
buffer->save(asset);
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ namespace Assets
|
|||||||
{
|
{
|
||||||
buffer->align(Utils::Stream::ALIGN_4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
|
|
||||||
Game::menuDef_t **destMenus = buffer->dest<Game::menuDef_t*>();
|
auto** destMenus = buffer->dest<Game::menuDef_t*>();
|
||||||
buffer->saveArray(asset->menus, asset->menuCount);
|
buffer->saveArray(asset->menus, asset->menuCount);
|
||||||
|
|
||||||
for (int i = 0; i < asset->menuCount; ++i)
|
for (int i = 0; i < asset->menuCount; ++i)
|
||||||
|
@ -18,23 +18,24 @@ namespace Assets
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto data = Utils::Compression::ZLib::Compress(rawFile.getBuffer());
|
|
||||||
|
|
||||||
asset->name = builder->getAllocator()->duplicateString(name);
|
asset->name = builder->getAllocator()->duplicateString(name);
|
||||||
|
asset->len = static_cast<int>(rawFile.getBuffer().size());
|
||||||
|
|
||||||
if (data.size() < rawFile.getBuffer().size())
|
const auto compressedData = Utils::Compression::ZLib::Compress(rawFile.getBuffer());
|
||||||
|
// Only save the compressed buffer if we gained space
|
||||||
|
if (compressedData.size() < rawFile.getBuffer().size())
|
||||||
{
|
{
|
||||||
asset->buffer = builder->getAllocator()->duplicateString(data);
|
asset->buffer = builder->getAllocator()->allocateArray<char>(compressedData.size());
|
||||||
asset->compressedLen = static_cast<int>(data.size());
|
std::memcpy(const_cast<char*>(asset->buffer), compressedData.data(), compressedData.size());
|
||||||
|
asset->compressedLen = static_cast<int>(compressedData.size());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
asset->buffer = builder->getAllocator()->duplicateString(rawFile.getBuffer());
|
asset->buffer = builder->getAllocator()->allocateArray<char>(rawFile.getBuffer().size() + 1);
|
||||||
|
std::memcpy(const_cast<char*>(asset->buffer), rawFile.getBuffer().data(), rawFile.getBuffer().size());
|
||||||
asset->compressedLen = 0;
|
asset->compressedLen = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
asset->len = static_cast<int>(rawFile.getBuffer().size());
|
|
||||||
|
|
||||||
header->rawfile = asset;
|
header->rawfile = asset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ namespace Assets
|
|||||||
void IWeapon::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
void IWeapon::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
||||||
{
|
{
|
||||||
// Try loading raw weapon
|
// Try loading raw weapon
|
||||||
if (Components::FileSystem::File(Utils::String::VA("weapons/mp/%s", name.data())).exists())
|
if (Components::FileSystem::File(std::format("weapons/mp/{}", name)))
|
||||||
{
|
{
|
||||||
// let the function see temporary assets when calling DB_FindXAssetHeader during the loading function
|
// let the function see temporary assets when calling DB_FindXAssetHeader during the loading function
|
||||||
// otherwise it fails to link things properly
|
// otherwise it fails to link things properly
|
||||||
@ -280,7 +280,7 @@ namespace Assets
|
|||||||
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
|
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
|
||||||
buffer->saveArray(def->notetrackSoundMapKeys, 16);
|
buffer->saveArray(def->notetrackSoundMapKeys, 16);
|
||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
builder->mapScriptString(&scriptStringTable[i]);
|
builder->mapScriptString(scriptStringTable[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::Stream::ClearPointer(&dest->notetrackSoundMapKeys);
|
Utils::Stream::ClearPointer(&dest->notetrackSoundMapKeys);
|
||||||
@ -292,7 +292,7 @@ namespace Assets
|
|||||||
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
|
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
|
||||||
buffer->saveArray(def->notetrackSoundMapValues, 16);
|
buffer->saveArray(def->notetrackSoundMapValues, 16);
|
||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
builder->mapScriptString(&scriptStringTable[i]);
|
builder->mapScriptString(scriptStringTable[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::Stream::ClearPointer(&dest->notetrackSoundMapValues);
|
Utils::Stream::ClearPointer(&dest->notetrackSoundMapValues);
|
||||||
@ -304,7 +304,7 @@ namespace Assets
|
|||||||
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
|
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
|
||||||
buffer->saveArray(def->notetrackRumbleMapKeys, 16);
|
buffer->saveArray(def->notetrackRumbleMapKeys, 16);
|
||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
builder->mapScriptString(&scriptStringTable[i]);
|
builder->mapScriptString(scriptStringTable[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::Stream::ClearPointer(&dest->notetrackRumbleMapKeys);
|
Utils::Stream::ClearPointer(&dest->notetrackRumbleMapKeys);
|
||||||
@ -316,7 +316,7 @@ namespace Assets
|
|||||||
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
|
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
|
||||||
buffer->saveArray(def->notetrackRumbleMapValues, 16);
|
buffer->saveArray(def->notetrackRumbleMapValues, 16);
|
||||||
for (int i = 0; i < 16; i++) {
|
for (int i = 0; i < 16; i++) {
|
||||||
builder->mapScriptString(&scriptStringTable[i]);
|
builder->mapScriptString(scriptStringTable[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::Stream::ClearPointer(&dest->notetrackRumbleMapValues);
|
Utils::Stream::ClearPointer(&dest->notetrackRumbleMapValues);
|
||||||
@ -725,7 +725,7 @@ namespace Assets
|
|||||||
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
|
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
|
||||||
buffer->saveArray(asset->hideTags, 32);
|
buffer->saveArray(asset->hideTags, 32);
|
||||||
for (int i = 0; i < 32; i++) {
|
for (int i = 0; i < 32; i++) {
|
||||||
builder->mapScriptString(&scriptStringTable[i]);
|
builder->mapScriptString(scriptStringTable[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::Stream::ClearPointer(&dest->hideTags);
|
Utils::Stream::ClearPointer(&dest->hideTags);
|
||||||
|
@ -7,7 +7,7 @@ namespace Assets
|
|||||||
{
|
{
|
||||||
void IXAnimParts::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
void IXAnimParts::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
Components::FileSystem::File animFile(Utils::String::VA("xanim/%s.iw4xAnim", name.data()));
|
Components::FileSystem::File animFile(std::format("xanim/{}.iw4xAnim", name));
|
||||||
|
|
||||||
if (animFile.exists())
|
if (animFile.exists())
|
||||||
{
|
{
|
||||||
@ -39,7 +39,7 @@ namespace Assets
|
|||||||
xanim->names = builder->getAllocator()->allocateArray<unsigned short>(xanim->boneCount[Game::PART_TYPE_ALL]);
|
xanim->names = builder->getAllocator()->allocateArray<unsigned short>(xanim->boneCount[Game::PART_TYPE_ALL]);
|
||||||
for (int i = 0; i < xanim->boneCount[Game::PART_TYPE_ALL]; ++i)
|
for (int i = 0; i < xanim->boneCount[Game::PART_TYPE_ALL]; ++i)
|
||||||
{
|
{
|
||||||
xanim->names[i] = Game::SL_GetString(reader.readCString(), 0);
|
xanim->names[i] = static_cast<std::uint16_t>(Game::SL_GetString(reader.readCString(), 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ namespace Assets
|
|||||||
|
|
||||||
for (int i = 0; i < xanim->notifyCount; ++i)
|
for (int i = 0; i < xanim->notifyCount; ++i)
|
||||||
{
|
{
|
||||||
xanim->notify[i].name = Game::SL_GetString(reader.readCString(), 0);
|
xanim->notify[i].name = static_cast<std::uint16_t>(Game::SL_GetString(reader.readCString(), 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,7 +264,7 @@ namespace Assets
|
|||||||
|
|
||||||
for (char i = 0; i < asset->boneCount[Game::PART_TYPE_ALL]; ++i)
|
for (char i = 0; i < asset->boneCount[Game::PART_TYPE_ALL]; ++i)
|
||||||
{
|
{
|
||||||
builder->mapScriptString(&destTagnames[i]);
|
builder->mapScriptString(destTagnames[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::Stream::ClearPointer(&dest->names);
|
Utils::Stream::ClearPointer(&dest->names);
|
||||||
@ -280,7 +280,7 @@ namespace Assets
|
|||||||
|
|
||||||
for (char i = 0; i < asset->notifyCount; ++i)
|
for (char i = 0; i < asset->notifyCount; ++i)
|
||||||
{
|
{
|
||||||
builder->mapScriptString(&destNotetracks[i].name);
|
builder->mapScriptString(destNotetracks[i].name);
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::Stream::ClearPointer(&dest->notify);
|
Utils::Stream::ClearPointer(&dest->notify);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
#include "IXModel.hpp"
|
#include "IXModel.hpp"
|
||||||
|
|
||||||
#define IW4X_MODEL_VERSION 5
|
#define IW4X_MODEL_VERSION 8
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
@ -9,32 +9,32 @@ namespace Assets
|
|||||||
{
|
{
|
||||||
if (entry->nodes)
|
if (entry->nodes)
|
||||||
{
|
{
|
||||||
entry->nodes = reader->readArray<Game::XSurfaceCollisionNode>(entry->nodeCount);
|
entry->nodes = reader->readArrayOnce<Game::XSurfaceCollisionNode>(entry->nodeCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry->leafs)
|
if (entry->leafs)
|
||||||
{
|
{
|
||||||
entry->leafs = reader->readArray<Game::XSurfaceCollisionLeaf>(entry->leafCount);
|
entry->leafs = reader->readArrayOnce<Game::XSurfaceCollisionLeaf>(entry->leafCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IXModel::loadXSurface(Game::XSurface* surf, Utils::Stream::Reader* reader, Components::ZoneBuilder::Zone* builder)
|
void IXModel::loadXSurface(Game::XSurface* surf, Utils::Stream::Reader* reader, [[maybe_unused]] Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
if (surf->vertInfo.vertsBlend)
|
if (surf->vertInfo.vertsBlend)
|
||||||
{
|
{
|
||||||
surf->vertInfo.vertsBlend = reader->readArray<unsigned short>(surf->vertInfo.vertCount[0] + (surf->vertInfo.vertCount[1] * 3) + (surf->vertInfo.vertCount[2] * 5) + (surf->vertInfo.vertCount[3] * 7));
|
surf->vertInfo.vertsBlend = reader->readArrayOnce<unsigned short>(surf->vertInfo.vertCount[0] + (surf->vertInfo.vertCount[1] * 3) + (surf->vertInfo.vertCount[2] * 5) + (surf->vertInfo.vertCount[3] * 7));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Access vertex block
|
// Access vertex block
|
||||||
if (surf->verts0)
|
if (surf->verts0)
|
||||||
{
|
{
|
||||||
surf->verts0 = reader->readArray<Game::GfxPackedVertex>(surf->vertCount);
|
surf->verts0 = reader->readArrayOnce<Game::GfxPackedVertex>(surf->vertCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save_XRigidVertListArray
|
// Save_XRigidVertListArray
|
||||||
if (surf->vertList)
|
if (surf->vertList)
|
||||||
{
|
{
|
||||||
surf->vertList = reader->readArray<Game::XRigidVertList>(surf->vertListCount);
|
surf->vertList = reader->readArrayOnce<Game::XRigidVertList>(surf->vertListCount);
|
||||||
|
|
||||||
for (unsigned int i = 0; i < surf->vertListCount; ++i)
|
for (unsigned int i = 0; i < surf->vertListCount; ++i)
|
||||||
{
|
{
|
||||||
@ -51,17 +51,7 @@ namespace Assets
|
|||||||
// Access index block
|
// Access index block
|
||||||
if (surf->triIndices)
|
if (surf->triIndices)
|
||||||
{
|
{
|
||||||
void* oldPtr = surf->triIndices;
|
surf->triIndices = reader->readArrayOnce<unsigned short>(surf->triCount * 3);
|
||||||
surf->triIndices = reader->readArray<unsigned short>(surf->triCount * 3);
|
|
||||||
|
|
||||||
if (builder->getAllocator()->isPointerMapped(oldPtr))
|
|
||||||
{
|
|
||||||
surf->triIndices = builder->getAllocator()->getPointer<unsigned short>(oldPtr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
builder->getAllocator()->mapPointer(oldPtr, surf->triIndices);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +64,7 @@ namespace Assets
|
|||||||
|
|
||||||
if (asset->surfs)
|
if (asset->surfs)
|
||||||
{
|
{
|
||||||
asset->surfs = reader->readArray<Game::XSurface>(asset->numsurfs);
|
asset->surfs = reader->readArrayOnce<Game::XSurface>(asset->numsurfs);
|
||||||
|
|
||||||
for (int i = 0; i < asset->numsurfs; ++i)
|
for (int i = 0; i < asset->numsurfs; ++i)
|
||||||
{
|
{
|
||||||
@ -85,7 +75,7 @@ namespace Assets
|
|||||||
|
|
||||||
void IXModel::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
void IXModel::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
Components::FileSystem::File modelFile(Utils::String::VA("xmodel/%s.iw4xModel", name.data()));
|
Components::FileSystem::File modelFile(std::format("xmodel/{}.iw4xModel", name));
|
||||||
|
|
||||||
if (!builder->isPrimaryAsset() && (!Components::ZoneBuilder::PreferDiskAssetsDvar.get<bool>() || !modelFile.exists()))
|
if (!builder->isPrimaryAsset() && (!Components::ZoneBuilder::PreferDiskAssetsDvar.get<bool>() || !modelFile.exists()))
|
||||||
{
|
{
|
||||||
@ -110,11 +100,6 @@ namespace Assets
|
|||||||
Components::Logger::Error(Game::ERR_FATAL, "Reading model '{}' failed, expected version is {}, but it was {}!", name, IW4X_MODEL_VERSION, version);
|
Components::Logger::Error(Game::ERR_FATAL, "Reading model '{}' failed, expected version is {}, but it was {}!", name, IW4X_MODEL_VERSION, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version == 4)
|
|
||||||
{
|
|
||||||
Components::Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "Model '{}' is in legacy format, please update it!\n", name);
|
|
||||||
}
|
|
||||||
|
|
||||||
Game::XModel* asset = reader.readObject<Game::XModel>();
|
Game::XModel* asset = reader.readObject<Game::XModel>();
|
||||||
|
|
||||||
if (asset->name)
|
if (asset->name)
|
||||||
@ -128,33 +113,33 @@ namespace Assets
|
|||||||
|
|
||||||
for (char i = 0; i < asset->numBones; ++i)
|
for (char i = 0; i < asset->numBones; ++i)
|
||||||
{
|
{
|
||||||
asset->boneNames[i] = Game::SL_GetString(reader.readCString(), 0);
|
asset->boneNames[i] = static_cast<std::uint16_t>(Game::SL_GetString(reader.readCString(), 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (asset->parentList)
|
if (asset->parentList)
|
||||||
{
|
{
|
||||||
asset->parentList = reader.readArray<char>(asset->numBones - asset->numRootBones);
|
asset->parentList = reader.readArrayOnce<unsigned char>(asset->numBones - asset->numRootBones);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (asset->quats)
|
if (asset->quats)
|
||||||
{
|
{
|
||||||
asset->quats = reader.readArray<short>((asset->numBones - asset->numRootBones) * 4);
|
asset->quats = reader.readArrayOnce<short>((asset->numBones - asset->numRootBones) * 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (asset->trans)
|
if (asset->trans)
|
||||||
{
|
{
|
||||||
asset->trans = reader.readArray<float>((asset->numBones - asset->numRootBones) * 3);
|
asset->trans = reader.readArrayOnce<float>((asset->numBones - asset->numRootBones) * 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (asset->partClassification)
|
if (asset->partClassification)
|
||||||
{
|
{
|
||||||
asset->partClassification = reader.readArray<char>(asset->numBones);
|
asset->partClassification = reader.readArrayOnce<unsigned char>(asset->numBones);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (asset->baseMat)
|
if (asset->baseMat)
|
||||||
{
|
{
|
||||||
asset->baseMat = reader.readArray<Game::DObjAnimMat>(asset->numBones);
|
asset->baseMat = reader.readArrayOnce<Game::DObjAnimMat>(asset->numBones);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (asset->materialHandles)
|
if (asset->materialHandles)
|
||||||
@ -172,7 +157,7 @@ namespace Assets
|
|||||||
|
|
||||||
// Save_XModelLodInfoArray
|
// Save_XModelLodInfoArray
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 4; ++i)
|
for (unsigned int i = 0; i < 4; ++i)
|
||||||
{
|
{
|
||||||
if (asset->lodInfo[i].modelSurfs)
|
if (asset->lodInfo[i].modelSurfs)
|
||||||
{
|
{
|
||||||
@ -246,12 +231,6 @@ namespace Assets
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (asset->physCollmap)
|
if (asset->physCollmap)
|
||||||
{
|
|
||||||
if (version == 4)
|
|
||||||
{
|
|
||||||
asset->physCollmap = nullptr;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
Game::PhysCollmap* collmap = reader.readObject<Game::PhysCollmap>();
|
Game::PhysCollmap* collmap = reader.readObject<Game::PhysCollmap>();
|
||||||
asset->physCollmap = collmap;
|
asset->physCollmap = collmap;
|
||||||
@ -302,7 +281,6 @@ namespace Assets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_PHYSCOLLMAP, { asset->physCollmap });
|
Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_PHYSCOLLMAP, { asset->physCollmap });
|
||||||
// asset->physCollmap = nullptr;
|
// asset->physCollmap = nullptr;
|
||||||
@ -386,7 +364,7 @@ namespace Assets
|
|||||||
|
|
||||||
for (char i = 0; i < asset->numBones; ++i)
|
for (char i = 0; i < asset->numBones; ++i)
|
||||||
{
|
{
|
||||||
builder->mapScriptString(&destBoneNames[i]);
|
builder->mapScriptString(destBoneNames[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::Stream::ClearPointer(&dest->boneNames);
|
Utils::Stream::ClearPointer(&dest->boneNames);
|
||||||
@ -394,39 +372,79 @@ namespace Assets
|
|||||||
|
|
||||||
if (asset->parentList)
|
if (asset->parentList)
|
||||||
{
|
{
|
||||||
|
if (builder->hasPointer(asset->parentList))
|
||||||
|
{
|
||||||
|
dest->parentList = builder->getPointer(asset->parentList);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder->storePointer(asset->parentList);
|
||||||
buffer->save(asset->parentList, asset->numBones - asset->numRootBones);
|
buffer->save(asset->parentList, asset->numBones - asset->numRootBones);
|
||||||
Utils::Stream::ClearPointer(&dest->parentList);
|
Utils::Stream::ClearPointer(&dest->parentList);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (asset->quats)
|
if (asset->quats)
|
||||||
|
{
|
||||||
|
if (builder->hasPointer(asset->quats))
|
||||||
|
{
|
||||||
|
dest->quats = builder->getPointer(asset->quats);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
buffer->align(Utils::Stream::ALIGN_2);
|
buffer->align(Utils::Stream::ALIGN_2);
|
||||||
|
builder->storePointer(asset->quats);
|
||||||
buffer->saveArray(asset->quats, (asset->numBones - asset->numRootBones) * 4);
|
buffer->saveArray(asset->quats, (asset->numBones - asset->numRootBones) * 4);
|
||||||
Utils::Stream::ClearPointer(&dest->quats);
|
Utils::Stream::ClearPointer(&dest->quats);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (asset->trans)
|
if (asset->trans)
|
||||||
|
{
|
||||||
|
if (builder->hasPointer(asset->trans))
|
||||||
|
{
|
||||||
|
dest->trans = builder->getPointer(asset->trans);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
buffer->align(Utils::Stream::ALIGN_4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
|
builder->storePointer(asset->trans);
|
||||||
buffer->saveArray(asset->trans, (asset->numBones - asset->numRootBones) * 3);
|
buffer->saveArray(asset->trans, (asset->numBones - asset->numRootBones) * 3);
|
||||||
Utils::Stream::ClearPointer(&dest->trans);
|
Utils::Stream::ClearPointer(&dest->trans);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (asset->partClassification)
|
if (asset->partClassification)
|
||||||
{
|
{
|
||||||
|
if (builder->hasPointer(asset->partClassification))
|
||||||
|
{
|
||||||
|
dest->partClassification = builder->getPointer(asset->partClassification);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder->storePointer(asset->partClassification);
|
||||||
buffer->save(asset->partClassification, asset->numBones);
|
buffer->save(asset->partClassification, asset->numBones);
|
||||||
Utils::Stream::ClearPointer(&dest->partClassification);
|
Utils::Stream::ClearPointer(&dest->partClassification);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (asset->baseMat)
|
if (asset->baseMat)
|
||||||
{
|
{
|
||||||
AssertSize(Game::DObjAnimMat, 32);
|
AssertSize(Game::DObjAnimMat, 32);
|
||||||
|
if (builder->hasPointer(asset->baseMat))
|
||||||
|
{
|
||||||
|
dest->baseMat = builder->getPointer(asset->baseMat);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
buffer->align(Utils::Stream::ALIGN_4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
|
builder->storePointer(asset->baseMat);
|
||||||
buffer->saveArray(asset->baseMat, asset->numBones);
|
buffer->saveArray(asset->baseMat, asset->numBones);
|
||||||
Utils::Stream::ClearPointer(&dest->baseMat);
|
Utils::Stream::ClearPointer(&dest->baseMat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if (asset->materialHandles)
|
if (asset->materialHandles)
|
||||||
{
|
{
|
||||||
buffer->align(Utils::Stream::ALIGN_4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
|
@ -37,10 +37,19 @@ namespace Assets
|
|||||||
|
|
||||||
if (surf->vertInfo.vertsBlend)
|
if (surf->vertInfo.vertsBlend)
|
||||||
{
|
{
|
||||||
|
if (builder->hasPointer(surf->vertInfo.vertsBlend))
|
||||||
|
{
|
||||||
|
destSurf->vertInfo.vertsBlend = builder->getPointer(surf->vertInfo.vertsBlend);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
buffer->align(Utils::Stream::ALIGN_2);
|
buffer->align(Utils::Stream::ALIGN_2);
|
||||||
|
builder->storePointer(surf->vertInfo.vertsBlend);
|
||||||
buffer->saveArray(surf->vertInfo.vertsBlend, surf->vertInfo.vertCount[0] + (surf->vertInfo.vertCount[1] * 3) + (surf->vertInfo.vertCount[2] * 5) + (surf->vertInfo.vertCount[3] * 7));
|
buffer->saveArray(surf->vertInfo.vertsBlend, surf->vertInfo.vertCount[0] + (surf->vertInfo.vertCount[1] * 3) + (surf->vertInfo.vertCount[2] * 5) + (surf->vertInfo.vertCount[3] * 7));
|
||||||
Utils::Stream::ClearPointer(&destSurf->vertInfo.vertsBlend);
|
Utils::Stream::ClearPointer(&destSurf->vertInfo.vertsBlend);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Access vertex block
|
// Access vertex block
|
||||||
buffer->pushBlock(Game::XFILE_BLOCK_VERTEX);
|
buffer->pushBlock(Game::XFILE_BLOCK_VERTEX);
|
||||||
@ -48,10 +57,18 @@ namespace Assets
|
|||||||
{
|
{
|
||||||
AssertSize(Game::GfxPackedVertex, 32);
|
AssertSize(Game::GfxPackedVertex, 32);
|
||||||
|
|
||||||
|
if (builder->hasPointer(surf->verts0))
|
||||||
|
{
|
||||||
|
destSurf->verts0 = builder->getPointer(surf->verts0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
buffer->align(Utils::Stream::ALIGN_16);
|
buffer->align(Utils::Stream::ALIGN_16);
|
||||||
|
builder->storePointer(surf->verts0);
|
||||||
buffer->saveArray(surf->verts0, surf->vertCount);
|
buffer->saveArray(surf->verts0, surf->vertCount);
|
||||||
Utils::Stream::ClearPointer(&destSurf->verts0);
|
Utils::Stream::ClearPointer(&destSurf->verts0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
buffer->popBlock();
|
buffer->popBlock();
|
||||||
|
|
||||||
// Save_XRigidVertListArray
|
// Save_XRigidVertListArray
|
||||||
@ -59,7 +76,14 @@ namespace Assets
|
|||||||
{
|
{
|
||||||
AssertSize(Game::XRigidVertList, 12);
|
AssertSize(Game::XRigidVertList, 12);
|
||||||
|
|
||||||
|
if (builder->hasPointer(surf->vertList))
|
||||||
|
{
|
||||||
|
destSurf->vertList = builder->getPointer(surf->vertList);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
buffer->align(Utils::Stream::ALIGN_4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
|
builder->storePointer(surf->vertList);
|
||||||
|
|
||||||
Game::XRigidVertList* destCt = buffer->dest<Game::XRigidVertList>();
|
Game::XRigidVertList* destCt = buffer->dest<Game::XRigidVertList>();
|
||||||
buffer->saveArray(surf->vertList, surf->vertListCount);
|
buffer->saveArray(surf->vertList, surf->vertListCount);
|
||||||
@ -71,14 +95,22 @@ namespace Assets
|
|||||||
|
|
||||||
if (rigidVertList->collisionTree)
|
if (rigidVertList->collisionTree)
|
||||||
{
|
{
|
||||||
|
if (builder->hasPointer(rigidVertList->collisionTree))
|
||||||
|
{
|
||||||
|
destRigidVertList->collisionTree = builder->getPointer(rigidVertList->collisionTree);
|
||||||
|
}
|
||||||
|
else {
|
||||||
buffer->align(Utils::Stream::ALIGN_4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
|
builder->storePointer(rigidVertList->collisionTree);
|
||||||
this->saveXSurfaceCollisionTree(rigidVertList->collisionTree, builder);
|
this->saveXSurfaceCollisionTree(rigidVertList->collisionTree, builder);
|
||||||
Utils::Stream::ClearPointer(&destRigidVertList->collisionTree);
|
Utils::Stream::ClearPointer(&destRigidVertList->collisionTree);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Utils::Stream::ClearPointer(&destSurf->vertList);
|
Utils::Stream::ClearPointer(&destSurf->vertList);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Access index block
|
// Access index block
|
||||||
buffer->pushBlock(Game::XFILE_BLOCK_INDEX);
|
buffer->pushBlock(Game::XFILE_BLOCK_INDEX);
|
||||||
@ -89,6 +121,7 @@ namespace Assets
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
buffer->align(Utils::Stream::ALIGN_16);
|
buffer->align(Utils::Stream::ALIGN_16);
|
||||||
|
builder->storePointer(surf->triIndices);
|
||||||
buffer->saveArray(surf->triIndices, surf->triCount * 3);
|
buffer->saveArray(surf->triIndices, surf->triCount * 3);
|
||||||
Utils::Stream::ClearPointer(&destSurf->triIndices);
|
Utils::Stream::ClearPointer(&destSurf->triIndices);
|
||||||
}
|
}
|
||||||
|
@ -573,7 +573,7 @@ namespace Assets
|
|||||||
Utils::String::Replace(name, "maps/mp/", "");
|
Utils::String::Replace(name, "maps/mp/", "");
|
||||||
Utils::String::Replace(name, ".d3dbsp", "");
|
Utils::String::Replace(name, ".d3dbsp", "");
|
||||||
|
|
||||||
Components::FileSystem::File clipFile(Utils::String::VA("clipmap/%s.iw4xClipMap", name.data()));
|
Components::FileSystem::File clipFile(std::format("clipmap/{}.iw4xClipMap", name));
|
||||||
if (!clipFile.exists())
|
if (!clipFile.exists())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -882,7 +882,7 @@ namespace Assets
|
|||||||
clipMap->smodelNodeCount = reader.read<unsigned short>();
|
clipMap->smodelNodeCount = reader.read<unsigned short>();
|
||||||
clipMap->smodelNodes = reader.readArray<Game::SModelAabbNode>(clipMap->smodelNodeCount);
|
clipMap->smodelNodes = reader.readArray<Game::SModelAabbNode>(clipMap->smodelNodeCount);
|
||||||
|
|
||||||
clipMap->mapEnts = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MAP_ENTS, Utils::String::VA("maps/mp/%s.d3dbsp", name.data()), builder).mapEnts;
|
clipMap->mapEnts = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MAP_ENTS, std::format("maps/mp/{}.d3dbsp", name), builder).mapEnts;
|
||||||
|
|
||||||
// add triggers to mapEnts
|
// add triggers to mapEnts
|
||||||
if (version >= 2) {
|
if (version >= 2) {
|
||||||
|
@ -9,9 +9,9 @@ namespace Assets
|
|||||||
void ImenuDef_t::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
void ImenuDef_t::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
||||||
{
|
{
|
||||||
// load from disk
|
// load from disk
|
||||||
auto menus = Components::Menus::LoadMenu(Utils::String::VA("ui_mp/%s.menu", name.data()));
|
auto menus = Components::Menus::LoadMenu(std::format("ui_mp/{}.menu", name));
|
||||||
|
|
||||||
if (menus.size() == 0) return;
|
if (menus.empty()) return;
|
||||||
if (menus.size() > 1) Components::Logger::Print("Menu '{}' on disk has more than one menudef in it. Only saving the first one\n", name);
|
if (menus.size() > 1) Components::Logger::Print("Menu '{}' on disk has more than one menudef in it. Only saving the first one\n", name);
|
||||||
|
|
||||||
header->menu = menus[0].second;
|
header->menu = menus[0].second;
|
||||||
@ -20,7 +20,7 @@ namespace Assets
|
|||||||
|
|
||||||
void ImenuDef_t::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
void ImenuDef_t::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
Game::menuDef_t *asset = header.menu;
|
auto* asset = header.menu;
|
||||||
|
|
||||||
if (asset->window.background)
|
if (asset->window.background)
|
||||||
{
|
{
|
||||||
@ -59,14 +59,14 @@ namespace Assets
|
|||||||
|
|
||||||
buffer->align(Utils::Stream::ALIGN_4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
|
|
||||||
Game::ExpressionSupportingData *dest = buffer->dest<Game::ExpressionSupportingData>();
|
auto* dest = buffer->dest<Game::ExpressionSupportingData>();
|
||||||
buffer->save(asset);
|
buffer->save(asset);
|
||||||
|
|
||||||
if (asset->uifunctions.functions)
|
if (asset->uifunctions.functions)
|
||||||
{
|
{
|
||||||
buffer->align(Utils::Stream::ALIGN_4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
|
|
||||||
Game::Statement_s **destStatement = buffer->dest<Game::Statement_s*>();
|
auto** destStatement = buffer->dest<Game::Statement_s*>();
|
||||||
buffer->saveArray(asset->uifunctions.functions, asset->uifunctions.totalFunctions);
|
buffer->saveArray(asset->uifunctions.functions, asset->uifunctions.totalFunctions);
|
||||||
|
|
||||||
for (int i = 0; i < asset->uifunctions.totalFunctions; ++i)
|
for (int i = 0; i < asset->uifunctions.totalFunctions; ++i)
|
||||||
@ -87,17 +87,17 @@ namespace Assets
|
|||||||
{
|
{
|
||||||
buffer->align(Utils::Stream::ALIGN_4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
|
|
||||||
Game::StaticDvar **destStaticDvars = buffer->dest<Game::StaticDvar*>();
|
auto** destStaticDvars = buffer->dest<Game::StaticDvar*>();
|
||||||
buffer->saveArray(asset->staticDvarList.staticDvars, asset->staticDvarList.numStaticDvars);
|
buffer->saveArray(asset->staticDvarList.staticDvars, asset->staticDvarList.numStaticDvars);
|
||||||
|
|
||||||
for (int i = 0; i < asset->staticDvarList.numStaticDvars; ++i)
|
for (auto i = 0; i < asset->staticDvarList.numStaticDvars; ++i)
|
||||||
{
|
{
|
||||||
if (asset->staticDvarList.staticDvars[i])
|
if (asset->staticDvarList.staticDvars[i])
|
||||||
{
|
{
|
||||||
Utils::Stream::ClearPointer(&destStaticDvars[i]);
|
Utils::Stream::ClearPointer(&destStaticDvars[i]);
|
||||||
|
|
||||||
buffer->align(Utils::Stream::ALIGN_4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
Game::StaticDvar *destStaticDvar = buffer->dest<Game::StaticDvar>();
|
auto* destStaticDvar = buffer->dest<Game::StaticDvar>();
|
||||||
buffer->save(asset->staticDvarList.staticDvars[i]);
|
buffer->save(asset->staticDvarList.staticDvars[i]);
|
||||||
|
|
||||||
if (asset->staticDvarList.staticDvars[i]->dvarName)
|
if (asset->staticDvarList.staticDvars[i]->dvarName)
|
||||||
@ -115,7 +115,7 @@ namespace Assets
|
|||||||
{
|
{
|
||||||
buffer->align(Utils::Stream::ALIGN_4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
|
|
||||||
const char **destuiStrings = buffer->dest<const char*>();
|
const auto** destUIStrings = buffer->dest<const char*>();
|
||||||
buffer->saveArray(asset->uiStrings.strings, asset->uiStrings.totalStrings);
|
buffer->saveArray(asset->uiStrings.strings, asset->uiStrings.totalStrings);
|
||||||
|
|
||||||
for (int i = 0; i < asset->uiStrings.totalStrings; ++i)
|
for (int i = 0; i < asset->uiStrings.totalStrings; ++i)
|
||||||
@ -123,7 +123,7 @@ namespace Assets
|
|||||||
if (asset->uiStrings.strings[i])
|
if (asset->uiStrings.strings[i])
|
||||||
{
|
{
|
||||||
buffer->saveString(asset->uiStrings.strings[i]);
|
buffer->saveString(asset->uiStrings.strings[i]);
|
||||||
Utils::Stream::ClearPointer(&destuiStrings[i]);
|
Utils::Stream::ClearPointer(&destUIStrings[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,7 +143,7 @@ namespace Assets
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Write header data
|
// Write header data
|
||||||
Game::Statement_s *dest = buffer->dest<Game::Statement_s>();
|
auto* dest = buffer->dest<Game::Statement_s>();
|
||||||
buffer->save(asset);
|
buffer->save(asset);
|
||||||
|
|
||||||
// Write statement entries
|
// Write statement entries
|
||||||
@ -155,7 +155,7 @@ namespace Assets
|
|||||||
buffer->align(Utils::Stream::ALIGN_4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
|
|
||||||
// Write entries
|
// Write entries
|
||||||
Game::expressionEntry *destEntries = buffer->dest<Game::expressionEntry>();
|
auto* destEntries = buffer->dest<Game::expressionEntry>();
|
||||||
buffer->save(asset->entries, sizeof(Game::expressionEntry), asset->numEntries);
|
buffer->save(asset->entries, sizeof(Game::expressionEntry), asset->numEntries);
|
||||||
|
|
||||||
// Loop through entries
|
// Loop through entries
|
||||||
@ -222,7 +222,7 @@ namespace Assets
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Write header data
|
// Write header data
|
||||||
Game::MenuEventHandlerSet *destset = buffer->dest<Game::MenuEventHandlerSet>();
|
auto* destset = buffer->dest<Game::MenuEventHandlerSet>();
|
||||||
buffer->save(asset);
|
buffer->save(asset);
|
||||||
|
|
||||||
// Event handlers
|
// Event handlers
|
||||||
@ -234,7 +234,7 @@ namespace Assets
|
|||||||
buffer->save(asset->eventHandlers, sizeof(Game::MenuEventHandler*), asset->eventHandlerCount);
|
buffer->save(asset->eventHandlers, sizeof(Game::MenuEventHandler*), asset->eventHandlerCount);
|
||||||
|
|
||||||
// Loop through eventHandlers
|
// Loop through eventHandlers
|
||||||
for (int i = 0; i < asset->eventHandlerCount; ++i)
|
for (auto i = 0; i < asset->eventHandlerCount; ++i)
|
||||||
{
|
{
|
||||||
if (asset->eventHandlers[i])
|
if (asset->eventHandlers[i])
|
||||||
{
|
{
|
||||||
@ -244,7 +244,7 @@ namespace Assets
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Write menu event handler
|
// Write menu event handler
|
||||||
Game::MenuEventHandler *dest = buffer->dest<Game::MenuEventHandler>();
|
auto* dest = buffer->dest<Game::MenuEventHandler>();
|
||||||
buffer->save(asset->eventHandlers[i]);
|
buffer->save(asset->eventHandlers[i]);
|
||||||
|
|
||||||
// Write additional data based on type
|
// Write additional data based on type
|
||||||
@ -264,7 +264,7 @@ namespace Assets
|
|||||||
if (asset->eventHandlers[i]->eventData.conditionalScript)
|
if (asset->eventHandlers[i]->eventData.conditionalScript)
|
||||||
{
|
{
|
||||||
buffer->align(Utils::Stream::ALIGN_4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
Game::ConditionalScript *destConditionalScript = buffer->dest<Game::ConditionalScript>();
|
auto* destConditionalScript = buffer->dest<Game::ConditionalScript>();
|
||||||
buffer->save(asset->eventHandlers[i]->eventData.conditionalScript);
|
buffer->save(asset->eventHandlers[i]->eventData.conditionalScript);
|
||||||
|
|
||||||
// eventExpression
|
// eventExpression
|
||||||
@ -307,7 +307,7 @@ namespace Assets
|
|||||||
buffer->align(Utils::Stream::ALIGN_4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
|
|
||||||
// header data
|
// header data
|
||||||
Game::SetLocalVarData *destLocalVarData = buffer->dest<Game::SetLocalVarData>();
|
auto* destLocalVarData = buffer->dest<Game::SetLocalVarData>();
|
||||||
buffer->save(asset->eventHandlers[i]->eventData.setLocalVarData);
|
buffer->save(asset->eventHandlers[i]->eventData.setLocalVarData);
|
||||||
|
|
||||||
// localVarName
|
// localVarName
|
||||||
@ -354,7 +354,7 @@ namespace Assets
|
|||||||
while (asset)
|
while (asset)
|
||||||
{
|
{
|
||||||
// Write header
|
// Write header
|
||||||
Game::ItemKeyHandler* dest = buffer->dest<Game::ItemKeyHandler>();
|
auto* dest = buffer->dest<Game::ItemKeyHandler>();
|
||||||
buffer->save(asset);
|
buffer->save(asset);
|
||||||
|
|
||||||
// MenuEventHandlerSet
|
// MenuEventHandlerSet
|
||||||
@ -367,7 +367,7 @@ namespace Assets
|
|||||||
|
|
||||||
if (asset->next)
|
if (asset->next)
|
||||||
{
|
{
|
||||||
// align every indice, besides the first one?
|
// align every index, besides the first one?
|
||||||
buffer->align(Utils::Stream::ALIGN_4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,20 +379,20 @@ namespace Assets
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#define EVENTHANDLERSET(__indice) \
|
#define EVENTHANDLERSET(__index) \
|
||||||
if (asset->__indice) \
|
if (asset->__index) \
|
||||||
{ \
|
{ \
|
||||||
buffer->align(Utils::Stream::ALIGN_4); \
|
buffer->align(Utils::Stream::ALIGN_4); \
|
||||||
this->save_MenuEventHandlerSet(asset->__indice, builder); \
|
this->save_MenuEventHandlerSet(asset->__index, builder); \
|
||||||
Utils::Stream::ClearPointer(&dest->__indice); \
|
Utils::Stream::ClearPointer(&dest->__index); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define STATEMENT(__indice) \
|
#define STATEMENT(__index) \
|
||||||
if (asset->__indice) \
|
if (asset->__index) \
|
||||||
{ \
|
{ \
|
||||||
buffer->align(Utils::Stream::ALIGN_4); \
|
buffer->align(Utils::Stream::ALIGN_4); \
|
||||||
this->save_Statement_s(asset->__indice, builder); \
|
this->save_Statement_s(asset->__index, builder); \
|
||||||
Utils::Stream::ClearPointer(&dest->__indice); \
|
Utils::Stream::ClearPointer(&dest->__index); \
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImenuDef_t::save_itemDefData_t(Game::itemDefData_t* asset, int type, Game::itemDef_s* dest, Components::ZoneBuilder::Zone* builder)
|
void ImenuDef_t::save_itemDefData_t(Game::itemDefData_t* asset, int type, Game::itemDef_s* dest, Components::ZoneBuilder::Zone* builder)
|
||||||
@ -412,7 +412,7 @@ namespace Assets
|
|||||||
if (type == 6)
|
if (type == 6)
|
||||||
{
|
{
|
||||||
buffer->align(Utils::Stream::ALIGN_4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
Game::listBoxDef_s* destlb = buffer->dest<Game::listBoxDef_s>();
|
auto* destlb = buffer->dest<Game::listBoxDef_s>();
|
||||||
buffer->save(asset->listBox);
|
buffer->save(asset->listBox);
|
||||||
|
|
||||||
if (asset->listBox->onDoubleClick)
|
if (asset->listBox->onDoubleClick)
|
||||||
@ -427,17 +427,7 @@ namespace Assets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// HexRays spaghetti
|
// HexRays spaghetti
|
||||||
else if (type != 4
|
else if (type != 4 && type != 9 && type != 16 && type != 18 && type != 11 && type != 14 && type != 10 && type != 17 && type != 22 && type != 23 && type != 0)
|
||||||
&& type != 9
|
|
||||||
&& type != 16
|
|
||||||
&& type != 18
|
|
||||||
&& type != 11
|
|
||||||
&& type != 14
|
|
||||||
&& type != 10
|
|
||||||
&& type != 17
|
|
||||||
&& type != 22
|
|
||||||
&& type != 23
|
|
||||||
&& type != 0)
|
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
@ -457,7 +447,7 @@ namespace Assets
|
|||||||
break;
|
break;
|
||||||
case 12:
|
case 12:
|
||||||
buffer->align(Utils::Stream::ALIGN_4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
Game::multiDef_s* destdef = buffer->dest<Game::multiDef_s>();
|
auto* destdef = buffer->dest<Game::multiDef_s>();
|
||||||
buffer->save(asset->multi);
|
buffer->save(asset->multi);
|
||||||
|
|
||||||
for (int i = 0; i < 32; ++i)
|
for (int i = 0; i < 32; ++i)
|
||||||
@ -497,10 +487,10 @@ namespace Assets
|
|||||||
|
|
||||||
void ImenuDef_t::save_itemDef_s(Game::itemDef_s *asset, Components::ZoneBuilder::Zone* builder)
|
void ImenuDef_t::save_itemDef_s(Game::itemDef_s *asset, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
AssertSize(Game::itemDef_s, 380);
|
AssertSize(Game::itemDef_s, 0x17C);
|
||||||
|
|
||||||
Utils::Stream* buffer = builder->getBuffer();
|
Utils::Stream* buffer = builder->getBuffer();
|
||||||
Game::itemDef_s* dest = buffer->dest<Game::itemDef_s>();
|
auto* dest = buffer->dest<Game::itemDef_s>();
|
||||||
|
|
||||||
#ifdef WRITE_LOGS
|
#ifdef WRITE_LOGS
|
||||||
if (asset->window.name)
|
if (asset->window.name)
|
||||||
@ -587,7 +577,7 @@ namespace Assets
|
|||||||
buffer->enterStruct("floatExpressions");
|
buffer->enterStruct("floatExpressions");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Game::ItemFloatExpression* destExp = buffer->dest<Game::ItemFloatExpression>();
|
auto* destExp = buffer->dest<Game::ItemFloatExpression>();
|
||||||
buffer->saveArray(asset->floatExpressions, asset->floatExpressionCount);
|
buffer->saveArray(asset->floatExpressions, asset->floatExpressionCount);
|
||||||
|
|
||||||
for (int i = 0; i < asset->floatExpressionCount; ++i)
|
for (int i = 0; i < asset->floatExpressionCount; ++i)
|
||||||
@ -618,14 +608,15 @@ namespace Assets
|
|||||||
void ImenuDef_t::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
void ImenuDef_t::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
AssertSize(Game::menuDef_t, 400);
|
AssertSize(Game::menuDef_t, 400);
|
||||||
|
AssertSize(Game::windowDef_t, 0xA4);
|
||||||
|
|
||||||
#ifdef WRITE_LOGS
|
#ifdef WRITE_LOGS
|
||||||
buffer->enterStruct("ImenuDef_t");
|
buffer->enterStruct("ImenuDef_t");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Utils::Stream* buffer = builder->getBuffer();
|
Utils::Stream* buffer = builder->getBuffer();
|
||||||
Game::menuDef_t* asset = header.menu;
|
auto* asset = header.menu;
|
||||||
Game::menuDef_t* dest = buffer->dest<Game::menuDef_t>();
|
auto* dest = buffer->dest<Game::menuDef_t>();
|
||||||
buffer->save(asset);
|
buffer->save(asset);
|
||||||
|
|
||||||
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
|
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
|
||||||
|
@ -5,7 +5,7 @@ namespace Assets
|
|||||||
{
|
{
|
||||||
void Isnd_alias_list_t::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
void Isnd_alias_list_t::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
Components::FileSystem::File aliasFile(Utils::String::VA("sounds/%s.json", name.data()));
|
Components::FileSystem::File aliasFile(std::format("sounds/{}.json", name));
|
||||||
if (!aliasFile.exists())
|
if (!aliasFile.exists())
|
||||||
{
|
{
|
||||||
header->sound = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).sound;
|
header->sound = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).sound;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "Bans.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "Bans.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
|
@ -1,16 +1,20 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "Bots.hpp"
|
||||||
|
|
||||||
#include "GSC/Script.hpp"
|
#include "GSC/Script.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
std::vector<std::string> Bots::BotNames;
|
std::vector<Bots::botData> Bots::BotNames;
|
||||||
|
|
||||||
|
Dvar::Var Bots::SVRandomBotNames;
|
||||||
|
|
||||||
struct BotMovementInfo
|
struct BotMovementInfo
|
||||||
{
|
{
|
||||||
int buttons; // Actions
|
std::int32_t buttons; // Actions
|
||||||
int8_t forward;
|
std::int8_t forward;
|
||||||
int8_t right;
|
std::int8_t right;
|
||||||
uint16_t weapon;
|
std::uint16_t weapon;
|
||||||
bool active;
|
bool active;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -19,67 +23,94 @@ namespace Components
|
|||||||
struct BotAction
|
struct BotAction
|
||||||
{
|
{
|
||||||
std::string action;
|
std::string action;
|
||||||
int key;
|
std::int32_t key;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const BotAction BotActions[] =
|
static const BotAction BotActions[] =
|
||||||
{
|
{
|
||||||
{ "gostand", Game::usercmdButtonBits::CMD_BUTTON_UP },
|
{ "gostand", Game::CMD_BUTTON_UP },
|
||||||
{ "gocrouch", Game::usercmdButtonBits::CMD_BUTTON_CROUCH },
|
{ "gocrouch", Game::CMD_BUTTON_CROUCH },
|
||||||
{ "goprone", Game::usercmdButtonBits::CMD_BUTTON_PRONE },
|
{ "goprone", Game::CMD_BUTTON_PRONE },
|
||||||
{ "fire", Game::usercmdButtonBits::CMD_BUTTON_ATTACK },
|
{ "fire", Game::CMD_BUTTON_ATTACK },
|
||||||
{ "melee", Game::usercmdButtonBits::CMD_BUTTON_MELEE },
|
{ "melee", Game::CMD_BUTTON_MELEE },
|
||||||
{ "frag", Game::usercmdButtonBits::CMD_BUTTON_FRAG },
|
{ "frag", Game::CMD_BUTTON_FRAG },
|
||||||
{ "smoke", Game::usercmdButtonBits::CMD_BUTTON_OFFHAND_SECONDARY },
|
{ "smoke", Game::CMD_BUTTON_OFFHAND_SECONDARY },
|
||||||
{ "reload", Game::usercmdButtonBits::CMD_BUTTON_RELOAD },
|
{ "reload", Game::CMD_BUTTON_RELOAD },
|
||||||
{ "sprint", Game::usercmdButtonBits::CMD_BUTTON_SPRINT },
|
{ "sprint", Game::CMD_BUTTON_SPRINT },
|
||||||
{ "leanleft", Game::usercmdButtonBits::CMD_BUTTON_LEAN_LEFT },
|
{ "leanleft", Game::CMD_BUTTON_LEAN_LEFT },
|
||||||
{ "leanright", Game::usercmdButtonBits::CMD_BUTTON_LEAN_RIGHT },
|
{ "leanright", Game::CMD_BUTTON_LEAN_RIGHT },
|
||||||
{ "ads", Game::usercmdButtonBits::CMD_BUTTON_ADS },
|
{ "ads", Game::CMD_BUTTON_ADS },
|
||||||
{ "holdbreath", Game::usercmdButtonBits::CMD_BUTTON_BREATH },
|
{ "holdbreath", Game::CMD_BUTTON_BREATH },
|
||||||
{ "usereload", Game::usercmdButtonBits::CMD_BUTTON_USE_RELOAD },
|
{ "usereload", Game::CMD_BUTTON_USE_RELOAD },
|
||||||
{ "activate", Game::usercmdButtonBits::CMD_BUTTON_ACTIVATE },
|
{ "activate", Game::CMD_BUTTON_ACTIVATE },
|
||||||
};
|
};
|
||||||
|
|
||||||
int Bots::BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port)
|
int Bots::BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port)
|
||||||
{
|
{
|
||||||
static size_t botId = 0;
|
static size_t botId = 0; // Loop over the BotNames vector
|
||||||
static bool loadedNames = false; // Load file only once
|
static bool loadedNames = false; // Load file only once
|
||||||
const char* botName;
|
const char* botName;
|
||||||
|
const char* clanName;
|
||||||
|
|
||||||
if (Bots::BotNames.empty() && !loadedNames)
|
if (BotNames.empty() && !loadedNames)
|
||||||
{
|
{
|
||||||
FileSystem::File bots("bots.txt");
|
FileSystem::File bots("bots.txt");
|
||||||
loadedNames = true;
|
loadedNames = true;
|
||||||
|
|
||||||
if (bots.exists())
|
if (bots.exists())
|
||||||
{
|
{
|
||||||
auto names = Utils::String::Split(bots.getBuffer(), '\n');
|
auto data = Utils::String::Split(bots.getBuffer(), '\n');
|
||||||
|
|
||||||
for (auto& name : names)
|
if (SVRandomBotNames.get<bool>())
|
||||||
{
|
{
|
||||||
Utils::String::Replace(name, "\r", "");
|
std::random_device rd;
|
||||||
name = Utils::String::Trim(name);
|
std::mt19937 gen(rd());
|
||||||
|
std::ranges::shuffle(data, gen);
|
||||||
|
}
|
||||||
|
|
||||||
if (!name.empty())
|
for (auto& entry : data)
|
||||||
{
|
{
|
||||||
Bots::BotNames.push_back(name);
|
// Take into account for CR line endings
|
||||||
|
Utils::String::Replace(entry, "\r", "");
|
||||||
|
// Remove whitespace
|
||||||
|
Utils::String::Trim(entry);
|
||||||
|
|
||||||
|
if (!entry.empty())
|
||||||
|
{
|
||||||
|
std::string clanAbbrev;
|
||||||
|
|
||||||
|
// Check if there is a clan tag
|
||||||
|
if (const auto pos = entry.find(','); pos != std::string::npos)
|
||||||
|
{
|
||||||
|
// Only start copying over from non-null characters (otherwise it can be "<=")
|
||||||
|
if ((pos + 1) < entry.size())
|
||||||
|
{
|
||||||
|
clanAbbrev = entry.substr(pos + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = entry.substr(0, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
BotNames.emplace_back(std::make_pair(entry, clanAbbrev));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Bots::BotNames.empty())
|
if (!BotNames.empty())
|
||||||
{
|
{
|
||||||
botId %= Bots::BotNames.size();
|
botId %= BotNames.size();
|
||||||
botName = Bots::BotNames[botId++].data();
|
const auto index = botId++;
|
||||||
|
botName = BotNames[index].first.data();
|
||||||
|
clanName = BotNames[index].second.data();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
botName = Utils::String::VA("bot%d", ++botId);
|
botName = Utils::String::VA("bot%d", ++botId);
|
||||||
|
clanName = "BOT";
|
||||||
}
|
}
|
||||||
|
|
||||||
return _snprintf_s(buffer, 0x400, _TRUNCATE, connectString, num, botName, protocol, checksum, statVer, statStuff, port);
|
return _snprintf_s(buffer, 0x400, _TRUNCATE, connectString, num, botName, clanName, protocol, checksum, statVer, statStuff, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bots::Spawn(unsigned int count)
|
void Bots::Spawn(unsigned int count)
|
||||||
@ -96,13 +127,13 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Game::Scr_AddString("autoassign");
|
Game::Scr_AddString("autoassign");
|
||||||
Game::Scr_AddString("team_marinesopfor");
|
Game::Scr_AddString("team_marinesopfor");
|
||||||
Game::Scr_Notify(ent, Game::SL_GetString("menuresponse", 0), 2);
|
Game::Scr_Notify(ent, static_cast<std::uint16_t>(Game::SL_GetString("menuresponse", 0)), 2);
|
||||||
|
|
||||||
Scheduler::Once([ent]
|
Scheduler::Once([ent]
|
||||||
{
|
{
|
||||||
Game::Scr_AddString(Utils::String::VA("class%u", Utils::Cryptography::Rand::GenerateInt() % 5u));
|
Game::Scr_AddString(Utils::String::VA("class%u", Utils::Cryptography::Rand::GenerateInt() % 5u));
|
||||||
Game::Scr_AddString("changeclass");
|
Game::Scr_AddString("changeclass");
|
||||||
Game::Scr_Notify(ent, Game::SL_GetString("menuresponse", 0), 2);
|
Game::Scr_Notify(ent, static_cast<std::uint16_t>(Game::SL_GetString("menuresponse", 0)), 2);
|
||||||
}, Scheduler::Pipeline::SERVER, 1s);
|
}, Scheduler::Pipeline::SERVER, 1s);
|
||||||
|
|
||||||
}, Scheduler::Pipeline::SERVER, 1s);
|
}, Scheduler::Pipeline::SERVER, 1s);
|
||||||
@ -111,16 +142,21 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bots::GScr_isTestClient(Game::scr_entref_t entref)
|
void Bots::GScr_isTestClient(const Game::scr_entref_t entref)
|
||||||
{
|
{
|
||||||
const auto* ent = Game::GetPlayerEntity(entref);
|
const auto* ent = Game::GetEntity(entref);
|
||||||
|
if (!ent->client)
|
||||||
|
{
|
||||||
|
Game::Scr_Error("isTestClient: entity must be a player entity");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Game::Scr_AddBool(Game::SV_IsTestClient(ent->s.number) != 0);
|
Game::Scr_AddBool(Game::SV_IsTestClient(ent->s.number) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bots::AddMethods()
|
void Bots::AddMethods()
|
||||||
{
|
{
|
||||||
Script::AddMethod("IsBot", Bots::GScr_isTestClient); // Usage: self IsBot();
|
Script::AddMethMultiple(GScr_isTestClient, false, {"IsTestClient", "IsBot"}); // Usage: self IsTestClient();
|
||||||
Script::AddMethod("IsTestClient", Bots::GScr_isTestClient); // Usage: self IsTestClient();
|
|
||||||
|
|
||||||
Script::AddMethod("BotStop", [](Game::scr_entref_t entref) // Usage: <bot> BotStop();
|
Script::AddMethod("BotStop", [](Game::scr_entref_t entref) // Usage: <bot> BotStop();
|
||||||
{
|
{
|
||||||
@ -234,7 +270,8 @@ namespace Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::usercmd_s userCmd = {0};
|
Game::usercmd_s userCmd;
|
||||||
|
ZeroMemory(&userCmd, sizeof(Game::usercmd_s));
|
||||||
|
|
||||||
userCmd.serverTime = *Game::svs_time;
|
userCmd.serverTime = *Game::svs_time;
|
||||||
|
|
||||||
@ -254,7 +291,7 @@ namespace Components
|
|||||||
pushad
|
pushad
|
||||||
|
|
||||||
push edi
|
push edi
|
||||||
call Bots::BotAiAction
|
call BotAiAction
|
||||||
add esp, 4
|
add esp, 4
|
||||||
|
|
||||||
popad
|
popad
|
||||||
@ -278,7 +315,7 @@ namespace Components
|
|||||||
|
|
||||||
push [esp + 0x20 + 0x8]
|
push [esp + 0x20 + 0x8]
|
||||||
push [esp + 0x20 + 0x8]
|
push [esp + 0x20 + 0x8]
|
||||||
call Bots::G_SelectWeaponIndex
|
call G_SelectWeaponIndex
|
||||||
add esp, 0x8
|
add esp, 0x8
|
||||||
|
|
||||||
popad
|
popad
|
||||||
@ -298,15 +335,17 @@ namespace Components
|
|||||||
AssertOffset(Game::client_t, ping, 0x212C8);
|
AssertOffset(Game::client_t, ping, 0x212C8);
|
||||||
|
|
||||||
// Replace connect string
|
// Replace connect string
|
||||||
Utils::Hook::Set<const char*>(0x48ADA6, "connect bot%d \"\\cg_predictItems\\1\\cl_anonymous\\0\\color\\4\\head\\default\\model\\multi\\snaps\\20\\rate\\5000\\name\\%s\\protocol\\%d\\checksum\\%d\\statver\\%d %u\\qport\\%d\"");
|
Utils::Hook::Set<const char*>(0x48ADA6, "connect bot%d \"\\cg_predictItems\\1\\cl_anonymous\\0\\color\\4\\head\\default\\model\\multi\\snaps\\20\\rate\\5000\\name\\%s\\clanAbbrev\\%s\\protocol\\%d\\checksum\\%d\\statver\\%d %u\\qport\\%d\"");
|
||||||
|
|
||||||
// Intercept sprintf for the connect string
|
// Intercept sprintf for the connect string
|
||||||
Utils::Hook(0x48ADAB, Bots::BuildConnectString, HOOK_CALL).install()->quick();
|
Utils::Hook(0x48ADAB, BuildConnectString, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
Utils::Hook(0x627021, Bots::SV_BotUserMove_Hk, HOOK_CALL).install()->quick();
|
Utils::Hook(0x627021, SV_BotUserMove_Hk, HOOK_CALL).install()->quick();
|
||||||
Utils::Hook(0x627241, Bots::SV_BotUserMove_Hk, HOOK_CALL).install()->quick();
|
Utils::Hook(0x627241, SV_BotUserMove_Hk, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
Utils::Hook(0x441B80, Bots::G_SelectWeaponIndex_Hk, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x441B80, G_SelectWeaponIndex_Hk, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
|
SVRandomBotNames = Dvar::Register<bool>("sv_randomBotNames", false, Game::DVAR_NONE, "Randomize the bots' names");
|
||||||
|
|
||||||
// Reset BotMovementInfo.active when client is dropped
|
// Reset BotMovementInfo.active when client is dropped
|
||||||
Events::OnClientDisconnect([](const int clientNum)
|
Events::OnClientDisconnect([](const int clientNum)
|
||||||
@ -359,10 +398,10 @@ namespace Components
|
|||||||
Toast::Show("cardicon_headshot", "^2Success", Utils::String::VA("Spawning %d %s...", count, (count == 1 ? "bot" : "bots")), 3000);
|
Toast::Show("cardicon_headshot", "^2Success", Utils::String::VA("Spawning %d %s...", count, (count == 1 ? "bot" : "bots")), 3000);
|
||||||
Logger::Debug("Spawning {} {}", count, (count == 1 ? "bot" : "bots"));
|
Logger::Debug("Spawning {} {}", count, (count == 1 ? "bot" : "bots"));
|
||||||
|
|
||||||
Bots::Spawn(count);
|
Spawn(count);
|
||||||
});
|
});
|
||||||
|
|
||||||
Bots::AddMethods();
|
AddMethods();
|
||||||
|
|
||||||
// In case a loaded mod didn't call "BotStop" before the VM shutdown
|
// In case a loaded mod didn't call "BotStop" before the VM shutdown
|
||||||
Events::OnVMShutdown([]
|
Events::OnVMShutdown([]
|
||||||
|
@ -8,7 +8,10 @@ namespace Components
|
|||||||
Bots();
|
Bots();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::vector<std::string> BotNames;
|
using botData = std::pair< std::string, std::string>;
|
||||||
|
static std::vector<botData> BotNames;
|
||||||
|
|
||||||
|
static Dvar::Var SVRandomBotNames;
|
||||||
|
|
||||||
static int BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port);
|
static int BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port);
|
||||||
|
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "Branding.hpp"
|
||||||
|
|
||||||
|
#include <version.hpp>
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -125,5 +128,19 @@ namespace Components
|
|||||||
|
|
||||||
// Hook CG_DrawFullScreenDebugOverlays so we may render the version when it's appropriate
|
// Hook CG_DrawFullScreenDebugOverlays so we may render the version when it's appropriate
|
||||||
Utils::Hook(0x5AC975, Branding::CG_DrawVersion_Hk, HOOK_CALL).install()->quick();
|
Utils::Hook(0x5AC975, Branding::CG_DrawVersion_Hk, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
|
// Console title
|
||||||
|
if (ZoneBuilder::IsEnabled())
|
||||||
|
{
|
||||||
|
Utils::Hook::Set<const char*>(0x4289E8, "IW4x (" VERSION "): ZoneBuilder");
|
||||||
|
}
|
||||||
|
else if (Dedicated::IsEnabled())
|
||||||
|
{
|
||||||
|
Utils::Hook::Set<const char*>(0x4289E8, "IW4x (" VERSION "): Dedicated");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Utils::Hook::Set<const char*>(0x4289E8, "IW4x (" VERSION "): Console");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,19 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "Bullet.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
Dvar::Var Bullet::BGSurfacePenetration;
|
Dvar::Var Bullet::BGSurfacePenetration;
|
||||||
Game::dvar_t* Bullet::BGBulletRange;
|
Game::dvar_t* Bullet::BGBulletRange;
|
||||||
|
|
||||||
|
float Bullet::ContactPointSave[3];
|
||||||
|
float Bullet::VCSave[3];
|
||||||
|
float Bullet::CalcRicochetSave[3];
|
||||||
|
|
||||||
|
float Bullet::ColorYellow[] = {1.0f, 1.0f, 0.0f, 1.0f};
|
||||||
|
float Bullet::ColorBlue[] = {0.0f, 0.0f, 1.0f, 1.0f};
|
||||||
|
float Bullet::ColorOrange[] = {1.0f, 0.7f, 0.0f, 1.0f};
|
||||||
|
|
||||||
float Bullet::BG_GetSurfacePenetrationDepthStub(const Game::WeaponDef* weapDef, int surfaceType)
|
float Bullet::BG_GetSurfacePenetrationDepthStub(const Game::WeaponDef* weapDef, int surfaceType)
|
||||||
{
|
{
|
||||||
assert(weapDef);
|
assert(weapDef);
|
||||||
@ -47,6 +56,63 @@ namespace Components
|
|||||||
*pHoldrand = static_cast<unsigned int>(std::rand());
|
*pHoldrand = static_cast<unsigned int>(std::rand());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Bullet::BulletRicochet_Save(const float* contactPoint)
|
||||||
|
{
|
||||||
|
std::memcpy(ContactPointSave, contactPoint, sizeof(float[3]));
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(naked) void Bullet::BulletRicochet_Stub()
|
||||||
|
{
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
pushad
|
||||||
|
push [esp + 0x20 + 0xC]
|
||||||
|
call BulletRicochet_Save
|
||||||
|
add esp, 0x4
|
||||||
|
popad
|
||||||
|
|
||||||
|
// Game's code
|
||||||
|
sub esp, 0x4C
|
||||||
|
push ebp
|
||||||
|
mov ebp, dword ptr [esp + 0x60]
|
||||||
|
|
||||||
|
push 0x5D5B08
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bullet::_VectorMA_Stub(float* va, float scale, float* vb, float* vc)
|
||||||
|
{
|
||||||
|
vc[0] = va[0] + scale * vb[0];
|
||||||
|
vc[1] = va[1] + scale * vb[1];
|
||||||
|
vc[2] = va[2] + scale * vb[2];
|
||||||
|
|
||||||
|
std::memcpy(VCSave, vc, sizeof(float[3]));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bullet::CalcRicochet_Stub(const float* incoming, const float* normal, float* result)
|
||||||
|
{
|
||||||
|
Utils::Hook::Call<void(const float*, const float*, float*)>(0x5D59F0)(incoming, normal, result);
|
||||||
|
std::memcpy(CalcRicochetSave, result, sizeof(float[3]));
|
||||||
|
}
|
||||||
|
|
||||||
|
int Bullet::Bullet_Fire_Stub(Game::gentity_s* attacker, [[maybe_unused]] float spread, Game::weaponParms* wp, Game::gentity_s* weaponEnt, Game::PlayerHandIndex hand, int gameTime)
|
||||||
|
{
|
||||||
|
float tmp[3];
|
||||||
|
|
||||||
|
Game::G_DebugStar(ContactPointSave, ColorYellow);
|
||||||
|
|
||||||
|
tmp[0] = (CalcRicochetSave[0] * 100.0f) + VCSave[0];
|
||||||
|
tmp[1] = (CalcRicochetSave[1] * 100.0f) + VCSave[1];
|
||||||
|
tmp[2] = (CalcRicochetSave[2] * 100.0f) + VCSave[1];
|
||||||
|
|
||||||
|
Game::G_DebugLineWithDuration(VCSave, tmp, ColorOrange, 1, 100);
|
||||||
|
Game::G_DebugStar(tmp, ColorBlue);
|
||||||
|
|
||||||
|
// Set the spread to 0 when drawing
|
||||||
|
return Game::Bullet_Fire(attacker, 0.0f, wp, weaponEnt, hand, gameTime);
|
||||||
|
}
|
||||||
|
|
||||||
Bullet::Bullet()
|
Bullet::Bullet()
|
||||||
{
|
{
|
||||||
BGSurfacePenetration = Dvar::Register<float>("bg_surfacePenetration", 0.0f,
|
BGSurfacePenetration = Dvar::Register<float>("bg_surfacePenetration", 0.0f,
|
||||||
@ -58,5 +124,20 @@ namespace Components
|
|||||||
Utils::Hook(0x440340, Bullet_FireStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x440340, Bullet_FireStub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
Utils::Hook(0x440368, BG_srand_Hk, HOOK_CALL).install()->quick();
|
Utils::Hook(0x440368, BG_srand_Hk, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
|
std::memset(ContactPointSave, 0, sizeof(float[3]));
|
||||||
|
std::memset(VCSave, 0, sizeof(float[3]));
|
||||||
|
std::memset(CalcRicochetSave, 0, sizeof(float[3]));
|
||||||
|
|
||||||
|
#ifdef DEBUG_RIOT_SHIELD
|
||||||
|
Utils::Hook(0x5D5B00, BulletRicochet_Stub, HOOK_JUMP).install()->quick();
|
||||||
|
Utils::Hook::Nop(0x5D5B00 + 5, 3);
|
||||||
|
|
||||||
|
Utils::Hook(0x5D5BBA, CalcRicochet_Stub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
|
Utils::Hook(0x5D5BD7, _VectorMA_Stub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
|
Utils::Hook(0x5D5C0B, Bullet_Fire_Stub, HOOK_CALL).install()->quick();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,10 +12,27 @@ namespace Components
|
|||||||
// Can't use Var class inside assembly stubs
|
// Can't use Var class inside assembly stubs
|
||||||
static Game::dvar_t* BGBulletRange;
|
static Game::dvar_t* BGBulletRange;
|
||||||
|
|
||||||
|
static float ContactPointSave[];
|
||||||
|
static float VCSave[];
|
||||||
|
static float CalcRicochetSave[];
|
||||||
|
|
||||||
|
static float ColorYellow[];
|
||||||
|
static float ColorBlue[];
|
||||||
|
static float ColorOrange[];
|
||||||
|
|
||||||
static float BG_GetSurfacePenetrationDepthStub(const Game::WeaponDef* weapDef, int surfaceType);
|
static float BG_GetSurfacePenetrationDepthStub(const Game::WeaponDef* weapDef, int surfaceType);
|
||||||
|
|
||||||
static void Bullet_FireStub();
|
static void Bullet_FireStub();
|
||||||
|
|
||||||
static void BG_srand_Hk(unsigned int* pHoldrand);
|
static void BG_srand_Hk(unsigned int* pHoldrand);
|
||||||
|
|
||||||
|
static void BulletRicochet_Save(const float* contactPoint);
|
||||||
|
static void BulletRicochet_Stub();
|
||||||
|
|
||||||
|
static void CalcRicochet_Stub(const float* incoming, const float* normal, float* result);
|
||||||
|
|
||||||
|
static void _VectorMA_Stub(float* va, float scale, float* vb, float* vc);
|
||||||
|
|
||||||
|
static int Bullet_Fire_Stub(Game::gentity_s* attacker, float spread, Game::weaponParms* wp, Game::gentity_s* weaponEnt, Game::PlayerHandIndex hand, int gameTime);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "CardTitles.hpp"
|
||||||
|
#include "ServerCommands.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
std::string CardTitles::CustomTitles[18];
|
char CardTitles::CustomTitles[Game::MAX_CLIENTS][18];
|
||||||
Dvar::Var CardTitles::CustomTitle;
|
Dvar::Var CardTitles::CustomTitle;
|
||||||
|
|
||||||
CClient* CardTitles::GetClientByIndex(std::uint32_t index)
|
CClient* CardTitles::GetClientByIndex(std::uint32_t index)
|
||||||
@ -10,35 +12,34 @@ namespace Components
|
|||||||
return &reinterpret_cast<CClient*>(0x8E77B0)[index];
|
return &reinterpret_cast<CClient*>(0x8E77B0)[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::int32_t CardTitles::GetPlayerCardClientInfo(std::int32_t lookupResult, Game::PlayerCardData* data)
|
int CardTitles::GetPlayerCardClientInfo(int lookupResult, Game::PlayerCardData* data)
|
||||||
{
|
{
|
||||||
std::int32_t returnResult = lookupResult;
|
auto result = lookupResult;
|
||||||
|
|
||||||
std::string username = Dvar::Var("name").get<std::string>();
|
const auto* username = Dvar::Var("name").get<const char*>();
|
||||||
|
if (std::strcmp(data->name, username) == 0)
|
||||||
if (data->name == username)
|
|
||||||
{
|
{
|
||||||
returnResult += 0xFE000000;
|
result += 0xFE000000;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (std::size_t clientNum = 0; clientNum < Game::MAX_CLIENTS; ++clientNum)
|
for (std::size_t i = 0; i < Game::MAX_CLIENTS; ++i)
|
||||||
{
|
{
|
||||||
CClient* c = GetClientByIndex(clientNum);
|
CClient* c = GetClientByIndex(i);
|
||||||
if (c != nullptr)
|
if (c != nullptr)
|
||||||
{
|
{
|
||||||
if (!std::strcmp(data->name, c->Name))
|
if (!std::strcmp(data->name, c->Name))
|
||||||
{
|
{
|
||||||
// Since a 4 byte integer is overkill for a row num: We can use it to store the customprefix + clientNum and use a 2 byte integer for the row number
|
// Since a 4 byte integer is overkill for a row num: We can use it to store the customprefix + clientNum and use a 2 byte integer for the row number
|
||||||
returnResult += 0xFF000000;
|
result += 0xFF000000;
|
||||||
returnResult += clientNum * 0x10000;
|
result += i * 0x10000;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnResult;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void __declspec(naked) CardTitles::GetPlayerCardClientInfoStub()
|
void __declspec(naked) CardTitles::GetPlayerCardClientInfoStub()
|
||||||
@ -71,7 +72,7 @@ namespace Components
|
|||||||
std::uint8_t prefix = (request->tableRow >> (8 * 3)) & 0xFF;
|
std::uint8_t prefix = (request->tableRow >> (8 * 3)) & 0xFF;
|
||||||
std::uint8_t data = (request->tableRow >> (8 * 2)) & 0xFF;
|
std::uint8_t data = (request->tableRow >> (8 * 2)) & 0xFF;
|
||||||
|
|
||||||
if (data >= ARRAYSIZE(CardTitles::CustomTitles)) return nullptr;
|
if (data >= Game::MAX_CLIENTS) return nullptr;
|
||||||
|
|
||||||
if (request->tablename == "mp/cardTitleTable.csv"s)
|
if (request->tablename == "mp/cardTitleTable.csv"s)
|
||||||
{
|
{
|
||||||
@ -82,10 +83,10 @@ namespace Components
|
|||||||
{
|
{
|
||||||
if (prefix == 0xFE)
|
if (prefix == 0xFE)
|
||||||
{
|
{
|
||||||
if (!CardTitles::CustomTitle.get<std::string>().empty())
|
if (!CustomTitle.get<std::string>().empty())
|
||||||
{
|
{
|
||||||
// 0xFF in front of the title to skip localization. Or else it will wait for a couple of seconds for the asset of type localize
|
// 0xFF in front of the title to skip localization. Or else it will wait for a couple of seconds for the asset of type localize
|
||||||
const char* title = Utils::String::VA("\x15%s", CardTitles::CustomTitle.get<const char*>());
|
const auto* title = Utils::String::VA("\x15%s", CustomTitle.get<const char*>());
|
||||||
|
|
||||||
// prepare return value
|
// prepare return value
|
||||||
operand->internals.stringVal.string = title;
|
operand->internals.stringVal.string = title;
|
||||||
@ -96,9 +97,9 @@ namespace Components
|
|||||||
}
|
}
|
||||||
else if (prefix == 0xFF)
|
else if (prefix == 0xFF)
|
||||||
{
|
{
|
||||||
if (!CardTitles::CustomTitles[data].empty())
|
if (CustomTitles[data][0] != '\0')
|
||||||
{
|
{
|
||||||
const char* title = Utils::String::VA("\x15%s", CardTitles::CustomTitles[data].data());
|
const auto* title = Utils::String::VA("\x15%s", CustomTitles[data]);
|
||||||
|
|
||||||
// prepare return value
|
// prepare return value
|
||||||
operand->internals.stringVal.string = title;
|
operand->internals.stringVal.string = title;
|
||||||
@ -156,11 +157,11 @@ namespace Components
|
|||||||
{
|
{
|
||||||
std::string list;
|
std::string list;
|
||||||
|
|
||||||
for (std::size_t i = 0; i < Game::MAX_CLIENTS; i++)
|
for (std::size_t i = 0; i < Game::MAX_CLIENTS; ++i)
|
||||||
{
|
{
|
||||||
char playerTitle[18];
|
char playerTitle[18]{};
|
||||||
|
|
||||||
if (Game::svs_clients[i].header.state >= Game::CS_CONNECTED)
|
if (Game::svs_clients[i].userinfo[0] != '\0')
|
||||||
{
|
{
|
||||||
strncpy_s(playerTitle, Game::Info_ValueForKey(Game::svs_clients[i].userinfo, "customTitle"), _TRUNCATE);
|
strncpy_s(playerTitle, Game::Info_ValueForKey(Game::svs_clients[i].userinfo, "customTitle"), _TRUNCATE);
|
||||||
}
|
}
|
||||||
@ -169,10 +170,10 @@ namespace Components
|
|||||||
playerTitle[0] = '\0';
|
playerTitle[0] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
list.append(Utils::String::VA("\\%s\\%s", std::to_string(i).data(), playerTitle));
|
list.append(std::format("\\{}\\{}", std::to_string(i), playerTitle));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto* command = Utils::String::VA("%c customTitles \"%s\"", 21, list.data());
|
const auto* command = Utils::String::Format("{:c} customTitles \"{}\"", 21, list);
|
||||||
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, command);
|
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, command);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,10 +181,17 @@ namespace Components
|
|||||||
{
|
{
|
||||||
for (std::size_t i = 0; i < Game::MAX_CLIENTS; ++i)
|
for (std::size_t i = 0; i < Game::MAX_CLIENTS; ++i)
|
||||||
{
|
{
|
||||||
const char* playerTitle = Game::Info_ValueForKey(msg, std::to_string(i).c_str());
|
const auto index = std::to_string(i);
|
||||||
|
const auto* playerTitle = Game::Info_ValueForKey(msg, index.data());
|
||||||
|
|
||||||
if (playerTitle) CardTitles::CustomTitles[i] = playerTitle;
|
if (playerTitle[0] == '\0')
|
||||||
else CardTitles::CustomTitles[i].clear();
|
{
|
||||||
|
CustomTitles[i][0] = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Game::I_strncpyz(CustomTitles[i], playerTitle, sizeof(CustomTitles[0]) / sizeof(char));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,16 +199,18 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Scheduler::Once([]
|
Scheduler::Once([]
|
||||||
{
|
{
|
||||||
CardTitles::CustomTitle = Dvar::Register<const char*>("customTitle", "", Game::DVAR_USERINFO | Game::DVAR_ARCHIVE, "Custom card title");
|
CustomTitle = Dvar::Register<const char*>("customTitle", "", Game::DVAR_USERINFO | Game::DVAR_ARCHIVE, "Custom card title");
|
||||||
}, Scheduler::Pipeline::MAIN);
|
}, Scheduler::Pipeline::MAIN);
|
||||||
|
|
||||||
|
std::memset(&CustomTitles, 0, sizeof(char[Game::MAX_CLIENTS][18]));
|
||||||
|
|
||||||
ServerCommands::OnCommand(21, [](Command::Params* params)
|
ServerCommands::OnCommand(21, [](Command::Params* params)
|
||||||
{
|
{
|
||||||
if (params->get(1) == "customTitles"s && !Dedicated::IsEnabled())
|
if (std::strcmp(params->get(1), "customTitles") == 0)
|
||||||
{
|
{
|
||||||
if (params->size() == 3)
|
if (params->size() == 3)
|
||||||
{
|
{
|
||||||
CardTitles::ParseCustomTitles(params->get(2));
|
ParseCustomTitles(params->get(2));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -209,10 +219,10 @@ namespace Components
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Utils::Hook(0x62EB26, CardTitles::GetPlayerCardClientInfoStub).install()->quick();
|
Utils::Hook(0x62EB26, GetPlayerCardClientInfoStub).install()->quick();
|
||||||
|
|
||||||
// Table lookup stuff
|
// Table lookup stuff
|
||||||
Utils::Hook(0x62DCC1, CardTitles::TableLookupByRowHookStub).install()->quick();
|
Utils::Hook(0x62DCC1, TableLookupByRowHookStub).install()->quick();
|
||||||
Utils::Hook::Nop(0x62DCC6, 1);
|
Utils::Hook::Nop(0x62DCC6, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,19 +44,20 @@ namespace Components
|
|||||||
public:
|
public:
|
||||||
AssertOffset(Game::PlayerCardData, Game::PlayerCardData::name, 0x1C);
|
AssertOffset(Game::PlayerCardData, Game::PlayerCardData::name, 0x1C);
|
||||||
|
|
||||||
static Dvar::Var CustomTitle;
|
|
||||||
static std::string CustomTitles[18];
|
|
||||||
|
|
||||||
static void SendCustomTitlesToClients();
|
static void SendCustomTitlesToClients();
|
||||||
static void ParseCustomTitles(const char* msg);
|
|
||||||
|
|
||||||
CardTitles();
|
CardTitles();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static Dvar::Var CustomTitle;
|
||||||
|
static char CustomTitles[Game::MAX_CLIENTS][18];
|
||||||
|
|
||||||
static CClient* GetClientByIndex(std::uint32_t index);
|
static CClient* GetClientByIndex(std::uint32_t index);
|
||||||
static std::int32_t GetPlayerCardClientInfo(std::int32_t lookupResult, Game::PlayerCardData* data);
|
static int GetPlayerCardClientInfo(int lookupResult, Game::PlayerCardData* data);
|
||||||
static void GetPlayerCardClientInfoStub();
|
static void GetPlayerCardClientInfoStub();
|
||||||
static const char* TableLookupByRowHook(Game::Operand* operand, tablelookuprequest_s* request);
|
static const char* TableLookupByRowHook(Game::Operand* operand, tablelookuprequest_s* request);
|
||||||
static void TableLookupByRowHookStub();
|
static void TableLookupByRowHookStub();
|
||||||
|
|
||||||
|
static void ParseCustomTitles(const char* msg);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "Ceg.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "Changelog.hpp"
|
||||||
|
#include "UIFeeder.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -7,44 +9,44 @@ namespace Components
|
|||||||
|
|
||||||
void Changelog::LoadChangelog()
|
void Changelog::LoadChangelog()
|
||||||
{
|
{
|
||||||
//if (!Changelog::Lines.empty())
|
std::lock_guard _(Mutex);
|
||||||
// return;
|
Lines.clear();
|
||||||
|
|
||||||
std::lock_guard<std::mutex> _(Changelog::Mutex);
|
const auto data = Utils::Cache::GetFile("/develop/CHANGELOG.md");
|
||||||
Changelog::Lines.clear();
|
|
||||||
std::string data = Utils::Cache::GetFile("/develop/CHANGELOG.md");
|
|
||||||
|
|
||||||
if (data.empty())
|
if (data.empty())
|
||||||
{
|
{
|
||||||
data = "^1Unable to get changelog.";
|
Lines.emplace_back("^1Unable to get changelog.");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Changelog::Lines = Utils::String::Split(data, '\n');
|
auto buffer = Utils::String::Split(data, '\n');
|
||||||
|
for (auto& line : buffer)
|
||||||
for (auto& line : Changelog::Lines)
|
|
||||||
{
|
{
|
||||||
Utils::String::Replace(line, "\r", "");
|
Utils::String::Replace(line, "\r", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Lines = buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int Changelog::GetChangelogCount()
|
unsigned int Changelog::GetChangelogCount()
|
||||||
{
|
{
|
||||||
return Changelog::Lines.size();
|
return Lines.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Omit column here
|
// Omit column here
|
||||||
const char* Changelog::GetChangelogText(unsigned int item, int /*column*/)
|
const char* Changelog::GetChangelogText(unsigned int item, [[maybe_unused]] int column)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> _(Changelog::Mutex);
|
std::lock_guard _(Mutex);
|
||||||
if (item < Changelog::Lines.size())
|
if (item < Lines.size())
|
||||||
{
|
{
|
||||||
return Utils::String::VA("%s", Changelog::Lines[item].data());
|
return Utils::String::Format("{}", Lines[item]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
void Changelog::SelectChangelog(unsigned int /*index*/)
|
void Changelog::SelectChangelog([[maybe_unused]] unsigned int index)
|
||||||
{
|
{
|
||||||
// Don't do anything in here
|
// Don't do anything in here
|
||||||
}
|
}
|
||||||
@ -54,6 +56,6 @@ namespace Components
|
|||||||
if (Dedicated::IsEnabled()) return;
|
if (Dedicated::IsEnabled()) return;
|
||||||
|
|
||||||
// Changelog
|
// Changelog
|
||||||
UIFeeder::Add(62.0f, Changelog::GetChangelogCount, Changelog::GetChangelogText, Changelog::SelectChangelog);
|
UIFeeder::Add(62.0f, GetChangelogCount, GetChangelogText, SelectChangelog);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "Chat.hpp"
|
||||||
|
#include "PlayerName.hpp"
|
||||||
|
#include "Voice.hpp"
|
||||||
|
|
||||||
#include "GSC/Script.hpp"
|
#include "GSC/Script.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
@ -73,7 +77,7 @@ namespace Components
|
|||||||
|
|
||||||
Game::Scr_AddEntity(player);
|
Game::Scr_AddEntity(player);
|
||||||
Game::Scr_AddString(text + msgIndex);
|
Game::Scr_AddString(text + msgIndex);
|
||||||
Game::Scr_NotifyLevel(Game::SL_GetString("say", 0), 2);
|
Game::Scr_NotifyLevel(static_cast<std::uint16_t>(Game::SL_GetString("say", 0)), 2);
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
@ -268,8 +272,7 @@ namespace Components
|
|||||||
});
|
});
|
||||||
|
|
||||||
Logger::Print("{} was muted\n", client->name);
|
Logger::Print("{} was muted\n", client->name);
|
||||||
Game::SV_GameSendServerCommand(client - Game::svs_clients, Game::SV_CMD_CAN_IGNORE,
|
Game::SV_GameSendServerCommand(client - Game::svs_clients, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"You were muted\"", 0x65));
|
||||||
Utils::String::VA("%c \"You were muted\"", 0x65));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chat::UnmuteClient(const Game::client_t* client)
|
void Chat::UnmuteClient(const Game::client_t* client)
|
||||||
@ -277,8 +280,7 @@ namespace Components
|
|||||||
UnmuteInternal(client->steamID);
|
UnmuteInternal(client->steamID);
|
||||||
|
|
||||||
Logger::Print("{} was unmuted\n", client->name);
|
Logger::Print("{} was unmuted\n", client->name);
|
||||||
Game::SV_GameSendServerCommand(client - Game::svs_clients, Game::SV_CMD_CAN_IGNORE,
|
Game::SV_GameSendServerCommand(client - Game::svs_clients, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"You were unmuted\"", 0x65));
|
||||||
Utils::String::VA("%c \"You were unmuted\"", 0x65));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chat::UnmuteInternal(const std::uint64_t id, bool everyone)
|
void Chat::UnmuteInternal(const std::uint64_t id, bool everyone)
|
||||||
@ -369,12 +371,12 @@ namespace Components
|
|||||||
|
|
||||||
if (!name.empty())
|
if (!name.empty())
|
||||||
{
|
{
|
||||||
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s: %s\"", 0x68, name.data(), message.data()));
|
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::Format("{:c} \"{}: {}\"", 0x68, name, message));
|
||||||
Logger::Print(Game::CON_CHANNEL_SERVER, "{}: {}\n", name, message);
|
Logger::Print(Game::CON_CHANNEL_SERVER, "{}: {}\n", name, message);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"Console: %s\"", 0x68, message.data()));
|
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::Format("{:c} \"Console: {}\"", 0x68, message));
|
||||||
Logger::Print(Game::CON_CHANNEL_SERVER, "Console: {}\n", message);
|
Logger::Print(Game::CON_CHANNEL_SERVER, "Console: {}\n", message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -395,12 +397,12 @@ namespace Components
|
|||||||
|
|
||||||
if (!name.empty())
|
if (!name.empty())
|
||||||
{
|
{
|
||||||
Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s: %s\"", 0x68, name.data(), message.data()));
|
Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::Format("{:c} \"{}: {}\"", 0x68, name.data(), message));
|
||||||
Logger::Print(Game::CON_CHANNEL_SERVER, "{} -> {}: {}\n", name, client, message);
|
Logger::Print(Game::CON_CHANNEL_SERVER, "{} -> {}: {}\n", name, client, message);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"Console: %s\"", 104, message.data()));
|
Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::Format("{:c} \"Console: {}\"", 0x68, message));
|
||||||
Logger::Print(Game::CON_CHANNEL_SERVER, "Console -> {}: {}\n", client, message);
|
Logger::Print(Game::CON_CHANNEL_SERVER, "Console -> {}: {}\n", client, message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -416,7 +418,7 @@ namespace Components
|
|||||||
if (params->size() < 2) return;
|
if (params->size() < 2) return;
|
||||||
|
|
||||||
auto message = params->join(1);
|
auto message = params->join(1);
|
||||||
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x68, message.data()));
|
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::Format("{:c} \"{}\"", 0x68, message));
|
||||||
Logger::Print(Game::CON_CHANNEL_SERVER, "Raw: {}\n", message);
|
Logger::Print(Game::CON_CHANNEL_SERVER, "Raw: {}\n", message);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -432,7 +434,7 @@ namespace Components
|
|||||||
|
|
||||||
const auto client = atoi(params->get(1));
|
const auto client = atoi(params->get(1));
|
||||||
std::string message = params->join(2);
|
std::string message = params->join(2);
|
||||||
Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x68, message.data()));
|
Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::Format("{:c} \"{}\"", 0x68, message));
|
||||||
Logger::Print(Game::CON_CHANNEL_SERVER, "Raw -> {}: {}\n", client, message);
|
Logger::Print(Game::CON_CHANNEL_SERVER, "Raw -> {}: {}\n", client, message);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "ClanTags.hpp"
|
||||||
|
#include "PlayerName.hpp"
|
||||||
|
#include "ServerCommands.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -28,15 +31,15 @@ namespace Components
|
|||||||
list.append(std::format("\\{}\\{}", std::to_string(i), ClientState[i]));
|
list.append(std::format("\\{}\\{}", std::to_string(i), ClientState[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto* command = Utils::String::VA("%c clanNames \"%s\"", 22, list.data());
|
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::Format("{:c} clanNames \"{}\"", 22, list));
|
||||||
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, command);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClanTags::ParseClanTags(const char* infoString)
|
void ClanTags::ParseClanTags(const char* infoString)
|
||||||
{
|
{
|
||||||
for (std::size_t i = 0; i < Game::MAX_CLIENTS; ++i)
|
for (std::size_t i = 0; i < Game::MAX_CLIENTS; ++i)
|
||||||
{
|
{
|
||||||
const auto* clanTag = Game::Info_ValueForKey(infoString, std::to_string(i).data());
|
const auto index = std::to_string(i);
|
||||||
|
const auto* clanTag = Game::Info_ValueForKey(infoString, index.data());
|
||||||
|
|
||||||
if (clanTag[0] == '\0')
|
if (clanTag[0] == '\0')
|
||||||
{
|
{
|
||||||
@ -70,9 +73,7 @@ namespace Components
|
|||||||
|
|
||||||
void ClanTags::CL_SanitizeClanName()
|
void ClanTags::CL_SanitizeClanName()
|
||||||
{
|
{
|
||||||
char saneNameBuf[5];
|
char saneNameBuf[5]{};
|
||||||
std::memset(saneNameBuf, 0, sizeof(saneNameBuf));
|
|
||||||
|
|
||||||
auto* saneName = saneNameBuf;
|
auto* saneName = saneNameBuf;
|
||||||
|
|
||||||
assert(ClanName);
|
assert(ClanName);
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "ClientCommand.hpp"
|
||||||
|
|
||||||
#include "GSC/Script.hpp"
|
#include "GSC/Script.hpp"
|
||||||
|
|
||||||
|
using namespace Utils::String;
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
std::unordered_map<std::string, std::function<void(Game::gentity_s*, const Command::ServerParams*)>> ClientCommand::HandlersSV;
|
std::unordered_map<std::string, std::function<void(Game::gentity_s*, const Command::ServerParams*)>> ClientCommand::HandlersSV;
|
||||||
@ -12,14 +16,14 @@ namespace Components
|
|||||||
if (!(*Game::g_cheats)->current.enabled)
|
if (!(*Game::g_cheats)->current.enabled)
|
||||||
{
|
{
|
||||||
Logger::Debug("Cheats are disabled!");
|
Logger::Debug("Cheats are disabled!");
|
||||||
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"GAME_CHEATSNOTENABLED\"", 0x65));
|
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_CHEATSNOTENABLED\"", 0x65));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ent->health < 1)
|
if (ent->health < 1)
|
||||||
{
|
{
|
||||||
Logger::Debug("Entity {} must be alive to use this command!", entNum);
|
Logger::Debug("Entity {} must be alive to use this command!", entNum);
|
||||||
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"GAME_MUSTBEALIVECOMMAND\"", 0x65));
|
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_MUSTBEALIVECOMMAND\"", 0x65));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,9 +41,9 @@ namespace Components
|
|||||||
{
|
{
|
||||||
const auto ent = &Game::g_entities[clientNum];
|
const auto ent = &Game::g_entities[clientNum];
|
||||||
|
|
||||||
if (ent->client == nullptr)
|
if (!ent->client)
|
||||||
{
|
{
|
||||||
Logger::Debug("ClientCommand: client {} is not fully in game yet", clientNum);
|
Logger::Debug("ClientCommand: client {} is not fully connected", clientNum);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,8 +71,7 @@ namespace Components
|
|||||||
const auto entNum = ent->s.number;
|
const auto entNum = ent->s.number;
|
||||||
Logger::Debug("Noclip toggled for entity {}", entNum);
|
Logger::Debug("Noclip toggled for entity {}", entNum);
|
||||||
|
|
||||||
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x65,
|
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, VA("%c \"%s\"", 0x65, (ent->client->flags & Game::PLAYER_FLAG_NOCLIP) ? "GAME_NOCLIPON" : "GAME_NOCLIPOFF"));
|
||||||
(ent->client->flags & Game::PLAYER_FLAG_NOCLIP) ? "GAME_NOCLIPON" : "GAME_NOCLIPOFF"));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Add("ufo", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
Add("ufo", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
@ -81,8 +84,7 @@ namespace Components
|
|||||||
const auto entNum = ent->s.number;
|
const auto entNum = ent->s.number;
|
||||||
Logger::Debug("UFO toggled for entity {}", entNum);
|
Logger::Debug("UFO toggled for entity {}", entNum);
|
||||||
|
|
||||||
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x65,
|
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, VA("%c \"%s\"", 0x65, (ent->client->flags & Game::PLAYER_FLAG_UFO) ? "GAME_UFOON" : "GAME_UFOOFF"));
|
||||||
(ent->client->flags & Game::PLAYER_FLAG_UFO) ? "GAME_UFOON" : "GAME_UFOOFF"));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Add("god", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
Add("god", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
@ -95,8 +97,7 @@ namespace Components
|
|||||||
const auto entNum = ent->s.number;
|
const auto entNum = ent->s.number;
|
||||||
Logger::Debug("God toggled for entity {}", entNum);
|
Logger::Debug("God toggled for entity {}", entNum);
|
||||||
|
|
||||||
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x65,
|
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, VA("%c \"%s\"", 0x65, (ent->flags & Game::FL_GODMODE) ? "GAME_GODMODE_ON" : "GAME_GODMODE_OFF"));
|
||||||
(ent->flags & Game::FL_GODMODE) ? "GAME_GODMODE_ON" : "GAME_GODMODE_OFF"));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Add("demigod", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
Add("demigod", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
@ -109,8 +110,7 @@ namespace Components
|
|||||||
const auto entNum = ent->s.number;
|
const auto entNum = ent->s.number;
|
||||||
Logger::Debug("Demigod toggled for entity {}", entNum);
|
Logger::Debug("Demigod toggled for entity {}", entNum);
|
||||||
|
|
||||||
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x65,
|
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, VA("%c \"%s\"", 0x65, (ent->flags & Game::FL_DEMI_GODMODE) ? "GAME_DEMI_GODMODE_ON" : "GAME_DEMI_GODMODE_OFF"));
|
||||||
(ent->flags & Game::FL_DEMI_GODMODE) ? "GAME_DEMI_GODMODE_ON" : "GAME_DEMI_GODMODE_OFF"));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Add("notarget", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
Add("notarget", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
@ -123,13 +123,12 @@ namespace Components
|
|||||||
const auto entNum = ent->s.number;
|
const auto entNum = ent->s.number;
|
||||||
Logger::Debug("Notarget toggled for entity {}", entNum);
|
Logger::Debug("Notarget toggled for entity {}", entNum);
|
||||||
|
|
||||||
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x65,
|
Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, VA("%c \"%s\"", 0x65, (ent->flags & Game::FL_NOTARGET) ? "GAME_NOTARGETON" : "GAME_NOTARGETOFF"));
|
||||||
(ent->flags & Game::FL_NOTARGET) ? "GAME_NOTARGETON" : "GAME_NOTARGETOFF"));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Add("setviewpos", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
Add("setviewpos", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
{
|
{
|
||||||
assert(ent != nullptr);
|
assert(ent);
|
||||||
|
|
||||||
if (!CheatsOk(ent))
|
if (!CheatsOk(ent))
|
||||||
return;
|
return;
|
||||||
@ -138,8 +137,7 @@ namespace Components
|
|||||||
|
|
||||||
if (params->size() < 4 || params->size() > 6)
|
if (params->size() < 4 || params->size() > 6)
|
||||||
{
|
{
|
||||||
Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_CAN_IGNORE,
|
Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_USAGE\x15: setviewpos x y z [yaw] [pitch]\n\"", 0x65));
|
||||||
Utils::String::VA("%c \"GAME_USAGE\x15: setviewpos x y z [yaw] [pitch]\n\"", 0x65));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,8 +168,7 @@ namespace Components
|
|||||||
|
|
||||||
if (params->size() < 2)
|
if (params->size() < 2)
|
||||||
{
|
{
|
||||||
Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_CAN_IGNORE,
|
Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_USAGE\x15: give <weapon name>\"", 0x65));
|
||||||
Utils::String::VA("%c \"GAME_USAGE\x15: give <weapon name>\"", 0x65));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,19 +242,26 @@ namespace Components
|
|||||||
|
|
||||||
Add("kill", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
Add("kill", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
{
|
{
|
||||||
assert(ent->client != nullptr);
|
assert(ent->client);
|
||||||
assert(ent->client->sess.connected != Game::CON_DISCONNECTED);
|
assert(ent->client->sess.connected != Game::CON_DISCONNECTED);
|
||||||
|
|
||||||
if (ent->client->sess.sessionState != Game::SESS_STATE_PLAYING || !CheatsOk(ent))
|
if (ent->client->sess.sessionState != Game::SESS_STATE_PLAYING || !CheatsOk(ent))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Scheduler::Once([ent]
|
auto** bgs = Game::Sys::GetTls<Game::bgs_t*>(Game::Sys::TLS_OFFSET::LEVEL_BGS);
|
||||||
{
|
|
||||||
|
assert(*bgs == nullptr);
|
||||||
|
|
||||||
|
*bgs = Game::level_bgs;
|
||||||
|
|
||||||
ent->flags &= ~(Game::FL_GODMODE | Game::FL_DEMI_GODMODE);
|
ent->flags &= ~(Game::FL_GODMODE | Game::FL_DEMI_GODMODE);
|
||||||
ent->health = 0;
|
ent->health = 0;
|
||||||
ent->client->ps.stats[0] = 0;
|
ent->client->ps.stats[0] = 0;
|
||||||
Game::player_die(ent, ent, ent, 100000, Game::MOD_SUICIDE, 0, nullptr, Game::HITLOC_NONE, 0);
|
Game::player_die(ent, ent, ent, 100000, Game::MOD_SUICIDE, 0, nullptr, Game::HITLOC_NONE, 0);
|
||||||
}, Scheduler::Pipeline::SERVER);
|
|
||||||
|
assert(*bgs == Game::level_bgs);
|
||||||
|
|
||||||
|
*bgs = nullptr;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,7 +303,7 @@ namespace Components
|
|||||||
duration = static_cast<int>(std::floorf(input * 1000.0f + 0.5f));
|
duration = static_cast<int>(std::floorf(input * 1000.0f + 0.5f));
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(ent->client != nullptr);
|
assert(ent->client);
|
||||||
|
|
||||||
constexpr auto visMode = Game::visionSetMode_t::VISIONSET_NORMAL;
|
constexpr auto visMode = Game::visionSetMode_t::VISIONSET_NORMAL;
|
||||||
const auto* name = params->get(1);
|
const auto* name = params->get(1);
|
||||||
@ -308,8 +312,7 @@ namespace Components
|
|||||||
strncpy_s(ent->client->visionName[visMode],
|
strncpy_s(ent->client->visionName[visMode],
|
||||||
sizeof(Game::gclient_t::visionName[0]) / sizeof(char), name, _TRUNCATE);
|
sizeof(Game::gclient_t::visionName[0]) / sizeof(char), name, _TRUNCATE);
|
||||||
|
|
||||||
Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_RELIABLE,
|
Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_RELIABLE, VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration));
|
||||||
Utils::String::VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Add("visionsetnight", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
Add("visionsetnight", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
@ -327,7 +330,7 @@ namespace Components
|
|||||||
duration = static_cast<int>(std::floorf(input * 1000.0f + 0.5f));
|
duration = static_cast<int>(std::floorf(input * 1000.0f + 0.5f));
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(ent->client != nullptr);
|
assert(ent->client);
|
||||||
|
|
||||||
constexpr auto visMode = Game::visionSetMode_t::VISIONSET_NIGHT;
|
constexpr auto visMode = Game::visionSetMode_t::VISIONSET_NIGHT;
|
||||||
const auto* name = params->get(1);
|
const auto* name = params->get(1);
|
||||||
@ -336,13 +339,12 @@ namespace Components
|
|||||||
strncpy_s(ent->client->visionName[visMode],
|
strncpy_s(ent->client->visionName[visMode],
|
||||||
sizeof(Game::gclient_t::visionName[0]) / sizeof(char), name, _TRUNCATE);
|
sizeof(Game::gclient_t::visionName[0]) / sizeof(char), name, _TRUNCATE);
|
||||||
|
|
||||||
Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_RELIABLE,
|
Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_RELIABLE, VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration));
|
||||||
Utils::String::VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Add("g_testCmd", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
Add("g_testCmd", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params)
|
||||||
{
|
{
|
||||||
assert(ent != nullptr);
|
assert(ent);
|
||||||
|
|
||||||
ent->client->ps.stunTime = 1000 + Game::level->time; // 1000 is the default test stun time
|
ent->client->ps.stunTime = 1000 + Game::level->time; // 1000 is the default test stun time
|
||||||
Logger::Debug("playerState_s.stunTime is {}", ent->client->ps.stunTime);
|
Logger::Debug("playerState_s.stunTime is {}", ent->client->ps.stunTime);
|
||||||
@ -406,7 +408,7 @@ namespace Components
|
|||||||
|
|
||||||
// See description of the format string in the function G_DumpEntityDebugInfoToCSV
|
// See description of the format string in the function G_DumpEntityDebugInfoToCSV
|
||||||
// If empty it means the item does not exist in the current version of the game or it was not possible to reverse it
|
// If empty it means the item does not exist in the current version of the game or it was not possible to reverse it
|
||||||
return Utils::String::VA("%i,%s,%.0f,%s,%s,%s,%s,%s,%s,%s,%s,%s,%.0f %.0f %.0f,%.0f %.0f %.0f,%i\n",
|
return VA("%i,%s,%.0f,%s,%s,%s,%s,%s,%s,%s,%s,%s,%.0f %.0f %.0f,%.0f %.0f %.0f,%i\n",
|
||||||
entNum, eventType, distance, classname, codeClassname, (model) ? model->name : "",
|
entNum, eventType, distance, classname, codeClassname, (model) ? model->name : "",
|
||||||
targetName, target, "", scriptLinkName, team, "",
|
targetName, target, "", scriptLinkName, team, "",
|
||||||
point[0], point[1], point[2], angles[0], angles[1], angles[2], 0);
|
point[0], point[1], point[2], angles[0], angles[1], angles[2], 0);
|
||||||
@ -442,7 +444,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
assert(filenameSuffix);
|
assert(filenameSuffix);
|
||||||
|
|
||||||
const auto* fileName = Utils::String::VA("%s%s%s%s", "EntInfo", (*filenameSuffix) ? "_" : "", filenameSuffix, ".csv");
|
const auto* fileName = VA("%s%s%s%s", "EntInfo", (*filenameSuffix) ? "_" : "", filenameSuffix, ".csv");
|
||||||
Logger::Print(Game::CON_CHANNEL_SERVER, "Opening file \"{}\" for writing.\n", fileName);
|
Logger::Print(Game::CON_CHANNEL_SERVER, "Opening file \"{}\" for writing.\n", fileName);
|
||||||
|
|
||||||
auto h = Game::FS_FOpenTextFileWrite(fileName);
|
auto h = Game::FS_FOpenTextFileWrite(fileName);
|
||||||
@ -464,9 +466,11 @@ namespace Components
|
|||||||
|
|
||||||
const auto* line = EntInfoLine(i);
|
const auto* line = EntInfoLine(i);
|
||||||
const auto lineLen = std::strlen(line);
|
const auto lineLen = std::strlen(line);
|
||||||
|
|
||||||
assert(line);
|
assert(line);
|
||||||
assert(lineLen);
|
assert(lineLen);
|
||||||
Game::FS_Write(line, lineLen, h);
|
|
||||||
|
Game::FS_Write(line, static_cast<int>(lineLen), h);
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::FS_FCloseFile(h);
|
Game::FS_FCloseFile(h);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "ConnectProtocol.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -7,17 +8,17 @@ namespace Components
|
|||||||
|
|
||||||
bool ConnectProtocol::IsEvaluated()
|
bool ConnectProtocol::IsEvaluated()
|
||||||
{
|
{
|
||||||
return ConnectProtocol::Evaluated;
|
return Evaluated;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConnectProtocol::Used()
|
bool ConnectProtocol::Used()
|
||||||
{
|
{
|
||||||
if (!ConnectProtocol::IsEvaluated())
|
if (!IsEvaluated())
|
||||||
{
|
{
|
||||||
ConnectProtocol::EvaluateProtocol();
|
EvaluateProtocol();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (!ConnectProtocol::ConnectString.empty());
|
return (!ConnectString.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConnectProtocol::InstallProtocol()
|
bool ConnectProtocol::InstallProtocol()
|
||||||
@ -25,8 +26,8 @@ namespace Components
|
|||||||
HKEY hKey = nullptr;
|
HKEY hKey = nullptr;
|
||||||
std::string data;
|
std::string data;
|
||||||
|
|
||||||
char ownPth[MAX_PATH] = {0};
|
char ownPth[MAX_PATH]{};
|
||||||
char workdir[MAX_PATH] = {0};
|
char workdir[MAX_PATH]{};
|
||||||
|
|
||||||
DWORD dwsize = MAX_PATH;
|
DWORD dwsize = MAX_PATH;
|
||||||
HMODULE hModule = GetModuleHandleA(nullptr);
|
HMODULE hModule = GetModuleHandleA(nullptr);
|
||||||
@ -65,13 +66,13 @@ namespace Components
|
|||||||
LONG openRes = RegOpenKeyExA(HKEY_CURRENT_USER, "SOFTWARE\\Classes\\iw4x\\shell\\open\\command", 0, KEY_ALL_ACCESS, &hKey);
|
LONG openRes = RegOpenKeyExA(HKEY_CURRENT_USER, "SOFTWARE\\Classes\\iw4x\\shell\\open\\command", 0, KEY_ALL_ACCESS, &hKey);
|
||||||
if (openRes == ERROR_SUCCESS)
|
if (openRes == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
char regred[MAX_PATH] = { 0 };
|
char regred[MAX_PATH]{};
|
||||||
|
|
||||||
// Check if the game has been moved.
|
// Check if the game has been moved.
|
||||||
openRes = RegQueryValueExA(hKey, nullptr, nullptr, nullptr, reinterpret_cast<BYTE*>(regred), &dwsize);
|
openRes = RegQueryValueExA(hKey, nullptr, nullptr, nullptr, reinterpret_cast<BYTE*>(regred), &dwsize);
|
||||||
if (openRes == ERROR_SUCCESS)
|
if (openRes == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
char* endPtr = strstr(regred, "\" \"%1\"");
|
auto* endPtr = std::strstr(regred, "\" \"%1\"");
|
||||||
if (endPtr != nullptr)
|
if (endPtr != nullptr)
|
||||||
{
|
{
|
||||||
*endPtr = 0;
|
*endPtr = 0;
|
||||||
@ -82,7 +83,8 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
RegCloseKey(hKey);
|
RegCloseKey(hKey);
|
||||||
if (strcmp(regred + 1, ownPth))
|
|
||||||
|
if (std::strcmp(regred + 1, ownPth) != 0)
|
||||||
{
|
{
|
||||||
RegDeleteKeyA(HKEY_CURRENT_USER, "SOFTWARE\\Classes\\iw4x");
|
RegDeleteKeyA(HKEY_CURRENT_USER, "SOFTWARE\\Classes\\iw4x");
|
||||||
}
|
}
|
||||||
@ -173,8 +175,8 @@ namespace Components
|
|||||||
|
|
||||||
void ConnectProtocol::EvaluateProtocol()
|
void ConnectProtocol::EvaluateProtocol()
|
||||||
{
|
{
|
||||||
if (ConnectProtocol::Evaluated) return;
|
if (Evaluated) return;
|
||||||
ConnectProtocol::Evaluated = true;
|
Evaluated = true;
|
||||||
|
|
||||||
std::string cmdLine = GetCommandLineA();
|
std::string cmdLine = GetCommandLineA();
|
||||||
|
|
||||||
@ -190,15 +192,15 @@ namespace Components
|
|||||||
cmdLine = cmdLine.substr(0, pos);
|
cmdLine = cmdLine.substr(0, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectProtocol::ConnectString = cmdLine;
|
ConnectString = cmdLine;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConnectProtocol::Invocation()
|
void ConnectProtocol::Invocation()
|
||||||
{
|
{
|
||||||
if (ConnectProtocol::Used())
|
if (Used())
|
||||||
{
|
{
|
||||||
Command::Execute(Utils::String::VA("connect %s", ConnectProtocol::ConnectString.data()), false);
|
Command::Execute(std::format("connect {}", ConnectString), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,28 +211,28 @@ namespace Components
|
|||||||
// IPC handler
|
// IPC handler
|
||||||
IPCPipe::On("connect", [](const std::string& data)
|
IPCPipe::On("connect", [](const std::string& data)
|
||||||
{
|
{
|
||||||
Command::Execute(Utils::String::VA("connect %s", data.data()), false);
|
Command::Execute(std::format("connect {}", data), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Invocation handler
|
// Invocation handler
|
||||||
Scheduler::OnGameInitialized(ConnectProtocol::Invocation, Scheduler::Pipeline::MAIN);
|
Scheduler::OnGameInitialized(Invocation, Scheduler::Pipeline::MAIN);
|
||||||
|
|
||||||
ConnectProtocol::InstallProtocol();
|
InstallProtocol();
|
||||||
ConnectProtocol::EvaluateProtocol();
|
EvaluateProtocol();
|
||||||
|
|
||||||
// Fire protocol handlers
|
// Fire protocol handlers
|
||||||
// Make sure this happens after the pipe-initialization!
|
// Make sure this happens after the pipe-initialization!
|
||||||
if (ConnectProtocol::Used())
|
if (Used())
|
||||||
{
|
{
|
||||||
if (!Singleton::IsFirstInstance())
|
if (!Singleton::IsFirstInstance())
|
||||||
{
|
{
|
||||||
IPCPipe::Write("connect", ConnectProtocol::ConnectString);
|
IPCPipe::Write("connect", ConnectString);
|
||||||
ExitProcess(0);
|
ExitProcess(0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Only skip intro here, invocation will be done later.
|
// Only skip intro here, invocation will be done later.
|
||||||
Utils::Hook::Set<BYTE>(0x60BECF, 0xEB);
|
Utils::Hook::Set<std::uint8_t>(0x60BECF, 0xEB);
|
||||||
|
|
||||||
Scheduler::Once([]
|
Scheduler::Once([]
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
#undef MOUSE_MOVED
|
#include "Console.hpp"
|
||||||
|
|
||||||
|
#include <version.hpp>
|
||||||
|
|
||||||
|
#ifdef MOUSE_MOVED
|
||||||
|
#undef MOUSE_MOVED
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <curses.h>
|
#include <curses.h>
|
||||||
|
|
||||||
#define REMOVE_HEADERBAR 1
|
#define REMOVE_HEADERBAR 1
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "D3D9Ex.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "Debug.hpp"
|
||||||
|
|
||||||
#include "Game/Engine/ScopedCriticalSection.hpp"
|
#include "Game/Engine/ScopedCriticalSection.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
@ -6,7 +8,7 @@ namespace Components
|
|||||||
const Game::dvar_t* Debug::DebugOverlay;
|
const Game::dvar_t* Debug::DebugOverlay;
|
||||||
const Game::dvar_t* Debug::BugName;
|
const Game::dvar_t* Debug::BugName;
|
||||||
|
|
||||||
Game::dvar_t** Debug::PlayerDebugHealth = reinterpret_cast<Game::dvar_t**>(0x7A9F7C);
|
const Game::dvar_t* Debug::PlayerDebugHealth;
|
||||||
|
|
||||||
const char* Debug::PMFlagsValues[] =
|
const char* Debug::PMFlagsValues[] =
|
||||||
{
|
{
|
||||||
@ -92,15 +94,15 @@ namespace Components
|
|||||||
"EF_SOFT",
|
"EF_SOFT",
|
||||||
};
|
};
|
||||||
|
|
||||||
const char Debug::strButtons[] =
|
const char Debug::StrButtons[] =
|
||||||
{
|
{
|
||||||
'\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x0E', '\x0F', '\x10',
|
'\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x0E', '\x0F', '\x10',
|
||||||
'\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\0'
|
'\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\0'
|
||||||
};
|
};
|
||||||
|
|
||||||
const char Debug::strTemplate[] = "%s: %s All those moments will be lost in time, like tears in rain.";
|
const char Debug::StrTemplate[] = "%s: %s All those moments will be lost in time, like tears in rain.";
|
||||||
|
|
||||||
const float Debug::colorWhite[] = {1.0f, 1.0f, 1.0f, 1.0f};
|
const float Debug::ColorWhite[] = {1.0f, 1.0f, 1.0f, 1.0f};
|
||||||
|
|
||||||
std::string Debug::BuildPMFlagsString(const Game::playerState_s* ps)
|
std::string Debug::BuildPMFlagsString(const Game::playerState_s* ps)
|
||||||
{
|
{
|
||||||
@ -161,19 +163,19 @@ namespace Components
|
|||||||
auto* const font2 = Game::UI_GetFontHandle(scrPlace, 6, MY_SCALE2);
|
auto* const font2 = Game::UI_GetFontHandle(scrPlace, 6, MY_SCALE2);
|
||||||
|
|
||||||
Game::UI_DrawText(scrPlace, "Client View of Flags", maxChars, font2, -60.0f, 0, 1, 1,
|
Game::UI_DrawText(scrPlace, "Client View of Flags", maxChars, font2, -60.0f, 0, 1, 1,
|
||||||
MY_SCALE2, colorWhite, 1);
|
MY_SCALE2, ColorWhite, 1);
|
||||||
|
|
||||||
const auto pmf = BuildPMFlagsString(&cgameGlob->predictedPlayerState);
|
const auto pmf = BuildPMFlagsString(&cgameGlob->predictedPlayerState);
|
||||||
Game::UI_DrawText(scrPlace, pmf.data(), maxChars, font1, 30.0f, MY_Y, 1, 1, MY_SCALE_2, colorWhite, 3);
|
Game::UI_DrawText(scrPlace, pmf.data(), maxChars, font1, 30.0f, MY_Y, 1, 1, MY_SCALE_2, ColorWhite, 3);
|
||||||
|
|
||||||
const auto pof = BuildPOFlagsString(&cgameGlob->predictedPlayerState);
|
const auto pof = BuildPOFlagsString(&cgameGlob->predictedPlayerState);
|
||||||
Game::UI_DrawText(scrPlace, pof.data(), maxChars, font1, 350.0f, MY_Y, 1, 1, MY_SCALE_2, colorWhite, 3);
|
Game::UI_DrawText(scrPlace, pof.data(), maxChars, font1, 350.0f, MY_Y, 1, 1, MY_SCALE_2, ColorWhite, 3);
|
||||||
|
|
||||||
const auto plf = BuildPLFlagsString(&cgameGlob->predictedPlayerState);
|
const auto plf = BuildPLFlagsString(&cgameGlob->predictedPlayerState);
|
||||||
Game::UI_DrawText(scrPlace, plf.data(), maxChars, font1, 350.0f, 250.0f, 1, 1, MY_SCALE_2, colorWhite, 3);
|
Game::UI_DrawText(scrPlace, plf.data(), maxChars, font1, 350.0f, 250.0f, 1, 1, MY_SCALE_2, ColorWhite, 3);
|
||||||
|
|
||||||
const auto pef = BuildPEFlagsString(&cgameGlob->predictedPlayerState);
|
const auto pef = BuildPEFlagsString(&cgameGlob->predictedPlayerState);
|
||||||
Game::UI_DrawText(scrPlace, pef.data(), maxChars, font1, 525.0f, MY_Y, 1, 1, MY_SCALE_2, colorWhite, 3);
|
Game::UI_DrawText(scrPlace, pef.data(), maxChars, font1, 525.0f, MY_Y, 1, 1, MY_SCALE_2, ColorWhite, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Debug::CG_DrawDebugPlayerHealth(const int localClientNum)
|
void Debug::CG_DrawDebugPlayerHealth(const int localClientNum)
|
||||||
@ -182,7 +184,7 @@ namespace Components
|
|||||||
constexpr float color1[] = {0.0f, 0.0f, 0.0f, 1.0f};
|
constexpr float color1[] = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||||
constexpr float color2[] = {0.0f, 1.0f, 0.0f, 1.0f};
|
constexpr float color2[] = {0.0f, 1.0f, 0.0f, 1.0f};
|
||||||
|
|
||||||
assert((*PlayerDebugHealth)->current.enabled);
|
assert(PlayerDebugHealth->current.enabled);
|
||||||
const auto* cgameGlob = Game::cgArray;
|
const auto* cgameGlob = Game::cgArray;
|
||||||
|
|
||||||
if (cgameGlob->predictedPlayerState.stats[0] && cgameGlob->predictedPlayerState.stats[2])
|
if (cgameGlob->predictedPlayerState.stats[0] && cgameGlob->predictedPlayerState.stats[2])
|
||||||
@ -219,25 +221,25 @@ namespace Components
|
|||||||
auto* const font5 = Game::UI_GetFontHandle(scrPlace, 5, 0.4f);
|
auto* const font5 = Game::UI_GetFontHandle(scrPlace, 5, 0.4f);
|
||||||
auto* const font6 = Game::UI_GetFontHandle(scrPlace, 6, 0.4f);
|
auto* const font6 = Game::UI_GetFontHandle(scrPlace, 6, 0.4f);
|
||||||
|
|
||||||
sprintf_s(strFinal, strTemplate, font1->fontName, strButtons);
|
sprintf_s(strFinal, StrTemplate, font1->fontName, StrButtons);
|
||||||
Game::UI_FilterStringForButtonAnimation(strFinal, sizeof(strFinal));
|
Game::UI_FilterStringForButtonAnimation(strFinal, sizeof(strFinal));
|
||||||
Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits<int>::max(), font1, MY_X, 10.0f, 1, 1, 0.4f, colorWhite, 3);
|
Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits<int>::max(), font1, MY_X, 10.0f, 1, 1, 0.4f, ColorWhite, 3);
|
||||||
|
|
||||||
sprintf_s(strFinal, strTemplate, font2->fontName, strButtons);
|
sprintf_s(strFinal, StrTemplate, font2->fontName, StrButtons);
|
||||||
Game::UI_FilterStringForButtonAnimation(strFinal, sizeof(strFinal));
|
Game::UI_FilterStringForButtonAnimation(strFinal, sizeof(strFinal));
|
||||||
Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits<int>::max(), font2, MY_X, 35.0f, 1, 1, 0.4f, colorWhite, 3);
|
Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits<int>::max(), font2, MY_X, 35.0f, 1, 1, 0.4f, ColorWhite, 3);
|
||||||
|
|
||||||
sprintf_s(strFinal, strTemplate, font3->fontName, strButtons);
|
sprintf_s(strFinal, StrTemplate, font3->fontName, StrButtons);
|
||||||
Game::UI_FilterStringForButtonAnimation(strFinal, sizeof(strFinal));
|
Game::UI_FilterStringForButtonAnimation(strFinal, sizeof(strFinal));
|
||||||
Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits<int>::max(), font3, MY_X, 60.0f, 1, 1, 0.4f, colorWhite, 3);
|
Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits<int>::max(), font3, MY_X, 60.0f, 1, 1, 0.4f, ColorWhite, 3);
|
||||||
|
|
||||||
sprintf_s(strFinal, strTemplate, font5->fontName, strButtons);
|
sprintf_s(strFinal, StrTemplate, font5->fontName, StrButtons);
|
||||||
Game::UI_FilterStringForButtonAnimation(strFinal, sizeof(strFinal));
|
Game::UI_FilterStringForButtonAnimation(strFinal, sizeof(strFinal));
|
||||||
Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits<int>::max(), font5, MY_X, 85.0f, 1, 1, 0.4f, colorWhite, 3);
|
Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits<int>::max(), font5, MY_X, 85.0f, 1, 1, 0.4f, ColorWhite, 3);
|
||||||
|
|
||||||
sprintf_s(strFinal, strTemplate, font6->fontName, strButtons);
|
sprintf_s(strFinal, StrTemplate, font6->fontName, StrButtons);
|
||||||
Game::UI_FilterStringForButtonAnimation(strFinal, sizeof(strFinal));
|
Game::UI_FilterStringForButtonAnimation(strFinal, sizeof(strFinal));
|
||||||
Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits<int>::max(), font6, MY_X, 110.0f, 1, 1, 0.4f, colorWhite, 3);
|
Game::UI_DrawText(scrPlace, strFinal, std::numeric_limits<int>::max(), font6, MY_X, 110.0f, 1, 1, 0.4f, ColorWhite, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Debug::CG_DrawDebugOverlays_Hk(const int localClientNum)
|
void Debug::CG_DrawDebugOverlays_Hk(const int localClientNum)
|
||||||
@ -254,7 +256,7 @@ namespace Components
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((*PlayerDebugHealth)->current.enabled)
|
if (PlayerDebugHealth->current.enabled)
|
||||||
{
|
{
|
||||||
CG_DrawDebugPlayerHealth(localClientNum);
|
CG_DrawDebugPlayerHealth(localClientNum);
|
||||||
}
|
}
|
||||||
@ -304,10 +306,31 @@ namespace Components
|
|||||||
|
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
Logger::PrintError(1, "CopyFile failed({}) {} {}\n", GetLastError(), "console_mp.log", newFileName);
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "CopyFile failed({}) {} {}\n", GetLastError(), "console_mp.log", newFileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Debug::Com_BugNameInc_f()
|
||||||
|
{
|
||||||
|
char buf[260]{};
|
||||||
|
|
||||||
|
if (std::strlen(BugName->current.string) < 4)
|
||||||
|
{
|
||||||
|
Game::Dvar_SetString(BugName, "bug0");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::strncmp(BugName->current.string, "bug", 3) != 0)
|
||||||
|
{
|
||||||
|
Game::Dvar_SetString(BugName, "bug0");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto n = std::strtol(BugName->current.string + 3, nullptr, 10);
|
||||||
|
sprintf_s(buf, "bug%d", n + 1);
|
||||||
|
Game::Dvar_SetString(BugName, buf);
|
||||||
|
}
|
||||||
|
|
||||||
void Debug::CL_InitDebugDvars()
|
void Debug::CL_InitDebugDvars()
|
||||||
{
|
{
|
||||||
static const char* debugOverlayNames_0[] =
|
static const char* debugOverlayNames_0[] =
|
||||||
@ -327,6 +350,12 @@ namespace Components
|
|||||||
Game::DVAR_CHEAT | Game::DVAR_CODINFO, "Name appended to the copied console log");
|
Game::DVAR_CHEAT | Game::DVAR_CODINFO, "Name appended to the copied console log");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Game::dvar_t* Debug::Dvar_Register_PlayerDebugHealth(const char* name, bool value, [[maybe_unused]] std::uint16_t flags, const char* description)
|
||||||
|
{
|
||||||
|
PlayerDebugHealth = Game::Dvar_RegisterBool(name, value, Game::DVAR_NONE, description);
|
||||||
|
return PlayerDebugHealth;
|
||||||
|
}
|
||||||
|
|
||||||
Debug::Debug()
|
Debug::Debug()
|
||||||
{
|
{
|
||||||
Scheduler::Once(CL_InitDebugDvars, Scheduler::Pipeline::MAIN);
|
Scheduler::Once(CL_InitDebugDvars, Scheduler::Pipeline::MAIN);
|
||||||
@ -336,8 +365,11 @@ namespace Components
|
|||||||
|
|
||||||
Utils::Hook::Set<void(*)()>(0x60BCEA, Com_Assert_f);
|
Utils::Hook::Set<void(*)()>(0x60BCEA, Com_Assert_f);
|
||||||
|
|
||||||
|
Utils::Hook(0x4487F7, Dvar_Register_PlayerDebugHealth, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
Command::Add("bug", Com_Bug_f);
|
Command::Add("bug", Com_Bug_f);
|
||||||
|
Command::Add("bug_name_inc", Com_BugNameInc_f);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,15 +12,15 @@ namespace Components
|
|||||||
static const Game::dvar_t* BugName;
|
static const Game::dvar_t* BugName;
|
||||||
|
|
||||||
// Game dvars
|
// Game dvars
|
||||||
static Game::dvar_t** PlayerDebugHealth;
|
static const Game::dvar_t* PlayerDebugHealth;
|
||||||
|
|
||||||
static const char* PMFlagsValues[];
|
static const char* PMFlagsValues[];
|
||||||
static const char* POFlagsValues[];
|
static const char* POFlagsValues[];
|
||||||
static const char* PLFlagsValues[];
|
static const char* PLFlagsValues[];
|
||||||
static const char* PEFlagsValues[];
|
static const char* PEFlagsValues[];
|
||||||
|
|
||||||
static const char strButtons[];
|
static const char StrButtons[];
|
||||||
static const char strTemplate[];
|
static const char StrTemplate[];
|
||||||
|
|
||||||
static constexpr auto MY_SCALE2 = 0.5f;
|
static constexpr auto MY_SCALE2 = 0.5f;
|
||||||
static constexpr auto MY_SCALE_2 = 0.201f;
|
static constexpr auto MY_SCALE_2 = 0.201f;
|
||||||
@ -28,7 +28,7 @@ namespace Components
|
|||||||
static constexpr auto MY_X = -25.0f;
|
static constexpr auto MY_X = -25.0f;
|
||||||
static constexpr auto MY_Y = 20.0f;
|
static constexpr auto MY_Y = 20.0f;
|
||||||
|
|
||||||
static const float colorWhite[];
|
static const float ColorWhite[];
|
||||||
|
|
||||||
static std::string BuildPMFlagsString(const Game::playerState_s* ps);
|
static std::string BuildPMFlagsString(const Game::playerState_s* ps);
|
||||||
static std::string BuildPOFlagsString(const Game::playerState_s* ps);
|
static std::string BuildPOFlagsString(const Game::playerState_s* ps);
|
||||||
@ -43,7 +43,9 @@ namespace Components
|
|||||||
|
|
||||||
static void Com_Assert_f();
|
static void Com_Assert_f();
|
||||||
static void Com_Bug_f(Command::Params* params);
|
static void Com_Bug_f(Command::Params* params);
|
||||||
|
static void Com_BugNameInc_f();
|
||||||
|
|
||||||
static void CL_InitDebugDvars();
|
static void CL_InitDebugDvars();
|
||||||
|
static const Game::dvar_t* Dvar_Register_PlayerDebugHealth(const char* name, bool value, std::uint16_t flags, const char* description);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "CardTitles.hpp"
|
||||||
|
#include "ClanTags.hpp"
|
||||||
|
#include "ServerCommands.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -114,7 +117,7 @@ namespace Components
|
|||||||
if (!partyEnable) // Time wrapping should not occur in party servers, but yeah...
|
if (!partyEnable) // Time wrapping should not occur in party servers, but yeah...
|
||||||
{
|
{
|
||||||
if (mapname.empty()) mapname = "mp_rust";
|
if (mapname.empty()) mapname = "mp_rust";
|
||||||
Command::Execute(Utils::String::VA("map %s", mapname.data()), true);
|
Command::Execute(std::format("map {}", mapname), true);
|
||||||
}
|
}
|
||||||
}, Scheduler::Pipeline::SERVER);
|
}, Scheduler::Pipeline::SERVER);
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "Discovery.hpp"
|
||||||
|
#include "ServerList.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -7,39 +9,42 @@ namespace Components
|
|||||||
std::thread Discovery::Thread;
|
std::thread Discovery::Thread;
|
||||||
std::string Discovery::Challenge;
|
std::string Discovery::Challenge;
|
||||||
|
|
||||||
|
Dvar::Var Discovery::NetDiscoveryPortRangeMin;
|
||||||
|
Dvar::Var Discovery::NetDiscoveryPortRangeMax;
|
||||||
|
|
||||||
void Discovery::Perform()
|
void Discovery::Perform()
|
||||||
{
|
{
|
||||||
Discovery::IsPerforming = true;
|
IsPerforming = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Discovery::Discovery()
|
Discovery::Discovery()
|
||||||
{
|
{
|
||||||
Dvar::Register<int>("net_discoveryPortRangeMin", 25000, 0, 65535, Game::DVAR_ARCHIVE, "Minimum scan range port for local server discovery");
|
NetDiscoveryPortRangeMin = Dvar::Register<int>("net_discoveryPortRangeMin", 25000, 0, 65535, Game::DVAR_NONE, "Minimum scan range port for local server discovery");
|
||||||
Dvar::Register<int>("net_discoveryPortRangeMax", 35000, 1, 65536, Game::DVAR_ARCHIVE, "Maximum scan range port for local server discovery");
|
NetDiscoveryPortRangeMax = Dvar::Register<int>("net_discoveryPortRangeMax", 35000, 1, 65536, Game::DVAR_NONE, "Maximum scan range port for local server discovery");
|
||||||
|
|
||||||
// An additional thread prevents lags
|
// An additional thread prevents lags
|
||||||
// Not sure if that's the best way though
|
// Not sure if that's the best way though
|
||||||
Discovery::IsPerforming = false;
|
IsPerforming = false;
|
||||||
Discovery::IsTerminating = false;
|
IsTerminating = false;
|
||||||
Discovery::Thread = std::thread([]()
|
Thread = std::thread([]
|
||||||
{
|
{
|
||||||
while (!Discovery::IsTerminating)
|
while (!IsTerminating)
|
||||||
{
|
{
|
||||||
if (Discovery::IsPerforming)
|
if (IsPerforming)
|
||||||
{
|
{
|
||||||
int start = Game::Sys_Milliseconds();
|
const auto start = Game::Sys_Milliseconds();
|
||||||
|
|
||||||
Logger::Print("Starting local server discovery...\n");
|
Logger::Print("Starting local server discovery...\n");
|
||||||
|
|
||||||
Discovery::Challenge = Utils::Cryptography::Rand::GenerateChallenge();
|
Challenge = Utils::Cryptography::Rand::GenerateChallenge();
|
||||||
|
|
||||||
unsigned int minPort = Dvar::Var("net_discoveryPortRangeMin").get<unsigned int>();
|
const auto minPort = NetDiscoveryPortRangeMin.get<unsigned int>();
|
||||||
unsigned int maxPort = Dvar::Var("net_discoveryPortRangeMax").get<unsigned int>();
|
const auto maxPort = NetDiscoveryPortRangeMax.get<unsigned int>();
|
||||||
Network::BroadcastRange(minPort, maxPort, Utils::String::VA("discovery %s", Discovery::Challenge.data()));
|
Network::BroadcastRange(minPort, maxPort, std::format("discovery {}", Challenge));
|
||||||
|
|
||||||
Logger::Print("Discovery sent within {}ms, awaiting responses...\n", Game::Sys_Milliseconds() - start);
|
Logger::Print("Discovery sent within {}ms, awaiting responses...\n", Game::Sys_Milliseconds() - start);
|
||||||
|
|
||||||
Discovery::IsPerforming = false;
|
IsPerforming = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::this_thread::sleep_for(50ms);
|
std::this_thread::sleep_for(50ms);
|
||||||
@ -70,7 +75,7 @@ namespace Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Utils::ParseChallenge(data) != Discovery::Challenge)
|
if (Utils::ParseChallenge(data) != Challenge)
|
||||||
{
|
{
|
||||||
Logger::Print("Received discovery with invalid challenge from: {}\n", address.getString());
|
Logger::Print("Received discovery with invalid challenge from: {}\n", address.getString());
|
||||||
return;
|
return;
|
||||||
@ -87,12 +92,12 @@ namespace Components
|
|||||||
|
|
||||||
void Discovery::preDestroy()
|
void Discovery::preDestroy()
|
||||||
{
|
{
|
||||||
Discovery::IsPerforming = false;
|
IsPerforming = false;
|
||||||
Discovery::IsTerminating = true;
|
IsTerminating = true;
|
||||||
|
|
||||||
if (Discovery::Thread.joinable())
|
if (Thread.joinable())
|
||||||
{
|
{
|
||||||
Discovery::Thread.join();
|
Thread.join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,5 +16,8 @@ namespace Components
|
|||||||
static bool IsPerforming;
|
static bool IsPerforming;
|
||||||
static std::thread Thread;
|
static std::thread Thread;
|
||||||
static std::string Challenge;
|
static std::string Challenge;
|
||||||
|
|
||||||
|
static Dvar::Var NetDiscoveryPortRangeMin;
|
||||||
|
static Dvar::Var NetDiscoveryPortRangeMax;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
#include "GSC/Script.hpp"
|
#include "Download.hpp"
|
||||||
|
#include "ServerInfo.hpp"
|
||||||
|
|
||||||
#include <mongoose.h>
|
#include <mongoose.h>
|
||||||
|
|
||||||
@ -10,19 +11,19 @@ namespace Components
|
|||||||
Download::ClientDownload Download::CLDownload;
|
Download::ClientDownload Download::CLDownload;
|
||||||
|
|
||||||
std::thread Download::ServerThread;
|
std::thread Download::ServerThread;
|
||||||
bool Download::Terminate;
|
volatile bool Download::Terminate;
|
||||||
bool Download::ServerRunning;
|
bool Download::ServerRunning;
|
||||||
|
|
||||||
#pragma region Client
|
#pragma region Client
|
||||||
|
|
||||||
void Download::InitiateMapDownload(const std::string& map, bool needPassword)
|
void Download::InitiateMapDownload(const std::string& map, bool needPassword)
|
||||||
{
|
{
|
||||||
Download::InitiateClientDownload(map, needPassword, true);
|
InitiateClientDownload(map, needPassword, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Download::InitiateClientDownload(const std::string& mod, bool needPassword, bool map)
|
void Download::InitiateClientDownload(const std::string& mod, bool needPassword, bool map)
|
||||||
{
|
{
|
||||||
if (Download::CLDownload.running) return;
|
if (CLDownload.running) return;
|
||||||
|
|
||||||
Scheduler::Once([]
|
Scheduler::Once([]
|
||||||
{
|
{
|
||||||
@ -43,20 +44,20 @@ namespace Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Download::CLDownload.hashedPassword = Utils::String::DumpHex(Utils::Cryptography::SHA256::Compute(password), "");
|
CLDownload.hashedPassword = Utils::String::DumpHex(Utils::Cryptography::SHA256::Compute(password), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
Download::CLDownload.running = true;
|
CLDownload.running = true;
|
||||||
Download::CLDownload.isMap = map;
|
CLDownload.isMap = map;
|
||||||
Download::CLDownload.mod = mod;
|
CLDownload.mod = mod;
|
||||||
Download::CLDownload.terminateThread = false;
|
CLDownload.terminateThread = false;
|
||||||
Download::CLDownload.totalBytes = 0;
|
CLDownload.totalBytes = 0;
|
||||||
Download::CLDownload.lastTimeStamp = 0;
|
CLDownload.lastTimeStamp = 0;
|
||||||
Download::CLDownload.downBytes = 0;
|
CLDownload.downBytes = 0;
|
||||||
Download::CLDownload.timeStampBytes = 0;
|
CLDownload.timeStampBytes = 0;
|
||||||
Download::CLDownload.isPrivate = needPassword;
|
CLDownload.isPrivate = needPassword;
|
||||||
Download::CLDownload.target = Party::Target();
|
CLDownload.target = Party::Target();
|
||||||
Download::CLDownload.thread = std::thread(Download::ModDownloader, &Download::CLDownload);
|
CLDownload.thread = std::thread(ModDownloader, &CLDownload);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Download::ParseModList(ClientDownload* download, const std::string& list)
|
bool Download::ParseModList(ClientDownload* download, const std::string& list)
|
||||||
@ -93,7 +94,7 @@ namespace Components
|
|||||||
const auto name = file.at("name").get<std::string>();
|
const auto name = file.at("name").get<std::string>();
|
||||||
const auto size = file.at("size").get<std::size_t>();
|
const auto size = file.at("size").get<std::size_t>();
|
||||||
|
|
||||||
Download::ClientDownload::File fileEntry;
|
ClientDownload::File fileEntry;
|
||||||
fileEntry.name = name;
|
fileEntry.name = name;
|
||||||
fileEntry.hash = hash;
|
fileEntry.hash = hash;
|
||||||
fileEntry.size = size;
|
fileEntry.size = size;
|
||||||
@ -120,7 +121,7 @@ namespace Components
|
|||||||
|
|
||||||
auto file = download->files[index];
|
auto file = download->files[index];
|
||||||
|
|
||||||
std::string path = download->mod + "/" + file.name;
|
auto path = download->mod + "/" + file.name;
|
||||||
if (download->isMap)
|
if (download->isMap)
|
||||||
{
|
{
|
||||||
path = "usermaps/" + path;
|
path = "usermaps/" + path;
|
||||||
@ -128,8 +129,7 @@ namespace Components
|
|||||||
|
|
||||||
if (Utils::IO::FileExists(path))
|
if (Utils::IO::FileExists(path))
|
||||||
{
|
{
|
||||||
std::string data = Utils::IO::ReadFile(path);
|
auto data = Utils::IO::ReadFile(path);
|
||||||
|
|
||||||
if (data.size() == file.size && Utils::String::DumpHex(Utils::Cryptography::SHA256::Compute(data), "") == file.hash)
|
if (data.size() == file.size && Utils::String::DumpHex(Utils::Cryptography::SHA256::Compute(data), "") == file.hash)
|
||||||
{
|
{
|
||||||
download->totalBytes += file.size;
|
download->totalBytes += file.size;
|
||||||
@ -186,7 +186,7 @@ namespace Components
|
|||||||
|
|
||||||
Logger::Print("Downloading from url {}\n", url);
|
Logger::Print("Downloading from url {}\n", url);
|
||||||
|
|
||||||
Download::FileDownload fDownload;
|
FileDownload fDownload;
|
||||||
fDownload.file = file;
|
fDownload.file = file;
|
||||||
fDownload.index = index;
|
fDownload.index = index;
|
||||||
fDownload.download = download;
|
fDownload.download = download;
|
||||||
@ -200,7 +200,7 @@ namespace Components
|
|||||||
fDownload.downloading = true;
|
fDownload.downloading = true;
|
||||||
|
|
||||||
Utils::WebIO webIO;
|
Utils::WebIO webIO;
|
||||||
webIO.setProgressCallback([&fDownload, &webIO](size_t bytes, size_t)
|
webIO.setProgressCallback([&fDownload, &webIO](std::size_t bytes, std::size_t)
|
||||||
{
|
{
|
||||||
if(!fDownload.downloading || fDownload.download->terminateThread)
|
if(!fDownload.downloading || fDownload.download->terminateThread)
|
||||||
{
|
{
|
||||||
@ -208,7 +208,7 @@ namespace Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Download::DownloadProgress(&fDownload, bytes - fDownload.receivedBytes);
|
DownloadProgress(&fDownload, bytes - fDownload.receivedBytes);
|
||||||
});
|
});
|
||||||
|
|
||||||
bool result = false;
|
bool result = false;
|
||||||
@ -232,13 +232,13 @@ namespace Components
|
|||||||
|
|
||||||
void Download::ModDownloader(ClientDownload* download)
|
void Download::ModDownloader(ClientDownload* download)
|
||||||
{
|
{
|
||||||
if (!download) download = &Download::CLDownload;
|
if (!download) download = &CLDownload;
|
||||||
|
|
||||||
std::string host = "http://" + download->target.getString();
|
const auto host = "http://" + download->target.getString();
|
||||||
|
|
||||||
std::string listUrl = host + (download->isMap ? "/map" : "/list") + (download->isPrivate ? ("?password=" + download->hashedPassword) : "");
|
const auto listUrl = host + (download->isMap ? "/map" : "/list") + (download->isPrivate ? ("?password=" + download->hashedPassword) : "");
|
||||||
|
|
||||||
std::string list = Utils::WebIO("IW4x", listUrl).setTimeout(5000)->get();
|
const auto list = Utils::WebIO("IW4x", listUrl).setTimeout(5000)->get();
|
||||||
if (list.empty())
|
if (list.empty())
|
||||||
{
|
{
|
||||||
if (download->terminateThread) return;
|
if (download->terminateThread) return;
|
||||||
@ -257,7 +257,7 @@ namespace Components
|
|||||||
|
|
||||||
if (download->terminateThread) return;
|
if (download->terminateThread) return;
|
||||||
|
|
||||||
if (!Download::ParseModList(download, list))
|
if (!ParseModList(download, list))
|
||||||
{
|
{
|
||||||
if (download->terminateThread) return;
|
if (download->terminateThread) return;
|
||||||
|
|
||||||
@ -278,15 +278,15 @@ namespace Components
|
|||||||
static std::string mod;
|
static std::string mod;
|
||||||
mod = download->mod;
|
mod = download->mod;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < download->files.size(); ++i)
|
for (std::size_t i = 0; i < download->files.size(); ++i)
|
||||||
{
|
{
|
||||||
if (download->terminateThread) return;
|
if (download->terminateThread) return;
|
||||||
|
|
||||||
if (!Download::DownloadFile(download, i))
|
if (!DownloadFile(download, i))
|
||||||
{
|
{
|
||||||
if (download->terminateThread) return;
|
if (download->terminateThread) return;
|
||||||
|
|
||||||
mod = Utils::String::VA("Failed to download file: %s!", download->files[i].name.data());
|
mod = std::format("Failed to download file: {}!", download->files[i].name);
|
||||||
download->thread.detach();
|
download->thread.detach();
|
||||||
download->clear();
|
download->clear();
|
||||||
|
|
||||||
@ -321,7 +321,6 @@ namespace Components
|
|||||||
Scheduler::Once([]
|
Scheduler::Once([]
|
||||||
{
|
{
|
||||||
Game::Dvar_SetString(*Game::fs_gameDirVar, mod.data());
|
Game::Dvar_SetString(*Game::fs_gameDirVar, mod.data());
|
||||||
const_cast<Game::dvar_t*>(*Game::fs_gameDirVar)->modified = true;
|
|
||||||
|
|
||||||
mod.clear();
|
mod.clear();
|
||||||
|
|
||||||
@ -370,7 +369,7 @@ namespace Components
|
|||||||
}, Scheduler::Pipeline::CLIENT);
|
}, Scheduler::Pipeline::CLIENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
int delta = Game::Sys_Milliseconds() - fDownload->download->lastTimeStamp;
|
auto delta = Game::Sys_Milliseconds() - fDownload->download->lastTimeStamp;
|
||||||
if (delta > 300)
|
if (delta > 300)
|
||||||
{
|
{
|
||||||
bool doFormat = fDownload->download->lastTimeStamp != 0;
|
bool doFormat = fDownload->download->lastTimeStamp != 0;
|
||||||
@ -387,7 +386,7 @@ namespace Components
|
|||||||
|
|
||||||
if (doFormat)
|
if (doFormat)
|
||||||
{
|
{
|
||||||
static size_t dlTsBytes;
|
static std::size_t dlTsBytes;
|
||||||
static int dlDelta, dlTimeLeft;
|
static int dlDelta, dlTimeLeft;
|
||||||
dlTimeLeft = timeLeft;
|
dlTimeLeft = timeLeft;
|
||||||
dlDelta = delta;
|
dlDelta = delta;
|
||||||
@ -435,7 +434,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
// Score and ping are irrelevant
|
// Score and ping are irrelevant
|
||||||
const auto* name = Game::PartyHost_GetMemberName(Game::g_lobbyData, i);
|
const auto* name = Game::PartyHost_GetMemberName(Game::g_lobbyData, i);
|
||||||
if (!name || *name == '\0') continue;
|
if (name == nullptr || *name == '\0') continue;
|
||||||
|
|
||||||
playerInfo["name"] = name;
|
playerInfo["name"] = name;
|
||||||
}
|
}
|
||||||
@ -452,7 +451,7 @@ namespace Components
|
|||||||
static nlohmann::json jsonList;
|
static nlohmann::json jsonList;
|
||||||
static auto handled = false;
|
static auto handled = false;
|
||||||
|
|
||||||
const std::string fs_gameDirVar = (*Game::fs_gameDirVar)->current.string;
|
const std::filesystem::path fs_gameDirVar((*Game::fs_gameDirVar)->current.string);
|
||||||
|
|
||||||
if (!fs_gameDirVar.empty() && !handled)
|
if (!fs_gameDirVar.empty() && !handled)
|
||||||
{
|
{
|
||||||
@ -460,17 +459,20 @@ namespace Components
|
|||||||
|
|
||||||
std::vector<nlohmann::json> fileList;
|
std::vector<nlohmann::json> fileList;
|
||||||
|
|
||||||
const auto path = Dvar::Var("fs_basepath").get<std::string>() + "\\" + fs_gameDirVar;
|
const auto path = Dvar::Var("fs_basepath").get<std::string>() / fs_gameDirVar;
|
||||||
auto list = FileSystem::GetSysFileList(path, "iwd", false);
|
auto list = FileSystem::GetSysFileList(path.generic_string(), "iwd", false);
|
||||||
list.emplace_back("mod.ff");
|
list.emplace_back("mod.ff");
|
||||||
|
|
||||||
for (const auto& file : list)
|
for (const auto& file : list)
|
||||||
{
|
{
|
||||||
std::string filename = path + "\\" + file;
|
auto filename = path / file;
|
||||||
if (file.find("_svr_") == std::string::npos)
|
if (file.find("_svr_") != std::string::npos)
|
||||||
{
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
std::unordered_map<std::string, nlohmann::json> jsonFileList;
|
std::unordered_map<std::string, nlohmann::json> jsonFileList;
|
||||||
std::string fileBuffer = Utils::IO::ReadFile(filename);
|
auto fileBuffer = Utils::IO::ReadFile(filename.generic_string());
|
||||||
if (fileBuffer.empty())
|
if (fileBuffer.empty())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@ -482,7 +484,6 @@ namespace Components
|
|||||||
|
|
||||||
fileList.emplace_back(jsonFileList);
|
fileList.emplace_back(jsonFileList);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
jsonList = fileList;
|
jsonList = fileList;
|
||||||
}
|
}
|
||||||
@ -507,14 +508,15 @@ namespace Components
|
|||||||
|
|
||||||
mapNamePre = mapName;
|
mapNamePre = mapName;
|
||||||
|
|
||||||
const auto path = Dvar::Var("fs_basepath").get<std::string>() + "\\usermaps\\" + mapName;
|
const std::filesystem::path basePath(Dvar::Var("fs_basepath").get<std::string>());
|
||||||
|
const auto path = basePath / "usermaps" / mapName;
|
||||||
|
|
||||||
for (auto i = 0; i < ARRAYSIZE(Maps::UserMapFiles); ++i)
|
for (std::size_t i = 0; i < ARRAYSIZE(Maps::UserMapFiles); ++i)
|
||||||
{
|
{
|
||||||
const auto filename = path + "\\" + mapName + Maps::UserMapFiles[i];
|
const auto filename = std::format("{}\\{}{}", path.generic_string(), mapName, Maps::UserMapFiles[i]);
|
||||||
|
|
||||||
std::map<std::string, nlohmann::json> file;
|
std::unordered_map<std::string, nlohmann::json> file;
|
||||||
std::string fileBuffer = Utils::IO::ReadFile(filename);
|
auto fileBuffer = Utils::IO::ReadFile(filename);
|
||||||
if (fileBuffer.empty())
|
if (fileBuffer.empty())
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@ -551,7 +553,7 @@ namespace Components
|
|||||||
|
|
||||||
auto mapName = (Party::IsInUserMapLobby() ? Dvar::Var("ui_mapname").get<std::string>() : Maps::GetUserMap()->getName());
|
auto mapName = (Party::IsInUserMapLobby() ? Dvar::Var("ui_mapname").get<std::string>() : Maps::GetUserMap()->getName());
|
||||||
auto isValidFile = false;
|
auto isValidFile = false;
|
||||||
for (auto i = 0; i < ARRAYSIZE(Maps::UserMapFiles); ++i)
|
for (std::size_t i = 0; i < ARRAYSIZE(Maps::UserMapFiles); ++i)
|
||||||
{
|
{
|
||||||
if (url == (mapName + Maps::UserMapFiles[i]))
|
if (url == (mapName + Maps::UserMapFiles[i]))
|
||||||
{
|
{
|
||||||
@ -672,6 +674,10 @@ namespace Components
|
|||||||
|
|
||||||
Download::Download()
|
Download::Download()
|
||||||
{
|
{
|
||||||
|
AssertSize(Game::va_info_t, 0x804);
|
||||||
|
AssertSize(jmp_buf, 0x40);
|
||||||
|
AssertSize(Game::TraceThreadInfo, 0x8);
|
||||||
|
|
||||||
if (Dedicated::IsEnabled())
|
if (Dedicated::IsEnabled())
|
||||||
{
|
{
|
||||||
mg_mgr_init(&Mgr);
|
mg_mgr_init(&Mgr);
|
||||||
@ -685,11 +691,13 @@ namespace Components
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Download::ServerRunning = true;
|
ServerRunning = true;
|
||||||
Download::Terminate = false;
|
Terminate = false;
|
||||||
Download::ServerThread = std::thread([]
|
ServerThread = Utils::Thread::CreateNamedThread("Mongoose", []
|
||||||
{
|
{
|
||||||
while (!Download::Terminate)
|
Com_InitThreadData();
|
||||||
|
|
||||||
|
while (!Terminate)
|
||||||
{
|
{
|
||||||
mg_mgr_poll(&Mgr, 100);
|
mg_mgr_poll(&Mgr, 100);
|
||||||
}
|
}
|
||||||
@ -706,7 +714,7 @@ namespace Components
|
|||||||
|
|
||||||
UIScript::Add("mod_download_cancel", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
UIScript::Add("mod_download_cancel", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
||||||
{
|
{
|
||||||
Download::CLDownload.clear();
|
CLDownload.clear();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -715,14 +723,11 @@ namespace Components
|
|||||||
Dvar::Register<bool>("sv_wwwDownload", false, Game::DVAR_NONE, "Set to true to enable downloading maps/mods from an external server.");
|
Dvar::Register<bool>("sv_wwwDownload", false, Game::DVAR_NONE, "Set to true to enable downloading maps/mods from an external server.");
|
||||||
Dvar::Register<const char*>("sv_wwwBaseUrl", "", Game::DVAR_NONE, "Set to the base url for the external map download.");
|
Dvar::Register<const char*>("sv_wwwBaseUrl", "", Game::DVAR_NONE, "Set to the base url for the external map download.");
|
||||||
}, Scheduler::Pipeline::MAIN);
|
}, Scheduler::Pipeline::MAIN);
|
||||||
|
|
||||||
Script::AddFunction("HttpGet", Script::ShowDeprecationWarning);
|
|
||||||
Script::AddFunction("HttpCancel", Script::ShowDeprecationWarning);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Download::~Download()
|
Download::~Download()
|
||||||
{
|
{
|
||||||
if (Download::ServerRunning)
|
if (ServerRunning)
|
||||||
{
|
{
|
||||||
mg_mgr_free(&Mgr);
|
mg_mgr_free(&Mgr);
|
||||||
}
|
}
|
||||||
@ -730,15 +735,15 @@ namespace Components
|
|||||||
|
|
||||||
void Download::preDestroy()
|
void Download::preDestroy()
|
||||||
{
|
{
|
||||||
Download::Terminate = true;
|
Terminate = true;
|
||||||
if (Download::ServerThread.joinable())
|
if (ServerThread.joinable())
|
||||||
{
|
{
|
||||||
Download::ServerThread.join();
|
ServerThread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Dedicated::IsEnabled())
|
if (!Dedicated::IsEnabled())
|
||||||
{
|
{
|
||||||
Download::CLDownload.clear();
|
CLDownload.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ namespace Components
|
|||||||
|
|
||||||
static ClientDownload CLDownload;
|
static ClientDownload CLDownload;
|
||||||
static std::thread ServerThread;
|
static std::thread ServerThread;
|
||||||
static bool Terminate;
|
static volatile bool Terminate;
|
||||||
static bool ServerRunning;
|
static bool ServerRunning;
|
||||||
|
|
||||||
static void DownloadProgress(FileDownload* fDownload, std::size_t bytes);
|
static void DownloadProgress(FileDownload* fDownload, std::size_t bytes);
|
||||||
|
@ -186,24 +186,24 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> Dvar::Var Dvar::Register(const char* dvarName, bool value, Flag flag, const char* description)
|
template<> Dvar::Var Dvar::Register(const char* dvarName, bool value, std::uint16_t flag, const char* description)
|
||||||
{
|
{
|
||||||
return Game::Dvar_RegisterBool(dvarName, value, flag.val, description);
|
return Game::Dvar_RegisterBool(dvarName, value, flag, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> Dvar::Var Dvar::Register(const char* dvarName, const char* value, Flag flag, const char* description)
|
template<> Dvar::Var Dvar::Register(const char* dvarName, const char* value, std::uint16_t flag, const char* description)
|
||||||
{
|
{
|
||||||
return Game::Dvar_RegisterString(dvarName, value, flag.val, description);
|
return Game::Dvar_RegisterString(dvarName, value, flag, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> Dvar::Var Dvar::Register(const char* dvarName, int value, int min, int max, Flag flag, const char* description)
|
template<> Dvar::Var Dvar::Register(const char* dvarName, int value, int min, int max, std::uint16_t flag, const char* description)
|
||||||
{
|
{
|
||||||
return Game::Dvar_RegisterInt(dvarName, value, min, max, flag.val, description);
|
return Game::Dvar_RegisterInt(dvarName, value, min, max, flag, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> Dvar::Var Dvar::Register(const char* dvarName, float value, float min, float max, Flag flag, const char* description)
|
template<> Dvar::Var Dvar::Register(const char* dvarName, float value, float min, float max, std::uint16_t flag, const char* description)
|
||||||
{
|
{
|
||||||
return Game::Dvar_RegisterFloat(dvarName, value, min, max, flag.val, description);
|
return Game::Dvar_RegisterFloat(dvarName, value, min, max, flag, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dvar::ResetDvarsValue()
|
void Dvar::ResetDvarsValue()
|
||||||
@ -229,7 +229,8 @@ namespace Components
|
|||||||
// 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;
|
||||||
|
|
||||||
std::string saneName = TextRenderer::StripAllTextIcons(TextRenderer::StripColors(Utils::String::Trim(name)));
|
Utils::String::Trim(name);
|
||||||
|
auto saneName = TextRenderer::StripAllTextIcons(TextRenderer::StripColors(name));
|
||||||
if (saneName.size() < 3 || (saneName[0] == '[' && saneName[1] == '{'))
|
if (saneName.size() < 3 || (saneName[0] == '[' && saneName[1] == '{'))
|
||||||
{
|
{
|
||||||
Logger::PrintError(Game::CON_CHANNEL_ERROR, "Username '{}' is invalid. It must at least be 3 characters long and not appear empty!\n", name);
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "Username '{}' is invalid. It must at least be 3 characters long and not appear empty!\n", name);
|
||||||
@ -240,7 +241,7 @@ namespace Components
|
|||||||
lastValidName = name;
|
lastValidName = name;
|
||||||
Friends::UpdateName();
|
Friends::UpdateName();
|
||||||
}
|
}
|
||||||
}, Scheduler::CLIENT, 3s); // Don't need to do this every frame
|
}, Scheduler::Pipeline::CLIENT, 3s); // Don't need to do this every frame
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string username = "Unknown Soldier";
|
std::string username = "Unknown Soldier";
|
||||||
@ -306,10 +307,10 @@ namespace Components
|
|||||||
{
|
{
|
||||||
if (!Utils::IO::FileExists(ArchiveDvarPath))
|
if (!Utils::IO::FileExists(ArchiveDvarPath))
|
||||||
{
|
{
|
||||||
Utils::IO::WriteFile(ArchiveDvarPath, "// generated by IW4x, do not modify\n");
|
Utils::IO::WriteFile(ArchiveDvarPath, "// generated by IW4x, do not modify\n", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::IO::WriteFile(ArchiveDvarPath, Utils::String::VA("seta %s \"%s\"\n", var->name, Game::Dvar_DisplayableValue(var)), true);
|
Utils::IO::WriteFile(ArchiveDvarPath, std::format("set {} \"{}\"\n", var->name, Game::Dvar_DisplayableValue(var)), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dvar::DvarSetFromStringByName_Stub(const char* dvarName, const char* value)
|
void Dvar::DvarSetFromStringByName_Stub(const char* dvarName, const char* value)
|
||||||
@ -320,7 +321,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
if (AreArchiveDvarsProtected())
|
if (AreArchiveDvarsProtected())
|
||||||
{
|
{
|
||||||
Logger::Print(Game::CON_CHANNEL_CONSOLEONLY, "Not allowing server to override saved dvar '{}'\n", dvarName);
|
Logger::Print(Game::CON_CHANNEL_CONSOLEONLY, "Not allowing server to override saved dvar '{}'\n", dvar->name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,6 +331,12 @@ namespace Components
|
|||||||
SaveArchiveDvar(dvar);
|
SaveArchiveDvar(dvar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dvar != nullptr && std::strcmp(dvar->name, "com_errorResolveCommand") == 0)
|
||||||
|
{
|
||||||
|
Logger::Print(Game::CON_CHANNEL_CONSOLEONLY, "Not allowing server to set '{}'\n", dvar->name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Utils::Hook::Call<void(const char*, const char*)>(0x4F52E0)(dvarName, value);
|
Utils::Hook::Call<void(const char*, const char*)>(0x4F52E0)(dvarName, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -392,15 +399,29 @@ namespace Components
|
|||||||
// un-cheat cg_fovscale and add archive flags
|
// un-cheat cg_fovscale and add archive flags
|
||||||
Utils::Hook::Xor<std::uint8_t>(0x4F8E68, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE);
|
Utils::Hook::Xor<std::uint8_t>(0x4F8E68, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE);
|
||||||
|
|
||||||
|
// un-cheat cg_fovMin and add archive flags
|
||||||
|
Utils::Hook::Xor<std::uint8_t>(0x4F8E9D, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE);
|
||||||
|
|
||||||
// un-cheat cg_debugInfoCornerOffset and add archive flags
|
// un-cheat cg_debugInfoCornerOffset and add archive flags
|
||||||
Utils::Hook::Xor<std::uint8_t>(0x4F8FC2, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE);
|
Utils::Hook::Xor<std::uint8_t>(0x4F8FC2, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE);
|
||||||
|
|
||||||
// remove archive flags for cg_hudchatposition
|
// un-cheat cg_drawGun
|
||||||
|
Utils::Hook::Set<std::uint8_t>(0x4F8DC6, Game::DVAR_NONE);
|
||||||
|
|
||||||
|
// un-cheat cg_draw2D
|
||||||
|
Utils::Hook::Set<std::uint8_t>(0x4F8EEE, Game::DVAR_NONE);
|
||||||
|
|
||||||
|
// remove archive flags for cg_hudChatPosition
|
||||||
Utils::Hook::Xor<std::uint8_t>(0x4F9992, Game::DVAR_ARCHIVE);
|
Utils::Hook::Xor<std::uint8_t>(0x4F9992, Game::DVAR_ARCHIVE);
|
||||||
|
|
||||||
// remove write protection from fs_game
|
// remove write protection from fs_game
|
||||||
Utils::Hook::Xor<std::uint32_t>(0x6431EA, Game::DVAR_INIT);
|
Utils::Hook::Xor<std::uint32_t>(0x6431EA, Game::DVAR_INIT);
|
||||||
|
|
||||||
|
// cheat protect g_hardcore
|
||||||
|
Utils::Hook::Xor<std::uint32_t>(0x5E374F, Game::DVAR_CHEAT);
|
||||||
|
Utils::Hook::Xor<std::uint32_t>(0x4D3689, Game::DVAR_CHEAT);
|
||||||
|
Utils::Hook::Xor<std::uint32_t>(0x4197C3, Game::DVAR_CHEAT);
|
||||||
|
|
||||||
// set cg_fov max to 160.0
|
// set cg_fov max to 160.0
|
||||||
// because that's the max on SP
|
// because that's the max on SP
|
||||||
static float cg_Fov = 160.0f;
|
static float cg_Fov = 160.0f;
|
||||||
|
@ -5,15 +5,6 @@ namespace Components
|
|||||||
class Dvar : public Component
|
class Dvar : public Component
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
class Flag
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Flag(Game::DvarFlags flag) : val(flag) {}
|
|
||||||
Flag(std::uint16_t flag) : Flag(static_cast<Game::DvarFlags>(flag)) {}
|
|
||||||
|
|
||||||
Game::DvarFlags val;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Var
|
class Var
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -44,8 +35,8 @@ namespace Components
|
|||||||
~Dvar();
|
~Dvar();
|
||||||
|
|
||||||
// Only strings and bools use this type of declaration
|
// Only strings and bools use this type of declaration
|
||||||
template<typename T> static Var Register(const char* dvarName, T value, Flag flag, const char* description);
|
template<typename T> static Var Register(const char* dvarName, T value, std::uint16_t flag, const char* description);
|
||||||
template<typename T> static Var Register(const char* dvarName, T value, T min, T max, Flag flag, const char* description);
|
template<typename T> static Var Register(const char* dvarName, T value, T min, T max, std::uint16_t flag, const char* description);
|
||||||
|
|
||||||
static void ResetDvarsValue();
|
static void ResetDvarsValue();
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "Elevators.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
|
@ -1,24 +1,17 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "Console.hpp"
|
||||||
|
|
||||||
|
#include <version.hpp>
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
Utils::Hook Exception::SetFilterHook;
|
Utils::Hook Exception::SetFilterHook;
|
||||||
int Exception::MiniDumpType;
|
int Exception::MiniDumpType;
|
||||||
|
|
||||||
__declspec(noreturn) void Exception::ErrorLongJmp(jmp_buf _Buf, int _Value)
|
__declspec(noreturn) void Exception::LongJmp_Internal_Stub(jmp_buf env, int status)
|
||||||
{
|
|
||||||
if (!*reinterpret_cast<DWORD*>(0x1AD7EB4))
|
|
||||||
{
|
|
||||||
TerminateProcess(GetCurrentProcess(), 1337);
|
|
||||||
}
|
|
||||||
|
|
||||||
longjmp(_Buf, _Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
__declspec(noreturn) void Exception::LongJmp(jmp_buf _Buf, int _Value)
|
|
||||||
{
|
{
|
||||||
AssetHandler::ResetBypassState();
|
AssetHandler::ResetBypassState();
|
||||||
longjmp(_Buf, _Value);
|
Game::longjmp_internal(env, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Exception::SuspendProcess()
|
void Exception::SuspendProcess()
|
||||||
@ -74,7 +67,7 @@ namespace Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto lock = GlobalLock(hMem);
|
auto* lock = GlobalLock(hMem);
|
||||||
if (lock != nullptr)
|
if (lock != nullptr)
|
||||||
{
|
{
|
||||||
std::memcpy(lock, error.data(), error.size() + 1);
|
std::memcpy(lock, error.data(), error.size() + 1);
|
||||||
@ -105,17 +98,15 @@ namespace Components
|
|||||||
errorStr = Utils::String::VA("Fatal error (0x%08X) at 0x%08X.\nCopy exception address to clipboard?", ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo->ExceptionRecord->ExceptionAddress);
|
errorStr = Utils::String::VA("Fatal error (0x%08X) at 0x%08X.\nCopy exception address to clipboard?", ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo->ExceptionRecord->ExceptionAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Exception::SuspendProcess();
|
|
||||||
|
|
||||||
// Message should be copied to the keyboard if no button is pressed
|
// Message should be copied to the keyboard if no button is pressed
|
||||||
if (MessageBoxA(nullptr, errorStr.data(), nullptr, MB_YESNO | MB_ICONERROR) == IDYES)
|
if (MessageBoxA(nullptr, errorStr.data(), nullptr, MB_YESNO | MB_ICONERROR) == IDYES)
|
||||||
{
|
{
|
||||||
Exception::CopyMessageToClipboard(Utils::String::VA("0x%08X", ExceptionInfo->ExceptionRecord->ExceptionAddress));
|
CopyMessageToClipboard(Utils::String::VA("0x%08X", ExceptionInfo->ExceptionRecord->ExceptionAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Flags::HasFlag("bigminidumps"))
|
if (Flags::HasFlag("bigminidumps"))
|
||||||
{
|
{
|
||||||
Exception::SetMiniDumpType(true, false);
|
SetMiniDumpType(true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Current executable name
|
// Current executable name
|
||||||
@ -125,30 +116,29 @@ namespace Components
|
|||||||
PathRemoveExtensionA(exeFileName);
|
PathRemoveExtensionA(exeFileName);
|
||||||
|
|
||||||
// Generate filename
|
// Generate filename
|
||||||
char filenameFriendlyTime[MAX_PATH];
|
char filenameFriendlyTime[MAX_PATH]{};
|
||||||
__time64_t time;
|
__time64_t time;
|
||||||
tm ltime;
|
tm ltime;
|
||||||
_time64(&time);
|
_time64(&time);
|
||||||
_localtime64_s(<ime, &time);
|
_localtime64_s(<ime, &time);
|
||||||
strftime(filenameFriendlyTime, sizeof(filenameFriendlyTime) - 1, "%Y%m%d%H%M%S", <ime);
|
strftime(filenameFriendlyTime, sizeof(filenameFriendlyTime) - 1, "%Y%m%d%H%M%S", <ime);
|
||||||
|
|
||||||
// Combine with queuedMinidumpsFolder
|
// Combine with queued MinidumpsFolder
|
||||||
char filename[MAX_PATH] = { 0 };
|
char filename[MAX_PATH]{};
|
||||||
Utils::IO::CreateDir("minidumps");
|
CreateDirectoryA("minidumps", nullptr);
|
||||||
PathCombineA(filename, "minidumps\\", Utils::String::VA("%s-" VERSION "-%s.dmp", exeFileName, filenameFriendlyTime));
|
PathCombineA(filename, "minidumps\\", Utils::String::VA("%s-" VERSION "-%s.dmp", exeFileName, filenameFriendlyTime));
|
||||||
|
|
||||||
DWORD fileShare = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
constexpr auto fileShare = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||||
HANDLE hFile = CreateFileA(filename, GENERIC_WRITE | GENERIC_READ, fileShare, nullptr, (fileShare & FILE_SHARE_WRITE) > 0 ? OPEN_ALWAYS : OPEN_EXISTING, NULL, nullptr);
|
HANDLE hFile = CreateFileA(filename, GENERIC_WRITE | GENERIC_READ, fileShare, nullptr, (fileShare & FILE_SHARE_WRITE) > 0 ? OPEN_ALWAYS : OPEN_EXISTING, NULL, nullptr);
|
||||||
MINIDUMP_EXCEPTION_INFORMATION ex = { GetCurrentThreadId(), ExceptionInfo, FALSE };
|
MINIDUMP_EXCEPTION_INFORMATION ex = { GetCurrentThreadId(), ExceptionInfo, FALSE };
|
||||||
if (!MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, static_cast<MINIDUMP_TYPE>(Exception::MiniDumpType), &ex, nullptr, nullptr))
|
if (!MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, static_cast<MINIDUMP_TYPE>(MiniDumpType), &ex, nullptr, nullptr))
|
||||||
{
|
{
|
||||||
MessageBoxA(nullptr, Utils::String::VA("There was an error creating the minidump (%s)! Hit OK to close the program.", Utils::GetLastWindowsError().data()), "Minidump Error", MB_OK | MB_ICONERROR);
|
MessageBoxA(nullptr, Utils::String::Format("There was an error creating the minidump ({})! Hit OK to close the program.", Utils::GetLastWindowsError()), "ERROR", MB_OK | MB_ICONERROR);
|
||||||
OutputDebugStringA("Failed to create new minidump!");
|
OutputDebugStringA("Failed to create new minidump!");
|
||||||
Utils::OutputDebugLastError();
|
Utils::OutputDebugLastError();
|
||||||
TerminateProcess(GetCurrentProcess(), ExceptionInfo->ExceptionRecord->ExceptionCode);
|
TerminateProcess(GetCurrentProcess(), ExceptionInfo->ExceptionRecord->ExceptionCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
//if (ExceptionInfo->ExceptionRecord->ExceptionFlags == EXCEPTION_NONCONTINUABLE)
|
|
||||||
{
|
{
|
||||||
TerminateProcess(GetCurrentProcess(), ExceptionInfo->ExceptionRecord->ExceptionCode);
|
TerminateProcess(GetCurrentProcess(), ExceptionInfo->ExceptionRecord->ExceptionCode);
|
||||||
}
|
}
|
||||||
@ -156,54 +146,50 @@ namespace Components
|
|||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI Exception::SetUnhandledExceptionFilterStub(LPTOP_LEVEL_EXCEPTION_FILTER)
|
|
||||||
{
|
|
||||||
Exception::SetFilterHook.uninstall();
|
|
||||||
LPTOP_LEVEL_EXCEPTION_FILTER retval = SetUnhandledExceptionFilter(&Exception::ExceptionFilter);
|
|
||||||
Exception::SetFilterHook.install();
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
LPTOP_LEVEL_EXCEPTION_FILTER Exception::Hook()
|
|
||||||
{
|
|
||||||
return SetUnhandledExceptionFilter(&Exception::ExceptionFilter);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Exception::SetMiniDumpType(bool codeseg, bool dataseg)
|
void Exception::SetMiniDumpType(bool codeseg, bool dataseg)
|
||||||
{
|
{
|
||||||
Exception::MiniDumpType = MiniDumpIgnoreInaccessibleMemory;
|
MiniDumpType = MiniDumpIgnoreInaccessibleMemory;
|
||||||
Exception::MiniDumpType |= MiniDumpWithHandleData;
|
MiniDumpType |= MiniDumpWithHandleData;
|
||||||
Exception::MiniDumpType |= MiniDumpScanMemory;
|
MiniDumpType |= MiniDumpScanMemory;
|
||||||
Exception::MiniDumpType |= MiniDumpWithProcessThreadData;
|
MiniDumpType |= MiniDumpWithProcessThreadData;
|
||||||
Exception::MiniDumpType |= MiniDumpWithFullMemoryInfo;
|
MiniDumpType |= MiniDumpWithFullMemoryInfo;
|
||||||
Exception::MiniDumpType |= MiniDumpWithThreadInfo;
|
MiniDumpType |= MiniDumpWithThreadInfo;
|
||||||
//Exception::MiniDumpType |= MiniDumpWithModuleHeaders;
|
|
||||||
|
|
||||||
if (codeseg)
|
if (codeseg)
|
||||||
{
|
{
|
||||||
Exception::MiniDumpType |= MiniDumpWithCodeSegs;
|
MiniDumpType |= MiniDumpWithCodeSegs;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dataseg)
|
if (dataseg)
|
||||||
{
|
{
|
||||||
Exception::MiniDumpType |= MiniDumpWithDataSegs;
|
MiniDumpType |= MiniDumpWithDataSegs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI Exception::SetUnhandledExceptionFilter_Stub(LPTOP_LEVEL_EXCEPTION_FILTER)
|
||||||
|
{
|
||||||
|
SetFilterHook.uninstall();
|
||||||
|
LPTOP_LEVEL_EXCEPTION_FILTER result = ::SetUnhandledExceptionFilter(&ExceptionFilter);
|
||||||
|
SetFilterHook.install();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Exception::Exception()
|
Exception::Exception()
|
||||||
{
|
{
|
||||||
Exception::SetMiniDumpType(Flags::HasFlag("bigminidumps"), Flags::HasFlag("reallybigminidumps"));
|
SetMiniDumpType(Flags::HasFlag("bigminidumps"), Flags::HasFlag("reallybigminidumps"));
|
||||||
|
|
||||||
#if !defined(DEBUG) || defined(FORCE_EXCEPTION_HANDLER)
|
SetFilterHook.initialize(::SetUnhandledExceptionFilter, SetUnhandledExceptionFilter_Stub, HOOK_JUMP);
|
||||||
Exception::SetFilterHook.initialize(SetUnhandledExceptionFilter, Exception::SetUnhandledExceptionFilterStub, HOOK_JUMP);
|
SetFilterHook.install();
|
||||||
Exception::SetFilterHook.install();
|
|
||||||
|
|
||||||
SetUnhandledExceptionFilter(&Exception::ExceptionFilter);
|
::SetUnhandledExceptionFilter(&ExceptionFilter);
|
||||||
#endif
|
|
||||||
|
|
||||||
//Utils::Hook(0x4B241F, Exception::ErrorLongJmp, HOOK_CALL).install()->quick();
|
Utils::Hook(0x4B241F, LongJmp_Internal_Stub, HOOK_CALL).install()->quick();
|
||||||
Utils::Hook(0x6B8898, Exception::LongJmp, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x61DB44, LongJmp_Internal_Stub, HOOK_CALL).install()->quick();
|
||||||
|
Utils::Hook(0x61F17D, LongJmp_Internal_Stub, HOOK_CALL).install()->quick();
|
||||||
|
Utils::Hook(0x61F248, LongJmp_Internal_Stub, HOOK_CALL).install()->quick();
|
||||||
|
Utils::Hook(0x61F5E7, LongJmp_Internal_Stub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef MAP_TEST
|
||||||
Command::Add("mapTest", [](Command::Params* params)
|
Command::Add("mapTest", [](Command::Params* params)
|
||||||
{
|
{
|
||||||
Game::UI_UpdateArenas();
|
Game::UI_UpdateArenas();
|
||||||
@ -225,6 +211,6 @@ namespace Components
|
|||||||
|
|
||||||
Exception::~Exception()
|
Exception::~Exception()
|
||||||
{
|
{
|
||||||
Exception::SetFilterHook.uninstall();
|
SetFilterHook.uninstall();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,12 +15,12 @@ namespace Components
|
|||||||
private:
|
private:
|
||||||
static void SuspendProcess();
|
static void SuspendProcess();
|
||||||
static LONG WINAPI ExceptionFilter(LPEXCEPTION_POINTERS ExceptionInfo);
|
static LONG WINAPI ExceptionFilter(LPEXCEPTION_POINTERS ExceptionInfo);
|
||||||
static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilterStub(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter);
|
static __declspec(noreturn) void LongJmp_Internal_Stub(jmp_buf env, int status);
|
||||||
static __declspec(noreturn) void ErrorLongJmp(jmp_buf _Buf, int _Value);
|
|
||||||
static __declspec(noreturn) void LongJmp(jmp_buf _Buf, int _Value);
|
|
||||||
|
|
||||||
static void CopyMessageToClipboard(const std::string& error);
|
static void CopyMessageToClipboard(const std::string& error);
|
||||||
|
|
||||||
|
static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilter_Stub(LPTOP_LEVEL_EXCEPTION_FILTER);
|
||||||
|
|
||||||
static int MiniDumpType;
|
static int MiniDumpType;
|
||||||
static Utils::Hook SetFilterHook;
|
static Utils::Hook SetFilterHook;
|
||||||
};
|
};
|
||||||
|
@ -235,10 +235,11 @@ namespace Components
|
|||||||
const char* dir = Dvar::Var("fs_basepath").get<const char*>();
|
const char* dir = Dvar::Var("fs_basepath").get<const char*>();
|
||||||
|
|
||||||
std::vector<std::string> paths;
|
std::vector<std::string> paths;
|
||||||
std::string modDir = Dvar::Var("fs_game").get<std::string>();
|
auto modDir = Dvar::Var("fs_game").get<std::string>();
|
||||||
|
|
||||||
if ((file == "mod"s || file == "mod.ff"s) && !modDir.empty())
|
if ((file == "mod"s || file == "mod.ff"s) && !modDir.empty())
|
||||||
{
|
{
|
||||||
paths.push_back(Utils::String::VA("%s\\", modDir.data()));
|
paths.push_back(std::format("{}\\", modDir));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Utils::String::StartsWith(file, "mp_"))
|
if (Utils::String::StartsWith(file, "mp_"))
|
||||||
@ -256,17 +257,17 @@ namespace Components
|
|||||||
Utils::String::Replace(zone, "_load", "");
|
Utils::String::Replace(zone, "_load", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Utils::IO::FileExists(Utils::String::VA("usermaps\\%s\\%s.ff", zone.data(), filename.data())))
|
if (Utils::IO::FileExists(std::format("usermaps\\{}\\{}.ff", zone, filename)))
|
||||||
{
|
{
|
||||||
return Utils::String::VA("usermaps\\%s\\", zone.data());
|
return Utils::String::Format("usermaps\\{}\\", zone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::Merge(&paths, FastFiles::ZonePaths);
|
Utils::Merge(&paths, FastFiles::ZonePaths);
|
||||||
|
|
||||||
for (auto &path : paths)
|
for (auto& path : paths)
|
||||||
{
|
{
|
||||||
std::string absoluteFile = Utils::String::VA("%s\\%s%s", dir, path.data(), file);
|
auto absoluteFile = std::format("{}\\{}{}", dir, path, file);
|
||||||
|
|
||||||
// No ".ff" appended, append it manually
|
// No ".ff" appended, append it manually
|
||||||
if (!Utils::String::EndsWith(absoluteFile, ".ff"))
|
if (!Utils::String::EndsWith(absoluteFile, ".ff"))
|
||||||
@ -277,11 +278,11 @@ namespace Components
|
|||||||
// Check if FastFile exists
|
// Check if FastFile exists
|
||||||
if (Utils::IO::FileExists(absoluteFile))
|
if (Utils::IO::FileExists(absoluteFile))
|
||||||
{
|
{
|
||||||
return Utils::String::VA("%s", path.data());
|
return Utils::String::Format("{}", path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Utils::String::VA("zone\\%s\\", Game::Win_GetLanguage());
|
return Utils::String::Format("zone\\{}\\", Game::Win_GetLanguage());
|
||||||
}
|
}
|
||||||
|
|
||||||
void FastFiles::AddZonePath(const std::string& path)
|
void FastFiles::AddZonePath(const std::string& path)
|
||||||
|
@ -78,19 +78,19 @@ namespace Components
|
|||||||
Utils::Memory::Allocator allocator;
|
Utils::Memory::Allocator allocator;
|
||||||
if (!this->exists()) return std::string();
|
if (!this->exists()) return std::string();
|
||||||
|
|
||||||
int position = Game::FS_FTell(this->handle);
|
const auto position = Game::FS_FTell(this->handle);
|
||||||
this->seek(0, Game::FS_SEEK_SET);
|
this->seek(0, Game::FS_SEEK_SET);
|
||||||
|
|
||||||
char* buffer = allocator.allocateArray<char>(this->size);
|
char* buffer = allocator.allocateArray<char>(this->size);
|
||||||
if (!this->read(buffer, this->size))
|
if (!this->read(buffer, this->size))
|
||||||
{
|
{
|
||||||
this->seek(position, Game::FS_SEEK_SET);
|
this->seek(position, Game::FS_SEEK_SET);
|
||||||
return std::string();
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
this->seek(position, Game::FS_SEEK_SET);
|
this->seek(position, Game::FS_SEEK_SET);
|
||||||
|
|
||||||
return std::string(buffer, this->size);
|
return {buffer, static_cast<std::size_t>(this->size)};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileSystem::FileReader::read(void* buffer, size_t _size)
|
bool FileSystem::FileReader::read(void* buffer, size_t _size)
|
||||||
|
@ -13,6 +13,11 @@ namespace Components
|
|||||||
virtual bool exists() = 0;
|
virtual bool exists() = 0;
|
||||||
virtual std::string getName() = 0;
|
virtual std::string getName() = 0;
|
||||||
virtual std::string& getBuffer() = 0;
|
virtual std::string& getBuffer() = 0;
|
||||||
|
|
||||||
|
virtual explicit operator bool()
|
||||||
|
{
|
||||||
|
return this->exists();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class File : public AbstractFile
|
class File : public AbstractFile
|
||||||
|
@ -6,9 +6,9 @@ namespace Components
|
|||||||
|
|
||||||
bool Flags::HasFlag(const std::string& flag)
|
bool Flags::HasFlag(const std::string& flag)
|
||||||
{
|
{
|
||||||
Flags::ParseFlags();
|
ParseFlags();
|
||||||
|
|
||||||
for (const auto& entry : Flags::EnabledFlags)
|
for (const auto& entry : EnabledFlags)
|
||||||
{
|
{
|
||||||
if (Utils::String::ToLower(entry) == Utils::String::ToLower(flag))
|
if (Utils::String::ToLower(entry) == Utils::String::ToLower(flag))
|
||||||
{
|
{
|
||||||
@ -40,7 +40,7 @@ namespace Components
|
|||||||
if (wFlag[0] == L'-')
|
if (wFlag[0] == L'-')
|
||||||
{
|
{
|
||||||
wFlag.erase(wFlag.begin());
|
wFlag.erase(wFlag.begin());
|
||||||
Flags::EnabledFlags.emplace_back(Utils::String::Convert(wFlag));
|
EnabledFlags.emplace_back(Utils::String::Convert(wFlag));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,9 +48,9 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Workaround for wine
|
// Workaround for wine
|
||||||
if (Utils::IsWineEnvironment() && Dedicated::IsEnabled() && !Flags::HasFlag("console") && !Flags::HasFlag("stdout"))
|
if (Utils::IsWineEnvironment() && Dedicated::IsEnabled() && !HasFlag("console") && !HasFlag("stdout"))
|
||||||
{
|
{
|
||||||
Flags::EnabledFlags.emplace_back("stdout");
|
EnabledFlags.emplace_back("stdout");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "UIFeeder.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "IO.hpp"
|
#include "IO.hpp"
|
||||||
#include "Script.hpp"
|
#include "Script.hpp"
|
||||||
#include "ScriptExtension.hpp"
|
#include "ScriptExtension.hpp"
|
||||||
|
#include "ScriptPatches.hpp"
|
||||||
#include "ScriptStorage.hpp"
|
#include "ScriptStorage.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
@ -14,6 +15,7 @@ namespace Components
|
|||||||
Loader::Register(new IO());
|
Loader::Register(new IO());
|
||||||
Loader::Register(new Script());
|
Loader::Register(new Script());
|
||||||
Loader::Register(new ScriptExtension());
|
Loader::Register(new ScriptExtension());
|
||||||
|
Loader::Register(new ScriptPatches());
|
||||||
Loader::Register(new ScriptStorage());
|
Loader::Register(new ScriptStorage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto p = "scriptdata" / std::filesystem::path(path);
|
const auto p = "scriptdata"s / std::filesystem::path(path);
|
||||||
const auto folder = p.parent_path().string();
|
const auto folder = p.parent_path().string();
|
||||||
const auto file = p.filename().string();
|
const auto file = p.filename().string();
|
||||||
Game::Scr_AddInt(FileSystem::_DeleteFile(folder, file));
|
Game::Scr_AddInt(FileSystem::_DeleteFile(folder, file));
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
std::unordered_map<std::string, Script::ScriptFunction> Script::CustomScrFunctions;
|
std::vector<Script::ScriptFunction> Script::CustomScrFunctions;
|
||||||
std::unordered_map<std::string, Script::ScriptMethod> Script::CustomScrMethods;
|
std::vector<Script::ScriptMethod> Script::CustomScrMethods;
|
||||||
|
|
||||||
std::string Script::ScriptName;
|
std::string Script::ScriptName;
|
||||||
std::vector<std::string> Script::ScriptNameStack;
|
std::vector<std::string> Script::ScriptNameStack;
|
||||||
@ -15,29 +15,21 @@ namespace Components
|
|||||||
std::unordered_map<const char*, const char*> Script::ReplacedFunctions;
|
std::unordered_map<const char*, const char*> Script::ReplacedFunctions;
|
||||||
const char* Script::ReplacedPos = nullptr;
|
const char* Script::ReplacedPos = nullptr;
|
||||||
|
|
||||||
std::vector<int> Script::ScriptMainHandles;
|
std::unordered_map<std::string, int> Script::ScriptMainHandles;
|
||||||
std::vector<int> Script::ScriptInitHandles;
|
std::unordered_map<std::string, int> Script::ScriptInitHandles;
|
||||||
|
|
||||||
void Script::ShowDeprecationWarning()
|
|
||||||
{
|
|
||||||
Toast::Show("cardicon_gumby", "WARNING!", "You are using deprecated HttpGet/HttpCancel GSC function.", 2048);
|
|
||||||
Logger::Print(Game::CON_CHANNEL_SCRIPT, "*** DEPRECATION WARNING ***\n");
|
|
||||||
Logger::PrintError(Game::CON_CHANNEL_ERROR, "Attempted to execute deprecated built-in HttpGet/HttpCancel! These functions have been deemed unsafe and are scheduled for removal. Please update your mod!\n");
|
|
||||||
Logger::Print(Game::CON_CHANNEL_SCRIPT, "***************************\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Script::FunctionError()
|
void Script::FunctionError()
|
||||||
{
|
{
|
||||||
const auto* funcName = Game::SL_ConvertToString(Script::FunctionName);
|
const auto* funcName = Game::SL_ConvertToString(FunctionName);
|
||||||
|
|
||||||
Game::Scr_ShutdownAllocNode();
|
Game::Scr_ShutdownAllocNode();
|
||||||
|
|
||||||
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "\n");
|
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "\n");
|
||||||
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "******* script compile error *******\n");
|
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "******* script compile error *******\n");
|
||||||
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "Error: unknown function {} in {}\n", funcName, Script::ScriptName);
|
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "Error: unknown function {} in {}\n", funcName, ScriptName);
|
||||||
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "************************************\n");
|
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "************************************\n");
|
||||||
|
|
||||||
Logger::Error(Game::ERR_SCRIPT_DROP, "script compile error\nunknown function {}\n{}\n\n", funcName, Script::ScriptName);
|
Logger::Error(Game::ERR_SCRIPT_DROP, "script compile error\nunknown function {}\n{}\n\n", funcName, ScriptName);
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(naked) void Script::StoreFunctionNameStub()
|
__declspec(naked) void Script::StoreFunctionNameStub()
|
||||||
@ -45,7 +37,7 @@ namespace Components
|
|||||||
__asm
|
__asm
|
||||||
{
|
{
|
||||||
mov eax, [esp - 8h]
|
mov eax, [esp - 8h]
|
||||||
mov Script::FunctionName, ax
|
mov FunctionName, ax
|
||||||
|
|
||||||
sub esp, 0Ch
|
sub esp, 0Ch
|
||||||
push 0
|
push 0
|
||||||
@ -87,12 +79,12 @@ namespace Components
|
|||||||
|
|
||||||
void Script::StoreScriptName(const char* name)
|
void Script::StoreScriptName(const char* name)
|
||||||
{
|
{
|
||||||
Script::ScriptNameStack.push_back(Script::ScriptName);
|
ScriptNameStack.push_back(ScriptName);
|
||||||
Script::ScriptName = name;
|
ScriptName = name;
|
||||||
|
|
||||||
if (!Utils::String::EndsWith(Script::ScriptName, ".gsc"))
|
if (!Utils::String::EndsWith(ScriptName, ".gsc"))
|
||||||
{
|
{
|
||||||
Script::ScriptName.append(".gsc");
|
ScriptName.append(".gsc");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +97,7 @@ namespace Components
|
|||||||
lea ecx, [esp + 30h]
|
lea ecx, [esp + 30h]
|
||||||
push ecx
|
push ecx
|
||||||
|
|
||||||
call Script::StoreScriptName
|
call StoreScriptName
|
||||||
add esp, 4h
|
add esp, 4h
|
||||||
|
|
||||||
popad
|
popad
|
||||||
@ -120,8 +112,8 @@ namespace Components
|
|||||||
|
|
||||||
void Script::RestoreScriptName()
|
void Script::RestoreScriptName()
|
||||||
{
|
{
|
||||||
Script::ScriptName = Script::ScriptNameStack.back();
|
ScriptName = ScriptNameStack.back();
|
||||||
Script::ScriptNameStack.pop_back();
|
ScriptNameStack.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(naked) void Script::RestoreScriptNameStub()
|
__declspec(naked) void Script::RestoreScriptNameStub()
|
||||||
@ -129,7 +121,7 @@ namespace Components
|
|||||||
__asm
|
__asm
|
||||||
{
|
{
|
||||||
pushad
|
pushad
|
||||||
call Script::RestoreScriptName
|
call RestoreScriptName
|
||||||
popad
|
popad
|
||||||
|
|
||||||
mov ds:1CDEAA8h, ebp
|
mov ds:1CDEAA8h, ebp
|
||||||
@ -205,17 +197,19 @@ namespace Components
|
|||||||
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "\n");
|
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "\n");
|
||||||
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "******* script compile error *******\n");
|
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "******* script compile error *******\n");
|
||||||
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "Error: {} ", msgbuf);
|
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "Error: {} ", msgbuf);
|
||||||
Script::PrintSourcePos(Script::ScriptName.data(), offset);
|
PrintSourcePos(ScriptName.data(), offset);
|
||||||
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "************************************\n\n");
|
Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "************************************\n\n");
|
||||||
|
|
||||||
Logger::Error(Game::ERR_SCRIPT_DROP, "script compile error\n{}\n{}\n(see console for actual details)\n", msgbuf, Script::ScriptName);
|
Logger::Error(Game::ERR_SCRIPT_DROP, "script compile error\n{}\n{}\n(see console for actual details)\n", msgbuf, ScriptName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Script::Scr_LoadGameType_Stub()
|
void Script::Scr_LoadGameType_Stub()
|
||||||
{
|
{
|
||||||
for (const auto& handle : Script::ScriptMainHandles)
|
for (const auto& handle : ScriptMainHandles)
|
||||||
{
|
{
|
||||||
const auto id = Game::Scr_ExecThread(handle, 0);
|
Logger::Print("Executing '{}::main'\n", handle.first.data());
|
||||||
|
|
||||||
|
const auto id = Game::Scr_ExecThread(handle.second, 0);
|
||||||
Game::Scr_FreeThread(static_cast<std::uint16_t>(id));
|
Game::Scr_FreeThread(static_cast<std::uint16_t>(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,9 +218,11 @@ namespace Components
|
|||||||
|
|
||||||
void Script::Scr_StartupGameType_Stub()
|
void Script::Scr_StartupGameType_Stub()
|
||||||
{
|
{
|
||||||
for (const auto& handle : Script::ScriptInitHandles)
|
for (const auto& handle : ScriptInitHandles)
|
||||||
{
|
{
|
||||||
const auto id = Game::Scr_ExecThread(handle, 0);
|
Logger::Print("Executing '{}::init'\n", handle.first.data());
|
||||||
|
|
||||||
|
const auto id = Game::Scr_ExecThread(handle.second, 0);
|
||||||
Game::Scr_FreeThread(static_cast<std::uint16_t>(id));
|
Game::Scr_FreeThread(static_cast<std::uint16_t>(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,8 +233,8 @@ namespace Components
|
|||||||
void Script::GScr_LoadGameTypeScript_Stub()
|
void Script::GScr_LoadGameTypeScript_Stub()
|
||||||
{
|
{
|
||||||
// Clear handles (from previous GSC loading session)
|
// Clear handles (from previous GSC loading session)
|
||||||
Script::ScriptMainHandles.clear();
|
ScriptMainHandles.clear();
|
||||||
Script::ScriptInitHandles.clear();
|
ScriptInitHandles.clear();
|
||||||
|
|
||||||
char path[MAX_PATH]{};
|
char path[MAX_PATH]{};
|
||||||
|
|
||||||
@ -262,18 +258,17 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
Logger::Print("Script {}.gsc loaded successfully.\n", path);
|
Logger::Print("Script {}.gsc loaded successfully.\n", path);
|
||||||
Logger::Debug("Finding script handle main or init...");
|
|
||||||
|
|
||||||
const auto initHandle = Game::Scr_GetFunctionHandle(path, "init");
|
const auto initHandle = Game::Scr_GetFunctionHandle(path, "init");
|
||||||
if (initHandle != 0)
|
if (initHandle != 0)
|
||||||
{
|
{
|
||||||
Script::ScriptInitHandles.push_back(initHandle);
|
ScriptInitHandles.insert_or_assign(path, initHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto mainHandle = Game::Scr_GetFunctionHandle(path, "main");
|
const auto mainHandle = Game::Scr_GetFunctionHandle(path, "main");
|
||||||
if (mainHandle != 0)
|
if (mainHandle != 0)
|
||||||
{
|
{
|
||||||
Script::ScriptMainHandles.push_back(mainHandle);
|
ScriptMainHandles.insert_or_assign(path, mainHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow scripts with no handles
|
// Allow scripts with no handles
|
||||||
@ -283,43 +278,74 @@ namespace Components
|
|||||||
Game::GScr_LoadGameTypeScript();
|
Game::GScr_LoadGameTypeScript();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Script::AddFunction(const std::string& name, Game::BuiltinFunction func, bool type)
|
void Script::AddFunction(const std::string& name, const Game::BuiltinFunction func, const bool type)
|
||||||
{
|
{
|
||||||
Script::ScriptFunction toAdd;
|
ScriptFunction toAdd;
|
||||||
toAdd.actionFunc = func;
|
toAdd.actionFunc = func;
|
||||||
toAdd.type = type;
|
toAdd.type = type;
|
||||||
|
toAdd.aliases.push_back({Utils::String::ToLower(name)});
|
||||||
|
|
||||||
CustomScrFunctions.insert_or_assign(Utils::String::ToLower(name), toAdd);
|
CustomScrFunctions.emplace_back(toAdd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Script::AddMethod(const std::string& name, Game::BuiltinMethod func, bool type)
|
void Script::AddMethod(const std::string& name, const Game::BuiltinMethod func, const bool type)
|
||||||
{
|
{
|
||||||
Script::ScriptMethod toAdd;
|
ScriptMethod toAdd;
|
||||||
toAdd.actionFunc = func;
|
toAdd.actionFunc = func;
|
||||||
toAdd.type = type;
|
toAdd.type = type;
|
||||||
|
toAdd.aliases.push_back({Utils::String::ToLower(name)});
|
||||||
|
|
||||||
CustomScrMethods.insert_or_assign(Utils::String::ToLower(name), toAdd);
|
CustomScrMethods.emplace_back(toAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Script::AddFuncMultiple(Game::BuiltinFunction func, bool type, scriptNames aliases)
|
||||||
|
{
|
||||||
|
ScriptFunction toAdd;
|
||||||
|
auto aliasesToAdd = Utils::String::ApplyToLower(aliases);
|
||||||
|
|
||||||
|
toAdd.actionFunc = func;
|
||||||
|
toAdd.type = type;
|
||||||
|
toAdd.aliases = std::move(aliasesToAdd);
|
||||||
|
|
||||||
|
CustomScrFunctions.emplace_back(toAdd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Script::AddMethMultiple(Game::BuiltinMethod func, bool type, scriptNames aliases)
|
||||||
|
{
|
||||||
|
ScriptMethod toAdd;
|
||||||
|
auto aliasesToAdd = Utils::String::ApplyToLower(aliases);
|
||||||
|
|
||||||
|
toAdd.actionFunc = func;
|
||||||
|
toAdd.type = type;
|
||||||
|
toAdd.aliases = std::move(aliasesToAdd);
|
||||||
|
|
||||||
|
CustomScrMethods.emplace_back(toAdd);
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::BuiltinFunction Script::BuiltIn_GetFunctionStub(const char** pName, int* type)
|
Game::BuiltinFunction Script::BuiltIn_GetFunctionStub(const char** pName, int* type)
|
||||||
{
|
{
|
||||||
if (pName != nullptr)
|
if (pName != nullptr)
|
||||||
{
|
{
|
||||||
// If no function was found let's call game's function
|
const auto name = Utils::String::ToLower(*pName);
|
||||||
if (const auto itr = Script::CustomScrFunctions.find(Utils::String::ToLower(*pName)); itr != Script::CustomScrFunctions.end())
|
for (const auto& func : CustomScrFunctions)
|
||||||
{
|
{
|
||||||
*type = itr->second.type;
|
if (Utils::Contains(&func.aliases, name))
|
||||||
return itr->second.actionFunc;
|
{
|
||||||
|
*type = func.type;
|
||||||
|
return func.actionFunc;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (const auto& [name, builtin] : Script::CustomScrFunctions)
|
for (const auto& func : CustomScrFunctions)
|
||||||
{
|
{
|
||||||
Game::Scr_RegisterFunction(reinterpret_cast<int>(builtin.actionFunc), name.data());
|
const auto& name = func.aliases.at(0);
|
||||||
|
Game::Scr_RegisterFunction(reinterpret_cast<int>(func.actionFunc), name.data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If no function was found let's call game's function
|
||||||
return Utils::Hook::Call<Game::BuiltinFunction(const char**, int*)>(0x5FA2B0)(pName, type); // BuiltIn_GetFunction
|
return Utils::Hook::Call<Game::BuiltinFunction(const char**, int*)>(0x5FA2B0)(pName, type); // BuiltIn_GetFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,27 +353,32 @@ namespace Components
|
|||||||
{
|
{
|
||||||
if (pName != nullptr)
|
if (pName != nullptr)
|
||||||
{
|
{
|
||||||
// If no method was found let's call game's function
|
const auto name = Utils::String::ToLower(*pName);
|
||||||
if (const auto itr = Script::CustomScrMethods.find(Utils::String::ToLower(*pName)); itr != Script::CustomScrMethods.end())
|
for (const auto& meth : CustomScrMethods)
|
||||||
{
|
{
|
||||||
*type = itr->second.type;
|
if (Utils::Contains(&meth.aliases, name))
|
||||||
return itr->second.actionFunc;
|
{
|
||||||
|
*type = meth.type;
|
||||||
|
return meth.actionFunc;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (const auto& [name, builtin] : Script::CustomScrMethods)
|
for (const auto& meth : CustomScrMethods)
|
||||||
{
|
{
|
||||||
Game::Scr_RegisterFunction(reinterpret_cast<int>(builtin.actionFunc), name.data());
|
const auto& name = meth.aliases.at(0);
|
||||||
|
Game::Scr_RegisterFunction(reinterpret_cast<int>(meth.actionFunc), name.data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If no method was found let's call game's function
|
||||||
return Utils::Hook::Call<Game::BuiltinMethod(const char**, int*)>(0x5FA360)(pName, type); // Player_GetMethod
|
return Utils::Hook::Call<Game::BuiltinMethod(const char**, int*)>(0x5FA360)(pName, type); // Player_GetMethod
|
||||||
}
|
}
|
||||||
|
|
||||||
void Script::StoreScriptBaseProgramNum()
|
void Script::StoreScriptBaseProgramNum()
|
||||||
{
|
{
|
||||||
Script::ScriptBaseProgramNum.insert_or_assign(Utils::Hook::Get<int>(0x1CFEEF8), Script::ScriptName);
|
ScriptBaseProgramNum.insert_or_assign(Utils::Hook::Get<int>(0x1CFEEF8), ScriptName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Script::Scr_PrintPrevCodePos(int scriptPos)
|
void Script::Scr_PrintPrevCodePos(int scriptPos)
|
||||||
@ -355,7 +386,7 @@ namespace Components
|
|||||||
auto bestCodePos = -1, nextCodePos = -1, offset = -1;
|
auto bestCodePos = -1, nextCodePos = -1, offset = -1;
|
||||||
std::string file;
|
std::string file;
|
||||||
|
|
||||||
for (const auto& [key, value] : Script::ScriptBaseProgramNum)
|
for (const auto& [key, value] : ScriptBaseProgramNum)
|
||||||
{
|
{
|
||||||
const auto codePos = key;
|
const auto codePos = key;
|
||||||
|
|
||||||
@ -388,7 +419,7 @@ namespace Components
|
|||||||
__asm
|
__asm
|
||||||
{
|
{
|
||||||
push esi
|
push esi
|
||||||
call Script::Scr_PrintPrevCodePos
|
call Scr_PrintPrevCodePos
|
||||||
add esp, 4h
|
add esp, 4h
|
||||||
|
|
||||||
pop esi
|
pop esi
|
||||||
@ -402,7 +433,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
// execute our hook
|
// execute our hook
|
||||||
pushad
|
pushad
|
||||||
call Script::StoreScriptBaseProgramNum
|
call StoreScriptBaseProgramNum
|
||||||
popad
|
popad
|
||||||
|
|
||||||
// execute overwritten code caused by the jump hook
|
// execute overwritten code caused by the jump hook
|
||||||
@ -452,9 +483,9 @@ namespace Components
|
|||||||
|
|
||||||
void Script::GetReplacedPos(const char* pos)
|
void Script::GetReplacedPos(const char* pos)
|
||||||
{
|
{
|
||||||
if (Script::ReplacedFunctions.contains(pos))
|
if (ReplacedFunctions.contains(pos))
|
||||||
{
|
{
|
||||||
Script::ReplacedPos = Script::ReplacedFunctions[pos];
|
ReplacedPos = ReplacedFunctions[pos];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -466,12 +497,12 @@ namespace Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Script::ReplacedFunctions.contains(what))
|
if (ReplacedFunctions.contains(what))
|
||||||
{
|
{
|
||||||
Logger::Warning(Game::CON_CHANNEL_SCRIPT, "ReplacedFunctions already contains codePosValue for a function\n");
|
Logger::Warning(Game::CON_CHANNEL_SCRIPT, "ReplacedFunctions already contains codePosValue for a function\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
Script::ReplacedFunctions[what] = with;
|
ReplacedFunctions[what] = with;
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(naked) void Script::VMExecuteInternalStub()
|
__declspec(naked) void Script::VMExecuteInternalStub()
|
||||||
@ -481,12 +512,12 @@ namespace Components
|
|||||||
pushad
|
pushad
|
||||||
|
|
||||||
push edx
|
push edx
|
||||||
call Script::GetReplacedPos
|
call GetReplacedPos
|
||||||
|
|
||||||
pop edx
|
pop edx
|
||||||
popad
|
popad
|
||||||
|
|
||||||
cmp Script::ReplacedPos, 0
|
cmp ReplacedPos, 0
|
||||||
jne SetPos
|
jne SetPos
|
||||||
|
|
||||||
movzx eax, byte ptr [edx]
|
movzx eax, byte ptr [edx]
|
||||||
@ -509,8 +540,8 @@ namespace Components
|
|||||||
retn
|
retn
|
||||||
|
|
||||||
SetPos:
|
SetPos:
|
||||||
mov edx, Script::ReplacedPos
|
mov edx, ReplacedPos
|
||||||
mov Script::ReplacedPos, 0
|
mov ReplacedPos, 0
|
||||||
|
|
||||||
movzx eax, byte ptr [edx]
|
movzx eax, byte ptr [edx]
|
||||||
inc edx
|
inc edx
|
||||||
@ -521,7 +552,7 @@ namespace Components
|
|||||||
|
|
||||||
Game::client_t* Script::GetClient(const Game::gentity_t* ent)
|
Game::client_t* Script::GetClient(const Game::gentity_t* ent)
|
||||||
{
|
{
|
||||||
assert(ent != nullptr);
|
assert(ent);
|
||||||
|
|
||||||
if (ent->client == nullptr)
|
if (ent->client == nullptr)
|
||||||
{
|
{
|
||||||
@ -529,7 +560,7 @@ namespace Components
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ent->s.number >= *Game::svs_clientCount)
|
if (static_cast<std::size_t>(ent->s.number) >= Game::MAX_CLIENTS)
|
||||||
{
|
{
|
||||||
Game::Scr_ObjectError(Utils::String::VA("Entity %i is out of bounds", ent->s.number));
|
Game::Scr_ObjectError(Utils::String::VA("Entity %i is out of bounds", ent->s.number));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -540,7 +571,7 @@ namespace Components
|
|||||||
|
|
||||||
void Script::AddFunctions()
|
void Script::AddFunctions()
|
||||||
{
|
{
|
||||||
Script::AddFunction("ReplaceFunc", [] // gsc: ReplaceFunc(<function>, <function>)
|
AddFunction("ReplaceFunc", [] // gsc: ReplaceFunc(<function>, <function>)
|
||||||
{
|
{
|
||||||
if (Game::Scr_GetNumParam() != 2)
|
if (Game::Scr_GetNumParam() != 2)
|
||||||
{
|
{
|
||||||
@ -548,14 +579,14 @@ namespace Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto what = Script::GetCodePosForParam(0);
|
const auto what = GetCodePosForParam(0);
|
||||||
const auto with = Script::GetCodePosForParam(1);
|
const auto with = GetCodePosForParam(1);
|
||||||
|
|
||||||
Script::SetReplacedPos(what, with);
|
SetReplacedPos(what, with);
|
||||||
});
|
});
|
||||||
|
|
||||||
// System time
|
// System time
|
||||||
Script::AddFunction("GetSystemMilliseconds", [] // gsc: GetSystemMilliseconds()
|
AddFunction("GetSystemMilliseconds", [] // gsc: GetSystemMilliseconds()
|
||||||
{
|
{
|
||||||
SYSTEMTIME time;
|
SYSTEMTIME time;
|
||||||
GetSystemTime(&time);
|
GetSystemTime(&time);
|
||||||
@ -564,7 +595,7 @@ namespace Components
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Executes command to the console
|
// Executes command to the console
|
||||||
Script::AddFunction("Exec", [] // gsc: Exec(<string>)
|
AddFunction("Exec", [] // gsc: Exec(<string>)
|
||||||
{
|
{
|
||||||
const auto str = Game::Scr_GetString(0);
|
const auto str = Game::Scr_GetString(0);
|
||||||
|
|
||||||
@ -578,7 +609,7 @@ namespace Components
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Allow printing to the console even when developer is 0
|
// Allow printing to the console even when developer is 0
|
||||||
Script::AddFunction("PrintConsole", [] // gsc: PrintConsole(<string>)
|
AddFunction("PrintConsole", [] // gsc: PrintConsole(<string>)
|
||||||
{
|
{
|
||||||
for (std::size_t i = 0; i < Game::Scr_GetNumParam(); ++i)
|
for (std::size_t i = 0; i < Game::Scr_GetNumParam(); ++i)
|
||||||
{
|
{
|
||||||
@ -595,9 +626,9 @@ namespace Components
|
|||||||
});
|
});
|
||||||
|
|
||||||
// PlayerCmd_AreControlsFrozen GSC function from Black Ops 2
|
// PlayerCmd_AreControlsFrozen GSC function from Black Ops 2
|
||||||
Script::AddMethod("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self AreControlsFrozen();
|
AddMethod("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self AreControlsFrozen();
|
||||||
{
|
{
|
||||||
const auto* ent = Game::GetPlayerEntity(entref);
|
const auto* ent = Scr_GetPlayerEntity(entref);
|
||||||
|
|
||||||
Game::Scr_AddBool((ent->client->flags & Game::PLAYER_FLAG_FROZEN) != 0);
|
Game::Scr_AddBool((ent->client->flags & Game::PLAYER_FLAG_FROZEN) != 0);
|
||||||
});
|
});
|
||||||
@ -605,34 +636,34 @@ namespace Components
|
|||||||
|
|
||||||
Script::Script()
|
Script::Script()
|
||||||
{
|
{
|
||||||
Utils::Hook(0x612DB0, Script::StoreFunctionNameStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x612DB0, StoreFunctionNameStub, HOOK_JUMP).install()->quick();
|
||||||
Utils::Hook(0x427E71, Script::RestoreScriptNameStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x427E71, RestoreScriptNameStub, HOOK_JUMP).install()->quick();
|
||||||
Utils::Hook(0x427DBC, Script::StoreScriptNameStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x427DBC, StoreScriptNameStub, HOOK_JUMP).install()->quick();
|
||||||
Utils::Hook(0x426C2D, Script::StoreScriptBaseProgramNumStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x426C2D, StoreScriptBaseProgramNumStub, HOOK_JUMP).install()->quick();
|
||||||
Utils::Hook(0x42281B, Script::Scr_PrintPrevCodePosStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x42281B, Scr_PrintPrevCodePosStub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
Utils::Hook(0x61E3AD, Script::RuntimeError, HOOK_CALL).install()->quick();
|
Utils::Hook(0x61E3AD, RuntimeError, HOOK_CALL).install()->quick();
|
||||||
Utils::Hook(0x621976, Script::RuntimeError, HOOK_CALL).install()->quick();
|
Utils::Hook(0x621976, RuntimeError, HOOK_CALL).install()->quick();
|
||||||
Utils::Hook(0x62246E, Script::RuntimeError, HOOK_CALL).install()->quick();
|
Utils::Hook(0x62246E, RuntimeError, HOOK_CALL).install()->quick();
|
||||||
// Skip check in GScr_CheckAllowedToSetPersistentData to prevent log spam in RuntimeError.
|
// Skip check in GScr_CheckAllowedToSetPersistentData to prevent log spam in RuntimeError.
|
||||||
// On IW5 the function is entirely nullsubbed
|
// On IW5 the function is entirely nullsubbed
|
||||||
Utils::Hook::Set<BYTE>(0x5F8DBF, 0xEB);
|
Utils::Hook::Set<std::uint8_t>(0x5F8DBF, 0xEB);
|
||||||
|
|
||||||
Utils::Hook(0x612E8D, Script::FunctionError, HOOK_CALL).install()->quick();
|
Utils::Hook(0x612E8D, FunctionError, HOOK_CALL).install()->quick();
|
||||||
Utils::Hook(0x612EA2, Script::FunctionError, HOOK_CALL).install()->quick();
|
Utils::Hook(0x612EA2, FunctionError, HOOK_CALL).install()->quick();
|
||||||
Utils::Hook(0x434260, Script::CompileError, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x434260, CompileError, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
Utils::Hook(0x48EFFE, Script::Scr_LoadGameType_Stub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x48EFFE, Scr_LoadGameType_Stub, HOOK_CALL).install()->quick();
|
||||||
Utils::Hook(0x48F008, Script::Scr_StartupGameType_Stub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x48F008, Scr_StartupGameType_Stub, HOOK_CALL).install()->quick();
|
||||||
Utils::Hook(0x45D44A, Script::GScr_LoadGameTypeScript_Stub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x45D44A, GScr_LoadGameTypeScript_Stub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
// Fetch custom functions
|
// Fetch custom functions
|
||||||
Utils::Hook(0x44E72E, Script::BuiltIn_GetFunctionStub, HOOK_CALL).install()->quick(); // Scr_GetFunction
|
Utils::Hook(0x44E72E, BuiltIn_GetFunctionStub, HOOK_CALL).install()->quick(); // Scr_GetFunction
|
||||||
Utils::Hook(0x4EC8DD, Script::BuiltIn_GetMethodStub, HOOK_CALL).install()->quick(); // Scr_GetMethod
|
Utils::Hook(0x4EC8DD, BuiltIn_GetMethodStub, HOOK_CALL).install()->quick(); // Scr_GetMethod
|
||||||
|
|
||||||
Utils::Hook(0x5F41A3, Script::SetExpFogStub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x5F41A3, SetExpFogStub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
Utils::Hook(0x61E92E, Script::VMExecuteInternalStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x61E92E, VMExecuteInternalStub, HOOK_JUMP).install()->quick();
|
||||||
Utils::Hook::Nop(0x61E933, 1);
|
Utils::Hook::Nop(0x61E933, 1);
|
||||||
|
|
||||||
Scheduler::Loop([]
|
Scheduler::Loop([]
|
||||||
@ -642,9 +673,9 @@ namespace Components
|
|||||||
|
|
||||||
const auto nowMs = Game::Sys_Milliseconds();
|
const auto nowMs = Game::Sys_Milliseconds();
|
||||||
|
|
||||||
if (Script::LastFrameTime != -1)
|
if (LastFrameTime != -1)
|
||||||
{
|
{
|
||||||
const auto timeTaken = (nowMs - Script::LastFrameTime) * static_cast<int>((*Game::com_timescale)->current.value);
|
const auto timeTaken = (nowMs - LastFrameTime) * static_cast<int>((*Game::com_timescale)->current.value);
|
||||||
|
|
||||||
if (timeTaken >= 500)
|
if (timeTaken >= 500)
|
||||||
{
|
{
|
||||||
@ -652,11 +683,11 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Script::LastFrameTime = nowMs;
|
LastFrameTime = nowMs;
|
||||||
}, Scheduler::Pipeline::SERVER);
|
}, Scheduler::Pipeline::SERVER);
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
Script::AddFunction("DebugBox", []
|
AddFunction("DebugBox", []
|
||||||
{
|
{
|
||||||
const auto* message = Game::Scr_GetString(0);
|
const auto* message = Game::Scr_GetString(0);
|
||||||
|
|
||||||
@ -666,14 +697,14 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
MessageBoxA(nullptr, message, "DEBUG", MB_OK);
|
MessageBoxA(nullptr, message, "DEBUG", MB_OK);
|
||||||
}, 1);
|
}, true);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Script::AddFunctions();
|
AddFunctions();
|
||||||
|
|
||||||
Events::OnVMShutdown([]
|
Events::OnVMShutdown([]
|
||||||
{
|
{
|
||||||
Script::ReplacedFunctions.clear();
|
ReplacedFunctions.clear();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,30 +7,55 @@ namespace Components
|
|||||||
public:
|
public:
|
||||||
Script();
|
Script();
|
||||||
|
|
||||||
|
using scriptNames = std::vector<std::string>;
|
||||||
static void AddFunction(const std::string& name, Game::BuiltinFunction func, bool type = false);
|
static void AddFunction(const std::string& name, Game::BuiltinFunction func, bool type = false);
|
||||||
static void AddMethod(const std::string& name, Game::BuiltinMethod func, bool type = false);
|
static void AddMethod(const std::string& name, Game::BuiltinMethod func, bool type = false);
|
||||||
|
|
||||||
|
static void AddFuncMultiple(Game::BuiltinFunction func, bool type, scriptNames);
|
||||||
|
static void AddMethMultiple(Game::BuiltinMethod func, bool type, scriptNames);
|
||||||
|
|
||||||
static Game::client_t* GetClient(const Game::gentity_t* gentity);
|
static Game::client_t* GetClient(const Game::gentity_t* gentity);
|
||||||
|
|
||||||
static const char* GetCodePosForParam(int index);
|
static const char* GetCodePosForParam(int index);
|
||||||
|
|
||||||
static void ShowDeprecationWarning();
|
// Probably a macro 'originally' but this is fine
|
||||||
|
static Game::gentity_s* Scr_GetPlayerEntity(Game::scr_entref_t entref)
|
||||||
|
{
|
||||||
|
if (entref.classnum != 0)
|
||||||
|
{
|
||||||
|
Game::Scr_ObjectError("not an entity");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(entref.entnum < Game::MAX_GENTITIES);
|
||||||
|
|
||||||
|
auto* ent = &Game::g_entities[entref.entnum];
|
||||||
|
if (ent->client == nullptr)
|
||||||
|
{
|
||||||
|
Game::Scr_ObjectError(Utils::String::VA("entity %i is not a player", entref.entnum));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ent;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ScriptFunction
|
struct ScriptFunction
|
||||||
{
|
{
|
||||||
Game::BuiltinFunction actionFunc;
|
Game::BuiltinFunction actionFunc;
|
||||||
bool type;
|
bool type;
|
||||||
|
scriptNames aliases;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ScriptMethod
|
struct ScriptMethod
|
||||||
{
|
{
|
||||||
Game::BuiltinMethod actionFunc;
|
Game::BuiltinMethod actionFunc;
|
||||||
bool type;
|
bool type;
|
||||||
|
scriptNames aliases;
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::unordered_map<std::string, ScriptFunction> CustomScrFunctions;
|
static std::vector<ScriptFunction> CustomScrFunctions;
|
||||||
static std::unordered_map<std::string, ScriptMethod> CustomScrMethods;
|
static std::vector<ScriptMethod> CustomScrMethods;
|
||||||
|
|
||||||
static std::string ScriptName;
|
static std::string ScriptName;
|
||||||
static std::vector<std::string> ScriptNameStack;
|
static std::vector<std::string> ScriptNameStack;
|
||||||
@ -38,8 +63,8 @@ namespace Components
|
|||||||
static std::unordered_map<int, std::string> ScriptBaseProgramNum;
|
static std::unordered_map<int, std::string> ScriptBaseProgramNum;
|
||||||
static int LastFrameTime;
|
static int LastFrameTime;
|
||||||
|
|
||||||
static std::vector<int> ScriptMainHandles;
|
static std::unordered_map<std::string, int> ScriptMainHandles;
|
||||||
static std::vector<int> ScriptInitHandles;
|
static std::unordered_map<std::string, int> ScriptInitHandles;
|
||||||
|
|
||||||
static std::unordered_map<const char*, const char*> ReplacedFunctions;
|
static std::unordered_map<const char*, const char*> ReplacedFunctions;
|
||||||
static const char* ReplacedPos;
|
static const char* ReplacedPos;
|
||||||
|
@ -273,30 +273,6 @@ namespace Components
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptExtension::Scr_TableLookupIStringByRow()
|
|
||||||
{
|
|
||||||
if (Game::Scr_GetNumParam() < 3)
|
|
||||||
{
|
|
||||||
Game::Scr_Error("USAGE: tableLookupIStringByRow( filename, rowNum, returnValueColumnNum )\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto* fileName = Game::Scr_GetString(0);
|
|
||||||
const auto rowNum = Game::Scr_GetInt(1);
|
|
||||||
const auto returnValueColumnNum = Game::Scr_GetInt(2);
|
|
||||||
|
|
||||||
const auto* table = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_STRINGTABLE, fileName).stringTable;
|
|
||||||
|
|
||||||
if (table == nullptr)
|
|
||||||
{
|
|
||||||
Game::Scr_ParamError(0, Utils::String::VA("%s does not exist\n", fileName));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto* value = Game::StringTable_GetColumnValueForRow(table, rowNum, returnValueColumnNum);
|
|
||||||
Game::Scr_AddIString(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScriptExtension::AddEntityFields()
|
void ScriptExtension::AddEntityFields()
|
||||||
{
|
{
|
||||||
AddEntityField("entityflags", Game::F_INT,
|
AddEntityField("entityflags", Game::F_INT,
|
||||||
@ -330,16 +306,10 @@ namespace Components
|
|||||||
AddEntityFields();
|
AddEntityFields();
|
||||||
AddClientFields();
|
AddClientFields();
|
||||||
|
|
||||||
// Correct builtin function pointer
|
|
||||||
Utils::Hook::Set<Game::BuiltinFunction>(0x79A90C, Scr_TableLookupIStringByRow);
|
|
||||||
|
|
||||||
Utils::Hook(0x4EC721, GScr_AddFieldsForEntityStub, HOOK_CALL).install()->quick(); // GScr_AddFieldsForEntity
|
Utils::Hook(0x4EC721, GScr_AddFieldsForEntityStub, HOOK_CALL).install()->quick(); // GScr_AddFieldsForEntity
|
||||||
|
|
||||||
Utils::Hook(0x41BED2, Scr_SetObjectFieldStub, HOOK_CALL).install()->quick(); // SetEntityFieldValue
|
Utils::Hook(0x41BED2, Scr_SetObjectFieldStub, HOOK_CALL).install()->quick(); // SetEntityFieldValue
|
||||||
Utils::Hook(0x5FBF01, Scr_SetClientFieldStub, HOOK_CALL).install()->quick(); // Scr_SetObjectField
|
Utils::Hook(0x5FBF01, Scr_SetClientFieldStub, HOOK_CALL).install()->quick(); // Scr_SetObjectField
|
||||||
Utils::Hook(0x4FF413, Scr_GetEntityFieldStub, HOOK_CALL).install()->quick(); // Scr_GetObjectField
|
Utils::Hook(0x4FF413, Scr_GetEntityFieldStub, HOOK_CALL).install()->quick(); // Scr_GetObjectField
|
||||||
|
|
||||||
// Fix format string in Scr_RandomFloatRange
|
|
||||||
Utils::Hook::Set<const char*>(0x5F10C6, "Scr_RandomFloatRange parms: %f %f ");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,5 @@ namespace Components
|
|||||||
static void AddMethods();
|
static void AddMethods();
|
||||||
static void AddEntityFields();
|
static void AddEntityFields();
|
||||||
static void AddClientFields();
|
static void AddClientFields();
|
||||||
static void Scr_TableLookupIStringByRow();
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
66
src/Components/Modules/GSC/ScriptPatches.cpp
Normal file
66
src/Components/Modules/GSC/ScriptPatches.cpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#include <STDInclude.hpp>
|
||||||
|
#include "ScriptPatches.hpp"
|
||||||
|
#include "Script.hpp"
|
||||||
|
|
||||||
|
using namespace Utils::String;
|
||||||
|
|
||||||
|
namespace Components
|
||||||
|
{
|
||||||
|
constexpr auto offset = 511;
|
||||||
|
|
||||||
|
Game::game_hudelem_s* ScriptPatches::HECmd_GetHudElem(Game::scr_entref_t entref)
|
||||||
|
{
|
||||||
|
if (entref.classnum != 1)
|
||||||
|
{
|
||||||
|
Game::Scr_ObjectError("not a hud element");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(entref.entnum < 1024);
|
||||||
|
return &Game::g_hudelems[entref.entnum];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScriptPatches::Scr_TableLookupIStringByRow_Hk()
|
||||||
|
{
|
||||||
|
if (Game::Scr_GetNumParam() < 3)
|
||||||
|
{
|
||||||
|
Game::Scr_Error("USAGE: tableLookupIStringByRow( filename, rowNum, returnValueColumnNum )\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* fileName = Game::Scr_GetString(0);
|
||||||
|
const auto rowNum = Game::Scr_GetInt(1);
|
||||||
|
const auto returnValueColumnNum = Game::Scr_GetInt(2);
|
||||||
|
|
||||||
|
const auto* table = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_STRINGTABLE, fileName).stringTable;
|
||||||
|
|
||||||
|
if (table == nullptr)
|
||||||
|
{
|
||||||
|
Game::Scr_ParamError(0, Utils::String::VA("%s does not exist\n", fileName));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* value = Game::StringTable_GetColumnValueForRow(table, rowNum, returnValueColumnNum);
|
||||||
|
Game::Scr_AddIString(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptPatches::ScriptPatches()
|
||||||
|
{
|
||||||
|
// Fix format string in Scr_RandomFloatRange
|
||||||
|
Utils::Hook::Set<const char*>(0x5F10C6, "Scr_RandomFloatRange parms: %f %f ");
|
||||||
|
|
||||||
|
// Correct builtin function pointer
|
||||||
|
Utils::Hook::Set<Game::BuiltinFunction>(0x79A90C, Scr_TableLookupIStringByRow_Hk);
|
||||||
|
|
||||||
|
Script::AddMethod("ClearHudText", [](Game::scr_entref_t entref) -> void
|
||||||
|
{
|
||||||
|
auto* hud = HECmd_GetHudElem(entref);
|
||||||
|
|
||||||
|
// Frees config string up
|
||||||
|
if ((hud->elem).text)
|
||||||
|
{
|
||||||
|
Game::SV_SetConfigstring((hud->elem).text + offset, nullptr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
15
src/Components/Modules/GSC/ScriptPatches.hpp
Normal file
15
src/Components/Modules/GSC/ScriptPatches.hpp
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Components
|
||||||
|
{
|
||||||
|
class ScriptPatches : public Component
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ScriptPatches();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static Game::game_hudelem_s* HECmd_GetHudElem(Game::scr_entref_t entref);
|
||||||
|
|
||||||
|
static void Scr_TableLookupIStringByRow_Hk();
|
||||||
|
};
|
||||||
|
}
|
@ -86,6 +86,27 @@ namespace Components
|
|||||||
FileSystem::FileWriter("scriptdata/scriptstorage.json").write(json.dump());
|
FileSystem::FileWriter("scriptdata/scriptstorage.json").write(json.dump());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Script::AddFunction("StorageLoad", [] // gsc: StorageLoad();
|
||||||
|
{
|
||||||
|
FileSystem::File storageFile("scriptdata/scriptstorage.json");
|
||||||
|
if (!storageFile.exists())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& buffer = storageFile.getBuffer();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const nlohmann::json storageDef = nlohmann::json::parse(buffer);
|
||||||
|
const auto& newData = storageDef.get<std::unordered_map<std::string, std::string>>();
|
||||||
|
Data.insert(newData.begin(), newData.end());
|
||||||
|
}
|
||||||
|
catch (const std::exception& ex)
|
||||||
|
{
|
||||||
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "Json Parse Error: {}. File {} is invalid\n", ex.what(), storageFile.getName());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Script::AddFunction("StorageClear", [] // gsc: StorageClear();
|
Script::AddFunction("StorageClear", [] // gsc: StorageClear();
|
||||||
{
|
{
|
||||||
Data.clear();
|
Data.clear();
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "Gamepad.hpp"
|
||||||
|
#include "RawMouse.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -749,7 +751,7 @@ namespace Components
|
|||||||
-yawRight
|
-yawRight
|
||||||
};
|
};
|
||||||
|
|
||||||
Game::cgArray[0].selectedLocationAngle = Game::AngleNormalize360(Game::vectoyaw(&vec));
|
Game::cgArray[0].selectedLocationAngle = Game::AngleNormalize360(Game::vectoryaw(&vec));
|
||||||
Game::cgArray[0].selectedAngleLocation[0] = Game::cgArray[0].selectedLocation[0];
|
Game::cgArray[0].selectedAngleLocation[0] = Game::cgArray[0].selectedLocation[0];
|
||||||
Game::cgArray[0].selectedAngleLocation[1] = Game::cgArray[0].selectedLocation[1];
|
Game::cgArray[0].selectedAngleLocation[1] = Game::cgArray[0].selectedLocation[1];
|
||||||
}
|
}
|
||||||
@ -788,7 +790,7 @@ namespace Components
|
|||||||
|
|
||||||
bool Gamepad::CG_ShouldUpdateViewAngles(const int localClientNum)
|
bool Gamepad::CG_ShouldUpdateViewAngles(const int localClientNum)
|
||||||
{
|
{
|
||||||
return !Game::Key_IsKeyCatcherActive(localClientNum, Game::KEYCATCH_MASK_ANY) || Game::UI_GetActiveMenu(localClientNum) == Game::UIMENU_SCOREBOARD;
|
return !Game::Key_IsCatcherActive(localClientNum, Game::KEYCATCH_MASK_ANY) || Game::UI_GetActiveMenu(localClientNum) == Game::UIMENU_SCOREBOARD;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Gamepad::CL_GamepadAxisValue(const int gamePadIndex, const Game::GamepadVirtualAxis virtualAxis)
|
float Gamepad::CL_GamepadAxisValue(const int gamePadIndex, const Game::GamepadVirtualAxis virtualAxis)
|
||||||
@ -1096,7 +1098,7 @@ namespace Components
|
|||||||
|
|
||||||
auto& gamePadGlobal = gamePadGlobals[gamePadIndex];
|
auto& gamePadGlobal = gamePadGlobals[gamePadIndex];
|
||||||
|
|
||||||
if (Game::Key_IsKeyCatcherActive(gamePadIndex, Game::KEYCATCH_UI))
|
if (Game::Key_IsCatcherActive(gamePadIndex, Game::KEYCATCH_UI))
|
||||||
{
|
{
|
||||||
const int scrollDelayFirst = gpad_menu_scroll_delay_first.get<int>();
|
const int scrollDelayFirst = gpad_menu_scroll_delay_first.get<int>();
|
||||||
const int scrollDelayRest = gpad_menu_scroll_delay_rest.get<int>();
|
const int scrollDelayRest = gpad_menu_scroll_delay_rest.get<int>();
|
||||||
@ -1149,7 +1151,7 @@ namespace Components
|
|||||||
if (pressedOrUpdated && CL_CheckForIgnoreDueToRepeat(gamePadIndex, key, keyState.keys[key].repeats, time))
|
if (pressedOrUpdated && CL_CheckForIgnoreDueToRepeat(gamePadIndex, key, keyState.keys[key].repeats, time))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (Game::Key_IsKeyCatcherActive(gamePadIndex, Game::KEYCATCH_LOCATION_SELECTION) && pressedOrUpdated)
|
if (Game::Key_IsCatcherActive(gamePadIndex, Game::KEYCATCH_LOCATION_SELECTION) && pressedOrUpdated)
|
||||||
{
|
{
|
||||||
if (key == Game::K_BUTTON_B || keyState.keys[key].binding && strcmp(keyState.keys[key].binding, "+actionslot 4") == 0)
|
if (key == Game::K_BUTTON_B || keyState.keys[key].binding && strcmp(keyState.keys[key].binding, "+actionslot 4") == 0)
|
||||||
{
|
{
|
||||||
@ -1176,7 +1178,7 @@ namespace Components
|
|||||||
char cmd[1024];
|
char cmd[1024];
|
||||||
if (pressedOrUpdated)
|
if (pressedOrUpdated)
|
||||||
{
|
{
|
||||||
if (Game::Key_IsKeyCatcherActive(gamePadIndex, Game::KEYCATCH_UI))
|
if (Game::Key_IsCatcherActive(gamePadIndex, Game::KEYCATCH_UI))
|
||||||
{
|
{
|
||||||
UI_GamepadKeyEvent(gamePadIndex, key, pressedOrUpdated);
|
UI_GamepadKeyEvent(gamePadIndex, key, pressedOrUpdated);
|
||||||
return;
|
return;
|
||||||
@ -1203,7 +1205,7 @@ namespace Components
|
|||||||
Game::Cbuf_AddText(gamePadIndex, cmd);
|
Game::Cbuf_AddText(gamePadIndex, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Game::Key_IsKeyCatcherActive(gamePadIndex, Game::KEYCATCH_UI))
|
if (Game::Key_IsCatcherActive(gamePadIndex, Game::KEYCATCH_UI))
|
||||||
{
|
{
|
||||||
UI_GamepadKeyEvent(gamePadIndex, key, pressedOrUpdated);
|
UI_GamepadKeyEvent(gamePadIndex, key, pressedOrUpdated);
|
||||||
}
|
}
|
||||||
@ -1218,7 +1220,7 @@ namespace Components
|
|||||||
gamePad.inUse = true;
|
gamePad.inUse = true;
|
||||||
gpad_in_use.setRaw(true);
|
gpad_in_use.setRaw(true);
|
||||||
|
|
||||||
if (Game::Key_IsKeyCatcherActive(gamePadIndex, Game::KEYCATCH_UI))
|
if (Game::Key_IsCatcherActive(gamePadIndex, Game::KEYCATCH_UI))
|
||||||
CL_GamepadResetMenuScrollTime(gamePadIndex, key, buttonEvent == Game::GPAD_BUTTON_PRESSED, time);
|
CL_GamepadResetMenuScrollTime(gamePadIndex, key, buttonEvent == Game::GPAD_BUTTON_PRESSED, time);
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,102 +0,0 @@
|
|||||||
#include <STDInclude.hpp>
|
|
||||||
|
|
||||||
namespace Components
|
|
||||||
{
|
|
||||||
unsigned int Gametypes::GetGametypeCount()
|
|
||||||
{
|
|
||||||
return *Game::gameTypeCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* Gametypes::GetGametypeText(unsigned int index, int)
|
|
||||||
{
|
|
||||||
if (static_cast<unsigned int>(*Game::gameTypeCount) > index)
|
|
||||||
{
|
|
||||||
return Game::SEH_StringEd_GetString(Game::gameTypes[index].uiName);
|
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
void Gametypes::SelectGametype(unsigned int index)
|
|
||||||
{
|
|
||||||
if (!*Game::gameTypeCount) return;
|
|
||||||
if (static_cast<unsigned int>(*Game::gameTypeCount) <= index) index = 0;
|
|
||||||
|
|
||||||
std::string gametype = Game::gameTypes[index].gameType;
|
|
||||||
|
|
||||||
Dvar::Var("ui_gametype").set(gametype);
|
|
||||||
//Dvar::Var("g_gametype").set(gametype);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* Gametypes::BuildGametypeList(const char*, void* buffer, size_t size)
|
|
||||||
{
|
|
||||||
std::vector<std::string> gametypes;
|
|
||||||
|
|
||||||
auto pushGametype = [&](std::string gametype)
|
|
||||||
{
|
|
||||||
auto pos = gametype.find_last_of("/\\");
|
|
||||||
if (pos != std::string::npos)
|
|
||||||
{
|
|
||||||
gametype = gametype.substr(pos + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Utils::String::EndsWith(gametype, ".txt"))
|
|
||||||
{
|
|
||||||
gametype = gametype.substr(0, gametype.size() - 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
// No need for that :)
|
|
||||||
if (gametype == "_gametypes") return;
|
|
||||||
|
|
||||||
if (std::find(gametypes.begin(), gametypes.end(), gametype) == gametypes.end())
|
|
||||||
{
|
|
||||||
gametypes.push_back(gametype);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get the gametypes we can find in the filesystem
|
|
||||||
std::vector<std::string> rawGametypes = FileSystem::GetFileList("maps/mp/gametypes/", "txt");
|
|
||||||
|
|
||||||
// Get the gametypes we can find in the database
|
|
||||||
Game::DB_EnumXAssets(Game::XAssetType::ASSET_TYPE_RAWFILE, [](Game::XAssetHeader header, void* data)
|
|
||||||
{
|
|
||||||
std::string name = header.rawfile->name;
|
|
||||||
std::vector<std::string>* rawGametypes = reinterpret_cast<std::vector<std::string>*>(data);
|
|
||||||
|
|
||||||
if (Utils::String::StartsWith(name, "maps/mp/gametypes/") && Utils::String::EndsWith(name, ".txt"))
|
|
||||||
{
|
|
||||||
if (std::count(name.begin(), name.end(), '/') == 3 && std::count(name.begin(), name.end(), '\\') == 0)
|
|
||||||
{
|
|
||||||
rawGametypes->push_back(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}, &rawGametypes, false);
|
|
||||||
|
|
||||||
std::for_each(rawGametypes.begin(), rawGametypes.end(), pushGametype);
|
|
||||||
|
|
||||||
std::string data;
|
|
||||||
for (auto& gametype : gametypes)
|
|
||||||
{
|
|
||||||
if (Game::Scr_AddSourceBuffer(nullptr, Utils::String::VA("maps/mp/gametypes/%s.txt", gametype.data()), nullptr, false))
|
|
||||||
{
|
|
||||||
data.append(gametype);
|
|
||||||
data.append("\r\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy to the actual buffer
|
|
||||||
std::memcpy(buffer, data.data(), std::min(size, data.size() + 1));
|
|
||||||
|
|
||||||
return (data.empty() ? nullptr : buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
Gametypes::Gametypes()
|
|
||||||
{
|
|
||||||
UIFeeder::Add(29.0f, Gametypes::GetGametypeCount, Gametypes::GetGametypeText, Gametypes::SelectGametype);
|
|
||||||
|
|
||||||
// Dynamically grab gametypes
|
|
||||||
Utils::Hook(0x5FA46C, Gametypes::BuildGametypeList, HOOK_CALL).install()->quick(); // Scr_UpdateGameTypeList
|
|
||||||
Utils::Hook(0x632155, Gametypes::BuildGametypeList, HOOK_CALL).install()->quick(); // UI_UpdateGameTypesList
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace Components
|
|
||||||
{
|
|
||||||
class Gametypes : public Component
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Gametypes();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static unsigned int GetGametypeCount();
|
|
||||||
static const char* GetGametypeText(unsigned int index, int column);
|
|
||||||
static void SelectGametype(unsigned int index);
|
|
||||||
|
|
||||||
static void* BuildGametypeList(const char* file, void* buffer, size_t size);
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,4 +1,5 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "Lean.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "Console.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -6,7 +7,7 @@ namespace Components
|
|||||||
std::vector<std::string> Logger::MessageQueue;
|
std::vector<std::string> Logger::MessageQueue;
|
||||||
std::vector<Network::Address> Logger::LoggingAddresses[2];
|
std::vector<Network::Address> Logger::LoggingAddresses[2];
|
||||||
|
|
||||||
std::function<void(const std::string&)> Logger::PipeCallback;
|
void(*Logger::PipeCallback)(const std::string&) = nullptr;;
|
||||||
|
|
||||||
bool Logger::IsConsoleReady()
|
bool Logger::IsConsoleReady()
|
||||||
{
|
{
|
||||||
@ -19,10 +20,10 @@ namespace Components
|
|||||||
|
|
||||||
va_list va;
|
va_list va;
|
||||||
va_start(va, message);
|
va_start(va, message);
|
||||||
_vsnprintf_s(buf, _TRUNCATE, message, va);
|
vsnprintf_s(buf, _TRUNCATE, message, va);
|
||||||
va_end(va);
|
va_end(va);
|
||||||
|
|
||||||
Logger::MessagePrint(channel, {buf});
|
MessagePrint(channel, {buf});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::MessagePrint(const int channel, const std::string& msg)
|
void Logger::MessagePrint(const int channel, const std::string& msg)
|
||||||
@ -42,14 +43,14 @@ namespace Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Logger::IsConsoleReady())
|
if (!IsConsoleReady())
|
||||||
{
|
{
|
||||||
OutputDebugStringA(out.data());
|
OutputDebugStringA(out.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Game::Sys_IsMainThread())
|
if (!Game::Sys_IsMainThread())
|
||||||
{
|
{
|
||||||
Logger::EnqueueMessage(msg);
|
EnqueueMessage(msg);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -57,7 +58,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::DebugInternal(std::string_view fmt, std::format_args&& args, [[maybe_unused]] const std::source_location& loc)
|
void Logger::DebugInternal(const std::string_view& fmt, std::format_args&& args, [[maybe_unused]] const std::source_location& loc)
|
||||||
{
|
{
|
||||||
#ifdef LOGGER_TRACE
|
#ifdef LOGGER_TRACE
|
||||||
const auto msg = std::vformat(fmt, args);
|
const auto msg = std::vformat(fmt, args);
|
||||||
@ -67,17 +68,17 @@ namespace Components
|
|||||||
const auto out = std::format("^2{}\n", msg);
|
const auto out = std::format("^2{}\n", msg);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Logger::MessagePrint(Game::CON_CHANNEL_DONT_FILTER, out);
|
MessagePrint(Game::CON_CHANNEL_DONT_FILTER, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::PrintInternal(int channel, std::string_view fmt, std::format_args&& args)
|
void Logger::PrintInternal(Game::conChannel_t channel, const std::string_view& fmt, std::format_args&& args)
|
||||||
{
|
{
|
||||||
const auto msg = std::vformat(fmt, args);
|
const auto msg = std::vformat(fmt, args);
|
||||||
|
|
||||||
Logger::MessagePrint(channel, msg);
|
MessagePrint(channel, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::ErrorInternal(const Game::errorParm_t error, const std::string_view fmt, std::format_args&& args)
|
void Logger::ErrorInternal(const Game::errorParm_t error, const std::string_view& fmt, std::format_args&& args)
|
||||||
{
|
{
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
if (IsDebuggerPresent()) __debugbreak();
|
if (IsDebuggerPresent()) __debugbreak();
|
||||||
@ -87,53 +88,53 @@ namespace Components
|
|||||||
Game::Com_Error(error, "%s", msg.data());
|
Game::Com_Error(error, "%s", msg.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::PrintErrorInternal(int channel, std::string_view fmt, std::format_args&& args)
|
void Logger::PrintErrorInternal(Game::conChannel_t channel, const std::string_view& fmt, std::format_args&& args)
|
||||||
{
|
{
|
||||||
const auto msg = "^1Error: " + std::vformat(fmt, args);
|
const auto msg = "^1Error: " + std::vformat(fmt, args);
|
||||||
|
|
||||||
++(*Game::com_errorPrintsCount);
|
++(*Game::com_errorPrintsCount);
|
||||||
Logger::MessagePrint(channel, msg);
|
MessagePrint(channel, msg);
|
||||||
|
|
||||||
if (*Game::cls_uiStarted != 0 && (*Game::com_fixedConsolePosition == 0))
|
if (Game::cls->uiStarted != 0 && (*Game::com_fixedConsolePosition == 0))
|
||||||
{
|
{
|
||||||
Game::CL_ConsoleFixPosition();
|
Game::CL_ConsoleFixPosition();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::WarningInternal(int channel, std::string_view fmt, std::format_args&& args)
|
void Logger::WarningInternal(Game::conChannel_t channel, const std::string_view& fmt, std::format_args&& args)
|
||||||
{
|
{
|
||||||
const auto msg = "^3" + std::vformat(fmt, args);
|
const auto msg = "^3" + std::vformat(fmt, args);
|
||||||
|
|
||||||
Logger::MessagePrint(channel, msg);
|
MessagePrint(channel, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::Frame()
|
void Logger::Frame()
|
||||||
{
|
{
|
||||||
std::unique_lock _(Logger::MessageMutex);
|
std::unique_lock _(MessageMutex);
|
||||||
|
|
||||||
for (auto i = Logger::MessageQueue.begin(); i != Logger::MessageQueue.end();)
|
for (auto i = MessageQueue.begin(); i != MessageQueue.end();)
|
||||||
{
|
{
|
||||||
Game::Com_PrintMessage(Game::CON_CHANNEL_DONT_FILTER, i->data(), 0);
|
Game::Com_PrintMessage(Game::CON_CHANNEL_DONT_FILTER, i->data(), 0);
|
||||||
|
|
||||||
if (!Logger::IsConsoleReady())
|
if (!IsConsoleReady())
|
||||||
{
|
{
|
||||||
OutputDebugStringA(i->data());
|
OutputDebugStringA(i->data());
|
||||||
}
|
}
|
||||||
|
|
||||||
i = Logger::MessageQueue.erase(i);
|
i = MessageQueue.erase(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::PipeOutput(const std::function<void(const std::string&)>& callback)
|
void Logger::PipeOutput(void(*callback)(const std::string&))
|
||||||
{
|
{
|
||||||
Logger::PipeCallback = callback;
|
PipeCallback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::PrintMessagePipe(const char* data)
|
void Logger::PrintMessagePipe(const char* data)
|
||||||
{
|
{
|
||||||
if (Logger::PipeCallback)
|
if (PipeCallback)
|
||||||
{
|
{
|
||||||
Logger::PipeCallback(data);
|
PipeCallback(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +145,7 @@ namespace Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& addr : Logger::LoggingAddresses[gLog & 1])
|
for (const auto& addr : LoggingAddresses[gLog & 1])
|
||||||
{
|
{
|
||||||
Network::SendCommand(addr, "print", data);
|
Network::SendCommand(addr, "print", data);
|
||||||
}
|
}
|
||||||
@ -169,20 +170,20 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Allow the network log to run even if logFile was not opened
|
// Allow the network log to run even if logFile was not opened
|
||||||
Logger::NetworkLog(string, true);
|
NetworkLog(string, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(naked) void Logger::PrintMessage_Stub()
|
__declspec(naked) void Logger::PrintMessage_Stub()
|
||||||
{
|
{
|
||||||
__asm
|
__asm
|
||||||
{
|
{
|
||||||
mov eax, Logger::PipeCallback
|
mov eax, PipeCallback
|
||||||
test eax, eax
|
test eax, eax
|
||||||
jz returnPrint
|
jz returnPrint
|
||||||
|
|
||||||
pushad
|
pushad
|
||||||
push [esp + 28h]
|
push [esp + 28h]
|
||||||
call Logger::PrintMessagePipe
|
call PrintMessagePipe
|
||||||
add esp, 4h
|
add esp, 4h
|
||||||
popad
|
popad
|
||||||
retn
|
retn
|
||||||
@ -191,7 +192,7 @@ namespace Components
|
|||||||
pushad
|
pushad
|
||||||
push 0
|
push 0
|
||||||
push [esp + 2Ch]
|
push [esp + 2Ch]
|
||||||
call Logger::NetworkLog
|
call NetworkLog
|
||||||
add esp, 8h
|
add esp, 8h
|
||||||
popad
|
popad
|
||||||
|
|
||||||
@ -205,8 +206,8 @@ namespace Components
|
|||||||
|
|
||||||
void Logger::EnqueueMessage(const std::string& message)
|
void Logger::EnqueueMessage(const std::string& message)
|
||||||
{
|
{
|
||||||
std::unique_lock _(Logger::MessageMutex);
|
std::unique_lock _(MessageMutex);
|
||||||
Logger::MessageQueue.push_back(message);
|
MessageQueue.push_back(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::RedirectOSPath(const char* file, char* folder)
|
void Logger::RedirectOSPath(const char* file, char* folder)
|
||||||
@ -232,7 +233,7 @@ namespace Components
|
|||||||
push [esp + 28h]
|
push [esp + 28h]
|
||||||
push [esp + 30h]
|
push [esp + 30h]
|
||||||
|
|
||||||
call Logger::RedirectOSPath
|
call RedirectOSPath
|
||||||
|
|
||||||
add esp, 8h
|
add esp, 8h
|
||||||
|
|
||||||
@ -256,9 +257,9 @@ namespace Components
|
|||||||
|
|
||||||
Network::Address addr(params->get(1));
|
Network::Address addr(params->get(1));
|
||||||
|
|
||||||
if (std::find(Logger::LoggingAddresses[0].begin(), Logger::LoggingAddresses[0].end(), addr) == Logger::LoggingAddresses[0].end())
|
if (std::find(LoggingAddresses[0].begin(), LoggingAddresses[0].end(), addr) == LoggingAddresses[0].end())
|
||||||
{
|
{
|
||||||
Logger::LoggingAddresses[0].push_back(addr);
|
LoggingAddresses[0].push_back(addr);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -266,38 +267,38 @@ namespace Components
|
|||||||
{
|
{
|
||||||
if (params->size() < 2) return;
|
if (params->size() < 2) return;
|
||||||
|
|
||||||
const auto num = atoi(params->get(1));
|
const auto num = std::atoi(params->get(1));
|
||||||
if (Utils::String::VA("%i", num) == std::string(params->get(1)) && static_cast<unsigned int>(num) < Logger::LoggingAddresses[0].size())
|
if (!std::strcmp(Utils::String::VA("%i", num), params->get(1)) && static_cast<unsigned int>(num) < LoggingAddresses[0].size())
|
||||||
{
|
{
|
||||||
auto addr = Logger::LoggingAddresses[0].begin() + num;
|
auto addr = Logger::LoggingAddresses[0].begin() + num;
|
||||||
Logger::Print("Address {} removed\n", addr->getString());
|
Print("Address {} removed\n", addr->getString());
|
||||||
Logger::LoggingAddresses[0].erase(addr);
|
LoggingAddresses[0].erase(addr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Network::Address addr(params->get(1));
|
Network::Address addr(params->get(1));
|
||||||
|
|
||||||
const auto i = std::find(Logger::LoggingAddresses[0].begin(), Logger::LoggingAddresses[0].end(), addr);
|
const auto i = std::find(LoggingAddresses[0].begin(), LoggingAddresses[0].end(), addr);
|
||||||
if (i != Logger::LoggingAddresses[0].end())
|
if (i != LoggingAddresses[0].end())
|
||||||
{
|
{
|
||||||
Logger::LoggingAddresses[0].erase(i);
|
LoggingAddresses[0].erase(i);
|
||||||
Logger::Print("Address {} removed\n", addr.getString());
|
Print("Address {} removed\n", addr.getString());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger::Print("Address {} not found!\n", addr.getString());
|
Print("Address {} not found!\n", addr.getString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Command::AddSV("log_list", [](Command::Params*)
|
Command::AddSV("log_list", []([[maybe_unused]] Command::Params* params)
|
||||||
{
|
{
|
||||||
Logger::Print("# ID: Address\n");
|
Print("# ID: Address\n");
|
||||||
Logger::Print("-------------\n");
|
Print("-------------\n");
|
||||||
|
|
||||||
for (unsigned int i = 0; i < Logger::LoggingAddresses[0].size(); ++i)
|
for (unsigned int i = 0; i < LoggingAddresses[0].size(); ++i)
|
||||||
{
|
{
|
||||||
Logger::Print("#{:03d}: {}\n", i, Logger::LoggingAddresses[0][i].getString());
|
Print("#{:03d}: {}\n", i, LoggingAddresses[0][i].getString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -307,9 +308,9 @@ namespace Components
|
|||||||
|
|
||||||
const Network::Address addr(params->get(1));
|
const Network::Address addr(params->get(1));
|
||||||
|
|
||||||
if (std::find(Logger::LoggingAddresses[1].begin(), Logger::LoggingAddresses[1].end(), addr) == Logger::LoggingAddresses[1].end())
|
if (std::find(LoggingAddresses[1].begin(), LoggingAddresses[1].end(), addr) == LoggingAddresses[1].end())
|
||||||
{
|
{
|
||||||
Logger::LoggingAddresses[1].push_back(addr);
|
LoggingAddresses[1].push_back(addr);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -318,37 +319,37 @@ namespace Components
|
|||||||
if (params->size() < 2) return;
|
if (params->size() < 2) return;
|
||||||
|
|
||||||
const auto num = std::atoi(params->get(1));
|
const auto num = std::atoi(params->get(1));
|
||||||
if (Utils::String::VA("%i", num) == std::string(params->get(1)) && static_cast<unsigned int>(num) < Logger::LoggingAddresses[1].size())
|
if (!std::strcmp(Utils::String::VA("%i", num), params->get(1)) && static_cast<unsigned int>(num) < LoggingAddresses[1].size())
|
||||||
{
|
{
|
||||||
const auto addr = Logger::LoggingAddresses[1].begin() + num;
|
const auto addr = LoggingAddresses[1].begin() + num;
|
||||||
Logger::Print("Address {} removed\n", addr->getString());
|
Print("Address {} removed\n", addr->getString());
|
||||||
Logger::LoggingAddresses[1].erase(addr);
|
LoggingAddresses[1].erase(addr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const Network::Address addr(params->get(1));
|
const Network::Address addr(params->get(1));
|
||||||
|
|
||||||
const auto i = std::find(Logger::LoggingAddresses[1].begin(), Logger::LoggingAddresses[1].end(), addr);
|
const auto i = std::ranges::find(LoggingAddresses[1].begin(), LoggingAddresses[1].end(), addr);
|
||||||
if (i != Logger::LoggingAddresses[1].end())
|
if (i != LoggingAddresses[1].end())
|
||||||
{
|
{
|
||||||
Logger::LoggingAddresses[1].erase(i);
|
LoggingAddresses[1].erase(i);
|
||||||
Logger::Print("Address {} removed\n", addr.getString());
|
Print("Address {} removed\n", addr.getString());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger::Print("Address {} not found!\n", addr.getString());
|
Print("Address {} not found!\n", addr.getString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Command::AddSV("g_log_list", [](Command::Params*)
|
Command::AddSV("g_log_list", [](Command::Params*)
|
||||||
{
|
{
|
||||||
Logger::Print("# ID: Address\n");
|
Print("# ID: Address\n");
|
||||||
Logger::Print("-------------\n");
|
Print("-------------\n");
|
||||||
|
|
||||||
for (std::size_t i = 0; i < Logger::LoggingAddresses[1].size(); ++i)
|
for (std::size_t i = 0; i < LoggingAddresses[1].size(); ++i)
|
||||||
{
|
{
|
||||||
Logger::Print("#{:03d}: {}\n", i, Logger::LoggingAddresses[1][i].getString());
|
Print("#{:03d}: {}\n", i, LoggingAddresses[1][i].getString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -356,30 +357,28 @@ namespace Components
|
|||||||
Logger::Logger()
|
Logger::Logger()
|
||||||
{
|
{
|
||||||
Dvar::Register<bool>("iw4x_onelog", false, Game::DVAR_LATCH | Game::DVAR_ARCHIVE, "Only write the game log to the 'userraw' OS folder");
|
Dvar::Register<bool>("iw4x_onelog", false, Game::DVAR_LATCH | Game::DVAR_ARCHIVE, "Only write the game log to the 'userraw' OS folder");
|
||||||
Utils::Hook(0x642139, Logger::BuildOSPath_Stub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x642139, BuildOSPath_Stub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
Logger::PipeOutput(nullptr);
|
Scheduler::Loop(Frame, Scheduler::Pipeline::SERVER);
|
||||||
|
|
||||||
Scheduler::Loop(Logger::Frame, Scheduler::Pipeline::SERVER);
|
Utils::Hook(Game::G_LogPrintf, G_LogPrintf_Hk, HOOK_JUMP).install()->quick();
|
||||||
|
Utils::Hook(Game::Com_PrintMessage, PrintMessage_Stub, HOOK_JUMP).install()->quick();
|
||||||
Utils::Hook(Game::G_LogPrintf, Logger::G_LogPrintf_Hk, HOOK_JUMP).install()->quick();
|
|
||||||
Utils::Hook(Game::Com_PrintMessage, Logger::PrintMessage_Stub, HOOK_JUMP).install()->quick();
|
|
||||||
|
|
||||||
if (Loader::IsPerformingUnitTests())
|
if (Loader::IsPerformingUnitTests())
|
||||||
{
|
{
|
||||||
Utils::Hook(Game::Com_Printf, Logger::Print_Stub, HOOK_JUMP).install()->quick();
|
Utils::Hook(Game::Com_Printf, Print_Stub, HOOK_JUMP).install()->quick();
|
||||||
}
|
}
|
||||||
|
|
||||||
Events::OnSVInit(Logger::AddServerCommands);
|
Events::OnSVInit(AddServerCommands);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::~Logger()
|
Logger::~Logger()
|
||||||
{
|
{
|
||||||
Logger::LoggingAddresses[0].clear();
|
LoggingAddresses[0].clear();
|
||||||
Logger::LoggingAddresses[1].clear();
|
LoggingAddresses[1].clear();
|
||||||
|
|
||||||
std::unique_lock lock(Logger::MessageMutex);
|
std::unique_lock lock(MessageMutex);
|
||||||
Logger::MessageQueue.clear();
|
MessageQueue.clear();
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
// Flush the console log
|
// Flush the console log
|
||||||
|
@ -12,90 +12,107 @@ namespace Components
|
|||||||
|
|
||||||
static void Print_Stub(int channel, const char* message, ...);
|
static void Print_Stub(int channel, const char* message, ...);
|
||||||
|
|
||||||
static void PipeOutput(const std::function<void(const std::string&)>& callback);
|
static void PipeOutput(void(*callback)(const std::string&));
|
||||||
|
|
||||||
static void PrintInternal(int channel, std::string_view fmt, std::format_args&& args);
|
static void PrintInternal(Game::conChannel_t channel, const std::string_view& fmt, std::format_args&& args);
|
||||||
static void ErrorInternal(Game::errorParm_t error, std::string_view fmt, std::format_args&& args);
|
static void ErrorInternal(Game::errorParm_t error, const std::string_view& fmt, std::format_args&& args);
|
||||||
static void PrintErrorInternal(int channel, std::string_view fmt, std::format_args&& args);
|
static void PrintErrorInternal(Game::conChannel_t channel, const std::string_view& fmt, std::format_args&& args);
|
||||||
static void WarningInternal(int channel, std::string_view fmt, std::format_args&& args);
|
static void WarningInternal(Game::conChannel_t channel, const std::string_view& fmt, std::format_args&& args);
|
||||||
static void DebugInternal(std::string_view fmt, std::format_args&& args, const std::source_location& loc);
|
static void DebugInternal(const std::string_view& fmt, std::format_args&& args, const std::source_location& loc);
|
||||||
|
|
||||||
static void Print(std::string_view fmt)
|
static void Print(const std::string_view& fmt)
|
||||||
{
|
{
|
||||||
PrintInternal(Game::CON_CHANNEL_DONT_FILTER, fmt, std::make_format_args(0));
|
PrintInternal(Game::CON_CHANNEL_DONT_FILTER, fmt, std::make_format_args(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Print(int channel, std::string_view fmt)
|
static void Print(Game::conChannel_t channel, const std::string_view& fmt)
|
||||||
{
|
{
|
||||||
PrintInternal(channel, fmt, std::make_format_args(0));
|
PrintInternal(channel, fmt, std::make_format_args(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
static void Print(std::string_view fmt, Args&&... args)
|
static void Print(const std::string_view& fmt, Args&&... args)
|
||||||
{
|
{
|
||||||
|
(Utils::String::SanitizeFormatArgs(args), ...);
|
||||||
PrintInternal(Game::CON_CHANNEL_DONT_FILTER, fmt, std::make_format_args(args...));
|
PrintInternal(Game::CON_CHANNEL_DONT_FILTER, fmt, std::make_format_args(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
static void Print(int channel, std::string_view fmt, Args&&... args)
|
static void Print(Game::conChannel_t channel, const std::string_view& fmt, Args&&... args)
|
||||||
{
|
{
|
||||||
|
(Utils::String::SanitizeFormatArgs(args), ...);
|
||||||
PrintInternal(channel, fmt, std::make_format_args(args...));
|
PrintInternal(channel, fmt, std::make_format_args(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Error(Game::errorParm_t error, std::string_view fmt)
|
static void Error(Game::errorParm_t error, const std::string_view& fmt)
|
||||||
{
|
{
|
||||||
ErrorInternal(error, fmt, std::make_format_args(0));
|
ErrorInternal(error, fmt, std::make_format_args(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
static void Error(Game::errorParm_t error, std::string_view fmt, Args&&... args)
|
static void Error(Game::errorParm_t error, const std::string_view& fmt, Args&&... args)
|
||||||
{
|
{
|
||||||
|
(Utils::String::SanitizeFormatArgs(args), ...);
|
||||||
ErrorInternal(error, fmt, std::make_format_args(args...));
|
ErrorInternal(error, fmt, std::make_format_args(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Warning(int channel, std::string_view fmt)
|
static void Warning(Game::conChannel_t channel, const std::string_view& fmt)
|
||||||
{
|
{
|
||||||
WarningInternal(channel, fmt, std::make_format_args(0));
|
WarningInternal(channel, fmt, std::make_format_args(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
static void Warning(int channel, std::string_view fmt, Args&&... args)
|
static void Warning(Game::conChannel_t channel, const std::string_view& fmt, Args&&... args)
|
||||||
{
|
{
|
||||||
|
(Utils::String::SanitizeFormatArgs(args), ...);
|
||||||
WarningInternal(channel, fmt, std::make_format_args(args...));
|
WarningInternal(channel, fmt, std::make_format_args(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void PrintError(int channel, std::string_view fmt)
|
static void PrintError(Game::conChannel_t channel, const std::string_view& fmt)
|
||||||
{
|
{
|
||||||
PrintErrorInternal(channel, fmt, std::make_format_args(0));
|
PrintErrorInternal(channel, fmt, std::make_format_args(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
static void PrintError(int channel, std::string_view fmt, Args&&... args)
|
static void PrintError(Game::conChannel_t channel, const std::string_view& fmt, Args&&... args)
|
||||||
{
|
{
|
||||||
|
(Utils::String::SanitizeFormatArgs(args), ...);
|
||||||
PrintErrorInternal(channel, fmt, std::make_format_args(args...));
|
PrintErrorInternal(channel, fmt, std::make_format_args(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
struct FormatWithLocation
|
||||||
class Debug
|
|
||||||
{
|
{
|
||||||
public:
|
std::string_view format;
|
||||||
Debug([[maybe_unused]] std::string_view fmt, [[maybe_unused]] const Args&... args, [[maybe_unused]] const std::source_location& loc = std::source_location::current())
|
std::source_location location;
|
||||||
|
|
||||||
|
FormatWithLocation(const std::string_view& fmt, std::source_location loc = std::source_location::current())
|
||||||
|
: format(fmt)
|
||||||
|
, location(std::move(loc))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FormatWithLocation(const char* fmt, std::source_location loc = std::source_location::current())
|
||||||
|
: format(fmt)
|
||||||
|
, location(std::move(loc))
|
||||||
{
|
{
|
||||||
#ifdef _DEBUG
|
|
||||||
DebugInternal(fmt, std::make_format_args(args...), loc);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
Debug(std::string_view fmt, const Args&... args) -> Debug<Args...>;
|
static void Debug([[maybe_unused]] const FormatWithLocation& f, [[maybe_unused]] const Args&... args)
|
||||||
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
|
(Utils::String::SanitizeFormatArgs(args), ...);
|
||||||
|
DebugInternal(f.format, std::make_format_args(args...), f.location);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::mutex MessageMutex;
|
static std::mutex MessageMutex;
|
||||||
static std::vector<std::string> MessageQueue;
|
static std::vector<std::string> MessageQueue;
|
||||||
static std::vector<Network::Address> LoggingAddresses[2];
|
static std::vector<Network::Address> LoggingAddresses[2];
|
||||||
|
|
||||||
static std::function<void(const std::string&)> PipeCallback;
|
static void(*PipeCallback)(const std::string&);
|
||||||
|
|
||||||
static void MessagePrint(int channel, const std::string& msg);
|
static void MessagePrint(int channel, const std::string& msg);
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "MapDump.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -362,27 +363,8 @@ namespace Components
|
|||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This is still wrong.
|
|
||||||
if (image->mapType == 5 && false)
|
|
||||||
{
|
|
||||||
for (auto i = 0; i < 6; ++i)
|
|
||||||
{
|
|
||||||
IDirect3DSurface9* surface = nullptr;
|
|
||||||
image->texture.cubemap->GetCubeMapSurface(D3DCUBEMAP_FACES(i), 0, &surface);
|
|
||||||
|
|
||||||
if (surface)
|
|
||||||
{
|
|
||||||
std::string _name = Utils::String::VA("raw/mapdump/%s/textures/%s_%i.png", this->world_->baseName, image->name, i);
|
|
||||||
D3DXSaveSurfaceToFileA(_name.data(), D3DXIFF_PNG, surface, nullptr, nullptr);
|
|
||||||
surface->Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::string _name = Utils::String::VA("raw/mapdump/%s/textures/%s.png", this->world_->baseName, image->name);
|
std::string _name = Utils::String::VA("raw/mapdump/%s/textures/%s.png", this->world_->baseName, image->name);
|
||||||
D3DXSaveTextureToFileA(_name.data(), D3DXIFF_PNG, image->texture.map, nullptr);
|
D3DXSaveTextureToFileA(_name.data(), D3DXIFF_PNG, image->texture.map, nullptr);
|
||||||
}
|
|
||||||
|
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "MapRotation.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -26,7 +27,7 @@ namespace Components
|
|||||||
this->rotationEntries_.emplace_back(std::make_pair(key, value));
|
this->rotationEntries_.emplace_back(std::make_pair(key, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t MapRotation::RotationData::getEntriesSize() const
|
std::size_t MapRotation::RotationData::getEntriesSize() const noexcept
|
||||||
{
|
{
|
||||||
return this->rotationEntries_.size();
|
return this->rotationEntries_.size();
|
||||||
}
|
}
|
||||||
@ -47,7 +48,7 @@ namespace Components
|
|||||||
const auto& key = tokens[i];
|
const auto& key = tokens[i];
|
||||||
const auto& value = tokens[i + 1];
|
const auto& value = tokens[i + 1];
|
||||||
|
|
||||||
if (key == "map" || key == "gametype")
|
if (key == "map"s || key == "gametype"s)
|
||||||
{
|
{
|
||||||
this->addEntry(key, value);
|
this->addEntry(key, value);
|
||||||
}
|
}
|
||||||
@ -58,10 +59,14 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MapRotation::RotationData::empty() const noexcept
|
||||||
|
{
|
||||||
|
return this->rotationEntries_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
bool MapRotation::RotationData::contains(const std::string& key, const std::string& value) const
|
bool MapRotation::RotationData::contains(const std::string& key, const std::string& value) const
|
||||||
{
|
{
|
||||||
return std::ranges::any_of(this->rotationEntries_,
|
return std::ranges::any_of(this->rotationEntries_, [&](const auto& entry)
|
||||||
[&](const auto& entry)
|
|
||||||
{
|
{
|
||||||
return entry.first == key && entry.second == value;
|
return entry.first == key && entry.second == value;
|
||||||
});
|
});
|
||||||
@ -74,18 +79,18 @@ namespace Components
|
|||||||
|
|
||||||
for (const auto& [key, val] : this->rotationEntries_)
|
for (const auto& [key, val] : this->rotationEntries_)
|
||||||
{
|
{
|
||||||
if (key == "map")
|
if (key == "map"s)
|
||||||
{
|
{
|
||||||
mapVector.emplace_back(val);
|
mapVector.emplace_back(val);
|
||||||
}
|
}
|
||||||
else if (key == "gametype")
|
else if (key == "gametype"s)
|
||||||
{
|
{
|
||||||
gametypeVector.emplace_back(val);
|
gametypeVector.emplace_back(val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
nlohmann::json mapRotationJson = nlohmann::json
|
auto mapRotationJson = nlohmann::json
|
||||||
{
|
{
|
||||||
{"maps", mapVector},
|
{"maps", mapVector},
|
||||||
{"gametypes", gametypeVector},
|
{"gametypes", gametypeVector},
|
||||||
@ -194,7 +199,7 @@ namespace Components
|
|||||||
void MapRotation::ApplyGametype(const std::string& gametype)
|
void MapRotation::ApplyGametype(const std::string& gametype)
|
||||||
{
|
{
|
||||||
assert(!gametype.empty());
|
assert(!gametype.empty());
|
||||||
Dvar::Var("g_gametype").set(gametype.data());
|
Dvar::Var("g_gametype").set(gametype);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MapRotation::RestartCurrentMap()
|
void MapRotation::RestartCurrentMap()
|
||||||
@ -204,7 +209,7 @@ namespace Components
|
|||||||
if (svMapname.empty())
|
if (svMapname.empty())
|
||||||
{
|
{
|
||||||
Logger::Print(Game::CON_CHANNEL_SERVER, "mapname dvar is empty! Defaulting to mp_afghan\n");
|
Logger::Print(Game::CON_CHANNEL_SERVER, "mapname dvar is empty! Defaulting to mp_afghan\n");
|
||||||
svMapname = "mp_afghan";
|
svMapname = "mp_afghan"s;
|
||||||
}
|
}
|
||||||
|
|
||||||
ApplyMap(svMapname);
|
ApplyMap(svMapname);
|
||||||
@ -212,7 +217,7 @@ namespace Components
|
|||||||
|
|
||||||
void MapRotation::ApplyRotation(RotationData& rotation)
|
void MapRotation::ApplyRotation(RotationData& rotation)
|
||||||
{
|
{
|
||||||
assert(rotation.getEntriesSize() != 0);
|
assert(!rotation.empty());
|
||||||
|
|
||||||
// Continue to apply gametype until a map is found
|
// Continue to apply gametype until a map is found
|
||||||
auto foundMap = false;
|
auto foundMap = false;
|
||||||
@ -222,7 +227,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
const auto& entry = rotation.getNextEntry();
|
const auto& entry = rotation.getNextEntry();
|
||||||
|
|
||||||
if (entry.first == "map")
|
if (entry.first == "map"s)
|
||||||
{
|
{
|
||||||
Logger::Debug("Loading new map: '{}'", entry.second);
|
Logger::Debug("Loading new map: '{}'", entry.second);
|
||||||
ApplyMap(entry.second);
|
ApplyMap(entry.second);
|
||||||
@ -230,7 +235,7 @@ namespace Components
|
|||||||
// Map was found so we exit the loop
|
// Map was found so we exit the loop
|
||||||
foundMap = true;
|
foundMap = true;
|
||||||
}
|
}
|
||||||
else if (entry.first == "gametype")
|
else if (entry.first == "gametype"s)
|
||||||
{
|
{
|
||||||
Logger::Debug("Applying new gametype: '{}'", entry.second);
|
Logger::Debug("Applying new gametype: '{}'", entry.second);
|
||||||
ApplyGametype(entry.second);
|
ApplyGametype(entry.second);
|
||||||
@ -260,7 +265,7 @@ namespace Components
|
|||||||
|
|
||||||
Game::Dvar_SetString(*Game::sv_mapRotationCurrent, "");
|
Game::Dvar_SetString(*Game::sv_mapRotationCurrent, "");
|
||||||
|
|
||||||
if (rotationCurrent.getEntriesSize() == 0)
|
if (rotationCurrent.empty())
|
||||||
{
|
{
|
||||||
Logger::Print(Game::CON_CHANNEL_SERVER, "{} is empty or contains invalid data. Restarting map\n", (*Game::sv_mapRotationCurrent)->name);
|
Logger::Print(Game::CON_CHANNEL_SERVER, "{} is empty or contains invalid data. Restarting map\n", (*Game::sv_mapRotationCurrent)->name);
|
||||||
RestartCurrentMap();
|
RestartCurrentMap();
|
||||||
@ -302,7 +307,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
LoadMapRotation();
|
LoadMapRotation();
|
||||||
if (DedicatedRotation.getEntriesSize() == 0)
|
if (DedicatedRotation.empty())
|
||||||
{
|
{
|
||||||
Logger::Print(Game::CON_CHANNEL_SERVER, "{} is empty or contains invalid data. Restarting map\n", (*Game::sv_mapRotation)->name);
|
Logger::Print(Game::CON_CHANNEL_SERVER, "{} is empty or contains invalid data. Restarting map\n", (*Game::sv_mapRotation)->name);
|
||||||
RestartCurrentMap();
|
RestartCurrentMap();
|
||||||
@ -339,7 +344,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
catch (const std::exception& ex)
|
catch (const std::exception& ex)
|
||||||
{
|
{
|
||||||
Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}: parsing of 'normal' failed", ex.what());
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}: parsing of 'normal' failed\n", ex.what());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,11 +30,12 @@ namespace Components
|
|||||||
// this method should be called to add a new entry (gamemode/map & value)
|
// this method should be called to add a new entry (gamemode/map & value)
|
||||||
void addEntry(const std::string& key, const std::string& value);
|
void addEntry(const std::string& key, const std::string& value);
|
||||||
|
|
||||||
[[nodiscard]] std::size_t getEntriesSize() const;
|
[[nodiscard]] std::size_t getEntriesSize() const noexcept;
|
||||||
rotationEntry& getNextEntry();
|
rotationEntry& getNextEntry();
|
||||||
|
|
||||||
void parse(const std::string& data);
|
void parse(const std::string& data);
|
||||||
|
|
||||||
|
[[nodiscard]] bool empty() const noexcept;
|
||||||
[[nodiscard]] bool contains(const std::string& key, const std::string& value) const;
|
[[nodiscard]] bool contains(const std::string& key, const std::string& value) const;
|
||||||
|
|
||||||
[[nodiscard]] nlohmann::json to_json() const;
|
[[nodiscard]] nlohmann::json to_json() const;
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "RawFiles.hpp"
|
||||||
|
#include "StartupMessages.hpp"
|
||||||
|
#include "Theatre.hpp"
|
||||||
|
#include "UIFeeder.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -29,8 +33,8 @@ namespace Components
|
|||||||
{
|
{
|
||||||
if (this->isValid() && !this->searchPath.iwd)
|
if (this->isValid() && !this->searchPath.iwd)
|
||||||
{
|
{
|
||||||
std::string iwdName = Utils::String::VA("%s.iwd", this->mapname.data());
|
auto iwdName = std::format("{}.iwd", this->mapname);
|
||||||
std::string path = Utils::String::VA("%s\\usermaps\\%s\\%s", Dvar::Var("fs_basepath").get<const char*>(), this->mapname.data(), iwdName.data());
|
auto path = std::format("{}\\usermaps\\{}\\{}", Dvar::Var("fs_basepath").get<std::string>(), this->mapname, iwdName);
|
||||||
|
|
||||||
if (Utils::IO::FileExists(path))
|
if (Utils::IO::FileExists(path))
|
||||||
{
|
{
|
||||||
@ -146,10 +150,10 @@ namespace Components
|
|||||||
team.allocFlags = zoneInfo->allocFlags;
|
team.allocFlags = zoneInfo->allocFlags;
|
||||||
team.freeFlags = zoneInfo->freeFlags;
|
team.freeFlags = zoneInfo->freeFlags;
|
||||||
|
|
||||||
team.name = allocator.duplicateString(Utils::String::VA("iw4x_team_%s", teams.first.data()));
|
team.name = allocator.duplicateString(std::format("iw4x_team_{}", teams.first));
|
||||||
data.push_back(team);
|
data.push_back(team);
|
||||||
|
|
||||||
team.name = allocator.duplicateString(Utils::String::VA("iw4x_team_%s", teams.second.data()));
|
team.name = allocator.duplicateString(std::format("iw4x_team_{}", teams.second));
|
||||||
data.push_back(team);
|
data.push_back(team);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,7 +170,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load patch files
|
// Load patch files
|
||||||
std::string patchZone = Utils::String::VA("patch_%s", zoneInfo->name);
|
auto patchZone = std::format("patch_{}", zoneInfo->name);
|
||||||
if (FastFiles::Exists(patchZone))
|
if (FastFiles::Exists(patchZone))
|
||||||
{
|
{
|
||||||
data.push_back({patchZone.data(), zoneInfo->allocFlags, zoneInfo->freeFlags});
|
data.push_back({patchZone.data(), zoneInfo->allocFlags, zoneInfo->freeFlags});
|
||||||
@ -301,7 +305,7 @@ namespace Components
|
|||||||
|
|
||||||
Game::GfxWorld* world = *reinterpret_cast<Game::GfxWorld**>(0x66DEE94);
|
Game::GfxWorld* world = *reinterpret_cast<Game::GfxWorld**>(0x66DEE94);
|
||||||
|
|
||||||
if (FileSystem::File(Utils::String::VA("sun/%s.sun", Maps::CurrentMainZone.data())).exists())
|
if (FileSystem::File(std::format("sun/{}.sun", Maps::CurrentMainZone)))
|
||||||
{
|
{
|
||||||
Game::R_LoadSunThroughDvars(Maps::CurrentMainZone.data(), &world->sun);
|
Game::R_LoadSunThroughDvars(Maps::CurrentMainZone.data(), &world->sun);
|
||||||
}
|
}
|
||||||
@ -404,13 +408,13 @@ namespace Components
|
|||||||
|
|
||||||
unsigned int Maps::GetUsermapHash(const std::string& map)
|
unsigned int Maps::GetUsermapHash(const std::string& map)
|
||||||
{
|
{
|
||||||
if (Utils::IO::DirectoryExists(Utils::String::VA("usermaps/%s", map.data())))
|
if (Utils::IO::DirectoryExists(std::format("usermaps/{}", map)))
|
||||||
{
|
{
|
||||||
std::string hash;
|
std::string hash;
|
||||||
|
|
||||||
for (std::size_t i = 0; i < ARRAYSIZE(Maps::UserMapFiles); ++i)
|
for (std::size_t i = 0; i < ARRAYSIZE(Maps::UserMapFiles); ++i)
|
||||||
{
|
{
|
||||||
std::string filePath = Utils::String::VA("usermaps/%s/%s%s", map.data(), map.data(), Maps::UserMapFiles[i]);
|
auto filePath = std::format("usermaps/{}/{}{}", map, map, Maps::UserMapFiles[i]);
|
||||||
if (Utils::IO::FileExists(filePath))
|
if (Utils::IO::FileExists(filePath))
|
||||||
{
|
{
|
||||||
hash.append(Utils::Cryptography::SHA256::Compute(Utils::IO::ReadFile(filePath)));
|
hash.append(Utils::Cryptography::SHA256::Compute(Utils::IO::ReadFile(filePath)));
|
||||||
@ -564,13 +568,13 @@ namespace Components
|
|||||||
bool Maps::IsCustomMap()
|
bool Maps::IsCustomMap()
|
||||||
{
|
{
|
||||||
Game::GfxWorld*& gameWorld = *reinterpret_cast<Game::GfxWorld**>(0x66DEE94);
|
Game::GfxWorld*& gameWorld = *reinterpret_cast<Game::GfxWorld**>(0x66DEE94);
|
||||||
if(gameWorld) return gameWorld->checksum == 0xDEADBEEF;
|
if (gameWorld) return (gameWorld->checksum & 0xFFFF0000) == 0xC0D40000;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Maps::IsUserMap(const std::string& mapname)
|
bool Maps::IsUserMap(const std::string& mapname)
|
||||||
{
|
{
|
||||||
return Utils::IO::DirectoryExists(Utils::String::VA("usermaps/%s", mapname.data())) && Utils::IO::FileExists(Utils::String::VA("usermaps/%s/%s.ff", mapname.data(), mapname.data()));
|
return Utils::IO::DirectoryExists(std::format("usermaps/{}", mapname)) && Utils::IO::FileExists(std::format("usermaps/{}/{}.ff", mapname, mapname));
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::XAssetEntry* Maps::GetAssetEntryPool()
|
Game::XAssetEntry* Maps::GetAssetEntryPool()
|
||||||
@ -619,7 +623,7 @@ namespace Components
|
|||||||
|
|
||||||
for (unsigned int i = 0; i < gameWorld->dpvs.smodelCount; ++i)
|
for (unsigned int i = 0; i < gameWorld->dpvs.smodelCount; ++i)
|
||||||
{
|
{
|
||||||
if (gameWorld->dpvs.smodelDrawInsts[i].model->name == model)
|
if (model == "all"s || gameWorld->dpvs.smodelDrawInsts[i].model->name == model)
|
||||||
{
|
{
|
||||||
gameWorld->dpvs.smodelVisData[0][i] = 0;
|
gameWorld->dpvs.smodelVisData[0][i] = 0;
|
||||||
gameWorld->dpvs.smodelVisData[1][i] = 0;
|
gameWorld->dpvs.smodelVisData[1][i] = 0;
|
||||||
@ -641,86 +645,6 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::dvar_t* Maps::GetDistortionDvar()
|
|
||||||
{
|
|
||||||
Game::dvar_t*& r_distortion = *reinterpret_cast<Game::dvar_t**>(0x69F0DCC);
|
|
||||||
|
|
||||||
if(Maps::IsCustomMap())
|
|
||||||
{
|
|
||||||
static Game::dvar_t noDistortion;
|
|
||||||
ZeroMemory(&noDistortion, sizeof noDistortion);
|
|
||||||
return &noDistortion;
|
|
||||||
}
|
|
||||||
|
|
||||||
return r_distortion;
|
|
||||||
}
|
|
||||||
|
|
||||||
__declspec(naked) void Maps::SetDistortionStub()
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
push eax
|
|
||||||
pushad
|
|
||||||
call Maps::GetDistortionDvar
|
|
||||||
|
|
||||||
mov [esp + 20h], eax
|
|
||||||
popad
|
|
||||||
|
|
||||||
pop eax
|
|
||||||
retn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Game::dvar_t* Maps::GetSpecularDvar()
|
|
||||||
{
|
|
||||||
Game::dvar_t*& r_specular = *reinterpret_cast<Game::dvar_t**>(0x69F0D94);
|
|
||||||
static Game::dvar_t* r_specularCustomMaps = Game::Dvar_RegisterBool("r_specularCustomMaps", false, Game::DVAR_ARCHIVE, "Allows shaders to use phong specular lighting on custom maps");
|
|
||||||
|
|
||||||
if (Maps::IsCustomMap())
|
|
||||||
{
|
|
||||||
if (!r_specularCustomMaps->current.enabled)
|
|
||||||
{
|
|
||||||
static Game::dvar_t noSpecular;
|
|
||||||
ZeroMemory(&noSpecular, sizeof noSpecular);
|
|
||||||
return &noSpecular;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return r_specular;
|
|
||||||
}
|
|
||||||
|
|
||||||
__declspec(naked) void Maps::SetSpecularStub1()
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
push eax
|
|
||||||
pushad
|
|
||||||
call Maps::GetSpecularDvar
|
|
||||||
|
|
||||||
mov [esp + 20h], eax
|
|
||||||
popad
|
|
||||||
|
|
||||||
pop eax
|
|
||||||
retn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
__declspec(naked) void Maps::SetSpecularStub2()
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
push eax
|
|
||||||
pushad
|
|
||||||
call Maps::GetSpecularDvar
|
|
||||||
|
|
||||||
mov [esp + 20h], eax
|
|
||||||
popad
|
|
||||||
|
|
||||||
pop edx
|
|
||||||
retn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Maps::G_SpawnTurretHook(Game::gentity_s* ent, int unk, int unk2)
|
void Maps::G_SpawnTurretHook(Game::gentity_s* ent, int unk, int unk2)
|
||||||
{
|
{
|
||||||
if (Maps::CurrentMainZone == "mp_backlot_sh"s || Maps::CurrentMainZone == "mp_con_spring"s ||
|
if (Maps::CurrentMainZone == "mp_backlot_sh"s || Maps::CurrentMainZone == "mp_con_spring"s ||
|
||||||
@ -764,7 +688,7 @@ namespace Components
|
|||||||
Maps::AddDlc({ 6, "Freighter", {"mp_cargoship_sh"} });
|
Maps::AddDlc({ 6, "Freighter", {"mp_cargoship_sh"} });
|
||||||
Maps::AddDlc({ 7, "Resurrection Pack", {"mp_shipment_long", "mp_rust_long", "mp_firingrange"} });
|
Maps::AddDlc({ 7, "Resurrection Pack", {"mp_shipment_long", "mp_rust_long", "mp_firingrange"} });
|
||||||
Maps::AddDlc({ 8, "Recycled Pack", {"mp_bloc_sh", "mp_crash_tropical", "mp_estate_tropical", "mp_fav_tropical", "mp_storm_spring"} });
|
Maps::AddDlc({ 8, "Recycled Pack", {"mp_bloc_sh", "mp_crash_tropical", "mp_estate_tropical", "mp_fav_tropical", "mp_storm_spring"} });
|
||||||
Maps::AddDlc({ 9, "Classics Pack #3", {"mp_farm", "mp_backlot", "mp_pipeline", "mp_countdown", "mp_crash_snow", "mp_carentan"}});
|
Maps::AddDlc({ 9, "Classics Pack #3", {"mp_farm", "mp_backlot", "mp_pipeline", "mp_countdown", "mp_crash_snow", "mp_carentan", "mp_broadcast", "mp_showdown", "mp_convoy"} });
|
||||||
|
|
||||||
Maps::UpdateDlcStatus();
|
Maps::UpdateDlcStatus();
|
||||||
|
|
||||||
@ -842,16 +766,6 @@ namespace Components
|
|||||||
// Allow loading raw suns
|
// Allow loading raw suns
|
||||||
Utils::Hook(0x51B46A, Maps::LoadRawSun, HOOK_CALL).install()->quick();
|
Utils::Hook(0x51B46A, Maps::LoadRawSun, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
// Disable distortion on custom maps
|
|
||||||
//Utils::Hook(0x50AA47, Maps::SetDistortionStub, HOOK_CALL).install()->quick();
|
|
||||||
|
|
||||||
// Disable speculars on custom maps
|
|
||||||
Utils::Hook(0x525EA6, Maps::SetSpecularStub1, HOOK_CALL).install()->quick();
|
|
||||||
Utils::Hook(0x51FBC7, Maps::SetSpecularStub2, HOOK_CALL).install()->quick();
|
|
||||||
Utils::Hook(0x522A2E, Maps::SetSpecularStub2, HOOK_CALL).install()->quick();
|
|
||||||
Utils::Hook::Nop(0x51FBCC, 1);
|
|
||||||
Utils::Hook::Nop(0x522A33, 1);
|
|
||||||
|
|
||||||
// Intercept map loading for usermap initialization
|
// Intercept map loading for usermap initialization
|
||||||
Utils::Hook(0x6245E3, Maps::SpawnServerStub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x6245E3, Maps::SpawnServerStub, HOOK_CALL).install()->quick();
|
||||||
Utils::Hook(0x62493E, Maps::SpawnServerStub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x62493E, Maps::SpawnServerStub, HOOK_CALL).install()->quick();
|
||||||
@ -916,7 +830,7 @@ namespace Components
|
|||||||
unsigned int i = 0;
|
unsigned int i = 0;
|
||||||
for (auto& model : models)
|
for (auto& model : models)
|
||||||
{
|
{
|
||||||
Game::R_AddCmdDrawText(Utils::String::VA("%d %s", model.second, model.first.data()), 0x7FFFFFFF, font, 15.0f, (height * scale + 1) * (i++ + 1) + 15.0f, scale, scale, 0.0f, color, Game::ITEM_TEXTSTYLE_NORMAL);
|
Game::R_AddCmdDrawText(Utils::String::VA("%d %s", model.second, model.first.data()), std::numeric_limits<int>::max(), font, 15.0f, (height * scale + 1) * (i++ + 1) + 15.0f, scale, scale, 0.0f, color, Game::ITEM_TEXTSTYLE_NORMAL);
|
||||||
}
|
}
|
||||||
}, Scheduler::Pipeline::RENDERER);
|
}, Scheduler::Pipeline::RENDERER);
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user