Initial commit

This commit is contained in:
Mat 2021-09-03 10:52:42 +00:00
commit fee9898651
2 changed files with 395 additions and 0 deletions

19
LICENSE.md Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2021 mat
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

376
boostfix.sp Normal file
View File

@ -0,0 +1,376 @@
#include <sourcemod>
#include <sdkhooks>
#include <sdktools>
#pragma semicolon 1
#pragma newdecls required
public Plugin myinfo = {
name = "boostfix",
version = "1.0.0",
author = "https://github.com/t5mat",
description = "Fixes buggy push triggers; prevents crouchboosting",
url = "https://github.com/t5mat/boostfix",
};
#define SOLID_NONE (0)
#define FSOLID_NOT_SOLID (0x0004)
#define SF_TRIG_PUSH_ONCE (0x80)
#define SF_TRIG_PUSH_AFFECT_PLAYER_ON_LADDER (0x100)
#define VEC_HULL_MIN (view_as<float>({-16.0, -16.0, 0.0}))
#define VEC_HULL_MAX (view_as<float>({16.0, 16.0, 72.0}))
#define VEC_DUCK_HULL_MIN (view_as<float>({-16.0, -16.0, 0.0}))
#define VEC_DUCK_HULL_MAX (view_as<float>({16.0, 16.0, 54.0}))
#define CROUCHBOOST_TIME (0.25)
enum struct Engine
{
int m_nSolidType;
int m_usSolidFlags;
int m_spawnflags;
int m_vecBaseVelocity;
int m_flLaggedMovementValue;
int m_hGroundEntity;
Handle PassesTriggerFilters_;
int m_hMoveParent;
int m_vecAbsOrigin;
int m_angAbsRotation;
int m_vecAbsVelocity;
int m_flSpeed;
int m_vecPushDir;
void Initialize()
{
(this.m_nSolidType = FindSendPropInfo("CBaseEntity", "m_nSolidType")) == -1 && SetFailState("CBaseEntity::m_nSolidType");
(this.m_usSolidFlags = FindSendPropInfo("CBaseEntity", "m_usSolidFlags")) == -1 && SetFailState("CBaseEntity::m_usSolidFlags");
(this.m_spawnflags = FindSendPropInfo("CBaseTrigger", "m_spawnflags")) == -1 && SetFailState("CBaseTrigger::m_spawnflags");
(this.m_vecBaseVelocity = FindSendPropInfo("CBasePlayer", "m_vecBaseVelocity")) == -1 && SetFailState("CBasePlayer::m_vecBaseVelocity");
(this.m_flLaggedMovementValue = FindSendPropInfo("CBasePlayer", "m_flLaggedMovementValue")) == -1 && SetFailState("CBasePlayer::m_flLaggedMovementValue");
(this.m_hGroundEntity = FindSendPropInfo("CBasePlayer", "m_hGroundEntity")) == -1 && SetFailState("CBasePlayer::m_hGroundEntity");
GameData gd;
(gd = new GameData("rngfix.games")) == null && SetFailState("rngfix.games");
StartPrepSDKCall(SDKCall_Entity);
PrepSDKCall_SetFromConf(gd, SDKConf_Virtual, "CBaseTrigger::PassesTriggerFilters");
PrepSDKCall_SetReturnInfo(SDKType_Bool, SDKPass_Plain);
PrepSDKCall_AddParameter(SDKType_CBaseEntity, SDKPass_Pointer);
(this.PassesTriggerFilters_ = EndPrepSDKCall()) == null && SetFailState("CBaseTrigger::PassesTriggerFilters");
delete gd;
this.m_hMoveParent = -1;
this.m_vecPushDir = -1;
}
void InitializeCBaseEntityOffsets(int entity)
{
if (this.m_hMoveParent != -1) {
return;
}
(this.m_hMoveParent = FindDataMapInfo(entity, "m_hMoveParent")) == -1 && SetFailState("CBaseEntity::m_hMoveParent");
(this.m_vecAbsOrigin = FindDataMapInfo(entity, "m_vecAbsOrigin")) == -1 && SetFailState("CBaseEntity::m_vecAbsOrigin");
(this.m_angAbsRotation = FindDataMapInfo(entity, "m_angAbsRotation")) == -1 && SetFailState("CBaseEntity::m_angAbsRotation");
(this.m_vecAbsVelocity = FindDataMapInfo(entity, "m_vecAbsVelocity")) == -1 && SetFailState("CBaseEntity::m_vecAbsVelocity");
(this.m_flSpeed = FindDataMapInfo(entity, "m_flSpeed")) == -1 && SetFailState("CBaseEntity::m_flSpeed");
}
void InitializeCTriggerPushOffsets(int entity)
{
if (this.m_vecPushDir != -1) {
return;
}
(this.m_vecPushDir = FindDataMapInfo(entity, "m_vecPushDir")) == -1 && SetFailState("CTriggerPush::m_vecPushDir");
}
bool PassesTriggerFilters(int entity, int other)
{
return SDKCall(this.PassesTriggerFilters_, entity, other);
}
}
enum struct Client
{
float origin[3];
float velocity[3];
int flags;
float frame;
bool touching[4096];
float endTouchFrame[4096];
bool endTouchDuck[4096];
}
bool g_late;
ConVar g_boostfix_pushfix;
ConVar g_boostfix_crouchboostfix;
Engine g_engine;
Client g_clients[MAXPLAYERS + 1];
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
{
if (GetEngineVersion() != Engine_CSGO) {
FormatEx(error, err_max, "Not supported");
return APLRes_Failure;
}
g_late = late;
return APLRes_Success;
}
public void OnPluginStart()
{
g_engine.Initialize();
g_boostfix_pushfix = CreateConVar("boostfix_pushfix", "1", "Enable trigger_push fix", FCVAR_NOTIFY, true, 0.0, true, 1.0);
g_boostfix_crouchboostfix = CreateConVar("boostfix_crouchboostfix", "1", "Enable crouchboost prevention", FCVAR_NOTIFY, true, 0.0, true, 1.0);
if (g_late) {
for (int e = 0; e < sizeof(Client::touching); ++e) {
if ((e < 1 || e > sizeof(g_clients) - 1) && IsValidEntity(e)) {
char classname[64];
GetEntityClassname(e, classname, sizeof(classname));
OnEntityCreated(e, classname);
}
}
for (int c = 1; c <= MaxClients; ++c) {
if (IsClientInGame(c)) {
OnClientPutInServer(c);
}
}
}
}
public void OnEntityCreated(int entity, const char[] classname)
{
g_engine.InitializeCBaseEntityOffsets(entity);
if (StrEqual(classname, "trigger_push")) {
g_engine.InitializeCTriggerPushOffsets(entity);
for (int i = 0; i < sizeof(g_clients); ++i) {
g_clients[i].touching[entity] = false;
}
SDKHook(entity, SDKHook_StartTouch, Hook_PushStartTouch);
SDKHook(entity, SDKHook_EndTouch, Hook_PushEndTouch);
SDKHook(entity, SDKHook_Touch, Hook_PushTouch);
}
}
public void OnClientPutInServer(int client)
{
g_clients[client].frame = 0.0;
for (int i = 0; i < sizeof(Client::endTouchFrame); ++i) {
g_clients[client].endTouchFrame[i] = -1.0;
}
SDKHook(client, SDKHook_PreThinkPost, Hook_ClientPreThinkPost);
}
void Hook_ClientPreThinkPost(int client)
{
GetEntDataVector(client, g_engine.m_vecAbsOrigin, g_clients[client].origin);
GetEntDataVector(client, g_engine.m_vecAbsVelocity, g_clients[client].velocity);
g_clients[client].flags = GetEntityFlags(client);
g_clients[client].frame += GetEntDataFloat(client, g_engine.m_flLaggedMovementValue);
}
Action Hook_PushStartTouch(int entity, int other)
{
if (other > sizeof(g_clients) - 1) {
return Plugin_Continue;
}
if (g_boostfix_crouchboostfix.BoolValue && g_clients[other].endTouchFrame[entity] != -1.0 && g_clients[other].frame - g_clients[other].endTouchFrame[entity] < CROUCHBOOST_TIME / GetTickInterval()) {
bool startTouchUnduck = false;
if (!g_clients[other].endTouchDuck[entity]) {
// Were we mid-air last tick?
if (!(g_clients[other].flags & FL_ONGROUND)) {
// Did we unduck?
if ((g_clients[other].flags & FL_DUCKING) && !(GetEntityFlags(other) & FL_DUCKING)) {
// Had we not unducked, would we still be not touching the trigger?
float origin[3];
origin = g_clients[other].velocity;
ScaleVector(origin, GetTickInterval() * GetEntDataFloat(other, g_engine.m_flLaggedMovementValue));
AddVectors(origin, g_clients[other].origin, origin);
if (!DoesHullEntityIntersect(origin, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX, entity)) {
// This StartTouch was caused by a mid-air unduck
startTouchUnduck = true;
}
}
}
}
// If this StartTouch happened too soon after the last EndTouch, and either:
// - the last EndTouch was caused by a mid-air duck, or
// - this StartTouch was caused by a mid-air unduck
// then this StartTouch is considered "invalid", disable pushing so we don't get boosted again
g_clients[other].touching[entity] = !(g_clients[other].endTouchDuck[entity] || startTouchUnduck);
} else {
g_clients[other].touching[entity] = true;
}
if (!g_clients[other].touching[entity]) {
// Prevent outputs from being queued
return Plugin_Handled;
}
return Plugin_Continue;
}
Action Hook_PushEndTouch(int entity, int other)
{
if (other > sizeof(g_clients) - 1) {
return Plugin_Continue;
}
g_clients[other].touching[entity] = false;
g_clients[other].endTouchFrame[entity] = g_clients[other].frame;
g_clients[other].endTouchDuck[entity] = false;
// Were we mid-air last tick?
if (!(g_clients[other].flags & FL_ONGROUND)) {
// Did we duck?
if (!(g_clients[other].flags & FL_DUCKING) && (GetEntityFlags(other) & FL_DUCKING)) {
// Had we not ducked, would we still be touching the trigger?
float origin[3];
origin = g_clients[other].velocity;
ScaleVector(origin, GetTickInterval() * GetEntDataFloat(other, g_engine.m_flLaggedMovementValue));
AddVectors(origin, g_clients[other].origin, origin);
if (DoesHullEntityIntersect(origin, VEC_HULL_MIN, VEC_HULL_MAX, entity)) {
// This EndTouch was caused by a mid-air duck
g_clients[other].endTouchDuck[entity] = true;
}
}
}
return Plugin_Continue;
}
Action Hook_PushTouch(int entity, int other)
{
if (other > sizeof(g_clients) - 1) {
return Plugin_Continue;
}
if (!g_clients[other].touching[entity]) {
return Plugin_Handled;
}
MoveType moveType = GetEntityMoveType(other);
if (moveType == MOVETYPE_VPHYSICS) {
return Plugin_Continue;
}
if (!g_boostfix_pushfix.BoolValue) {
return Plugin_Continue;
}
// https://github.com/perilouswithadollarsign/cstrike15_src/blob/29e4c1fda9698d5cebcdaf1a0de4b829fa149bf8/game/server/triggers.cpp#L2545
if (moveType == MOVETYPE_NONE || moveType == MOVETYPE_PUSH) {
return Plugin_Handled;
}
if ((GetEntData(other, g_engine.m_nSolidType, 1) == SOLID_NONE) || (GetEntData(other, g_engine.m_usSolidFlags, 2) & FSOLID_NOT_SOLID)) {
return Plugin_Handled;
}
if (GetEntDataEnt2(other, g_engine.m_hMoveParent) != -1) {
return Plugin_Handled;
}
if (!g_engine.PassesTriggerFilters(entity, other)) {
return Plugin_Handled;
}
float direction[3];
{
float rotation[3];
GetEntDataVector(entity, g_engine.m_angAbsRotation, rotation);
float local[3];
GetEntDataVector(entity, g_engine.m_vecPushDir, local);
float sy = Sine(DegToRad(rotation[1]));
float cy = Cosine(DegToRad(rotation[1]));
float sp = Sine(DegToRad(rotation[0]));
float cp = Cosine(DegToRad(rotation[0]));
float sr = Sine(DegToRad(rotation[2]));
float cr = Cosine(DegToRad(rotation[2]));
direction[0] = local[0] * (cp * cy) + local[1] * (sp * sr * cy - cr * sy) + local[2] * (sp * cr * cy + sr * sy);
direction[1] = local[0] * (cp * sy) + local[1] * (sp * sr * sy + cr * cy) + local[2] * (sp * cr * sy - sr * cy);
direction[2] = local[0] * (-sp) + local[1] * (sr * cp) + local[2] * (cr * cp);
}
float speed = GetEntDataFloat(entity, g_engine.m_flSpeed);
float push[3];
push = direction;
ScaleVector(push, speed);
if (GetEntData(entity, g_engine.m_spawnflags) & SF_TRIG_PUSH_ONCE) {
float velocity[3];
GetEntDataVector(other, g_engine.m_vecAbsVelocity, velocity);
AddVectors(velocity, push, velocity);
TeleportEntity(other, NULL_VECTOR, NULL_VECTOR, velocity);
if (direction[2] > 0.0) {
SetEntDataEnt2(other, g_engine.m_hGroundEntity, -1);
}
RemoveEdict(entity);
return Plugin_Handled;
}
int flags = GetEntityFlags(other);
if (flags & FL_BASEVELOCITY) {
float base[3];
GetEntDataVector(other, g_engine.m_vecBaseVelocity, base);
AddVectors(push, base, push);
}
// https://forums.alliedmods.net/showpost.php?p=2561673&postcount=17
float velocity[3];
GetEntDataVector(other, g_engine.m_vecAbsVelocity, velocity);
velocity[2] += push[2] * GetTickInterval() * GetEntDataFloat(other, g_engine.m_flLaggedMovementValue);
push[2] = 0.0;
TeleportEntity(other, NULL_VECTOR, NULL_VECTOR, velocity);
SetEntDataVector(other, g_engine.m_vecBaseVelocity, push);
SetEntityFlags(other, flags | FL_BASEVELOCITY);
return Plugin_Handled;
}
bool g_DoesHullEntityIntersect_hit;
bool DoesHullEntityIntersect(const float origin[3], const float mins[3], const float maxs[3], int entity, int mask = PARTITION_TRIGGER_EDICTS)
{
g_DoesHullEntityIntersect_hit = false;
TR_EnumerateEntitiesHull(origin, origin, mins, maxs, mask, Trace_DoesHullEntityIntersect, entity);
return g_DoesHullEntityIntersect_hit;
}
bool Trace_DoesHullEntityIntersect(int entity, any data)
{
if (entity == data) {
TR_ClipCurrentRayToEntity(MASK_ALL, entity);
g_DoesHullEntityIntersect_hit = TR_DidHit();
return false;
}
return true;
}