Changeset - r716:ae3edbaa50e7
[Not reviewed]
master
0 15 0
truelight - 20 years ago 2004-12-19 10:17:26
truelight@openttd.org
(svn r1168) -Cleanup: [Network] Cleaned the network code a bit. Added 'const'
and 'void' where needed, prefixed all functions, typedefs and global
vars with 'Network' and organized all externals nicely.
15 files changed with 118 insertions and 116 deletions:
0 comments (0 inline, 0 general)
console_cmds.c
Show inline comments
 
@@ -102,167 +102,167 @@ DEF_CONSOLE_CMD_HOOK(ConCmdHookNeedNetwo
 
	}
 
	return true;
 
}
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 
/* **************************** */
 
/* reset commands               */
 
/* **************************** */
 

	
 
DEF_CONSOLE_CMD(ConResetEngines)
 
{
 
	StartupEngines();
 
	return 0;
 
}
 

	
 
#ifdef _DEBUG
 
DEF_CONSOLE_CMD(ConResetTile)
 
{
 
	if (argc == 2) {
 
		TileIndex tile = (TileIndex)GetArgumentInteger(argv[1]);
 
		DoClearSquare(tile);
 
	}
 

	
 
	return 0;
 
}
 
#endif
 

	
 
DEF_CONSOLE_CMD(ConScrollToTile)
 
{
 
	if (argc == 2) {
 
		TileIndex tile = (TileIndex)GetArgumentInteger(argv[1]);
 
		ScrollMainWindowToTile(tile);
 
	}
 

	
 
	return 0;
 
}
 

	
 

	
 
// ********************************* //
 
// * Network Core Console Commands * //
 
// ********************************* //
 
#ifdef ENABLE_NETWORK
 

	
 
DEF_CONSOLE_CMD(ConStatus)
 
{
 
	const char *status;
 
	int lag;
 
	const ClientState *cs;
 
	const NetworkClientState *cs;
 
	const NetworkClientInfo *ci;
 
	FOR_ALL_CLIENTS(cs) {
 
		lag = NetworkCalculateLag(cs);
 
		ci = DEREF_CLIENT_INFO(cs);
 

	
 
		switch (cs->status) {
 
			case STATUS_INACTIVE:
 
				status = "inactive";
 
				break;
 
			case STATUS_AUTH:
 
				status = "authorized";
 
				break;
 
			case STATUS_MAP:
 
				status = "loading map";
 
				break;
 
			case STATUS_DONE_MAP:
 
				status = "done map";
 
				break;
 
			case STATUS_PRE_ACTIVE:
 
				status = "ready";
 
				break;
 
			case STATUS_ACTIVE:
 
				status = "active";
 
				break;
 
			default:
 
				status = "unknown";
 
				break;
 
		}
 
		IConsolePrintF(8, "Client #%d/%s  status: %s  frame-lag: %d  play-as: %d  unique-id: %s",
 
			cs->index, ci->client_name, status, lag, ci->client_playas, ci->unique_id);
 
	}
 

	
 
	return NULL;
 
}
 

	
 
DEF_CONSOLE_CMD(ConKick)
 
{
 
	NetworkClientInfo *ci;
 

	
 
	if (argc == 2) {
 
		uint32 index = atoi(argv[1]);
 
		if (index == NETWORK_SERVER_INDEX) {
 
			IConsolePrint(_iconsole_color_default, "Silly boy, you can not kick yourself!");
 
			return NULL;
 
		}
 
		if (index == 0) {
 
			IConsoleError("Invalid Client-ID");
 
			return NULL;
 
		}
 

	
 
		ci = NetworkFindClientInfoFromIndex(index);
 

	
 
		if (ci != NULL) {
 
			SEND_COMMAND(PACKET_SERVER_ERROR)(NetworkFindClientStateFromIndex(index), NETWORK_ERROR_KICKED);
 
			return NULL;
 
		} else {
 
			IConsoleError("Client-ID not found");
 
			return NULL;
 
		}
 
	}
 

	
 
	IConsolePrint(_iconsole_color_default, "Unknown usage. Usage: kick <client-id>. For client-ids, see 'clients'.");
 

	
 
	return NULL;
 
}
 

	
 
DEF_CONSOLE_CMD(ConResetCompany)
 
{
 
	Player *p;
 
	ClientState *cs;
 
	NetworkClientState *cs;
 
	NetworkClientInfo *ci;
 

	
 
	if (argc == 2) {
 
		uint32 index = atoi(argv[1]);
 

	
 
		/* Check valid range */
 
		if (index < 1 || index > MAX_PLAYERS) {
 
			IConsolePrintF(_iconsole_color_error, "Company does not exist. Company-ID must be between 1 and %d.", MAX_PLAYERS);
 
			return NULL;
 
		}
 

	
 
		/* Check if company does exist */
 
		index--;
 
		p = DEREF_PLAYER(index);
 
		if (!p->is_active) {
 
			IConsolePrintF(_iconsole_color_error, "Company does not exist.");
 
			return NULL;
 
		}
 

	
 
		if (p->is_ai) {
 
			IConsolePrintF(_iconsole_color_error, "Company is owned by an AI.");
 
			return NULL;
 
		}
 

	
 
		/* Check if the company has active players */
 
		FOR_ALL_CLIENTS(cs) {
 
			ci = DEREF_CLIENT_INFO(cs);
 
			if (ci->client_playas-1 == index) {
 
				IConsolePrintF(_iconsole_color_error, "Cannot remove company: a client is connected to that company.");
 
				return NULL;
 
			}
 
		}
 
		ci = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX);
 
		if (ci->client_playas-1 == index) {
 
			IConsolePrintF(_iconsole_color_error, "Cannot remove company: a client is connected to that company.");
 
			return NULL;
 
		}
 

	
 
		/* It is safe to remove this company */
 
		DoCommandP(0, 2, index, NULL, CMD_PLAYER_CTRL);
 
		IConsolePrint(_iconsole_color_default, "Company deleted.");
 
		return NULL;
 
	}
 

	
 
	IConsolePrint(_iconsole_color_default, "Unknown usage. Usage: reset_company <company-id>.");
 

	
 
	return NULL;
 
}
economy.c
Show inline comments
 
