Line data Source code
1 : /* Copyright (C) 2021 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
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.
8 : *
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.
13 : *
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/>.
16 : */
17 :
18 : #include "precompiled.h"
19 :
20 : #include "NetMessage.h"
21 :
22 : #include "lib/utf8.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
28 :
29 : #include <sstream>
30 :
31 : class CBufferBinarySerializerImpl
32 : {
33 : public:
34 1 : CBufferBinarySerializerImpl(u8* buffer) :
35 1 : m_Buffer(buffer)
36 : {
37 1 : }
38 :
39 11 : void Put(const char* name, const u8* data, size_t len)
40 : {
41 : #if DEBUG_SERIALIZER_ANNOTATE
42 : std::string tag = "<";
43 : tag.append(name);
44 : tag.append(">");
45 : memcpy(m_Buffer, tag.c_str(), tag.length());
46 : m_Buffer += tag.length();
47 : #else
48 : UNUSED2(name);
49 : #endif
50 11 : memcpy(m_Buffer, data, len);
51 11 : m_Buffer += len;
52 11 : }
53 :
54 : u8* m_Buffer;
55 : };
56 :
57 : /**
58 : * Serializer instance that writes directly to a buffer (which must be long enough).
59 : */
60 1 : class CBufferBinarySerializer : public CBinarySerializer<CBufferBinarySerializerImpl>
61 : {
62 : public:
63 1 : CBufferBinarySerializer(const ScriptInterface& scriptInterface, u8* buffer) :
64 1 : CBinarySerializer<CBufferBinarySerializerImpl>(scriptInterface, buffer)
65 : {
66 1 : }
67 :
68 1 : u8* GetBuffer()
69 : {
70 1 : return m_Impl.m_Buffer;
71 : }
72 : };
73 :
74 : class CLengthBinarySerializerImpl
75 : {
76 : public:
77 2 : CLengthBinarySerializerImpl() :
78 2 : m_Length(0)
79 : {
80 2 : }
81 :
82 22 : void Put(const char* name, const u8* UNUSED(data), size_t len)
83 : {
84 : #if DEBUG_SERIALIZER_ANNOTATE
85 : m_Length += 2; // '<' and '>'
86 : m_Length += strlen(name);
87 : #else
88 : UNUSED2(name);
89 : #endif
90 22 : m_Length += len;
91 22 : }
92 :
93 : size_t m_Length;
94 : };
95 :
96 : /**
97 : * Serializer instance that simply counts how many bytes would be written.
98 : */
99 2 : class CLengthBinarySerializer : public CBinarySerializer<CLengthBinarySerializerImpl>
100 : {
101 : public:
102 2 : CLengthBinarySerializer(const ScriptInterface& scriptInterface) :
103 2 : CBinarySerializer<CLengthBinarySerializerImpl>(scriptInterface)
104 : {
105 2 : }
106 :
107 2 : size_t GetLength()
108 : {
109 2 : return m_Impl.m_Length;
110 : }
111 : };
112 :
113 1 : CSimulationMessage::CSimulationMessage(const ScriptInterface& scriptInterface) :
114 1 : CNetMessage(NMT_SIMULATION_COMMAND), m_ScriptInterface(scriptInterface)
115 : {
116 2 : ScriptRequest rq(scriptInterface);
117 1 : m_Data.init(rq.cx);
118 1 : }
119 :
120 1 : CSimulationMessage::CSimulationMessage(const ScriptInterface& scriptInterface, u32 client, i32 player, u32 turn, JS::HandleValue data) :
121 : CNetMessage(NMT_SIMULATION_COMMAND), m_ScriptInterface(scriptInterface),
122 1 : m_Client(client), m_Player(player), m_Turn(turn)
123 : {
124 2 : ScriptRequest rq(scriptInterface);
125 1 : m_Data.init(rq.cx, data);
126 1 : }
127 :
128 0 : CSimulationMessage::CSimulationMessage(const CSimulationMessage& orig) :
129 0 : m_Client(orig.m_Client),
130 0 : m_Player(orig.m_Player),
131 0 : m_ScriptInterface(orig.m_ScriptInterface),
132 0 : m_Turn(orig.m_Turn),
133 0 : CNetMessage(orig)
134 : {
135 0 : ScriptRequest rq(m_ScriptInterface);
136 0 : m_Data.init(rq.cx, orig.m_Data);
137 0 : }
138 :
139 1 : u8* CSimulationMessage::Serialize(u8* pBuffer) const
140 : {
141 : // TODO: ought to handle serialization exceptions
142 : // TODO: ought to represent common commands more efficiently
143 1 : u8* pos = CNetMessage::Serialize(pBuffer);
144 2 : CBufferBinarySerializer serializer(m_ScriptInterface, pos);
145 1 : serializer.NumberU32_Unbounded("client", m_Client);
146 1 : serializer.NumberI32_Unbounded("player", m_Player);
147 1 : serializer.NumberU32_Unbounded("turn", m_Turn);
148 :
149 1 : serializer.ScriptVal("command", const_cast<JS::PersistentRootedValue*>(&m_Data));
150 2 : return serializer.GetBuffer();
151 : }
152 :
153 1 : const u8* CSimulationMessage::Deserialize(const u8* pStart, const u8* pEnd)
154 : {
155 : // TODO: ought to handle serialization exceptions
156 : // TODO: ought to represent common commands more efficiently
157 1 : const u8* pos = CNetMessage::Deserialize(pStart, pEnd);
158 2 : std::istringstream stream(std::string(pos, pEnd));
159 2 : CStdDeserializer deserializer(m_ScriptInterface, stream);
160 1 : deserializer.NumberU32_Unbounded("client", m_Client);
161 1 : deserializer.NumberI32_Unbounded("player", m_Player);
162 1 : deserializer.NumberU32_Unbounded("turn", m_Turn);
163 1 : deserializer.ScriptVal("command", &m_Data);
164 2 : return pEnd;
165 : }
166 :
167 2 : size_t CSimulationMessage::GetSerializedLength() const
168 : {
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 4 : CLengthBinarySerializer serializer(m_ScriptInterface);
172 2 : serializer.NumberU32_Unbounded("client", m_Client);
173 2 : serializer.NumberI32_Unbounded("player", m_Player);
174 2 : serializer.NumberU32_Unbounded("turn", m_Turn);
175 :
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 2 : serializer.ScriptVal("command", const_cast<JS::PersistentRootedValue*>(&m_Data));
179 4 : return CNetMessage::GetSerializedLength() + serializer.GetLength();
180 : }
181 :
182 2 : CStr CSimulationMessage::ToString() const
183 : {
184 4 : std::string source = Script::ToString(ScriptRequest(m_ScriptInterface), const_cast<JS::PersistentRootedValue*>(&m_Data));
185 :
186 4 : std::stringstream stream;
187 2 : stream << "CSimulationMessage { m_Client: " << m_Client << ", m_Player: " << m_Player << ", m_Turn: " << m_Turn << ", m_Data: " << source << " }";
188 4 : return CStr(stream.str());
189 : }
190 :
191 :
192 0 : CGameSetupMessage::CGameSetupMessage(const ScriptInterface& scriptInterface) :
193 0 : CNetMessage(NMT_GAME_SETUP), m_ScriptInterface(scriptInterface)
194 : {
195 0 : ScriptRequest rq(m_ScriptInterface);
196 0 : m_Data.init(rq.cx);
197 0 : }
198 :
199 0 : CGameSetupMessage::CGameSetupMessage(const ScriptInterface& scriptInterface, JS::HandleValue data) :
200 0 : CNetMessage(NMT_GAME_SETUP), m_ScriptInterface(scriptInterface)
201 : {
202 0 : ScriptRequest rq(m_ScriptInterface);
203 0 : m_Data.init(rq.cx, data);
204 0 : }
205 :
206 0 : u8* CGameSetupMessage::Serialize(u8* pBuffer) const
207 : {
208 : // TODO: ought to handle serialization exceptions
209 0 : u8* pos = CNetMessage::Serialize(pBuffer);
210 0 : CBufferBinarySerializer serializer(m_ScriptInterface, pos);
211 0 : serializer.ScriptVal("command", const_cast<JS::PersistentRootedValue*>(&m_Data));
212 0 : return serializer.GetBuffer();
213 : }
214 :
215 0 : const u8* CGameSetupMessage::Deserialize(const u8* pStart, const u8* pEnd)
216 : {
217 : // TODO: ought to handle serialization exceptions
218 0 : const u8* pos = CNetMessage::Deserialize(pStart, pEnd);
219 0 : std::istringstream stream(std::string(pos, pEnd));
220 0 : CStdDeserializer deserializer(m_ScriptInterface, stream);
221 0 : deserializer.ScriptVal("command", const_cast<JS::PersistentRootedValue*>(&m_Data));
222 0 : return pEnd;
223 : }
224 :
225 0 : size_t CGameSetupMessage::GetSerializedLength() const
226 : {
227 0 : CLengthBinarySerializer serializer(m_ScriptInterface);
228 0 : serializer.ScriptVal("command", const_cast<JS::PersistentRootedValue*>(&m_Data));
229 0 : return CNetMessage::GetSerializedLength() + serializer.GetLength();
230 : }
231 :
232 0 : CStr CGameSetupMessage::ToString() const
233 : {
234 0 : std::string source = Script::ToString(ScriptRequest(m_ScriptInterface), const_cast<JS::PersistentRootedValue*>(&m_Data));
235 :
236 0 : std::stringstream stream;
237 0 : stream << "CGameSetupMessage { m_Data: " << source << " }";
238 0 : return CStr(stream.str());
239 : }
|