sources/network/rconsession.cpp

Tue, 16 Dec 2014 02:48:18 +0200

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Tue, 16 Dec 2014 02:48:18 +0200
changeset 58
d175243ad169
parent 57
a2f41245e387
child 70
0e947b487b18
permissions
-rw-r--r--

- rcon sessions are no longer allocated on the heap

#include "rconsession.h"
#include "../interface.h"

// -------------------------------------------------------------------------------------------------
//
RCONSession::RCONSession() :
	m_state (RCON_DISCONNECTED),
	m_lastPing (0),
	m_numAdmins (0)
{
	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() {}

// -------------------------------------------------------------------------------------------------
//
METHOD
RCONSession::connect (IPAddress address) -> void
{
	m_address = address;
	m_state = RCON_CONNECTING;
	Interface::update_statusbar();
	send_hello();
}

// -------------------------------------------------------------------------------------------------
//
METHOD
RCONSession::disconnect() -> void
{
	if (m_state > RCON_CONNECTING)
	{
		// Say goodbye to remote
		Bytestream packet;
		packet.write_byte (CLRC_DISCONNECT);
		this->send (packet);
		print ("Disconnected from %1\n", m_address.to_string (IP_WITH_PORT));
		Interface::update_statusbar();
	}

	m_state = RCON_DISCONNECTED;
}

// -------------------------------------------------------------------------------------------------
//
METHOD
RCONSession::send (const Bytestream& packet) -> void
{
	m_socket.send (m_address, packet);
}

// -------------------------------------------------------------------------------------------------
//
METHOD
RCONSession::tick() -> void
{
	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);
}

// -------------------------------------------------------------------------------------------------
//
METHOD
RCONSession::handle_packet (Bytestream& packet, const IPAddress& from) -> void
{
	if (from != m_address)
		return;

	try
	{
		while (packet.bytes_left() > 0)
		{
			int header = packet.read_byte();

			switch (ServerResponse (header))
			{
			case SVRC_OLDPROTOCOL:
				print ("Your RCON client is using outdated protocol.\n");
				m_state = RCON_DISCONNECTED;
				break;

			case SVRC_BANNED:
				print ("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:
				print ("Password incorrect.\n");
				m_state = RCON_DISCONNECTED;
				break;

			case SVRC_MESSAGE:
				{
					String message = packet.read_string();
					message.normalize();
					print ("%1\n", message);
				}
				break;

			case SVRC_LOGGEDIN:
				print ("Login successful!\n");
				m_serverProtocol = packet.read_byte();
				m_hostname = packet.read_string();
				Interface::set_title (m_hostname);
				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:
		{
			StringList players;

			for (int i = packet.read_byte(); i > 0; --i)
				players << packet.read_string();

			Interface::set_player_names (players);
		}
		break;

	case SVRCU_ADMINCOUNT:
		m_numAdmins = packet.read_byte();
		Interface::update_statusbar();
		break;

	case SVRCU_MAP:
		m_level = packet.read_string();
		Interface::update_statusbar();
		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*
{
	static RCONSession session;
	return &session;
}

// -------------------------------------------------------------------------------------------------
//
METHOD
RCONSession::is_active() const -> bool
{
	return state() != RCON_DISCONNECTED;
}

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

// -------------------------------------------------------------------------------------------------
//
METHOD
RCONSession::state() const -> RCONSessionState
{
	return m_state;
}

// -------------------------------------------------------------------------------------------------
//
METHOD
RCONSession::address() const -> const IPAddress&
{
	return m_address;
}

// -------------------------------------------------------------------------------------------------
//
METHOD
RCONSession::num_admins() const -> int
{
	return m_numAdmins;
}

// -------------------------------------------------------------------------------------------------
//
METHOD
RCONSession::level() const -> const String&
{
	return m_level;
}

mercurial