@@ -357,97 +357,97 @@ static void PlayersCheckBankrupt(Player 
 
	p->quarters_of_bankrupcy++;
 

	
 
	owner = p->index;
 

	
 
	switch (p->quarters_of_bankrupcy) {
 
		case 2:
 
			AddNewsItem( (StringID)(owner + 16),
 
				NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
 
			break;
 
		case 3: {
 
			/* XXX - In multiplayer, should we ask other players if it wants to take
 
		          over when it is a human company? -- TrueLight */
 
			if (IS_HUMAN_PLAYER(owner)) {
 
				AddNewsItem( (StringID)(owner + 16),
 
					NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
 
				break;
 
			}
 

	
 
			// Check if the company has any value.. if not, declare it bankrupt
 
			//  right now
 
			val = CalculateCompanyValue(p);
 
			if (val > 0) {
 
				p->bankrupt_value = val;
 
				p->bankrupt_asked = 1 << owner; // Don't ask the owner
 
				p->bankrupt_timeout = 0;
 
				break;
 
			}
 
			// Else, falltrue to case 4...
 
		}
 
		case 4: {
 
			// Close everything the owner has open
 
			DeletePlayerWindows(owner);
 

	
 
//		Show bankrupt news
 
			SetDParam(0, p->name_1);
 
			SetDParam(1, p->name_2);
 
			AddNewsItem( (StringID)(owner + 16*3), NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
 

	
 
			// If the player is human, and it is no network play, leave the player playing
 
			if (IS_HUMAN_PLAYER(owner) && !_networking) {
 
				p->bankrupt_asked = 255;
 
				p->bankrupt_timeout = 0x456;
 
			} else {
 
#ifdef ENABLE_NETWORK
 
				if (IS_HUMAN_PLAYER(owner) && _network_server) {
 
					// If we are the server, make sure it is clear that his player is no
 
					//  longer with us!
 
					NetworkClientInfo *ci;
 
					ClientState *cs;
 
					NetworkClientState *cs;
 
					/* Find all clients that were in control of this company */
 
					FOR_ALL_CLIENTS(cs) {
 
						ci = DEREF_CLIENT_INFO(cs);
 
						if ((ci->client_playas-1) == owner) {
 
							ci->client_playas = OWNER_SPECTATOR;
 
							// Send the new info to all the clients
 
							NetworkUpdateClientInfo(_network_own_client_index);
 
						}
 
					}
 
				}
 
				// Make sure the player no longer controls the company
 
				if (IS_HUMAN_PLAYER(owner) && owner == _local_player) {
 
					// Switch the player to spectator..
 
					_local_player = OWNER_SPECTATOR;
 
				}
 
#endif /* ENABLE_NETWORK */
 

	
 
				// Convert everything the player owns to NO_OWNER
 
				p->money64 = p->player_money = 100000000;
 
				ChangeOwnershipOfPlayerItems(owner, 0xFF); // 255 is no owner
 
				// Register the player as not-active
 
				p->is_active = false;
 
			}
 
		}
 
	}
 
}
 

	
 
void DrawNewsBankrupcy(Window *w)
 
{
 
	Player *p;
 

	
 
	DrawNewsBorder(w);
 

	
 
	p = DEREF_PLAYER(WP(w,news_d).ni->string_id & 15);
 
	DrawPlayerFace(p->face, p->player_color, 2, 23);
 
	GfxFillRect(3, 23, 3+91, 23+118, 0x4323);
 

	
 
	SetDParam(0, p->president_name_1);
 
	SetDParam(1, p->president_name_2);
 

	
 
	DrawStringMultiCenter(49, 148, STR_7058_PRESIDENT, 94);
 

	
 
	switch(WP(w,news_d).ni->string_id >> 4) {
 
	case 1:
 
		DrawStringCentered(w->width>>1, 1, STR_7056_TRANSPORT_COMPANY_IN_TROUBLE, 0);
 

	
 
		SetDParam(0, p->name_1);
 
		SetDParam(1, p->name_2);
network.c
Show inline comments
 
#include "stdafx.h"
 
#include "map.h"
 
#include "network_data.h"
 

	
 
#if defined(WITH_REV)
 
	extern const char _openttd_revision[];
 
#elif defined(WITH_REV_HACK)
 
	#define WITH_REV
 
	const char _openttd_revision[] = WITH_REV_HACK;
 
#else
 
	const char _openttd_revision[] = NOREV_STRING;
 
#endif
 

	
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "table/strings.h"
 
#include "network_client.h"
 
#include "network_server.h"
 
#include "network_udp.h"
 
#include "network_gamelist.h"
 
#include "console.h" /* IConsoleCmdExec */
 
#include <stdarg.h> /* va_list */
 
#include "md5.h"
 

	
 
// The listen socket for the server
 
static SOCKET _listensocket;
 

	
 
// Network copy of patches, so the patches of a client are not fucked up
 
//  after he joined a server
 
static Patches network_tmp_patches;
 

	
 
// The amount of clients connected
 
static byte _network_clients_connected = 0;
 
// The index counter for new clients (is never decreased)
 
static uint16 _network_client_index = NETWORK_SERVER_INDEX + 1;
 

	
 
/* Some externs / forwards */
 
extern void ShowJoinStatusWindow();
 
extern void StateGameLoop();
 

	
 
// Function that looks up the CI for a given client-index
 
NetworkClientInfo *NetworkFindClientInfoFromIndex(uint16 client_index)
 
{
 
	NetworkClientInfo *ci;
 

	
 
	for (ci = _network_client_info; ci != &_network_client_info[MAX_CLIENT_INFO]; ci++)
 
		if (ci->client_index == client_index)
 
			return ci;
 

	
 
	return NULL;
 
}
 

	
 
// Function that looks up the CS for a given client-index
 
ClientState *NetworkFindClientStateFromIndex(uint16 client_index)
 
NetworkClientState *NetworkFindClientStateFromIndex(uint16 client_index)
 
{
 
	ClientState *cs;
 
	NetworkClientState *cs;
 

	
 
	for (cs = _clients; cs != &_clients[MAX_CLIENT_INFO]; cs++)
 
		if (cs->index == client_index)
 
			return cs;
 

	
 
	return NULL;
 
}
 

	
 
// NetworkGetClientName is a server-safe function to get the name of the client
 
//  if the user did not send it yet, Client #<no> is used.
 
void NetworkGetClientName(char *client_name, size_t size, ClientState *cs)
 
void NetworkGetClientName(char *client_name, size_t size, const NetworkClientState *cs)
 
{
 
	NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs);
 
	if (ci->client_name[0] == '\0')
 
		snprintf(client_name, size, "Client #%d", cs->index);
 
	else
 
		snprintf(client_name, size, "%s", ci->client_name);
 
}
 

	
 
// This puts a text-message to the console, or in the future, the chat-box,
 
//  (to keep it all a bit more general)
 
void CDECL NetworkTextMessage(NetworkAction action, uint16 color, const char *name, const char *str, ...)
 
{
 
	char buf[1024];
 
	va_list va;
 
	const int duration = 10; // Game days the messages stay visible
 

	
 
	va_start(va, str);
 
	vsprintf(buf, str, va);
 
	va_end(va);
 

	
 
	switch (action) {
 
		case NETWORK_ACTION_JOIN_LEAVE:
 
			IConsolePrintF(color, "*** %s %s", name, buf);
 
			AddTextMessage(color, duration, "*** %s %s", name, buf);
 
			break;
 
		case NETWORK_ACTION_GIVE_MONEY:
 
			IConsolePrintF(color, "*** %s %s", name, buf);
 
			AddTextMessage(color, duration, "*** %s %s", name, buf);
 
			break;
 
		case NETWORK_ACTION_CHAT_PLAYER:
 
			IConsolePrintF(color, "[Team] %s: %s", name, buf);
 
			AddTextMessage(color, duration, "[Team] %s: %s", name, buf);
 
			break;
 
		case NETWORK_ACTION_CHAT_CLIENT:
 
			IConsolePrintF(color, "[Private] %s: %s", name, buf);
 
			AddTextMessage(color, duration, "[Private] %s: %s", name, buf);
 
			break;
 
		case NETWORK_ACTION_CHAT_TO_CLIENT:
 
			IConsolePrintF(color, "[Private] To %s: %s", name, buf);
 
			AddTextMessage(color, duration, "[Private] To %s: %s", name, buf);
 
			break;
 
		case NETWORK_ACTION_CHAT_TO_PLAYER:
 
			IConsolePrintF(color, "[Team] To %s: %s", name, buf);
 
			AddTextMessage(color, duration, "[Team] To %s: %s", name, buf);
 
			break;
 
		case NETWORK_ACTION_NAME_CHANGE:
 
			IConsolePrintF(color, "*** %s changed his name to %s", name, buf);
 
			AddTextMessage(color, duration, "*** %s changed his name to %s", name, buf);
 
			break;
 
		default:
 
			IConsolePrintF(color, "[All] %s: %s", name, buf);
 
			AddTextMessage(color, duration, "[All] %s: %s", name, buf);
 
			break;
 
	}
 
}
 

	
 
// Calculate the frame-lag of a client
 
uint NetworkCalculateLag(const ClientState *cs)
 
uint NetworkCalculateLag(const NetworkClientState *cs)
 
{
 
	int lag = cs->last_frame_server - cs->last_frame;
 
	// This client has missed his ACK packet after 1 DAY_TICKS..
 
	//  so we increase his lag for every frame that passes!
 
	// The packet can be out by a max of _net_frame_freq
 
	if (cs->last_frame_server + DAY_TICKS + _network_frame_freq < _frame_counter)
 
		lag += _frame_counter - (cs->last_frame_server + DAY_TICKS + _network_frame_freq);
 

	
 
	return lag;
 
}
 

	
 

	
 
// There was a non-recoverable error, drop back to the main menu with a nice
 
//  error
 
void NetworkError(StringID error_string)
 
{
 
	_switch_mode = SM_MENU;
 
	_switch_mode_errorstr = error_string;
 
}
 

	
 
void ClientStartError(char *error) {
 
	DEBUG(net, 0)("[NET] Client could not start network: %s",error);
 
	NetworkError(STR_NETWORK_ERR_CLIENT_START);
 
}
 

	
 
void ServerStartError(char *error) {
 
	DEBUG(net, 0)("[NET] Server could not start network: %s",error);
 
	NetworkError(STR_NETWORK_ERR_SERVER_START);
 
}
 

	
 
void NetworkClientError(byte res, ClientState *cs) {
 
static void NetworkClientError(byte res, NetworkClientState *cs) {
 
	// First, send a CLIENT_ERROR to the server, so he knows we are
 
	//  disconnection (and why!)
 
	NetworkErrorCode errorno;
 

	
 
	// We just want to close the connection..
 
	if (res == NETWORK_RECV_STATUS_CLOSE_QUERY) {
 
		cs->quited = true;
 
		CloseClient(cs);
 
		NetworkCloseClient(cs);
 
		_networking = false;
 

	
 
		DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 
		return;
 
	}
 

	
 
	switch(res) {
 
		case NETWORK_RECV_STATUS_DESYNC: errorno = NETWORK_ERROR_DESYNC; break;
 
		case NETWORK_RECV_STATUS_SAVEGAME: errorno = NETWORK_ERROR_SAVEGAME_FAILED; break;
 
		default: errorno = NETWORK_ERROR_GENERAL;
 
	}
 
	// This means we fucked up and the server closed the connection
 
	if (res != NETWORK_RECV_STATUS_SERVER_ERROR && res != NETWORK_RECV_STATUS_SERVER_FULL) {
 
		SEND_COMMAND(PACKET_CLIENT_ERROR)(errorno);
 

	
 
		// Dequeue all commands before closing the socket
 
		NetworkSend_Packets(DEREF_CLIENT(0));
 
	}
 

	
 
	_switch_mode = SM_MENU;
 
	CloseClient(cs);
 
	NetworkCloseClient(cs);
 
	_networking = false;
 
}
 

	
 
// Find all IP-aliases for this host
 
void NetworkFindIPs(void)
 
static void NetworkFindIPs(void)
 
{
 
	int i, last;
 

	
 
#if defined(BEOS_NET_SERVER) /* doesn't have neither getifaddrs or net/if.h */
 
	/* Based on Andrew Bachmann's netstat+.c. Big thanks to him! */
 
	int _netstat(int fd, char **output, int verbose);
 

	
 
	int seek_past_header(char **pos, const char *header) {
 
		char *new_pos = strstr(*pos, header);
 
		if (new_pos == 0) {
 
			return B_ERROR;
 
		}
 
		*pos += strlen(header) + new_pos - *pos + 1;
 
		return B_OK;
 
	}
 

	
 
	int output_length;
 
	char *output_pointer = NULL;
 
	char **output;
 
	int sock = socket(AF_INET, SOCK_DGRAM, 0);
 
	i = 0;
 

	
 
	// If something fails, make sure the list is empty
 
	_network_ip_list[0] = 0;
 

	
 
	if (sock < 0) {
 
		DEBUG(net, 0)("Error creating socket!");
 
		return;
 
	}
 

	
 
	output_length = _netstat(sock, &output_pointer, 1);
 
	if (output_length < 0) {
 
		DEBUG(net, 0)("Error running _netstat!");
 
		return;
 
	}
 

	
 
	output = &output_pointer;
 
	if (seek_past_header(output, "IP Interfaces:") == B_OK) {
 
		for (;;) {
 
			uint32 n, fields, read;
 
			uint8 i1, i2, i3, i4, j1, j2, j3, j4;
 
			struct in_addr inaddr;
 
			fields = sscanf(*output, "%u: %hhu.%hhu.%hhu.%hhu, netmask %hhu.%hhu.%hhu.%hhu%n",
 
												&n, &i1,&i2,&i3,&i4, &j1,&j2,&j3,&j4, &read);
 
			read += 1;
 
			if (fields != 9) {
 
				break;
 
			}
 
@@ -343,877 +347,875 @@ void NetworkFindIPs(void)
 
		i++;
 
	}
 
}
 

	
 
// Resolve a hostname to a inet_addr
 
unsigned long NetworkResolveHost(const char *hostname)
 
{
 
	in_addr_t ip;
 

	
 
	// First try: is it an ip address?
 
	ip = inet_addr(hostname);
 

	
 
	// If not try to resolve the name
 
	if (ip == INADDR_NONE) {
 
		struct hostent *he = gethostbyname(hostname);
 
		if (he == NULL) {
 
			DEBUG(net, 0) ("[NET] Cannot resolve %s", hostname);
 
		} else {
 
			struct in_addr addr = *(struct in_addr *)he->h_addr_list[0];
 
			DEBUG(net, 1) ("[NET] Resolved %s to %s", hostname, inet_ntoa(addr));
 
			ip = addr.s_addr;
 
		}
 
	}
 
	return ip;
 
}
 

	
 
// Converts a string to ip/port/player
 
//  Format: IP#player:port
 
//
 
// connection_string will be re-terminated to seperate out the hostname, and player and port will
 
// be set to the player and port strings given by the user, inside the memory area originally
 
// occupied by connection_string.
 
void ParseConnectionString(const byte **player, const byte **port, byte *connection_string)
 
{
 
	byte *p;
 
	for (p = connection_string; *p != '\0'; p++) {
 
		if (*p == '#') {
 
			*player = p + 1;
 
			*p = '\0';
 
		} else if (*p == ':') {
 
			*port = p + 1;
 
			*p = '\0';
 
		}
 
	}
 
}
 

	
 
// Creates a new client from a socket
 
//   Used both by the server and the client
 
static ClientState *AllocClient(SOCKET s)
 
static NetworkClientState *NetworkAllocClient(SOCKET s)
 
{
 
	ClientState *cs;
 
	NetworkClientState *cs;
 
	NetworkClientInfo *ci;
 
	byte client_no;
 

	
 
	client_no = 0;
 

	
 
	if (_network_server) {
 
		// Can we handle a new client?
 
		if (_network_clients_connected >= MAX_CLIENTS)
 
			return NULL;
 

	
 
		if (_network_game_info.clients_on >= _network_game_info.clients_max)
 
			return NULL;
 

	
 
		// Register the login
 
		client_no = _network_clients_connected++;
 
	}
 

	
 
	cs = &_clients[client_no];
 
	memset(cs, 0, sizeof(*cs));
 
	cs->socket = s;
 
	cs->last_frame = 0;
 
	cs->quited = false;
 

	
 
	if (_network_server) {
 
		ci = DEREF_CLIENT_INFO(cs);
 
		memset(ci, 0, sizeof(*ci));
 

	
 
		cs->index = _network_client_index++;
 
		ci->client_index = cs->index;
 
		ci->join_date = _date;
 

	
 
		InvalidateWindow(WC_CLIENT_LIST, 0);
 
	}
 

	
 
	return cs;
 
}
 

	
 
// Close a connection
 
void CloseClient(ClientState *cs)
 
void NetworkCloseClient(NetworkClientState *cs)
 
{
 
	NetworkClientInfo *ci;
 
	// Socket is already dead
 
	if (cs->socket == INVALID_SOCKET) return;
 

	
 
	DEBUG(net, 1) ("[NET] Closed client connection");
 

	
 
	if (!cs->quited && _network_server && cs->status > STATUS_INACTIVE) {
 
		// We did not receive a leave message from this client...
 
		NetworkErrorCode errorno = NETWORK_ERROR_CONNECTION_LOST;
 
		char str1[100], str2[100];
 
		char client_name[NETWORK_NAME_LENGTH];
 
		ClientState *new_cs;
 
		NetworkClientState *new_cs;
 

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

	
 
		GetString(str1, STR_NETWORK_ERR_LEFT);
 
		GetString(str2, STR_NETWORK_ERR_CLIENT_GENERAL + errorno);
 

	
 
		NetworkTextMessage(NETWORK_ACTION_JOIN_LEAVE, 1, client_name, "%s (%s)", str1, str2);
 

	
 
		// Inform other clients of this... strange leaving ;)
 
		FOR_ALL_CLIENTS(new_cs) {
 
			if (new_cs->status > STATUS_AUTH && cs != new_cs) {
 
				SEND_COMMAND(PACKET_SERVER_ERROR_QUIT)(new_cs, cs->index, errorno);
 
			}
 
		}
 
	}
 

	
 
	closesocket(cs->socket);
 
	cs->writable = false;
 

	
 
	// Free all pending and partially received packets
 
	while (cs->packet_queue != NULL) {
 
		Packet *p = cs->packet_queue->next;
 
		free(cs->packet_queue);
 
		cs->packet_queue = p;
 
	}
 
	free(cs->packet_recv);
 
	cs->packet_recv = NULL;
 

	
 
	while (cs->command_queue != NULL) {
 
		CommandPacket *p = cs->command_queue->next;
 
		free(cs->command_queue);
 
		cs->command_queue = p;
 
	}
 

	
 
	// Close the gap in the client-list
 
	ci = DEREF_CLIENT_INFO(cs);
 

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

	
 
		while ((cs + 1) != DEREF_CLIENT(MAX_CLIENTS) && (cs + 1)->socket != INVALID_SOCKET) {
 
			*cs = *(cs + 1);
 
			*ci = *(ci + 1);
 
			cs++;
 
			ci++;
 
		}
 

	
 
		InvalidateWindow(WC_CLIENT_LIST, 0);
 
	}
 

	
 
	// Reset the status of the last socket
 
	cs->socket = INVALID_SOCKET;
 
	cs->status = STATUS_INACTIVE;
 
	cs->index = NETWORK_EMPTY_INDEX;
 
	ci->client_index = NETWORK_EMPTY_INDEX;
 
}
 

	
 
extern void ShowJoinStatusWindow();
 

	
 
// A client wants to connect to a server
 
bool NetworkConnect(const char *hostname, int port)
 
static bool NetworkConnect(const char *hostname, int port)
 
{
 
	SOCKET s;
 
	struct sockaddr_in sin;
 

	
 
	DEBUG(net, 1) ("[NET] Connecting to %s %d", hostname, port);
 

	
 
	s = socket(AF_INET, SOCK_STREAM, 0);
 
	if (s == INVALID_SOCKET) {
 
		ClientStartError("socket() failed");
 
		return false;
 
	}
 

	
 
	{ // set nodelay /* XXX should this be done at all? */
 
		#if !defined(BEOS_NET_SERVER) // not implemented on BeOS net_server...
 
		int b = 1;
 
		// The (const char*) cast is needed for windows!!
 
		if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (const char*)&b, sizeof(b)) != 0)
 
			DEBUG(net, 1)("[NET] Setting TCP_NODELAY failed");
 
		#endif
 
	}
 

	
 
	sin.sin_family = AF_INET;
 
	sin.sin_addr.s_addr = NetworkResolveHost(hostname);
 
	sin.sin_port = htons(port);
 
	_network_last_host_ip = sin.sin_addr.s_addr;
 

	
 
	if (connect(s, (struct sockaddr*) &sin, sizeof(sin)) != 0) {
 
		// We failed to connect for which reason what so ever
 
		return false;
 
	}
 

	
 
	{ // set nonblocking mode for socket..
 
		unsigned long blocking = 1;
 
		#if defined(__BEOS__) && defined(BEOS_NET_SERVER)
 
		byte nonblocking = 1;
 
		if (setsockopt(s, SOL_SOCKET, SO_NONBLOCK, &nonblocking, sizeof(blocking)) != 0)
 
		#else
 
		if (ioctlsocket(s, FIONBIO, &blocking) != 0)
 
		#endif
 
			DEBUG(net, 0)("[NET] Setting non-blocking failed"); /* XXX should this be an error? */
 
	}
 

	
 
	// in client mode, only the first client field is used. it's pointing to the server.
 
	AllocClient(s);
 
	NetworkAllocClient(s);
 

	
 
	ShowJoinStatusWindow();
 

	
 
	memcpy(&network_tmp_patches, &_patches, sizeof(_patches));
 

	
 
	return true;
 
}
 

	
 
// For the server, to accept new clients
 
static void NetworkAcceptClients(void)
 
{
 
	struct sockaddr_in sin;
 
	SOCKET s;
 
	ClientState *cs;
 
	NetworkClientState *cs;
 
#ifndef __MORPHOS__
 
	int sin_len;
 
#else
 
	LONG sin_len; // for some reason we need a 'LONG' under MorphOS
 
#endif
 

	
 
	// Should never ever happen.. is it possible??
 
	assert(_listensocket != INVALID_SOCKET);
 

	
 
	for (;;) {
 
		sin_len = sizeof(sin);
 
		s = accept(_listensocket, (struct sockaddr*)&sin, &sin_len);
 
		if (s == INVALID_SOCKET) return;
 

	
 
		// set nonblocking mode for client socket
 
		#if defined(__BEOS__) && defined(BEOS_NET_SERVER)
 
		{ unsigned long blocking = 1; byte nonblocking = 1; setsockopt(s, SOL_SOCKET, SO_NONBLOCK, &nonblocking, sizeof(blocking)); }
 
		#else
 
		{ unsigned long blocking = 1; ioctlsocket(s, FIONBIO, &blocking); }
 
		#endif
 

	
 
		DEBUG(net, 1) ("[NET] Client connected from %s on frame %d", inet_ntoa(sin.sin_addr), _frame_counter);
 

	
 
		// set nodelay
 
		#if !defined(BEOS_NET_SERVER) // not implemented on BeOS net_server...
 
		// The (const char*) cast is needed for windows!!
 
		{int b = 1; setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (const char*)&b, sizeof(b));}
 
		#endif
 

	
 
		cs = AllocClient(s);
 
		cs = NetworkAllocClient(s);
 
		if (cs == NULL) {
 
			// no more clients allowed?
 
			// Send to the client that we are full!
 
			Packet *p = NetworkSend_Init(PACKET_SERVER_FULL);
 

	
 
			p->buffer[0] = p->size & 0xFF;
 
			p->buffer[1] = p->size >> 8;
 

	
 
			send(s, p->buffer, p->size, 0);
 
			closesocket(s);
 

	
 
			free(p);
 

	
 
			continue;
 
		}
 

	
 
		// a new client has connected. We set him at inactive for now
 
		//  maybe he is only requesting server-info. Till he has sent a PACKET_CLIENT_MAP_OK
 
		//  the client stays inactive
 
		cs->status = STATUS_INACTIVE;
 

	
 
		{
 
			// Save the IP of the client
 
			NetworkClientInfo *ci;
 
			ci = DEREF_CLIENT_INFO(cs);
 
			ci->client_ip = sin.sin_addr.s_addr;
 
		}
 
	}
 
}
 

	
 
// Set up the listen socket for the server
 
bool NetworkListen(void)
 
static bool NetworkListen(void)
 
{
 
	SOCKET ls;
 
	struct sockaddr_in sin;
 
	int port;
 

	
 
	port = _network_server_port;
 

	
 
	DEBUG(net, 1) ("[NET] Listening on %s:%d", _network_server_bind_ip_host, port);
 

	
 
	ls = socket(AF_INET, SOCK_STREAM, 0);
 
	if (ls == INVALID_SOCKET) {
 
		ServerStartError("socket() on listen socket failed");
 
		return false;
 
	}
 

	
 
	{ // reuse the socket
 
		int reuse = 1;
 
		// The (const char*) cast is needed for windows!!
 
		if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) == -1) {
 
			ServerStartError("setsockopt() on listen socket failed");
 
			return false;
 
		}
 
	}
 

	
 
	{ // set nonblocking mode for socket
 
		unsigned long blocking = 1;
 
		#if defined(__BEOS__) && defined(BEOS_NET_SERVER)
 
		byte nonblocking = 1;
 
		if (setsockopt(ls, SOL_SOCKET, SO_NONBLOCK, &nonblocking, sizeof(blocking)) != 0)
 
		#else
 
		if (ioctlsocket(ls, FIONBIO, &blocking) != 0)
 
		#endif
 
			DEBUG(net, 0)("[NET] Setting non-blocking failed"); /* XXX should this be an error? */
 
	}
 

	
 
	sin.sin_family = AF_INET;
 
	sin.sin_addr.s_addr = _network_server_bind_ip;
 
	sin.sin_port = htons(port);
 

	
 
	if (bind(ls, (struct sockaddr*)&sin, sizeof(sin)) != 0) {
 
		ServerStartError("bind() failed");
 
		return false;
 
	}
 

	
 
	if (listen(ls, 1) != 0) {
 
		ServerStartError("listen() failed");
 
		return false;
 
	}
 

	
 
	_listensocket = ls;
 

	
 
	return true;
 
}
 

	
 
