Fri, 15 May 2015 20:03:35 +0300
Massive refactor
#include "rconsession.h" #include "../interface.h" // ------------------------------------------------------------------------------------------------- // RCONSession::RCONSession (Interface* iface) : m_state (RCON_DISCONNECTED), m_lastPing (0), m_numAdmins (0), m_interface (iface) { if (not m_socket.set_blocking (false)) { print_to (stderr, "unable to set socket as non-blocking: %s\n", m_socket.error_string().chars()); exit (EXIT_FAILURE); } } // ------------------------------------------------------------------------------------------------- // RCONSession::~RCONSession() {} // ------------------------------------------------------------------------------------------------- // void RCONSession::connect (IPAddress address) { m_address = address; m_state = RCON_CONNECTING; m_interface->update_statusbar(); send_hello(); } // ------------------------------------------------------------------------------------------------- // void RCONSession::disconnect() { if (m_state > RCON_CONNECTING) { // Say goodbye to remote Bytestream packet; packet.write_byte (CLRC_DISCONNECT); this->send (packet); m_interface->print ("Disconnected from %1\n", m_address.to_string (IP_WITH_PORT)); m_interface->update_statusbar(); } m_state = RCON_DISCONNECTED; } // ------------------------------------------------------------------------------------------------- // void RCONSession::send (const Bytestream& packet) { m_socket.send (m_address, packet); } // ------------------------------------------------------------------------------------------------- // void RCONSession::tick() { if (m_state == RCON_DISCONNECTED) return; 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); } // ------------------------------------------------------------------------------------------------- // void RCONSession::handle_packet (Bytestream& packet, const IPAddress& from) { if (from != m_address) return; try { while (packet.bytes_left() > 0) { int header = packet.read_byte(); switch (ServerResponse (header)) { case SVRC_OLDPROTOCOL: m_interface->print_error ("Your RCON client is using outdated protocol.\n"); m_state = RCON_DISCONNECTED; break; case SVRC_BANNED: m_interface->print_error ("You have been banned from the server.\n"); m_state = RCON_DISCONNECTED; break; case SVRC_SALT: m_salt = packet.read_string(); m_state = RCON_AUTHENTICATING; send_password(); break; case SVRC_INVALIDPASSWORD: m_interface->print_error ("Login failed.\n"); m_state = RCON_DISCONNECTED; break; case SVRC_MESSAGE: { String message = packet.read_string(); message.normalize(); m_interface->print ("%1\n", message); } break; case SVRC_LOGGEDIN: m_interface->print ("Login successful!\n"); m_serverProtocol = packet.read_byte(); m_hostname = packet.read_string(); m_interface->set_title (m_hostname); m_state = RCON_CONNECTED; for (int i = packet.read_byte(); i > 0; --i) process_server_updates (packet); m_interface->print ("Previous messages:\n"); for (int i = packet.read_byte(); i > 0; --i) { String message = packet.read_string(); message.normalize(); m_interface->print ("--- %1\n", message); } m_interface->print ("End of previous messages.\n"); break; case SVRC_UPDATE: process_server_updates (packet); break; case SVRC_TOOMANYTABCOMPLETES: { unsigned int numCompletions = packet.read_short(); m_interface->print ("%1 completions for '%2'.\n", int (numCompletions), m_lastTabComplete); } break; case SVRC_TABCOMPLETE: { StringList completes; for (signed int i = packet.read_byte(); i > 0; --i) completes << packet.read_string(); if (completes.size() == 1) { m_interface->tab_complete (m_lastTabComplete, completes[0]); } else if (not completes.is_empty()) { m_interface->print ("Completions for '%1':\n", m_lastTabComplete); for (int i = 0; i < completes.size(); i += 8) { Range<int> spliceRange (i, min (i + 8, completes.size() - 1)); StringList splice (completes.splice (spliceRange)); m_interface->print ("- %1\n", splice.join (", ")); } } } break; } } } catch (std::exception& e) { m_interface->print_warning ("Couldn't process packet: %1\n", e.what()); } } void RCONSession::process_server_updates (Bytestream& packet) { int header = packet.read_byte(); switch (RCONUpdateType (header)) { case SVRCU_PLAYERDATA: { StringList players; for (int i = packet.read_byte(); i > 0; --i) players.append (packet.read_string()); m_interface->set_player_names (players); } break; case SVRCU_ADMINCOUNT: m_numAdmins = packet.read_byte(); m_interface->update_statusbar(); break; case SVRCU_MAP: m_level = packet.read_string(); m_interface->update_statusbar(); break; default: m_interface->print_warning ("Unknown server update type: %d\n", header); break; } } // ------------------------------------------------------------------------------------------------- // UDPSocket* RCONSession::socket() { return &m_socket; } // ------------------------------------------------------------------------------------------------- // void RCONSession::send_hello() { m_interface->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(); } // ------------------------------------------------------------------------------------------------- // void RCONSession::send_password() { m_interface->print ("Authenticating...\n"); Bytestream packet; packet.write_byte (CLRC_PASSWORD); packet.write_string ((m_salt + m_password).md5()); send (packet); bump_last_ping(); } // ------------------------------------------------------------------------------------------------- // void RCONSession::set_password (const String& password) { m_password = password; } // ------------------------------------------------------------------------------------------------- // void RCONSession::bump_last_ping() { time_t now; time (&now); m_lastPing = now; } // ------------------------------------------------------------------------------------------------- // bool RCONSession::is_active() const { return state() != RCON_DISCONNECTED; } // ------------------------------------------------------------------------------------------------- // Returns true if the message was successfully sent. // bool RCONSession::send_command (const String& message) { 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; } // ------------------------------------------------------------------------------------------------- // RCONSessionState RCONSession::state() const { return m_state; } // ------------------------------------------------------------------------------------------------- // const IPAddress& RCONSession::address() const { return m_address; } // ------------------------------------------------------------------------------------------------- // int RCONSession::num_admins() const { return m_numAdmins; } // ------------------------------------------------------------------------------------------------- // const String& RCONSession::level() const { return m_level; } // ------------------------------------------------------------------------------------------------- // void RCONSession::request_tab_complete (const String& part) { if (m_serverProtocol >= 4) { Bytestream packet; packet.write_byte (CLRC_TABCOMPLETE); packet.write_string (part); send (packet); bump_last_ping(); m_lastTabComplete = part; } else m_interface->print ("Server protocol is %1, cannot tab-complete\n", m_serverProtocol); }