Skip to content

Commit

Permalink
Implement player model glow feature
Browse files Browse the repository at this point in the history
  • Loading branch information
danielkrupinski committed Nov 13, 2024
1 parent d0c0de9 commit 71aca7c
Show file tree
Hide file tree
Showing 31 changed files with 375 additions and 12 deletions.
7 changes: 7 additions & 0 deletions Source/CS2/Classes/CSceneObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,16 @@ enum SceneObjectFlags {
SceneObjectFlag_IsDeleted = 0x20
};

struct SceneObjectAttributes {
struct FloatAttributes {};

FloatAttributes floatAttributes;
};

struct CSceneObject {
using m_nObjectClass = std::uint8_t;
using flags = std::uint8_t;
using attributes = SceneObjectAttributes*;
};

}
1 change: 1 addition & 0 deletions Source/CS2/Classes/CSceneSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ struct CSceneObject;

struct CSceneSystem {
using DeleteSceneObject = void(CSceneSystem* sceneSystem, CSceneObject* sceneObject);
using AllocateAttributeList = void(CSceneSystem* sceneSystem, CSceneObject* sceneObject);
};

}
2 changes: 2 additions & 0 deletions Source/CS2/Classes/Entities/C_CSPlayerPawn.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <CS2/Classes/EntitySystem/CEntityHandle.h>
#include <CS2/Classes/SceneObjectUpdaterHandle_t.h>
#include <Platform/Macros/PlatformSpecific.h>

#include "C_BaseModelEntity.h"
Expand All @@ -19,6 +20,7 @@ struct C_CSPlayerPawn : C_BaseModelEntity {
using m_bIsGrabbingHostage = bool;
using m_pHostageServices = CCSPlayer_HostageServices*;
using m_flFlashBangTime = float;
using sceneObjectUpdaterHandle = SceneObjectUpdaterHandle_t*;
};

}
5 changes: 5 additions & 0 deletions Source/CS2/Classes/SceneObjectUpdaterHandle_t.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
#pragma once

#include <cstdint>

namespace cs2
{

struct CSceneObject;

struct SceneObjectUpdaterHandle_t {
using sceneObject = CSceneObject*;

void* updaterFunctionParameter;
std::uint64_t(*updaterFunction)(void* parameter);
};

}
8 changes: 8 additions & 0 deletions Source/Endpoints.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,12 @@ void ViewRenderHook_onRenderStart_cpp(cs2::CViewRender* thisptr) noexcept
dependencies.make<GlowSceneObjects>().removeUnreferencedObjects();
}

#if IS_LINUX()
[[gnu::aligned(2)]]
#endif
std::uint64_t PlayerPawn_sceneObjectUpdater_cpp(cs2::C_CSPlayerPawn* playerPawn) noexcept
{
return GlobalContext::instance().playerPawnSceneObjectUpdater(playerPawn);
}

}
5 changes: 4 additions & 1 deletion Source/FeatureHelpers/RenderingHookEntityLoop.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <CS2/Classes/Entities/CEntityInstance.h>
#include <Features/Visuals/ModelGlow/ModelGlow.h>
#include <Features/Visuals/OutlineGlow/OutlineGlow.h>
#include <Features/Visuals/PlayerInfoInWorld/PlayerInfoInWorld.h>

