Fixed "meta unload" segfault.

Added fake responces to server queries.
Added dynamic link ents. (except android).
This commit is contained in:
Dmitry 2019-07-31 14:05:36 +03:00 committed by jeefo
commit 535f298621
16 changed files with 763 additions and 246 deletions

View file

@ -205,7 +205,7 @@ public:
if (index + count > m_capacity) { if (index + count > m_capacity) {
return false; return false;
} }
for (size_t i = m_length; i < m_length + count; i++) { for (size_t i = index; i < index + count; i++) {
alloc.destruct (&m_data[i]); alloc.destruct (&m_data[i]);
} }
m_length -= count; m_length -= count;

View file

@ -27,6 +27,7 @@
#include <crlib/cr-random.h> #include <crlib/cr-random.h>
#include <crlib/cr-ulz.h> #include <crlib/cr-ulz.h>
#include <crlib/cr-color.h> #include <crlib/cr-color.h>
#include <crlib/cr-hook.h>
CR_NAMESPACE_BEGIN CR_NAMESPACE_BEGIN

132
include/crlib/cr-hook.h Normal file
View file

@ -0,0 +1,132 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <crlib/cr-basic.h>
#if !defined (CR_WINDOWS)
# include <sys/mman.h>
#endif
CR_NAMESPACE_BEGIN
class SimpleHook : DenyCopying {
private:
enum : uint32 {
CodeLength = 12
};
private:
bool m_patched;
uint32 m_pageSize;
uint32 m_origFunc;
uint32 m_hookFunc;
uint8 m_origBytes[CodeLength];
uint8 m_hookBytes[CodeLength];
private:
void setPageSize () {
#if defined (CR_WINDOWS)
SYSTEM_INFO sysinfo;
GetSystemInfo (&sysinfo);
m_pageSize = sysinfo.dwPageSize;
#else
m_pageSize = sysconf (_SC_PAGESIZE);
#endif
}
inline void *align (void *address) {
return reinterpret_cast <void *> ((reinterpret_cast <long> (address) & ~(m_pageSize - 1)));
}
bool unprotect () {
auto orig = reinterpret_cast <void *> (m_origFunc);
#if defined (CR_WINDOWS)
DWORD oldProt;
FlushInstructionCache (GetCurrentProcess (), orig, m_pageSize);
return VirtualProtect (orig, m_pageSize, PAGE_EXECUTE_READWRITE, &oldProt);
#else
auto aligned = align (orig);
return !mprotect (aligned, m_pageSize, PROT_READ | PROT_WRITE | PROT_EXEC);
#endif
}
public:
SimpleHook () : m_patched (false), m_origFunc (0), m_hookFunc (0), m_pageSize (0) {
setPageSize ();
}
~SimpleHook () {
disable ();
}
public:
bool patch (void *address, void *replacement) {
uint8 *ptr = reinterpret_cast <uint8 *> (address);
while (*reinterpret_cast <uint16 *> (ptr) == 0x25ff) {
ptr = **reinterpret_cast <uint8 * **> ((ptr + 2));
}
m_origFunc = reinterpret_cast <uint32> (ptr);
if (!m_origFunc) {
return false;
}
m_hookFunc = reinterpret_cast <uint32> (replacement);
m_hookBytes[0] = 0x68;
m_hookBytes[5] = 0xc3;
*reinterpret_cast <uint32 *> (&m_hookBytes[1]) = m_hookFunc;
if (unprotect ()) {
memcpy (m_origBytes, reinterpret_cast <void *> (m_origFunc), CodeLength);
return enable ();
}
return false;
}
bool enable () {
if (m_patched) {
return false;
}
m_patched = true;
if (unprotect ()) {
memcpy (reinterpret_cast <void *> (m_origFunc), m_hookBytes, CodeLength);
return true;
}
return false;
}
bool disable () {
if (!m_patched) {
return false;
}
m_patched = false;
if (unprotect ()) {
memcpy (reinterpret_cast <void *> (m_origFunc), m_origBytes, CodeLength);
return true;
}
return false;
}
bool enabled () const {
return m_patched;
}
};
CR_NAMESPACE_END

View file

@ -20,14 +20,13 @@
# include <netinet/in.h> # include <netinet/in.h>
# include <sys/socket.h> # include <sys/socket.h>
# include <sys/types.h> # include <sys/types.h>
# include <sys/uio.h>
# include <arpa/inet.h> # include <arpa/inet.h>
# include <unistd.h> # include <unistd.h>
# include <errno.h> # include <errno.h>
# include <netdb.h> # include <netdb.h>
# include <fcntl.h> # include <fcntl.h>
#elif defined (CR_WINDOWS) #elif defined (CR_WINDOWS)
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <winsock2.h> # include <winsock2.h>
#endif #endif
@ -138,6 +137,30 @@ public:
template <typename U> int32 recv (U *buffer, int32 length) { template <typename U> int32 recv (U *buffer, int32 length) {
return ::recv (m_socket, reinterpret_cast <char *> (buffer), length, 0); return ::recv (m_socket, reinterpret_cast <char *> (buffer), length, 0);
} }
public:
static int32 CR_STDCALL sendto (int socket, const void *message, size_t length, int flags, const struct sockaddr *dest, int32 destLength) {
#if defined (CR_WINDOWS)
WSABUF buffer = { length, const_cast <char *> (reinterpret_cast <const char *> (message)) };
DWORD sendLength = 0;
if (WSASendTo (socket, &buffer, 1, &sendLength, flags, dest, destLength, NULL, NULL) == SOCKET_ERROR) {
errno = WSAGetLastError ();
return -1;
}
return static_cast <int32> (sendLength);
#else
iovec iov = { const_cast <void *> (message), length };
msghdr msg = { 0, };
msg.msg_name = reinterpret_cast <void *> (const_cast <struct sockaddr *> (dest));
msg.msg_namelen = destLength;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
return sendmsg (socket, &msg, flags);
#endif
}
}; };
namespace detail { namespace detail {

View file

@ -169,5 +169,4 @@ public:
} }
}; };
CR_NAMESPACE_END CR_NAMESPACE_END

View file

