Changeset - r17642:ab10f4b22179
[Not reviewed]
master
0 8 0
rubidium - 14 years ago 2011-05-05 16:24:48
rubidium@openttd.org
(svn r22424) -Document: some more bits
8 files changed with 283 insertions and 43 deletions:
0 comments (0 inline, 0 general)
src/network/network.cpp
Show inline comments
 
@@ -117,25 +117,25 @@ NetworkClientInfo::~NetworkClientInfo()
 
	FOR_ALL_CLIENT_INFOS(ci) {
 
		if (ci->client_id == client_id) return ci;
 
	}
 

	
 
	return NULL;
 
}
 

	
 
/**
 
 * Return the client state given it's client-identifier
 
 * @param client_id the ClientID to search for
 
 * @return return a pointer to the corresponding NetworkClientSocket struct or NULL when not found
 
 */
 
/* static */ NetworkClientSocket *NetworkClientSocket::GetByClientID(ClientID client_id)
 
/* static */ ServerNetworkGameSocketHandler *ServerNetworkGameSocketHandler::GetByClientID(ClientID client_id)
 
{
 
	NetworkClientSocket *cs;
 

	
 
	FOR_ALL_CLIENT_SOCKETS(cs) {
 
		if (cs->client_id == client_id) return cs;
 
	}
 

	
 
	return NULL;
 
}
 

	
 
byte NetworkSpectatorCount()
 
{
 
@@ -470,24 +470,29 @@ void ParseConnectionString(const char **
 
				*p = '\0';
 
				break;
 

	
 
			case ':':
 
				if (ipv6) break;
 
				*port = p + 1;
 
				*p = '\0';
 
				break;
 
		}
 
	}
 
}
 

	
 
/**
 
 * Handle the acception of a connection to the server.
 
 * @param s The socket of the new connection.
 
 * @param address The address of the peer.
 
 */
 
/* static */ void ServerNetworkGameSocketHandler::AcceptConnection(SOCKET s, const NetworkAddress &address)
 
{
 
	/* Register the login */
 
	_network_clients_connected++;
 

	
 
	SetWindowDirty(WC_CLIENT_LIST, 0);
 
	ServerNetworkGameSocketHandler *cs = new ServerNetworkGameSocketHandler(s);
 
	cs->client_address = address; // Save the IP of the client
 
}
 

	
 
/**
 
 * Resets the pools used for network clients, and the admin pool if needed.
src/network/network_admin.cpp
Show inline comments
 
@@ -98,189 +98,233 @@ ServerNetworkAdminSocketHandler::~Server
 
	FOR_ALL_ADMIN_SOCKETS(as) {
 
		if (as->status == ADMIN_STATUS_INACTIVE && as->realtime_connect + ADMIN_AUTHORISATION_TIMEOUT < _realtime_tick) {
 
			DEBUG(net, 1, "[admin] Admin did not send its authorisation within %d seconds", ADMIN_AUTHORISATION_TIMEOUT / 1000);
 
			as->CloseConnection(true);
 
			continue;
 
		}
 
		if (as->writable) {
 
			as->SendPackets();
 
		}
 
	}
 
}
 

	
 
/**
 
 * Handle the acception of a connection.
 
 * @param s The socket of the new connection.
 
 * @param address The address of the peer.
 
 */
 
/* static */ void ServerNetworkAdminSocketHandler::AcceptConnection(SOCKET s, const NetworkAddress &address)
 
{
 
	ServerNetworkAdminSocketHandler *as = new ServerNetworkAdminSocketHandler(s);
 
	as->address = address; // Save the IP of the client
 
}
 

	
 
/***********
 
 * Sending functions for admin network
 
 ************/
 

	
 
/**
 
 * Send an error to the admin.
 
 * @param error The error to send.
 
 */
 
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendError(NetworkErrorCode error)
 
{
 
	Packet *p = new Packet(ADMIN_PACKET_SERVER_ERROR);
 

	
 
	p->Send_uint8(error);
 
	this->SendPacket(p);
 

	
 
	char str[100];
 
	StringID strid = GetNetworkErrorMsg(error);
 
	GetString(str, strid, lastof(str));
 

	
 
	DEBUG(net, 1, "[admin] the admin '%s' (%s) made an error and has been disconnected. Reason: '%s'", this->admin_name, this->admin_version, str);
 

	
 
	return this->CloseConnection(true);
 
}
 

	
 
/** Send the protocol version to the admin. */
 
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendProtocol()
 
{
 
	Packet *p = new Packet(ADMIN_PACKET_SERVER_PROTOCOL);
 

	
 
	/* announce the protocol version */
 
	p->Send_uint8(NETWORK_GAME_ADMIN_VERSION);
 

	
 
	for (int i = 0; i < ADMIN_UPDATE_END; i++) {
 
		p->Send_bool  (true);
 
		p->Send_uint16(i);
 
		p->Send_uint16(_admin_update_type_frequencies[i]);
 
	}
 

	
 
	p->Send_bool(false);
 
	this->SendPacket(p);
 

	
 
	return this->SendWelcome();
 
}
 

	
 
/** Send a welcome message to the admin. */
 
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendWelcome()
 
{
 
	this->status = ADMIN_STATUS_ACTIVE;
 

	
 
	Packet *p = new Packet(ADMIN_PACKET_SERVER_WELCOME);
 

	
 
	p->Send_string(_settings_client.network.server_name);
 
	p->Send_string(_openttd_revision);
 
	p->Send_bool  (_network_dedicated);
 

	
 
	p->Send_string(_network_game_info.map_name);
 
	p->Send_uint32(_settings_game.game_creation.generation_seed);
 
	p->Send_uint8 (_settings_game.game_creation.landscape);
 
	p->Send_uint32(ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1));
 
	p->Send_uint16(MapSizeX());
 
	p->Send_uint16(MapSizeY());
 

	
 
	this->SendPacket(p);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/** Tell the admin we started a new game. */
 
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendNewGame()
 
