sources/network/rconsession.cpp

Sun, 14 Dec 2014 20:47:44 +0200

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Sun, 14 Dec 2014 20:47:44 +0200
changeset 23
f7221183a994
parent 22
77d02446edf0
child 24
e651d02802c0
permissions
-rw-r--r--

- the prompt is now colored
- the titlebar now changes to the server hostname upon connection

#include "rconsession.h"
#include "../interface.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();
				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:
		{
			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;
}

mercurial