// Close all current connections
 
void NetworkClose(void)
 
static void NetworkClose(void)
 
{
 
	ClientState *cs;
 
	NetworkClientState *cs;
 

	
 
	FOR_ALL_CLIENTS(cs) {
 
		if (!_network_server) {
 
			SEND_COMMAND(PACKET_CLIENT_QUIT)("leaving");
 
			NetworkSend_Packets(cs);
 
		}
 
		CloseClient(cs);
 
		NetworkCloseClient(cs);
 
	}
 

	
 
	if (_network_server) {
 
		// We are a server, also close the listensocket
 
		closesocket(_listensocket);
 
		_listensocket = INVALID_SOCKET;
 
		DEBUG(net, 1) ("[NET] Closed listener");
 
		NetworkUDPClose();
 
	}
 
}
 

	
 
// Inits the network (cleans sockets and stuff)
 
void NetworkInitialize(void)
 
static void NetworkInitialize(void)
 
{
 
	ClientState *cs;
 
	NetworkClientState *cs;
 
	uint i;
 

	
 
	_local_command_queue = NULL;
 

	
 
	// Clean all client-sockets
 
	memset(_clients, 0, sizeof(_clients));
 
	for (cs = _clients; cs != &_clients[MAX_CLIENTS]; cs++) {
 
		cs->socket = INVALID_SOCKET;
 
		cs->status = STATUS_INACTIVE;
 
		cs->command_queue = NULL;
 
	}
 

	
 
	// Clean the client_info memory
 
	memset(_network_client_info, 0, sizeof(_network_client_info));
 
	memset(_network_player_info, 0, sizeof(_network_player_info));
 

	
 
	_sync_frame = 0;
 
	_network_first_time = true;
 

	
 
	_network_reconnect = 0;
 

	
 
	InitPlayerRandoms();
 

	
 
	NetworkUDPInitialize();
 

	
 
	// add all servers from the config file to our list
 
	for (i=0; i != lengthof(_network_server_list); i++) {
 
		if (_network_server_list[i] == NULL) break;
 
		AddServer(_network_server_list[i]);
 
		NetworkAddServer(_network_server_list[i]);
 
	}
 
}
 

	
 
// Query a server to fetch his game-info
 
//  If game_info is true, only the gameinfo is fetched,
 
//   else only the client_info is fetched
 
void NetworkQueryServer(const byte* host, unsigned short port, bool game_info)
 
{
 
	if (!_network_available) return;
 

	
 
	NetworkDisconnect();
 

	
 
	if (game_info) {
 
		NetworkUDPQueryServer(host, port);
 
		return;
 
	}
 

	
 
	NetworkInitialize();
 

	
 
	_network_server = false;
 

	
 
	// Try to connect
 
	_networking = NetworkConnect(host, port);
 

	
 
//	ttd_strlcpy(_network_last_host, host, sizeof(_network_last_host));
 
//	_network_last_port = port;
 

	
 
	// We are connected
 
	if (_networking) {
 
		SEND_COMMAND(PACKET_CLIENT_COMPANY_INFO)();
 
		return;
 
	}
 

	
 
	// No networking, close everything down again
 
	NetworkDisconnect();
 
}
 

	
 
// validates an address entered as a string and adds the server to
 
// the list
 
void AddServer(byte *b)
 
void NetworkAddServer(const byte *b)
 
{
 
	if (*b != '\0') {
 
		const byte *port = NULL;
 
		const byte *player = NULL;
 
		byte host[NETWORK_HOSTNAME_LENGTH];
 
		uint16 rport;
 

	
 
		ttd_strlcpy(host, b, lengthof(host));
 

	
 
		ttd_strlcpy(_network_default_ip, b, lengthof(_network_default_ip));
 
		rport = NETWORK_DEFAULT_PORT;
 

	
 
		ParseConnectionString(&player, &port, b);
 
		ParseConnectionString(&player, &port, host);
 

	
 
		if (player != NULL) _network_playas = atoi(player);
 
		if (port != NULL) rport = atoi(port);
 

	
 
		NetworkQueryServer(b, rport, true);
 
		NetworkQueryServer(host, rport, true);
 
	}
 
}
 

	
 
// Used by clients, to connect to a server
 
bool NetworkClientConnectGame(const byte* host, unsigned short port)
 
{
 
	if (!_network_available) return false;
 

	
 
	if (port == 0) return false;
 

	
 
	ttd_strlcpy(_network_last_host, host, sizeof(_network_last_host));
 
	_network_last_port = port;
 

	
 
	NetworkDisconnect();
 
	NetworkUDPClose();
 
	NetworkInitialize();
 

	
 
	// Try to connect
 
	_networking = NetworkConnect(host, port);
 

	
 
	// We are connected
 
	if (_networking) {
 
		IConsoleCmdExec("exec scripts/on_client.scr 0");
 
		NetworkClient_Connected();
 
	} else {
 
		// Connecting failed
 
		NetworkError(STR_NETWORK_ERR_NOCONNECTION);
 
	}
 

	
 
	return _networking;
 
}
 

	
 
void NetworkInitGameInfo(void)
 
static void NetworkInitGameInfo(void)
 
{
 
	NetworkClientInfo *ci;
 

	
 
	ttd_strlcpy(_network_game_info.server_name, _network_server_name, sizeof(_network_game_info.server_name));
 
	if (_network_game_info.server_name[0] == '\0')
 
		snprintf(_network_game_info.server_name, sizeof(_network_game_info.server_name), "Unnamed Server");
 

	
 
	// The server is a client too ;)
 
	if (_network_dedicated) {
 
		_network_game_info.clients_on = 0;
 
		_network_game_info.dedicated = true;
 
	} else {
 
		_network_game_info.clients_on = 1;
 
		_network_game_info.dedicated = false;
 
	}
 
	ttd_strlcpy(_network_game_info.server_revision, _openttd_revision, sizeof(_network_game_info.server_revision));
 
	_network_game_info.spectators_on = 0;
 
	_network_game_info.game_date = _date;
 
	_network_game_info.start_date = ConvertIntDate(_patches.starting_date);
 
	_network_game_info.map_width = TILES_X;
 
	_network_game_info.map_height = TILES_Y;
 
	_network_game_info.map_set = _opt.landscape;
 

	
 
	if (_network_game_info.server_password[0] == '\0') {
 
		_network_game_info.use_password = 0;
 
	} else {
 
		_network_game_info.use_password = 1;
 
	}
 

	
 
	// We use _network_client_info[MAX_CLIENT_INFO - 1] to store the server-data in it
 
	//  The index is NETWORK_SERVER_INDEX ( = 1)
 
	ci = &_network_client_info[MAX_CLIENT_INFO - 1];
 
	memset(ci, 0, sizeof(*ci));
 

	
 
	ci->client_index = NETWORK_SERVER_INDEX;
 
	if (_network_dedicated)
 
		ci->client_playas = OWNER_SPECTATOR;
 
	else
 
		ci->client_playas = _local_player + 1;
 
	ttd_strlcpy(ci->client_name, _network_player_name, sizeof(ci->client_name));
 
	ttd_strlcpy(ci->unique_id, _network_unique_id, sizeof(ci->unique_id));
 
}
 

	
 
bool NetworkServerStart(void)
 
{
 
	if (!_network_available) return false;
 

	
 
	/* Call the pre-scripts */
 
	IConsoleCmdExec("exec scripts/pre_server.scr 0");
 
	if (_network_dedicated) IConsoleCmdExec("exec scripts/pre_dedicated.scr 0");
 

	
 
	NetworkInitialize();
 
	if (!NetworkListen())
 
		return false;
 

	
 
	// Try to start UDP-server
 
	_network_udp_server = true;
 
	_network_udp_server = NetworkUDPListen(_network_server_bind_ip, _network_server_port);
 

	
 
	_network_server = true;
 
	_networking = true;
 
	_frame_counter = 0;
 
	_frame_counter_server = 0;
 
	_frame_counter_max = 0;
 
	_network_own_client_index = NETWORK_SERVER_INDEX;
 

	
 
	if (!_network_dedicated)
 
		_network_playas = 1;
 

	
 
	_network_clients_connected = 0;
 

	
 
	NetworkInitGameInfo();
 

	
 
	// execute server initialization script
 
	IConsoleCmdExec("exec scripts/on_server.scr 0");
 
	// if the server is dedicated ... add some other script
 
	if (_network_dedicated) IConsoleCmdExec("exec scripts/on_dedicated.scr 0");
 

	
 
	/* Try to register us to the master server */
 
	_network_last_advertise_date = 0;
 
	NetworkUDPAdvertise();
 
	return true;
 
}
 

	
 
// The server is rebooting...
 
// The only difference with NetworkDisconnect, is the packets that is sent
 
void NetworkReboot(void)
 
{
 
	if (_network_server) {
 
		ClientState *cs;
 
		NetworkClientState *cs;
 
		FOR_ALL_CLIENTS(cs) {
 
			SEND_COMMAND(PACKET_SERVER_NEWGAME)(cs);
 
			NetworkSend_Packets(cs);
 
		}
 
	}
 

	
 
	NetworkClose();
 

	
 
	// Free all queued commands
 
	while (_local_command_queue != NULL) {
 
		CommandPacket *p = _local_command_queue;
 
		_local_command_queue = _local_command_queue->next;
 
		free(p);
 
	}
 

	
 
	_networking = false;
 
	_network_server = false;
 
}
 

	
 
// We want to disconnect from the host/clients
 
void NetworkDisconnect(void)
 
{
 
	if (_network_server) {
 
		ClientState *cs;
 
		NetworkClientState *cs;
 
		FOR_ALL_CLIENTS(cs) {
 
			SEND_COMMAND(PACKET_SERVER_SHUTDOWN)(cs);
 
			NetworkSend_Packets(cs);
 
		}
 
	}
 

	
 
	DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 

	
 
	NetworkClose();
 

	
 
	// Free all queued commands
 
	while (_local_command_queue != NULL) {
 
		CommandPacket *p = _local_command_queue;
 
		_local_command_queue = _local_command_queue->next;
 
		free(p);
 
	}
 

	
 
	if (_networking && !_network_server) {
 
		memcpy(&_patches, &network_tmp_patches, sizeof(_patches));
 
	}
 

	
 
	_networking = false;
 
	_network_server = false;
 
}
 

	
 
// Receives something from the network
 
bool NetworkReceive(void)
 
static bool NetworkReceive(void)
 
{
 
	ClientState *cs;
 
	NetworkClientState *cs;
 
	int n;
 
	fd_set read_fd, write_fd;
 
	struct timeval tv;
 

	
 
	FD_ZERO(&read_fd);
 
	FD_ZERO(&write_fd);
 

	
 
	FOR_ALL_CLIENTS(cs) {
 
		FD_SET(cs->socket, &read_fd);
 
		FD_SET(cs->socket, &write_fd);
 
	}
 

	
 
	// take care of listener port
 
	if (_network_server) {
 
		FD_SET(_listensocket, &read_fd);
 
	}
 

	
 
	tv.tv_sec = tv.tv_usec = 0; // don't block at all.
 
#if !defined(__MORPHOS__) && !defined(__AMIGA__)
 
	n = select(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv);
 
#else
 
	n = WaitSelect(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv, NULL);
 
#endif
 
	if (n == -1 && !_network_server) NetworkError(STR_NETWORK_ERR_LOSTCONNECTION);
 

	
 
	// accept clients..
 
	if (_network_server && FD_ISSET(_listensocket, &read_fd))
 
		NetworkAcceptClients();
 

	
 
	// read stuff from clients
 
	FOR_ALL_CLIENTS(cs) {
 
		cs->writable = !!FD_ISSET(cs->socket, &write_fd);
 
		if (FD_ISSET(cs->socket, &read_fd)) {
 
			if (_network_server)
 
				NetworkServer_ReadPackets(cs);
 
			else {
 
				byte res;
 
				// The client already was quiting!
 
				if (cs->quited) return false;
 
				if ((res = NetworkClient_ReadPackets(cs)) != NETWORK_RECV_STATUS_OKAY) {
 
					// The client made an error of which we can not recover
 
					//   close the client and drop back to main menu
 

	
 
					NetworkClientError(res, cs);
 
					return false;
 
				}
 
			}
 
		}
 
	}
 
	return true;
 
}
 

	
 
