Skip to content

Commit

Permalink
Collect parsing errors and show them on user screen
Browse files Browse the repository at this point in the history
  • Loading branch information
FreeSlave committed Sep 26, 2024
1 parent f4bcd0f commit a7b0975
Show file tree
Hide file tree
Showing 10 changed files with 238 additions and 12 deletions.
2 changes: 2 additions & 0 deletions cl_dll/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ set (CLDLL_SOURCES
health.cpp
hud.cpp
hud_caption.cpp
hud_error_collection.cpp
hud_inventory.cpp
hud_msg.cpp
hud_redraw.cpp
Expand All @@ -130,6 +131,7 @@ set (CLDLL_SOURCES
../pm_shared/pm_shared.cpp
../game_shared/tex_materials.cpp
../game_shared/parsetext.cpp
../game_shared/error_collector.cpp
../game_shared/fx_types.cpp
../game_shared/json_utils.cpp
../game_shared/util_shared.cpp
Expand Down
9 changes: 9 additions & 0 deletions cl_dll/hud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "demo_api.h"

#include "environment.h"
#include "error_collector.h"

hud_player_info_t g_PlayerInfoList[MAX_PLAYERS+1]; // player info from the engine
extra_player_info_t g_PlayerExtraInfo[MAX_PLAYERS+1]; // additional player info sent directly to the client dll
Expand Down Expand Up @@ -734,6 +735,7 @@ void CHud::Init( void )

m_MOTD.Init();
m_Scoreboard.Init();
m_ErrorCollection.Init();

m_Menu.Init();

Expand All @@ -748,6 +750,12 @@ void CHud::Init( void )
MsgFunc_ResetHUD( 0, 0, NULL );
ClientCmd( "richpresence_gamemode\n" );
ClientCmd( "richpresence_update\n" );

if (g_errorCollector.HasErrors())
{
m_ErrorCollection.SetClientErrors(g_errorCollector.GetFullString());
g_errorCollector.Clear();
}
}

const char* strStartsWith(const char* str, const char* start)
Expand Down Expand Up @@ -1251,6 +1259,7 @@ void CHud::VidInit( void )
#endif
m_MOTD.VidInit();
m_Scoreboard.VidInit();
m_ErrorCollection.VidInit();
m_Nightvision.VidInit();

m_Caption.VidInit();
Expand Down
20 changes: 20 additions & 0 deletions cl_dll/hud.h
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,25 @@ class CHudMOTD : public CHudBase
int m_iMaxLength;
};

class CHudErrorCollection : public CHudBase
{
public:
int Init();
int VidInit();
void Reset();
int Draw(float flTime);
int MsgFunc_ParseErrors( const char *pszName, int iSize, void *pbuf );
void SetClientErrors(const std::string& str);

private:
int DrawMultiLineString(const char* str, int xpos, int ypos, int xmax, const int LineHeight);

std::string m_clientErrorString;
std::string m_serverErrorString;

cvar_t* m_pCvarDeveloper;
};

