Sun, 14 Dec 2014 20:24:16 +0200
- don't send empty messages
#include "rconsession.h" RCONSession* g_rconSession = nullptr; // ------------------------------------------------------------------------------------------------- // RCONSession::RCONSession() : m_state (RCON_DISCONNECTED), m_lastPing (0) { if (g_rconSession != NULL) { g_rconSession->disconnect(); delete g_rconSession; } g_rconSession = this; if (not m_socket.set_blocking (false)) { // TODO: find a better way to deal with errors print ("unable to set socket as non-blocking: %s\n", m_socket.error_string().chars()); } } // ------------------------------------------------------------------------------------------------- // RCONSession::~RCONSession() { if (g_rconSession == this) g_rconSession = nullptr; } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::connect (IPAddress address) -> void { m_address = address; m_state = RCON_CONNECTING; } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::disconnect() -> void { if (m_state == RCON_CONNECTED) { // Say goodbye to remote Bytestream packet; packet.write_byte (CLRC_DISCONNECT); this->send (packet); } m_state = RCON_DISCONNECTED; } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::send (const Bytestream& packet) -> void { m_socket.send (m_address, packet); } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::tick() -> void { time_t now; time (&now); if (m_lastPing < now) { if (m_state == RCON_CONNECTING) { send_hello(); } else if (m_state == RCON_AUTHENTICATING) { send_password(); } else if (m_state == RCON_CONNECTED and m_lastPing + 5 < now) { Bytestream packet; packet.write_byte (CLRC_PONG); send (packet); bump_last_ping(); } } for (Datagram datagram; m_socket.read (datagram);) handle_packet (datagram.data, datagram.from); } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::handle_packet (Bytestream& packet, const IPAddress& from) -> void { try { while (packet.bytes_left() > 0) { int header = packet.read_byte(); switch (ServerResponse (header)) { case SVRC_OLDPROTOCOL: print ("wrong version\n"); m_state = RCON_DISCONNECTED; break; case SVRC_BANNED: print ("you're banned\n"); m_state = RCON_DISCONNECTED; break; case SVRC_SALT: { String salt = packet.read_string(); m_salt = salt; m_state = RCON_AUTHENTICATING; send_password(); } break; case SVRC_INVALIDPASSWORD: print ("bad password\n"); m_state = RCON_DISCONNECTED; break; case SVRC_MESSAGE: { String message = packet.read_string(); if (message.ends_with ("\n")) message.remove_from_end (1); print ("%1\n", message); } break; case SVRC_LOGGEDIN: print ("Login successful!\n"); m_serverProtocol = packet.read_byte(); m_hostname = packet.read_string(); m_state = RCON_CONNECTED; for (int i = packet.read_byte(); i > 0; --i) process_server_updates (packet); print ("Previous messages:\n"); for (int i = packet.read_byte(); i > 0; --i) { String message = packet.read_string(); message.normalize(); print ("--- %1\n", message); } print ("End of previous messages.\n"); break; case SVRC_UPDATE: process_server_updates (packet); break; } } } catch (std::exception& e) { print ("error while reading packet: %1\n", e.what()); } } METHOD RCONSession::process_server_updates (Bytestream& packet) -> void { switch (RCONUpdateType (packet.read_byte())) { case SVRCU_PLAYERDATA: { Vector<String> players; for (int i = packet.read_byte(); i > 0; --i) players << packet.read_string(); print ("Players: %1\n", players); } break; case SVRCU_ADMINCOUNT: print ("Admin count: %d1\n", packet.read_byte()); break; case SVRCU_MAP: print ("New level: %1\n", packet.read_string()); break; } } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::socket() -> UDPSocket* { return &m_socket; } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::send_hello() -> void { print ("Connecting to %1...\n", m_address.to_string (IP_WITH_PORT)); Bytestream packet; packet.write_byte (CLRC_BEGINCONNECTION); packet.write_byte (RCON_PROTOCOL_VERSION); send (packet); bump_last_ping(); } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::send_password() -> void { print ("Authenticating...\n"); Bytestream packet; packet.write_byte (CLRC_PASSWORD); packet.write_string ((m_salt + m_password).md5()); send (packet); bump_last_ping(); } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::set_password (const String& password) -> void { m_password = password; } // ------------------------------------------------------------------------------------------------- // METHOD RCONSession::bump_last_ping() -> void { time_t now; time (&now); m_lastPing = now; } // ------------------------------------------------------------------------------------------------- // STATIC METHOD RCONSession::get_session() -> RCONSession* { return g_rconSession; } // ------------------------------------------------------------------------------------------------- // Returns true if the message was successfully sent. // METHOD RCONSession::send_command (const String& message) -> bool { if (m_state != RCON_CONNECTED or message.is_empty()) return false; Bytestream packet; packet.write_byte (CLRC_COMMAND); packet.write_string (message); send (packet); bump_last_ping(); return true; }