diff --git a/regamedll/dlls/API/CAPI_Impl.cpp b/regamedll/dlls/API/CAPI_Impl.cpp index 398c8a136..7708d1f83 100644 --- a/regamedll/dlls/API/CAPI_Impl.cpp +++ b/regamedll/dlls/API/CAPI_Impl.cpp @@ -402,4 +402,19 @@ bool CReGameApi::BGetIGameRules(const char *pchVersion) const return false; } +void CReGameApi::QueueHudMessage(int client, const struct hudtextparms_s &textparms, const char *pMessage) +{ + if (client == 0) + { + for (int i = 1; i <= gpGlobals->maxClients; i++) + { + g_HudQueue.QueueMessage(i, textparms, pMessage); + } + } + else + { + g_HudQueue.QueueMessage(client, textparms, pMessage); + } +} + EXPOSE_SINGLE_INTERFACE(CReGameApi, IReGameApi, VRE_GAMEDLL_API_VERSION); diff --git a/regamedll/dlls/API/CAPI_Impl.h b/regamedll/dlls/API/CAPI_Impl.h index 4bb154a9a..855097c6b 100644 --- a/regamedll/dlls/API/CAPI_Impl.h +++ b/regamedll/dlls/API/CAPI_Impl.h @@ -1116,4 +1116,5 @@ class CReGameApi: public IReGameApi { EXT_FUNC virtual AmmoInfoStruct *GetAmmoInfoEx(const char *ammoName); EXT_FUNC virtual bool BGetICSEntity(const char *pchVersion) const; EXT_FUNC virtual bool BGetIGameRules(const char *pchVersion) const; + EXT_FUNC virtual void QueueHudMessage(int client, const struct hudtextparms_s &textparms, const char *pMessage); }; diff --git a/regamedll/dlls/client.cpp b/regamedll/dlls/client.cpp index daee11d12..917e142fc 100644 --- a/regamedll/dlls/client.cpp +++ b/regamedll/dlls/client.cpp @@ -1,4 +1,6 @@ #include "precompiled.h" +#include "weapons.h" +#include "gamerules.h" int gmsgWeapPickup = 0; int gmsgHudText = 0; @@ -3910,7 +3912,7 @@ void EXT_FUNC PlayerPostThink(edict_t *pEntity) void EXT_FUNC ParmsNewLevel() { - ; + g_HudQueue.Reset(); } void EXT_FUNC ParmsChangeLevel() @@ -3924,6 +3926,60 @@ void EXT_FUNC ParmsChangeLevel() } } +extern cvar_t entity_gc; + +void CollectGarbage() +{ + if (entity_gc.value <= 0.0f) + return; + + // Check if we are near the max entities limit + if (NUMBER_OF_ENTITIES() < (gpGlobals->maxEntities - ENTITY_INTOLERANCE)) + return; + + int count = 0; + // We iterate from oldest (lowest index) to newest + for (int i = 1; i < gpGlobals->maxEntities; i++) + { + edict_t *pEdict = INDEXENT(i); + if (!pEdict || pEdict->free) + continue; + + CBaseEntity *pEntity = CBaseEntity::Instance(pEdict); + if (!pEntity) + continue; + + if (FClassnameIs(pEdict, "weaponbox")) + { + CWeaponBox *pBox = (CWeaponBox *)pEntity; + if (pBox->m_bIsBomb) + continue; // DO NOT remove the C4! + + UTIL_Remove(pEntity); + count++; + } + else if (FClassnameIs(pEdict, "weapon_shield")) + { + UTIL_Remove(pEntity); + count++; + } + else if (FClassnameIs(pEdict, "gib")) + { + UTIL_Remove(pEntity); + count++; + } + + // Stop if we cleared enough entities (e.g., 50 entities) to avoid lagging the server by doing too much at once + if (count >= 50) + break; + } + + if (count > 0) + { + ALERT(at_console, "Garbage Collector: Removed %d old entities to prevent server crash.\n", count); + } +} + void EXT_FUNC StartFrame() { if (g_pGameRules) @@ -3933,6 +3989,9 @@ void EXT_FUNC StartFrame() return; } + CollectGarbage(); + + g_HudQueue.Think(); CLocalNav::Think(); gpGlobals->teamplay = 1.0f; diff --git a/regamedll/dlls/game.cpp b/regamedll/dlls/game.cpp index 7f3e8102f..6fa532f2e 100644 --- a/regamedll/dlls/game.cpp +++ b/regamedll/dlls/game.cpp @@ -22,6 +22,7 @@ cvar_t fragsleft = { "mp_fragsleft", "0", FCVAR_SERVER | FCVAR_UNLOG cvar_t timeleft = { "mp_timeleft", "0", FCVAR_SERVER | FCVAR_UNLOGGED, 0.0f, nullptr }; cvar_t friendlyfire = { "mp_friendlyfire", "0", FCVAR_SERVER, 0.0f, nullptr }; +cvar_t entity_gc = { "mp_entity_gc", "1", 0, 0.0f, nullptr }; cvar_t infiniteAmmo = { "mp_infinite_ammo", "0", FCVAR_SERVER, 0.0f, nullptr }; cvar_t infiniteGrenades = { "mp_infinite_grenades", "0", FCVAR_SERVER, 0.0f, nullptr }; cvar_t allowmonsters = { "mp_allowmonsters", "0", FCVAR_SERVER, 0.0f, nullptr }; @@ -279,6 +280,7 @@ void EXT_FUNC GameDLLInit() CVAR_REGISTER(&displaysoundlist); CVAR_REGISTER(&timelimit); CVAR_REGISTER(&friendlyfire); + CVAR_REGISTER(&entity_gc); #ifdef BUILD_LATEST CVAR_REGISTER(&infiniteAmmo); diff --git a/regamedll/dlls/game.h b/regamedll/dlls/game.h index 32e6ae015..8af25a450 100644 --- a/regamedll/dlls/game.h +++ b/regamedll/dlls/game.h @@ -60,6 +60,7 @@ extern cvar_t fadetoblack; extern cvar_t fragsleft; extern cvar_t timeleft; extern cvar_t friendlyfire; +extern cvar_t entity_gc; extern cvar_t infiniteAmmo; extern cvar_t infiniteGrenades; extern cvar_t allowmonsters; diff --git a/regamedll/dlls/hud_queue.cpp b/regamedll/dlls/hud_queue.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/regamedll/dlls/util.cpp b/regamedll/dlls/util.cpp index e0ffed532..36c8d442f 100644 --- a/regamedll/dlls/util.cpp +++ b/regamedll/dlls/util.cpp @@ -592,47 +592,128 @@ void UTIL_ScreenFade(CBaseEntity *pEntity, const Vector &color, float fadeTime, UTIL_ScreenFadeWrite(fade, pEntity); } +CHudMessageQueue g_HudQueue; + +CHudMessageQueue::CHudMessageQueue() { + Reset(); +} + +void CHudMessageQueue::Reset() { + for (int i = 0; i < 33; i++) { + for (int j = 0; j < NUM_HUD_CHANNELS; j++) { + m_players[i].channelFreeTime[j] = 0.0f; + } + for (int j = 0; j < MAX_HUD_QUEUE; j++) { + m_players[i].messages[j].inUse = false; + } + } +} + +void CHudMessageQueue::QueueMessage(int client, const hudtextparms_t &textparms, const char *pMessage) { + if (client < 1 || client >= 33) return; + + int freeSlot = -1; + for (int i = 0; i < MAX_HUD_QUEUE; i++) { + if (!m_players[client].messages[i].inUse) { + freeSlot = i; + break; + } + } + + if (freeSlot == -1) { + return; + } + + m_players[client].messages[freeSlot].textparms = textparms; + Q_strlcpy(m_players[client].messages[freeSlot].message, pMessage, sizeof(m_players[client].messages[freeSlot].message)); + m_players[client].messages[freeSlot].inUse = true; +} + +void CHudMessageQueue::Think() { + float currentTime = gpGlobals->time; + + for (int i = 1; i <= gpGlobals->maxClients; i++) { + CBaseEntity *pPlayer = UTIL_PlayerByIndex(i); + if (!pPlayer || !pPlayer->IsNetClient()) + continue; + + for (int j = 0; j < MAX_HUD_QUEUE; j++) { + if (!m_players[i].messages[j].inUse) + continue; + + hudtextparms_t parms = m_players[i].messages[j].textparms; + + int targetChannel = parms.channel - 1; + if (parms.channel <= 0 || parms.channel > NUM_HUD_CHANNELS) { + int bestChannel = 0; + float oldestTime = m_players[i].channelFreeTime[0]; + for (int c = 1; c < NUM_HUD_CHANNELS; c++) { + if (m_players[i].channelFreeTime[c] < oldestTime) { + oldestTime = m_players[i].channelFreeTime[c]; + bestChannel = c; + } + } + targetChannel = bestChannel; + parms.channel = bestChannel + 1; + } + + if (currentTime >= m_players[i].channelFreeTime[targetChannel]) { + UTIL_HudMessage(pPlayer, parms, m_players[i].messages[j].message); + m_players[i].channelFreeTime[targetChannel] = currentTime + parms.fadeinTime + parms.holdTime + parms.fadeoutTime + 0.2f; + + m_players[i].messages[j].inUse = false; + } + } + } +} + void UTIL_HudMessage(CBaseEntity *pEntity, const hudtextparms_t &textparms, const char *pMessage) { if (!pEntity || !pEntity->IsNetClient()) return; MESSAGE_BEGIN(MSG_ONE, SVC_TEMPENTITY, nullptr, pEntity->edict()); - WRITE_BYTE(TE_TEXTMESSAGE); - WRITE_BYTE(textparms.channel & 0xFF); - WRITE_SHORT(FixedSigned16(textparms.x, (1<<13))); - WRITE_SHORT(FixedSigned16(textparms.y, (1<<13))); - WRITE_BYTE(textparms.effect); - WRITE_BYTE(textparms.r1); - WRITE_BYTE(textparms.g1); - WRITE_BYTE(textparms.b1); - WRITE_BYTE(textparms.a1); - WRITE_BYTE(textparms.r2); - WRITE_BYTE(textparms.g2); - WRITE_BYTE(textparms.b2); - WRITE_BYTE(textparms.a2); - WRITE_SHORT(FixedUnsigned16(textparms.fadeinTime, (1<<8))); - WRITE_SHORT(FixedUnsigned16(textparms.fadeoutTime, (1<<8))); - WRITE_SHORT(FixedUnsigned16(textparms.holdTime, (1<<8))); - - if (textparms.effect == 2) - WRITE_SHORT(FixedUnsigned16(textparms.fxTime, (1<<8))); - - if (!pMessage) - WRITE_STRING(" "); + WRITE_BYTE(TE_TEXTMESSAGE); + WRITE_BYTE(textparms.channel & 0xFF); + + WRITE_SHORT(FixedSigned16(textparms.x, 1 << 13)); + WRITE_SHORT(FixedSigned16(textparms.y, 1 << 13)); + WRITE_BYTE(textparms.effect); + + WRITE_BYTE(textparms.r1); + WRITE_BYTE(textparms.g1); + WRITE_BYTE(textparms.b1); + WRITE_BYTE(textparms.a1); + + WRITE_BYTE(textparms.r2); + WRITE_BYTE(textparms.g2); + WRITE_BYTE(textparms.b2); + WRITE_BYTE(textparms.a2); + + WRITE_SHORT(FixedUnsigned16(textparms.fadeinTime, 1 << 8)); + WRITE_SHORT(FixedUnsigned16(textparms.fadeoutTime, 1 << 8)); + WRITE_SHORT(FixedUnsigned16(textparms.holdTime, 1 << 8)); + + if (textparms.effect == 2) + WRITE_SHORT(FixedUnsigned16(textparms.fxTime, 1 << 8)); + + if (!pMessage) + { + WRITE_STRING(" "); + } + else + { + if (Q_strlen(pMessage) >= 512) + { + char tmp[512]; + Q_strlcpy(tmp, pMessage); + WRITE_STRING(tmp); + } else { - if (Q_strlen(pMessage) >= 512) - { - char tmp[512]; - Q_strlcpy(tmp, pMessage); - WRITE_STRING(tmp); - } - else - { - WRITE_STRING(pMessage); - } + WRITE_STRING(pMessage); } + } MESSAGE_END(); } diff --git a/regamedll/dlls/util.h b/regamedll/dlls/util.h index f96be669b..6b55681c7 100644 --- a/regamedll/dlls/util.h +++ b/regamedll/dlls/util.h @@ -227,6 +227,36 @@ void UTIL_ScreenFadeBuild(ScreenFade &fade, const Vector &color, float fadeTime, void UTIL_ScreenFadeWrite(const ScreenFade &fade, CBaseEntity *pEntity); void UTIL_ScreenFadeAll(const Vector &color, float fadeTime, float fadeHold, int alpha, int flags); void UTIL_ScreenFade(CBaseEntity *pEntity, const Vector &color, float fadeTime, float fadeHold = 0.0f, int alpha = 0, int flags = 0); +// ---------------------------------------------------- +// Native HUD Messages Queue +// ---------------------------------------------------- +#define MAX_HUD_QUEUE 10 +#define NUM_HUD_CHANNELS 4 + +struct QueuedHudMessage_t { + hudtextparms_t textparms; + char message[512]; + bool inUse; +}; + +class CHudMessageQueue { +public: + CHudMessageQueue(); + + void QueueMessage(int client, const hudtextparms_t &textparms, const char *pMessage); + void Think(); + void Reset(); + +private: + struct PlayerHudQueue { + QueuedHudMessage_t messages[MAX_HUD_QUEUE]; + float channelFreeTime[NUM_HUD_CHANNELS]; + }; + PlayerHudQueue m_players[33]; +}; + +extern CHudMessageQueue g_HudQueue; + void UTIL_HudMessage(CBaseEntity *pEntity, const hudtextparms_t &textparms, const char *pMessage); void UTIL_HudMessageAll(const hudtextparms_t &textparms, const char *pMessage); void UTIL_ClientPrintAll(int msg_dest, const char *msg_name, const char *param1 = nullptr, const char *param2 = nullptr, const char *param3 = nullptr, const char *param4 = nullptr); diff --git a/regamedll/public/regamedll/regamedll_api.h b/regamedll/public/regamedll/regamedll_api.h index 466ec4b42..2cbb06a3d 100644 --- a/regamedll/public/regamedll/regamedll_api.h +++ b/regamedll/public/regamedll/regamedll_api.h @@ -856,6 +856,7 @@ class IReGameApi { virtual struct AmmoInfoStruct *GetAmmoInfoEx(const char *ammoName) = 0; virtual bool BGetICSEntity(const char *pchVersion) const = 0; virtual bool BGetIGameRules(const char *pchVersion) const = 0; + virtual void QueueHudMessage(int client, const struct hudtextparms_s &textparms, const char *pMessage) = 0; }; #define VRE_GAMEDLL_API_VERSION "VRE_GAMEDLL_API_VERSION001"