diff --git a/ext/crlib b/ext/crlib index fdeb120..11e1a6d 160000 --- a/ext/crlib +++ b/ext/crlib @@ -1 +1 @@ -Subproject commit fdeb120576a13457867e720d49bfd2969e10d195 +Subproject commit 11e1a6d4c09b16ceda1f5a9d362fe35bec3f0b85 diff --git a/inc/control.h b/inc/control.h index b404160..b7b8fdb 100644 --- a/inc/control.h +++ b/inc/control.h @@ -231,8 +231,14 @@ public: void collectArgs () { m_args.clear (); - for (int i = 0; i < engfuncs.pfnCmd_Argc (); ++i) { - m_args.emplace (String (engfuncs.pfnCmd_Argv (i)).lowercase ()); + for (auto i = 0; i < engfuncs.pfnCmd_Argc (); ++i) { + String arg = engfuncs.pfnCmd_Argv (i); + + // only make case-insensetive command itself and first argument + if (i < 2) { + arg = arg.lowercase (); + } + m_args.emplace (arg); } } diff --git a/inc/engine.h b/inc/engine.h index eb0a79b..582954b 100644 --- a/inc/engine.h +++ b/inc/engine.h @@ -316,27 +316,27 @@ public: } // gets edict pointer out of entity index - edict_t *entityOfIndex (const int index) { + CR_FORCE_INLINE edict_t *entityOfIndex (const int index) { return static_cast (m_startEntity + index); }; // gets edict pointer out of entity index (player) - edict_t *playerOfIndex (const int index) { + CR_FORCE_INLINE edict_t *playerOfIndex (const int index) { return entityOfIndex (index) + 1; }; // gets edict index out of it's pointer - int indexOfEntity (const edict_t *ent) { + CR_FORCE_INLINE int indexOfEntity (const edict_t *ent) { return static_cast (ent - m_startEntity); }; // gets edict index of it's pointer (player) - int indexOfPlayer (const edict_t *ent) { + CR_FORCE_INLINE int indexOfPlayer (const edict_t *ent) { return indexOfEntity (ent) - 1; } // verify entity isn't null - bool isNullEntity (const edict_t *ent) { + CR_FORCE_INLINE bool isNullEntity (const edict_t *ent) { return !ent || !indexOfEntity (ent) || ent->free; } diff --git a/inc/fakeping.h b/inc/fakeping.h index be60c79..3d771ee 100644 --- a/inc/fakeping.h +++ b/inc/fakeping.h @@ -82,6 +82,7 @@ private: private: CountdownTimer m_recalcTime {}; + PingBitMsg m_pbm {}; public: explicit BotFakePingManager () = default; diff --git a/inc/graph.h b/inc/graph.h index fbba7d4..4d2f283 100644 --- a/inc/graph.h +++ b/inc/graph.h @@ -189,11 +189,12 @@ public: SmallArray m_paths {}; HashMap , EmptyHash > m_hashTable {}; - String m_graphAuthor {}; - String m_graphModified {}; - - ExtenHeader m_extenHeader {}; - StorageHeader m_graphHeader {}; + struct GraphInfo { + String author {}; + String modified {}; + ExtenHeader exten {}; + StorageHeader header {}; + } m_info {}; edict_t *m_editor {}; @@ -270,11 +271,11 @@ public: public: StringRef getAuthor () const { - return m_graphAuthor; + return m_info.author; } StringRef getModifiedBy () const { - return m_graphModified; + return m_info.modified; } bool hasChanged () const { @@ -339,12 +340,12 @@ public: // set exten header from binary storage void setExtenHeader (ExtenHeader *hdr) { - memcpy (&m_extenHeader, hdr, sizeof (ExtenHeader)); + memcpy (&m_info.exten, hdr, sizeof (ExtenHeader)); } // set graph header from binary storage void setGraphHeader (StorageHeader *hdr) { - memcpy (&m_graphHeader, hdr, sizeof (StorageHeader)); + memcpy (&m_info.header, hdr, sizeof (StorageHeader)); } // gets the node numbers diff --git a/inc/hooks.h b/inc/hooks.h index fb76a18..a65396c 100644 --- a/inc/hooks.h +++ b/inc/hooks.h @@ -105,13 +105,13 @@ public: } public: - static int32_t CR_STDCALL sendTo (int socket, const void *message, size_t length, int flags, const struct sockaddr *dest, int destLength); + CR_FORCE_STACK_ALIGN static int32_t CR_STDCALL sendTo (int socket, const void *message, size_t length, int flags, const struct sockaddr *dest, int destLength); }; // used for transit calls between game dll and engine without all needed functions on bot side class DynamicLinkerHook : public Singleton { private: -#if defined (CR_WINDOWS) +#if defined(CR_WINDOWS) # define DLSYM_FUNCTION GetProcAddress # define DLCLOSE_FUNCTION FreeLibrary #else @@ -177,11 +177,11 @@ public: } public: - static SharedLibrary::Func CR_STDCALL lookupHandler (SharedLibrary::Handle module, const char *function) { + CR_FORCE_STACK_ALIGN static SharedLibrary::Func CR_STDCALL lookupHandler (SharedLibrary::Handle module, const char *function) { return instance ().lookup (module, function); } - static int CR_STDCALL closeHandler (SharedLibrary::Handle module) { + CR_FORCE_STACK_ALIGN static int CR_STDCALL closeHandler (SharedLibrary::Handle module) { return instance ().close (module); } }; diff --git a/inc/manager.h b/inc/manager.h index 34b41c4..437ec74 100644 --- a/inc/manager.h +++ b/inc/manager.h @@ -117,7 +117,7 @@ public: void notifyBombDefuse (); void execGameEntity (edict_t *ent); void forEach (ForEachBot handler); - void erase (Bot *bot); + void disconnectBot (Bot *bot); void handleDeath (edict_t *killer, edict_t *victim); void setLastWinner (int winner); void checkBotModel (edict_t *ent, char *infobuffer); diff --git a/inc/practice.h b/inc/practice.h index bdf2ade..0dc43c1 100644 --- a/inc/practice.h +++ b/inc/practice.h @@ -115,6 +115,9 @@ public: void load (); void save (); +private: + void syncLoad (); + public: template U getHighestDamageForTeam (int32_t team) const { return static_cast (cr::max (1, m_teamHighestDamage[team])); diff --git a/inc/product.h b/inc/product.h index 9ed01c8..f8b299f 100644 --- a/inc/product.h +++ b/inc/product.h @@ -15,58 +15,59 @@ #include VERSION_HEADER +// compile time build string +#define CTS_BUILD_STR static inline constexpr StringRef + // simple class for bot internal information -class Product final : public Singleton { +static constexpr class Product final { public: explicit constexpr Product () = default; ~Product () = default; public: - struct Build { - static constexpr StringRef hash { MODULE_COMMIT_COUNT }; - static constexpr StringRef count { MODULE_COMMIT_HASH }; - static constexpr StringRef author { MODULE_AUTHOR }; - static constexpr StringRef machine { MODULE_MACHINE }; - static constexpr StringRef compiler { MODULE_COMPILER }; - static constexpr StringRef id { MODULE_BUILD_ID }; - } build {}; + static constexpr struct BuildInfo { + CTS_BUILD_STR hash { MODULE_COMMIT_HASH }; + CTS_BUILD_STR count { MODULE_COMMIT_COUNT }; + CTS_BUILD_STR author { MODULE_AUTHOR }; + CTS_BUILD_STR machine { MODULE_MACHINE }; + CTS_BUILD_STR compiler { MODULE_COMPILER }; + CTS_BUILD_STR id { MODULE_BUILD_ID }; + } bi {}; public: - static constexpr StringRef name { "YaPB" }; - static constexpr StringRef nameLower { "yapb" }; - static constexpr StringRef year { &__DATE__[7] }; - static constexpr StringRef author { "YaPB Project" }; - static constexpr StringRef email { "yapb@jeefo.net" }; - static constexpr StringRef url { "https://yapb.jeefo.net/" }; - static constexpr StringRef download { "yapb.jeefo.net" }; - static constexpr StringRef upload { "yapb.jeefo.net/upload" }; - static constexpr StringRef httpScheme { "http" }; - static constexpr StringRef logtag { "YB" }; - static constexpr StringRef dtime { __DATE__ " " __TIME__ }; - static constexpr StringRef date { __DATE__ }; - static constexpr StringRef version { MODULE_VERSION "." MODULE_COMMIT_COUNT }; - static constexpr StringRef cmdPri { "yb" }; - static constexpr StringRef cmdSec { "yapb" }; -}; + CTS_BUILD_STR name { "YaPB" }; + CTS_BUILD_STR nameLower { "yapb" }; + CTS_BUILD_STR year { &__DATE__[7] }; + CTS_BUILD_STR author { "YaPB Project" }; + CTS_BUILD_STR email { "yapb@jeefo.net" }; + CTS_BUILD_STR url { "https://yapb.jeefo.net/" }; + CTS_BUILD_STR download { "yapb.jeefo.net" }; + CTS_BUILD_STR upload { "yapb.jeefo.net/upload" }; + CTS_BUILD_STR httpScheme { "http" }; + CTS_BUILD_STR logtag { "YB" }; + CTS_BUILD_STR dtime { __DATE__ " " __TIME__ }; + CTS_BUILD_STR date { __DATE__ }; + CTS_BUILD_STR version { MODULE_VERSION "." MODULE_COMMIT_COUNT }; + CTS_BUILD_STR cmdPri { "yb" }; + CTS_BUILD_STR cmdSec { "yapb" }; +} product {}; -class Folders final : public Singleton { +static constexpr class Folders final { public: explicit constexpr Folders () = default; ~Folders () = default; public: - static constexpr StringRef bot { "yapb" }; - static constexpr StringRef addons { "addons" }; - static constexpr StringRef config { "conf" }; - static constexpr StringRef data { "data" }; - static constexpr StringRef lang { "lang" }; - static constexpr StringRef logs { "logs" }; - static constexpr StringRef train { "train" }; - static constexpr StringRef graph { "graph" }; - static constexpr StringRef podbot { "pwf" }; - static constexpr StringRef ebot { "ewp" }; -}; + CTS_BUILD_STR bot { "yapb" }; + CTS_BUILD_STR addons { "addons" }; + CTS_BUILD_STR config { "conf" }; + CTS_BUILD_STR data { "data" }; + CTS_BUILD_STR lang { "lang" }; + CTS_BUILD_STR logs { "logs" }; + CTS_BUILD_STR train { "train" }; + CTS_BUILD_STR graph { "graph" }; + CTS_BUILD_STR podbot { "pwf" }; + CTS_BUILD_STR ebot { "ewp" }; +} folders {}; -// expose product info -CR_EXPOSE_GLOBAL_SINGLETON (Product, product); -CR_EXPOSE_GLOBAL_SINGLETON (Folders, folders); +#undef CTS_BUILD_STR diff --git a/inc/storage.h b/inc/storage.h index b03f385..db41308 100644 --- a/inc/storage.h +++ b/inc/storage.h @@ -103,7 +103,7 @@ public: } }; -#if !defined (BOT_STORAGE_EXPLICIT_INSTANTIATIONS) +#if !defined(BOT_STORAGE_EXPLICIT_INSTANTIATIONS) # define BOT_STORAGE_EXPLICIT_INSTANTIATIONS # include "../src/storage.cpp" #endif diff --git a/inc/support.h b/inc/support.h index dcb1367..965c754 100644 --- a/inc/support.h +++ b/inc/support.h @@ -80,7 +80,7 @@ public: StringRef getFakeSteamId (edict_t *ent); // get's the wave length - float getWaveLength (StringRef filename); + float getWaveFileDuration (StringRef filename); // set custom cvar descriptions void setCustomCvarDescriptions (); diff --git a/inc/version.h b/inc/version.h index 5707df5..c7d41b6 100644 --- a/inc/version.h +++ b/inc/version.h @@ -10,7 +10,7 @@ // fallback if no git or custom build #define MODULE_COMMIT_COUNT "0" #define MODULE_COMMIT_HASH "0" -#define MODULE_AUTHOR "default@mail.net" +#define MODULE_AUTHOR "yapb-local@jeefo.net" #define MODULE_MACHINE "localhost" #define MODULE_COMPILER "default" #define MODULE_VERSION "4.5" diff --git a/inc/yapb.h b/inc/yapb.h index 8fe2a48..43b9290 100644 --- a/inc/yapb.h +++ b/inc/yapb.h @@ -617,7 +617,7 @@ public: float m_preventFlashing {}; // bot turned away from flashbang float m_blindTime {}; // time when bot is blinded float m_blindMoveSpeed {}; // mad speeds when bot is blind - float m_blindSidemoveSpeed {}; // mad side move speeds when bot is blind + float m_blindSideMoveSpeed {}; // mad side move speeds when bot is blind float m_fallDownTime {}; // time bot started to fall float m_duckForJump {}; // is bot needed to duck for double jump float m_baseAgressionLevel {}; // base aggression level (on initializing) @@ -720,11 +720,7 @@ public: public: Bot (edict_t *bot, int difficulty, int personality, int team, int skin); - - // need to wait until all threads will finish it's work before terminating bot object - ~Bot () { - MutexScopedLock lock1 (m_pathFindLock); - } + ~Bot () = default; public: void logic (); /// the things that can be executed while skipping frames diff --git a/meson.build b/meson.build index 4e02dee..0a733fa 100644 --- a/meson.build +++ b/meson.build @@ -42,6 +42,7 @@ opt_64bit = get_option('64bit') opt_native = get_option('native') opt_winxp = get_option('winxp') opt_nosimd = get_option('nosimd') +opt_static_linkent = get_option('static_linkent') # cpp and ldflags from scratch cxxflags = [] @@ -143,17 +144,21 @@ if cxx == 'clang' or cxx == 'gcc' ] if os != 'darwin' and os != 'windows' and cpu != 'aarch64' and cpu != 'arm' and not cpu.startswith('ppc') - cxxflags += [ - '-fdata-sections', - '-ffunction-sections', - '-fcf-protection=none', - '-fno-plt' - ] + if not opt_static_linkent + cxxflags += [ + '-fdata-sections', + '-ffunction-sections', + '-fcf-protection=none', + '-fno-plt' + ] - ldflags += [ - '-Wl,--version-script=' + meson.project_source_root() + '/ext/ldscripts/version.lds', - '-Wl,--gc-sections' - ] + ldflags += [ + '-Wl,--version-script=' + meson.project_source_root() + '/ext/ldscripts/version.lds', + '-Wl,--gc-sections' + ] + else + cxxflags += ['-DLINKENT_STATIC'] + endif if cxx == 'gcc' cxxflags += [ diff --git a/meson_options.txt b/meson_options.txt index 930a2d2..e6b30d5 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -16,4 +16,7 @@ option('winxp', type : 'boolean', value : false, option('nosimd', type : 'boolean', value : false, description: 'Disables all SIMD or NEON optimizations.') + +option('static_linkent', type : 'boolean', value : false, + description: 'Use predefined entity link list, instead of hooking dlsym.') diff --git a/src/botlib.cpp b/src/botlib.cpp index 412b572..0fa0e9b 100644 --- a/src/botlib.cpp +++ b/src/botlib.cpp @@ -3712,20 +3712,20 @@ void Bot::takeBlind (int alpha) { if (m_difficulty <= Difficulty::Normal) { m_blindMoveSpeed = 0.0f; - m_blindSidemoveSpeed = 0.0f; + m_blindSideMoveSpeed = 0.0f; m_blindButton = IN_DUCK; return; } m_blindNodeIndex = findCoverNode (512.0f); m_blindMoveSpeed = -pev->maxspeed; - m_blindSidemoveSpeed = 0.0f; + m_blindSideMoveSpeed = 0.0f; if (rg.chance (50)) { - m_blindSidemoveSpeed = pev->maxspeed; + m_blindSideMoveSpeed = pev->maxspeed; } else { - m_blindSidemoveSpeed = -pev->maxspeed; + m_blindSideMoveSpeed = -pev->maxspeed; } if (m_healthValue < 85.0f) { @@ -3994,7 +3994,7 @@ void Bot::runMovement () { // translate bot buttons translateInput (); - engfuncs.pfnRunPlayerMove (pev->pContainingEntity, + engfuncs.pfnRunPlayerMove (ent (), getRpmAngles (), m_moveSpeed, m_strafeSpeed, 0.0f, static_cast (pev->button), static_cast (pev->impulse), msecVal); diff --git a/src/chatlib.cpp b/src/chatlib.cpp index 9f313c9..d6726c0 100644 --- a/src/chatlib.cpp +++ b/src/chatlib.cpp @@ -142,7 +142,7 @@ void Bot::prepareChatMessage (StringRef message) { m_chatBuffer = message; // must be called before return or on the end - auto finishPreparation = [&] () { + auto addChatErrors = [&] () { if (!m_chatBuffer.empty ()) { chatlib.addChatErrors (m_chatBuffer); } @@ -153,7 +153,7 @@ void Bot::prepareChatMessage (StringRef message) { // nothing found, bail out if (pos == String::InvalidIndex || pos >= message.length ()) { - finishPreparation (); + addChatErrors (); return; } @@ -241,6 +241,11 @@ void Bot::prepareChatMessage (StringRef message) { return humanizedName (playerIndex); } else if (!needsEnemy && m_team == client.team) { + if (util.isPlayer (pev->dmg_inflictor) + && game.getRealTeam (pev->dmg_inflictor) == m_team) { + + return humanizedName (game.indexOfPlayer (pev->dmg_inflictor)); + } return humanizedName (playerIndex); } } @@ -304,7 +309,7 @@ void Bot::prepareChatMessage (StringRef message) { }; ++replaceCounter; } - finishPreparation (); + addChatErrors (); } bool Bot::checkChatKeywords (String &reply) { diff --git a/src/combat.cpp b/src/combat.cpp index 692f744..8f5b8e3 100644 --- a/src/combat.cpp +++ b/src/combat.cpp @@ -191,7 +191,7 @@ bool Bot::checkBodyPartsWithOffsets (edict_t *target) { const auto &eyes = getEyesPos (); auto spot = target->v.origin; - auto self = pev->pContainingEntity; + auto self = ent (); // creatures can't hurt behind anything const auto ignoreFlags = m_isCreature ? TraceIgnore::None : (cv_aim_trace_consider_glass ? TraceIgnore::Monsters : TraceIgnore::Everything); @@ -266,7 +266,7 @@ bool Bot::checkBodyPartsWithOffsets (edict_t *target) { } bool Bot::checkBodyPartsWithHitboxes (edict_t *target) { - const auto self = pev->pContainingEntity; + const auto self = ent (); const auto refresh = m_frameInterval * 1.5f; TraceResult result {}; diff --git a/src/config.cpp b/src/config.cpp index b8ecea5..1520923 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -248,10 +248,10 @@ void BotConfig::loadChatterConfig () { m_chatter.clear (); - struct EventMap { - String str; - int code; - float repeat; + static constexpr struct EventMap { + StringRef name {}; + int code {}; + float repeat {}; } chatterEventMap[] = { { "Radio_CoverMe", Radio::CoverMe, kMaxChatterRepeatInterval }, { "Radio_YouTakePoint", Radio::YouTakeThePoint, kMaxChatterRepeatInterval }, @@ -361,18 +361,21 @@ void BotConfig::loadChatterConfig () { items[1].trim ("(;)"); for (const auto &event : chatterEventMap) { - if (event.str == items.first ()) { + if (event.name == items.first ().chars ()) { // this does common work of parsing comma-separated chatter line auto sentences = items[1].split (","); sentences.shuffle (); for (auto &sound : sentences) { sound.trim ().trim ("\""); - const auto duration = util.getWaveLength (sound.chars ()); + const auto duration = util.getWaveFileDuration (sound.chars ()); if (duration > 0.0f) { m_chatter[event.code].emplace (cr::move (sound), event.repeat, duration); } + else { + game.print ("Warning: Couldn't get duration of sound '%s.wav.", sound); + } } sentences.clear (); } @@ -383,7 +386,12 @@ void BotConfig::loadChatterConfig () { } else { cv_radio_mode.set (1); - game.print ("Bots chatter communication disabled."); + + + // only notify if has bot voice, but failed to open file + if (game.is (GameFlags::HasBotVoice)) { + game.print ("Bots chatter communication disabled."); + } } } diff --git a/src/control.cpp b/src/control.cpp index a674e36..51163b8 100644 --- a/src/control.cpp +++ b/src/control.cpp @@ -154,12 +154,12 @@ int BotControl::cmdWeaponMode () { } int BotControl::cmdVersion () { - const auto &build = product.build; + constexpr auto &bi = product.bi; - msg ("%s v%s (ID %s)", product.name, product.version, build.id); + msg ("%s v%s (ID %s)", product.name, product.version, bi.id); msg (" by %s (%s)", product.author, product.email); msg (" %s", product.url); - msg ("compiled: %s on %s with %s", product.dtime, build.machine, build.compiler); + msg ("compiled: %s on %s with %s", product.dtime, bi.machine, bi.compiler); return BotCommandResult::Handled; } diff --git a/src/engine.cpp b/src/engine.cpp index 8cc760c..2bb1f28 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -187,17 +187,17 @@ void Game::levelInitialize (edict_t *entities, int max) { } void Game::levelShutdown () { - // stop thread pool - worker.shutdown (); - // save collected practice on shutdown practice.save (); + // stop thread pool + worker.shutdown (); + // destroy global killer entity bots.destroyKillerEntity (); // ensure players are off on xash3d - if (game.is (GameFlags::Xash3D)) { + if (game.is (GameFlags::Xash3DLegacy)) { bots.kickEveryone (true, false); } @@ -310,7 +310,7 @@ const char *Game::getRunningModName () { return name.chars (); } - char engineModName[256] {}; + char engineModName[StringBuffer::StaticBufferSize] {}; engfuncs.pfnGetGameDir (engineModName); name = engineModName; @@ -545,7 +545,7 @@ void Game::prepareBotArgs (edict_t *ent, String str) { m_botArgs.emplace (args.substr (quote, args.length () - 1).trim ("\"")); // add string with trimmed quotes } else { - for (auto &&arg : args.split (" ")) { + for (auto arg : args.split (" ")) { m_botArgs.emplace (arg); } } @@ -560,8 +560,8 @@ void Game::prepareBotArgs (edict_t *ent, String str) { }; if (str.find (';', 0) != String::InvalidIndex) { - for (auto &&part : str.split (";")) { - parsePartArgs (part); + for (auto part : str.split (";")) { + parsePartArgs (part.trim ()); } } else { @@ -594,7 +594,7 @@ bool Game::isSoftwareRenderer () { return false; } - // and on only windows version you can use software-render game. Linux, OSX always defaults to OpenGL + // and on only windows version you can use software-render game. Linux, macOS always defaults to OpenGL if (plat.win) { return plat.hasModule ("sw"); } @@ -605,7 +605,7 @@ bool Game::is25thAnniversaryUpdate () { static ConVarRef sv_use_steam_networking ("sv_use_steam_networking"); static ConVarRef host_hl25_extended_structs ("host_hl25_extended_structs"); - return sv_use_steam_networking.exists () || host_hl25_extended_structs.exists (); + return sv_use_steam_networking.exists () || host_hl25_extended_structs.value () > 0.0f; } void Game::pushConVar (StringRef name, StringRef value, StringRef info, bool bounded, float min, float max, int32_t varType, bool missingAction, StringRef regval, ConVar *self) { @@ -1161,7 +1161,7 @@ void Game::printBotVersion () { if (is (GameFlags::Xash3D)) { if (is (GameFlags::Xash3DLegacy)) { - gameVersionStr.append (" @ Xash3D (Old)"); + gameVersionStr.append (" @ Xash3D-NG"); } else { gameVersionStr.append (" @ Xash3D FWGS"); @@ -1315,7 +1315,7 @@ void LightMeasure::animateLight () { for (auto j = 0; j < MAX_LIGHTSTYLES; ++j) { if (!m_lightstyle[j].length) { - m_lightstyleValue[j] = 256; + m_lightstyleValue[j] = MAX_LIGHTSTYLEVALUE; continue; } m_lightstyleValue[j] = static_cast (m_lightstyle[j].map[index % m_lightstyle[j].length] - 'a') * 22u; diff --git a/src/entities.cpp b/src/entities.cpp index f655f18..dbc810b 100644 --- a/src/entities.cpp +++ b/src/entities.cpp @@ -11,7 +11,7 @@ // nice interface to handle with linkents. if ever rehlds or hlds engine will ever run on ARM or // other platforms, and you want to run bot on it without metamod, consider enabling LINKENT_STATIC_THUNKS // when compiling the bot, to get it supported. -#if defined(LINKENT_STATIC_THUNKS) +#if defined(LINKENT_STATIC) void forwardEntity_helper (EntityProto &addr, const char *name, entvars_t *pev) { if (!addr) { addr = game.lib ().resolve (name); diff --git a/src/fakeping.cpp b/src/fakeping.cpp index ee6b2d5..62b851b 100644 --- a/src/fakeping.cpp +++ b/src/fakeping.cpp @@ -22,22 +22,21 @@ void BotFakePingManager::reset (edict_t *to) { if (!hasFeature ()) { return; } - static PingBitMsg pbm {}; for (const auto &client : util.getClients ()) { if (!(client.flags & ClientFlags::Used) || util.isFakeClient (client.ent)) { continue; } - pbm.start (client.ent); + m_pbm.start (client.ent); - pbm.write (1, PingBitMsg::Single); - pbm.write (game.indexOfPlayer (to), PingBitMsg::PlayerID); - pbm.write (0, PingBitMsg::Ping); - pbm.write (0, PingBitMsg::Loss); + m_pbm.write (1, PingBitMsg::Single); + m_pbm.write (game.indexOfPlayer (to), PingBitMsg::PlayerID); + m_pbm.write (0, PingBitMsg::Ping); + m_pbm.write (0, PingBitMsg::Loss); - pbm.send (); + m_pbm.send (); } - pbm.flush (); + m_pbm.flush (); } void BotFakePingManager::syncCalculate () { @@ -107,19 +106,18 @@ void BotFakePingManager::emit (edict_t *ent) { if (!util.isPlayer (ent)) { return; } - static PingBitMsg pbm {}; for (const auto &bot : bots) { - pbm.start (ent); + m_pbm.start (ent); - pbm.write (1, PingBitMsg::Single); - pbm.write (bot->entindex () - 1, PingBitMsg::PlayerID); - pbm.write (bot->m_ping, PingBitMsg::Ping); - pbm.write (0, PingBitMsg::Loss); + m_pbm.write (1, PingBitMsg::Single); + m_pbm.write (bot->entindex () - 1, PingBitMsg::PlayerID); + m_pbm.write (bot->m_ping, PingBitMsg::Ping); + m_pbm.write (0, PingBitMsg::Loss); - pbm.send (); + m_pbm.send (); } - pbm.flush (); + m_pbm.flush (); } void BotFakePingManager::restartTimer () { diff --git a/src/graph.cpp b/src/graph.cpp index 51809a7..ef3b202 100644 --- a/src/graph.cpp +++ b/src/graph.cpp @@ -31,8 +31,8 @@ void BotGraph::reset () { m_narrowChecked = false; m_lightChecked = false; - m_graphAuthor.clear (); - m_graphModified.clear (); + m_info.author.clear (); + m_info.modified.clear (); m_paths.clear (); } @@ -600,7 +600,7 @@ IntArray BotGraph::getNearestInRadius (float radius, const Vector &origin, int m } void BotGraph::add (int type, const Vector &pos) { - if (game.isNullEntity (m_editor) && !analyzer.isAnalyzing ()) { + if (!hasEditor () && !analyzer.isAnalyzing ()) { return; } int index = kInvalidNodeIndex; @@ -610,7 +610,7 @@ void BotGraph::add (int type, const Vector &pos) { Vector newOrigin = pos; if (newOrigin.empty ()) { - if (game.isNullEntity (m_editor)) { + if (!hasEditor ()) { return; } newOrigin = m_editor->v.origin; @@ -1277,21 +1277,24 @@ void BotGraph::showStats () { } void BotGraph::showFileInfo () { + const auto &info = m_info.header; + const auto &exten = m_info.exten; + msg ("header:"); - msg (" magic: %d", m_graphHeader.magic); - msg (" version: %d", m_graphHeader.version); - msg (" node_count: %d", m_graphHeader.length); - msg (" compressed_size: %dkB", m_graphHeader.compressed / 1024); - msg (" uncompressed_size: %dkB", m_graphHeader.uncompressed / 1024); - msg (" options: %d", m_graphHeader.options); // display as string ? + msg (" magic: %d", info.magic); + msg (" version: %d", info.version); + msg (" node_count: %d", info.length); + msg (" compressed_size: %dkB", info.compressed / 1024); + msg (" uncompressed_size: %dkB", info.uncompressed / 1024); + msg (" options: %d", info.options); // display as string ? msg (" analyzed: %s", isAnalyzed () ? conf.translate ("yes") : conf.translate ("no")); // display as string ? msg (""); msg ("extensions:"); - msg (" author: %s", m_extenHeader.author); - msg (" modified_by: %s", m_extenHeader.modified); - msg (" bsp_size: %d", m_extenHeader.mapSize); + msg (" author: %s", exten.author); + msg (" modified_by: %s", exten.modified); + msg (" bsp_size: %d", exten.mapSize); } void BotGraph::emitNotify (int32_t sound) { @@ -1576,7 +1579,7 @@ void BotGraph::initNarrowPlaces () { constexpr int32_t kNarrowPlacesMinGraphVersion = 2; // if version 2 or higher, narrow places already initialized and saved into file - if (m_graphHeader.version >= kNarrowPlacesMinGraphVersion && !hasEditFlag (GraphEdit::On)) { + if (m_info.header.version >= kNarrowPlacesMinGraphVersion && !hasEditFlag (GraphEdit::On)) { m_narrowChecked = true; return; } @@ -1747,7 +1750,7 @@ bool BotGraph::convertOldFormat () { if (!m_paths.empty ()) { msg ("Converting old PWF to new format Graph."); - m_graphAuthor = header.author; + m_info.author = header.author; // clean editor so graph will be saved with header's author auto editor = m_editor; @@ -1774,8 +1777,8 @@ bool BotGraph::loadGraphData () { ExtenHeader exten {}; int32_t outOptions = 0; - m_graphHeader = {}; - m_extenHeader = {}; + m_info.header = {}; + m_info.exten = {}; // re-initialize paths reset (); @@ -1797,15 +1800,15 @@ bool BotGraph::loadGraphData () { StringRef author = exten.author; if ((outOptions & StorageOption::Official) || author.startsWith ("official") || author.length () < 2) { - m_graphAuthor.assign (product.name); + m_info.author.assign (product.name); } else { - m_graphAuthor.assign (author); + m_info.author.assign (author); } StringRef modified = exten.modified; if (!modified.empty () && !modified.contains ("(none)")) { - m_graphModified.assign (exten.modified); + m_info.modified.assign (exten.modified); } planner.init (); // initialize our little path planner practice.load (); // load bots practice @@ -1848,8 +1851,8 @@ bool BotGraph::saveGraphData () { auto options = StorageOption::Graph | StorageOption::Exten; String editorName {}; - if (game.isNullEntity (m_editor) && !m_graphAuthor.empty ()) { - editorName = m_graphAuthor; + if (!hasEditor () && !m_info.author.empty ()) { + editorName = m_info.author; if (!game.isDedicated ()) { options |= StorageOption::Recovered; @@ -1874,15 +1877,15 @@ bool BotGraph::saveGraphData () { ExtenHeader exten {}; // only modify the author if no author currently assigned to graph file - if (m_graphAuthor.empty () || strings.isEmpty (m_extenHeader.author)) { + if (m_info.author.empty () || strings.isEmpty (m_info.exten.author)) { strings.copy (exten.author, editorName.chars (), cr::bufsize (exten.author)); } else { - strings.copy (exten.author, m_extenHeader.author, cr::bufsize (exten.author)); + strings.copy (exten.author, m_info.exten.author, cr::bufsize (exten.author)); } // only update modified by, if name differs - if (m_graphAuthor != editorName && !strings.isEmpty (m_extenHeader.author)) { + if (m_info.author != editorName && !strings.isEmpty (m_info.exten.author)) { strings.copy (exten.modified, editorName.chars (), cr::bufsize (exten.author)); } exten.mapSize = getBspSize (); @@ -1901,8 +1904,8 @@ void BotGraph::saveOldFormat () { String editorName {}; - if (game.isNullEntity (m_editor) && !m_graphAuthor.empty ()) { - editorName = m_graphAuthor; + if (!hasEditor () && !m_info.author.empty ()) { + editorName = m_info.author; } else if (!game.isNullEntity (m_editor)) { editorName = m_editor->v.netname.chars (); @@ -2284,7 +2287,7 @@ void BotGraph::frame () { // draw the radius circle Vector origin = (path.flags & NodeFlag::Crouch) ? path.origin : path.origin - Vector (0.0f, 0.0f, 18.0f); - static Color radiusColor { 36, 36, 255 }; + constexpr Color radiusColor { 36, 36, 255 }; // if radius is nonzero, draw a full circle if (path.radius > 0.0f) { @@ -2846,7 +2849,7 @@ const Array &BotGraph::getNodesInBucket (const Vector &pos) { } bool BotGraph::isAnalyzed () const { - return (m_graphHeader.options & StorageOption::Analyzed); + return (m_info.header.options & StorageOption::Analyzed); } void BotGraph::eraseFromBucket (const Vector &pos, int index) { diff --git a/src/hooks.cpp b/src/hooks.cpp index 81b78d1..2059595 100644 --- a/src/hooks.cpp +++ b/src/hooks.cpp @@ -124,7 +124,7 @@ SharedLibrary::Func DynamicLinkerHook::lookup (SharedLibrary::Handle module, con return resolve (module); } -#if defined (CR_WINDOWS) +#if defined(CR_WINDOWS) if (HIWORD (function) == 0) { return resolve (module); } @@ -174,15 +174,20 @@ bool DynamicLinkerHook::needsBypass () const { } void DynamicLinkerHook::initialize () { +#if defined(LINKENT_STATIC) + return; +#endif + if (plat.isNonX86 () || game.is (GameFlags::Metamod)) { return; } + constexpr StringRef kKernel32Module = "kernel32.dll"; - m_dlsym.initialize ("kernel32.dll", "GetProcAddress", DLSYM_FUNCTION); + m_dlsym.initialize (kKernel32Module, "GetProcAddress", DLSYM_FUNCTION); m_dlsym.install (reinterpret_cast (lookupHandler), true); if (needsBypass ()) { - m_dlclose.initialize ("kernel32.dll", "FreeLibrary", DLCLOSE_FUNCTION); + m_dlclose.initialize (kKernel32Module, "FreeLibrary", DLCLOSE_FUNCTION); m_dlclose.install (reinterpret_cast (closeHandler), true); } m_self.locate (&engfuncs); diff --git a/src/linkage.cpp b/src/linkage.cpp index bd60fbb..d912cbd 100644 --- a/src/linkage.cpp +++ b/src/linkage.cpp @@ -34,7 +34,7 @@ plugin_info_t Plugin_info = { // compilers can't create lambdas with vaargs, so put this one in it's own namespace namespace Hooks { -void handler_engClientCommand (edict_t *ent, char const *format, ...) { +CR_FORCE_STACK_ALIGN void handler_engClientCommand (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 // stores the command string; if ever that string is filled with characters, the client DLL @@ -99,7 +99,7 @@ CR_EXPORT int GetEntityAPI (gamefuncs_t *table, int interfaceVersion) { memcpy (table, &dllapi, sizeof (gamefuncs_t)); } - table->pfnGameInit = [] () { + table->pfnGameInit = [] () CR_FORCE_STACK_ALIGN { // 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 @@ -121,7 +121,7 @@ CR_EXPORT int GetEntityAPI (gamefuncs_t *table, int interfaceVersion) { dllapi.pfnGameInit (); }; - table->pfnSpawn = [] (edict_t *ent) { + table->pfnSpawn = [] (edict_t *ent) CR_FORCE_STACK_ALIGN { // 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, @@ -141,7 +141,7 @@ CR_EXPORT int GetEntityAPI (gamefuncs_t *table, int interfaceVersion) { return result; }; - table->pfnTouch = [] (edict_t *pentTouched, edict_t *pentOther) { + table->pfnTouch = [] (edict_t *pentTouched, edict_t *pentOther) CR_FORCE_STACK_ALIGN { // this function is called when two entities' bounding boxes enter in collision. For example, // when a player walks upon a gun, the player entity bounding box collides to the gun entity // bounding box, and the result is that this function is called. It is used by the game for @@ -170,7 +170,7 @@ CR_EXPORT int GetEntityAPI (gamefuncs_t *table, int interfaceVersion) { dllapi.pfnTouch (pentTouched, pentOther); }; - table->pfnClientConnect = [] (edict_t *ent, const char *name, const char *addr, char rejectReason[128]) { + table->pfnClientConnect = [] (edict_t *ent, const char *name, const char *addr, char rejectReason[128]) CR_FORCE_STACK_ALIGN { // this function is called in order to tell the MOD DLL that a client attempts to connect the // game. The entity pointer of this client is ent, the name under which he connects is // pointed to by the pszName pointer, and its IP address string is pointed by the pszAddress @@ -210,7 +210,7 @@ CR_EXPORT int GetEntityAPI (gamefuncs_t *table, int interfaceVersion) { return dllapi.pfnClientConnect (ent, name, addr, rejectReason); }; - table->pfnClientDisconnect = [] (edict_t *ent) { + table->pfnClientDisconnect = [] (edict_t *ent) CR_FORCE_STACK_ALIGN { // this function is called whenever a client is VOLUNTARILY disconnected from the server, // either because the client dropped the connection, or because the server dropped him from // the game (latency timeout). The effect is the freeing of a client slot on the server. Note @@ -224,7 +224,7 @@ CR_EXPORT int GetEntityAPI (gamefuncs_t *table, int interfaceVersion) { for (auto &bot : bots) { if (bot->pev == &ent->v) { - bots.erase (bot.get ()); // remove the bot from bots array + bots.disconnectBot (bot.get ()); // remove the bot from bots array break; } @@ -249,8 +249,7 @@ CR_EXPORT int GetEntityAPI (gamefuncs_t *table, int interfaceVersion) { dllapi.pfnClientDisconnect (ent); }; - - table->pfnClientPutInServer = [] (edict_t *ent) { + table->pfnClientPutInServer = [] (edict_t *ent) CR_FORCE_STACK_ALIGN { // this function is called once a just connected client actually enters the game, after // having downloaded and synchronized its resources with the of the server's. It's the // perfect place to hook for client connecting, since a client can always try to connect @@ -271,7 +270,7 @@ CR_EXPORT int GetEntityAPI (gamefuncs_t *table, int interfaceVersion) { dllapi.pfnClientPutInServer (ent); }; - table->pfnClientUserInfoChanged = [] (edict_t *ent, char *infobuffer) { + table->pfnClientUserInfoChanged = [] (edict_t *ent, char *infobuffer) CR_FORCE_STACK_ALIGN { // this function is called when a player changes model, or changes team. Occasionally it // enforces rules on these changes (for example, some MODs don't want to allow players to // change their player model). But most commonly, this function is in charge of handling @@ -286,7 +285,7 @@ CR_EXPORT int GetEntityAPI (gamefuncs_t *table, int interfaceVersion) { dllapi.pfnClientUserInfoChanged (ent, infobuffer); }; - table->pfnClientCommand = [] (edict_t *ent) { + table->pfnClientCommand = [] (edict_t *ent) CR_FORCE_STACK_ALIGN { // this function is called whenever the client whose player entity is ent issues a client // command. How it works is that clients all have a global string in their client DLL that // stores the command string; if ever that string is filled with characters, the client DLL @@ -323,7 +322,7 @@ CR_EXPORT int GetEntityAPI (gamefuncs_t *table, int interfaceVersion) { dllapi.pfnClientCommand (ent); }; - table->pfnServerActivate = [] (edict_t *edictList, int edictCount, int clientMax) { + table->pfnServerActivate = [] (edict_t *edictList, int edictCount, int clientMax) CR_FORCE_STACK_ALIGN { // 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 @@ -340,7 +339,7 @@ CR_EXPORT int GetEntityAPI (gamefuncs_t *table, int interfaceVersion) { game.levelInitialize (edictList, edictCount); }; - table->pfnServerDeactivate = [] () { + table->pfnServerDeactivate = [] () CR_FORCE_STACK_ALIGN { // 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 @@ -360,7 +359,7 @@ CR_EXPORT int GetEntityAPI (gamefuncs_t *table, int interfaceVersion) { dllapi.pfnServerDeactivate (); }; - table->pfnStartFrame = [] () { + table->pfnStartFrame = [] () CR_FORCE_STACK_ALIGN { // 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 @@ -415,7 +414,7 @@ CR_EXPORT int GetEntityAPI (gamefuncs_t *table, int interfaceVersion) { }; if (game.is (GameFlags::HasFakePings) && !game.is (GameFlags::Metamod)) { - table->pfnUpdateClientData = [] (const struct edict_s *player, int sendweapons, struct clientdata_s *cd) { + table->pfnUpdateClientData = [] (const struct edict_s *player, int sendweapons, struct clientdata_s *cd) CR_FORCE_STACK_ALIGN { // this function is a synchronization tool that is used periodically by the engine to tell // the game DLL to send player info over the network to one of its clients when it suspects // that this client is desynchronizing. Early bots were using it to ask the game DLL for the @@ -437,7 +436,7 @@ CR_EXPORT int GetEntityAPI (gamefuncs_t *table, int interfaceVersion) { }; } - table->pfnPM_Move = [] (playermove_t *pm, int server) { + table->pfnPM_Move = [] (playermove_t *pm, int server) CR_FORCE_STACK_ALIGN { // this is the player movement code clients run to predict things when the server can't update // them often enough (or doesn't want to). The server runs exactly the same function for // moving players. There is normally no distinction between them, else client-side prediction @@ -451,7 +450,7 @@ CR_EXPORT int GetEntityAPI (gamefuncs_t *table, int interfaceVersion) { dllapi.pfnPM_Move (pm, server); }; - table->pfnKeyValue = [] (edict_t *ent, KeyValueData *kvd) { + table->pfnKeyValue = [] (edict_t *ent, KeyValueData *kvd) CR_FORCE_STACK_ALIGN { // this function is called when the game requests a pointer to some entity's keyvalue data. // The keyvalue data is held in each entity's infobuffer (basically a char buffer where each // game DLL can put the stuff it wants) under - as it says - the form of a key/value pair. A @@ -475,7 +474,7 @@ CR_EXPORT int GetEntityAPI (gamefuncs_t *table, int interfaceVersion) { return HLTrue; } -CR_LINKAGE_C int GetEntityAPI_Post (gamefuncs_t *table, int) { +CR_C_LINKAGE int GetEntityAPI_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 @@ -488,7 +487,7 @@ CR_LINKAGE_C int GetEntityAPI_Post (gamefuncs_t *table, int) { plat.bzero (table, sizeof (gamefuncs_t)); - table->pfnSpawn = [] (edict_t *ent) { + table->pfnSpawn = [] (edict_t *ent) CR_FORCE_STACK_ALIGN { // 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, @@ -502,7 +501,7 @@ CR_LINKAGE_C int GetEntityAPI_Post (gamefuncs_t *table, int) { RETURN_META_VALUE (MRES_HANDLED, 0); }; - table->pfnStartFrame = [] () { + table->pfnStartFrame = [] () CR_FORCE_STACK_ALIGN { // 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 @@ -515,7 +514,7 @@ CR_LINKAGE_C int GetEntityAPI_Post (gamefuncs_t *table, int) { RETURN_META (MRES_IGNORED); }; - table->pfnServerActivate = [] (edict_t *edictList, int edictCount, int) { + table->pfnServerActivate = [] (edict_t *edictList, int edictCount, int) CR_FORCE_STACK_ALIGN { // 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 @@ -531,7 +530,7 @@ CR_LINKAGE_C int GetEntityAPI_Post (gamefuncs_t *table, int) { }; if (game.is (GameFlags::HasFakePings)) { - table->pfnUpdateClientData = [] (const struct edict_s *player, int, struct clientdata_s *) { + table->pfnUpdateClientData = [] (const struct edict_s *player, int, struct clientdata_s *) CR_FORCE_STACK_ALIGN { // this function is a synchronization tool that is used periodically by the engine to tell // the game DLL to send player info over the network to one of its clients when it suspects // that this client is desynchronizing. Early bots were using it to ask the game DLL for the @@ -555,13 +554,13 @@ CR_LINKAGE_C int GetEntityAPI_Post (gamefuncs_t *table, int) { return HLTrue; } -CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { +CR_C_LINKAGE int GetEngineFunctions (enginefuncs_t *table, int *) { if (game.is (GameFlags::Metamod)) { plat.bzero (table, sizeof (enginefuncs_t)); } if (entlink.needsBypass () && !game.is (GameFlags::Metamod)) { - table->pfnCreateNamedEntity = [] (string_t classname) -> edict_t * { + table->pfnCreateNamedEntity = [] (string_t classname) CR_FORCE_STACK_ALIGN { if (entlink.isPaused ()) { entlink.enable (); @@ -572,7 +571,7 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { } if (game.is (GameFlags::Legacy)) { - table->pfnFindEntityByString = [] (edict_t *edictStartSearchAfter, const char *field, const char *value) { + table->pfnFindEntityByString = [] (edict_t *edictStartSearchAfter, const char *field, const char *value) CR_FORCE_STACK_ALIGN { // round starts in counter-strike 1.5 if (strcmp (value, "info_map_parameters") == 0) { bots.initRound (); @@ -583,10 +582,22 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { } return engfuncs.pfnFindEntityByString (edictStartSearchAfter, field, value); }; + + table->pfnChangeLevel = [] (char *s1, char *s2) CR_FORCE_STACK_ALIGN { + // this function gets called when server is changing a level + + // kick off all the bots, needed for legacy engine versions + bots.kickEveryone (true, false); + + if (game.is (GameFlags::Metamod)) { + RETURN_META (MRES_IGNORED); + } + engfuncs.pfnChangeLevel (s1, s2); + }; } if (!game.is (GameFlags::Legacy)) { - table->pfnLightStyle = [] (int style, char *val) { + table->pfnLightStyle = [] (int style, char *val) CR_FORCE_STACK_ALIGN { // this function update lightstyle for the bots illum.updateLight (style, val); @@ -597,7 +608,7 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { engfuncs.pfnLightStyle (style, val); }; - table->pfnGetPlayerAuthId = [] (edict_t *e) -> const char * { + table->pfnGetPlayerAuthId = [] (edict_t *e) CR_FORCE_STACK_ALIGN { if (bots[e]) { auto authid = util.getFakeSteamId (e); @@ -608,13 +619,13 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { } if (game.is (GameFlags::Metamod)) { - RETURN_META_VALUE (MRES_IGNORED, nullptr); + RETURN_META_VALUE (MRES_IGNORED, ""); } return engfuncs.pfnGetPlayerAuthId (e); }; } - table->pfnEmitSound = [] (edict_t *entity, int channel, const char *sample, float volume, float attenuation, int flags, int pitch) { + table->pfnEmitSound = [] (edict_t *entity, int channel, const char *sample, float volume, float attenuation, int flags, int pitch) CR_FORCE_STACK_ALIGN { // this function tells the engine that the entity pointed to by "entity", is emitting a sound // which fileName is "sample", at level "channel" (CHAN_VOICE, etc...), with "volume" as // loudness multiplicator (normal volume VOL_NORM is 1.0), with a pitch of "pitch" (normal @@ -633,7 +644,7 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { engfuncs.pfnEmitSound (entity, channel, sample, volume, attenuation, flags, pitch); }; - table->pfnMessageBegin = [] (int msgDest, int msgType, const float *origin, edict_t *ed) { + table->pfnMessageBegin = [] (int msgDest, int msgType, const float *origin, edict_t *ed) CR_FORCE_STACK_ALIGN { // this function called each time a message is about to sent. msgs.start (ed, msgType); @@ -644,7 +655,7 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { }; if (!game.is (GameFlags::Metamod)) { - table->pfnMessageEnd = [] () { + table->pfnMessageEnd = [] () CR_FORCE_STACK_ALIGN { engfuncs.pfnMessageEnd (); // this allows us to send messages right in handler code @@ -652,7 +663,7 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { }; } - table->pfnWriteByte = [] (int value) { + table->pfnWriteByte = [] (int value) CR_FORCE_STACK_ALIGN { // if this message is for a bot, call the client message function... msgs.collect (value); @@ -662,7 +673,7 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { engfuncs.pfnWriteByte (value); }; - table->pfnWriteChar = [] (int value) { + table->pfnWriteChar = [] (int value) CR_FORCE_STACK_ALIGN { // if this message is for a bot, call the client message function... msgs.collect (value); @@ -672,7 +683,7 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { engfuncs.pfnWriteChar (value); }; - table->pfnWriteShort = [] (int value) { + table->pfnWriteShort = [] (int value) CR_FORCE_STACK_ALIGN { // if this message is for a bot, call the client message function... msgs.collect (value); @@ -682,7 +693,7 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { engfuncs.pfnWriteShort (value); }; - table->pfnWriteLong = [] (int value) { + table->pfnWriteLong = [] (int value) CR_FORCE_STACK_ALIGN { // if this message is for a bot, call the client message function... msgs.collect (value); @@ -692,7 +703,7 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { engfuncs.pfnWriteLong (value); }; - table->pfnWriteAngle = [] (float value) { + table->pfnWriteAngle = [] (float value) CR_FORCE_STACK_ALIGN { // if this message is for a bot, call the client message function... msgs.collect (value); @@ -702,7 +713,7 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { engfuncs.pfnWriteAngle (value); }; - table->pfnWriteCoord = [] (float value) { + table->pfnWriteCoord = [] (float value) CR_FORCE_STACK_ALIGN { // if this message is for a bot, call the client message function... msgs.collect (value); @@ -712,7 +723,7 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { engfuncs.pfnWriteCoord (value); }; - table->pfnWriteString = [] (const char *sz) { + table->pfnWriteString = [] (const char *sz) CR_FORCE_STACK_ALIGN { // if this message is for a bot, call the client message function... msgs.collect (sz); @@ -722,7 +733,7 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { engfuncs.pfnWriteString (sz); }; - table->pfnWriteEntity = [] (int value) { + table->pfnWriteEntity = [] (int value) CR_FORCE_STACK_ALIGN { // if this message is for a bot, call the client message function... msgs.collect (value); @@ -736,7 +747,7 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { table->pfnClientCommand = Hooks::handler_engClientCommand; if (!game.is (GameFlags::Metamod)) { - table->pfnRegUserMsg = [] (const char *name, int size) { + table->pfnRegUserMsg = [] (const char *name, int size) CR_FORCE_STACK_ALIGN { // this function registers a "user message" by the engine side. User messages are network // messages the game DLL asks the engine to send to clients. Since many MODs have completely // different client features (Counter-Strike has a radar and a timer, for example), network @@ -751,7 +762,7 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { }; } - table->pfnClientPrintf = [] (edict_t *ent, PRINT_TYPE printType, const char *message) { + table->pfnClientPrintf = [] (edict_t *ent, PRINT_TYPE printType, const char *message) CR_FORCE_STACK_ALIGN { // this function prints the text message string pointed to by message by the client side of // the client entity pointed to by ent, in a manner depending of printType (print_console, // print_center or print_chat). Be certain never to try to feed a bot with this function, @@ -771,7 +782,7 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { engfuncs.pfnClientPrintf (ent, printType, message); }; - table->pfnCmd_Args = [] () { + table->pfnCmd_Args = [] () CR_FORCE_STACK_ALIGN { // 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 @@ -793,7 +804,7 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { return engfuncs.pfnCmd_Args (); // ask the client command string to the engine }; - table->pfnCmd_Argv = [] (int argc) { + table->pfnCmd_Argv = [] (int argc) CR_FORCE_STACK_ALIGN { // this function returns a pointer to a certain argument of the current client command. 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 @@ -815,7 +826,7 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { return engfuncs.pfnCmd_Argv (argc); // ask the argument number "argc" to the engine }; - table->pfnCmd_Argc = [] () { + table->pfnCmd_Argc = [] () CR_FORCE_STACK_ALIGN { // 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 @@ -837,7 +848,7 @@ CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { return engfuncs.pfnCmd_Argc (); // ask the engine how many arguments there are }; - table->pfnSetClientMaxspeed = [] (const edict_t *ent, float newMaxspeed) { + table->pfnSetClientMaxspeed = [] (const edict_t *ent, float newMaxspeed) CR_FORCE_STACK_ALIGN { auto bot = bots[const_cast (ent)]; // check wether it's not a bot @@ -875,7 +886,7 @@ CR_EXPORT int GetNewDLLFunctions (newgamefuncs_t *table, int *interfaceVersion) plat.bzero (table, sizeof (newgamefuncs_t)); if (!game.is (GameFlags::Legacy)) { - table->pfnOnFreeEntPrivateData = [] (edict_t *ent) { + table->pfnOnFreeEntPrivateData = [] (edict_t *ent) CR_FORCE_STACK_ALIGN { for (auto &bot : bots) { if (bot->m_enemy == ent) { bot->m_enemy = nullptr; @@ -895,16 +906,16 @@ CR_EXPORT int GetNewDLLFunctions (newgamefuncs_t *table, int *interfaceVersion) return HLTrue; } -CR_LINKAGE_C int GetEngineFunctions_Post (enginefuncs_t *table, int *) { +CR_C_LINKAGE int GetEngineFunctions_Post (enginefuncs_t *table, int *) { plat.bzero (table, sizeof (enginefuncs_t)); - table->pfnMessageEnd = [] () { + table->pfnMessageEnd = [] () CR_FORCE_STACK_ALIGN { msgs.stop (); // this allows us to send messages right in handler code RETURN_META (MRES_IGNORED); }; - table->pfnRegUserMsg = [] (const char *name, int) { + table->pfnRegUserMsg = [] (const char *name, int) CR_FORCE_STACK_ALIGN { // this function registers a "user message" by the engine side. User messages are network // messages the game DLL asks the engine to send to clients. Since many MODs have completely // different client features (Counter-Strike has a radar and a timer, for example), network @@ -995,15 +1006,15 @@ CR_EXPORT int Meta_Detach (PLUG_LOADTIME now, PL_UNLOAD_REASON reason) { gpMetaUtilFuncs->pfnLogError (PLID, "%s: plugin NOT detaching (can't unload plugin right now)", Plugin_info.name); return HLFalse; // returning FALSE prevents metamod from unloading this plugin } - // stop the worker - worker.shutdown (); - // kick all bots off this server bots.kickEveryone (true); // save collected practice on shutdown practice.save (); + // stop the worker + worker.shutdown (); + // disable hooks fakequeries.disable (); @@ -1023,13 +1034,13 @@ CR_EXPORT void Meta_Init () { // games GiveFnptrsToDll is a bit tricky #if defined(CR_WINDOWS) # if defined(CR_CXX_MSVC) || (defined(CR_CXX_CLANG) && !defined(CR_CXX_GCC)) -# if defined (CR_ARCH_X86) +# if defined(CR_ARCH_X32) # pragma comment(linker, "/EXPORT:GiveFnptrsToDll=_GiveFnptrsToDll@8,@1") # endif # pragma comment(linker, "/SECTION:.data,RW") # endif # if defined(CR_CXX_MSVC) && !defined(CR_ARCH_X64) -# define DLL_GIVEFNPTRSTODLL CR_LINKAGE_C void CR_STDCALL +# define DLL_GIVEFNPTRSTODLL CR_C_LINKAGE void CR_STDCALL # elif defined(CR_CXX_CLANG) || defined(CR_CXX_GCC) || defined(CR_ARCH_X64) # define DLL_GIVEFNPTRSTODLL CR_EXPORT void CR_STDCALL # endif diff --git a/src/manager.cpp b/src/manager.cpp index a1c3d3d..0f09d88 100644 --- a/src/manager.cpp +++ b/src/manager.cpp @@ -1116,14 +1116,14 @@ Bot::Bot (edict_t *bot, int difficulty, int personality, int team, int skin) { if (cv_show_latency.as () == 1) { engfuncs.pfnSetClientKeyValue (clientIndex, buffer, "*bot", "1"); } - auto avatar = conf.getRandomAvatar (); + const auto &avatar = conf.getRandomAvatar (); if (cv_show_avatars && !avatar.empty ()) { engfuncs.pfnSetClientKeyValue (clientIndex, buffer, "*sid", avatar.chars ()); } } - char reject[256] = { 0, }; + char reject[StringBuffer::StaticBufferSize] = { 0, }; MDLL_ClientConnect (bot, bot->v.netname.chars (), strings.format ("127.0.0.%d", clientIndex + 100), reject); if (!strings.isEmpty (reject)) { @@ -1228,7 +1228,9 @@ Bot::Bot (edict_t *bot, int difficulty, int personality, int team, int skin) { m_pathWalk.init (m_planner->getMaxLength ()); // init player models parts enumerator - m_hitboxEnumerator = cr::makeUnique (); + if (cv_use_hitbox_enemy_targeting) { + m_hitboxEnumerator = cr::makeUnique (); + } // bot is not kicked by rotation m_kickedByRotation = false; @@ -1335,21 +1337,23 @@ bool BotManager::isTeamStacked (int team) { return teamCount[team] + 1 > teamCount[team == Team::CT ? Team::Terrorist : Team::CT] + limitTeams; } -void BotManager::erase (Bot *bot) { +void BotManager::disconnectBot (Bot *bot) { for (auto &e : m_bots) { if (e.get () != bot) { continue; } + bot->markStale (); if (!bot->m_kickedByRotation && cv_save_bots_names) { m_saveBotNames.emplaceLast (bot->pev->netname.str ()); } - bot->markStale (); const auto index = m_bots.index (e); e.reset (); m_bots.erase (index, 1); // remove from bots array + bot = nullptr; + break; } } @@ -1556,7 +1560,10 @@ void Bot::newRound () { m_followWaitTime = 0.0f; m_hostages.clear (); - m_hitboxEnumerator->reset (); + + if (cv_use_hitbox_enemy_targeting) { + m_hitboxEnumerator->reset (); + } m_approachingLadderTimer.invalidate (); m_forgetLastVictimTimer.invalidate (); @@ -1745,6 +1752,10 @@ void Bot::kick (bool silent) { } void Bot::markStale () { + // wait till threads tear down + MutexScopedLock lock1 (m_pathFindLock); + MutexScopedLock lock2 (m_predictLock); + // switch chatter icon off showChatterIcon (false, true); @@ -2180,13 +2191,19 @@ void BotThreadWorker::shutdown () { } void BotThreadWorker::startup (int workers) { - String disableWorkerEnv = plat.env ("YAPB_SINGLE_THREADED"); + StringRef disableWorkerEnv = plat.env ("YAPB_SINGLE_THREADED"); // disable on legacy games const bool isLegacyGame = game.is (GameFlags::Legacy); + // do not do any threading when timescale enabled + ConVarRef timescale ("sys_timescale"); + // disable worker if requested via env variable or workers are disabled - if (isLegacyGame || workers == 0 || (!disableWorkerEnv.empty () && disableWorkerEnv == "1")) { + if (isLegacyGame + || workers == 0 + || timescale.value () > 0 + || (!disableWorkerEnv.empty () && disableWorkerEnv == "1")) { return; } m_pool = cr::makeUnique (); diff --git a/src/module.cpp b/src/module.cpp index f5741db..05baaaa 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -117,7 +117,7 @@ CR_EXPORT IYaPBModule *GetBotAPI (int version) { if (version != kYaPBModuleVersion) { return nullptr; } - static YaPBModule botModule; + static YaPBModule botModule {}; return &botModule; } diff --git a/src/navigate.cpp b/src/navigate.cpp index 551caca..a20c132 100644 --- a/src/navigate.cpp +++ b/src/navigate.cpp @@ -3357,7 +3357,7 @@ edict_t *Bot::lookupButton (StringRef target, bool blindTest) { const Vector &pos = game.getEntityOrigin (ent); if (!blindTest) { - game.testLine (pev->origin, pos, TraceIgnore::Monsters, pev->pContainingEntity, &tr); + game.testLine (pev->origin, pos, TraceIgnore::Monsters, this->ent (), &tr); } // check if this place safe @@ -3449,11 +3449,6 @@ void Bot::findShortestPath (int srcIndex, int destIndex) { void Bot::syncFindPath (int srcIndex, int destIndex, FindPath pathType) { // this function finds a path from srcIndex to destIndex; - // stale bots shouldn't do pathfinding - if (m_isStale) { - return; - } - if (!m_pathFindLock.tryLock ()) { return; // allow only single instance of syncFindPath per-bot } @@ -3559,6 +3554,11 @@ void Bot::syncFindPath (int srcIndex, int destIndex, FindPath pathType) { } void Bot::findPath (int srcIndex, int destIndex, FindPath pathType /*= FindPath::Fast */) { + // stale bots shouldn't do pathfinding + if (m_isStale) { + return; + } + worker.enqueue ([this, srcIndex, destIndex, pathType] () { syncFindPath (srcIndex, destIndex, pathType); }); diff --git a/src/practice.cpp b/src/practice.cpp index 81b5d58..2e3b75e 100644 --- a/src/practice.cpp +++ b/src/practice.cpp @@ -163,7 +163,7 @@ void BotPractice::save () { bstor.save (data); } -void BotPractice::load () { +void BotPractice::syncLoad () { if (!graph.length ()) { return; // no action } @@ -182,3 +182,9 @@ void BotPractice::load () { } } +void BotPractice::load () { + worker.enqueue ([this] () { + syncLoad (); + }); +} + diff --git a/src/storage.cpp b/src/storage.cpp index 5ec70aa..eb3e608 100644 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -7,7 +7,7 @@ #include -#if defined (BOT_STORAGE_EXPLICIT_INSTANTIATIONS) +#if defined(BOT_STORAGE_EXPLICIT_INSTANTIATIONS) template bool BotStorage::load (SmallArray &data, ExtenHeader *exten, int32_t *outOptions) { auto type = guessType (); diff --git a/src/support.cpp b/src/support.cpp index e405485..164dca2 100644 --- a/src/support.cpp +++ b/src/support.cpp @@ -436,81 +436,42 @@ StringRef BotSupport::weaponIdToAlias (int32_t id) { return none; } -// helper class for reading wave header -class WaveEndianessHelper final : public NonCopyable { -private: -#if defined (CR_ARCH_CPU_BIG_ENDIAN) - bool little { false }; -#else - bool little { true }; -#endif +float BotSupport::getWaveFileDuration (StringRef filename) { + constexpr auto kZeroLength = 0.0f; -public: - uint16_t read16 (uint16_t value) { - return little ? value : static_cast ((value >> 8) | (value << 8)); - } - - uint32_t read32 (uint32_t value) { - return little ? value : (((value & 0x000000ff) << 24) | ((value & 0x0000ff00) << 8) | ((value & 0x00ff0000) >> 8) | ((value & 0xff000000) >> 24)); - } - - bool isWave (char *format) const { - if (little && memcmp (format, "WAVE", 4) == 0) { - return true; - } - return *reinterpret_cast (format) == 0x57415645; - } -}; - -float BotSupport::getWaveLength (StringRef filename) { + using WaveHeader = WaveHelper <>::Header; auto filePath = strings.joinPath (cv_chatter_path.as (), strings.format ("%s.wav", filename)); MemFile fp (filePath); // we're got valid handle? if (!fp) { - return 0.0f; + return kZeroLength; } - // else fuck with manual search - struct WavHeader { - char riff[4]; - uint32_t chunkSize; - char wave[4]; - char fmt[4]; - uint32_t subchunk1Size; - uint16_t audioFormat; - uint16_t numChannels; - uint32_t sampleRate; - uint32_t byteRate; - uint16_t blockAlign; - uint16_t bitsPerSample; - char dataChunkId[4]; - uint32_t dataChunkLength; - } header {}; + WaveHeader hdr {}; + static WaveHelper wh {}; - static WaveEndianessHelper weh {}; - - if (fp.read (&header, sizeof (WavHeader)) == 0) { - logger.error ("Wave File %s - has wrong or unsupported format", filePath); - return 0.0f; + if (fp.read (&hdr, sizeof (WaveHeader)) == 0) { + logger.error ("WAVE %s - has wrong or unsupported format.", filePath); + return kZeroLength; } fp.close (); - if (!weh.isWave (header.wave)) { - logger.error ("Wave File %s - has wrong wave chunk id", filePath); - return 0.0f; + if (!wh.isWave (hdr.wave)) { + logger.error ("WAVE %s - has wrong wave chunk id.", filePath); + return kZeroLength; } - if (weh.read32 (header.dataChunkLength) == 0) { - logger.error ("Wave File %s - has zero length!", filePath); - return 0.0f; + if (wh.read32 (hdr.dataChunkLength) == 0) { + logger.error ("WAVE %s - has zero length!.", filePath); + return kZeroLength; } - const auto length = static_cast (weh.read32 (header.dataChunkLength)); - const auto bps = static_cast (weh.read16 (header.bitsPerSample)) / 8; - const auto channels = static_cast (weh.read16 (header.numChannels)); - const auto rate = static_cast (weh.read32 (header.sampleRate)); + const auto length = wh.read32 (hdr.dataChunkLength); + const auto bps = wh.read16 (hdr.bitsPerSample) / 8.0f; + const auto channels = wh.read16 (hdr.numChannels); + const auto rate = wh.read32 (hdr.sampleRate); return length / bps / channels / rate; } diff --git a/src/tasks.cpp b/src/tasks.cpp index bf9d1ad..f71c8b9 100644 --- a/src/tasks.cpp +++ b/src/tasks.cpp @@ -331,7 +331,7 @@ void Bot::spraypaint_ () { if (getTask ()->time - 0.5f < game.time ()) { // emit spray can sound - engfuncs.pfnEmitSound (pev->pContainingEntity, CHAN_VOICE, "player/sprayer.wav", 1.0f, ATTN_NORM, 0, 100); + engfuncs.pfnEmitSound (ent (), CHAN_VOICE, "player/sprayer.wav", 1.0f, ATTN_NORM, 0, 100); game.testLine (getEyesPos (), getEyesPos () + forward * 128.0f, TraceIgnore::Monsters, ent (), &tr); @@ -594,7 +594,7 @@ void Bot::blind_ () { m_blindNodeIndex = kInvalidNodeIndex; m_blindMoveSpeed = 0.0f; - m_blindSidemoveSpeed = 0.0f; + m_blindSideMoveSpeed = 0.0f; m_blindButton = 0; m_states |= Sense::SuspectEnemy; @@ -610,7 +610,7 @@ void Bot::blind_ () { } else { m_moveSpeed = m_blindMoveSpeed; - m_strafeSpeed = m_blindSidemoveSpeed; + m_strafeSpeed = m_blindSideMoveSpeed; pev->button |= m_blindButton; m_states |= Sense::SuspectEnemy; diff --git a/src/vision.cpp b/src/vision.cpp index 0d4e7ff..a6b5b69 100644 --- a/src/vision.cpp +++ b/src/vision.cpp @@ -375,14 +375,19 @@ void Frustum::calculate (Planes &planes, const Vector &viewAngle, const Vector & auto fc = viewOffset + forward * kMaxViewDistance; auto nc = viewOffset + forward * kMinViewDistance; - auto fbl = fc + (up * m_farHeight * 0.5f) - (right * m_farWidth * 0.5f); - auto fbr = fc + (up * m_farHeight * 0.5f) + (right * m_farWidth * 0.5f); - auto ftl = fc - (up * m_farHeight * 0.5f) - (right * m_farWidth * 0.5f); - auto ftr = fc - (up * m_farHeight * 0.5f) + (right * m_farWidth * 0.5f); - auto nbl = nc + (up * m_nearHeight * 0.5f) - (right * m_nearWidth * 0.5f); - auto nbr = nc + (up * m_nearHeight * 0.5f) + (right * m_nearWidth * 0.5f); - auto ntl = nc - (up * m_nearHeight * 0.5f) - (right * m_nearWidth * 0.5f); - auto ntr = nc - (up * m_nearHeight * 0.5f) + (right * m_nearWidth * 0.5f); + auto up_half_far = up * m_farHeight * 0.5f; + auto right_half_far = right * m_farWidth * 0.5f; + auto up_half_near = up * m_nearHeight * 0.5f; + auto right_half_near = right * m_nearWidth * 0.5f; + + auto fbl = fc - right_half_far + up_half_far; + auto fbr = fc + right_half_far + up_half_far; + auto ftl = fc - right_half_far - up_half_far; + auto ftr = fc + right_half_far - up_half_far; + auto nbl = nc - right_half_near + up_half_near; + auto nbr = nc + right_half_near + up_half_near; + auto ntl = nc - right_half_near - up_half_near; + auto ntr = nc + right_half_near - up_half_near; auto setPlane = [&] (PlaneSide side, const Vector &v1, const Vector &v2, const Vector &v3) { auto &plane = planes[static_cast (side)]; diff --git a/src/vistable.cpp b/src/vistable.cpp index e2f81c2..40230ca 100644 --- a/src/vistable.cpp +++ b/src/vistable.cpp @@ -124,14 +124,19 @@ void GraphVistable::rebuild () { else { m_sliceIndex += rg (250, 400); } + auto notifyProgress = [] (int value) { + game.print ("Rebuilding vistable... %d%% done.", value); + }; // notify host about rebuilding if (m_notifyMsgTimestamp > 0.0f && m_notifyMsgTimestamp < game.time () && end == m_length) { - game.print ("Rebuilding vistable... %d%% done.", m_curIndex * 100 / m_length); + notifyProgress (m_curIndex * 100 / m_length); m_notifyMsgTimestamp = game.time () + 1.0f; } if (m_curIndex == m_length && end == m_length) { + notifyProgress (100); + m_rebuild = false; m_notifyMsgTimestamp = 0.0f; diff --git a/vc/yapb.vcxproj b/vc/yapb.vcxproj index 7883623..bc07e08 100644 --- a/vc/yapb.vcxproj +++ b/vc/yapb.vcxproj @@ -51,6 +51,7 @@ + @@ -150,7 +151,7 @@ DynamicLibrary false - ClangCL + v143 false false diff --git a/vc/yapb.vcxproj.filters b/vc/yapb.vcxproj.filters index 5de97c1..386c0b3 100644 --- a/vc/yapb.vcxproj.filters +++ b/vc/yapb.vcxproj.filters @@ -195,6 +195,9 @@ inc + + inc\ext\crlib +