Changeset - r14988:e84b1f39bc47
[Not reviewed]
master
0 3 0
smatz - 14 years ago 2010-04-11 17:32:14
smatz@openttd.org
(svn r19610) -Codechange: rename STATUS_AUTH to STATUS_AUTHORIZED
3 files changed with 26 insertions and 26 deletions:
0 comments (0 inline, 0 general)
src/network/core/tcp_game.h
Show inline comments
 
@@ -31,96 +31,96 @@ enum {
 
	PACKET_SERVER_FULL,
 
	PACKET_SERVER_BANNED,
 
	PACKET_CLIENT_JOIN,
 
	PACKET_SERVER_ERROR,
 
	PACKET_CLIENT_COMPANY_INFO,
 
	PACKET_SERVER_COMPANY_INFO,
 
	PACKET_SERVER_CLIENT_INFO,
 
	PACKET_SERVER_NEED_GAME_PASSWORD,
 
	PACKET_SERVER_NEED_COMPANY_PASSWORD,
 
	PACKET_CLIENT_GAME_PASSWORD,
 
	PACKET_CLIENT_COMPANY_PASSWORD,
 
	PACKET_SERVER_WELCOME,
 
	PACKET_CLIENT_GETMAP,
 
	PACKET_SERVER_WAIT,
 
	PACKET_SERVER_MAP,
 
	PACKET_CLIENT_MAP_OK,
 
	PACKET_SERVER_JOIN,
 
	PACKET_SERVER_FRAME,
 
	PACKET_SERVER_SYNC,
 
	PACKET_CLIENT_ACK,
 
	PACKET_CLIENT_COMMAND,
 
	PACKET_SERVER_COMMAND,
 
	PACKET_CLIENT_CHAT,
 
	PACKET_SERVER_CHAT,
 
	PACKET_CLIENT_SET_PASSWORD,
 
	PACKET_CLIENT_SET_NAME,
 
	PACKET_CLIENT_QUIT,
 
	PACKET_CLIENT_ERROR,
 
	PACKET_SERVER_QUIT,
 
	PACKET_SERVER_ERROR_QUIT,
 
	PACKET_SERVER_SHUTDOWN,
 
	PACKET_SERVER_NEWGAME,
 
	PACKET_SERVER_RCON,
 
	PACKET_CLIENT_RCON,
 
	PACKET_SERVER_CHECK_NEWGRFS,
 
	PACKET_CLIENT_NEWGRFS_CHECKED,
 
	PACKET_SERVER_MOVE,
 
	PACKET_CLIENT_MOVE,
 
	PACKET_SERVER_COMPANY_UPDATE,
 
	PACKET_SERVER_CONFIG_UPDATE,
 
	PACKET_END                   ///< Must ALWAYS be on the end of this list!! (period)
 
};
 

	
 
/** Packet that wraps a command */
 
struct CommandPacket;
 

	
 
/** Status of a client */
 
enum ClientStatus {
 
	STATUS_INACTIVE,   ///< The client is not connected nor active
 
	STATUS_AUTH_GAME,  ///< The client is authorizing with game (server) password
 
	STATUS_INACTIVE,     ///< The client is not connected nor active
 
	STATUS_AUTH_GAME,    ///< The client is authorizing with game (server) password
 
	STATUS_AUTH_COMPANY, ///< The client is authorizing with company password
 
	STATUS_AUTH,       ///< The client is authorized
 
	STATUS_MAP_WAIT,   ///< The client is waiting as someone else is downloading the map
 
	STATUS_MAP,        ///< The client is downloading the map
 
	STATUS_DONE_MAP,   ///< The client has downloaded the map
 
	STATUS_PRE_ACTIVE, ///< The client is catching up the delayed frames
 
	STATUS_ACTIVE,     ///< The client is active within in the game
 
	STATUS_AUTHORIZED,   ///< The client is authorized
 
	STATUS_MAP_WAIT,     ///< The client is waiting as someone else is downloading the map
 
	STATUS_MAP,          ///< The client is downloading the map
 
	STATUS_DONE_MAP,     ///< The client has downloaded the map
 
	STATUS_PRE_ACTIVE,   ///< The client is catching up the delayed frames
 
	STATUS_ACTIVE,       ///< The client is active within in the game
 
};
 

	
 
class NetworkClientSocket;
 
typedef Pool<NetworkClientSocket, ClientIndex, 8, MAX_CLIENT_SLOTS> NetworkClientSocketPool;
 
extern NetworkClientSocketPool _networkclientsocket_pool;
 

	
 
/** Base socket handler for all TCP sockets */
 
class NetworkClientSocket : public NetworkClientSocketPool::PoolItem<&_networkclientsocket_pool>, public NetworkTCPSocketHandler {
 
/* TODO: rewrite into a proper class */
 
private:
 
	NetworkClientInfo *info;  ///< Client info related to this socket
 
public:
 
	ClientID client_id;       ///< Client identifier
 
	uint32 last_frame;        ///< Last frame we have executed
 
	uint32 last_frame_server; ///< Last frame the server has executed
 
	byte lag_test;            ///< Byte used for lag-testing the client
 

	
 
	ClientStatus status;      ///< Status of this client
 

	
 
	CommandPacket *command_queue; ///< The command-queue awaiting delivery
 

	
 
	NetworkRecvStatus CloseConnection(bool error = true);
 

	
 
	NetworkClientSocket(ClientID client_id = INVALID_CLIENT_ID);
 
	~NetworkClientSocket();
 

	
 
	inline void SetInfo(NetworkClientInfo *info) { assert(info != NULL && this->info == NULL); this->info = info; }
 
