Files
kam-mobile/vendor/kam_remake/src/KM_Crashreport.pas
T

189 lines
5.9 KiB
ObjectPascal
Raw Normal View History

2026-09-12 16:50:30 +05:00
unit KM_Crashreport;
{$I KaM_Remake.inc}
interface
uses
MadExcept,
KM_GameTypes,
KM_Defaults;
type
// Intention of this class is to encapsulate the crash report generation,
// since it is already quite complex task and spans several methods,
// which are not quite related to the Game's SRP
TKMCrashReport = class
private
fExceptIntf: IMEException;
fZipFile: UnicodeString;
fAttachedFilesStr: UnicodeString;
procedure AttachFile(const aFile: UnicodeString);
procedure AttachEverything;
procedure AttachSaveFiles(const aFile: UnicodeString; aAttachRNG: Boolean = True);
procedure AttachLoadedFiles(aAttachRNG: Boolean);
public
constructor Create(const aExceptIntf: IMEException; const aZipFile: UnicodeString);
class procedure Generate(const aExceptIntf: IMEException; const aZipFile: UnicodeString);
end;
implementation
uses
SysUtils, Math,
KM_CommonUtils, KM_InterfaceTypes, KM_Log, KM_Game;
constructor TKMCrashReport.Create(const aExceptIntf: IMEException; const aZipFile: UnicodeString);
begin
inherited Create;
fExceptIntf := aExceptIntf;
fZipFile := aZipFile;
end;
procedure TKMCrashReport.AttachFile(const aFile: UnicodeString);
begin
if (aFile <> '') and FileExists(aFile) then
begin
if Pos(aFile, fAttachedFilesStr) = 0 then
begin
fAttachedFilesStr := fAttachedFilesStr + aFile + '; ';
fExceptIntf.AdditionalAttachments.Add(aFile, '', fZipFile);
gLog.AddTime('Attached file: ' + aFile);
end
else
gLog.AddTime('File already attached: ' + aFile);
end;
end;
procedure TKMCrashReport.AttachSaveFiles(const aFile: UnicodeString; aAttachRNG: Boolean = True);
begin
AttachFile(ChangeFileExt(aFile, EXT_SAVE_MAIN_DOT));
AttachFile(ChangeFileExt(aFile, EXT_SAVE_BASE_DOT));
AttachFile(ChangeFileExt(aFile, EXT_SAVE_REPLAY_DOT));
AttachFile(ChangeFileExt(aFile, EXT_SAVE_MP_LOCAL_DOT));
if aAttachRNG then
AttachFile(ChangeFileExt(aFile, EXT_SAVE_RNG_LOG_DOT));
end;
procedure TKMCrashReport.AttachLoadedFiles(aAttachRNG: Boolean);
begin
if gGame.SaveFile = '' then Exit;
gLog.AddTime('Attaching game loaded file: ' + ExeDir + gGame.SaveFile);
AttachSaveFiles(ExeDir + gGame.SaveFile, aAttachRNG);
end;
procedure TKMCrashReport.AttachEverything;
var
index: Byte;
missionFile, path: UnicodeString;
searchRec: TSearchRec;
indexesSet: set of Byte;
begin
gLog.AddTime('Creating crash report...');
// Attempt to save the game, but if the state is too messed up it might fail
gGame.SaveWorkerThreadHolder.Worker.fSynchronousExceptionMode := True; //Do saving synchronously in main thread
try
try
if (gGame.Params.Mode in [gmSingle, gmCampaign, gmMulti, gmMultiSpectate])
and not (gGame.GamePlayInterface.UIMode = umReplay) then //In case game mode was altered or loaded with logical error
begin
gGame.Save(CRASHREPORT_SAVE_NAME, UTCNow, gGame.SaveWorkerThreadHolder.Worker);
gGame.SaveWorkerThreadHolder.Worker.WaitForAllWorkToComplete; //Wait till save is made
AttachSaveFiles(gGame.SaveName(CRASHREPORT_SAVE_NAME, EXT_SAVE_MAIN, gGame.Params.IsMultiPlayerOrSpec));
end;
except
on E : Exception do
gLog.AddTime('Exception while trying to save game for crash report: ' + E.ClassName + ': ' + E.Message
{$IFDEF WDC}+ sLineBreak + E.StackTrace{$ENDIF});
end;
finally
gGame.SaveWorkerThreadHolder.Worker.fSynchronousExceptionMode := False;
end;
missionFile := gGame.Params.MissionFileRel;
path := ExtractFilePath(ExeDir + missionFile);
// Try to attach the dat+map
AttachFile(ExeDir + missionFile);
AttachFile(ExeDir + ChangeFileExt(missionFile, '.map'));
AttachFile(ExeDir + ChangeFileExt(missionFile, '.txt'));
// Try to add main script file and all other scripts, because they could be included
if FileExists(ExeDir + ChangeFileExt(missionFile, '.script')) then
begin
FindFirst(path + '*.script', faAnyFile - faDirectory, searchRec);
try
repeat
if (searchRec.Name <> '.') and (searchRec.Name <> '..') then
AttachFile(path + searchRec.Name);
until (FindNext(searchRec) <> 0);
finally
FindClose(searchRec);
end;
end;
// Try to add libx files
FindFirst(path + '*.libx', faAnyFile - faDirectory, searchRec);
try
repeat
if (searchRec.Name <> '.') and (searchRec.Name <> '..') then
AttachFile(path + searchRec.Name);
until (FindNext(searchRec) <> 0);
finally
FindClose(searchRec);
end;
if gGame.Params.IsReplay
or ((gGame.GamePlayInterface <> nil) and (gGame.GamePlayInterface.UIMode = umReplay)) then //In case game mode was altered or loaded with logical error
// For replays attach only replay save files
AttachLoadedFiles(False)
else
if not gGame.Params.IsMapEditor then // no need autosaves for MapEd error...
begin
// For other game modes attach last saves
// We want to attach up to 3 saves, lets determine what are best indexes of last saves to attach
indexesSet := [];
// 0 is the oldest save
case gGame.LastSaves.Count of
0: ;
1,2,3: indexesSet := [0,1,2];
4: indexesSet := [0,2,3];
else
indexesSet := [0, gGame.LastSaves.Count div 2, gGame.LastSaves.Count - 1];
end;
for index in indexesSet do
begin
if index >= gGame.LastSaves.Count then Break;
AttachSaveFiles(gGame.SaveName(gGame.LastSaves[index], EXT_SAVE_MAIN, gGame.Params.IsMultiPlayerOrSpec));
end;
// It could be usefull to attach loaded file, to check what was wrong there
AttachLoadedFiles(True); // Also attach RNG file
end;
gLog.AddTime('Crash report created');
end;
class procedure TKMCrashReport.Generate(const aExceptIntf: IMEException; const aZipFile: UnicodeString);
var
cr: TKMCrashReport;
begin
cr := TKMCrashReport.Create(aExceptIntf, aZipFile);
try
cr.AttachEverything;
finally
cr.Free;
end;
end;
end.