// This sends all buffered commands (if possible)
 
static void NetworkSend(void)
 
{
 
	ClientState *cs;
 
	NetworkClientState *cs;
 
	FOR_ALL_CLIENTS(cs) {
 
		if (cs->writable) {
 
			NetworkSend_Packets(cs);
 

	
 
			if (cs->status == STATUS_MAP) {
 
				// This client is in the middle of a map-send, call the function for that
 
				SEND_COMMAND(PACKET_SERVER_MAP)(cs);
 
			}
 
		}
 
	}
 
}
 

	
 
// Handle the local-command-queue
 
void NetworkHandleLocalQueue(void)
 
static void NetworkHandleLocalQueue(void)
 
{
 
	if (_local_command_queue != NULL) {
 
		CommandPacket *cp;
 
		CommandPacket *cp_prev;
 

	
 
		cp = _local_command_queue;
 
		cp_prev = NULL;
 

	
 
		while (cp != NULL) {
 
			if (_frame_counter > cp->frame) {
 
				// We can execute this command
 
				NetworkExecuteCommand(cp);
 

	
 
				if (cp_prev != NULL) {
 
					cp_prev->next = cp->next;
 
					free(cp);
 
					cp = cp_prev->next;
 
				} else {
 
					// This means we are at our first packet
 
					_local_command_queue = cp->next;
 
					free(cp);
 
					cp = _local_command_queue;
 
				}
 

	
 
			} else {
 
				// Command is in the future, skip to next
 
				//  (commands don't have to be in order in the queue!!)
 
				cp_prev = cp;
 
				cp = cp->next;
 
			}
 
		}
 
	}
 
}
 

	
 

	
 
extern void StateGameLoop();
 

	
 
bool NetworkDoClientLoop(void)
 
static bool NetworkDoClientLoop(void)
 
{
 
	_frame_counter++;
 

	
 
	NetworkHandleLocalQueue();
 

	
 
	StateGameLoop();
 

	
 
	// Check if we are in sync!
 
	if (_sync_frame != 0) {
 
		if (_sync_frame == _frame_counter) {
 
#ifdef NETWORK_SEND_DOUBLE_SEED
 
			if (_sync_seed_1 != _random_seeds[0][0] || _sync_seed_2 != _random_seeds[0][1]) {
 
#else
 
			if (_sync_seed_1 != _random_seeds[0][0]) {
 
#endif
 
				NetworkError(STR_NETWORK_ERR_DESYNC);
 
				DEBUG(net, 0)("[NET] Sync error detected!");
 
				NetworkClientError(NETWORK_RECV_STATUS_DESYNC, DEREF_CLIENT(0));
 
				return false;
 
			}
 

	
 
			// If this is the first time we have a sync-frame, we
 
			//   need to let the server know that we are ready and at the same
 
			//   frame as he is.. so we can start playing!
 
			if (_network_first_time) {
 
				_network_first_time = false;
 
				SEND_COMMAND(PACKET_CLIENT_ACK)();
 
			}
 

	
 
			_sync_frame = 0;
 
		} else if (_sync_frame < _frame_counter) {
 
			DEBUG(net, 1)("[NET] Missed frame for sync-test (%d / %d)", _sync_frame, _frame_counter);
 
			_sync_frame = 0;
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 
// We have to do some UDP checking
 
void NetworkUDPGameLoop(void)
 
{
 
	if (_network_udp_server)
 
		NetworkUDPReceive();
 
	else if (_udp_client_socket != INVALID_SOCKET) {
 
		NetworkUDPReceive();
 
		if (_network_udp_broadcast > 0)
 
			_network_udp_broadcast--;
 
	}
 
}
 

	
 
// The main loop called from ttd.c
 
//  Here we also have to do StateGameLoop if needed!
 
void NetworkGameLoop(void)
 
{
 
	if (!_networking) return;
 

	
 
	if (!NetworkReceive()) return;
 

	
 
	if (_network_server) {
 
		// We first increase the _frame_counter
 
		_frame_counter++;
 

	
 
		NetworkHandleLocalQueue();
 

	
 
		// Then we make the frame
 
		StateGameLoop();
 

	
 
		_sync_seed_1 = _random_seeds[0][0];
 
#ifdef NETWORK_SEND_DOUBLE_SEED
 
		_sync_seed_2 = _random_seeds[0][1];
 
#endif
 

	
 
		NetworkServer_Tick();
 
	} else {
 
		// Client
 

	
 
		// Make sure we are at the frame were the server is (quick-frames)
 
		if (_frame_counter_server > _frame_counter) {
 
			while (_frame_counter_server > _frame_counter) {
 
				if (!NetworkDoClientLoop()) break;
 
			}
 
		} else {
 
			// Else, keep on going till _frame_counter_max
 
			if (_frame_counter_max > _frame_counter) {
 
				NetworkDoClientLoop();
 
			}
 
		}
 
	}
 

	
 
	NetworkSend();
 
}
 

	
 
void NetworkGenerateUniqueId()
 
static void NetworkGenerateUniqueId(void)
 
{
 
	md5_state_t state;
 
	md5_byte_t digest[16];
 
	char hex_output[16*2 + 1];
 
	char coding_string[NETWORK_NAME_LENGTH];
 
	int di;
 

	
 
	snprintf(coding_string, sizeof(coding_string), "%d%s", (uint)Random(), "OpenTTD Unique ID");
 

	
 
	/* Generate the MD5 hash */
 
	md5_init(&state);
 
	md5_append(&state, coding_string, strlen(coding_string));
 
	md5_finish(&state, digest);
 

	
 
	for (di = 0; di < 16; ++di)
 
		sprintf(hex_output + di * 2, "%02x", digest[di]);
 

	
 
	/* _network_unique_id is our id */
 
	snprintf(_network_unique_id, sizeof(_network_unique_id), "%s", hex_output);
 
}
 

	
 
// This tries to launch the network for a given OS
 
void NetworkStartUp(void)
 
{
 
	DEBUG(net, 3) ("[NET][Core] Starting network...");
 
	// Network is available
 
	_network_available = true;
 
	_network_dedicated = false;
 
	_network_last_advertise_date = 0;
 

	
 
	/* Load the ip from the openttd.cfg */
 
	_network_server_bind_ip = inet_addr(_network_server_bind_ip_host);
 
	/* And put the data back in it in case it was an invalid ip */
 
	snprintf(_network_server_bind_ip_host, sizeof(_network_server_bind_ip_host), "%s", inet_ntoa(*(struct in_addr *)&_network_server_bind_ip));
 

	
 
	/* Generate an unique id when there is none yet */
 
	if (_network_unique_id[0] == '\0')
 
		NetworkGenerateUniqueId();
 

	
 
	memset(&_network_game_info, 0, sizeof(_network_game_info));
 

	
 
	/* XXX - Hard number here, because the strings can currently handle no more
 
	    then 10 clients -- TrueLight */
 
	_network_game_info.clients_max = 10;
 

	
 
	// Let's load the network in windows
 
	#if defined(WIN32)
 
	{
network.h
Show inline comments
 
@@ -148,51 +148,51 @@ VARDEF uint16 _network_lobby_company_cou
 

	
 
VARDEF uint _network_server_port;
 
/* We use bind_ip and bind_ip_host, where bind_ip_host is the readable form of
 
    bind_ip_host, and bind_ip the numeric value, because we want a nice number
 
    in the openttd.cfg, but we wants to use the uint32 internally.. */
 
VARDEF uint32 _network_server_bind_ip;
 
VARDEF char _network_server_bind_ip_host[NETWORK_HOSTNAME_LENGTH];
 
VARDEF bool _is_network_server; // Does this client wants to be a network-server?
 
VARDEF char _network_server_name[NETWORK_NAME_LENGTH];
 

	
 
VARDEF uint16 _network_sync_freq;
 
VARDEF uint8 _network_frame_freq;
 

	
 
VARDEF uint32 _sync_seed_1, _sync_seed_2;
 
VARDEF uint32 _sync_frame;
 
VARDEF bool _network_first_time;
 
// Vars needed for the join-GUI
 
VARDEF NetworkJoinStatus _network_join_status;
 
VARDEF uint8 _network_join_waiting;
 
VARDEF uint16 _network_join_kbytes;
 
VARDEF uint16 _network_join_kbytes_total;
 

	
 
VARDEF char _network_last_host[NETWORK_HOSTNAME_LENGTH];
 
VARDEF short _network_last_port;
 
VARDEF uint32 _network_last_host_ip;
 
VARDEF uint8 _network_reconnect;
 

	
 
VARDEF bool _network_udp_server;
 
VARDEF uint16 _network_udp_broadcast;
 

	
 
VARDEF bool _network_advertise;
 
VARDEF uint16 _network_last_advertise_date;
 

	
 
VARDEF bool _network_autoclean_companies;
 
VARDEF uint8 _network_autoclean_unprotected; // Remove a company after X months
 
VARDEF uint8 _network_autoclean_protected;   // Unprotect a company after X months
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 
// Those variables must always be registered!
 
VARDEF bool _networking;
 
VARDEF bool _network_available;  // is network mode available?
 
VARDEF bool _network_server; // network-server is active
 
VARDEF bool _network_dedicated; // are we a dedicated server?
 
VARDEF byte _network_playas; // an id to play as..
 

	
 
void ParseConnectionString(const byte **player, const byte **port, byte *connection_string);
 
void NetworkUpdateClientInfo(uint16 client_index);
 
void AddServer(byte *b);
 
void NetworkAddServer(const byte *b);
 

	
 
#endif /* NETWORK_H */
network_client.c
Show inline comments
 
#include "stdafx.h"
 
#include "network_data.h"
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "table/strings.h"
 
#include "network_client.h"
 
#include "network_gamelist.h"
 
#include "command.h"
 
#include "gfx.h"
 
#include "window.h"
 
#include "settings.h"
 

	
 

	
 
// This file handles all the client-commands
 

	
 

	
 
extern const char _openttd_revision[];
 

	
 
// So we don't make too much typos ;)
 
#define MY_CLIENT DEREF_CLIENT(0)
 

	
 
static uint32 last_ack_frame;
 

	
 
void NetworkRecvPatchSettings(Packet *p);
 

	
 
// **********
 
// Sending functions
 
//   DEF_CLIENT_SEND_COMMAND has no parameters
 
// **********
 

	
 
DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_COMPANY_INFO)
 
{
 
	//
 
	// Packet: CLIENT_COMPANY_INFO
 
	// Function: Request company-info (in detail)
 
	// Data:
 
	//    <none>
 
	//
 
	Packet *p;
 
	_network_join_status = NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO;
 
	InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0);
 

	
 
	p = NetworkSend_Init(PACKET_CLIENT_COMPANY_INFO);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
extern const char _openttd_revision[];
 

	
 
DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_JOIN)
 
{
 
	//
 
	// Packet: CLIENT_JOIN
 
	// Function: Try to join the server
 
	// Data:
 
	//    String: OpenTTD Revision (norev000 if no revision)
 
	//    String: Player Name (max NETWORK_NAME_LENGTH)
 
	//    uint8:  Play as Player id (1..MAX_PLAYERS)
 
	//    uint8:  Language ID
 
	//    String: Unique id to find the player back in server-listing
 
	//
 

	
 
	Packet *p;
 
	_network_join_status = NETWORK_JOIN_STATUS_AUTHORIZING;
 
	InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0);
 

	
 
	p = NetworkSend_Init(PACKET_CLIENT_JOIN);
 
	NetworkSend_string(p, _openttd_revision);
 
	NetworkSend_string(p, _network_player_name); // Player name
 
	NetworkSend_uint8(p, _network_playas); // PlayAs
 
	NetworkSend_uint8(p, NETLANG_ANY); // Language
 
	NetworkSend_string(p, _network_unique_id);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_PASSWORD)(NetworkPasswordType type, const char *password)
 
{
 
	//
 
	// Packet: CLIENT_PASSWORD
 
	// Function: Send a password to the server to authorize
 
	// Data:
 
	//    uint8:  NetworkPasswordType
 
	//    String: Password
 
	//
 
	Packet *p = NetworkSend_Init(PACKET_CLIENT_PASSWORD);
 
	NetworkSend_uint8(p, type);
 
	NetworkSend_string(p, password);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_GETMAP)
 
{
 
	//
 
	// Packet: CLIENT_GETMAP
 
	// Function: Request the map from the server
 
	// Data:
 
	//    <none>
 
@@ -760,69 +760,69 @@ static NetworkClientPacket* const _netwo
 
};
 

	
 
// If this fails, check the array above with network_data.h
 
assert_compile(lengthof(_network_client_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!!
 
void NetworkRecvPatchSettings(Packet *p)
 
{
 
	const SettingDesc *item;
 

	
 
	item = patch_settings;
 

	
 
	while (item->name != NULL) {
 
		switch (item->flags) {
 
			case SDT_BOOL:
 
			case SDT_INT8:
 
			case SDT_UINT8:
 
				*(uint8 *)(item->ptr) = NetworkRecv_uint8(p);
 
				break;
 
			case SDT_INT16:
 
			case SDT_UINT16:
 
				*(uint16 *)(item->ptr) = NetworkRecv_uint16(p);
 
				break;
 
			case SDT_INT32:
 
			case SDT_UINT32:
 
				*(uint32 *)(item->ptr) = NetworkRecv_uint32(p);
 
				break;
 
		}
 
		item++;
 
	}
 
}
 

	
 
// Is called after a client is connected to the server
 
void NetworkClient_Connected(void)
 
{
 
	// Set the frame-counter to 0 so nothing happens till we are ready
 
	_frame_counter = 0;
 
	_frame_counter_server = 0;
 
	last_ack_frame = 0;
 
	// Request the game-info
 
	SEND_COMMAND(PACKET_CLIENT_JOIN)();
 
}
 

	
 
// Reads the packets from the socket-stream, if available
 
NetworkRecvStatus NetworkClient_ReadPackets(ClientState *cs)
 
NetworkRecvStatus NetworkClient_ReadPackets(NetworkClientState *cs)
 
{
 
	Packet *p;
 
	NetworkRecvStatus res = NETWORK_RECV_STATUS_OKAY;
 

	
 
	while (res == NETWORK_RECV_STATUS_OKAY && (p = NetworkRecv_Packet(cs, &res)) != NULL) {
 
		byte type = NetworkRecv_uint8(p);
 
		if (type < PACKET_END && _network_client_packet[type] != NULL) {
 
			res = _network_client_packet[type](p);
 
		}	else {
 
			res = NETWORK_RECV_STATUS_MALFORMED_PACKET;
 
			DEBUG(net, 0)("[NET][client] Received invalid packet type %d", type);
 
		}
 

	
 
		free(p);
 
	}
 

	
 
	return res;
 
}
 

	
 
#endif /* ENABLE_NETWORK */
network_client.h
Show inline comments
 
#ifndef NETWORK_CLIENT_H
 
#define NETWORK_CLIENT_H
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_GAME_INFO);
 
DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_COMPANY_INFO);
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_COMMAND)(CommandPacket *cp);
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_ERROR)(NetworkErrorCode errorno);
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_QUIT)(const char *leavemsg);
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_CHAT)(NetworkAction action, DestType desttype, int dest, const char *msg);
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_PASSWORD)(NetworkPasswordType type, const char *password);
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_SET_PASSWORD)(const char *password);
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_SET_NAME)(const char *name);
 
DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_ACK);
 

	
 
NetworkRecvStatus NetworkClient_ReadPackets(ClientState *cs);
 
NetworkRecvStatus NetworkClient_ReadPackets(NetworkClientState *cs);
 
void NetworkClient_Connected(void);
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 
#endif // NETWORK_CLIENT_H
network_data.c
Show inline comments
 
@@ -41,147 +41,147 @@ void NetworkSend_uint8(Packet *packet, u
 

	
 
void NetworkSend_uint16(Packet *packet, uint16 data)
 
{
 
	assert(packet->size < sizeof(packet->buffer) - sizeof(data));
 
	packet->buffer[packet->size++] = data & 0xFF;
 
	packet->buffer[packet->size++] = (data >> 8) & 0xFF;
 
}
 

	
 
void NetworkSend_uint32(Packet *packet, uint32 data)
 
{
 
	assert(packet->size < sizeof(packet->buffer) - sizeof(data));
 
	packet->buffer[packet->size++] = data & 0xFF;
 
	packet->buffer[packet->size++] = (data >>= 8) & 0xFF;
 
	packet->buffer[packet->size++] = (data >>= 8) & 0xFF;
 
	packet->buffer[packet->size++] = (data >> 8) & 0xFF;
 
}
 

	
 
void NetworkSend_uint64(Packet *packet, uint64 data)
 
{
 
	assert(packet->size < sizeof(packet->buffer) - sizeof(data));
 
	packet->buffer[packet->size++] = data & 0xFF;
 
	packet->buffer[packet->size++] = (data >>= 8) & 0xFF;
 
	packet->buffer[packet->size++] = (data >>= 8) & 0xFF;
 
	packet->buffer[packet->size++] = (data >>= 8) & 0xFF;
 
	packet->buffer[packet->size++] = (data >>= 8) & 0xFF;
 
	packet->buffer[packet->size++] = (data >>= 8) & 0xFF;
 
	packet->buffer[packet->size++] = (data >>= 8) & 0xFF;
 
	packet->buffer[packet->size++] = (data >> 8) & 0xFF;
 
}
 

	
 
// Sends a string over the network. It sends out
 
//  the string + '\0'. No size-byte or something.
 
void NetworkSend_string(Packet *packet, const char* data)
 
{
 
	assert(data != NULL);
 
	assert(packet->size < sizeof(packet->buffer) - strlen(data) - 1);
 
	while ((packet->buffer[packet->size++] = *data++) != '\0') {}
 
}
 

	
 
// If PacketSize changes of size, you have to change the 2 packet->size
 
//   lines below matching the size of packet->size/PacketSize!
 
// (line 'packet->buffer[0] = packet->size & 0xFF;'  and below)
 
assert_compile(sizeof(PacketSize) == 2);
 

	
 
// This function puts the packet in the send-queue and it is send
 
//  as soon as possible
 
// (that is: the next tick, or maybe one tick later if the
 
//   OS-network-buffer is full)
 
void NetworkSend_Packet(Packet *packet, ClientState *cs)
 
void NetworkSend_Packet(Packet *packet, NetworkClientState *cs)
 
{
 
	Packet *p;
 
	assert(packet != NULL);
 

	
 
	packet->pos = 0;
 
	packet->next = NULL;
 

	
 
	packet->buffer[0] = packet->size & 0xFF;
 
	packet->buffer[1] = packet->size >> 8;
 

	
 
	// Locate last packet buffered for the client
 
	p = cs->packet_queue;
 
	if (p == NULL) {
 
		// No packets yet
 
		cs->packet_queue = packet;
 
	} else {
 
		// Skip to the last packet
 
		while (p->next != NULL) p = p->next;
 
		p->next = packet;
 
	}
 
}
 

	
 
// Functions to help NetworkRecv_Packet/NetworkSend_Packet a bit
 
//  A socket can make errors. When that happens
 
//  this handles what to do.
 
// For clients: close connection and drop back to main-menu
 
// For servers: close connection and that is it
 
NetworkRecvStatus CloseConnection(ClientState *cs)
 
NetworkRecvStatus CloseConnection(NetworkClientState *cs)
 
{
 
	CloseClient(cs);
 
	NetworkCloseClient(cs);
 

	
 
	// Clients drop back to the main menu
 
	if (!_network_server) {
 
		_switch_mode = SM_MENU;
 
		_networking = false;
 
		_switch_mode_errorstr = STR_NETWORK_ERR_LOSTCONNECTION;
 

	
 
		return NETWORK_RECV_STATUS_CONN_LOST;
 
	}
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
// Sends all the buffered packets out for this client
 
//  it stops when:
 
//   1) all packets are send (queue is empty)
 
//   2) the OS reports back that it can not send any more
 
//        data right now (full network-buffer, it happens ;))
 
//   3) sending took too long
 
bool NetworkSend_Packets(ClientState *cs)
 
bool NetworkSend_Packets(NetworkClientState *cs)
 
{
 
	ssize_t res;
 
	Packet *p;
 

	
 
	// We can not write to this socket!!
 
	if (!cs->writable) return false;
 
	if (cs->socket == INVALID_SOCKET) return false;
 

	
 
	p = cs->packet_queue;
 
	while (p != NULL) {
 
		res = send(cs->socket, p->buffer + p->pos, p->size - p->pos, 0);
 
		if (res == -1) {
 
			int err = GET_LAST_ERROR();
 
			if (err != EWOULDBLOCK) {
 
				// Something went wrong.. close client!
 
				DEBUG(net, 0) ("[NET] send() failed with error %d", err);
 
				CloseConnection(cs);
 
				return false;
 
			}
 
			return true;
 
		}
 
		if (res == 0) {
 
			// Client/server has left us :(
 
			CloseConnection(cs);
 
			return false;
 
		}
 

	
 
		p->pos += res;
 

	
 
		// Is this packet sent?
 
		if (p->pos == p->size) {
 
			// Go to the next packet
 
			cs->packet_queue = p->next;
 
			free(p);
 
			p = cs->packet_queue;
 
		} else
 
			return true;
 
	}
 

	
 
	return true;
 
}
 

	
 

	
 
// Receiving commands
 
// Again, the next couple of functions are endian-safe
 
//  see the comment around NetworkSend_uint8 for more info.
 
uint8 NetworkRecv_uint8(Packet *packet)
 
{
 
@@ -197,238 +197,238 @@ uint16 NetworkRecv_uint16(Packet *packet
 
}
 

	
 
uint32 NetworkRecv_uint32(Packet *packet)
 
{
 
	uint32 n;
 
	n  = (uint32)packet->buffer[packet->pos++];
 
	n += (uint32)packet->buffer[packet->pos++] << 8;
 
	n += (uint32)packet->buffer[packet->pos++] << 16;
 
	n += (uint32)packet->buffer[packet->pos++] << 24;
 
	return n;
 
}
 

	
 
uint64 NetworkRecv_uint64(Packet *packet)
 
{
 
	uint64 n;
 
	n  = (uint64)packet->buffer[packet->pos++];
 
	n += (uint64)packet->buffer[packet->pos++] << 8;
 
	n += (uint64)packet->buffer[packet->pos++] << 16;
 
	n += (uint64)packet->buffer[packet->pos++] << 24;
 
	n += (uint64)packet->buffer[packet->pos++] << 32;
 
	n += (uint64)packet->buffer[packet->pos++] << 40;
 
	n += (uint64)packet->buffer[packet->pos++] << 48;
 
	n += (uint64)packet->buffer[packet->pos++] << 56;
 
	return n;
 
}
 

	
 
// Reads a string till it finds a '\0' in the stream
 
void NetworkRecv_string(Packet *p, char* buffer, size_t size)
 
{
 
	int pos;
 
	pos = p->pos;
 
	while (--size > 0 && pos < p->size && (*buffer++ = p->buffer[pos++]) != '\0') {}
 
	if (size == 0 || pos == p->size)
 
	{
 
		*buffer = '\0';
 
		// If size was sooner to zero then the string in the stream
 
		//  skip till the \0, so the packet can be read out correctly for the rest
 
		while (pos < p->size && p->buffer[pos] != '\0') ++pos;
 
		++pos;
 
	}
 
	p->pos = pos;
 
}
 

	
 
// If PacketSize changes of size, you have to change the 2 packet->size
 
//   lines below matching the size of packet->size/PacketSize!
 
// (the line: 'p->size = (uint16)p->buffer[0];' and below)
 
assert_compile(sizeof(PacketSize) == 2);
 

	
 
Packet *NetworkRecv_Packet(ClientState *cs, NetworkRecvStatus *status)
 
Packet *NetworkRecv_Packet(NetworkClientState *cs, NetworkRecvStatus *status)
 
{
 
	ssize_t res;
 
	Packet *p;
 

	
 
	*status = NETWORK_RECV_STATUS_OKAY;
 

	
 
	if (cs->socket == INVALID_SOCKET) return NULL;
 

	
 
	if (cs->packet_recv == NULL) {
 
		cs->packet_recv = malloc(sizeof(Packet));
 
		if (cs->packet_recv == NULL) error("Failed to allocate packet");
 
		// Set pos to zero!
 
		cs->packet_recv->pos = 0;
 
		cs->packet_recv->size = 0; // Can be ommited, just for safety reasons
 
	}
 

	
 
	p = cs->packet_recv;
 

	
 
	// Read packet size
 
	if (p->pos < sizeof(PacketSize)) {
 
		while (p->pos < sizeof(PacketSize)) {
 
			// Read the size of the packet
 
			res = recv(cs->socket, p->buffer + p->pos, sizeof(PacketSize) - p->pos, 0);
 
			if (res == -1) {
 
				int err = GET_LAST_ERROR();
 
				if (err != EWOULDBLOCK) {
 
					// Something went wrong..
 
					if (err != 104) // 104 is Connection Reset by Peer
 
						DEBUG(net, 0) ("[NET] recv() failed with error %d", err);
 
					*status = CloseConnection(cs);
 
					return NULL;
 
				}
 
				// Connection would block, so stop for now
 
				return NULL;
 
			}
 
			if (res == 0) {
 
				// Client/server has left us :(
 
				*status = CloseConnection(cs);
 
				return NULL;
 
			}
 
			p->pos += res;
 
		}
 

	
 
		p->size = (uint16)p->buffer[0];
 
		p->size += (uint16)p->buffer[1] << 8;
 

	
 
		if (p->size > SEND_MTU) {
 
			*status = CloseConnection(cs);
 
			return NULL;
 
		}
 
	}
 

	
 
	// Read rest of packet
 
	while (p->pos < p->size) {
 
		res = recv(cs->socket, p->buffer + p->pos, p->size - p->pos, 0);
 
		if (res == -1) {
 
			int err = GET_LAST_ERROR();
 
			if (err != EWOULDBLOCK) {
 
				// Something went wrong..
 
				if (err != 104) // 104 is Connection Reset by Peer
 
					DEBUG(net, 0) ("[NET] recv() failed with error %d", err);
 
				*status = CloseConnection(cs);
 
				return NULL;
 
			}
 
			// Connection would block
 
			return NULL;
 
		}
 
		if (res == 0) {
 
			// Client/server has left us :(
 
			*status = CloseConnection(cs);
 
			return NULL;
 
		}
 

	
 
		p->pos += res;
 
	}
 

	
 
	// We have a complete packet, return it!
 
	p->pos = 2;
 
	p->next = NULL; // Should not be needed, but who knows...
 

	
 
	// Prepare for receiving a new packet
 
	cs->packet_recv = NULL;
 

	
 
	return p;
 
}
 

	
 
// Add a command to the local command queue
 
void NetworkAddCommandQueue(ClientState *cs, CommandPacket *cp)
 
void NetworkAddCommandQueue(NetworkClientState *cs, CommandPacket *cp)
 
{
 
	CommandPacket *new_cp = malloc(sizeof(CommandPacket));
 

	
 
	*new_cp = *cp;
 

	
 
	if (cs->command_queue == NULL)
 
		cs->command_queue = new_cp;
 
	else {
 
		CommandPacket *c = cs->command_queue;
 
		while (c->next != NULL) c = c->next;
 
		c->next = new_cp;
 
	}
 
}
 

	
 
// If this fails, make sure you change the following line below:
 
//   'memcpy(qp->dp, _decode_parameters, 10 * sizeof(uint32));'
 
// Also, in network_data.h, change the size of CommandPacket->dp!
 
// (this protection is there to make sure in network.h dp is of the right size!)
 
assert_compile(sizeof(_decode_parameters) == 20 * sizeof(uint32));
 

	
 
// Prepare a DoCommand to be send over the network
 
void NetworkSend_Command(uint32 tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback)
 
{
 
	CommandPacket *c = malloc(sizeof(CommandPacket));
 
	byte temp_callback;
 

	
 
	c->player = _local_player;
 
	c->next = NULL;
 
	c->tile = tile;
 
	c->p1 = p1;
 
	c->p2 = p2;
 
	c->cmd = cmd;
 
	c->callback = 0;
 

	
 
	temp_callback = 0;
 

	
 
	while (temp_callback < _callback_table_count && _callback_table[temp_callback] != callback)
 
		temp_callback++;
 
	if (temp_callback == _callback_table_count) {
 
		DEBUG(net, 0) ("[NET] Unknown callback. (Pointer: %p) No callback sent.", callback);
 
		temp_callback = 0; /* _callback_table[0] == NULL */
 
	}
 

	
 
	if (_network_server) {
 
		// We are the server, so set the command to be executed next possible frame
 
		c->frame = _frame_counter_max + 1;
 
	} else {
 
		c->frame = 0; // The client can't tell which frame, so just make it 0
 
	}
 

	
 
	// Copy the _decode_parameters to dp
 
	memcpy(c->dp, _decode_parameters, 20 * sizeof(uint32));
 

	
 
	if (_network_server) {
 
		// If we are the server, we queue the command in our 'special' queue.
 
		//   In theory, we could execute the command right away, but then the
 
		//   client on the server can do everything 1 tick faster then others.
 
		//   So to keep the game fair, we delay the command with 1 tick
 
		//   which gives about the same speed as most clients.
 
		ClientState *cs;
 
		NetworkClientState *cs;
 

	
 
		// And we queue it for delivery to the clients
 
		FOR_ALL_CLIENTS(cs) {
 
			if (cs->status > STATUS_AUTH) {
 
				NetworkAddCommandQueue(cs, c);
 
			}
 
		}
 

	
 
		// Only the server gets the callback, because clients should not get them
 
		c->callback = temp_callback;
 
		if (_local_command_queue == NULL) {
 
			_local_command_queue = c;
 
		} else {
 
			// Find last packet
 
			CommandPacket *cp = _local_command_queue;
 
			while (cp->next != NULL) cp = cp->next;
 
			cp->next = c;
 
		}
 

	
 
		return;
 
	}
 

	
 
	// Clients send their command to the server and forget all about the packet
 
	c->callback = temp_callback;
 
	SEND_COMMAND(PACKET_CLIENT_COMMAND)(c);
 
}
 

	
 
// Execute a DoCommand we received from the network
 
void NetworkExecuteCommand(CommandPacket *cp)
 
{
 
	_current_player = cp->player;
 
	memcpy(_decode_parameters, cp->dp, sizeof(cp->dp));
 
	/* cp->callback is unsigned. so we don't need to do lower bounds checking. */
 
	if (cp->callback > _callback_table_count) {
 
		DEBUG(net,0) ("[NET] Received out-of-bounds callback! (%d)", cp->callback);
 
		cp->callback = 0;
 
	}
 
	DoCommandP(cp->tile, cp->p1, cp->p2, _callback_table[cp->callback], cp->cmd | CMD_NETWORK_COMMAND);
 
}
 

	
 
#endif /* ENABLE_NETWORK */
network_data.h
Show inline comments
 
@@ -62,164 +62,164 @@ typedef enum {
 
typedef enum {
 
	NETWORK_RECV_STATUS_OKAY,
 
	NETWORK_RECV_STATUS_DESYNC,
 
	NETWORK_RECV_STATUS_SAVEGAME,
 
	NETWORK_RECV_STATUS_CONN_LOST,
 
	NETWORK_RECV_STATUS_MALFORMED_PACKET,
 
	NETWORK_RECV_STATUS_SERVER_ERROR, // The server told us we made an error
 
	NETWORK_RECV_STATUS_SERVER_FULL,
 
	NETWORK_RECV_STATUS_CLOSE_QUERY, // Done quering the server
 
} NetworkRecvStatus;
 

	
 
typedef enum {
 
	NETWORK_ERROR_GENERAL, // Try to use thisone like never
 

	
 
	// Signals from clients
 
	NETWORK_ERROR_DESYNC,
 
	NETWORK_ERROR_SAVEGAME_FAILED,
 
	NETWORK_ERROR_CONNECTION_LOST,
 
	NETWORK_ERROR_ILLEGAL_PACKET,
 

	
 
	// Signals from servers
 
	NETWORK_ERROR_NOT_AUTHORIZED,
 
	NETWORK_ERROR_NOT_EXPECTED,
 
	NETWORK_ERROR_WRONG_REVISION,
 
	NETWORK_ERROR_NAME_IN_USE,
 
	NETWORK_ERROR_WRONG_PASSWORD,
 
	NETWORK_ERROR_PLAYER_MISMATCH, // Happens in CLIENT_COMMAND
 
	NETWORK_ERROR_KICKED,
 
} NetworkErrorCode;
 

	
 
// Actions that can be used for NetworkTextMessage
 
typedef enum {
 
	NETWORK_ACTION_JOIN_LEAVE,
 
	NETWORK_ACTION_CHAT,
 
	NETWORK_ACTION_CHAT_PLAYER,
 
	NETWORK_ACTION_CHAT_CLIENT,
 
	NETWORK_ACTION_CHAT_TO_CLIENT,
 
	NETWORK_ACTION_CHAT_TO_PLAYER,
 
	NETWORK_ACTION_GIVE_MONEY,
 
	NETWORK_ACTION_NAME_CHANGE,
 
} NetworkAction;
 

	
 
typedef enum {
 
	NETWORK_GAME_PASSWORD,
 
	NETWORK_COMPANY_PASSWORD,
 
} NetworkPasswordType;
 

	
 
// To keep the clients all together
 
typedef struct ClientState {
 
typedef struct NetworkClientState {
 
	int socket;
 
	uint16 index;
 
	uint32 last_frame;
 
	uint32 last_frame_server;
 
	byte lag_test; // This byte is used for lag-testing the client
 

	
 
	ClientStatus status;
 
	bool writable; // is client ready to write to?
 
	bool quited;
 

	
 
	Packet *packet_queue; // Packets that are awaiting delivery
 
	Packet *packet_recv; // Partially received packet
 

	
 
	CommandPacket *command_queue; // The command-queue awaiting delivery
 
} ClientState;
 
} NetworkClientState;
 

	
 
// What packet types are there
 
// WARNING: The first 3 packets can NEVER change order again
 
//   it protects old clients from joining newer servers (because SERVER_ERROR
 
//   is the respond to a wrong revision)
 
typedef enum {
 
	PACKET_SERVER_FULL,
 
	PACKET_CLIENT_JOIN,
 
	PACKET_SERVER_ERROR,
 
	PACKET_CLIENT_COMPANY_INFO,
 
	PACKET_SERVER_COMPANY_INFO,
 
	PACKET_SERVER_CLIENT_INFO,
 
	PACKET_SERVER_NEED_PASSWORD,
 
	PACKET_CLIENT_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_END // Should ALWAYS be on the end of this list!! (period)
 
} PacketType;
 

	
 
typedef enum {
 
	DESTTYPE_BROADCAST,
 
	DESTTYPE_PLAYER,
 
	DESTTYPE_CLIENT
 
} DestType;
 

	
 
CommandPacket *_local_command_queue;
 

	
 
SOCKET _udp_client_socket; // udp client socket
 

	
 
// Here we keep track of the clients
 
//  (and the client uses [0] for his own communication)
 
ClientState _clients[MAX_CLIENTS];
 
NetworkClientState _clients[MAX_CLIENTS];
 
#define DEREF_CLIENT(i) (&_clients[i])
 
// This returns the NetworkClientInfo from a ClientState
 
// This returns the NetworkClientInfo from a NetworkClientState
 
#define DEREF_CLIENT_INFO(cs) (&_network_client_info[cs - _clients])
 

	
 
// Macros to make life a bit more easier
 
#define DEF_CLIENT_RECEIVE_COMMAND(type) NetworkRecvStatus NetworkPacketReceive_ ## type ## _command(Packet *p)
 
#define DEF_CLIENT_SEND_COMMAND(type) void NetworkPacketSend_ ## type ## _command(void)
 
#define DEF_CLIENT_SEND_COMMAND_PARAM(type) void NetworkPacketSend_ ## type ## _command
 
#define DEF_SERVER_RECEIVE_COMMAND(type) void NetworkPacketReceive_ ## type ## _command(ClientState *cs, Packet *p)
 
#define DEF_SERVER_SEND_COMMAND(type) void NetworkPacketSend_ ## type ## _command(ClientState *cs)
 
#define DEF_SERVER_RECEIVE_COMMAND(type) void NetworkPacketReceive_ ## type ## _command(NetworkClientState *cs, Packet *p)
 
#define DEF_SERVER_SEND_COMMAND(type) void NetworkPacketSend_ ## type ## _command(NetworkClientState *cs)
 
#define DEF_SERVER_SEND_COMMAND_PARAM(type) void NetworkPacketSend_ ## type ## _command
 

	
 
#define SEND_COMMAND(type) NetworkPacketSend_ ## type ## _command
 
#define RECEIVE_COMMAND(type) NetworkPacketReceive_ ## type ## _command
 

	
 
#define FOR_ALL_CLIENTS(cs) for (cs = _clients; cs != &_clients[MAX_CLIENTS] && cs->socket != INVALID_SOCKET; cs++)
 

	
 
Packet *NetworkSend_Init(PacketType type);
 
void NetworkSend_uint8(Packet *packet, uint8 data);
 
void NetworkSend_uint16(Packet *packet, uint16 data);
 
void NetworkSend_uint32(Packet *packet, uint32 data);
 
void NetworkSend_uint64(Packet *packet, uint64 data);
 
void NetworkSend_string(Packet *packet, const char* data);
 
void NetworkSend_Packet(Packet *packet, ClientState *cs);
 
void NetworkSend_Packet(Packet *packet, NetworkClientState *cs);
 

	
 
uint8 NetworkRecv_uint8(Packet *packet);
 
uint16 NetworkRecv_uint16(Packet *packet);
 
uint32 NetworkRecv_uint32(Packet *packet);
 
uint64 NetworkRecv_uint64(Packet *packet);
 
void NetworkRecv_string(Packet *packet, char* buffer, size_t size);
 
Packet *NetworkRecv_Packet(ClientState *cs, NetworkRecvStatus *status);
 
Packet *NetworkRecv_Packet(NetworkClientState *cs, NetworkRecvStatus *status);
 

	
 
bool NetworkSend_Packets(ClientState *cs);
 
bool NetworkSend_Packets(NetworkClientState *cs);
 
void NetworkExecuteCommand(CommandPacket *cp);
 
void NetworkAddCommandQueue(ClientState *cs, CommandPacket *cp);
 
void NetworkAddCommandQueue(NetworkClientState *cs, CommandPacket *cp);
 

	
 
// from network.c
 
void CloseClient(ClientState *cs);
 
void NetworkCloseClient(NetworkClientState *cs);
 
void CDECL NetworkTextMessage(NetworkAction action, uint16 color, const char *name, const char *str, ...);
 
void NetworkGetClientName(char *clientname, size_t size, ClientState *cs);
 
uint NetworkCalculateLag(const ClientState *cs);
 
void NetworkGetClientName(char *clientname, size_t size, const NetworkClientState *cs);
 
uint NetworkCalculateLag(const NetworkClientState *cs);
 
byte NetworkGetCurrentLanguageIndex();
 
NetworkClientInfo *NetworkFindClientInfoFromIndex(uint16 client_index);
 
ClientState *NetworkFindClientStateFromIndex(uint16 client_index);
 
NetworkClientState *NetworkFindClientStateFromIndex(uint16 client_index);
 
unsigned long NetworkResolveHost(const char *hostname);
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 
#endif // NETWORK_DATA_H
network_gamelist.c
Show inline comments
 
@@ -20,64 +20,64 @@ void NetworkGameListClear(void)
 
		next = item->next;
 
		free(item);
 
		item = next;
 
	}
 
	_network_game_list = NULL;
 
	_network_game_count = 0;
 

	
 
	UpdateNetworkGameWindow(true);
 

	
 
	DEBUG(net, 4)("[NET][GameList] Cleared list");
 
}
 

	
 
NetworkGameList *NetworkGameListAddItem(uint32 ip, uint16 port)
 
{
 
	NetworkGameList *item;
 

	
 
	item = _network_game_list;
 
	if (item != NULL) {
 
		while (item->next != NULL) {
 
			if (item->ip == ip && item->port == port)
 
				return item;
 
			item = item->next;
 
		}
 

	
 
		if (item->ip == ip && item->port == port)
 
			return item;
 

	
 
		item->next = malloc(sizeof(*item));
 
		item = item->next;
 
	} else {
 
		item = malloc(sizeof(*item));
 
		_network_game_list = item;
 
	}
 

	
 
	DEBUG(net, 4) ("[NET][GameList] Added server to list");
 

	
 
	memset(item, 0, sizeof(*item));
 

	
 
	item->next = NULL;
 
	item->ip = ip;
 
	item->port = port;
 
	_network_game_count++;
 

	
 
	UpdateNetworkGameWindow(false);
 

	
 
	return item;
 
}
 

	
 
void NetworkGameListAddQueriedItem(NetworkGameInfo *info, bool server_online)
 
void NetworkGameListAddQueriedItem(const NetworkGameInfo *info, bool server_online)
 
{
 
	// We queried a server and now we are going to add it to the list
 
	NetworkGameList *item;
 

	
 
	item = NetworkGameListAddItem(_network_last_host_ip, _network_last_port);
 
	item->online = server_online;
 
	memcpy(&item->info, info, sizeof(NetworkGameInfo));
 
	ttd_strlcpy(item->info.hostname, _network_last_host, sizeof(item->info.hostname));
 

	
 
	UpdateNetworkGameWindow(false);
 
}
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 

	
network_gamelist.h
Show inline comments
 
#ifndef NETWORK_GAMELIST_H
 
#define NETWORK_GAMELIST_H
 

	
 
void NetworkGameListClear(void);
 
NetworkGameList *NetworkGameListAddItem(uint32 ip, uint16 port);
 
void NetworkGameListAddQueriedItem(NetworkGameInfo *info, bool server_online);
 
void NetworkGameListAddQueriedItem(const NetworkGameInfo *info, bool server_online);
 

	
 
#endif /* NETWORK_GAMELIST_H */
network_gui.c
Show inline comments
 
@@ -272,97 +272,97 @@ static void NetworkGameWindowWndProc(Win
 
				w->window_class,
 
				w->window_number);
 
				DeleteName(str);
 
		} break;
 
		case 11: /* Start server */
 
			ShowNetworkStartServerWindow();
 
			break;
 
		case 15: /* Join Game */
 
			if (_selected_item != NULL) {
 
				memcpy(&_network_game_info, &_selected_item->info, sizeof(NetworkGameInfo));
 
				snprintf(_network_last_host, sizeof(_network_last_host), "%s", inet_ntoa(*(struct in_addr *)&_selected_item->ip));
 
				_network_last_port = _selected_item->port;
 
				ShowNetworkLobbyWindow();
 
			}
 
			break;
 
		case 16: // Refresh
 
			if (_selected_item != NULL) {
 
				NetworkQueryServer(_selected_item->info.hostname, _selected_item->port, true);
 
			}
 
			break;
 

	
 
	}	break;
 

	
 
	case WE_MOUSELOOP:
 
		if (_selected_field == 3)
 
			HandleEditBox(w, 3);
 

	
 
		break;
 

	
 
	case WE_KEYPRESS:
 
		if (_selected_field != 3)
 
			break;
 

	
 
		switch (HandleEditBoxKey(w, 3, e)) {
 
		case 1:
 
			HandleButtonClick(w, 8);
 
			break;
 
		}
 

	
 
		// The name is only allowed when it starts with a letter!
 
		if (_edit_str_buf[0] != '\0' && _edit_str_buf[0] != ' ')
 
			ttd_strlcpy(_network_player_name, _edit_str_buf, lengthof(_network_player_name));
 
		else
 
			ttd_strlcpy(_network_player_name, "Player", lengthof(_network_player_name));
 

	
 
		break;
 

	
 
	case WE_ON_EDIT_TEXT: {
 
		AddServer(e->edittext.str);
 
		NetworkAddServer(e->edittext.str);
 
	} break;
 

	
 
	case WE_CREATE: {
 
		_selected_item = NULL;
 
	} break;
 
	}
 
}
 

	
 
