commit
c3e7c83dbd
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: Discord Server
|
||||||
|
url: https://discord.gg/sKeVmR3/
|
||||||
|
about: Please ask and answer support questions here.
|
||||||
|
- name: XLabsProject Website
|
||||||
|
url: https://xlabs.dev/
|
||||||
|
about: The official website.
|
30
.github/ISSUE_TEMPLATE/get-help.md
vendored
Normal file
30
.github/ISSUE_TEMPLATE/get-help.md
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
---
|
||||||
|
name: Get help
|
||||||
|
about: Get help using IW4x
|
||||||
|
title: ''
|
||||||
|
labels: discussion
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Do not open an issue here if you need help with modding or have a problem getting the client to run.
|
||||||
|
It is very likely your problem will be resolved by reading the [FAQ](https://xlabs.dev/iw4x_faq) carefully.
|
||||||
|
Ask in `iw4x-support` or `iw4x-modding` channels on the [Discord](https://discord.gg/sKeVmR3) server if you still have problems.
|
||||||
|
If this does not apply, please continue by filling in the template below._
|
||||||
|
|
||||||
|
**What are you trying to do?**
|
||||||
|
A short, concise description of the outcome you are trying to achieve.
|
||||||
|
|
||||||
|
**What problem are you having?**
|
||||||
|
A clear and concise description of the problem that is blocking you from your desired outcome, ex. "IW4x is crashing with this error message: ..."
|
||||||
|
Please walk us through the steps you have taken when you encountered this problem.
|
||||||
|
If IW4x is crashing, include the minidump file and the crash address in text form. Screenshots showing the message box with the crash address are not acceptable.
|
||||||
|
|
||||||
|
**What have you tried so far?**
|
||||||
|
Describe any steps you've already taken to try to get past this issue. Have you found a workaround?
|
||||||
|
|
||||||
|
**What version of IW4x are you using?**
|
||||||
|
`iw4x.exe -version` will show you the version. Please make sure you are up to date with the latest master branch.
|
||||||
|
|
||||||
|
**Anything else we should know?**
|
||||||
|
Add any other context about the problem here.
|
26
.github/ISSUE_TEMPLATE/request-a-feature.md
vendored
Normal file
26
.github/ISSUE_TEMPLATE/request-a-feature.md
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
name: Request a feature
|
||||||
|
about: Suggest a new feature or enhancement
|
||||||
|
title: ''
|
||||||
|
labels: feature
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Before opening a new feature request, please see [Issues](https://github.com/XLabsProject/iw4x-client/issues) and check that a similar issue does not already exist.
|
||||||
|
If this a new request, help us help you by filling in the template below._
|
||||||
|
|
||||||
|
**What problem will this solve?**
|
||||||
|
A clear and concise description of the problem this new feature is meant to solve, ex. "There is something wrong with the game" or "There is something wrong with IW4x's source code".
|
||||||
|
Please limit your request to a single feature; create multiple feature requests instead.
|
||||||
|
|
||||||
|
**What might be a solution?**
|
||||||
|
A clear and concise description of what you want to happen. If you are proposing changes to IW4x's source code, tell us which component you want to be changed.
|
||||||
|
If you propose changes to the game, you may use images or videos to illustrate what aspect of the game should be changed.
|
||||||
|
|
||||||
|
**What other alternatives have you already considered?**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
It may help others to find workarounds until the problem is resolved.
|
||||||
|
|
||||||
|
**Anything else we should know?**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
19
.github/pull_request_template.md
vendored
Normal file
19
.github/pull_request_template.md
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
**What does this PR do?**
|
||||||
|
|
||||||
|
Thanks for the contribution! Please provide a concise description of the problem this request solves.
|
||||||
|
|
||||||
|
**How does this PR change IW4x's behaviour?**
|
||||||
|
|
||||||
|
Are there any breaking changes? Will any existing behaviour change?
|
||||||
|
Specify the new expected behaviour if this PR adds a new gameplay feature or alters an existing game mechanic. You may provide an image or video showing the changes.
|
||||||
|
|
||||||
|
**Anything else we should know?**
|
||||||
|
|
||||||
|
Add any other context about your changes here.
|
||||||
|
|
||||||
|
**Did you check all the boxes?**
|
||||||
|
|
||||||
|
- [ ] Focus on a single fix or feature; remove any unrelated formatting or code changes
|
||||||
|
- [ ] Mention any [related issues](https://github.com/XLabsProject/iw4x-client/issues) (put `closes #XXXX` in comment to auto-close issue when PR is merged)
|
||||||
|
- [ ] Follow our [coding conventions](https://github.com/XLabsProject/iw4x-client/blob/master/CODESTYLE.md)
|
||||||
|
- [ ] Minimize the number of commits
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -149,5 +149,5 @@ ida/*
|
|||||||
# User scripts
|
# User scripts
|
||||||
user*.bat
|
user*.bat
|
||||||
|
|
||||||
# Premake binary
|
### Visual Studio Code
|
||||||
#premake5.exe
|
.vscode/
|
||||||
|
47
CHANGELOG.md
47
CHANGELOG.md
@ -4,6 +4,46 @@ 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.4] - 2022-07-28
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Add `DisableWeaponPickup` GSC method (#329)
|
||||||
|
- Add `EnableWeaponPickup` GSC method (#329)
|
||||||
|
- Add `protect-saved-dvars` command line argument (#335)
|
||||||
|
- Add `clanName` dvar. Can be edited in the `barracks` menu (#361)
|
||||||
|
- Add DLC9 containing classic maps from CoD4: Backlot, Chinatown, Winter Crash, Pipeline and Downpour
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- `sv_mapRotationCurrent` functionality has been restored for backwards compatibility (#328)
|
||||||
|
- GSC IO Functions are restricted to the `scriptdata` folder (#322)
|
||||||
|
- `scriptablehttp` command line argument is no longer needed (#322)
|
||||||
|
- Removed `noclip` built-in GSC method. Replaced with script-based solution (#387)
|
||||||
|
- Removed `ufo` built-in GSC method. Replaced with script-based solution (#387)
|
||||||
|
- Removed `god` built-in GSC method. Replaced with script-based solution (#388)
|
||||||
|
- Removed `demiGod` built-in GSC method. Replaced with script-based solution (#388)
|
||||||
|
- Removed `noTarget` built-in GSC method. Replaced with script-based solution (#388)
|
||||||
|
- Added to the iw4x-rawfiles `common_scripts\iw4x_utility` GSC script, it contains the scripts-based solution for the removed GSC built-in methods
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Node system works again for clients (#331)
|
||||||
|
- Fixed output of `g_log_list` & `log_list` (#336)
|
||||||
|
- Fixed toast notifications (#337)
|
||||||
|
- Fixed `banClient` command (#311)
|
||||||
|
- Fixed singleplayer maps crashing in renderer module (#340)
|
||||||
|
- Fixed muzzle flash on COD:OL maps (#352)
|
||||||
|
- Server commands are no longer being registered twice (#339)
|
||||||
|
- Fixed issue where grenades and projectiles had no bounce sounds (#368)
|
||||||
|
- Fixed issue that could cause the game to crash when loading CoD4 maps (#372)
|
||||||
|
|
||||||
|
### 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.3] - 2022-06-23
|
## [0.7.3] - 2022-06-23
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
@ -19,6 +59,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
|
|||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- `openLink` command was removed for security reasons (#286)
|
- `openLink` command was removed for security reasons (#286)
|
||||||
|
- `sv_mapRotationCurrent` is not being used anymore (#302)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
@ -26,7 +67,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
|
|||||||
- Fix crash on clearing key binds (#278)
|
- Fix crash on clearing key binds (#278)
|
||||||
- Fix maps dropping out of the map rotation when using `sv_randomMapRotation` (#283)
|
- Fix maps dropping out of the map rotation when using `sv_randomMapRotation` (#283)
|
||||||
|
|
||||||
### Known issue
|
### Known issues
|
||||||
|
|
||||||
- HTTPS is not supported for fast downloads at the moment.
|
- HTTPS is not supported for fast downloads at the moment.
|
||||||
- Sound issue fix is experimental as the bug is not fully understood.
|
- Sound issue fix is experimental as the bug is not fully understood.
|
||||||
@ -50,7 +91,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
|
|||||||
- Fixed internet, local and favorites filters (#260)
|
- Fixed internet, local and favorites filters (#260)
|
||||||
- `sv_lanOnly` Dvar now prevents the server from sending heartbeats to master if set to true (#246)
|
- `sv_lanOnly` Dvar now prevents the server from sending heartbeats to master if set to true (#246)
|
||||||
|
|
||||||
### Known issue
|
### Known issues
|
||||||
|
|
||||||
- HTTPS is not supported for fast downloads at the moment.
|
- HTTPS is not supported for fast downloads at the moment.
|
||||||
- Sound issue fix is experimental as the bug is not fully understood.
|
- Sound issue fix is experimental as the bug is not fully understood.
|
||||||
@ -84,7 +125,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.
|
|||||||
- Fixed client crash when cg_chatHeight was set to 0 (#237)
|
- Fixed client crash when cg_chatHeight was set to 0 (#237)
|
||||||
- Fixed GSC function Scr_TableLookupIStringByRow (#162)
|
- Fixed GSC function Scr_TableLookupIStringByRow (#162)
|
||||||
|
|
||||||
### Known issue
|
### Known issues
|
||||||
|
|
||||||
- HTTPS is not supported for fast downloads at the moment.
|
- HTTPS is not supported for fast downloads at the moment.
|
||||||
- Sound issue fix is experimental as the bug is not fully understood.
|
- Sound issue fix is experimental as the bug is not fully understood.
|
||||||
|
@ -33,7 +33,6 @@
|
|||||||
| `-stdout` | Redirect all logging output to the terminal iw4x is started from, or if there is none, creates a new terminal window to write log information in. |
|
| `-stdout` | Redirect all logging output to the terminal iw4x is started from, or if there is none, creates a new terminal window to write log information in. |
|
||||||
| `-console` | Allow the game to display its own separate interactive console window. |
|
| `-console` | Allow the game to display its own separate interactive console window. |
|
||||||
| `-dedicated` | Starts the game as a headless dedicated server. |
|
| `-dedicated` | Starts the game as a headless dedicated server. |
|
||||||
| `-scriptablehttp` | Enable HTTP related gsc functions. |
|
|
||||||
| `-bigminidumps` | Include all code sections from loaded modules in the dump. |
|
| `-bigminidumps` | Include all code sections from loaded modules in the dump. |
|
||||||
| `-reallybigminidumps` | Include data sections from all loaded modules in the dump. |
|
| `-reallybigminidumps` | Include data sections from all loaded modules in the dump. |
|
||||||
| `-dump` | Write info of loaded assets to the raw folder as they are being loaded. |
|
| `-dump` | Write info of loaded assets to the raw folder as they are being loaded. |
|
||||||
@ -42,6 +41,7 @@
|
|||||||
| `-version` | Print IW4x build info on startup. |
|
| `-version` | Print IW4x build info on startup. |
|
||||||
| `-zonebuilder` | Start the interactive zonebuilder tool console instead of starting the game. |
|
| `-zonebuilder` | Start the interactive zonebuilder tool console instead of starting the game. |
|
||||||
| `-nosteam` | Disable friends feature and do not update Steam about the game's current status just like an invisible mode. |
|
| `-nosteam` | Disable friends feature and do not update Steam about the game's current status just like an invisible mode. |
|
||||||
|
| `-protect-saved-dvars` | Block the server from modifying saved/archive dvars. |
|
||||||
|
|
||||||
|
|
||||||
## Disclaimer
|
## Disclaimer
|
||||||
|
2
deps/GSL
vendored
2
deps/GSL
vendored
@ -1 +1 @@
|
|||||||
Subproject commit d9fc52e20e32fcc804502480a781120b210afd41
|
Subproject commit 330583f47800c60cf001239550d291d16274756a
|
2
deps/libtomcrypt
vendored
2
deps/libtomcrypt
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 06a81aeb227424182125363f7554fad5146d6d2a
|
Subproject commit 8fd5dad96b56beb53b5cf199cb63fb76dfba32bb
|
2
deps/protobuf
vendored
2
deps/protobuf
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 6e9e60367d8744e86856590d8ea0e793c61deeec
|
Subproject commit fb6f8da08b60b6beb5bb360d79dd3feda0147da7
|
2
deps/zlib
vendored
2
deps/zlib
vendored
@ -1 +1 @@
|
|||||||
Subproject commit ec3df00224d4b396e2ac6586ab5d25f673caa4c2
|
Subproject commit 2333419cd76cb9ae5f15c9b240b16a2052b27691
|
@ -31,8 +31,8 @@ namespace Components
|
|||||||
|
|
||||||
Loader::Register(new Flags());
|
Loader::Register(new Flags());
|
||||||
Loader::Register(new Singleton());
|
Loader::Register(new Singleton());
|
||||||
Loader::Register(new Exception()); // install our exception handler as early as posssible to get better debug dumps from startup crashes
|
// Install our exception handler as early as posssible to get better debug dumps from startup crashes
|
||||||
|
Loader::Register(new Exception());
|
||||||
Loader::Register(new Auth());
|
Loader::Register(new Auth());
|
||||||
Loader::Register(new Bans());
|
Loader::Register(new Bans());
|
||||||
Loader::Register(new Bots());
|
Loader::Register(new Bots());
|
||||||
@ -49,7 +49,6 @@ namespace Components
|
|||||||
Loader::Register(new Zones());
|
Loader::Register(new Zones());
|
||||||
Loader::Register(new D3D9Ex());
|
Loader::Register(new D3D9Ex());
|
||||||
Loader::Register(new Logger());
|
Loader::Register(new Logger());
|
||||||
Loader::Register(new Script());
|
|
||||||
Loader::Register(new Weapon());
|
Loader::Register(new Weapon());
|
||||||
Loader::Register(new Window());
|
Loader::Register(new Window());
|
||||||
Loader::Register(new Command());
|
Loader::Register(new Command());
|
||||||
@ -62,7 +61,7 @@ namespace Components
|
|||||||
Loader::Register(new Network());
|
Loader::Register(new Network());
|
||||||
Loader::Register(new Session());
|
Loader::Register(new Session());
|
||||||
Loader::Register(new Theatre());
|
Loader::Register(new Theatre());
|
||||||
//Loader::Register(new ClanTags());
|
Loader::Register(new ClanTags());
|
||||||
Loader::Register(new Download());
|
Loader::Register(new Download());
|
||||||
Loader::Register(new Playlist());
|
Loader::Register(new Playlist());
|
||||||
Loader::Register(new RawFiles());
|
Loader::Register(new RawFiles());
|
||||||
@ -104,7 +103,7 @@ namespace Components
|
|||||||
Loader::Register(new Movement());
|
Loader::Register(new Movement());
|
||||||
Loader::Register(new Elevators());
|
Loader::Register(new Elevators());
|
||||||
Loader::Register(new ClientCommand());
|
Loader::Register(new ClientCommand());
|
||||||
Loader::Register(new ScriptExtension());
|
Loader::Register(new VisionFile());
|
||||||
Loader::Register(new Branding());
|
Loader::Register(new Branding());
|
||||||
Loader::Register(new Debug());
|
Loader::Register(new Debug());
|
||||||
Loader::Register(new RawMouse());
|
Loader::Register(new RawMouse());
|
||||||
@ -114,6 +113,8 @@ namespace Components
|
|||||||
Loader::Register(new UserInfo());
|
Loader::Register(new UserInfo());
|
||||||
Loader::Register(new Events());
|
Loader::Register(new Events());
|
||||||
|
|
||||||
|
Loader::Register(new GSC());
|
||||||
|
|
||||||
Loader::Pregame = false;
|
Loader::Pregame = false;
|
||||||
|
|
||||||
// Make sure preDestroy is called when the game shuts down
|
// Make sure preDestroy is called when the game shuts down
|
||||||
|
@ -74,7 +74,6 @@ namespace Components
|
|||||||
#include "Modules/Toast.hpp"
|
#include "Modules/Toast.hpp"
|
||||||
#include "Modules/Zones.hpp"
|
#include "Modules/Zones.hpp"
|
||||||
#include "Modules/D3D9Ex.hpp"
|
#include "Modules/D3D9Ex.hpp"
|
||||||
#include "Modules/Script.hpp"
|
|
||||||
#include "Modules/Weapon.hpp"
|
#include "Modules/Weapon.hpp"
|
||||||
#include "Modules/Window.hpp"
|
#include "Modules/Window.hpp"
|
||||||
#include "Modules/Command.hpp"
|
#include "Modules/Command.hpp"
|
||||||
@ -134,8 +133,8 @@ namespace Components
|
|||||||
#include "Modules/Movement.hpp"
|
#include "Modules/Movement.hpp"
|
||||||
#include "Modules/Elevators.hpp"
|
#include "Modules/Elevators.hpp"
|
||||||
#include "Modules/ClientCommand.hpp"
|
#include "Modules/ClientCommand.hpp"
|
||||||
|
#include "Modules/VisionFile.hpp"
|
||||||
#include "Modules/Gamepad.hpp"
|
#include "Modules/Gamepad.hpp"
|
||||||
#include "Modules/ScriptExtension.hpp"
|
|
||||||
#include "Modules/Branding.hpp"
|
#include "Modules/Branding.hpp"
|
||||||
#include "Modules/Debug.hpp"
|
#include "Modules/Debug.hpp"
|
||||||
#include "Modules/RawMouse.hpp"
|
#include "Modules/RawMouse.hpp"
|
||||||
@ -144,3 +143,5 @@ namespace Components
|
|||||||
#include "Modules/Ceg.hpp"
|
#include "Modules/Ceg.hpp"
|
||||||
#include "Modules/UserInfo.hpp"
|
#include "Modules/UserInfo.hpp"
|
||||||
#include "Modules/Events.hpp"
|
#include "Modules/Events.hpp"
|
||||||
|
|
||||||
|
#include "Modules/GSC/GSC.hpp"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include <StdInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
#include "AssetInterfaces/IFont_s.hpp"
|
#include "AssetInterfaces/IFont_s.hpp"
|
||||||
#include "AssetInterfaces/IWeapon.hpp"
|
#include "AssetInterfaces/IWeapon.hpp"
|
||||||
@ -269,6 +269,13 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type == Game::XAssetType::ASSET_TYPE_GFXWORLD && Zones::Version() >= 316)
|
||||||
|
{
|
||||||
|
asset.gfxWorld->sortKeyEffectDecal = 39;
|
||||||
|
asset.gfxWorld->sortKeyEffectAuto = 48;
|
||||||
|
asset.gfxWorld->sortKeyDistortion = 43;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AssetHandler::IsAssetEligible(Game::XAssetType type, Game::XAssetHeader *asset)
|
bool AssetHandler::IsAssetEligible(Game::XAssetType type, Game::XAssetHeader *asset)
|
||||||
|
@ -44,6 +44,7 @@ namespace Assets
|
|||||||
{"wc_unlit_alphatest", "wc_unlit_blend_lin"},
|
{"wc_unlit_alphatest", "wc_unlit_blend_lin"},
|
||||||
{"wc_unlit_blend", "wc_unlit_blend_lin_ua"},
|
{"wc_unlit_blend", "wc_unlit_blend_lin_ua"},
|
||||||
{"wc_unlit_replace", "wc_unlit_replace_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_replace", "mc_unlit_replace_lin"},
|
||||||
{"mc_unlit_nofog", "mc_unlit_blend_nofog_ua"},
|
{"mc_unlit_nofog", "mc_unlit_blend_nofog_ua"},
|
||||||
@ -112,8 +113,9 @@ namespace Assets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
Components::Logger::Print("Techset {} exists with the same name in iw4, and was mapped 1:1 with %s\n", techsetName, asset->techniqueSet->name);
|
{
|
||||||
|
Components::Logger::Print("Techset {} exists with the same name in iw4, and was mapped 1:1 with {}\n", techsetName, asset->techniqueSet->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!asset->techniqueSet)
|
if (!asset->techniqueSet)
|
||||||
@ -257,7 +259,8 @@ namespace Assets
|
|||||||
{
|
{
|
||||||
Game::XAssetHeader header = entry->asset.header;
|
Game::XAssetHeader header = entry->asset.header;
|
||||||
|
|
||||||
if (header.material->techniqueSet == iw4TechSet->asset.header.techniqueSet)
|
if (header.material->techniqueSet == iw4TechSet->asset.header.techniqueSet
|
||||||
|
&& !std::string(header.material->info.name).contains("icon")) // Yeah this has a tendency to fuck up a LOT of transparent materials
|
||||||
{
|
{
|
||||||
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);
|
||||||
|
@ -432,7 +432,7 @@ namespace Components
|
|||||||
Scheduler::Loop(Auth::Frame, Scheduler::Pipeline::MAIN);
|
Scheduler::Loop(Auth::Frame, Scheduler::Pipeline::MAIN);
|
||||||
|
|
||||||
// Register dvar
|
// Register dvar
|
||||||
Dvar::Register<int>("sv_securityLevel", 23, 0, 512, Game::dvar_flag::DVAR_SERVERINFO, "Security level for GUID certificates (POW)");
|
Dvar::Register<int>("sv_securityLevel", 23, 0, 512, Game::DVAR_SERVERINFO, "Security level for GUID certificates (POW)");
|
||||||
|
|
||||||
// Install registration hook
|
// Install registration hook
|
||||||
Utils::Hook(0x6265F9, Auth::DirectConnectStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x6265F9, Auth::DirectConnectStub, HOOK_JUMP).install()->quick();
|
||||||
|
@ -2,14 +2,18 @@
|
|||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
std::recursive_mutex Bans::AccessMutex;
|
// Have only one instance of IW4x read/write the file
|
||||||
|
std::unique_lock<Utils::NamedMutex> Bans::Lock()
|
||||||
bool Bans::IsBanned(Bans::Entry entry)
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(Bans::AccessMutex);
|
static Utils::NamedMutex mutex{"iw4x-ban-list-lock"};
|
||||||
|
std::unique_lock lock{mutex};
|
||||||
|
return lock;
|
||||||
|
}
|
||||||
|
|
||||||
Bans::BanList list;
|
bool Bans::IsBanned(const banEntry& entry)
|
||||||
Bans::LoadBans(&list);
|
{
|
||||||
|
BanList list;
|
||||||
|
LoadBans(&list);
|
||||||
|
|
||||||
if (entry.first.bits)
|
if (entry.first.bits)
|
||||||
{
|
{
|
||||||
@ -24,7 +28,7 @@ namespace Components
|
|||||||
|
|
||||||
if (entry.second.full)
|
if (entry.second.full)
|
||||||
{
|
{
|
||||||
for (auto& ipEntry : list.ipList)
|
for (const auto& ipEntry : list.ipList)
|
||||||
{
|
{
|
||||||
if (ipEntry.full == entry.second.full)
|
if (ipEntry.full == entry.second.full)
|
||||||
{
|
{
|
||||||
@ -36,17 +40,15 @@ namespace Components
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bans::InsertBan(Bans::Entry entry)
|
void Bans::InsertBan(const banEntry& entry)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(Bans::AccessMutex);
|
BanList list;
|
||||||
|
LoadBans(&list);
|
||||||
Bans::BanList list;
|
|
||||||
Bans::LoadBans(&list);
|
|
||||||
|
|
||||||
if (entry.first.bits)
|
if (entry.first.bits)
|
||||||
{
|
{
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (auto& idEntry : list.idList)
|
for (const auto& idEntry : list.idList)
|
||||||
{
|
{
|
||||||
if (idEntry.bits == entry.first.bits)
|
if (idEntry.bits == entry.first.bits)
|
||||||
{
|
{
|
||||||
@ -64,7 +66,7 @@ namespace Components
|
|||||||
if (entry.second.full)
|
if (entry.second.full)
|
||||||
{
|
{
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (auto& ipEntry : list.ipList)
|
for (const auto& ipEntry : list.ipList)
|
||||||
{
|
{
|
||||||
if (ipEntry.full == entry.second.full)
|
if (ipEntry.full == entry.second.full)
|
||||||
{
|
{
|
||||||
@ -79,12 +81,14 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Bans::SaveBans(&list);
|
SaveBans(&list);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bans::SaveBans(BanList* list)
|
void Bans::SaveBans(const BanList* list)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(Bans::AccessMutex);
|
assert(list != nullptr);
|
||||||
|
|
||||||
|
const auto _ = Lock();
|
||||||
|
|
||||||
std::vector<std::string> idVector;
|
std::vector<std::string> idVector;
|
||||||
std::vector<std::string> ipVector;
|
std::vector<std::string> ipVector;
|
||||||
@ -109,32 +113,40 @@ namespace Components
|
|||||||
{ "id", idVector },
|
{ "id", idVector },
|
||||||
};
|
};
|
||||||
|
|
||||||
FileSystem::FileWriter ban("bans.json");
|
FileSystem::FileWriter ("bans.json").write(bans.dump());
|
||||||
ban.write(bans.dump());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bans::LoadBans(Bans::BanList* list)
|
void Bans::LoadBans(BanList* list)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(Bans::AccessMutex);
|
assert(list != nullptr);
|
||||||
|
|
||||||
|
const auto _ = Lock();
|
||||||
|
|
||||||
FileSystem::File bans("bans.json");
|
FileSystem::File bans("bans.json");
|
||||||
|
|
||||||
if (bans.exists())
|
if (!bans.exists())
|
||||||
{
|
{
|
||||||
|
Logger::Debug("bans.json does not exist");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
json11::Json banData = json11::Json::parse(bans.getBuffer(), error);
|
const auto banData = json11::Json::parse(bans.getBuffer(), error);
|
||||||
|
|
||||||
if (!error.empty())
|
if (!error.empty())
|
||||||
{
|
{
|
||||||
Logger::Error(Game::ERR_FATAL, "Failed to parse bans (bans.json): {}", error);
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "Failed to parse bans.json: {}\n", error);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!list) return;
|
if (!banData.is_object())
|
||||||
|
|
||||||
if (banData.is_object())
|
|
||||||
{
|
{
|
||||||
auto idList = banData["id"];
|
Logger::Debug("bans.json contains invalid data");
|
||||||
auto ipList = banData["ip"];
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& idList = banData["id"];
|
||||||
|
const auto& ipList = banData["ip"];
|
||||||
|
|
||||||
if (idList.is_array())
|
if (idList.is_array())
|
||||||
{
|
{
|
||||||
@ -163,41 +175,23 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bans::BanClientNum(int num, const std::string& reason)
|
void Bans::BanClient(Game::client_t* cl, const std::string& reason)
|
||||||
{
|
{
|
||||||
if (!Dvar::Var("sv_running").get<bool>())
|
|
||||||
{
|
|
||||||
Logger::Print("Server is not running.\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*Game::svs_clientCount <= num)
|
|
||||||
{
|
|
||||||
Logger::Print("Player {} is not on the server\n", num);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Game::client_t* client = &Game::svs_clients[num];
|
|
||||||
|
|
||||||
SteamID guid;
|
SteamID guid;
|
||||||
guid.bits = client->steamID;
|
guid.bits = cl->steamID;
|
||||||
|
|
||||||
Bans::InsertBan({guid, client->netchan.remoteAddress.ip});
|
InsertBan({guid, cl->netchan.remoteAddress.ip});
|
||||||
|
|
||||||
Game::SV_GameDropClient(num, reason.data());
|
Game::SV_DropClient(cl, reason.data(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bans::UnbanClient(SteamID id)
|
void Bans::UnbanClient(SteamID id)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(Bans::AccessMutex);
|
BanList list;
|
||||||
|
LoadBans(&list);
|
||||||
|
|
||||||
Bans::BanList list;
|
const auto entry = std::find_if(list.idList.begin(), list.idList.end(), [&id](const SteamID& entry)
|
||||||
Bans::LoadBans(&list);
|
|
||||||
|
|
||||||
auto entry = std::find_if(list.idList.begin(), list.idList.end(), [&id](SteamID& entry)
|
|
||||||
{
|
{
|
||||||
return id.bits == entry.bits;
|
return id.bits == entry.bits;
|
||||||
});
|
});
|
||||||
@ -207,17 +201,15 @@ namespace Components
|
|||||||
list.idList.erase(entry);
|
list.idList.erase(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
Bans::SaveBans(&list);
|
SaveBans(&list);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bans::UnbanClient(Game::netIP_t ip)
|
void Bans::UnbanClient(Game::netIP_t ip)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(Bans::AccessMutex);
|
BanList list;
|
||||||
|
LoadBans(&list);
|
||||||
|
|
||||||
Bans::BanList list;
|
const auto entry = std::find_if(list.ipList.begin(), list.ipList.end(), [&ip](const Game::netIP_t& entry)
|
||||||
Bans::LoadBans(&list);
|
|
||||||
|
|
||||||
auto entry = std::find_if(list.ipList.begin(), list.ipList.end(), [&ip](Game::netIP_t& entry)
|
|
||||||
{
|
{
|
||||||
return ip.full == entry.full;
|
return ip.full == entry.full;
|
||||||
});
|
});
|
||||||
@ -227,31 +219,75 @@ namespace Components
|
|||||||
list.ipList.erase(entry);
|
list.ipList.erase(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
Bans::SaveBans(&list);
|
SaveBans(&list);
|
||||||
}
|
}
|
||||||
|
|
||||||
Bans::Bans()
|
Bans::Bans()
|
||||||
{
|
{
|
||||||
Command::Add("banclient", [](Command::Params* params)
|
Command::Add("banClient", [](Command::Params* params)
|
||||||
{
|
{
|
||||||
if (params->size() < 2) return;
|
if (!Dvar::Var("sv_running").get<bool>())
|
||||||
|
{
|
||||||
|
Logger::Print("Server is not running.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string reason = "EXE_ERR_BANNED_PERM";
|
if (params->size() < 2)
|
||||||
if (params->size() >= 3) reason = params->join(2);
|
{
|
||||||
|
Logger::Print("{} <client number> : permanently ban a client\n", params->get(0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Bans::BanClientNum(atoi(params->get(1)), reason);
|
const auto* input = params->get(1);
|
||||||
|
|
||||||
|
for (auto i = 0; input[i] != '\0'; ++i)
|
||||||
|
{
|
||||||
|
if (input[i] < '0' || input[i] > '9')
|
||||||
|
{
|
||||||
|
Logger::Print("Bad slot number: {}\n", input);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto num = std::atoi(input);
|
||||||
|
|
||||||
|
if (num < 0 || num >= *Game::svs_clientCount)
|
||||||
|
{
|
||||||
|
Logger::Print("Bad client slot: {}\n", num);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* cl = &Game::svs_clients[num];
|
||||||
|
if (cl->state == Game::CS_FREE)
|
||||||
|
{
|
||||||
|
Logger::Print("Client {} is not active\n", num);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string reason = params->size() < 3 ? "EXE_ERR_BANNED_PERM" : params->join(2);
|
||||||
|
Bans::BanClient(&Game::svs_clients[num], reason);
|
||||||
});
|
});
|
||||||
|
|
||||||
Command::Add("unbanclient", [](Command::Params* params)
|
Command::Add("unbanClient", [](Command::Params* params)
|
||||||
{
|
{
|
||||||
if (params->size() < 2) return;
|
if (!Dvar::Var("sv_running").get<bool>())
|
||||||
|
{
|
||||||
|
Logger::Print("Server is not running.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::string type = params->get(1);
|
if (params->size() < 3)
|
||||||
|
{
|
||||||
|
Logger::Print("{} <type> <ip or guid>\n", params->get(0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* type = params->get(1);
|
||||||
|
|
||||||
if (type == "ip"s)
|
if (type == "ip"s)
|
||||||
{
|
{
|
||||||
Network::Address address(params->get(2));
|
Network::Address address(params->get(2));
|
||||||
Bans::UnbanClient(address.getIP());
|
UnbanClient(address.getIP());
|
||||||
|
|
||||||
Logger::Print("Unbanned IP {}\n", params->get(2));
|
Logger::Print("Unbanned IP {}\n", params->get(2));
|
||||||
|
|
||||||
@ -261,17 +297,10 @@ namespace Components
|
|||||||
SteamID id;
|
SteamID id;
|
||||||
id.bits = strtoull(params->get(2), nullptr, 16);
|
id.bits = strtoull(params->get(2), nullptr, 16);
|
||||||
|
|
||||||
Bans::UnbanClient(id);
|
UnbanClient(id);
|
||||||
|
|
||||||
Logger::Print("Unbanned GUID {}\n", params->get(2));
|
Logger::Print("Unbanned GUID {}\n", params->get(2));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Verify the list on startup
|
|
||||||
Scheduler::OnGameInitialized([]
|
|
||||||
{
|
|
||||||
Bans::BanList list;
|
|
||||||
Bans::LoadBans(&list);
|
|
||||||
}, Scheduler::Pipeline::SERVER);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,27 +5,27 @@ namespace Components
|
|||||||
class Bans : public Component
|
class Bans : public Component
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef std::pair<SteamID, Game::netIP_t> Entry;
|
using banEntry = std::pair<SteamID, Game::netIP_t>;
|
||||||
|
|
||||||
Bans();
|
Bans();
|
||||||
|
|
||||||
static void BanClientNum(int num, const std::string& reason);
|
static std::unique_lock<Utils::NamedMutex> Lock();
|
||||||
|
|
||||||
|
static void BanClient(Game::client_t* cl, const std::string& reason);
|
||||||
static void UnbanClient(SteamID id);
|
static void UnbanClient(SteamID id);
|
||||||
static void UnbanClient(Game::netIP_t ip);
|
static void UnbanClient(Game::netIP_t ip);
|
||||||
|
|
||||||
static bool IsBanned(Entry entry);
|
static bool IsBanned(const banEntry& entry);
|
||||||
static void InsertBan(Entry entry);
|
static void InsertBan(const banEntry& entry);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class BanList
|
struct BanList
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
std::vector<SteamID> idList;
|
std::vector<SteamID> idList;
|
||||||
std::vector<Game::netIP_t> ipList;
|
std::vector<Game::netIP_t> ipList;
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::recursive_mutex AccessMutex;
|
|
||||||
static void LoadBans(BanList* list);
|
static void LoadBans(BanList* list);
|
||||||
static void SaveBans(BanList* list);
|
static void SaveBans(const BanList* list);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "GSC/Script.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -121,24 +122,6 @@ namespace Components
|
|||||||
Script::AddMethod("IsBot", Bots::GScr_isTestClient); // Usage: self IsBot();
|
Script::AddMethod("IsBot", Bots::GScr_isTestClient); // Usage: self IsBot();
|
||||||
Script::AddMethod("IsTestClient", Bots::GScr_isTestClient); // Usage: self IsTestClient();
|
Script::AddMethod("IsTestClient", Bots::GScr_isTestClient); // Usage: self IsTestClient();
|
||||||
|
|
||||||
Script::AddMethod("SetPing", [](Game::scr_entref_t entref) // gsc: self SetPing(<int>)
|
|
||||||
{
|
|
||||||
auto ping = Game::Scr_GetInt(0);
|
|
||||||
|
|
||||||
ping = std::clamp(ping, 0, 999);
|
|
||||||
|
|
||||||
const auto* ent = Game::GetPlayerEntity(entref);
|
|
||||||
auto* client = Script::GetClient(ent);
|
|
||||||
|
|
||||||
if (Game::SV_IsTestClient(ent->s.number) == 0)
|
|
||||||
{
|
|
||||||
Game::Scr_Error("^1SetPing: Can only call on a bot!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
client->ping = static_cast<int16_t>(ping);
|
|
||||||
});
|
|
||||||
|
|
||||||
Script::AddMethod("BotStop", [](Game::scr_entref_t entref) // Usage: <bot> BotStop();
|
Script::AddMethod("BotStop", [](Game::scr_entref_t entref) // Usage: <bot> BotStop();
|
||||||
{
|
{
|
||||||
const auto* ent = Game::GetPlayerEntity(entref);
|
const auto* ent = Game::GetPlayerEntity(entref);
|
||||||
@ -201,7 +184,7 @@ namespace Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto i = 0u; i < std::extent_v<decltype(BotActions)>; ++i)
|
for (std::size_t i = 0; i < std::extent_v<decltype(BotActions)>; ++i)
|
||||||
{
|
{
|
||||||
if (Utils::String::ToLower(&action[1]) != BotActions[i].action)
|
if (Utils::String::ToLower(&action[1]) != BotActions[i].action)
|
||||||
continue;
|
continue;
|
||||||
@ -331,9 +314,9 @@ namespace Components
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Zero the bot command array
|
// Zero the bot command array
|
||||||
for (auto i = 0u; i < std::extent_v<decltype(g_botai)>; i++)
|
for (std::size_t i = 0; i < std::extent_v<decltype(g_botai)>; ++i)
|
||||||
{
|
{
|
||||||
g_botai[i] = {0};
|
std::memset(&g_botai[i], 0, sizeof(BotMovementInfo));
|
||||||
g_botai[i].weapon = 1; // Prevent the bots from defaulting to the 'none' weapon
|
g_botai[i].weapon = 1; // Prevent the bots from defaulting to the 'none' weapon
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -383,7 +366,7 @@ namespace Components
|
|||||||
// 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([]
|
||||||
{
|
{
|
||||||
for (std::size_t i = 0; i < std::extent_v<decltype(g_botai)>; i++)
|
for (std::size_t i = 0; i < std::extent_v<decltype(g_botai)>; ++i)
|
||||||
{
|
{
|
||||||
g_botai[i].active = false;
|
g_botai[i].active = false;
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ namespace Components
|
|||||||
[[maybe_unused]] float y, float min, float max, [[maybe_unused]] int flags, const char* description)
|
[[maybe_unused]] float y, float min, float max, [[maybe_unused]] int flags, const char* description)
|
||||||
{
|
{
|
||||||
return Game::Dvar_RegisterVec2(dvarName, -60.0f,
|
return Game::Dvar_RegisterVec2(dvarName, -60.0f,
|
||||||
474.0f, min, max, Game::dvar_flag::DVAR_ROM, description);
|
474.0f, min, max, Game::DVAR_ROM, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Branding::RegisterBrandingDvars()
|
void Branding::RegisterBrandingDvars()
|
||||||
@ -86,11 +86,11 @@ namespace Components
|
|||||||
constexpr auto value = false;
|
constexpr auto value = false;
|
||||||
#endif
|
#endif
|
||||||
Branding::CGDrawVersion = Dvar::Register<bool>("cg_drawVersion", value,
|
Branding::CGDrawVersion = Dvar::Register<bool>("cg_drawVersion", value,
|
||||||
Game::dvar_flag::DVAR_NONE, "Draw the game version");
|
Game::DVAR_NONE, "Draw the game version");
|
||||||
Branding::CGDrawVersionX = Dvar::Register<float>("cg_drawVersionX", 10.0f,
|
Branding::CGDrawVersionX = Dvar::Register<float>("cg_drawVersionX", 10.0f,
|
||||||
0.0f, 512.0f, Game::dvar_flag::DVAR_NONE, "X offset for the version string");
|
0.0f, 512.0f, Game::DVAR_NONE, "X offset for the version string");
|
||||||
Branding::CGDrawVersionY = Dvar::Register<float>("cg_drawVersionY", 455.0f,
|
Branding::CGDrawVersionY = Dvar::Register<float>("cg_drawVersionY", 455.0f,
|
||||||
0.0f, 512.0f, Game::dvar_flag::DVAR_NONE, "Y offset for the version string");
|
0.0f, 512.0f, Game::DVAR_NONE, "Y offset for the version string");
|
||||||
}
|
}
|
||||||
|
|
||||||
Branding::Branding()
|
Branding::Branding()
|
||||||
|
@ -45,10 +45,10 @@ namespace Components
|
|||||||
Bullet::Bullet()
|
Bullet::Bullet()
|
||||||
{
|
{
|
||||||
BGSurfacePenetration = Dvar::Register<float>("bg_surfacePenetration", 0.0f,
|
BGSurfacePenetration = Dvar::Register<float>("bg_surfacePenetration", 0.0f,
|
||||||
0.0f, std::numeric_limits<float>::max(), Game::dvar_flag::DVAR_CODINFO,
|
0.0f, std::numeric_limits<float>::max(), Game::DVAR_CODINFO,
|
||||||
"Set to a value greater than 0 to override the surface penetration depth");
|
"Set to a value greater than 0 to override the surface penetration depth");
|
||||||
BGBulletRange = Game::Dvar_RegisterFloat("bg_bulletRange", 8192.0f,
|
BGBulletRange = Game::Dvar_RegisterFloat("bg_bulletRange", 8192.0f,
|
||||||
0.0f, std::numeric_limits<float>::max(), Game::dvar_flag::DVAR_CODINFO,
|
0.0f, std::numeric_limits<float>::max(), Game::DVAR_CODINFO,
|
||||||
"Max range used when calculating the bullet end position");
|
"Max range used when calculating the bullet end position");
|
||||||
|
|
||||||
Utils::Hook(0x4F6980, BG_GetSurfacePenetrationDepthStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x4F6980, BG_GetSurfacePenetrationDepthStub, HOOK_JUMP).install()->quick();
|
||||||
|
@ -3,14 +3,14 @@
|
|||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
std::string CardTitles::CustomTitles[18];
|
std::string CardTitles::CustomTitles[18];
|
||||||
Dvar::Var CardTitles::CustomTitleDvar;
|
Dvar::Var CardTitles::CustomTitle;
|
||||||
|
|
||||||
CClient* CardTitles::GetClientByIndex(std::uint32_t index)
|
CClient* CardTitles::GetClientByIndex(std::uint32_t index)
|
||||||
{
|
{
|
||||||
return &reinterpret_cast<CClient*>(0x8E77B0)[index];
|
return &reinterpret_cast<CClient*>(0x8E77B0)[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::int32_t CardTitles::GetPlayerCardClientInfo(std::int32_t lookupResult, playercarddata_s* data)
|
std::int32_t CardTitles::GetPlayerCardClientInfo(std::int32_t lookupResult, Game::PlayerCardData* data)
|
||||||
{
|
{
|
||||||
std::int32_t returnResult = lookupResult;
|
std::int32_t returnResult = lookupResult;
|
||||||
|
|
||||||
@ -22,12 +22,12 @@ namespace Components
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (auto clientNum = 0; clientNum < 18; clientNum++)
|
for (std::size_t clientNum = 0; clientNum < Game::MAX_CLIENTS; ++clientNum)
|
||||||
{
|
{
|
||||||
CClient* c = GetClientByIndex(clientNum);
|
CClient* c = GetClientByIndex(clientNum);
|
||||||
if (c != nullptr)
|
if (c != nullptr)
|
||||||
{
|
{
|
||||||
if (!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;
|
returnResult += 0xFF000000;
|
||||||
@ -62,7 +62,6 @@ namespace Components
|
|||||||
mov [ebx + 4], eax
|
mov [ebx + 4], eax
|
||||||
pop ebx
|
pop ebx
|
||||||
|
|
||||||
push 62EB2Ch
|
|
||||||
retn
|
retn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,10 +82,10 @@ namespace Components
|
|||||||
{
|
{
|
||||||
if (prefix == 0xFE)
|
if (prefix == 0xFE)
|
||||||
{
|
{
|
||||||
if (!CardTitles::CustomTitleDvar.get<std::string>().empty())
|
if (!CardTitles::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::CustomTitleDvar.get<const char*>());
|
const char* title = Utils::String::VA("\x15%s", CardTitles::CustomTitle.get<const char*>());
|
||||||
|
|
||||||
// prepare return value
|
// prepare return value
|
||||||
operand->internals.stringVal.string = title;
|
operand->internals.stringVal.string = title;
|
||||||
@ -157,20 +156,20 @@ namespace Components
|
|||||||
{
|
{
|
||||||
std::string list;
|
std::string list;
|
||||||
|
|
||||||
for (int i = 0; i < 18; i++)
|
for (std::size_t i = 0; i < Game::MAX_CLIENTS; i++)
|
||||||
{
|
{
|
||||||
char playerTitle[18];
|
char playerTitle[18];
|
||||||
|
|
||||||
if (Game::svs_clients[i].state >= 3)
|
if (Game::svs_clients[i].state >= Game::CS_CONNECTED)
|
||||||
{
|
{
|
||||||
strncpy_s(playerTitle, Game::Info_ValueForKey(Game::svs_clients[i].connectInfoString, "customTitle"), 17);
|
strncpy_s(playerTitle, Game::Info_ValueForKey(Game::svs_clients[i].userinfo, "customTitle"), _TRUNCATE);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
memset(playerTitle, 0, 18);
|
playerTitle[0] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
list.append(Utils::String::VA("\\%s\\%s", std::to_string(i).c_str(), playerTitle));
|
list.append(Utils::String::VA("\\%s\\%s", std::to_string(i).data(), playerTitle));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto* command = Utils::String::VA("%c customTitles \"%s\"", 21, list.data());
|
const auto* command = Utils::String::VA("%c customTitles \"%s\"", 21, list.data());
|
||||||
@ -179,7 +178,7 @@ namespace Components
|
|||||||
|
|
||||||
void CardTitles::ParseCustomTitles(const char* msg)
|
void CardTitles::ParseCustomTitles(const char* msg)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 18; ++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 char* playerTitle = Game::Info_ValueForKey(msg, std::to_string(i).c_str());
|
||||||
|
|
||||||
@ -192,7 +191,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Scheduler::Once([]
|
Scheduler::Once([]
|
||||||
{
|
{
|
||||||
CardTitles::CustomTitleDvar = Dvar::Register<const char*>("customtitle", "", Game::dvar_flag::DVAR_USERINFO | Game::dvar_flag::DVAR_ARCHIVE, "Custom card title");
|
CardTitles::CustomTitle = Dvar::Register<const char*>("customTitle", "", Game::DVAR_USERINFO | Game::DVAR_ARCHIVE, "Custom card title");
|
||||||
}, Scheduler::Pipeline::MAIN);
|
}, Scheduler::Pipeline::MAIN);
|
||||||
|
|
||||||
ServerCommands::OnCommand(21, [](Command::Params* params)
|
ServerCommands::OnCommand(21, [](Command::Params* params)
|
||||||
@ -215,8 +214,5 @@ namespace Components
|
|||||||
// Table lookup stuff
|
// Table lookup stuff
|
||||||
Utils::Hook(0x62DCC1, CardTitles::TableLookupByRowHookStub).install()->quick();
|
Utils::Hook(0x62DCC1, CardTitles::TableLookupByRowHookStub).install()->quick();
|
||||||
Utils::Hook::Nop(0x62DCC6, 1);
|
Utils::Hook::Nop(0x62DCC6, 1);
|
||||||
|
|
||||||
// This is placed here in case the anticheat has been disabled!
|
|
||||||
// This checks specifically for launching the process suspended to inject a dll
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,18 +12,6 @@ namespace Components
|
|||||||
std::int32_t tableColumn;
|
std::int32_t tableColumn;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct playercarddata_s
|
|
||||||
{
|
|
||||||
std::uint32_t padding;
|
|
||||||
std::uint32_t playercardNumber;
|
|
||||||
std::uint32_t unknown;
|
|
||||||
std::uint32_t unknown2;
|
|
||||||
std::uint32_t level; //Level is counted from 0 -> Value 69 is Level 70
|
|
||||||
std::uint32_t prestige;
|
|
||||||
std::uint32_t padding2;
|
|
||||||
char name[40];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CClient
|
struct CClient
|
||||||
{
|
{
|
||||||
std::uint32_t IsValid; // 0x0000
|
std::uint32_t IsValid; // 0x0000
|
||||||
@ -54,7 +42,9 @@ namespace Components
|
|||||||
class CardTitles : public Component
|
class CardTitles : public Component
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static Dvar::Var CustomTitleDvar;
|
AssertOffset(Game::PlayerCardData, Game::PlayerCardData::name, 0x1C);
|
||||||
|
|
||||||
|
static Dvar::Var CustomTitle;
|
||||||
static std::string CustomTitles[18];
|
static std::string CustomTitles[18];
|
||||||
|
|
||||||
static void SendCustomTitlesToClients();
|
static void SendCustomTitlesToClients();
|
||||||
@ -64,11 +54,9 @@ namespace Components
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static CClient* GetClientByIndex(std::uint32_t index);
|
static CClient* GetClientByIndex(std::uint32_t index);
|
||||||
static std::int32_t GetPlayerCardClientInfo(std::int32_t lookupResult, playercarddata_s * data);
|
static std::int32_t GetPlayerCardClientInfo(std::int32_t 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();
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Ceg::Ceg()
|
Ceg::Ceg()
|
||||||
{
|
{
|
||||||
Utils::Hook::Signature signature(0x401000, 0x740000);
|
Utils::Hook::Signature signature(0x401000, 0x340000);
|
||||||
|
|
||||||
// Generic killer caller.
|
// Generic killer caller.
|
||||||
signature.add({
|
signature.add({
|
||||||
@ -36,10 +36,6 @@ namespace Components
|
|||||||
Utils::Hook::Nop(0x43EC96, 9);
|
Utils::Hook::Nop(0x43EC96, 9);
|
||||||
Utils::Hook::Nop(0x4675C6, 9);
|
Utils::Hook::Nop(0x4675C6, 9);
|
||||||
|
|
||||||
// Something useless that can be skipped
|
|
||||||
Utils::Hook::Nop(0x4BB671, 2);
|
|
||||||
Utils::Hook::Nop(0x40A54D, 2);
|
|
||||||
|
|
||||||
// Random checks scattered throughout the binary
|
// Random checks scattered throughout the binary
|
||||||
Utils::Hook::Set<BYTE>(0x499F90, 0xC3);
|
Utils::Hook::Set<BYTE>(0x499F90, 0xC3);
|
||||||
Utils::Hook::Set<BYTE>(0x4FC700, 0xC3);
|
Utils::Hook::Set<BYTE>(0x4FC700, 0xC3);
|
||||||
@ -47,5 +43,13 @@ namespace Components
|
|||||||
Utils::Hook::Set<BYTE>(0x49E8C0, 0xC3);
|
Utils::Hook::Set<BYTE>(0x49E8C0, 0xC3);
|
||||||
Utils::Hook::Set<BYTE>(0x42DB00, 0xC3);
|
Utils::Hook::Set<BYTE>(0x42DB00, 0xC3);
|
||||||
Utils::Hook::Set<BYTE>(0x4F4CF0, 0xC3);
|
Utils::Hook::Set<BYTE>(0x4F4CF0, 0xC3);
|
||||||
|
Utils::Hook::Set<BYTE>(0x432180, 0xC3);
|
||||||
|
Utils::Hook::Set<BYTE>(0x461930, 0xC3);
|
||||||
|
|
||||||
|
// Looking for stuff in the registry
|
||||||
|
Utils::Hook::Nop(0x4826F8, 5);
|
||||||
|
|
||||||
|
// Live_Init
|
||||||
|
Utils::Hook::Nop(0x420937, 5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "GSC/Script.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -369,6 +370,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
const auto entityId = Game::Scr_GetEntityId(self->s.number, 0);
|
const auto entityId = Game::Scr_GetEntityId(self->s.number, 0);
|
||||||
|
|
||||||
|
Scripting::StackIsolation _;
|
||||||
Game::Scr_AddInt(mode);
|
Game::Scr_AddInt(mode);
|
||||||
Game::Scr_AddString(message);
|
Game::Scr_AddString(message);
|
||||||
|
|
||||||
@ -409,8 +411,8 @@ namespace Components
|
|||||||
Chat::Chat()
|
Chat::Chat()
|
||||||
{
|
{
|
||||||
cg_chatWidth = Dvar::Register<int>("cg_chatWidth", 52, 1, std::numeric_limits<int>::max(), Game::DVAR_ARCHIVE, "The normalized maximum width of a chat message");
|
cg_chatWidth = Dvar::Register<int>("cg_chatWidth", 52, 1, std::numeric_limits<int>::max(), Game::DVAR_ARCHIVE, "The normalized maximum width of a chat message");
|
||||||
sv_disableChat = Dvar::Register<bool>("sv_disableChat", false, Game::dvar_flag::DVAR_NONE, "Disable chat messages from clients");
|
sv_disableChat = Dvar::Register<bool>("sv_disableChat", false, Game::DVAR_NONE, "Disable chat messages from clients");
|
||||||
Scheduler::OnGameInitialized(AddChatCommands, Scheduler::Pipeline::SERVER);
|
Events::OnSVInit(AddChatCommands);
|
||||||
|
|
||||||
// Intercept chat sending
|
// Intercept chat sending
|
||||||
Utils::Hook(0x4D000B, PreSayStub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x4D000B, PreSayStub, HOOK_CALL).install()->quick();
|
||||||
|
289
src/Components/Modules/ClanTags.cpp
Normal file
289
src/Components/Modules/ClanTags.cpp
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
|
namespace Components
|
||||||
|
{
|
||||||
|
Game::dvar_t* ClanTags::ClanName;
|
||||||
|
|
||||||
|
// bgs_t and clientState_s do not have this
|
||||||
|
char ClanTags::ClientState[Game::MAX_CLIENTS][5];
|
||||||
|
|
||||||
|
const char* ClanTags::GetClanTagWithName(int clientNum, const char* playerName)
|
||||||
|
{
|
||||||
|
assert(static_cast<std::size_t>(clientNum) < Game::MAX_CLIENTS);
|
||||||
|
|
||||||
|
if (ClientState[clientNum][0] == '\0')
|
||||||
|
{
|
||||||
|
return playerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Utils::String::VA("[%s]%s", ClientState[clientNum], playerName);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClanTags::SendClanTagsToClients()
|
||||||
|
{
|
||||||
|
std::string list;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < Game::MAX_CLIENTS; ++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, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClanTags::ParseClanTags(const char* infoString)
|
||||||
|
{
|
||||||
|
for (std::size_t i = 0; i < Game::MAX_CLIENTS; ++i)
|
||||||
|
{
|
||||||
|
const auto* clanTag = Game::Info_ValueForKey(infoString, std::to_string(i).data());
|
||||||
|
|
||||||
|
if (clanTag[0] == '\0')
|
||||||
|
{
|
||||||
|
ClientState[i][0] = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Game::I_strncpyz(ClientState[i], clanTag, sizeof(ClientState[0]) / sizeof(char));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ClanTags::CL_FilterChar(unsigned char input)
|
||||||
|
{
|
||||||
|
if (input == '^')
|
||||||
|
{
|
||||||
|
return ' ';
|
||||||
|
}
|
||||||
|
if (input < ' ')
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input == 188 || input == 189)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClanTags::CL_SanitizeClanName()
|
||||||
|
{
|
||||||
|
char saneNameBuf[5];
|
||||||
|
std::memset(saneNameBuf, 0, sizeof(saneNameBuf));
|
||||||
|
|
||||||
|
auto* saneName = saneNameBuf;
|
||||||
|
|
||||||
|
assert(ClanName);
|
||||||
|
const auto* currentName = ClanName->current.string;
|
||||||
|
if (currentName)
|
||||||
|
{
|
||||||
|
auto nameLen = std::strlen(currentName);
|
||||||
|
for (std::size_t i = 0; (i < nameLen) && (i < sizeof(saneNameBuf)); ++i)
|
||||||
|
{
|
||||||
|
auto curChar = CL_FilterChar(static_cast<unsigned char>(currentName[i]));
|
||||||
|
if (curChar > 0)
|
||||||
|
{
|
||||||
|
*saneName++ = (curChar & 0xFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saneNameBuf[sizeof(saneNameBuf) - 1] = '\0';
|
||||||
|
Game::Dvar_SetString(ClanName, saneNameBuf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char* ClanTags::GamerProfile_GetClanName(int controllerIndex)
|
||||||
|
{
|
||||||
|
assert(static_cast<std::size_t>(controllerIndex) < Game::MAX_LOCAL_CLIENTS);
|
||||||
|
assert(ClanName);
|
||||||
|
|
||||||
|
CL_SanitizeClanName();
|
||||||
|
Game::I_strncpyz(Game::gamerSettings[controllerIndex].exeConfig.clanPrefix, ClanName->current.string, sizeof(Game::GamerSettingExeConfig::clanPrefix));
|
||||||
|
|
||||||
|
return Game::gamerSettings[controllerIndex].exeConfig.clanPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClanTags::Dvar_InfoString_Stub(char* s, const char* key, const char* value)
|
||||||
|
{
|
||||||
|
Utils::Hook::Call<void(char*, const char*, const char*)>(0x4AE560)(s, key, value); // Info_SetValueForKey
|
||||||
|
|
||||||
|
// Set 'clanAbbrev' in the info string
|
||||||
|
Utils::Hook::Call<void(char*, const char*, const char*)>(0x4AE560)(s, "clanAbbrev", GamerProfile_GetClanName(0)); // Info_SetValueForKey
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClanTags::ClientUserinfoChanged(const char* s, int clientNum)
|
||||||
|
{
|
||||||
|
assert(static_cast<std::size_t>(clientNum) < Game::MAX_CLIENTS);
|
||||||
|
|
||||||
|
auto* clanAbbrev = Game::Info_ValueForKey(s, "clanAbbrev");
|
||||||
|
|
||||||
|
if (clanAbbrev[0] == '\0')
|
||||||
|
{
|
||||||
|
ClientState[clientNum][0] = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Game::I_strncpyz(ClientState[clientNum], clanAbbrev, sizeof(ClientState[0]) / sizeof(char));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __declspec(naked) ClanTags::ClientUserinfoChanged_Stub()
|
||||||
|
{
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
pushad
|
||||||
|
|
||||||
|
push [esp + 0x20 + 0x824] // clientNum
|
||||||
|
push ecx // s
|
||||||
|
call ClientUserinfoChanged
|
||||||
|
add esp, 0x8
|
||||||
|
|
||||||
|
popad
|
||||||
|
|
||||||
|
push 0x445334 // Return address
|
||||||
|
push 0x47C820 // Info_ValueForKey
|
||||||
|
// Jump to Info_ValueForKey & add return address
|
||||||
|
retn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __declspec(naked) ClanTags::DrawPlayerNameOnScoreboard()
|
||||||
|
{
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
push eax
|
||||||
|
pushad
|
||||||
|
|
||||||
|
push edi
|
||||||
|
push [ebp]
|
||||||
|
|
||||||
|
call GetClanTagWithName
|
||||||
|
add esp, 0x8
|
||||||
|
|
||||||
|
mov [esp + 0x20], eax
|
||||||
|
|
||||||
|
popad
|
||||||
|
pop edi
|
||||||
|
|
||||||
|
push 0x591247 // Return address
|
||||||
|
push 0x5909E0 // DrawListString
|
||||||
|
retn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// s1 is always an empty string
|
||||||
|
int ClanTags::PartyClient_Frame_Stub(const char* s0, [[maybe_unused]] const char* s1)
|
||||||
|
{
|
||||||
|
return Utils::Hook::Call<int(const char*, const char*)>(0x4B0100)(s0, GamerProfile_GetClanName(0)); // I_strcmp
|
||||||
|
}
|
||||||
|
|
||||||
|
// clanAbbrev is always an empty string
|
||||||
|
void ClanTags::Party_UpdateClanName_Stub(Game::PartyData* party, [[maybe_unused]] const char* clanAbbrev)
|
||||||
|
{
|
||||||
|
Utils::Hook::Call<void(Game::PartyData*, const char*)>(0x4B3B10)(party, GamerProfile_GetClanName(0)); // Party_UpdateClanName
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClanTags::PlayerCards_SetCachedPlayerData(Game::PlayerCardData* data, const int clientNum)
|
||||||
|
{
|
||||||
|
Game::I_strncpyz(data->clanAbbrev, ClientState[clientNum], sizeof(Game::PlayerCardData::clanAbbrev));
|
||||||
|
}
|
||||||
|
|
||||||
|
void __declspec(naked) ClanTags::PlayerCards_SetCachedPlayerData_Stub()
|
||||||
|
{
|
||||||
|
static DWORD func = 0x4D6F80; // I_strncpyz
|
||||||
|
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
call func
|
||||||
|
add esp, 0xC
|
||||||
|
|
||||||
|
mov byte ptr [esi + 0x3C], 0x0
|
||||||
|
|
||||||
|
// Copy the clanName
|
||||||
|
push [esp + 0xC] // clientNum
|
||||||
|
push esi // g_PlayerCardCache
|
||||||
|
call PlayerCards_SetCachedPlayerData
|
||||||
|
add esp, 0x8
|
||||||
|
|
||||||
|
// Exit function
|
||||||
|
pop esi
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::PlayerCardData* ClanTags::PlayerCards_GetLiveProfileDataForClient_Stub(const unsigned int clientIndex)
|
||||||
|
{
|
||||||
|
auto* result = Utils::Hook::Call<Game::PlayerCardData*(unsigned int)>(0x46C0F0)(clientIndex);
|
||||||
|
Game::I_strncpyz(result->clanAbbrev, GamerProfile_GetClanName(static_cast<int>(clientIndex)), sizeof(Game::PlayerCardData::clanAbbrev));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::PlayerCardData* ClanTags::PlayerCards_GetLiveProfileDataForController_Stub(const unsigned int controllerIndex)
|
||||||
|
{
|
||||||
|
auto* result = Utils::Hook::Call<Game::PlayerCardData*(unsigned int)>(0x463B90)(controllerIndex);
|
||||||
|
// controllerIndex should always be 0
|
||||||
|
Game::I_strncpyz(result->clanAbbrev, GamerProfile_GetClanName(static_cast<int>(controllerIndex)), sizeof(Game::PlayerCardData::clanAbbrev));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClanTags::ClanTags()
|
||||||
|
{
|
||||||
|
Events::OnClientInit([]
|
||||||
|
{
|
||||||
|
ClanName = Game::Dvar_RegisterString("clanName", "", Game::DVAR_ARCHIVE,
|
||||||
|
"Your clan abbreviation");
|
||||||
|
});
|
||||||
|
|
||||||
|
std::memset(&ClientState, 0, sizeof(char[Game::MAX_CLIENTS][5]));
|
||||||
|
|
||||||
|
ServerCommands::OnCommand(22, [](Command::Params* params)
|
||||||
|
{
|
||||||
|
if (std::strcmp(params->get(1), "clanNames") == 0)
|
||||||
|
{
|
||||||
|
if (params->size() == 3)
|
||||||
|
{
|
||||||
|
ParseClanTags(params->get(2));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
Utils::Hook(0x430B00, Dvar_InfoString_Stub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
|
Utils::Hook(0x44532F, ClientUserinfoChanged_Stub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
|
// clanName before playerName
|
||||||
|
Utils::Hook(0x591242, DrawPlayerNameOnScoreboard, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
|
Utils::Hook(0x49765B, PartyClient_Frame_Stub, HOOK_CALL).install()->quick();
|
||||||
|
Utils::Hook(0x49767E, Party_UpdateClanName_Stub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
|
// clanName in the PlayerCard (GetPlayerCardClientData)
|
||||||
|
Utils::Hook(0x458DF4, PlayerCards_SetCachedPlayerData_Stub, HOOK_JUMP).install()->quick();
|
||||||
|
Utils::Hook(0x62EAB6, PlayerCards_GetLiveProfileDataForClient_Stub, HOOK_CALL).install()->quick();
|
||||||
|
Utils::Hook(0x62EAC3, PlayerCards_GetLiveProfileDataForController_Stub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
|
// clanName in CG_Obituary
|
||||||
|
Utils::Hook(0x586DD6, PlayerName::GetClientName, HOOK_CALL).install()->quick();
|
||||||
|
Utils::Hook(0x586E2A, PlayerName::GetClientName, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
|
Command::Add("statGet", [](Command::Params* params)
|
||||||
|
{
|
||||||
|
if (params->size() < 2)
|
||||||
|
{
|
||||||
|
Logger::PrintError(Game::CON_CHANNEL_SERVER, "statget usage: statget <index>\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto index = std::atoi(params->get(1));
|
||||||
|
const auto stat = Game::LiveStorage_GetStat(0, index);
|
||||||
|
Logger::Print(Game::CON_CHANNEL_SYSTEM, "Stat {}: {}\n", index, stat);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
47
src/Components/Modules/ClanTags.hpp
Normal file
47
src/Components/Modules/ClanTags.hpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Components
|
||||||
|
{
|
||||||
|
class ClanTags : public Component
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ClanTags();
|
||||||
|
|
||||||
|
static const char* GetClanTagWithName(int clientNum, const char* playerName);
|
||||||
|
|
||||||
|
static void SendClanTagsToClients();
|
||||||
|
|
||||||
|
static void CL_SanitizeClanName();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static Game::dvar_t* ClanName;
|
||||||
|
|
||||||
|
static const char* dvarNameList[];
|
||||||
|
|
||||||
|
static char ClientState[Game::MAX_CLIENTS][5];
|
||||||
|
|
||||||
|
static void ParseClanTags(const char* infoString);
|
||||||
|
|
||||||
|
static int CL_FilterChar(unsigned char input);
|
||||||
|
|
||||||
|
static char* GamerProfile_GetClanName(int controllerIndex);
|
||||||
|
|
||||||
|
static void Dvar_InfoString_Stub(char* s, const char* key, const char* value);
|
||||||
|
|
||||||
|
static void SetCachedPlayerData(int clientNum);
|
||||||
|
|
||||||
|
static void ClientUserinfoChanged(const char* s, int clientNum);
|
||||||
|
static void ClientUserinfoChanged_Stub();
|
||||||
|
|
||||||
|
static void DrawPlayerNameOnScoreboard();
|
||||||
|
|
||||||
|
static int PartyClient_Frame_Stub(const char* s0, const char* s1);
|
||||||
|
static void Party_UpdateClanName_Stub(Game::PartyData* party, const char* clanAbbrev);
|
||||||
|
|
||||||
|
static void PlayerCards_SetCachedPlayerData(Game::PlayerCardData* data, int clientNum);
|
||||||
|
static void PlayerCards_SetCachedPlayerData_Stub();
|
||||||
|
|
||||||
|
static Game::PlayerCardData* PlayerCards_GetLiveProfileDataForClient_Stub(unsigned int clientIndex);
|
||||||
|
static Game::PlayerCardData* PlayerCards_GetLiveProfileDataForController_Stub(unsigned int controllerIndex);
|
||||||
|
};
|
||||||
|
}
|
@ -1,100 +0,0 @@
|
|||||||
#include <STDInclude.hpp>
|
|
||||||
|
|
||||||
namespace Components
|
|
||||||
{
|
|
||||||
std::string ClanTags::Tags[18];
|
|
||||||
|
|
||||||
void ClanTags::ParseClantags(const char* infoString)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 18; i++)
|
|
||||||
{
|
|
||||||
const char* clantag = Game::Info_ValueForKey(infoString, std::to_string(i).data());
|
|
||||||
|
|
||||||
if (clantag) ClanTags::Tags[i] = clantag;
|
|
||||||
else ClanTags::Tags[i].clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClanTags::SendClantagsToClients()
|
|
||||||
{
|
|
||||||
std::string list;
|
|
||||||
|
|
||||||
for (int i = 0; i < 18; ++i)
|
|
||||||
{
|
|
||||||
char clantag[5] = { 0 };
|
|
||||||
|
|
||||||
if (Game::svs_clients[i].state >= 3)
|
|
||||||
{
|
|
||||||
strncpy_s(clantag, Game::Info_ValueForKey(Game::svs_clients[i].connectInfoString, "clantag"), 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
list.append(Utils::String::VA("\\%s\\%s", std::to_string(i).data(), clantag));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string command = Utils::String::VA("%c clantags \"%s\"", 22, list.data());
|
|
||||||
Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, command.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* ClanTags::GetUserClantag(std::uint32_t /*clientnum*/, const char* playername)
|
|
||||||
{
|
|
||||||
#if 0
|
|
||||||
if (ClanTags::Tags[clientnum].empty()) return playername;
|
|
||||||
return Utils::String::VA("[%s] %s", ClanTags::Tags[clientnum].data(), playername);
|
|
||||||
#else
|
|
||||||
return playername;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
__declspec(naked) void ClanTags::DrawPlayerNameOnScoreboard()
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
push eax
|
|
||||||
pushad
|
|
||||||
|
|
||||||
push edi
|
|
||||||
push [ebp]
|
|
||||||
|
|
||||||
call ClanTags::GetUserClantag
|
|
||||||
add esp, 8
|
|
||||||
|
|
||||||
mov [esp + 20h], eax
|
|
||||||
|
|
||||||
popad
|
|
||||||
pop edi
|
|
||||||
|
|
||||||
push 591247h // Return address
|
|
||||||
push 5909E0h // Draw string func
|
|
||||||
retn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ClanTags::ClanTags()
|
|
||||||
{
|
|
||||||
// Create clantag dvar
|
|
||||||
Scheduler::Once([]
|
|
||||||
{
|
|
||||||
Dvar::Register<const char*>("clantag", "", Game::dvar_flag::DVAR_USERINFO | Game::dvar_flag::DVAR_ARCHIVE,
|
|
||||||
"If set, your clantag will be shown on the scoreboard.");
|
|
||||||
}, Scheduler::Pipeline::MAIN);
|
|
||||||
|
|
||||||
// Servercommand hook
|
|
||||||
ServerCommands::OnCommand(22, [](Command::Params* params)
|
|
||||||
{
|
|
||||||
if (params->get(1) == "clantags"s && !Dedicated::IsEnabled())
|
|
||||||
{
|
|
||||||
if (params->size() == 3)
|
|
||||||
{
|
|
||||||
ClanTags::ParseClantags(params->get(2));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Draw clantag before playername
|
|
||||||
Utils::Hook(0x591242, ClanTags::DrawPlayerNameOnScoreboard).install()->quick();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace Components
|
|
||||||
{
|
|
||||||
class ClanTags : public Component
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static void ParseClantags(const char * infoString);
|
|
||||||
static void SendClantagsToClients();
|
|
||||||
static const char* GetUserClantag(std::uint32_t clientnum, const char * playername);
|
|
||||||
|
|
||||||
ClanTags();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static std::string Tags[18];
|
|
||||||
|
|
||||||
static void DrawPlayerNameOnScoreboard();
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,4 +1,5 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "GSC/Script.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -25,11 +26,11 @@ namespace Components
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientCommand::Add(const char* name, std::function<void(Game::gentity_s*, Command::ServerParams*)> callback)
|
void ClientCommand::Add(const char* name, const std::function<void(Game::gentity_s*, Command::ServerParams*)>& callback)
|
||||||
{
|
{
|
||||||
const auto command = Utils::String::ToLower(name);
|
const auto command = Utils::String::ToLower(name);
|
||||||
|
|
||||||
ClientCommand::HandlersSV[command] = std::move(callback);
|
ClientCommand::HandlersSV[command] = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientCommand::ClientCommandStub(const int clientNum)
|
void ClientCommand::ClientCommandStub(const int clientNum)
|
||||||
@ -51,7 +52,7 @@ namespace Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::Hook::Call<void(const int)>(0x416790)(clientNum);
|
Utils::Hook::Call<void(int)>(0x416790)(clientNum);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientCommand::AddCheatCommands()
|
void ClientCommand::AddCheatCommands()
|
||||||
@ -333,128 +334,23 @@ namespace Components
|
|||||||
ClientCommand::Add("kill", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
ClientCommand::Add("kill", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params)
|
||||||
{
|
{
|
||||||
assert(ent->client != nullptr);
|
assert(ent->client != nullptr);
|
||||||
assert(ent->client->connected != Game::clientConnected_t::CON_DISCONNECTED);
|
assert(ent->client->sess.connected != Game::CON_DISCONNECTED);
|
||||||
|
|
||||||
if (ent->client->sessionState != Game::sessionState_t::SESS_STATE_PLAYING || !ClientCommand::CheatsOk(ent))
|
if (ent->client->sess.sessionState != Game::SESS_STATE_PLAYING || !ClientCommand::CheatsOk(ent))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Scheduler::Once([ent]
|
Scheduler::Once([ent]
|
||||||
{
|
{
|
||||||
ent->flags &= ~(Game::entityFlag::FL_GODMODE | Game::entityFlag::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, 12, 0, nullptr, Game::hitLocation_t::HITLOC_NONE, 0);
|
Game::player_die(ent, ent, ent, 100000, 12, 0, nullptr, Game::HITLOC_NONE, 0);
|
||||||
}, Scheduler::Pipeline::SERVER);
|
}, Scheduler::Pipeline::SERVER);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientCommand::AddScriptFunctions()
|
void ClientCommand::AddScriptFunctions()
|
||||||
{
|
{
|
||||||
Script::AddMethod("Noclip", [](Game::scr_entref_t entref) // gsc: Noclip(<optional int toggle>);
|
|
||||||
{
|
|
||||||
const auto* ent = Game::GetPlayerEntity(entref);
|
|
||||||
|
|
||||||
if (Game::Scr_GetNumParam() >= 1u)
|
|
||||||
{
|
|
||||||
if (Game::Scr_GetInt(0))
|
|
||||||
{
|
|
||||||
ent->client->flags |= Game::PLAYER_FLAG_NOCLIP;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ent->client->flags &= ~Game::PLAYER_FLAG_NOCLIP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ent->client->flags ^= Game::PLAYER_FLAG_NOCLIP;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Script::AddMethod("Ufo", [](Game::scr_entref_t entref) // gsc: Ufo(<optional int toggle>);
|
|
||||||
{
|
|
||||||
const auto* ent = Game::GetPlayerEntity(entref);
|
|
||||||
|
|
||||||
if (Game::Scr_GetNumParam() >= 1u)
|
|
||||||
{
|
|
||||||
if (Game::Scr_GetInt(0))
|
|
||||||
{
|
|
||||||
ent->client->flags |= Game::PLAYER_FLAG_UFO;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ent->client->flags &= ~Game::PLAYER_FLAG_UFO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ent->client->flags ^= Game::PLAYER_FLAG_UFO;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Script::AddMethod("God", [](Game::scr_entref_t entref) // gsc: God(<optional int toggle>);
|
|
||||||
{
|
|
||||||
auto* ent = Game::GetEntity(entref);
|
|
||||||
|
|
||||||
if (Game::Scr_GetNumParam() >= 1u)
|
|
||||||
{
|
|
||||||
if (Game::Scr_GetInt(0))
|
|
||||||
{
|
|
||||||
ent->flags |= Game::FL_GODMODE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ent->flags &= ~Game::FL_GODMODE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ent->flags ^= Game::FL_GODMODE;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Script::AddMethod("Demigod", [](Game::scr_entref_t entref) // gsc: Demigod(<optional int toggle>);
|
|
||||||
{
|
|
||||||
auto* ent = Game::GetEntity(entref);
|
|
||||||
|
|
||||||
if (Game::Scr_GetNumParam() >= 1u)
|
|
||||||
{
|
|
||||||
if (Game::Scr_GetInt(0))
|
|
||||||
{
|
|
||||||
ent->flags |= Game::FL_DEMI_GODMODE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ent->flags &= ~Game::FL_DEMI_GODMODE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ent->flags ^= Game::FL_DEMI_GODMODE;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Script::AddMethod("Notarget", [](Game::scr_entref_t entref) // gsc: Notarget(<optional int toggle>);
|
|
||||||
{
|
|
||||||
auto* ent = Game::GetEntity(entref);
|
|
||||||
|
|
||||||
if (Game::Scr_GetNumParam() >= 1u)
|
|
||||||
{
|
|
||||||
if (Game::Scr_GetInt(0))
|
|
||||||
{
|
|
||||||
ent->flags |= Game::FL_NOTARGET;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ent->flags &= ~Game::FL_NOTARGET;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ent->flags ^= Game::FL_NOTARGET;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Script::AddFunction("DropAllBots", [] // gsc: DropAllBots();
|
Script::AddFunction("DropAllBots", [] // gsc: DropAllBots();
|
||||||
{
|
{
|
||||||
Game::SV_DropAllBots();
|
Game::SV_DropAllBots();
|
||||||
|
@ -7,13 +7,13 @@ namespace Components
|
|||||||
public:
|
public:
|
||||||
ClientCommand();
|
ClientCommand();
|
||||||
|
|
||||||
static void Add(const char* name, std::function<void(Game::gentity_s*, Command::ServerParams*)> callback);
|
static void Add(const char* name, const std::function<void(Game::gentity_s*, Command::ServerParams*)>& callback);
|
||||||
static bool CheatsOk(const Game::gentity_s* ent);
|
static bool CheatsOk(const Game::gentity_s* ent);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::unordered_map<std::string, std::function<void(Game::gentity_s*, Command::ServerParams*)>> HandlersSV;
|
static std::unordered_map<std::string, std::function<void(Game::gentity_s*, Command::ServerParams*)>> HandlersSV;
|
||||||
|
|
||||||
static void ClientCommandStub(const int clientNum);
|
static void ClientCommandStub(int clientNum);
|
||||||
static void AddCheatCommands();
|
static void AddCheatCommands();
|
||||||
static void AddDevelopmentCommands();
|
static void AddDevelopmentCommands();
|
||||||
static void AddScriptFunctions();
|
static void AddScriptFunctions();
|
||||||
|
@ -98,9 +98,6 @@ namespace Components
|
|||||||
if (!Command::FunctionMapSV.contains(command))
|
if (!Command::FunctionMapSV.contains(command))
|
||||||
{
|
{
|
||||||
Command::AddRawSV(name, Command::MainCallbackSV);
|
Command::AddRawSV(name, Command::MainCallbackSV);
|
||||||
|
|
||||||
// If the main command is registered as Cbuf_AddServerText, the command will be redirected to the SV handler
|
|
||||||
Command::AddRaw(name, Game::Cbuf_AddServerText);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionMapSV.insert_or_assign(command, callback);
|
FunctionMapSV.insert_or_assign(command, callback);
|
||||||
@ -116,7 +113,7 @@ namespace Components
|
|||||||
Game::Cmd_AddServerCommand(name, callback, Command::Allocate());
|
Game::Cmd_AddServerCommand(name, callback, Command::Allocate());
|
||||||
|
|
||||||
// If the main command is registered as Cbuf_AddServerText, the command will be redirected to the SV handler
|
// If the main command is registered as Cbuf_AddServerText, the command will be redirected to the SV handler
|
||||||
Command::AddRaw(name, Game::Cbuf_AddServerText);
|
Command::AddRaw(name, Game::Cbuf_AddServerText, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Command::Execute(std::string command, bool sync)
|
void Command::Execute(std::string command, bool sync)
|
||||||
|
@ -68,7 +68,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
maxclientCount = Dvar::Var("party_maxplayers").get<int>();
|
maxclientCount = Dvar::Var("party_maxplayers").get<int>();
|
||||||
//maxclientCount = Game::Party_GetMaxPlayers(*Game::partyIngame);
|
//maxclientCount = Game::Party_GetMaxPlayers(*Game::partyIngame);
|
||||||
clientCount = Game::PartyHost_CountMembers(reinterpret_cast<Game::PartyData_s*>(0x1081C00));
|
clientCount = Game::PartyHost_CountMembers(reinterpret_cast<Game::PartyData*>(0x1081C00));
|
||||||
}
|
}
|
||||||
|
|
||||||
wclear(Console::InfoWindow);
|
wclear(Console::InfoWindow);
|
||||||
@ -324,10 +324,10 @@ namespace Components
|
|||||||
|
|
||||||
va_list va;
|
va_list va;
|
||||||
va_start(va, fmt);
|
va_start(va, fmt);
|
||||||
_vsnprintf_s(buf, _TRUNCATE, fmt, va);
|
vsnprintf_s(buf, _TRUNCATE, fmt, va);
|
||||||
va_end(va);
|
va_end(va);
|
||||||
|
|
||||||
Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}", buf);
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}\n", buf);
|
||||||
|
|
||||||
Console::RefreshOutput();
|
Console::RefreshOutput();
|
||||||
|
|
||||||
@ -534,10 +534,42 @@ namespace Components
|
|||||||
return reinterpret_cast<Game::Dvar_RegisterVec4_t>(0x471500)(dvarName, r, g, b, a, min, max, flags, description);
|
return reinterpret_cast<Game::Dvar_RegisterVec4_t>(0x471500)(dvarName, r, g, b, a, min, max, flags, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Console::Con_ToggleConsole()
|
||||||
|
{
|
||||||
|
Game::Field_Clear(Game::g_consoleField);
|
||||||
|
if (Game::conDrawInputGlob->matchIndex >= 0 && Game::conDrawInputGlob->autoCompleteChoice[0] != '\0')
|
||||||
|
{
|
||||||
|
Game::conDrawInputGlob->matchIndex = -1;
|
||||||
|
Game::conDrawInputGlob->autoCompleteChoice[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::g_consoleField->fixedSize = 1;
|
||||||
|
Game::con->outputVisible = false;
|
||||||
|
Game::g_consoleField->widthInPixels = *Game::g_console_field_width;
|
||||||
|
Game::g_consoleField->charHeight = *Game::g_console_char_height;
|
||||||
|
|
||||||
|
for (std::size_t localClientNum = 0; localClientNum < Game::MAX_LOCAL_CLIENTS; ++localClientNum)
|
||||||
|
{
|
||||||
|
assert((Game::clientUIActives[0].keyCatchers & Game::KEYCATCH_CONSOLE) == (Game::clientUIActives[localClientNum].keyCatchers & Game::KEYCATCH_CONSOLE));
|
||||||
|
Game::clientUIActives[localClientNum].keyCatchers ^= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Console::AddConsoleCommand()
|
||||||
|
{
|
||||||
|
Command::Add("con_echo", []
|
||||||
|
{
|
||||||
|
Console::Con_ToggleConsole();
|
||||||
|
Game::I_strncpyz(Game::g_consoleField->buffer, "\\echo ", sizeof(Game::field_t::buffer));
|
||||||
|
Game::g_consoleField->cursor = static_cast<int>(std::strlen(Game::g_consoleField->buffer));
|
||||||
|
Game::Field_AdjustScroll(Game::ScrPlace_GetFullPlacement(), Game::g_consoleField);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Console::Console()
|
Console::Console()
|
||||||
{
|
{
|
||||||
// Console '%s: %s> ' string
|
// Console '%s: %s> ' string
|
||||||
Utils::Hook::Set<const char*>(0x5A44B4, "IW4x: " VERSION "> ");
|
Utils::Hook::Set<const char*>(0x5A44B4, "IW4x MP: " VERSION "> ");
|
||||||
|
|
||||||
// Patch console color
|
// Patch console color
|
||||||
static float consoleColor[] = { 0.70f, 1.00f, 0.00f, 1.00f };
|
static float consoleColor[] = { 0.70f, 1.00f, 0.00f, 1.00f };
|
||||||
@ -577,6 +609,10 @@ namespace Components
|
|||||||
// Don't resize the console
|
// Don't resize the console
|
||||||
Utils::Hook(0x64DC6B, 0x64DCC2, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x64DC6B, 0x64DCC2, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
Console::AddConsoleCommand();
|
||||||
|
#endif
|
||||||
|
|
||||||
if (Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled())
|
if (Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled())
|
||||||
{
|
{
|
||||||
Scheduler::Loop(Console::RefreshStatus, Scheduler::Pipeline::MAIN);
|
Scheduler::Loop(Console::RefreshStatus, Scheduler::Pipeline::MAIN);
|
||||||
|
@ -66,6 +66,9 @@ namespace Components
|
|||||||
static void ToggleConsole();
|
static void ToggleConsole();
|
||||||
static char** GetAutoCompleteFileList(const char *path, const char *extension, Game::FsListBehavior_e behavior, int *numfiles, int allocTrackType);
|
static char** GetAutoCompleteFileList(const char *path, const char *extension, Game::FsListBehavior_e behavior, int *numfiles, int allocTrackType);
|
||||||
|
|
||||||
|
static void Con_ToggleConsole();
|
||||||
|
static void AddConsoleCommand();
|
||||||
|
|
||||||
static Game::dvar_t* RegisterConColor(const char* dvarName, float r, float g, float b, float a, float min, float max, unsigned __int16 flags, const char* description);
|
static Game::dvar_t* RegisterConColor(const char* dvarName, float r, float g, float b, float a, float min, float max, unsigned __int16 flags, const char* description);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -748,7 +748,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
if (Dedicated::IsEnabled()) return;
|
if (Dedicated::IsEnabled()) return;
|
||||||
|
|
||||||
Dvar::Register<bool>("r_useD3D9Ex", false, Game::dvar_flag::DVAR_ARCHIVE, "Use extended d3d9 interface!");
|
Dvar::Register<bool>("r_useD3D9Ex", false, Game::DVAR_ARCHIVE, "Use extended d3d9 interface!");
|
||||||
|
|
||||||
// Hook Interface creation
|
// Hook Interface creation
|
||||||
Utils::Hook::Set(0x6D74D0, D3D9Ex::Direct3DCreate9Stub);
|
Utils::Hook::Set(0x6D74D0, D3D9Ex::Direct3DCreate9Stub);
|
||||||
|
@ -213,6 +213,11 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Debug::Com_Assert_f()
|
||||||
|
{
|
||||||
|
assert(("a", false));
|
||||||
|
}
|
||||||
|
|
||||||
void Debug::CL_InitDebugDvars()
|
void Debug::CL_InitDebugDvars()
|
||||||
{
|
{
|
||||||
static const char* debugOverlayNames_0[] =
|
static const char* debugOverlayNames_0[] =
|
||||||
@ -227,7 +232,7 @@ namespace Components
|
|||||||
};
|
};
|
||||||
|
|
||||||
DebugOverlay = Game::Dvar_RegisterEnum("debugOverlay", debugOverlayNames_0, 0,
|
DebugOverlay = Game::Dvar_RegisterEnum("debugOverlay", debugOverlayNames_0, 0,
|
||||||
Game::dvar_flag::DVAR_NONE, "Toggles the display of various debug info.");
|
Game::DVAR_NONE, "Toggles the display of various debug info.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug::Debug()
|
Debug::Debug()
|
||||||
@ -236,5 +241,7 @@ namespace Components
|
|||||||
|
|
||||||
// Hook end of CG_DrawDebugOverlays (This is to ensure some checks are done before our hook is executed).
|
// Hook end of CG_DrawDebugOverlays (This is to ensure some checks are done before our hook is executed).
|
||||||
Utils::Hook(0x49CB0A, CG_DrawDebugOverlays_Hk, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x49CB0A, CG_DrawDebugOverlays_Hk, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
|
Utils::Hook::Set<void(*)()>(0x60BCEA, Com_Assert_f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,8 @@ namespace Components
|
|||||||
|
|
||||||
static void CG_DrawDebugOverlays_Hk(int localClientNum);
|
static void CG_DrawDebugOverlays_Hk(int localClientNum);
|
||||||
|
|
||||||
|
static void Com_Assert_f();
|
||||||
|
|
||||||
static void CL_InitDebugDvars();
|
static void CL_InitDebugDvars();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
list.append(Utils::String::VA(" %llX", Game::svs_clients[i].steamID));
|
list.append(Utils::String::VA(" %llX", Game::svs_clients[i].steamID));
|
||||||
|
|
||||||
Utils::InfoString info(Game::svs_clients[i].connectInfoString);
|
Utils::InfoString info(Game::svs_clients[i].userinfo);
|
||||||
list.append(Utils::String::VA(" %llX", strtoull(info.get("realsteamId").data(), nullptr, 16)));
|
list.append(Utils::String::VA(" %llX", strtoull(info.get("realsteamId").data(), nullptr, 16)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -135,7 +135,7 @@ namespace Components
|
|||||||
|
|
||||||
Game::dvar_t* Dedicated::Dvar_RegisterSVNetworkFps(const char* dvarName, int, int min, int, int, const char* description)
|
Game::dvar_t* Dedicated::Dvar_RegisterSVNetworkFps(const char* dvarName, int, int min, int, int, const char* description)
|
||||||
{
|
{
|
||||||
return Game::Dvar_RegisterInt(dvarName, 1000, min, 1000, Game::dvar_flag::DVAR_NONE, description);
|
return Game::Dvar_RegisterInt(dvarName, 1000, min, 1000, Game::DVAR_NONE, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dedicated::AddDedicatedCommands()
|
void Dedicated::AddDedicatedCommands()
|
||||||
@ -206,7 +206,7 @@ namespace Components
|
|||||||
Dedicated::Dedicated()
|
Dedicated::Dedicated()
|
||||||
{
|
{
|
||||||
Dedicated::COMLogFilter = Dvar::Register<bool>("com_logFilter", true,
|
Dedicated::COMLogFilter = Dvar::Register<bool>("com_logFilter", true,
|
||||||
Game::dvar_flag::DVAR_LATCH, "Removes ~95% of unneeded lines from the log");
|
Game::DVAR_LATCH, "Removes ~95% of unneeded lines from the log");
|
||||||
|
|
||||||
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled())
|
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled())
|
||||||
{
|
{
|
||||||
@ -214,7 +214,7 @@ namespace Components
|
|||||||
Scheduler::Loop(Steam::SteamAPI_RunCallbacks, Scheduler::Pipeline::SERVER);
|
Scheduler::Loop(Steam::SteamAPI_RunCallbacks, Scheduler::Pipeline::SERVER);
|
||||||
|
|
||||||
Dedicated::SVLanOnly = Dvar::Register<bool>("sv_lanOnly", false,
|
Dedicated::SVLanOnly = Dvar::Register<bool>("sv_lanOnly", false,
|
||||||
Game::dvar_flag::DVAR_NONE, "Don't act as node");
|
Game::DVAR_NONE, "Don't act as node");
|
||||||
|
|
||||||
Utils::Hook(0x60BE98, Dedicated::InitDedicatedServer, HOOK_CALL).install()->quick();
|
Utils::Hook(0x60BE98, Dedicated::InitDedicatedServer, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
@ -281,11 +281,11 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Scheduler::Once([]
|
Scheduler::Once([]
|
||||||
{
|
{
|
||||||
Dvar::Register<const char*>("sv_sayName", "^7Console", Game::dvar_flag::DVAR_NONE, "The name to pose as for 'say' commands");
|
Dvar::Register<const char*>("sv_sayName", "^7Console", Game::DVAR_NONE, "The name to pose as for 'say' commands");
|
||||||
Dvar::Register<const char*>("sv_motd", "", Game::dvar_flag::DVAR_NONE, "A custom message of the day for servers");
|
Dvar::Register<const char*>("sv_motd", "", Game::DVAR_NONE, "A custom message of the day for servers");
|
||||||
}, Scheduler::Pipeline::MAIN);
|
}, Scheduler::Pipeline::MAIN);
|
||||||
|
|
||||||
Scheduler::OnGameInitialized(Dedicated::AddDedicatedCommands, Scheduler::Pipeline::SERVER);
|
Events::OnSVInit(Dedicated::AddDedicatedCommands);
|
||||||
|
|
||||||
// Post initialization point
|
// Post initialization point
|
||||||
Utils::Hook(0x60BFBF, Dedicated::PostInitializationStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x60BFBF, Dedicated::PostInitializationStub, HOOK_JUMP).install()->quick();
|
||||||
@ -294,7 +294,7 @@ namespace Components
|
|||||||
Scheduler::Loop([]
|
Scheduler::Loop([]
|
||||||
{
|
{
|
||||||
CardTitles::SendCustomTitlesToClients();
|
CardTitles::SendCustomTitlesToClients();
|
||||||
//Clantags::SendClantagsToClients();
|
ClanTags::SendClanTagsToClients();
|
||||||
}, Scheduler::Pipeline::SERVER, 10s);
|
}, Scheduler::Pipeline::SERVER, 10s);
|
||||||
|
|
||||||
// Heartbeats
|
// Heartbeats
|
||||||
|
@ -14,8 +14,8 @@ namespace Components
|
|||||||
|
|
||||||
Discovery::Discovery()
|
Discovery::Discovery()
|
||||||
{
|
{
|
||||||
Dvar::Register<int>("net_discoveryPortRangeMin", 25000, 0, 65535, Game::dvar_flag::DVAR_ARCHIVE, "Minimum scan range port for local server discovery");
|
Dvar::Register<int>("net_discoveryPortRangeMin", 25000, 0, 65535, Game::DVAR_ARCHIVE, "Minimum scan range port for local server discovery");
|
||||||
Dvar::Register<int>("net_discoveryPortRangeMax", 35000, 1, 65536, Game::dvar_flag::DVAR_ARCHIVE, "Maximum 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");
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "GSC/Script.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -759,7 +760,7 @@ namespace Components
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Score and ping are irrelevant
|
// Score and ping are irrelevant
|
||||||
const char* namePtr = Game::PartyHost_GetMemberName(reinterpret_cast<Game::PartyData_t*>(0x1081C00), i);
|
const char* namePtr = Game::PartyHost_GetMemberName(reinterpret_cast<Game::PartyData*>(0x1081C00), i);
|
||||||
if (!namePtr || !namePtr[0]) continue;
|
if (!namePtr || !namePtr[0]) continue;
|
||||||
|
|
||||||
playerInfo["name"] = namePtr;
|
playerInfo["name"] = namePtr;
|
||||||
@ -903,9 +904,9 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Scheduler::Once([]
|
Scheduler::Once([]
|
||||||
{
|
{
|
||||||
Dvar::Register<const char*>("ui_dl_timeLeft", "", Game::dvar_flag::DVAR_NONE, "");
|
Dvar::Register<const char*>("ui_dl_timeLeft", "", Game::DVAR_NONE, "");
|
||||||
Dvar::Register<const char*>("ui_dl_progress", "", Game::dvar_flag::DVAR_NONE, "");
|
Dvar::Register<const char*>("ui_dl_progress", "", Game::DVAR_NONE, "");
|
||||||
Dvar::Register<const char*>("ui_dl_transRate", "", Game::dvar_flag::DVAR_NONE, "");
|
Dvar::Register<const char*>("ui_dl_transRate", "", Game::DVAR_NONE, "");
|
||||||
}, Scheduler::Pipeline::MAIN);
|
}, Scheduler::Pipeline::MAIN);
|
||||||
|
|
||||||
UIScript::Add("mod_download_cancel", [](UIScript::Token)
|
UIScript::Add("mod_download_cancel", [](UIScript::Token)
|
||||||
@ -916,13 +917,13 @@ namespace Components
|
|||||||
|
|
||||||
Scheduler::Once([]
|
Scheduler::Once([]
|
||||||
{
|
{
|
||||||
Dvar::Register<bool>("sv_wwwDownload", false, Game::dvar_flag::DVAR_ARCHIVE, "Set to true to enable downloading maps/mods from an external server.");
|
Dvar::Register<bool>("sv_wwwDownload", false, Game::DVAR_ARCHIVE, "Set to true to enable downloading maps/mods from an external server.");
|
||||||
Dvar::Register<const char*>("sv_wwwBaseUrl", "", Game::dvar_flag::DVAR_ARCHIVE, "Set to the base url for the external map download.");
|
Dvar::Register<const char*>("sv_wwwBaseUrl", "", Game::DVAR_ARCHIVE, "Set to the base url for the external map download.");
|
||||||
|
|
||||||
// Force users to enable this because we don't want to accidentally turn everyone's pc into a http server into all their files again
|
// Force users to enable this because we don't want to accidentally turn everyone's pc into a http server into all their files again
|
||||||
// not saying we are but ya know... accidents happen
|
// not saying we are but ya know... accidents happen
|
||||||
// by having it saved we force the user to enable it in config_mp because it only checks the dvar on startup to see if we should init download or not
|
// by having it saved we force the user to enable it in config_mp because it only checks the dvar on startup to see if we should init download or not
|
||||||
Dvar::Register<bool>("mod_force_download_server", false, Game::dvar_flag::DVAR_ARCHIVE, "Set to true to force the client to run the download server for mods (for mods in private matches).");
|
Dvar::Register<bool>("mod_force_download_server", false, Game::DVAR_ARCHIVE, "Set to true to force the client to run the download server for mods (for mods in private matches).");
|
||||||
}, Scheduler::Pipeline::MAIN);
|
}, Scheduler::Pipeline::MAIN);
|
||||||
|
|
||||||
Scheduler::Loop([]
|
Scheduler::Loop([]
|
||||||
@ -968,9 +969,6 @@ namespace Components
|
|||||||
|
|
||||||
Script::AddFunction("HttpGet", []
|
Script::AddFunction("HttpGet", []
|
||||||
{
|
{
|
||||||
if (!Flags::HasFlag("scriptablehttp"))
|
|
||||||
return;
|
|
||||||
|
|
||||||
const auto* url = Game::Scr_GetString(0);
|
const auto* url = Game::Scr_GetString(0);
|
||||||
|
|
||||||
if (url == nullptr)
|
if (url == nullptr)
|
||||||
@ -989,9 +987,6 @@ namespace Components
|
|||||||
|
|
||||||
Script::AddFunction("HttpCancel", []
|
Script::AddFunction("HttpCancel", []
|
||||||
{
|
{
|
||||||
if (!Flags::HasFlag("scriptablehttp"))
|
|
||||||
return;
|
|
||||||
|
|
||||||
const auto object = Game::Scr_GetObject(0);
|
const auto object = Game::Scr_GetObject(0);
|
||||||
for (const auto& download : Download::ScriptDownloads)
|
for (const auto& download : Download::ScriptDownloads)
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <Game/Functions.hpp>
|
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
|
@ -52,14 +52,14 @@ namespace Components
|
|||||||
template <> unsigned int Dvar::Var::get()
|
template <> unsigned int Dvar::Var::get()
|
||||||
{
|
{
|
||||||
if (this->dvar == nullptr)
|
if (this->dvar == nullptr)
|
||||||
return 0u;
|
return 0;
|
||||||
|
|
||||||
if (this->dvar->type == Game::dvar_type::DVAR_TYPE_INT)
|
if (this->dvar->type == Game::dvar_type::DVAR_TYPE_INT)
|
||||||
{
|
{
|
||||||
return this->dvar->current.unsignedInt;
|
return this->dvar->current.unsignedInt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0u;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> float Dvar::Var::get()
|
template <> float Dvar::Var::get()
|
||||||
@ -249,7 +249,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Dvar::Register<const char*>(name, username.data(), flags | Game::dvar_flag::DVAR_ARCHIVE, description).get<Game::dvar_t*>();
|
return Dvar::Register<const char*>(name, username.data(), flags | Game::DVAR_ARCHIVE, description).get<Game::dvar_t*>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dvar::SetFromStringByNameSafeExternal(const char* dvarName, const char* string)
|
void Dvar::SetFromStringByNameSafeExternal(const char* dvarName, const char* string)
|
||||||
@ -283,6 +283,18 @@ namespace Components
|
|||||||
Game::Dvar_SetFromStringByNameFromSource(dvarName, string, Game::DvarSetSource::DVAR_SOURCE_EXTERNAL);
|
Game::Dvar_SetFromStringByNameFromSource(dvarName, string, Game::DvarSetSource::DVAR_SOURCE_EXTERNAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Dvar::AreArchiveDvarsProtected()
|
||||||
|
{
|
||||||
|
static std::optional<bool> flag;
|
||||||
|
|
||||||
|
if (!flag.has_value())
|
||||||
|
{
|
||||||
|
flag.emplace(Flags::HasFlag("protect-saved-dvars"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return flag.value();
|
||||||
|
}
|
||||||
|
|
||||||
void Dvar::SaveArchiveDvar(const Game::dvar_t* var)
|
void Dvar::SaveArchiveDvar(const Game::dvar_t* var)
|
||||||
{
|
{
|
||||||
if (!Utils::IO::FileExists(Dvar::ArchiveDvarPath))
|
if (!Utils::IO::FileExists(Dvar::ArchiveDvarPath))
|
||||||
@ -299,36 +311,73 @@ namespace Components
|
|||||||
{
|
{
|
||||||
// Save the dvar original value if it has the archive flag
|
// Save the dvar original value if it has the archive flag
|
||||||
const auto* dvar = Game::Dvar_FindVar(dvarName);
|
const auto* dvar = Game::Dvar_FindVar(dvarName);
|
||||||
if (dvar != nullptr && dvar->flags & Game::dvar_flag::DVAR_ARCHIVE)
|
if (dvar != nullptr && dvar->flags & Game::DVAR_ARCHIVE)
|
||||||
{
|
{
|
||||||
|
if (Dvar::AreArchiveDvarsProtected())
|
||||||
|
{
|
||||||
|
Logger::Print(Game::CON_CHANNEL_CONSOLEONLY, "Not allowing server to override saved dvar '{}'\n", dvarName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_DVARS
|
||||||
|
Logger::Print(Game::CON_CHANNEL_CONSOLEONLY, "Server is overriding saved dvar '{}'\n", dvarName);
|
||||||
|
#endif
|
||||||
Dvar::SaveArchiveDvar(dvar);
|
Dvar::SaveArchiveDvar(dvar);
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::Hook::Call<void(const char*, const char*)>(0x4F52E0)(dvarName, value);
|
Utils::Hook::Call<void(const char*, const char*)>(0x4F52E0)(dvarName, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Dvar::OnRegisterVariant([[maybe_unused]] Game::dvar_t* dvar)
|
||||||
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
|
dvar->flags &= ~Game::DVAR_CHEAT;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(naked) void Dvar::Dvar_RegisterVariant_Stub()
|
||||||
|
{
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
pushad
|
||||||
|
|
||||||
|
push eax
|
||||||
|
call Dvar::OnRegisterVariant
|
||||||
|
add esp, 0x4
|
||||||
|
|
||||||
|
popad
|
||||||
|
|
||||||
|
// Game's code
|
||||||
|
pop edi
|
||||||
|
pop esi
|
||||||
|
pop ebp
|
||||||
|
pop ebx
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Dvar::Dvar()
|
Dvar::Dvar()
|
||||||
{
|
{
|
||||||
// set flags of cg_drawFPS to archive
|
// set flags of cg_drawFPS to archive
|
||||||
Utils::Hook::Or<BYTE>(0x4F8F69, Game::dvar_flag::DVAR_ARCHIVE);
|
Utils::Hook::Or<BYTE>(0x4F8F69, Game::DVAR_ARCHIVE);
|
||||||
|
|
||||||
// un-cheat camera_thirdPersonCrosshairOffset and add archive flags
|
// un-cheat camera_thirdPersonCrosshairOffset and add archive flags
|
||||||
Utils::Hook::Xor<BYTE>(0x447B41, Game::dvar_flag::DVAR_CHEAT | Game::dvar_flag::DVAR_ARCHIVE);
|
Utils::Hook::Xor<BYTE>(0x447B41, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE);
|
||||||
|
|
||||||
// un-cheat cg_fov and add archive flags
|
// un-cheat cg_fov and add archive flags
|
||||||
Utils::Hook::Xor<BYTE>(0x4F8E35, Game::dvar_flag::DVAR_CHEAT | Game::dvar_flag::DVAR_ARCHIVE);
|
Utils::Hook::Xor<BYTE>(0x4F8E35, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE);
|
||||||
|
|
||||||
// un-cheat cg_fovscale and add archive flags
|
// un-cheat cg_fovscale and add archive flags
|
||||||
Utils::Hook::Xor<BYTE>(0x4F8E68, Game::dvar_flag::DVAR_CHEAT | Game::dvar_flag::DVAR_ARCHIVE);
|
Utils::Hook::Xor<BYTE>(0x4F8E68, 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<BYTE>(0x4F8FC2, Game::dvar_flag::DVAR_CHEAT | Game::dvar_flag::DVAR_ARCHIVE);
|
Utils::Hook::Xor<BYTE>(0x4F8FC2, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE);
|
||||||
|
|
||||||
// remove archive flags for cg_hudchatposition
|
// remove archive flags for cg_hudchatposition
|
||||||
Utils::Hook::Xor<BYTE>(0x4F9992, Game::dvar_flag::DVAR_ARCHIVE);
|
Utils::Hook::Xor<BYTE>(0x4F9992, Game::DVAR_ARCHIVE);
|
||||||
|
|
||||||
// remove write protection from fs_game
|
// remove write protection from fs_game
|
||||||
Utils::Hook::Xor<DWORD>(0x6431EA, Game::dvar_flag::DVAR_INIT);
|
Utils::Hook::Xor<DWORD>(0x6431EA, Game::DVAR_INIT);
|
||||||
|
|
||||||
// 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
|
||||||
@ -340,19 +389,19 @@ namespace Components
|
|||||||
Utils::Hook::Set<float*>(0x408078, &volume);
|
Utils::Hook::Set<float*>(0x408078, &volume);
|
||||||
|
|
||||||
// Uncheat ui_showList
|
// Uncheat ui_showList
|
||||||
Utils::Hook::Xor<BYTE>(0x6310DC, Game::dvar_flag::DVAR_CHEAT);
|
Utils::Hook::Xor<BYTE>(0x6310DC, Game::DVAR_CHEAT);
|
||||||
|
|
||||||
// Uncheat ui_debugMode
|
// Uncheat ui_debugMode
|
||||||
Utils::Hook::Xor<BYTE>(0x6312DE, Game::dvar_flag::DVAR_CHEAT);
|
Utils::Hook::Xor<BYTE>(0x6312DE, Game::DVAR_CHEAT);
|
||||||
|
|
||||||
// Hook dvar 'name' registration
|
// Hook dvar 'name' registration
|
||||||
Utils::Hook(0x40531C, Dvar::Dvar_RegisterName, HOOK_CALL).install()->quick();
|
Utils::Hook(0x40531C, Dvar::Dvar_RegisterName, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
// un-cheat safeArea_* and add archive flags
|
// un-cheat safeArea_* and add archive flags
|
||||||
Utils::Hook::Xor<INT>(0x42E3F5, Game::dvar_flag::DVAR_ROM | Game::dvar_flag::DVAR_ARCHIVE); //safeArea_adjusted_horizontal
|
Utils::Hook::Xor<INT>(0x42E3F5, Game::DVAR_ROM | Game::DVAR_ARCHIVE); //safeArea_adjusted_horizontal
|
||||||
Utils::Hook::Xor<INT>(0x42E423, Game::dvar_flag::DVAR_ROM | Game::dvar_flag::DVAR_ARCHIVE); //safeArea_adjusted_vertical
|
Utils::Hook::Xor<INT>(0x42E423, Game::DVAR_ROM | Game::DVAR_ARCHIVE); //safeArea_adjusted_vertical
|
||||||
Utils::Hook::Xor<BYTE>(0x42E398, Game::dvar_flag::DVAR_CHEAT | Game::dvar_flag::DVAR_ARCHIVE); //safeArea_horizontal
|
Utils::Hook::Xor<BYTE>(0x42E398, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE); //safeArea_horizontal
|
||||||
Utils::Hook::Xor<BYTE>(0x42E3C4, Game::dvar_flag::DVAR_CHEAT | Game::dvar_flag::DVAR_ARCHIVE); //safeArea_vertical
|
Utils::Hook::Xor<BYTE>(0x42E3C4, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE); //safeArea_vertical
|
||||||
|
|
||||||
// Don't allow setting cheat protected dvars via menus
|
// Don't allow setting cheat protected dvars via menus
|
||||||
Utils::Hook(0x63C897, Dvar::SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
Utils::Hook(0x63C897, Dvar::SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||||
@ -383,6 +432,10 @@ namespace Components
|
|||||||
|
|
||||||
// Reset archive dvars when client leaves a server
|
// Reset archive dvars when client leaves a server
|
||||||
Events::OnSteamDisconnect(Dvar::ResetDvarsValue);
|
Events::OnSteamDisconnect(Dvar::ResetDvarsValue);
|
||||||
|
|
||||||
|
// For debugging
|
||||||
|
Utils::Hook(0x6483FA, Dvar::Dvar_RegisterVariant_Stub, HOOK_JUMP).install()->quick();
|
||||||
|
Utils::Hook(0x648438, Dvar::Dvar_RegisterVariant_Stub, HOOK_JUMP).install()->quick();
|
||||||
}
|
}
|
||||||
|
|
||||||
Dvar::~Dvar()
|
Dvar::~Dvar()
|
||||||
|
@ -8,10 +8,10 @@ namespace Components
|
|||||||
class Flag
|
class Flag
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Flag(Game::dvar_flag flag) : val(flag) {}
|
Flag(Game::DvarFlags flag) : val(flag) {}
|
||||||
Flag(unsigned __int16 flag) : Flag(static_cast<Game::dvar_flag>(flag)) {}
|
Flag(unsigned __int16 flag) : Flag(static_cast<Game::DvarFlags>(flag)) {}
|
||||||
|
|
||||||
Game::dvar_flag val;
|
Game::DvarFlags val;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Var
|
class Var
|
||||||
@ -57,7 +57,11 @@ namespace Components
|
|||||||
static void SetFromStringByNameExternal(const char* dvar, const char* value);
|
static void SetFromStringByNameExternal(const char* dvar, const char* value);
|
||||||
static void SetFromStringByNameSafeExternal(const char* dvar, const char* value);
|
static void SetFromStringByNameSafeExternal(const char* dvar, const char* value);
|
||||||
|
|
||||||
|
static bool AreArchiveDvarsProtected();
|
||||||
static void SaveArchiveDvar(const Game::dvar_t* var);
|
static void SaveArchiveDvar(const Game::dvar_t* var);
|
||||||
static void DvarSetFromStringByNameStub(const char* dvarName, const char* value);
|
static void DvarSetFromStringByNameStub(const char* dvarName, const char* value);
|
||||||
|
|
||||||
|
static void OnRegisterVariant(Game::dvar_t* dvar);
|
||||||
|
static void Dvar_RegisterVariant_Stub();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@ namespace Components
|
|||||||
Utils::Signal<Events::ClientCallback> Events::ClientDisconnectSignal;
|
Utils::Signal<Events::ClientCallback> Events::ClientDisconnectSignal;
|
||||||
Utils::Signal<Events::Callback> Events::SteamDisconnectSignal;
|
Utils::Signal<Events::Callback> Events::SteamDisconnectSignal;
|
||||||
Utils::Signal<Events::Callback> Events::ShutdownSystemSignal;
|
Utils::Signal<Events::Callback> Events::ShutdownSystemSignal;
|
||||||
|
Utils::Signal<Events::Callback> Events::ClientInitSignal;
|
||||||
|
Utils::Signal<Events::Callback> Events::ServerInitSignal;
|
||||||
|
|
||||||
void Events::OnClientDisconnect(const Utils::Slot<ClientCallback>& callback)
|
void Events::OnClientDisconnect(const Utils::Slot<ClientCallback>& callback)
|
||||||
{
|
{
|
||||||
@ -21,6 +23,16 @@ namespace Components
|
|||||||
ShutdownSystemSignal.connect(callback);
|
ShutdownSystemSignal.connect(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Events::OnClientInit(const Utils::Slot<Callback>& callback)
|
||||||
|
{
|
||||||
|
ClientInitSignal.connect(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Events::OnSVInit(const Utils::Slot<Callback>& callback)
|
||||||
|
{
|
||||||
|
ServerInitSignal.connect(callback);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Should be called when a client drops from the server
|
* Should be called when a client drops from the server
|
||||||
* but not "between levels" (Quake-III-Arena)
|
* but not "between levels" (Quake-III-Arena)
|
||||||
@ -46,6 +58,22 @@ namespace Components
|
|||||||
Utils::Hook::Call<void(unsigned char)>(0x421EE0)(sys); // Scr_ShutdownSystem
|
Utils::Hook::Call<void(unsigned char)>(0x421EE0)(sys); // Scr_ShutdownSystem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Events::CL_InitOnceForAllClients_HK()
|
||||||
|
{
|
||||||
|
ClientInitSignal();
|
||||||
|
ClientInitSignal.clear();
|
||||||
|
|
||||||
|
Utils::Hook::Call<void()>(0x404CA0)(); // CL_InitOnceForAllClients
|
||||||
|
}
|
||||||
|
|
||||||
|
void Events::SV_Init_Hk()
|
||||||
|
{
|
||||||
|
ServerInitSignal();
|
||||||
|
ServerInitSignal.clear();
|
||||||
|
|
||||||
|
Utils::Hook::Call<void()>(0x474320)(); // SV_InitGameMode
|
||||||
|
}
|
||||||
|
|
||||||
Events::Events()
|
Events::Events()
|
||||||
{
|
{
|
||||||
Utils::Hook(0x625235, ClientDisconnect_Hk, HOOK_CALL).install()->quick(); // SV_FreeClient
|
Utils::Hook(0x625235, ClientDisconnect_Hk, HOOK_CALL).install()->quick(); // SV_FreeClient
|
||||||
@ -54,5 +82,9 @@ namespace Components
|
|||||||
|
|
||||||
Utils::Hook(0x47548B, Scr_ShutdownSystem_Hk, HOOK_CALL).install()->quick(); // G_LoadGame
|
Utils::Hook(0x47548B, Scr_ShutdownSystem_Hk, HOOK_CALL).install()->quick(); // G_LoadGame
|
||||||
Utils::Hook(0x4D06BA, Scr_ShutdownSystem_Hk, HOOK_CALL).install()->quick(); // G_ShutdownGame
|
Utils::Hook(0x4D06BA, Scr_ShutdownSystem_Hk, HOOK_CALL).install()->quick(); // G_ShutdownGame
|
||||||
|
|
||||||
|
Utils::Hook(0x60BE5B, CL_InitOnceForAllClients_HK, HOOK_CALL).install()->quick(); // Com_Init_Try_Block_Function
|
||||||
|
|
||||||
|
Utils::Hook(0x4D3665, SV_Init_Hk, HOOK_CALL).install()->quick(); // SV_Init
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,13 +18,22 @@ namespace Components
|
|||||||
|
|
||||||
static void OnVMShutdown(const Utils::Slot<Callback>& callback);
|
static void OnVMShutdown(const Utils::Slot<Callback>& callback);
|
||||||
|
|
||||||
|
static void OnClientInit(const Utils::Slot<Callback>& callback);
|
||||||
|
|
||||||
|
// Client & Server (triggered once)
|
||||||
|
static void OnSVInit(const Utils::Slot<Callback>& callback);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Utils::Signal<ClientCallback> ClientDisconnectSignal;
|
static Utils::Signal<ClientCallback> ClientDisconnectSignal;
|
||||||
static Utils::Signal<Callback> SteamDisconnectSignal;
|
static Utils::Signal<Callback> SteamDisconnectSignal;
|
||||||
static Utils::Signal<Callback> ShutdownSystemSignal;
|
static Utils::Signal<Callback> ShutdownSystemSignal;
|
||||||
|
static Utils::Signal<Callback> ClientInitSignal;
|
||||||
|
static Utils::Signal<Callback> ServerInitSignal;
|
||||||
|
|
||||||
static void ClientDisconnect_Hk(int clientNum);
|
static void ClientDisconnect_Hk(int clientNum);
|
||||||
static void SteamDisconnect_Hk();
|
static void SteamDisconnect_Hk();
|
||||||
static void Scr_ShutdownSystem_Hk(unsigned char sys);
|
static void Scr_ShutdownSystem_Hk(unsigned char sys);
|
||||||
|
static void CL_InitOnceForAllClients_HK();
|
||||||
|
static void SV_Init_Hk();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -495,7 +495,7 @@ namespace Components
|
|||||||
|
|
||||||
FastFiles::FastFiles()
|
FastFiles::FastFiles()
|
||||||
{
|
{
|
||||||
Dvar::Register<bool>("ui_zoneDebug", false, Game::dvar_flag::DVAR_ARCHIVE, "Display current loaded zone.");
|
Dvar::Register<bool>("ui_zoneDebug", false, Game::DVAR_ARCHIVE, "Display current loaded zone.");
|
||||||
|
|
||||||
// Fix XSurface assets
|
// Fix XSurface assets
|
||||||
Utils::Hook(0x0048E8A5, FastFiles::Load_XSurfaceArray, HOOK_CALL).install()->quick();
|
Utils::Hook(0x0048E8A5, FastFiles::Load_XSurfaceArray, HOOK_CALL).install()->quick();
|
||||||
|
@ -6,17 +6,29 @@ namespace Components
|
|||||||
std::recursive_mutex FileSystem::FSMutex;
|
std::recursive_mutex FileSystem::FSMutex;
|
||||||
Utils::Memory::Allocator FileSystem::MemAllocator;
|
Utils::Memory::Allocator FileSystem::MemAllocator;
|
||||||
|
|
||||||
void FileSystem::File::read()
|
void FileSystem::File::read(Game::FsThread thread)
|
||||||
{
|
{
|
||||||
char* _buffer = nullptr;
|
std::lock_guard _(FileSystem::FSMutex);
|
||||||
int size = Game::FS_ReadFile(this->filePath.data(), &_buffer);
|
|
||||||
|
|
||||||
this->buffer.clear();
|
assert(!filePath.empty());
|
||||||
|
|
||||||
if (size >= 0)
|
int handle;
|
||||||
|
const auto len = Game::FS_FOpenFileReadForThread(filePath.data(), &handle, thread);
|
||||||
|
|
||||||
|
if (handle)
|
||||||
{
|
{
|
||||||
this->buffer.append(_buffer, size);
|
auto* buf = AllocateFile(len + 1);
|
||||||
Game::FS_FreeFile(_buffer);
|
|
||||||
|
[[maybe_unused]] auto bytesRead = Game::FS_Read(buf, len, handle);
|
||||||
|
|
||||||
|
assert(bytesRead == len);
|
||||||
|
|
||||||
|
buf[len] = '\0';
|
||||||
|
|
||||||
|
Game::FS_FCloseFile(handle);
|
||||||
|
|
||||||
|
this->buffer.append(buf, len);
|
||||||
|
FreeFile(buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,7 +197,7 @@ namespace Components
|
|||||||
else *buffer = nullptr;
|
else *buffer = nullptr;
|
||||||
if (!path) return -1;
|
if (!path) return -1;
|
||||||
|
|
||||||
std::lock_guard<std::mutex> _(FileSystem::Mutex);
|
std::lock_guard _(FileSystem::Mutex);
|
||||||
FileSystem::FileReader reader(path);
|
FileSystem::FileReader reader(path);
|
||||||
|
|
||||||
int size = reader.getSize();
|
int size = reader.getSize();
|
||||||
@ -256,13 +268,13 @@ namespace Components
|
|||||||
|
|
||||||
void FileSystem::FsStartupSync(const char* a1)
|
void FileSystem::FsStartupSync(const char* a1)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(FileSystem::FSMutex);
|
std::lock_guard _(FileSystem::FSMutex);
|
||||||
return Utils::Hook::Call<void(const char*)>(0x4823A0)(a1); // FS_Startup
|
return Utils::Hook::Call<void(const char*)>(0x4823A0)(a1); // FS_Startup
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystem::FsRestartSync(int a1, int a2)
|
void FileSystem::FsRestartSync(int a1, int a2)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(FileSystem::FSMutex);
|
std::lock_guard _(FileSystem::FSMutex);
|
||||||
Maps::GetUserMap()->freeIwd();
|
Maps::GetUserMap()->freeIwd();
|
||||||
Utils::Hook::Call<void(int, int)>(0x461A50)(a1, a2); // FS_Restart
|
Utils::Hook::Call<void(int, int)>(0x461A50)(a1, a2); // FS_Restart
|
||||||
Maps::GetUserMap()->reloadIwd();
|
Maps::GetUserMap()->reloadIwd();
|
||||||
@ -270,7 +282,7 @@ namespace Components
|
|||||||
|
|
||||||
void FileSystem::FsShutdownSync(int a1)
|
void FileSystem::FsShutdownSync(int a1)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(FileSystem::FSMutex);
|
std::lock_guard _(FileSystem::FSMutex);
|
||||||
Maps::GetUserMap()->freeIwd();
|
Maps::GetUserMap()->freeIwd();
|
||||||
Utils::Hook::Call<void(int)>(0x4A46C0)(a1); // FS_Shutdown
|
Utils::Hook::Call<void(int)>(0x4A46C0)(a1); // FS_Shutdown
|
||||||
}
|
}
|
||||||
@ -283,7 +295,7 @@ namespace Components
|
|||||||
|
|
||||||
int FileSystem::LoadTextureSync(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image)
|
int FileSystem::LoadTextureSync(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(FileSystem::FSMutex);
|
std::lock_guard _(FileSystem::FSMutex);
|
||||||
return Game::Load_Texture(loadDef, image);
|
return Game::Load_Texture(loadDef, image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,8 +18,9 @@ namespace Components
|
|||||||
class File : public AbstractFile
|
class File : public AbstractFile
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
File() {};
|
File() = default;
|
||||||
File(const std::string& file) : filePath(file) { this->read(); };
|
File(std::string file) : filePath{std::move(file)} { this->read(); };
|
||||||
|
File(std::string file, Game::FsThread thread) : filePath{std::move(file)} { this->read(thread); };
|
||||||
|
|
||||||
bool exists() override { return !this->buffer.empty(); };
|
bool exists() override { return !this->buffer.empty(); };
|
||||||
std::string getName() override { return this->filePath; };
|
std::string getName() override { return this->filePath; };
|
||||||
@ -29,7 +30,7 @@ namespace Components
|
|||||||
std::string filePath;
|
std::string filePath;
|
||||||
std::string buffer;
|
std::string buffer;
|
||||||
|
|
||||||
void read();
|
void read(Game::FsThread thread = Game::FS_THREAD_MAIN);
|
||||||
};
|
};
|
||||||
|
|
||||||
class RawFile : public AbstractFile
|
class RawFile : public AbstractFile
|
||||||
|
17
src/Components/Modules/GSC/GSC.cpp
Normal file
17
src/Components/Modules/GSC/GSC.cpp
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
|
#include "IO.hpp"
|
||||||
|
#include "Script.hpp"
|
||||||
|
#include "ScriptExtension.hpp"
|
||||||
|
#include "ScriptStorage.hpp"
|
||||||
|
|
||||||
|
namespace Components
|
||||||
|
{
|
||||||
|
GSC::GSC()
|
||||||
|
{
|
||||||
|
Loader::Register(new IO());
|
||||||
|
Loader::Register(new Script());
|
||||||
|
Loader::Register(new ScriptExtension());
|
||||||
|
Loader::Register(new ScriptStorage());
|
||||||
|
}
|
||||||
|
}
|
10
src/Components/Modules/GSC/GSC.hpp
Normal file
10
src/Components/Modules/GSC/GSC.hpp
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Components
|
||||||
|
{
|
||||||
|
class GSC : public Component
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GSC();
|
||||||
|
};
|
||||||
|
}
|
115
src/Components/Modules/GSC/IO.cpp
Normal file
115
src/Components/Modules/GSC/IO.cpp
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
#include <STDInclude.hpp>
|
||||||
|
#include "IO.hpp"
|
||||||
|
#include "Script.hpp"
|
||||||
|
|
||||||
|
namespace Components
|
||||||
|
{
|
||||||
|
const char* IO::QueryStrings[] = { R"(..)", R"(../)", R"(..\)" };
|
||||||
|
|
||||||
|
void IO::AddScriptFunctions()
|
||||||
|
{
|
||||||
|
Script::AddFunction("FileWrite", [] // gsc: FileWrite(<filepath>, <string>, <mode>)
|
||||||
|
{
|
||||||
|
const auto* path = Game::Scr_GetString(0);
|
||||||
|
auto* text = Game::Scr_GetString(1);
|
||||||
|
auto* mode = Game::Scr_GetString(2);
|
||||||
|
|
||||||
|
if (path == nullptr)
|
||||||
|
{
|
||||||
|
Game::Scr_ParamError(0, "^1FileWrite: filepath is not defined!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text == nullptr || mode == nullptr)
|
||||||
|
{
|
||||||
|
Game::Scr_Error("^1FileWrite: Illegal parameters!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < ARRAYSIZE(QueryStrings); ++i)
|
||||||
|
{
|
||||||
|
if (std::strstr(path, QueryStrings[i]) != nullptr)
|
||||||
|
{
|
||||||
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "FileWrite: directory traversal is not allowed!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode != "append"s && mode != "write"s)
|
||||||
|
{
|
||||||
|
Logger::Warning(Game::CON_CHANNEL_SCRIPT, "FileWrite: mode not defined or was wrong, defaulting to 'write'\n");
|
||||||
|
mode = "write";
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* scriptData = Utils::String::VA("%s/%s", "scriptdata", path);
|
||||||
|
|
||||||
|
if (mode == "write"s)
|
||||||
|
{
|
||||||
|
FileSystem::FileWriter(scriptData).write(text);
|
||||||
|
}
|
||||||
|
else if (mode == "append"s)
|
||||||
|
{
|
||||||
|
FileSystem::FileWriter(scriptData, true).write(text);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddFunction("FileRead", [] // gsc: FileRead(<filepath>)
|
||||||
|
{
|
||||||
|
const auto* path = Game::Scr_GetString(0);
|
||||||
|
|
||||||
|
if (path == nullptr)
|
||||||
|
{
|
||||||
|
Game::Scr_ParamError(0, "^1FileRead: filepath is not defined!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < ARRAYSIZE(QueryStrings); ++i)
|
||||||
|
{
|
||||||
|
if (std::strstr(path, QueryStrings[i]) != nullptr)
|
||||||
|
{
|
||||||
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "FileRead: directory traversal is not allowed!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* scriptData = Utils::String::VA("%s/%s", "scriptdata", path);
|
||||||
|
|
||||||
|
if (!FileSystem::FileReader(scriptData).exists())
|
||||||
|
{
|
||||||
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "FileRead: file '{}' not found!\n", scriptData);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::Scr_AddString(FileSystem::FileReader(scriptData).getBuffer().data());
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddFunction("FileExists", [] // gsc: FileExists(<filepath>)
|
||||||
|
{
|
||||||
|
const auto* path = Game::Scr_GetString(0);
|
||||||
|
|
||||||
|
if (path == nullptr)
|
||||||
|
{
|
||||||
|
Game::Scr_ParamError(0, "^1FileExists: filepath is not defined!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < ARRAYSIZE(QueryStrings); ++i)
|
||||||
|
{
|
||||||
|
if (std::strstr(path, QueryStrings[i]) != nullptr)
|
||||||
|
{
|
||||||
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "FileExists: directory traversal is not allowed!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* scriptData = Utils::String::VA("%s/%s", "scriptdata", path);
|
||||||
|
|
||||||
|
Game::Scr_AddBool(FileSystem::FileReader(scriptData).exists());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
IO::IO()
|
||||||
|
{
|
||||||
|
AddScriptFunctions();
|
||||||
|
}
|
||||||
|
}
|
15
src/Components/Modules/GSC/IO.hpp
Normal file
15
src/Components/Modules/GSC/IO.hpp
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Components
|
||||||
|
{
|
||||||
|
class IO : public Component
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IO();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const char* QueryStrings[];
|
||||||
|
|
||||||
|
static void AddScriptFunctions();
|
||||||
|
};
|
||||||
|
}
|
@ -1,18 +1,22 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "Script.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
|
std::unordered_map<std::string, Script::ScriptFunction> Script::CustomScrFunctions;
|
||||||
|
std::unordered_map<std::string, Script::ScriptMethod> Script::CustomScrMethods;
|
||||||
|
|
||||||
std::string Script::ScriptName;
|
std::string Script::ScriptName;
|
||||||
std::vector<int> Script::ScriptHandles;
|
|
||||||
std::unordered_map<std::string, Game::BuiltinFunctionDef> Script::CustomScrFunctions;
|
|
||||||
std::unordered_map<std::string, Game::BuiltinMethodDef> Script::CustomScrMethods;
|
|
||||||
std::vector<std::string> Script::ScriptNameStack;
|
std::vector<std::string> Script::ScriptNameStack;
|
||||||
unsigned short Script::FunctionName;
|
unsigned short Script::FunctionName;
|
||||||
std::unordered_map<std::string, std::string> Script::ScriptStorage;
|
|
||||||
std::unordered_map<int, std::string> Script::ScriptBaseProgramNum;
|
std::unordered_map<int, std::string> Script::ScriptBaseProgramNum;
|
||||||
|
int Script::LastFrameTime = -1;
|
||||||
|
|
||||||
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;
|
||||||
int Script::LastFrameTime = -1;
|
|
||||||
|
std::vector<int> Script::ScriptMainHandles;
|
||||||
|
std::vector<int> Script::ScriptInitHandles;
|
||||||
|
|
||||||
void Script::FunctionError()
|
void Script::FunctionError()
|
||||||
{
|
{
|
||||||
@ -201,87 +205,87 @@ namespace Components
|
|||||||
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, Script::ScriptName);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Script::LoadScriptAndLabel(const std::string& script, const std::string& label)
|
void Script::Scr_LoadGameType_Stub()
|
||||||
{
|
{
|
||||||
|
for (const auto& handle : Script::ScriptMainHandles)
|
||||||
|
{
|
||||||
|
const auto id = Game::Scr_ExecThread(handle, 0);
|
||||||
|
Game::Scr_FreeThread(static_cast<std::uint16_t>(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::Scr_LoadGameType();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Script::Scr_StartupGameType_Stub()
|
||||||
|
{
|
||||||
|
for (const auto& handle : Script::ScriptInitHandles)
|
||||||
|
{
|
||||||
|
const auto id = Game::Scr_ExecThread(handle, 0);
|
||||||
|
Game::Scr_FreeThread(static_cast<std::uint16_t>(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::Scr_StartupGameType();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Script::GScr_LoadGameTypeScript_Stub()
|
||||||
|
{
|
||||||
|
// Clear handles (from previous GSC loading session)
|
||||||
|
Script::ScriptMainHandles.clear();
|
||||||
|
Script::ScriptInitHandles.clear();
|
||||||
|
|
||||||
|
const auto list = FileSystem::GetFileList("scripts/", "gsc");
|
||||||
|
|
||||||
|
for (const auto& file : list)
|
||||||
|
{
|
||||||
|
std::string script = "scripts/" + file;
|
||||||
|
|
||||||
|
if (Utils::String::EndsWith(script, ".gsc"))
|
||||||
|
{
|
||||||
|
script = script.substr(0, script.size() - 4);
|
||||||
|
}
|
||||||
|
|
||||||
Logger::Print("Loading script {}.gsc...\n", script);
|
Logger::Print("Loading script {}.gsc...\n", script);
|
||||||
|
|
||||||
if (!Game::Scr_LoadScript(script.data()))
|
if (!Game::Scr_LoadScript(script.data()))
|
||||||
{
|
{
|
||||||
Logger::Print("Script {} encountered an error while loading. (doesn't exist?)", script);
|
Logger::Print("Script {} encountered an error while loading. (doesn't exist?)", script);
|
||||||
Logger::Error(Game::ERR_DROP, "Could not find script '{}'", script);
|
Logger::Error(Game::ERR_DROP, "Could not find script '{}'", script);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger::Print("Script {}.gsc loaded successfully.\n", script);
|
Logger::Print("Script {}.gsc loaded successfully.\n", script);
|
||||||
|
Logger::Debug("Finding script handle main or init...");
|
||||||
|
|
||||||
|
const auto initHandle = Game::Scr_GetFunctionHandle(script.data(), "init");
|
||||||
|
if (initHandle != 0)
|
||||||
|
{
|
||||||
|
Script::ScriptInitHandles.push_back(initHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::Debug("Finding script handle {}::{}...", script.data(), label.data());
|
const auto mainHandle = Game::Scr_GetFunctionHandle(script.data(), "main");
|
||||||
const auto handle = Game::Scr_GetFunctionHandle(script.data(), label.data());
|
if (mainHandle != 0)
|
||||||
if (handle)
|
|
||||||
{
|
{
|
||||||
Logger::Print("Script handle {}::{} loaded successfully.\n", script, label);
|
Script::ScriptMainHandles.push_back(mainHandle);
|
||||||
return handle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::Print("Script handle {}::{} couldn't be loaded. (file with no entry point?)\n", script, label);
|
// Allow scripts with no handles
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Script::LoadGameType()
|
|
||||||
{
|
|
||||||
for (const auto& handle : Script::ScriptHandles)
|
|
||||||
{
|
|
||||||
Game::Scr_FreeThread(Game::Scr_ExecThread(handle, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
Game::Scr_LoadGameType();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Script::LoadGameTypeScript()
|
|
||||||
{
|
|
||||||
Script::ScriptHandles.clear();
|
|
||||||
|
|
||||||
const auto list = FileSystem::GetFileList("scripts/", "gsc");
|
|
||||||
|
|
||||||
for (auto file : list)
|
|
||||||
{
|
|
||||||
file.insert(0, "scripts/");
|
|
||||||
|
|
||||||
if (Utils::String::EndsWith(file, ".gsc"))
|
|
||||||
{
|
|
||||||
file = file.substr(0, file.size() - 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto handle = Script::LoadScriptAndLabel(file, "init");
|
|
||||||
|
|
||||||
if (handle)
|
|
||||||
{
|
|
||||||
Script::ScriptHandles.push_back(handle);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
handle = Script::LoadScriptAndLabel(file, "main");
|
|
||||||
if (handle) Script::ScriptHandles.push_back(handle);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::GScr_LoadGameTypeScript();
|
Game::GScr_LoadGameTypeScript();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Script::AddFunction(const char* name, Game::BuiltinFunction func, int type)
|
void Script::AddFunction(const std::string& name, Game::BuiltinFunction func, bool type)
|
||||||
{
|
{
|
||||||
Game::BuiltinFunctionDef toAdd;
|
Script::ScriptFunction toAdd;
|
||||||
toAdd.actionString = name;
|
|
||||||
toAdd.actionFunc = func;
|
toAdd.actionFunc = func;
|
||||||
toAdd.type = type;
|
toAdd.type = type;
|
||||||
|
|
||||||
CustomScrFunctions.insert_or_assign(Utils::String::ToLower(name), toAdd);
|
CustomScrFunctions.insert_or_assign(Utils::String::ToLower(name), toAdd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Script::AddMethod(const char* name, Game::BuiltinMethod func, int type)
|
void Script::AddMethod(const std::string& name, Game::BuiltinMethod func, bool type)
|
||||||
{
|
{
|
||||||
Game::BuiltinMethodDef toAdd;
|
Script::ScriptMethod toAdd;
|
||||||
toAdd.actionString = name;
|
|
||||||
toAdd.actionFunc = func;
|
toAdd.actionFunc = func;
|
||||||
toAdd.type = type;
|
toAdd.type = type;
|
||||||
|
|
||||||
@ -312,7 +316,7 @@ namespace Components
|
|||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::BuiltinMethod Script::BuiltIn_GetMethod(const char** pName, int* type)
|
Game::BuiltinMethod Script::BuiltIn_GetMethodStub(const char** pName, int* type)
|
||||||
{
|
{
|
||||||
if (pName != nullptr)
|
if (pName != nullptr)
|
||||||
{
|
{
|
||||||
@ -411,11 +415,11 @@ namespace Components
|
|||||||
|
|
||||||
unsigned int Script::SetExpFogStub()
|
unsigned int Script::SetExpFogStub()
|
||||||
{
|
{
|
||||||
if (Game::Scr_GetNumParam() == 6u)
|
if (Game::Scr_GetNumParam() == 6)
|
||||||
{
|
{
|
||||||
std::memmove(&Game::scrVmPub->top[-4], &Game::scrVmPub->top[-5], sizeof(Game::VariableValue) * 6);
|
std::memmove(&Game::scrVmPub->top[-4], &Game::scrVmPub->top[-5], sizeof(Game::VariableValue) * 6);
|
||||||
Game::scrVmPub->top += 1;
|
Game::scrVmPub->top += 1;
|
||||||
Game::scrVmPub->top[-6].type = Game::scrParamType_t::VAR_FLOAT;
|
Game::scrVmPub->top[-6].type = Game::VAR_FLOAT;
|
||||||
Game::scrVmPub->top[-6].u.floatValue = 0.0f;
|
Game::scrVmPub->top[-6].u.floatValue = 0.0f;
|
||||||
|
|
||||||
++Game::scrVmPub->outparamcount;
|
++Game::scrVmPub->outparamcount;
|
||||||
@ -432,7 +436,7 @@ namespace Components
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto value = &Game::scrVmPub->top[-index];
|
const auto* value = &Game::scrVmPub->top[-index];
|
||||||
|
|
||||||
if (value->type != Game::scrParamType_t::VAR_FUNCTION)
|
if (value->type != Game::scrParamType_t::VAR_FUNCTION)
|
||||||
{
|
{
|
||||||
@ -535,7 +539,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Script::AddFunction("ReplaceFunc", [] // gsc: ReplaceFunc(<function>, <function>)
|
Script::AddFunction("ReplaceFunc", [] // gsc: ReplaceFunc(<function>, <function>)
|
||||||
{
|
{
|
||||||
if (Game::Scr_GetNumParam() != 2u)
|
if (Game::Scr_GetNumParam() != 2)
|
||||||
{
|
{
|
||||||
Game::Scr_Error("^1ReplaceFunc: Needs two parameters!\n");
|
Game::Scr_Error("^1ReplaceFunc: Needs two parameters!\n");
|
||||||
return;
|
return;
|
||||||
@ -587,78 +591,6 @@ namespace Components
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Script Storage Functions
|
|
||||||
Script::AddFunction("StorageSet", [] // gsc: StorageSet(<str key>, <str data>);
|
|
||||||
{
|
|
||||||
const auto* key = Game::Scr_GetString(0);
|
|
||||||
const auto* value = Game::Scr_GetString(1);
|
|
||||||
|
|
||||||
if (key == nullptr || value == nullptr)
|
|
||||||
{
|
|
||||||
Game::Scr_Error("^1StorageSet: Illegal parameters!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Script::ScriptStorage.insert_or_assign(key, value);
|
|
||||||
});
|
|
||||||
|
|
||||||
Script::AddFunction("StorageRemove", [] // gsc: StorageRemove(<str key>);
|
|
||||||
{
|
|
||||||
const auto* key = Game::Scr_GetString(0);
|
|
||||||
|
|
||||||
if (key == nullptr)
|
|
||||||
{
|
|
||||||
Game::Scr_ParamError(0, "^1StorageRemove: Illegal parameter!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Script::ScriptStorage.contains(key))
|
|
||||||
{
|
|
||||||
Game::Scr_Error(Utils::String::VA("^1StorageRemove: Store does not have key '%s'!\n", key));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Script::ScriptStorage.erase(key);
|
|
||||||
});
|
|
||||||
|
|
||||||
Script::AddFunction("StorageGet", [] // gsc: StorageGet(<str key>);
|
|
||||||
{
|
|
||||||
const auto* key = Game::Scr_GetString(0);
|
|
||||||
|
|
||||||
if (key == nullptr)
|
|
||||||
{
|
|
||||||
Game::Scr_ParamError(0, "^1StorageGet: Illegal parameter!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Script::ScriptStorage.contains(key))
|
|
||||||
{
|
|
||||||
Game::Scr_Error(Utils::String::VA("^1StorageGet: Store does not have key '%s'!\n", key));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& data = Script::ScriptStorage.at(key);
|
|
||||||
Game::Scr_AddString(data.data());
|
|
||||||
});
|
|
||||||
|
|
||||||
Script::AddFunction("StorageHas", [] // gsc: StorageHas(<str key>);
|
|
||||||
{
|
|
||||||
const auto* key = Game::Scr_GetString(0);
|
|
||||||
|
|
||||||
if (key == nullptr)
|
|
||||||
{
|
|
||||||
Game::Scr_ParamError(0, "^1StorageHas: Illegal parameter!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Game::Scr_AddBool(Script::ScriptStorage.contains(key));
|
|
||||||
});
|
|
||||||
|
|
||||||
Script::AddFunction("StorageClear", [] // gsc: StorageClear();
|
|
||||||
{
|
|
||||||
Script::ScriptStorage.clear();
|
|
||||||
});
|
|
||||||
|
|
||||||
// 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();
|
Script::AddMethod("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self AreControlsFrozen();
|
||||||
{
|
{
|
||||||
@ -687,12 +619,13 @@ namespace Components
|
|||||||
Utils::Hook(0x612EA2, Script::FunctionError, HOOK_CALL).install()->quick();
|
Utils::Hook(0x612EA2, Script::FunctionError, HOOK_CALL).install()->quick();
|
||||||
Utils::Hook(0x434260, Script::CompileError, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x434260, Script::CompileError, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
Utils::Hook(0x48EFFE, Script::LoadGameType, HOOK_CALL).install()->quick();
|
Utils::Hook(0x48EFFE, Script::Scr_LoadGameType_Stub, HOOK_CALL).install()->quick();
|
||||||
Utils::Hook(0x45D44A, Script::LoadGameTypeScript, HOOK_CALL).install()->quick();
|
Utils::Hook(0x48F008, Script::Scr_StartupGameType_Stub, HOOK_CALL).install()->quick();
|
||||||
|
Utils::Hook(0x45D44A, Script::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, Script::BuiltIn_GetFunctionStub, HOOK_CALL).install()->quick(); // Scr_GetFunction
|
||||||
Utils::Hook(0x4EC8DD, Script::BuiltIn_GetMethod, HOOK_CALL).install()->quick(); // Scr_GetMethod
|
Utils::Hook(0x4EC8DD, Script::BuiltIn_GetMethodStub, HOOK_CALL).install()->quick(); // Scr_GetMethod
|
||||||
|
|
||||||
Utils::Hook(0x5F41A3, Script::SetExpFogStub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x5F41A3, Script::SetExpFogStub, HOOK_CALL).install()->quick();
|
||||||
|
|
@ -1,5 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <Game/Structs.hpp>
|
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -8,27 +7,40 @@ namespace Components
|
|||||||
public:
|
public:
|
||||||
Script();
|
Script();
|
||||||
|
|
||||||
static int LoadScriptAndLabel(const std::string& script, const std::string& label);
|
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 AddFunction(const char* name, Game::BuiltinFunction func, int type = 0);
|
|
||||||
static void AddMethod(const char* name, Game::BuiltinMethod func, int type = 0);
|
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct ScriptFunction
|
||||||
|
{
|
||||||
|
Game::BuiltinFunction actionFunc;
|
||||||
|
bool type;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ScriptMethod
|
||||||
|
{
|
||||||
|
Game::BuiltinMethod actionFunc;
|
||||||
|
bool type;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::unordered_map<std::string, ScriptFunction> CustomScrFunctions;
|
||||||
|
static std::unordered_map<std::string, ScriptMethod> CustomScrMethods;
|
||||||
|
|
||||||
static std::string ScriptName;
|
static std::string ScriptName;
|
||||||
static std::vector<int> ScriptHandles;
|
|
||||||
static std::unordered_map<std::string, Game::BuiltinFunctionDef> CustomScrFunctions;
|
|
||||||
static std::unordered_map<std::string, Game::BuiltinMethodDef> CustomScrMethods;
|
|
||||||
static std::vector<std::string> ScriptNameStack;
|
static std::vector<std::string> ScriptNameStack;
|
||||||
static unsigned short FunctionName;
|
static unsigned short FunctionName;
|
||||||
static std::unordered_map<std::string, std::string> ScriptStorage;
|
|
||||||
static std::unordered_map<int, std::string> ScriptBaseProgramNum;
|
static std::unordered_map<int, std::string> ScriptBaseProgramNum;
|
||||||
|
static int LastFrameTime;
|
||||||
|
|
||||||
|
static std::vector<int> ScriptMainHandles;
|
||||||
|
static std::vector<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;
|
||||||
static int LastFrameTime;
|
|
||||||
|
|
||||||
static void CompileError(unsigned int offset, const char* message, ...);
|
static void CompileError(unsigned int offset, const char* message, ...);
|
||||||
static void PrintSourcePos(const char* filename, unsigned int offset);
|
static void PrintSourcePos(const char* filename, unsigned int offset);
|
||||||
@ -43,11 +55,13 @@ namespace Components
|
|||||||
static void RestoreScriptName();
|
static void RestoreScriptName();
|
||||||
static void RestoreScriptNameStub();
|
static void RestoreScriptNameStub();
|
||||||
|
|
||||||
static void LoadGameType();
|
static void Scr_LoadGameType_Stub();
|
||||||
static void LoadGameTypeScript();
|
static void Scr_StartupGameType_Stub();
|
||||||
|
static void GScr_LoadGameTypeScript_Stub();
|
||||||
|
|
||||||
|
static bool IsDeprecated(const std::string& name);
|
||||||
static Game::BuiltinFunction BuiltIn_GetFunctionStub(const char** pName, int* type);
|
static Game::BuiltinFunction BuiltIn_GetFunctionStub(const char** pName, int* type);
|
||||||
static Game::BuiltinMethod BuiltIn_GetMethod(const char** pName, int* type);
|
static Game::BuiltinMethod BuiltIn_GetMethodStub(const char** pName, int* type);
|
||||||
|
|
||||||
static void StoreScriptBaseProgramNumStub();
|
static void StoreScriptBaseProgramNumStub();
|
||||||
static void StoreScriptBaseProgramNum();
|
static void StoreScriptBaseProgramNum();
|
@ -1,9 +1,9 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "ScriptExtension.hpp"
|
||||||
|
#include "Script.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
const char* ScriptExtension::QueryStrings[] = { R"(..)", R"(../)", R"(..\)" };
|
|
||||||
|
|
||||||
std::unordered_map<std::uint16_t, Game::ent_field_t> ScriptExtension::CustomEntityFields;
|
std::unordered_map<std::uint16_t, Game::ent_field_t> ScriptExtension::CustomEntityFields;
|
||||||
std::unordered_map<std::uint16_t, Game::client_fields_s> ScriptExtension::CustomClientFields;
|
std::unordered_map<std::uint16_t, Game::client_fields_s> ScriptExtension::CustomClientFields;
|
||||||
|
|
||||||
@ -13,7 +13,7 @@ namespace Components
|
|||||||
static std::uint16_t fieldOffsetStart = 15; // fields count
|
static std::uint16_t fieldOffsetStart = 15; // fields count
|
||||||
assert((fieldOffsetStart & Game::ENTFIELD_MASK) == Game::ENTFIELD_ENTITY);
|
assert((fieldOffsetStart & Game::ENTFIELD_MASK) == Game::ENTFIELD_ENTITY);
|
||||||
|
|
||||||
ScriptExtension::CustomEntityFields[fieldOffsetStart] = {name, fieldOffsetStart, type, setter, getter};
|
CustomEntityFields[fieldOffsetStart] = {name, fieldOffsetStart, type, setter, getter};
|
||||||
++fieldOffsetStart;
|
++fieldOffsetStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,20 +26,20 @@ namespace Components
|
|||||||
const auto offset = fieldOffsetStart | Game::ENTFIELD_CLIENT; // This is how client field's offset is calculated
|
const auto offset = fieldOffsetStart | Game::ENTFIELD_CLIENT; // This is how client field's offset is calculated
|
||||||
|
|
||||||
// Use 'index' in 'array' as map key. It will be used later in Scr_SetObjectFieldStub
|
// Use 'index' in 'array' as map key. It will be used later in Scr_SetObjectFieldStub
|
||||||
ScriptExtension::CustomClientFields[fieldOffsetStart] = {name, offset, type, setter, getter};
|
CustomClientFields[fieldOffsetStart] = {name, offset, type, setter, getter};
|
||||||
++fieldOffsetStart;
|
++fieldOffsetStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptExtension::GScr_AddFieldsForEntityStub()
|
void ScriptExtension::GScr_AddFieldsForEntityStub()
|
||||||
{
|
{
|
||||||
for (const auto& [offset, field] : ScriptExtension::CustomEntityFields)
|
for (const auto& [offset, field] : CustomEntityFields)
|
||||||
{
|
{
|
||||||
Game::Scr_AddClassField(Game::ClassNum::CLASS_NUM_ENTITY, field.name, field.ofs);
|
Game::Scr_AddClassField(Game::ClassNum::CLASS_NUM_ENTITY, field.name, field.ofs);
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::Hook::Call<void()>(0x4A7CF0)(); // GScr_AddFieldsForClient
|
Utils::Hook::Call<void()>(0x4A7CF0)(); // GScr_AddFieldsForClient
|
||||||
|
|
||||||
for (const auto& [offset, field] : ScriptExtension::CustomClientFields)
|
for (const auto& [offset, field] : CustomClientFields)
|
||||||
{
|
{
|
||||||
Game::Scr_AddClassField(Game::ClassNum::CLASS_NUM_ENTITY, field.name, field.ofs);
|
Game::Scr_AddClassField(Game::ClassNum::CLASS_NUM_ENTITY, field.name, field.ofs);
|
||||||
}
|
}
|
||||||
@ -52,8 +52,8 @@ namespace Components
|
|||||||
{
|
{
|
||||||
const auto entity_offset = static_cast<std::uint16_t>(offset);
|
const auto entity_offset = static_cast<std::uint16_t>(offset);
|
||||||
|
|
||||||
const auto got = ScriptExtension::CustomEntityFields.find(entity_offset);
|
const auto got =CustomEntityFields.find(entity_offset);
|
||||||
if (got != ScriptExtension::CustomEntityFields.end())
|
if (got != CustomEntityFields.end())
|
||||||
{
|
{
|
||||||
got->second.setter(&Game::g_entities[entnum], offset);
|
got->second.setter(&Game::g_entities[entnum], offset);
|
||||||
return 1;
|
return 1;
|
||||||
@ -69,8 +69,8 @@ namespace Components
|
|||||||
{
|
{
|
||||||
const auto client_offset = static_cast<std::uint16_t>(offset);
|
const auto client_offset = static_cast<std::uint16_t>(offset);
|
||||||
|
|
||||||
const auto got = ScriptExtension::CustomClientFields.find(client_offset);
|
const auto got = CustomClientFields.find(client_offset);
|
||||||
if (got != ScriptExtension::CustomClientFields.end())
|
if (got != CustomClientFields.end())
|
||||||
{
|
{
|
||||||
got->second.setter(client, &got->second);
|
got->second.setter(client, &got->second);
|
||||||
return;
|
return;
|
||||||
@ -89,8 +89,8 @@ namespace Components
|
|||||||
{
|
{
|
||||||
const auto client_offset = static_cast<std::uint16_t>(offset & ~Game::ENTFIELD_MASK);
|
const auto client_offset = static_cast<std::uint16_t>(offset & ~Game::ENTFIELD_MASK);
|
||||||
|
|
||||||
const auto got = ScriptExtension::CustomClientFields.find(client_offset);
|
const auto got =CustomClientFields.find(client_offset);
|
||||||
if (got != ScriptExtension::CustomClientFields.end())
|
if (got != CustomClientFields.end())
|
||||||
{
|
{
|
||||||
// Game functions probably don't ever need to use the reference to client_fields_s...
|
// Game functions probably don't ever need to use the reference to client_fields_s...
|
||||||
got->second.getter(Game::g_entities[entnum].client, &got->second);
|
got->second.getter(Game::g_entities[entnum].client, &got->second);
|
||||||
@ -102,8 +102,8 @@ namespace Components
|
|||||||
// Regular entity offsets can be searched directly in our custom handler
|
// Regular entity offsets can be searched directly in our custom handler
|
||||||
const auto entity_offset = static_cast<std::uint16_t>(offset);
|
const auto entity_offset = static_cast<std::uint16_t>(offset);
|
||||||
|
|
||||||
const auto got = ScriptExtension::CustomEntityFields.find(entity_offset);
|
const auto got = CustomEntityFields.find(entity_offset);
|
||||||
if (got != ScriptExtension::CustomEntityFields.end())
|
if (got != CustomEntityFields.end())
|
||||||
{
|
{
|
||||||
got->second.getter(&Game::g_entities[entnum], offset);
|
got->second.getter(&Game::g_entities[entnum], offset);
|
||||||
return;
|
return;
|
||||||
@ -115,125 +115,6 @@ namespace Components
|
|||||||
|
|
||||||
void ScriptExtension::AddFunctions()
|
void ScriptExtension::AddFunctions()
|
||||||
{
|
{
|
||||||
// File functions
|
|
||||||
Script::AddFunction("FileWrite", [] // gsc: FileWrite(<filepath>, <string>, <mode>)
|
|
||||||
{
|
|
||||||
const auto* path = Game::Scr_GetString(0);
|
|
||||||
auto* text = Game::Scr_GetString(1);
|
|
||||||
auto* mode = Game::Scr_GetString(2);
|
|
||||||
|
|
||||||
if (path == nullptr)
|
|
||||||
{
|
|
||||||
Game::Scr_ParamError(0, "^1FileWrite: filepath is not defined!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (text == nullptr || mode == nullptr)
|
|
||||||
{
|
|
||||||
Game::Scr_Error("^1FileWrite: Illegal parameters!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto i = 0u; i < ARRAYSIZE(ScriptExtension::QueryStrings); ++i)
|
|
||||||
{
|
|
||||||
if (std::strstr(path, ScriptExtension::QueryStrings[i]) != nullptr)
|
|
||||||
{
|
|
||||||
Logger::Print("^1FileWrite: directory traversal is not allowed!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode != "append"s && mode != "write"s)
|
|
||||||
{
|
|
||||||
Logger::Print("^3FileWrite: mode not defined or was wrong, defaulting to 'write'\n");
|
|
||||||
mode = "write";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode == "write"s)
|
|
||||||
{
|
|
||||||
FileSystem::FileWriter(path).write(text);
|
|
||||||
}
|
|
||||||
else if (mode == "append"s)
|
|
||||||
{
|
|
||||||
FileSystem::FileWriter(path, true).write(text);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Script::AddFunction("FileRead", [] // gsc: FileRead(<filepath>)
|
|
||||||
{
|
|
||||||
const auto* path = Game::Scr_GetString(0);
|
|
||||||
|
|
||||||
if (path == nullptr)
|
|
||||||
{
|
|
||||||
Game::Scr_ParamError(0, "^1FileRead: filepath is not defined!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto i = 0u; i < ARRAYSIZE(ScriptExtension::QueryStrings); ++i)
|
|
||||||
{
|
|
||||||
if (std::strstr(path, ScriptExtension::QueryStrings[i]) != nullptr)
|
|
||||||
{
|
|
||||||
Logger::Print("^1FileRead: directory traversal is not allowed!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!FileSystem::FileReader(path).exists())
|
|
||||||
{
|
|
||||||
Logger::Print("^1FileRead: file not found!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Game::Scr_AddString(FileSystem::FileReader(path).getBuffer().data());
|
|
||||||
});
|
|
||||||
|
|
||||||
Script::AddFunction("FileExists", [] // gsc: FileExists(<filepath>)
|
|
||||||
{
|
|
||||||
const auto* path = Game::Scr_GetString(0);
|
|
||||||
|
|
||||||
if (path == nullptr)
|
|
||||||
{
|
|
||||||
Game::Scr_ParamError(0, "^1FileExists: filepath is not defined!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto i = 0u; i < ARRAYSIZE(ScriptExtension::QueryStrings); ++i)
|
|
||||||
{
|
|
||||||
if (std::strstr(path, ScriptExtension::QueryStrings[i]) != nullptr)
|
|
||||||
{
|
|
||||||
Logger::Print("^1FileExists: directory traversal is not allowed!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Game::Scr_AddInt(FileSystem::FileReader(path).exists());
|
|
||||||
});
|
|
||||||
|
|
||||||
Script::AddFunction("FileRemove", [] // gsc: FileRemove(<filepath>)
|
|
||||||
{
|
|
||||||
const auto* path = Game::Scr_GetString(0);
|
|
||||||
|
|
||||||
if (path == nullptr)
|
|
||||||
{
|
|
||||||
Game::Scr_ParamError(0, "^1FileRemove: filepath is not defined!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto i = 0u; i < ARRAYSIZE(ScriptExtension::QueryStrings); ++i)
|
|
||||||
{
|
|
||||||
if (std::strstr(path, ScriptExtension::QueryStrings[i]) != nullptr)
|
|
||||||
{
|
|
||||||
Logger::Print("^1FileRemove: directory traversal is not allowed!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto p = std::filesystem::path(path);
|
|
||||||
const auto& folder = p.parent_path().string();
|
|
||||||
const auto& file = p.filename().string();
|
|
||||||
Game::Scr_AddInt(FileSystem::DeleteFile(folder, file));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Misc functions
|
// Misc functions
|
||||||
Script::AddFunction("ToUpper", [] // gsc: ToUpper(<string>)
|
Script::AddFunction("ToUpper", [] // gsc: ToUpper(<string>)
|
||||||
{
|
{
|
||||||
@ -308,14 +189,14 @@ namespace Components
|
|||||||
|
|
||||||
Script::AddFunction("IsArray", [] // gsc: IsArray(<object>)
|
Script::AddFunction("IsArray", [] // gsc: IsArray(<object>)
|
||||||
{
|
{
|
||||||
const auto type = Game::Scr_GetType(0);
|
auto type = Game::Scr_GetType(0);
|
||||||
|
|
||||||
bool result;
|
bool result;
|
||||||
if (type == Game::scrParamType_t::VAR_POINTER)
|
if (type == Game::VAR_POINTER)
|
||||||
{
|
{
|
||||||
const auto ptr_type = Game::Scr_GetPointerType(0);
|
type = Game::Scr_GetPointerType(0);
|
||||||
assert(ptr_type >= Game::FIRST_OBJECT);
|
assert(type >= Game::FIRST_OBJECT);
|
||||||
result = (ptr_type == Game::scrParamType_t::VAR_ARRAY);
|
result = (type == Game::VAR_ARRAY);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -350,6 +231,18 @@ namespace Components
|
|||||||
|
|
||||||
Game::Scr_AddInt(client->ping);
|
Game::Scr_AddInt(client->ping);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Script::AddMethod("SetPing", [](Game::scr_entref_t entref) // gsc: self SetPing(<int>)
|
||||||
|
{
|
||||||
|
auto ping = Game::Scr_GetInt(0);
|
||||||
|
|
||||||
|
ping = std::clamp(ping, 0, 999);
|
||||||
|
|
||||||
|
const auto* ent = Game::GetPlayerEntity(entref);
|
||||||
|
auto* client = Script::GetClient(ent);
|
||||||
|
|
||||||
|
client->ping = static_cast<int16_t>(ping);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptExtension::Scr_TableLookupIStringByRow()
|
void ScriptExtension::Scr_TableLookupIStringByRow()
|
||||||
@ -378,7 +271,7 @@ namespace Components
|
|||||||
|
|
||||||
void ScriptExtension::AddEntityFields()
|
void ScriptExtension::AddEntityFields()
|
||||||
{
|
{
|
||||||
ScriptExtension::AddEntityField("entityflags", Game::fieldtype_t::F_INT,
|
AddEntityField("entityflags", Game::fieldtype_t::F_INT,
|
||||||
[](Game::gentity_s* ent, [[maybe_unused]] int offset)
|
[](Game::gentity_s* ent, [[maybe_unused]] int offset)
|
||||||
{
|
{
|
||||||
ent->flags = Game::Scr_GetInt(0);
|
ent->flags = Game::Scr_GetInt(0);
|
||||||
@ -391,7 +284,7 @@ namespace Components
|
|||||||
|
|
||||||
void ScriptExtension::AddClientFields()
|
void ScriptExtension::AddClientFields()
|
||||||
{
|
{
|
||||||
ScriptExtension::AddClientField("clientflags", Game::fieldtype_t::F_INT,
|
AddClientField("clientflags", Game::fieldtype_t::F_INT,
|
||||||
[](Game::gclient_s* pSelf, [[maybe_unused]] const Game::client_fields_s* pField)
|
[](Game::gclient_s* pSelf, [[maybe_unused]] const Game::client_fields_s* pField)
|
||||||
{
|
{
|
||||||
pSelf->flags = Game::Scr_GetInt(0);
|
pSelf->flags = Game::Scr_GetInt(0);
|
||||||
@ -404,19 +297,19 @@ namespace Components
|
|||||||
|
|
||||||
ScriptExtension::ScriptExtension()
|
ScriptExtension::ScriptExtension()
|
||||||
{
|
{
|
||||||
ScriptExtension::AddFunctions();
|
AddFunctions();
|
||||||
ScriptExtension::AddMethods();
|
AddMethods();
|
||||||
ScriptExtension::AddEntityFields();
|
AddEntityFields();
|
||||||
ScriptExtension::AddClientFields();
|
AddClientFields();
|
||||||
|
|
||||||
// Correct builtin function pointer
|
// Correct builtin function pointer
|
||||||
Utils::Hook::Set<void(*)()>(0x79A90C, ScriptExtension::Scr_TableLookupIStringByRow);
|
Utils::Hook::Set<Game::BuiltinFunction>(0x79A90C, Scr_TableLookupIStringByRow);
|
||||||
|
|
||||||
Utils::Hook(0x4EC721, ScriptExtension::GScr_AddFieldsForEntityStub, HOOK_CALL).install()->quick(); // GScr_AddFieldsForEntity
|
Utils::Hook(0x4EC721, GScr_AddFieldsForEntityStub, HOOK_CALL).install()->quick(); // GScr_AddFieldsForEntity
|
||||||
|
|
||||||
Utils::Hook(0x41BED2, ScriptExtension::Scr_SetObjectFieldStub, HOOK_CALL).install()->quick(); // SetEntityFieldValue
|
Utils::Hook(0x41BED2, Scr_SetObjectFieldStub, HOOK_CALL).install()->quick(); // SetEntityFieldValue
|
||||||
Utils::Hook(0x5FBF01, ScriptExtension::Scr_SetClientFieldStub, HOOK_CALL).install()->quick(); // Scr_SetObjectField
|
Utils::Hook(0x5FBF01, Scr_SetClientFieldStub, HOOK_CALL).install()->quick(); // Scr_SetObjectField
|
||||||
Utils::Hook(0x4FF413, ScriptExtension::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
|
// Fix format string in Scr_RandomFloatRange
|
||||||
Utils::Hook::Set<const char*>(0x5F10C6, "Scr_RandomFloatRange parms: %f %f ");
|
Utils::Hook::Set<const char*>(0x5F10C6, "Scr_RandomFloatRange parms: %f %f ");
|
@ -11,8 +11,6 @@ namespace Components
|
|||||||
static void AddClientField(const char* name, Game::fieldtype_t type, const Game::ScriptCallbackClient& setter, const Game::ScriptCallbackClient& getter);
|
static void AddClientField(const char* name, Game::fieldtype_t type, const Game::ScriptCallbackClient& setter, const Game::ScriptCallbackClient& getter);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const char* QueryStrings[];
|
|
||||||
|
|
||||||
static std::unordered_map<std::uint16_t, Game::ent_field_t> CustomEntityFields;
|
static std::unordered_map<std::uint16_t, Game::ent_field_t> CustomEntityFields;
|
||||||
static std::unordered_map<std::uint16_t, Game::client_fields_s> CustomClientFields;
|
static std::unordered_map<std::uint16_t, Game::client_fields_s> CustomClientFields;
|
||||||
|
|
101
src/Components/Modules/GSC/ScriptStorage.cpp
Normal file
101
src/Components/Modules/GSC/ScriptStorage.cpp
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#include <STDInclude.hpp>
|
||||||
|
#include "ScriptStorage.hpp"
|
||||||
|
#include "Script.hpp"
|
||||||
|
|
||||||
|
namespace Components
|
||||||
|
{
|
||||||
|
std::unordered_map<std::string, std::string> ScriptStorage::Data;
|
||||||
|
|
||||||
|
void ScriptStorage::AddScriptFunctions()
|
||||||
|
{
|
||||||
|
Script::AddFunction("StorageSet", [] // gsc: StorageSet(<str key>, <str data>);
|
||||||
|
{
|
||||||
|
const auto* key = Game::Scr_GetString(0);
|
||||||
|
const auto* value = Game::Scr_GetString(1);
|
||||||
|
|
||||||
|
if (key == nullptr || value == nullptr)
|
||||||
|
{
|
||||||
|
Game::Scr_Error("^1StorageSet: Illegal parameters!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Data.insert_or_assign(key, value);
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddFunction("StorageRemove", [] // gsc: StorageRemove(<str key>);
|
||||||
|
{
|
||||||
|
const auto* key = Game::Scr_GetString(0);
|
||||||
|
|
||||||
|
if (key == nullptr)
|
||||||
|
{
|
||||||
|
Game::Scr_ParamError(0, "^1StorageRemove: Illegal parameter!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Data.contains(key))
|
||||||
|
{
|
||||||
|
Game::Scr_Error(Utils::String::VA("^1StorageRemove: Store does not have key '%s'!\n", key));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Data.erase(key);
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddFunction("StorageGet", [] // gsc: StorageGet(<str key>);
|
||||||
|
{
|
||||||
|
const auto* key = Game::Scr_GetString(0);
|
||||||
|
|
||||||
|
if (key == nullptr)
|
||||||
|
{
|
||||||
|
Game::Scr_ParamError(0, "^1StorageGet: Illegal parameter!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Data.contains(key))
|
||||||
|
{
|
||||||
|
Game::Scr_Error(Utils::String::VA("^1StorageGet: Store does not have key '%s'!\n", key));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& data = Data.at(key);
|
||||||
|
Game::Scr_AddString(data.data());
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddFunction("StorageHas", [] // gsc: StorageHas(<str key>);
|
||||||
|
{
|
||||||
|
const auto* key = Game::Scr_GetString(0);
|
||||||
|
|
||||||
|
if (key == nullptr)
|
||||||
|
{
|
||||||
|
Game::Scr_ParamError(0, "^1StorageHas: Illegal parameter!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::Scr_AddBool(Data.contains(key));
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddFunction("StorageDump", [] // gsc: StorageDump();
|
||||||
|
{
|
||||||
|
if (Data.empty())
|
||||||
|
{
|
||||||
|
Game::Scr_Error("^1StorageDump: ScriptStorage is empty!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const json11::Json json = Data;
|
||||||
|
|
||||||
|
FileSystem::FileWriter("scriptdata/scriptstorage.json").write(json.dump());
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddFunction("StorageClear", [] // gsc: StorageClear();
|
||||||
|
{
|
||||||
|
Data.clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptStorage::ScriptStorage()
|
||||||
|
{
|
||||||
|
AddScriptFunctions();
|
||||||
|
}
|
||||||
|
}
|
15
src/Components/Modules/GSC/ScriptStorage.hpp
Normal file
15
src/Components/Modules/GSC/ScriptStorage.hpp
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Components
|
||||||
|
{
|
||||||
|
class ScriptStorage : public Component
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ScriptStorage();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::unordered_map<std::string, std::string> Data;
|
||||||
|
|
||||||
|
static void AddScriptFunctions();
|
||||||
|
};
|
||||||
|
}
|
@ -78,7 +78,7 @@ namespace Components
|
|||||||
std::string data;
|
std::string data;
|
||||||
for (auto& gametype : gametypes)
|
for (auto& gametype : gametypes)
|
||||||
{
|
{
|
||||||
if (Game::LoadModdableRawfile(0, Utils::String::VA("maps/mp/gametypes/%s.txt", gametype.data())))
|
if (Game::Scr_AddSourceBuffer(nullptr, Utils::String::VA("maps/mp/gametypes/%s.txt", gametype.data()), nullptr, false))
|
||||||
{
|
{
|
||||||
data.append(gametype);
|
data.append(gametype);
|
||||||
data.append("\r\n");
|
data.append("\r\n");
|
||||||
|
@ -9,7 +9,7 @@ namespace Components
|
|||||||
|
|
||||||
void Localization::Set(const std::string& key, const std::string& value)
|
void Localization::Set(const std::string& key, const std::string& value)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(Localization::LocalizeMutex);
|
std::lock_guard _(Localization::LocalizeMutex);
|
||||||
Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator();
|
Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator();
|
||||||
|
|
||||||
if (Localization::LocalizeMap.contains(key))
|
if (Localization::LocalizeMap.contains(key))
|
||||||
@ -50,7 +50,7 @@ namespace Components
|
|||||||
|
|
||||||
Game::LocalizeEntry* entry = nullptr;
|
Game::LocalizeEntry* entry = nullptr;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(Localization::LocalizeMutex);
|
std::lock_guard _(Localization::LocalizeMutex);
|
||||||
|
|
||||||
if (Localization::TempLocalizeMap.contains(key))
|
if (Localization::TempLocalizeMap.contains(key))
|
||||||
{
|
{
|
||||||
@ -77,7 +77,7 @@ namespace Components
|
|||||||
|
|
||||||
void Localization::SetTemp(const std::string& key, const std::string& value)
|
void Localization::SetTemp(const std::string& key, const std::string& value)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(Localization::LocalizeMutex);
|
std::lock_guard _(Localization::LocalizeMutex);
|
||||||
Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator();
|
Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator();
|
||||||
|
|
||||||
if (Localization::TempLocalizeMap.contains(key))
|
if (Localization::TempLocalizeMap.contains(key))
|
||||||
@ -112,7 +112,7 @@ namespace Components
|
|||||||
|
|
||||||
void Localization::ClearTemp()
|
void Localization::ClearTemp()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> _(Localization::LocalizeMutex);
|
std::lock_guard _(Localization::LocalizeMutex);
|
||||||
Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator();
|
Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator();
|
||||||
|
|
||||||
for (auto i = Localization::TempLocalizeMap.begin(); i != Localization::TempLocalizeMap.end(); ++i)
|
for (auto i = Localization::TempLocalizeMap.begin(); i != Localization::TempLocalizeMap.end(); ++i)
|
||||||
@ -165,6 +165,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
static const char* staff[] =
|
static const char* staff[] =
|
||||||
{
|
{
|
||||||
|
"Snake",
|
||||||
"/dev/../",
|
"/dev/../",
|
||||||
"/dev/console",
|
"/dev/console",
|
||||||
"/dev/full",
|
"/dev/full",
|
||||||
@ -172,8 +173,14 @@ namespace Components
|
|||||||
"/dev/sr0",
|
"/dev/sr0",
|
||||||
"/dev/tty0",
|
"/dev/tty0",
|
||||||
"/dev/urandom",
|
"/dev/urandom",
|
||||||
"Snake",
|
"Dss0",
|
||||||
"lsb_release -a"
|
"FutureRave",
|
||||||
|
"H3X1C",
|
||||||
|
"Homura",
|
||||||
|
"Laupetin",
|
||||||
|
"Louvenarde",
|
||||||
|
"lsb_release -a",
|
||||||
|
"quaK",
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char* contributors[] =
|
static const char* contributors[] =
|
||||||
@ -186,17 +193,12 @@ namespace Components
|
|||||||
"Dasfonia",
|
"Dasfonia",
|
||||||
"Deity",
|
"Deity",
|
||||||
"Dizzy",
|
"Dizzy",
|
||||||
"Dss0",
|
|
||||||
"FutureRave",
|
|
||||||
"H3X1C",
|
|
||||||
"HardNougat",
|
"HardNougat",
|
||||||
"Homura",
|
|
||||||
"INeedGames",
|
"INeedGames",
|
||||||
|
"JTAG",
|
||||||
"Killera",
|
"Killera",
|
||||||
"Lithium",
|
"Lithium",
|
||||||
"Louvenarde",
|
|
||||||
"OneFourOne",
|
"OneFourOne",
|
||||||
"quaK",
|
|
||||||
"RaidMax",
|
"RaidMax",
|
||||||
"Revo",
|
"Revo",
|
||||||
"RezTech",
|
"RezTech",
|
||||||
@ -204,7 +206,7 @@ namespace Components
|
|||||||
"Slykuiper",
|
"Slykuiper",
|
||||||
"st0rm",
|
"st0rm",
|
||||||
"VVLNT",
|
"VVLNT",
|
||||||
"X3RX35"
|
"X3RX35",
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char* specials[] =
|
static const char* specials[] =
|
||||||
@ -219,7 +221,7 @@ namespace Components
|
|||||||
|
|
||||||
std::string credits = "^2The IW4x Team:^7\n";
|
std::string credits = "^2The IW4x Team:^7\n";
|
||||||
|
|
||||||
for (int i = 0; i < ARRAYSIZE(staff); ++i)
|
for (std::size_t i = 0; i < ARRAYSIZE(staff); ++i)
|
||||||
{
|
{
|
||||||
credits.append(staff[i]);
|
credits.append(staff[i]);
|
||||||
credits.append("\n");
|
credits.append("\n");
|
||||||
@ -227,7 +229,7 @@ namespace Components
|
|||||||
|
|
||||||
credits.append("\n^3Contributors:^7\n");
|
credits.append("\n^3Contributors:^7\n");
|
||||||
|
|
||||||
for (int i = 0; i < ARRAYSIZE(contributors); ++i)
|
for (std::size_t i = 0; i < ARRAYSIZE(contributors); ++i)
|
||||||
{
|
{
|
||||||
credits.append(contributors[i]);
|
credits.append(contributors[i]);
|
||||||
credits.append("\n");
|
credits.append("\n");
|
||||||
@ -235,7 +237,7 @@ namespace Components
|
|||||||
|
|
||||||
credits.append("\n^5Special thanks to:^7\n");
|
credits.append("\n^5Special thanks to:^7\n");
|
||||||
|
|
||||||
for (int i = 0; i < ARRAYSIZE(specials); ++i)
|
for (std::size_t i = 0; i < ARRAYSIZE(specials); ++i)
|
||||||
{
|
{
|
||||||
credits.append(specials[i]);
|
credits.append(specials[i]);
|
||||||
credits.append("\n");
|
credits.append("\n");
|
||||||
@ -254,7 +256,7 @@ namespace Components
|
|||||||
AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_LOCALIZE_ENTRY, [](Game::XAssetType, const std::string& filename)
|
AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_LOCALIZE_ENTRY, [](Game::XAssetType, const std::string& filename)
|
||||||
{
|
{
|
||||||
Game::XAssetHeader header = { nullptr };
|
Game::XAssetHeader header = { nullptr };
|
||||||
std::lock_guard<std::recursive_mutex> _(Localization::LocalizeMutex);
|
std::lock_guard _(Localization::LocalizeMutex);
|
||||||
|
|
||||||
if (Localization::TempLocalizeMap.contains(filename))
|
if (Localization::TempLocalizeMap.contains(filename))
|
||||||
{
|
{
|
||||||
@ -277,7 +279,7 @@ namespace Components
|
|||||||
// Overwrite SetString
|
// Overwrite SetString
|
||||||
Utils::Hook(0x4CE5EE, Localization::SetStringStub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x4CE5EE, Localization::SetStringStub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
Localization::UseLocalization = Dvar::Register<bool>("ui_localize", true, Game::dvar_flag::DVAR_NONE, "Use localization strings");
|
Localization::UseLocalization = Dvar::Register<bool>("ui_localize", true, Game::DVAR_NONE, "Use localization strings");
|
||||||
|
|
||||||
// Generate localized entries for custom classes above 10
|
// Generate localized entries for custom classes above 10
|
||||||
AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader asset, const std::string& name, bool* /*restrict*/)
|
AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader asset, const std::string& name, bool* /*restrict*/)
|
||||||
|
@ -12,7 +12,7 @@ namespace Components
|
|||||||
return (IsWindow(Console::GetWindow()) != FALSE || (Dedicated::IsEnabled() && !Flags::HasFlag("console")));
|
return (IsWindow(Console::GetWindow()) != FALSE || (Dedicated::IsEnabled() && !Flags::HasFlag("console")));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::PrintStub(const int channel, const char* message, ...)
|
void Logger::Print_Stub(const int channel, const char* message, ...)
|
||||||
{
|
{
|
||||||
char buf[4096] = {0};
|
char buf[4096] = {0};
|
||||||
|
|
||||||
@ -26,35 +26,44 @@ namespace Components
|
|||||||
|
|
||||||
void Logger::MessagePrint(const int channel, const std::string& msg)
|
void Logger::MessagePrint(const int channel, const std::string& msg)
|
||||||
{
|
{
|
||||||
|
std::string out = msg;
|
||||||
|
|
||||||
|
// Filter out coloured strings
|
||||||
|
if (out[0] == '^' && out[1] != '\0')
|
||||||
|
{
|
||||||
|
out = out.substr(2);
|
||||||
|
}
|
||||||
|
|
||||||
if (Flags::HasFlag("stdout") || Loader::IsPerformingUnitTests())
|
if (Flags::HasFlag("stdout") || Loader::IsPerformingUnitTests())
|
||||||
{
|
{
|
||||||
printf("%s", msg.data());
|
printf("%s", out.data());
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Logger::IsConsoleReady())
|
if (!Logger::IsConsoleReady())
|
||||||
{
|
{
|
||||||
OutputDebugStringA(msg.data());
|
OutputDebugStringA(out.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Game::Sys_IsMainThread())
|
if (!Game::Sys_IsMainThread())
|
||||||
{
|
{
|
||||||
Logger::EnqueueMessage(msg);
|
Logger::EnqueueMessage(out);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Game::Com_PrintMessage(channel, msg.data(), 0);
|
Game::Com_PrintMessage(channel, out.data(), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::DebugInternal(std::string_view fmt, std::format_args&& args, [[maybe_unused]] const std::source_location& loc)
|
void Logger::DebugInternal(std::string_view fmt, std::format_args&& args, [[maybe_unused]] const std::source_location& loc)
|
||||||
{
|
{
|
||||||
const auto msg = std::vformat(fmt, args);
|
|
||||||
#ifdef LOGGER_TRACE
|
#ifdef LOGGER_TRACE
|
||||||
|
const auto msg = std::vformat(fmt, args);
|
||||||
const auto out = std::format("Debug:\n {}\nFile: {}\nLine: {}\n", msg, loc.file_name(), loc.line());
|
const auto out = std::format("Debug:\n {}\nFile: {}\nLine: {}\n", msg, loc.file_name(), loc.line());
|
||||||
#else
|
#else
|
||||||
const auto out = std::format("Debug:\n {}\n", msg);
|
const auto msg = std::vformat(fmt, args);
|
||||||
|
const auto out = std::format("^2{}\n", msg);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Logger::MessagePrint(Game::CON_CHANNEL_DONT_FILTER, out);
|
Logger::MessagePrint(Game::CON_CHANNEL_DONT_FILTER, out);
|
||||||
@ -144,34 +153,40 @@ namespace Components
|
|||||||
|
|
||||||
void Logger::NetworkLog(const char* data, bool gLog)
|
void Logger::NetworkLog(const char* data, bool gLog)
|
||||||
{
|
{
|
||||||
if (!data) return;
|
if (data == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string buffer(data);
|
|
||||||
for (const auto& addr : Logger::LoggingAddresses[gLog & 1])
|
for (const auto& addr : Logger::LoggingAddresses[gLog & 1])
|
||||||
{
|
{
|
||||||
Network::SendCommand(addr, "print", buffer);
|
Network::SendCommand(addr, "print", data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(naked) void Logger::GameLogStub()
|
void Logger::G_LogPrintf_Hk(const char* fmt, ...)
|
||||||
{
|
{
|
||||||
__asm
|
char string[1024]{};
|
||||||
|
char string2[1024]{};
|
||||||
|
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vsnprintf_s(string2, _TRUNCATE, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
const auto time = Game::level->time / 1000;
|
||||||
|
const auto len = _snprintf_s(string, _TRUNCATE, "%3i:%i%i %s", time / 60, time % 60 / 10, time % 60 % 10, string2);
|
||||||
|
|
||||||
|
if (Game::level->logFile != nullptr)
|
||||||
{
|
{
|
||||||
pushad
|
Game::FS_Write(string, len, reinterpret_cast<int>(Game::level->logFile));
|
||||||
|
|
||||||
push 1
|
|
||||||
push [esp + 28h]
|
|
||||||
call Logger::NetworkLog
|
|
||||||
add esp, 8h
|
|
||||||
|
|
||||||
popad
|
|
||||||
|
|
||||||
push 4576C0h
|
|
||||||
retn
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(naked) void Logger::PrintMessageStub()
|
// Allow the network log to run even if logFile was not opened
|
||||||
|
Logger::NetworkLog(string, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(naked) void Logger::PrintMessage_Stub()
|
||||||
{
|
{
|
||||||
__asm
|
__asm
|
||||||
{
|
{
|
||||||
@ -222,7 +237,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(naked) void Logger::BuildOSPathStub()
|
__declspec(naked) void Logger::BuildOSPath_Stub()
|
||||||
{
|
{
|
||||||
__asm
|
__asm
|
||||||
{
|
{
|
||||||
@ -265,7 +280,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
if (params->size() < 2) return;
|
if (params->size() < 2) return;
|
||||||
|
|
||||||
int num = atoi(params->get(1));
|
const auto num = 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 (Utils::String::VA("%i", num) == std::string(params->get(1)) && static_cast<unsigned int>(num) < Logger::LoggingAddresses[0].size())
|
||||||
{
|
{
|
||||||
auto addr = Logger::LoggingAddresses[0].begin() + num;
|
auto addr = Logger::LoggingAddresses[0].begin() + num;
|
||||||
@ -296,7 +311,7 @@ namespace Components
|
|||||||
|
|
||||||
for (unsigned int i = 0; i < Logger::LoggingAddresses[0].size(); ++i)
|
for (unsigned int i = 0; i < Logger::LoggingAddresses[0].size(); ++i)
|
||||||
{
|
{
|
||||||
Logger::Print("{}: {}\n", i, Logger::LoggingAddresses[0][i].getCString());
|
Logger::Print("#{:03d}: {}\n", i, Logger::LoggingAddresses[0][i].getCString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -316,7 +331,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
if (params->size() < 2) return;
|
if (params->size() < 2) return;
|
||||||
|
|
||||||
int 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[1].size())
|
if (Utils::String::VA("%i", num) == std::string(params->get(1)) && static_cast<unsigned int>(num) < Logger::LoggingAddresses[1].size())
|
||||||
{
|
{
|
||||||
const auto addr = Logger::LoggingAddresses[1].begin() + num;
|
const auto addr = Logger::LoggingAddresses[1].begin() + num;
|
||||||
@ -347,29 +362,29 @@ namespace Components
|
|||||||
|
|
||||||
for (std::size_t i = 0; i < Logger::LoggingAddresses[1].size(); ++i)
|
for (std::size_t i = 0; i < Logger::LoggingAddresses[1].size(); ++i)
|
||||||
{
|
{
|
||||||
Logger::Print("{}: {}\n", i, Logger::LoggingAddresses[1][i].getCString());
|
Logger::Print("#{:03d}: {}\n", i, Logger::LoggingAddresses[1][i].getCString());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::Logger()
|
Logger::Logger()
|
||||||
{
|
{
|
||||||
Dvar::Register<bool>("iw4x_onelog", false, Game::dvar_flag::DVAR_LATCH | Game::dvar_flag::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::BuildOSPathStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x642139, Logger::BuildOSPath_Stub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
Logger::PipeOutput(nullptr);
|
Logger::PipeOutput(nullptr);
|
||||||
|
|
||||||
Scheduler::Loop(Logger::Frame, Scheduler::Pipeline::SERVER);
|
Scheduler::Loop(Logger::Frame, Scheduler::Pipeline::SERVER);
|
||||||
|
|
||||||
Utils::Hook(0x4B0218, Logger::GameLogStub, HOOK_CALL).install()->quick();
|
Utils::Hook(Game::G_LogPrintf, Logger::G_LogPrintf_Hk, HOOK_JUMP).install()->quick();
|
||||||
Utils::Hook(Game::Com_PrintMessage, Logger::PrintMessageStub, 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::PrintStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(Game::Com_Printf, Logger::Print_Stub, HOOK_JUMP).install()->quick();
|
||||||
}
|
}
|
||||||
|
|
||||||
Scheduler::OnGameInitialized(Logger::AddServerCommands, Scheduler::Pipeline::SERVER);
|
Events::OnSVInit(Logger::AddServerCommands);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::~Logger()
|
Logger::~Logger()
|
||||||
|
@ -10,13 +10,12 @@ namespace Components
|
|||||||
|
|
||||||
static bool IsConsoleReady();
|
static bool IsConsoleReady();
|
||||||
|
|
||||||
static void PrintStub(int channel, const char* message, ...);
|
static void Print_Stub(int channel, const char* message, ...);
|
||||||
|
|
||||||
static void PipeOutput(void(*callback)(const std::string&));
|
static void PipeOutput(void(*callback)(const std::string&));
|
||||||
|
|
||||||
static void Flush();
|
static void Flush();
|
||||||
|
|
||||||
static void MessagePrint(int channel, const std::string& msg);
|
|
||||||
static void PrintInternal(int channel, std::string_view fmt, std::format_args&& args);
|
static void PrintInternal(int channel, 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, std::string_view fmt, std::format_args&& args);
|
||||||
static void PrintErrorInternal(int channel, std::string_view fmt, std::format_args&& args);
|
static void PrintErrorInternal(int channel, std::string_view fmt, std::format_args&& args);
|
||||||
@ -99,13 +98,14 @@ namespace Components
|
|||||||
static std::vector<Network::Address> LoggingAddresses[2];
|
static std::vector<Network::Address> LoggingAddresses[2];
|
||||||
static void(*PipeCallback)(const std::string&);
|
static void(*PipeCallback)(const std::string&);
|
||||||
|
|
||||||
|
static void MessagePrint(int channel, const std::string& msg);
|
||||||
static void Frame();
|
static void Frame();
|
||||||
static void GameLogStub();
|
static void G_LogPrintf_Hk(const char* fmt, ...);
|
||||||
static void PrintMessageStub();
|
static void PrintMessage_Stub();
|
||||||
static void PrintMessagePipe(const char* data);
|
static void PrintMessagePipe(const char* data);
|
||||||
static void EnqueueMessage(const std::string& message);
|
static void EnqueueMessage(const std::string& message);
|
||||||
|
|
||||||
static void BuildOSPathStub();
|
static void BuildOSPath_Stub();
|
||||||
static void RedirectOSPath(const char* file, char* folder);
|
static void RedirectOSPath(const char* file, char* folder);
|
||||||
|
|
||||||
static void NetworkLog(const char* data, bool gLog);
|
static void NetworkLog(const char* data, bool gLog);
|
||||||
|
@ -6,6 +6,7 @@ namespace Components
|
|||||||
Dvar::Var MapRotation::SVDontRotate;
|
Dvar::Var MapRotation::SVDontRotate;
|
||||||
|
|
||||||
Game::dvar_t** MapRotation::SVMapRotation = reinterpret_cast<Game::dvar_t**>(0x62C7C44);
|
Game::dvar_t** MapRotation::SVMapRotation = reinterpret_cast<Game::dvar_t**>(0x62C7C44);
|
||||||
|
Game::dvar_t** MapRotation::SVMapRotationCurrent = reinterpret_cast<Game::dvar_t**>(0x2098DF0);
|
||||||
Game::dvar_t** MapRotation::SVMapname = reinterpret_cast<Game::dvar_t**>(0x2098DDC);
|
Game::dvar_t** MapRotation::SVMapname = reinterpret_cast<Game::dvar_t**>(0x2098DDC);
|
||||||
|
|
||||||
MapRotation::RotationData MapRotation::DedicatedRotation;
|
MapRotation::RotationData MapRotation::DedicatedRotation;
|
||||||
@ -98,18 +99,18 @@ namespace Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loaded = true;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
DedicatedRotation.parse(data);
|
DedicatedRotation.parse(data);
|
||||||
}
|
}
|
||||||
catch (const std::exception& ex)
|
catch (const std::exception& ex)
|
||||||
{
|
{
|
||||||
Logger::Print(Game::CON_CHANNEL_SERVER, "{}: sv_mapRotation contains invalid data!\n", ex.what());
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}: {} contains invalid data!\n", ex.what(), (*SVMapRotation)->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::Debug("DedicatedRotation size after parsing is '{}'", DedicatedRotation.getEntriesSize());
|
Logger::Debug("DedicatedRotation size after parsing is '{}'", DedicatedRotation.getEntriesSize());
|
||||||
|
|
||||||
loaded = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MapRotation::AddMapRotationCommands()
|
void MapRotation::AddMapRotationCommands()
|
||||||
@ -155,6 +156,26 @@ namespace Components
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MapRotation::ApplyMap(const std::string& map)
|
||||||
|
{
|
||||||
|
assert(!map.empty());
|
||||||
|
|
||||||
|
if (Dvar::Var("sv_cheats").get<bool>())
|
||||||
|
{
|
||||||
|
Command::Execute(Utils::String::VA("devmap %s", map.data()), true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Command::Execute(Utils::String::VA("map %s", map.data()), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MapRotation::ApplyGametype(const std::string& gametype)
|
||||||
|
{
|
||||||
|
assert(!gametype.empty());
|
||||||
|
Dvar::Var("g_gametype").set(gametype.data());
|
||||||
|
}
|
||||||
|
|
||||||
void MapRotation::RestartCurrentMap()
|
void MapRotation::RestartCurrentMap()
|
||||||
{
|
{
|
||||||
std::string svMapname = (*SVMapname)->current.string;
|
std::string svMapname = (*SVMapname)->current.string;
|
||||||
@ -165,30 +186,25 @@ namespace Components
|
|||||||
svMapname = "mp_afghan";
|
svMapname = "mp_afghan";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Dvar::Var("sv_cheats").get<bool>())
|
ApplyMap(svMapname);
|
||||||
{
|
|
||||||
Command::Execute(Utils::String::VA("map %s", svMapname.data()), true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Command::Execute(Utils::String::VA("devmap %s", svMapname.data()), true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MapRotation::ApplyMapRotation()
|
void MapRotation::ApplyRotation(RotationData& rotation)
|
||||||
{
|
{
|
||||||
|
assert(rotation.getEntriesSize() != 0);
|
||||||
|
|
||||||
// Continue to apply gametype until a map is found
|
// Continue to apply gametype until a map is found
|
||||||
auto foundMap = false;
|
auto foundMap = false;
|
||||||
|
|
||||||
std::size_t i = 0;
|
std::size_t i = 0;
|
||||||
while (!foundMap && i < DedicatedRotation.getEntriesSize())
|
while (!foundMap && i < rotation.getEntriesSize())
|
||||||
{
|
{
|
||||||
const auto& entry = DedicatedRotation.getNextEntry();
|
const auto& entry = rotation.getNextEntry();
|
||||||
|
|
||||||
if (entry.first == "map")
|
if (entry.first == "map")
|
||||||
{
|
{
|
||||||
Logger::Debug("Loading new map: '{}'", entry.second);
|
Logger::Debug("Loading new map: '{}'", entry.second);
|
||||||
Command::Execute(Utils::String::VA("map %s", entry.second.data()), true);
|
ApplyMap(entry.second);
|
||||||
|
|
||||||
// Map was found so we exit the loop
|
// Map was found so we exit the loop
|
||||||
foundMap = true;
|
foundMap = true;
|
||||||
@ -196,13 +212,43 @@ namespace Components
|
|||||||
else if (entry.first == "gametype")
|
else if (entry.first == "gametype")
|
||||||
{
|
{
|
||||||
Logger::Debug("Applying new gametype: '{}'", entry.second);
|
Logger::Debug("Applying new gametype: '{}'", entry.second);
|
||||||
Dvar::Var("g_gametype").set(entry.second);
|
ApplyGametype(entry.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MapRotation::ApplyMapRotationCurrent(const std::string& data)
|
||||||
|
{
|
||||||
|
assert(!data.empty());
|
||||||
|
|
||||||
|
// Ook, ook, eek
|
||||||
|
Logger::Warning(Game::CON_CHANNEL_SERVER, "You are using deprecated {}", (*SVMapRotationCurrent)->name);
|
||||||
|
|
||||||
|
RotationData rotationCurrent;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Logger::Debug("Parsing {}", (*SVMapRotationCurrent)->name);
|
||||||
|
rotationCurrent.parse(data);
|
||||||
|
}
|
||||||
|
catch (const std::exception& ex)
|
||||||
|
{
|
||||||
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}: {} contains invalid data!\n", ex.what(), (*SVMapRotationCurrent)->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::Dvar_SetString(*SVMapRotationCurrent, "");
|
||||||
|
|
||||||
|
if (rotationCurrent.getEntriesSize() == 0)
|
||||||
|
{
|
||||||
|
Logger::Print(Game::CON_CHANNEL_SERVER, "{} is empty or contains invalid data. Restarting map\n", (*SVMapRotationCurrent)->name);
|
||||||
|
RestartCurrentMap();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplyRotation(rotationCurrent);
|
||||||
|
}
|
||||||
|
|
||||||
void MapRotation::RandomizeMapRotation()
|
void MapRotation::RandomizeMapRotation()
|
||||||
{
|
{
|
||||||
if (SVRandomMapRotation.get<bool>())
|
if (SVRandomMapRotation.get<bool>())
|
||||||
@ -224,8 +270,17 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
Logger::Print(Game::CON_CHANNEL_SERVER, "Rotating map...\n");
|
Logger::Print(Game::CON_CHANNEL_SERVER, "Rotating map...\n");
|
||||||
const std::string mapRotation = (*SVMapRotation)->current.string;
|
|
||||||
|
|
||||||
|
// This takes priority because of backwards compatibility
|
||||||
|
const std::string mapRotationCurrent = (*SVMapRotationCurrent)->current.string;
|
||||||
|
if (!mapRotationCurrent.empty())
|
||||||
|
{
|
||||||
|
Logger::Debug("Applying {}", (*SVMapRotationCurrent)->name);
|
||||||
|
ApplyMapRotationCurrent(mapRotationCurrent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string mapRotation = (*SVMapRotation)->current.string;
|
||||||
// People may have sv_mapRotation empty because they only use 'addMap' or 'addGametype'
|
// People may have sv_mapRotation empty because they only use 'addMap' or 'addGametype'
|
||||||
if (!mapRotation.empty())
|
if (!mapRotation.empty())
|
||||||
{
|
{
|
||||||
@ -235,14 +290,14 @@ namespace Components
|
|||||||
|
|
||||||
if (DedicatedRotation.getEntriesSize() == 0)
|
if (DedicatedRotation.getEntriesSize() == 0)
|
||||||
{
|
{
|
||||||
Logger::Print(Game::CON_CHANNEL_SERVER, "sv_mapRotation is empty or contains invalid data. Restarting map\n");
|
Logger::Print(Game::CON_CHANNEL_SERVER, "{} is empty or contains invalid data. Restarting map\n", (*SVMapRotation)->name);
|
||||||
RestartCurrentMap();
|
RestartCurrentMap();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RandomizeMapRotation();
|
RandomizeMapRotation();
|
||||||
|
|
||||||
ApplyMapRotation();
|
ApplyRotation(DedicatedRotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
MapRotation::MapRotation()
|
MapRotation::MapRotation()
|
||||||
@ -251,9 +306,9 @@ namespace Components
|
|||||||
Utils::Hook::Set<void(*)()>(0x4152E8, SV_MapRotate_f);
|
Utils::Hook::Set<void(*)()>(0x4152E8, SV_MapRotate_f);
|
||||||
|
|
||||||
SVRandomMapRotation = Dvar::Register<bool>("sv_randomMapRotation", false,
|
SVRandomMapRotation = Dvar::Register<bool>("sv_randomMapRotation", false,
|
||||||
Game::dvar_flag::DVAR_ARCHIVE, "Randomize map rotation when true");
|
Game::DVAR_ARCHIVE, "Randomize map rotation when true");
|
||||||
SVDontRotate = Dvar::Register<bool>("sv_dontRotate", false,
|
SVDontRotate = Dvar::Register<bool>("sv_dontRotate", false,
|
||||||
Game::dvar_flag::DVAR_NONE, "Do not perform map rotation");
|
Game::DVAR_NONE, "Do not perform map rotation");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MapRotation::unitTest()
|
bool MapRotation::unitTest()
|
||||||
|
@ -47,6 +47,7 @@ namespace Components
|
|||||||
static Dvar::Var SVDontRotate;
|
static Dvar::Var SVDontRotate;
|
||||||
// Game Dvars
|
// Game Dvars
|
||||||
static Game::dvar_t** SVMapRotation;
|
static Game::dvar_t** SVMapRotation;
|
||||||
|
static Game::dvar_t** SVMapRotationCurrent;
|
||||||
static Game::dvar_t** SVMapname;
|
static Game::dvar_t** SVMapname;
|
||||||
|
|
||||||
// Holds the parsed data from sv_mapRotation
|
// Holds the parsed data from sv_mapRotation
|
||||||
@ -58,8 +59,11 @@ namespace Components
|
|||||||
static void AddMapRotationCommands();
|
static void AddMapRotationCommands();
|
||||||
|
|
||||||
static bool ShouldRotate();
|
static bool ShouldRotate();
|
||||||
|
static void ApplyMap(const std::string& map);
|
||||||
|
static void ApplyGametype(const std::string& gametype);
|
||||||
static void RestartCurrentMap();
|
static void RestartCurrentMap();
|
||||||
static void ApplyMapRotation();
|
static void ApplyRotation(RotationData& rotation);
|
||||||
|
static void ApplyMapRotationCurrent(const std::string& data);
|
||||||
static void RandomizeMapRotation();
|
static void RandomizeMapRotation();
|
||||||
|
|
||||||
static void SV_MapRotate_f();
|
static void SV_MapRotate_f();
|
||||||
|
@ -96,7 +96,7 @@ namespace Components
|
|||||||
|
|
||||||
const char* Maps::LoadArenaFileStub(const char* name, char* buffer, int size)
|
const char* Maps::LoadArenaFileStub(const char* name, char* buffer, int size)
|
||||||
{
|
{
|
||||||
std::string data = Game::LoadModdableRawfile(0, name);
|
std::string data = Game::Scr_AddSourceBuffer(nullptr, name, nullptr, false);
|
||||||
|
|
||||||
if(Maps::UserMap.isValid())
|
if(Maps::UserMap.isValid())
|
||||||
{
|
{
|
||||||
@ -130,27 +130,17 @@ namespace Components
|
|||||||
|
|
||||||
Maps::SPMap = false;
|
Maps::SPMap = false;
|
||||||
Maps::CurrentMainZone = zoneInfo->name;
|
Maps::CurrentMainZone = zoneInfo->name;
|
||||||
|
|
||||||
Maps::CurrentDependencies.clear();
|
Maps::CurrentDependencies.clear();
|
||||||
for (auto i = Maps::DependencyList.begin(); i != Maps::DependencyList.end(); ++i)
|
|
||||||
{
|
|
||||||
if (std::regex_match(zoneInfo->name, std::regex(i->first)))
|
|
||||||
{
|
|
||||||
if (std::find(Maps::CurrentDependencies.begin(), Maps::CurrentDependencies.end(), i->second) == Maps::CurrentDependencies.end())
|
|
||||||
{
|
|
||||||
Maps::CurrentDependencies.push_back(i->second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Utils::Memory::Allocator allocator;
|
auto dependencies = GetDependenciesForMap(zoneInfo->name);
|
||||||
auto teams = Maps::GetTeamsForMap(Maps::CurrentMainZone);
|
|
||||||
|
|
||||||
auto dependencies = Maps::GetDependenciesForMap(Maps::CurrentMainZone);
|
|
||||||
Utils::Merge(&Maps::CurrentDependencies, dependencies.data(), dependencies.size());
|
|
||||||
|
|
||||||
std::vector<Game::XZoneInfo> data;
|
std::vector<Game::XZoneInfo> data;
|
||||||
Utils::Merge(&data, zoneInfo, zoneCount);
|
Utils::Merge(&data, zoneInfo, zoneCount);
|
||||||
|
Utils::Memory::Allocator allocator;
|
||||||
|
|
||||||
|
if (dependencies.requiresTeamZones)
|
||||||
|
{
|
||||||
|
auto teams = dependencies.requiredTeams;
|
||||||
|
|
||||||
Game::XZoneInfo team;
|
Game::XZoneInfo team;
|
||||||
team.allocFlags = zoneInfo->allocFlags;
|
team.allocFlags = zoneInfo->allocFlags;
|
||||||
@ -161,7 +151,9 @@ namespace Components
|
|||||||
|
|
||||||
team.name = allocator.duplicateString(Utils::String::VA("iw4x_team_%s", teams.second.data()));
|
team.name = allocator.duplicateString(Utils::String::VA("iw4x_team_%s", teams.second.data()));
|
||||||
data.push_back(team);
|
data.push_back(team);
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils::Merge(&Maps::CurrentDependencies, dependencies.requiredMaps.data(), dependencies.requiredMaps.size());
|
||||||
for (unsigned int i = 0; i < Maps::CurrentDependencies.size(); ++i)
|
for (unsigned int i = 0; i < Maps::CurrentDependencies.size(); ++i)
|
||||||
{
|
{
|
||||||
Game::XZoneInfo info;
|
Game::XZoneInfo info;
|
||||||
@ -255,7 +247,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::string mapEntities;
|
static std::string mapEntities;
|
||||||
FileSystem::File ents(name + ".ents");
|
FileSystem::File ents(name + ".ents", Game::FS_THREAD_DATABASE);
|
||||||
if (ents.exists())
|
if (ents.exists())
|
||||||
{
|
{
|
||||||
mapEntities = ents.getBuffer();
|
mapEntities = ents.getBuffer();
|
||||||
@ -336,66 +328,49 @@ namespace Components
|
|||||||
Maps::SPMap = true;
|
Maps::SPMap = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Maps::AddDependency(const std::string& expression, const std::string& zone)
|
|
||||||
{
|
|
||||||
// Test expression before adding it
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::regex _(expression);
|
|
||||||
}
|
|
||||||
catch (const std::regex_error ex)
|
|
||||||
{
|
|
||||||
MessageBoxA(nullptr, Utils::String::VA("Invalid regular expression: %s", expression.data()), "Warning", MB_ICONEXCLAMATION);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Maps::DependencyList.push_back({expression, zone});
|
|
||||||
}
|
|
||||||
|
|
||||||
int Maps::IgnoreEntityStub(const char* entity)
|
int Maps::IgnoreEntityStub(const char* entity)
|
||||||
{
|
{
|
||||||
return (Utils::String::StartsWith(entity, "dyn_") || Utils::String::StartsWith(entity, "node_") || Utils::String::StartsWith(entity, "actor_"));
|
return (Utils::String::StartsWith(entity, "dyn_") || Utils::String::StartsWith(entity, "node_") || Utils::String::StartsWith(entity, "actor_"));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> Maps::GetDependenciesForMap(const std::string& map)
|
Maps::MapDependencies Maps::GetDependenciesForMap(const std::string& map)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < *Game::arenaCount; ++i)
|
std::string teamAxis = "opforce_composite";
|
||||||
{
|
std::string teamAllies = "us_army";
|
||||||
Game::newMapArena_t* arena = &ArenaLength::NewArenas[i];
|
|
||||||
if (arena->mapName == map)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < ARRAYSIZE(arena->keys); ++j)
|
|
||||||
{
|
|
||||||
if (arena->keys[j] == "dependency"s)
|
|
||||||
{
|
|
||||||
return Utils::String::Split(arena->values[j], ' ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
Maps::MapDependencies dependencies{};
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<std::string, std::string> Maps::GetTeamsForMap(const std::string& map)
|
// True by default - cause some maps won't have an arenafile entry
|
||||||
{
|
dependencies.requiresTeamZones = true;
|
||||||
std::string team_axis = "opforce_composite";
|
|
||||||
std::string team_allies = "us_army";
|
|
||||||
|
|
||||||
for (int i = 0; i < *Game::arenaCount; ++i)
|
for (int i = 0; i < *Game::arenaCount; ++i)
|
||||||
{
|
{
|
||||||
Game::newMapArena_t* arena = &ArenaLength::NewArenas[i];
|
Game::newMapArena_t* arena = &ArenaLength::NewArenas[i];
|
||||||
if (arena->mapName == map)
|
if (arena->mapName == map)
|
||||||
{
|
{
|
||||||
for (int j = 0; j < ARRAYSIZE(arena->keys); ++j)
|
// If it's in the arena file, surely it's a vanilla map that doesn't need teams...
|
||||||
|
dependencies.requiresTeamZones = false;
|
||||||
|
|
||||||
|
for (std::size_t j = 0; j < std::extent_v<decltype(Game::newMapArena_t::keys)>; ++j)
|
||||||
{
|
{
|
||||||
if (arena->keys[j] == "allieschar"s)
|
const auto* key = arena->keys[j];
|
||||||
|
const auto* value = arena->values[j];
|
||||||
|
if (key == "dependency"s)
|
||||||
{
|
{
|
||||||
team_allies = arena->values[j];
|
dependencies.requiredMaps = Utils::String::Split(arena->values[j], ' ');
|
||||||
}
|
}
|
||||||
else if (arena->keys[j] == "axischar"s)
|
else if (key == "allieschar"s)
|
||||||
{
|
{
|
||||||
team_axis = arena->values[j];
|
teamAllies = value;
|
||||||
|
}
|
||||||
|
else if (key == "axischar"s)
|
||||||
|
{
|
||||||
|
teamAxis = value;
|
||||||
|
}
|
||||||
|
else if (key == "useteamzones"s)
|
||||||
|
{
|
||||||
|
// ... unless it specifies so! This allows loading of CODO/COD4 zones that might not have the correct teams
|
||||||
|
dependencies.requiresTeamZones = Utils::String::ToLower(value) == "true"s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -403,7 +378,9 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {team_axis, team_allies};
|
dependencies.requiredTeams = std::make_pair(teamAllies, teamAxis);
|
||||||
|
|
||||||
|
return dependencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Maps::PrepareUsermap(const char* mapname)
|
void Maps::PrepareUsermap(const char* mapname)
|
||||||
@ -905,13 +882,6 @@ namespace Components
|
|||||||
// Load usermap arena file
|
// Load usermap arena file
|
||||||
Utils::Hook(0x630A88, Maps::LoadArenaFileStub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x630A88, Maps::LoadArenaFileStub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
// Dependencies
|
|
||||||
//Maps::AddDependency("oilrig", "mp_subbase");
|
|
||||||
//Maps::AddDependency("gulag", "mp_subbase");
|
|
||||||
//Maps::AddDependency("invasion", "mp_rust");
|
|
||||||
//Maps::AddDependency("co_hunted", "mp_storm");
|
|
||||||
//Maps::AddDependency("mp_shipment", "mp_shipment_long");
|
|
||||||
|
|
||||||
// Allow hiding specific smodels
|
// Allow hiding specific smodels
|
||||||
Utils::Hook(0x50E67C, Maps::HideModelStub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x50E67C, Maps::HideModelStub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
|
@ -50,10 +50,6 @@ namespace Components
|
|||||||
~Maps();
|
~Maps();
|
||||||
|
|
||||||
static void HandleAsSPMap();
|
static void HandleAsSPMap();
|
||||||
static void AddDependency(const std::string& expression, const std::string& zone);
|
|
||||||
|
|
||||||
static std::pair<std::string, std::string> GetTeamsForMap(const std::string& map);
|
|
||||||
static std::vector<std::string> GetDependenciesForMap(const std::string& map);
|
|
||||||
|
|
||||||
static std::string CurrentMainZone;
|
static std::string CurrentMainZone;
|
||||||
static const char* UserMapFiles[4];
|
static const char* UserMapFiles[4];
|
||||||
@ -76,6 +72,13 @@ namespace Components
|
|||||||
std::vector<std::string> maps;
|
std::vector<std::string> maps;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct MapDependencies
|
||||||
|
{
|
||||||
|
std::vector<std::string> requiredMaps;
|
||||||
|
std::pair<std::string, std::string> requiredTeams;
|
||||||
|
bool requiresTeamZones;
|
||||||
|
};
|
||||||
|
|
||||||
static bool SPMap;
|
static bool SPMap;
|
||||||
static UserMapContainer UserMap;
|
static UserMapContainer UserMap;
|
||||||
static std::vector<DLC> DlcPacks;
|
static std::vector<DLC> DlcPacks;
|
||||||
@ -91,6 +94,7 @@ namespace Components
|
|||||||
static void UnloadMapZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync);
|
static void UnloadMapZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync);
|
||||||
|
|
||||||
static void OverrideMapEnts(Game::MapEnts* ents);
|
static void OverrideMapEnts(Game::MapEnts* ents);
|
||||||
|
static MapDependencies GetDependenciesForMap(const std::string& map);
|
||||||
|
|
||||||
static int IgnoreEntityStub(const char* entity);
|
static int IgnoreEntityStub(const char* entity);
|
||||||
|
|
||||||
|
@ -855,10 +855,10 @@ namespace Components
|
|||||||
Menus::Add("ui_mp/popup_friends.menu");
|
Menus::Add("ui_mp/popup_friends.menu");
|
||||||
Menus::Add("ui_mp/menu_first_launch.menu");
|
Menus::Add("ui_mp/menu_first_launch.menu");
|
||||||
Menus::Add("ui_mp/startup_messages.menu");
|
Menus::Add("ui_mp/startup_messages.menu");
|
||||||
Menus::Add("ui_mp/pc_store.menu");
|
|
||||||
Menus::Add("ui_mp/iw4x_credits.menu");
|
Menus::Add("ui_mp/iw4x_credits.menu");
|
||||||
Menus::Add("ui_mp/resetclass.menu");
|
Menus::Add("ui_mp/resetclass.menu");
|
||||||
Menus::Add("ui_mp/popup_customtitle.menu");
|
Menus::Add("ui_mp/popup_customtitle.menu");
|
||||||
|
Menus::Add("ui_mp/popup_customclan.menu");
|
||||||
}
|
}
|
||||||
|
|
||||||
Menus::~Menus()
|
Menus::~Menus()
|
||||||
|
@ -93,7 +93,7 @@ namespace Components
|
|||||||
if (Dedicated::IsEnabled()) return;
|
if (Dedicated::IsEnabled()) return;
|
||||||
|
|
||||||
ModList::CurrentMod = 0;
|
ModList::CurrentMod = 0;
|
||||||
Dvar::Register("cl_modVidRestart", true, Game::dvar_flag::DVAR_ARCHIVE, "Perform a vid_restart when loading a mod.");
|
Dvar::Register("cl_modVidRestart", true, Game::DVAR_ARCHIVE, "Perform a vid_restart when loading a mod.");
|
||||||
|
|
||||||
UIScript::Add("LoadMods", ModList::UIScript_LoadMods);
|
UIScript::Add("LoadMods", ModList::UIScript_LoadMods);
|
||||||
UIScript::Add("RunMod", ModList::UIScript_RunMod);
|
UIScript::Add("RunMod", ModList::UIScript_RunMod);
|
||||||
|
@ -283,10 +283,10 @@ namespace Components
|
|||||||
|
|
||||||
bool Network::HandleCommand(Game::netadr_t* address, const char* command, const Game::msg_t* message)
|
bool Network::HandleCommand(Game::netadr_t* address, const char* command, const Game::msg_t* message)
|
||||||
{
|
{
|
||||||
const auto cmd_string = Utils::String::ToLower(command);
|
const auto command_ = Utils::String::ToLower(command);
|
||||||
const auto handler = Network::Callbacks.find(cmd_string);
|
const auto handler = Network::Callbacks.find(command_);
|
||||||
|
|
||||||
const auto offset = cmd_string.size() + 5;
|
const auto offset = command_.size() + 5;
|
||||||
if (static_cast<std::size_t>(message->cursize) < offset || handler == Network::Callbacks.end())
|
if (static_cast<std::size_t>(message->cursize) < offset || handler == Network::Callbacks.end())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -366,6 +366,11 @@ namespace Components
|
|||||||
// Handle client packets
|
// Handle client packets
|
||||||
Utils::Hook(0x5AA703, Network::CL_HandleCommandStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x5AA703, Network::CL_HandleCommandStub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
|
// Disable unused OOB packets handlers just to be sure
|
||||||
|
Utils::Hook::Set<BYTE>(0x5AA5B6, 0xEB); // CL_SteamServerAuth
|
||||||
|
Utils::Hook::Set<BYTE>(0x5AA69F, 0xEB); // echo
|
||||||
|
Utils::Hook::Set<BYTE>(0x5AAA82, 0xEB); // SP
|
||||||
|
|
||||||
Network::OnPacket("resolveAddress", [](const Address& address, [[maybe_unused]] const std::string& data)
|
Network::OnPacket("resolveAddress", [](const Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
Network::SendRaw(address, address.getString());
|
Network::SendRaw(address, address.getString());
|
||||||
|
@ -57,17 +57,6 @@ namespace Components
|
|||||||
Utils::OpenUrl(Utils::Cache::GetStaticUrl(""));
|
Utils::OpenUrl(Utils::Cache::GetStaticUrl(""));
|
||||||
});
|
});
|
||||||
|
|
||||||
UIScript::Add("visitWiki", [](UIScript::Token)
|
|
||||||
{
|
|
||||||
//Utils::OpenUrl(Utils::Cache::GetStaticUrl("/wiki/"));
|
|
||||||
Utils::OpenUrl("https://github.com/Emosewaj/IW4x/wiki");
|
|
||||||
});
|
|
||||||
|
|
||||||
UIScript::Add("visitDiscord", [](UIScript::Token)
|
|
||||||
{
|
|
||||||
Utils::OpenUrl("https://discord.gg/sKeVmR3");
|
|
||||||
});
|
|
||||||
|
|
||||||
Localization::Set("MPUI_CHANGELOG_TEXT", "Loading...");
|
Localization::Set("MPUI_CHANGELOG_TEXT", "Loading...");
|
||||||
Localization::Set("MPUI_MOTD_TEXT", NEWS_MOTD_DEFAULT);
|
Localization::Set("MPUI_MOTD_TEXT", NEWS_MOTD_DEFAULT);
|
||||||
|
|
||||||
|
@ -208,7 +208,7 @@ namespace Components
|
|||||||
i = Node::Nodes.erase(i);
|
i = Node::Nodes.erase(i);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else if (sentRequests < queryLimit.get<int>() && i->requiresRequest())
|
if (sentRequests < queryLimit.get<int>() && i->requiresRequest())
|
||||||
{
|
{
|
||||||
++sentRequests;
|
++sentRequests;
|
||||||
i->sendRequest();
|
i->sendRequest();
|
||||||
@ -326,7 +326,7 @@ namespace Components
|
|||||||
Logger::Debug("Sending {} nodeListResponse length to {}\n", nodeListData.length(), address.getCString());
|
Logger::Debug("Sending {} nodeListResponse length to {}\n", nodeListData.length(), address.getCString());
|
||||||
#endif
|
#endif
|
||||||
Session::Send(address, "nodeListResponse", nodeListData);
|
Session::Send(address, "nodeListResponse", nodeListData);
|
||||||
}, Scheduler::Pipeline::SERVER, NODE_SEND_RATE * i++);
|
}, Scheduler::Pipeline::MAIN, NODE_SEND_RATE * i++);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,10 +346,10 @@ namespace Components
|
|||||||
Node::StoreNodes(false);
|
Node::StoreNodes(false);
|
||||||
}, Scheduler::Pipeline::ASYNC);
|
}, Scheduler::Pipeline::ASYNC);
|
||||||
|
|
||||||
Scheduler::Loop(Node::RunFrame, Scheduler::Pipeline::SERVER);
|
Scheduler::Loop(Node::RunFrame, Scheduler::Pipeline::MAIN);
|
||||||
|
|
||||||
Session::Handle("nodeListResponse", Node::HandleResponse);
|
Session::Handle("nodeListResponse", Node::HandleResponse);
|
||||||
Session::Handle("nodeListRequest", [](const Network::Address& address, const std::string&)
|
Session::Handle("nodeListRequest", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
{
|
{
|
||||||
Node::SendList(address);
|
Node::SendList(address);
|
||||||
});
|
});
|
||||||
|
@ -76,9 +76,9 @@ namespace Components
|
|||||||
return Party::Container.motd;
|
return Party::Container.motd;
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::dvar_t* Party::RegisterMinPlayers(const char* name, int /*value*/, int /*min*/, int max, Game::dvar_flag flag, const char* description)
|
Game::dvar_t* Party::RegisterMinPlayers(const char* name, int /*value*/, int /*min*/, int max, Game::DvarFlags flag, const char* description)
|
||||||
{
|
{
|
||||||
return Dvar::Register<int>(name, 1, 1, max, Game::dvar_flag::DVAR_INIT | flag, description).get<Game::dvar_t*>();
|
return Dvar::Register<int>(name, 1, 1, max, Game::DVAR_INIT | flag, description).get<Game::dvar_t*>();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Party::PlaylistAwaiting()
|
bool Party::PlaylistAwaiting()
|
||||||
@ -154,8 +154,8 @@ namespace Components
|
|||||||
|
|
||||||
Party::Party()
|
Party::Party()
|
||||||
{
|
{
|
||||||
Party::PartyEnable = Dvar::Register<bool>("party_enable", Dedicated::IsEnabled(), Game::dvar_flag::DVAR_NONE, "Enable party system");
|
Party::PartyEnable = Dvar::Register<bool>("party_enable", Dedicated::IsEnabled(), Game::DVAR_NONE, "Enable party system");
|
||||||
Dvar::Register<bool>("xblive_privatematch", true, Game::dvar_flag::DVAR_INIT, "");
|
Dvar::Register<bool>("xblive_privatematch", true, Game::DVAR_INIT, "");
|
||||||
|
|
||||||
// various changes to SV_DirectConnect-y stuff to allow non-party joinees
|
// various changes to SV_DirectConnect-y stuff to allow non-party joinees
|
||||||
Utils::Hook::Set<WORD>(0x460D96, 0x90E9);
|
Utils::Hook::Set<WORD>(0x460D96, 0x90E9);
|
||||||
@ -254,12 +254,12 @@ namespace Components
|
|||||||
Utils::Hook::Set<const char*>(0x5E3772, "sv_maxclients");
|
Utils::Hook::Set<const char*>(0x5E3772, "sv_maxclients");
|
||||||
|
|
||||||
// Unlatch maxclient dvars
|
// Unlatch maxclient dvars
|
||||||
Utils::Hook::Xor<BYTE>(0x426187, Game::dvar_flag::DVAR_LATCH);
|
Utils::Hook::Xor<BYTE>(0x426187, Game::DVAR_LATCH);
|
||||||
Utils::Hook::Xor<BYTE>(0x4D374E, Game::dvar_flag::DVAR_LATCH);
|
Utils::Hook::Xor<BYTE>(0x4D374E, Game::DVAR_LATCH);
|
||||||
Utils::Hook::Xor<BYTE>(0x5E376A, Game::dvar_flag::DVAR_LATCH);
|
Utils::Hook::Xor<BYTE>(0x5E376A, Game::DVAR_LATCH);
|
||||||
Utils::Hook::Xor<DWORD>(0x4261A1, Game::dvar_flag::DVAR_LATCH);
|
Utils::Hook::Xor<DWORD>(0x4261A1, Game::DVAR_LATCH);
|
||||||
Utils::Hook::Xor<DWORD>(0x4D376D, Game::dvar_flag::DVAR_LATCH);
|
Utils::Hook::Xor<DWORD>(0x4D376D, Game::DVAR_LATCH);
|
||||||
Utils::Hook::Xor<DWORD>(0x5E3789, Game::dvar_flag::DVAR_LATCH);
|
Utils::Hook::Xor<DWORD>(0x5E3789, Game::DVAR_LATCH);
|
||||||
|
|
||||||
// Patch Live_PlayerHasLoopbackAddr
|
// Patch Live_PlayerHasLoopbackAddr
|
||||||
//Utils::Hook::Set<DWORD>(0x418F30, 0x90C3C033);
|
//Utils::Hook::Set<DWORD>(0x418F30, 0x90C3C033);
|
||||||
@ -332,7 +332,7 @@ namespace Components
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
maxclientCount = Dvar::Var("party_maxplayers").get<int>();
|
maxclientCount = Dvar::Var("party_maxplayers").get<int>();
|
||||||
clientCount = Game::PartyHost_CountMembers(reinterpret_cast<Game::PartyData_s*>(0x1081C00));
|
clientCount = Game::PartyHost_CountMembers(reinterpret_cast<Game::PartyData*>(0x1081C00));
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::InfoString info;
|
Utils::InfoString info;
|
||||||
|
@ -50,7 +50,7 @@ namespace Components
|
|||||||
|
|
||||||
static SteamID GenerateLobbyId();
|
static SteamID GenerateLobbyId();
|
||||||
|
|
||||||
static Game::dvar_t* RegisterMinPlayers(const char* name, int value, int min, int max, Game::dvar_flag flag, const char* description);
|
static Game::dvar_t* RegisterMinPlayers(const char* name, int value, int min, int max, Game::DvarFlags flag, const char* description);
|
||||||
|
|
||||||
static DWORD UIDvarIntStub(char* dvar);
|
static DWORD UIDvarIntStub(char* dvar);
|
||||||
};
|
};
|
||||||
|
@ -44,14 +44,14 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char* PlayerName::GetClientName(int localClientNum, int index, char* buf, size_t size)
|
int PlayerName::GetClientName(int localClientNum, int index, char* buf, int size)
|
||||||
{
|
{
|
||||||
Game::CL_GetClientName(localClientNum, index, buf, size);
|
const auto result = Game::CL_GetClientName(localClientNum, index, buf, size);
|
||||||
|
|
||||||
// Append clantag to username & remove the colors
|
// Prepend clanName to username & remove the colors
|
||||||
strncpy_s(buf, size, TextRenderer::StripColors(ClanTags::GetUserClantag(index, buf)).data(), size);
|
strncpy_s(buf, size, TextRenderer::StripColors(ClanTags::GetClanTagWithName(index, buf)).data(), size);
|
||||||
|
|
||||||
return buf;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* PlayerName::CleanStrStub(char* string)
|
char* PlayerName::CleanStrStub(char* string)
|
||||||
@ -106,7 +106,7 @@ namespace Components
|
|||||||
|
|
||||||
PlayerName::PlayerName()
|
PlayerName::PlayerName()
|
||||||
{
|
{
|
||||||
sv_allowColoredNames = Dvar::Register<bool>("sv_allowColoredNames", true, Game::dvar_flag::DVAR_NONE, "Allow colored names on the server");
|
sv_allowColoredNames = Dvar::Register<bool>("sv_allowColoredNames", true, Game::DVAR_NONE, "Allow colored names on the server");
|
||||||
|
|
||||||
// Disable SV_UpdateUserinfo_f, to block changing the name ingame
|
// Disable SV_UpdateUserinfo_f, to block changing the name ingame
|
||||||
Utils::Hook::Set<BYTE>(0x6258D0, 0xC3);
|
Utils::Hook::Set<BYTE>(0x6258D0, 0xC3);
|
||||||
|
@ -9,6 +9,8 @@ namespace Components
|
|||||||
|
|
||||||
static void UserInfoCopy(char* buffer, const char* name, size_t size);
|
static void UserInfoCopy(char* buffer, const char* name, size_t size);
|
||||||
|
|
||||||
|
static int GetClientName(int localClientNum, int index, char* buf, int size);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Dvar::Var sv_allowColoredNames;
|
static Dvar::Var sv_allowColoredNames;
|
||||||
// Message used when kicking players
|
// Message used when kicking players
|
||||||
@ -16,7 +18,6 @@ namespace Components
|
|||||||
|
|
||||||
static char* CleanStrStub(char* string);
|
static char* CleanStrStub(char* string);
|
||||||
static void ClientCleanName();
|
static void ClientCleanName();
|
||||||
static char* GetClientName(int localClientNum, int index, char* buf, size_t size);
|
|
||||||
|
|
||||||
static bool CopyClientNameCheck(char* dest, const char* source, int size);
|
static bool CopyClientNameCheck(char* dest, const char* source, int size);
|
||||||
static void SV_UserinfoChangedStub();
|
static void SV_UserinfoChangedStub();
|
||||||
|
@ -175,20 +175,20 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL QuickPatch::IsDynClassnameStub(char* a1)
|
BOOL QuickPatch::IsDynClassnameStub(const char* classname)
|
||||||
{
|
{
|
||||||
auto version = Zones::GetEntitiesZoneVersion();
|
const auto version = Zones::Version();
|
||||||
|
|
||||||
if (version >= VERSION_LATEST_CODO)
|
if (version >= VERSION_LATEST_CODO)
|
||||||
{
|
{
|
||||||
for (auto i = 0; i < Game::spawnVars->numSpawnVars; i++)
|
for (auto i = 0; i < Game::spawnVars->numSpawnVars; i++)
|
||||||
{
|
{
|
||||||
char** kvPair = Game::spawnVars->spawnVars[i];
|
char** kvPair = Game::spawnVars->spawnVars[i];
|
||||||
auto key = kvPair[0];
|
const auto* key = kvPair[0];
|
||||||
auto val = kvPair[1];
|
const auto* val = kvPair[1];
|
||||||
|
|
||||||
bool isSpecOps = strncmp(key, "script_specialops", 17) == 0;
|
auto isSpecOps = std::strncmp(key, "script_specialops", 17) == 0;
|
||||||
bool isSpecOpsOnly = val[0] == '1' && val[1] == '\0';
|
auto isSpecOpsOnly = (val[0] == '1') && (val[1] == '\0');
|
||||||
|
|
||||||
if (isSpecOps && isSpecOpsOnly)
|
if (isSpecOps && isSpecOpsOnly)
|
||||||
{
|
{
|
||||||
@ -199,8 +199,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Passthrough to the game's own IsDynClassname
|
return Utils::Hook::Call<BOOL(const char*)>(0x444810)(classname); // IsDynClassname
|
||||||
return Utils::Hook::Call<BOOL(char*)>(0x444810)(a1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuickPatch::CL_KeyEvent_OnEscape()
|
void QuickPatch::CL_KeyEvent_OnEscape()
|
||||||
@ -248,7 +247,7 @@ namespace Components
|
|||||||
Utils::Hook(0x4F66A3, CL_KeyEvent_ConsoleEscape_Stub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x4F66A3, CL_KeyEvent_ConsoleEscape_Stub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
// Intermission time dvar
|
// Intermission time dvar
|
||||||
Game::Dvar_RegisterFloat("scr_intermissionTime", 10, 0, 120, Game::dvar_flag::DVAR_NONE, "Time in seconds before match server loads the next map");
|
Game::Dvar_RegisterFloat("scr_intermissionTime", 10, 0, 120, Game::DVAR_NONE, "Time in seconds before match server loads the next map");
|
||||||
|
|
||||||
g_antilag = Game::Dvar_RegisterBool("g_antilag", true, Game::DVAR_CODINFO, "Perform antilag");
|
g_antilag = Game::Dvar_RegisterBool("g_antilag", true, Game::DVAR_CODINFO, "Perform antilag");
|
||||||
Utils::Hook(0x5D6D56, QuickPatch::ClientEventsFireWeaponStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x5D6D56, QuickPatch::ClientEventsFireWeaponStub, HOOK_JUMP).install()->quick();
|
||||||
@ -328,7 +327,7 @@ namespace Components
|
|||||||
|
|
||||||
// Numerical ping (cg_scoreboardPingText 1)
|
// Numerical ping (cg_scoreboardPingText 1)
|
||||||
Utils::Hook::Set<BYTE>(0x45888E, 1);
|
Utils::Hook::Set<BYTE>(0x45888E, 1);
|
||||||
Utils::Hook::Set<BYTE>(0x45888C, Game::dvar_flag::DVAR_CHEAT);
|
Utils::Hook::Set<BYTE>(0x45888C, Game::DVAR_CHEAT);
|
||||||
|
|
||||||
// increase font sizes for chat on higher resolutions
|
// increase font sizes for chat on higher resolutions
|
||||||
static float float13 = 13.0f;
|
static float float13 = 13.0f;
|
||||||
@ -404,7 +403,6 @@ namespace Components
|
|||||||
// fs_game fixes
|
// fs_game fixes
|
||||||
Utils::Hook::Nop(0x4A5D74, 2); // remove fs_game profiles
|
Utils::Hook::Nop(0x4A5D74, 2); // remove fs_game profiles
|
||||||
Utils::Hook::Set<BYTE>(0x4081FD, 0xEB); // defaultweapon
|
Utils::Hook::Set<BYTE>(0x4081FD, 0xEB); // defaultweapon
|
||||||
Utils::Hook::Set<BYTE>(0x452C1D, 0xEB); // LoadObj weaponDefs
|
|
||||||
|
|
||||||
// filesystem init default_mp.cfg check
|
// filesystem init default_mp.cfg check
|
||||||
Utils::Hook::Nop(0x461A9E, 5);
|
Utils::Hook::Nop(0x461A9E, 5);
|
||||||
@ -683,7 +681,7 @@ namespace Components
|
|||||||
Utils::Hook::Set<bool>(0x60AE2B, true);
|
Utils::Hook::Set<bool>(0x60AE2B, true);
|
||||||
|
|
||||||
// Disable cheat protection for dvars
|
// Disable cheat protection for dvars
|
||||||
Utils::Hook::Set<BYTE>(0x647682, 0xEB);
|
Utils::Hook::Set<BYTE>(0x646515, 0xEB); // Dvar_IsCheatProtected
|
||||||
#else
|
#else
|
||||||
// Remove missing tag message
|
// Remove missing tag message
|
||||||
Utils::Hook::Nop(0x4EBF1A, 5);
|
Utils::Hook::Nop(0x4EBF1A, 5);
|
||||||
|
@ -23,7 +23,7 @@ namespace Components
|
|||||||
static void ClientEventsFireWeaponStub();
|
static void ClientEventsFireWeaponStub();
|
||||||
static void ClientEventsFireWeaponMeleeStub();
|
static void ClientEventsFireWeaponMeleeStub();
|
||||||
|
|
||||||
static BOOL IsDynClassnameStub(char* a1);
|
static BOOL IsDynClassnameStub(const char* classname);
|
||||||
|
|
||||||
static void CL_KeyEvent_OnEscape();
|
static void CL_KeyEvent_OnEscape();
|
||||||
static void CL_KeyEvent_ConsoleEscape_Stub();
|
static void CL_KeyEvent_ConsoleEscape_Stub();
|
||||||
|
@ -78,8 +78,8 @@ namespace Components
|
|||||||
|
|
||||||
Scheduler::Once([]
|
Scheduler::Once([]
|
||||||
{
|
{
|
||||||
RCon::RconPassword = Dvar::Register<const char*>("rcon_password", "", Game::dvar_flag::DVAR_NONE, "The password for rcon");
|
RCon::RconPassword = Dvar::Register<const char*>("rcon_password", "", Game::DVAR_NONE, "The password for rcon");
|
||||||
RCon::RconLogRequests = Dvar::Register<bool>("rcon_log_requests", false, Game::dvar_flag::DVAR_NONE, "Print remote commands in the output log");
|
RCon::RconLogRequests = Dvar::Register<bool>("rcon_log_requests", false, Game::DVAR_NONE, "Print remote commands in the output log");
|
||||||
}, Scheduler::Pipeline::MAIN);
|
}, Scheduler::Pipeline::MAIN);
|
||||||
|
|
||||||
Network::OnPacket("rcon", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
Network::OnPacket("rcon", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
||||||
|
@ -2,26 +2,74 @@
|
|||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
void* RawFiles::LoadModdableRawfileFunc(const char* filename)
|
char* RawFiles::ReadRawFile(const char* filename, char* buf, int size)
|
||||||
{
|
{
|
||||||
return Game::LoadModdableRawfile(0, filename);
|
auto fileHandle = 0;
|
||||||
|
auto fileSize = Game::FS_FOpenFileRead(filename, &fileHandle);
|
||||||
|
|
||||||
|
if (fileHandle != 0)
|
||||||
|
{
|
||||||
|
if ((fileSize + 1) <= size)
|
||||||
|
{
|
||||||
|
Game::FS_Read(buf, fileSize, fileHandle);
|
||||||
|
buf[fileSize] = 0;
|
||||||
|
Game::FS_FCloseFile(fileHandle);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::FS_FCloseFile(fileHandle);
|
||||||
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "Ignoring raw file '{}' as it exceeds buffer size {} > {}\n", filename, fileSize, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* rawfile = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_RAWFILE, filename).rawfile;
|
||||||
|
if (Game::DB_IsXAssetDefault(Game::ASSET_TYPE_RAWFILE, filename))
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::DB_GetRawBuffer(rawfile, buf, size);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* RawFiles::GetMenuBuffer(const char* filename)
|
||||||
|
{
|
||||||
|
auto fileHandle = 0;
|
||||||
|
auto fileSize = Game::FS_FOpenFileRead(filename, &fileHandle);
|
||||||
|
|
||||||
|
if (fileHandle != 0)
|
||||||
|
{
|
||||||
|
if (fileSize < 0x8000)
|
||||||
|
{
|
||||||
|
auto* buffer = static_cast<char*>(Game::Z_VirtualAlloc(fileSize + 1));
|
||||||
|
Game::FS_Read(buffer, fileSize, fileHandle);
|
||||||
|
Game::FS_FCloseFile(fileHandle);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::FS_FCloseFile(fileHandle);
|
||||||
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "Menu file too large: {} is {}, max allowed is {}\n", filename, fileSize, 0x8000);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* rawfile = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_RAWFILE, filename).rawfile;
|
||||||
|
if (Game::DB_IsXAssetDefault(Game::ASSET_TYPE_RAWFILE, filename))
|
||||||
|
{
|
||||||
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "Menu file not found: {}, using default\n", filename);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* buffer = static_cast<char*>(Game::Z_VirtualAlloc(rawfile->len + 1));
|
||||||
|
Game::DB_GetRawBuffer(rawfile, buffer, rawfile->len + 1);
|
||||||
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
RawFiles::RawFiles()
|
RawFiles::RawFiles()
|
||||||
{
|
{
|
||||||
Utils::Hook(0x632155, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick();
|
|
||||||
Utils::Hook(0x5FA46C, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick();
|
|
||||||
Utils::Hook(0x5FA4D6, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick();
|
|
||||||
Utils::Hook(0x6321EF, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick();
|
|
||||||
//Utils::Hook(0x630A88, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); // Arena parsing, handled by usermap hook
|
|
||||||
Utils::Hook(0x59A6F8, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick();
|
|
||||||
Utils::Hook(0x57F1E6, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick();
|
|
||||||
Utils::Hook(0x57ED36, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick();
|
|
||||||
//Utils::Hook(0x609832, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick();
|
|
||||||
|
|
||||||
// remove fs_game check for moddable rawfiles - allows non-fs_game to modify rawfiles
|
// remove fs_game check for moddable rawfiles - allows non-fs_game to modify rawfiles
|
||||||
Utils::Hook::Nop(0x61AB76, 2);
|
Utils::Hook::Nop(0x61AB76, 2);
|
||||||
|
|
||||||
|
Utils::Hook(0x4DA0D0, ReadRawFile, HOOK_JUMP).install()->quick();
|
||||||
|
Utils::Hook(0x631640, GetMenuBuffer, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
Command::Add("dumpraw", [](Command::Params* params)
|
Command::Add("dumpraw", [](Command::Params* params)
|
||||||
{
|
{
|
||||||
if (params->size() < 2)
|
if (params->size() < 2)
|
||||||
@ -38,9 +86,9 @@ namespace Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* data = Game::LoadModdableRawfile(0, file.getName().data());
|
const char* data = Game::Scr_AddSourceBuffer(nullptr, file.getName().data(), nullptr, false);
|
||||||
|
|
||||||
if (data)
|
if (data != nullptr)
|
||||||
{
|
{
|
||||||
Utils::IO::WriteFile("raw/" + file.getName(), data);
|
Utils::IO::WriteFile("raw/" + file.getName(), data);
|
||||||
Logger::Print("File '{}' written to raw!\n", file.getName());
|
Logger::Print("File '{}' written to raw!\n", file.getName());
|
||||||
|
@ -7,6 +7,8 @@ namespace Components
|
|||||||
public:
|
public:
|
||||||
RawFiles();
|
RawFiles();
|
||||||
|
|
||||||
static void* LoadModdableRawfileFunc(const char* filename);
|
private:
|
||||||
|
static char* ReadRawFile(const char* filename, char* buf, int size);
|
||||||
|
static char* GetMenuBuffer(const char* filename);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,7 @@ namespace Components
|
|||||||
Utils::Hook(0x467C03, RawMouse::IN_Init, HOOK_CALL).install()->quick();
|
Utils::Hook(0x467C03, RawMouse::IN_Init, HOOK_CALL).install()->quick();
|
||||||
Utils::Hook(0x64D095, RawMouse::IN_Init, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x64D095, RawMouse::IN_Init, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
RawMouse::M_RawInput = Dvar::Register<bool>("m_rawinput", true, Game::dvar_flag::DVAR_ARCHIVE, "Use raw mouse input, Improves accuracy & has better support for higher polling rates. Use in_restart to take effect if not enabled.");
|
RawMouse::M_RawInput = Dvar::Register<bool>("m_rawinput", true, Game::DVAR_ARCHIVE, "Use raw mouse input, Improves accuracy & has better support for higher polling rates. Use in_restart to take effect if not enabled.");
|
||||||
|
|
||||||
Window::OnWndMessage(WM_INPUT, RawMouse::OnRawInput);
|
Window::OnWndMessage(WM_INPUT, RawMouse::OnRawInput);
|
||||||
Window::OnCreate(RawMouse::IN_RawMouse_Init);
|
Window::OnCreate(RawMouse::IN_RawMouse_Init);
|
||||||
|
@ -177,7 +177,7 @@ namespace Components
|
|||||||
|
|
||||||
auto entities = Game::g_entities;
|
auto entities = Game::g_entities;
|
||||||
|
|
||||||
for (auto i = 0u; i < 0x800u; i++)
|
for (std::size_t i = 0; i < Game::MAX_GENTITIES; ++i)
|
||||||
{
|
{
|
||||||
auto* ent = &entities[i];
|
auto* ent = &entities[i];
|
||||||
|
|
||||||
@ -229,7 +229,7 @@ namespace Components
|
|||||||
|
|
||||||
auto scene = Game::scene;
|
auto scene = Game::scene;
|
||||||
|
|
||||||
for (auto i = 0; i < scene->sceneModelCount; i++)
|
for (auto i = 0; i < scene->sceneModelCount; ++i)
|
||||||
{
|
{
|
||||||
if (!scene->sceneModel[i].model)
|
if (!scene->sceneModel[i].model)
|
||||||
continue;
|
continue;
|
||||||
@ -255,23 +255,27 @@ namespace Components
|
|||||||
|
|
||||||
if (!val) return;
|
if (!val) return;
|
||||||
|
|
||||||
// Ingame only
|
auto clientNum = Game::CG_GetClientNum();
|
||||||
int clientNum = Game::CG_GetClientNum();
|
Game::gentity_t* clientEntity = &Game::g_entities[clientNum];
|
||||||
if (!Game::CL_IsCgameInitialized() ||
|
|
||||||
clientNum >= 18 ||
|
|
||||||
clientNum < 0 ||
|
|
||||||
Game::g_entities[clientNum].client == nullptr) {
|
|
||||||
|
|
||||||
|
// Ingame only & player only
|
||||||
|
if (!Game::CL_IsCgameInitialized() || clientEntity->client == nullptr)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::gentity_t* clientEntity = &Game::g_entities[clientNum];
|
|
||||||
|
|
||||||
float playerPosition[3]{ clientEntity->r.currentOrigin[0], clientEntity->r.currentOrigin[1], clientEntity->r.currentOrigin[2] };
|
float playerPosition[3]{ clientEntity->r.currentOrigin[0], clientEntity->r.currentOrigin[1], clientEntity->r.currentOrigin[2] };
|
||||||
|
|
||||||
const auto mapName = Dvar::Var("mapname").get<const char*>();
|
const auto mapName = Dvar::Var("mapname").get<const char*>();
|
||||||
auto scene = Game::scene;
|
auto scene = Game::scene;
|
||||||
auto world = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_GFXWORLD, Utils::String::VA("maps/mp/%s.d3dbsp", mapName))->asset.header.gfxWorld;
|
auto gfxAsset = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_GFXWORLD, Utils::String::VA("maps/mp/%s.d3dbsp", mapName));
|
||||||
|
|
||||||
|
if (gfxAsset == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto world = gfxAsset->asset.header.gfxWorld;
|
||||||
|
|
||||||
auto drawDistance = r_playerDrawDebugDistance.get<int>();
|
auto drawDistance = r_playerDrawDebugDistance.get<int>();
|
||||||
auto sqrDist = drawDistance * drawDistance;
|
auto sqrDist = drawDistance * drawDistance;
|
||||||
@ -284,7 +288,7 @@ namespace Components
|
|||||||
if (!scene->sceneModel[i].model)
|
if (!scene->sceneModel[i].model)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (Utils::Vec3SqrDistance(playerPosition, scene->sceneModel[i].placement.base.origin) < sqrDist)
|
if (Utils::Maths::Vec3SqrDistance(playerPosition, scene->sceneModel[i].placement.base.origin) < sqrDist)
|
||||||
{
|
{
|
||||||
auto b = scene->sceneModel[i].model->bounds;
|
auto b = scene->sceneModel[i].model->bounds;
|
||||||
b.midPoint[0] += scene->sceneModel[i].placement.base.origin[0];
|
b.midPoint[0] += scene->sceneModel[i].placement.base.origin[0];
|
||||||
@ -297,12 +301,11 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
for (auto i = 0; i < scene->sceneDObjCount; i++)
|
for (auto i = 0; i < scene->sceneDObjCount; i++)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (Utils::Vec3SqrDistance(playerPosition, scene->sceneDObj[i].cull.bounds.midPoint) < sqrDist)
|
if (Utils::Maths::Vec3SqrDistance(playerPosition, scene->sceneDObj[i].cull.bounds.midPoint) < sqrDist)
|
||||||
{
|
{
|
||||||
scene->sceneDObj[i].cull.bounds.halfSize[0] = std::abs(scene->sceneDObj[i].cull.bounds.halfSize[0]);
|
scene->sceneDObj[i].cull.bounds.halfSize[0] = std::abs(scene->sceneDObj[i].cull.bounds.halfSize[0]);
|
||||||
scene->sceneDObj[i].cull.bounds.halfSize[1] = std::abs(scene->sceneDObj[i].cull.bounds.halfSize[1]);
|
scene->sceneDObj[i].cull.bounds.halfSize[1] = std::abs(scene->sceneDObj[i].cull.bounds.halfSize[1]);
|
||||||
@ -320,14 +323,13 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
// Static models
|
// Static models
|
||||||
for (size_t i = 0; i < world->dpvs.smodelCount; i++)
|
for (size_t i = 0; i < world->dpvs.smodelCount; i++)
|
||||||
{
|
{
|
||||||
auto staticModel = world->dpvs.smodelDrawInsts[i];
|
auto staticModel = world->dpvs.smodelDrawInsts[i];
|
||||||
|
|
||||||
if (Utils::Vec3SqrDistance(playerPosition, staticModel.placement.origin) < sqrDist)
|
if (Utils::Maths::Vec3SqrDistance(playerPosition, staticModel.placement.origin) < sqrDist)
|
||||||
{
|
{
|
||||||
if (staticModel.model)
|
if (staticModel.model)
|
||||||
{
|
{
|
||||||
@ -344,6 +346,8 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,41 +357,45 @@ namespace Components
|
|||||||
|
|
||||||
if (!val) return;
|
if (!val) return;
|
||||||
|
|
||||||
// Ingame only
|
auto clientNum = Game::CG_GetClientNum();
|
||||||
int clientNum = Game::CG_GetClientNum();
|
Game::gentity_t* clientEntity = &Game::g_entities[clientNum];
|
||||||
if (!Game::CL_IsCgameInitialized() ||
|
|
||||||
clientNum >= 18 ||
|
|
||||||
clientNum < 0 ||
|
|
||||||
Game::g_entities[clientNum].client == nullptr) {
|
|
||||||
|
|
||||||
|
// Ingame only & player only
|
||||||
|
if (!Game::CL_IsCgameInitialized() || clientEntity->client == nullptr)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::gentity_t* clientEntity = &Game::g_entities[clientNum];
|
|
||||||
|
|
||||||
float playerPosition[3]{ clientEntity->r.currentOrigin[0], clientEntity->r.currentOrigin[1], clientEntity->r.currentOrigin[2] };
|
float playerPosition[3]{ clientEntity->r.currentOrigin[0], clientEntity->r.currentOrigin[1], clientEntity->r.currentOrigin[2] };
|
||||||
|
|
||||||
const auto mapName = Dvar::Var("mapname").get<const char*>();
|
const auto mapName = Dvar::Var("mapname").get<const char*>();
|
||||||
auto scene = Game::scene;
|
auto scene = Game::scene;
|
||||||
auto world = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_GFXWORLD, Utils::String::VA("maps/mp/%s.d3dbsp", mapName))->asset.header.gfxWorld;
|
auto gfxAsset = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_GFXWORLD, Utils::String::VA("maps/mp/%s.d3dbsp", mapName));
|
||||||
|
|
||||||
|
if (gfxAsset == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto world = gfxAsset->asset.header.gfxWorld;
|
||||||
|
|
||||||
auto drawDistance = r_playerDrawDebugDistance.get<int>();
|
auto drawDistance = r_playerDrawDebugDistance.get<int>();
|
||||||
auto sqrDist = drawDistance * drawDistance;
|
auto sqrDist = drawDistance * drawDistance;
|
||||||
|
|
||||||
switch (val) {
|
switch (val)
|
||||||
|
{
|
||||||
case 1:
|
case 1:
|
||||||
for (auto i = 0; i < scene->sceneModelCount; i++)
|
for (auto i = 0; i < scene->sceneModelCount; i++)
|
||||||
{
|
{
|
||||||
if (!scene->sceneModel[i].model)
|
if (!scene->sceneModel[i].model)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (Utils::Vec3SqrDistance(playerPosition, scene->sceneModel[i].placement.base.origin) < sqrDist)
|
if (Utils::Maths::Vec3SqrDistance(playerPosition, scene->sceneModel[i].placement.base.origin) < static_cast<float>(sqrDist))
|
||||||
{
|
{
|
||||||
Game::R_AddDebugString(sceneModelsColor, scene->sceneModel[i].placement.base.origin, 1.0, scene->sceneModel[i].model->name);
|
Game::R_AddDebugString(sceneModelsColor, scene->sceneModel[i].placement.base.origin, 1.0, scene->sceneModel[i].model->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
for (auto i = 0; i < scene->sceneDObjCount; i++)
|
for (auto i = 0; i < scene->sceneDObjCount; i++)
|
||||||
{
|
{
|
||||||
@ -395,7 +403,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
for (int j = 0; j < scene->sceneDObj[i].obj->numModels; j++)
|
for (int j = 0; j < scene->sceneDObj[i].obj->numModels; j++)
|
||||||
{
|
{
|
||||||
if (Utils::Vec3SqrDistance(playerPosition, scene->sceneDObj[i].placement.origin) < sqrDist)
|
if (Utils::Maths::Vec3SqrDistance(playerPosition, scene->sceneDObj[i].placement.origin) < static_cast<float>(sqrDist))
|
||||||
{
|
{
|
||||||
Game::R_AddDebugString(dobjsColor, scene->sceneDObj[i].placement.origin, 1.0, scene->sceneDObj[i].obj->models[j]->name);
|
Game::R_AddDebugString(dobjsColor, scene->sceneDObj[i].placement.origin, 1.0, scene->sceneDObj[i].obj->models[j]->name);
|
||||||
}
|
}
|
||||||
@ -403,7 +411,6 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
// Static models
|
// Static models
|
||||||
for (size_t i = 0; i < world->dpvs.smodelCount; i++)
|
for (size_t i = 0; i < world->dpvs.smodelCount; i++)
|
||||||
@ -411,14 +418,16 @@ namespace Components
|
|||||||
auto staticModel = world->dpvs.smodelDrawInsts[i];
|
auto staticModel = world->dpvs.smodelDrawInsts[i];
|
||||||
if (staticModel.model)
|
if (staticModel.model)
|
||||||
{
|
{
|
||||||
auto dist = Utils::Vec3SqrDistance(playerPosition, staticModel.placement.origin);
|
const auto dist = Utils::Maths::Vec3SqrDistance(playerPosition, staticModel.placement.origin);
|
||||||
if (dist < sqrDist)
|
if (dist < static_cast<float>(sqrDist))
|
||||||
{
|
{
|
||||||
Game::R_AddDebugString(staticModelsColor, staticModel.placement.origin, 1.0, staticModel.model->name);
|
Game::R_AddDebugString(staticModelsColor, staticModel.placement.origin, 1.0, staticModel.model->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -467,14 +476,14 @@ namespace Components
|
|||||||
Utils::Hook(0x536A80, Renderer::BackendFrameStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x536A80, Renderer::BackendFrameStub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
// Begin device recovery (not D3D9Ex)
|
// Begin device recovery (not D3D9Ex)
|
||||||
Utils::Hook(0x508298, []()
|
Utils::Hook(0x508298, []
|
||||||
{
|
{
|
||||||
Game::DB_BeginRecoverLostDevice();
|
Game::DB_BeginRecoverLostDevice();
|
||||||
Renderer::BeginRecoverDeviceSignal();
|
Renderer::BeginRecoverDeviceSignal();
|
||||||
}, HOOK_CALL).install()->quick();
|
}, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
// End device recovery (not D3D9Ex)
|
// End device recovery (not D3D9Ex)
|
||||||
Utils::Hook(0x508355, []()
|
Utils::Hook(0x508355, []
|
||||||
{
|
{
|
||||||
Renderer::EndRecoverDeviceSignal();
|
Renderer::EndRecoverDeviceSignal();
|
||||||
Game::DB_EndRecoverLostDevice();
|
Game::DB_EndRecoverLostDevice();
|
||||||
|
@ -67,9 +67,9 @@ namespace Components
|
|||||||
Pipelines[type].execute();
|
Pipelines[type].execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scheduler::REndFrame_Hk()
|
void Scheduler::ScrPlace_EndFrame_Hk()
|
||||||
{
|
{
|
||||||
Utils::Hook::Call<void()>(0x50AB20)();
|
Utils::Hook::Call<void()>(0x4AA720)();
|
||||||
Execute(Pipeline::RENDERER);
|
Execute(Pipeline::RENDERER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,9 +165,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Utils::Hook(0x4DBE9A, REndFrame_Hk, HOOK_CALL).install()->quick();
|
Utils::Hook(0x5ACB9E, ScrPlace_EndFrame_Hk, HOOK_CALL).install()->quick();
|
||||||
Utils::Hook(0x518D5C, REndFrame_Hk, HOOK_CALL).install()->quick();
|
|
||||||
Utils::Hook(0x5ACBA3, REndFrame_Hk, HOOK_CALL).install()->quick();
|
|
||||||
|
|
||||||
// Hook G_Glass_Update so we may fix TLS issues
|
// Hook G_Glass_Update so we may fix TLS issues
|
||||||
Utils::Hook(0x416049, ServerFrame_Hk, HOOK_CALL).install()->quick();
|
Utils::Hook(0x416049, ServerFrame_Hk, HOOK_CALL).install()->quick();
|
||||||
|
@ -59,7 +59,7 @@ namespace Components
|
|||||||
|
|
||||||
static void Execute(Pipeline type);
|
static void Execute(Pipeline type);
|
||||||
|
|
||||||
static void REndFrame_Hk();
|
static void ScrPlace_EndFrame_Hk();
|
||||||
static void ServerFrame_Hk();
|
static void ServerFrame_Hk();
|
||||||
static void ClientFrame_Hk(int localClientNum);
|
static void ClientFrame_Hk(int localClientNum);
|
||||||
static void MainFrame_Hk();
|
static void MainFrame_Hk();
|
||||||
|
@ -64,5 +64,6 @@ namespace Components
|
|||||||
{
|
{
|
||||||
// Server command receive hook
|
// Server command receive hook
|
||||||
Utils::Hook(0x59449F, ServerCommands::CG_DeployServerCommand_Stub).install()->quick();
|
Utils::Hook(0x59449F, ServerCommands::CG_DeployServerCommand_Stub).install()->quick();
|
||||||
|
Utils::Hook::Nop(0x5944A4, 6);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -217,7 +217,7 @@ namespace Components
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Score and ping are irrelevant
|
// Score and ping are irrelevant
|
||||||
const auto* namePtr = Game::PartyHost_GetMemberName(reinterpret_cast<Game::PartyData_t*>(0x1081C00), i);
|
const auto* namePtr = Game::PartyHost_GetMemberName(reinterpret_cast<Game::PartyData*>(0x1081C00), i);
|
||||||
if (!namePtr || !namePtr[0]) continue;
|
if (!namePtr || !namePtr[0]) continue;
|
||||||
|
|
||||||
name = namePtr;
|
name = namePtr;
|
||||||
|
@ -27,11 +27,11 @@ namespace Components
|
|||||||
{
|
{
|
||||||
return &ServerList::OnlineList;
|
return &ServerList::OnlineList;
|
||||||
}
|
}
|
||||||
else if (ServerList::IsOfflineList())
|
if (ServerList::IsOfflineList())
|
||||||
{
|
{
|
||||||
return &ServerList::OfflineList;
|
return &ServerList::OfflineList;
|
||||||
}
|
}
|
||||||
else if (ServerList::IsFavouriteList())
|
if (ServerList::IsFavouriteList())
|
||||||
{
|
{
|
||||||
return &ServerList::FavouriteList;
|
return &ServerList::FavouriteList;
|
||||||
}
|
}
|
||||||
@ -289,6 +289,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Logger::Print("Could not resolve address for {}:{}", masterServerName, masterPort);
|
Logger::Print("Could not resolve address for {}:{}", masterServerName, masterPort);
|
||||||
Toast::Show("cardicon_headshot", "^1Error", Utils::String::VA("Could not resolve address for %s:%u", masterServerName, masterPort), 5000);
|
Toast::Show("cardicon_headshot", "^1Error", Utils::String::VA("Could not resolve address for %s:%u", masterServerName, masterPort), 5000);
|
||||||
|
useMasterServer = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -778,14 +779,14 @@ namespace Components
|
|||||||
Scheduler::Once([]
|
Scheduler::Once([]
|
||||||
{
|
{
|
||||||
ServerList::UIServerSelected = Dvar::Register<bool>("ui_serverSelected", false,
|
ServerList::UIServerSelected = Dvar::Register<bool>("ui_serverSelected", false,
|
||||||
Game::dvar_flag::DVAR_NONE, "Whether a server has been selected in the serverlist");
|
Game::DVAR_NONE, "Whether a server has been selected in the serverlist");
|
||||||
ServerList::UIServerSelectedMap = Dvar::Register<const char*>("ui_serverSelectedMap", "mp_afghan",
|
ServerList::UIServerSelectedMap = Dvar::Register<const char*>("ui_serverSelectedMap", "mp_afghan",
|
||||||
Game::dvar_flag::DVAR_NONE, "Map of the selected server");
|
Game::DVAR_NONE, "Map of the selected server");
|
||||||
|
|
||||||
ServerList::NETServerQueryLimit = Dvar::Register<int>("net_serverQueryLimit", 1,
|
ServerList::NETServerQueryLimit = Dvar::Register<int>("net_serverQueryLimit", 1,
|
||||||
1, 10, Dedicated::IsEnabled() ? Game::dvar_flag::DVAR_NONE : Game::dvar_flag::DVAR_ARCHIVE, "Amount of server queries per frame");
|
1, 10, Dedicated::IsEnabled() ? Game::DVAR_NONE : Game::DVAR_ARCHIVE, "Amount of server queries per frame");
|
||||||
ServerList::NETServerFrames = Dvar::Register<int>("net_serverFrames", 30,
|
ServerList::NETServerFrames = Dvar::Register<int>("net_serverFrames", 30,
|
||||||
1, 60, Dedicated::IsEnabled() ? Game::dvar_flag::DVAR_NONE : Game::dvar_flag::DVAR_ARCHIVE, "Amount of server query frames per second");
|
1, 60, Dedicated::IsEnabled() ? Game::DVAR_NONE : Game::DVAR_ARCHIVE, "Amount of server query frames per second");
|
||||||
}, Scheduler::Pipeline::MAIN);
|
}, Scheduler::Pipeline::MAIN);
|
||||||
|
|
||||||
// Fix ui_netsource dvar
|
// Fix ui_netsource dvar
|
||||||
@ -828,8 +829,8 @@ namespace Components
|
|||||||
|
|
||||||
// Set default masterServerName + port and save it
|
// Set default masterServerName + port and save it
|
||||||
Utils::Hook::Set<const char*>(0x60AD92, "master.xlabs.dev");
|
Utils::Hook::Set<const char*>(0x60AD92, "master.xlabs.dev");
|
||||||
Utils::Hook::Set<BYTE>(0x60AD90, Game::dvar_flag::DVAR_ARCHIVE); // masterServerName
|
Utils::Hook::Set<BYTE>(0x60AD90, Game::DVAR_NONE); // masterServerName
|
||||||
Utils::Hook::Set<BYTE>(0x60ADC6, Game::dvar_flag::DVAR_ARCHIVE); // masterPort
|
Utils::Hook::Set<BYTE>(0x60ADC6, Game::DVAR_NONE); // masterPort
|
||||||
|
|
||||||
// Add server list feeder
|
// Add server list feeder
|
||||||
UIFeeder::Add(2.0f, ServerList::GetServerCount, ServerList::GetServerText, ServerList::SelectServer);
|
UIFeeder::Add(2.0f, ServerList::GetServerCount, ServerList::GetServerText, ServerList::SelectServer);
|
||||||
|
@ -37,7 +37,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Network::SendCommand(delayData->target, delayData->command, delayData->data);
|
Network::SendCommand(delayData->target, delayData->command, delayData->data);
|
||||||
delete delayData;
|
delete delayData;
|
||||||
}, Scheduler::Pipeline::SERVER, 500ms + std::chrono::milliseconds(std::rand() % 200));
|
}, Scheduler::Pipeline::MAIN, 500ms + std::chrono::milliseconds(std::rand() % 200));
|
||||||
#else
|
#else
|
||||||
std::lock_guard _(Session::Mutex);
|
std::lock_guard _(Session::Mutex);
|
||||||
|
|
||||||
|
@ -198,7 +198,8 @@ namespace Components
|
|||||||
// First check if all versions are present
|
// First check if all versions are present
|
||||||
for (int i = 156;; ++i)
|
for (int i = 156;; ++i)
|
||||||
{
|
{
|
||||||
FileSystem::File definition(Utils::String::VA("%s/%d.json", filename.data(), i));
|
// We're on DB thread (OnLoad) so use DB thread for FS
|
||||||
|
FileSystem::File definition(Utils::String::VA("%s/%d.json", filename.data(), i), Game::FsThread::FS_THREAD_DATABASE);
|
||||||
if (!definition.exists()) break;
|
if (!definition.exists()) break;
|
||||||
|
|
||||||
std::vector<std::vector<std::string>> enumContainer;
|
std::vector<std::vector<std::string>> enumContainer;
|
||||||
|
@ -1599,10 +1599,10 @@ namespace Components
|
|||||||
{
|
{
|
||||||
currentColorTable = &colorTableDefault;
|
currentColorTable = &colorTableDefault;
|
||||||
|
|
||||||
cg_newColors = Dvar::Register<bool>("cg_newColors", true, Game::dvar_flag::DVAR_ARCHIVE, "Use Warfare 2 color code style.");
|
cg_newColors = Dvar::Register<bool>("cg_newColors", true, Game::DVAR_ARCHIVE, "Use Warfare 2 color code style.");
|
||||||
cg_fontIconAutocomplete = Dvar::Register<bool>("cg_fontIconAutocomplete", true, Game::dvar_flag::DVAR_ARCHIVE, "Show autocomplete for fonticons when typing.");
|
cg_fontIconAutocomplete = Dvar::Register<bool>("cg_fontIconAutocomplete", true, Game::DVAR_ARCHIVE, "Show autocomplete for fonticons when typing.");
|
||||||
cg_fontIconAutocompleteHint = Dvar::Register<bool>("cg_fontIconAutocompleteHint", true, Game::dvar_flag::DVAR_ARCHIVE, "Show hint text in autocomplete for fonticons.");
|
cg_fontIconAutocompleteHint = Dvar::Register<bool>("cg_fontIconAutocompleteHint", true, Game::DVAR_ARCHIVE, "Show hint text in autocomplete for fonticons.");
|
||||||
sv_customTextColor = Game::Dvar_RegisterColor("sv_customTextColor", 1, 0.7f, 0, 1, Game::dvar_flag::DVAR_CODINFO, "Color for the extended color code.");
|
sv_customTextColor = Game::Dvar_RegisterColor("sv_customTextColor", 1, 0.7f, 0, 1, Game::DVAR_CODINFO, "Color for the extended color code.");
|
||||||
|
|
||||||
// Initialize font icons when initializing UI
|
// Initialize font icons when initializing UI
|
||||||
Utils::Hook(0x4B5422, UI_Init_Hk, HOOK_CALL).install()->quick();
|
Utils::Hook(0x4B5422, UI_Init_Hk, HOOK_CALL).install()->quick();
|
||||||
@ -1617,11 +1617,11 @@ namespace Components
|
|||||||
Utils::Hook(0x417770, ColorIndex, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x417770, ColorIndex, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
// Add a colorblind mode for team colors
|
// Add a colorblind mode for team colors
|
||||||
r_colorBlind = Dvar::Register<bool>("r_colorBlind", false, Game::dvar_flag::DVAR_ARCHIVE, "Use color-blindness-friendly colors");
|
r_colorBlind = Dvar::Register<bool>("r_colorBlind", false, Game::DVAR_ARCHIVE, "Use color-blindness-friendly colors");
|
||||||
// A dark red
|
// A dark red
|
||||||
g_ColorBlind_EnemyTeam = Game::Dvar_RegisterColor("g_ColorBlind_EnemyTeam", 0.659f, 0.088f, 0.145f, 1, Game::dvar_flag::DVAR_ARCHIVE, "Enemy team color for colorblind mode");
|
g_ColorBlind_EnemyTeam = Game::Dvar_RegisterColor("g_ColorBlind_EnemyTeam", 0.659f, 0.088f, 0.145f, 1, Game::DVAR_ARCHIVE, "Enemy team color for colorblind mode");
|
||||||
// A bright yellow
|
// A bright yellow
|
||||||
g_ColorBlind_MyTeam = Game::Dvar_RegisterColor("g_ColorBlind_MyTeam", 1, 0.859f, 0.125f, 1, Game::dvar_flag::DVAR_ARCHIVE, "Ally team color for colorblind mode");
|
g_ColorBlind_MyTeam = Game::Dvar_RegisterColor("g_ColorBlind_MyTeam", 1, 0.859f, 0.125f, 1, Game::DVAR_ARCHIVE, "Ally team color for colorblind mode");
|
||||||
|
|
||||||
// Replace team colors with colorblind team colors when colorblind is enabled
|
// Replace team colors with colorblind team colors when colorblind is enabled
|
||||||
Utils::Hook(0x406530, GetUnpackedColorByNameStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x406530, GetUnpackedColorByNameStub, HOOK_JUMP).install()->quick();
|
||||||
|
@ -19,7 +19,7 @@ namespace Components
|
|||||||
void Theatre::RecordGamestateStub()
|
void Theatre::RecordGamestateStub()
|
||||||
{
|
{
|
||||||
int sequence = (*Game::serverMessageSequence - 1);
|
int sequence = (*Game::serverMessageSequence - 1);
|
||||||
Game::FS_Write(&sequence, 4, *Game::demoFile);
|
Game::FS_WriteToDemo(&sequence, 4, *Game::demoFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Theatre::StoreBaseline(PBYTE snapshotMsg)
|
void Theatre::StoreBaseline(PBYTE snapshotMsg)
|
||||||
@ -62,10 +62,10 @@ namespace Components
|
|||||||
int byte8 = 8;
|
int byte8 = 8;
|
||||||
char byte0 = 0;
|
char byte0 = 0;
|
||||||
|
|
||||||
Game::FS_Write(&byte0, 1, *Game::demoFile);
|
Game::FS_WriteToDemo(&byte0, 1, *Game::demoFile);
|
||||||
Game::FS_Write(Game::serverMessageSequence, 4, *Game::demoFile);
|
Game::FS_WriteToDemo(Game::serverMessageSequence, 4, *Game::demoFile);
|
||||||
Game::FS_Write(&fileCompressedSize, 4, *Game::demoFile);
|
Game::FS_WriteToDemo(&fileCompressedSize, 4, *Game::demoFile);
|
||||||
Game::FS_Write(&byte8, 4, *Game::demoFile);
|
Game::FS_WriteToDemo(&byte8, 4, *Game::demoFile);
|
||||||
|
|
||||||
for (int i = 0; i < compressedSize; i += 1024)
|
for (int i = 0; i < compressedSize; i += 1024)
|
||||||
{
|
{
|
||||||
@ -73,11 +73,11 @@ namespace Components
|
|||||||
|
|
||||||
if (i + size >= sizeof(cmpData))
|
if (i + size >= sizeof(cmpData))
|
||||||
{
|
{
|
||||||
Logger::Print("Error: Writing compressed demo baseline exceeded buffer\n");
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "Writing compressed demo baseline exceeded buffer\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::FS_Write(&cmpData[i], size, *Game::demoFile);
|
Game::FS_WriteToDemo(&cmpData[i], size, *Game::demoFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,8 +342,8 @@ namespace Components
|
|||||||
|
|
||||||
Theatre::Theatre()
|
Theatre::Theatre()
|
||||||
{
|
{
|
||||||
Dvar::Register<bool>("cl_autoRecord", true, Game::dvar_flag::DVAR_ARCHIVE, "Automatically record games.");
|
Dvar::Register<bool>("cl_autoRecord", true, Game::DVAR_ARCHIVE, "Automatically record games.");
|
||||||
Dvar::Register<int>("cl_demosKeep", 30, 1, 999, Game::dvar_flag::DVAR_ARCHIVE, "How many demos to keep with autorecord.");
|
Dvar::Register<int>("cl_demosKeep", 30, 1, 999, Game::DVAR_ARCHIVE, "How many demos to keep with autorecord.");
|
||||||
|
|
||||||
Utils::Hook(0x5A8370, Theatre::GamestateWriteStub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x5A8370, Theatre::GamestateWriteStub, HOOK_CALL).install()->quick();
|
||||||
Utils::Hook(0x5A85D2, Theatre::RecordGamestateStub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x5A85D2, Theatre::RecordGamestateStub, HOOK_CALL).install()->quick();
|
||||||
|
@ -5,30 +5,16 @@ namespace Components
|
|||||||
std::queue<Toast::UIToast> Toast::Queue;
|
std::queue<Toast::UIToast> Toast::Queue;
|
||||||
std::mutex Toast::Mutex;
|
std::mutex Toast::Mutex;
|
||||||
|
|
||||||
void Toast::Show(const std::string& image, const std::string& title, const std::string& description, int length, Utils::Slot<void()> callback)
|
void Toast::Show(const std::string& image, const std::string& title, const std::string& description, int length, const Utils::Slot<void()>& callback)
|
||||||
{
|
{
|
||||||
Game::Material* material = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, image.data()).material;
|
Game::Material* material = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, image.data()).material;
|
||||||
return Show(material, title, description, length, callback);
|
return Show(material, title, description, length, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Toast::Show(Game::Material* material, const std::string& title, const std::string& description, int length, Utils::Slot<void()> callback)
|
void Toast::Show(Game::Material* material, const std::string& title, const std::string& description, int length, const Utils::Slot<void()>& callback)
|
||||||
{
|
{
|
||||||
Toast::Mutex.lock();
|
std::lock_guard _(Mutex);
|
||||||
Toast::Queue.push({ material, Utils::String::ToUpper(title), description, length, 0, callback });
|
Queue.push({material, Utils::String::ToUpper(title), description, length, 0, callback});
|
||||||
Toast::Mutex.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Toast::GetIcon()
|
|
||||||
{
|
|
||||||
char ourPath[MAX_PATH] = { 0 };
|
|
||||||
std::string file(ourPath, GetModuleFileNameA(GetModuleHandle(nullptr), ourPath, sizeof(ourPath)));
|
|
||||||
|
|
||||||
auto pos = file.find_last_of("/\\");
|
|
||||||
if (pos != std::string::npos) file = file.substr(0, pos);
|
|
||||||
|
|
||||||
file.append("\\iw4x\\images\\icon.png");
|
|
||||||
Utils::String::Replace(file, "/", "\\");
|
|
||||||
return file;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Toast::Draw(UIToast* toast)
|
void Toast::Draw(UIToast* toast)
|
||||||
@ -51,9 +37,14 @@ namespace Components
|
|||||||
float fontSize = 0.9f;
|
float fontSize = 0.9f;
|
||||||
float descSize = 0.9f;
|
float descSize = 0.9f;
|
||||||
|
|
||||||
Game::Material* white = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, "white").material; if (!white) return;
|
Game::Font_s* font = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FONT, "fonts/objectiveFont").font;
|
||||||
Game::Font_s* font = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FONT, "fonts/objectiveFont").font; if (!font) return;
|
Game::Font_s* descfont = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FONT, "fonts/normalFont").font;
|
||||||
Game::Font_s* descfont = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FONT, "fonts/normalFont").font; if (!descfont) return;
|
|
||||||
|
if (font == nullptr || descfont == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Game::vec4_t wColor = { 1.0f, 1.0f, 1.0f, 1.0f };
|
Game::vec4_t wColor = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||||
Game::vec4_t bgColor = { 0.0f, 0.0f, 0.0f, 0.8f };
|
Game::vec4_t bgColor = { 0.0f, 0.0f, 0.0f, 0.8f };
|
||||||
Game::vec4_t borderColor = { 1.0f, 1.0f, 1.0f, 0.2f };
|
Game::vec4_t borderColor = { 1.0f, 1.0f, 1.0f, 0.2f };
|
||||||
@ -64,7 +55,7 @@ namespace Components
|
|||||||
if (Game::Sys_Milliseconds() < startTime || (startTime + duration) < Game::Sys_Milliseconds()) return;
|
if (Game::Sys_Milliseconds() < startTime || (startTime + duration) < Game::Sys_Milliseconds()) return;
|
||||||
|
|
||||||
// Fadein stuff
|
// Fadein stuff
|
||||||
else if (Game::Sys_Milliseconds() - startTime < slideTime)
|
if (Game::Sys_Milliseconds() - startTime < slideTime)
|
||||||
{
|
{
|
||||||
int diffH = Renderer::Height() / 5;
|
int diffH = Renderer::Height() / 5;
|
||||||
int diff = Game::Sys_Milliseconds() - startTime;
|
int diff = Game::Sys_Milliseconds() - startTime;
|
||||||
@ -88,8 +79,8 @@ namespace Components
|
|||||||
// Calculate width data
|
// Calculate width data
|
||||||
int iOffset = (bHeight - imgDim) / 2;
|
int iOffset = (bHeight - imgDim) / 2;
|
||||||
int iOffsetLeft = iOffset * 2;
|
int iOffsetLeft = iOffset * 2;
|
||||||
float titleSize = Game::R_TextWidth(toast->title.data(), 0x7FFFFFFF, font) * fontSize;
|
float titleSize = Game::R_TextWidth(toast->title.data(), std::numeric_limits<int>::max(), font) * fontSize;
|
||||||
float descrSize = Game::R_TextWidth(toast->desc.data(), 0x7FFFFFFF, descfont) * descSize;
|
float descrSize = Game::R_TextWidth(toast->desc.data(), std::numeric_limits<int>::max(), descfont) * descSize;
|
||||||
float bWidth = iOffsetLeft * 3 + imgDim + std::max(titleSize, descrSize);
|
float bWidth = iOffsetLeft * 3 + imgDim + std::max(titleSize, descrSize);
|
||||||
|
|
||||||
// Make stuff divisible by 2
|
// Make stuff divisible by 2
|
||||||
@ -99,33 +90,40 @@ namespace Components
|
|||||||
bHeight += (bHeight % 2);
|
bHeight += (bHeight % 2);
|
||||||
|
|
||||||
// Background
|
// Background
|
||||||
Game::CL_DrawStretchPicPhysical(static_cast<float>(width / 2 - bWidth / 2), static_cast<float>(height - bHeight / 2), bWidth * 1.0f, bHeight * 1.0f, 0, 0, 1.0f, 1.0f, bgColor, white);
|
Game::CL_DrawStretchPicPhysical(static_cast<float>(width / 2 - bWidth / 2), static_cast<float>(height - bHeight / 2), bWidth * 1.0f, bHeight * 1.0f, 0, 0, 1.0f, 1.0f, bgColor, *Game::whiteMaterial);
|
||||||
|
|
||||||
// Border
|
// Border
|
||||||
Game::CL_DrawStretchPicPhysical(static_cast<float>(width / 2 - bWidth / 2 - border), static_cast<float>(height - bHeight / 2 - border), border * 1.0f, bHeight + (border * 2.0f), 0, 0, 1.0f, 1.0f, borderColor, white); // Left
|
Game::CL_DrawStretchPicPhysical(static_cast<float>(width / 2 - bWidth / 2 - border), static_cast<float>(height - bHeight / 2 - border), border * 1.0f, bHeight + (border * 2.0f), 0, 0, 1.0f, 1.0f, borderColor, *Game::whiteMaterial); // Left
|
||||||
Game::CL_DrawStretchPicPhysical(static_cast<float>(width / 2 - bWidth / 2 + bWidth), static_cast<float>(height - bHeight / 2 - border), border * 1.0f, bHeight + (border * 2.0f), 0, 0, 1.0f, 1.0f, borderColor, white); // Right
|
Game::CL_DrawStretchPicPhysical(static_cast<float>(width / 2 - bWidth / 2 + bWidth), static_cast<float>(height - bHeight / 2 - border), border * 1.0f, bHeight + (border * 2.0f), 0, 0, 1.0f, 1.0f, borderColor, *Game::whiteMaterial); // Right
|
||||||
Game::CL_DrawStretchPicPhysical(static_cast<float>(width / 2 - bWidth / 2), static_cast<float>(height - bHeight / 2 - border), bWidth * 1.0f, border * 1.0f, 0, 0, 1.0f, 1.0f, borderColor, white); // Top
|
Game::CL_DrawStretchPicPhysical(static_cast<float>(width / 2 - bWidth / 2), static_cast<float>(height - bHeight / 2 - border), bWidth * 1.0f, border * 1.0f, 0, 0, 1.0f, 1.0f, borderColor, *Game::whiteMaterial); // Top
|
||||||
Game::CL_DrawStretchPicPhysical(static_cast<float>(width / 2 - bWidth / 2), static_cast<float>(height + bHeight / 2), bWidth * 1.0f, border * 1.0f, 0, 0, 1.0f, 1.0f, borderColor, white); // Bottom
|
Game::CL_DrawStretchPicPhysical(static_cast<float>(width / 2 - bWidth / 2), static_cast<float>(height + bHeight / 2), bWidth * 1.0f, border * 1.0f, 0, 0, 1.0f, 1.0f, borderColor, *Game::whiteMaterial); // Bottom
|
||||||
|
|
||||||
// Image
|
// Image
|
||||||
Game::Material* image = toast->image;
|
Game::Material* image = toast->image;
|
||||||
if (!Materials::IsValid(image)) image = Game::DB_FindXAssetDefaultHeaderInternal(Game::XAssetType::ASSET_TYPE_MATERIAL).material;
|
if (!Materials::IsValid(image))
|
||||||
|
{
|
||||||
|
image = Game::DB_FindXAssetDefaultHeaderInternal(Game::XAssetType::ASSET_TYPE_MATERIAL).material;
|
||||||
|
}
|
||||||
|
|
||||||
Game::CL_DrawStretchPicPhysical(static_cast<float>(width / 2 - bWidth / 2 + iOffsetLeft), static_cast<float>(height - bHeight / 2 + iOffset), imgDim * 1.0f, imgDim * 1.0f, 0, 0, 1.0f, 1.0f, wColor, image);
|
Game::CL_DrawStretchPicPhysical(static_cast<float>(width / 2 - bWidth / 2 + iOffsetLeft), static_cast<float>(height - bHeight / 2 + iOffset), imgDim * 1.0f, imgDim * 1.0f, 0, 0, 1.0f, 1.0f, wColor, image);
|
||||||
|
|
||||||
// Text
|
// Text
|
||||||
float leftText = width / 2 - bWidth / 2 - cornerSize + iOffsetLeft * 2 + imgDim;
|
float leftText = width / 2 - bWidth / 2 - cornerSize + iOffsetLeft * 2 + imgDim;
|
||||||
float rightText = width / 2 + bWidth / 2 - cornerSize - iOffsetLeft;
|
float rightText = width / 2 + bWidth / 2 - cornerSize - iOffsetLeft;
|
||||||
Game::R_AddCmdDrawText(toast->title.data(), 0x7FFFFFFF, font, static_cast<float>(leftText + (rightText - leftText) / 2 - titleSize / 2 + cornerSize), static_cast<float>(height - bHeight / 2 + cornerSize * 2 + 7), fontSize, fontSize, 0, wColor, Game::ITEM_TEXTSTYLE_SHADOWED); // Title
|
Game::R_AddCmdDrawText(toast->title.data(), std::numeric_limits<int>::max(), font, static_cast<float>(leftText + (rightText - leftText) / 2 - titleSize / 2 + cornerSize), static_cast<float>(height - bHeight / 2 + cornerSize * 2 + 7), fontSize, fontSize, 0, wColor, Game::ITEM_TEXTSTYLE_SHADOWED); // Title
|
||||||
Game::R_AddCmdDrawText(toast->desc.data(), 0x7FFFFFFF, descfont, leftText + (rightText - leftText) / 2 - descrSize / 2 + cornerSize, static_cast<float>(height - bHeight / 2 + cornerSize * 2 + 33), descSize, descSize, 0, wColor, Game::ITEM_TEXTSTYLE_SHADOWED); // Description
|
Game::R_AddCmdDrawText(toast->desc.data(), std::numeric_limits<int>::max(), descfont, leftText + (rightText - leftText) / 2 - descrSize / 2 + cornerSize, static_cast<float>(height - bHeight / 2 + cornerSize * 2 + 33), descSize, descSize, 0, wColor, Game::ITEM_TEXTSTYLE_SHADOWED); // Description
|
||||||
}
|
}
|
||||||
|
|
||||||
void Toast::Handler()
|
void Toast::Handler()
|
||||||
{
|
{
|
||||||
if (Toast::Queue.empty()) return;
|
if (Queue.empty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<std::mutex> _(Toast::Mutex);
|
std::lock_guard _(Mutex);
|
||||||
|
|
||||||
Toast::UIToast* toast = &Toast::Queue.front();
|
UIToast* toast = &Queue.front();
|
||||||
|
|
||||||
// Set start time
|
// Set start time
|
||||||
if (!toast->start)
|
if (!toast->start)
|
||||||
@ -136,30 +134,28 @@ namespace Components
|
|||||||
if ((toast->start + toast->length) < Game::Sys_Milliseconds())
|
if ((toast->start + toast->length) < Game::Sys_Milliseconds())
|
||||||
{
|
{
|
||||||
if (toast->callback) toast->callback();
|
if (toast->callback) toast->callback();
|
||||||
Toast::Queue.pop();
|
Queue.pop();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Toast::Draw(toast);
|
Draw(toast);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Toast::Toast()
|
Toast::Toast()
|
||||||
{
|
{
|
||||||
if (Dedicated::IsEnabled() || Monitor::IsEnabled()) return;
|
if (Dedicated::IsEnabled() || Monitor::IsEnabled() || ZoneBuilder::IsEnabled())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Scheduler::OnGameInitialized(Toast::Handler, Scheduler::Pipeline::RENDERER);
|
Scheduler::Loop(Handler, Scheduler::Pipeline::RENDERER);
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef TEST_TOAST
|
||||||
Command::Add("testtoast", []([[maybe_unused]] Command::Params* params)
|
Command::Add("testtoast", []([[maybe_unused]] Command::Params* params)
|
||||||
{
|
{
|
||||||
Toast::Show("cardicon_prestige10", "Test", "This is a test toast", 3000);
|
Show("cardicon_prestige10", "Test", "This is a test toast", 3000);
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Toast::~Toast()
|
|
||||||
{
|
|
||||||
Toast::Queue = std::queue<Toast::UIToast>();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,8 @@ namespace Components
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Toast();
|
Toast();
|
||||||
~Toast();
|
static void Show(const std::string& image, const std::string& title, const std::string& description, int length, const Utils::Slot<void()>& callback = Utils::Slot<void()>());
|
||||||
|
static void Show(Game::Material* material, const std::string& title, const std::string& description, int length, const Utils::Slot<void()>& callback = Utils::Slot<void()>());
|
||||||
static void Show(const std::string& image, const std::string& title, const std::string& description, int length, Utils::Slot<void()> callback = Utils::Slot<void()>());
|
|
||||||
static void Show(Game::Material* material, const std::string& title, const std::string& description, int length, Utils::Slot<void()> callback = Utils::Slot<void()>());
|
|
||||||
|
|
||||||
static std::string GetIcon();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class UIToast
|
class UIToast
|
||||||
|
@ -383,9 +383,9 @@ namespace Components
|
|||||||
|
|
||||||
Scheduler::Once([]
|
Scheduler::Once([]
|
||||||
{
|
{
|
||||||
Dvar::Register<const char*>("ui_map_long", "Afghan", Game::dvar_flag::DVAR_NONE, "");
|
Dvar::Register<const char*>("ui_map_long", "Afghan", Game::DVAR_NONE, "");
|
||||||
Dvar::Register<const char*>("ui_map_name", "mp_afghan", Game::dvar_flag::DVAR_NONE, "");
|
Dvar::Register<const char*>("ui_map_name", "mp_afghan", Game::DVAR_NONE, "");
|
||||||
Dvar::Register<const char*>("ui_map_desc", "", Game::dvar_flag::DVAR_NONE, "");
|
Dvar::Register<const char*>("ui_map_desc", "", Game::DVAR_NONE, "");
|
||||||
}, Scheduler::Pipeline::MAIN);
|
}, Scheduler::Pipeline::MAIN);
|
||||||
|
|
||||||
// Get feeder item count
|
// Get feeder item count
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "GSC/Script.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -51,6 +52,7 @@ namespace Components
|
|||||||
if (name == nullptr)
|
if (name == nullptr)
|
||||||
{
|
{
|
||||||
Game::Scr_ParamError(0, "^1SetName: Illegal parameter!\n");
|
Game::Scr_ParamError(0, "^1SetName: Illegal parameter!\n");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::Debug("Setting name of {} to {}", ent->s.number, name);
|
Logger::Debug("Setting name of {} to {}", ent->s.number, name);
|
||||||
@ -66,6 +68,31 @@ namespace Components
|
|||||||
UserInfoOverrides[ent->s.number].erase("name");
|
UserInfoOverrides[ent->s.number].erase("name");
|
||||||
Game::ClientUserinfoChanged(ent->s.number);
|
Game::ClientUserinfoChanged(ent->s.number);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Script::AddMethod("SetClanTag", [](Game::scr_entref_t entref) // gsc: self SetClanTag(<string>)
|
||||||
|
{
|
||||||
|
const auto* ent = Game::GetPlayerEntity(entref);
|
||||||
|
const auto* clanName = Game::Scr_GetString(0);
|
||||||
|
|
||||||
|
if (clanName == nullptr)
|
||||||
|
{
|
||||||
|
Game::Scr_ParamError(0, "^1SetClanTag: Illegal parameter!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::Debug("Setting clanName of {} to {}", ent->s.number, clanName);
|
||||||
|
UserInfoOverrides[ent->s.number]["clanAbbrev"] = clanName;
|
||||||
|
Game::ClientUserinfoChanged(ent->s.number);
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddMethod("ResetClanTag", [](Game::scr_entref_t entref) // gsc: self ResetClanTag()
|
||||||
|
{
|
||||||
|
const auto* ent = Game::GetPlayerEntity(entref);
|
||||||
|
|
||||||
|
Logger::Debug("Resetting clanName of {}", ent->s.number);
|
||||||
|
UserInfoOverrides[ent->s.number].erase("clanAbbrev");
|
||||||
|
Game::ClientUserinfoChanged(ent->s.number);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
UserInfo::UserInfo()
|
UserInfo::UserInfo()
|
||||||
|
137
src/Components/Modules/VisionFile.cpp
Normal file
137
src/Components/Modules/VisionFile.cpp
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
|
namespace Components
|
||||||
|
{
|
||||||
|
std::vector<std::string> VisionFile::DvarExceptions =
|
||||||
|
{
|
||||||
|
"r_pretess",
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_map<std::string, std::string> VisionFile::VisionReplacements
|
||||||
|
{
|
||||||
|
{"511", "r_glow"},
|
||||||
|
{"516", "r_glowRadius0"},
|
||||||
|
{"512", "r_glowBloomCutoff"},
|
||||||
|
{"513", "r_glowBloomDesaturation"},
|
||||||
|
{"514", "r_glowBloomIntensity0"},
|
||||||
|
{"520", "r_filmEnable"},
|
||||||
|
{"522", "r_filmContrast"},
|
||||||
|
{"521", "r_filmBrightness"},
|
||||||
|
{"523", "r_filmDesaturation"},
|
||||||
|
{"524", "r_filmDesaturationDark"},
|
||||||
|
{"525", "r_filmInvert"},
|
||||||
|
{"526", "r_filmLightTint"},
|
||||||
|
{"527", "r_filmMediumTint"},
|
||||||
|
{"528", "r_filmDarkTint"},
|
||||||
|
{"529", "r_primaryLightUseTweaks"},
|
||||||
|
{"530", "r_primaryLightTweakDiffuseStrength"},
|
||||||
|
{"531", "r_primaryLightTweakSpecularStrength"},
|
||||||
|
};
|
||||||
|
|
||||||
|
bool VisionFile::ApplyExemptDvar(const char* dvarName, const char** buffer, const char* filename)
|
||||||
|
{
|
||||||
|
for (auto& exceptions : DvarExceptions)
|
||||||
|
{
|
||||||
|
if (!_stricmp(dvarName, exceptions.data()))
|
||||||
|
{
|
||||||
|
const auto* dvar = Game::Dvar_FindVar(dvarName);
|
||||||
|
const auto* parsedValue = Game::Com_ParseOnLine(buffer);
|
||||||
|
|
||||||
|
assert(dvar);
|
||||||
|
assert(parsedValue);
|
||||||
|
|
||||||
|
Game::Dvar_SetFromStringFromSource(dvar, parsedValue, Game::DvarSetSource::DVAR_SOURCE_INTERNAL);
|
||||||
|
Logger::Print("Overriding '{}' from '{}'\n", dvar->name, filename);
|
||||||
|
|
||||||
|
// Successfully found and tried to apply the string value to the dvar
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VisionFile::LoadVisionSettingsFromBuffer(const char* buffer, const char* filename, Game::visionSetVars_t* settings)
|
||||||
|
{
|
||||||
|
assert(settings);
|
||||||
|
|
||||||
|
bool wasRead[21]{};
|
||||||
|
Game::Com_BeginParseSession(filename);
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
auto* token = Game::Com_Parse(&buffer);
|
||||||
|
|
||||||
|
if (!*token)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto found = false;
|
||||||
|
auto fieldNum = 0;
|
||||||
|
|
||||||
|
const auto it = VisionReplacements.find(token);
|
||||||
|
for (fieldNum = 0; fieldNum < 21; ++fieldNum)
|
||||||
|
{
|
||||||
|
if (!wasRead[fieldNum] && !_stricmp((it == VisionReplacements.end()) ? token : it->second.data(), Game::visionDefFields[fieldNum].name))
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
if (!ApplyExemptDvar(token, &buffer, filename))
|
||||||
|
{
|
||||||
|
Logger::Warning(Game::CON_CHANNEL_SYSTEM, "WARNING: unknown dvar '{}' in file '{}'\n", token, filename);
|
||||||
|
Game::Com_SkipRestOfLine(&buffer);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
token = Game::Com_ParseOnLine(&buffer);
|
||||||
|
if (ApplyTokenToField(fieldNum, token, settings))
|
||||||
|
{
|
||||||
|
wasRead[fieldNum] = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger::Warning(Game::CON_CHANNEL_SYSTEM, "WARNING: malformed dvar '{}' in file '{}'\n", token, filename);
|
||||||
|
Game::Com_SkipRestOfLine(&buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::Com_EndParseSession();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(naked) bool VisionFile::LoadVisionSettingsFromBuffer_Stub()
|
||||||
|
{
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
push eax
|
||||||
|
pushad
|
||||||
|
|
||||||
|
push [esp + 0x24 + 0x8] // settings
|
||||||
|
push ebx // filename
|
||||||
|
push [esp + 0x24 + 0xC] // buffer
|
||||||
|
call LoadVisionSettingsFromBuffer
|
||||||
|
add esp, 0xC
|
||||||
|
|
||||||
|
mov [esp + 0x20], eax
|
||||||
|
popad
|
||||||
|
pop eax
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VisionFile::VisionFile()
|
||||||
|
{
|
||||||
|
AssertSize(Game::visField_t, 12);
|
||||||
|
|
||||||
|
// Place hook in LoadVisionFile function
|
||||||
|
Utils::Hook(0x59A98A, LoadVisionSettingsFromBuffer_Stub, HOOK_CALL).install()->quick();
|
||||||
|
}
|
||||||
|
}
|
19
src/Components/Modules/VisionFile.hpp
Normal file
19
src/Components/Modules/VisionFile.hpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Components
|
||||||
|
{
|
||||||
|
class VisionFile : public Component
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VisionFile();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::vector<std::string> DvarExceptions;
|
||||||
|
static std::unordered_map<std::string, std::string> VisionReplacements;
|
||||||
|
|
||||||
|
static bool ApplyExemptDvar(const char* dvarName, const char** buffer, const char* filename);
|
||||||
|
|
||||||
|
static bool LoadVisionSettingsFromBuffer(const char* buffer, const char* filename, Game::visionSetVars_t* settings);
|
||||||
|
static bool LoadVisionSettingsFromBuffer_Stub();
|
||||||
|
};
|
||||||
|
}
|
@ -1,18 +1,17 @@
|
|||||||
#include <STDInclude.hpp>
|
#include <STDInclude.hpp>
|
||||||
|
#include "GSC/Script.hpp"
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
Game::XAssetHeader Weapon::WeaponFileLoad(Game::XAssetType /*type*/, const std::string& filename)
|
Game::WeaponCompleteDef* Weapon::LoadWeaponCompleteDef(const char* name)
|
||||||
{
|
{
|
||||||
Game::XAssetHeader header = { nullptr };
|
if (auto* rawWeaponFile = Game::BG_LoadWeaponCompleteDefInternal("mp", name))
|
||||||
|
|
||||||
// Try loading raw weapon
|
|
||||||
if (FileSystem::File(Utils::String::VA("weapons/mp/%s", filename.data())).exists())
|
|
||||||
{
|
{
|
||||||
header.data = Game::BG_LoadWeaponDef_LoadObj(filename.data());
|
return rawWeaponFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
return header;
|
auto* zoneWeaponFile = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_WEAPON, name).weapon;
|
||||||
|
return Game::DB_IsXAssetDefault(Game::ASSET_TYPE_WEAPON, name) ? nullptr : zoneWeaponFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* Weapon::GetWeaponConfigString(int index)
|
const char* Weapon::GetWeaponConfigString(int index)
|
||||||
@ -448,13 +447,104 @@ namespace Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void __declspec(naked) Weapon::CG_UpdatePrimaryForAltModeWeapon_Stub()
|
||||||
|
{
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
mov eax, 0x440EB0 // BG_GetWeaponDef
|
||||||
|
call eax
|
||||||
|
add esp, 0x4
|
||||||
|
|
||||||
|
test eax, eax
|
||||||
|
jz null
|
||||||
|
|
||||||
|
// Game code
|
||||||
|
push 0x59E349
|
||||||
|
retn
|
||||||
|
|
||||||
|
null:
|
||||||
|
mov al, 1
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __declspec(naked) Weapon::CG_SelectWeaponIndex_Stub()
|
||||||
|
{
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
mov eax, 0x440EB0 // BG_GetWeaponDef
|
||||||
|
call eax
|
||||||
|
add esp, 0x4
|
||||||
|
|
||||||
|
test eax, eax
|
||||||
|
jz null
|
||||||
|
|
||||||
|
// Game code
|
||||||
|
push 0x48BB2D
|
||||||
|
retn
|
||||||
|
|
||||||
|
null:
|
||||||
|
push 0x48BB1F // Exit function
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __declspec(naked) Weapon::WeaponEntCanBeGrabbed_Stub()
|
||||||
|
{
|
||||||
|
using namespace Game;
|
||||||
|
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
cmp dword ptr [esp + 0x8], 0x0
|
||||||
|
jz touched
|
||||||
|
|
||||||
|
push 0x56E82C
|
||||||
|
retn
|
||||||
|
|
||||||
|
touched:
|
||||||
|
test dword ptr [edi + 0x2BC], PWF_DISABLE_WEAPON_PICKUP
|
||||||
|
jnz exit_func
|
||||||
|
|
||||||
|
// Game code
|
||||||
|
test eax, eax
|
||||||
|
jz continue_func
|
||||||
|
|
||||||
|
exit_func:
|
||||||
|
xor eax, eax
|
||||||
|
ret
|
||||||
|
|
||||||
|
continue_func:
|
||||||
|
push 0x56E84C
|
||||||
|
retn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Weapon::AddScriptMethods()
|
||||||
|
{
|
||||||
|
Script::AddMethod("DisableWeaponPickup", [](Game::scr_entref_t entref)
|
||||||
|
{
|
||||||
|
const auto* ent = Game::GetPlayerEntity(entref);
|
||||||
|
|
||||||
|
ent->client->ps.weapCommon.weapFlags |= Game::PWF_DISABLE_WEAPON_PICKUP;
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddMethod("EnableWeaponPickup", [](Game::scr_entref_t entref)
|
||||||
|
{
|
||||||
|
const auto* ent = Game::GetPlayerEntity(entref);
|
||||||
|
|
||||||
|
ent->client->ps.weapCommon.weapFlags &= ~Game::PWF_DISABLE_WEAPON_PICKUP;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Weapon::Weapon()
|
Weapon::Weapon()
|
||||||
{
|
{
|
||||||
Weapon::PatchLimit();
|
PatchLimit();
|
||||||
Weapon::PatchConfigStrings();
|
PatchConfigStrings();
|
||||||
|
|
||||||
// Intercept weapon loading
|
// BG_LoadWEaponCompleteDef_FastFile
|
||||||
AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_WEAPON, Weapon::WeaponFileLoad);
|
Utils::Hook(0x57B650, LoadWeaponCompleteDef, HOOK_JUMP).install()->quick();
|
||||||
|
// Disable warning if raw weapon file cannot be found
|
||||||
|
Utils::Hook::Nop(0x57AF60, 5);
|
||||||
|
|
||||||
// weapon asset existence check
|
// weapon asset existence check
|
||||||
Utils::Hook::Nop(0x408228, 5); // find asset header
|
Utils::Hook::Nop(0x408228, 5); // find asset header
|
||||||
@ -466,16 +556,18 @@ namespace Components
|
|||||||
|
|
||||||
// Weapon swap fix
|
// Weapon swap fix
|
||||||
Utils::Hook::Nop(0x4B3670, 5);
|
Utils::Hook::Nop(0x4B3670, 5);
|
||||||
Utils::Hook(0x57B4F0, LoadNoneWeaponHookStub).install()->quick();
|
Utils::Hook(0x57B4F0, LoadNoneWeaponHookStub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
// Don't load bounce sounds for now, it causes crashes
|
|
||||||
// TODO: Actually check the weaponfiles and/or reset the soundtable correctly!
|
|
||||||
//Utils::Hook::Nop(0x57A360, 5);
|
|
||||||
//Utils::Hook::Nop(0x57A366, 6);
|
|
||||||
Utils::Hook::Nop(0x5795E9, 2);
|
|
||||||
|
|
||||||
// Clear weapons independently from fs_game
|
// Clear weapons independently from fs_game
|
||||||
//Utils::Hook::Nop(0x452C1D, 2);
|
Utils::Hook::Nop(0x452C1D, 2);
|
||||||
//Utils::Hook::Nop(0x452C24, 5);
|
Utils::Hook::Nop(0x452C24, 5);
|
||||||
|
|
||||||
|
Utils::Hook(0x59E341, CG_UpdatePrimaryForAltModeWeapon_Stub, HOOK_JUMP).install()->quick();
|
||||||
|
Utils::Hook(0x48BB25, CG_SelectWeaponIndex_Stub, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
|
AddScriptMethods();
|
||||||
|
|
||||||
|
AssertOffset(Game::playerState_s, Game::playerState_s::weapCommon.weapFlags, 0x2BC);
|
||||||
|
Utils::Hook(0x56E825, WeaponEntCanBeGrabbed_Stub, HOOK_JUMP).install()->quick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ namespace Components
|
|||||||
Weapon();
|
Weapon();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Game::XAssetHeader WeaponFileLoad(Game::XAssetType type, const std::string& filename);
|
static Game::WeaponCompleteDef* LoadWeaponCompleteDef(const char* name);
|
||||||
static void PatchLimit();
|
static void PatchLimit();
|
||||||
static void* LoadNoneWeaponHook();
|
static void* LoadNoneWeaponHook();
|
||||||
static void LoadNoneWeaponHookStub();
|
static void LoadNoneWeaponHookStub();
|
||||||
@ -25,5 +25,11 @@ namespace Components
|
|||||||
static void ParseConfigStrings();
|
static void ParseConfigStrings();
|
||||||
static int ParseWeaponConfigStrings();
|
static int ParseWeaponConfigStrings();
|
||||||
static int ClearConfigStrings(void* dest, int value, int size);
|
static int ClearConfigStrings(void* dest, int value, int size);
|
||||||
|
|
||||||
|
static void CG_UpdatePrimaryForAltModeWeapon_Stub();
|
||||||
|
static void CG_SelectWeaponIndex_Stub();
|
||||||
|
|
||||||
|
static void WeaponEntCanBeGrabbed_Stub();
|
||||||
|
static void AddScriptMethods();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -165,8 +165,8 @@ namespace Components
|
|||||||
Window::Window()
|
Window::Window()
|
||||||
{
|
{
|
||||||
// Borderless window
|
// Borderless window
|
||||||
Window::NoBorder = Dvar::Register<bool>("r_noborder", true, Game::dvar_flag::DVAR_ARCHIVE, "Do not use a border in windowed mode");
|
Window::NoBorder = Dvar::Register<bool>("r_noborder", true, Game::DVAR_ARCHIVE, "Do not use a border in windowed mode");
|
||||||
Window::NativeCursor = Dvar::Register<bool>("ui_nativeCursor", false, Game::dvar_flag::DVAR_ARCHIVE, "Display native cursor");
|
Window::NativeCursor = Dvar::Register<bool>("ui_nativeCursor", false, Game::DVAR_ARCHIVE, "Display native cursor");
|
||||||
|
|
||||||
Utils::Hook(0x507643, Window::StyleHookStub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x507643, Window::StyleHookStub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
|
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