network_server.c
author Darkvater
Thu, 02 Mar 2006 00:32:48 +0000
changeset 3117 8066b8d0802b
parent 2952 6a26eeda9679
child 3121 2e50f731567a
permissions -rw-r--r--
(svn r3721) - [3/4] Present the game with a unified structure for the configuration-ini, saveload, console and gui representations of the settings. From part 3 on, OpenTTD is once again compilable.
- Code has been added to the saveload code to honour the SLF_SAVE_NO and SLF_NETWORK_NO flags. SLF_NETWORK_NO just reads in the the bytestream and then discards it because that setting is not synchronised. For this the function SlSkipBytes() has been reinstated
- SAVEGAME_VERSION has been changed from a constant ENUM to a constant integer. This was done for the configuration-code to be able to tell which version of a CONDVAR type to handle. As said before, because settings can be saved to the savegame, they will become conditional at some point. The configuration code always has to read the 'most recent' version.
- GameOptions are saved through the new structure. It is fully compatible with any old savegame...however it is better. Because of the move to this new format we can instruct the loader to skip certain variables. Autosave for example isn't synchronised anymore (in the network). The same goes for currency and kilometers :D. That is the only functionality change this patch is supposed to have if I have written it correctly.
- NOTE! Patches are still not saved so for Multiplayer to work network_client.c and network_server.c needed slight modifications.
/* $Id$ */

#include "stdafx.h"
#include "debug.h"
#include "string.h"
#include "strings.h"
#include "network_data.h"
#include "train.h"

#ifdef ENABLE_NETWORK

#include "table/strings.h"
#include "functions.h"
#include "network_server.h"
#include "network_udp.h"
#include "console.h"
#include "command.h"
#include "gfx.h"
#include "saveload.h"
#include "vehicle.h"
#include "station.h"
#include "settings.h"
#include "variables.h"

// This file handles all the server-commands

static void NetworkHandleCommandQueue(NetworkClientState* cs);
static void NetworkSendPatchSettings(NetworkClientState* cs);

void NetworkPopulateCompanyInfo(void);

// Is the network enabled?

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

DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_CLIENT_INFO)(NetworkClientState *cs, NetworkClientInfo *ci)
{
	//
	// Packet: SERVER_CLIENT_INFO
	// Function: Sends info about a client
	// Data:
	//    uint16:  The index of the client (always unique on a server. 1 = server)
	//    uint8:  As which player the client is playing
	//    String: The name of the client
	//    String: The unique id of the client
	//

	Packet *p;

	if (ci->client_index != NETWORK_EMPTY_INDEX) {
		p = NetworkSend_Init(PACKET_SERVER_CLIENT_INFO);
		NetworkSend_uint16(p, ci->client_index);
		NetworkSend_uint8 (p, ci->client_playas);
		NetworkSend_string(p, ci->client_name);
		NetworkSend_string(p, ci->unique_id);

		NetworkSend_Packet(p, cs);
	}
}

DEF_SERVER_SEND_COMMAND(PACKET_SERVER_COMPANY_INFO)
{
//
	// Packet: SERVER_COMPANY_INFO
	// Function: Sends info about the companies
	// Data:
	//

	int i;

	Player *player;
	Packet *p;

	byte active = ActivePlayerCount();

	if (active == 0) {
		Packet *p = NetworkSend_Init(PACKET_SERVER_COMPANY_INFO);

		NetworkSend_uint8 (p, NETWORK_COMPANY_INFO_VERSION);
		NetworkSend_uint8 (p, active);

		NetworkSend_Packet(p, cs);
		return;
	}

	NetworkPopulateCompanyInfo();

	FOR_ALL_PLAYERS(player) {
		if (!player->is_active)
			continue;

		p = NetworkSend_Init(PACKET_SERVER_COMPANY_INFO);

		NetworkSend_uint8 (p, NETWORK_COMPANY_INFO_VERSION);
		NetworkSend_uint8 (p, active);
		NetworkSend_uint8 (p, player->index);

		NetworkSend_string(p, _network_player_info[player->index].company_name);
		NetworkSend_uint8 (p, _network_player_info[player->index].inaugurated_year);
		NetworkSend_uint64(p, _network_player_info[player->index].company_value);
		NetworkSend_uint64(p, _network_player_info[player->index].money);
		NetworkSend_uint64(p, _network_player_info[player->index].income);
		NetworkSend_uint16(p, _network_player_info[player->index].performance);

		/* Send 1 if there is a passord for the company else send 0 */
		if (_network_player_info[player->index].password[0] != '\0') {
			NetworkSend_uint8 (p, 1);
		} else {
			NetworkSend_uint8 (p, 0);
		}

		for (i = 0; i < NETWORK_VEHICLE_TYPES; i++)
			NetworkSend_uint16(p, _network_player_info[player->index].num_vehicle[i]);

		for (i = 0; i < NETWORK_STATION_TYPES; i++)
			NetworkSend_uint16(p, _network_player_info[player->index].num_station[i]);

		if (_network_player_info[player->index].players[0] == '\0')
			NetworkSend_string(p, "<none>");
		else
			NetworkSend_string(p, _network_player_info[player->index].players);

		NetworkSend_Packet(p, cs);
	}

	p = NetworkSend_Init(PACKET_SERVER_COMPANY_INFO);

	NetworkSend_uint8 (p, NETWORK_COMPANY_INFO_VERSION);
	NetworkSend_uint8 (p, 0);

	NetworkSend_Packet(p, cs);
}

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

	NetworkClientState *new_cs;
	char str[100];
	char client_name[NETWORK_CLIENT_NAME_LENGTH];

	Packet *p = NetworkSend_Init(PACKET_SERVER_ERROR);
	NetworkSend_uint8(p, error);
	NetworkSend_Packet(p, cs);

	GetString(str, STR_NETWORK_ERR_CLIENT_GENERAL + error);

	// Only send when the current client was in game
	if (cs->status > STATUS_AUTH) {
		NetworkGetClientName(client_name, sizeof(client_name), cs);

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

		NetworkTextMessage(NETWORK_ACTION_LEAVE, 1, false, client_name, "%s", str);

		FOR_ALL_CLIENTS(new_cs) {
			if (new_cs->status > STATUS_AUTH && 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->index, error);
			}
		}
	} else {
		DEBUG(net, 2) ("[NET] Client %d made an error and has been disconnected. Reason: %s", cs->index, str);
	}

	cs->quited = true;

	// Make sure the data get's there before we close the connection
	NetworkSend_Packets(cs);

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

DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_NEED_PASSWORD)(NetworkClientState *cs, NetworkPasswordType type)
{
	//
	// Packet: SERVER_NEED_PASSWORD
	// Function: Indication to the client that the server needs a password
	// Data:
	//    uint8:  Type of password
	//

	Packet *p = NetworkSend_Init(PACKET_SERVER_NEED_PASSWORD);
	NetworkSend_uint8(p, type);
	NetworkSend_Packet(p, cs);
}

DEF_SERVER_SEND_COMMAND(PACKET_SERVER_WELCOME)
{
	//
	// Packet: SERVER_WELCOME
	// Function: The client is joined and ready to receive his map
	// Data:
	//    uint16:  Own ClientID
	//

	Packet *p;
	const NetworkClientState *new_cs;

	// Invalid packet when status is AUTH or higher
	if (cs->status >= STATUS_AUTH) return;

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

	p = NetworkSend_Init(PACKET_SERVER_WELCOME);
	NetworkSend_uint16(p, cs->index);
	NetworkSend_Packet(p, cs);

		// Transmit info about all the active clients
	FOR_ALL_CLIENTS(new_cs) {
		if (new_cs != cs && new_cs->status > STATUS_AUTH)
			SEND_COMMAND(PACKET_SERVER_CLIENT_INFO)(cs, DEREF_CLIENT_INFO(new_cs));
	}
	// Also send the info of the server
	SEND_COMMAND(PACKET_SERVER_CLIENT_INFO)(cs, NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX));
}

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;
	NetworkClientState *new_cs;
	Packet *p;

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

	p = NetworkSend_Init(PACKET_SERVER_WAIT);
	NetworkSend_uint8(p, waiting);
	NetworkSend_Packet(p, cs);
}

// 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:
	//    uint32: seed0 of player
	//    uint32: seed1 of player
	//      last 2 are repeated MAX_PLAYERS time
	//

	char filename[256];
	static FILE *file_pointer;
	static uint sent_packets; // How many packets we did send succecfully last time

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

		// Make a dump of the current game
		sprintf(filename, "%s%snetwork_server.tmp",  _path.autosave_dir, PATHSEP);
		if (SaveOrLoad(filename, SL_SAVE) != SL_OK) error("network savedump failed");

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

		// Now send the _frame_counter and how many packets are coming
		p = NetworkSend_Init(PACKET_SERVER_MAP);
		NetworkSend_uint8(p, MAP_PACKET_START);
		NetworkSend_uint32(p, _frame_counter);
		NetworkSend_uint32(p, ftell(file_pointer));
		NetworkSend_Packet(p, cs);

		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 = NetworkSend_Init(PACKET_SERVER_MAP);
			NetworkSend_uint8(p, MAP_PACKET_NORMAL);
			res = fread(p->buffer + p->size, 1, SEND_MTU - p->size, file_pointer);
			if (ferror(file_pointer)) {
				error("Error reading temporary network savegame!");
			}
			p->size += res;
			NetworkSend_Packet(p, cs);
			if (feof(file_pointer)) {
				// Done reading!
				Packet *p;

				// XXX - Delete this when patch-settings are saved in-game
				NetworkSendPatchSettings(cs);

				p = NetworkSend_Init(PACKET_SERVER_MAP);
				NetworkSend_uint8(p, MAP_PACKET_END);
				NetworkSend_Packet(p, cs);

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

				{
					NetworkClientState *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_CLIENTS(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_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
		NetworkSend_Packets(cs);
		if (cs->packet_queue == NULL) {
			// 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;
		}
	}
}

DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_JOIN)(NetworkClientState *cs, uint16 client_index)
{
	//
	// 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:
	//    uint16:  Client-Index
	//

	Packet *p = NetworkSend_Init(PACKET_SERVER_JOIN);

	NetworkSend_uint16(p, client_index);

	NetworkSend_Packet(p, cs);
}


DEF_SERVER_SEND_COMMAND(PACKET_SERVER_FRAME)
{
	//
	// Packet: SERVER_FRAME
	// Function: Sends the current frame-counter to the client
	// Data:
	//    uint32: Frame Counter
	//    uint32: Frame Counter Max (how far may the client walk before the server?)
	//    [uint32: general-seed-1]
	//    [uint32: general-seed-2]
	//      (last two depends on compile-settings, and are not default settings)
	//

	Packet *p = NetworkSend_Init(PACKET_SERVER_FRAME);
	NetworkSend_uint32(p, _frame_counter);
	NetworkSend_uint32(p, _frame_counter_max);
#ifdef ENABLE_NETWORK_SYNC_EVERY_FRAME
	NetworkSend_uint32(p, _sync_seed_1);
#ifdef NETWORK_SEND_DOUBLE_SEED
	NetworkSend_uint32(p, _sync_seed_2);
#endif
#endif
	NetworkSend_Packet(p, cs);
}

DEF_SERVER_SEND_COMMAND(PACKET_SERVER_SYNC)
{
	//
	// Packet: SERVER_SYNC
	// Function: Sends a sync-check to the client
	// Data:
	//    uint32: Frame Counter
	//    uint32: General-seed-1
	//    [uint32: general-seed-2]
	//      (last one depends on compile-settings, and are not default settings)
	//

	Packet *p = NetworkSend_Init(PACKET_SERVER_SYNC);
	NetworkSend_uint32(p, _frame_counter);
	NetworkSend_uint32(p, _sync_seed_1);

#ifdef NETWORK_SEND_DOUBLE_SEED
	NetworkSend_uint32(p, _sync_seed_2);
#endif
	NetworkSend_Packet(p, cs);
}

DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_COMMAND)(NetworkClientState *cs, CommandPacket *cp)
{
	//
	// Packet: SERVER_COMMAND
	// Function: Sends a DoCommand to the client
	// Data:
	//    uint8:  PlayerID (0..MAX_PLAYERS-1)
	//    uint32: CommandID (see command.h)
	//    uint32: P1 (free variables used in DoCommand)
	//    uint32: P2
	//    uint32: Tile
	//    string: text
	//    uint8:  CallBackID (see callback_table.c)
	//    uint32: Frame of execution
	//

	Packet *p = NetworkSend_Init(PACKET_SERVER_COMMAND);

	NetworkSend_uint8(p, cp->player);
	NetworkSend_uint32(p, cp->cmd);
	NetworkSend_uint32(p, cp->p1);
	NetworkSend_uint32(p, cp->p2);
	NetworkSend_uint32(p, cp->tile);
	NetworkSend_string(p, cp->text);
	NetworkSend_uint8(p, cp->callback);
	NetworkSend_uint32(p, cp->frame);

	NetworkSend_Packet(p, cs);
}

DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_CHAT)(NetworkClientState *cs, NetworkAction action, uint16 client_index, bool self_send, const char *msg)
{
	//
	// Packet: SERVER_CHAT
	// Function: Sends a chat-packet to the client
	// Data:
	//    uint8:  ActionID (see network_data.h, NetworkAction)
	//    uint16:  Client-index
	//    String: Message (max MAX_TEXT_MSG_LEN)
	//

	Packet *p = NetworkSend_Init(PACKET_SERVER_CHAT);

	NetworkSend_uint8(p, action);
	NetworkSend_uint16(p, client_index);
	NetworkSend_uint8(p, self_send);
	NetworkSend_string(p, msg);

	NetworkSend_Packet(p, cs);
}

DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_ERROR_QUIT)(NetworkClientState *cs, uint16 client_index, NetworkErrorCode errorno)
{
	//
	// Packet: SERVER_ERROR_QUIT
	// Function: One of the clients made an error and is quiting the game
	//      This packet informs the other clients of that.
	// Data:
	//    uint16:  Client-index
	//    uint8:  ErrorID (see network_data.h, NetworkErrorCode)
	//

	Packet *p = NetworkSend_Init(PACKET_SERVER_ERROR_QUIT);

	NetworkSend_uint16(p, client_index);
	NetworkSend_uint8(p, errorno);

	NetworkSend_Packet(p, cs);
}

DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_QUIT)(NetworkClientState *cs, uint16 client_index, const char *leavemsg)
{
	//
	// Packet: SERVER_ERROR_QUIT
	// Function: A client left the game, and this packets informs the other clients
	//      of that.
	// Data:
	//    uint16:  Client-index
	//    String: leave-message
	//

	Packet *p = NetworkSend_Init(PACKET_SERVER_QUIT);

	NetworkSend_uint16(p, client_index);
	NetworkSend_string(p, leavemsg);

	NetworkSend_Packet(p, cs);
}

DEF_SERVER_SEND_COMMAND(PACKET_SERVER_SHUTDOWN)
{
	//
	// Packet: SERVER_SHUTDOWN
	// Function: Let the clients know that the server is closing
	// Data:
	//     <none>
	//

	Packet *p = NetworkSend_Init(PACKET_SERVER_SHUTDOWN);
	NetworkSend_Packet(p, cs);
}

DEF_SERVER_SEND_COMMAND(PACKET_SERVER_NEWGAME)
{
	//
	// Packet: PACKET_SERVER_NEWGAME
	// Function: Let the clients know that the server is loading a new map
	// Data:
	//     <none>
	//

	Packet *p = NetworkSend_Init(PACKET_SERVER_NEWGAME);
	NetworkSend_Packet(p, cs);
}

DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_RCON)(NetworkClientState *cs, uint16 color, const char *command)
{
	Packet *p = NetworkSend_Init(PACKET_SERVER_RCON);

	NetworkSend_uint16(p, color);
	NetworkSend_string(p, command);
	NetworkSend_Packet(p, cs);
}

// **********
// Receiving functions
//   DEF_SERVER_RECEIVE_COMMAND has parameter: NetworkClientState *cs, Packet *p
// **********

DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMPANY_INFO)
{
	SEND_COMMAND(PACKET_SERVER_COMPANY_INFO)(cs);
}

DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_JOIN)
{
	char name[NETWORK_NAME_LENGTH];
	char unique_id[NETWORK_NAME_LENGTH];
	NetworkClientInfo *ci;
	byte playas;
	NetworkLanguage client_lang;
	char client_revision[NETWORK_REVISION_LENGTH];

	NetworkRecv_string(cs, p, client_revision, sizeof(client_revision));

#if defined(WITH_REV) || defined(WITH_REV_HACK)
	// Check if the client has revision control enabled
	if (strncmp(NOREV_STRING, client_revision, sizeof(client_revision)) != 0) {
		if (strncmp(_network_game_info.server_revision, client_revision, sizeof(_network_game_info.server_revision) - 1) != 0) {
			// Different revisions!!
			SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_WRONG_REVISION);

			return;
		}
	}
#endif

	NetworkRecv_string(cs, p, name, sizeof(name));
	playas = NetworkRecv_uint8(cs, p);
	client_lang = NetworkRecv_uint8(cs, p);
	NetworkRecv_string(cs, p, unique_id, sizeof(unique_id));

	if (cs->quited) return;

	// join another company does not affect these values
	switch (playas) {
		case 0: /* New company */
			if (ActivePlayerCount() >= _network_game_info.companies_max) {
				SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_FULL);
				return;
			}
			break;
		case OWNER_SPECTATOR: /* Spectator */
			if (NetworkSpectatorCount() >= _network_game_info.spectators_max) {
				SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_FULL);
				return;
			}
			break;
	}

	// We need a valid name.. make it Player
	if (name[0] == '\0') snprintf(name, sizeof(name), "Player");

	if (!NetworkFindName(name)) { // Change name if duplicate
		// We could not create a name for this player
		SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NAME_IN_USE);
		return;
	}

	ci = DEREF_CLIENT_INFO(cs);

	snprintf(ci->client_name, sizeof(ci->client_name), "%s", name);
	snprintf(ci->unique_id, sizeof(ci->unique_id), "%s", unique_id);
	ci->client_playas = playas;
	ci->client_lang = client_lang;

	// We now want a password from the client
	//  else we do not allow him in!
	if (_network_game_info.use_password)
		SEND_COMMAND(PACKET_SERVER_NEED_PASSWORD)(cs, NETWORK_GAME_PASSWORD);
	else {
		if (ci->client_playas > 0 && ci->client_playas <= MAX_PLAYERS && _network_player_info[ci->client_playas - 1].password[0] != '\0') {
			SEND_COMMAND(PACKET_SERVER_NEED_PASSWORD)(cs, NETWORK_COMPANY_PASSWORD);
		}
		else {
			SEND_COMMAND(PACKET_SERVER_WELCOME)(cs);
		}
	}

	/* Make sure companies to who people try to join are not autocleaned */
	if (playas >= 1 && playas <= MAX_PLAYERS)
		_network_player_info[playas-1].months_empty = 0;
}

DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_PASSWORD)
{
	NetworkPasswordType type;
	char password[NETWORK_PASSWORD_LENGTH];
	NetworkClientInfo *ci;

	type = NetworkRecv_uint8(cs, p);
	NetworkRecv_string(cs, p, password, sizeof(password));

	if (cs->status == STATUS_INACTIVE && type == NETWORK_GAME_PASSWORD) {
		// Check game-password
		if (strncmp(password, _network_game_info.server_password, sizeof(password)) != 0) {
			// Password is invalid
			SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_WRONG_PASSWORD);
			return;
		}

		ci = DEREF_CLIENT_INFO(cs);

		if (ci->client_playas <= MAX_PLAYERS && _network_player_info[ci->client_playas - 1].password[0] != '\0') {
			SEND_COMMAND(PACKET_SERVER_NEED_PASSWORD)(cs, NETWORK_COMPANY_PASSWORD);
			return;
		}

		// Valid password, allow user
		SEND_COMMAND(PACKET_SERVER_WELCOME)(cs);
		return;
	} else if (cs->status == STATUS_INACTIVE && type == NETWORK_COMPANY_PASSWORD) {
		ci = DEREF_CLIENT_INFO(cs);

		if (strncmp(password, _network_player_info[ci->client_playas - 1].password, sizeof(password)) != 0) {
			// Password is invalid
			SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_WRONG_PASSWORD);
			return;
		}

		SEND_COMMAND(PACKET_SERVER_WELCOME)(cs);
		return;
	}


	SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED);
	return;
}

DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_GETMAP)
{
	NetworkClientState *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_AUTH || cs->quited) {
		SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_AUTHORIZED);
		return;
	}

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

	// We receive a request to upload the map.. give it to the client!
	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->quited) {
		char client_name[NETWORK_CLIENT_NAME_LENGTH];
		NetworkClientState *new_cs;

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

		NetworkTextMessage(NETWORK_ACTION_JOIN, 1, false, client_name, "");

		// 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_CLIENTS(new_cs) {
			if (new_cs->status > STATUS_AUTH) {
				SEND_COMMAND(PACKET_SERVER_CLIENT_INFO)(new_cs, DEREF_CLIENT_INFO(cs));
				SEND_COMMAND(PACKET_SERVER_JOIN)(new_cs, cs->index);
			}
		}

		if (_network_pause_on_join) {
			/* Now pause the game till the client is in sync */
			DoCommandP(0, 1, 0, NULL, CMD_PAUSE);

			NetworkServer_HandleChat(NETWORK_ACTION_CHAT, DESTTYPE_BROADCAST, 0, "Game paused (incoming client)", NETWORK_SERVER_INDEX);
		}
	} else {
		// Wrong status for this packet, give a warning to client, and close connection
		SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED);
	}
}

/** Enforce the command flags.
 * Eg a server-only command can only be executed by a server, etc.
 * @param *cp the commandpacket that is going to be checked
 * @param *ci client information for debugging output to console
 */
static bool CheckCommandFlags(const CommandPacket *cp, const NetworkClientInfo *ci)
{
	byte flags = GetCommandFlags(cp->cmd);

	if (flags & CMD_SERVER && ci->client_index != NETWORK_SERVER_INDEX) {
		IConsolePrintF(_icolour_err, "WARNING: server only command from player %d (IP: %s), kicking...", ci->client_playas, GetPlayerIP(ci));
		return false;
	}

	if (flags & CMD_OFFLINE) {
		IConsolePrintF(_icolour_err, "WARNING: offline only command from player %d (IP: %s), kicking...", ci->client_playas, GetPlayerIP(ci));
		return false;
	}
	return true;
}

/** 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)
{
	NetworkClientState *new_cs;
	const NetworkClientInfo *ci;
	byte callback;

	CommandPacket *cp = malloc(sizeof(CommandPacket));

	// 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->quited) {
		SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED);
		return;
	}

	cp->player = NetworkRecv_uint8(cs, p);
	cp->cmd    = NetworkRecv_uint32(cs, p);
	cp->p1     = NetworkRecv_uint32(cs, p);
	cp->p2     = NetworkRecv_uint32(cs, p);
	cp->tile   = NetworkRecv_uint32(cs, p);
	NetworkRecv_string(cs, p, cp->text, lengthof(cp->text));

	callback = NetworkRecv_uint8(cs, p);

	if (cs->quited) return;

	ci = DEREF_CLIENT_INFO(cs);

	/* Check if cp->cmd is valid */
	if (!IsValidCommand(cp->cmd)) {
		IConsolePrintF(_icolour_err, "WARNING: invalid command from player %d (IP: %s).", ci->client_playas, GetPlayerIP(ci));
		SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED);
		return;
	}

	if (!CheckCommandFlags(cp, ci)) {
		SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_KICKED);
		return;
	}

	/** Only CMD_PLAYER_CTRL is always allowed, for the rest, playas needs
	 * to match the player in the packet. If it doesn't, the client has done
	 * something pretty naughty (or a bug), and will be kicked
	 */
	if (!(cp->cmd == CMD_PLAYER_CTRL && cp->p1 == 0) && ci->client_playas - 1 != cp->player) {
		IConsolePrintF(_icolour_err, "WARNING: player %d (IP: %s) tried to execute a command as player %d, kicking...",
									 ci->client_playas - 1, GetPlayerIP(ci), cp->player);
		SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_PLAYER_MISMATCH);
		return;
	}

	/** @todo CMD_PLAYER_CTRL with p1 = 0 announces a new player to the server. To give the
	 * player the correct ID, the server injects p2 and executes the command. Any other p1
	 * is prohibited. Pretty ugly and should be redone together with its function.
	 * @see CmdPlayerCtrl() players.c:655
	 */
	if (cp->cmd == CMD_PLAYER_CTRL) {
		if (cp->p1 != 0) {
			SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_CHEATER);
			return;
		}

		// XXX - UGLY! p2 is mis-used to get the client-id in CmdPlayerCtrl
		cp->p2 = cs - _clients;
	}

	// The frame can be executed in the same frame as the next frame-packet
	//  That frame just before that frame is saved in _frame_counter_max
	cp->frame = _frame_counter_max + 1;
	cp->next  = NULL;

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

	cp->callback = 0;
	// Queue the command on the server
	if (_local_command_queue == NULL) {
		_local_command_queue = cp;
	} else {
		// Find last packet
		CommandPacket *c = _local_command_queue;
		while (c->next != NULL) c = c->next;
		c->next = cp;
	}
}

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
	NetworkClientState *new_cs;
	byte errorno = NetworkRecv_uint8(cs, p);
	char str[100];
	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->quited) {
		cs->quited = true;
		return;
	}

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

	GetString(str, STR_NETWORK_ERR_CLIENT_GENERAL + errorno);

	DEBUG(net, 2)("[NET] %s reported an error and is closing his connection (%s)", client_name, str);

	NetworkTextMessage(NETWORK_ACTION_LEAVE, 1, false, client_name, "%s", str);

	FOR_ALL_CLIENTS(new_cs) {
		if (new_cs->status > STATUS_AUTH) {
			SEND_COMMAND(PACKET_SERVER_ERROR_QUIT)(new_cs, cs->index, errorno);
		}
	}

	cs->quited = true;
}

DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_QUIT)
{
	// The client wants to leave. Display this and report it to the other
	//  clients.
	NetworkClientState *new_cs;
	char str[100];
	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->quited) {
		cs->quited = true;
		return;
	}

	NetworkRecv_string(cs, p, str, lengthof(str));

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

	NetworkTextMessage(NETWORK_ACTION_LEAVE, 1, false, client_name, "%s", str);

	FOR_ALL_CLIENTS(new_cs) {
		if (new_cs->status > STATUS_AUTH) {
			SEND_COMMAND(PACKET_SERVER_QUIT)(new_cs, cs->index, str);
		}
	}

	cs->quited = true;
}

DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_ACK)
{
	uint32 frame = NetworkRecv_uint32(cs, p);

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

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

		if (_network_pause_on_join) {
			DoCommandP(0, 0, 0, NULL, CMD_PAUSE);
			NetworkServer_HandleChat(NETWORK_ACTION_CHAT, DESTTYPE_BROADCAST, 0, "Game unpaused", NETWORK_SERVER_INDEX);
		}
	}

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



void NetworkServer_HandleChat(NetworkAction action, DestType desttype, int dest, const char *msg, uint16 from_index)
{
	NetworkClientState *cs;
	NetworkClientInfo *ci, *ci_own, *ci_to;

	switch (desttype) {
	case DESTTYPE_CLIENT:
		/* Are we sending to the server? */
		if (dest == NETWORK_SERVER_INDEX) {
			ci = NetworkFindClientInfoFromIndex(from_index);
			/* Display the text locally, and that is it */
			if (ci != NULL)
				NetworkTextMessage(action, GetDrawStringPlayerColor(ci->client_playas-1), false, ci->client_name, "%s", msg);
		} else {
			/* Else find the client to send the message to */
			FOR_ALL_CLIENTS(cs) {
				if (cs->index == dest) {
					SEND_COMMAND(PACKET_SERVER_CHAT)(cs, action, from_index, false, msg);
					break;
				}
			}
		}

		// Display the message locally (so you know you have sent it)
		if (from_index != dest) {
			if (from_index == NETWORK_SERVER_INDEX) {
				ci = NetworkFindClientInfoFromIndex(from_index);
				ci_to = NetworkFindClientInfoFromIndex(dest);
				if (ci != NULL && ci_to != NULL)
					NetworkTextMessage(action, GetDrawStringPlayerColor(ci->client_playas-1), true, ci_to->client_name, "%s", msg);
			} else {
				FOR_ALL_CLIENTS(cs) {
					if (cs->index == from_index) {
						SEND_COMMAND(PACKET_SERVER_CHAT)(cs, action, dest, true, msg);
						break;
					}
				}
			}
		}
		break;
	case DESTTYPE_PLAYER: {
		bool show_local = true; // If this is false, the message is already displayed
														// on the client who did sent it.
		/* Find all clients that belong to this player */
		ci_to = NULL;
		FOR_ALL_CLIENTS(cs) {
			ci = DEREF_CLIENT_INFO(cs);
			if (ci->client_playas == dest) {
				SEND_COMMAND(PACKET_SERVER_CHAT)(cs, action, from_index, false, msg);
				if (cs->index == from_index) {
					show_local = false;
				}
				ci_to = ci; // Remember a client that is in the company for company-name
			}
		}

		ci = NetworkFindClientInfoFromIndex(from_index);
		ci_own = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX);
		if (ci != NULL && ci_own != NULL && ci_own->client_playas == dest) {
			NetworkTextMessage(action, GetDrawStringPlayerColor(ci->client_playas-1), false, ci->client_name, "%s", msg);
			if (from_index == NETWORK_SERVER_INDEX)
				show_local = false;
			ci_to = ci_own;
		}

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

		// Display the message locally (so you know you have sent it)
		if (ci != NULL && show_local) {
			if (from_index == NETWORK_SERVER_INDEX) {
				char name[NETWORK_NAME_LENGTH];
				GetString(name, GetPlayer(ci_to->client_playas-1)->name_1);
				NetworkTextMessage(action, GetDrawStringPlayerColor(ci_own->client_playas-1), true, name, "%s", msg);
			} else {
				FOR_ALL_CLIENTS(cs) {
					if (cs->index == from_index) {
						SEND_COMMAND(PACKET_SERVER_CHAT)(cs, action, ci_to->client_index, true, msg);
					}
				}
			}
		}
		}
		break;
	default:
		DEBUG(net, 0)("[NET][Server] Received unknown destination type %d. Doing broadcast instead.");
		/* fall-through to next case */
	case DESTTYPE_BROADCAST:
		FOR_ALL_CLIENTS(cs) {
			SEND_COMMAND(PACKET_SERVER_CHAT)(cs, action, from_index, false, msg);
		}
		ci = NetworkFindClientInfoFromIndex(from_index);
		if (ci != NULL)
			NetworkTextMessage(action, GetDrawStringPlayerColor(ci->client_playas-1), false, ci->client_name, "%s", msg);
		break;
	}
}

DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_CHAT)
{
	NetworkAction action = NetworkRecv_uint8(cs, p);
	DestType desttype = NetworkRecv_uint8(cs, p);
	int dest = NetworkRecv_uint8(cs, p);
	char msg[MAX_TEXT_MSG_LEN];

	NetworkRecv_string(cs, p, msg, MAX_TEXT_MSG_LEN);

	NetworkServer_HandleChat(action, desttype, dest, msg, cs->index);
}

DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_SET_PASSWORD)
{
	char password[NETWORK_PASSWORD_LENGTH];
	NetworkClientInfo *ci;

	NetworkRecv_string(cs, p, password, sizeof(password));
	ci = DEREF_CLIENT_INFO(cs);

	if (ci->client_playas <= MAX_PLAYERS) {
		ttd_strlcpy(_network_player_info[ci->client_playas - 1].password, password, sizeof(_network_player_info[ci->client_playas - 1].password));
	}
}

DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_SET_NAME)
{
	char client_name[NETWORK_CLIENT_NAME_LENGTH];
	NetworkClientInfo *ci;

	NetworkRecv_string(cs, p, client_name, sizeof(client_name));
	ci = DEREF_CLIENT_INFO(cs);

	if (cs->quited)
		return;

	if (ci != NULL) {
		// Display change
		if (NetworkFindName(client_name)) {
			NetworkTextMessage(NETWORK_ACTION_NAME_CHANGE, 1, false, ci->client_name, "%s", client_name);
			ttd_strlcpy(ci->client_name, client_name, sizeof(ci->client_name));
			NetworkUpdateClientInfo(ci->client_index);
		}
	}
}

DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_RCON)
{
	char pass[NETWORK_PASSWORD_LENGTH];
	char command[NETWORK_RCONCOMMAND_LENGTH];

	if (_network_game_info.rcon_password[0] == '\0')
		return;

	NetworkRecv_string(cs, p, pass, sizeof(pass));
	NetworkRecv_string(cs, p, command, sizeof(command));

	if (strncmp(pass, _network_game_info.rcon_password, sizeof(pass)) != 0) {
		DEBUG(net, 0)("[RCon] Wrong password from client-id %d", cs->index);
		return;
	}

	DEBUG(net, 0)("[RCon] Client-id %d executed: %s", cs->index, command);

	_redirect_console_to_client = cs->index;
	IConsoleCmdExec(command);
	_redirect_console_to_client = 0;
	return;
}

// The layout for the receive-functions by the server
typedef void NetworkServerPacket(NetworkClientState *cs, Packet *p);


// This array matches PacketType. At an incoming
//  packet it is matches against this array
//  and that way the right function to handle that
//  packet is found.
static NetworkServerPacket* const _network_server_packet[] = {
	NULL, /*PACKET_SERVER_FULL,*/
	NULL, /*PACKET_SERVER_BANNED,*/
	RECEIVE_COMMAND(PACKET_CLIENT_JOIN),
	NULL, /*PACKET_SERVER_ERROR,*/
	RECEIVE_COMMAND(PACKET_CLIENT_COMPANY_INFO),
	NULL, /*PACKET_SERVER_COMPANY_INFO,*/
	NULL, /*PACKET_SERVER_CLIENT_INFO,*/
	NULL, /*PACKET_SERVER_NEED_PASSWORD,*/
	RECEIVE_COMMAND(PACKET_CLIENT_PASSWORD),
	NULL, /*PACKET_SERVER_WELCOME,*/
	RECEIVE_COMMAND(PACKET_CLIENT_GETMAP),
	NULL, /*PACKET_SERVER_WAIT,*/
	NULL, /*PACKET_SERVER_MAP,*/
	RECEIVE_COMMAND(PACKET_CLIENT_MAP_OK),
	NULL, /*PACKET_SERVER_JOIN,*/
	NULL, /*PACKET_SERVER_FRAME,*/
	NULL, /*PACKET_SERVER_SYNC,*/
	RECEIVE_COMMAND(PACKET_CLIENT_ACK),
	RECEIVE_COMMAND(PACKET_CLIENT_COMMAND),
	NULL, /*PACKET_SERVER_COMMAND,*/
	RECEIVE_COMMAND(PACKET_CLIENT_CHAT),
	NULL, /*PACKET_SERVER_CHAT,*/
	RECEIVE_COMMAND(PACKET_CLIENT_SET_PASSWORD),
	RECEIVE_COMMAND(PACKET_CLIENT_SET_NAME),
	RECEIVE_COMMAND(PACKET_CLIENT_QUIT),
	RECEIVE_COMMAND(PACKET_CLIENT_ERROR),
	NULL, /*PACKET_SERVER_QUIT,*/
	NULL, /*PACKET_SERVER_ERROR_QUIT,*/
	NULL, /*PACKET_SERVER_SHUTDOWN,*/
	NULL, /*PACKET_SERVER_NEWGAME,*/
	NULL, /*PACKET_SERVER_RCON,*/
	RECEIVE_COMMAND(PACKET_CLIENT_RCON),
};

// If this fails, check the array above with network_data.h
assert_compile(lengthof(_network_server_packet) == PACKET_END);


extern const SettingDesc _patch_settings[];

// This is a TEMPORARY solution to get the patch-settings
//  to the client. When the patch-settings are saved in the savegame
//  this should be removed!!
static void NetworkSendPatchSettings(NetworkClientState* cs)
{
	const SettingDesc *item;
	Packet *p = NetworkSend_Init(PACKET_SERVER_MAP);
	NetworkSend_uint8(p, MAP_PACKET_PATCH);
	// Now send all the patch-settings in a pretty order..

	item = _patch_settings;

	for (; item->save.cmd != SL_END; item++) {
		const void *var = ini_get_variable(&item->save, &_patches);
		switch (GetVarMemType(item->save.conv)) {
			case SLE_VAR_BL:
			case SLE_VAR_I8:
			case SLE_VAR_U8:
				NetworkSend_uint8(p, *(uint8 *)var);
				break;
			case SLE_VAR_I16:
			case SLE_VAR_U16:
				NetworkSend_uint16(p, *(uint16 *)var);
				break;
			case SLE_VAR_I32:
			case SLE_VAR_U32:
				NetworkSend_uint32(p, *(uint32 *)var);
				break;
		}
	}

	NetworkSend_Packet(p, cs);
}