struct CaptionProfile_t
{
char firstLetter;
Expand Down Expand Up @@ -1011,6 +1030,7 @@ class CHud
CHudStatusIcons m_StatusIcons;
CHudScoreboard m_Scoreboard;
CHudMOTD m_MOTD;
CHudErrorCollection m_ErrorCollection;
CHudNightvision m_Nightvision;
CHudCaption m_Caption;

Expand Down
116 changes: 116 additions & 0 deletions cl_dll/hud_error_collection.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#include "hud.h"
#include "cl_util.h"
#include "parsemsg.h"
#include "r_studioint.h"

extern engine_studio_api_t IEngineStudio;

DECLARE_MESSAGE( m_ErrorCollection, ParseErrors )

int CHudErrorCollection::Init()
{
m_pCvarDeveloper = nullptr;
gHUD.AddHudElem(this);
m_iFlags &= ~HUD_ACTIVE;
HOOK_MESSAGE(ParseErrors);
return 1;
}

int CHudErrorCollection::VidInit()
{
m_pCvarDeveloper = IEngineStudio.GetCvar("developer");
return 1;
}

void CHudErrorCollection::Reset()
{
if (m_clientErrorString.empty())
m_iFlags &= ~HUD_ACTIVE;
else
m_iFlags |= HUD_ACTIVE;
m_serverErrorString.clear();
}

int CHudErrorCollection::Draw(float flTime)
{
if (!m_pCvarDeveloper || m_pCvarDeveloper->value == 0)
return 1;

if (m_serverErrorString.empty() && m_clientErrorString.empty())
return 1;

const int LineHeight = CHud::UtfText::LineHeight();
int ypos = LineHeight * 2;
int xpos = 30;
int xmax = ScreenWidth;

int r = 255;
int g = 140;
int b = 0;

if (m_serverErrorString.size())
{
CHud::UtfText::DrawString(xpos, ypos, xmax, "SERVER ERRORS:", r, g, b);
ypos += LineHeight;
ypos = DrawMultiLineString(m_serverErrorString.c_str(), xpos, ypos, xmax, LineHeight);
ypos += LineHeight;
}

if (m_clientErrorString.size())
{
CHud::UtfText::DrawString(xpos, ypos, xmax, "CLIENT ERRORS:", r, g, b);
ypos += LineHeight;
DrawMultiLineString(m_clientErrorString.c_str(), xpos, ypos, xmax, LineHeight);
}


return 1;
}

int CHudErrorCollection::MsgFunc_ParseErrors(const char *pszName, int iSize, void *pbuf)
{
BEGIN_READ( pbuf, iSize );
int is_finished = READ_BYTE();
const char* str = READ_STRING();

m_serverErrorString += str;

if (is_finished)
{
m_iFlags |= HUD_ACTIVE;
}

return is_finished ? 1 : 0;
}

void CHudErrorCollection::SetClientErrors(const std::string &str)
{
m_clientErrorString = str;
if (m_clientErrorString.size())
{
m_iFlags |= HUD_ACTIVE;
}
}

int CHudErrorCollection::DrawMultiLineString(const char *str, int xpos, int ypos, int xmax, const int LineHeight)
{
int r = 255;
int g = 140;
int b = 0;

const char *ch = str;
while(*ch)
{
const char *next_line = ch;
for(; *next_line != '\n' && *next_line != '\0'; next_line++)
;
CHud::UtfText::DrawString( xpos, ypos, xmax, ch, r, g, b, next_line - ch );

ypos += LineHeight;

ch = next_line;
if (*ch == '\n')
ch++;
}
return ypos;
}
1 change: 1 addition & 0 deletions dlls/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ set (SVDLL_SOURCES
../pm_shared/pm_shared.cpp
../game_shared/tex_materials.cpp
../game_shared/parsetext.cpp
../game_shared/error_collector.cpp
../game_shared/fx_types.cpp
../game_shared/json_utils.cpp
../game_shared/util_shared.cpp
Expand Down
30 changes: 30 additions & 0 deletions dlls/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include "inventory.h"
#include "followers.h"
#include "common_soundscripts.h"
#include "error_collector.h"

#if FEATURE_ROPE
#include "ropes.h"
Expand Down Expand Up @@ -199,6 +200,7 @@ int gmsgTeamInfo = 0;
int gmsgTeamScore = 0;
int gmsgGameMode = 0;
int gmsgMOTD = 0;
int gmsgParseErrors = 0;
int gmsgServerName = 0;
int gmsgAmmoPickup = 0;
int gmsgWeapPickup = 0;
Expand Down Expand Up @@ -300,6 +302,7 @@ void LinkUserMessages( void )
gmsgTeamScore = REG_USER_MSG( "TeamScore", -1 ); // sets the score of a team on the scoreboard
gmsgGameMode = REG_USER_MSG( "GameMode", 1 );
gmsgMOTD = REG_USER_MSG( "MOTD", -1 );
gmsgParseErrors = REG_USER_MSG( "ParseErrors", -1 );
gmsgServerName = REG_USER_MSG( "ServerName", -1 );
gmsgAmmoPickup = REG_USER_MSG( "AmmoPickup", 3 );
gmsgWeapPickup = REG_USER_MSG( "WeapPickup", 1 );
Expand Down Expand Up @@ -4919,6 +4922,24 @@ void CBasePlayer::SendAmmoUpdate( void )
}
}

static void SendParseErrorsToClient(edict_t* client, const std::string& str)
{
char chunk[121];
const char* cstr = str.c_str();
size_t offset = 0;
while (offset <= str.size())
{
strncpy(chunk, cstr + offset, sizeof(chunk)-1);
chunk[sizeof(chunk)-1] = '\0';
offset += sizeof(chunk)-1;

MESSAGE_BEGIN( MSG_ONE, gmsgParseErrors, NULL, client );
WRITE_BYTE( offset >= str.size() ? TRUE : FALSE );
WRITE_STRING( chunk );
MESSAGE_END();
}
}

