savepoint, changelog later..

This commit is contained in:
Dmitry 2019-07-27 17:36:24 +03:00 committed by jeefo
commit 1bc1fd1913
45 changed files with 12866 additions and 10981 deletions

View file

@ -1,7 +1,6 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
include $(XASH3D_CONFIG)
LOCAL_MODULE := yapb
@ -25,9 +24,10 @@ LOCAL_SRC_FILES := \
interface.cpp \
navigate.cpp \
support.cpp \
waypoint.cpp \
graph.cpp \
LOCAL_CFLAGS += -O2 -std=c++11 -DLINUX -D_LINUX -DPOSIX -pipe -fno-strict-aliasing -Wall -Werror
LOCAL_CFLAGS += -O3 -std=c++11 -DLINUX -D_LINUX -DPOSIX -pipe -fno-strict-aliasing -Wall -Werror -Wno-array-bounds
LOCAL_CPPFLAGS += -fno-exceptions -fno-rtti
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)

File diff suppressed because it is too large Load diff

View file

@ -19,11 +19,11 @@ void BotUtils::stripTags (String &line) {
for (const auto &tag : m_tags) {
const size_t start = line.find (tag.first, 0);
if (start != String::INVALID_INDEX) {
if (start != String::kInvalidIndex) {
const size_t end = line.find (tag.second, start);
const size_t diff = end - start;
if (end != String::INVALID_INDEX && end > start && diff < 32 && diff > 4) {
if (end != String::kInvalidIndex && end > start && diff < 32 && diff > 4) {
line.erase (start, diff + tag.second.length ());
break;
}
@ -37,7 +37,7 @@ void BotUtils::humanizePlayerName (String &playerName) {
}
// drop tag marks, 80 percent of time
if (rng.chance (80)) {
if (rg.chance (80)) {
stripTags (playerName);
}
else {
@ -45,14 +45,14 @@ void BotUtils::humanizePlayerName (String &playerName) {
}
// sometimes switch name to lower characters, only valid for the english languge
if (rng.chance (15) && strcmp (yb_language.str (), "en") == 0) {
if (rg.chance (8) && strcmp (yb_language.str (), "en") == 0) {
playerName.lowercase ();
}
}
void BotUtils::addChatErrors (String &line) {
// sometimes switch name to lower characters, only valid for the english languge
if (rng.chance (15) && strcmp (yb_language.str (), "en") == 0) {
if (rg.chance (8) && strcmp (yb_language.str (), "en") == 0) {
line.lowercase ();
}
auto length = line.length ();
@ -61,13 +61,13 @@ void BotUtils::addChatErrors (String &line) {
size_t percentile = line.length () / 2;
// "length / 2" percent of time drop a character
if (rng.chance (percentile)) {
line.erase (rng.getInt (length / 8, length - length / 8));
if (rg.chance (percentile)) {
line.erase (rg.int_ (length / 8, length - length / 8), 1);
}
// "length" / 4 precent of time swap character
if (rng.chance (percentile / 2)) {
size_t pos = rng.getInt (length / 8, 3 * length / 8); // choose random position in string
if (rg.chance (percentile / 2)) {
size_t pos = rg.int_ (length / 8, 3 * length / 8); // choose random position in string
cr::swap (line[pos], line[pos + 1]);
}
}
@ -76,7 +76,7 @@ void BotUtils::addChatErrors (String &line) {
bool BotUtils::checkKeywords (const String &line, String &reply) {
// this function checks is string contain keyword, and generates reply to it
if (!yb_chat.boolean () || line.empty ()) {
if (!yb_chat.bool_ () || line.empty ()) {
return false;
}
@ -84,7 +84,7 @@ bool BotUtils::checkKeywords (const String &line, String &reply) {
for (const auto &keyword : factory.keywords) {
// check is keyword has occurred in message
if (line.find (keyword, 0) != String::INVALID_INDEX) {
if (line.find (keyword) != String::kInvalidIndex) {
StringArray &usedReplies = factory.usedReplies;
if (usedReplies.length () >= factory.replies.length () / 4) {
@ -113,11 +113,9 @@ bool BotUtils::checkKeywords (const String &line, String &reply) {
}
}
}
auto &chat = conf.getChat ();
// didn't find a keyword? 70% of the time use some universal reply
if (rng.chance (70) && !chat[CHAT_NOKW].empty ()) {
reply.assign (chat[CHAT_NOKW].random ());
if (rg.chance (70) && conf.hasChatBank (Chat::NoKeyword)) {
reply.assign (conf.pickRandomFromChatBank (Chat::NoKeyword));
return true;
}
return false;
@ -126,47 +124,49 @@ bool BotUtils::checkKeywords (const String &line, String &reply) {
void Bot::prepareChatMessage (const String &message) {
// this function parses messages from the botchat, replaces keywords and converts names into a more human style
if (!yb_chat.boolean () || message.empty ()) {
if (!yb_chat.bool_ () || message.empty ()) {
return;
}
m_chatBuffer = message;
m_chatBuffer.assign (message.chars ());
// must be called before return or on the end
auto finishPreparation = [&] (void) {
auto finishPreparation = [&] () {
if (!m_chatBuffer.empty ()) {
util.addChatErrors (m_chatBuffer);
}
};
// need to check if we're have special symbols
size_t pos = message.find ('%', 0);
size_t pos = message.find ('%');
// nothing found, bail out
if (pos == String::INVALID_INDEX || pos >= message.length ()) {
if (pos == String::kInvalidIndex || pos >= message.length ()) {
finishPreparation ();
return;
}
// get the humanized name out of client
auto humanizedName = [] (const Client &client) -> String {
if (!util.isPlayer (client.ent)) {
return cr::move (String ("unknown"));
auto humanizedName = [] (int index) -> String {
auto ent = game.playerOfIndex (index);
if (!util.isPlayer (ent)) {
return "unknown";
}
String playerName = STRING (client.ent->v.netname);
String playerName = STRING (ent->v.netname);
util.humanizePlayerName (playerName);
return cr::move (playerName);
return playerName;
};
// find highfrag player
auto getHighfragPlayer = [&] (void) -> String {
auto getHighfragPlayer = [&] () -> String {
int highestFrags = -1;
int index = 0;
for (int i = 0; i < game.maxClients (); i++) {
for (int i = 0; i < game.maxClients (); ++i) {
const Client &client = util.getClient (i);
if (!(client.flags & CF_USED) || client.ent == ent ()) {
if (!(client.flags & ClientFlags::Used) || client.ent == ent ()) {
continue;
}
int frags = static_cast <int> (client.ent->v.frags);
@ -176,147 +176,116 @@ void Bot::prepareChatMessage (const String &message) {
index = i;
}
}
return humanizedName (util.getClient (index));
return humanizedName (index);
};
// get roundtime
auto getRoundTime = [] (void) -> String {
auto getRoundTime = [] () -> String {
auto roundTimeSecs = static_cast <int> (bots.getRoundEndTime () - game.timebase ());
String roundTime;
roundTime.assign ("%02d:%02d", cr::clamp (roundTimeSecs / 60, 0, 59), cr::clamp (cr::abs (roundTimeSecs % 60), 0, 59));
roundTime.assignf ("%02d:%02d", cr::clamp (roundTimeSecs / 60, 0, 59), cr::clamp (cr::abs (roundTimeSecs % 60), 0, 59));
return cr::move (roundTime);
return roundTime;
};
// get bot's victim
auto getMyVictim = [&] (void) -> String {
for (const Client &client : util.getClients ()) {
if (client.ent == m_lastVictim) {
return humanizedName (client);
}
}
return cr::move (String ("unknown"));
auto getMyVictim = [&] () -> String {;
return humanizedName (game.indexOfPlayer (m_lastVictim));
};
// get the game name alias
auto getGameName = [] (void) -> String {
auto getGameName = [] () -> String {
String gameName;
if (game.is (GAME_CZERO)) {
if (rng.chance (30)) {
if (game.is (GameFlags::ConditionZero)) {
if (rg.chance (30)) {
gameName = "CZ";
}
else {
gameName = "Condition Zero";
}
}
else if (game.is (GAME_CSTRIKE16) || game.is (GAME_LEGACY)) {
if (rng.chance (30)) {
else if (game.is (GameFlags::Modern) || game.is (GameFlags::Legacy)) {
if (rg.chance (30)) {
gameName = "CS";
}
else {
gameName = "Counter-Strike";
}
}
return cr::move (gameName);
return gameName;
};
// get enemy or teammate alive
auto getPlayerAlive = [&] (bool needsEnemy) -> String {
int index;
for (index = 0; index < game.maxClients (); index++) {
const Client &client = util.getClient (index);
if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.ent == ent ()) {
for (const auto &client : util.getClients ()) {
if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive) || client.ent == ent ()) {
continue;
}
if ((needsEnemy && m_team == client.team) || (!needsEnemy && m_team != client.team)) {
continue;
if (needsEnemy && m_team != client.team) {
return humanizedName (game.indexOfPlayer (client.ent));
}
else if (!needsEnemy && m_team == client.team) {
return humanizedName (game.indexOfPlayer (client.ent));
}
break;
}
return "UnknowPA";
};
size_t replaceCounter = 0;
if (index < game.maxClients ()) {
if (!needsEnemy && util.isPlayer (pev->dmg_inflictor) && m_team == game.getTeam (pev->dmg_inflictor)) {
return humanizedName (util.getClient (game.indexOfEntity (pev->dmg_inflictor) - 1));
while (replaceCounter < 6 && (pos = m_chatBuffer.find ('%')) != String::kInvalidIndex) {
// found one, let's do replace
switch (message[pos + 1]) {
// the highest frag player
case 'f':
m_chatBuffer.replace ("%f", getHighfragPlayer ());
break;
// current map name
case 'm':
m_chatBuffer.replace ("%m", game.getMapName ());
break;
// round time
case 'r':
m_chatBuffer.replace ("%r", getRoundTime ());
break;
// chat reply
case 's':
if (m_sayTextBuffer.entityIndex != -1) {
m_chatBuffer.replace ("%s", humanizedName (m_sayTextBuffer.entityIndex));
}
else {
return humanizedName (util.getClient (index));
m_chatBuffer.replace ("%s", getHighfragPlayer ());
}
}
else {
for (index = 0; index < game.maxClients (); index++) {
const Client &client = util.getClient (index);
break;
if (!(client.flags & CF_USED) || client.team != m_team || client.ent == ent ()) {
continue;
}
// last bot victim
case 'v':
m_chatBuffer.replace ("%v", getMyVictim ());
break;
if ((needsEnemy && m_team != client.team) || (!needsEnemy && m_team == client.team)) {
continue;
}
break;
}
// game name
case 'd':
m_chatBuffer.replace ("%d", getGameName ());
break;
if (index < game.maxClients ()) {
return humanizedName (util.getClient (index));
}
}
return cr::move (String ("unknown"));
};
// teammate alive
case 't':
m_chatBuffer.replace ("%t", getPlayerAlive (false));
break;
// found one, let's do replace
switch (message[pos + 1]) {
// the highest frag player
case 'f':
m_chatBuffer.replace ("%f", getHighfragPlayer ());
break;
// current map name
case 'm':
m_chatBuffer.replace ("%m", game.getMapName ());
break;
// round time
case 'r':
m_chatBuffer.replace ("%r", getRoundTime ());
break;
// chat reply
case 's':
if (m_sayTextBuffer.entityIndex != -1) {
m_chatBuffer.replace ("%s", humanizedName (util.getClient (m_sayTextBuffer.entityIndex)));
}
else {
m_chatBuffer.replace ("%s", getHighfragPlayer ());
}
break;
// last bot victim
case 'v':
m_chatBuffer.replace ("%v", getMyVictim ());
break;
// game name
case 'd':
m_chatBuffer.replace ("%d", getGameName ());
break;
// teammate alive
case 't':
m_chatBuffer.replace ("%t", getPlayerAlive (false));
break;
// enemy alive
case 'e':
m_chatBuffer.replace ("%e", getPlayerAlive (true));
break;
};
// enemy alive
case 'e':
m_chatBuffer.replace ("%e", getPlayerAlive (true));
break;
};
replaceCounter++;
}
finishPreparation ();
}
@ -327,18 +296,17 @@ bool Bot::checkChatKeywords (String &reply) {
return util.checkKeywords (message.uppercase (), reply);
}
bool Bot::isReplyingToChat (void) {
bool Bot::isReplyingToChat () {
// this function sends reply to a player
if (m_sayTextBuffer.entityIndex != -1 && !m_sayTextBuffer.sayText.empty ()) {
// check is time to chat is good
if (m_sayTextBuffer.timeNextChat < game.timebase ()) {
if (m_sayTextBuffer.timeNextChat < game.timebase () + rg.float_ (m_sayTextBuffer.chatDelay / 2, m_sayTextBuffer.chatDelay)) {
String replyText;
if (rng.chance (m_sayTextBuffer.chatProbability + rng.getInt (25, 45)) && checkChatKeywords (replyText)) {
if (rg.chance (m_sayTextBuffer.chatProbability + rg.int_ (20, 50)) && checkChatKeywords (replyText)) {
prepareChatMessage (replyText);
pushMsgQueue (GAME_MSG_SAY_CMD);
pushMsgQueue (BotMsg::Say);
m_sayTextBuffer.entityIndex = -1;
m_sayTextBuffer.timeNextChat = game.timebase () + m_sayTextBuffer.chatDelay;
@ -353,19 +321,17 @@ bool Bot::isReplyingToChat (void) {
return false;
}
void Bot::checkForChat (void) {
void Bot::checkForChat () {
// say a text every now and then
if (rng.chance (35) || m_notKilled || !yb_chat.boolean ()) {
if (rg.chance (30) || m_notKilled || !yb_chat.bool_ ()) {
return;
}
// bot chatting turned on?
if (m_lastChatTime + 10.0 < game.timebase () && bots.getLastChatTimestamp () + 5.0f < game.timebase () && !isReplyingToChat ()) {
auto &chat = conf.getChat ();
if (!chat[CHAT_DEAD].empty ()) {
const String &phrase = chat[CHAT_DEAD].random ();
if (m_lastChatTime + rg.float_ (6.0f, 10.0f) < game.timebase () && bots.getLastChatTimestamp () + rg.float_ (2.5f, 5.0f) < game.timebase () && !isReplyingToChat ()) {
if (conf.hasChatBank (Chat::Dead)) {
const auto &phrase = conf.pickRandomFromChatBank (Chat::Dead);
bool sayBufferExists = false;
// search for last messages, sayed
@ -375,9 +341,10 @@ void Bot::checkForChat (void) {
break;
}
}
if (!sayBufferExists) {
prepareChatMessage (phrase);
pushMsgQueue (GAME_MSG_SAY_CMD);
pushMsgQueue (BotMsg::Say);
m_lastChatTime = game.timebase ();
bots.setLastChatTimestamp (game.timebase ());
@ -388,7 +355,7 @@ void Bot::checkForChat (void) {
}
// clear the used line buffer every now and then
if (static_cast <int> (m_sayTextBuffer.lastUsedSentences.length ()) > rng.getInt (4, 6)) {
if (static_cast <int> (m_sayTextBuffer.lastUsedSentences.length ()) > rg.int_ (4, 6)) {
m_sayTextBuffer.lastUsedSentences.clear ();
}
}
@ -397,17 +364,17 @@ void Bot::checkForChat (void) {
void Bot::say (const char *text) {
// this function prints saytext message to all players
if (util.isEmptyStr (text) || !yb_chat.boolean ()) {
if (util.isEmptyStr (text) || !yb_chat.bool_ ()) {
return;
}
game.execBotCmd (ent (), "say \"%s\"", text);
game.botCommand (ent (), "say \"%s\"", text);
}
void Bot::sayTeam (const char *text) {
// this function prints saytext message only for teammates
if (util.isEmptyStr (text) || !yb_chat.boolean ()) {
if (util.isEmptyStr (text) || !yb_chat.bool_ ()) {
return;
}
game.execBotCmd (ent (), "say_team \"%s\"", text);
game.botCommand (ent (), "say_team \"%s\"", text);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

2940
source/graph.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -9,7 +9,7 @@
#include <yapb.h>
ConVar yb_version ("yb_version", PRODUCT_VERSION, VT_READONLY);
ConVar yb_version ("yb_version", PRODUCT_VERSION, Var::ReadOnly);
gamefuncs_t dllapi;
enginefuncs_t engfuncs;
@ -33,7 +33,7 @@ plugin_info_t Plugin_info = {
PT_ANYTIME, // when unloadable
};
namespace VariadicCallbacks {
namespace variadic {
void clientCommand (edict_t *ent, char const *format, ...) {
// this function forces the client whose player entity is ent to issue a client command.
// How it works is that clients all have a argv global string in their client DLL that
@ -50,31 +50,31 @@ namespace VariadicCallbacks {
// case it's a bot asking for a client command, we handle it like we do for bot commands
va_list ap;
char buffer[MAX_PRINT_BUFFER];
auto buffer = strings.chars ();
va_start (ap, format);
_vsnprintf (buffer, cr::bufsize (buffer), format, ap);
_vsnprintf (buffer, StringBuffer::StaticBufferSize, format, ap);
va_end (ap);
if (ent && (ent->v.flags & (FL_FAKECLIENT | FL_DORMANT))) {
if (bots.getBot (ent)) {
game.execBotCmd (ent, buffer);
if (bots[ent]) {
game.botCommand (ent, buffer);
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_SUPERCEDE); // prevent bots to be forced to issue client commands
}
return;
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnClientCommand (ent, buffer);
}
}
SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
CR_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
// this function is called right after GiveFnptrsToDll() by the engine in the game DLL (or
// what it BELIEVES to be the game DLL), in order to copy the list of MOD functions that can
// be called by the engine, into a memory block pointed to by the functionTable pointer
@ -87,13 +87,12 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
memset (functionTable, 0, sizeof (gamefuncs_t));
if (!(game.is (GAME_METAMOD))) {
auto api_GetEntityAPI = game.getLib ().resolve <int (*) (gamefuncs_t *, int)> ("GetEntityAPI");
if (!(game.is (GameFlags::Metamod))) {
auto api_GetEntityAPI = game.lib ().resolve <int (*) (gamefuncs_t *, int)> ("GetEntityAPI");
// pass other DLLs engine callbacks to function table...
if (api_GetEntityAPI (&dllapi, INTERFACE_VERSION) == 0) {
util.logEntry (true, LL_FATAL, "GetEntityAPI2: ERROR - Not Initialized.");
return FALSE; // error initializing function table!!!
if (!api_GetEntityAPI || api_GetEntityAPI (&dllapi, INTERFACE_VERSION) == 0) {
logger.fatal ("Could not resolve symbol \"%s\" in the game dll.", "GetEntityAPI");
}
dllfuncs.dllapi_table = &dllapi;
gpGamedllFuncs = &dllfuncs;
@ -101,7 +100,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
memcpy (functionTable, &dllapi, sizeof (gamefuncs_t));
}
functionTable->pfnGameInit = [] (void) {
functionTable->pfnGameInit = [] () {
// this function is a one-time call, and appears to be the second function called in the
// DLL after GiveFntprsToDll() has been called. Its purpose is to tell the MOD DLL to
// initialize the game before the engine actually hooks into it with its video frames and
@ -110,6 +109,14 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
// server is enabled. Here is a good place to do our own game session initialization, and
// to register by the engine side the server commands we need to administrate our bots.
// register bot cvars
game.registerCvars ();
// register logger
logger.initialize (strings.format ("%slogs/yapb.log", graph.getDataDirectory (false)), [] (const char *msg) {
game.print (msg);
});
conf.initWeapons ();
// register server command(s)
@ -117,20 +124,20 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
game.registerCmd ("yb", BotControl::handleEngineCommands);
// set correct version string
yb_version.set (util.format ("%d.%d.%d", PRODUCT_VERSION_DWORD_INTERNAL, util.buildNumber ()));
yb_version.set (strings.format ("%d.%d.%d", PRODUCT_VERSION_DWORD_INTERNAL, util.buildNumber ()));
// execute main config
conf.load (true);
conf.loadMainConfig ();
// register fake metamod command handler if we not! under mm
if (!(game.is (GAME_METAMOD))) {
game.registerCmd ("meta", [] (void) {
if (!(game.is (GameFlags::Metamod))) {
game.registerCmd ("meta", [] () {
game.print ("You're launched standalone version of yapb. Metamod is not installed or not enabled!");
});
}
conf.adjustWeaponPrices ();
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
dllapi.pfnGameInit ();
@ -144,7 +151,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
game.precache ();
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META_VALUE (MRES_IGNORED, 0);
}
int result = dllapi.pfnSpawn (ent); // get result
@ -168,7 +175,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
// is called twice, once for each entity moving.
if (!game.isNullEntity (pentTouched) && pentOther != game.getStartEntity ()) {
Bot *bot = bots.getBot (pentTouched);
auto bot = bots[pentTouched];
if (bot != nullptr && pentOther != bot->ent ()) {
@ -181,7 +188,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
}
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
dllapi.pfnTouch (pentTouched, pentOther);
@ -212,13 +219,13 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
if (strcmp (addr, "loopback") == 0) {
game.setLocalEntity (ent); // save the edict of the listen server client...
// if not dedicated set the default editor for waypoints
// if not dedicated set the default editor for graph
if (!game.isDedicated ()) {
waypoints.setEditor (ent);
graph.setEditor (ent);
}
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META_VALUE (MRES_IGNORED, 0);
}
return dllapi.pfnClientConnect (ent, name, addr, rejectReason);
@ -236,18 +243,18 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
// to reset his entity pointer for safety. There are still a few server frames to go once a
// listen server client disconnects, and we don't want to send him any sort of message then.
int index = game.indexOfEntity (ent) - 1;
if (index >= 0 && index < MAX_ENGINE_PLAYERS) {
auto bot = bots.getBot (index);
// check if its a bot
if (bot != nullptr && bot->pev == &ent->v) {
for (auto &bot : bots) {
if (bot->pev == &ent->v) {
bot->showChaterIcon (false);
bots.destroy (index);
conf.clearUsedName (bot.get ()); // clear the bot name
bots.erase (bot.get ()); // remove the bot from bots array
break;
}
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
dllapi.pfnClientDisconnect (ent);
@ -261,7 +268,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
ctrl.assignAdminRights (ent, infobuffer);
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
dllapi.pfnClientUserInfoChanged (ent, infobuffer);
@ -282,14 +289,14 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
// clients. Hence it can lack of commenting a bit, since this code is very subject to change.
if (ctrl.handleClientCommands (ent)) {
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_SUPERCEDE);
}
return;
}
else if (ctrl.handleMenuCommands (ent)) {
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_SUPERCEDE);
}
return;
@ -298,7 +305,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
// record stuff about radio and chat
bots.captureChatRadio (engfuncs.pfnCmd_Argv (0), engfuncs.pfnCmd_Argv (1), ent);
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
dllapi.pfnClientCommand (ent);
@ -312,8 +319,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
// loading the bot profiles, and drawing the world map (ie, filling the navigation hashtable).
// Once this function has been called, the server can be considered as "running".
bots.destroy ();
conf.load (false); // initialize all config files
conf.loadConfigs (); // initialize all config files
// do a level initialization
game.levelInitialize (pentEdictList, edictCount);
@ -322,27 +328,26 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
illum.resetWorldModel ();
// do level initialization stuff here...
waypoints.init ();
waypoints.load ();
graph.loadGraphData ();
// execute main config
conf.load (true);
conf.loadMainConfig ();
if (File::exists (util.format ("%s/maps/%s_yapb.cfg", game.getModName (), game.getMapName ()))) {
game.execCmd ("exec maps/%s_yapb.cfg", game.getMapName ());
if (File::exists (strings.format ("%s/maps/%s_yapb.cfg", game.getModName (), game.getMapName ()))) {
game.serverCommand ("exec maps/%s_yapb.cfg", game.getMapName ());
game.print ("Executing Map-Specific config file");
}
bots.initQuota ();
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
dllapi.pfnServerActivate (pentEdictList, edictCount, clientMax);
waypoints.rebuildVisibility ();
graph.rebuildVisibility ();
};
functionTable->pfnServerDeactivate = [] (void) {
functionTable->pfnServerDeactivate = [] () {
// this function is called when the server is shutting down. A particular note about map
// changes: changing the map means shutting down the server and starting a new one. Of course
// this process is transparent to the user, but either in single player when the hero reaches
@ -354,8 +359,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
// the loading of new bots and the new BSP data parsing there.
// save collected experience on shutdown
waypoints.saveExperience ();
waypoints.saveVisibility ();
graph.savePractice ();
// destroy global killer entity
bots.destroyKillerEntity ();
@ -370,19 +374,21 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
util.setNeedForWelcome (false);
// xash is not kicking fakeclients on changelevel
if (game.is (GAME_XASH_ENGINE)) {
if (game.is (GameFlags::Xash3D)) {
bots.kickEveryone (true, false);
bots.destroy ();
}
waypoints.init ();
graph.initGraph ();
if (game.is (GAME_METAMOD)) {
// clear all the bots
bots.destroy ();
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
dllapi.pfnServerDeactivate ();
};
functionTable->pfnStartFrame = [] (void) {
functionTable->pfnStartFrame = [] () {
// this function starts a video frame. It is called once per video frame by the game. If
// you run Half-Life at 90 fps, this function will then be called 90 times per second. By
// placing a hook on it, we have a good place to do things that should be done continuously
@ -400,10 +406,9 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
// update some stats for clients
util.updateClients ();
if (waypoints.hasEditFlag (WS_EDIT_ENABLED) && waypoints.hasEditor ()) {
waypoints.frame ();
if (graph.hasEditFlag (GraphEdit::On) && graph.hasEditor ()) {
graph.frame ();
}
bots.updateDeathMsgState (false);
// run stuff periodically
game.slowFrame ();
@ -419,7 +424,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
// keep bot number up to date
bots.maintainQuota ();
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
dllapi.pfnStartFrame ();
@ -428,17 +433,24 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
bots.slowFrame ();
};
functionTable->pfnUpdateClientData = [] (const struct edict_s *ent, int sendweapons, struct clientdata_s *cd) {
extern ConVar yb_latency_display;
functionTable->pfnCmdStart = [] (const edict_t *player, usercmd_t *cmd, unsigned int random_seed) {
auto ent = const_cast <edict_t *> (player);
if (game.is (GAME_SUPPORT_SVC_PINGS) && yb_latency_display.integer () == 2 && bots.hasBotsOnline ()) {
bots.sendPingOffsets (const_cast <edict_t *> (ent));
// if we're handle pings for bots and clients, clear IN_SCORE button so SV_ShouldUpdatePing engine function return false
// and SV_EmitPings will not overwrite our results
if (game.is (GameFlags::HasFakePings) && yb_show_latency.int_ () == 2) {
if ((cmd->buttons & IN_SCORE) || (ent->v.oldbuttons & IN_SCORE)) {
cmd->buttons &= ~IN_SCORE;
// send our version of pings
util.sendPings (ent);
}
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
dllapi.pfnUpdateClientData (ent, sendweapons, cd);
dllapi.pfnCmdStart (player, cmd, random_seed);
};
functionTable->pfnPM_Move = [] (playermove_t *playerMove, int server) {
@ -449,7 +461,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
illum.setWorldModel (playerMove->physents[0].model);
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
dllapi.pfnPM_Move (playerMove, server);
@ -457,8 +469,8 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) {
return TRUE;
}
SHARED_LIBRARAY_EXPORT int GetEntityAPI2_Post (gamefuncs_t *functionTable, int *) {
// this function is called right after FuncPointers_t() by the engine in the game DLL (or
CR_EXPORT int GetEntityAPI2_Post (gamefuncs_t *table, int *) {
// this function is called right after GiveFnptrsToDll() by the engine in the game DLL (or
// what it BELIEVES to be the game DLL), in order to copy the list of MOD functions that can
// be called by the engine, into a memory block pointed to by the functionTable pointer
// that is passed into this function (explanation comes straight from botman). This allows
@ -468,9 +480,9 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2_Post (gamefuncs_t *functionTable, int *
// engine, and then calls the MOD DLL's version of GetEntityAPI to get the REAL gamedll
// functions this time (to use in the bot code). Post version, called only by metamod.
memset (functionTable, 0, sizeof (gamefuncs_t));
memset (table, 0, sizeof (gamefuncs_t));
functionTable->pfnSpawn = [] (edict_t *ent) {
table->pfnSpawn = [] (edict_t *ent) {
// this function asks the game DLL to spawn (i.e, give a physical existence in the virtual
// world, in other words to 'display') the entity pointed to by ent in the game. The
// Spawn() function is one of the functions any entity is supposed to have in the game DLL,
@ -484,7 +496,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2_Post (gamefuncs_t *functionTable, int *
RETURN_META_VALUE (MRES_IGNORED, 0);
};
functionTable->pfnStartFrame = [] (void) {
table->pfnStartFrame = [] () {
// this function starts a video frame. It is called once per video frame by the game. If
// you run Half-Life at 90 fps, this function will then be called 90 times per second. By
// placing a hook on it, we have a good place to do things that should be done continuously
@ -496,7 +508,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2_Post (gamefuncs_t *functionTable, int *
RETURN_META (MRES_IGNORED);
};
functionTable->pfnServerActivate = [] (edict_t *, int, int) {
table->pfnServerActivate = [] (edict_t *, int, int) {
// this function is called when the server has fully loaded and is about to manifest itself
// on the network as such. Since a mapchange is actually a server shutdown followed by a
// restart, this function is also called when a new map is being loaded. Hence it's the
@ -505,7 +517,7 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2_Post (gamefuncs_t *functionTable, int *
// Once this function has been called, the server can be considered as "running". Post version
// called only by metamod.
waypoints.rebuildVisibility ();
graph.rebuildVisibility ();
RETURN_META (MRES_IGNORED);
};
@ -513,21 +525,21 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2_Post (gamefuncs_t *functionTable, int *
return TRUE;
}
SHARED_LIBRARAY_EXPORT int GetNewDLLFunctions (newgamefuncs_t *functionTable, int *interfaceVersion) {
CR_EXPORT int GetNewDLLFunctions (newgamefuncs_t *functionTable, int *interfaceVersion) {
// it appears that an extra function table has been added in the engine to gamedll interface
// since the date where the first enginefuncs table standard was frozen. These ones are
// facultative and we don't hook them, but since some MODs might be featuring it, we have to
// pass them too, else the DLL interfacing wouldn't be complete and the game possibly wouldn't
// run properly.
auto api_GetNewDLLFunctions = game.getLib ().resolve <int (*) (newgamefuncs_t *, int *)> ("GetNewDLLFunctions");
auto api_GetNewDLLFunctions = game.lib ().resolve <int (*) (newgamefuncs_t *, int *)> (__FUNCTION__);
if (api_GetNewDLLFunctions == nullptr) {
return FALSE;
}
if (!api_GetNewDLLFunctions (functionTable, interfaceVersion)) {
util.logEntry (true, LL_ERROR, "GetNewDLLFunctions: ERROR - Not Initialized.");
if (!api_GetNewDLLFunctions || !api_GetNewDLLFunctions (functionTable, interfaceVersion)) {
logger.error ("Could not resolve symbol \"%s\" in the game dll. Continuing...", __FUNCTION__);
return FALSE;
}
@ -535,8 +547,8 @@ SHARED_LIBRARAY_EXPORT int GetNewDLLFunctions (newgamefuncs_t *functionTable, in
return TRUE;
}
SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) {
if (game.is (GAME_METAMOD)) {
CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) {
if (game.is (GameFlags::Metamod)) {
memset (functionTable, 0, sizeof (enginefuncs_t));
}
@ -551,10 +563,9 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
// spawn point named "tr_2lm".
// save collected experience on map change
waypoints.saveExperience ();
waypoints.saveVisibility ();
graph.savePractice ();
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnChangeLevel (s1, s2);
@ -565,7 +576,7 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
illum.updateLight (style, val);
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnLightStyle (style, val);
@ -573,11 +584,11 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
functionTable->pfnFindEntityByString = [] (edict_t *edictStartSearchAfter, const char *field, const char *value) {
// round starts in counter-strike 1.5
if ((game.is (GAME_LEGACY)) && strcmp (value, "info_map_parameters") == 0) {
if ((game.is (GameFlags::Legacy)) && strcmp (value, "info_map_parameters") == 0) {
bots.initRound ();
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META_VALUE (MRES_IGNORED, static_cast <edict_t *> (nullptr));
}
return engfuncs.pfnFindEntityByString (edictStartSearchAfter, field, value);
@ -596,7 +607,7 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
util.attachSoundsToClients (entity, sample, volume);
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnEmitSound (entity, channel, sample, volume, attenuation, flags, pitch);
@ -607,29 +618,26 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
game.beginMessage (ed, msgDest, msgType);
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnMessageBegin (msgDest, msgType, origin, ed);
};
functionTable->pfnMessageEnd = [] (void) {
functionTable->pfnMessageEnd = [] () {
game.resetMessages ();
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnMessageEnd ();
// send latency fix
bots.sendDeathMsgFix ();
};
functionTable->pfnWriteByte = [] (int value) {
// if this message is for a bot, call the client message function...
game.processMessages ((void *) &value);
game.processMessages (reinterpret_cast <void *> (&value));
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnWriteByte (value);
@ -637,9 +645,9 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
functionTable->pfnWriteChar = [] (int value) {
// if this message is for a bot, call the client message function...
game.processMessages ((void *) &value);
game.processMessages (reinterpret_cast <void *> (&value));
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnWriteChar (value);
@ -647,9 +655,9 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
functionTable->pfnWriteShort = [] (int value) {
// if this message is for a bot, call the client message function...
game.processMessages ((void *) &value);
game.processMessages (reinterpret_cast <void *> (&value));
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnWriteShort (value);
@ -657,9 +665,9 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
functionTable->pfnWriteLong = [] (int value) {
// if this message is for a bot, call the client message function...
game.processMessages ((void *) &value);
game.processMessages (reinterpret_cast <void *> (&value));
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnWriteLong (value);
@ -667,9 +675,9 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
functionTable->pfnWriteAngle = [] (float value) {
// if this message is for a bot, call the client message function...
game.processMessages ((void *) &value);
game.processMessages (reinterpret_cast <void *> (&value));
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnWriteAngle (value);
@ -677,9 +685,9 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
functionTable->pfnWriteCoord = [] (float value) {
// if this message is for a bot, call the client message function...
game.processMessages ((void *) &value);
game.processMessages (reinterpret_cast <void *> (&value));
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnWriteCoord (value);
@ -687,9 +695,9 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
functionTable->pfnWriteString = [] (const char *sz) {
// if this message is for a bot, call the client message function...
game.processMessages ((void *) sz);
game.processMessages (reinterpret_cast <void *> (const_cast <char *> (sz)));
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnWriteString (sz);
@ -697,9 +705,9 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
functionTable->pfnWriteEntity = [] (int value) {
// if this message is for a bot, call the client message function...
game.processMessages ((void *) &value);
game.processMessages (reinterpret_cast <void *> (&value));
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnWriteEntity (value);
@ -716,76 +724,76 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
// using pfnMessageBegin (), it will know what message ID number to send, and the engine will
// know what to do, only for non-metamod version
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META_VALUE (MRES_IGNORED, 0);
}
int message = engfuncs.pfnRegUserMsg (name, size);
if (strcmp (name, "VGUIMenu") == 0) {
game.setMessageId (NETMSG_VGUI, message);
game.setMessageId (NetMsg::VGUI, message);
}
else if (strcmp (name, "ShowMenu") == 0) {
game.setMessageId (NETMSG_SHOWMENU, message);
game.setMessageId (NetMsg::ShowMenu, message);
}
else if (strcmp (name, "WeaponList") == 0) {
game.setMessageId (NETMSG_WEAPONLIST, message);
game.setMessageId (NetMsg::WeaponList, message);
}
else if (strcmp (name, "CurWeapon") == 0) {
game.setMessageId (NETMSG_CURWEAPON, message);
game.setMessageId (NetMsg::CurWeapon, message);
}
else if (strcmp (name, "AmmoX") == 0) {
game.setMessageId (NETMSG_AMMOX, message);
game.setMessageId (NetMsg::AmmoX, message);
}
else if (strcmp (name, "AmmoPickup") == 0) {
game.setMessageId (NETMSG_AMMOPICKUP, message);
game.setMessageId (NetMsg::AmmoPickup, message);
}
else if (strcmp (name, "Damage") == 0) {
game.setMessageId (NETMSG_DAMAGE, message);
game.setMessageId (NetMsg::Damage, message);
}
else if (strcmp (name, "Money") == 0) {
game.setMessageId (NETMSG_MONEY, message);
game.setMessageId (NetMsg::Money, message);
}
else if (strcmp (name, "StatusIcon") == 0) {
game.setMessageId (NETMSG_STATUSICON, message);
game.setMessageId (NetMsg::StatusIcon, message);
}
else if (strcmp (name, "DeathMsg") == 0) {
game.setMessageId (NETMSG_DEATH, message);
game.setMessageId (NetMsg::DeathMsg, message);
}
else if (strcmp (name, "ScreenFade") == 0) {
game.setMessageId (NETMSG_SCREENFADE, message);
game.setMessageId (NetMsg::ScreenFade, message);
}
else if (strcmp (name, "HLTV") == 0) {
game.setMessageId (NETMSG_HLTV, message);
game.setMessageId (NetMsg::HLTV, message);
}
else if (strcmp (name, "TextMsg") == 0) {
game.setMessageId (NETMSG_TEXTMSG, message);
game.setMessageId (NetMsg::TextMsg, message);
}
else if (strcmp (name, "TeamInfo") == 0) {
game.setMessageId (NETMSG_TEAMINFO, message);
game.setMessageId (NetMsg::TeamInfo, message);
}
else if (strcmp (name, "BarTime") == 0) {
game.setMessageId (NETMSG_BARTIME, message);
game.setMessageId (NetMsg::BarTime, message);
}
else if (strcmp (name, "SendAudio") == 0) {
game.setMessageId (NETMSG_SENDAUDIO, message);
game.setMessageId (NetMsg::SendAudio, message);
}
else if (strcmp (name, "SayText") == 0) {
game.setMessageId (NETMSG_SAYTEXT, message);
game.setMessageId (NetMsg::SayText, message);
}
else if (strcmp (name, "BotVoice") == 0) {
game.setMessageId (NETMSG_BOTVOICE, message);
game.setMessageId (NetMsg::BotVoice, message);
}
else if (strcmp (name, "NVGToggle") == 0) {
game.setMessageId (NETMSG_NVGTOGGLE, message);
game.setMessageId (NetMsg::NVGToggle, message);
}
else if (strcmp (name, "FlashBat") == 0) {
game.setMessageId (NETMSG_FLASHBAT, message);
game.setMessageId (NetMsg::FlashBat, message);
}
else if (strcmp (name, "Flashlight") == 0) {
game.setMessageId (NETMSG_FLASHLIGHT, message);
game.setMessageId (NetMsg::Fashlight, message);
}
else if (strcmp (name, "ItemStatus") == 0) {
game.setMessageId (NETMSG_ITEMSTATUS, message);
game.setMessageId (NetMsg::ItemStatus, message);
}
return message;
};
@ -798,19 +806,19 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
// we know, right ? But since stupidity rules this world, we do a preventive check :)
if (util.isFakeClient (ent)) {
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_SUPERCEDE);
}
return;
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnClientPrintf (ent, printType, message);
};
functionTable->pfnCmd_Args = [] (void) {
functionTable->pfnCmd_Args = [] () {
// this function returns a pointer to the whole current client command string. Since bots
// have no client DLL and we may want a bot to execute a client command, we had to implement
// a argv string in the bot DLL for holding the bots' commands, and also keep track of the
@ -820,13 +828,13 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
// is this a bot issuing that client command?
if (game.isBotCmd ()) {
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META_VALUE (MRES_SUPERCEDE, game.botArgs ());
}
return game.botArgs (); // else return the whole bot client command string we know
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META_VALUE (MRES_IGNORED, static_cast <const char *> (nullptr));
}
return engfuncs.pfnCmd_Args (); // ask the client command string to the engine
@ -842,19 +850,19 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
// is this a bot issuing that client command?
if (game.isBotCmd ()) {
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META_VALUE (MRES_SUPERCEDE, game.botArgv (argc));
}
return game.botArgv (argc); // if so, then return the wanted argument we know
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META_VALUE (MRES_IGNORED, static_cast <const char *> (nullptr));
}
return engfuncs.pfnCmd_Argv (argc); // ask the argument number "argc" to the engine
};
functionTable->pfnCmd_Argc = [] (void) {
functionTable->pfnCmd_Argc = [] () {
// this function returns the number of arguments the current client command string has. Since
// bots have no client DLL and we may want a bot to execute a client command, we had to
// implement a argv string in the bot DLL for holding the bots' commands, and also keep
@ -864,61 +872,52 @@ SHARED_LIBRARAY_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int
// is this a bot issuing that client command?
if (game.isBotCmd ()) {
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META_VALUE (MRES_SUPERCEDE, game.botArgc ());
}
return game.botArgc (); // if so, then return the argument count we know
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META_VALUE (MRES_IGNORED, 0);
}
return engfuncs.pfnCmd_Argc (); // ask the engine how many arguments there are
};
functionTable->pfnSetClientMaxspeed = [] (const edict_t *ent, float newMaxspeed) {
Bot *bot = bots.getBot (const_cast <edict_t *> (ent));
auto bot = bots[const_cast <edict_t *> (ent)];
// check wether it's not a bot
if (bot != nullptr) {
bot->pev->maxspeed = newMaxspeed;
}
if (game.is (GAME_METAMOD)) {
if (game.is (GameFlags::Metamod)) {
RETURN_META (MRES_IGNORED);
}
engfuncs.pfnSetClientMaxspeed (ent, newMaxspeed);
};
functionTable->pfnClientCommand = variadic::clientCommand;
return TRUE;
}
SHARED_LIBRARAY_EXPORT int GetEngineFunctions_Post (enginefuncs_t *functionTable, int *) {
memset (functionTable, 0, sizeof (enginefuncs_t));
functionTable->pfnMessageEnd = [] (void) {
// send latency fix
bots.sendDeathMsgFix ();
RETURN_META (MRES_IGNORED);
};
return TRUE;
}
SHARED_LIBRARAY_EXPORT int Server_GetBlendingInterface (int version, void **ppinterface, void *pstudio, float (*rotationmatrix)[3][4], float (*bonetransform)[128][3][4]) {
CR_EXPORT int Server_GetBlendingInterface (int version, void **ppinterface, void *pstudio, float (*rotationmatrix)[3][4], float (*bonetransform)[128][3][4]) {
// this function synchronizes the studio model animation blending interface (i.e, what parts
// of the body move, which bones, which hitboxes and how) between the server and the game DLL.
// some MODs can be using a different hitbox scheme than the standard one.
auto api_GetBlendingInterface = game.getLib ().resolve <int (*) (int, void **, void *, float(*)[3][4], float(*)[128][3][4])> ("Server_GetBlendingInterface");
auto api_GetBlendingInterface = game.lib ().resolve <int (*) (int, void **, void *, float(*)[3][4], float(*)[128][3][4])> (__FUNCTION__);
if (api_GetBlendingInterface == nullptr) {
if (!api_GetBlendingInterface) {
logger.error ("Could not resolve symbol \"%s\" in the game dll. Continuing...", __FUNCTION__);
return FALSE;
}
return api_GetBlendingInterface (version, ppinterface, pstudio, rotationmatrix, bonetransform);
}
SHARED_LIBRARAY_EXPORT int Meta_Query (char *, plugin_info_t **pPlugInfo, mutil_funcs_t *pMetaUtilFuncs) {
CR_EXPORT int Meta_Query (char *, plugin_info_t **pPlugInfo, mutil_funcs_t *pMetaUtilFuncs) {
// this function is the first function ever called by metamod in the plugin DLL. Its purpose
// is for metamod to retrieve basic information about the plugin, such as its meta-interface
// version, for ensuring compatibility with the current version of the running metamod.
@ -929,7 +928,7 @@ SHARED_LIBRARAY_EXPORT int Meta_Query (char *, plugin_info_t **pPlugInfo, mutil_
return TRUE; // tell metamod this plugin looks safe
}
SHARED_LIBRARAY_EXPORT int Meta_Attach (PLUG_LOADTIME, metamod_funcs_t *functionTable, meta_globals_t *pMGlobals, gamedll_funcs_t *pGamedllFuncs) {
CR_EXPORT int Meta_Attach (PLUG_LOADTIME, metamod_funcs_t *functionTable, meta_globals_t *pMGlobals, gamedll_funcs_t *pGamedllFuncs) {
// this function is called when metamod attempts to load the plugin. Since it's the place
// where we can tell if the plugin will be allowed to run or not, we wait until here to make
// our initialization stuff, like registering CVARs and dedicated server commands.
@ -943,7 +942,7 @@ SHARED_LIBRARAY_EXPORT int Meta_Attach (PLUG_LOADTIME, metamod_funcs_t *function
nullptr, // pfnGetNewDLLFunctions ()
nullptr, // pfnGetNewDLLFunctions_Post ()
GetEngineFunctions, // pfnGetEngineFunctions ()
GetEngineFunctions_Post, // pfnGetEngineFunctions_Post ()
nullptr, // pfnGetEngineFunctions_Post ()
};
// keep track of the pointers to engine function tables metamod gives us
@ -954,23 +953,44 @@ SHARED_LIBRARAY_EXPORT int Meta_Attach (PLUG_LOADTIME, metamod_funcs_t *function
return TRUE; // returning true enables metamod to attach this plugin
}
SHARED_LIBRARAY_EXPORT int Meta_Detach (PLUG_LOADTIME, PL_UNLOAD_REASON) {
CR_EXPORT int Meta_Detach (PLUG_LOADTIME, PL_UNLOAD_REASON) {
// this function is called when metamod unloads the plugin. A basic check is made in order
// to prevent unloading the plugin if its processing should not be interrupted.
bots.kickEveryone (true); // kick all bots off this server
waypoints.init ();
// save collected experience on shutdown
graph.savePractice ();
return TRUE;
}
SHARED_LIBRARAY_EXPORT void Meta_Init (void) {
CR_EXPORT void Meta_Init () {
// this function is called by metamod, before any other interface functions. Purpose of this
// function to give plugin a chance to determine is plugin running under metamod or not.
game.addGameFlag (GAME_METAMOD);
game.addGameFlag (GameFlags::Metamod);
}
// games GiveFnptrsToDll is a bit tricky
#if defined(CR_WINDOWS)
# if defined(CR_CXX_MSVC) || defined (CR_CXX_MSVC)
# if defined (CR_ARCH_X86)
# pragma comment(linker, "/EXPORT:GiveFnptrsToDll=_GiveFnptrsToDll@8,@1")
# endif
# pragma comment(linker, "/SECTION:.data,RW")
# endif
# define DLL_STDCALL __stdcall
# if defined(CR_CXX_MSVC) && !defined(CR_ARCH_X64)
# define DLL_GIVEFNPTRSTODLL extern "C" void DLL_STDCALL
# elif defined(CR_CXX_CLANG) || defined(CR_ARCH_X64)
# define DLL_GIVEFNPTRSTODLL CR_EXPORT void DLL_STDCALL
# endif
#elif defined(CR_LINUX) || defined (CR_OSX) || defined (CR_ANDROID)
# define DLL_GIVEFNPTRSTODLL CR_EXPORT void
# define DLL_STDCALL
#endif
DLL_GIVEFNPTRSTODLL GiveFnptrsToDll (enginefuncs_t *functionTable, globalvars_t *pGlobals) {
// this is the very first function that is called in the game DLL by the game. Its purpose
// is to set the functions interfacing up, by exchanging the functionTable function list
@ -990,30 +1010,22 @@ DLL_GIVEFNPTRSTODLL GiveFnptrsToDll (enginefuncs_t *functionTable, globalvars_t
if (game.postload ()) {
return;
}
auto api_GiveFnptrsToDll = game.getLib ().resolve <void (STD_CALL *) (enginefuncs_t *, globalvars_t *)> ("GiveFnptrsToDll");
assert (api_GiveFnptrsToDll != nullptr);
auto api_GiveFnptrsToDll = game.lib ().resolve <void (DLL_STDCALL *) (enginefuncs_t *, globalvars_t *)> (__FUNCTION__);
if (!api_GiveFnptrsToDll) {
logger.fatal ("Could not resolve symbol \"%s\" in the game dll.", __FUNCTION__);
}
GetEngineFunctions (functionTable, nullptr);
// give the engine functions to the other DLL...
api_GiveFnptrsToDll (functionTable, pGlobals);
}
DLL_ENTRYPOINT {
// dynamic library entry point, can be used for uninitialization stuff. NOT for initializing
// anything because if you ever attempt to wander outside the scope of this function on a
// DLL attach, LoadLibrary() will simply fail. And you can't do I/Os here either.
// dynamic library detaching ??
if (DLL_DETACHING) {
waypoints.init (); // free everything that's freeable
if (api_GiveFnptrsToDll) {
api_GiveFnptrsToDll (functionTable, pGlobals);
}
DLL_RETENTRY; // the return data type is OS specific too
}
void helper_LinkEntity (EntityFunction &addr, const char *name, entvars_t *pev) {
if (addr == nullptr) {
addr = game.getLib ().resolve <EntityFunction> (name);
addr = game.lib ().resolve <EntityFunction> (name);
}
if (addr == nullptr) {
@ -1022,10 +1034,10 @@ void helper_LinkEntity (EntityFunction &addr, const char *name, entvars_t *pev)
addr (pev);
}
#define LINK_ENTITY(entityName) \
SHARED_LIBRARAY_EXPORT void entityName (entvars_t *pev) { \
static EntityFunction addr; \
helper_LinkEntity (addr, #entityName, pev); \
#define LINK_ENTITY(entityName) \
CR_EXPORT void entityName (entvars_t *pev) { \
static EntityFunction addr; \
helper_LinkEntity (addr, #entityName, pev); \
}
// entities in counter-strike...

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -10,7 +10,7 @@
ConVar yb_display_welcome_text ("yb_display_welcome_text", "1");
BotUtils::BotUtils (void) {
BotUtils::BotUtils () {
m_needToSendWelcome = false;
m_welcomeReceiveTime = 0.0f;
@ -32,47 +32,30 @@ BotUtils::BotUtils (void) {
m_sentences.push ("attention, expect experimental armed hostile presence");
m_sentences.push ("warning, medical attention required");
m_tags.push ({ "[[", "]]" });
m_tags.push ({ "-=", "=-" });
m_tags.push ({ "-[", "]-" });
m_tags.push ({ "-]", "[-" });
m_tags.push ({ "-}", "{-" });
m_tags.push ({ "-{", "}-" });
m_tags.push ({ "<[", "]>" });
m_tags.push ({ "<]", "[>" });
m_tags.push ({ "[-", "-]" });
m_tags.push ({ "]-", "-[" });
m_tags.push ({ "{-", "-}" });
m_tags.push ({ "}-", "-{" });
m_tags.push ({ "[", "]" });
m_tags.push ({ "{", "}" });
m_tags.push ({ "<", "[" });
m_tags.push ({ ">", "<" });
m_tags.push ({ "-", "-" });
m_tags.push ({ "|", "|" });
m_tags.push ({ "=", "=" });
m_tags.push ({ "+", "+" });
m_tags.push ({ "(", ")" });
m_tags.push ({ ")", "(" });
m_tags.emplace ("[[", "]]");
m_tags.emplace ("-=", "=-");
m_tags.emplace ("-[", "]-");
m_tags.emplace ("-]", "[-");
m_tags.emplace ("-}", "{-");
m_tags.emplace ("-{", "}-");
m_tags.emplace ("<[", "]>");
m_tags.emplace ("<]", "[>");
m_tags.emplace ("[-", "-]");
m_tags.emplace ("]-", "-[");
m_tags.emplace ("{-", "-}");
m_tags.emplace ("}-", "-{");
m_tags.emplace ("[", "]");
m_tags.emplace ("{", "}");
m_tags.emplace ("<", "[");
m_tags.emplace (">", "<");
m_tags.emplace ("-", "-");
m_tags.emplace ("|", "|");
m_tags.emplace ("=", "=");
m_tags.emplace ("+", "+");
m_tags.emplace ("(", ")");
m_tags.emplace (")", "(");
m_clients.resize (MAX_ENGINE_PLAYERS + 1);
}
const char *BotUtils::format (const char *format, ...) {
static char strBuffer[2][MAX_PRINT_BUFFER];
static int rotator = 0;
if (format == nullptr) {
return strBuffer[rotator];
}
static char *ptr = strBuffer[rotator ^= 1];
va_list ap;
va_start (ap, format);
vsnprintf (ptr, MAX_PRINT_BUFFER - 1, format, ap);
va_end (ap);
return ptr;
m_clients.resize (kGameMaxPlayers + 1);
}
bool BotUtils::isAlive (edict_t *ent) {
@ -90,7 +73,7 @@ float BotUtils::getShootingCone (edict_t *ent, const Vector &position) {
}
bool BotUtils::isInViewCone (const Vector &origin, edict_t *ent) {
return getShootingCone (ent, origin) >= cr::cosf (cr::deg2rad ((ent->v.fov > 0 ? ent->v.fov : 90.0f) * 0.5f));
return getShootingCone (ent, origin) >= cr::cosf (cr::degreesToRadians ((ent->v.fov > 0 ? ent->v.fov : 90.0f) * 0.5f));
}
bool BotUtils::isVisible (const Vector &origin, edict_t *ent) {
@ -98,7 +81,7 @@ bool BotUtils::isVisible (const Vector &origin, edict_t *ent) {
return false;
}
TraceResult tr;
game.testLine (ent->v.origin + ent->v.view_ofs, origin, TRACE_IGNORE_EVERYTHING, ent, &tr);
game.testLine (ent->v.origin + ent->v.view_ofs, origin, TraceIgnore::Everything, ent, &tr);
if (tr.flFraction != 1.0f) {
return false;
@ -109,13 +92,10 @@ bool BotUtils::isVisible (const Vector &origin, edict_t *ent) {
void BotUtils::traceDecals (entvars_t *pev, TraceResult *trace, int logotypeIndex) {
// this function draw spraypaint depending on the tracing results.
static StringArray logotypes;
auto logo = conf.getRandomLogoName (logotypeIndex);
if (logotypes.empty ()) {
logotypes = String ("{biohaz;{graf003;{graf004;{graf005;{lambda06;{target;{hand1;{spit2;{bloodhand6;{foot_l;{foot_r").split (";");
}
int entityIndex = -1, message = TE_DECAL;
int decalIndex = engfuncs.pfnDecalIndex (logotypes[logotypeIndex].chars ());
int decalIndex = engfuncs.pfnDecalIndex (logo.chars ());
if (decalIndex < 0) {
decalIndex = engfuncs.pfnDecalIndex ("{lambda06");
@ -151,7 +131,7 @@ void BotUtils::traceDecals (entvars_t *pev, TraceResult *trace, int logotypeInde
}
}
if (logotypes[logotypeIndex].contains ("{")) {
if (logo.startsWith ("{")) {
MessageWriter (MSG_BROADCAST, SVC_TEMPENTITY)
.writeByte (TE_PLAYERDECAL)
.writeByte (game.indexOfEntity (pev->pContainingEntity))
@ -187,14 +167,14 @@ bool BotUtils::isPlayer (edict_t *ent) {
return false;
}
if ((ent->v.flags & (FL_CLIENT | FL_FAKECLIENT)) || bots.getBot (ent) != nullptr) {
if ((ent->v.flags & (FL_CLIENT | FL_FAKECLIENT)) || bots[ent] != nullptr) {
return !isEmptyStr (STRING (ent->v.netname));
}
return false;
}
bool BotUtils::isPlayerVIP (edict_t *ent) {
if (!game.mapIs (MAP_AS)) {
if (!game.mapIs (MapFlags::Assassination)) {
return false;
}
@ -205,14 +185,14 @@ bool BotUtils::isPlayerVIP (edict_t *ent) {
}
bool BotUtils::isFakeClient (edict_t *ent) {
if (bots.getBot (ent) != nullptr || (!game.isNullEntity (ent) && (ent->v.flags & FL_FAKECLIENT))) {
if (bots[ent] != nullptr || (!game.isNullEntity (ent) && (ent->v.flags & FL_FAKECLIENT))) {
return true;
}
return false;
}
bool BotUtils::openConfig (const char *fileName, const char *errorIfNotExists, MemFile *outFile, bool languageDependant /*= false*/) {
if (outFile->isValid ()) {
if (*outFile) {
outFile->close ();
}
@ -225,165 +205,75 @@ bool BotUtils::openConfig (const char *fileName, const char *errorIfNotExists, M
if (strcmp (fileName, "lang.cfg") == 0 && strcmp (yb_language.str (), "en") == 0) {
return false;
}
const char *langConfig = format ("%s/lang/%s_%s", configDir, yb_language.str (), fileName);
// check file existence
int size = 0;
uint8 *buffer = nullptr;
auto langConfig = strings.format ("%s/lang/%s_%s", configDir, yb_language.str (), fileName);
// check is file is exists for this language
if ((buffer = MemoryLoader::ref ().load (langConfig, &size)) != nullptr) {
MemoryLoader::ref ().unload (buffer);
// unload and reopen file using MemoryFile
outFile->open (langConfig);
}
else {
outFile->open (format ("%s/lang/en_%s", configDir, fileName));
if (!outFile->open (langConfig)) {
outFile->open (strings.format ("%s/lang/en_%s", configDir, fileName));
}
}
else {
outFile->open (format ("%s/%s", configDir, fileName));
outFile->open (strings.format ("%s/%s", configDir, fileName));
}
if (!outFile->isValid ()) {
logEntry (true, LL_ERROR, errorIfNotExists);
if (!*outFile) {
logger.error (errorIfNotExists);
return false;
}
return true;
}
void BotUtils::checkWelcome (void) {
void BotUtils::checkWelcome () {
// the purpose of this function, is to send quick welcome message, to the listenserver entity.
if (game.isDedicated () || !yb_display_welcome_text.boolean () || !m_needToSendWelcome) {
if (game.isDedicated () || !yb_display_welcome_text.bool_ () || !m_needToSendWelcome) {
return;
}
m_welcomeReceiveTime = 0.0f;
if (game.is (GAME_LEGACY)) {
m_needToSendWelcome = true;
return;
}
bool needToSendMsg = (waypoints.length () > 0 ? m_needToSendWelcome : true);
if (isAlive (game.getLocalEntity ()) && m_welcomeReceiveTime < 1.0 && needToSendMsg) {
bool needToSendMsg = (graph.length () > 0 ? m_needToSendWelcome : true);
auto receiveEntity = game.getLocalEntity ();
if (isAlive (receiveEntity) && m_welcomeReceiveTime < 1.0 && needToSendMsg) {
m_welcomeReceiveTime = game.timebase () + 4.0f; // receive welcome message in four seconds after game has commencing
}
if (m_welcomeReceiveTime > 0.0f && needToSendMsg) {
if (!game.is (GAME_MOBILITY | GAME_XASH_ENGINE)) {
game.execCmd ("speak \"%s\"", m_sentences.random ().chars ());
}
game.chatPrint ("----- %s v%s (Build: %u), {%s}, (c) %s, by %s (%s)-----", PRODUCT_SHORT_NAME, PRODUCT_VERSION, buildNumber (), PRODUCT_DATE, PRODUCT_END_YEAR, PRODUCT_AUTHOR, PRODUCT_URL);
MessageWriter (MSG_ONE, SVC_TEMPENTITY, Vector::null (), game.getLocalEntity ())
if (m_welcomeReceiveTime > 0.0f && needToSendMsg) {
if (!game.is (GameFlags::Mobility | GameFlags::Xash3D)) {
game.serverCommand ("speak \"%s\"", m_sentences.random ().chars ());
}
MessageWriter (MSG_ONE, game.getMessageId (NetMsg::TextMsg), nullvec, receiveEntity)
.writeByte (HUD_PRINTTALK)
.writeString (strings.format ("----- %s v%s (Build: %u), {%s}, (c) %s, by %s (%s)-----", PRODUCT_SHORT_NAME, PRODUCT_VERSION, buildNumber (), PRODUCT_DATE, PRODUCT_END_YEAR, PRODUCT_AUTHOR, PRODUCT_URL));
MessageWriter (MSG_ONE, SVC_TEMPENTITY, nullvec, receiveEntity)
.writeByte (TE_TEXTMESSAGE)
.writeByte (1)
.writeShort (MessageWriter::fs16 (-1, 1 << 13))
.writeShort (MessageWriter::fs16 (-1, 1 << 13))
.writeShort (MessageWriter::fs16 (-1.0f, 13.0f))
.writeShort (MessageWriter::fs16 (-1.0f, 13.0f))
.writeByte (2)
.writeByte (rng.getInt (33, 255))
.writeByte (rng.getInt (33, 255))
.writeByte (rng.getInt (33, 255))
.writeByte (rg.int_ (33, 255))
.writeByte (rg.int_ (33, 255))
.writeByte (rg.int_ (33, 255))
.writeByte (0)
.writeByte (rng.getInt (230, 255))
.writeByte (rng.getInt (230, 255))
.writeByte (rng.getInt (230, 255))
.writeByte (rg.int_ (230, 255))
.writeByte (rg.int_ (230, 255))
.writeByte (rg.int_ (230, 255))
.writeByte (200)
.writeShort (MessageWriter::fu16 (0.0078125f, 1 << 8))
.writeShort (MessageWriter::fu16 (2.0f, 1 << 8))
.writeShort (MessageWriter::fu16 (6.0f, 1 << 8))
.writeShort (MessageWriter::fu16 (0.1f, 1 << 8))
.writeString (format ("\nServer is running %s v%s (Build: %u)\nDeveloped by %s\n\n%s", PRODUCT_SHORT_NAME, PRODUCT_VERSION, buildNumber (), PRODUCT_AUTHOR, waypoints.getAuthor ()));
.writeShort (MessageWriter::fu16 (0.0078125f, 8.0f))
.writeShort (MessageWriter::fu16 (2.0f, 8.0f))
.writeShort (MessageWriter::fu16 (6.0f, 8.0f))
.writeShort (MessageWriter::fu16 (0.1f, 8.0f))
.writeString (strings.format ("\nServer is running %s v%s (Build: %u)\nDeveloped by %s\n\n%s", PRODUCT_SHORT_NAME, PRODUCT_VERSION, buildNumber (), PRODUCT_AUTHOR, graph.getAuthor ()));
m_welcomeReceiveTime = 0.0f;
m_needToSendWelcome = false;
}
}
void BotUtils::logEntry (bool outputToConsole, int logLevel, const char *format, ...) {
// this function logs a message to the message log file root directory.
va_list ap;
char buffer[MAX_PRINT_BUFFER] = { 0, }, levelString[32] = { 0, };
va_start (ap, format);
vsnprintf (buffer, cr::bufsize (buffer), format, ap);
va_end (ap);
switch (logLevel) {
case LL_DEFAULT:
strcpy (levelString, "LOG: ");
break;
case LL_WARNING:
strcpy (levelString, "WARN: ");
break;
case LL_ERROR:
strcpy (levelString, "ERROR: ");
break;
case LL_FATAL:
strcpy (levelString, "FATAL: ");
break;
}
if (outputToConsole) {
game.print ("%s%s", levelString, buffer);
}
// now check if logging disabled
if (!(logLevel & LL_IGNORE)) {
extern ConVar yb_debug;
if (logLevel == LL_DEFAULT && yb_debug.integer () < 3) {
return; // no log, default logging is disabled
}
if (logLevel == LL_WARNING && yb_debug.integer () < 2) {
return; // no log, warning logging is disabled
}
if (logLevel == LL_ERROR && yb_debug.integer () < 1) {
return; // no log, error logging is disabled
}
}
// open file in a standard stream
File fp ("yapb.txt", "at");
// check if we got a valid handle
if (!fp.isValid ()) {
return;
}
time_t tickTime = time (&tickTime);
tm *time = localtime (&tickTime);
fp.writeFormat ("%02d:%02d:%02d --> %s%s\n", time->tm_hour, time->tm_min, time->tm_sec, levelString, buffer);
fp.close ();
if (logLevel == LL_FATAL) {
bots.kickEveryone (true);
waypoints.init ();
#if defined(PLATFORM_WIN32)
DestroyWindow (GetForegroundWindow ());
MessageBoxA (GetActiveWindow (), buffer, "YaPB Error", MB_ICONSTOP);
#else
printf ("%s\n", buffer);
#endif
#if defined(PLATFORM_WIN32)
_exit (1);
#else
exit (1);
#endif
}
}
bool BotUtils::findNearestPlayer (void **pvHolder, edict_t *to, float searchDistance, bool sameTeam, bool needBot, bool needAlive, bool needDrawn, bool needBotWithC4) {
// this function finds nearest to to, player with set of parameters, like his
// team, live status, search distance etc. if needBot is true, then pvHolder, will
@ -395,11 +285,11 @@ bool BotUtils::findNearestPlayer (void **pvHolder, edict_t *to, float searchDist
int toTeam = game.getTeam (to);
for (const auto &client : m_clients) {
if (!(client.flags & CF_USED) || client.ent == to) {
if (!(client.flags & ClientFlags::Used) || client.ent == to) {
continue;
}
if ((sameTeam && client.team != toTeam) || (needAlive && !(client.flags & CF_ALIVE)) || (needBot && !isFakeClient (client.ent)) || (needDrawn && (client.ent->v.effects & EF_NODRAW)) || (needBotWithC4 && (client.ent->v.weapons & WEAPON_C4))) {
if ((sameTeam && client.team != toTeam) || (needAlive && !(client.flags & ClientFlags::Alive)) || (needBot && !isFakeClient (client.ent)) || (needDrawn && (client.ent->v.effects & EF_NODRAW)) || (needBotWithC4 && (client.ent->v.weapons & Weapon::C4))) {
continue; // filter players with parameters
}
float distance = (client.ent->v.origin - to->v.origin).length ();
@ -416,7 +306,7 @@ bool BotUtils::findNearestPlayer (void **pvHolder, edict_t *to, float searchDist
// fill the holder
if (needBot) {
*pvHolder = reinterpret_cast <void *> (bots.getBot (survive));
*pvHolder = reinterpret_cast <void *> (bots[survive]);
}
else {
*pvHolder = reinterpret_cast <void *> (survive);
@ -436,16 +326,16 @@ void BotUtils::attachSoundsToClients (edict_t *ent, const char *sample, float vo
if (origin.empty ()) {
return;
}
int index = game.indexOfEntity (ent) - 1;
int index = game.indexOfPlayer (ent);
if (index < 0 || index >= game.maxClients ()) {
float nearestDistance = 99999.0f;
// loop through all players
for (int i = 0; i < game.maxClients (); i++) {
for (int i = 0; i < game.maxClients (); ++i) {
const Client &client = m_clients[i];
if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE)) {
if (!(client.flags & ClientFlags::Used) || !(client.flags & ClientFlags::Alive)) {
continue;
}
float distance = (client.origin - origin).length ();
@ -545,9 +435,9 @@ void BotUtils::simulateSoundUpdates (int playerIndex) {
else {
extern ConVar mp_footsteps;
if (mp_footsteps.boolean ()) {
if (mp_footsteps.bool_ ()) {
// moves fast enough?
hearDistance = 1280.0f * (client.ent->v.velocity.length2D () / 260.0f);
hearDistance = 1280.0f * (client.ent->v.velocity.length2d () / 260.0f);
timeSound = game.timebase () + 0.3f;
}
}
@ -573,36 +463,129 @@ void BotUtils::simulateSoundUpdates (int playerIndex) {
}
}
void BotUtils::updateClients (void) {
void BotUtils::updateClients () {
// record some stats of all players on the server
for (int i = 0; i < game.maxClients (); i++) {
edict_t *player = game.entityOfIndex (i + 1);
for (int i = 0; i < game.maxClients (); ++i) {
edict_t *player = game.playerOfIndex (i);
Client &client = m_clients[i];
if (!game.isNullEntity (player) && (player->v.flags & FL_CLIENT)) {
client.ent = player;
client.flags |= CF_USED;
client.flags |= ClientFlags::Used;
if (util.isAlive (player)) {
client.flags |= CF_ALIVE;
client.flags |= ClientFlags::Alive;
}
else {
client.flags &= ~CF_ALIVE;
client.flags &= ~ClientFlags::Alive;
}
if (client.flags & CF_ALIVE) {
if (client.flags & ClientFlags::Alive) {
client.origin = player->v.origin;
simulateSoundUpdates (i);
}
}
else {
client.flags &= ~(CF_USED | CF_ALIVE);
client.flags &= ~(ClientFlags::Used | ClientFlags::Alive);
client.ent = nullptr;
}
}
}
int BotUtils::buildNumber (void) {
int BotUtils::getPingBitmask (edict_t *ent, int loss, int ping) {
// this function generats bitmask for SVC_PINGS engine message. See SV_EmitPings from engine for details
const auto emit = [] (int s0, int s1, int s2) {
return (s0 & (cr::bit (s1) - 1)) << s2;
};
return emit (loss, 7, 18) | emit (ping, 12, 6) | emit (game.indexOfPlayer (ent), 5, 1) | 1;
}
void BotUtils::calculatePings () {
if (!game.is (GameFlags::HasFakePings) || yb_show_latency.int_ () != 2) {
return;
}
Twin <int, int> average { 0, 0 };
int numHumans = 0;
// first get average ping on server, and store real client pings
for (auto &client : m_clients) {
if (!(client.flags & ClientFlags::Used) || isFakeClient (client.ent)) {
continue;
}
int ping, loss;
engfuncs.pfnGetPlayerStats (client.ent, &ping, &loss);
// store normal client ping
client.ping = getPingBitmask (client.ent, loss, ping > 0 ? ping / 2 : rg.int_ (8, 16)); // getting player ping sometimes fails
client.pingUpdate = true; // force resend ping
numHumans++;
average.first += ping;
average.second += loss;
}
if (numHumans > 0) {
average.first /= numHumans;
average.second /= numHumans;
}
else {
average.first = rg.int_ (30, 40);
average.second = rg.int_ (5, 10);
}
// now calculate bot ping based on average from players
for (auto &client : m_clients) {
if (!(client.flags & ClientFlags::Used)) {
continue;
}
auto bot = bots[client.ent];
// we're only intrested in bots here
if (!bot) {
continue;
}
int part = static_cast <int> (average.first * 0.2f);
int botPing = bot->m_basePing + rg.int_ (average.first - part, average.first + part) + rg.int_ (bot->m_difficulty / 2, bot->m_difficulty);
int botLoss = rg.int_ (average.second / 2, average.second);
client.ping = getPingBitmask (client.ent, botLoss, botPing);
client.pingUpdate = true; // force resend ping
}
}
void BotUtils::sendPings (edict_t *to) {
MessageWriter msg;
// missing from sdk
constexpr int kGamePingSVC = 17;
for (auto &client : m_clients) {
if (!(client.flags & ClientFlags::Used)) {
continue;
}
if (!client.pingUpdate) {
continue;
}
client.pingUpdate = false;
// no ping, no fun
if (!client.ping) {
client.ping = getPingBitmask (client.ent, rg.int_ (5, 10), rg.int_ (15, 40));
}
msg.start (MSG_ONE_UNRELIABLE, kGamePingSVC, nullvec, to)
.writeLong (client.ping)
.end ();
}
return;
}
int BotUtils::buildNumber () {
// this function generates build number from the compiler date macros
static int buildNumber = 0;
@ -624,7 +607,7 @@ int BotUtils::buildNumber (void) {
int i = 0;
// go through all months, and calculate, days since year start
for (i = 0; i < 11; i++) {
for (i = 0; i < 11; ++i) {
if (strncmp (&date[0], months[i], 3) == 0) {
break; // found current month break
}
@ -655,38 +638,38 @@ int BotUtils::getWeaponAlias (bool needString, const char *weaponAlias, int weap
// weapon enumeration
WeaponTab_t weaponTab[] = {
{WEAPON_USP, "usp"}, // HK USP .45 Tactical
{WEAPON_GLOCK, "glock"}, // Glock18 Select Fire
{WEAPON_DEAGLE, "deagle"}, // Desert Eagle .50AE
{WEAPON_P228, "p228"}, // SIG P228
{WEAPON_ELITE, "elite"}, // Dual Beretta 96G Elite
{WEAPON_FIVESEVEN, "fn57"}, // FN Five-Seven
{WEAPON_M3, "m3"}, // Benelli M3 Super90
{WEAPON_XM1014, "xm1014"}, // Benelli XM1014
{WEAPON_MP5, "mp5"}, // HK MP5-Navy
{WEAPON_TMP, "tmp"}, // Steyr Tactical Machine Pistol
{WEAPON_P90, "p90"}, // FN P90
{WEAPON_MAC10, "mac10"}, // Ingram MAC-10
{WEAPON_UMP45, "ump45"}, // HK UMP45
{WEAPON_AK47, "ak47"}, // Automat Kalashnikov AK-47
{WEAPON_GALIL, "galil"}, // IMI Galil
{WEAPON_FAMAS, "famas"}, // GIAT FAMAS
{WEAPON_SG552, "sg552"}, // Sig SG-552 Commando
{WEAPON_M4A1, "m4a1"}, // Colt M4A1 Carbine
{WEAPON_AUG, "aug"}, // Steyr Aug
{WEAPON_SCOUT, "scout"}, // Steyr Scout
{WEAPON_AWP, "awp"}, // AI Arctic Warfare/Magnum
{WEAPON_G3SG1, "g3sg1"}, // HK G3/SG-1 Sniper Rifle
{WEAPON_SG550, "sg550"}, // Sig SG-550 Sniper
{WEAPON_M249, "m249"}, // FN M249 Para
{WEAPON_FLASHBANG, "flash"}, // Concussion Grenade
{WEAPON_EXPLOSIVE, "hegren"}, // High-Explosive Grenade
{WEAPON_SMOKE, "sgren"}, // Smoke Grenade
{WEAPON_ARMOR, "vest"}, // Kevlar Vest
{WEAPON_ARMORHELM, "vesthelm"}, // Kevlar Vest and Helmet
{WEAPON_DEFUSER, "defuser"}, // Defuser Kit
{WEAPON_SHIELD, "shield"}, // Tactical Shield
{WEAPON_KNIFE, "knife"} // Knife
{Weapon::USP, "usp"}, // HK USP .45 Tactical
{Weapon::Glock18, "glock"}, // Glock18 Select Fire
{Weapon::Deagle, "deagle"}, // Desert Eagle .50AE
{Weapon::P228, "p228"}, // SIG P228
{Weapon::Elite, "elite"}, // Dual Beretta 96G Elite
{Weapon::FiveSeven, "fn57"}, // FN Five-Seven
{Weapon::M3, "m3"}, // Benelli M3 Super90
{Weapon::XM1014, "xm1014"}, // Benelli XM1014
{Weapon::MP5, "mp5"}, // HK MP5-Navy
{Weapon::TMP, "tmp"}, // Steyr Tactical Machine Pistol
{Weapon::P90, "p90"}, // FN P90
{Weapon::MAC10, "mac10"}, // Ingram MAC-10
{Weapon::UMP45, "ump45"}, // HK UMP45
{Weapon::AK47, "ak47"}, // Automat Kalashnikov AK-47
{Weapon::Galil, "galil"}, // IMI Galil
{Weapon::Famas, "famas"}, // GIAT FAMAS
{Weapon::SG552, "sg552"}, // Sig SG-552 Commando
{Weapon::M4A1, "m4a1"}, // Colt M4A1 Carbine
{Weapon::AUG, "aug"}, // Steyr Aug
{Weapon::Scout, "scout"}, // Steyr Scout
{Weapon::AWP, "awp"}, // AI Arctic Warfare/Magnum
{Weapon::G3SG1, "g3sg1"}, // HK G3/SG-1 Sniper Rifle
{Weapon::SG550, "sg550"}, // Sig SG-550 Sniper
{Weapon::M249, "m249"}, // FN M249 Para
{Weapon::Flashbang, "flash"}, // Concussion Grenade
{Weapon::Explosive, "hegren"}, // High-Explosive Grenade
{Weapon::Smoke, "sgren"}, // Smoke Grenade
{Weapon::Armor, "vest"}, // Kevlar Vest
{Weapon::ArmorHelm, "vesthelm"}, // Kevlar Vest and Helmet
{Weapon::Defuser, "defuser"}, // Defuser Kit
{Weapon::Shield, "shield"}, // Tactical Shield
{Weapon::Knife, "knife"} // Knife
};
// if we need to return the string, find by weapon id

File diff suppressed because it is too large Load diff