Skip to content

Commit 3819003

Browse files
committed
feat: start implementing demo clean start feature
1 parent afcc590 commit 3819003

File tree

9 files changed

+231
-11
lines changed

9 files changed

+231
-11
lines changed

src/Entity.cpp

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ static std::unordered_map<std::string, std::unordered_map<std::string, std::pair
110110
static std::unordered_map<std::string, std::unordered_map<std::string, std::pair<size_t, EntField::Type>>> g_client_offsets;
111111

112112
const std::pair<size_t, EntField::Type> &EntField::getServerOffset(void *ent, const char *field) {
113-
ServerClass *sc = Memory::VMT<ServerClass *(__rescall *)(void *)>(ent, Offsets::GetServerClass)(ent);
113+
ServerClass *sc = ((ServerEnt*)ent)->GetServerClass();
114114
datamap_t *dm = Memory::VMT<datamap_t *(__rescall *)(void *)>(ent, Offsets::S_GetDataDescMap)(ent);
115115

116116
// dumb, but construct class name by concating datamap and serverclass names
@@ -127,12 +127,7 @@ const std::pair<size_t, EntField::Type> &EntField::getServerOffset(void *ent, co
127127
}
128128

129129
const std::pair<size_t, EntField::Type> &EntField::getClientOffset(void *ent, const char *field) {
130-
// multiple inheritance is cringe
131-
#ifdef _WIN32
132-
ClientClass *cc = ((ClientClass *(__rescall *)(void *))((void ***)ent)[2][2])(ent);
133-
#else
134-
ClientClass *cc = Memory::VMT<ClientClass *(__rescall *)(void *)>(ent, 19)(ent);
135-
#endif
130+
ClientClass *cc = ((ClientEnt *)ent)->GetClientClass();
136131
datamap_t *pm = Memory::VMT<datamap_t *(__rescall *)(void *)>(ent, Offsets::GetPredDescMap)(ent); // datamap used for prediction stuff
137132
datamap_t *dm = Memory::VMT<datamap_t *(__rescall *)(void *)>(ent, Offsets::C_GetDataDescMap)(ent);
138133

@@ -150,6 +145,22 @@ const std::pair<size_t, EntField::Type> &EntField::getClientOffset(void *ent, co
150145
return g_client_offsets[class_name][std::string(field)];
151146
}
152147

148+
ServerClass *ServerEnt::GetServerClass() {
149+
ServerClass *sc = Memory::VMT<ServerClass *(__rescall *)(void *)>(this, Offsets::GetServerClass)(this);
150+
return sc;
151+
}
152+
153+
ClientClass *ClientEnt::GetClientClass() {
154+
// multiple inheritance is cringe
155+
#ifdef _WIN32
156+
ClientClass *cc = ((ClientClass * (__rescall *)(void *))((void ***)this)[2][2])(this);
157+
#else
158+
ClientClass *cc = Memory::VMT<ClientClass *(__rescall *)(void *)>(this, 19)(this);
159+
#endif
160+
return cc;
161+
}
162+
163+
153164
static const char *g_type_names[] = {
154165
"void",
155166
"bool",

src/Entity.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ namespace EntField {
3737
M2(ANY_INT, SHORT, signed short)
3838
M2(ANY_INT, INT, signed int)
3939
M2(ANY_INT, INT, unsigned int)
40+
M2(ANY_INT, COLOR, color32)
4041
M1(FLOAT, float)
4142
M1(STRING, const char *)
4243
M1(STRING, char *)
@@ -79,6 +80,8 @@ struct ServerEnt {
7980
// Ensure type is opaque
8081
ServerEnt(const ServerEnt &) = delete;
8182

83+
ServerClass *GetServerClass();
84+
8285
template <typename T> T &field(const char *field) {
8386
auto val = EntField::getServerOffset(this, field);
8487
if (!EntField::matchFieldType<T>(val.second)) EntField::warnBadFieldType(this, field, typeid(T).name(), val.second, true);
@@ -114,6 +117,8 @@ struct ClientEnt {
114117
// Ensure type is opaque
115118
ClientEnt(const ClientEnt &) = delete;
116119

120+
ClientClass *GetClientClass();
121+
117122
template <typename T> T &field(const char *field) {
118123
auto val = EntField::getClientOffset(this, field);
119124
if (!EntField::matchFieldType<T>(val.second)) EntField::warnBadFieldType(this, field, typeid(T).name(), val.second, false);
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#include <Event.hpp>
2+
#include <Variable.hpp>
3+
#include <Modules/Client.hpp>
4+
#include <Modules/Engine.hpp>
5+
#include <Features/Renderer.hpp>
6+
7+
Variable sar_demo_clean_start("sar_demo_clean_start", "0", 0,
8+
"Attempts to minimize visual interpolation of some elements (like post-processing or lighting) when demo playback begins.\n", 0);
9+
Variable sar_demo_clean_start_tonemap("sar_demo_clean_start_tonemap", "0", 0,
10+
"Overrides initial tonemap scalar value used in auto-exposure."
11+
"Setting it to 0 will attempt to skip over to target value for several ticks.\n", 0);
12+
13+
14+
bool g_tonemapScaleDemoLookupInProgress = false;
15+
int g_tonemapScaleDemoLookupTick;
16+
bool g_tonemapScaleContinouosSearchEnabled = false;
17+
int g_tonemapScaleTweakStartTick = 0;
18+
19+
const float TONEMAP_SCALE_SEARCH_DISABLE_THRESHOLD = 0.01f;
20+
const int TONEMAP_SCALE_SEARCH_MAX_NOCHANGE_TICKS = 30;
21+
22+
void startTonemapScaleDemoLookup(int tick) {
23+
g_tonemapScaleDemoLookupInProgress = true;
24+
g_tonemapScaleDemoLookupTick = tick;
25+
}
26+
27+
void stopTonemapScaleDemoLookup(bool finished = false) {
28+
if (!g_tonemapScaleDemoLookupInProgress) {
29+
return;
30+
}
31+
32+
if (!finished) {
33+
console->Print("Failed to sample tonemap scale before the end of the demo.\n");
34+
}
35+
g_tonemapScaleDemoLookupInProgress = false;
36+
g_tonemapScaleDemoLookupTick = -1;
37+
}
38+
39+
void updateTonemapScaleDemoLookup() {
40+
if (!g_tonemapScaleDemoLookupInProgress) {
41+
return;
42+
}
43+
44+
if (!engine->demoplayer->IsPlaying() || engine->demoplayer->GetTick() < g_tonemapScaleDemoLookupTick) {
45+
return;
46+
}
47+
48+
float tonemapScale = client->GetCurrentTonemappingSystem()->m_flCurrentTonemapScale;
49+
sar_demo_clean_start_tonemap.SetValue(tonemapScale);
50+
console->Print("Sampled tonemap scale value %.6f and stored it in \"sar_demo_clean_start_tonemap\" variable.\n", tonemapScale);
51+
52+
stopTonemapScaleDemoLookup(true);
53+
}
54+
55+
void triggerTonemapScaleTweak() {
56+
float tonemapScaleOverride = sar_demo_clean_start_tonemap.GetFloat();
57+
58+
if (tonemapScaleOverride == 0.0f) {
59+
g_tonemapScaleContinouosSearchEnabled = true;
60+
auto tonemapSystem = client->GetCurrentTonemappingSystem();
61+
g_tonemapScaleTweakStartTick = tonemapSystem->m_nCurrentQueryFrame;
62+
}
63+
64+
client->ResetToneMapping(tonemapScaleOverride);
65+
}
66+
67+
void disableTonemapScaleContinouosSearch() {
68+
g_tonemapScaleContinouosSearchEnabled = false;
69+
}
70+
71+
void updateTonemapScaleContinouosSearch() {
72+
if (!g_tonemapScaleContinouosSearchEnabled) {
73+
return;
74+
}
75+
76+
auto tonemapSystem = client->GetCurrentTonemappingSystem();
77+
if (tonemapSystem == nullptr) {
78+
disableTonemapScaleContinouosSearch();
79+
return;
80+
}
81+
82+
float diff = tonemapSystem->m_flTargetTonemapScale - tonemapSystem->m_flCurrentTonemapScale;
83+
84+
if (fabsf(diff) > TONEMAP_SCALE_SEARCH_DISABLE_THRESHOLD) {
85+
// last sampled luminance was different enough to change target scale, reset it
86+
tonemapSystem->m_flCurrentTonemapScale = tonemapSystem->m_flTargetTonemapScale;
87+
return;
88+
}
89+
90+
int searchDuration = tonemapSystem->m_nCurrentQueryFrame - g_tonemapScaleTweakStartTick;
91+
if (searchDuration > TONEMAP_SCALE_SEARCH_MAX_NOCHANGE_TICKS) {
92+
disableTonemapScaleContinouosSearch();
93+
}
94+
}
95+
96+
void forceProjectedTexturesTargetColor() {
97+
for (int i = 0; i < Offsets::NUM_ENT_ENTRIES; ++i) {
98+
auto clientEntity = client->GetEntity(i);
99+
if (!clientEntity) {
100+
continue;
101+
}
102+
103+
if (strcmp(clientEntity->GetClientClass()->m_pNetworkName, "CEnvProjectedTexture") != 0) {
104+
continue;
105+
}
106+
107+
auto targetColor = clientEntity->field<color32>("m_LightColor");
108+
auto &m_CurrentLinearFlotLightColor = clientEntity->fieldOff<Vector>("m_LightColor", 0x04);
109+
auto &m_flCurrentLinearFloatLightAlpha = clientEntity->fieldOff<float>("m_LightColor", 0x10);
110+
111+
m_CurrentLinearFlotLightColor = Vector(targetColor.r, targetColor.g, targetColor.b);
112+
m_flCurrentLinearFloatLightAlpha = targetColor.a;
113+
}
114+
}
115+
116+
CON_COMMAND(sar_demo_clean_start_tonemap_sample,
117+
"sar_demo_clean_start_tonemap_sample [tick] - samples tonemap scale from current demo "
118+
"at given tick and stores it in \"sar_demo_clean_start_tonemap\" variable. "
119+
"If no tick is given, sampling will happen when __END__ is seen in demo playback.") {
120+
if (args.ArgC() > 2) {
121+
return console->Print(sar_demo_clean_start_tonemap_sample.ThisPtr()->m_pszHelpString);
122+
}
123+
124+
if (!engine->demoplayer->IsPlaying()) {
125+
return console->Print("No demo is currently being played back.\n");
126+
}
127+
128+
g_tonemapScaleDemoLookupInProgress = true;
129+
130+
if (args.ArgC() == 2) {
131+
startTonemapScaleDemoLookup(std::atoi(args[1]));
132+
return console->Print("Tonemap sampling set up to trigger at tick %d\n", g_tonemapScaleDemoLookupTick);
133+
} else {
134+
startTonemapScaleDemoLookup(Renderer::segmentEndTick);
135+
return console->Print("Tonemap sampling set up to trigger at __END__ tick (%d)\n", g_tonemapScaleDemoLookupTick);
136+
}
137+
}
138+
139+
140+
ON_EVENT(FRAME) {
141+
updateTonemapScaleContinouosSearch();
142+
updateTonemapScaleDemoLookup();
143+
}
144+
145+
ON_EVENT(DEMO_START) {
146+
if (!sar_demo_clean_start.GetBool()) {
147+
return;
148+
}
149+
150+
triggerTonemapScaleTweak();
151+
forceProjectedTexturesTargetColor();
152+
}
153+
154+
ON_EVENT(DEMO_STOP) {
155+
disableTonemapScaleContinouosSearch();
156+
stopTonemapScaleDemoLookup();
157+
}

src/Modules/Client.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ DECL_CVAR_CALLBACK(cl_viewmodelfov) {
144144
fovChanger->Force();
145145
}
146146

147-
ClientEnt *Client::GetPlayer(int index) {
147+
ClientEnt *Client::GetEntity(int index) {
148148
if ((index & Offsets::ENT_ENTRY_MASK) == Offsets::ENT_ENTRY_MASK) {
149149
return nullptr;
150150
}
@@ -996,6 +996,9 @@ bool Client::Init() {
996996

997997
Client::DispatchParticleEffect = (Client::_DispatchParticleEffect)Memory::Scan(this->Name(), Offsets::DispatchParticleEffect);
998998
Client::PrecacheParticleSystem = (Client::_PrecacheParticleSystem)Memory::Scan(this->Name(), Offsets::PrecacheParticleSystem);
999+
1000+
Client::GetCurrentTonemappingSystem = (Client::_GetCurrentTonemappingSystem)Memory::Scan(this->Name(), Offsets::GetCurrentTonemappingSystem);
1001+
Client::ResetToneMapping = (Client::_ResetToneMapping)Memory::Scan(this->Name(), Offsets::ResetToneMapping);
9991002

10001003
this->g_ClientDLL->Hook(Client::LevelInitPreEntity_Hook, Client::LevelInitPreEntity, Offsets::LevelInitPreEntity);
10011004

src/Modules/Client.hpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ class Client : public Module {
4848
using _AddAvatarPanelItem = void(__cdecl *)(void *pLeaderboard, void *pStatLists, const PortalLeaderboardItem_t *pData, int nScore, int nType, int nPlayerType, int nAvatarIndex, int nHeight, int nSlot, bool bHUDElement);
4949
using _PrecacheParticleSystem = int(__cdecl *)(const char *pszParticleName);
5050
using _DispatchParticleEffect = void (__cdecl *)(const char *pszParticleName, Vector vecOrigin, Vector vecStart, QAngle vecAngles, void *pEntity, int nSplitScreenPlayerSlot, void *filter);
51-
51+
using _GetCurrentTonemappingSystem = CTonemapSystem*(*)();
52+
using _ResetToneMapping = void(__cdecl *)(float flTonemappingScale);
5253

5354
_GetClientEntity GetClientEntity = nullptr;
5455
_KeyDown KeyDown = nullptr;
@@ -63,6 +64,8 @@ class Client : public Module {
6364
_AddAvatarPanelItem AddAvatarPanelItem = nullptr;
6465
_DispatchParticleEffect DispatchParticleEffect = nullptr;
6566
_PrecacheParticleSystem PrecacheParticleSystem = nullptr;
67+
_GetCurrentTonemappingSystem GetCurrentTonemappingSystem = nullptr;
68+
_ResetToneMapping ResetToneMapping = nullptr;
6669

6770
ChapterContextData_t *g_ChapterContextNames;
6871
ChapterContextData_t *g_ChapterMPContextNames;
@@ -79,7 +82,9 @@ class Client : public Module {
7982
void **gamerules;
8083

8184
public:
82-
ClientEnt *GetPlayer(int index);
85+
ClientEnt *GetEntity(int index);
86+
inline ClientEnt *GetPlayer(int index) { return GetEntity(index); }
87+
8388
void CalcButtonBits(int nSlot, int &bits, int in_button, int in_ignore, kbutton_t *button, bool reset);
8489
bool ShouldDrawCrosshair();
8590
void Chat(Color col, const char *str);

src/Offsets/Portal 2 9568.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,9 @@ SIGSCAN_DEFAULT(PrecacheParticleSystem, "55 8B EC 8B 0D ? ? ? ? 8B 01 8B 50 ? 56
443443
SIGSCAN_DEFAULT(DispatchParticleEffect, "55 8B EC 8B 45 ? 50 E8 ? ? ? ? 8B 4D ? 8B 55 ? F3 0F 7E 45 ? 83 C4 04 51 8B 4D ? 52 8B 55 ? 51 83 EC 0C 8B CC 66 0F D6 01 F3 0F 7E 45 ? 89 51 ? 8B 55 ? 83 EC 0C 8B CC 66 0F D6 01 F3 0F 7E 45",
444444
"53 81 EC 84 00 00 00 8B 9C 24 ? ? ? ? FF B4 24 ? ? ? ? E8 ? ? ? ? 31 D2 F3 0F 10 84 24 ? ? ? ? C7 44 24 ? FF FF FF FF") // "projected_wall_impact" xref -> either usage same function -> DispatchParticleEffect
445445

446+
SIGSCAN_DEFAULT(GetCurrentTonemappingSystem, "F6 05 ? ? ? ? 01 75 ? 83 0D ? ? ? ? 01 56 57", "55 89 E5 53 83 EC 04 0F B6 05 ? ? ? ? 84 C0 74 ? A1 ? ? ? ?")
447+
SIGSCAN_DEFAULT(ResetToneMapping, "55 8B EC 83 EC 14 F3 0F 10 45 ?", "55 89 E5 53 83 EC 14 E8 ? ? ? ? 66 0F EF C0")
448+
446449
// Engine
447450
SIGSCAN_DEFAULT(ParseSmoothingInfoSig, "55 8B EC 0F 57 C0 81 EC ? ? ? ? B9 ? ? ? ? 8D 85 ? ? ? ? EB", ""); // "cl_demosmootherpanel.cpp" xref -> CDemoSmootherPanel::ParseSmoothingInfo
448451
OFFSET_DEFAULT(ParseSmoothingInfoOff, 178, -1)

src/Utils/SDK.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "SDK/PlayerLocalData.hpp"
2626
#include "SDK/PortalPlacement.hpp"
2727
#include "SDK/ServerPlugin.hpp"
28+
#include "SDK/TonemapSystem.hpp"
2829
#include "SDK/Trace.hpp"
2930
#include "SDK/UserCmd.hpp"
3031
#include "SDK/UtlMemory.hpp"

src/Utils/SDK/Color.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ struct Color {
2323

2424
struct color32 {
2525
bool operator!=( const struct color32 &other ) const;
26-
int r, g, b, a;
26+
uint8_t r, g, b, a;
2727

2828
inline unsigned *asInt(void) { return reinterpret_cast<unsigned*>(this); }
2929
inline const unsigned *asInt(void) const { return reinterpret_cast<const unsigned*>(this); }

src/Utils/SDK/TonemapSystem.hpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#pragma once
2+
3+
#define NUM_HISTOGRAM_BUCKETS 31
4+
5+
enum HistogramEntryState_t {
6+
HESTATE_INITIAL = 0,
7+
HESTATE_FIRST_QUERY_IN_FLIGHT,
8+
HESTATE_QUERY_IN_FLIGHT,
9+
HESTATE_QUERY_DONE,
10+
};
11+
12+
struct CHistogramBucket {
13+
HistogramEntryState_t m_state;
14+
int m_hOcclusionQueryHandle;
15+
int m_nFrameQueued;
16+
int m_nPixels;
17+
int m_nPixelsInRange;
18+
float m_flMinLuminance, m_flMaxLuminance;
19+
float m_flScreenMinX, m_flScreenMinY, m_flScreenMaxX, m_flScreenMaxY;
20+
};
21+
22+
struct CTonemapSystem {
23+
CHistogramBucket m_histogramBucketArray[NUM_HISTOGRAM_BUCKETS];
24+
int m_nCurrentQueryFrame;
25+
int m_nCurrentAlgorithm;
26+
27+
float m_flTargetTonemapScale;
28+
float m_flCurrentTonemapScale;
29+
30+
int m_nNumMovingAverageValid;
31+
float m_movingAverageTonemapScale[10];
32+
33+
bool m_bOverrideTonemapScaleEnabled;
34+
float m_flOverrideTonemapScale;
35+
};

0 commit comments

Comments
 (0)