1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#ifndef ENGINE_SHARED_DEMO_H
4#define ENGINE_SHARED_DEMO_H
5
6#include "snapshot.h"
7
8#include <base/hash.h>
9
10#include <engine/demo.h>
11#include <engine/shared/protocol.h>
12
13#include <functional>
14#include <vector>
15
16typedef std::function<void()> TUpdateIntraTimesFunc;
17
18class CSnapshotDelta;
19class IConsole;
20class IStorage;
21
22class CDemoRecorder : public IDemoRecorder
23{
24 IConsole *m_pConsole;
25 IStorage *m_pStorage;
26
27 IOHANDLE m_File;
28 char m_aCurrentFilename[IO_MAX_PATH_LENGTH];
29 int m_LastTickMarker;
30 int m_LastKeyFrame;
31 int m_FirstTick;
32
33 CSnapshotBuffer m_LastSnapshotData;
34 CSnapshotDelta *m_pSnapshotDelta;
35
36 int m_NumTimelineMarkers;
37 int m_aTimelineMarkers[MAX_TIMELINE_MARKERS];
38
39 bool m_NoMapData;
40
41 DEMOFUNC_FILTER m_pfnFilter;
42 void *m_pUser;
43
44 void WriteTickMarker(int Tick, bool Keyframe);
45 void Write(int Type, const void *pData, int Size);
46
47public:
48 CDemoRecorder(CSnapshotDelta *pSnapshotDelta, bool NoMapData = false);
49 CDemoRecorder() = default;
50 ~CDemoRecorder() override;
51
52 int Start(IStorage *pStorage, IConsole *pConsole, const char *pFilename, const char *pNetversion, const char *pMap, const SHA256_DIGEST &Sha256, unsigned MapCrc, const char *pType, unsigned MapSize, unsigned char *pMapData, IOHANDLE MapFile, DEMOFUNC_FILTER pfnFilter, void *pUser);
53 int Stop(IDemoRecorder::EStopMode Mode, const char *pTargetFilename = "") override;
54
55 void AddDemoMarker();
56 void AddDemoMarker(int Tick);
57
58 void RecordSnapshot(int Tick, const void *pData, int Size);
59 void RecordMessage(const void *pData, int Size);
60
61 bool IsRecording() const override { return m_File != nullptr; }
62 const char *CurrentFilename() const override { return m_aCurrentFilename; }
63
64 int Length() const override { return (m_LastTickMarker - m_FirstTick) / SERVER_TICK_SPEED; }
65};
66
67class CDemoPlayer : public IDemoPlayer
68{
69public:
70 class IListener
71 {
72 public:
73 virtual ~IListener() = default;
74 virtual void OnDemoPlayerSnapshot(void *pData, int Size) = 0;
75 virtual void OnDemoPlayerMessage(void *pData, int Size) = 0;
76 };
77
78 class CPlaybackInfo
79 {
80 public:
81 CDemoHeader m_Header;
82 CTimelineMarkers m_TimelineMarkers;
83
84 IDemoPlayer::CInfo m_Info;
85
86 int64_t m_LastUpdate;
87 int64_t m_LastScan;
88 int64_t m_CurrentTime;
89
90 int m_NextTick;
91 int m_PreviousTick;
92
93 float m_IntraTick;
94 float m_IntraTickSincePrev;
95 float m_TickTime;
96
97 bool m_LiveStateUpdating;
98 int m_LiveStateFailedCount;
99 int m_LiveStateUnchangedCount;
100 };
101
102private:
103 IListener *m_pListener;
104
105 TUpdateIntraTimesFunc m_UpdateIntraTimesFunc;
106
107 // Playback
108 class CKeyFrame
109 {
110 public:
111 int64_t m_Filepos;
112 int m_Tick;
113
114 CKeyFrame(int64_t Filepos, int Tick) :
115 m_Filepos(Filepos), m_Tick(Tick)
116 {
117 }
118 };
119
120 IConsole *m_pConsole;
121 IOHANDLE m_File;
122 int64_t m_MapOffset;
123 char m_aFilename[IO_MAX_PATH_LENGTH];
124 char m_aErrorMessage[256];
125 std::vector<CKeyFrame> m_vKeyFrames;
126 CMapInfo m_MapInfo;
127 int m_SpeedIndex;
128
129 CPlaybackInfo m_Info;
130 unsigned char m_aCompressedSnapshotData[CSnapshot::MAX_SIZE];
131 unsigned char m_aDecompressedSnapshotData[CSnapshot::MAX_SIZE];
132
133 // Depending on the chunk header
134 // this is either a full CSnapshot or a CSnapshotDelta.
135 unsigned char m_aChunkData[CSnapshot::MAX_SIZE];
136 // Storage for the full snapshot
137 // where the delta gets unpacked into.
138 CSnapshotBuffer m_Snapshot;
139 CSnapshotBuffer m_LastSnapshotData;
140 int m_LastSnapshotDataSize;
141 CSnapshotDelta *m_pSnapshotDelta;
142 CSnapshotDelta *m_pSnapshotDeltaSixup;
143
144 bool m_UseVideo;
145#if defined(CONF_VIDEORECORDER)
146 bool m_WasRecording = false;
147#endif
148
149 enum EReadChunkHeaderResult
150 {
151 CHUNKHEADER_SUCCESS,
152 CHUNKHEADER_ERROR,
153 CHUNKHEADER_EOF,
154 };
155 EReadChunkHeaderResult ReadChunkHeader(int *pType, int *pSize, int *pTick);
156 void DoTick();
157 enum class EScanFileResult
158 {
159 SUCCESS,
160 ERROR_RECOVERABLE,
161 ERROR_UNRECOVERABLE,
162 };
163 EScanFileResult ScanFile();
164 void UpdateTimes();
165
166 int64_t Time();
167 bool m_Sixup;
168
169 CSnapshotDelta *SnapshotDelta();
170 void Construct(CSnapshotDelta *pSnapshotDelta, CSnapshotDelta *pSnapshotDeltaSixup, bool UseVideo);
171
172public:
173 CDemoPlayer(CSnapshotDelta *pSnapshotDelta, CSnapshotDelta *pSnapshotDeltaSixup, bool UseVideo);
174 CDemoPlayer(CSnapshotDelta *pSnapshotDelta, CSnapshotDelta *pSnapshotDeltaSixup, bool UseVideo, TUpdateIntraTimesFunc &&UpdateIntraTimesFunc);
175 ~CDemoPlayer() override;
176
177 void SetListener(IListener *pListener);
178
179 int Load(IStorage *pStorage, IConsole *pConsole, const char *pFilename, int StorageType);
180 unsigned char *GetMapData(IStorage *pStorage);
181 bool ExtractMap(IStorage *pStorage);
182 void Play();
183 void Pause() override;
184 void Unpause() override;
185 void Stop(const char *pErrorMessage = "");
186 void SetSpeed(float Speed) override;
187 void SetSpeedIndex(int SpeedIndex) override;
188 void AdjustSpeedIndex(int Offset) override;
189 bool SeekPercent(float Percent) override;
190 bool SeekTime(float Seconds) override;
191 bool SeekTick(ETickOffset TickOffset) override;
192 bool SetPos(int WantedTick) override;
193 const CInfo *BaseInfo() const override { return &m_Info.m_Info; }
194 void GetDemoName(char *pBuffer, size_t BufferSize) const override;
195 bool GetDemoInfo(IStorage *pStorage, IConsole *pConsole, const char *pFilename, int StorageType, CDemoHeader *pDemoHeader, CTimelineMarkers *pTimelineMarkers, CMapInfo *pMapInfo, IOHANDLE *pFile = nullptr, char *pErrorMessage = nullptr, size_t ErrorMessageSize = 0) const override;
196 const char *Filename() const { return m_aFilename; }
197 const char *ErrorMessage() const override { return m_aErrorMessage; }
198
199 void Update(bool RealTime = true);
200 bool IsSixup() const { return m_Sixup; }
201
202 const CPlaybackInfo *Info() const { return &m_Info; }
203 bool IsPlaying() const override { return m_File != nullptr; }
204 const CMapInfo *GetMapInfo() const { return &m_MapInfo; }
205};
206
207class CDemoEditor : public IDemoEditor
208{
209 IConsole *m_pConsole;
210 IStorage *m_pStorage;
211 CSnapshotDelta *m_pSnapshotDelta;
212 CSnapshotDelta *m_pSnapshotDeltaSixup;
213
214public:
215 virtual void Init(CSnapshotDelta *pSnapshotDelta, CSnapshotDelta *pSnapshotDeltaSixup, IConsole *pConsole, IStorage *pStorage);
216 bool Slice(const char *pDemo, const char *pDst, int StartTick, int EndTick, DEMOFUNC_FILTER pfnFilter, void *pUser) override;
217};
218
219#endif
220