{
 
	Packet *p = new Packet(ADMIN_PACKET_SERVER_NEWGAME);
 
	this->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/** Tell the admin we're shutting down. */
 
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendShutdown()
 
{
 
	Packet *p = new Packet(ADMIN_PACKET_SERVER_SHUTDOWN);
 
	this->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/** Tell the admin the date. */
 
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendDate()
 
{
 
	Packet *p = new Packet(ADMIN_PACKET_SERVER_DATE);
 

	
 
	p->Send_uint32(_date);
 
	this->SendPacket(p);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Tell the admin that a client joined.
 
 * @param client_id The client that joined.
 
 */
 
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientJoin(ClientID client_id)
 
{
 
	Packet *p = new Packet(ADMIN_PACKET_SERVER_CLIENT_JOIN);
 

	
 
	p->Send_uint32(client_id);
 
	this->SendPacket(p);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Send an initial set of data from some client's information.
 
 * @param cs The information about a client.
 
 */
 
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientInfo(const NetworkClientSocket *cs)
 
{
 
	/* Only send data when we're a proper client, not just someone trying to query the server. */
 
	const NetworkClientInfo *ci = cs->GetInfo();
 
	if (ci == NULL) return NETWORK_RECV_STATUS_OKAY;
 

	
 
	Packet *p = new Packet(ADMIN_PACKET_SERVER_CLIENT_INFO);
 

	
 
	p->Send_uint32(ci->client_id);
 
	p->Send_string(const_cast<NetworkAddress &>(cs->client_address).GetHostname());
 
	p->Send_string(ci->client_name);
 
	p->Send_uint8 (ci->client_lang);
 
	p->Send_uint32(ci->join_date);
 
	p->Send_uint8 (ci->client_playas);
 

	
 
	this->SendPacket(p);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 

	
 
/**
 
 * Send an update for some client's information.
 
 * @param ci The information about a client.
 
 */
 
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientUpdate(const NetworkClientInfo *ci)
 
{
 
	Packet *p = new Packet(ADMIN_PACKET_SERVER_CLIENT_UPDATE);
 

	
 
	p->Send_uint32(ci->client_id);
 
	p->Send_string(ci->client_name);
 
	p->Send_uint8 (ci->client_playas);
 

	
 
	this->SendPacket(p);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Tell the admin that a client quit.
 
 * @param client_id The client that quit.
 
 */
 
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientQuit(ClientID client_id)
 
{
 
	Packet *p = new Packet(ADMIN_PACKET_SERVER_CLIENT_QUIT);
 

	
 
	p->Send_uint32(client_id);
 
	this->SendPacket(p);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Tell the admin that a client made an error.
 
 * @param client_id The client that made the error.
 
 * @param error The error that was made.
 
 */
 
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientError(ClientID client_id, NetworkErrorCode error)
 
{
 
	Packet *p = new Packet(ADMIN_PACKET_SERVER_CLIENT_ERROR);
 

	
 
	p->Send_uint32(client_id);
 
	p->Send_uint8 (error);
 
	this->SendPacket(p);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Tell the admin that a new company was founded.
 
 * @param company_id The company that was founded.
 
 */
 
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyNew(CompanyID company_id)
 
{
 
	Packet *p = new Packet(ADMIN_PACKET_SERVER_COMPANY_NEW);
 
	p->Send_uint8(company_id);
 

	
 
	this->SendPacket(p);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Send the admin some information about a company.
 
 * @param c The company to send the information about.
 
 */
 
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyInfo(const Company *c)
 
{
 
	char company_name[NETWORK_COMPANY_NAME_LENGTH];
 
	char manager_name[NETWORK_COMPANY_NAME_LENGTH];
 

	
 
	SetDParam(0, c->index);
 
	GetString(company_name, STR_COMPANY_NAME, lastof(company_name));
 

	
 
	SetDParam(0, c->index);
 
	GetString(manager_name, STR_PRESIDENT_NAME, lastof(manager_name));
 

	
 
	Packet *p = new Packet(ADMIN_PACKET_SERVER_COMPANY_INFO);
 
@@ -289,24 +333,29 @@ NetworkRecvStatus ServerNetworkAdminSock
 
	p->Send_string(company_name);
 
	p->Send_string(manager_name);
 
	p->Send_uint8 (c->colour);
 
	p->Send_bool  (NetworkCompanyIsPassworded(c->index));
 
	p->Send_uint32(c->inaugurated_year);
 
	p->Send_bool  (c->is_ai);
 

	
 
	this->SendPacket(p);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 

	
 
/**
 
 * Send an update about a company.
 
 * @param c The company to send the update of.
 
 */
 
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyUpdate(const Company *c)
 
{
 
	char company_name[NETWORK_COMPANY_NAME_LENGTH];
 
	char manager_name[NETWORK_COMPANY_NAME_LENGTH];
 

	
 
	SetDParam(0, c->index);
 
	GetString(company_name, STR_COMPANY_NAME, lastof(company_name));
 

	
 
	SetDParam(0, c->index);
 
	GetString(manager_name, STR_PRESIDENT_NAME, lastof(manager_name));
 

	
 
	Packet *p = new Packet(ADMIN_PACKET_SERVER_COMPANY_UPDATE);
 
@@ -318,36 +367,42 @@ NetworkRecvStatus ServerNetworkAdminSock
 
	p->Send_bool  (NetworkCompanyIsPassworded(c->index));
 
	p->Send_uint8 (c->quarters_of_bankruptcy);
 

	
 
	for (size_t i = 0; i < lengthof(c->share_owners); i++) {
 
		p->Send_uint8(c->share_owners[i]);
 
	}
 

	
 
	this->SendPacket(p);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Tell the admin that a company got removed.
 
 * @param company_id The company that got removed.
 
 * @param acrr The reason for removal, e.g. bankruptcy or merger.
 
 */
 
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyRemove(CompanyID company_id, AdminCompanyRemoveReason acrr)
 
{
 
	Packet *p = new Packet(ADMIN_PACKET_SERVER_COMPANY_REMOVE);
 

	
 
	p->Send_uint8(company_id);
 
	p->Send_uint8(acrr);
 

	
 
	this->SendPacket(p);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/** Send economic information of all companies. */
 
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyEconomy()
 
{
 
	const Company *company;
 
	FOR_ALL_COMPANIES(company) {
 
		/* Get the income. */
 
		Money income = 0;
 
		for (uint i = 0; i < lengthof(company->yearly_expenses[0]); i++) {
 
			income -= company->yearly_expenses[0][i];
 
		}
 

	
 
		Packet *p = new Packet(ADMIN_PACKET_SERVER_COMPANY_ECONOMY);
 

	
 
@@ -364,24 +419,25 @@ NetworkRecvStatus ServerNetworkAdminSock
 
			p->Send_uint64(company->old_economy[i].company_value);
 
			p->Send_uint16(company->old_economy[i].performance_history);
 
			p->Send_uint16(company->old_economy[i].delivered_cargo);
 
		}
 

	
 
		this->SendPacket(p);
 
	}
 

	
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/** Send statistics about the companies. */
 
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyStats()
 
{
 
	/* Fetch the latest version of the stats. */
 
	NetworkCompanyStats company_stats[MAX_COMPANIES];
 
	NetworkPopulateCompanyStats(company_stats);
 

	
 
	const Company *company;
 

	
 
	/* Go through all the companies. */
 
	FOR_ALL_COMPANIES(company) {
 
		Packet *p = new Packet(ADMIN_PACKET_SERVER_COMPANY_STATS);
 

	
 
@@ -393,38 +449,51 @@ NetworkRecvStatus ServerNetworkAdminSock
 
		}
 

	
 
		for (uint i = 0; i < NETWORK_VEH_END; i++) {
 
			p->Send_uint16(company_stats[company->index].num_station[i]);
 
		}
 

	
 
		this->SendPacket(p);
 
	}
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Send a chat message.
 
 * @param action The action associated with the message.
 
 * @param desttype The destination type.
 
 * @param client_id The origin of the chat message.
 
 * @param msg The actual message.
 
 * @param data Arbitrary extra data.
 
 */
 
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendChat(NetworkAction action, DestType desttype, ClientID client_id, const char *msg, int64 data)
 
{
 
	Packet *p = new Packet(ADMIN_PACKET_SERVER_CHAT);
 

	
 
	p->Send_uint8 (action);
 
	p->Send_uint8 (desttype);
 
	p->Send_uint32(client_id);
 
	p->Send_string(msg);
 
	p->Send_uint64(data);
 

	
 
	this->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Send the reply of an rcon command.
 
 * @param colour The colour of the text.
 
 * @param result The result of the command.
 
 */
 
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendRcon(uint16 colour, const char *result)
 
{
 
	Packet *p = new Packet(ADMIN_PACKET_SERVER_RCON);
 

	
 
	p->Send_uint16(colour);
 
	p->Send_string(result);
 
	this->SendPacket(p);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
NetworkRecvStatus ServerNetworkAdminSocketHandler::Receive_ADMIN_RCON(Packet *p)
 
@@ -434,41 +503,47 @@ NetworkRecvStatus ServerNetworkAdminSock
 
	char command[NETWORK_RCONCOMMAND_LENGTH];
 

	
 
	p->Recv_string(command, sizeof(command));
 

	
 
	DEBUG(net, 2, "[admin] Rcon command from '%s' (%s): '%s'", this->admin_name, this->admin_version, command);
 

	
 
	_redirect_console_to_admin = this->index;
 
	IConsoleCmdExec(command);
 
	_redirect_console_to_admin = INVALID_ADMIN_ID;
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Send console output of other clients.
 
 * @param origin The origin of the string.
 
 * @param string The string that's put on the console.
 
 */
 
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendConsole(const char *origin, const char *string)
 
{
 
	/* If the length of both strings, plus the 2 '\0' terminations and 3 bytes of the packet
 
	 * are bigger than the MTU, just ignore the message. Better safe than sorry. It should
 
	 * never occur though as the longest strings are chat messages, which are still 30%
 
	 * smaller than SEND_MTU. */
 
	if (strlen(origin) + strlen(string) + 2 + 3 >= SEND_MTU) return NETWORK_RECV_STATUS_OKAY;
 

	
 
	Packet *p = new Packet(ADMIN_PACKET_SERVER_CONSOLE);
 

	
 
	p->Send_string(origin);
 
	p->Send_string(string);
 
	this->SendPacket(p);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/** Send the names of the commands. */
 
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCmdNames()
 
{
 
	Packet *p = new Packet(ADMIN_PACKET_SERVER_CMD_NAMES);
 

	
 
	for (uint i = 0; i < CMD_END; i++) {
 
		const char *cmdname = GetCommandName(i);
 

	
 
		/* Should SEND_MTU be exceeded, start a new packet
 
		 * (magic 5: 1 bool "more data" and one uint16 "command id", one
 
		 * byte for string '\0' termination and 1 bool "no more data" */
 
		if (p->size + strlen(cmdname) + 5 >= SEND_MTU) {
 
			p->Send_bool(false);
 
@@ -480,24 +555,29 @@ NetworkRecvStatus ServerNetworkAdminSock
 
		p->Send_bool(true);
 
		p->Send_uint16(i);
 
		p->Send_string(cmdname);
 
	}
 

	
 
	/* Marker to notify the end of the packet has been reached. */
 
	p->Send_bool(false);
 
	this->SendPacket(p);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Send a command for logging purposes.
 
 * @param client_id The client executing the command.
 
 * @param cp The command that would be executed.
 
 */
 
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCmdLogging(ClientID client_id, const CommandPacket *cp)
 
{
 
	Packet *p = new Packet(ADMIN_PACKET_SERVER_CMD_LOGGING);
 

	
 
	p->Send_uint32(client_id);
 
	p->Send_uint8 (cp->company);
 
	p->Send_uint16(cp->cmd & CMD_ID_MASK);
 
	p->Send_uint32(cp->p1);
 
	p->Send_uint32(cp->p2);
 
	p->Send_uint32(cp->tile);
 
	p->Send_string(cp->text);
 
	p->Send_uint32(cp->frame);
src/network/network_admin.h
Show inline comments
 
@@ -12,24 +12,25 @@
 
#ifndef NETWORK_ADMIN_H
 
#define NETWORK_ADMIN_H
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "network_internal.h"
 
#include "core/tcp_listen.h"
 
#include "core/tcp_admin.h"
 

	
 
extern AdminIndex _redirect_console_to_admin;
 

	
 
class ServerNetworkAdminSocketHandler;
 
/** Pool with all admin connections. */
 
typedef Pool<ServerNetworkAdminSocketHandler, AdminIndex, 2, MAX_ADMINS, PT_NADMIN> NetworkAdminSocketPool;
 
extern NetworkAdminSocketPool _networkadminsocket_pool;
 

	
 
/** Class for handling the server side of the game connection. */
 
class ServerNetworkAdminSocketHandler : public NetworkAdminSocketPool::PoolItem<&_networkadminsocket_pool>, public NetworkAdminSocketHandler, public TCPListenHandler<ServerNetworkAdminSocketHandler, ADMIN_PACKET_SERVER_FULL, ADMIN_PACKET_SERVER_BANNED> {
 
protected:
 
	virtual NetworkRecvStatus Receive_ADMIN_JOIN(Packet *p);
 
	virtual NetworkRecvStatus Receive_ADMIN_QUIT(Packet *p);
 
	virtual NetworkRecvStatus Receive_ADMIN_UPDATE_FREQUENCY(Packet *p);
 
	virtual NetworkRecvStatus Receive_ADMIN_POLL(Packet *p);
 
	virtual NetworkRecvStatus Receive_ADMIN_CHAT(Packet *p);
 
	virtual NetworkRecvStatus Receive_ADMIN_RCON(Packet *p);
 
@@ -73,25 +74,35 @@ public:
 
	static void WelcomeAll();
 

	
 
	/**
 
	 * Get the name used by the listener.
 
	 * @return the name to show in debug logs and the like.
 
	 */
 
	static const char *GetName()
 
	{
 
		return "admin";
 
	}
 
};
 

	
 
/**
 
 * Iterate over all the sockets from a given starting point.
 
 * @param var The variable to iterate with.
 
 * @param start The start of the iteration.
 
 */
 
#define FOR_ALL_ADMIN_SOCKETS_FROM(var, start) FOR_ALL_ITEMS_FROM(ServerNetworkAdminSocketHandler, adminsocket_index, var, start)
 

	
 
/**
 
 * Iterate over all the sockets.
 
 * @param var The variable to iterate with.
 
 */
 
#define FOR_ALL_ADMIN_SOCKETS(var) FOR_ALL_ADMIN_SOCKETS_FROM(var, 0)
 

	
 
void NetworkAdminClientInfo(const NetworkClientSocket *cs, bool new_client = false);
 
void NetworkAdminClientUpdate(const NetworkClientInfo *ci);
 
void NetworkAdminClientQuit(ClientID client_id);
 
void NetworkAdminClientError(ClientID client_id, NetworkErrorCode error_code);
 
void NetworkAdminCompanyInfo(const Company *company, bool new_company);
 
void NetworkAdminCompanyUpdate(const Company *company);
 
void NetworkAdminCompanyRemove(CompanyID company_id, AdminCompanyRemoveReason bcrr);
 

	
 
void NetworkAdminChat(NetworkAction action, DestType desttype, ClientID client_id, const char *msg, int64 data = 0, bool from_admin = false);
 
void NetworkAdminUpdate(AdminUpdateFrequency freq);
src/network/network_chat_gui.cpp
Show inline comments
 
@@ -21,48 +21,56 @@
 
#include "../video/video_driver.hpp"
 
#include "../table/sprites.h"
 
#include "../querystring_gui.h"
 
#include "../town.h"
 
#include "../window_func.h"
 
#include "../core/geometry_func.hpp"
 
#include "network.h"
 
#include "network_client.h"
 
#include "network_base.h"
 

	
 
#include "table/strings.h"
 

	
 
/* The draw buffer must be able to contain the chat message, client name and the "[All]" message,
 
/** The draw buffer must be able to contain the chat message, client name and the "[All]" message,
 
 * some spaces and possible translations of [All] to other languages. */
 
assert_compile((int)DRAW_STRING_BUFFER >= (int)NETWORK_CHAT_LENGTH + NETWORK_NAME_LENGTH + 40);
 

	
 
/** Spacing between chat lines. */
 
static const uint NETWORK_CHAT_LINE_SPACING = 3;
 

	
 
/** Container for a message. */
 
struct ChatMessage {
 
	char message[DRAW_STRING_BUFFER];
 
	TextColour colour;
 
	uint32 remove_time;
 
	char message[DRAW_STRING_BUFFER]; ///< The action message.
 
	TextColour colour;  ///< The colour of the message.
 
	uint32 remove_time; ///< The time to remove the message.
 
};
 

	
 
/* used for chat window */
 
static ChatMessage *_chatmsg_list = NULL;
 
static bool _chatmessage_dirty = false;
 
static bool _chatmessage_visible = false;
 
static bool _chat_tab_completion_active;
 
static uint MAX_CHAT_MESSAGES = 0;
 
static ChatMessage *_chatmsg_list = NULL; ///< The actual chat message list.
 
static bool _chatmessage_dirty = false;   ///< Does the chat message need repainting?
 
static bool _chatmessage_visible = false; ///< Is a chat message visible.
 
static bool _chat_tab_completion_active;  ///< Whether tab completion is active.
 
static uint MAX_CHAT_MESSAGES = 0;        ///< The limit of chat messages to show.
 

	
 
/* The chatbox grows from the bottom so the coordinates are pixels from
 
 * the left and pixels from the bottom. The height is the maximum height */
 
/**
 
 * The chatbox grows from the bottom so the coordinates are pixels from
 
 * the left and pixels from the bottom. The height is the maximum height.
 
 */
 
static PointDimension _chatmsg_box;
 
static uint8 *_chatmessage_backup = NULL;
 
static uint8 *_chatmessage_backup = NULL; ///< Backup in case text is moved.
 

	
 
/**
 
 * Count the chat messages.
 
 * @return The number of chat messages.
 
 */
 
static inline uint GetChatMessageCount()
 
{
 
	uint i = 0;
 
	for (; i < MAX_CHAT_MESSAGES; i++) {
 
		if (_chatmsg_list[i].message[0] == '\0') break;
 
	}
 

	
 
	return i;
 
}
 

	
 
/**
 
 * Add a text message to the 'chat window' to be shown
 
@@ -110,24 +118,25 @@ void CDECL NetworkAddChatMessage(TextCol
 

	
 
	_chatmessage_dirty = true;
 
}
 

	
 
/** Initialize all font-dependent chat box sizes. */
 
void NetworkReInitChatBoxSize()
 
{
 
	_chatmsg_box.y       = 3 * FONT_HEIGHT_NORMAL;
 
	_chatmsg_box.height  = MAX_CHAT_MESSAGES * (FONT_HEIGHT_NORMAL + NETWORK_CHAT_LINE_SPACING) + 2;
 
	_chatmessage_backup  = ReallocT(_chatmessage_backup, _chatmsg_box.width * _chatmsg_box.height * BlitterFactoryBase::GetCurrentBlitter()->GetBytesPerPixel());
 
}
 

	
 
/** Initialize all buffers of the chat visualisation. */
 
void NetworkInitChatMessage()
 
{
 
	MAX_CHAT_MESSAGES    = _settings_client.gui.network_chat_box_height;
 

	
 
	_chatmsg_list        = ReallocT(_chatmsg_list, _settings_client.gui.network_chat_box_height);
 
	_chatmsg_box.x       = 10;
 
	_chatmsg_box.width   = _settings_client.gui.network_chat_box_width;
 
	NetworkReInitChatBoxSize();
 
	_chatmessage_visible = false;
 

	
 
	for (uint i = 0; i < MAX_CHAT_MESSAGES; i++) {
 
		_chatmsg_list[i].message[0] = '\0';
 
@@ -249,49 +258,61 @@ void NetworkDrawChatMessage()
 
	/* Paint the chat messages starting with the lowest at the bottom */
 
	for (uint y = FONT_HEIGHT_NORMAL + NETWORK_CHAT_LINE_SPACING; count-- != 0; y += (FONT_HEIGHT_NORMAL + NETWORK_CHAT_LINE_SPACING)) {
 
		DrawString(_chatmsg_box.x + 3, _chatmsg_box.x + _chatmsg_box.width - 1, _screen.height - _chatmsg_box.y - y + 1, _chatmsg_list[count].message, _chatmsg_list[count].colour);
 
	}
 

	
 
	/* Make sure the data is updated next flush */
 
	_video_driver->MakeDirty(x, y, width, height);
 

	
 
	_chatmessage_visible = true;
 
	_chatmessage_dirty = false;
 
}
 

	
 

	
 
/**
 
 * Send an actual chat message.
 
 * @param buf The message to send.
 
 * @param type The type of destination.
 
 * @param dest The actual destination index.
 
 */
 
static void SendChat(const char *buf, DestType type, int dest)
 
{
 
	if (StrEmpty(buf)) return;
 
	if (!_network_server) {
 
		MyClient::SendChat((NetworkAction)(NETWORK_ACTION_CHAT + type), type, dest, buf, 0);
 
	} else {
 
		NetworkServerSendChat((NetworkAction)(NETWORK_ACTION_CHAT + type), type, dest, buf, CLIENT_ID_SERVER);
 
	}
 
}
 

	
 
/** Widget numbers of the chat window. */
 
enum NetWorkChatWidgets {
 
	NWCW_CLOSE,
 
	NWCW_BACKGROUND,
 
	NWCW_DESTINATION,
 
	NWCW_TEXTBOX,
 
	NWCW_SENDBUTTON,
 
};
 

	
 
/** Window to enter the chat message in. */
 
struct NetworkChatWindow : public QueryStringBaseWindow {
 
	DestType dtype;
 
	StringID dest_string;
 
	int dest;
 
	DestType dtype;       ///< The type of destination.
 
	StringID dest_string; ///< String representation of the destination.
 
	int dest;             ///< The identifier of the destination.
 

	
 
	/**
 
	 * Create a chat input window.
 
	 * @param desc Description of the looks of the window.
 
	 * @param type The type of destination.
 
	 * @param dest The actual destination index.
 
	 */
 
	NetworkChatWindow(const WindowDesc *desc, DestType type, int dest) : QueryStringBaseWindow(NETWORK_CHAT_LENGTH)
 
	{
 
		this->dtype   = type;
 
		this->dest    = dest;
 
		this->afilter = CS_ALPHANUMERAL;
 
		InitializeTextBuffer(&this->text, this->edit_str_buf, this->edit_str_size);
 

	
 
		static const StringID chat_captions[] = {
 
			STR_NETWORK_CHAT_ALL_CAPTION,
 
			STR_NETWORK_CHAT_COMPANY_CAPTION,
 
			STR_NETWORK_CHAT_CLIENT_CAPTION
 
		};
 
@@ -533,40 +554,48 @@ struct NetworkChatWindow : public QueryS
 

	
 
	/**
 
	 * Some data on this window has become invalid.
 
	 * @param data Information about the changed data.
 
	 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
 
	 */
 
	virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
 
	{
 
		if (data == this->dest) delete this;
 
	}
 
};
 

	
 
/** The widgets of the chat window. */
 
static const NWidgetPart _nested_chat_window_widgets[] = {
 
	NWidget(NWID_HORIZONTAL),
 
		NWidget(WWT_CLOSEBOX, COLOUR_GREY, NWCW_CLOSE),
 
		NWidget(WWT_PANEL, COLOUR_GREY, NWCW_BACKGROUND),
 
			NWidget(NWID_HORIZONTAL),
 
				NWidget(WWT_TEXT, COLOUR_GREY, NWCW_DESTINATION), SetMinimalSize(62, 12), SetPadding(1, 0, 1, 0), SetDataTip(STR_NULL, STR_NULL),
 
				NWidget(WWT_EDITBOX, COLOUR_GREY, NWCW_TEXTBOX), SetMinimalSize(100, 12), SetPadding(1, 0, 1, 0), SetResize(1, 0),
 
																	SetDataTip(STR_NETWORK_CHAT_OSKTITLE, STR_NULL),
 
				NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, NWCW_SENDBUTTON), SetMinimalSize(62, 12), SetPadding(1, 0, 1, 0), SetDataTip(STR_NETWORK_CHAT_SEND, STR_NULL),
 
			EndContainer(),
 
		EndContainer(),
 
	EndContainer(),
 
};
 

	
 
/** The description of the chat window. */
 
static const WindowDesc _chat_window_desc(
 
	WDP_MANUAL, 640, 14, // x, y, width, height
 
	WC_SEND_NETWORK_MSG, WC_NONE,
 
	0,
 
	_nested_chat_window_widgets, lengthof(_nested_chat_window_widgets)
 
);
 

	
 

	
 
/**
 
 * Show the chat window.
 
 * @param type The type of destination.
 
 * @param dest The actual destination index.
 
 */
 
void ShowNetworkChatQueryWindow(DestType type, int dest)
 
{
 
	DeleteWindowByClass(WC_SEND_NETWORK_MSG);
 
	new NetworkChatWindow(&_chat_window_desc, type, dest);
 
}
 

	
 
#endif /* ENABLE_NETWORK */
src/network/network_gui.cpp
Show inline comments
 
@@ -2036,29 +2036,28 @@ struct NetworkClientListWindow : Window 
 
		this->selected_item = item;
 

	
 
		/* Repaint */
 
		this->SetDirty();
 
	}
 
};
 

	
 
void ShowClientList()
 
{
 
	AllocateWindowDescFront<NetworkClientListWindow>(&_client_list_desc, 0);
 
}
 

	
 
/* Vars needed for the join-GUI */
 
NetworkJoinStatus _network_join_status;
 
uint8 _network_join_waiting;
 
uint32 _network_join_bytes;
 
uint32 _network_join_bytes_total;
 
NetworkJoinStatus _network_join_status; ///< The status of joining.
 
uint8 _network_join_waiting;            ///< The number of clients waiting in front of us.
 
uint32 _network_join_bytes;             ///< The number of bytes we already downloaded.
 
uint32 _network_join_bytes_total;       ///< The total number of bytes to download.
 

	
 
/** Widgets used for the join status window. */
 
enum NetworkJoinStatusWidgets {
 
	NJSW_BACKGROUND, ///< Background
 
	NJSW_CANCELOK,   ///< Cancel/OK button
 
};
 

	
 
struct NetworkJoinStatusWindow : Window {
 
	NetworkPasswordType password_type;
 

	
 
	NetworkJoinStatusWindow(const WindowDesc *desc) : Window()
 
	{
src/network/network_internal.h
Show inline comments
 
@@ -43,25 +43,25 @@
 
 * Used to load the desync debug logs, i.e. for reproducing a desync.
 
 * There's basically no need to ever enable this, unless you really know what
 
 * you are doing, i.e. debugging a desync.
 
 */
 
#ifdef DEBUG_DUMP_COMMANDS
 
extern bool _ddc_fastforward;
 
#else
 
#define _ddc_fastforward (false)
 
#endif /* DEBUG_DUMP_COMMANDS */
 

	
 
typedef class ServerNetworkGameSocketHandler NetworkClientSocket;
 

	
 

	
 
/** Status of the clients during joining. */
 
enum NetworkJoinStatus {
 
	NETWORK_JOIN_STATUS_CONNECTING,
 
	NETWORK_JOIN_STATUS_AUTHORIZING,
 
	NETWORK_JOIN_STATUS_WAITING,
 
	NETWORK_JOIN_STATUS_DOWNLOADING,
 
	NETWORK_JOIN_STATUS_PROCESSING,
 
	NETWORK_JOIN_STATUS_REGISTERING,
 

	
 
	NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO,
 
	NETWORK_JOIN_STATUS_END,
 
};
 

	
src/network/network_server.cpp
Show inline comments
 
@@ -34,26 +34,28 @@
 
#include "../core/random_func.hpp"
 
#include "../rev.h"
 

	
 

	
 
/* This file handles all the server-commands */
 

	
 
DECLARE_POSTFIX_INCREMENT(ClientID)
 
/** The identifier counter for new clients (is never decreased) */
 
static ClientID _network_client_id = CLIENT_ID_FIRST;
 

	
 
/** Make very sure the preconditions given in network_type.h are actually followed */
 
assert_compile(MAX_CLIENT_SLOTS > MAX_CLIENTS);
 
/** Yes... */
 
assert_compile(NetworkClientSocketPool::MAX_SIZE == MAX_CLIENT_SLOTS);
 

	
 
/** The pool with clients. */
 
NetworkClientSocketPool _networkclientsocket_pool("NetworkClientSocket");
 
INSTANTIATE_POOL_METHODS(NetworkClientSocket)
 

	
 
/** Instantiate the listen sockets. */
 
template SocketList TCPListenHandler<ServerNetworkGameSocketHandler, PACKET_SERVER_FULL, PACKET_SERVER_BANNED>::sockets;
 

	
 
/** Writing a savegame directly to a number of packets. */
 
struct PacketWriter : SaveFilter {
 
	ServerNetworkGameSocketHandler *cs; ///< Socket we are associated with.
 
	Packet *current;                    ///< The packet we're currently writing to.
 
	size_t total_size;                  ///< Total size of the compressed savegame.
 

	
 
@@ -287,37 +289,42 @@ NetworkRecvStatus ServerNetworkGameSocke
 
			}
 
		}
 
	}
 
}
 

	
 
static void NetworkHandleCommandQueue(NetworkClientSocket *cs);
 

	
 
/***********
 
 * Sending functions
 
 *   DEF_SERVER_SEND_COMMAND has parameter: NetworkClientSocket *cs
 
 ************/
 

	
 
/**
 
 * Send the client information about a client.
 
 * @param ci The client to send information about.
 
 */
 
NetworkRecvStatus ServerNetworkGameSocketHandler::SendClientInfo(NetworkClientInfo *ci)
 
{
 
	if (ci->client_id != INVALID_CLIENT_ID) {
 
		Packet *p = new Packet(PACKET_SERVER_CLIENT_INFO);
 
		p->Send_uint32(ci->client_id);
 
		p->Send_uint8 (ci->client_playas);
 
		p->Send_string(ci->client_name);
 

	
 
		this->SendPacket(p);
 
	}
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/** Send the client information about the companies. */
 
NetworkRecvStatus ServerNetworkGameSocketHandler::SendCompanyInfo()
 
{
 
	/* Fetch the latest version of the stats */
 
	NetworkCompanyStats company_stats[MAX_COMPANIES];
 
	NetworkPopulateCompanyStats(company_stats);
 

	
 
	/* Make a list of all clients per company */
 
	char clients[MAX_COMPANIES][NETWORK_CLIENTS_LENGTH];
 
	NetworkClientSocket *csi;
 
	memset(clients, 0, sizeof(clients));
 

	
 
	/* Add the local player (if not dedicated) */
 
@@ -362,24 +369,28 @@ NetworkRecvStatus ServerNetworkGameSocke
 
		this->SendPacket(p);
 
	}
 

	
 
	p = new Packet(PACKET_SERVER_COMPANY_INFO);
 

	
 
	p->Send_uint8 (NETWORK_COMPANY_INFO_VERSION);
 
	p->Send_bool  (false);
 

	
 
	this->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Send an error to the client, and close its connection.
 
 * @param error The error to disconnect for.
 
 */
 
NetworkRecvStatus ServerNetworkGameSocketHandler::SendError(NetworkErrorCode error)
 
{
 
	char str[100];
 
	Packet *p = new Packet(PACKET_SERVER_ERROR);
 

	
 
	p->Send_uint8(error);
 
	this->SendPacket(p);
 

	
 
	StringID strid = GetNetworkErrorMsg(error);
 
	GetString(str, strid, lastof(str));
 

	
 
	/* Only send when the current client was in game */
 
@@ -404,69 +415,73 @@ NetworkRecvStatus ServerNetworkGameSocke
 
			}
 
		}
 

	
 
		NetworkAdminClientError(this->client_id, error);
 
	} else {
 
		DEBUG(net, 1, "Client %d made an error and has been disconnected. Reason: '%s'", this->client_id, str);
 
	}
 

	
 
	/* The client made a mistake, so drop his connection now! */
 
	return this->CloseConnection(NETWORK_RECV_STATUS_SERVER_ERROR);
 
}
 

	
 
/** Send the check for the NewGRFs. */
 
NetworkRecvStatus ServerNetworkGameSocketHandler::SendNewGRFCheck()
 
{
 
	Packet *p = new Packet(PACKET_SERVER_CHECK_NEWGRFS);
 
	const GRFConfig *c;
 
	uint grf_count = 0;
 

	
 
	for (c = _grfconfig; c != NULL; c = c->next) {
 
		if (!HasBit(c->flags, GCF_STATIC)) grf_count++;
 
	}
 

	
 
	p->Send_uint8 (grf_count);
 
	for (c = _grfconfig; c != NULL; c = c->next) {
 
		if (!HasBit(c->flags, GCF_STATIC)) this->SendGRFIdentifier(p, &c->ident);
 
	}
 

	
 
	this->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/** Request the game password. */
 
NetworkRecvStatus ServerNetworkGameSocketHandler::SendNeedGamePassword()
 
{
 
	/* Invalid packet when status is STATUS_AUTH_GAME or higher */
 
	if (this->status >= STATUS_AUTH_GAME) return this->CloseConnection(NETWORK_RECV_STATUS_MALFORMED_PACKET);
 

	
 
	this->status = STATUS_AUTH_GAME;
 

	
 
	Packet *p = new Packet(PACKET_SERVER_NEED_GAME_PASSWORD);
 
	this->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/** Request the company password. */
 
NetworkRecvStatus ServerNetworkGameSocketHandler::SendNeedCompanyPassword()
 
{
 
	/* Invalid packet when status is STATUS_AUTH_COMPANY or higher */
 
	if (this->status >= STATUS_AUTH_COMPANY) return this->CloseConnection(NETWORK_RECV_STATUS_MALFORMED_PACKET);
 

	
 
	this->status = STATUS_AUTH_COMPANY;
 

	
 
	Packet *p = new Packet(PACKET_SERVER_NEED_COMPANY_PASSWORD);
 
	p->Send_uint32(_settings_game.game_creation.generation_seed);
 
	p->Send_string(_settings_client.network.network_id);
 
	this->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/** Send the client a welcome message with some basic information. */
 
NetworkRecvStatus ServerNetworkGameSocketHandler::SendWelcome()
 
{
 
	Packet *p;
 
	NetworkClientSocket *new_cs;
 

	
 
	/* Invalid packet when status is AUTH or higher */
 
	if (this->status >= STATUS_AUTHORIZED) return this->CloseConnection(NETWORK_RECV_STATUS_MALFORMED_PACKET);
 

	
 
	this->status = STATUS_AUTHORIZED;
 
	_network_game_info.clients_on++;
 

	
 
	p = new Packet(PACKET_SERVER_WELCOME);
 
@@ -476,43 +491,44 @@ NetworkRecvStatus ServerNetworkGameSocke
 
	this->SendPacket(p);
 

	
 
	/* Transmit info about all the active clients */
 
	FOR_ALL_CLIENT_SOCKETS(new_cs) {
 
		if (new_cs != this && new_cs->status > STATUS_AUTHORIZED) {
 
			this->SendClientInfo(new_cs->GetInfo());
 
		}
 
	}
 
	/* Also send the info of the server */
 
	return this->SendClientInfo(NetworkClientInfo::GetByClientID(CLIENT_ID_SERVER));
 
}
 

	
 
/** Tell the client that its put in a waiting queue. */
 
NetworkRecvStatus ServerNetworkGameSocketHandler::SendWait()
 
{
 
	int waiting = 0;
 
	NetworkClientSocket *new_cs;
 
	Packet *p;
 

	
 
	/* Count how many clients are waiting in the queue, in front of you! */
 
	FOR_ALL_CLIENT_SOCKETS(new_cs) {
 
		if (new_cs->status != STATUS_MAP_WAIT) continue;
 
		if (new_cs->GetInfo()->join_date < this->GetInfo()->join_date || (new_cs->GetInfo()->join_date == this->GetInfo()->join_date && new_cs->client_id < this->client_id)) waiting++;
 
	}
 

	
 
	p = new Packet(PACKET_SERVER_WAIT);
 
	p->Send_uint8(waiting);
 
	this->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/* This sends the map to the client */
 
/** This sends the map to the client */
 
NetworkRecvStatus ServerNetworkGameSocketHandler::SendMap()
 
{
 
	static uint sent_packets; // How many packets we did send succecfully last time
 

	
 
	if (this->status < STATUS_AUTHORIZED) {
 
		/* Illegal call, return error and ignore the packet */
 
		return this->SendError(NETWORK_ERROR_NOT_AUTHORIZED);
 
	}
 

	
 
	if (this->status == STATUS_AUTHORIZED) {
 
		this->savegame = new PacketWriter(this);
 

	
 
@@ -600,160 +616,200 @@ NetworkRecvStatus ServerNetworkGameSocke
 
				/* Only a part is sent; leave the transmission state. */
 
				break;
 

	
 
			case SPS_NONE_SENT:
 
				/* Not everything is sent, decrease the sent_packets */
 
				if (sent_packets > 1) sent_packets /= 2;
 
				break;
 
		}
 
	}
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Tell that a client joined.
 
 * @param client_id The client that joined.
 
 */
 
NetworkRecvStatus ServerNetworkGameSocketHandler::SendJoin(ClientID client_id)
 
{
 
	Packet *p = new Packet(PACKET_SERVER_JOIN);
 

	
 
	p->Send_uint32(client_id);
 

	
 
	this->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 

	
 
/** Tell the client that they may run to a particular frame. */
 
NetworkRecvStatus ServerNetworkGameSocketHandler::SendFrame()
 
{
 
	Packet *p = new Packet(PACKET_SERVER_FRAME);
 
	p->Send_uint32(_frame_counter);
 
	p->Send_uint32(_frame_counter_max);
 
#ifdef ENABLE_NETWORK_SYNC_EVERY_FRAME
 
	p->Send_uint32(_sync_seed_1);
 
#ifdef NETWORK_SEND_DOUBLE_SEED
 
	p->Send_uint32(_sync_seed_2);
 
#endif
 
#endif
 

	
 
	/* If token equals 0, we need to make a new token and send that. */
 
	if (this->last_token == 0) {
 
		this->last_token = InteractiveRandomRange(UINT8_MAX - 1) + 1;
 
		p->Send_uint8(this->last_token);
 
	}
 

	
 
	this->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/** Request the client to sync. */
 
NetworkRecvStatus ServerNetworkGameSocketHandler::SendSync()
 
{
 
	Packet *p = new Packet(PACKET_SERVER_SYNC);
 
	p->Send_uint32(_frame_counter);
 
	p->Send_uint32(_sync_seed_1);
 

	
 
#ifdef NETWORK_SEND_DOUBLE_SEED
 
	p->Send_uint32(_sync_seed_2);
 
#endif
 
	this->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Send a command to the client to execute.
 
 * @param cp The command to send.
 
 */
 
NetworkRecvStatus ServerNetworkGameSocketHandler::SendCommand(const CommandPacket *cp)
 
{
 
	Packet *p = new Packet(PACKET_SERVER_COMMAND);
 

	
 
	this->NetworkGameSocketHandler::SendCommand(p, cp);
 
	p->Send_uint32(cp->frame);
 
	p->Send_bool  (cp->my_cmd);
 

	
 
	this->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Send a chat message.
 
 * @param action The action associated with the message.
 
 * @param client_id The origin of the chat message.
 
 * @param self_send Whether we did send the message.
 
 * @param msg The actual message.
 
 * @param data Arbitrary extra data.
 
 */
 
NetworkRecvStatus ServerNetworkGameSocketHandler::SendChat(NetworkAction action, ClientID client_id, bool self_send, const char *msg, int64 data)
 
{
 
	Packet *p = new Packet(PACKET_SERVER_CHAT);
 

	
 
	p->Send_uint8 (action);
 
	p->Send_uint32(client_id);
 
	p->Send_bool  (self_send);
 
	p->Send_string(msg);
 
	p->Send_uint64(data);
 

	
 
	this->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Tell the client another client quit with an error.
 
 * @param client_id The client that quit.
 
 * @param errorno The reason the client quit.
 
 */
 
NetworkRecvStatus ServerNetworkGameSocketHandler::SendErrorQuit(ClientID client_id, NetworkErrorCode errorno)
 
{
 
	Packet *p = new Packet(PACKET_SERVER_ERROR_QUIT);
 

	
 
	p->Send_uint32(client_id);
 
	p->Send_uint8 (errorno);
 

	
 
	this->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Tell the client another client quit.
 
 * @param client_id The client that quit.
 
 */
 
NetworkRecvStatus ServerNetworkGameSocketHandler::SendQuit(ClientID client_id)
 
{
 
	Packet *p = new Packet(PACKET_SERVER_QUIT);
 

	
 
	p->Send_uint32(client_id);
 

	
 
	this->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/** Tell the client we're shutting down. */
 
NetworkRecvStatus ServerNetworkGameSocketHandler::SendShutdown()
 
{
 
	Packet *p = new Packet(PACKET_SERVER_SHUTDOWN);
 
	this->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/** Tell the client we're starting a new game. */
 
NetworkRecvStatus ServerNetworkGameSocketHandler::SendNewGame()
 
{
 
	Packet *p = new Packet(PACKET_SERVER_NEWGAME);
 
	this->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Send the result of a console action.
 
 * @param colour The colour of the result.
 
 * @param command The command that was executed.
 
 */
 
NetworkRecvStatus ServerNetworkGameSocketHandler::SendRConResult(uint16 colour, const char *command)
 
{
 
	Packet *p = new Packet(PACKET_SERVER_RCON);
 

	
 
	p->Send_uint16(colour);
 
	p->Send_string(command);
 
	this->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Tell that a client moved to another company.
 
 * @param client_id The client that moved.
 
 * @param company_id The company the client moved to.
 
 */
 
NetworkRecvStatus ServerNetworkGameSocketHandler::SendMove(ClientID client_id, CompanyID company_id)
 
{
 
	Packet *p = new Packet(PACKET_SERVER_MOVE);
 

	
 
	p->Send_uint32(client_id);
 
	p->Send_uint8(company_id);
 
	this->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/** Send an update about the company password states. */
 
NetworkRecvStatus ServerNetworkGameSocketHandler::SendCompanyUpdate()
 
{
 
	Packet *p = new Packet(PACKET_SERVER_COMPANY_UPDATE);
 

	
 
	p->Send_uint16(_network_company_passworded);
 
	this->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/** Send an update about the max company/spectator counts. */
 
NetworkRecvStatus ServerNetworkGameSocketHandler::SendConfigUpdate()
 
{
 
	Packet *p = new Packet(PACKET_SERVER_CONFIG_UPDATE);
 

	
 
	p->Send_uint8(_settings_client.network.max_companies);
 
	p->Send_uint8(_settings_client.network.max_spectators);
 
	this->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/***********
 
 * Receiving functions
 
@@ -1159,25 +1215,34 @@ NetworkRecvStatus ServerNetworkGameSocke
 
		/* Request a new token. */
 
		this->last_token = 0;
 
	}
 

	
 
	/* The client received the frame, make note of it */
 
	this->last_frame = frame;
 
	/* With those 2 values we can calculate the lag realtime */
 
	this->last_frame_server = _frame_counter;
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 

	
 

	
 
/**
 
 * Send an actual chat message.
 
 * @param action The action that's performed.
 
 * @param desttype The type of destination.
 
 * @param dest The actual destination index.
 
 * @param msg The actual message.
 
 * @param from_id The origin of the message.
 
 * @param data Arbitrary data.
 
 * @param from_admin Whether the origin is an admin or not.
 
 */
 
void NetworkServerSendChat(NetworkAction action, DestType desttype, int dest, const char *msg, ClientID from_id, int64 data, bool from_admin)
 
{
 
	NetworkClientSocket *cs;
 
	const NetworkClientInfo *ci, *ci_own, *ci_to;
 

	
 
	switch (desttype) {
 
		case DESTTYPE_CLIENT:
 
			/* Are we sending to the server? */
 
			if ((ClientID)dest == CLIENT_ID_SERVER) {
 
				ci = NetworkClientInfo::GetByClientID(from_id);
 
				/* Display the text locally, and that is it */
 
				if (ci != NULL) {
 
@@ -1490,56 +1555,60 @@ void NetworkPopulateCompanyStats(Network
 
		if (Company::IsValidID(s->owner)) {
 
			NetworkCompanyStats *npi = &stats[s->owner];
 

	
 
			if (s->facilities & FACIL_TRAIN)      npi->num_station[NETWORK_VEH_TRAIN]++;
 
			if (s->facilities & FACIL_TRUCK_STOP) npi->num_station[NETWORK_VEH_LORRY]++;
 
			if (s->facilities & FACIL_BUS_STOP)   npi->num_station[NETWORK_VEH_BUS]++;
 
			if (s->facilities & FACIL_AIRPORT)    npi->num_station[NETWORK_VEH_PLANE]++;
 
			if (s->facilities & FACIL_DOCK)       npi->num_station[NETWORK_VEH_SHIP]++;
 
		}
 
	}
 
}
 

	
 
/* Send a packet to all clients with updated info about this client_id */
 
/**
 
 * Send updated client info of a particular client.
 
 * @param client_id The client to send it for.
 
 */
 
void NetworkUpdateClientInfo(ClientID client_id)
 
{
 
	NetworkClientSocket *cs;
 
	NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(client_id);
 

	
 
	if (ci == NULL) return;
 

	
 
	DEBUG(desync, 1, "client: %08x; %02x; %02x; %04x", _date, _date_fract, (int)ci->client_playas, client_id);
 

	
 
	FOR_ALL_CLIENT_SOCKETS(cs) {
 
		cs->SendClientInfo(ci);
 
	}
 

	
 
	NetworkAdminClientUpdate(ci);
 
}
 

	
 
/* Check if we want to restart the map */
 
/** Check if we want to restart the map */
 
static void NetworkCheckRestartMap()
 
{
 
	if (_settings_client.network.restart_game_year != 0 && _cur_year >= _settings_client.network.restart_game_year) {
 
		DEBUG(net, 0, "Auto-restarting map. Year %d reached", _cur_year);
 

	
 
		StartNewGameWithoutGUI(GENERATE_NEW_SEED);
 
	}
 
}
 

	
 
/* Check if the server has autoclean_companies activated
 
    Two things happen:
 
      1) If a company is not protected, it is closed after 1 year (for example)
 
      2) If a company is protected, protection is disabled after 3 years (for example)
 
           (and item 1. happens a year later) */
 
/** Check if the server has autoclean_companies activated
 
 * Two things happen:
 
 *     1) If a company is not protected, it is closed after 1 year (for example)
 
 *     2) If a company is protected, protection is disabled after 3 years (for example)
 
 *          (and item 1. happens a year later)
 
 */
 
static void NetworkAutoCleanCompanies()
 
{
 
	const NetworkClientInfo *ci;
 
	const Company *c;
 
	bool clients_in_company[MAX_COMPANIES];
 
	int vehicles_in_company[MAX_COMPANIES];
 

	
 
	if (!_settings_client.network.autoclean_companies) return;
 

	
 
	memset(clients_in_company, 0, sizeof(clients_in_company));
 

	
 
	/* Detect the active companies */
 
@@ -1591,26 +1660,29 @@ static void NetworkAutoCleanCompanies()
 
				/* Shut the company down */
 
				DoCommandP(0, 2 | c->index << 16, 0, CMD_COMPANY_CTRL);
 
				NetworkAdminCompanyRemove(c->index, ADMIN_CRR_AUTOCLEAN);
 
				IConsolePrintF(CC_DEFAULT, "Auto-cleaned company #%d with no vehicles", c->index + 1);
 
			}
 
		} else {
 
			/* It is not empty, reset the date */
 
			_network_company_states[c->index].months_empty = 0;
 
		}
 
	}
 
}
 

	
 
/* This function changes new_name to a name that is unique (by adding #1 ...)
 
 *  and it returns true if that succeeded. */
 
/**
 
 * Check whether a name is unique, and otherwise try to make it unique.
 
 * @param new_name The name to check/modify.
 
 * @return True if an unique name was achieved.
 
 */
 
bool NetworkFindName(char new_name[NETWORK_CLIENT_NAME_LENGTH])
 
{
 
	bool found_name = false;
 
	uint number = 0;
 
	char original_name[NETWORK_CLIENT_NAME_LENGTH];
 

	
 
	/* We use NETWORK_CLIENT_NAME_LENGTH in here, because new_name is really a pointer */
 
	ttd_strlcpy(original_name, new_name, NETWORK_CLIENT_NAME_LENGTH);
 

	
 
	while (!found_name) {
 
		const NetworkClientInfo *ci;
 

	
 
@@ -1674,35 +1746,41 @@ bool NetworkServerChangeClientName(Clien
 
void NetworkServerSetCompanyPassword(CompanyID company_id, const char *password, bool already_hashed)
 
{
 
	if (!Company::IsValidHumanID(company_id)) return;
 

	
 
	if (!already_hashed) {
 
		password = GenerateCompanyPasswordHash(password, _settings_client.network.network_id, _settings_game.game_creation.generation_seed);
 
	}
 

	
 
	strecpy(_network_company_states[company_id].password, password, lastof(_network_company_states[company_id].password));
 
	NetworkServerUpdateCompanyPassworded(company_id, !StrEmpty(_network_company_states[company_id].password));
 
}
 

	
 
/* Handle the local command-queue */
 
/**
 
 * Handle the command-queue of a socket.
 
 * @param cs The socket to handle the queue for.
 
 */
 
static void NetworkHandleCommandQueue(NetworkClientSocket *cs)
 
{
 
	CommandPacket *cp;
 
	while ((cp = cs->outgoing_queue.Pop()) != NULL) {
 
		cs->SendCommand(cp);
 
		free(cp);
 
	}
 
}
 

	
 
/* This is called every tick if this is a _network_server */
 
/**
 
 * This is called every tick if this is a _network_server
 
 * @param send_frame Whether to send the frame to the clients.
 
 */
 
void NetworkServer_Tick(bool send_frame)
 
{
 
	NetworkClientSocket *cs;
 
#ifndef ENABLE_NETWORK_SYNC_EVERY_FRAME
 
	bool send_sync = false;
 
#endif
 

	
 
#ifndef ENABLE_NETWORK_SYNC_EVERY_FRAME
 
	if (_frame_counter >= _last_sync_frame + _settings_client.network.sync_freq) {
 
		_last_sync_frame = _frame_counter;
 
		send_sync = true;
 
	}
 
@@ -1808,24 +1886,25 @@ void NetworkServerDailyLoop()
 
	if ((_date % 7) == 3) NetworkAdminUpdate(ADMIN_FREQUENCY_WEEKLY);
 
}
 

	
 
/**
 
 * Get the IP address/hostname of the connected client.
 
 * @return The IP address.
 
 */
 
const char *ServerNetworkGameSocketHandler::GetClientIP()
 
{
 
	return this->client_address.GetHostname();
 
}
 

	
 
/** Show the status message of all clients on the console. */
 
void NetworkServerShowStatusToConsole()
 
{
 
	static const char * const stat_str[] = {
 
		"inactive",
 
		"checking NewGRFs",
 
		"authorizing (server password)",
 
		"authorizing (company password)",
 
		"authorized",
 
		"waiting",
 
		"loading map",
 
		"map done",
 
		"ready",
 
@@ -1851,24 +1930,29 @@ void NetworkServerShowStatusToConsole()
 
/**
 
 * Send Config Update
 
 */
 
void NetworkServerSendConfigUpdate()
 
{
 
	NetworkClientSocket *cs;
 

	
 
	FOR_ALL_CLIENT_SOCKETS(cs) {
 
		if (cs->status >= NetworkClientSocket::STATUS_PRE_ACTIVE) cs->SendConfigUpdate();
 
	}
 
}
 

	
 
/**
 
 * Tell that a particular company is (not) passworded.
 
 * @param company_id The company that got/removed the password.
 
 * @param passworded Whether the password was received or removed.
 
 */
 
void NetworkServerUpdateCompanyPassworded(CompanyID company_id, bool passworded)
 
{
 
	if (NetworkCompanyIsPassworded(company_id) == passworded) return;
 

	
 
	SB(_network_company_passworded, company_id, 1, !!passworded);
 
	SetWindowClassesDirty(WC_COMPANY);
 

	
 
	NetworkClientSocket *cs;
 
	FOR_ALL_CLIENT_SOCKETS(cs) {
 
		if (cs->status >= NetworkClientSocket::STATUS_PRE_ACTIVE) cs->SendCompanyUpdate();
 
	}
 

	
 
@@ -1900,65 +1984,85 @@ void NetworkServerDoMove(ClientID client
 
		/* When the company isn't authorized we can't move them yet. */
 
		if (cs->status < NetworkClientSocket::STATUS_AUTHORIZED) return;
 
		cs->SendMove(client_id, company_id);
 
	}
 

	
 
	/* announce the client's move */
 
	NetworkUpdateClientInfo(client_id);
 

	
 
	NetworkAction action = (company_id == COMPANY_SPECTATOR) ? NETWORK_ACTION_COMPANY_SPECTATOR : NETWORK_ACTION_COMPANY_JOIN;
 
	NetworkServerSendChat(action, DESTTYPE_BROADCAST, 0, "", client_id, company_id + 1);
 
}
 

	
 
/**
 
 * Send an rcon reply to the client.
 
 * @param client_id The identifier of the client.
 
 * @param colour_code The colour of the text.
 
 * @param string The actual reply.
 
 */
 
void NetworkServerSendRcon(ClientID client_id, TextColour colour_code, const char *string)
 
{
 
	NetworkClientSocket::GetByClientID(client_id)->SendRConResult(colour_code, string);
 
}
 

	
 
static void NetworkServerSendError(ClientID client_id, NetworkErrorCode error)
 
{
 
	NetworkClientSocket::GetByClientID(client_id)->SendError(error);
 
}
 

	
 
/**
 
 * Kick a single client.
 
 * @param client_id The client to kick.
 
 */
 
void NetworkServerKickClient(ClientID client_id)
 
{
 
	if (client_id == CLIENT_ID_SERVER) return;
 
	NetworkServerSendError(client_id, NETWORK_ERROR_KICKED);
 
	NetworkClientSocket::GetByClientID(client_id)->SendError(NETWORK_ERROR_KICKED);
 
}
 

	
 
/**
 
 * Ban, or kick, everyone joined from the given client's IP.
 
 * @param client_id The client to check for.
 
 * @param ban Whether to ban or kick.
 
 */
 
uint NetworkServerKickOrBanIP(ClientID client_id, bool ban)
 
{
 
	return NetworkServerKickOrBanIP(NetworkClientSocket::GetByClientID(client_id)->GetClientIP(), ban);
 
}
 

	
 
/**
 
 * Kick or ban someone based on an IP address.
 
 * @param ip The IP address/range to ban/kick.
 
 * @param ban Whether to ban or just kick.
 
 */
 
uint NetworkServerKickOrBanIP(const char *ip, bool ban)
 
{
 
	/* Add address to ban-list */
 
	if (ban) *_network_ban_list.Append() = strdup(ip);
 

	
 
	uint n = 0;
 

	
 
	/* There can be multiple clients with the same IP, kick them all */
 
	NetworkClientSocket *cs;
 
	FOR_ALL_CLIENT_SOCKETS(cs) {
 
		if (cs->client_id == CLIENT_ID_SERVER) continue;
 
		if (cs->client_address.IsInNetmask(const_cast<char *>(ip))) {
 
			NetworkServerKickClient(cs->client_id);
 
			n++;
 
		}
 
	}
 

	
 
	return n;
 
}
 

	
 
/**
 
 * Check whether a particular company has clients.
 
 * @param company The company to check.
 
 * @return True if at least one client is joined to the company.
 
 */
 
bool NetworkCompanyHasClients(CompanyID company)
 
{
 
	const NetworkClientInfo *ci;
 
	FOR_ALL_CLIENT_INFOS(ci) {
 
		if (ci->client_playas == company) return true;
 
	}
 
	return false;
 
}
 

	
 

	
 
/**
 
 * Get the name of the client, if the user did not send it yet, Client #<no> is used.
src/network/network_server.h
Show inline comments
 
@@ -10,25 +10,27 @@
 
/** @file network_server.h Server part of the network protocol. */
 

	
 
#ifndef NETWORK_SERVER_H
 
#define NETWORK_SERVER_H
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "network_internal.h"
 
#include "core/tcp_listen.h"
 
#include "../thread/thread.h"
 

	
 
class ServerNetworkGameSocketHandler;
 
/** Make the code look slightliy nicer/simpler. */
 
typedef ServerNetworkGameSocketHandler NetworkClientSocket;
 
/** Pool with all client sockets. */
 
typedef Pool<NetworkClientSocket, ClientIndex, 8, MAX_CLIENT_SLOTS, PT_NCLIENT> NetworkClientSocketPool;
 
extern NetworkClientSocketPool _networkclientsocket_pool;
 

	
 
/** Class for handling the server side of the game connection. */
 
class ServerNetworkGameSocketHandler : public NetworkClientSocketPool::PoolItem<&_networkclientsocket_pool>, public NetworkGameSocketHandler, public TCPListenHandler<ServerNetworkGameSocketHandler, PACKET_SERVER_FULL, PACKET_SERVER_BANNED> {
 
protected:
 
	virtual NetworkRecvStatus Receive_CLIENT_JOIN(Packet *p);
 
	virtual NetworkRecvStatus Receive_CLIENT_COMPANY_INFO(Packet *p);
 
	virtual NetworkRecvStatus Receive_CLIENT_GAME_PASSWORD(Packet *p);
 
	virtual NetworkRecvStatus Receive_CLIENT_COMPANY_PASSWORD(Packet *p);
 
	virtual NetworkRecvStatus Receive_CLIENT_GETMAP(Packet *p);
 
	virtual NetworkRecvStatus Receive_CLIENT_MAP_OK(Packet *p);
 
@@ -116,24 +118,34 @@ public:
 
	{
 
		return "server";
 
	}
 

	
 
	const char *GetClientIP();
 

	
 
	static ServerNetworkGameSocketHandler *GetByClientID(ClientID client_id);
 
};
 

	
 
void NetworkServer_Tick(bool send_frame);
 
void NetworkServerSetCompanyPassword(CompanyID company_id, const char *password, bool already_hashed = true);
 

	
 
/**
 
 * Iterate over all the sockets from a given starting point.
 
 * @param var The variable to iterate with.
 
 * @param start The start of the iteration.
 
 */
 
#define FOR_ALL_CLIENT_SOCKETS_FROM(var, start) FOR_ALL_ITEMS_FROM(NetworkClientSocket, clientsocket_index, var, start)
 

	
 
/**
 
 * Iterate over all the sockets.
 
 * @param var The variable to iterate with.
 
 */
 
#define FOR_ALL_CLIENT_SOCKETS(var) FOR_ALL_CLIENT_SOCKETS_FROM(var, 0)
 

	
 
#else /* ENABLE_NETWORK */
 
/* Network function stubs when networking is disabled */
 

	
 
static inline void NetworkServerMonthlyLoop() {}
 
static inline void NetworkServerYearlyLoop() {}
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 
#endif /* NETWORK_SERVER_H */
0 comments (0 inline, 0 general)