/*
=========================================================
UpdateClientData
Expand Down Expand Up @@ -4960,6 +4981,15 @@ void CBasePlayer::UpdateClientData( void )
}
}

if ( g_psv_developer->value > 0 && !g_pGameRules->IsMultiplayer() || (entindex() == 1 && !IS_DEDICATED_SERVER()) )
{
if (g_errorCollector.HasErrors())
{
std::string fullErrorString = g_errorCollector.GetFullString();
SendParseErrorsToClient(edict(), fullErrorString);
}
}

m_fGameHUDInitialized = TRUE;

m_iObserverLastMode = OBS_ROAMING;
Expand Down
1 change: 1 addition & 0 deletions dlls/wscript
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def build(bld):
source = bld.path.ant_glob('**/*.cpp', excl=excluded_files)
source += bld.path.parent.ant_glob([
'pm_shared/*.cpp',
'game_shared/error_collector.cpp',
'game_shared/fx_types.cpp',
'game_shared/json_utils.cpp',
'game_shared/parsetext.cpp',
Expand Down
30 changes: 30 additions & 0 deletions game_shared/error_collector.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include "error_collector.h"

void ErrorCollector::AddError(const char *str)
{
if (str)
_errors.push_back(str);
}

bool ErrorCollector::HasErrors() const
{
return _errors.size() > 0;
}

std::string ErrorCollector::GetFullString() const
{
std::string fullString;
for (const std::string& str : _errors)
{
fullString += str;
fullString += '\n';
}
return fullString;
}

void ErrorCollector::Clear()
{
_errors.clear();
}

ErrorCollector g_errorCollector;
21 changes: 21 additions & 0 deletions game_shared/error_collector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once
#ifndef ERROR_COLLECTOR_H
#define ERROR_COLLECTOR_H

#include <vector>
#include <string>

class ErrorCollector
{
public:
void AddError(const char* str);
bool HasErrors() const;
std::string GetFullString() const;
void Clear();
private:
std::vector<std::string> _errors;
};

extern ErrorCollector g_errorCollector;

#endif
20 changes: 8 additions & 12 deletions game_shared/json_utils.cpp
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
#include "json_utils.h"

#if CLIENT_DLL
#include "cl_dll.h"
#define JSON_LOG gEngfuncs.Con_DPrintf
#define JSON_ERROR gEngfuncs.Con_DPrintf
#else
#include "util.h"
#define JSON_LOG(...) ALERT(at_aiconsole, ##__VA_ARGS__ )
#define JSON_ERROR(...) ALERT(at_error, ##__VA_ARGS__ )
#endif

#include "color_utils.h"
#include "parsetext.h"
#include "error_collector.h"

#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
Expand Down Expand Up @@ -103,7 +94,10 @@ static void ReportParseErrors(const char* fileName, ParseResult& parseResult, co
{
size_t errorLine, errorColumn;
CalculateLineAndColumnFromOffset(pMemFile, parseResult.Offset(), errorLine, errorColumn);
JSON_ERROR("%s: JSON parse error: %s (Line %lu, column %lu)\n", fileName, GetParseError_En(parseResult.Code()), errorLine, errorColumn);

char buf[1024];
_snprintf(buf, sizeof(buf), "%s: JSON parse error: %s (Line %zu, column %zu)", fileName, GetParseError_En(parseResult.Code()), errorLine, errorColumn);
g_errorCollector.AddError(buf);
}

class DefinitionsProvider : public IRemoteSchemaDocumentProvider
Expand Down Expand Up @@ -177,13 +171,15 @@ bool ReadJsonDocumentWithSchema(Document &document, const char *pMemFile, int fi
schemaPartValue->Accept(writer);
}

JSON_ERROR("%s: value %s of property '%s' doesn't match the constraint '%s' in '%s': %s\n",
char buf[1028];
_snprintf(buf, sizeof(buf), "%s: value %s of property '%s' doesn't match the constraint '%s' in '%s': %s\n",
fileName,
badValueBuffer.GetString(),
docPathBuffer.GetString(),
validator.GetInvalidSchemaKeyword(),
schemaPathBuffer.GetString(),
schemaPartBuffer.GetString());
g_errorCollector.AddError(buf);

return false;
}
Expand Down

0 comments on commit a7b0975

Please sign in to comment.