Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 13 additions & 14 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ To trigger builds:
- **Remove trailing whitespace**

### Memory Management
- Use `Handle` for DHooks detours (current pattern in codebase)
- Use the `DynamicDetour` methodmap for DHooks detours and the `GameData` methodmap for gamedata
- **Important**: Use `delete` instead of `CloseHandle()` for newer SourceMod versions
- Never check for null before `delete` - it's safe to call on null handles
- For StringMap/ArrayList: Use `delete` instead of `.Clear()` to prevent memory leaks
Expand All @@ -71,33 +71,37 @@ When working with DHooks in this codebase:

```sourcepawn
// 1. Load gamedata
Handle hGameConf = LoadGameConfigFile("FixFuncRotating.games");
if(hGameConf == INVALID_HANDLE) {
GameData hGameConf = new GameData("FixFuncRotating.games");
if(hGameConf == null) {
LogError("Couldn't load gamedata!");
return;
}

// 2. Get function address
Address pFunction = GameConfGetAddress(hGameConf, "FunctionName");
Address pFunction = hGameConf.GetAddress("FunctionName");
if(!pFunction) {
LogError("Could not find function address");
delete hGameConf;
return;
}

// 3. Create detour
Handle detour = DHookCreateDetour(pFunction, CallConv_THISCALL, ReturnType_Void, ThisPointer_CBaseEntity);
DynamicDetour detour = new DynamicDetour(pFunction, CallConv_THISCALL, ReturnType_Void, ThisPointer_CBaseEntity);

// 4. Add parameters if needed
DHookAddParam(detour, HookParamType_Float);
detour.AddParam(HookParamType_Float);

// 5. Enable detour
if(!DHookEnableDetour(detour, false, CallbackFunction)) {
if(!detour.Enable(Hook_Pre, CallbackFunction)) {
LogError("Could not enable detour");
}

// 6. Free gamedata
delete hGameConf;
```

### Error Handling
- Always check gamedata loading with `INVALID_HANDLE`
- Always check gamedata loading against `null`
- Validate all function addresses before creating detours
- Log meaningful error messages using `LogError()`
- Use proper return values (`MRES_Ignored`, etc.)
Expand Down Expand Up @@ -188,12 +192,7 @@ The GitHub Actions pipeline validates:
- `sm_dump_classes CFuncRotating`: Show entity properties (if available)

### Backward Compatibility
The plugin includes compatibility code for SourceMod < 1.13:
```sourcepawn
#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR < 13
// FloatMod implementation for older versions
#endif
```
The plugin targets SourceMod 1.12+ and relies on the native float `%` operator and the `GameData` / `DynamicDetour` methodmaps; no compatibility shims are kept for older versions.

## Performance Considerations
- DHooks detours add minimal overhead when functions are called
Expand Down
40 changes: 14 additions & 26 deletions addons/sourcemod/scripting/FixFuncRotating.sp
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,12 @@ public Plugin myinfo =
name = "FixFuncRotating",
author = "Cloud Strife",
description = "Fixes func_rotating`s StartForward and StopAtStartPos inputs",
version = "1.0.3",
version = "1.0.4",
url = ""
};

Handle g_CFuncRotating_StartForward = null;
Handle g_CFuncRotating_UpdateSpeed = null;

#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR < 12
stock float FloatMod(float num, float denom)
{
return num - denom * RoundToFloor(num / denom);
}

stock float operator%(float oper1, float oper2)
{
return FloatMod(oper1, oper2);
}
#endif
DynamicDetour g_CFuncRotating_StartForward = null;
DynamicDetour g_CFuncRotating_UpdateSpeed = null;

// Set m_bStopAtStartPos to false
public MRESReturn CFuncRotating_InputStartForward(int entity)
Expand All @@ -37,11 +25,11 @@ public MRESReturn CFuncRotating_InputStartForward(int entity)
return MRES_Ignored;
}

public MRESReturn CFuncRotating_UpdateSpeed(int entity, Handle hParams)
public MRESReturn CFuncRotating_UpdateSpeed(int entity, DHookParam hParams)
{
if(GetEntProp(entity, Prop_Data, "m_bStopAtStartPos", 1))
{
float flNewSpeed = view_as<float>(DHookGetParam(hParams, 1));
float flNewSpeed = hParams.Get(1);
if(flNewSpeed <= 25)
{
float vecMoveAng[3], angStart[3], angRotation[3], avelpertick[3];
Expand Down Expand Up @@ -71,37 +59,37 @@ public MRESReturn CFuncRotating_UpdateSpeed(int entity, Handle hParams)

public void OnPluginStart()
{
Handle hGameConf = LoadGameConfigFile("FixFuncRotating.games");
if(hGameConf == INVALID_HANDLE)
GameData hGameConf = new GameData("FixFuncRotating.games");
if(hGameConf == null)
{
LogError("Couldn't load FixFuncRotating.games game config!");
return;
}

Address pStartForward = GameConfGetAddress(hGameConf, "CFuncRotating::InputStartForward");
Address pStartForward = hGameConf.GetAddress("CFuncRotating::InputStartForward");
if(pStartForward)
{
g_CFuncRotating_StartForward = DHookCreateDetour(pStartForward, CallConv_THISCALL, ReturnType_Void, ThisPointer_CBaseEntity);
g_CFuncRotating_StartForward = new DynamicDetour(pStartForward, CallConv_THISCALL, ReturnType_Void, ThisPointer_CBaseEntity);

if(!DHookEnableDetour(g_CFuncRotating_StartForward, false, CFuncRotating_InputStartForward))
if(!g_CFuncRotating_StartForward.Enable(Hook_Pre, CFuncRotating_InputStartForward))
{
LogError("Could not enable detour for CFuncRotating::InputStartForward");
}
}
else LogError("Could not find CFuncRotating::InputStartForward address");

Address pUpdateSpeed = GameConfGetAddress(hGameConf, "CFuncRotating::UpdateSpeed");
Address pUpdateSpeed = hGameConf.GetAddress("CFuncRotating::UpdateSpeed");
if(!pUpdateSpeed)
{
LogError("Could not find CFuncRotating::UpdateSpeed address");
delete hGameConf;
return;
}

g_CFuncRotating_UpdateSpeed = DHookCreateDetour(pUpdateSpeed, CallConv_THISCALL, ReturnType_Void, ThisPointer_CBaseEntity);
DHookAddParam(g_CFuncRotating_UpdateSpeed, HookParamType_Float);
g_CFuncRotating_UpdateSpeed = new DynamicDetour(pUpdateSpeed, CallConv_THISCALL, ReturnType_Void, ThisPointer_CBaseEntity);
g_CFuncRotating_UpdateSpeed.AddParam(HookParamType_Float);

if(!DHookEnableDetour(g_CFuncRotating_UpdateSpeed, false, CFuncRotating_UpdateSpeed))
if(!g_CFuncRotating_UpdateSpeed.Enable(Hook_Pre, CFuncRotating_UpdateSpeed))
{
LogError("Could not enable detour for CFuncRotating::UpdateSpeed");
}
Expand Down
Loading