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_SNAPSHOT_H
4#define ENGINE_SHARED_SNAPSHOT_H
5
6#include <cstddef>
7#include <cstdint>
8#include <optional>
9
10// CSnapshot
11
12class CSnapshotItem
13{
14 friend class CSnapshotBuilder;
15
16 int *Data() { return (int *)(this + 1); }
17
18public:
19 int m_TypeAndId;
20
21 const int *Data() const { return (int *)(this + 1); }
22 int InternalType() const { return m_TypeAndId >> 16; }
23 int Id() const { return m_TypeAndId & 0xffff; }
24 int Key() const { return m_TypeAndId; }
25 void Invalidate() { m_TypeAndId = -1; }
26};
27
28class CSnapshot
29{
30 friend class CSnapshotBuilder;
31 int m_DataSize = 0;
32 int m_NumItems = 0;
33
34 int *Offsets() const { return (int *)(this + 1); }
35 char *DataStart() const { return (char *)(Offsets() + m_NumItems); }
36
37 size_t OffsetSize() const { return sizeof(int) * m_NumItems; }
38 size_t TotalSize() const { return sizeof(CSnapshot) + OffsetSize() + m_DataSize; }
39
40 static const CSnapshot ms_EmptySnapshot;
41
42public:
43 enum
44 {
45 OFFSET_UUID_TYPE = 0x4000,
46 MAX_TYPE = 0x7fff,
47 MAX_ID = 0xffff,
48 MAX_ITEMS = 1024,
49 MAX_PARTS = 64,
50 MAX_SIZE = MAX_PARTS * 1024
51 };
52
53 int NumItems() const { return m_NumItems; }
54 int DataSize() const { return m_DataSize; }
55 const CSnapshotItem *GetItem(int Index) const;
56 int GetItemSize(int Index) const;
57 int GetItemIndex(int Key) const;
58 void InvalidateItem(int Index);
59 int GetItemType(int Index) const;
60 int GetExternalItemType(int InternalType) const;
61 const void *FindItem(int Type, int Id) const;
62
63 unsigned Crc() const;
64 // Prints the raw snapshot data showing item and int boundaries.
65 // See also `CNetObjHandler::DebugDumpSnapshot(const CSnapshot *pSnap)`
66 // For more detailed annotations of the data.
67 void DebugDump() const;
68 bool IsValid(size_t ActualSize) const;
69
70 static const CSnapshot *EmptySnapshot() { return &ms_EmptySnapshot; }
71};
72
73class alignas(int32_t) CSnapshotBuffer
74{
75public:
76 unsigned char m_aData[CSnapshot::MAX_SIZE];
77
78 CSnapshot *AsSnapshot() { return (CSnapshot *)m_aData; }
79 const CSnapshot *AsSnapshot() const { return (const CSnapshot *)m_aData; }
80};
81
82// CSnapshotDelta
83
84class CSnapshotDelta
85{
86public:
87 class CData
88 {
89 public:
90 int m_NumDeletedItems;
91 int m_NumUpdateItems;
92 int m_NumTempItems; // needed?
93 int m_aData[1];
94 };
95
96private:
97 enum
98 {
99 MAX_NETOBJSIZES = 64
100 };
101 short m_aItemSizes[MAX_NETOBJSIZES];
102 uint64_t m_aSnapshotDataRate[CSnapshot::MAX_TYPE + 1];
103 uint64_t m_aSnapshotDataUpdates[CSnapshot::MAX_TYPE + 1];
104 CData m_Empty;
105
106 static void UndiffItem(const int *pPast, const int *pDiff, int *pOut, int Size, uint64_t *pDataRate);
107
108public:
109 static int DiffItem(const int *pPast, const int *pCurrent, int *pOut, int Size);
110 CSnapshotDelta();
111 CSnapshotDelta(const CSnapshotDelta &Old);
112 uint64_t GetDataRate(int Index) const { return m_aSnapshotDataRate[Index]; }
113 uint64_t GetDataUpdates(int Index) const { return m_aSnapshotDataUpdates[Index]; }
114 void SetStaticsize(int ItemType, size_t Size);
115 const CData *EmptyDelta() const;
116 int CreateDelta(const CSnapshot *pFrom, const CSnapshot *pTo, void *pDstData);
117 int UnpackDelta(const CSnapshot *pFrom, CSnapshotBuffer *pTo, const void *pSrcData, int DataSize);
118 int DebugDumpDelta(const void *pSrcData, int DataSize);
119};
120
121// CSnapshotStorage
122
123class CSnapshotStorage
124{
125public:
126 class CHolder
127 {
128 public:
129 CHolder *m_pPrev;
130 CHolder *m_pNext;
131
132 int64_t m_Tagtime;
133 int m_Tick;
134
135 int m_SnapSize;
136 int m_AltSnapSize;
137
138 CSnapshot *m_pSnap;
139 CSnapshot *m_pAltSnap;
140 };
141
142 CHolder *m_pFirst;
143 CHolder *m_pLast;
144
145 CSnapshotStorage() { Init(); }
146 ~CSnapshotStorage() { PurgeAll(); }
147 void Init();
148 void PurgeAll();
149 void PurgeUntil(int Tick);
150 void Add(int Tick, int64_t Tagtime, size_t DataSize, const void *pData, size_t AltDataSize, const void *pAltData);
151 int Get(int Tick, int64_t *pTagtime, const CSnapshot **ppData, const CSnapshot **ppAltData) const;
152};
153
154class CSnapshotBuilder
155{
156 enum
157 {
158 MAX_EXTENDED_ITEM_TYPES = 64,
159 };
160
161 char m_aData[CSnapshot::MAX_SIZE];
162 int m_DataSize;
163
164 int m_aOffsets[CSnapshot::MAX_ITEMS];
165 int m_NumItems;
166
167 int m_aExtendedItemTypes[MAX_EXTENDED_ITEM_TYPES];
168 int m_NumExtendedItemTypes = 0;
169
170 bool AddExtendedItemType(int Index);
171 int GetExtendedItemTypeIndex(int TypeId);
172 int GetTypeFromIndex(int Index) const;
173
174 bool m_Building = false;
175 bool m_HasDroppedItem = false;
176 bool m_Sixup = false;
177
178public:
179 void Init(bool Sixup = false);
180 void Init7(const CSnapshot *pSnapshot);
181
182 bool NewItem(int Type, int Id, const void *pData, int Size);
183 void *NewItemRaw(int Type, int Id, int Size);
184
185 CSnapshotItem *GetItem(int Index);
186 int GetItemSize(int Index) const;
187 int *GetItemData(int Index);
188 std::optional<int> FindItemIndexByKey(int Key);
189
190 int FinishIfNoDroppedItems(CSnapshotBuffer *pSnapData);
191 int Finish(CSnapshotBuffer *pBuffer);
192};
193
194#endif // ENGINE_SHARED_SNAPSHOT_H
195