@ -46,8 +46,8 @@ public:
} }
public: public:
inline bool load (const String &file) noexcept { bool load (const String &file) noexcept {
#ifdef CR_WINDOWS #if defined (CR_WINDOWS)
m_handle = LoadLibraryA (file.chars ()); m_handle = LoadLibraryA (file.chars ());
#else #else
m_handle = dlopen (file.chars (), RTLD_NOW); m_handle = dlopen (file.chars (), RTLD_NOW);
@ -55,15 +55,39 @@ public:
return m_handle != nullptr; return m_handle != nullptr;
} }
bool locate (void *address) {
#if defined (CR_WINDOWS)
MEMORY_BASIC_INFORMATION mbi;
if (!VirtualQuery (address, &mbi, sizeof (mbi))) {
return false;
}
if (mbi.State != MEM_COMMIT) {
return false;
}
m_handle = reinterpret_cast <void *> (mbi.AllocationBase);
#else
Dl_info dli;
memset (&dli, 0, sizeof (dli));
if (dladdr (address, &dli)) {
return load (dli.dli_fname);
}
#endif
return m_handle != nullptr;
}
void unload () noexcept { void unload () noexcept {
if (!*this) { if (!*this) {
return; return;
} }
#ifdef CR_WINDOWS #if defined (CR_WINDOWS)
FreeLibrary (static_cast <HMODULE> (m_handle)); FreeLibrary (static_cast <HMODULE> (m_handle));
#else #else
dlclose (m_handle); dlclose (m_handle);
#endif #endif
m_handle = nullptr;
} }
template <typename R> R resolve (const char *function) const { template <typename R> R resolve (const char *function) const {
@ -72,7 +96,7 @@ public:
} }
return reinterpret_cast <R> ( return reinterpret_cast <R> (
#ifdef CR_WINDOWS #if defined (CR_WINDOWS)
GetProcAddress (static_cast <HMODULE> (m_handle), function) GetProcAddress (static_cast <HMODULE> (m_handle), function)
#else #else
dlsym (m_handle, function) dlsym (m_handle, function)
@ -80,6 +104,10 @@ public:
); );
} }
void *handle () const {
return m_handle;
}
public: public:
explicit operator bool () const { explicit operator bool () const {
return m_handle != nullptr; return m_handle != nullptr;

View file

@ -34,11 +34,13 @@ CR_NAMESPACE_BEGIN
# define CR_CXX_CLANG # define CR_CXX_CLANG
#endif #endif
// configure export macros // configure macroses
#if defined(CR_WINDOWS) #if defined(CR_WINDOWS)
# define CR_EXPORT extern "C" __declspec (dllexport) # define CR_EXPORT extern "C" __declspec (dllexport)
# define CR_STDCALL __stdcall
#elif defined(CR_LINUX) || defined(CR_OSX) #elif defined(CR_LINUX) || defined(CR_OSX)
# define CR_EXPORT extern "C" __attribute__((visibility("default"))) # define CR_EXPORT extern "C" __attribute__((visibility("default")))
# define CR_STDCALL
#else #else
# error "Can't configure export macros. Compiler unrecognized." # error "Can't configure export macros. Compiler unrecognized."
#endif #endif
@ -56,11 +58,14 @@ CR_NAMESPACE_BEGIN
CR_NAMESPACE_END CR_NAMESPACE_END
#if defined(CR_WINDOWS) #if defined(CR_WINDOWS)
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <direct.h> # include <direct.h>
#else #else
# include <unistd.h> # include <unistd.h>
# include <strings.h> # include <strings.h>
# include <sys/stat.h> # include <sys/stat.h>
# include <sys/time.h>
#endif #endif
#include <assert.h> #include <assert.h>
@ -154,6 +159,25 @@ struct Platform : public Singleton <Platform> {
#endif #endif
} }
float seconds () {
#if defined(CR_WINDOWS)
LARGE_INTEGER count, freq;
count.QuadPart = 0;
freq.QuadPart = 0;
QueryPerformanceFrequency (&freq);
QueryPerformanceCounter (&count);
return static_cast <float> (count.QuadPart) / static_cast <float> (freq.QuadPart);
#else
timeval tv;
gettimeofday (&tv, NULL);
return static_cast <float> (tv.tv_sec) + (static_cast <float> (tv.tv_usec)) / 1000000.0;
#endif
}
void abort (const char *msg = "OUT OF MEMORY!") noexcept { void abort (const char *msg = "OUT OF MEMORY!") noexcept {
fprintf (stderr, "%s\n", msg); fprintf (stderr, "%s\n", msg);

View file

@ -165,7 +165,7 @@ public:
void precache (); void precache ();
// initialize levels // initialize levels
void levelInitialize (edict_t *ents, int max); void levelInitialize (edict_t *entities, int max);
// display world line // display world line
void drawLine (edict_t *ent, const Vector &start, const Vector &end, int width, int noise, const Color &color, int brightness, int speed, int life, DrawLine type = DrawLine::Simple); void drawLine (edict_t *ent, const Vector &start, const Vector &end, int width, int noise, const Color &color, int brightness, int speed, int life, DrawLine type = DrawLine::Simple);
@ -304,8 +304,8 @@ public:
int getTeam (edict_t *ent); int getTeam (edict_t *ent);
// adds translation pair from config // adds translation pair from config
void addTranslation (const String &original, const String &translated) { void addTranslation (const String &m_origBytes, const String &translated) {
m_language.push (original, translated); m_language.push (m_origBytes, translated);
} }
// clear the translation table // clear the translation table
@ -573,3 +573,131 @@ public:
m_doAnimation = enable; m_doAnimation = enable;
} }
}; };
// simple handler for parsing and rewriting queries (fake queries)
class QueryBuffer {
SmallArray <uint8> m_buffer;
size_t m_cursor;
public:
QueryBuffer (const uint8 *msg, size_t length, size_t shift) : m_cursor (0) {
m_buffer.insert (0, msg, length);
m_cursor += shift;
}
public:
template <typename T> T read () {
T result;
auto size = sizeof (T);
if (m_cursor + size > m_buffer.length ()) {
return 0;
}
memcpy (&result, m_buffer.data () + m_cursor, size);
m_cursor += size;
return result;
}
// must be called right after read
template <typename T> void write (T value) {
auto size = sizeof (value);
memcpy (m_buffer.data () + m_cursor - size, &value, size);
}
template <typename T> void skip () {
auto size = sizeof (T);
if (m_cursor + size > m_buffer.length ()) {
return;
}
m_cursor += size;
}
void skipString () {
if (m_buffer.length () < m_cursor) {
return;
}
for (; m_cursor < m_buffer.length () && m_buffer[m_cursor] != '\0'; ++m_cursor) { }
++m_cursor;
}
void shiftToEnd () {
m_cursor = m_buffer.length ();
}
public:
Twin <const uint8 *, size_t> data () {
return { m_buffer.data (), m_buffer.length () };
}
};
// for android
#if defined (CR_ANDROID)
extern "C" void player (entvars_t *pev);
#endif
class DynamicEntityLink : public Singleton <DynamicEntityLink> {
private:
#if defined (CR_WINDOWS)
# define CastType HMODULE
# define LookupSymbol GetProcAddress
#else
# define CastType void *
# define LookupSymbol dlsym
#endif
private:
using Handle = void *;
using Name = const char *;
private:
template <typename K> struct FunctionHash {
uint32 operator () (Name key) const {
char *str = const_cast <char *> (key);
uint32 hash = 0;
while (*str++) {
hash = ((hash << 5) + hash) + *str;
}
return hash;
}
};
private:
SharedLibrary m_self;
SimpleHook m_dlsym;
Dictionary <Name, Handle, FunctionHash <Name>> m_exports;
public:
DynamicEntityLink () = default;
~DynamicEntityLink () {
m_dlsym.disable ();
}
public:
Handle search (Handle module, Name function);
public:
void initialize () {
if (plat.isAndroid) {
return;
}
m_dlsym.patch (reinterpret_cast <void *> (&LookupSymbol), reinterpret_cast <void *> (&DynamicEntityLink::replacement));
m_self.locate (&engfuncs);
}
EntityFunction getPlayerFunction () {
#if defined (CR_ANDROID)
return player;
#else
return reinterpret_cast <EntityFunction> (search (Game::get ().lib ().handle (), "player"));
#endif
}
public:
static Handle CR_STDCALL replacement (Handle module, Name function) {
return DynamicEntityLink::get ().search (module, function);
}
};