static const Widget _network_game_window_widgets[] = {
 
{   WWT_CLOSEBOX,   BGC,     0,    10,     0,    13, STR_00C5,										STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   BGC,    11,   479,     0,    13, STR_NETWORK_MULTIPLAYER,			STR_NULL},
 
{     WWT_IMGBTN,   BGC,     0,   479,    14,   214, 0x0,													STR_NULL},
 

	
 
/* LEFT SIDE */
 
{     WWT_IMGBTN,   BGC,    90,   230,    22,    33, 0x0,													STR_NETWORK_ENTER_NAME_TIP},
 

	
 
{  WWT_SCROLLBAR,   BGC,   220,   230,    62,   185, 0x0,													STR_0190_SCROLL_BAR_SCROLLS_LIST},
 

	
 
{     WWT_IMGBTN,   BTC,    10,   130,    62,    73, 0x0,													STR_NETWORK_GAME_NAME_TIP },
 
{     WWT_IMGBTN,   BTC,   131,   180,    62,    73, 0x0,													STR_NETWORK_CLIENTS_CAPTION_TIP },
 
{     WWT_IMGBTN,   BTC,   181,   219,    62,    73, 0x0,													STR_NETWORK_INFO_ICONS_TIP },
 

	
 
{     WWT_MATRIX,   BGC,    10,   219,    74,   185, 0x801,												STR_NETWORK_CLICK_GAME_TO_SELECT},
 

	
 
{ WWT_PUSHTXTBTN,   BTC,    10,   115,   195,   206, STR_NETWORK_FIND_SERVER,			STR_NETWORK_FIND_SERVER_TIP},
 
{ WWT_PUSHTXTBTN,   BTC,   125,   230,   195,   206, STR_NETWORK_ADD_SERVER,			STR_NETWORK_ADD_SERVER_TIP},
 
{ WWT_PUSHTXTBTN,   BTC,   250,   355,   195,   206, STR_NETWORK_START_SERVER,		STR_NETWORK_START_SERVER_TIP},
 
{ WWT_PUSHTXTBTN,   BTC,   365,   470,   195,   206, STR_012E_CANCEL,							STR_NULL},
 

	
 
/* RIGHT SIDE */
 
{     WWT_IMGBTN,   BGC,   250,   470,    22,   185, 0x0,					STR_NULL},
 
{          WWT_6,   BGC,   251,   469,    23,   184, 0x0,					STR_NULL},
 

	
 
{ WWT_PUSHTXTBTN,   BTC,   260,   355,   164,   175, STR_NETWORK_JOIN_GAME,					STR_NULL},
 
{ WWT_PUSHTXTBTN,   BTC,   365,   460,   164,   175, STR_NETWORK_REFRESH,					STR_NETWORK_REFRESH_TIP},
 

	
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _network_game_window_desc = {
 
	WDP_CENTER, WDP_CENTER, 480, 215,
 
	WC_NETWORK_WINDOW,0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESTORE_DPARAM,
 
	_network_game_window_widgets,
 
	NetworkGameWindowWndProc,
 
};
 

	
 
static FiosItem *selected_map = NULL; // to highlight slected map
network_server.c
Show inline comments
 
#include "stdafx.h"
 
#include "network_data.h"
 

	
 
#ifdef ENABLE_NETWORK
 

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

	
 
// This file handles all the server-commands
 

	
 
void NetworkHandleCommandQueue(ClientState *cs);
 
void NetworkHandleCommandQueue(NetworkClientState *cs);
 
void NetworkPopulateCompanyInfo(void);
 
void NetworkSendPatchSettings(ClientState *cs);
 
void NetworkSendPatchSettings(NetworkClientState *cs);
 

	
 
extern const char _openttd_revision[];
 

	
 
// Is the network enabled?
 

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

	
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_CLIENT_INFO)(ClientState *cs, NetworkClientInfo *ci)
 
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 = 0;
 
	byte current = 0;
 

	
 

	
 
	FOR_ALL_PLAYERS(player) {
 
		if (player->is_active)
 
			active++;
 
	}
 

	
 
	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;
 

	
 
		current++;
 

	
 
		p = NetworkSend_Init(PACKET_SERVER_COMPANY_INFO);
 

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

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

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

	
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_ERROR)(ClientState *cs, NetworkErrorCode error)
 
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)
 
	//
 

	
 
	ClientState *new_cs;
 
	NetworkClientState *new_cs;
 
	char str1[100], str2[100];
 
	char client_name[NETWORK_NAME_LENGTH];
 

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

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

	
 
		GetString(str1, STR_NETWORK_ERR_LEFT);
 
		GetString(str2, STR_NETWORK_ERR_CLIENT_GENERAL + error);
 

	
 
		DEBUG(net, 2)("[NET] %s made an error (%s) and his connection is closed", client_name, str2);
 

	
 
		NetworkTextMessage(NETWORK_ACTION_JOIN_LEAVE, 1, client_name, "%s (%s)", str1, str2);
 

	
 
		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] Clientno %d has made an error and his connection is closed", cs->index);
 
	}
 

	
 
	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!
 
	CloseClient(cs);
 
	NetworkCloseClient(cs);
 
}
 

	
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_NEED_PASSWORD)(ClientState *cs, NetworkPasswordType type)
 
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;
 
	ClientState *new_cs;
 
	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;
 
	ClientState *new_cs;
 
	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");
 

	
 
