1 /* Copyright (C) 2021 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
18 #include "precompiled.h"
20 #include "NetMessage.h"
23 #include "scriptinterface/ScriptRequest.h"
24 #include "scriptinterface/JSON.h"
25 #include "simulation2/serialization/BinarySerializer.h"
26 #include "simulation2/serialization/StdDeserializer.h"
27 #include "simulation2/serialization/StdSerializer.h" // for DEBUG_SERIALIZER_ANNOTATE
31 class CBufferBinarySerializerImpl
34 CBufferBinarySerializerImpl(u8
* buffer
) :
39 void Put(const char* name
, const u8
* data
, size_t len
)
41 #if DEBUG_SERIALIZER_ANNOTATE
42 std::string tag
= "<";
45 memcpy(m_Buffer
, tag
.c_str(), tag
.length());
46 m_Buffer
+= tag
.length();
50 memcpy(m_Buffer
, data
, len
);
58 * Serializer instance that writes directly to a buffer (which must be long enough).
60 class CBufferBinarySerializer
: public CBinarySerializer
<CBufferBinarySerializerImpl
>
63 CBufferBinarySerializer(const ScriptInterface
& scriptInterface
, u8
* buffer
) :
64 CBinarySerializer
<CBufferBinarySerializerImpl
>(scriptInterface
, buffer
)
70 return m_Impl
.m_Buffer
;
74 class CLengthBinarySerializerImpl
77 CLengthBinarySerializerImpl() :
82 void Put(const char* name
, const u8
* UNUSED(data
), size_t len
)
84 #if DEBUG_SERIALIZER_ANNOTATE
85 m_Length
+= 2; // '<' and '>'
86 m_Length
+= strlen(name
);
97 * Serializer instance that simply counts how many bytes would be written.
99 class CLengthBinarySerializer
: public CBinarySerializer
<CLengthBinarySerializerImpl
>
102 CLengthBinarySerializer(const ScriptInterface
& scriptInterface
) :
103 CBinarySerializer
<CLengthBinarySerializerImpl
>(scriptInterface
)
109 return m_Impl
.m_Length
;
113 CSimulationMessage::CSimulationMessage(const ScriptInterface
& scriptInterface
) :
114 CNetMessage(NMT_SIMULATION_COMMAND
), m_ScriptInterface(scriptInterface
)
116 ScriptRequest
rq(scriptInterface
);
120 CSimulationMessage::CSimulationMessage(const ScriptInterface
& scriptInterface
, u32 client
, i32 player
, u32 turn
, JS::HandleValue data
) :
121 CNetMessage(NMT_SIMULATION_COMMAND
), m_ScriptInterface(scriptInterface
),
122 m_Client(client
), m_Player(player
), m_Turn(turn
)
124 ScriptRequest
rq(scriptInterface
);
125 m_Data
.init(rq
.cx
, data
);
128 CSimulationMessage::CSimulationMessage(const CSimulationMessage
& orig
) :
129 m_Client(orig
.m_Client
),
130 m_Player(orig
.m_Player
),
131 m_ScriptInterface(orig
.m_ScriptInterface
),
135 ScriptRequest
rq(m_ScriptInterface
);
136 m_Data
.init(rq
.cx
, orig
.m_Data
);
139 u8
* CSimulationMessage::Serialize(u8
* pBuffer
) const
141 // TODO: ought to handle serialization exceptions
142 // TODO: ought to represent common commands more efficiently
143 u8
* pos
= CNetMessage::Serialize(pBuffer
);
144 CBufferBinarySerializer
serializer(m_ScriptInterface
, pos
);
145 serializer
.NumberU32_Unbounded("client", m_Client
);
146 serializer
.NumberI32_Unbounded("player", m_Player
);
147 serializer
.NumberU32_Unbounded("turn", m_Turn
);
149 serializer
.ScriptVal("command", const_cast<JS::PersistentRootedValue
*>(&m_Data
));
150 return serializer
.GetBuffer();
153 const u8
* CSimulationMessage::Deserialize(const u8
* pStart
, const u8
* pEnd
)
155 // TODO: ought to handle serialization exceptions
156 // TODO: ought to represent common commands more efficiently
157 const u8
* pos
= CNetMessage::Deserialize(pStart
, pEnd
);
158 std::istringstream
stream(std::string(pos
, pEnd
));
159 CStdDeserializer
deserializer(m_ScriptInterface
, stream
);
160 deserializer
.NumberU32_Unbounded("client", m_Client
);
161 deserializer
.NumberI32_Unbounded("player", m_Player
);
162 deserializer
.NumberU32_Unbounded("turn", m_Turn
);
163 deserializer
.ScriptVal("command", &m_Data
);
167 size_t CSimulationMessage::GetSerializedLength() const
169 // TODO: serializing twice is stupidly inefficient - we should just
170 // do it once, store the result, and use it here and in Serialize
171 CLengthBinarySerializer
serializer(m_ScriptInterface
);
172 serializer
.NumberU32_Unbounded("client", m_Client
);
173 serializer
.NumberI32_Unbounded("player", m_Player
);
174 serializer
.NumberU32_Unbounded("turn", m_Turn
);
176 // TODO: The cast can probably be removed if and when ScriptVal can take a JS::HandleValue instead of
177 // a JS::MutableHandleValue (relies on JSAPI change). Also search for other casts like this one in that case.
178 serializer
.ScriptVal("command", const_cast<JS::PersistentRootedValue
*>(&m_Data
));
179 return CNetMessage::GetSerializedLength() + serializer
.GetLength();
182 CStr
CSimulationMessage::ToString() const
184 std::string source
= Script::ToString(ScriptRequest(m_ScriptInterface
), const_cast<JS::PersistentRootedValue
*>(&m_Data
));
186 std::stringstream stream
;
187 stream
<< "CSimulationMessage { m_Client: " << m_Client
<< ", m_Player: " << m_Player
<< ", m_Turn: " << m_Turn
<< ", m_Data: " << source
<< " }";
188 return CStr(stream
.str());
192 CGameSetupMessage::CGameSetupMessage(const ScriptInterface
& scriptInterface
) :
193 CNetMessage(NMT_GAME_SETUP
), m_ScriptInterface(scriptInterface
)
195 ScriptRequest
rq(m_ScriptInterface
);
199 CGameSetupMessage::CGameSetupMessage(const ScriptInterface
& scriptInterface
, JS::HandleValue data
) :
200 CNetMessage(NMT_GAME_SETUP
), m_ScriptInterface(scriptInterface
)
202 ScriptRequest
rq(m_ScriptInterface
);
203 m_Data
.init(rq
.cx
, data
);
206 u8
* CGameSetupMessage::Serialize(u8
* pBuffer
) const
208 // TODO: ought to handle serialization exceptions
209 u8
* pos
= CNetMessage::Serialize(pBuffer
);
210 CBufferBinarySerializer
serializer(m_ScriptInterface
, pos
);
211 serializer
.ScriptVal("command", const_cast<JS::PersistentRootedValue
*>(&m_Data
));
212 return serializer
.GetBuffer();
215 const u8
* CGameSetupMessage::Deserialize(const u8
* pStart
, const u8
* pEnd
)
217 // TODO: ought to handle serialization exceptions
218 const u8
* pos
= CNetMessage::Deserialize(pStart
, pEnd
);
219 std::istringstream
stream(std::string(pos
, pEnd
));
220 CStdDeserializer
deserializer(m_ScriptInterface
, stream
);
221 deserializer
.ScriptVal("command", const_cast<JS::PersistentRootedValue
*>(&m_Data
));
225 size_t CGameSetupMessage::GetSerializedLength() const
227 CLengthBinarySerializer
serializer(m_ScriptInterface
);
228 serializer
.ScriptVal("command", const_cast<JS::PersistentRootedValue
*>(&m_Data
));
229 return CNetMessage::GetSerializedLength() + serializer
.GetLength();
232 CStr
CGameSetupMessage::ToString() const
234 std::string source
= Script::ToString(ScriptRequest(m_ScriptInterface
), const_cast<JS::PersistentRootedValue
*>(&m_Data
));
236 std::stringstream stream
;
237 stream
<< "CGameSetupMessage { m_Data: " << source
<< " }";
238 return CStr(stream
.str());