View file

@ -8,7 +8,7 @@
// //
#pragma once #pragma once
#define _CRTDBG_MAP_ALLOC
#include <engine/extdll.h> #include <engine/extdll.h>
#include <engine/meta_api.h> #include <engine/meta_api.h>
@ -846,6 +846,8 @@ private:
float m_oldCombatDesire; // holds old desire for filtering float m_oldCombatDesire; // holds old desire for filtering
float m_avoidTime; // time to avoid players around float m_avoidTime; // time to avoid players around
float m_itemCheckTime; // time next search for items needs to be done float m_itemCheckTime; // time next search for items needs to be done
float m_joinServerTime; // time when bot joined the game
float m_playServerTime; // time bot spent in the game
bool m_moveToGoal; // bot currently moving to goal?? bool m_moveToGoal; // bot currently moving to goal??
bool m_isStuck; // bot is stuck bool m_isStuck; // bot is stuck
@ -1322,6 +1324,7 @@ public:
int getHumansCount (bool ignoreSpectators = false); int getHumansCount (bool ignoreSpectators = false);
int getAliveHumansCount (); int getAliveHumansCount ();
int getBotCount (); int getBotCount ();
float getConnectionTime (int botId);
void setBombPlanted (bool isPlanted); void setBombPlanted (bool isPlanted);
void slowFrame (); void slowFrame ();
@ -1894,6 +1897,8 @@ private:
SmallArray <Client> m_clients; SmallArray <Client> m_clients;
SmallArray <Twin <String, String>> m_tags; SmallArray <Twin <String, String>> m_tags;
SimpleHook m_sendToHook;
public: public:
BotUtils (); BotUtils ();
~BotUtils () = default; ~BotUtils () = default;
@ -1968,6 +1973,9 @@ public:
// send modified pings to all the clients // send modified pings to all the clients
void sendPings (edict_t *to); void sendPings (edict_t *to);
// installs the sendto function intreception
void installSendTo ();
public: public:
// re-show welcome after changelevel ? // re-show welcome after changelevel ?
@ -1990,6 +1998,16 @@ public:
return m_clients[index]; return m_clients[index];
} }
// disables send hook
bool disableSendTo () {
return m_sendToHook.disable ();
}
// enables send hook
bool enableSendTo () {
return m_sendToHook.enable ();
}
// checks if string is not empty // checks if string is not empty
bool isEmptyStr (const char *input) const { bool isEmptyStr (const char *input) const {
if (input == nullptr) { if (input == nullptr) {
@ -1997,6 +2015,9 @@ public:
} }
return *input == '\0'; return *input == '\0';
} }
public:
static int32 CR_STDCALL sendTo (int socket, const void *message, size_t length, int flags, const struct sockaddr *dest, int destLength);
}; };
// bot command manager // bot command manager
@ -2185,6 +2206,7 @@ static auto &util = BotUtils::get ();
static auto &ctrl = BotControl::get (); static auto &ctrl = BotControl::get ();
static auto &game = Game::get (); static auto &game = Game::get ();
static auto &illum = LightMeasure::get (); static auto &illum = LightMeasure::get ();
static auto &ents = DynamicEntityLink::get ();
// very global convars // very global convars
extern ConVar yb_jasonmode; extern ConVar yb_jasonmode;

View file

@ -19,6 +19,7 @@
<ClInclude Include="..\include\crlib\cr-complete.h" /> <ClInclude Include="..\include\crlib\cr-complete.h" />
<ClInclude Include="..\include\crlib\cr-dict.h" /> <ClInclude Include="..\include\crlib\cr-dict.h" />
<ClInclude Include="..\include\crlib\cr-files.h" /> <ClInclude Include="..\include\crlib\cr-files.h" />
<ClInclude Include="..\include\crlib\cr-hook.h" />
<ClInclude Include="..\include\crlib\cr-http.h" /> <ClInclude Include="..\include\crlib\cr-http.h" />
<ClInclude Include="..\include\crlib\cr-lambda.h" /> <ClInclude Include="..\include\crlib\cr-lambda.h" />
<ClInclude Include="..\include\crlib\cr-library.h" /> <ClInclude Include="..\include\crlib\cr-library.h" />
@ -44,6 +45,7 @@
<ClInclude Include="..\include\resource.h" /> <ClInclude Include="..\include\resource.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\source\android.cpp" />
<ClCompile Include="..\source\basecode.cpp" /> <ClCompile Include="..\source\basecode.cpp" />
<ClCompile Include="..\source\combat.cpp" /> <ClCompile Include="..\source\combat.cpp" />
<ClCompile Include="..\source\engine.cpp" /> <ClCompile Include="..\source\engine.cpp" />