@@ -282,335 +284,333 @@ DEF_SERVER_SEND_COMMAND(PACKET_SERVER_MA
 
		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;
 
	}
 

	
 
	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!
 
				int i;
 
				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);
 
				// Send the player_seeds in this packet
 
				for (i = 0; i < MAX_PLAYERS; i++) {
 
					NetworkSend_uint32(p, _player_seeds[i][0]);
 
					NetworkSend_uint32(p, _player_seeds[i][1]);
 
				}
 
				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);
 

	
 
				{
 
					ClientState *new_cs;
 
					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)(ClientState *cs, uint16 client_index)
 
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)(ClientState *cs, CommandPacket *cp)
 
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
 
	//    uint32: decode_params
 
	//      10 times the last one (lengthof(cp->dp))
 
	//    uint8:  CallBackID (see callback_table.c)
 
	//    uint32: Frame of execution
 
	//
 

	
 
	int i;
 
	char *dparam_char;
 
	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);
 
	/* We are going to send them byte by byte, because dparam is misused
 
	    for chars (if it is used), and else we have a BigEndian / LittleEndian
 
	    problem.. we should fix the misuse of dparam... -- TrueLight */
 
	dparam_char = (char *)&cp->dp[0];
 
	for (i = 0; i < lengthof(cp->dp) * 4; i++) {
 
		NetworkSend_uint8(p, *dparam_char);
 
		dparam_char++;
 
	}
 
	NetworkSend_uint8(p, cp->callback);
 
	NetworkSend_uint32(p, cp->frame);
 

	
 
	NetworkSend_Packet(p, cs);
 
}
 

	
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_CHAT)(ClientState *cs, NetworkAction action, uint16 client_index, const char *msg)
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_CHAT)(NetworkClientState *cs, NetworkAction action, uint16 client_index, 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_string(p, msg);
 

	
 
	NetworkSend_Packet(p, cs);
 
}
 

	
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_ERROR_QUIT)(ClientState *cs, uint16 client_index, NetworkErrorCode errorno)
 
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)(ClientState *cs, uint16 client_index, const char *leavemsg)
 
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);
 
}
 

	
 
// **********
 
// Receiving functions
 
//   DEF_SERVER_RECEIVE_COMMAND has parameter: ClientState *cs, Packet *p
 
//   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);
 
}
 

	
 
extern const char _openttd_revision[];
 

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

	
 

	
 
	NetworkRecv_string(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(p, name, sizeof(name));
 
	playas = NetworkRecv_uint8(p);
 
	client_lang = NetworkRecv_uint8(p);
 
	NetworkRecv_string(p, unique_id, sizeof(unique_id));
 

	
 
	// Check if someone else already has that name
 
	snprintf(test_name, sizeof(test_name), "%s", name);
 

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

	
 
	if (!NetworkFindName(test_name)) {
 
		// 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", test_name);
 
	snprintf(ci->unique_id, sizeof(ci->unique_id), "%s", unique_id);
 
@@ -637,323 +637,323 @@ DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT
 

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

	
 
	type = NetworkRecv_uint8(p);
 
	NetworkRecv_string(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)
 
{
 
	ClientState *new_cs;
 
	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_NAME_LENGTH];
 
		char str[100];
 
		ClientState *new_cs;
 
		NetworkClientState *new_cs;
 
		GetString(str, STR_NETWORK_CLIENT_JOINED);
 

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

	
 
		NetworkTextMessage(NETWORK_ACTION_JOIN_LEAVE, 1, client_name, str);
 

	
 
		// 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);
 
			}
 
		}
 
	} else {
 
		// Wrong status for this packet, give a warning to client, and close connection
 
		SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED);
 
	}
 
}
 

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_COMMAND)
 
{
 
	// The client has done a command and wants us to handle it
 
	int i;
 
	byte callback;
 
	ClientState *new_cs;
 
	NetworkClientState *new_cs;
 
	NetworkClientInfo *ci;
 
	char *dparam_char;
 

	
 
	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(p);
 
	cp->cmd = NetworkRecv_uint32(p);
 
	cp->p1 = NetworkRecv_uint32(p);
 
	cp->p2 = NetworkRecv_uint32(p);
 
	cp->tile = NetworkRecv_uint32(p);
 
	/* We are going to send them byte by byte, because dparam is misused
 
	    for chars (if it is used), and else we have a BigEndian / LittleEndian
 
	    problem.. we should fix the misuse of dparam... -- TrueLight */
 
	dparam_char = (char *)&cp->dp[0];
 
	for (i = 0; i < lengthof(cp->dp) * 4; i++) {
 
		*dparam_char = NetworkRecv_uint8(p);
 
		dparam_char++;
 
	}
 

	
 
	callback = NetworkRecv_uint8(p);
 

	
 
	ci = DEREF_CLIENT_INFO(cs);
 
	// Only CMD_PLAYER_CTRL is always allowed, for the rest, playas needs
 
	//  to match the player in the packet
 
	if (!(cp->cmd == CMD_PLAYER_CTRL && cp->p1 == 0) && ci->client_playas-1 != cp->player) {
 
		// The player did a command with the wrong player_id.. bad!!
 
		SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_PLAYER_MISMATCH);
 
		return;
 
	}
 
	if (cp->cmd == CMD_PLAYER_CTRL) {
 
		if (cp->p1 == 0)
 
			// UGLY! p2 is mis-used to get the client-id in CmdPlayerCtrl
 
			cp->p2 = cs - _clients;
 
		else {
 
			/* We do NOT allow any client to send any PLAYER_CTRL packet..
 
			    (they can delete random players with it if they like */
 
			SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_PLAYER_MISMATCH);
 
			return;
 
		}
 
	}
 

	
 

	
 
	// 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.
 
			if (new_cs != cs)
 
				cp->callback = 0;
 
			else
 
				cp->callback = 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
 
	ClientState *new_cs;
 
	NetworkClientState *new_cs;
 
	byte errorno = NetworkRecv_uint8(p);
 
	char str1[100], str2[100];
 
	char client_name[NETWORK_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(str1, STR_NETWORK_ERR_LEFT);
 
	GetString(str2, STR_NETWORK_ERR_CLIENT_GENERAL + errorno);
 

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

	
 
	NetworkTextMessage(NETWORK_ACTION_JOIN_LEAVE, 1, client_name, "%s (%s)", str1, str2);
 

	
 
	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.
 
	ClientState *new_cs;
 
	NetworkClientState *new_cs;
 
	char str1[100], str2[100];
 
	char client_name[NETWORK_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(p, str2, 100);
 

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

	
 
	GetString(str1, STR_NETWORK_ERR_LEFT);
 

	
 
	NetworkTextMessage(NETWORK_ACTION_JOIN_LEAVE, 1, client_name, "%s (%s)", str1, str2);
 

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

	
 
	cs->quited = true;
 
}
 

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_ACK)
 