	inline NetworkClientInfo *GetInfo() const { return this->info; }
 

	
 
	const char *Recv_Command(Packet *p, CommandPacket *cp);
 
	void Send_Command(Packet *p, const CommandPacket *cp);
 
};
 

	
 
#define FOR_ALL_CLIENT_SOCKETS_FROM(var, start) FOR_ALL_ITEMS_FROM(NetworkClientSocket, clientsocket_index, var, start)
 
#define FOR_ALL_CLIENT_SOCKETS(var) FOR_ALL_CLIENT_SOCKETS_FROM(var, 0)
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 
#endif /* NETWORK_CORE_TCP_GAME_H */
src/network/network.cpp
Show inline comments
 
@@ -407,214 +407,214 @@ void NetworkHandlePauseChange(PauseMode 
 
 */
 
static void CheckPauseHelper(bool pause, PauseMode pm)
 
{
 
	if (pause == ((_pause_mode & pm) != PM_UNPAUSED)) return;
 

	
 
	DoCommandP(0, pm, pause ? 1 : 0, CMD_PAUSE);
 
}
 

	
 
/**
 
 * Counts the number of active clients connected.
 
 * It has to be in STATUS_ACTIVE and not a spectator
 
 * @return number of active clients
 
 */
 
static uint NetworkCountActiveClients()
 
{
 
	const NetworkClientSocket *cs;
 
	uint count = 0;
 

	
 
	FOR_ALL_CLIENT_SOCKETS(cs) {
 
		if (cs->status != STATUS_ACTIVE) continue;
 
		if (!Company::IsValidID(cs->GetInfo()->client_playas)) continue;
 
		count++;
 
	}
 

	
 
	return count;
 
}
 

	
 
/**
 
 * Check if the minimum number of active clients has been reached and pause or unpause the game as appropriate
 
 */
 
static void CheckMinActiveClients()
 
{
 
	if ((_pause_mode & PM_PAUSED_ERROR) != PM_UNPAUSED ||
 
			!_network_dedicated ||
 
			(_settings_client.network.min_active_clients == 0 && (_pause_mode & PM_PAUSED_ACTIVE_CLIENTS) == PM_UNPAUSED)) {
 
		return;
 
	}
 
	CheckPauseHelper(NetworkCountActiveClients() < _settings_client.network.min_active_clients, PM_PAUSED_ACTIVE_CLIENTS);
 
}
 

	
 
/**
 
 * Checks whether there is a joining client
 
 * @return true iff one client is joining (but not authorizing)
 
 */
 
static bool NetworkHasJoiningClient()
 
{
 
	const NetworkClientSocket *cs;
 
	FOR_ALL_CLIENT_SOCKETS(cs) {
 
		if (cs->status >= STATUS_AUTH && cs->status < STATUS_ACTIVE) return true;
 
		if (cs->status >= STATUS_AUTHORIZED && cs->status < STATUS_ACTIVE) return true;
 
	}
 

	
 
	return false;
 
}
 

	
 
/**
 
 * Check whether we should pause on join
 
 */
 
static void CheckPauseOnJoin()
 
{
 
	if ((_pause_mode & PM_PAUSED_ERROR) != PM_UNPAUSED ||
 
			(!_settings_client.network.pause_on_join && (_pause_mode & PM_PAUSED_JOIN) == PM_UNPAUSED)) {
 
		return;
 
	}
 
	CheckPauseHelper(NetworkHasJoiningClient(), PM_PAUSED_JOIN);
 
}
 

	
 
/** Converts a string to ip/port/company
 
 *  Format: IP:port#company
 
 *
 
 * connection_string will be re-terminated to seperate out the hostname, and company and port will
 
 * be set to the company and port strings given by the user, inside the memory area originally
 
 * occupied by connection_string. */
 
void ParseConnectionString(const char **company, const char **port, char *connection_string)
 
{
 
	bool ipv6 = (strchr(connection_string, ':') != strrchr(connection_string, ':'));
 
	char *p;
 
	for (p = connection_string; *p != '\0'; p++) {
 
		switch (*p) {
 
			case '[':
 
				ipv6 = true;
 
				break;
 

	
 
			case ']':
 
				ipv6 = false;
 
				break;
 

	
 
			case '#':
 
				*company = p + 1;
 
				*p = '\0';
 
				break;
 

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

	
 
/* Creates a new client from a socket
 
 *   Used both by the server and the client */
 
static NetworkClientSocket *NetworkAllocClient(SOCKET s)
 
{
 
	if (_network_server) {
 
		/* Can we handle a new client? */
 
		if (_network_clients_connected >= MAX_CLIENTS) return NULL;
 
		if (_network_game_info.clients_on >= _settings_client.network.max_clients) return NULL;
 

	
 
		/* Register the login */
 
		_network_clients_connected++;
 
	}
 

	
 
	NetworkClientSocket *cs = new NetworkClientSocket(INVALID_CLIENT_ID);
 
	cs->sock = s;
 
	cs->last_frame = _frame_counter;
 
	cs->last_frame_server = _frame_counter;
 

	
 
	if (_network_server) {
 
		cs->client_id = _network_client_id++;
 
		NetworkClientInfo *ci = new NetworkClientInfo(cs->client_id);
 
		cs->SetInfo(ci);
 
		ci->client_playas = COMPANY_INACTIVE_CLIENT;
 
		ci->join_date = _date;
 

	
 
		SetWindowDirty(WC_CLIENT_LIST, 0);
 
	}
 

	
 
	return cs;
 
}
 

	
 
/* Close a connection */
 
NetworkRecvStatus NetworkCloseClient(NetworkClientSocket *cs, NetworkRecvStatus status)
 
{
 
	assert(status != NETWORK_RECV_STATUS_OKAY);
 
	/*
 
	 * Sending a message just before leaving the game calls cs->Send_Packets.
 
	 * This might invoke this function, which means that when we close the
 
	 * connection after cs->Send_Packets we will close an already closed
 
	 * connection. This handles that case gracefully without having to make
 
	 * that code any more complex or more aware of the validity of the socket.
 
	 */
 
	if (cs->sock == INVALID_SOCKET) return status;
 

	
 
	if (status != NETWORK_RECV_STATUS_CONN_LOST && !cs->HasClientQuit() && _network_server && cs->status >= STATUS_AUTH) {
 
	if (status != NETWORK_RECV_STATUS_CONN_LOST && !cs->HasClientQuit() && _network_server && cs->status >= STATUS_AUTHORIZED) {
 
		/* We did not receive a leave message from this client... */
 
		char client_name[NETWORK_CLIENT_NAME_LENGTH];
 
		NetworkClientSocket *new_cs;
 

	
 
		NetworkGetClientName(client_name, sizeof(client_name), cs);
 

	
 
		NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, client_name, NULL, STR_NETWORK_ERROR_CLIENT_CONNECTION_LOST);
 

	
 
		/* Inform other clients of this... strange leaving ;) */
 
		FOR_ALL_CLIENT_SOCKETS(new_cs) {
 
			if (new_cs->status > STATUS_AUTH && cs != new_cs) {
 
			if (new_cs->status > STATUS_AUTHORIZED && cs != new_cs) {
 
				SEND_COMMAND(PACKET_SERVER_ERROR_QUIT)(new_cs, cs->client_id, NETWORK_ERROR_CONNECTION_LOST);
 
			}
 
		}
 
	}
 

	
 
	DEBUG(net, 1, "Closed client connection %d", cs->client_id);
 

	
 
	if (_network_server) {
 
		/* We just lost one client :( */
 
		if (cs->status >= STATUS_AUTH) _network_game_info.clients_on--;
 
		if (cs->status >= STATUS_AUTHORIZED) _network_game_info.clients_on--;
 
		_network_clients_connected--;
 

	
 
		SetWindowDirty(WC_CLIENT_LIST, 0);
 
	}
 

	
 
	cs->Send_Packets(true);
 

	
 
	delete cs->GetInfo();
 
	delete cs;
 

	
 
	return status;
 
}
 

	
 
/* For the server, to accept new clients */
 
static void NetworkAcceptClients(SOCKET ls)
 
{
 
	for (;;) {
 
		struct sockaddr_storage sin;
 
		memset(&sin, 0, sizeof(sin));
 
		socklen_t sin_len = sizeof(sin);
 
		SOCKET s = accept(ls, (struct sockaddr*)&sin, &sin_len);
 
		if (s == INVALID_SOCKET) return;
 

	
 
		SetNonBlocking(s); // XXX error handling?
 

	
 
		NetworkAddress address(sin, sin_len);
 
		DEBUG(net, 1, "Client connected from %s on frame %d", address.GetHostname(), _frame_counter);
 

	
 
		SetNoDelay(s); // XXX error handling?
 

	
 
		/* Check if the client is banned */
 
		bool banned = false;
 
		for (char **iter = _network_ban_list.Begin(); iter != _network_ban_list.End(); iter++) {
 
			banned = address.IsInNetmask(*iter);
 
			if (banned) {
 
				Packet p(PACKET_SERVER_BANNED);
 
				p.PrepareToSend();
 

	
 
				DEBUG(net, 1, "Banned ip tried to join (%s), refused", *iter);
 

	
 
				send(s, (const char*)p.buffer, p.size, 0);
 
				closesocket(s);
 
				break;
 
			}
 
		}
 
		/* If this client is banned, continue with next client */
 
		if (banned) continue;
 

	
src/network/network_server.cpp
Show inline comments
 
@@ -107,108 +107,108 @@ DEF_SERVER_SEND_COMMAND(PACKET_SERVER_CO
 

	
 
	Company *company;
 
	Packet *p;
 

	
 
	FOR_ALL_COMPANIES(company) {
 
		p = new Packet(PACKET_SERVER_COMPANY_INFO);
 

	
 
		p->Send_uint8 (NETWORK_COMPANY_INFO_VERSION);
 
		p->Send_bool  (true);
 
		cs->Send_CompanyInformation(p, company, &company_stats[company->index]);
 

	
 
		if (StrEmpty(clients[company->index])) {
 
			p->Send_string("<none>");
 
		} else {
 
			p->Send_string(clients[company->index]);
 
		}
 

	
 
		cs->Send_Packet(p);
 
	}
 

	
 
	p = new Packet(PACKET_SERVER_COMPANY_INFO);
 

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

	
 
	cs->Send_Packet(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_ERROR)(NetworkClientSocket *cs, NetworkErrorCode error)
 
{
 
	/*
 
	 * Packet: SERVER_ERROR
 
	 * Function: The client made an error
 
	 * Data:
 
	 *    uint8:  ErrorID (see network_data.h, NetworkErrorCode)
 
	 */
 

	
 
	char str[100];
 
	Packet *p = new Packet(PACKET_SERVER_ERROR);
 

	
 
	p->Send_uint8(error);
 
	cs->Send_Packet(p);
 

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

	
 
	/* Only send when the current client was in game */
 
	if (cs->status > STATUS_AUTH) {
 
	if (cs->status > STATUS_AUTHORIZED) {
 
		NetworkClientSocket *new_cs;
 
		char client_name[NETWORK_CLIENT_NAME_LENGTH];
 

	
 
		NetworkGetClientName(client_name, sizeof(client_name), cs);
 

	
 
		DEBUG(net, 1, "'%s' made an error and has been disconnected. Reason: '%s'", client_name, str);
 

	
 
		NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, client_name, NULL, strid);
 

	
 
		FOR_ALL_CLIENT_SOCKETS(new_cs) {
 
			if (new_cs->status > STATUS_AUTH && new_cs != cs) {
 
			if (new_cs->status > STATUS_AUTHORIZED && new_cs != cs) {
 
				/* Some errors we filter to a more general error. Clients don't have to know the real
 
				 *  reason a joining failed. */
 
				if (error == NETWORK_ERROR_NOT_AUTHORIZED || error == NETWORK_ERROR_NOT_EXPECTED || error == NETWORK_ERROR_WRONG_REVISION)
 
					error = NETWORK_ERROR_ILLEGAL_PACKET;
 

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

	
 
	/* The client made a mistake, so drop his connection now! */
 
	return NetworkCloseClient(cs, NETWORK_RECV_STATUS_SERVER_ERROR);
 
}
 

	
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_CHECK_NEWGRFS)(NetworkClientSocket *cs)
 
{
 
	/*
 
	 * Packet: PACKET_SERVER_CHECK_NEWGRFS
 
	 * Function: Sends info about the used GRFs to the client
 
	 * Data:
 
	 *      uint8:  Amount of GRFs
 
	 *    And then for each GRF:
 
	 *      uint32: GRF ID
 
	 * 16 * uint8:  MD5 checksum of the GRF
 
	 */
 

	
 
	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)) cs->Send_GRFIdentifier(p, &c->ident);
 
	}
 

	
 
	cs->Send_Packet(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_NEED_GAME_PASSWORD)(NetworkClientSocket *cs)
 
{
 
	/*
 
@@ -216,227 +216,227 @@ DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SER
 
	 * Function: Indication to the client that the server needs a game password
 
	 */
 

	
 
	/* Invalid packet when status is STATUS_AUTH_GAME or higher */
 
	if (cs->status >= STATUS_AUTH_GAME) return NetworkCloseClient(cs, NETWORK_RECV_STATUS_MALFORMED_PACKET);
 

	
 
	cs->status = STATUS_AUTH_GAME;
 

	
 
	Packet *p = new Packet(PACKET_SERVER_NEED_GAME_PASSWORD);
 
	cs->Send_Packet(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_NEED_COMPANY_PASSWORD)(NetworkClientSocket *cs)
 
{
 
	/*
 
	 * Packet: PACKET_SERVER_NEED_COMPANY_PASSWORD
 
	 * Function: Indication to the client that the server needs a company password
 
	 * Data:
 
	 *    uint32:  Generation seed
 
	 *    string:  Network ID of the server
 
	 */
 

	
 
	/* Invalid packet when status is STATUS_AUTH_COMPANY or higher */
 
	if (cs->status >= STATUS_AUTH_COMPANY) return NetworkCloseClient(cs, NETWORK_RECV_STATUS_MALFORMED_PACKET);
 

	
 
	cs->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);
 
	cs->Send_Packet(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_SERVER_SEND_COMMAND(PACKET_SERVER_WELCOME)
 
{
 
	/*
 
	 * Packet: SERVER_WELCOME
 
	 * Function: The client is joined and ready to receive his map
 
	 * Data:
 
	 *    uint32:  Own Client identifier
 
	 */
 

	
 
	Packet *p;
 
	NetworkClientSocket *new_cs;
 

	
 
	/* Invalid packet when status is AUTH or higher */
 
	if (cs->status >= STATUS_AUTH) return NetworkCloseClient(cs, NETWORK_RECV_STATUS_MALFORMED_PACKET);
 
	if (cs->status >= STATUS_AUTHORIZED) return NetworkCloseClient(cs, NETWORK_RECV_STATUS_MALFORMED_PACKET);
 

	
 
	cs->status = STATUS_AUTH;
 
	cs->status = STATUS_AUTHORIZED;
 
	_network_game_info.clients_on++;
 

	
 
	p = new Packet(PACKET_SERVER_WELCOME);
 
	p->Send_uint32(cs->client_id);
 
	p->Send_uint32(_settings_game.game_creation.generation_seed);
 
	p->Send_string(_settings_client.network.network_id);
 
	cs->Send_Packet(p);
 

	
 
		/* Transmit info about all the active clients */
 
	FOR_ALL_CLIENT_SOCKETS(new_cs) {
 
		if (new_cs != cs && new_cs->status > STATUS_AUTH)
 
		if (new_cs != cs && new_cs->status > STATUS_AUTHORIZED)
 
			SEND_COMMAND(PACKET_SERVER_CLIENT_INFO)(cs, new_cs->GetInfo());
 
	}
 
	/* Also send the info of the server */
 
	return SEND_COMMAND(PACKET_SERVER_CLIENT_INFO)(cs, NetworkFindClientInfoFromClientID(CLIENT_ID_SERVER));
 
}
 

	
 
DEF_SERVER_SEND_COMMAND(PACKET_SERVER_WAIT)
 
{
 
	/*
 
	 * Packet: PACKET_SERVER_WAIT
 
	 * Function: The client can not receive the map at the moment because
 
	 *             someone else is already receiving the map
 
	 * Data:
 
	 *    uint8:  Clients awaiting map
 
	 */
 
	int waiting = 0;
 
	NetworkClientSocket *new_cs;
 
	Packet *p;
 

	
 
	/* Count how many clients are waiting in the queue */
 
	FOR_ALL_CLIENT_SOCKETS(new_cs) {
 
		if (new_cs->status == STATUS_MAP_WAIT) waiting++;
 
	}
 

	
 
	p = new Packet(PACKET_SERVER_WAIT);
 
	p->Send_uint8(waiting);
 
	cs->Send_Packet(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/* This sends the map to the client */
 
DEF_SERVER_SEND_COMMAND(PACKET_SERVER_MAP)
 
{
 
	/*
 
	 * Packet: SERVER_MAP
 
	 * Function: Sends the map to the client, or a part of it (it is splitted in
 
	 *   a lot of multiple packets)
 
	 * Data:
 
	 *    uint8:  packet-type (MAP_PACKET_START, MAP_PACKET_NORMAL and MAP_PACKET_END)
 
	 *  if MAP_PACKET_START:
 
	 *    uint32: The current FrameCounter
 
	 *  if MAP_PACKET_NORMAL:
 
	 *    piece of the map (till max-size of packet)
 
	 *  if MAP_PACKET_END:
 
	 *    nothing
 
	 */
 

	
 
	static FILE *file_pointer;
 
	static uint sent_packets; // How many packets we did send succecfully last time
 

	
 
	if (cs->status < STATUS_AUTH) {
 
	if (cs->status < STATUS_AUTHORIZED) {
 
		/* Illegal call, return error and ignore the packet */
 
		return SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_AUTHORIZED);
 
	}
 

	
 
	if (cs->status == STATUS_AUTH) {
 
	if (cs->status == STATUS_AUTHORIZED) {
 
		const char *filename = "network_server.tmp";
 
		Packet *p;
 

	
 
		/* Make a dump of the current game */
 
		if (SaveOrLoad(filename, SL_SAVE, AUTOSAVE_DIR) != SL_OK) usererror("network savedump failed");
 

	
 
		file_pointer = FioFOpenFile(filename, "rb", AUTOSAVE_DIR);
 
		fseek(file_pointer, 0, SEEK_END);
 

	
 
		if (ftell(file_pointer) == 0) usererror("network savedump failed - zero sized savegame?");
 

	
 
		/* Now send the _frame_counter and how many packets are coming */
 
		p = new Packet(PACKET_SERVER_MAP);
 
		p->Send_uint8 (MAP_PACKET_START);
 
		p->Send_uint32(_frame_counter);
 
		p->Send_uint32(ftell(file_pointer));
 
		cs->Send_Packet(p);
 

	
 
		fseek(file_pointer, 0, SEEK_SET);
 

	
 
		sent_packets = 4; // We start with trying 4 packets
 

	
 
		cs->status = STATUS_MAP;
 
		/* Mark the start of download */
 
		cs->last_frame = _frame_counter;
 
		cs->last_frame_server = _frame_counter;
 
	}
 

	
 
	if (cs->status == STATUS_MAP) {
 
		uint i;
 
		int res;
 
		for (i = 0; i < sent_packets; i++) {
 
			Packet *p = new Packet(PACKET_SERVER_MAP);
 
			p->Send_uint8(MAP_PACKET_NORMAL);
 
			res = (int)fread(p->buffer + p->size, 1, SEND_MTU - p->size, file_pointer);
 

	
 
			if (ferror(file_pointer)) usererror("Error reading temporary network savegame!");
 

	
 
			p->size += res;
 
			cs->Send_Packet(p);
 
			if (feof(file_pointer)) {
 
				/* Done reading! */
 
				Packet *p = new Packet(PACKET_SERVER_MAP);
 
				p->Send_uint8(MAP_PACKET_END);
 
				cs->Send_Packet(p);
 

	
 
				/* Set the status to DONE_MAP, no we will wait for the client
 
				 *  to send it is ready (maybe that happens like never ;)) */
 
				cs->status = STATUS_DONE_MAP;
 
				fclose(file_pointer);
 

	
 
				NetworkClientSocket *new_cs;
 
				bool new_map_client = false;
 
				/* Check if there is a client waiting for receiving the map
 
				 *  and start sending him the map */
 
				FOR_ALL_CLIENT_SOCKETS(new_cs) {
 
					if (new_cs->status == STATUS_MAP_WAIT) {
 
						/* Check if we already have a new client to send the map to */
 
						if (!new_map_client) {
 
							/* If not, this client will get the map */
 
							new_cs->status = STATUS_AUTH;
 
							new_cs->status = STATUS_AUTHORIZED;
 
							new_map_client = true;
 
							SEND_COMMAND(PACKET_SERVER_MAP)(new_cs);
 
						} else {
 
							/* Else, send the other clients how many clients are in front of them */
 
							SEND_COMMAND(PACKET_SERVER_WAIT)(new_cs);
 
						}
 
					}
 
				}
 

	
 
				/* There is no more data, so break the for */
 
				break;
 
			}
 
		}
 

	
 
		/* Send all packets (forced) and check if we have send it all */
 
		cs->Send_Packets();
 
		if (cs->IsPacketQueueEmpty()) {
 
			/* All are sent, increase the sent_packets */
 
			sent_packets *= 2;
 
		} else {
 
			/* Not everything is sent, decrease the sent_packets */
 
			if (sent_packets > 1) sent_packets /= 2;
 
		}
 
	}
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_JOIN)(NetworkClientSocket *cs, ClientID client_id)
 
{
 
	/*
 
	 * Packet: SERVER_JOIN
 
	 * Function: A client is joined (all active clients receive this after a
 
	 *     PACKET_CLIENT_MAP_OK) Mostly what directly follows is a
 
	 *     PACKET_SERVER_CLIENT_INFO
 
	 * Data:
 
	 *    uint32:  Client-identifier
 
	 */
 

	
 
	Packet *p = new Packet(PACKET_SERVER_JOIN);
 

	
 
	p->Send_uint32(client_id);
 

	
 
	cs->Send_Packet(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 

	
 
DEF_SERVER_SEND_COMMAND(PACKET_SERVER_FRAME)
 
@@ -770,138 +770,138 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT
 
	return SEND_COMMAND(PACKET_SERVER_WELCOME)(cs);
 
}
 

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMPANY_PASSWORD)
 
{
 
	if (cs->status != STATUS_AUTH_COMPANY) {
 
		return SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED);
 
	}
 

	
 
	char password[NETWORK_PASSWORD_LENGTH];
 
	p->Recv_string(password, sizeof(password));
 

	
 
	/* Check company password. Allow joining if we cleared the password meanwhile */
 
	const NetworkClientInfo *ci = cs->GetInfo();
 
	if (!StrEmpty(_network_company_states[ci->client_playas].password) &&
 
			strcmp(password, _network_company_states[ci->client_playas].password) != 0) {
 
		/* Password is invalid */
 
		return SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_WRONG_PASSWORD);
 
	}
 

	
 
	return SEND_COMMAND(PACKET_SERVER_WELCOME)(cs);
 
}
 

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_GETMAP)
 
{
 
	NetworkClientSocket *new_cs;
 

	
 
	/* Do an extra version match. We told the client our version already,
 
	 * lets confirm that the client isn't lieing to us.
 
	 * But only do it for stable releases because of those we are sure
 
	 * that everybody has the same NewGRF version. For trunk and the
 
	 * branches we make tarballs of the OpenTTDs compiled from tarball
 
	 * will have the lower bits set to 0. As such they would become
 
	 * incompatible, which we would like to prevent by this. */
 
	if (HasBit(_openttd_newgrf_version, 19)) {
 
		if (_openttd_newgrf_version != p->Recv_uint32()) {
 
			/* The version we get from the client differs, it must have the
 
			 * wrong version. The client must be wrong. */
 
			return SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED);
 
		}
 
	} else if (p->size != 3) {
 
		/* We received a packet from a version that claims to be stable.
 
		 * That shouldn't happen. The client must be wrong. */
 
		return SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED);
 
	}
 

	
 
	/* The client was never joined.. so this is impossible, right?
 
	 *  Ignore the packet, give the client a warning, and close his connection */
 
	if (cs->status < STATUS_AUTH || cs->HasClientQuit()) {
 
	if (cs->status < STATUS_AUTHORIZED || cs->HasClientQuit()) {
 
		return SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_AUTHORIZED);
 
	}
 

	
 
	/* Check if someone else is receiving the map */
 
	FOR_ALL_CLIENT_SOCKETS(new_cs) {
 
		if (new_cs->status == STATUS_MAP) {
 
			/* Tell the new client to wait */
 
			cs->status = STATUS_MAP_WAIT;
 
			return SEND_COMMAND(PACKET_SERVER_WAIT)(cs);
 
		}
 
	}
 

	
 
	/* We receive a request to upload the map.. give it to the client! */
 
	return SEND_COMMAND(PACKET_SERVER_MAP)(cs);
 
}
 

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_MAP_OK)
 
{
 
	/* Client has the map, now start syncing */
 
	if (cs->status == STATUS_DONE_MAP && !cs->HasClientQuit()) {
 
		char client_name[NETWORK_CLIENT_NAME_LENGTH];
 
		NetworkClientSocket *new_cs;
 

	
 
		NetworkGetClientName(client_name, sizeof(client_name), cs);
 

	
 
		NetworkTextMessage(NETWORK_ACTION_JOIN, CC_DEFAULT, false, client_name, NULL, cs->client_id);
 

	
 
		/* Mark the client as pre-active, and wait for an ACK
 
		 *  so we know he is done loading and in sync with us */
 
		cs->status = STATUS_PRE_ACTIVE;
 
		NetworkHandleCommandQueue(cs);
 
		SEND_COMMAND(PACKET_SERVER_FRAME)(cs);
 
		SEND_COMMAND(PACKET_SERVER_SYNC)(cs);
 

	
 
		/* This is the frame the client receives
 
		 *  we need it later on to make sure the client is not too slow */
 
		cs->last_frame = _frame_counter;
 
		cs->last_frame_server = _frame_counter;
 

	
 
		FOR_ALL_CLIENT_SOCKETS(new_cs) {
 
			if (new_cs->status > STATUS_AUTH) {
 
			if (new_cs->status > STATUS_AUTHORIZED) {
 
				SEND_COMMAND(PACKET_SERVER_CLIENT_INFO)(new_cs, cs->GetInfo());
 
				SEND_COMMAND(PACKET_SERVER_JOIN)(new_cs, cs->client_id);
 
			}
 
		}
 

	
 
		/* also update the new client with our max values */
 
		SEND_COMMAND(PACKET_SERVER_CONFIG_UPDATE)(cs);
 

	
 
		/* quickly update the syncing client with company details */
 
		return SEND_COMMAND(PACKET_SERVER_COMPANY_UPDATE)(cs);
 
	}
 

	
 
	/* Wrong status for this packet, give a warning to client, and close connection */
 
	return SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED);
 
}
 

	
 
/** The client has done a command and wants us to handle it
 
 * @param *cs the connected client that has sent the command
 
 * @param *p the packet in which the command was sent
 
 */
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND)
 
{
 
	NetworkClientSocket *new_cs;
 

	
 
	/* The client was never joined.. so this is impossible, right?
 
	 *  Ignore the packet, give the client a warning, and close his connection */
 
	if (cs->status < STATUS_DONE_MAP || cs->HasClientQuit()) {
 
		return SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED);
 
	}
 

	
 
	CommandPacket cp;
 
	const char *err = cs->Recv_Command(p, &cp);
 

	
 
	if (cs->HasClientQuit()) return NETWORK_RECV_STATUS_CONN_LOST;
 

	
 
	NetworkClientInfo *ci = cs->GetInfo();
 

	
 
	if (err != NULL) {
 
		IConsolePrintF(CC_ERROR, "WARNING: %s from client %d (IP: %s).", err, ci->client_id, GetClientIP(ci));
 
		return SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED);
 
	}
 

	
 

	
 
	if ((GetCommandFlags(cp.cmd) & CMD_SERVER) && ci->client_id != CLIENT_ID_SERVER) {
 
		IConsolePrintF(CC_ERROR, "WARNING: server only command from: client %d (IP: %s), kicking...", ci->client_id, GetClientIP(ci));
 
		return SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_KICKED);
 
	}
 

	
 
@@ -944,135 +944,135 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT
 
	cp.frame = _frame_counter_max + 1;
 
	cp.next  = NULL;
 

	
 
	CommandCallback *callback = cp.callback;
 

	
 
	/* Queue the command for the clients (are send at the end of the frame
 
	 *   if they can handle it ;)) */
 
	FOR_ALL_CLIENT_SOCKETS(new_cs) {
 
		if (new_cs->status >= STATUS_MAP) {
 
			/* Callbacks are only send back to the client who sent them in the
 
			 *  first place. This filters that out. */
 
			cp.callback = (new_cs != cs) ? NULL : callback;
 
			cp.my_cmd = (new_cs == cs);
 
			NetworkAddCommandQueue(cp, new_cs);
 
		}
 
	}
 

	
 
	cp.callback = NULL;
 
	cp.my_cmd = false;
 
	NetworkAddCommandQueue(cp);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_ERROR)
 
{
 
	/* This packets means a client noticed an error and is reporting this
 
	 *  to us. Display the error and report it to the other clients */
 
	NetworkClientSocket *new_cs;
 
	char str[100];
 
	char client_name[NETWORK_CLIENT_NAME_LENGTH];
 
	NetworkErrorCode errorno = (NetworkErrorCode)p->Recv_uint8();
 

	
 
	/* The client was never joined.. thank the client for the packet, but ignore it */
 
	if (cs->status < STATUS_DONE_MAP || cs->HasClientQuit()) {
 
		cs->CloseConnection();
 
		return NETWORK_RECV_STATUS_CONN_LOST;
 
	}
 

	
 
	NetworkGetClientName(client_name, sizeof(client_name), cs);
 

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

	
 
	DEBUG(net, 2, "'%s' reported an error and is closing its connection (%s)", client_name, str);
 

	
 
	NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, client_name, NULL, strid);
 

	
 
	FOR_ALL_CLIENT_SOCKETS(new_cs) {
 
		if (new_cs->status > STATUS_AUTH) {
 
		if (new_cs->status > STATUS_AUTHORIZED) {
 
			SEND_COMMAND(PACKET_SERVER_ERROR_QUIT)(new_cs, cs->client_id, errorno);
 
		}
 
	}
 

	
 
	cs->CloseConnection(false);
 
	return NETWORK_RECV_STATUS_CONN_LOST;
 
}
 

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_QUIT)
 
{
 
	/* The client wants to leave. Display this and report it to the other
 
	 *  clients. */
 
	NetworkClientSocket *new_cs;
 
	char client_name[NETWORK_CLIENT_NAME_LENGTH];
 

	
 
	/* The client was never joined.. thank the client for the packet, but ignore it */
 
	if (cs->status < STATUS_DONE_MAP || cs->HasClientQuit()) {
 
		cs->CloseConnection();
 
		return NETWORK_RECV_STATUS_CONN_LOST;
 
	}
 

	
 
	NetworkGetClientName(client_name, sizeof(client_name), cs);
 

	
 
	NetworkTextMessage(NETWORK_ACTION_LEAVE, CC_DEFAULT, false, client_name, NULL, STR_NETWORK_MESSAGE_CLIENT_LEAVING);
 

	
 
	FOR_ALL_CLIENT_SOCKETS(new_cs) {
 
		if (new_cs->status > STATUS_AUTH) {
 
		if (new_cs->status > STATUS_AUTHORIZED) {
 
			SEND_COMMAND(PACKET_SERVER_QUIT)(new_cs, cs->client_id);
 
		}
 
	}
 

	
 
	cs->CloseConnection(false);
 
	return NETWORK_RECV_STATUS_CONN_LOST;
 
}
 

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_ACK)
 
{
 
	if (cs->status < STATUS_AUTH) {
 
	if (cs->status < STATUS_AUTHORIZED) {
 
		/* Illegal call, return error and ignore the packet */
 
		return SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_AUTHORIZED);
 
	}
 

	
 
	uint32 frame = p->Recv_uint32();
 

	
 
	/* The client is trying to catch up with the server */
 
	if (cs->status == STATUS_PRE_ACTIVE) {
 
		/* The client is not yet catched up? */
 
		if (frame + DAY_TICKS < _frame_counter) return NETWORK_RECV_STATUS_OKAY;
 

	
 
		/* Now he is! Unpause the game */
 
		cs->status = STATUS_ACTIVE;
 

	
 
		/* Execute script for, e.g. MOTD */
 
		IConsoleCmdExec("exec scripts/on_server_connect.scr 0");
 
	}
 

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

	
 

	
 

	
 
void NetworkServerSendChat(NetworkAction action, DestType desttype, int dest, const char *msg, ClientID from_id, int64 data)
 
{
 
	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 = NetworkFindClientInfoFromClientID(from_id);
 
			/* Display the text locally, and that is it */
 
			if (ci != NULL)
 
				NetworkTextMessage(action, (ConsoleColour)GetDrawStringCompanyColour(ci->client_playas), false, ci->client_name, msg, data);
 
		} else {
 
			/* Else find the client to send the message to */
 
			FOR_ALL_CLIENT_SOCKETS(cs) {
 
				if (cs->client_id == (ClientID)dest) {
 
					SEND_COMMAND(PACKET_SERVER_CHAT)(cs, action, from_id, false, msg, data);
 
					break;
 
				}
 
			}
 
@@ -1109,97 +1109,97 @@ void NetworkServerSendChat(NetworkAction
 
			}
 
		}
 

	
 
		ci = NetworkFindClientInfoFromClientID(from_id);
 
		ci_own = NetworkFindClientInfoFromClientID(CLIENT_ID_SERVER);
 
		if (ci != NULL && ci_own != NULL && ci_own->client_playas == dest) {
 
			NetworkTextMessage(action, (ConsoleColour)GetDrawStringCompanyColour(ci->client_playas), false, ci->client_name, msg, data);
 
			if (from_id == CLIENT_ID_SERVER) show_local = false;
 
			ci_to = ci_own;
 
		}
 

	
 
		/* There is no such client */
 
		if (ci_to == NULL) break;
 

	
 
		/* Display the message locally (so you know you have sent it) */
 
		if (ci != NULL && show_local) {
 
			if (from_id == CLIENT_ID_SERVER) {
 
				char name[NETWORK_NAME_LENGTH];
 
				StringID str = Company::IsValidID(ci_to->client_playas) ? STR_COMPANY_NAME : STR_NETWORK_SPECTATORS;
 
				SetDParam(0, ci_to->client_playas);
 
				GetString(name, str, lastof(name));
 
				NetworkTextMessage(action, (ConsoleColour)GetDrawStringCompanyColour(ci_own->client_playas), true, name, msg, data);
 
			} else {
 
				FOR_ALL_CLIENT_SOCKETS(cs) {
 
					if (cs->client_id == from_id) {
 
						SEND_COMMAND(PACKET_SERVER_CHAT)(cs, action, ci_to->client_id, true, msg, data);
 
					}
 
				}
 
			}
 
		}
 
		}
 
		break;
 
	default:
 
		DEBUG(net, 0, "[server] received unknown chat destination type %d. Doing broadcast instead", desttype);
 
		/* fall-through to next case */
 
	case DESTTYPE_BROADCAST:
 
		FOR_ALL_CLIENT_SOCKETS(cs) {
 
			SEND_COMMAND(PACKET_SERVER_CHAT)(cs, action, from_id, false, msg, data);
 
		}
 
		ci = NetworkFindClientInfoFromClientID(from_id);
 
		if (ci != NULL)
 
			NetworkTextMessage(action, (ConsoleColour)GetDrawStringCompanyColour(ci->client_playas), false, ci->client_name, msg, data);
 
		break;
 
	}
 
}
 

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_CHAT)
 
