sources/network/rconsession.cpp

Mon, 15 Dec 2014 20:19:18 +0200

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Mon, 15 Dec 2014 20:19:18 +0200
branch
experimental
changeset 41
9ab869656b9e
parent 40
cf85b5f77541
child 71
4f7c2c944637
permissions
-rw-r--r--

- re-commit tab-complete

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

RCONSession* g_rconSession = nullptr;

// -------------------------------------------------------------------------------------------------
//
RCONSession::RCONSession() :
	m_state (RCON_DISCONNECTED),
	m_lastPing (0),
	m_numAdmins (0)
{
	if (g_rconSession != NULL)
	{
		g_rconSession->disconnect();
		delete g_rconSession;
	}

	g_rconSession = this;
}

// -------------------------------------------------------------------------------------------------
//
STATIC METHOD
RCONSession::new_session() -> RCONSession*
{
	RCONSession* session = new RCONSession;

	if (not session->socket()->set_blocking (false))
	{
		print ("unable to set socket as non-blocking: %s\n",
			session->socket()->error_string().chars());
		delete session;
		return nullptr;
	}

	return session;
}

// -------------------------------------------------------------------------------------------------
//
RCONSession::~RCONSession()
{
	disconnect();

	if (g_rconSession == this)
		g_rconSession = nullptr;
}

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

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

			case SVRC_TOOMANYTABCOMPLETES:
				{
					unsigned int numCompletions = packet.read_short();
					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)
						Interface::tab_complete (m_lastTabComplete, completes[0]);
					else if (not completes.is_empty())
					{
						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));
							print ("- %1\n", splice.join (", "));
						}
					}
				}
				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();
		}
		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*
{
	if (g_rconSession == nullptr)
		new_session();

	return g_rconSession;
}

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

// -------------------------------------------------------------------------------------------------
//
METHOD
RCONSession::request_tab_complete (const String& part) -> void
{
	if (m_serverProtocol >= 4)
	{
		Bytestream packet;
		packet.write_byte (CLRC_TABCOMPLETE);
		packet.write_string (part);
		send (packet);
		bump_last_ping();
		m_lastTabComplete = part;
	}
	else
		print ("Server protocol is %1, cannot tab-complete\n", m_serverProtocol);
}

mercurial