| 34 BEGIN_ZFC_NAMESPACE |
34 BEGIN_ZFC_NAMESPACE |
| 35 |
35 |
| 36 // ------------------------------------------------------------------------------------------------- |
36 // ------------------------------------------------------------------------------------------------- |
| 37 // |
37 // |
| 38 RCONSession::RCONSession() : |
38 RCONSession::RCONSession() : |
| 39 m_state (RCON_DISCONNECTED), |
39 m_state(RCON_DISCONNECTED), |
| 40 m_lastPing (0), |
40 m_lastPing(0), |
| 41 m_numAdmins (0), |
41 m_adminCount(0), |
| 42 m_interface (nullptr) |
42 m_interface(nullptr) |
| 43 { |
43 { |
| 44 if (not m_socket.set_blocking (false)) |
44 if (not m_socket.set_blocking(false)) |
| 45 { |
45 { |
| 46 fprintf (stderr, "unable to set socket as non-blocking: %s\n", |
46 fprintf(stderr, "unable to set socket as non-blocking: %s\n", |
| 47 m_socket.error_string().chars()); |
47 m_socket.error_string().chars()); |
| 48 exit (EXIT_FAILURE); |
48 exit(EXIT_FAILURE); |
| 49 } |
49 } |
| 50 } |
50 } |
| 51 |
51 |
| 52 // ------------------------------------------------------------------------------------------------- |
52 // ------------------------------------------------------------------------------------------------- |
| 53 // |
53 // |
| 54 RCONSession::~RCONSession() {} |
54 RCONSession::~RCONSession() {} |
| 55 |
55 |
| 56 // ------------------------------------------------------------------------------------------------- |
56 // ------------------------------------------------------------------------------------------------- |
| 57 // |
57 // |
| 58 void RCONSession::connect (IPAddress address) |
58 void RCONSession::connect(IPAddress address) |
| 59 { |
59 { |
| 60 m_address = address; |
60 m_address = address; |
| 61 m_state = RCON_CONNECTING; |
61 m_state = RCON_CONNECTING; |
| 62 m_interface->updateStatusBar(); |
62 m_interface->updateStatusBar(); |
| 63 send_hello(); |
63 sendHello(); |
| 64 } |
64 } |
| 65 |
65 |
| 66 // ------------------------------------------------------------------------------------------------- |
66 // ------------------------------------------------------------------------------------------------- |
| 67 // |
67 // |
| 68 void RCONSession::disconnect() |
68 void RCONSession::disconnect() |
| 69 { |
69 { |
| 70 if (m_state > RCON_CONNECTING) |
70 if (m_state > RCON_CONNECTING) |
| 71 { |
71 { |
| 72 // Say goodbye to remote |
72 // Say goodbye to remote |
| 73 Bytestream packet; |
73 send({CLRC_DISCONNECT}); |
| 74 packet.write_byte (CLRC_DISCONNECT); |
|
| 75 this->send (packet); |
|
| 76 m_interface->disconnected(); |
74 m_interface->disconnected(); |
| 77 } |
75 } |
| 78 |
76 |
| 79 m_state = RCON_DISCONNECTED; |
77 m_state = RCON_DISCONNECTED; |
| 80 } |
78 } |
| 81 |
79 |
| 82 // ------------------------------------------------------------------------------------------------- |
80 // ------------------------------------------------------------------------------------------------- |
| 83 // |
81 // |
| 84 void RCONSession::send (const Bytestream& packet) |
82 void RCONSession::send(const ByteArray& packet) |
| 85 { |
83 { |
| 86 m_socket.send (m_address, packet); |
84 m_socket.send(m_address, packet); |
| 87 } |
85 } |
| 88 |
86 |
| 89 // ------------------------------------------------------------------------------------------------- |
87 // ------------------------------------------------------------------------------------------------- |
| 90 // |
88 // |
| 91 void RCONSession::tick() |
89 void RCONSession::tick() |
| 92 { |
90 { |
| 93 if (m_state == RCON_DISCONNECTED) |
91 if (m_state == RCON_DISCONNECTED) |
| 94 return; |
92 return; |
| 95 |
93 |
| 96 time_t now; |
94 time_t now; |
| 97 time (&now); |
95 time(&now); |
| 98 |
96 |
| 99 if (m_lastPing < now) |
97 if (m_lastPing < now) |
| 100 { |
98 { |
| 101 if (m_state == RCON_CONNECTING) |
99 if (m_state == RCON_CONNECTING) |
| 102 { |
100 { |
| 103 send_hello(); |
101 sendHello(); |
| 104 } |
102 } |
| 105 else if (m_state == RCON_AUTHENTICATING) |
103 else if (m_state == RCON_AUTHENTICATING) |
| 106 { |
104 { |
| 107 send_password(); |
105 sendPassword(); |
| 108 } |
106 } |
| 109 else if (m_state == RCON_CONNECTED and m_lastPing + 5 < now) |
107 else if (m_state == RCON_CONNECTED and m_lastPing + 5 < now) |
| 110 { |
108 { |
| 111 Bytestream packet; |
109 send({CLRC_PONG}); |
| 112 packet.write_byte (CLRC_PONG); |
110 bumpLastPing(); |
| 113 send (packet); |
|
| 114 bump_last_ping(); |
|
| 115 } |
111 } |
| 116 } |
112 } |
| 117 |
113 |
| 118 for (Datagram datagram; m_socket.read (datagram);) |
114 for (Datagram datagram; m_socket.read(datagram);) |
| 119 handle_packet (datagram); |
115 handlePacket(datagram); |
| 120 } |
116 } |
| 121 |
117 |
| 122 // ------------------------------------------------------------------------------------------------- |
118 // ------------------------------------------------------------------------------------------------- |
| 123 // |
119 // |
| 124 void RCONSession::handle_packet (Datagram& datagram) |
120 void RCONSession::handlePacket(Datagram& datagram) |
| 125 { |
121 { |
| 126 if (datagram.address != m_address) |
122 if (datagram.address != m_address) |
| 127 return; |
123 return; |
| |
124 |
| |
125 Bytestream stream(datagram.message); |
| 128 |
126 |
| 129 try |
127 try |
| 130 { |
128 { |
| 131 int32_t header = datagram.message.read_long(); |
129 int32_t header = datagram.message.read_long(); |
| 132 int32_t sequenceNumber = (header != 0) ? datagram.message.read_long() : 0; |
130 int32_t sequenceNumber = (header != 0) ? datagram.message.read_long() : 0; |
| 133 m_interface->print("Recieved packet with header 0x%x and sequence number #%d\n", header, sequenceNumber); |
131 m_interface->print("Recieved packet with header 0x%x and sequence number #%d\n", header, sequenceNumber); |
| 134 |
132 |
| 135 while (datagram.message.bytes_left() > 0) |
133 while (datagram.message.bytes_left() > 0) |
| 136 { |
134 { |
| 137 int header = datagram.message.read_byte(); |
135 int header = stream.readByte(); |
| 138 |
136 |
| 139 switch (ServerResponse (header)) |
137 switch (ServerResponse(header)) |
| 140 { |
138 { |
| 141 case SVRC_OLDPROTOCOL: |
139 case SVRC_OLDPROTOCOL: |
| 142 m_interface->printError ("Your RCON client is using outdated protocol.\n"); |
140 m_interface->printError("Your RCON client is using outdated protocol.\n"); |
| 143 m_state = RCON_DISCONNECTED; |
141 m_state = RCON_DISCONNECTED; |
| 144 break; |
142 break; |
| 145 |
143 |
| 146 case SVRC_BANNED: |
144 case SVRC_BANNED: |
| 147 m_interface->printError ("You have been banned from the server.\n"); |
145 m_interface->printError("You have been banned from the server.\n"); |
| 148 m_state = RCON_DISCONNECTED; |
146 m_state = RCON_DISCONNECTED; |
| 149 break; |
147 break; |
| 150 |
148 |
| 151 case SVRC_SALT: |
149 case SVRC_SALT: |
| 152 m_salt = datagram.message.read_string(); |
150 m_salt = stream.readString(); |
| 153 m_state = RCON_AUTHENTICATING; |
151 m_state = RCON_AUTHENTICATING; |
| 154 send_password(); |
152 sendPassword(); |
| 155 break; |
153 break; |
| 156 |
154 |
| 157 case SVRC_INVALIDPASSWORD: |
155 case SVRC_INVALIDPASSWORD: |
| 158 m_interface->printError ("Login failed.\n"); |
156 m_interface->printError("Login failed.\n"); |
| 159 m_state = RCON_DISCONNECTED; |
157 m_state = RCON_DISCONNECTED; |
| 160 break; |
158 break; |
| 161 |
159 |
| 162 case SVRC_MESSAGE: |
160 case SVRC_MESSAGE: |
| 163 { |
161 { |
| 164 String message = datagram.message.read_string(); |
162 String message = stream.readString(); |
| 165 message.normalize(); |
163 message.normalize(); |
| 166 m_interface->printText ("%s\n", message.chars()); |
164 m_interface->printText("%s\n", message.chars()); |
| 167 } |
165 } |
| 168 break; |
166 break; |
| 169 |
167 |
| 170 case SVRC_LOGGEDIN: |
168 case SVRC_LOGGEDIN: |
| 171 m_interface->print ("Login successful!\n"); |
169 m_interface->print("Login successful!\n"); |
| 172 m_serverProtocol = datagram.message.read_byte(); |
170 m_serverProtocol = stream.readByte(); |
| 173 m_hostname = datagram.message.read_string(); |
171 m_hostname = stream.readString(); |
| 174 m_interface->setTitle (m_hostname); |
172 m_interface->setTitle(m_hostname); |
| 175 m_state = RCON_CONNECTED; |
173 m_state = RCON_CONNECTED; |
| 176 |
174 |
| 177 for (int i = datagram.message.read_byte(); i > 0; --i) |
175 for (int i = stream.readByte(); i > 0; --i) |
| 178 process_server_updates (datagram.message); |
176 processServerUpdates(stream); |
| 179 |
177 |
| 180 m_interface->print ("Previous messages:\n"); |
178 m_interface->print("Previous messages:\n"); |
| 181 |
179 |
| 182 for (int i = datagram.message.read_byte(); i > 0; --i) |
180 for (int i = stream.readByte(); i > 0; --i) |
| 183 { |
181 { |
| 184 String message = datagram.message.read_string(); |
182 String message = stream.readString(); |
| 185 message.normalize(); |
183 message.normalize(); |
| 186 m_interface->printText ("--- %s\n", message.chars()); |
184 m_interface->printText("--- %s\n", message.chars()); |
| 187 } |
185 } |
| 188 |
186 |
| 189 m_interface->print ("End of previous messages.\n"); |
187 m_interface->print("End of previous messages.\n"); |
| 190 |
188 |
| 191 // Watch sv_hostname so that we can update the titlebar when it changes. |
189 // Watch sv_hostname so that we can update the titlebar when it changes. |
| 192 request_watch("sv_hostname"); |
190 request_watch("sv_hostname"); |
| 193 m_interface->print ("Watch requested.\n"); |
191 m_interface->print ("Watch requested.\n"); |
| 194 break; |
192 break; |
| 195 |
193 |
| 196 case SVRC_UPDATE: |
194 case SVRC_UPDATE: |
| 197 process_server_updates (datagram.message); |
195 processServerUpdates(stream); |
| 198 break; |
196 break; |
| 199 |
197 |
| 200 case SVRC_TOOMANYTABCOMPLETES: |
198 case SVRC_TOOMANYTABCOMPLETES: |
| 201 { |
199 { |
| 202 unsigned int numCompletions = datagram.message.read_short(); |
200 unsigned int numCompletions = stream.readShort(); |
| 203 m_interface->print ("%d completions for '%s'.\n", |
201 m_interface->print("%d completions for '%s'.\n", |
| 204 int (numCompletions), m_lastTabComplete.chars()); |
202 int(numCompletions), m_lastTabComplete.chars()); |
| 205 } |
203 } |
| 206 break; |
204 break; |
| 207 |
205 |
| 208 case SVRC_TABCOMPLETE: |
206 case SVRC_TABCOMPLETE: |
| 209 { |
207 { |
| 210 StringList completes; |
208 StringList completes; |
| 211 completes.resize(datagram.message.read_byte()); |
209 completes.resize(stream.readByte()); |
| 212 |
210 |
| 213 for (String& completion : completes) |
211 for (String& completion : completes) |
| 214 completion = datagram.message.read_string(); |
212 completion = stream.readString(); |
| 215 |
213 |
| 216 if (completes.size() == 1) |
214 if (completes.size() == 1) |
| 217 { |
215 { |
| 218 m_interface->tabComplete (m_lastTabComplete, completes[0]); |
216 m_interface->tabComplete(m_lastTabComplete, completes[0]); |
| 219 } |
217 } |
| 220 else if (not completes.is_empty()) |
218 else if (not completes.is_empty()) |
| 221 { |
219 { |
| 222 m_interface->print ("Completions for '%s':\n", m_lastTabComplete.chars()); |
220 m_interface->print("Completions for '%s':\n", m_lastTabComplete.chars()); |
| 223 |
221 |
| 224 for (int i : range(0, completes.size(), 8)) |
222 for (int i : range(0, completes.size(), 8)) |
| 225 { |
223 { |
| 226 Range<int> spliceRange (i, min (i + 8, completes.size())); |
224 Range<int> spliceRange(i, min(i + 8, completes.size())); |
| 227 StringList splice (completes.splice (spliceRange)); |
225 StringList splice(completes.splice(spliceRange)); |
| 228 m_interface->print ("- %s\n", splice.join (", ").chars()); |
226 m_interface->print("- %s\n", splice.join(", ").chars()); |
| 229 } |
227 } |
| 230 } |
228 } |
| 231 } |
229 } |
| 232 break; |
230 break; |
| 233 |
231 |
| 267 } |
265 } |
| 268 } |
266 } |
| 269 } |
267 } |
| 270 catch (std::exception& e) |
268 catch (std::exception& e) |
| 271 { |
269 { |
| 272 m_interface->printWarning ("Couldn't process packet: %s\n", e.what()); |
270 m_interface->printWarning("Couldn't process packet: %s\n", e.what()); |
| 273 } |
271 } |
| 274 } |
272 } |
| 275 |
273 |
| 276 void RCONSession::process_server_updates (Bytestream& packet) |
274 void RCONSession::processServerUpdates(Bytestream& packet) |
| 277 { |
275 { |
| 278 int header = packet.read_byte(); |
276 int header = packet.readByte(); |
| 279 |
277 |
| 280 switch (RCONUpdateType (header)) |
278 switch (RCONUpdateType(header)) |
| 281 { |
279 { |
| 282 case SVRCU_PLAYERDATA: |
280 case SVRCU_PLAYERDATA: |
| 283 { |
281 { |
| 284 StringList players; |
282 StringList players; |
| 285 |
283 |
| 286 for (int i = packet.read_byte(); i > 0; --i) |
284 for (int i = packet.readByte(); i > 0; --i) |
| 287 players.append (packet.read_string()); |
285 players.append(packet.readString()); |
| 288 |
286 |
| 289 m_interface->setPlayerNames (players); |
287 m_interface->setPlayerNames(players); |
| 290 } |
288 } |
| 291 break; |
289 break; |
| 292 |
290 |
| 293 case SVRCU_ADMINCOUNT: |
291 case SVRCU_ADMINCOUNT: |
| 294 m_numAdmins = packet.read_byte(); |
292 m_adminCount = packet.readByte(); |
| 295 m_interface->updateStatusBar(); |
293 m_interface->updateStatusBar(); |
| 296 break; |
294 break; |
| 297 |
295 |
| 298 case SVRCU_MAP: |
296 case SVRCU_MAP: |
| 299 m_level = packet.read_string(); |
297 m_level = packet.readString(); |
| 300 m_interface->updateStatusBar(); |
298 m_interface->updateStatusBar(); |
| 301 break; |
299 break; |
| 302 |
300 |
| 303 default: |
301 default: |
| 304 m_interface->printWarning ("Unknown server update type: %d\n", header); |
302 m_interface->printWarning("Unknown server update type: %d\n", header); |
| 305 break; |
303 break; |
| 306 } |
304 } |
| 307 } |
305 } |
| 308 |
306 |
| 309 // ------------------------------------------------------------------------------------------------- |
307 // ------------------------------------------------------------------------------------------------- |
| 310 // |
308 // |
| 311 UDPSocket* RCONSession::socket() |
309 UDPSocket* RCONSession::getSocket() |
| 312 { |
310 { |
| 313 return &m_socket; |
311 return &m_socket; |
| 314 } |
312 } |
| 315 |
313 |
| 316 // ------------------------------------------------------------------------------------------------- |
314 // ------------------------------------------------------------------------------------------------- |
| 317 // |
315 // |
| 318 void RCONSession::send_hello() |
316 void RCONSession::sendHello() |
| 319 { |
317 { |
| 320 m_interface->print ("Connecting to %s...\n", |
318 m_interface->print("Connecting to %s...\n", m_address.to_string(IPAddress::WITH_PORT).chars()); |
| 321 m_address.to_string (IPAddress::WITH_PORT).chars()); |
319 send({CLRC_BEGINCONNECTION, RCON_PROTOCOL_VERSION}); |
| 322 Bytestream packet; |
320 bumpLastPing(); |
| 323 packet.write_byte (CLRC_BEGINCONNECTION); |
321 } |
| 324 packet.write_byte (RCON_PROTOCOL_VERSION); |
322 |
| 325 send (packet); |
323 // ------------------------------------------------------------------------------------------------- |
| 326 bump_last_ping(); |
324 // |
| 327 } |
325 void RCONSession::sendPassword() |
| 328 |
326 { |
| 329 // ------------------------------------------------------------------------------------------------- |
327 m_interface->print("Authenticating...\n"); |
| 330 // |
328 ByteArray message; |
| 331 void RCONSession::send_password() |
329 Bytestream stream(message); |
| 332 { |
330 stream.writeByte(CLRC_PASSWORD); |
| 333 m_interface->print ("Authenticating...\n"); |
331 stream.writeString((m_salt + m_password).md5()); |
| 334 Bytestream packet; |
332 send(message); |
| 335 packet.write_byte (CLRC_PASSWORD); |
333 bumpLastPing(); |
| 336 packet.write_string ((m_salt + m_password).md5()); |
334 } |
| 337 send (packet); |
335 |
| 338 bump_last_ping(); |
336 // ------------------------------------------------------------------------------------------------- |
| 339 } |
337 // |
| 340 |
338 void RCONSession::setPassword(const String& password) |
| 341 // ------------------------------------------------------------------------------------------------- |
|
| 342 // |
|
| 343 void RCONSession::set_password (const String& password) |
|
| 344 { |
339 { |
| 345 m_password = password; |
340 m_password = password; |
| 346 } |
341 } |
| 347 |
342 |
| 348 // ------------------------------------------------------------------------------------------------- |
343 // ------------------------------------------------------------------------------------------------- |
| 349 // |
344 // |
| 350 void RCONSession::bump_last_ping() |
345 void RCONSession::bumpLastPing() |
| 351 { |
346 { |
| 352 time_t now; |
347 time_t now; |
| 353 time (&now); |
348 time(&now); |
| 354 m_lastPing = now; |
349 m_lastPing = now; |
| 355 } |
350 } |
| 356 |
351 |
| 357 // ------------------------------------------------------------------------------------------------- |
352 // ------------------------------------------------------------------------------------------------- |
| 358 // |
353 // |
| 359 bool RCONSession::is_active() const |
354 bool RCONSession::isActive() const |
| 360 { |
355 { |
| 361 return state() != RCON_DISCONNECTED; |
356 return getState() != RCON_DISCONNECTED; |
| 362 } |
357 } |
| 363 |
358 |
| 364 // ------------------------------------------------------------------------------------------------- |
359 // ------------------------------------------------------------------------------------------------- |
| 365 // Returns true if the message was successfully sent. |
360 // Returns true if the message was successfully sent. |
| 366 // |
361 // |
| 367 bool RCONSession::send_command (const String& message) |
362 bool RCONSession::sendCommand(const String& commandString) |
| 368 { |
363 { |
| 369 if (m_state != RCON_CONNECTED or message.isEmpty()) |
364 if (m_state != RCON_CONNECTED or commandString.isEmpty()) |
| 370 return false; |
365 return false; |
| 371 |
366 |
| 372 Bytestream packet; |
367 ByteArray message; |
| 373 packet.write_byte (CLRC_COMMAND); |
368 Bytestream stream(message); |
| 374 packet.write_string (message); |
369 stream.writeByte(CLRC_COMMAND); |
| 375 send (packet); |
370 stream.writeString(commandString); |
| 376 bump_last_ping(); |
371 send(message); |
| |
372 bumpLastPing(); |
| 377 return true; |
373 return true; |
| 378 } |
374 } |
| 379 |
375 |
| 380 // ------------------------------------------------------------------------------------------------- |
376 // ------------------------------------------------------------------------------------------------- |
| 381 // |
377 // |
| 382 RCONSessionState RCONSession::state() const |
378 RCONSessionState RCONSession::getState() const |
| 383 { |
379 { |
| 384 return m_state; |
380 return m_state; |
| 385 } |
381 } |
| 386 |
382 |
| 387 // ------------------------------------------------------------------------------------------------- |
383 // ------------------------------------------------------------------------------------------------- |