View file

@ -111,6 +111,9 @@
<ClInclude Include="..\include\crlib\cr-color.h"> <ClInclude Include="..\include\crlib\cr-color.h">
<Filter>include\crlib</Filter> <Filter>include\crlib</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\include\crlib\cr-hook.h">
<Filter>include\crlib</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\source\chatlib.cpp"> <ClCompile Include="..\source\chatlib.cpp">
@ -143,6 +146,9 @@
<ClCompile Include="..\source\graph.cpp"> <ClCompile Include="..\source\graph.cpp">
<Filter>source</Filter> <Filter>source</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\source\android.cpp">
<Filter>source</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="yapb.rc"> <ResourceCompile Include="yapb.rc">

236
source/android.cpp Normal file
View file

@ -0,0 +1,236 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#include <yapb.h>
// until hook code will be compatible with ARM, it's here
#if defined (CR_ANDROID)
void android_LinkEntity (EntityFunction &addr, const char *name, entvars_t *pev) {
if (!addr) {
addr = game.lib ().resolve <EntityFunction> (name);
}
if (!addr) {
return;
}
addr (pev);
}
#define LINK_ENTITY(entityName) \
CR_EXPORT void entityName (entvars_t *pev) { \
static EntityFunction addr; \
android_LinkEntity (addr, #entityName, pev); \
}
#else
#define LINK_ENTITY(entityName)
#endif
// entities in counter-strike...
LINK_ENTITY (DelayedUse)
LINK_ENTITY (ambient_generic)
LINK_ENTITY (ammo_338magnum)
LINK_ENTITY (ammo_357sig)
LINK_ENTITY (ammo_45acp)
LINK_ENTITY (ammo_50ae)
LINK_ENTITY (ammo_556nato)
LINK_ENTITY (ammo_556natobox)
LINK_ENTITY (ammo_57mm)
LINK_ENTITY (ammo_762nato)
LINK_ENTITY (ammo_9mm)
LINK_ENTITY (ammo_buckshot)
LINK_ENTITY (armoury_entity)
LINK_ENTITY (beam)
LINK_ENTITY (bodyque)
LINK_ENTITY (button_target)
LINK_ENTITY (cycler)
LINK_ENTITY (cycler_prdroid)
LINK_ENTITY (cycler_sprite)
LINK_ENTITY (cycler_weapon)
LINK_ENTITY (cycler_wreckage)
LINK_ENTITY (env_beam)
LINK_ENTITY (env_beverage)
LINK_ENTITY (env_blood)
LINK_ENTITY (env_bombglow)
LINK_ENTITY (env_bubbles)
LINK_ENTITY (env_debris)
LINK_ENTITY (env_explosion)
LINK_ENTITY (env_fade)
LINK_ENTITY (env_funnel)
LINK_ENTITY (env_global)
LINK_ENTITY (env_glow)
LINK_ENTITY (env_laser)
LINK_ENTITY (env_lightning)
LINK_ENTITY (env_message)
LINK_ENTITY (env_rain)
LINK_ENTITY (env_render)
LINK_ENTITY (env_shake)
LINK_ENTITY (env_shooter)
LINK_ENTITY (env_snow)
LINK_ENTITY (env_sound)
LINK_ENTITY (env_spark)
LINK_ENTITY (env_sprite)
LINK_ENTITY (fireanddie)
LINK_ENTITY (func_bomb_target)
LINK_ENTITY (func_breakable)
LINK_ENTITY (func_button)
LINK_ENTITY (func_buyzone)
LINK_ENTITY (func_conveyor)
LINK_ENTITY (func_door)
LINK_ENTITY (func_door_rotating)
LINK_ENTITY (func_escapezone)
LINK_ENTITY (func_friction)
LINK_ENTITY (func_grencatch)
LINK_ENTITY (func_guntarget)
LINK_ENTITY (func_healthcharger)
LINK_ENTITY (func_hostage_rescue)
LINK_ENTITY (func_illusionary)
LINK_ENTITY (func_ladder)
LINK_ENTITY (func_monsterclip)
LINK_ENTITY (func_mortar_field)
LINK_ENTITY (func_pendulum)
LINK_ENTITY (func_plat)
LINK_ENTITY (func_platrot)
LINK_ENTITY (func_pushable)
LINK_ENTITY (func_rain)
LINK_ENTITY (func_recharge)
LINK_ENTITY (func_rot_button)
LINK_ENTITY (func_rotating)
LINK_ENTITY (func_snow)
LINK_ENTITY (func_tank)
LINK_ENTITY (func_tankcontrols)
LINK_ENTITY (func_tanklaser)
LINK_ENTITY (func_tankmortar)
LINK_ENTITY (func_tankrocket)
LINK_ENTITY (func_trackautochange)
LINK_ENTITY (func_trackchange)
LINK_ENTITY (func_tracktrain)
LINK_ENTITY (func_train)
LINK_ENTITY (func_traincontrols)
LINK_ENTITY (func_vehicle)
LINK_ENTITY (func_vehiclecontrols)
LINK_ENTITY (func_vip_safetyzone)
LINK_ENTITY (func_wall)
LINK_ENTITY (func_wall_toggle)
LINK_ENTITY (func_water)
LINK_ENTITY (func_weaponcheck)
LINK_ENTITY (game_counter)
LINK_ENTITY (game_counter_set)
LINK_ENTITY (game_end)
LINK_ENTITY (game_player_equip)
LINK_ENTITY (game_player_hurt)
LINK_ENTITY (game_player_team)
LINK_ENTITY (game_score)
LINK_ENTITY (game_team_master)
LINK_ENTITY (game_team_set)
LINK_ENTITY (game_text)
LINK_ENTITY (game_zone_player)
LINK_ENTITY (gibshooter)
LINK_ENTITY (grenade)
LINK_ENTITY (hostage_entity)
LINK_ENTITY (info_bomb_target)
LINK_ENTITY (info_hostage_rescue)
LINK_ENTITY (info_intermission)
LINK_ENTITY (info_landmark)
LINK_ENTITY (info_map_parameters)
LINK_ENTITY (info_null)
LINK_ENTITY (info_player_deathmatch)
LINK_ENTITY (info_player_start)
LINK_ENTITY (info_target)
LINK_ENTITY (info_teleport_destination)
LINK_ENTITY (info_vip_start)
LINK_ENTITY (infodecal)
LINK_ENTITY (item_airtank)
LINK_ENTITY (item_airbox)
LINK_ENTITY (item_antidote)
LINK_ENTITY (item_assaultsuit)
LINK_ENTITY (item_battery)
LINK_ENTITY (item_healthkit)
LINK_ENTITY (item_kevlar)
LINK_ENTITY (item_longjump)
LINK_ENTITY (item_security)
LINK_ENTITY (item_sodacan)
LINK_ENTITY (item_suit)
LINK_ENTITY (item_thighpack)
LINK_ENTITY (light)
LINK_ENTITY (light_environment)
LINK_ENTITY (light_spot)
LINK_ENTITY (momentary_door)
LINK_ENTITY (momentary_rot_button)
LINK_ENTITY (monster_hevsuit_dead)
LINK_ENTITY (monster_mortar)
LINK_ENTITY (monster_scientist)
LINK_ENTITY (multi_manager)
LINK_ENTITY (multisource)
LINK_ENTITY (path_corner)
LINK_ENTITY (path_track)
LINK_ENTITY (player)
LINK_ENTITY (player_loadsaved)
LINK_ENTITY (player_weaponstrip)
LINK_ENTITY (point_clientcommand)
LINK_ENTITY (point_servercommand)
LINK_ENTITY (soundent)
LINK_ENTITY (spark_shower)
LINK_ENTITY (speaker)
LINK_ENTITY (target_cdaudio)
LINK_ENTITY (test_effect)
LINK_ENTITY (trigger)
LINK_ENTITY (trigger_auto)
LINK_ENTITY (trigger_autosave)
LINK_ENTITY (trigger_camera)
LINK_ENTITY (trigger_cdaudio)
LINK_ENTITY (trigger_changelevel)
LINK_ENTITY (trigger_changetarget)
LINK_ENTITY (trigger_counter)
LINK_ENTITY (trigger_endsection)
LINK_ENTITY (trigger_gravity)
LINK_ENTITY (trigger_hurt)
LINK_ENTITY (trigger_monsterjump)
LINK_ENTITY (trigger_multiple)
LINK_ENTITY (trigger_once)
LINK_ENTITY (trigger_push)
LINK_ENTITY (trigger_random)
LINK_ENTITY (trigger_random_time)
LINK_ENTITY (trigger_random_unique)
LINK_ENTITY (trigger_relay)
LINK_ENTITY (trigger_setorigin)
LINK_ENTITY (trigger_teleport)
LINK_ENTITY (trigger_transition)
LINK_ENTITY (weapon_ak47)
LINK_ENTITY (weapon_aug)
LINK_ENTITY (weapon_awp)
LINK_ENTITY (weapon_c4)
LINK_ENTITY (weapon_deagle)
LINK_ENTITY (weapon_elite)
LINK_ENTITY (weapon_famas)
LINK_ENTITY (weapon_fiveseven)
LINK_ENTITY (weapon_flashbang)
LINK_ENTITY (weapon_g3sg1)
LINK_ENTITY (weapon_galil)
LINK_ENTITY (weapon_glock18)
LINK_ENTITY (weapon_hegrenade)
LINK_ENTITY (weapon_knife)
LINK_ENTITY (weapon_m249)
LINK_ENTITY (weapon_m3)
LINK_ENTITY (weapon_m4a1)
LINK_ENTITY (weapon_mac10)
LINK_ENTITY (weapon_mp5navy)
LINK_ENTITY (weapon_p228)
LINK_ENTITY (weapon_p90)
LINK_ENTITY (weapon_scout)
LINK_ENTITY (weapon_sg550)
LINK_ENTITY (weapon_sg552)
LINK_ENTITY (weapon_shield)
LINK_ENTITY (weapon_shieldgun)
LINK_ENTITY (weapon_smokegrenade)
LINK_ENTITY (weapon_tmp)
LINK_ENTITY (weapon_ump45)
LINK_ENTITY (weapon_usp)
LINK_ENTITY (weapon_xm1014)
LINK_ENTITY (weaponbox)
LINK_ENTITY (world_items)
LINK_ENTITY (worldspawn)

View file

@ -64,7 +64,7 @@ void Game::precache () {
registerCvars (true); registerCvars (true);
} }
void Game::levelInitialize (edict_t *ents, int max) { void Game::levelInitialize (edict_t *entities, int max) {
// this function precaches needed models and initialize class variables // this function precaches needed models and initialize class variables
m_spawnCount[Team::CT] = 0; m_spawnCount[Team::CT] = 0;
@ -72,7 +72,7 @@ void Game::levelInitialize (edict_t *ents, int max) {
// go thru the all entities on map, and do whatever we're want // go thru the all entities on map, and do whatever we're want
for (int i = 0; i < max; ++i) { for (int i = 0; i < max; ++i) {
auto ent = ents + i; auto ent = entities + i;
// only valid entities // only valid entities
if (!ent || ent->free || ent->v.classname == 0) { if (!ent || ent->free || ent->v.classname == 0) {
@ -85,6 +85,9 @@ void Game::levelInitialize (edict_t *ents, int max) {
// initialize some structures // initialize some structures
bots.initRound (); bots.initRound ();
// install the sendto hook to fake queries
util.installSendTo ();
} }
else if (strcmp (classname, "player_weaponstrip") == 0) { else if (strcmp (classname, "player_weaponstrip") == 0) {
if ((is (GameFlags::Legacy)) && (STRING (ent->v.target))[0] == '\0') { if ((is (GameFlags::Legacy)) && (STRING (ent->v.target))[0] == '\0') {
@ -1479,3 +1482,41 @@ float LightMeasure::getLightLevel (const Vector &point) {
float LightMeasure::getSkyColor () { float LightMeasure::getSkyColor () {
return static_cast <float> (Color (sv_skycolor_r.int_ (), sv_skycolor_g.int_ (), sv_skycolor_b.int_ ()).avg ()); return static_cast <float> (Color (sv_skycolor_r.int_ (), sv_skycolor_g.int_ (), sv_skycolor_b.int_ ()).avg ());
} }
DynamicEntityLink::Handle DynamicEntityLink::search (Handle module, Name function) {
const auto lookup = [&] (Handle handle) {
Handle ret = nullptr;
if (m_dlsym.disable ()) {
ret = LookupSymbol (reinterpret_cast <CastType> (handle), function);
m_dlsym.enable ();
}
return ret;
};
static const auto &gamedll = game.lib ();
static const auto &yapb = m_self;
// if requested module is yapb, put in cache the looked up symbol
if (yapb.handle () == module) {
if (m_exports.exists (function)) {
return m_exports[function];
}
auto address = lookup (yapb.handle ());
if (!address) {
auto gameAddress = lookup (gamedll.handle ());
if (gameAddress) {
m_exports[function] = gameAddress;
}
}
else {
m_exports[function] = address;
}
if (m_exports.exists (function)) {
return m_exports[function];
}
}
return lookup (module);
}

View file

@ -961,6 +961,7 @@ CR_EXPORT int Meta_Detach (PLUG_LOADTIME, PL_UNLOAD_REASON) {
// save collected experience on shutdown // save collected experience on shutdown
graph.savePractice (); graph.savePractice ();
util.disableSendTo ();
return TRUE; return TRUE;
} }
@ -980,15 +981,13 @@ CR_EXPORT void Meta_Init () {
# endif # endif
# pragma comment(linker, "/SECTION:.data,RW") # pragma comment(linker, "/SECTION:.data,RW")
# endif # endif
# define DLL_STDCALL __stdcall
# if defined(CR_CXX_MSVC) && !defined(CR_ARCH_X64) # if defined(CR_CXX_MSVC) && !defined(CR_ARCH_X64)
# define DLL_GIVEFNPTRSTODLL extern "C" void DLL_STDCALL # define DLL_GIVEFNPTRSTODLL extern "C" void CR_STDCALL
# elif defined(CR_CXX_CLANG) || defined(CR_ARCH_X64) # elif defined(CR_CXX_CLANG) || defined(CR_ARCH_X64)
# define DLL_GIVEFNPTRSTODLL CR_EXPORT void DLL_STDCALL # define DLL_GIVEFNPTRSTODLL CR_EXPORT void CR_STDCALL
# endif # endif
#elif defined(CR_LINUX) || defined (CR_OSX) || defined (CR_ANDROID) #elif defined(CR_LINUX) || defined (CR_OSX) || defined (CR_ANDROID)
# define DLL_GIVEFNPTRSTODLL CR_EXPORT void # define DLL_GIVEFNPTRSTODLL CR_EXPORT void
# define DLL_STDCALL
#endif #endif
DLL_GIVEFNPTRSTODLL GiveFnptrsToDll (enginefuncs_t *functionTable, globalvars_t *pGlobals) { DLL_GIVEFNPTRSTODLL GiveFnptrsToDll (enginefuncs_t *functionTable, globalvars_t *pGlobals) {
@ -1010,237 +1009,21 @@ DLL_GIVEFNPTRSTODLL GiveFnptrsToDll (enginefuncs_t *functionTable, globalvars_t
if (game.postload ()) { if (game.postload ()) {
return; return;
} }
auto api_GiveFnptrsToDll = game.lib ().resolve <void (DLL_STDCALL *) (enginefuncs_t *, globalvars_t *)> (__FUNCTION__); auto api_GiveFnptrsToDll = game.lib ().resolve <void (CR_STDCALL *) (enginefuncs_t *, globalvars_t *)> (__FUNCTION__);
if (!api_GiveFnptrsToDll) { if (!api_GiveFnptrsToDll) {
logger.fatal ("Could not resolve symbol \"%s\" in the game dll.", __FUNCTION__); logger.fatal ("Could not resolve symbol \"%s\" in the game dll.", __FUNCTION__);
} }
GetEngineFunctions (functionTable, nullptr); GetEngineFunctions (functionTable, nullptr);
// initialize dynamic linkents
ents.initialize ();
// give the engine functions to the other DLL... // give the engine functions to the other DLL...
if (api_GiveFnptrsToDll) { if (api_GiveFnptrsToDll) {
api_GiveFnptrsToDll (functionTable, pGlobals); api_GiveFnptrsToDll (functionTable, pGlobals);
} }
} }
void helper_LinkEntity (EntityFunction &addr, const char *name, entvars_t *pev) { // add linkents for android
if (addr == nullptr) { #include "android.cpp"
addr = game.lib ().resolve <EntityFunction> (name);
}
if (addr == nullptr) {
return;
}
addr (pev);
}
#define LINK_ENTITY(entityName) \
CR_EXPORT void entityName (entvars_t *pev) { \
static EntityFunction addr; \
helper_LinkEntity (addr, #entityName, pev); \
}
// entities in counter-strike...
LINK_ENTITY (DelayedUse)
LINK_ENTITY (ambient_generic)
LINK_ENTITY (ammo_338magnum)
LINK_ENTITY (ammo_357sig)
LINK_ENTITY (ammo_45acp)
LINK_ENTITY (ammo_50ae)
LINK_ENTITY (ammo_556nato)
LINK_ENTITY (ammo_556natobox)
LINK_ENTITY (ammo_57mm)
LINK_ENTITY (ammo_762nato)
LINK_ENTITY (ammo_9mm)
LINK_ENTITY (ammo_buckshot)
LINK_ENTITY (armoury_entity)
LINK_ENTITY (beam)
LINK_ENTITY (bodyque)
LINK_ENTITY (button_target)
LINK_ENTITY (cycler)
LINK_ENTITY (cycler_prdroid)
LINK_ENTITY (cycler_sprite)
LINK_ENTITY (cycler_weapon)
LINK_ENTITY (cycler_wreckage)
LINK_ENTITY (env_beam)
LINK_ENTITY (env_beverage)
LINK_ENTITY (env_blood)
LINK_ENTITY (env_bombglow)
LINK_ENTITY (env_bubbles)
LINK_ENTITY (env_debris)
LINK_ENTITY (env_explosion)
LINK_ENTITY (env_fade)
LINK_ENTITY (env_funnel)
LINK_ENTITY (env_global)
LINK_ENTITY (env_glow)
LINK_ENTITY (env_laser)
LINK_ENTITY (env_lightning)
LINK_ENTITY (env_message)
LINK_ENTITY (env_rain)
LINK_ENTITY (env_render)
LINK_ENTITY (env_shake)
LINK_ENTITY (env_shooter)
LINK_ENTITY (env_snow)
LINK_ENTITY (env_sound)
LINK_ENTITY (env_spark)
LINK_ENTITY (env_sprite)
LINK_ENTITY (fireanddie)
LINK_ENTITY (func_bomb_target)
LINK_ENTITY (func_breakable)
LINK_ENTITY (func_button)
LINK_ENTITY (func_buyzone)
LINK_ENTITY (func_conveyor)
LINK_ENTITY (func_door)
LINK_ENTITY (func_door_rotating)
LINK_ENTITY (func_escapezone)
LINK_ENTITY (func_friction)
LINK_ENTITY (func_grencatch)
LINK_ENTITY (func_guntarget)
LINK_ENTITY (func_healthcharger)
LINK_ENTITY (func_hostage_rescue)
LINK_ENTITY (func_illusionary)
LINK_ENTITY (func_ladder)
LINK_ENTITY (func_monsterclip)
LINK_ENTITY (func_mortar_field)
LINK_ENTITY (func_pendulum)
LINK_ENTITY (func_plat)
LINK_ENTITY (func_platrot)
LINK_ENTITY (func_pushable)
LINK_ENTITY (func_rain)
LINK_ENTITY (func_recharge)
LINK_ENTITY (func_rot_button)
LINK_ENTITY (func_rotating)
LINK_ENTITY (func_snow)
LINK_ENTITY (func_tank)
LINK_ENTITY (func_tankcontrols)
LINK_ENTITY (func_tanklaser)
LINK_ENTITY (func_tankmortar)
LINK_ENTITY (func_tankrocket)
LINK_ENTITY (func_trackautochange)
LINK_ENTITY (func_trackchange)
LINK_ENTITY (func_tracktrain)
LINK_ENTITY (func_train)
LINK_ENTITY (func_traincontrols)
LINK_ENTITY (func_vehicle)
LINK_ENTITY (func_vehiclecontrols)
LINK_ENTITY (func_vip_safetyzone)
LINK_ENTITY (func_wall)
LINK_ENTITY (func_wall_toggle)
LINK_ENTITY (func_water)
LINK_ENTITY (func_weaponcheck)
LINK_ENTITY (game_counter)
LINK_ENTITY (game_counter_set)
LINK_ENTITY (game_end)
LINK_ENTITY (game_player_equip)
LINK_ENTITY (game_player_hurt)
LINK_ENTITY (game_player_team)
LINK_ENTITY (game_score)
LINK_ENTITY (game_team_master)
LINK_ENTITY (game_team_set)
LINK_ENTITY (game_text)
LINK_ENTITY (game_zone_player)
LINK_ENTITY (gibshooter)
LINK_ENTITY (grenade)
LINK_ENTITY (hostage_entity)
LINK_ENTITY (info_bomb_target)
LINK_ENTITY (info_hostage_rescue)
LINK_ENTITY (info_intermission)
LINK_ENTITY (info_landmark)
LINK_ENTITY (info_map_parameters)
LINK_ENTITY (info_null)
LINK_ENTITY (info_player_deathmatch)
LINK_ENTITY (info_player_start)
LINK_ENTITY (info_target)
LINK_ENTITY (info_teleport_destination)
LINK_ENTITY (info_vip_start)
LINK_ENTITY (infodecal)
LINK_ENTITY (item_airtank)
LINK_ENTITY (item_airbox)
LINK_ENTITY (item_antidote)
LINK_ENTITY (item_assaultsuit)
LINK_ENTITY (item_battery)
LINK_ENTITY (item_healthkit)
LINK_ENTITY (item_kevlar)
LINK_ENTITY (item_longjump)
LINK_ENTITY (item_security)
LINK_ENTITY (item_sodacan)
LINK_ENTITY (item_suit)
LINK_ENTITY (item_thighpack)
LINK_ENTITY (light)
LINK_ENTITY (light_environment)
LINK_ENTITY (light_spot)
LINK_ENTITY (momentary_door)
LINK_ENTITY (momentary_rot_button)
LINK_ENTITY (monster_hevsuit_dead)
LINK_ENTITY (monster_mortar)
LINK_ENTITY (monster_scientist)
LINK_ENTITY (multi_manager)
LINK_ENTITY (multisource)
LINK_ENTITY (path_corner)
LINK_ENTITY (path_track)
LINK_ENTITY (player)
LINK_ENTITY (player_loadsaved)
LINK_ENTITY (player_weaponstrip)
LINK_ENTITY (point_clientcommand)
LINK_ENTITY (point_servercommand)
LINK_ENTITY (soundent)
LINK_ENTITY (spark_shower)
LINK_ENTITY (speaker)
LINK_ENTITY (target_cdaudio)
LINK_ENTITY (test_effect)
LINK_ENTITY (trigger)
LINK_ENTITY (trigger_auto)
LINK_ENTITY (trigger_autosave)
LINK_ENTITY (trigger_camera)
LINK_ENTITY (trigger_cdaudio)
LINK_ENTITY (trigger_changelevel)
LINK_ENTITY (trigger_changetarget)
LINK_ENTITY (trigger_counter)
LINK_ENTITY (trigger_endsection)
LINK_ENTITY (trigger_gravity)
LINK_ENTITY (trigger_hurt)
LINK_ENTITY (trigger_monsterjump)
LINK_ENTITY (trigger_multiple)
LINK_ENTITY (trigger_once)
LINK_ENTITY (trigger_push)
LINK_ENTITY (trigger_random)
LINK_ENTITY (trigger_random_time)
LINK_ENTITY (trigger_random_unique)
LINK_ENTITY (trigger_relay)
LINK_ENTITY (trigger_setorigin)
LINK_ENTITY (trigger_teleport)
LINK_ENTITY (trigger_transition)
LINK_ENTITY (weapon_ak47)
LINK_ENTITY (weapon_aug)
LINK_ENTITY (weapon_awp)
LINK_ENTITY (weapon_c4)
LINK_ENTITY (weapon_deagle)
LINK_ENTITY (weapon_elite)
LINK_ENTITY (weapon_famas)
LINK_ENTITY (weapon_fiveseven)
LINK_ENTITY (weapon_flashbang)
LINK_ENTITY (weapon_g3sg1)
LINK_ENTITY (weapon_galil)
LINK_ENTITY (weapon_glock18)
LINK_ENTITY (weapon_hegrenade)
LINK_ENTITY (weapon_knife)
LINK_ENTITY (weapon_m249)
LINK_ENTITY (weapon_m3)
LINK_ENTITY (weapon_m4a1)
LINK_ENTITY (weapon_mac10)
LINK_ENTITY (weapon_mp5navy)
LINK_ENTITY (weapon_p228)
LINK_ENTITY (weapon_p90)
LINK_ENTITY (weapon_scout)
LINK_ENTITY (weapon_sg550)
LINK_ENTITY (weapon_sg552)
LINK_ENTITY (weapon_shield)
LINK_ENTITY (weapon_shieldgun)
LINK_ENTITY (weapon_smokegrenade)
LINK_ENTITY (weapon_tmp)
LINK_ENTITY (weapon_ump45)
LINK_ENTITY (weapon_usp)
LINK_ENTITY (weapon_xm1014)
LINK_ENTITY (weaponbox)
LINK_ENTITY (world_items)
LINK_ENTITY (worldspawn)

View file

@ -111,9 +111,6 @@ void BotManager::touchKillerEntity (Bot *bot) {
MDLL_Touch (m_killerEntity, bot->ent ()); MDLL_Touch (m_killerEntity, bot->ent ());
} }
// it's already defined in interface.cpp
extern "C" void player (entvars_t *pev);
void BotManager::execGameEntity (entvars_t *vars) { void BotManager::execGameEntity (entvars_t *vars) {
// this function calls gamedll player() function, in case to create player entity in game // this function calls gamedll player() function, in case to create player entity in game
@ -121,7 +118,7 @@ void BotManager::execGameEntity (entvars_t *vars) {
CALL_GAME_ENTITY (PLID, "player", vars); CALL_GAME_ENTITY (PLID, "player", vars);
return; return;
} }
player (vars); ents.getPlayerFunction () (vars);
} }
void BotManager::forEach (ForEachBot handler) { void BotManager::forEach (ForEachBot handler) {
@ -682,6 +679,23 @@ int BotManager::getBotCount () {
return m_bots.length (); return m_bots.length ();
} }
float BotManager::getConnectionTime (int botId) {
// this function get's fake bot player time.
for (const auto &bot : m_bots) {
if (bot->index () == botId) {
auto current = plat.seconds ();
if (current - bot->m_joinServerTime > bot->m_playServerTime || current - bot->m_joinServerTime <= 0) {
bot->m_playServerTime = 60.0f * rg.float_ (30.0f, 240.0f);
bot->m_joinServerTime = current - bot->m_playServerTime * rg.float_ (0.2f, 0.8f);
}
return current - bot->m_joinServerTime;
}
}
return 0.0f;
}
Twin <int, int> BotManager::countTeamPlayers () { Twin <int, int> BotManager::countTeamPlayers () {
int ts = 0, cts = 0; int ts = 0, cts = 0;
@ -846,6 +860,10 @@ Bot::Bot (edict_t *bot, int difficulty, int personality, int team, int member) {
m_frameInterval = game.timebase (); m_frameInterval = game.timebase ();
m_slowFrameTimestamp = 0.0f; m_slowFrameTimestamp = 0.0f;
// stuff from jk_botti
m_playServerTime = 60.0f * rg.float_ (30.0f, 240.0f);
m_joinServerTime = plat.seconds () - m_playServerTime * rg.float_ (0.2f, 0.8f);
switch (personality) { switch (personality) {
case 1: case 1:
m_personality = Personality::Rusher; m_personality = Personality::Rusher;

View file

@ -9,6 +9,7 @@
#include <yapb.h> #include <yapb.h>
ConVar yb_display_welcome_text ("yb_display_welcome_text", "1"); ConVar yb_display_welcome_text ("yb_display_welcome_text", "1");
ConVar yb_enable_query_hook ("yb_enable_query_hook", "1");
BotUtils::BotUtils () { BotUtils::BotUtils () {
m_needToSendWelcome = false; m_needToSendWelcome = false;
@ -585,6 +586,79 @@ void BotUtils::sendPings (edict_t *to) {
return; return;
} }
void BotUtils::installSendTo () {
// if previously requested to disable?
if (!yb_enable_query_hook.bool_ ()) {
if (m_sendToHook.enabled ()) {
disableSendTo ();
}
return;
}
// do not enable on not dedicated server
if (!game.isDedicated ()) {
return;
}
// enable only on modern games
if (game.is (GameFlags::Modern) && (plat.isLinux || plat.isWindows) && !plat.isAndroid && !m_sendToHook.enabled ()) {
m_sendToHook.patch (reinterpret_cast <void *> (&sendto), reinterpret_cast <void *> (&BotUtils::sendTo));
}
}
int32 BotUtils::sendTo (int socket, const void *message, size_t length, int flags, const sockaddr *dest, int destLength) {
const auto send = [&] (const Twin <const uint8 *, size_t> &msg) -> int32 {
return Socket::sendto (socket, msg.first, msg.second, flags, dest, destLength);
};
auto packet = reinterpret_cast <const uint8 *> (message);
// player replies response
if (length > 5 && packet[0] == 0xff && packet[1] == 0xff && packet[2] == 0xff && packet[3] == 0xff) {
if (packet[4] == 'D') {
QueryBuffer buffer (packet, length, 5);
auto count = buffer.read <uint8> ();
for (uint8 i = 0; i < count; ++i) {
buffer.read <uint8> (); // number
buffer.write <uint8> (i); // override number
buffer.skipString (); // name
buffer.skip <int32> (); // score
buffer.read <float> (); // override connection time
buffer.write <float> (bots.getConnectionTime (i));
}
return send (buffer.data ());
}
else if (packet[4] == 'I') {
QueryBuffer buffer (packet, length, 5);
buffer.skip <uint8> (); // protocol
// skip server name, folder, map game
for (size_t i = 0; i < 4; i++) {
buffer.skipString ();
}
buffer.skip <short> (); // steam app id
buffer.skip <uint8> (); // players
buffer.skip <uint8> (); // maxplayers
buffer.skip <uint8> (); // bots
buffer.write <uint8> (0); // zero out bot count
return send (buffer.data ());
}
else if (packet[4] == 'm') {
QueryBuffer buffer (packet, length, 5);
buffer.shiftToEnd (); // shift to the end of buffer
buffer.write <uint8> (0); // zero out bot count
return send (buffer.data ());
}
}
return send ({ packet, length });
}
int BotUtils::buildNumber () { int BotUtils::buildNumber () {
// this function generates build number from the compiler date macros // this function generates build number from the compiler date macros