Merge pull request #1014 from nullworks/warp_improvements

Alot of warp related stuff (see description)
This commit is contained in:
LightCat 2020-05-24 20:14:06 +02:00 committed by GitHub
commit 224ecfbaa3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 328 additions and 103 deletions

View File

@ -5,7 +5,7 @@
<AutoVariable width="fill" target="antiaim.draw-fakes" label="Draw Fakes"/>
<AutoVariable width="fill" target="antiaim.crouch" label="Fake Crouch"/>
<AutoVariable width="fill" target="antiaim.no-clamp" label="No Clamping"/>
<AutoVariable width="fill" target="misc.fakelag" label="Fakelag" min="0" max="15"/>
<AutoVariable width="fill" target="misc.fakelag" label="Fakelag" min="0" max="22"/>
<AutoVariable width="fill" target="antiaim.spin-speed" label="Spin speed" min="-45" max="45" step="0.1"/>
<AutoVariable width="fill" target="antiaim.roll" label="Roll"/>
<AutoVariable width="fill" target="antiaim.pitch.static" label="Custom Pitch"/>

View File

@ -59,6 +59,7 @@
<Box padding="12 6 6 6" width="content" height="content" name="Warp" x="370" y="65">
<List width="180">
<AutoVariable width="fill" target="warp.enabled" label="Enable Warp" tooltip="Allows you to charge a burst of speed"/>
<AutoVariable width="fill" target="warp.speed" label="Warp speed" tooltip="The Discharge speed (0.5 = +50%, 2 = +200%, +2300% is the maximum possible)"/>
<AutoVariable width="fill" target="warp.key" label="Warp Key" tooltip="Pressing this key will use all stored ticks to accelerate you"/>
<AutoVariable width="fill" target="warp.draw" label="Draw Warp" tooltip="Draws a bar indicating your warp readyness"/>
<AutoVariable width="fill" target="warp.bar-size" label="Bar Size"/>

View File

@ -202,8 +202,7 @@ template <condition cond> inline bool CondBitCheck(condition_data_s &data)
}
if (cond < 32)
{
if (data.cond_0)
return data.cond_0 & (1u << (cond));
return data.cond_0 & (1u << (cond));
}
return false;
}
@ -231,20 +230,19 @@ template <condition cond, bool state> inline void CondBitSet(condition_data_s &d
{
if (cond > 32 * 3)
{
data.cond_3 |= (1 << (cond % 32));
data.cond_3 |= (1u << (cond % 32));
}
else if (cond > 32 * 2)
{
data.cond_2 |= (1 << (cond % 32));
data.cond_2 |= (1u << (cond % 32));
}
else if (cond > 32 * 1)
{
data.cond_1 |= (1 << (cond % 32));
data.cond_1 |= (1u << (cond % 32));
}
else
{
if (data.cond_0)
data.cond_0 |= (1 << (cond));
data.cond_0 |= (1u << (cond));
}
}
else
@ -263,8 +261,7 @@ template <condition cond, bool state> inline void CondBitSet(condition_data_s &d
}
else
{
if (data.cond_0)
data.cond_0 &= ~(1u << (cond));
data.cond_0 &= ~(1u << (cond));
}
}
}

View File

@ -24,8 +24,26 @@ public:
{
if (g_pLocalPlayer->holding_sapper)
return 115;
IClientEntity *owner = re::C_TFWeaponBase::GetOwnerViaInterface(self);
bool add_charging = false;
if (owner)
{
CachedEntity *owner_ce = ENTITY(owner->entindex());
if (HasCondition<TFCond_Charging>(owner_ce))
{
CondBitSet<TFCond_Charging, false>(CE_VAR(owner_ce, netvar.iCond, condition_data_s));
add_charging = true;
}
}
typedef int (*fn_t)(IClientEntity *);
return vfunc<fn_t>(self, offsets::PlatformOffset(521, offsets::undefined, 521), 0)(self);
int return_value = vfunc<fn_t>(self, offsets::PlatformOffset(521, offsets::undefined, 521), 0)(self);
if (add_charging)
{
CachedEntity *owner_ce = ENTITY(owner->entindex());
CondBitSet<TFCond_Charging, true>(CE_VAR(owner_ce, netvar.iCond, condition_data_s));
}
return return_value;
}
};
} // namespace re

