sources/network/rconsession.cpp

Fri, 15 May 2015 20:03:35 +0300

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Fri, 15 May 2015 20:03:35 +0300
changeset 72
1b9c53e0c846
parent 71
4f7c2c944637
child 73
07dda51a7a8e
permissions
-rw-r--r--

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);
}

mercurial