Expand All @@ -16,6 +17,7 @@ class RenderingHookEntityLoop {
void run() const noexcept
{
hookContext.template make<EntitySystem>().iterateEntities([this](auto& entity) { handleEntity(entity); });
hookContext.template make<ModelGlow>().onEntityListTraversed();
}

private:
Expand All @@ -25,9 +27,10 @@ class RenderingHookEntityLoop {

const auto entityTypeInfo = hookContext.entityClassifier().classifyEntity(hookContext.gameDependencies().entitiesVMTs, entity.vmt);

if (entityTypeInfo.typeIndex == utils::typeIndex<cs2::C_CSPlayerPawn, KnownEntityTypes>()) {
if (entityTypeInfo.template is<cs2::C_CSPlayerPawn>()) {
auto&& playerPawn = hookContext.template make<PlayerPawn>(static_cast<cs2::C_CSPlayerPawn*>(&entity));
playerInformationThroughWalls.drawPlayerInformation(playerPawn);
hookContext.template make<ModelGlow>().updateSceneObjectUpdaterHook(playerPawn);
}

if (entityTypeInfo.isModelEntity())
Expand Down
112 changes: 112 additions & 0 deletions Source/Features/Visuals/ModelGlow/ModelGlow.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#pragma once

#include <CS2/Constants/ColorConstants.h>
#include <FeatureHelpers/TeamNumber.h>

#include "ModelGlowState.h"

extern "C" std::uint64_t PlayerPawn_sceneObjectUpdater_asm(cs2::C_CSPlayerPawn* playerPawn) noexcept;
extern "C" std::uint64_t PlayerPawn_sceneObjectUpdater_cpp(cs2::C_CSPlayerPawn* playerPawn) noexcept;

template <typename HookContext>
class ModelGlow {
public:
ModelGlow(HookContext& hookContext) noexcept
: hookContext{hookContext}
{
}

void updateSceneObjectUpdaterHook(auto&& playerPawn) const noexcept
{
if (!shouldUpdateSceneObjectUpdaterHook())
return;

if (shouldGlowPlayerModel(playerPawn))
hookPlayerSceneObjectUpdater(playerPawn);
else
unhookPlayerSceneObjectUpdater(playerPawn);
}

void applyModelGlow(auto&& playerPawn) const noexcept
{
if (shouldRun() && shouldGlowPlayerModel(playerPawn))
playerPawn.baseEntity().applySpawnProtectionEffectRecursively(getColor(playerPawn));
}

void onEntityListTraversed() const noexcept
{
if (state().masterSwitch == ModelGlowState::State::Disabling) {
state().masterSwitch = ModelGlowState::State::Disabled;
hookContext.hooks().viewRenderHook.decrementReferenceCount();
}

if (state().playerModelGlow == ModelGlowState::State::Disabling)
state().playerModelGlow = ModelGlowState::State::Disabled;
}

private:
[[nodiscard]] bool shouldUpdateSceneObjectUpdaterHook() const noexcept
{
return state().masterSwitch != ModelGlowState::State::Disabled && state().playerModelGlow != ModelGlowState::State::Disabled;
}

[[nodiscard]] bool shouldRun() const noexcept
{
return state().masterSwitch == ModelGlowState::State::Enabled && state().playerModelGlow == ModelGlowState::State::Enabled;
}

void hookPlayerSceneObjectUpdater(auto&& playerPawn) const noexcept
{
if (!hasSceneObjectUpdaterHooked(playerPawn)) {
storeOriginalSceneObjectUpdater(playerPawn);
hookSceneObjectUpdater(playerPawn);
}
}

void unhookPlayerSceneObjectUpdater(auto&& playerPawn) const noexcept
{
if (hasSceneObjectUpdaterHooked(playerPawn))
playerPawn.setSceneObjectUpdater(state().originalPlayerPawnSceneObjectUpdater);
}

void storeOriginalSceneObjectUpdater(auto&& playerPawn) const noexcept
{
if (state().originalPlayerPawnSceneObjectUpdater == nullptr)
state().originalPlayerPawnSceneObjectUpdater = playerPawn.getSceneObjectUpdater();
}

[[nodiscard]] bool hasSceneObjectUpdaterHooked(auto&& playerPawn) const noexcept
{
return playerPawn.getSceneObjectUpdater() == &PlayerPawn_sceneObjectUpdater_cpp;
}

void hookSceneObjectUpdater(auto&& playerPawn) const noexcept
{
playerPawn.setSceneObjectUpdater(&PlayerPawn_sceneObjectUpdater_cpp);
}

[[nodiscard]] bool shouldGlowPlayerModel(auto&& playerPawn) const noexcept
{
return playerPawn.isAlive().value_or(true)
&& playerPawn.health().greaterThan(0).valueOr(true)
&& !playerPawn.isControlledByLocalPlayer()
&& playerPawn.isTTorCT()
&& (!state().showOnlyEnemies || playerPawn.isEnemy().value_or(true));
}

[[nodiscard]] auto& state() const noexcept
{
return hookContext.featuresStates().visualFeaturesStates.modelGlowState;
}

[[nodiscard]] cs2::Color getColor(auto&& playerPawn) const noexcept
{
switch (playerPawn.teamNumber()) {
case TeamNumber::TT: return cs2::Color{255, 179, 0};
case TeamNumber::CT: return cs2::Color{0, 127, 255};
default: return cs2::kColorWhite;
}
}

HookContext& hookContext;
};
18 changes: 18 additions & 0 deletions Source/Features/Visuals/ModelGlow/ModelGlowState.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

#include <cstdint>
#include <CS2/Classes/Entities/C_CSPlayerPawn.h>

struct ModelGlowState {
enum class State : std::uint8_t {
Enabled,
Disabling,
Disabled
};

State masterSwitch{State::Disabled};
State playerModelGlow{State::Enabled};
bool showOnlyEnemies{true};

std::uint64_t(*originalPlayerPawnSceneObjectUpdater)(cs2::C_CSPlayerPawn* playerPawn){nullptr};
};
42 changes: 42 additions & 0 deletions Source/Features/Visuals/ModelGlow/ModelGlowToggle.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#pragma once

#include <FeatureHelpers/FeatureToggle.h>

template <typename HookContext>
class ModelGlowToggle {
public:
ModelGlowToggle(HookContext& hookContext) noexcept
: hookContext{hookContext}
{
}

void updateMasterSwitch(char option) noexcept
{
switch (option) {
case '0':
state().masterSwitch = ModelGlowState::State::Enabled;
hookContext.hooks().viewRenderHook.incrementReferenceCount();
break;
case '1':
state().masterSwitch = ModelGlowState::State::Disabling;
break;
}
}

void updatePlayerModelGlowToggle(char option) noexcept
{
switch (option) {
case '0': state().playerModelGlow = ModelGlowState::State::Enabled; state().showOnlyEnemies = true; break;
case '1': state().playerModelGlow = ModelGlowState::State::Enabled; state().showOnlyEnemies = false; break;
case '2': state().playerModelGlow = ModelGlowState::State::Disabling; break;
}
}

private:
[[nodiscard]] auto& state() const noexcept
{
return hookContext.featuresStates().visualFeaturesStates.modelGlowState;
}

HookContext& hookContext;
};
6 changes: 6 additions & 0 deletions Source/Features/Visuals/VisualFeatures.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <FeatureHelpers/FeatureHelpers.h>
#include "ModelGlow/ModelGlowToggle.h"
#include "PlayerInfoInWorld/PlayerInfoInWorld.h"
#include "OutlineGlow/DefuseKitOutlineGlow/DefuseKitOutlineGlowToggle.h"
#include "OutlineGlow/DroppedBombOutlineGlow/DroppedBombOutlineGlowToggle.h"
Expand Down Expand Up @@ -128,6 +129,11 @@ struct VisualFeatures {
return hookContext.template make<HostageOutlineGlowToggle>();
}

[[nodiscard]] decltype(auto) modelGlowToggle() const noexcept
{
return hookContext.template make<ModelGlowToggle>();
}

HookContext& hookContext;
VisualFeaturesStates& states;
ViewRenderHook& viewRenderHook;
Expand Down
2 changes: 2 additions & 0 deletions Source/Features/Visuals/VisualFeaturesStates.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include "ModelGlow/ModelGlowState.h"
#include "PlayerInfoInWorld/PlayerInfoInWorldState.h"
#include "OutlineGlow/DefuseKitOutlineGlow/DefuseKitOutlineGlowState.h"
#include "OutlineGlow/DroppedBombOutlineGlow/DroppedBombOutlineGlowState.h"
Expand All @@ -20,4 +21,5 @@ struct VisualFeaturesStates {
DroppedBombOutlineGlowState droppedBombOutlineGlowState;
TickingBombOutlineGlowState tickingBombOutlineGlowState;
HostageOutlineGlowState hostageOutlineGlowState;
ModelGlowState modelGlowState;
};
33 changes: 31 additions & 2 deletions Source/GameClasses/BaseEntity.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,35 @@ class BaseEntity {

void applyGlow(cs2::Color color, int glowRange = 0) const noexcept
{
renderComponent().sceneObjectUpdaters().forEachSceneObject([this, color, glowRange](auto&& sceneObject){
renderComponent().sceneObjectUpdaters().forEachSceneObject([this, color, glowRange](auto&& sceneObject) {
auto&& glowSceneObject = hookContext.template make<GlowSceneObjects>().getGlowSceneObject(sceneObject);
glowSceneObject.apply(sceneObject, color, glowRange);
glowSceneObject.setGlowEntity(*this);
});
}

void applySpawnProtectionEffect(cs2::Color color) const noexcept
{
renderComponent().sceneObjectUpdaters().forEachSceneObject([this, color](auto&& sceneObject) {
#if IS_WIN64()
const auto unknownEntityAttribute = getAttributeInt(0x8AD232BC, 0); // todo: find out the attribute name
#else
const auto unknownEntityAttribute = 0;
#endif
if (unknownEntityAttribute == 0) {
auto&& sceneObjectAttributes = sceneObject.attributes();
sceneObjectAttributes.setAttributeFloat(0x244EC9B0, 1.0f); // "SpawnInvulnerability"
sceneObjectAttributes.setAttributeColor3(0xB2CAF4DF, color); // "InvulnerabilityColor"
}
});
}

void applySpawnProtectionEffectRecursively(cs2::Color color) const noexcept
{
applySpawnProtectionEffect(color);
forEachChild([color](auto&& entity) { entity.applySpawnProtectionEffect(color); });
}

[[nodiscard]] auto hasOwner() const noexcept
{
return hookContext.clientPatternSearchResults().template get<OffsetToOwnerEntity>().of(entity).toOptional().notEqual(cs2::CEntityHandle{cs2::INVALID_EHANDLE_INDEX});
Expand All @@ -82,7 +104,7 @@ class BaseEntity {
{
return TeamNumber{hookContext.clientPatternSearchResults().template get<OffsetToTeamNumber>().of(entity).valueOr({})};
}

[[nodiscard]] auto vData() const noexcept
{
return hookContext.clientPatternSearchResults().template get<OffsetToVData>().of(entity).toOptional();
Expand All @@ -107,6 +129,13 @@ class BaseEntity {
}

private:
[[nodiscard]] int getAttributeInt(unsigned int attributeNameHash, int defaultValue) const noexcept
{
if (entity)
return hookContext.clientPatternSearchResults().template get<GetEntityAttributeInt>()(entity, attributeNameHash, defaultValue, nullptr);
return defaultValue;
}

[[nodiscard]] auto invokeWithGameSceneNodeOwner(auto& f) const noexcept
{
return [&f](auto&& gameSceneNode) { f(gameSceneNode.owner()); };
Expand Down
16 changes: 16 additions & 0 deletions Source/GameClasses/PlayerPawn.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,23 @@ class PlayerPawn {
return weaponServices().getActiveWeapon();
}

[[nodiscard]] auto getSceneObjectUpdater() const noexcept
{
return reinterpret_cast<std::uint64_t(*)(cs2::C_CSPlayerPawn*)>(sceneObjectUpdaterHandle() ? sceneObjectUpdaterHandle()->updaterFunction : nullptr);
}

void setSceneObjectUpdater(auto x) const noexcept
{
if (sceneObjectUpdaterHandle())
sceneObjectUpdaterHandle()->updaterFunction = reinterpret_cast<std::uint64_t(*)(void*)>(x);
}

private:
[[nodiscard]] auto sceneObjectUpdaterHandle() const noexcept
{
return hookContext.clientPatternSearchResults().template get<OffsetToPlayerPawnSceneObjectUpdaterHandle>().of(playerPawn).valueOr(nullptr);
}

[[nodiscard]] decltype(auto) hostageServices() const noexcept
{
return hookContext.template make<HostageServices>(hookContext.clientPatternSearchResults().template get<OffsetToHostageServices>().of(playerPawn).valueOr(nullptr));
Expand Down
Loading

0 comments on commit 71aca7c

Please sign in to comment.