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