unit GameDiagnosticsExport; {$mode objfpc}{$H+} interface type TDiagnosticExportOutcomeKind = (deokSuccess, deokCancel, deokError); TDiagnosticExportOutcome = record Kind: TDiagnosticExportOutcomeKind; ErrorMessage: string; end; TDiagnosticExportCallback = procedure(const AOutcome: TDiagnosticExportOutcome) of object; procedure ExportDiagnosticReport( const AJsonSnapshot: string; const ACallback: TDiagnosticExportCallback ); implementation uses Classes, CastleMessaging, CastleStringUtils, SysUtils; const ExportResultToken = 'diagnostic-report-export-result'; type TDiagnosticExportListener = class private FPending: Boolean; FCallback: TDiagnosticExportCallback; procedure Complete(const AKind: TDiagnosticExportOutcomeKind; const AErrorMessage: string); function MessageReceived(const Received: TCastleStringList; const ReceivedStream: TMemoryStream): Boolean; public constructor Create; destructor Destroy; override; procedure Export(const AJsonSnapshot: string; const ACallback: TDiagnosticExportCallback); end; constructor TDiagnosticExportListener.Create; begin inherited; Messaging.OnReceive.Add(@MessageReceived); end; destructor TDiagnosticExportListener.Destroy; begin Messaging.OnReceive.Remove(@MessageReceived); inherited; end; procedure TDiagnosticExportListener.Complete(const AKind: TDiagnosticExportOutcomeKind; const AErrorMessage: string); var Outcome: TDiagnosticExportOutcome; Callback: TDiagnosticExportCallback; begin Callback := FCallback; FCallback := nil; FPending := False; Outcome.Kind := AKind; Outcome.ErrorMessage := AErrorMessage; if Assigned(Callback) then Callback(Outcome); end; function TDiagnosticExportListener.MessageReceived( const Received: TCastleStringList; const ReceivedStream: TMemoryStream): Boolean; begin Result := (Received.Count > 0) and (Received[0] = ExportResultToken); if not Result or not FPending then Exit; if (Received.Count = 2) and (Received[1] = 'success') then Complete(deokSuccess, '') else if (Received.Count = 2) and (Received[1] = 'cancel') then Complete(deokCancel, '') else if (Received.Count = 3) and (Received[1] = 'error') then Complete(deokError, Received[2]) else Complete(deokError, 'Malformed export-service response'); end; procedure TDiagnosticExportListener.Export(const AJsonSnapshot: string; const ACallback: TDiagnosticExportCallback); begin if FPending then raise Exception.Create('Diagnostic report export is already pending'); FPending := True; FCallback := ACallback; Messaging.Send(['diagnostic-report-export', AJsonSnapshot]); end; var ExportListener: TDiagnosticExportListener; procedure ExportDiagnosticReport(const AJsonSnapshot: string; const ACallback: TDiagnosticExportCallback); begin ExportListener.Export(AJsonSnapshot, ACallback); end; initialization ExportListener := TDiagnosticExportListener.Create; finalization FreeAndNil(ExportListener); end.