Merge remote-tracking branch 'nullworks/master'
This commit is contained in:
commit
5a9b482519
@ -50,9 +50,6 @@ sudo cp "bin/libcathook.so" "/lib/i386-linux-gnu/${FILENAME}"
|
||||
|
||||
echo loading "$FILENAME" to "$proc"
|
||||
|
||||
sudo killall -19 steam
|
||||
sudo killall -19 steamwebhelper
|
||||
|
||||
sudo gdb -n -q -batch \
|
||||
-ex "attach $proc" \
|
||||
-ex "set \$dlopen = (void*(*)(char*, int)) dlopen" \
|
||||
@ -64,6 +61,3 @@ sudo gdb -n -q -batch \
|
||||
-ex "quit"
|
||||
|
||||
sudo rm "/lib/i386-linux-gnu/${FILENAME}"
|
||||
|
||||
sudo killall -18 steamwebhelper
|
||||
sudo killall -18 steam
|
||||
|
@ -50,7 +50,6 @@
|
||||
#include <visual/colors.hpp>
|
||||
|
||||
#if ENABLE_VISUALS
|
||||
|
||||
#include <visual/drawing.hpp>
|
||||
#include "visual/fidgetspinner.hpp"
|
||||
#include <visual/EffectGlow.hpp>
|
||||
@ -58,7 +57,6 @@
|
||||
#include <visual/EffectChams.hpp>
|
||||
#include <visual/drawmgr.hpp>
|
||||
#include "visual/menu/compatlayer.hpp"
|
||||
|
||||
#endif
|
||||
|
||||
#include "core/profiler.hpp"
|
||||
|
@ -162,6 +162,10 @@ struct offsets
|
||||
{
|
||||
return PlatformOffset(2, undefined, undefined);
|
||||
}
|
||||
static constexpr uint32_t PreDataUpdate()
|
||||
{
|
||||
return PlatformOffset(14, undefined, undefined);
|
||||
}
|
||||
static constexpr uint32_t Paint()
|
||||
{
|
||||
return PlatformOffset(14, undefined, undefined);
|
||||
|
@ -55,6 +55,7 @@
|
||||
#include <dbg.h>
|
||||
#include <ienginevgui.h>
|
||||
|
||||
#include "sdk/c_basetempentity.h"
|
||||
#include "sdk/in_buttons.h"
|
||||
#include "sdk/imaterialsystemfixed.h"
|
||||
#include "sdk/ScreenSpaceEffects.h"
|
||||
|
13
include/globals.h
Executable file → Normal file
13
include/globals.h
Executable file → Normal file
@ -6,9 +6,8 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/circular_buffer.hpp>
|
||||
#include <time.h>
|
||||
|
||||
class Vector;
|
||||
class ConVar;
|
||||
class CatVar;
|
||||
@ -38,14 +37,20 @@ extern char *disconnect_reason_newlined;
|
||||
extern CatVar disconnect_reason;
|
||||
|
||||
extern time_t time_injected;
|
||||
|
||||
struct brutestruct
|
||||
{
|
||||
int brutenum[32];
|
||||
Vector last_angles[32];
|
||||
std::deque<bool> choke[32];
|
||||
float lastsimtime;
|
||||
};
|
||||
class GlobalSettings
|
||||
{
|
||||
public:
|
||||
void Init();
|
||||
bool bInvalid{ true };
|
||||
bool is_create_move{ false };
|
||||
Vector last_angles;
|
||||
brutestruct brute;
|
||||
};
|
||||
|
||||
class CUserCmd;
|
||||
|
@ -1,6 +1,7 @@
|
||||
target_sources(cathook PRIVATE
|
||||
"${CMAKE_CURRENT_LIST_DIR}/AutoJoin.hpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/CatBot.hpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/SeedPrediction.hpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/Spam.hpp")
|
||||
if(NOT LagbotMode)
|
||||
target_sources(cathook PRIVATE
|
||||
|
52
include/hacks/SeedPrediction.hpp
Normal file
52
include/hacks/SeedPrediction.hpp
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* SeedPrediction.hpp
|
||||
*
|
||||
* Created on: Jul 27, 2018
|
||||
* Author: bencat07
|
||||
*/
|
||||
#include <boost/circular_buffer.hpp>
|
||||
#include "reclasses.hpp"
|
||||
#include "C_TEFireBullets.hpp"
|
||||
#include "common.hpp"
|
||||
#pragma once
|
||||
namespace hacks::tf2::seedprediction
|
||||
{
|
||||
void handleFireBullets(C_TEFireBullets *);
|
||||
struct seedstruct
|
||||
{
|
||||
int tickcount;
|
||||
int seed;
|
||||
float time;
|
||||
bool operator<(const seedstruct &rhs) const
|
||||
{
|
||||
return tickcount < rhs.tickcount;
|
||||
}
|
||||
};
|
||||
struct predictSeed2
|
||||
{
|
||||
seedstruct base;
|
||||
int tickcount;
|
||||
double resolution;
|
||||
bool operator<(const predictSeed2 &rhs) const
|
||||
{
|
||||
return tickcount < rhs.tickcount;
|
||||
}
|
||||
};
|
||||
struct IntervalEdge
|
||||
{
|
||||
int pos;
|
||||
double val;
|
||||
bool operator<(const IntervalEdge &rhs) const
|
||||
{
|
||||
return val < rhs.val;
|
||||
}
|
||||
};
|
||||
typedef boost::circular_buffer<seedstruct> buf;
|
||||
typedef boost::circular_buffer<predictSeed2> buf2;
|
||||
typedef boost::circular_buffer<IntervalEdge> buf3;
|
||||
extern buf bases;
|
||||
extern buf2 rebased;
|
||||
extern buf3 intervals;
|
||||
void selectBase();
|
||||
double predictOffset(const seedstruct& entry, int targetTick, double clockRes);
|
||||
}
|
@ -30,6 +30,7 @@
|
||||
#include "AutoDeadringer.hpp"
|
||||
#include "Bunnyhop.hpp"
|
||||
#include "LagExploit.hpp"
|
||||
#include "SeedPrediction.hpp"
|
||||
#endif
|
||||
#if ENABLE_VISUALS
|
||||
#include "Radar.hpp"
|
||||
|
@ -79,7 +79,8 @@ bool VisCheckEntFromEntVector(Vector startVector, CachedEntity *startEnt,
|
||||
CachedEntity *endEnt);
|
||||
Vector VischeckCorner(CachedEntity *player, CachedEntity *target, float maxdist,
|
||||
bool checkWalkable);
|
||||
std::pair<Vector,Vector> VischeckWall(CachedEntity *player, CachedEntity *target, float maxdist,
|
||||
std::pair<Vector, Vector> VischeckWall(CachedEntity *player,
|
||||
CachedEntity *target, float maxdist,
|
||||
bool checkWalkable);
|
||||
float vectorMax(Vector i);
|
||||
Vector vectorAbs(Vector i);
|
||||
|
@ -60,6 +60,7 @@ extern VMTHook clientmode4;
|
||||
extern VMTHook client;
|
||||
extern VMTHook engine;
|
||||
extern VMTHook netchannel;
|
||||
extern VMTHook firebullets;
|
||||
extern VMTHook clientdll;
|
||||
extern VMTHook matsurface;
|
||||
extern VMTHook studiorender;
|
||||
|
@ -7,7 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
#include "SeedPrediction.hpp"
|
||||
#if ENABLE_VISUALS
|
||||
union SDL_Event;
|
||||
struct SDL_Window;
|
||||
@ -34,9 +34,10 @@ struct SDL_Window;
|
||||
#define HOOK_ARGS(name) \
|
||||
hooked_methods::methods::name, offsets::name(), \
|
||||
&hooked_methods::original::name
|
||||
|
||||
namespace hooked_methods
|
||||
{
|
||||
// FireBullets
|
||||
DECLARE_HOOKED_METHOD(PreDataUpdate, void, void *, int);
|
||||
// ClientMode
|
||||
DECLARE_HOOKED_METHOD(CreateMove, bool, void *, float, CUserCmd *);
|
||||
DECLARE_HOOKED_METHOD(LevelInit, void, void *, const char *);
|
||||
|
@ -23,11 +23,9 @@ struct user_data
|
||||
bool has_software{ false };
|
||||
bool no_target{ false };
|
||||
bool is_developer{};
|
||||
#if ENABLE_VISUALS
|
||||
bool has_color{ false };
|
||||
colors::rgba_t color{};
|
||||
bool rainbow{ false };
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -3,6 +3,7 @@ target_sources(cathook PRIVATE
|
||||
"${CMAKE_CURRENT_LIST_DIR}/C_BaseEntity.hpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/C_BasePlayer.hpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/C_MannVsMachineStats.hpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/C_TEFireBullets.hpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/CTFGCClientSystem.hpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/CTFInventoryManager.hpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/CTFPartyClient.hpp"
|
||||
|
@ -20,6 +20,7 @@ public:
|
||||
static ITFGroupMatchCriteria *MutLocalGroupCriteria(CTFPartyClient *client);
|
||||
static bool BCanQueueForStandby(CTFPartyClient *this_);
|
||||
char RequestQueueForMatch(int type);
|
||||
bool BInQueueForMatchGroup(int type);
|
||||
char RequestLeaveForMatch(int type);
|
||||
int BInvitePlayerToParty(CSteamID steamid);
|
||||
int BRequestJoinPlayer(CSteamID steamid);
|
||||
|
20
include/reclasses/C_TEFireBullets.hpp
Normal file
20
include/reclasses/C_TEFireBullets.hpp
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* C_TEFireBullets.h
|
||||
*
|
||||
* Created on: Jul 27, 2018
|
||||
* Author: bencat07
|
||||
*/
|
||||
#pragma once
|
||||
#include "reclasses.hpp"
|
||||
class C_TEFireBullets : public C_BaseTempEntity
|
||||
{
|
||||
public:
|
||||
C_TEFireBullets() = delete;
|
||||
static C_TEFireBullets *GTEFireBullets();
|
||||
|
||||
public:
|
||||
int m_iSeed();
|
||||
int m_iWeaponID();
|
||||
int m_iPlayer();
|
||||
float m_flSpread();
|
||||
};
|
@ -22,5 +22,6 @@
|
||||
#include "ITFGroupMatchCriteria.hpp"
|
||||
#include "CTFPartyClient.hpp"
|
||||
|
||||
#include "C_TEFireBullets.hpp"
|
||||
#include "C_MannVsMachineStats.hpp"
|
||||
#include "CTFInventoryManager.hpp"
|
||||
|
@ -1,4 +1,5 @@
|
||||
target_sources(cathook PRIVATE
|
||||
"${CMAKE_CURRENT_LIST_DIR}/c_basetempentity.h"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/CGameRules.h"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/HUD.h"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/igamemovement.h"
|
||||
|
105
include/sdk/c_basetempentity.h
Normal file
105
include/sdk/c_basetempentity.h
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* c_basetempentity.h
|
||||
*
|
||||
* Created on: Jul 27, 2018
|
||||
* Author: bencat07
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "client_class.h"
|
||||
#include "iclientnetworkable.h"
|
||||
class C_BaseTempEntity : public IClientUnknown, public IClientNetworkable
|
||||
|
||||
{
|
||||
public:
|
||||
typedef C_BaseTempEntity ThisClass;
|
||||
DECLARE_CLIENTCLASS();
|
||||
|
||||
C_BaseTempEntity( void );
|
||||
virtual ~C_BaseTempEntity( void );
|
||||
|
||||
|
||||
// IClientUnknown implementation.
|
||||
public:
|
||||
|
||||
virtual void SetRefEHandle( const CBaseHandle &handle ) { Assert( false ); }
|
||||
virtual const CBaseHandle& GetRefEHandle() const { return *((CBaseHandle*)0); }
|
||||
|
||||
virtual IClientUnknown* GetIClientUnknown() { return this; }
|
||||
virtual ICollideable* GetCollideable() { return 0; }
|
||||
virtual IClientNetworkable* GetClientNetworkable() { return this; }
|
||||
virtual IClientRenderable* GetClientRenderable() { return 0; }
|
||||
virtual IClientEntity* GetIClientEntity() { return 0; }
|
||||
virtual C_BaseEntity* GetBaseEntity() { return 0; }
|
||||
virtual IClientThinkable* GetClientThinkable() { return 0; }
|
||||
|
||||
|
||||
// IClientNetworkable overrides.
|
||||
public:
|
||||
|
||||
virtual void Release();
|
||||
virtual void NotifyShouldTransmit( ShouldTransmitState_t state );
|
||||
virtual void PreDataUpdate( DataUpdateType_t updateType );
|
||||
virtual void PostDataUpdate( DataUpdateType_t updateType );
|
||||
virtual void OnDataUnchangedInPVS( void ) { }
|
||||
virtual void OnPreDataChanged( DataUpdateType_t updateType );
|
||||
virtual void OnDataChanged( DataUpdateType_t updateType );
|
||||
virtual void SetDormant( bool bDormant );
|
||||
virtual bool IsDormant( void );
|
||||
virtual int entindex( void ) const;
|
||||
virtual void ReceiveMessage( int classID, bf_read &msg );
|
||||
virtual void* GetDataTableBasePtr();
|
||||
virtual void SetDestroyedOnRecreateEntities( void );
|
||||
|
||||
public:
|
||||
|
||||
// Dummy for CNetworkVars.
|
||||
void NetworkStateChanged() {}
|
||||
void NetworkStateChanged( void *pVar ) {}
|
||||
|
||||
virtual bool Init(int entnum, int iSerialNum);
|
||||
|
||||
virtual void Precache( void );
|
||||
|
||||
// For dynamic entities, return true to allow destruction
|
||||
virtual bool ShouldDestroy( void ) { return false; };
|
||||
|
||||
C_BaseTempEntity *GetNext( void );
|
||||
|
||||
// Get list of tempentities
|
||||
static C_BaseTempEntity *GetList( void );
|
||||
|
||||
C_BaseTempEntity *GetNextDynamic( void );
|
||||
|
||||
// Determine the color modulation amount
|
||||
void GetColorModulation( float* color )
|
||||
{
|
||||
assert(color);
|
||||
color[0] = color[1] = color[2] = 1.0f;
|
||||
}
|
||||
|
||||
// Should this object be able to have shadows cast onto it?
|
||||
virtual bool ShouldReceiveProjectedTextures( int flags ) { return false; }
|
||||
|
||||
// Static members
|
||||
public:
|
||||
// List of dynamically allocated temp entis
|
||||
static C_BaseTempEntity *GetDynamicList();
|
||||
|
||||
// Called at startup to allow temp entities to precache any models/sounds that they need
|
||||
static void PrecacheTempEnts( void );
|
||||
|
||||
static void ClearDynamicTempEnts( void );
|
||||
|
||||
static void CheckDynamicTempEnts( void );
|
||||
|
||||
private:
|
||||
|
||||
// Next in chain
|
||||
C_BaseTempEntity *m_pNext;
|
||||
C_BaseTempEntity *m_pNextDynamic;
|
||||
|
||||
// TEs add themselves to this list for the executable.
|
||||
static C_BaseTempEntity *s_pTempEntities;
|
||||
static C_BaseTempEntity *s_pDynamicEntities;
|
||||
};
|
@ -14,4 +14,6 @@ void queue_start();
|
||||
void queue_leave();
|
||||
void dcandabandon();
|
||||
void abandon();
|
||||
extern CatVar queue;
|
||||
extern CatEnum queue_mode;
|
||||
}
|
||||
|
@ -93,18 +93,17 @@ CatCommand
|
||||
lock_single("achievement_lock_single", "Locks single achievement by INDEX!",
|
||||
[](const CCommand &args) {
|
||||
char *out = nullptr;
|
||||
int index = strtol(args.Arg(1), &out, 10);
|
||||
int index = atoi(args.Arg(1));
|
||||
if (out == args.Arg(1))
|
||||
{
|
||||
logging::Info("NaN achievement INDEX!");
|
||||
return;
|
||||
}
|
||||
IAchievement *ach =
|
||||
g_IAchievementMgr->GetAchievementByIndex(index);
|
||||
IAchievement *ach = reinterpret_cast<IAchievement *>(g_IAchievementMgr->GetAchievementByIndex(index));
|
||||
if (ach)
|
||||
{
|
||||
g_ISteamUserStats->RequestCurrentStats();
|
||||
g_ISteamUserStats->ClearAchievement(ach->GetName());
|
||||
g_ISteamUserStats->ClearAchievement(g_IAchievementMgr->GetAchievementByIndex(index)->GetName());
|
||||
g_ISteamUserStats->StoreStats();
|
||||
g_ISteamUserStats->RequestCurrentStats();
|
||||
}
|
||||
|
@ -198,11 +198,11 @@ bool BacktrackAimbot()
|
||||
if (iBestTarget == -1)
|
||||
return true;
|
||||
int tickcnt = 0;
|
||||
|
||||
int tickus = (float(hacks::shared::backtrack::latency) > 800.0f || float(hacks::shared::backtrack::latency) < 200.0f) ? 12 : 24;
|
||||
for (auto i : hacks::shared::backtrack::headPositions[iBestTarget])
|
||||
{
|
||||
bool good_tick = false;
|
||||
for (int j = 0; j < 12; ++j)
|
||||
for (int j = 0; j < tickus; ++j)
|
||||
if (tickcnt == hacks::shared::backtrack::sorted_ticks[j].tick &&
|
||||
hacks::shared::backtrack::sorted_ticks[j].tickcount != INT_MAX)
|
||||
good_tick = true;
|
||||
@ -220,8 +220,8 @@ bool BacktrackAimbot()
|
||||
if (CE_BAD(tar))
|
||||
continue;
|
||||
// target_eid = tar->m_IDX;
|
||||
Vector &angles = NET_VECTOR(tar, netvar.m_angEyeAngles);
|
||||
float &simtime = NET_FLOAT(tar, netvar.m_flSimulationTime);
|
||||
Vector &angles = NET_VECTOR(RAW_ENT(tar), netvar.m_angEyeAngles);
|
||||
float &simtime = CE_FLOAT(tar, netvar.m_flSimulationTime);
|
||||
angles.y = i.viewangles;
|
||||
simtime = i.simtime;
|
||||
g_pUserCmd->tick_count = i.tickcount;
|
||||
@ -255,7 +255,8 @@ void CreateMove()
|
||||
// Auto-Unzoom
|
||||
if (auto_unzoom)
|
||||
{
|
||||
if (g_pLocalPlayer->holding_sniper_rifle && g_pLocalPlayer->bZoomed && zoomTime.check(3000))
|
||||
if (g_pLocalPlayer->holding_sniper_rifle && g_pLocalPlayer->bZoomed &&
|
||||
zoomTime.check(3000))
|
||||
{
|
||||
g_pUserCmd->buttons |= IN_ATTACK2;
|
||||
}
|
||||
@ -509,6 +510,7 @@ CachedEntity *RetrieveBestTarget(bool aimkey_state)
|
||||
// We dont have a target currently so we must find one, reset statuses
|
||||
foundTarget = false;
|
||||
target_last = nullptr;
|
||||
target_eid = -1;
|
||||
|
||||
float target_highest_score, scr;
|
||||
CachedEntity *ent;
|
||||
@ -561,6 +563,9 @@ CachedEntity *RetrieveBestTarget(bool aimkey_state)
|
||||
// Save the ent for future use with target lock
|
||||
target_last = target_highest_ent;
|
||||
|
||||
if (CE_GOOD(target_last))
|
||||
target_eid = target_last->m_IDX;
|
||||
|
||||
return target_highest_ent;
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
#include "common.hpp"
|
||||
#include "hacks/Backtrack.hpp"
|
||||
|
||||
namespace hacks::tf2::autobackstab
|
||||
{
|
||||
|
||||
@ -23,23 +22,133 @@ const Vector GetWorldSpaceCenter(CachedEntity *ent)
|
||||
|
||||
static CatVar enabled(CV_SWITCH, "autobackstab", "0", "Auto Backstab",
|
||||
"Does not depend on triggerbot!");
|
||||
static CatVar value(CV_INT, "autobackstab_range", "75.0f",
|
||||
"Set Detection Distance to this much");
|
||||
static CatVar silent(CV_SWITCH, "autobackstab_silent", "1", "Silent");
|
||||
bool found;
|
||||
std::pair<Vector, Vector> GetHitboxBounds(CachedEntity *it, int hitbox)
|
||||
{
|
||||
std::pair<Vector, Vector> result(it->hitboxes.GetHitbox(hitbox)->min,it->hitboxes.GetHitbox(hitbox)->max);
|
||||
return result;
|
||||
}
|
||||
// TODO improve
|
||||
bool CanBackstab(CachedEntity *tar, Vector Local_ang)
|
||||
{
|
||||
if (CE_BAD(tar))
|
||||
return false;
|
||||
// Get the forward view vector of the target, ignore Z
|
||||
Vector vecVictimForward = NET_VECTOR(RAW_ENT(tar), netvar.m_angEyeAngles);
|
||||
vecVictimForward.z = 0.0f;
|
||||
vecVictimForward.NormalizeInPlace();
|
||||
|
||||
// Get a vector from my origin to my targets origin
|
||||
Vector vecToTarget;
|
||||
vecToTarget = GetWorldSpaceCenter(tar) - GetWorldSpaceCenter(LOCAL_E);
|
||||
vecToTarget.z = 0.0f;
|
||||
vecToTarget.NormalizeInPlace();
|
||||
|
||||
// Get a forward vector of the attacker.
|
||||
Vector vecOwnerForward = Local_ang;
|
||||
vecOwnerForward.z = 0.0f;
|
||||
vecOwnerForward.NormalizeInPlace();
|
||||
|
||||
float flDotOwner = DotProduct(vecOwnerForward, vecToTarget);
|
||||
float flDotVictim = DotProduct(vecVictimForward, vecToTarget);
|
||||
|
||||
// Make sure they're actually facing the target.
|
||||
// This needs to be done because lag compensation can place target slightly
|
||||
// behind the attacker.
|
||||
if (flDotOwner > 0.5)
|
||||
return (flDotVictim > -0.1);
|
||||
return false;
|
||||
}
|
||||
void CreateMove()
|
||||
{
|
||||
if (!enabled)
|
||||
return;
|
||||
if (!CE_GOOD(LOCAL_E))
|
||||
if (!CE_GOOD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer() || !CE_GOOD(LOCAL_W))
|
||||
return;
|
||||
if (!LOCAL_E->m_bAlivePlayer())
|
||||
return;
|
||||
if (g_pLocalPlayer->weapon()->m_iClassID() != CL_CLASS(CTFKnife))
|
||||
return;
|
||||
if (!hacks::shared::backtrack::enable &&
|
||||
CE_BYTE(g_pLocalPlayer->weapon(), netvar.m_bReadyToBackstab))
|
||||
int eid = -1;
|
||||
Vector endpos;
|
||||
ICollideable *p = RAW_ENT(LOCAL_E)->GetCollideable();
|
||||
const Vector &max1 = p->OBBMaxs() + RAW_ENT(LOCAL_E)->GetAbsOrigin();
|
||||
const Vector &min1 = p->OBBMins() + RAW_ENT(LOCAL_E)->GetAbsOrigin();
|
||||
WhatIAmLookingAt(&eid, &endpos);
|
||||
|
||||
CachedEntity *target = nullptr;
|
||||
if (eid > -1)
|
||||
target = ENTITY(eid);
|
||||
if (CE_GOOD(target) && target != LOCAL_E &&
|
||||
target->m_iTeam() != LOCAL_E->m_iTeam() && target->m_bAlivePlayer() &&
|
||||
target->m_Type() == ENTITY_PLAYER &&
|
||||
!hacks::shared::backtrack::enable &&
|
||||
CanBackstab(target, g_pLocalPlayer->v_OrigViewangles))
|
||||
{
|
||||
float swingrange =
|
||||
re::C_TFWeaponBaseMelee::GetSwingRange(RAW_ENT(LOCAL_W));
|
||||
const Vector &max2 = GetHitboxBounds(target, 1).second +
|
||||
Vector(swingrange, swingrange, swingrange);
|
||||
const Vector &min2 = GetHitboxBounds(target, 1).first -
|
||||
Vector(swingrange, swingrange, swingrange);
|
||||
if ((min1.x <= max2.x && max1.x >= min2.x) &&
|
||||
(min1.y <= max2.y && max1.y >= min2.y) &&
|
||||
(min1.z <= max2.z && max1.z >= min2.z))
|
||||
g_pUserCmd->buttons |= IN_ATTACK;
|
||||
}
|
||||
else if (!hacks::shared::backtrack::enable)
|
||||
{
|
||||
CachedEntity *tar = nullptr;
|
||||
float bestscr = 9999.9f;
|
||||
int bestent = -1;
|
||||
for (int i = 0; i < g_IEngine->GetMaxClients(); i++)
|
||||
{
|
||||
CachedEntity *tmp = ENTITY(i);
|
||||
if (CE_BAD(tmp))
|
||||
continue;
|
||||
if (tmp == LOCAL_E)
|
||||
continue;
|
||||
if (tmp->m_iTeam() == LOCAL_E->m_iTeam())
|
||||
continue;
|
||||
if (!tmp->m_bAlivePlayer())
|
||||
continue;
|
||||
if (tmp->m_Type() != ENTITY_PLAYER)
|
||||
continue;
|
||||
float swingrange =
|
||||
re::C_TFWeaponBaseMelee::GetSwingRange(RAW_ENT(LOCAL_W));
|
||||
ICollideable *c = RAW_ENT(tmp)->GetCollideable();
|
||||
const Vector &max2 = c->OBBMaxs() + tmp->m_vecOrigin() +
|
||||
Vector(swingrange, swingrange, swingrange);
|
||||
const Vector &min2 = c->OBBMins() + tmp->m_vecOrigin() -
|
||||
Vector(swingrange, swingrange, swingrange);
|
||||
if ((min1.x <= max2.x && max1.x >= min2.x) &&
|
||||
(min1.y <= max2.y && max1.y >= min2.y) &&
|
||||
(min1.z <= max2.z && max1.z >= min2.z) &&
|
||||
bestscr > tmp->m_flDistance())
|
||||
{
|
||||
bestent = tmp->m_IDX;
|
||||
bestscr = tmp->m_flDistance();
|
||||
}
|
||||
}
|
||||
if (bestent > -1)
|
||||
tar = ENTITY(bestent);
|
||||
if (CE_BAD(tar))
|
||||
return;
|
||||
Vector eyeang = g_pLocalPlayer->v_OrigViewangles;
|
||||
for (float i = -180.0f; i < 180.0f; i += 30.0f)
|
||||
{
|
||||
eyeang.y = i;
|
||||
if (CanBackstab(tar, eyeang))
|
||||
{
|
||||
g_pUserCmd->viewangles.y = eyeang.y;
|
||||
g_pUserCmd->buttons |= IN_ATTACK;
|
||||
if (silent)
|
||||
g_pLocalPlayer->bUseSilentAngles = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!hacks::shared::backtrack::enable)
|
||||
@ -48,10 +157,11 @@ void CreateMove()
|
||||
return;
|
||||
int iBestTarget = hacks::shared::backtrack::iBestTarget;
|
||||
int tickcnt = 0;
|
||||
int tickus = (float(hacks::shared::backtrack::latency) > 800.0f || float(hacks::shared::backtrack::latency) < 200.0f) ? 12 : 24;
|
||||
for (auto i : hacks::shared::backtrack::headPositions[iBestTarget])
|
||||
{
|
||||
bool good_tick = false;
|
||||
for (int j = 0; j < 12; ++j)
|
||||
for (int j = 0; j < tickus; ++j)
|
||||
if (tickcnt == hacks::shared::backtrack::sorted_ticks[j].tick &&
|
||||
hacks::shared::backtrack::sorted_ticks[j].tickcount !=
|
||||
INT_MAX)
|
||||
@ -70,8 +180,10 @@ void CreateMove()
|
||||
// ok just in case
|
||||
if (CE_BAD(tar))
|
||||
continue;
|
||||
Vector &angles = NET_VECTOR(tar, netvar.m_angEyeAngles);
|
||||
float &simtime = NET_FLOAT(tar, netvar.m_flSimulationTime);
|
||||
Vector &angles =
|
||||
NET_VECTOR(RAW_ENT(tar), netvar.m_angEyeAngles);
|
||||
float &simtime =
|
||||
NET_FLOAT(RAW_ENT(tar), netvar.m_flSimulationTime);
|
||||
angles.y = i.viewangles;
|
||||
simtime = i.simtime;
|
||||
g_pUserCmd->tick_count = i.tickcount;
|
||||
|
@ -91,14 +91,15 @@ void UpdateSearch()
|
||||
return;
|
||||
|
||||
re::CTFGCClientSystem *gc = re::CTFGCClientSystem::GTFGCClientSystem();
|
||||
re::CTFPartyClient *pc = re::CTFPartyClient::GTFPartyClient();
|
||||
if (g_pUserCmd && gc && gc->BConnectedToMatchServer(false) &&
|
||||
gc->BHaveLiveMatch())
|
||||
tfmm::queue_leave();
|
||||
if (!gc->BConnectedToMatchServer(false) &&
|
||||
if (gc && !gc->BConnectedToMatchServer(false) &&
|
||||
queuetime.test_and_set(10 * 1000 * 60) && !gc->BHaveLiveMatch())
|
||||
tfmm::queue_leave();
|
||||
if (gc && !gc->BConnectedToMatchServer(false) && !gc->BHaveLiveMatch() &&
|
||||
autoqueue_timer.test_and_set(1000 * 30))
|
||||
if (gc && !gc->BConnectedToMatchServer(false) && !gc->BHaveLiveMatch())
|
||||
if (!(pc && pc->BInQueueForMatchGroup(int(tfmm::queue))))
|
||||
{
|
||||
logging::Info("Starting queue");
|
||||
tfmm::queue_start();
|
||||
|
@ -136,6 +136,7 @@ void Run()
|
||||
float simtime = CE_FLOAT(pEntity, netvar.m_flSimulationTime);
|
||||
Vector hitbox_spine = pEntity->hitboxes.GetHitbox(3)->center;
|
||||
Vector ent_orig = pEntity->InternalEntity()->GetAbsOrigin();
|
||||
auto hdr = g_IModelInfo->GetStudiomodel(RAW_ENT(pEntity)->GetModel());
|
||||
headPositions[i][cmd->command_number % ticks] =
|
||||
BacktrackData{ cmd->tick_count, hitboxpos, min, max,
|
||||
hitbox_spine, viewangles, simtime, ent_orig };
|
||||
@ -160,7 +161,7 @@ void Run()
|
||||
float bestFOV = 180.0f;
|
||||
float distance, prev_distance_ticks = 9999;
|
||||
|
||||
for (int i = 0; i < 12; ++i)
|
||||
for (int i = 0; i < ticks; ++i)
|
||||
sorted_ticks[i] = BestTickData{ INT_MAX, i };
|
||||
for (int t = 0; t < ticks; ++t)
|
||||
{
|
||||
@ -169,10 +170,12 @@ void Run()
|
||||
BestTickData{ headPositions[iBestTarget][t].tickcount, t };
|
||||
}
|
||||
std::sort(sorted_ticks, sorted_ticks + ticks);
|
||||
int tickus = (float(latency) > 800.0f || float(latency) < 200.0f) ? 12 : 24;
|
||||
for (int t = 0; t < ticks; ++t)
|
||||
{
|
||||
bool good_tick = false;
|
||||
for (int i = 0; i < 12; ++i)
|
||||
|
||||
for (int i = 0; i < tickus; ++i)
|
||||
if (t == sorted_ticks[i].tick &&
|
||||
sorted_ticks[i].tickcount != INT_MAX &&
|
||||
sorted_ticks[i].tickcount)
|
||||
@ -207,8 +210,8 @@ void Run()
|
||||
return;
|
||||
auto i = headPositions[iBestTarget][bestTick];
|
||||
cmd->tick_count = i.tickcount;
|
||||
Vector &angles = NET_VECTOR(tar, netvar.m_angEyeAngles);
|
||||
float &simtime = NET_FLOAT(tar, netvar.m_flSimulationTime);
|
||||
Vector &angles = NET_VECTOR(RAW_ENT(tar), netvar.m_angEyeAngles);
|
||||
float &simtime = NET_FLOAT(RAW_ENT(tar), netvar.m_flSimulationTime);
|
||||
angles.y = i.viewangles;
|
||||
simtime = i.simtime;
|
||||
}
|
||||
@ -223,12 +226,14 @@ void Draw()
|
||||
return;
|
||||
if (!shouldDrawBt)
|
||||
return;
|
||||
int tickus = (float(latency) > 800.0f || float(latency) < 200.0f) ? 12 : 24;
|
||||
for (int i = 0; i < g_IEngine->GetMaxClients(); i++)
|
||||
{
|
||||
for (int j = 0; j < ticks; j++)
|
||||
{
|
||||
bool good_tick = false;
|
||||
for (int i = 0; i < 12; ++i)
|
||||
|
||||
for (int i = 0; i < tickus; ++i)
|
||||
if (j == sorted_ticks[i].tick)
|
||||
good_tick = true;
|
||||
if (!good_tick)
|
||||
|
@ -1,6 +1,7 @@
|
||||
target_sources(cathook PRIVATE
|
||||
"${CMAKE_CURRENT_LIST_DIR}/AutoJoin.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/CatBot.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/SeedPrediction.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/Spam.cpp")
|
||||
if(NOT LagbotMode)
|
||||
target_sources(cathook PRIVATE
|
||||
|
@ -244,7 +244,12 @@ void smart_crouch()
|
||||
{
|
||||
if (!LOCAL_E->hitboxes.GetHitbox(j))
|
||||
continue;
|
||||
if (!IsVectorVisible(ent->hitboxes.GetHitbox(0)->center, LOCAL_E->hitboxes.GetHitbox(j)->center) && !IsVectorVisible(ent->hitboxes.GetHitbox(0)->center, LOCAL_E->hitboxes.GetHitbox(j)->min) && !IsVectorVisible(ent->hitboxes.GetHitbox(0)->center, LOCAL_E->hitboxes.GetHitbox(j)->max))
|
||||
if (!IsVectorVisible(ent->hitboxes.GetHitbox(0)->center,
|
||||
LOCAL_E->hitboxes.GetHitbox(j)->center) &&
|
||||
!IsVectorVisible(ent->hitboxes.GetHitbox(0)->center,
|
||||
LOCAL_E->hitboxes.GetHitbox(j)->min) &&
|
||||
!IsVectorVisible(ent->hitboxes.GetHitbox(0)->center,
|
||||
LOCAL_E->hitboxes.GetHitbox(j)->max))
|
||||
continue;
|
||||
foundtar = true;
|
||||
crouch = true;
|
||||
|
@ -139,8 +139,8 @@ void addCrumbPair(CachedEntity *player1, CachedEntity *player2,
|
||||
int maxiterations = floor(corner2.DistTo(corner1)) / 40;
|
||||
for (int i = 0; i < maxiterations; i++)
|
||||
{
|
||||
breadcrumbs.push_back(corner1 + dist / vectorMax(vectorAbs(dist)) *
|
||||
40.0f * (i + 1));
|
||||
breadcrumbs.push_back(
|
||||
corner1 + dist / vectorMax(vectorAbs(dist)) * 40.0f * (i + 1));
|
||||
}
|
||||
}
|
||||
{
|
||||
@ -148,8 +148,8 @@ void addCrumbPair(CachedEntity *player1, CachedEntity *player2,
|
||||
int maxiterations = floor(corner2.DistTo(player2->m_vecOrigin())) / 40;
|
||||
for (int i = 0; i < maxiterations; i++)
|
||||
{
|
||||
breadcrumbs.push_back(corner2 + dist / vectorMax(vectorAbs(dist)) *
|
||||
40.0f * (i + 1));
|
||||
breadcrumbs.push_back(
|
||||
corner2 + dist / vectorMax(vectorAbs(dist)) * 40.0f * (i + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -236,14 +236,17 @@ void WorldTick()
|
||||
continue;
|
||||
if (corneractivate)
|
||||
{
|
||||
Vector indirectOrigin =
|
||||
VischeckCorner(LOCAL_E, entity, float(follow_activation) / 2,
|
||||
Vector indirectOrigin = VischeckCorner(
|
||||
LOCAL_E, entity, float(follow_activation) / 2,
|
||||
true); // get the corner location that the
|
||||
// future target is visible from
|
||||
std::pair<Vector, Vector> corners;
|
||||
if (!indirectOrigin.z && entity->m_IDX == lastent) // if we couldn't find it, run wallcheck instead
|
||||
if (!indirectOrigin.z &&
|
||||
entity->m_IDX == lastent) // if we couldn't find it, run
|
||||
// wallcheck instead
|
||||
{
|
||||
corners = VischeckWall(LOCAL_E, entity, float(follow_activation) / 2, true);
|
||||
corners = VischeckWall(LOCAL_E, entity,
|
||||
float(follow_activation) / 2, true);
|
||||
if (!corners.first.z || !corners.second.z)
|
||||
continue;
|
||||
// addCrumbs(LOCAL_E, corners.first);
|
||||
@ -430,13 +433,17 @@ void WorldTick()
|
||||
if (dist_to_target > (float) follow_distance)
|
||||
{
|
||||
// Check for jump
|
||||
if (autojump && lastJump.check(1000) && (idle_time.check(2000) || DistanceToGround({breadcrumbs[0].x,breadcrumbs[0].y,breadcrumbs[0].z + 5}) > 47))
|
||||
if (autojump && lastJump.check(1000) &&
|
||||
(idle_time.check(2000) ||
|
||||
DistanceToGround({ breadcrumbs[0].x, breadcrumbs[0].y,
|
||||
breadcrumbs[0].z + 5 }) > 47))
|
||||
{
|
||||
g_pUserCmd->buttons |= IN_JUMP;
|
||||
lastJump.update();
|
||||
}
|
||||
// Check if still moving. 70 HU = Sniper Zoomed Speed
|
||||
if (idle_time.check(3000) && CE_VECTOR(g_pLocalPlayer->entity, netvar.vVelocity).IsZero(60.0f))
|
||||
if (idle_time.check(3000) &&
|
||||
CE_VECTOR(g_pLocalPlayer->entity, netvar.vVelocity).IsZero(60.0f))
|
||||
{
|
||||
follow_target = 0;
|
||||
return;
|
||||
|
153
src/hacks/SeedPrediction.cpp
Normal file
153
src/hacks/SeedPrediction.cpp
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* SeedPrediction.cpp
|
||||
*
|
||||
* Created on: Jul 27, 2018
|
||||
* Author: bencat07
|
||||
*/
|
||||
#include "common.hpp"
|
||||
#include "SeedPrediction.hpp"
|
||||
#include "reclasses.hpp"
|
||||
constexpr double MIN_CLOCKRES = 0.25;
|
||||
constexpr double MAX_CLOCKRES = 8192.5;
|
||||
double clockRes;
|
||||
float seedFraction = 0.0f;
|
||||
//static CatVar enableSeedPrediction(CV_SWITCH, "seed_prediction", "1", "Seed Predcition", "Enable Seed prediction");
|
||||
namespace hacks::tf2::seedprediction
|
||||
{
|
||||
buf bases{9999};
|
||||
buf2 rebased{9999};
|
||||
buf3 intervals{9999};
|
||||
seedstruct selected{9999};
|
||||
// Server sends us seeds when other players are shooting.
|
||||
// Needs to be called in appropriate hook (not PostDataUpdate) since PostDataUpdate for TempEntities gives inaccurate tickcount
|
||||
void handleFireBullets(C_TEFireBullets* ent) {
|
||||
if (g_IEngine->IsInGame()) {
|
||||
INetChannel *ch = (INetChannel *)g_IEngine->GetNetChannelInfo();
|
||||
double time = g_GlobalVars->curtime * g_GlobalVars->interval_per_tick - (ch ? ch->GetLatency(MAX_FLOWS) / 2 : 0.0f);
|
||||
bases.push_back(seedstruct{ g_GlobalVars->tickcount, ent->m_iSeed(), time }); // It's circular buffer
|
||||
selectBase();
|
||||
}
|
||||
}
|
||||
|
||||
void selectBase() {
|
||||
if (bases.size() <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
int total = bases.size();
|
||||
selected = bases[bases.size()-1];
|
||||
seedFraction = 0.0;
|
||||
|
||||
// Algorithmic approach to estimate server time offset
|
||||
|
||||
// 1. Find clock resolution
|
||||
// For each reasonable precision value "rebase" seeds to the same tick
|
||||
// and check if they are close to each other (by looking for largest gap between).
|
||||
|
||||
int bestGap = 0;
|
||||
double newClockRes = 1.0;
|
||||
|
||||
for (double res = MIN_CLOCKRES; res < MAX_CLOCKRES+1.0; res *= 2.0) {
|
||||
rebased.clear();
|
||||
for (seedstruct& base : bases) {
|
||||
rebased.push_back(predictSeed2{base, selected.tickcount, res});
|
||||
}
|
||||
|
||||
std::sort(rebased.begin(), rebased.end());
|
||||
int gap = 0;
|
||||
|
||||
for (int i = 0; i < rebased.size(); i++) {
|
||||
int left = rebased[i].tickcount;
|
||||
int right = rebased[i+1 < rebased.size() ? i+1 : 0].tickcount;
|
||||
gap = max(gap, (right - left) % 256);
|
||||
}
|
||||
|
||||
gap = (gap > 0 ? gap : 256);
|
||||
if (bestGap < gap) {
|
||||
bestGap = gap;
|
||||
newClockRes = res;
|
||||
}
|
||||
}
|
||||
|
||||
if (total >= 5) {
|
||||
clockRes = newClockRes;
|
||||
}
|
||||
|
||||
// 2. Find seed fraction offset
|
||||
// Estimate time more precisely: "rebase" seeds to same tick (keep fraction part),
|
||||
// interpret them as intervals of size 1 and find offset which covers most of them.
|
||||
|
||||
double maxDisp = 5.0 / clockRes;
|
||||
intervals.clear();
|
||||
|
||||
for (seedstruct& base : bases) {
|
||||
double disp = double(base.seed) - double(selected.seed);
|
||||
disp = fmod(disp - predictOffset(selected, base.tickcount, clockRes), 256);
|
||||
disp = (disp > 128.0 ? disp - 256.0 : disp);
|
||||
|
||||
if (abs(disp) < max(1.2, maxDisp)) {
|
||||
intervals.push_back({ 1, disp - 0.5 }); // Actually "interval ends", not "intervals"
|
||||
intervals.push_back({ -1, disp + 0.5 });
|
||||
}
|
||||
}
|
||||
|
||||
int curChance = 0, bestChance = 0;
|
||||
sort(intervals.begin(), intervals.end());
|
||||
|
||||
for (int i = 0; i+1 < intervals.size(); i++) {
|
||||
IntervalEdge& inter = intervals[i];
|
||||
curChance += inter.val;
|
||||
|
||||
if (curChance > bestChance) {
|
||||
bestChance = curChance;
|
||||
seedFraction = (inter.pos + intervals[i+1].pos) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
logging::Info("seedpred-stats", "Seed prediction: res = %.3f, chance = %d%%\n", clockRes, bestChance*100/total);
|
||||
}
|
||||
|
||||
double predictOffset(const seedstruct& entry, int targetTick, double clockRes) {
|
||||
return (1000.0 * g_GlobalVars->interval_per_tick / clockRes) * double(targetTick-entry.tickcount);
|
||||
}
|
||||
|
||||
int predictSeed(const seedstruct& entry, int targetTick, double clockRes, double SeedOffset) {
|
||||
return (entry.seed + int(roundeven(predictOffset(entry, targetTick, clockRes)) + SeedOffset)) % 256;
|
||||
}
|
||||
|
||||
int predictTick(double targetTime) {
|
||||
INetChannel *ch = (INetChannel *)g_IEngine->GetNetChannelInfo();
|
||||
double ping = ch ? ch->GetLatency(MAX_FLOWS) / 2 : 0.0f;
|
||||
double deltaTime = targetTime - selected.time + ping;
|
||||
return int(double(selected.tickcount) + deltaTime / g_GlobalVars->interval_per_tick + 0.7);
|
||||
}
|
||||
|
||||
int predictTick() {
|
||||
return predictTick(g_GlobalVars->curtime * g_GlobalVars->interval_per_tick);
|
||||
}
|
||||
|
||||
int predictSeed(double targetTime) {
|
||||
INetChannel *ch = (INetChannel *)g_IEngine->GetNetChannelInfo();
|
||||
double ping = ch ? ch->GetLatency(MAX_FLOWS) / 2 : 0.0f;
|
||||
double deltaTime = targetTime - selected.time + ping;
|
||||
int tick = int(double(selected.tickcount) + deltaTime / g_GlobalVars->interval_per_tick + 0.7);
|
||||
double SeedOffset = predictOffset(selected, tick, clockRes);
|
||||
int seed = predictSeed(selected, tick, clockRes, SeedOffset);
|
||||
|
||||
logging::Info("seedpred-pred", "Last shot: guessed server tick = %d, guessed seed = %03d\n", tick, seed);
|
||||
return seed;
|
||||
}
|
||||
|
||||
int predictSeed() {
|
||||
return predictSeed(g_GlobalVars->curtime * g_GlobalVars->interval_per_tick);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
logging::Info("seedpred-stats", "Seed prediction: reset\n");
|
||||
if (!bases.empty()) {
|
||||
bases.clear();
|
||||
clockRes = 2.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,10 +82,11 @@ bool CanBacktrack()
|
||||
{
|
||||
int target = hacks::shared::backtrack::iBestTarget;
|
||||
int tickcnt = 0;
|
||||
int tickus = (float(hacks::shared::backtrack::latency) > 800.0f || float(hacks::shared::backtrack::latency) < 200.0f) ? 12 : 24;
|
||||
for (auto i : hacks::shared::backtrack::headPositions[target])
|
||||
{
|
||||
bool good_tick = false;
|
||||
for (int j = 0; j < 12; ++j)
|
||||
for (int j = 0; j < tickus; ++j)
|
||||
if (tickcnt == hacks::shared::backtrack::sorted_ticks[j].tick &&
|
||||
hacks::shared::backtrack::sorted_ticks[j].tickcount != INT_MAX)
|
||||
good_tick = true;
|
||||
@ -125,8 +126,8 @@ bool CanBacktrack()
|
||||
// ok just in case
|
||||
if (CE_BAD(tar))
|
||||
continue;
|
||||
Vector &angles = NET_VECTOR(tar, netvar.m_angEyeAngles);
|
||||
float &simtime = NET_FLOAT(tar, netvar.m_flSimulationTime);
|
||||
Vector &angles = NET_VECTOR(RAW_ENT(tar), netvar.m_angEyeAngles);
|
||||
float &simtime = NET_FLOAT(RAW_ENT(tar), netvar.m_flSimulationTime);
|
||||
angles.y = i.viewangles;
|
||||
g_pUserCmd->tick_count = i.tickcount;
|
||||
g_pUserCmd->buttons |= IN_ATTACK;
|
||||
|
@ -182,7 +182,8 @@ Vector VischeckCorner(CachedEntity *player, CachedEntity *target, float maxdist,
|
||||
}
|
||||
|
||||
// return Two Corners that connect perfectly to ent and local player
|
||||
std::pair<Vector,Vector> VischeckWall(CachedEntity *player, CachedEntity *target, float maxdist,
|
||||
std::pair<Vector, Vector> VischeckWall(CachedEntity *player,
|
||||
CachedEntity *target, float maxdist,
|
||||
bool checkWalkable)
|
||||
{
|
||||
int maxiterations = maxdist / 40;
|
||||
@ -278,21 +279,27 @@ std::pair<Vector,Vector> VischeckWall(CachedEntity *player, CachedEntity *target
|
||||
break;
|
||||
}
|
||||
// check if the virtualOrigin2 can see the target
|
||||
// if (!VisCheckEntFromEntVector(virtualOrigin2, player, target))
|
||||
// if
|
||||
// (!VisCheckEntFromEntVector(virtualOrigin2,
|
||||
// player, target))
|
||||
// continue;
|
||||
// if (!IsVectorVisible(virtualOrigin, virtualOrigin2, true))
|
||||
// if (!IsVectorVisible(virtualOrigin,
|
||||
// virtualOrigin2, true))
|
||||
// continue;
|
||||
// if (!IsVectorVisible(virtualOrigin2, target->m_vecOrigin(), true))
|
||||
// if (!IsVectorVisible(virtualOrigin2,
|
||||
// target->m_vecOrigin(), true))
|
||||
// continue;
|
||||
if (!IsVectorVisible(virtualOrigin, virtualOrigin2, true))
|
||||
continue;
|
||||
if (!IsVectorVisible(virtualOrigin2, target->m_vecOrigin()))
|
||||
continue;
|
||||
std::pair<Vector, Vector> toret(virtualOrigin, virtualOrigin2);
|
||||
std::pair<Vector, Vector> toret(virtualOrigin,
|
||||
virtualOrigin2);
|
||||
if (!checkWalkable)
|
||||
return toret;
|
||||
// check if the location is accessible
|
||||
if (!canReachVector(origin, virtualOrigin) || !canReachVector(virtualOrigin2, virtualOrigin))
|
||||
if (!canReachVector(origin, virtualOrigin) ||
|
||||
!canReachVector(virtualOrigin2, virtualOrigin))
|
||||
continue;
|
||||
if (canReachVector(virtualOrigin2, target->m_vecOrigin()))
|
||||
return toret;
|
||||
@ -371,7 +378,8 @@ bool canReachVector(Vector loc, Vector dest)
|
||||
else
|
||||
{
|
||||
// check if the vector is too high above ground
|
||||
// higher to avoid small false positives, player can jump 42 hu according to
|
||||
// higher to avoid small false positives, player can jump 42 hu
|
||||
// according to
|
||||
// the tf2 wiki
|
||||
if (DistanceToGround({ loc.x, loc.y, loc.z + 5 }) >= 40)
|
||||
return false;
|
||||
@ -400,7 +408,8 @@ bool canReachVector(Vector loc, Vector dest)
|
||||
trace_t trace;
|
||||
Ray_t ray;
|
||||
ray.Init(loc, directionalLoc);
|
||||
g_ITrace->TraceRay(ray, 0x4200400B, &trace::filter_no_player, &trace);
|
||||
g_ITrace->TraceRay(ray, 0x4200400B, &trace::filter_no_player,
|
||||
&trace);
|
||||
// distance of trace < than 26
|
||||
if (trace.startpos.DistTo(trace.endpos) < 26.0f)
|
||||
return false;
|
||||
@ -755,7 +764,8 @@ bool IsEntityVectorVisible(CachedEntity *entity, Vector endpos)
|
||||
g_ITrace->TraceRay(ray, MASK_SHOT_HULL, &trace::filter_default,
|
||||
&trace_object);
|
||||
}
|
||||
return (((IClientEntity *) trace_object.m_pEnt) == RAW_ENT(entity) || trace_object.fraction >= 0.99f);
|
||||
return (((IClientEntity *) trace_object.m_pEnt) == RAW_ENT(entity) ||
|
||||
trace_object.fraction >= 0.99f);
|
||||
}
|
||||
|
||||
// For when you need to vis check something that isnt the local player
|
||||
@ -1351,7 +1361,8 @@ void PrintChat(const char *fmt, ...)
|
||||
va_end(list);
|
||||
std::unique_ptr<char> str(strfmt("\x07%06X[\x07%06XCAT\x07%06X]\x01 %s",
|
||||
0x5e3252, 0xba3d9a, 0x5e3252,
|
||||
buf.get()).release());
|
||||
buf.get())
|
||||
.release());
|
||||
// FIXME DEBUG LOG
|
||||
logging::Info("%s", str.get());
|
||||
chat->Printf(str.get());
|
||||
|
@ -60,7 +60,9 @@ CatCommand debug_ammo("debug_ammo", "Debug ammo", []() {
|
||||
logging::Info("%d %d", i, CE_INT(LOCAL_E, netvar.m_iAmmo + i * 4));
|
||||
}
|
||||
});
|
||||
std::deque<int> entstocheck{};
|
||||
bool brutesoon[32];
|
||||
int lasthits = 0;
|
||||
std::array<Timer, 32> xd{};
|
||||
void Update()
|
||||
{
|
||||
CachedEntity *weapon = LOCAL_W;
|
||||
@ -84,10 +86,41 @@ void Update()
|
||||
// ONLY tracks primary ammo
|
||||
int ammo = CE_INT(LOCAL_E, netvar.m_iAmmo + 4);
|
||||
|
||||
INetChannel *ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
|
||||
static bool firstcall = true;
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
if (firstcall)
|
||||
xd[i].update();
|
||||
firstcall = false;
|
||||
if (ch &&
|
||||
xd[i].check(ch->GetLatency(MAX_FLOWS) * 1000.0f + 100.0f) &&
|
||||
brutesoon[i])
|
||||
{
|
||||
if (lasthits == count_hits)
|
||||
{
|
||||
logging::Info("Increased Brutenum of ent %d", i);
|
||||
g_Settings.brute.brutenum[i]++;
|
||||
}
|
||||
brutesoon[i] = false;
|
||||
lasthits = count_hits;
|
||||
}
|
||||
}
|
||||
if (lastweapon)
|
||||
{
|
||||
|
||||
if (ammo < lastammo)
|
||||
{
|
||||
if (hacks::shared::aimbot::target_eid > -1)
|
||||
{
|
||||
if (ch &&
|
||||
xd[hacks::shared::aimbot::target_eid].check(
|
||||
ch->GetLatency(MAX_FLOWS) * 1000.0f + 110.0f))
|
||||
{
|
||||
xd[hacks::shared::aimbot::target_eid].update();
|
||||
brutesoon[hacks::shared::aimbot::target_eid] = true;
|
||||
}
|
||||
}
|
||||
// for (auto i : entstocheck)
|
||||
//{
|
||||
OnShot();
|
||||
@ -131,7 +164,7 @@ class HurtListener : public IGameEventListener
|
||||
public:
|
||||
virtual void FireGameEvent(KeyValues *event)
|
||||
{
|
||||
if (strcmp("player_hurt", event->GetName()) ||
|
||||
if (strcmp("player_hurt", event->GetName()) &&
|
||||
strcmp("player_death", event->GetName()))
|
||||
return;
|
||||
if (g_IEngine->GetPlayerForUserID(event->GetInt("attacker")) ==
|
||||
@ -142,7 +175,7 @@ public:
|
||||
LOCAL_W->m_iClassID() == CL_CLASS(CTFSniperRifleDecap)))
|
||||
OnHit(strcmp("player_death", event->GetName())
|
||||
? event->GetBool("crit")
|
||||
: false);
|
||||
: true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -103,6 +103,7 @@ VMTHook client{};
|
||||
VMTHook engine{};
|
||||
VMTHook ctfpartyclient;
|
||||
VMTHook netchannel{};
|
||||
VMTHook firebullets{};
|
||||
VMTHook clientdll{};
|
||||
VMTHook matsurface{};
|
||||
VMTHook studiorender{};
|
||||
|
@ -10,6 +10,7 @@ target_sources(cathook PRIVATE
|
||||
"${CMAKE_CURRENT_LIST_DIR}/nographics.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/others.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/Paint.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/PreDataUpdate.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/SendNetMsg.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/Shutdown.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/FireEvent.cpp"
|
||||
|
@ -93,9 +93,14 @@ const char *cmds[7] = { "use", "voicecommand", "spec_next", "spec_prev",
|
||||
"spec_player", "invprev", "invnext" };
|
||||
namespace hooked_methods
|
||||
{
|
||||
|
||||
DEFINE_HOOKED_METHOD(CreateMove, bool, void *this_, float input_sample_time,
|
||||
CUserCmd *cmd)
|
||||
{
|
||||
#define TICK_INTERVAL (g_GlobalVars->interval_per_tick)
|
||||
#define TIME_TO_TICKS(dt) ((int) (0.5f + (float) (dt) / TICK_INTERVAL))
|
||||
#define TICKS_TO_TIME(t) (TICK_INTERVAL * (t))
|
||||
#define ROUND_TO_TICKS(t) (TICK_INTERVAL * TIME_TO_TICKS(t))
|
||||
uintptr_t **fp;
|
||||
__asm__("mov %%ebp, %0" : "=r"(fp));
|
||||
bSendPackets = reinterpret_cast<bool *>(**fp - 8);
|
||||
@ -163,6 +168,78 @@ DEFINE_HOOKED_METHOD(CreateMove, bool, void *this_, float input_sample_time,
|
||||
curtime_old = g_GlobalVars->curtime;
|
||||
|
||||
hacks::tf2::global::runcfg();
|
||||
static IClientEntity *enti;
|
||||
if (resolver && cathook && CE_GOOD(LOCAL_E))
|
||||
{
|
||||
for (int i = 0; i < g_IEngine->GetMaxClients(); i++)
|
||||
{
|
||||
if (i == g_IEngine->GetLocalPlayer())
|
||||
continue;
|
||||
enti = g_IEntityList->GetClientEntity(i);
|
||||
if (enti && !enti->IsDormant() &&
|
||||
!NET_BYTE(enti, netvar.iLifeState))
|
||||
{
|
||||
float quotat = 0;
|
||||
float quotaf = 0;
|
||||
if (!g_Settings.brute.choke[i].empty())
|
||||
for (auto it : g_Settings.brute.choke[i])
|
||||
{
|
||||
if (it)
|
||||
quotat++;
|
||||
else
|
||||
quotaf++;
|
||||
}
|
||||
float quota = quotat / quotaf;
|
||||
Vector &angles = NET_VECTOR(enti, netvar.m_angEyeAngles);
|
||||
static bool brutepitch = false;
|
||||
if (g_Settings.brute.brutenum[i] > 5)
|
||||
{
|
||||
g_Settings.brute.brutenum[i] = 0;
|
||||
brutepitch = !brutepitch;
|
||||
}
|
||||
angles.y = fmod(angles.y + 180.0f, 360.0f);
|
||||
if (angles.y < 0)
|
||||
angles.y += 360.0f;
|
||||
angles.y -= 180.0f;
|
||||
if (quota < 0.8f)
|
||||
switch (g_Settings.brute.brutenum[i])
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
angles.y += 180.0f;
|
||||
break;
|
||||
case 2:
|
||||
angles.y -= 90.0f;
|
||||
break;
|
||||
case 3:
|
||||
angles.y += 90.0f;
|
||||
break;
|
||||
case 4:
|
||||
angles.y -= 180.0f;
|
||||
break;
|
||||
case 5:
|
||||
angles.y = 0.0f;
|
||||
break;
|
||||
}
|
||||
if (brutepitch || quota < 0.8f)
|
||||
switch (g_Settings.brute.brutenum[i] % 4)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
angles.x = -89.0f;
|
||||
break;
|
||||
case 2:
|
||||
angles.x = 89.0f;
|
||||
break;
|
||||
case 3:
|
||||
angles.x = 0.0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nolerp)
|
||||
{
|
||||
// g_pUserCmd->tick_count += 1;
|
||||
@ -512,7 +589,29 @@ DEFINE_HOOKED_METHOD(CreateMove, bool, void *this_, float input_sample_time,
|
||||
(cmd->buttons & IN_ATTACK ||
|
||||
!(hacks::shared::antiaim::enabled &&
|
||||
float(hacks::shared::antiaim::yaw_mode) >= 9 && !*bSendPackets)))
|
||||
g_Settings.last_angles = cmd->viewangles;
|
||||
g_Settings.brute.last_angles[LOCAL_E->m_IDX] = cmd->viewangles;
|
||||
for (int i = 0; i < g_IEngine->GetMaxClients(); i++)
|
||||
{
|
||||
|
||||
CachedEntity *ent = ENTITY(i);
|
||||
if (CE_GOOD(LOCAL_E))
|
||||
if (ent == LOCAL_E)
|
||||
continue;
|
||||
if (CE_BAD(ent) || !ent->m_bAlivePlayer())
|
||||
continue;
|
||||
INetChannel *ch = (INetChannel *) g_IEngine->GetNetChannelInfo();
|
||||
if (NET_FLOAT(RAW_ENT(ent), netvar.m_flSimulationTime) <= 1.5f)
|
||||
continue;
|
||||
float latency = ch->GetAvgLatency(MAX_FLOWS);
|
||||
g_Settings.brute.choke[i].push_back(
|
||||
NET_FLOAT(RAW_ENT(ent), netvar.m_flSimulationTime) ==
|
||||
g_Settings.brute.lastsimtime);
|
||||
g_Settings.brute.last_angles[ent->m_IDX] =
|
||||
NET_VECTOR(RAW_ENT(ent), netvar.m_angEyeAngles);
|
||||
if (!g_Settings.brute.choke[i].empty() &&
|
||||
g_Settings.brute.choke[i].size() > 20)
|
||||
g_Settings.brute.choke[i].pop_front();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
int nextdata = 0;
|
||||
|
@ -107,6 +107,8 @@ DEFINE_HOOKED_METHOD(LevelInit, void, void *this_, const char *name)
|
||||
logging::Info("Loaded Skybox: %s", succ ? "true" : "false");
|
||||
ConVar *holiday = g_ICvar->FindVar("tf_forced_holiday");
|
||||
|
||||
for (int i = 0; i < 32; i++)
|
||||
g_Settings.brute.brutenum[i] = 0;
|
||||
if (halloween_mode)
|
||||
holiday->SetValue(2);
|
||||
else if (holiday->m_nValue == 2)
|
||||
|
20
src/hooks/PreDataUpdate.cpp
Normal file
20
src/hooks/PreDataUpdate.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* PreDataUpdate.cpp
|
||||
*
|
||||
* Created on: Jul 27, 2018
|
||||
* Author: bencat07
|
||||
*/
|
||||
#include "HookedMethods.hpp"
|
||||
#include "SeedPrediction.hpp"
|
||||
|
||||
namespace hooked_methods
|
||||
{
|
||||
DEFINE_HOOKED_METHOD(PreDataUpdate, void, void *_this, int ok)
|
||||
{
|
||||
hacks::tf2::seedprediction::handleFireBullets((C_TEFireBullets *)_this);
|
||||
original::PreDataUpdate(_this, ok);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -11,9 +11,6 @@ namespace hooked_methods
|
||||
DEFINE_HOOKED_METHOD(SendDatagram, int, INetChannel *ch, bf_write *buf)
|
||||
{
|
||||
#if not LAGBOT_MODE
|
||||
if (!hacks::shared::backtrack::enable ||
|
||||
(float) hacks::shared::backtrack::latency <= 200.0f)
|
||||
return original::SendDatagram(ch, buf);
|
||||
int in = ch->m_nInSequenceNr;
|
||||
auto state = ch->m_nInReliableState;
|
||||
|
||||
|
@ -13,7 +13,8 @@
|
||||
static CatVar nightmode(CV_FLOAT, "nightmode", "0", "Enable nightmode", "");
|
||||
namespace hooked_methods
|
||||
{
|
||||
|
||||
#include "reclasses.hpp"
|
||||
#include "C_TEFireBullets.hpp"
|
||||
DEFINE_HOOKED_METHOD(FrameStageNotify, void, void *this_,
|
||||
ClientFrameStage_t stage)
|
||||
{
|
||||
@ -61,7 +62,6 @@ DEFINE_HOOKED_METHOD(FrameStageNotify, void, void *this_,
|
||||
}
|
||||
OldNightmode = nightmode;
|
||||
}
|
||||
static IClientEntity *ent;
|
||||
|
||||
PROF_SECTION(FrameStageNotify_TOTAL);
|
||||
|
||||
@ -71,29 +71,6 @@ DEFINE_HOOKED_METHOD(FrameStageNotify, void, void *this_,
|
||||
PROF_SECTION(FSN_skinchanger);
|
||||
hacks::tf2::skinchanger::FrameStageNotify(stage);
|
||||
}
|
||||
if (resolver && cathook && !g_Settings.bInvalid &&
|
||||
stage == FRAME_NET_UPDATE_POSTDATAUPDATE_START)
|
||||
{
|
||||
PROF_SECTION(FSN_resolver);
|
||||
for (int i = 1; i < 32 && i < HIGHEST_ENTITY; i++)
|
||||
{
|
||||
if (i == g_IEngine->GetLocalPlayer())
|
||||
continue;
|
||||
ent = g_IEntityList->GetClientEntity(i);
|
||||
if (ent && !ent->IsDormant() && !NET_BYTE(ent, netvar.iLifeState))
|
||||
{
|
||||
Vector &angles = NET_VECTOR(ent, netvar.m_angEyeAngles);
|
||||
if (angles.x >= 90)
|
||||
angles.x = -89;
|
||||
if (angles.x <= -90)
|
||||
angles.x = 89;
|
||||
angles.y = fmod(angles.y + 180.0f, 360.0f);
|
||||
if (angles.y < 0)
|
||||
angles.y += 360.0f;
|
||||
angles.y -= 180.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cathook && stage == FRAME_RENDER_START)
|
||||
{
|
||||
INetChannel *ch;
|
||||
@ -110,6 +87,13 @@ DEFINE_HOOKED_METHOD(FrameStageNotify, void, void *this_,
|
||||
ipc::UpdateServerAddress();
|
||||
#endif
|
||||
}
|
||||
C_TEFireBullets *fire = C_TEFireBullets::GTEFireBullets();
|
||||
if (fire && !hooks::IsHooked((void *)fire))
|
||||
{
|
||||
hooks::firebullets.Set(fire);
|
||||
hooks::firebullets.HookMethod(HOOK_ARGS(PreDataUpdate));
|
||||
hooks::firebullets.Apply();
|
||||
}
|
||||
}
|
||||
if (cathook && !g_Settings.bInvalid && stage == FRAME_RENDER_START)
|
||||
{
|
||||
@ -128,9 +112,9 @@ DEFINE_HOOKED_METHOD(FrameStageNotify, void, void *this_,
|
||||
if (CE_GOOD(g_pLocalPlayer->entity))
|
||||
{
|
||||
CE_FLOAT(g_pLocalPlayer->entity, netvar.deadflag + 4) =
|
||||
g_Settings.last_angles.x;
|
||||
g_Settings.brute.last_angles[LOCAL_E->m_IDX].x;
|
||||
CE_FLOAT(g_pLocalPlayer->entity, netvar.deadflag + 8) =
|
||||
g_Settings.last_angles.y;
|
||||
g_Settings.brute.last_angles[LOCAL_E->m_IDX].y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,4 +4,5 @@ target_sources(cathook PRIVATE
|
||||
"${CMAKE_CURRENT_LIST_DIR}/CTFPartyClient.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/CTFParty.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/CTFGCClientSystem.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/C_TEFireBullets.cpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/ITFGroupMatchCriteria.cpp")
|
@ -78,6 +78,16 @@ char re::CTFPartyClient::RequestQueueForMatch(int type)
|
||||
|
||||
return RequestQueueForMatch_fn(this, type);
|
||||
}
|
||||
bool re::CTFPartyClient::BInQueueForMatchGroup(int type)
|
||||
{
|
||||
typedef bool (*BInQueueForMatchGroup_t)(re::CTFPartyClient *, int);
|
||||
static uintptr_t addr = gSignatures.GetClientSignature(
|
||||
"55 89 E5 56 53 8B 5D ? 8B 75 ? 89 D8 E8 ? ? ? ? 84 C0 74 ? 8B 4E");
|
||||
static BInQueueForMatchGroup_t BInQueueForMatchGroup_fn =
|
||||
BInQueueForMatchGroup_t(addr);
|
||||
|
||||
return BInQueueForMatchGroup_fn(this, type);
|
||||
}
|
||||
char re::CTFPartyClient::RequestLeaveForMatch(int type)
|
||||
{
|
||||
typedef char (*RequestLeaveForMatch_t)(re::CTFPartyClient *, int);
|
||||
|
34
src/reclasses/C_TEFireBullets.cpp
Normal file
34
src/reclasses/C_TEFireBullets.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* C_TEFireBullets.cpp
|
||||
*
|
||||
* Created on: Jul 27, 2018
|
||||
* Author: bencat07
|
||||
*/
|
||||
#include "reclasses.hpp"
|
||||
#pragma once
|
||||
|
||||
C_TEFireBullets *C_TEFireBullets::GTEFireBullets()
|
||||
{
|
||||
typedef C_TEFireBullets *(*GTEFireBullets_t)();
|
||||
static uintptr_t addr1 = gSignatures.GetClientSignature("55 B8 ? ? ? ? 89 E5 5D C3 8D B6 00 00 00 00 55 89 E5 56 53 83 EC ? C7 45");
|
||||
GTEFireBullets_t GTEFireBullets_fn = GTEFireBullets_t(addr1);
|
||||
|
||||
return GTEFireBullets_fn();
|
||||
}
|
||||
int C_TEFireBullets::m_iSeed()
|
||||
{
|
||||
return int(unsigned(this) + 0x34);
|
||||
}
|
||||
int C_TEFireBullets::m_iWeaponID()
|
||||
{
|
||||
return int(unsigned(this) + 0x2c);
|
||||
}
|
||||
int C_TEFireBullets::m_iPlayer()
|
||||
{
|
||||
return int(unsigned(this) + 0x10);
|
||||
}
|
||||
float C_TEFireBullets::m_flSpread()
|
||||
{
|
||||
return float(unsigned(this) + 0x38);
|
||||
}
|
||||
|
12
src/tfmm.cpp
12
src/tfmm.cpp
@ -13,12 +13,6 @@ CatCommand cmd_queue_start("mm_queue_casual", "Start casual queue",
|
||||
|
||||
CatCommand cmd_abandon("mm_abandon", "Abandon match",
|
||||
[]() { tfmm::abandon(); });
|
||||
static CatEnum queue_mode({ "MvmPractice", "MvmMannup", "LadderMatch6v6",
|
||||
"LadderMatch9v9", "LadderMatch12v12",
|
||||
"CasualMatch6v6", "CasualMatch9v9",
|
||||
"CasualMatch12v12", "CompetitiveEventMatch12v12" });
|
||||
static CatVar queue(queue_mode, "autoqueue_mode", "7",
|
||||
"Autoqueue for this mode", "");
|
||||
|
||||
CatCommand get_state("mm_state", "Get party state", []() {
|
||||
re::CTFParty *party = re::CTFParty::GetParty();
|
||||
@ -32,7 +26,11 @@ CatCommand get_state("mm_state", "Get party state", []() {
|
||||
|
||||
namespace tfmm
|
||||
{
|
||||
|
||||
CatEnum queue_mode({ "MvmPractice", "MvmMannup", "LadderMatch6v6",
|
||||
"LadderMatch9v9", "LadderMatch12v12", "CasualMatch6v6",
|
||||
"CasualMatch9v9", "CasualMatch12v12",
|
||||
"CompetitiveEventMatch12v12" });
|
||||
CatVar queue(queue_mode, "autoqueue_mode", "7", "Autoqueue for this mode", "");
|
||||
void queue_start()
|
||||
{
|
||||
re::CTFPartyClient *client = re::CTFPartyClient::GTFPartyClient();
|
||||
|
Reference in New Issue
Block a user