// This update the company_info-stuff
void NetworkPopulateCompanyInfo(void)
{
	char password[NETWORK_PASSWORD_LENGTH];
	Player *p;
	Vehicle *v;
	Station *s;
	NetworkClientState *cs;
	NetworkClientInfo *ci;
	int i;
	uint16 months_empty;

	FOR_ALL_PLAYERS(p) {
		if (!p->is_active) {
			memset(&_network_player_info[p->index], 0, sizeof(NetworkPlayerInfo));
			continue;
		}

		// Clean the info but not the password
		ttd_strlcpy(password, _network_player_info[p->index].password, sizeof(password));
		months_empty = _network_player_info[p->index].months_empty;
		memset(&_network_player_info[p->index], 0, sizeof(NetworkPlayerInfo));
		_network_player_info[p->index].months_empty = months_empty;
		ttd_strlcpy(_network_player_info[p->index].password, password, sizeof(_network_player_info[p->index].password));

		// Grap the company name
		SetDParam(0, p->name_1);
		SetDParam(1, p->name_2);
		GetString(_network_player_info[p->index].company_name, STR_JUST_STRING);

		// Check the income
		if (_cur_year - 1 == p->inaugurated_year)
			// The player is here just 1 year, so display [2], else display[1]
			for (i = 0; i < 13; i++)
				_network_player_info[p->index].income -= p->yearly_expenses[2][i];
		else
			for (i = 0; i < 13; i++)
				_network_player_info[p->index].income -= p->yearly_expenses[1][i];

		// Set some general stuff
		_network_player_info[p->index].inaugurated_year = p->inaugurated_year;
		_network_player_info[p->index].company_value = p->old_economy[0].company_value;
		_network_player_info[p->index].money = p->money64;
		_network_player_info[p->index].performance = p->old_economy[0].performance_history;
	}

	// Go through all vehicles and count the type of vehicles
	FOR_ALL_VEHICLES(v) {
		if (v->owner < MAX_PLAYERS)
			switch (v->type) {
				case VEH_Train:
					if (IsFrontEngine(v))
						_network_player_info[v->owner].num_vehicle[0]++;
					break;
				case VEH_Road:
					if (v->cargo_type != CT_PASSENGERS)
						_network_player_info[v->owner].num_vehicle[1]++;
					else
						_network_player_info[v->owner].num_vehicle[2]++;
					break;
				case VEH_Aircraft:
					if (v->subtype <= 2)
						_network_player_info[v->owner].num_vehicle[3]++;
					break;
				case VEH_Ship:
					_network_player_info[v->owner].num_vehicle[4]++;
					break;
				case VEH_Special:
				case VEH_Disaster:
					break;
			}
	}

	// Go through all stations and count the types of stations
	FOR_ALL_STATIONS(s) {
		if (s->owner < MAX_PLAYERS) {
			if ((s->facilities & FACIL_TRAIN))
				_network_player_info[s->owner].num_station[0]++;
			if ((s->facilities & FACIL_TRUCK_STOP))
				_network_player_info[s->owner].num_station[1]++;
			if ((s->facilities & FACIL_BUS_STOP))
				_network_player_info[s->owner].num_station[2]++;
			if ((s->facilities & FACIL_AIRPORT))
				_network_player_info[s->owner].num_station[3]++;
			if ((s->facilities & FACIL_DOCK))
				_network_player_info[s->owner].num_station[4]++;
		}
	}

	ci = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX);
	// Register local player (if not dedicated)
	if (ci != NULL && ci->client_playas > 0  && ci->client_playas <= MAX_PLAYERS)
		ttd_strlcpy(_network_player_info[ci->client_playas-1].players, ci->client_name, sizeof(_network_player_info[ci->client_playas-1].players));

	FOR_ALL_CLIENTS(cs) {
		char client_name[NETWORK_CLIENT_NAME_LENGTH];

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

		ci = DEREF_CLIENT_INFO(cs);
		if (ci != NULL && ci->client_playas > 0 && ci->client_playas <= MAX_PLAYERS) {
			if (strlen(_network_player_info[ci->client_playas-1].players) != 0)
				ttd_strlcat(_network_player_info[ci->client_playas - 1].players, ", ", lengthof(_network_player_info[ci->client_playas - 1].players));

			ttd_strlcat(_network_player_info[ci->client_playas - 1].players, client_name, lengthof(_network_player_info[ci->client_playas - 1].players));
		}
	}
}

// Send a packet to all clients with updated info about this client_index
void NetworkUpdateClientInfo(uint16 client_index)
{
	NetworkClientState *cs;
	NetworkClientInfo *ci;

	ci = NetworkFindClientInfoFromIndex(client_index);

	if (ci == NULL)
		return;

	FOR_ALL_CLIENTS(cs) {
		SEND_COMMAND(PACKET_SERVER_CLIENT_INFO)(cs, ci);
	}
}

extern void SwitchMode(int new_mode);

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

		_random_seeds[0][0] = Random();
		_random_seeds[0][1] = InteractiveRandom();

		SwitchMode(SM_NEWGAME);
	}
}

