From 296d3a3637f73096d738c0d32061971cdb2732fe Mon Sep 17 00:00:00 2001 From: jeefo Date: Thu, 29 Aug 2019 16:24:24 +0300 Subject: [PATCH] Added forced yb_csdm_mode. Do not export functions that aren't needed for plugin loading. --- include/crlib/cr-platform.h | 6 +- include/engine.h | 2 +- include/message.h | 2 +- source/android.cpp | 15 +++ source/basecode.cpp | 11 +- source/engine.cpp | 30 +++++- source/linkage.cpp | 197 +++++++++++++++++++----------------- source/message.cpp | 8 +- 8 files changed, 163 insertions(+), 108 deletions(-) diff --git a/include/crlib/cr-platform.h b/include/crlib/cr-platform.h index 2f2c59b..aafe0d9 100644 --- a/include/crlib/cr-platform.h +++ b/include/crlib/cr-platform.h @@ -35,11 +35,13 @@ CR_NAMESPACE_BEGIN #endif // configure macroses +#define CR_LINKAGE_C extern "C" + #if defined(CR_WINDOWS) -# define CR_EXPORT extern "C" __declspec (dllexport) +# define CR_EXPORT CR_LINKAGE_C __declspec (dllexport) # define CR_STDCALL __stdcall #elif defined(CR_LINUX) || defined(CR_OSX) -# define CR_EXPORT extern "C" __attribute__((visibility("default"))) +# define CR_EXPORT CR_LINKAGE_C __attribute__((visibility("default"))) # define CR_STDCALL #else # error "Can't configure export macros. Compiler unrecognized." diff --git a/include/engine.h b/include/engine.h index 364c020..194958a 100644 --- a/include/engine.h +++ b/include/engine.h @@ -171,7 +171,7 @@ public: bool postload (); // detects if csdm mod is in use - void detectDeathmatch (); + void applyGameModes (); // executes stuff every 1 second void slowFrame (); diff --git a/include/message.h b/include/message.h index a23571e..cdef77f 100644 --- a/include/message.h +++ b/include/message.h @@ -121,7 +121,7 @@ public: ~MessageDispatcher () = default; public: - void registerMessage (const String &name, int32 id); + int32 add (const String &name, int32 id); void start (edict_t *ent, int32 type); void stop (); diff --git a/source/android.cpp b/source/android.cpp index 7431e32..9941fc5 100644 --- a/source/android.cpp +++ b/source/android.cpp @@ -11,6 +11,21 @@ // until hook code will be compatible with ARM, it's here #if defined (CR_ANDROID) && defined(CR_ARCH_ARM) + +CR_EXPORT int Server_GetBlendingInterface (int version, struct sv_blending_interface_s **ppinterface, struct engine_studio_api_s *pstudio, float *rotationmatrix, float *bonetransform) { + // 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.lib ().resolve (__FUNCTION__); + + 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); +} + void android_LinkEntity (EntityFunction &addr, const char *name, entvars_t *pev) { if (!addr) { addr = game.lib ().resolve (name); diff --git a/source/basecode.cpp b/source/basecode.cpp index 06362f4..63b7c0f 100644 --- a/source/basecode.cpp +++ b/source/basecode.cpp @@ -1295,11 +1295,14 @@ void Bot::buyStuff () { const int *pref = conf.getWeaponPrefs (m_personality) + kNumWeapons; auto tab = conf.getRawWeapons (); - bool isPistolMode = tab[25].teamStandard == -1 && tab[3].teamStandard == 2; - bool teamEcoValid = bots.checkTeamEco (m_team); + const bool isPistolMode = tab[25].teamStandard == -1 && tab[3].teamStandard == 2; + const bool teamEcoValid = bots.checkTeamEco (m_team); // do this, because xash engine is not capable to run all the features goldsrc, but we have cs 1.6 on it, so buy table must be the same - bool isOldGame = game.is (GameFlags::Legacy) && !game.is (GameFlags::Xash3D); + const bool isOldGame = game.is (GameFlags::Legacy) && !game.is (GameFlags::Xash3D); + + const bool hasDefaultPistols = (pev->weapons & (cr::bit (Weapon::USP) | cr::bit (Weapon::Glock18))); + const bool isFirstRound = m_moneyAmount == mp_startmoney.int_ (); switch (m_buyState) { case BuyState::PrimaryWeapon: // if no primary weapon and bot has some money, buy a primary weapon @@ -1493,7 +1496,7 @@ void Bot::buyStuff () { break; case BuyState::SecondaryWeapon: // if bot has still some money, buy a better secondary weapon - if (isPistolMode || (hasPrimaryWeapon () && (pev->weapons & (cr::bit (Weapon::USP) | cr::bit (Weapon::Glock18))) && m_moneyAmount > rg.int_ (7500, 9000))) { + if (isPistolMode || (isFirstRound && hasDefaultPistols) || (hasDefaultPistols && bots.getLastWinner () != m_team) || (hasPrimaryWeapon () && hasDefaultPistols) && m_moneyAmount > rg.int_ (7500, 9000)) { do { pref--; diff --git a/source/engine.cpp b/source/engine.cpp index c03e64b..a184f92 100644 --- a/source/engine.cpp +++ b/source/engine.cpp @@ -9,6 +9,8 @@ #include +ConVar yb_csdm_mode ("yb_csdm_mode", "0", "Enables or disables CSDM / FFA mode for bots.\nAllowed values: '0', '1', '2', '3'.\nIf '0', CSDM / FFA mode is auto-detected.\nIf '1', CSDM mode is enabled, but FFA is disabled.\nIf '2' CSDM and FFA mode is enabled.\nIf '3' CSDM and FFA mode is disabled.", true, 0.0f, 3.0f); + ConVar sv_skycolor_r ("sv_skycolor_r", nullptr, Var::NoRegister); ConVar sv_skycolor_g ("sv_skycolor_g", nullptr, Var::NoRegister); ConVar sv_skycolor_b ("sv_skycolor_b", nullptr, Var::NoRegister); @@ -766,10 +768,34 @@ bool Game::postload () { return false; } -void Game::detectDeathmatch () { +void Game::applyGameModes () { if (!is (GameFlags::Metamod | GameFlags::ReGameDLL)) { return; } + + // handle cvar cases + switch (yb_csdm_mode.int_ ()) { + default: + case 0: + break; + + // force CSDM mode + case 1: + m_gameFlags |= GameFlags::CSDM; + m_gameFlags &= ~GameFlags::FreeForAll; + return; + + // force CSDM FFA mode + case 2: + m_gameFlags |= GameFlags::CSDM | GameFlags::FreeForAll; + return; + + // force disable everything + case 3: + m_gameFlags &= ~(GameFlags::CSDM | GameFlags::FreeForAll); + return; + } + static auto dmActive = engfuncs.pfnCVarGetPointer ("csdm_active"); static auto freeForAll = engfuncs.pfnCVarGetPointer ("mp_freeforall"); @@ -810,7 +836,7 @@ void Game::slowFrame () { graph.initLightLevels (); // detect csdm - detectDeathmatch (); + applyGameModes (); // check the cvar bounds checkCvarsBounds (); diff --git a/source/linkage.cpp b/source/linkage.cpp index c869e46..7904486 100644 --- a/source/linkage.cpp +++ b/source/linkage.cpp @@ -74,7 +74,7 @@ namespace variadic { } } -CR_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) { +CR_EXPORT int GetEntityAPI2 (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 @@ -85,7 +85,7 @@ CR_EXPORT int GetEntityAPI2 (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). - memset (functionTable, 0, sizeof (gamefuncs_t)); + memset (table, 0, sizeof (gamefuncs_t)); if (!(game.is (GameFlags::Metamod))) { auto api_GetEntityAPI = game.lib ().resolve ("GetEntityAPI"); @@ -97,10 +97,10 @@ CR_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) { dllfuncs.dllapi_table = &dllapi; gpGamedllFuncs = &dllfuncs; - memcpy (functionTable, &dllapi, sizeof (gamefuncs_t)); + memcpy (table, &dllapi, sizeof (gamefuncs_t)); } - functionTable->pfnGameInit = [] () { + table->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 @@ -127,7 +127,7 @@ CR_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) { dllapi.pfnGameInit (); }; - 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, @@ -146,7 +146,7 @@ CR_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) { return result; }; - functionTable->pfnTouch = [] (edict_t *pentTouched, edict_t *pentOther) { + table->pfnTouch = [] (edict_t *pentTouched, edict_t *pentOther) { // 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 @@ -161,7 +161,7 @@ CR_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) { if (!game.isNullEntity (pentTouched) && pentOther != game.getStartEntity ()) { auto bot = bots[pentTouched]; - if (bot && pentOther != bot->ent () && !util.isPlayer (pentOther)) { + if (bot && game.isShootableBreakable (pentOther)) { bot->checkBreakable (pentOther); } } @@ -172,7 +172,7 @@ CR_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) { dllapi.pfnTouch (pentTouched, pentOther); }; - functionTable->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]) { // 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 @@ -209,7 +209,7 @@ CR_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) { return dllapi.pfnClientConnect (ent, name, addr, rejectReason); }; - functionTable->pfnClientDisconnect = [] (edict_t *ent) { + table->pfnClientDisconnect = [] (edict_t *ent) { // 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 @@ -238,7 +238,7 @@ CR_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) { dllapi.pfnClientDisconnect (ent); }; - functionTable->pfnClientUserInfoChanged = [] (edict_t *ent, char *infobuffer) { + table->pfnClientUserInfoChanged = [] (edict_t *ent, char *infobuffer) { // 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 @@ -252,7 +252,7 @@ CR_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) { dllapi.pfnClientUserInfoChanged (ent, infobuffer); }; - functionTable->pfnClientCommand = [] (edict_t *ent) { + table->pfnClientCommand = [] (edict_t *ent) { // 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 @@ -289,7 +289,7 @@ CR_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) { dllapi.pfnClientCommand (ent); }; - functionTable->pfnServerActivate = [] (edict_t *pentEdictList, int edictCount, int clientMax) { + table->pfnServerActivate = [] (edict_t *pentEdictList, int edictCount, int clientMax) { // 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 @@ -325,7 +325,7 @@ CR_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) { graph.rebuildVisibility (); }; - functionTable->pfnServerDeactivate = [] () { + table->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 @@ -366,7 +366,7 @@ CR_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) { dllapi.pfnServerDeactivate (); }; - functionTable->pfnStartFrame = [] () { + 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 @@ -408,7 +408,7 @@ CR_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) { bots.frame (); }; - functionTable->pfnCmdStart = [] (const edict_t *player, usercmd_t *cmd, unsigned int random_seed) { + table->pfnCmdStart = [] (const edict_t *player, usercmd_t *cmd, unsigned int random_seed) { auto ent = const_cast (player); // if we're handle pings for bots and clients, clear IN_SCORE button so SV_ShouldUpdatePing engine function return false @@ -428,7 +428,7 @@ CR_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) { dllapi.pfnCmdStart (player, cmd, random_seed); }; - functionTable->pfnPM_Move = [] (playermove_t *playerMove, int server) { + table->pfnPM_Move = [] (playermove_t *playerMove, int server) { // 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 @@ -444,7 +444,7 @@ CR_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) { return TRUE; } -CR_EXPORT int GetEntityAPI2_Post (gamefuncs_t *table, int *) { +CR_LINKAGE_C 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 @@ -468,7 +468,7 @@ CR_EXPORT int GetEntityAPI2_Post (gamefuncs_t *table, int *) { if (ent->v.rendermode == kRenderTransTexture) { ent->v.flags &= ~FL_WORLDBRUSH; // clear the FL_WORLDBRUSH flag out of transparent ents } - RETURN_META_VALUE (MRES_IGNORED, 0); + RETURN_META_VALUE (MRES_HANDLED, 0); }; table->pfnStartFrame = [] () { @@ -501,30 +501,12 @@ CR_EXPORT int GetEntityAPI2_Post (gamefuncs_t *table, int *) { return TRUE; } -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.lib ().resolve (__FUNCTION__); - - if (!api_GetNewDLLFunctions || !api_GetNewDLLFunctions (functionTable, interfaceVersion)) { - logger.error ("Could not resolve symbol \"%s\" in the game dll. Continuing...", __FUNCTION__); - return FALSE; - } - - dllfuncs.newapi_table = functionTable; - return TRUE; -} - -CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) { +CR_LINKAGE_C int GetEngineFunctions (enginefuncs_t *table, int *) { if (game.is (GameFlags::Metamod)) { - memset (functionTable, 0, sizeof (enginefuncs_t)); + memset (table, 0, sizeof (enginefuncs_t)); } - functionTable->pfnChangeLevel = [] (char *s1, char *s2) { + table->pfnChangeLevel = [] (char *s1, char *s2) { // the purpose of this function is to ask the engine to shutdown the server and restart a // new one running the map whose name is s1. It is used ONLY IN SINGLE PLAYER MODE and is // transparent to the user, because it saves the player state and equipment and restores it @@ -543,7 +525,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) { engfuncs.pfnChangeLevel (s1, s2); }; - functionTable->pfnLightStyle = [] (int style, char *val) { + table->pfnLightStyle = [] (int style, char *val) { // ths function update lightstyle for the bots illum.updateLight (style, val); @@ -555,7 +537,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) { }; if (game.is (GameFlags::Legacy)) { - functionTable->pfnFindEntityByString = [] (edict_t *edictStartSearchAfter, const char *field, const char *value) { + table->pfnFindEntityByString = [] (edict_t *edictStartSearchAfter, const char *field, const char *value) { // round starts in counter-strike 1.5 if (strcmp (value, "info_map_parameters") == 0) { bots.initRound (); @@ -568,7 +550,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) { }; } - functionTable->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) { // 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 @@ -587,7 +569,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) { engfuncs.pfnEmitSound (entity, channel, sample, volume, attenuation, flags, pitch); }; - functionTable->pfnMessageBegin = [] (int msgDest, int msgType, const float *origin, edict_t *ed) { + table->pfnMessageBegin = [] (int msgDest, int msgType, const float *origin, edict_t *ed) { // this function called each time a message is about to sent. msgs.start (ed, msgType); @@ -598,16 +580,16 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) { engfuncs.pfnMessageBegin (msgDest, msgType, origin, ed); }; - functionTable->pfnMessageEnd = [] () { - msgs.stop (); + if (!game.is (GameFlags::Metamod)) { + table->pfnMessageEnd = [] () { + engfuncs.pfnMessageEnd (); - if (game.is (GameFlags::Metamod)) { - RETURN_META (MRES_IGNORED); - } - engfuncs.pfnMessageEnd (); - }; + // this allows us to send messages right in handler code + msgs.stop (); + }; + } - functionTable->pfnWriteByte = [] (int value) { + table->pfnWriteByte = [] (int value) { // if this message is for a bot, call the client message function... msgs.collect (value); @@ -617,7 +599,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) { engfuncs.pfnWriteByte (value); }; - functionTable->pfnWriteChar = [] (int value) { + table->pfnWriteChar = [] (int value) { // if this message is for a bot, call the client message function... msgs.collect (value); @@ -627,7 +609,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) { engfuncs.pfnWriteChar (value); }; - functionTable->pfnWriteShort = [] (int value) { + table->pfnWriteShort = [] (int value) { // if this message is for a bot, call the client message function... msgs.collect (value); @@ -637,7 +619,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) { engfuncs.pfnWriteShort (value); }; - functionTable->pfnWriteLong = [] (int value) { + table->pfnWriteLong = [] (int value) { // if this message is for a bot, call the client message function... msgs.collect (value); @@ -647,7 +629,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) { engfuncs.pfnWriteLong (value); }; - functionTable->pfnWriteAngle = [] (float value) { + table->pfnWriteAngle = [] (float value) { // if this message is for a bot, call the client message function... msgs.collect (value); @@ -657,7 +639,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) { engfuncs.pfnWriteAngle (value); }; - functionTable->pfnWriteCoord = [] (float value) { + table->pfnWriteCoord = [] (float value) { // if this message is for a bot, call the client message function... msgs.collect (value); @@ -667,7 +649,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) { engfuncs.pfnWriteCoord (value); }; - functionTable->pfnWriteString = [] (const char *sz) { + table->pfnWriteString = [] (const char *sz) { // if this message is for a bot, call the client message function... msgs.collect (sz); @@ -677,7 +659,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) { engfuncs.pfnWriteString (sz); }; - functionTable->pfnWriteEntity = [] (int value) { + table->pfnWriteEntity = [] (int value) { // if this message is for a bot, call the client message function... msgs.collect (value); @@ -687,29 +669,23 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) { engfuncs.pfnWriteEntity (value); }; - functionTable->pfnRegUserMsg = [] (const char *name, int size) { - // 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 - // messages just can't be the same for every MOD. Hence here the MOD DLL tells the engine, - // "Hey, you have to know that I use a network message whose name is pszName and it is size - // packets long". The engine books it, and returns the ID number under which he recorded that - // custom message. Thus every time the MOD DLL will be wanting to send a message named pszName - // 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 (GameFlags::Metamod)) { + table->pfnRegUserMsg = [] (const char *name, int size) { + // 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 + // messages just can't be the same for every MOD. Hence here the MOD DLL tells the engine, + // "Hey, you have to know that I use a network message whose name is pszName and it is size + // packets long". The engine books it, and returns the ID number under which he recorded that + // custom message. Thus every time the MOD DLL will be wanting to send a message named pszName + // using pfnMessageBegin (), it will know what message ID number to send, and the engine will + // know what to do, only for non-metamod version - int message = engfuncs.pfnRegUserMsg (name, size); + return msgs.add (name, engfuncs.pfnRegUserMsg (name, size)); // return privously registered message + }; + } - // register message for our needs - msgs.registerMessage (name, message); - - if (game.is (GameFlags::Metamod)) { - RETURN_META_VALUE (MRES_SUPERCEDE, message); - } - return message; - }; - - functionTable->pfnClientPrintf = [] (edict_t *ent, PRINT_TYPE printType, const char *message) { + table->pfnClientPrintf = [] (edict_t *ent, PRINT_TYPE printType, const char *message) { // 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, @@ -729,7 +705,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) { engfuncs.pfnClientPrintf (ent, printType, message); }; - functionTable->pfnCmd_Args = [] () { + table->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 @@ -751,7 +727,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) { return engfuncs.pfnCmd_Args (); // ask the client command string to the engine }; - functionTable->pfnCmd_Argv = [] (int argc) { + table->pfnCmd_Argv = [] (int argc) { // 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 @@ -773,7 +749,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) { return engfuncs.pfnCmd_Argv (argc); // ask the argument number "argc" to the engine }; - functionTable->pfnCmd_Argc = [] () { + table->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 @@ -795,7 +771,7 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) { return engfuncs.pfnCmd_Argc (); // ask the engine how many arguments there are }; - functionTable->pfnSetClientMaxspeed = [] (const edict_t *ent, float newMaxspeed) { + table->pfnSetClientMaxspeed = [] (const edict_t *ent, float newMaxspeed) { auto bot = bots[const_cast (ent)]; // check wether it's not a bot @@ -809,26 +785,57 @@ CR_EXPORT int GetEngineFunctions (enginefuncs_t *functionTable, int *) { engfuncs.pfnSetClientMaxspeed (ent, newMaxspeed); }; - functionTable->pfnClientCommand = variadic::clientCommand; + table->pfnClientCommand = variadic::clientCommand; return TRUE; } -#if defined (CR_ANDROID) -CR_EXPORT int Server_GetBlendingInterface (int version, struct sv_blending_interface_s **ppinterface, struct engine_studio_api_s *pstudio, float *rotationmatrix, float *bonetransform) { - // 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. +CR_EXPORT int GetNewDLLFunctions (newgamefuncs_t *table, 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_GetBlendingInterface = game.lib ().resolve (__FUNCTION__); + auto api_GetNewDLLFunctions = game.lib ().resolve (__FUNCTION__); - if (!api_GetBlendingInterface) { + if (!api_GetNewDLLFunctions || !api_GetNewDLLFunctions (table, interfaceVersion)) { logger.error ("Could not resolve symbol \"%s\" in the game dll. Continuing...", __FUNCTION__); return FALSE; } - return api_GetBlendingInterface (version, ppinterface, pstudio, rotationmatrix, bonetransform); + + dllfuncs.newapi_table = table; + return TRUE; +} + +CR_LINKAGE_C int GetEngineFunctions_Post (enginefuncs_t *table, int *) { + memset (table, 0, sizeof (enginefuncs_t)); + + table->pfnMessageEnd = [] () { + msgs.stop (); // this allows us to send messages right in handler code + + RETURN_META (MRES_IGNORED); + }; + + table->pfnRegUserMsg = [] (const char *name, int) { + // 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 + // messages just can't be the same for every MOD. Hence here the MOD DLL tells the engine, + // "Hey, you have to know that I use a network message whose name is pszName and it is size + // packets long". The engine books it, and returns the ID number under which he recorded that + // custom message. Thus every time the MOD DLL will be wanting to send a message named pszName + // using pfnMessageBegin (), it will know what message ID number to send, and the engine will + // know what to do, only for non-metamod version + + // register message for our needs + msgs.add (name, META_RESULT_ORIG_RET (int)); + + RETURN_META_VALUE (MRES_IGNORED, 0); + }; + + return TRUE; } -#endif 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 @@ -855,7 +862,7 @@ CR_EXPORT int Meta_Attach (PLUG_LOADTIME now, metamod_funcs_t *functionTable, me nullptr, // pfnGetNewDLLFunctions () nullptr, // pfnGetNewDLLFunctions_Post () GetEngineFunctions, // pfnGetEngineFunctions () - nullptr, // pfnGetEngineFunctions_Post () + GetEngineFunctions_Post, // pfnGetEngineFunctions_Post () }; if (now > Plugin_info.loadable) { @@ -904,7 +911,7 @@ CR_EXPORT void Meta_Init () { # pragma comment(linker, "/SECTION:.data,RW") # endif # if defined(CR_CXX_MSVC) && !defined(CR_ARCH_X64) -# define DLL_GIVEFNPTRSTODLL extern "C" void CR_STDCALL +# define DLL_GIVEFNPTRSTODLL CR_LINKAGE_C void CR_STDCALL # elif defined(CR_CXX_CLANG) || defined(CR_ARCH_X64) # define DLL_GIVEFNPTRSTODLL CR_EXPORT void CR_STDCALL # endif diff --git a/source/message.cpp b/source/message.cpp index 996031d..fabac6c 100644 --- a/source/message.cpp +++ b/source/message.cpp @@ -463,11 +463,13 @@ MessageDispatcher::MessageDispatcher () { m_teamInfoCache["CT"] = Team::CT; } -void MessageDispatcher::registerMessage (const String &name, int32 id) { +int32 MessageDispatcher::add (const String &name, int32 id) { if (!m_wanted.exists (name)) { - return; + return id; } - m_maps[m_wanted[name]] = id; // add message from engine RegUserMsg + m_maps[m_wanted[name]] = id; // add message from engine regusermsg + + return id; } void MessageDispatcher::start (edict_t *ent, int32 type) {