{
 
	if (cs->status < STATUS_AUTH) {
 
	if (cs->status < STATUS_AUTHORIZED) {
 
		/* Illegal call, return error and ignore the packet */
 
		return SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_AUTHORIZED);
 
	}
 

	
 
	NetworkAction action = (NetworkAction)p->Recv_uint8();
 
	DestType desttype = (DestType)p->Recv_uint8();
 
	int dest = p->Recv_uint32();
 
	char msg[NETWORK_CHAT_LENGTH];
 

	
 
	p->Recv_string(msg, NETWORK_CHAT_LENGTH);
 
	int64 data = p->Recv_uint64();
 

	
 
	NetworkClientInfo *ci = cs->GetInfo();
 
	switch (action) {
 
		case NETWORK_ACTION_GIVE_MONEY:
 
			if (!Company::IsValidID(ci->client_playas)) break;
 
			/* Fall-through */
 
		case NETWORK_ACTION_CHAT:
 
		case NETWORK_ACTION_CHAT_CLIENT:
 
		case NETWORK_ACTION_CHAT_COMPANY:
 
			NetworkServerSendChat(action, desttype, dest, msg, cs->client_id, data);
 
			break;
 
		default:
 
			IConsolePrintF(CC_ERROR, "WARNING: invalid chat action from client %d (IP: %s).", ci->client_id, GetClientIP(ci));
 
			return SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED);
 
			break;
 
	}
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_SET_PASSWORD)
 
{
 
	if (cs->status != STATUS_ACTIVE) {
 
		/* Illegal call, return error and ignore the packet */
 
		return SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED);
 
	}
 

	
 
	char password[NETWORK_PASSWORD_LENGTH];
 
	const NetworkClientInfo *ci;
 

	
 
	p->Recv_string(password, sizeof(password));
 
	ci = cs->GetInfo();
 

	
 
	if (Company::IsValidID(ci->client_playas)) {
 
		strecpy(_network_company_states[ci->client_playas].password, password, lastof(_network_company_states[ci->client_playas].password));
 
		NetworkServerUpdateCompanyPassworded(ci->client_playas, !StrEmpty(_network_company_states[ci->client_playas].password));
 
	}
 
	return NETWORK_RECV_STATUS_OKAY;
0 comments (0 inline, 0 general)