From 1c584e831908e1a2b5d5c8f6df1196a24e977719 Mon Sep 17 00:00:00 2001 From: Rain Date: Wed, 20 May 2026 05:02:15 +0300 Subject: [PATCH 1/3] Change HUD marker delimiter from ';' to ',' Change the "cl_neo_...marker" cvars' delimiter character from ';' to ',', because the engine tokenizer uses the semicolon as a command-level delimiter, and there is no escape character available. This causes issues for nested semicolons in config file binds etc, where the inner semicolon of the marker syntax is incorrectly intepreted as the end-of-command token. I'm taking the easy route of simply changing our marker delimiter character to something better supported, because we don't have source code access to the lexer internals, so else we'd have to detour or shim the engine interface which gets ugly. This has the one-time side effect of mauling people's marker configs for the next patch, but that shouldn't be too much of an issue since the config is trivially fixable for anyone with custom settings. We might also want to revisit the crosshair code delimiters and any other such serialized data in the future, and perhaps transfer them over to a more suitable delimiter character as well. --- .../client/neo/ui/neo_hud_friendly_marker.cpp | 45 ++++++++++++++----- .../client/neo/ui/neo_hud_friendly_marker.h | 14 +++--- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/src/game/client/neo/ui/neo_hud_friendly_marker.cpp b/src/game/client/neo/ui/neo_hud_friendly_marker.cpp index 9debc6117a..1c97fc5f78 100644 --- a/src/game/client/neo/ui/neo_hud_friendly_marker.cpp +++ b/src/game/client/neo/ui/neo_hud_friendly_marker.cpp @@ -407,7 +407,28 @@ bool ImportMarker(FriendlyMarkerInfo *crh, const char *pszSequence) for (int i = 0; i < iPszSequenceLength && iSegmentIdx < NEOIFFMARKER_SEGMENT__TOTAL; ++i) { const char ch = szMutSequence[i]; - if (ch == ';') + + // NEO NOTE (Rain): I am changing the delimiter away from ';' because the Source cmd tokenizer + // does not have a sensible character escape syntax, and ';' is already used as command end token, + // which causes issues when trying to chain commands with an inner semicolon. + // NEO TODO (Rain): we should probably also update the xhair syntax and any other such serializations + // to ideally use the same, non-semicolon token, and declare that in a global header somewhere. + constexpr char deprecated_delimiter = ';'; + if (ch == deprecated_delimiter) + { + char point_to[NEO_IFFMARKER_SEQMAX]; + V_memset(point_to, ' ', i); + point_to[i] = '^'; + point_to[i+1] = '\0'; + Warning("Please replace the \"%c\" characters with \"%c\" in your marker syntax.\n" + "Failed for input at pos %d:\n\t%s\n\t%s\n", + deprecated_delimiter, NEO_MARKER_DELIMITER, + i, pszSequence, + point_to); + return false; + } + + if (ch == NEO_MARKER_DELIMITER) { szMutSequence[i] = '\0'; const char *pszCurSegment = szMutSequence + iPrevSegment; @@ -454,15 +475,15 @@ bool ImportMarker(FriendlyMarkerInfo *crh, const char *pszSequence) void ExportMarker(const FriendlyMarkerInfo *crh, char (&szSequence)[NEO_IFFMARKER_SEQMAX]) { V_sprintf_safe(szSequence, - "%d;%d;%d;%d;%.2f;%d;%d;%d;%d;%d;", - crh->iInitialOffset, - static_cast(crh->bShowDistance), - static_cast(crh->bVerboseDistance), - static_cast(crh->bShowSquadMarker), - crh->flSquadMarkerScale, - static_cast(crh->bShowHealthBar), - static_cast(crh->bShowHealth), - static_cast(crh->bShowName), - static_cast(crh->bPrependClantagToName), - crh->iMaxNameLength); + "%d%c%d%c%d%c%d%c%.2f%c%d%c%d%c%d%c%d%c%d%c", + crh->iInitialOffset, NEO_MARKER_DELIMITER, + static_cast(crh->bShowDistance), NEO_MARKER_DELIMITER, + static_cast(crh->bVerboseDistance), NEO_MARKER_DELIMITER, + static_cast(crh->bShowSquadMarker), NEO_MARKER_DELIMITER, + crh->flSquadMarkerScale, NEO_MARKER_DELIMITER, + static_cast(crh->bShowHealthBar), NEO_MARKER_DELIMITER, + static_cast(crh->bShowHealth), NEO_MARKER_DELIMITER, + static_cast(crh->bShowName), NEO_MARKER_DELIMITER, + static_cast(crh->bPrependClantagToName), NEO_MARKER_DELIMITER, + crh->iMaxNameLength, NEO_MARKER_DELIMITER); } diff --git a/src/game/client/neo/ui/neo_hud_friendly_marker.h b/src/game/client/neo/ui/neo_hud_friendly_marker.h index 9f83af606a..485a7631ed 100644 --- a/src/game/client/neo/ui/neo_hud_friendly_marker.h +++ b/src/game/client/neo/ui/neo_hud_friendly_marker.h @@ -22,13 +22,15 @@ enum NeoIFFMarkerSegment NEOIFFMARKER_SEGMENT__TOTAL, }; -#define NEO_SQUAD_MARKER_DEFAULT "0;1;0;0;0.50;1;0;1;1;32;" -#define NEO_FRIENDLY_MARKER_DEFAULT "0;1;0;1;0.50;1;0;0;0;32;" -#define NEO_SPECTATOR_MARKER_DEFAULT "0;0;0;1;0.50;1;1;1;1;32;" +constexpr const char NEO_MARKER_DELIMITER = ','; + +#define NEO_SQUAD_MARKER_DEFAULT "0,1,0,0,0.50,1,0,1,1,32," +#define NEO_FRIENDLY_MARKER_DEFAULT "0,1,0,1,0.50,1,0,0,0,32," +#define NEO_SPECTATOR_MARKER_DEFAULT "0,0,0,1,0.50,1,1,1,1,32," #ifdef GLOWS_ENABLE -#define NEO_SQUAD_XRAY_MARKER_DEFAULT "4;0;0;0;0.50;1;0;1;0;32;" -#define NEO_FRIENDLY_XRAY_MARKER_DEFAULT "4;0;0;0;0.50;1;0;0;0;32;" -#define NEO_SPECTATOR_XRAY_MARKER_DEFAULT "4;0;0;0;0.50;1;0;1;1;32;" +#define NEO_SQUAD_XRAY_MARKER_DEFAULT "4,0,0,0,0.50,1,0,1,0,32," +#define NEO_FRIENDLY_XRAY_MARKER_DEFAULT "4,0,0,0,0.50,1,0,0,0,32," +#define NEO_SPECTATOR_XRAY_MARKER_DEFAULT "4,0,0,0,0.50,1,0,1,1,32," #endif // GLOWS_ENABLE constexpr int NEO_IFFMARKER_SEQMAX = 32; constexpr int MAX_MARKER_STRSIZE = 48 + NEO_MAX_CLANTAG_LENGTH + 1; From 1747f1d7f569f06649133d7994c2bf467581fe91 Mon Sep 17 00:00:00 2001 From: Rain Date: Wed, 20 May 2026 05:21:52 +0300 Subject: [PATCH 2/3] Only allow printable chars for marker cvars --- src/game/client/neo/ui/neo_hud_friendly_marker.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/game/client/neo/ui/neo_hud_friendly_marker.cpp b/src/game/client/neo/ui/neo_hud_friendly_marker.cpp index 1c97fc5f78..e7cfee65b9 100644 --- a/src/game/client/neo/ui/neo_hud_friendly_marker.cpp +++ b/src/game/client/neo/ui/neo_hud_friendly_marker.cpp @@ -49,13 +49,14 @@ void iffMarkerChangeCallback( IConVar *pConVar, char const* pOldString, float fl } } -ConVar cl_neo_squad_marker("cl_neo_squad_marker", NEO_SQUAD_MARKER_DEFAULT, FCVAR_ARCHIVE | FCVAR_DONTRECORD, "IFF Marker settings for squad-mates", iffMarkerChangeCallback); -ConVar cl_neo_friendly_marker("cl_neo_friendly_marker", NEO_FRIENDLY_MARKER_DEFAULT, FCVAR_ARCHIVE | FCVAR_DONTRECORD, "IFF Marker settings for team-mates", iffMarkerChangeCallback); -ConVar cl_neo_spectator_marker("cl_neo_spectator_marker", NEO_SPECTATOR_MARKER_DEFAULT, FCVAR_ARCHIVE | FCVAR_DONTRECORD, "IFF Marker settings for spectated players", iffMarkerChangeCallback); +constexpr auto markerFlags = FCVAR_ARCHIVE | FCVAR_PRINTABLEONLY | FCVAR_DONTRECORD; +ConVar cl_neo_squad_marker("cl_neo_squad_marker", NEO_SQUAD_MARKER_DEFAULT, markerFlags, "IFF Marker settings for squad-mates", iffMarkerChangeCallback); +ConVar cl_neo_friendly_marker("cl_neo_friendly_marker", NEO_FRIENDLY_MARKER_DEFAULT, markerFlags, "IFF Marker settings for team-mates", iffMarkerChangeCallback); +ConVar cl_neo_spectator_marker("cl_neo_spectator_marker", NEO_SPECTATOR_MARKER_DEFAULT, markerFlags, "IFF Marker settings for spectated players", iffMarkerChangeCallback); #ifdef GLOWS_ENABLE -ConVar cl_neo_squad_xray_marker("cl_neo_squad_xray_marker", NEO_SQUAD_XRAY_MARKER_DEFAULT, FCVAR_ARCHIVE | FCVAR_DONTRECORD, "IFF Marker settings for squad-mates with xray enabled", iffMarkerChangeCallback); -ConVar cl_neo_friendly_xray_marker("cl_neo_friendly_xray_marker", NEO_FRIENDLY_XRAY_MARKER_DEFAULT, FCVAR_ARCHIVE | FCVAR_DONTRECORD, "IFF Marker settings for team-mates with xray enabled", iffMarkerChangeCallback); -ConVar cl_neo_spectator_xray_marker("cl_neo_spectator_xray_marker", NEO_SPECTATOR_XRAY_MARKER_DEFAULT, FCVAR_ARCHIVE | FCVAR_DONTRECORD, "IFF Marker settings for spectated players with xray enabled", iffMarkerChangeCallback); +ConVar cl_neo_squad_xray_marker("cl_neo_squad_xray_marker", NEO_SQUAD_XRAY_MARKER_DEFAULT, markerFlags, "IFF Marker settings for squad-mates with xray enabled", iffMarkerChangeCallback); +ConVar cl_neo_friendly_xray_marker("cl_neo_friendly_xray_marker", NEO_FRIENDLY_XRAY_MARKER_DEFAULT, markerFlags, "IFF Marker settings for team-mates with xray enabled", iffMarkerChangeCallback); +ConVar cl_neo_spectator_xray_marker("cl_neo_spectator_xray_marker", NEO_SPECTATOR_XRAY_MARKER_DEFAULT, markerFlags, "IFF Marker settings for spectated players with xray enabled", iffMarkerChangeCallback); #endif // GLOWS_ENABLE void CNEOHud_FriendlyMarker::UpdateStateForNeoHudElementDraw() From 4e52486781c1dc9d161cfd9fcc8aaf04784bfa47 Mon Sep 17 00:00:00 2001 From: Rain Date: Wed, 20 May 2026 05:56:48 +0300 Subject: [PATCH 3/3] Add sanity check --- src/game/client/neo/ui/neo_hud_friendly_marker.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/game/client/neo/ui/neo_hud_friendly_marker.cpp b/src/game/client/neo/ui/neo_hud_friendly_marker.cpp index e7cfee65b9..94791f4ded 100644 --- a/src/game/client/neo/ui/neo_hud_friendly_marker.cpp +++ b/src/game/client/neo/ui/neo_hud_friendly_marker.cpp @@ -415,6 +415,7 @@ bool ImportMarker(FriendlyMarkerInfo *crh, const char *pszSequence) // NEO TODO (Rain): we should probably also update the xhair syntax and any other such serializations // to ideally use the same, non-semicolon token, and declare that in a global header somewhere. constexpr char deprecated_delimiter = ';'; + static_assert(deprecated_delimiter != NEO_MARKER_DELIMITER); if (ch == deprecated_delimiter) { char point_to[NEO_IFFMARKER_SEQMAX];