Cafu Engine
State.hpp
1 /*
2 Cafu Engine, http://www.cafu.de/
3 Copyright (c) Carsten Fuchs and other contributors.
4 This project is licensed under the terms of the MIT license.
5 */
6 
7 #ifndef CAFU_NETWORK_STATE_HPP_INCLUDED
8 #define CAFU_NETWORK_STATE_HPP_INCLUDED
9 
10 #include "Templates/Array.hpp"
11 
12 #include <cstring>
13 #include <string>
14 
15 #ifdef _WIN32
16 #define WIN32_LEAN_AND_MEAN
17 #define NOMINMAX
18 #include <winsock2.h>
19 #undef GetProp
20 #else
21 #include <netinet/in.h>
22 #endif
23 
24 #if defined(_WIN32) && _MSC_VER<1600
25 #include "pstdint.h" // Paul Hsieh's portable implementation of the stdint.h header.
26 #else
27 #include <stdint.h>
28 #endif
29 
30 
31 namespace cf {
32 namespace Network {
33 
34 
35 uint64_t htonll(uint64_t value);
36 uint64_t ntohll(uint64_t value);
37 
38 
39 /// This class holds the serialized state of another object (typically a game entity).
40 /// It is used in combination with the OutStreamT and InStreamT classes, which handle
41 /// the serialization and deserialization.
42 /// The delta messages created and taken by this class are intended for network transfer,
43 /// disk storage, etc.
44 /// Internally, the encapsulated state is kept as an array of raw bytes, and as such it
45 /// is "opaque" -- we don't have any idea of the meaning of the contained data.
46 class StateT
47 {
48  public:
49 
50  /// Constructor for creating an empty StateT instance.
51  /// The constructed instance is typically used with an OutStreamT,
52  /// filled indirectly in a call to e.g. BaseEntityT::Serialize(OutStreamT& Stream).
53  StateT() { }
54 
55  /// Constructor for creating a state from another StateT instance and a delta message.
56  /// @param Other The other state to create this state from.
57  /// @param DeltaMessage The delta message that expresses how this state is different from Other.
58  StateT(const StateT& Other, const ArrayT<uint8_t>& DeltaMessage);
59 
60  /// Creates a delta message from this state.
61  /// The delta message expresses how this state is different from another.
62  /// @param Other The other state that the generated delta message is relative to.
63  /// @param Compress Whether the delta message should be RLE-compressed.
64  ArrayT<uint8_t> GetDeltaMessage(const StateT& Other, bool Compress=true) const;
65 
66  /// Returns whether a delta message created by GetDeltaMessage() is empty (no change to the state).
67  static bool IsDeltaMessageEmpty(const ArrayT<uint8_t>& DeltaMessage);
68 
69 
70  private:
71 
72  friend class InStreamT;
73  friend class OutStreamT;
74 
75  ArrayT<uint8_t> m_Data;
76 };
77 
78 
79 /// This class is used for writing data into a StateT instance (serialization).
80 /// TODO: Optimize handling of strings and bools.
82 {
83  public:
84 
85  /// Creates a stream for writing data into the given state.
86  OutStreamT(StateT& State, bool Overwrite=true)
87  : m_Data(State.m_Data)
88  {
89  if (Overwrite)
90  m_Data.Overwrite();
91  }
92 
93  //@{
94  /// Writes a data item of the given type into the stream.
96  {
97  m_Data.PushBack(b);
98 
99  return *this;
100  }
101 
102  OutStreamT& operator << (uint8_t b)
103  {
104  m_Data.PushBack(b);
105 
106  return *this;
107  }
108 
109  OutStreamT& operator << (unsigned short w)
110  {
111  m_Data.PushBackEmpty(2);
112  *(unsigned short*)&m_Data[m_Data.Size()-2]=htons(w);
113 
114  return *this;
115  }
116 
117  OutStreamT& operator << (int32_t i)
118  {
119  m_Data.PushBackEmpty(4);
120  *(int32_t*)&m_Data[m_Data.Size()-4]=htonl(i);
121 
122  return *this;
123  }
124 
125  OutStreamT& operator << (uint32_t ui)
126  {
127  m_Data.PushBackEmpty(4);
128  *(uint32_t*)&m_Data[m_Data.Size()-4]=htonl(ui);
129 
130  return *this;
131  }
132 
133  OutStreamT& operator << (uint64_t ui)
134  {
135  assert(ntohll(htonll(ui)) == ui);
136  assert(htonll(ntohll(ui)) == ui);
137 
138  m_Data.PushBackEmpty(8);
139  *(uint64_t*)&m_Data[m_Data.Size()-8]=htonll(ui);
140 
141  return *this;
142  }
143 
144  OutStreamT& operator << (float f)
145  {
146  assert(sizeof(f) == sizeof(uint32_t));
147  *this << *(uint32_t*)&f;
148 
149  return *this;
150  }
151 
152  OutStreamT& operator << (double d)
153  {
154  assert(sizeof(d) == sizeof(uint64_t));
155  *this << *(uint64_t*)&d;
156 
157  return *this;
158  }
159 
160  OutStreamT& operator << (bool b)
161  {
162  m_Data.PushBack(b ? 1 : 0);
163 
164  return *this;
165  }
166 
167  OutStreamT& operator << (const char* s)
168  {
169  if (!s) s="NULL";
170 
171  const unsigned long Start =m_Data.Size();
172  const unsigned long Length=(unsigned long)strlen(s)+1;
173 
174  m_Data.PushBackEmpty(Length);
175  memcpy(&m_Data[Start], s, Length);
176 
177  return *this;
178  }
179 
180  OutStreamT& operator << (const std::string& str)
181  {
182  *this << str.c_str();
183 
184  return *this;
185  }
186 
187  template <class T> OutStreamT& operator << (const ArrayT<T>& A)
188  {
189  *this << uint32_t(A.Size());
190 
191  for (unsigned long i=0; i<A.Size(); i++)
192  *this << A[i];
193 
194  return *this;
195  }
196  //@}
197 
198 
199  private:
200 
201  ArrayT<uint8_t>& m_Data; ///< The buffer that this stream is writing to.
202 };
203 
204 
205 /// This class is used for reading data from a StateT instance (deserialization).
206 /// TODO: Optimize handling of strings and bools.
208 {
209  public:
210 
211  /// Creates a stream for reading data from the given state.
212  InStreamT(const StateT& State)
213  : m_Data(State.m_Data),
214  m_ReadPos(0),
215  m_ReadOfl(false)
216  {
217  }
218 
219  /// Re-initializes reading from the stream.
220  void Restart()
221  {
222  m_ReadPos=0;
223  m_ReadOfl=false;
224  }
225 
226  //@{
227  /// Reads a data item of the given type from the stream.
229  {
230  if (m_ReadPos+1 > m_Data.Size()) { m_ReadOfl=true; return *this; }
231 
232  b = m_Data[m_ReadPos];
233 
234  m_ReadPos+=1;
235  return *this;
236  }
237 
238  InStreamT& operator >> (uint8_t& b)
239  {
240  if (m_ReadPos+1 > m_Data.Size()) { m_ReadOfl=true; return *this; }
241 
242  b = m_Data[m_ReadPos];
243 
244  m_ReadPos+=1;
245  return *this;
246  }
247 
248  InStreamT& operator >> (unsigned short& w)
249  {
250  if (m_ReadPos+2 > m_Data.Size()) { m_ReadOfl=true; return *this; }
251 
252  w = ntohs(*(unsigned short*)&m_Data[m_ReadPos]);
253 
254  m_ReadPos+=2;
255  return *this;
256  }
257 
258  InStreamT& operator >> (int32_t& i)
259  {
260  if (m_ReadPos+4 > m_Data.Size()) { m_ReadOfl=true; return *this; }
261 
262  i = ntohl(*(int32_t*)&m_Data[m_ReadPos]);
263 
264  m_ReadPos+=4;
265  return *this;
266  }
267 
268  InStreamT& operator >> (uint32_t& ui)
269  {
270  if (m_ReadPos+4 > m_Data.Size()) { m_ReadOfl=true; return *this; }
271 
272  ui = ntohl(*(uint32_t*)&m_Data[m_ReadPos]);
273 
274  m_ReadPos+=4;
275  return *this;
276  }
277 
278  InStreamT& operator >> (uint64_t& ui)
279  {
280  if (m_ReadPos+8 > m_Data.Size()) { m_ReadOfl=true; return *this; }
281 
282  ui = ntohll(*(uint64_t*)&m_Data[m_ReadPos]);
283 
284  m_ReadPos+=8;
285  return *this;
286  }
287 
288  InStreamT& operator >> (float& f)
289  {
290  assert(sizeof(f) == sizeof(uint32_t));
291  uint32_t ui=0;
292 
293  *this >> ui;
294  f = *(float*)&ui;
295 
296  return *this;
297  }
298 
299  InStreamT& operator >> (double& d)
300  {
301  assert(sizeof(d) == sizeof(uint64_t));
302  uint64_t ui=0;
303 
304  *this >> ui;
305  d = *(double*)&ui;
306 
307  return *this;
308  }
309 
310  InStreamT& operator >> (bool& b)
311  {
312  if (m_ReadPos+1 > m_Data.Size()) { m_ReadOfl=true; return *this; }
313 
314  b = (m_Data[m_ReadPos] != 0);
315 
316  m_ReadPos+=1;
317  return *this;
318  }
319 
320  InStreamT& operator >> (char* s)
321  {
322  const unsigned long Start=m_ReadPos;
323 
324  while (m_ReadPos < m_Data.Size() && m_Data[m_ReadPos] != 0)
325  m_ReadPos++;
326 
327  // Also account for the trailing 0 character.
328  m_ReadPos++;
329 
330  if (m_ReadPos > m_Data.Size()) { m_ReadOfl=true; return *this; }
331 
332  memcpy(s, &m_Data[Start], m_ReadPos-Start);
333 
334  return *this;
335  }
336 
337  InStreamT& operator >> (std::string& s)
338  {
339  const unsigned long Start=m_ReadPos;
340 
341  while (m_ReadPos < m_Data.Size() && m_Data[m_ReadPos] != 0)
342  m_ReadPos++;
343 
344  // Also account for the trailing 0 character.
345  m_ReadPos++;
346 
347  if (m_ReadPos > m_Data.Size()) { m_ReadOfl=true; return *this; }
348 
349  s=std::string((char*)&m_Data[Start], (m_ReadPos-1)-Start);
350 
351  return *this;
352  }
353 
354  template <class T> InStreamT& operator >> (ArrayT<T>& A)
355  {
356  uint32_t Size=0;
357 
358  *this >> Size;
359  A.Overwrite();
360  A.PushBackEmptyExact(Size);
361 
362  for (uint32_t i=0; i<Size; i++)
363  *this >> A[i];
364 
365  return *this;
366  }
367  //@}
368 
369 
370  private:
371 
372  const ArrayT<uint8_t>& m_Data; ///< The buffer that this stream is reading from.
373  unsigned long m_ReadPos; ///< Current read position in the data.
374  bool m_ReadOfl; ///< Whether an attempt was made to read over the data buffer boundaries.
375 };
376 
377 } // namespace Network
378 } // namespace cf
379 
380 #endif
ArrayT< uint8_t > GetDeltaMessage(const StateT &Other, bool Compress=true) const
Creates a delta message from this state.
Definition: State.cpp:146
OutStreamT(StateT &State, bool Overwrite=true)
Creates a stream for writing data into the given state.
Definition: State.hpp:86
static bool IsDeltaMessageEmpty(const ArrayT< uint8_t > &DeltaMessage)
Returns whether a delta message created by GetDeltaMessage() is empty (no change to the state)...
Definition: State.cpp:211
InStreamT & operator>>(char &b)
Reads a data item of the given type from the stream.
Definition: State.hpp:228
unsigned long Size() const
Get size of array.
Definition: Array.hpp:138
This class is used for reading data from a StateT instance (deserialization).
Definition: State.hpp:207
OutStreamT & operator<<(char b)
Writes a data item of the given type into the stream.
Definition: State.hpp:95
void Restart()
Re-initializes reading from the stream.
Definition: State.hpp:220
InStreamT(const StateT &State)
Creates a stream for reading data from the given state.
Definition: State.hpp:212
This class holds the serialized state of another object (typically a game entity).
Definition: State.hpp:46
This class is used for writing data into a StateT instance (serialization).
Definition: State.hpp:81
void Overwrite()
Clear array (but reuse memory)
Definition: Array.hpp:154
StateT()
Constructor for creating an empty StateT instance.
Definition: State.hpp:53