View File

@ -14,6 +14,7 @@
#include <inetmsghandler.h>
#include <KeyValues.h>
#include "bitvec.h"
#include "CSignature.h"
#define DECLARE_BASE_MESSAGE(msgtype) \
public: \
@ -98,6 +99,26 @@ public:
return false;
}; // no handler set
public:
// I don't get what it does but we need it
virtual bool BIncomingMessageForProcessing(double param_1, int param_2)
{
// Call original to be sure nothing breaks
typedef bool (*BIncomingMessageForProcessing_t)(CNetMessage *, double, int);
static auto addr = gSignatures.GetEngineSignature("55 89 E5 56 53 83 EC 10 8B 5D ? F2 0F 10 45");
BIncomingMessageForProcessing_t BIncomingMessageForProcessing_fn = (BIncomingMessageForProcessing_t)addr;
return BIncomingMessageForProcessing_fn(this, param_1, param_2);
};
// I don't get what it does but we need it
virtual void SetRatePolicy()
{
// Call original to be sure nothing breaks
typedef bool (*SetRatePolicy_t)(CNetMessage *);
static auto addr = gSignatures.GetEngineSignature("55 89 E5 83 EC 18 C7 04 24 2C 00 00 00");
SetRatePolicy_t SetRatePolicy_fn = (SetRatePolicy_t)addr;
SetRatePolicy_fn(this);
};
protected:
bool m_bReliable; // true if message should be send reliable
INetChannel *m_NetChannel; // netchannel this message is from/for
@ -177,7 +198,7 @@ protected:
#define MAX_OSPATH 260
#define NETMSG_TYPE_BITS 5
#define NETMSG_TYPE_BITS 6
typedef int QueryCvarCookie_t;
typedef enum
{

View File

@ -72,7 +72,7 @@ static void updateAntiAfk()
if (!afk_timer)
afk_timer = g_ICvar->FindVar("mp_idlemaxtime");
// Trigger 10 seconds before kick
else if (afk_timer->m_nValue != 0 && anti_afk_timer.check(afk_timer->m_nValue * 60 * 1000 - 10000))
else if (afk_timer->GetInt() != 0 && anti_afk_timer.check(afk_timer->m_nValue * 60 * 1000 - 10000))
{
// Just duck tf
if (current_user_cmd->buttons & (IN_DUCK | IN_JUMP))
@ -85,7 +85,7 @@ static void updateAntiAfk()
current_late_user_cmd->buttons |= flip ? IN_FORWARD : IN_BACK;
// Flip flip
flip = !flip;
if (anti_afk_timer.check(afk_timer->m_nValue * 60 * 1000 + 1000))
if (anti_afk_timer.check(afk_timer->GetInt() * 60 * 1000 + 1000))
{
anti_afk_timer.update();
}

View File

@ -11,10 +11,12 @@
#endif
#include "MiscAimbot.hpp"
#include "PlayerTools.hpp"
#include "DetourHook.hpp"
namespace hacks::tf2::warp
{
static settings::Boolean enabled{ "warp.enabled", "false" };
static settings::Float speed{ "warp.speed", "23" };
static settings::Boolean draw{ "warp.draw", "false" };
static settings::Button warp_key{ "warp.key", "<null>" };
static settings::Boolean charge_passively{ "warp.charge-passively", "true" };
@ -29,68 +31,97 @@ static settings::Boolean warp_backwards{ "warp.on-hit.backwards", "false" };
static settings::Boolean warp_left{ "warp.on-hit.left", "true" };
static settings::Boolean warp_right{ "warp.on-hit.right", "true" };
// Hidden control rvars for communtiy servers
static settings::Int maxusrcmdprocessticks("warp.maxusrcmdprocessticks", "24");
// Draw control
static settings::Int size{ "warp.bar-size", "100" };
static settings::Int bar_x{ "warp.bar-x", "50" };
static settings::Int bar_y{ "warp.bar-y", "200" };
static bool should_charge = false;
static int warp_amount = 0;
static int warp_amount_override = 0;
static int warp_override_one_tick = 0;
static bool charged = false;
// Taken from MrSteyk
class clc_move_proto
{
public:
int VTable;
char reliable;
int netchan;
int field_C;
int field_10;
int m_nBackupCommands;
int m_nNewCommands;
int m_nLength;
bf_write m_DataIn;
bf_write m_DataOut;
};
int GetWarpAmount()
{
static auto sv_max_dropped_packets_to_process = g_ICvar->FindVar("sv_max_dropped_packets_to_process");
return warp_override_one_tick ? warp_override_one_tick : sv_max_dropped_packets_to_process->GetInt();
}
static bool should_charge = false;
static int warp_amount = 0;
static int warp_amount_override = 0;
static bool should_melee = false;
static bool charged = false;
static bool should_warp = true;
static bool was_hurt = false;
// Warping part
void Warp()
// Should we warp?
bool shouldWarp(bool check_amount)
{
return ((warp_key && warp_key.isKeyDown()) || was_hurt) && (!check_amount || warp_amount);
}
// How many ticks of excess we have (for decimal speeds)
float excess_ticks = 0.0f;
int GetWarpAmount(bool finalTick)
{
int max_extra_ticks = *maxusrcmdprocessticks - 1;
// No limit set
if (!*maxusrcmdprocessticks)
max_extra_ticks = INT_MAX;
float warp_amount_preprocessed = std::max(*speed, 0.05f);
// How many ticks to warp, add excess too
int warp_amount_processed = std::floor(warp_amount_preprocessed) + std::floor(excess_ticks);
// Remove the used amount from excess
if (finalTick)
excess_ticks -= std::floor(excess_ticks);
// Store excess
if (finalTick)
excess_ticks += warp_amount_preprocessed - std::floor(warp_amount_preprocessed);
// Return smallest of the two
return std::min(warp_amount_processed, max_extra_ticks);
}
DetourHook cl_move_detour;
typedef void (*CL_Move_t)(float accumulated_extra_samples, bool bFinalTick);
// Warping part, uses CL_Move
void Warp(float accumulated_extra_samples, bool finalTick)
{
auto ch = g_IEngine->GetNetChannelInfo();
if (!ch)
return;
if (!should_warp)
{
should_warp = true;
if (finalTick)
should_warp = true;
return;
}
int &m_nOutSequenceNr = *(int *) ((uintptr_t) ch + offsets::m_nOutSequenceNr());
int warp_ticks = warp_amount;
if (warp_amount_override)
warp_ticks = warp_amount_override;
// Don' add more than the warp_ticks
m_nOutSequenceNr += std::min(warp_ticks, GetWarpAmount());
warp_amount -= std::min(warp_ticks, GetWarpAmount());
warp_ticks -= std::min(warp_ticks, GetWarpAmount());
CL_Move_t original = (CL_Move_t) cl_move_detour.GetOriginalFunc();
// Call CL_Move once for every warp tick
int warp_amnt = GetWarpAmount(finalTick);
if (warp_amnt)
{
int calls = std::min(warp_ticks, warp_amnt);
for (int i = 0; i < calls; i++)
{
original(accumulated_extra_samples, finalTick);
// Only decrease ticks for the final CL_Move tick
if (finalTick)
{
warp_amount--;
warp_ticks--;
}
}
}
cl_move_detour.RestorePatch();
if (warp_amount_override)
warp_amount_override = warp_ticks;
// Don't attack while warping
current_user_cmd->buttons &= ~IN_ATTACK;
if (warp_ticks <= 0)
{
was_hurt = false;
@ -102,8 +133,7 @@ void Warp()
int GetMaxWarpTicks()
{
static auto usercmd_cvar = g_ICvar->FindVar("sv_maxusrcmdprocessticks");
int ticks = usercmd_cvar->GetInt();
int ticks = *maxusrcmdprocessticks;
// No limit set
if (!ticks)
ticks = INT_MAX;
@ -122,7 +152,8 @@ void SendNetMessage(INetMessage &msg)
if (should_charge && !charged)
{
int ticks = GetMaxWarpTicks();
auto movemsg = (clc_move_proto *) &msg;
auto movemsg = (CLC_Move *) &msg;
// Just null it :shrug:
movemsg->m_nBackupCommands = 0;
movemsg->m_nNewCommands = 0;
@ -138,22 +169,38 @@ void SendNetMessage(INetMessage &msg)
charged = true;
}
}
// Warp
if ((warp_key.isKeyDown() || was_hurt) && warp_amount)
{
Warp();
if (warp_amount < GetMaxWarpTicks())
charged = false;
}
}
should_charge = false;
}
static bool move_last_tick = true;
static bool warp_last_tick = false;
static bool should_warp_last_tick = false;
static bool was_hurt_last_tick = false;
static int ground_ticks = 0;
// Approximate demoknight shield speed at a given tick
float approximateSpeedAtTick(int ticks_since_start, float initial_speed)
{
// Formula only holds up until like 20 ticks
float speed = ticks_since_start >= 20 ? 750.0f : (ticks_since_start * (113.8f - 2.8f * ticks_since_start) + 1.0f);
return std::min(750.0f * g_GlobalVars->interval_per_tick, initial_speed * g_GlobalVars->interval_per_tick + speed * g_GlobalVars->interval_per_tick);
}
// Approximate the amount of ticks needed for a given distance as demoknight
int approximateTicksForDist(float distance, float initial_speed, int max_ticks)
{
float travelled_dist = 0.0f;
for (int i = 0; i <= max_ticks; i++)
{
travelled_dist += approximateSpeedAtTick(i, initial_speed);
// We hit the needed range
if (travelled_dist >= distance)
return i;
}
// Not within Max range
return -1;
}
static bool move_last_tick = true;
static bool warp_last_tick = false;
static bool was_hurt_last_tick = false;
static int ground_ticks = 0;
// Left and right by default
static std::vector<float> yaw_selections{ 90.0f, -90.0f };
@ -165,7 +212,19 @@ enum charge_state
DONE
};
charge_state current_state = ATTACK;
enum peek_state
{
IDLE = 0,
MOVE_TOWARDS,
MOVE_BACK,
STOP
};
charge_state current_state = ATTACK;
peek_state current_peek_state = IDLE;
// Used to determine when we should start moving back with peek
static int charge_at_start = 0;
// Used to determine when demoknight warp should be over
static bool was_overridden = false;
@ -175,11 +234,11 @@ void CreateMove()
return;
if (CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer())
return;
warp_override_one_tick = 0;
if (!warp_key.isKeyDown() && !was_hurt)
if (!shouldWarp(false))
{
warp_last_tick = false;
current_state = ATTACK;
warp_last_tick = false;
current_state = ATTACK;
current_peek_state = IDLE;
Vector velocity{};
velocity::EstimateAbsVelocity(RAW_ENT(LOCAL_E), velocity);
@ -267,25 +326,47 @@ void CreateMove()
if (result.first)
{
float distance = LOCAL_E->m_vecOrigin().DistTo(result.first->m_vecOrigin());
// Subtract melee range
distance -= 130.0f;
// Divide by the amount we travel per tick to get how much of our charge we'll need
int charge_ticks = distance / (750.0f * g_GlobalVars->interval_per_tick);
// We want to hit their bounding box, not their center
distance -= 40.0f;
// For every started 10 ticks We can subtract 1 because we'll get an extra CreateMove call.
charge_ticks -= std::ceil(charge_ticks / 10.0f);
// This approximates the ticks needed for the distance
Vector vel;
velocity::EstimateAbsVelocity(RAW_ENT(LOCAL_E), vel);
// +11 to account for the melee delay
int charge_ticks = approximateTicksForDist(distance, vel.Length(), GetMaxWarpTicks() + 11);
// Not in range, try again with melee range taken into compensation
if (charge_ticks <= 0)
charge_ticks = approximateTicksForDist(distance - 128.0f, vel.Length(), GetMaxWarpTicks() + 11);
// Out of range
if (charge_ticks <= 0)
{
charge_ticks = warp_amount;
should_melee = false;
}
else
{
charge_ticks = std::clamp(charge_ticks, 0, warp_amount);
// For every started batch We can subtract 1 because we'll get an extra CreateMove call.
charge_ticks -= std::ceil(charge_ticks / *speed);
should_melee = true;
}
// Use these ticks
warp_amount_override = std::clamp(charge_ticks, 0, warp_amount);
was_overridden = true;
warp_amount_override = charge_ticks;
was_overridden = true;
}
else
{
should_melee = false;
was_overridden = false;
}
// Force a crit
criticals::force_crit_this_tick = true;
current_user_cmd->buttons |= IN_ATTACK;
if (should_melee)
current_user_cmd->buttons |= IN_ATTACK;
current_state = CHARGE;
}
// Just warp normally if meter isn't full
@ -329,34 +410,59 @@ void CreateMove()
// Warp peaking
else if (warp_peek)
{
// We have Warp
if (warp_amount)
switch (current_peek_state)
{
// Warped last tick, time to reverse
if (warp_last_tick)
{
// Wait 1 tick before warping back
if (should_warp && !should_warp_last_tick)
{
should_warp_last_tick = true;
should_warp = false;
}
else
should_warp_last_tick = false;
case IDLE:
{
// Not doing anything, update warp amount
charge_at_start = warp_amount;
// Inverse direction
current_user_cmd->forwardmove = -current_user_cmd->forwardmove;
current_user_cmd->sidemove = -current_user_cmd->sidemove;
}
Vector vel;
velocity::EstimateAbsVelocity(RAW_ENT(LOCAL_E), vel);
// if we move more than 1.0 HU/s and buttons are pressed, and we are grounded, go to move towards statement...
if (CE_INT(LOCAL_E, netvar.iFlags) & FL_ONGROUND && !vel.IsZero(1.0f) && current_user_cmd->buttons & (IN_FORWARD | IN_BACK | IN_MOVELEFT | IN_MOVERIGHT))
current_peek_state = MOVE_TOWARDS;
// ...else don't warp
else
warp_override_one_tick = warp_amount / 2;
warp_last_tick = true;
{
should_warp = false;
break;
}
[[fallthrough]];
}
// Prevent movement so you don't overshoot when you don't want to
else
case MOVE_TOWARDS:
{
// Just wait until we used about a third of our warp, the rest has to be used for moving back
if (warp_amount <= charge_at_start * (2.0f / 3.0f))
current_peek_state = MOVE_BACK;
break;
}
case MOVE_BACK:
{
// Inverse direction if we still have warp left
if (warp_amount)
{
current_user_cmd->forwardmove *= -1.0f;
current_user_cmd->sidemove *= -1.0f;
break;
}
// ... Else we stop in our tracks
else
current_peek_state = STOP;
[[fallthrough]];
}
case STOP:
{
// Stop dead in our tracks while key is still held
current_user_cmd->forwardmove = 0.0f;
current_user_cmd->sidemove = 0.0f;
break;
}
default:
break;
}
}
was_hurt_last_tick = was_hurt;
@ -438,12 +544,94 @@ void rvarCallback(settings::VariableBase<bool> &, bool)
yaw_selections.push_back(90.0f);
}
DetourHook cl_sendmove_detour;
typedef void (*CL_SendMove_t)();
void CL_SendMove_hook()
{
byte data[4000];
// the +4 one is choked commands
int nextcommandnr = NET_INT(g_IBaseClientState, offsets::lastoutgoingcommand()) + NET_INT(g_IBaseClientState, offsets::lastoutgoingcommand() + 4) + 1;
// send the client update packet
CLC_Move moveMsg;
moveMsg.m_DataOut.StartWriting(data, sizeof(data));
// Determine number of backup commands to send along
int cl_cmdbackup = 2;
// How many real new commands have queued up
moveMsg.m_nNewCommands = 1 + NET_INT(g_IBaseClientState, offsets::lastoutgoingcommand() + 4);
moveMsg.m_nNewCommands = std::clamp(moveMsg.m_nNewCommands, 0, 15);
// Excessive commands (Used for longer fakelag, credits to https://www.unknowncheats.me/forum/source-engine/370916-23-tick-guwop-fakelag-break-lag-compensation-running.html)
int extra_commands = NET_INT(g_IBaseClientState, offsets::lastoutgoingcommand() + 4) + 1 - moveMsg.m_nNewCommands;
cl_cmdbackup = std::max(2, extra_commands);
moveMsg.m_nBackupCommands = std::clamp(cl_cmdbackup, 0, 7);
int numcmds = moveMsg.m_nNewCommands + moveMsg.m_nBackupCommands;
int from = -1; // first command is deltaed against zeros
bool bOK = true;
for (int to = nextcommandnr - numcmds + 1; to <= nextcommandnr; to++)
{
bool isnewcmd = to >= (nextcommandnr - moveMsg.m_nNewCommands + 1);
// Call the write to buffer
typedef bool (*WriteUsercmdDeltaToBuffer_t)(IBaseClientDLL *, bf_write *, int, int, bool);
// first valid command number is 1
bOK = bOK && vfunc<WriteUsercmdDeltaToBuffer_t>(g_IBaseClient, offsets::PlatformOffset(23, offsets::undefined, offsets::undefined), 0)(g_IBaseClient, &moveMsg.m_DataOut, from, to, isnewcmd);
from = to;
}
if (bOK)
{
// Make fakelag work as we want it to
if (extra_commands > 0)
((INetChannel *) g_IEngine->GetNetChannelInfo())->m_nChokedPackets -= extra_commands;
// only write message if all usercmds were written correctly, otherwise parsing would fail
((INetChannel *) g_IEngine->GetNetChannelInfo())->SendNetMsg(moveMsg);
}
}
void CL_Move_hook(float accumulated_extra_samples, bool bFinalTick)
{
CL_Move_t original = (CL_Move_t) cl_move_detour.GetOriginalFunc();
original(accumulated_extra_samples, bFinalTick);
cl_move_detour.RestorePatch();
// Should we warp?
if (shouldWarp(true))
{
Warp(accumulated_extra_samples, bFinalTick);
if (warp_amount < GetMaxWarpTicks())
charged = false;
}
}
static InitRoutine init([]() {
static auto cl_sendmove_addr = gSignatures.GetEngineSignature("55 89 E5 57 56 53 81 EC 2C 10 00 00 C6 85 ? ? ? ? 01");
cl_sendmove_detour.Init(cl_sendmove_addr, (void *) CL_SendMove_hook);
static auto cl_move_addr = gSignatures.GetEngineSignature("55 89 E5 57 56 53 81 EC 9C 00 00 00 83 3D ? ? ? ? 01");
cl_move_detour.Init(cl_move_addr, (void *) CL_Move_hook);
EC::Register(EC::LevelShutdown, LevelShutdown, "warp_levelshutdown");
EC::Register(EC::CreateMove, CreateMove, "warp_createmove", EC::very_late);
g_IEventManager2->AddListener(&listener, "player_hurt", false);
EC::Register(
EC::Shutdown, []() { g_IEventManager2->RemoveListener(&listener); }, "warp_shutdown");
EC::Shutdown,
[]() {
g_IEventManager2->RemoveListener(&listener);
cl_sendmove_detour.Shutdown();
cl_move_detour.Shutdown();
},
"warp_shutdown");
warp_forward.installChangeCallback(rvarCallback);
warp_backwards.installChangeCallback(rvarCallback);
warp_left.installChangeCallback(rvarCallback);

View File

@ -498,7 +498,7 @@ const char *CLC_ListenEvents::ToString(void) const
const char *CLC_Move::ToString(void) const
{
return strfmt("%s: backup %i, new %i, bytes %i", GetName(), m_nNewCommands, m_nBackupCommands, Bits2Bytes(m_nLength)).release();
return "";
}
bool CLC_Move::WriteToBuffer(bf_write &buffer)