From d350dea5f829353d05ce8f95f7cdc08a8a8f78b9 Mon Sep 17 00:00:00 2001 From: tmp64 Date: Sat, 16 Dec 2023 10:27:15 +0700 Subject: [PATCH] Server: Split long HUD messages (#198) Resolves #198 --- src/game/server/util.cpp | 68 +++++++++++++++++++++++++++++++++++----- src/game/server/util.h | 12 +++++++ 2 files changed, 72 insertions(+), 8 deletions(-) diff --git a/src/game/server/util.cpp b/src/game/server/util.cpp index 720b0d39..2613a90b 100644 --- a/src/game/server/util.cpp +++ b/src/game/server/util.cpp @@ -829,6 +829,12 @@ void UTIL_ScreenFade(CBaseEntity *pEntity, const Vector &color, float fadeTime, } void UTIL_HudMessage(CBaseEntity *pEntity, const hudtextparms_t &textparms, const char *pMessage) +{ + const char *splitMsg = UTIL_SplitHudMessage(pMessage); + UTIL_HudMessageRaw(pEntity, textparms, splitMsg); +} + +void UTIL_HudMessageRaw(CBaseEntity *pEntity, const hudtextparms_t &textparms, const char *pMessage) { if (!pEntity || !pEntity->IsNetClient()) return; @@ -874,13 +880,13 @@ void UTIL_HudMessage(CBaseEntity *pEntity, const hudtextparms_t &textparms, cons void UTIL_HudMessageAll(const hudtextparms_t &textparms, const char *pMessage) { - int i; + const char *splitMsg = UTIL_SplitHudMessage(pMessage); - for (i = 1; i <= gpGlobals->maxClients; i++) + for (int i = 1; i <= gpGlobals->maxClients; i++) { CBaseEntity *pPlayer = UTIL_PlayerByIndex(i); if (pPlayer) - UTIL_HudMessage(pPlayer, textparms, pMessage); + UTIL_HudMessageRaw(pPlayer, textparms, splitMsg); } } @@ -968,7 +974,48 @@ char *UTIL_dtos4(int d) return buf; } -void UTIL_ShowMessage(const char *pString, CBaseEntity *pEntity) +char *UTIL_SplitHudMessage(const char *src) +{ + // Copied from AMX Mod X: https://github.com/alliedmodders/amxmodx/blob/27f451a868c3154a0ab0d4d70e0a650074fd485d/amxmodx/util.cpp#L148-L183 + // Licensed under the GNU General Public License, version 3 or higher. + static char message[512]; + short b = 0, d = 0, e = 0, c = -1; + + while (src[d] && e < 480) + { + if (src[d] == ' ') + { + c = e; + } + else if (src[d] == '\n') + { + c = -1; + b = 0; + } + + message[e++] = src[d++]; + + if (++b == 69) + { + if (c == -1) + { + message[e++] = '\n'; + b = 0; + } + else + { + message[c] = '\n'; + b = e - c - 1; + c = -1; + } + } + } + + message[e] = 0; + return message; +} + +void UTIL_ShowMessageRaw(const char *pString, CBaseEntity *pEntity) { if (!pEntity || !pEntity->IsNetClient()) return; @@ -978,17 +1025,22 @@ void UTIL_ShowMessage(const char *pString, CBaseEntity *pEntity) MESSAGE_END(); } +void UTIL_ShowMessage(const char *pString, CBaseEntity *pEntity) +{ + const char *splitMsg = UTIL_SplitHudMessage(pString); + UTIL_ShowMessageRaw(splitMsg, pEntity); +} + void UTIL_ShowMessageAll(const char *pString) { - int i; + const char *splitMsg = UTIL_SplitHudMessage(pString); // loop through all players - - for (i = 1; i <= gpGlobals->maxClients; i++) + for (int i = 1; i <= gpGlobals->maxClients; i++) { CBaseEntity *pPlayer = UTIL_PlayerByIndex(i); if (pPlayer) - UTIL_ShowMessage(pString, pPlayer); + UTIL_ShowMessageRaw(splitMsg, pPlayer); } } diff --git a/src/game/server/util.h b/src/game/server/util.h index 2ec8cbbb..ac45e7eb 100644 --- a/src/game/server/util.h +++ b/src/game/server/util.h @@ -276,8 +276,19 @@ extern void UTIL_EmitAmbientSound(edict_t *entity, const Vector &vecOrigin, cons extern void UTIL_ParticleEffect(const Vector &vecOrigin, const Vector &vecDirection, ULONG ulColor, ULONG ulCount); extern void UTIL_ScreenShake(const Vector ¢er, float amplitude, float frequency, float duration, float radius); extern void UTIL_ScreenShakeAll(const Vector ¢er, float amplitude, float frequency, float duration); + +//! Splits any lines in a string longer than 68 characters. +//! Not UTF-8-aware. Will split by words if possible. +//! Prevents a client crash due to a buffer overflow. +//! See tmp64/BugfixedHL-Rebased#198 and ValveSoftware/halflife#3705. +//! @param src Original message text. Size after splitting is limited to 511 bytes. +//! @returns Pointer to a static buffer. +extern char *UTIL_SplitHudMessage(const char *src); + +extern void UTIL_ShowMessageRaw(const char *pString, CBaseEntity *pPlayer); extern void UTIL_ShowMessage(const char *pString, CBaseEntity *pPlayer); extern void UTIL_ShowMessageAll(const char *pString); + extern void UTIL_ScreenFadeAll(const Vector &color, float fadeTime, float holdTime, int alpha, int flags); extern void UTIL_ScreenFade(CBaseEntity *pEntity, const Vector &color, float fadeTime, float fadeHold, int alpha, int flags); @@ -376,6 +387,7 @@ typedef struct hudtextparms_s // prints as transparent 'title' to the HUD extern void UTIL_HudMessageAll(const hudtextparms_t &textparms, const char *pMessage); extern void UTIL_HudMessage(CBaseEntity *pEntity, const hudtextparms_t &textparms, const char *pMessage); +extern void UTIL_HudMessageRaw(CBaseEntity *pEntity, const hudtextparms_t &textparms, const char *pMessage); // for handy use with ClientPrint params extern char *UTIL_dtos1(int d);