{
 
	// The client received the frame, make note of it
 
	cs->last_frame = NetworkRecv_uint32(p);
 
	// With those 2 values we can calculate the lag realtime
 
	cs->last_frame_server = _frame_counter;
 

	
 
	// The client is now really active
 
	if (cs->status == STATUS_PRE_ACTIVE)
 
		cs->status = STATUS_ACTIVE;
 
}
 

	
 

	
 

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

	
 
	switch (desttype) {
 
	case DESTTYPE_CLIENT:
 
		if (dest == 1) {
 
			ci = NetworkFindClientInfoFromIndex(from_index);
 
			if (ci != NULL)
 
				NetworkTextMessage(action, GetDrawStringPlayerColor(ci->client_playas-1), ci->client_name, "%s", msg);
 
		} else {
 
			FOR_ALL_CLIENTS(cs) {
 
				if (cs->index == dest) {
 
					SEND_COMMAND(PACKET_SERVER_CHAT)(cs, action, from_index, msg);
 
					break;
 
				}
 
			}
 
		}
 

	
 
		// Display the message locally (so you know you have sent it)
 
		if (from_index != dest) {
 
			if (from_index == 1) {
 
				ci = NetworkFindClientInfoFromIndex(from_index);
 
				ci_to = NetworkFindClientInfoFromIndex(dest);
 
				if (ci != NULL && ci_to != NULL)
 
					NetworkTextMessage(NETWORK_ACTION_CHAT_TO_CLIENT, GetDrawStringPlayerColor(ci->client_playas-1), ci_to->client_name, "%s", msg);
 
			} else {
 
				FOR_ALL_CLIENTS(cs) {
 
					if (cs->index == from_index) {
 
						SEND_COMMAND(PACKET_SERVER_CHAT)(cs, NETWORK_ACTION_CHAT_TO_CLIENT, dest, 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.
 
		FOR_ALL_CLIENTS(cs) {
 
			ci = DEREF_CLIENT_INFO(cs);
 
			if (ci->client_playas == dest) {
 
				SEND_COMMAND(PACKET_SERVER_CHAT)(cs, action, from_index, msg);
 
				if (cs->index == from_index)
 
					show_local = false;
 
			}
 
		}
 
		ci = NetworkFindClientInfoFromIndex(from_index);
 
		ci_own = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX);
 
		if (ci != NULL && ci_own != NULL && ci_own->client_playas == dest) {
 
@@ -991,183 +991,183 @@ void NetworkServer_HandleChat(NetworkAct
 
		break;
 
	}
 
}
 

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

	
 
	NetworkRecv_string(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(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 name[NETWORK_NAME_LENGTH];
 
	NetworkClientInfo *ci;
 

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

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

	
 
// The layout for the receive-functions by the server
 
typedef void NetworkServerPacket(ClientState *cs, Packet *p);
 
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,*/
 
	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,*/
 
};
 

	
 
// 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!!
 
void NetworkSendPatchSettings(ClientState *cs)
 
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;
 

	
 
	while (item->name != NULL) {
 
		switch (item->flags) {
 
			case SDT_BOOL:
 
			case SDT_INT8:
 
			case SDT_UINT8:
 
				NetworkSend_uint8(p, *(uint8 *)item->ptr);
 
				break;
 
			case SDT_INT16:
 
			case SDT_UINT16:
 
				NetworkSend_uint16(p, *(uint16 *)item->ptr);
 
				break;
 
			case SDT_INT32:
 
			case SDT_UINT32:
 
				NetworkSend_uint32(p, *(uint32 *)item->ptr);
 
				break;
 
		}
 
		item++;
 
	}
 

	
 
	NetworkSend_Packet(p, cs);
 
}
 

	
 
// This update the company_info-stuff
 
void NetworkPopulateCompanyInfo(void)
 
{
 
	char password[NETWORK_PASSWORD_LENGTH];
 
	Player *p;
 
	Vehicle *v;
 
	Station *s;
 
	ClientState *cs;
 
	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
 
		GetString(_network_player_info[p->index].company_name, p->name_1);
 

	
 
		// 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 (v->subtype == 0)
 
						_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
 
@@ -1177,285 +1177,285 @@ void NetworkPopulateCompanyInfo(void)
 
					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;
 
			}
 
	}
 

	
 
	// 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_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)
 
				strncat(_network_player_info[ci->client_playas-1].players, ", ", sizeof(_network_player_info[ci->client_playas-1].players));
 

	
 
			strncat(_network_player_info[ci->client_playas-1].players, client_name, sizeof(_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)
 
{
 
	ClientState *cs;
 
	NetworkClientState *cs;
 
	NetworkClientInfo *ci;
 

	
 
	ci = NetworkFindClientInfoFromIndex(client_index);
 

	
 
	if (ci == NULL)
 
		return;
 

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

	
 
/* 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()
 
{
 
	ClientState *cs;
 
	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(_iconsole_color_default, "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(_iconsole_color_default, "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_NAME_LENGTH])
 
{
 
	ClientState *new_cs;
 
	NetworkClientState *new_cs;
 
	NetworkClientInfo *ci;
 
	bool found_name = false;
 
	byte number = 0;
 
	char original_name[NETWORK_NAME_LENGTH];
 

	
 
	// We use NETWORK_NAME_LENGTH in here, because new_name is really a pointer
 
	ttd_strlcpy(original_name, new_name, NETWORK_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_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_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 then 50 times..
 
			if (number++ > 50) break;
 
			snprintf(new_name, NETWORK_NAME_LENGTH, "%s #%d", original_name, number);
 
		}
 
	}
 

	
 
	return found_name;
 
}
 

	
 
// Reads a packet from the stream
 
bool NetworkServer_ReadPackets(ClientState *cs)
 
bool NetworkServer_ReadPackets(NetworkClientState *cs)
 
{
 
	Packet *p;
 
	NetworkRecvStatus res;
 
	while((p = NetworkRecv_Packet(cs, &res)) != NULL) {
 
		byte type = NetworkRecv_uint8(p);
 
		if (type < PACKET_END && _network_server_packet[type] != NULL)
 
			_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
 
void NetworkHandleCommandQueue(ClientState *cs) {
 
void NetworkHandleCommandQueue(NetworkClientState *cs) {
 
	if (cs->command_queue != NULL) {
 
		CommandPacket *cp;
 
		CommandPacket *cp_prev;
 

	
 
		cp = cs->command_queue;
 
		cp_prev = NULL;
 

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

	
 
			if (cp_prev != NULL) {
 
				cp_prev->next = cp->next;
 
				free(cp);
 
				cp = cp_prev->next;
 
			} else {
 
				// This means we are at our first packet
 
				cs->command_queue = cp->next;
 
				free(cp);
 
				cp = cs->command_queue;
 
			}
 
		}
 
	}
 
}
 

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

	
 
	// Update max-frame-counter
 
	if (_frame_counter > _frame_counter_max) {
 
		_frame_counter_max = _frame_counter + _network_frame_freq;
 
		send_frame = true;
 
	}
 

	
 
	// 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(_iconsole_color_error,"Client #%d is dropped because the client did not respond for more then 4 game-days", cs->index);
 
					CloseClient(cs);
 
					NetworkCloseClient(cs);
 
					continue;
 
				}
 

	
 
				// Report once per time we detect the lag
 
				if (cs->lag_test == 0) {
 
					IConsolePrintF(_iconsole_color_warning,"[%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;
 
			}
 
		}
 

	
 

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

	
 
		// Do we need to send the new frame-packet?
 
		if (send_frame && cs->status == STATUS_ACTIVE) {
 
			SEND_COMMAND(PACKET_SERVER_FRAME)(cs);
 
		}
 
#ifndef ENABLE_NETWORK_SYNC_EVERY_FRAME
 
		// Is it time to send a sync-packet to all clients?
 
		if (last_sync_frame + _network_sync_freq < _frame_counter) {
 
			SEND_COMMAND(PACKET_SERVER_SYNC)(cs);
 
		}
 
#endif
 
	}
 

	
 
#ifndef ENABLE_NETWORK_SYNC_EVERY_FRAME
 
	// Update the last_sync_frame if needed!
 
	if (last_sync_frame + _network_sync_freq < _frame_counter) {
 
		last_sync_frame = _frame_counter;
 
	}
 
#endif
 

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

	
 
void NetworkServerMonthlyLoop()
 
void NetworkServerMonthlyLoop(void)
 
{
 
	NetworkAutoCleanCompanies();
 
}
 

	
 
#endif /* ENABLE_NETWORK */
network_server.h
Show inline comments
 
#ifndef NETWORK_SERVER_H
 
#define NETWORK_SERVER_H
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
DEF_SERVER_SEND_COMMAND(PACKET_SERVER_MAP);
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_ERROR_QUIT)(ClientState *cs, uint16 client_index, NetworkErrorCode errorno);
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_ERROR)(ClientState *cs, NetworkErrorCode error);
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_ERROR_QUIT)(NetworkClientState *cs, uint16 client_index, NetworkErrorCode errorno);
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_ERROR)(NetworkClientState *cs, NetworkErrorCode error);
 
DEF_SERVER_SEND_COMMAND(PACKET_SERVER_SHUTDOWN);
 
DEF_SERVER_SEND_COMMAND(PACKET_SERVER_NEWGAME);
 

	
 
bool NetworkFindName(char new_name[NETWORK_NAME_LENGTH]);
 
void NetworkServer_HandleChat(NetworkAction action, DestType desttype, int dest, const char *msg, byte from_index);
 

	
 
bool NetworkServer_ReadPackets(ClientState *cs);
 
void NetworkServer_Tick();
 
void NetworkServerMonthlyLoop();
 
bool NetworkServer_ReadPackets(NetworkClientState *cs);
 
void NetworkServer_Tick(void);
 
void NetworkServerMonthlyLoop(void);
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 
#endif // NETWORK_SERVER_H
network_udp.c
Show inline comments
 
@@ -61,97 +61,97 @@ DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIEN
 
	NetworkSendUDP_Packet(packet, client_addr);
 

	
 
	free(packet);
 

	
 
	DEBUG(net, 2)("[NET][UDP] Queried from %s", inet_ntoa(client_addr->sin_addr));
 
}
 

	
 
DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_SERVER_RESPONSE)
 
{
 
	NetworkGameList *item;
 
	byte game_info_version;
 

	
 
	// Just a fail-safe.. should never happen
 
	if (_network_udp_server)
 
		return;
 

	
 
	game_info_version = NetworkRecv_uint8(p);
 

	
 
	// Find next item
 
	item = NetworkGameListAddItem(inet_addr(inet_ntoa(client_addr->sin_addr)), ntohs(client_addr->sin_port));
 

	
 
	if (game_info_version == 1) {
 
		NetworkRecv_string(p, item->info.server_name, sizeof(item->info.server_name));
 
		NetworkRecv_string(p, item->info.server_revision, sizeof(item->info.server_revision));
 
		item->info.server_lang = NetworkRecv_uint8(p);
 
		item->info.use_password = NetworkRecv_uint8(p);
 
		item->info.clients_max = NetworkRecv_uint8(p);
 
		item->info.clients_on = NetworkRecv_uint8(p);
 
		item->info.spectators_on = NetworkRecv_uint8(p);
 
		item->info.game_date = NetworkRecv_uint16(p);
 
		item->info.start_date = NetworkRecv_uint16(p);
 
		NetworkRecv_string(p, item->info.map_name, sizeof(item->info.map_name));
 
		item->info.map_width = NetworkRecv_uint16(p);
 
		item->info.map_height = NetworkRecv_uint16(p);
 
		item->info.map_set = NetworkRecv_uint8(p);
 
		item->info.dedicated = NetworkRecv_uint8(p);
 

	
 
		if (item->info.hostname[0] == '\0')
 
			snprintf(item->info.hostname, sizeof(item->info.hostname), "%s", inet_ntoa(client_addr->sin_addr));
 
	}
 

	
 
	item->online = true;
 

	
 
	UpdateNetworkGameWindow(false);
 
}
 

	
 
DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_CLIENT_DETAIL_INFO)
 
{
 
	ClientState *cs;
 
	NetworkClientState *cs;
 
	NetworkClientInfo *ci;
 
	Packet *packet;
 
	Player *player;
 
	byte active = 0;
 
	byte current = 0;
 
	int i;
 

	
 
	// Just a fail-safe.. should never happen
 
	if (!_network_udp_server)
 
		return;
 

	
 
	packet = NetworkSend_Init(PACKET_UDP_SERVER_DETAIL_INFO);
 

	
 
	FOR_ALL_PLAYERS(player) {
 
		if (player->is_active)
 
			active++;
 
	}
 

	
 
	/* Send the amount of active companies */
 
	NetworkSend_uint8 (packet, NETWORK_COMPANY_INFO_VERSION);
 
	NetworkSend_uint8 (packet, active);
 

	
 
	/* Fetch the latest version of everything */
 
	NetworkPopulateCompanyInfo();
 

	
 
	/* Go through all the players */
 
	FOR_ALL_PLAYERS(player) {
 
		/* Skip non-active players */
 
		if (!player->is_active)
 
			continue;
 

	
 
		current++;
 

	
 
		/* Send the information */
 
		NetworkSend_uint8 (packet, current);
 

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

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

	
 
		for (i = 0; i < NETWORK_STATION_TYPES; i++)
 
			NetworkSend_uint16(packet, _network_player_info[player->index].num_station[i]);
 
@@ -426,89 +426,89 @@ void NetworkUDPSearchGame(void)
 
	// No UDP-socket yet..
 
	if (_udp_client_socket == INVALID_SOCKET)
 
		if (!NetworkUDPListen(0, 0))
 
			return;
 

	
 
	DEBUG(net, 0)("[NET][UDP] Searching server");
 

	
 
	NetworkUDPBroadCast();
 
	_network_udp_broadcast = 300; // Stay searching for 300 ticks
 
}
 

	
 
void NetworkUDPQueryServer(const byte* host, unsigned short port)
 
{
 
	struct sockaddr_in out_addr;
 
	Packet *p;
 
	NetworkGameList *item;
 
	char hostname[NETWORK_HOSTNAME_LENGTH];
 

	
 
	// No UDP-socket yet..
 
	if (_udp_client_socket == INVALID_SOCKET)
 
		if (!NetworkUDPListen(0, 0))
 
			return;
 

	
 
	ttd_strlcpy(hostname, host, sizeof(hostname));
 

	
 
	out_addr.sin_family = AF_INET;
 
	out_addr.sin_port = htons(port);
 
	out_addr.sin_addr.s_addr = NetworkResolveHost(host);
 

	
 
	// Clear item in gamelist
 
	item = NetworkGameListAddItem(inet_addr(inet_ntoa(out_addr.sin_addr)), ntohs(out_addr.sin_port));
 
	memset(&item->info, 0, sizeof(item->info));
 
	snprintf(item->info.server_name, sizeof(item->info.server_name), "%s", hostname);
 
	snprintf(item->info.hostname, sizeof(item->info.hostname), "%s", hostname);
 
	item->online = false;
 

	
 
	// Init the packet
 
	p = NetworkSend_Init(PACKET_UDP_CLIENT_FIND_SERVER);
 

	
 
	NetworkSendUDP_Packet(p, &out_addr);
 

	
 
	free(p);
 

	
 
	UpdateNetworkGameWindow(false);
 
}
 

	
 
/* Register us to the master server
 
     This function checks if it needs to send an advertise */
 
void NetworkUDPAdvertise()
 
void NetworkUDPAdvertise(void)
 
{
 
	struct sockaddr_in out_addr;
 
	Packet *p;
 

	
 
	/* Check if we should send an advertise */
 
	if (!_networking || !_network_server || !_network_udp_server || !_network_advertise)
 
		return;
 

	
 
	/* Only send once in the 450 game-days (about 15 minutes) */
 
	if (_network_last_advertise_date + 450 > _date)
 
		return;
 
	_network_last_advertise_date = _date;
 

	
 
	/* Find somewhere to send */
 
	out_addr.sin_family = AF_INET;
 
	out_addr.sin_port = htons(NETWORK_MASTER_SERVER_PORT);
 
	out_addr.sin_addr.s_addr = NetworkResolveHost(NETWORK_MASTER_SERVER_HOST);
 

	
 
	DEBUG(net, 1)("[NET][UDP] Advertising to master server");
 

	
 
	/* Send the packet */
 
	p = NetworkSend_Init(PACKET_UDP_SERVER_REGISTER);
 
	/* Packet is: WELCOME_MESSAGE, Version, server_port */
 
	NetworkSend_string(p, NETWORK_MASTER_SERVER_WELCOME_MESSAGE);
 
	NetworkSend_uint8(p, NETWORK_MASTER_SERVER_VERSION);
 
	NetworkSend_uint16(p, _network_server_port);
 
	NetworkSendUDP_Packet(p, &out_addr);
 
	free(p);
 
}
 

	
 
void NetworkUDPInitialize(void)
 
{
 
	_udp_client_socket = INVALID_SOCKET;
 
	_udp_server_socket = INVALID_SOCKET;
 

	
 
	_network_udp_server = false;
 
	_network_udp_broadcast = 0;
 
}
 

	
 
#endif /* ENABLE_NETWORK */
network_udp.h
Show inline comments
 
#ifndef NETWORK_LAN_H
 
#define NETWORK_LAN_H
 

	
 
void NetworkUDPInitialize(void);
 
bool NetworkUDPListen(uint32 host, uint16 port);
 
void NetworkUDPReceive(void);
 
void NetworkUDPSearchGame(void);
 
void NetworkUDPQueryServer(const byte* host, unsigned short port);
 
void NetworkUDPAdvertise();
 
void NetworkUDPAdvertise(void);
 

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