fix: lang configs unable to parse last translated line (fixes #340) fix: last enemy isn't cleared instantly with dead entity anymore fix: bot weakness in pistol rounds analyzer: improved optimization of useless nodes linkage: make inability to call gamedll player( non-fatal linkage: fixed bot boot on WON engines pre 2000 builds (support for beta 6.5 restored) cvars: added suupport to revert all cvars to defaults via 'yb cvars defaults' cvars: added cv_preferred_personality to select bot default personality refactor: use single function to send hud messages over the bot code bot: added random original podbot welcome message to preserve origins of this bot conf: shuffle bot names and chatter items on conflig load conf: simplified a bit chatter.cfg syntax (old syntax still works build: added support for building with CMake (thanks @Velaron) refactor: rall the memory hooks moved into their one cpp file
179 lines
5.2 KiB
C++
179 lines
5.2 KiB
C++
//
|
|
// YaPB, based on PODBot by Markus Klinge ("CountFloyd").
|
|
// Copyright © YaPB Project Developers <yapb@jeefo.net>.
|
|
//
|
|
// SPDX-License-Identifier: MIT
|
|
//
|
|
|
|
#include <yapb.h>
|
|
|
|
int32_t BotSupport::sendTo (int socket, const void *message, size_t length, int flags, const sockaddr *dest, int destLength) {
|
|
const auto send = [&] (const Twin <const uint8_t *, size_t> &msg) -> int32_t {
|
|
return Socket::sendto (socket, msg.first, msg.second, flags, dest, destLength);
|
|
};
|
|
|
|
auto packet = reinterpret_cast <const uint8_t *> (message);
|
|
constexpr int32_t packetLength = 5;
|
|
|
|
// player replies response
|
|
if (length > packetLength && memcmp (packet, "\xff\xff\xff\xff", packetLength - 1) == 0) {
|
|
if (packet[4] == 'D') {
|
|
QueryBuffer buffer { packet, length, packetLength };
|
|
auto count = buffer.read <uint8_t> ();
|
|
|
|
for (uint8_t i = 0; i < count; ++i) {
|
|
buffer.skip <uint8_t> (); // number
|
|
auto name = buffer.readString (); // name
|
|
buffer.skip <int32_t> (); // score
|
|
|
|
auto ctime = buffer.read <float> (); // override connection time
|
|
buffer.write <float> (bots.getConnectTime (name, ctime));
|
|
}
|
|
return send (buffer.data ());
|
|
}
|
|
else if (packet[4] == 'I') {
|
|
QueryBuffer buffer { packet, length, packetLength };
|
|
buffer.skip <uint8_t> (); // 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_t> (); // players
|
|
buffer.skip <uint8_t> (); // maxplayers
|
|
buffer.skip <uint8_t> (); // bots
|
|
buffer.write <uint8_t> (0); // zero out bot count
|
|
|
|
return send (buffer.data ());
|
|
}
|
|
else if (packet[4] == 'm') {
|
|
QueryBuffer buffer { packet, length, packetLength };
|
|
|
|
buffer.shiftToEnd (); // shift to the end of buffer
|
|
buffer.write <uint8_t> (0); // zero out bot count
|
|
|
|
return send (buffer.data ());
|
|
}
|
|
}
|
|
return send ({ packet, length });
|
|
}
|
|
|
|
void ServerQueryHook::init () {
|
|
// if previously requested to disable?
|
|
if (!cv_enable_query_hook.bool_ ()) {
|
|
if (m_sendToDetour.detoured ()) {
|
|
disable ();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// do not enable on not dedicated server
|
|
if (!game.isDedicated ()) {
|
|
return;
|
|
}
|
|
SendToProto *sendToAddress = sendto;
|
|
|
|
// linux workaround with sendto
|
|
if (!plat.win && !plat.isNonX86 ()) {
|
|
if (game.elib ()) {
|
|
auto address = game.elib ().resolve <SendToProto *> ("sendto");
|
|
|
|
if (address != nullptr) {
|
|
sendToAddress = address;
|
|
}
|
|
}
|
|
}
|
|
m_sendToDetour.initialize ("ws2_32.dll", "sendto", sendToAddress);
|
|
|
|
// enable only on modern games
|
|
if (!game.is (GameFlags::Legacy) && (plat.nix || plat.win) && !plat.isNonX86 () && !m_sendToDetour.detoured ()) {
|
|
m_sendToDetour.install (reinterpret_cast <void *> (BotSupport::sendTo), true);
|
|
}
|
|
}
|
|
|
|
SharedLibrary::Func DynamicLinkerHook::lookup (SharedLibrary::Handle module, const char *function) {
|
|
static const auto &gamedll = game.lib ().handle ();
|
|
static const auto &self = m_self.handle ();
|
|
|
|
const auto resolve = [&] (SharedLibrary::Handle handle) {
|
|
return m_dlsym (handle, function);
|
|
};
|
|
|
|
if (entlink.needsBypass () && !strcmp (function, "CreateInterface")) {
|
|
entlink.setPaused (true);
|
|
auto ret = resolve (module);
|
|
|
|
entlink.disable ();
|
|
|
|
return ret;
|
|
}
|
|
|
|
// if requested module is yapb module, put in cache the looked up symbol
|
|
if (self != module) {
|
|
return resolve (module);
|
|
}
|
|
|
|
#if defined (CR_WINDOWS)
|
|
if (HIWORD (function) == 0) {
|
|
return resolve (module);
|
|
}
|
|
#endif
|
|
|
|
if (m_exports.exists (function)) {
|
|
return m_exports[function];
|
|
}
|
|
auto botAddr = resolve (self);
|
|
|
|
if (!botAddr) {
|
|
auto gameAddr = resolve (gamedll);
|
|
|
|
if (gameAddr) {
|
|
return m_exports[function] = gameAddr;
|
|
}
|
|
}
|
|
else {
|
|
return m_exports[function] = botAddr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool DynamicLinkerHook::callPlayerFunction (edict_t *ent) {
|
|
auto callPlayer = [&] () {
|
|
reinterpret_cast <EntityProto> (m_exports["player"]) (&ent->v);
|
|
};
|
|
|
|
if (m_exports.exists ("player")) {
|
|
callPlayer ();
|
|
return true;
|
|
}
|
|
auto playerFunction = game.lib ().resolve <EntityProto> ("player");
|
|
|
|
if (!playerFunction) {
|
|
logger.error ("Cannot resolve player() function in GameDLL.");
|
|
return false;
|
|
}
|
|
m_exports["player"] = reinterpret_cast <SharedLibrary::Func> (playerFunction);
|
|
callPlayer ();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool DynamicLinkerHook::needsBypass () const {
|
|
return !plat.win && !game.isDedicated ();
|
|
}
|
|
|
|
void DynamicLinkerHook::initialize () {
|
|
if (plat.isNonX86 () || game.is (GameFlags::Metamod)) {
|
|
return;
|
|
}
|
|
|
|
m_dlsym.initialize ("kernel32.dll", "GetProcAddress", DLSYM_FUNCTION);
|
|
m_dlsym.install (reinterpret_cast <void *> (lookupHandler), true);
|
|
|
|
if (needsBypass ()) {
|
|
m_dlclose.initialize ("kernel32.dll", "FreeLibrary", DLCLOSE_FUNCTION);
|
|
m_dlclose.install (reinterpret_cast <void *> (closeHandler), true);
|
|
}
|
|
m_self.locate (&engfuncs);
|
|
}
|