sources/network/rconsession.cpp

branch
protocol5
changeset 195
be953e1621d9
parent 176
060a13878ca0
parent 191
2e6cbacafdc7
child 199
927f4b8ef6f7
equal deleted inserted replaced
176:060a13878ca0 195:be953e1621d9
1 /* 1 /*
2 Copyright 2014 - 2016 Teemu Piippo 2 Copyright 2014 - 2021 Teemu Piippo
3 All rights reserved. 3 All rights reserved.
4 4
5 Redistribution and use in source and binary forms, with or without 5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions 6 modification, are permitted provided that the following conditions
7 are met: 7 are met:
29 */ 29 */
30 30
31 #include <time.h> 31 #include <time.h>
32 #include "rconsession.h" 32 #include "rconsession.h"
33 #include "../interface.h" 33 #include "../interface.h"
34 #include "../md5.h"
34 BEGIN_ZFC_NAMESPACE 35 BEGIN_ZFC_NAMESPACE
35 36
36 // ------------------------------------------------------------------------------------------------- 37 // -------------------------------------------------------------------------------------------------
37 // 38 //
38 RCONSession::RCONSession() : 39 RCONSession::RCONSession() :
39 m_state(RCON_DISCONNECTED), 40 m_state(RCON_DISCONNECTED),
40 m_lastPing(0), 41 m_lastPing(0),
41 m_adminCount(0), 42 m_adminCount(0),
42 m_interface(nullptr) 43 m_interface(nullptr)
43 { 44 {
44 if (not m_socket.set_blocking(false)) 45 std::stringstream errors;
45 { 46 if (not m_socket.set_blocking(false, errors))
46 fprintf(stderr, "unable to set socket as non-blocking: %s\n", 47 {
47 m_socket.error_string().chars()); 48 fprintf(stderr, "unable to set socket as non-blocking: %s\n", errors.str().data());
48 exit(EXIT_FAILURE); 49 exit(EXIT_FAILURE);
49 } 50 }
50 } 51 }
51 52
52 // ------------------------------------------------------------------------------------------------- 53 // -------------------------------------------------------------------------------------------------
53 // 54 //
54 RCONSession::~RCONSession() {} 55 RCONSession::~RCONSession() {}
55 56
56 // ------------------------------------------------------------------------------------------------- 57 // -------------------------------------------------------------------------------------------------
57 // 58 //
58 void RCONSession::connect(IPAddress address) 59 void RCONSession::connect(net::ip_address address)
59 { 60 {
60 m_address = address; 61 m_address = address;
61 m_state = RCON_CONNECTING; 62 m_state = RCON_CONNECTING;
62 m_interface->updateStatusBar(); 63 m_interface->updateStatusBar();
63 sendHello(); 64 sendHello();
77 m_state = RCON_DISCONNECTED; 78 m_state = RCON_DISCONNECTED;
78 } 79 }
79 80
80 // ------------------------------------------------------------------------------------------------- 81 // -------------------------------------------------------------------------------------------------
81 // 82 //
82 void RCONSession::send(const ByteArray& packet) 83 bool RCONSession::send(const std::vector<unsigned char>& packet)
83 { 84 {
84 m_socket.send(m_address, packet); 85 std::stringstream errors;
86 const bool result = m_socket.send(m_address, packet, errors);
87 if (not result)
88 {
89 this->m_interface->printError("Network error: %s\n", errors.str().data());
90 }
91 return result;
85 } 92 }
86 93
87 // ------------------------------------------------------------------------------------------------- 94 // -------------------------------------------------------------------------------------------------
88 // 95 //
89 void RCONSession::tick() 96 void RCONSession::tick()
110 bumpLastPing(); 117 bumpLastPing();
111 } 118 }
112 } 119 }
113 120
114 // Check for new packets in our socket 121 // Check for new packets in our socket
115 for (Datagram datagram; m_socket.read(datagram);) 122 std::stringstream errors;
116 { 123 for (net::Datagram datagram; m_socket.read(datagram, errors);)
124 {
125 if (errors.tellp() > 0)
126 {
127 m_interface->printError("Network error: %s\n", errors.str().data());
128 errors = {};
129 }
117 // Only process packets that originate from the game server. 130 // Only process packets that originate from the game server.
118 if (datagram.address == m_address) 131 if (datagram.address == m_address)
119 handlePacket(datagram.message); 132 handlePacket(datagram.message);
120 } 133 }
121 } 134 }
122 135
123 // ------------------------------------------------------------------------------------------------- 136 // -------------------------------------------------------------------------------------------------
124 // 137 //
125 void RCONSession::handlePacket(ByteArray& message) 138 void RCONSession::handlePacket(std::vector<unsigned char>& message)
126 { 139 {
127 Bytestream stream(message); 140 Bytestream stream(message);
128 141
129 try 142 try
130 { 143 {
155 m_state = RCON_DISCONNECTED; 168 m_state = RCON_DISCONNECTED;
156 break; 169 break;
157 170
158 case SVRC_MESSAGE: 171 case SVRC_MESSAGE:
159 { 172 {
160 String message = stream.readString(); 173 std::string message = stream.readString();
161 message.normalize(); 174 normalize(message);
162 m_interface->printText("%s\n", message.chars()); 175 m_interface->printText("%s\n", message.data());
163 } 176 }
164 break; 177 break;
165 178
166 case SVRC_LOGGEDIN: 179 case SVRC_LOGGEDIN:
167 m_interface->print("Login successful!\n"); 180 m_interface->print("Login successful!\n");
175 188
176 m_interface->print("Previous messages:\n"); 189 m_interface->print("Previous messages:\n");
177 190
178 for (int i = stream.readByte(); i > 0; --i) 191 for (int i = stream.readByte(); i > 0; --i)
179 { 192 {
180 String message = stream.readString(); 193 std::string message = stream.readString();
181 message.normalize(); 194 normalize(message);
182 m_interface->printText("--- %s\n", message.chars()); 195 m_interface->printText("--- %s\n", message.data());
183 } 196 }
184 197
185 m_interface->print("End of previous messages.\n"); 198 m_interface->print("End of previous messages.\n");
186 199
187 // Watch sv_hostname so that we can update the titlebar when it changes. 200 // Watch sv_hostname so that we can update the titlebar when it changes.
195 208
196 case SVRC_TOOMANYTABCOMPLETES: 209 case SVRC_TOOMANYTABCOMPLETES:
197 { 210 {
198 unsigned int numCompletions = stream.readShort(); 211 unsigned int numCompletions = stream.readShort();
199 m_interface->print("%d completions for '%s'.\n", 212 m_interface->print("%d completions for '%s'.\n",
200 int(numCompletions), m_lastTabComplete.chars()); 213 int(numCompletions), m_lastTabComplete.data());
201 } 214 }
202 break; 215 break;
203 216
204 case SVRC_TABCOMPLETE: 217 case SVRC_TABCOMPLETE:
205 { 218 {
206 StringList completes; 219 std::vector<std::string> completes;
207 completes.resize(stream.readByte()); 220 completes.resize(stream.readByte());
208 221
209 for (String& completion : completes) 222 for (std::string& completion : completes)
210 completion = stream.readString(); 223 completion = stream.readString();
211 224
212 if (completes.size() == 1) 225 if (completes.size() == 1)
213 { 226 {
214 m_interface->tabComplete(m_lastTabComplete, completes[0]); 227 m_interface->tabComplete(m_lastTabComplete, completes[0]);
215 } 228 }
216 else if (not completes.is_empty()) 229 else if (completes.size() > 0)
217 { 230 {
218 m_interface->print("Completions for '%s':\n", m_lastTabComplete.chars()); 231 m_interface->print("Completions for '%s':\n", m_lastTabComplete.data());
219 232
220 for (int i : range(0, completes.size(), 8)) 233 for (std::size_t i = 0; i < completes.size(); i += 8)
221 { 234 {
222 Range<int> spliceRange(i, min(i + 8, completes.size())); 235 const int end = min(i + 8, completes.size());
223 StringList splice(completes.splice(spliceRange)); 236 std::vector<std::string> splices = splice(completes, i, end);
224 m_interface->print("- %s\n", splice.join(", ").chars()); 237 m_interface->print("- %s\n", join_string_list(splices, ", ").data());
225 } 238 }
226 } 239 }
227 } 240 }
228 break; 241 break;
229 242
230 case SVRC_WATCHINGCVAR: 243 case SVRC_WATCHINGCVAR:
231 m_interface->print ("You are now watching %s\n", stream.readString().chars()); 244 m_interface->print ("You are now watching %s\n", stream.readString().data());
232 m_interface->print ("Its value is: %s\n", stream.readString().chars()); 245 m_interface->print ("Its value is: %s\n", stream.readString().data());
233 break; 246 break;
234 247
235 case SVRC_ALREADYWATCHINGCVAR: 248 case SVRC_ALREADYWATCHINGCVAR:
236 m_interface->print ("You are already watching %s\n", stream.readString().chars()); 249 m_interface->print ("You are already watching %s\n", stream.readString().data());
237 break; 250 break;
238 251
239 case SVRC_WATCHCVARNOTFOUND: 252 case SVRC_WATCHCVARNOTFOUND:
240 m_interface->print ("CVar %s not found\n", stream.readString().chars()); 253 m_interface->print ("CVar %s not found\n", stream.readString().data());
241 break; 254 break;
242 255
243 case SVRC_CVARCHANGED: 256 case SVRC_CVARCHANGED:
244 { 257 {
245 String name = stream.readString(); 258 String name = stream.readString();
246 String value = stream.readString(); 259 String value = stream.readString();
247 m_interface->print ("The value of CVar %s", name.chars()); 260 m_interface->print ("The value of CVar %s", name.data());
248 m_interface->print (" is now %s\n", value.chars()); 261 m_interface->print (" is now %s\n", value.data());
249 262
250 // If sv_hostname changes, update the titlebar 263 // If sv_hostname changes, update the titlebar
251 if (name == "sv_hostname") 264 if (name == "sv_hostname")
252 { 265 {
253 m_hostname = value; 266 m_hostname = value;
255 } 268 }
256 } 269 }
257 break; 270 break;
258 271
259 case SVRC_YOUREDISCONNECTED: 272 case SVRC_YOUREDISCONNECTED:
260 m_interface->print ("You have been disconnected: %s\n", stream.readString().chars()); 273 m_interface->print ("You have been disconnected: %s\n", stream.readString().data());
261 m_interface->disconnected(); 274 m_interface->disconnected();
262 break; 275 break;
263 } 276 }
264 } 277 }
265 } 278 }
266 catch (std::exception& e) 279 catch (std::exception& e)
267 { 280 {
268 m_interface->printWarning("Couldn't process packet: %s\n", e.what()); 281 m_interface->printWarning("Couldn't process packet: %s\n", e.what());
269 m_interface->printWarning("Packet contents was: %s\n", message.quote().chars()); 282 m_interface->printWarning("Packet contents was: %s\n", quote(message).data());
270 m_interface->printWarning("Stream position in payload was: %d\n", stream.position()); 283 m_interface->printWarning("Stream position in payload was: %d\n", stream.position());
271 } 284 }
272 } 285 }
273 286
274 void RCONSession::processServerUpdates(Bytestream& packet) 287 void RCONSession::processServerUpdates(Bytestream& packet)
277 290
278 switch (RCONUpdateType(header)) 291 switch (RCONUpdateType(header))
279 { 292 {
280 case SVRCU_PLAYERDATA: 293 case SVRCU_PLAYERDATA:
281 { 294 {
282 StringList players; 295 std::vector<std::string> players;
283 296
284 for (int i = packet.readByte(); i > 0; --i) 297 for (int i = packet.readByte(); i > 0; --i)
285 players.append(packet.readString()); 298 players.push_back(packet.readString());
286 299
287 m_interface->setPlayerNames(players); 300 m_interface->setPlayerNames(players);
288 } 301 }
289 break; 302 break;
290 303
304 } 317 }
305 } 318 }
306 319
307 // ------------------------------------------------------------------------------------------------- 320 // -------------------------------------------------------------------------------------------------
308 // 321 //
309 UDPSocket* RCONSession::getSocket() 322 net::UDPSocket* RCONSession::getSocket()
310 { 323 {
311 return &m_socket; 324 return &m_socket;
312 } 325 }
313 326
314 // ------------------------------------------------------------------------------------------------- 327 // -------------------------------------------------------------------------------------------------
315 // 328 //
316 void RCONSession::sendHello() 329 void RCONSession::sendHello()
317 { 330 {
318 m_interface->print("Connecting to %s...\n", m_address.to_string(IPAddress::WITH_PORT).chars()); 331 m_interface->print("Connecting to %s...\n", net::ip_address_to_string(m_address).data());
319 send({CLRC_BEGINCONNECTION, RCON_PROTOCOL_VERSION}); 332 send({CLRC_BEGINCONNECTION, RCON_PROTOCOL_VERSION});
320 bumpLastPing(); 333 bumpLastPing();
321 } 334 }
322 335
323 // ------------------------------------------------------------------------------------------------- 336 // -------------------------------------------------------------------------------------------------
324 // 337 //
325 void RCONSession::sendPassword() 338 void RCONSession::sendPassword()
326 { 339 {
327 m_interface->print("Authenticating...\n"); 340 m_interface->print("Authenticating...\n");
328 ByteArray message; 341 std::vector<unsigned char> message;
329 Bytestream stream(message); 342 Bytestream stream(message);
330 stream.writeByte(CLRC_PASSWORD); 343 stream.writeByte(CLRC_PASSWORD);
331 stream.writeString((m_salt + m_password).md5()); 344 stream.writeString(md5((m_salt + m_password).data()));
332 send(message); 345 send(message);
333 bumpLastPing(); 346 bumpLastPing();
334 } 347 }
335 348
336 // ------------------------------------------------------------------------------------------------- 349 // -------------------------------------------------------------------------------------------------
337 // 350 //
338 void RCONSession::setPassword(const String& password) 351 void RCONSession::setPassword(const std::string& password)
339 { 352 {
340 m_password = password; 353 m_password = password;
341 } 354 }
342 355
343 // ------------------------------------------------------------------------------------------------- 356 // -------------------------------------------------------------------------------------------------
357 } 370 }
358 371
359 // ------------------------------------------------------------------------------------------------- 372 // -------------------------------------------------------------------------------------------------
360 // Returns true if the message was successfully sent. 373 // Returns true if the message was successfully sent.
361 // 374 //
362 bool RCONSession::sendCommand(const String& commandString) 375 bool RCONSession::sendCommand(const std::string& commandString)
363 { 376 {
364 if (m_state != RCON_CONNECTED or commandString.isEmpty()) 377 if (m_state != RCON_CONNECTED or commandString.empty())
365 return false; 378 return false;
366 379
367 ByteArray message; 380 std::vector<unsigned char> message;
368 Bytestream stream(message); 381 Bytestream stream(message);
369 stream.writeByte(CLRC_COMMAND); 382 stream.writeByte(CLRC_COMMAND);
370 stream.writeString(commandString); 383 stream.writeString(commandString);
371 send(message); 384 send(message);
372 bumpLastPing(); 385 bumpLastPing();
380 return m_state; 393 return m_state;
381 } 394 }
382 395
383 // ------------------------------------------------------------------------------------------------- 396 // -------------------------------------------------------------------------------------------------
384 // 397 //
385 const IPAddress& RCONSession::address() const 398 const net::ip_address& RCONSession::address() const
386 { 399 {
387 return m_address; 400 return m_address;
388 } 401 }
389 402
390 // ------------------------------------------------------------------------------------------------- 403 // -------------------------------------------------------------------------------------------------
394 return m_adminCount; 407 return m_adminCount;
395 } 408 }
396 409
397 // ------------------------------------------------------------------------------------------------- 410 // -------------------------------------------------------------------------------------------------
398 // 411 //
399 const String& RCONSession::getLevel() const 412 const std::string& RCONSession::getLevel() const
400 { 413 {
401 return m_level; 414 return m_level;
402 } 415 }
403 416
404 // ------------------------------------------------------------------------------------------------- 417 // -------------------------------------------------------------------------------------------------
405 // 418 //
406 void RCONSession::requestTabCompletion(const String& part) 419 void RCONSession::requestTabCompletion(const std::string& part)
407 { 420 {
408 if (m_serverProtocol >= 4) 421 if (m_serverProtocol >= 4)
409 { 422 {
410 ByteArray message; 423 std::vector<unsigned char> message;
411 Bytestream stream(message); 424 Bytestream stream(message);
412 stream.writeByte(CLRC_TABCOMPLETE); 425 stream.writeByte(CLRC_TABCOMPLETE);
413 stream.writeString(part); 426 stream.writeString(part);
414 send(message); 427 send(message);
415 bumpLastPing(); 428 bumpLastPing();
416 m_lastTabComplete = part; 429 m_lastTabComplete = part;
417 } 430 }
418 else 431 else
419 { 432 {
420 m_interface->print("This server does not support tab-completion\n", m_serverProtocol); 433 m_interface->print("This server does not support tab-completion\n");
421 } 434 }
422 } 435 }
423 436
424 // ------------------------------------------------------------------------------------------------- 437 // -------------------------------------------------------------------------------------------------
425 // 438 //
430 443
431 // ------------------------------------------------------------------------------------------------- 444 // -------------------------------------------------------------------------------------------------
432 // 445 //
433 void RCONSession::requestWatch(const String& cvar) 446 void RCONSession::requestWatch(const String& cvar)
434 { 447 {
435 StringList cvars; 448 const std::vector<std::string> cvars{{cvar}};
436 cvars.append(cvar);
437 requestWatch(cvars); 449 requestWatch(cvars);
438 } 450 }
439 451
440 // ------------------------------------------------------------------------------------------------- 452 // -------------------------------------------------------------------------------------------------
441 // 453 //
442 void RCONSession::requestWatch(const StringList& cvars) 454 void RCONSession::requestWatch(const std::vector<std::string>& cvars)
443 { 455 {
444 ByteArray message; 456 std::vector<unsigned char> message;
445 Bytestream stream(message); 457 Bytestream stream(message);
446 stream.writeByte(CLRC_WATCHCVAR); 458 stream.writeByte(CLRC_WATCHCVAR);
447 459 for (String cvar : cvars)
448 for (const String& cvar : cvars) 460 {
449 stream.writeString(cvar.normalized()); 461 normalize(cvar);
450 462 stream.writeString(cvar);
463 }
451 stream.writeString(""); 464 stream.writeString("");
452 send(message); 465 send(message);
453 } 466 }
454 467
455 END_ZFC_NAMESPACE 468 END_ZFC_NAMESPACE

mercurial