/* 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(void)
{
	NetworkClientState *cs;
	NetworkClientInfo *ci;
	Player *p;
	bool clients_in_company[MAX_PLAYERS];

	if (!_network_autoclean_companies)
		return;

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

	/* Detect the active companies */
	FOR_ALL_CLIENTS(cs) {
		ci = DEREF_CLIENT_INFO(cs);
		if (ci->client_playas >= 1 && ci->client_playas <= MAX_PLAYERS) {
			clients_in_company[ci->client_playas-1] = true;
		}
	}
	if (!_network_dedicated) {
		ci = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX);
		if (ci->client_playas >= 1 && ci->client_playas <= MAX_PLAYERS) {
			clients_in_company[ci->client_playas-1] = true;
		}
	}

	/* Go through all the comapnies */
	FOR_ALL_PLAYERS(p) {
		/* Skip the non-active once */
		if (!p->is_active || p->is_ai)
			continue;

		if (!clients_in_company[p->index]) {
			/* The company is empty for one month more */
			_network_player_info[p->index].months_empty++;

			/* Is the company empty for autoclean_unprotected-months, and is there no protection? */
			if (_network_player_info[p->index].months_empty > _network_autoclean_unprotected && _network_player_info[p->index].password[0] == '\0') {
				/* Shut the company down */
				DoCommandP(0, 2, p->index, NULL, CMD_PLAYER_CTRL);
				IConsolePrintF(_icolour_def, "Auto-cleaned company #%d", p->index+1);
			}
			/* Is the compnay empty for autoclean_protected-months, and there is a protection? */
			if (_network_player_info[p->index].months_empty > _network_autoclean_protected && _network_player_info[p->index].password[0] != '\0') {
				/* Unprotect the company */
				_network_player_info[p->index].password[0] = '\0';
				IConsolePrintF(_icolour_def, "Auto-removed protection from company #%d", p->index+1);
				_network_player_info[p->index].months_empty = 0;
			}
		} else {
			/* It is not empty, reset the date */
			_network_player_info[p->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.
bool NetworkFindName(char new_name[NETWORK_CLIENT_NAME_LENGTH])
{
	NetworkClientState *new_cs;
	NetworkClientInfo *ci;
	bool found_name = false;
	byte number = 0;
	char original_name[NETWORK_CLIENT_NAME_LENGTH];

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

	while (!found_name) {
		found_name = true;
		FOR_ALL_CLIENTS(new_cs) {
			ci = DEREF_CLIENT_INFO(new_cs);
			if (strncmp(ci->client_name, new_name, NETWORK_CLIENT_NAME_LENGTH) == 0) {
				// Name already in use
				found_name = false;
				break;
			}
		}
		// Check if it is the same as the server-name
		ci = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX);
		if (ci != NULL) {
			if (strncmp(ci->client_name, new_name, NETWORK_CLIENT_NAME_LENGTH) == 0) {
				// Name already in use
				found_name = false;
			}
		}

		if (!found_name) {
			// Try a new name (<name> #1, <name> #2, and so on)

			// Stop if we tried for more than 50 times..
			if (number++ > 50) break;
			snprintf(new_name, NETWORK_CLIENT_NAME_LENGTH, "%s #%d", original_name, number);
		}
	}

	return found_name;
}

// Reads a packet from the stream
bool NetworkServer_ReadPackets(NetworkClientState *cs)
{
	Packet *p;
	NetworkRecvStatus res;
	while ((p = NetworkRecv_Packet(cs, &res)) != NULL) {
		byte type = NetworkRecv_uint8(cs, p);
		if (type < PACKET_END && _network_server_packet[type] != NULL && !cs->quited)
			_network_server_packet[type](cs, p);
		else
			DEBUG(net, 0)("[NET][Server] Received invalid packet type %d", type);
		free(p);
	}

	return true;
}

// Handle the local command-queue
static void NetworkHandleCommandQueue(NetworkClientState* cs)
{
	CommandPacket *cp;

	while ( (cp = cs->command_queue) != NULL) {
		SEND_COMMAND(PACKET_SERVER_COMMAND)(cs, cp);

		cs->command_queue = cp->next;
		free(cp);
	}
}

// This is called every tick if this is a _network_server
void NetworkServer_Tick(bool send_frame)
{
	NetworkClientState *cs;
#ifndef ENABLE_NETWORK_SYNC_EVERY_FRAME
	bool send_sync = false;
#endif

#ifndef ENABLE_NETWORK_SYNC_EVERY_FRAME
	if (_frame_counter >= _last_sync_frame + _network_sync_freq) {
		_last_sync_frame = _frame_counter;
		send_sync = true;
	}
#endif

	// Now we are done with the frame, inform the clients that they can
	//  do their frame!
	FOR_ALL_CLIENTS(cs) {
		// Check if the speed of the client is what we can expect from a client
		if (cs->status == STATUS_ACTIVE) {
			// 1 lag-point per day
			int lag = NetworkCalculateLag(cs) / DAY_TICKS;
			if (lag > 0) {
				if (lag > 3) {
					// Client did still not report in after 4 game-day, drop him
					//  (that is, the 3 of above, + 1 before any lag is counted)
					IConsolePrintF(_icolour_err,"Client #%d is dropped because the client did not respond for more than 4 game-days", cs->index);
					NetworkCloseClient(cs);
					continue;
				}

				// Report once per time we detect the lag
				if (cs->lag_test == 0) {
					IConsolePrintF(_icolour_warn,"[%d] Client #%d is slow, try increasing *net_frame_freq to a higher value!", _frame_counter, cs->index);
					cs->lag_test = 1;
				}
			} else {
				cs->lag_test = 0;
			}
		} else if (cs->status == STATUS_PRE_ACTIVE) {
			int lag = NetworkCalculateLag(cs);
			if (lag > _network_max_join_time) {
				IConsolePrintF(_icolour_err,"Client #%d is dropped because it took longer than %d ticks for him to join", cs->index, _network_max_join_time);
				NetworkCloseClient(cs);
			}
		}

		if (cs->status >= STATUS_PRE_ACTIVE) {
			// Check if we can send command, and if we have anything in the queue
			NetworkHandleCommandQueue(cs);

			// Send an updated _frame_counter_max to the client
			if (send_frame)
				SEND_COMMAND(PACKET_SERVER_FRAME)(cs);

#ifndef ENABLE_NETWORK_SYNC_EVERY_FRAME
			// Send a sync-check packet
			if (send_sync)
				SEND_COMMAND(PACKET_SERVER_SYNC)(cs);
#endif
		}
	}

	/* See if we need to advertise */
	NetworkUDPAdvertise();
}

void NetworkServerYearlyLoop(void)
{
	NetworkCheckRestartMap();
}

void NetworkServerMonthlyLoop(void)
{
	NetworkAutoCleanCompanies();
}

#endif /* ENABLE_NETWORK */