Changeset - r5469:9c38e5ce3f50
[Not reviewed]
master
! ! !
rubidium - 18 years ago 2007-01-02 17:34:03
rubidium@openttd.org
(svn r7751) -Codechange: move network_* to a new network map. Furthermore move the low level network functions to network/core, so they can be reused by the masterserver and website-serverlist-updater.
37 files changed:
0 comments (0 inline, 0 general)
Makefile
Show inline comments
 
@@ -732,13 +732,16 @@ SRCS += mixer.c
 
SRCS += music.c
 
SRCS += music_gui.c
 
SRCS += namegen.c
 
SRCS += network.c
 
SRCS += network_client.c
 
SRCS += network_data.c
 
SRCS += network_gamelist.c
 
SRCS += network_gui.c
 
SRCS += network_server.c
 
SRCS += network_udp.c
 
SRCS += network/core/packet.c
 
SRCS += network/core/tcp.c
 
SRCS += network/core/udp.c
 
SRCS += network/network.c
 
SRCS += network/network_client.c
 
SRCS += network/network_data.c
 
SRCS += network/network_gamelist.c
 
SRCS += network/network_gui.c
 
SRCS += network/network_server.c
 
SRCS += network/network_udp.c
 
SRCS += newgrf.c
 
SRCS += newgrf_cargo.c
 
SRCS += newgrf_config.c
ai/ai.c
Show inline comments
 
@@ -4,7 +4,7 @@
 
#include "../openttd.h"
 
#include "../variables.h"
 
#include "../command.h"
 
#include "../network.h"
 
#include "../network/network.h"
 
#include "ai.h"
 
#include "default/default.h"
 

	
ai/ai.h
Show inline comments
 
@@ -2,7 +2,7 @@
 
#define AI_H
 

	
 
#include "../functions.h"
 
#include "../network.h"
 
#include "../network/network.h"
 
#include "../player.h"
 
#include "../command.h"
 

	
command.c
Show inline comments
 
@@ -8,7 +8,7 @@
 
#include "gui.h"
 
#include "command.h"
 
#include "player.h"
 
#include "network.h"
 
#include "network/network.h"
 
#include "variables.h"
 
#include "genworld.h"
 

	
console.c
Show inline comments
 
@@ -13,9 +13,9 @@
 
#include <stdarg.h>
 
#include <string.h>
 
#include "console.h"
 
#include "network.h"
 
#include "network_data.h"
 
#include "network_server.h"
 
#include "network/network.h"
 
#include "network/network_data.h"
 
#include "network/network_server.h"
 

	
 
#define ICON_BUFFER 79
 
#define ICON_HISTORY_SIZE 20
console_cmds.c
Show inline comments
 
@@ -9,10 +9,10 @@
 
#include "saveload.h"
 
#include "string.h"
 
#include "variables.h"
 
#include "network_data.h"
 
#include "network_client.h"
 
#include "network_server.h"
 
#include "network_udp.h"
 
#include "network/network_data.h"
 
#include "network/network_client.h"
 
#include "network/network_server.h"
 
#include "network/network_udp.h"
 
#include "command.h"
 
#include "settings.h"
 
#include "fios.h"
 
@@ -22,7 +22,7 @@
 
#include "screenshot.h"
 
#include "genworld.h"
 
#include "date.h"
 
#include "network.h"
 
#include "network/network.h"
 

	
 
// ** scriptfile handling ** //
 
static FILE *_script_file;
date.c
Show inline comments
 
@@ -6,9 +6,9 @@
 
#include "variables.h"
 
#include "macros.h"
 
#include "vehicle.h"
 
#include "network.h"
 
#include "network_data.h"
 
#include "network_server.h"
 
#include "network/network.h"
 
#include "network/network_data.h"
 
#include "network/network_server.h"
 
#include "functions.h"
 
#include "currency.h"
 

	
date.h
Show inline comments
 
@@ -3,6 +3,8 @@
 
#ifndef DATE_H
 
#define DATE_H
 

	
 
#include "openttd.h"
 

	
 
/**
 
 * 1 day is 74 ticks; _date_fract used to be uint16 and incremented by 885. On
 
 *                    an overflow the new day begun and 65535 / 885 = 74.
economy.c
Show inline comments
 
@@ -19,10 +19,10 @@
 
#include "economy.h"
 
#include "industry.h"
 
#include "town.h"
 
#include "network.h"
 
#include "network/network.h"
 
#include "sound.h"
 
#include "engine.h"
 
#include "network_data.h"
 
#include "network/network_data.h"
 
#include "variables.h"
 
#include "vehicle_gui.h"
 
#include "ai/ai.h"
genworld.c
Show inline comments
 
@@ -11,7 +11,7 @@
 
#include "gfx.h"
 
#include "gfxinit.h"
 
#include "gui.h"
 
#include "network.h"
 
#include "network/network.h"
 
#include "debug.h"
 
#include "settings.h"
 
#include "heightmap.h"
genworld_gui.c
Show inline comments
 
@@ -19,7 +19,7 @@
 
#include "settings.h"
 
#include "debug.h"
 
#include "genworld.h"
 
#include "network.h"
 
#include "network/network.h"
 
#include "thread.h"
 
#include "date.h"
 
#include "newgrf_config.h"
intro_gui.c
Show inline comments
 
@@ -9,12 +9,12 @@
 
#include "gui.h"
 
#include "gfx.h"
 
#include "player.h"
 
#include "network.h"
 
#include "network/network.h"
 
#include "variables.h"
 
#include "settings.h"
 
#include "heightmap.h"
 
#include "genworld.h"
 
#include "network_gui.h"
 
#include "network/network_gui.h"
 
#include "newgrf.h"
 

	
 
static const Widget _select_game_widgets[] = {
main_gui.c
Show inline comments
 
@@ -23,7 +23,7 @@
 
#include "vehicle.h"
 
#include "console.h"
 
#include "sound.h"
 
#include "network.h"
 
#include "network/network.h"
 
#include "signs.h"
 
#include "waypoint.h"
 
#include "variables.h"
 
@@ -37,10 +37,10 @@
 
#include "vehicle_gui.h"
 
#include "newgrf_config.h"
 

	
 
#include "network_data.h"
 
#include "network_client.h"
 
#include "network_server.h"
 
#include "network_gui.h"
 
#include "network/network_data.h"
 
#include "network/network_client.h"
 
#include "network/network_server.h"
 
#include "network/network_gui.h"
 
#include "industry.h"
 

	
 
static int _rename_id = 1;
misc.c
Show inline comments
 
@@ -24,7 +24,7 @@ char _name_array[512][32];
 
#ifndef MERSENNE_TWISTER
 

	
 
#ifdef RANDOM_DEBUG
 
#include "network_data.h"
 
#include "network/network_data.h"
 
uint32 DoRandom(int line, const char *file)
 
#else // RANDOM_DEBUG
 
uint32 Random(void)
misc_cmd.c
Show inline comments
 
@@ -11,7 +11,7 @@
 
#include "window.h"
 
#include "gui.h"
 
#include "economy.h"
 
#include "network.h"
 
#include "network/network.h"
 
#include "variables.h"
 
#include "livery.h"
 

	
misc_gui.c
Show inline comments
 
@@ -22,7 +22,7 @@
 
#include "player.h"
 
#include "town.h"
 
#include "sound.h"
 
#include "network.h"
 
#include "network/network.h"
 
#include "string.h"
 
#include "variables.h"
 
#include "vehicle.h"
network.c
Show inline comments
 
deleted file
network.h
Show inline comments
 
deleted file
network/core/config.h
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifndef NETWORK_CORE_CONFIG_H
 
#define NETWORK_CORE_CONFIG_H
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
/** DNS hostname of the masterserver */
 
#define NETWORK_MASTER_SERVER_HOST "master.openttd.org"
 
/** Message sent to the masterserver to 'identify' this client as OpenTTD */
 
#define NETWORK_MASTER_SERVER_WELCOME_MESSAGE "OpenTTDRegister"
 

	
 
enum {
 
	NETWORK_MASTER_SERVER_PORT    = 3978, ///< The default port of the master server (UDP)
 
	NETWORK_DEFAULT_PORT          = 3979, ///< The default port of the game server (TCP & UDP)
 

	
 
	SEND_MTU                      = 1460, ///< Number of bytes we can pack in a single packet
 

	
 
	NETWORK_GAME_INFO_VERSION     =    4, ///< What version of game-info do we use?
 
	NETWORK_COMPANY_INFO_VERSION  =    4, ///< What version of company info is this?
 
	NETWORK_MASTER_SERVER_VERSION =    1, ///< What version of master-server-protocol do we use?
 

	
 
	NETWORK_NAME_LENGTH           =   80, ///< The maximum length of the server name and map name, in bytes including '\0'
 
	NETWORK_HOSTNAME_LENGTH       =   80, ///< The maximum length of the host name, in bytes including '\0'
 
	NETWORK_REVISION_LENGTH       =   15, ///< The maximum length of the revision, in bytes including '\0'
 
	NETWORK_PASSWORD_LENGTH       =   20, ///< The maximum length of the password, in bytes including '\0'
 
	NETWORK_PLAYERS_LENGTH        =  200, ///< The maximum length for the list of players that controls a company, in bytes including '\0'
 
	NETWORK_CLIENT_NAME_LENGTH    =   25, ///< The maximum length of a player, in bytes including '\0'
 
	NETWORK_RCONCOMMAND_LENGTH    =  500, ///< The maximum length of a rconsole command, in bytes including '\0'
 

	
 
	NETWORK_GRF_NAME_LENGTH       =   80, ///< Maximum length of the name of a GRF
 
	/**
 
	 * Maximum number of GRFs that can be sent.
 
	 * This value is related to number of handles (files) OpenTTD can open.
 
	 * This is currently 64 and about 10 are currently used when OpenTTD loads
 
	 * without any NewGRFs. Therefore one can only load about 55 NewGRFs, so
 
	 * this is not a limit, but rather a way to easily check whether the limit
 
	 * imposed by the handle count is reached. Secondly it isn't possible to
 
	 * send much more GRF IDs + MD5sums in the PACKET_UDP_SERVER_RESPONSE, due
 
	 * to the limited size of UDP packets.
 
	 */
 
	NETWORK_MAX_GRF_COUNT         =   55,
 

	
 
	NETWORK_NUM_LANGUAGES         =    4, ///< Number of known languages (to the network protocol) + 1 for 'any'.
 
};
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 
#endif /* NETWORK_CORE_CONFIG_H */
network/core/game.h
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifndef NETWORK_CORE_GAME_H
 
#define NETWORK_CORE_GAME_H
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
/**
 
 * @file game.h Information about a game that is sent between a
 
 *              game server, game client and masterserver.
 
 */
 

	
 
/**
 
 * This is the struct used by both client and server
 
 * some fields will be empty on the client (like game_password) by default
 
 * and only filled with data a player enters.
 
 */
 
typedef struct NetworkGameInfo {
 
	byte game_info_version;                         ///< Version of the game info
 
	char server_name[NETWORK_NAME_LENGTH];          ///< Server name
 
	char hostname[NETWORK_HOSTNAME_LENGTH];         ///< Hostname of the server (if any)
 
	char server_revision[NETWORK_REVISION_LENGTH];  ///< The version number the server is using (e.g.: 'r304' or 0.5.0)
 
	bool version_compatible;                        ///< Can we connect to this server or not? (based on server_revision)
 
	bool compatible;                                ///< Can we connect to this server or not? (based on server_revision _and_ grf_match
 
	byte server_lang;                               ///< Language of the server (we should make a nice table for this)
 
	byte use_password;                              ///< Is set to != 0 if it uses a password
 
	char server_password[NETWORK_PASSWORD_LENGTH];  ///< On the server: the game password, on the client: != "" if server has password
 
	byte clients_max;                               ///< Max clients allowed on server
 
	byte clients_on;                                ///< Current count of clients on server
 
	byte companies_max;                             ///< Max companies allowed on server
 
	byte companies_on;                              ///< How many started companies do we have
 
	byte spectators_max;                            ///< Max spectators allowed on server
 
	byte spectators_on;                             ///< How many spectators do we have?
 
	Date game_date;                                 ///< Current date
 
	Date start_date;                                ///< When the game started
 
	char map_name[NETWORK_NAME_LENGTH];             ///< Map which is played ["random" for a randomized map]
 
	uint16 map_width;                               ///< Map width
 
	uint16 map_height;                              ///< Map height
 
	byte map_set;                                   ///< Graphical set
 
	bool dedicated;                                 ///< Is this a dedicated server?
 
	char rcon_password[NETWORK_PASSWORD_LENGTH];    ///< RCon password for the server. "" if rcon is disabled
 
	struct GRFConfig *grfconfig;                    ///< List of NewGRF files used
 
} NetworkGameInfo;
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 
#endif /* NETWORK_CORE_GAME_H */
network/core/os_abstraction.h
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifndef NETWORK_CORE_OS_ABSTRACTION_H
 
#define NETWORK_CORE_OS_ABSTRACTION_H
 

	
 
/**
 
 * @file os_abstraction.h Network stuff has many things that needs to be
 
 *                        included and/or implemented by default.
 
 *                        All those things are in this file.
 
 */
 

	
 
/* Include standard stuff per OS */
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
/* Windows stuff */
 
#if defined(WIN32) || defined(WIN64)
 
#include <winsock2.h>
 
#include <ws2tcpip.h>
 
#include <windows.h>
 

	
 
#if !(defined(__MINGW32__) || defined(__CYGWIN__))
 
	/* Windows has some different names for some types */
 
	typedef SSIZE_T ssize_t;
 
	typedef int socklen_t;
 
#endif
 

	
 
#define GET_LAST_ERROR() WSAGetLastError()
 
#define EWOULDBLOCK WSAEWOULDBLOCK
 
/* Windows has some different names for some types */
 
typedef unsigned long in_addr_t;
 
#endif /* WIN32 */
 

	
 
/* UNIX stuff */
 
#if defined(UNIX)
 
#	define SOCKET int
 
#	define INVALID_SOCKET -1
 
#	if !defined(__MORPHOS__) && !defined(__AMIGA__)
 
#		define ioctlsocket ioctl
 
#	if !defined(BEOS_NET_SERVER)
 
#		define closesocket close
 
#	endif
 
#		define GET_LAST_ERROR() (errno)
 
#	endif
 
/* Need this for FIONREAD on solaris */
 
#	define BSD_COMP
 

	
 
/* Includes needed for UNIX-like systems */
 
#	include <unistd.h>
 
#	include <sys/ioctl.h>
 
#	if defined(__BEOS__) && defined(BEOS_NET_SERVER)
 
#		include <be/net/socket.h>
 
#		include <be/kernel/OS.h> // snooze()
 
#		include <be/net/netdb.h>
 
		typedef unsigned long in_addr_t;
 
#		define INADDR_NONE INADDR_BROADCAST
 
#	else
 
#		include <sys/socket.h>
 
#		include <netinet/in.h>
 
#		include <netinet/tcp.h>
 
#		include <arpa/inet.h>
 
#		include <net/if.h>
 
/* According to glibc/NEWS, <ifaddrs.h> appeared in glibc-2.3. */
 
#		if !defined(__sgi__) && !defined(SUNOS) && !defined(__MORPHOS__) && !defined(__BEOS__) && !defined(__INNOTEK_LIBC__) \
 
		   && !(defined(__GLIBC__) && (__GLIBC__ <= 2) && (__GLIBC_MINOR__ <= 2)) && !defined(__dietlibc__)
 
/* If for any reason ifaddrs.h does not exist on your system, comment out
 
 *   the following two lines and an alternative way will be used to fetch
 
 *   the list of IPs from the system. */
 
#			include <ifaddrs.h>
 
#			define HAVE_GETIFADDRS
 
#		endif
 
#		if defined(SUNOS) || defined(__MORPHOS__) || defined(__BEOS__)
 
#			define INADDR_NONE 0xffffffff
 
#		endif
 
#		if defined(__BEOS__) && !defined(BEOS_NET_SERVER)
 
			/* needed on Zeta */
 
#			include <sys/sockio.h>
 
#		endif
 
#	endif /* BEOS_NET_SERVER */
 

	
 
#	if !defined(__BEOS__) && defined(__GLIBC__) && (__GLIBC__ <= 2) && (__GLIBC_MINOR__ <= 1)
 
		typedef uint32_t in_addr_t;
 
#	endif
 

	
 
#	include <errno.h>
 
#	include <sys/time.h>
 
#	include <netdb.h>
 
#endif // UNIX
 

	
 
#ifdef __BEOS__
 
	typedef int socklen_t;
 
#endif
 

	
 
/* OS/2 stuff */
 
#if defined(__OS2__)
 
#	define SOCKET int
 
#	define INVALID_SOCKET -1
 
#	define ioctlsocket ioctl
 
#	define closesocket close
 
#	define GET_LAST_ERROR() (sock_errno())
 

	
 
/* Includes needed for OS/2 systems */
 
#	include <types.h>
 
#	include <unistd.h>
 
#	include <sys/ioctl.h>
 
#	include <sys/socket.h>
 
#	include <netinet/in.h>
 
#	include <netinet/tcp.h>
 
#	include <arpa/inet.h>
 
#	include <net/if.h>
 
#	include <errno.h>
 
#	include <sys/time.h>
 
#	include <netdb.h>
 
#	include <nerrno.h>
 
#	define INADDR_NONE 0xffffffff
 

	
 
typedef int socklen_t;
 
#if !defined(__INNOTEK_LIBC__)
 
typedef unsigned long in_addr_t;
 
#endif /* __INNOTEK_LIBC__ */
 
#endif /* OS/2 */
 

	
 
/* MorphOS and Amiga stuff */
 
#if defined(__MORPHOS__) || defined(__AMIGA__)
 
#	include <exec/types.h>
 
#	include <proto/exec.h>   // required for Open/CloseLibrary()
 
#	if defined(__MORPHOS__)
 
#		include <sys/filio.h>  // FIO* defines
 
#		include <sys/sockio.h> // SIO* defines
 
#		include <netinet/in.h>
 
#	else /* __AMIGA__ */
 
#		include	<proto/socket.h>
 
#	endif
 

	
 
/* Make the names compatible */
 
#	define closesocket(s) CloseSocket(s)
 
#	define GET_LAST_ERROR() Errno()
 
#	define ioctlsocket(s,request,status) IoctlSocket((LONG)s,(ULONG)request,(char*)status)
 
#	define ioctl ioctlsocket
 

	
 
	typedef unsigned int in_addr_t;
 
	typedef long         socklen_t;
 
	extern struct Library *SocketBase;
 

	
 
#	ifdef __AMIGA__
 
	/* for usleep() implementation */
 
	extern struct Device      *TimerBase;
 
	extern struct MsgPort     *TimerPort;
 
	extern struct timerequest *TimerRequest;
 
#	endif
 
#endif // __MORPHOS__ || __AMIGA__
 

	
 
static inline bool SetNonBlocking(int d)
 
{
 
#ifdef WIN32
 
	u_long nonblocking = 1;
 
#else
 
	int nonblocking = 1;
 
#endif
 
#if defined(__BEOS__) && defined(BEOS_NET_SERVER)
 
	return setsockopt(d, SOL_SOCKET, SO_NONBLOCK, &nonblocking, sizeof(nonblocking)) == 0;
 
#else
 
	return ioctlsocket(d, FIONBIO, &nonblocking) == 0;
 
#endif
 
}
 

	
 
static inline bool SetNoDelay(int d)
 
{
 
	/* 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 */
 
	return setsockopt(d, IPPROTO_TCP, TCP_NODELAY, (const char*)&b, sizeof(b)) == 0;
 
#else
 
	return true;
 
#endif
 
}
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 
#endif /* NETWORK_CORE_OS_ABSTRACTION_H */
network/core/packet.c
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "../../stdafx.h"
 
#include "../../macros.h"
 
#include "../../string.h"
 

	
 
#include "os_abstraction.h"
 
#include "config.h"
 
#include "packet.h"
 

	
 
/**
 
 * @file packet.h Basic functions to create, fill and read packets.
 
 */
 

	
 

	
 
/* Do not want to include functions.h and all required headers */
 
extern void NORETURN CDECL error(const char *str, ...);
 

	
 

	
 
/**
 
 * Create a packet for sending
 
 * @param type the of packet
 
 * @return the newly created packet
 
 */
 
Packet *NetworkSend_Init(PacketType type)
 
{
 
	Packet *packet = malloc(sizeof(Packet));
 
	/* An error is inplace here, because it simply means we ran out of memory. */
 
	if (packet == NULL) error("Failed to allocate Packet");
 

	
 
	/* Skip the size so we can write that in before sending the packet */
 
	packet->size = sizeof(packet->size);
 
	packet->buffer[packet->size++] = type;
 
	packet->pos = 0;
 

	
 
	return packet;
 
}
 

	
 
/**
 
 * Writes the packet size from the raw packet from packet->size
 
 * @param packet the packet to write the size of
 
 */
 
void NetworkSend_FillPacketSize(Packet *packet)
 
{
 
	packet->buffer[0] = GB(packet->size, 0, 8);
 
	packet->buffer[1] = GB(packet->size, 8, 8);
 
}
 

	
 
/**
 
 * The next couple of functions make sure we can send
 
 *  uint8, uint16, uint32 and uint64 endian-safe
 
 *  over the network. The least significant bytes are
 
 *  sent first.
 
 *
 
 *  So 0x01234567 would be sent as 67 45 23 01.
 
 */
 

	
 
void NetworkSend_uint8(Packet *packet, uint8 data)
 
{
 
	assert(packet->size < sizeof(packet->buffer) - sizeof(data));
 
	packet->buffer[packet->size++] = data;
 
}
 

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

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

	
 
void NetworkSend_uint64(Packet *packet, uint64 data)
 
{
 
	assert(packet->size < sizeof(packet->buffer) - sizeof(data));
 
	packet->buffer[packet->size++] = GB(data,  0, 8);
 
	packet->buffer[packet->size++] = GB(data,  8, 8);
 
	packet->buffer[packet->size++] = GB(data, 16, 8);
 
	packet->buffer[packet->size++] = GB(data, 24, 8);
 
	packet->buffer[packet->size++] = GB(data, 32, 8);
 
	packet->buffer[packet->size++] = GB(data, 40, 8);
 
	packet->buffer[packet->size++] = GB(data, 48, 8);
 
	packet->buffer[packet->size++] = GB(data, 56, 8);
 
}
 

	
 
/**
 
 *  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') {}
 
}
 

	
 

	
 
/**
 
 * Receiving commands
 
 * Again, the next couple of functions are endian-safe
 
 *  see the comment before NetworkSend_uint8 for more info.
 
 */
 

	
 

	
 
extern uint CloseConnection(NetworkClientState *cs);
 

	
 
/** Is it safe to read from the packet, i.e. didn't we run over the buffer ? */
 
static inline bool CanReadFromPacket(NetworkClientState *cs, Packet *packet, uint bytes_to_read)
 
{
 
	/* Don't allow reading from a closed socket */
 
	if (HasClientQuit(cs)) return false;
 

	
 
	/* Check if variable is within packet-size */
 
	if (packet->pos + bytes_to_read > packet->size) {
 
		CloseConnection(cs);
 
		return false;
 
	}
 

	
 
	return true;
 
}
 

	
 
/**
 
 * Reads the packet size from the raw packet and stores it in the packet->size
 
 * @param packet the packet to read the size of
 
 */
 
void NetworkRecv_ReadPacketSize(Packet *packet)
 
{
 
	packet->size  = (uint16)packet->buffer[0];
 
	packet->size += (uint16)packet->buffer[1] << 8;
 
}
 

	
 
uint8 NetworkRecv_uint8(NetworkClientState *cs, Packet *packet)
 
{
 
	uint8 n;
 

	
 
	if (!CanReadFromPacket(cs, packet, sizeof(n))) return 0;
 

	
 
	n = packet->buffer[packet->pos++];
 
	return n;
 
}
 

	
 
uint16 NetworkRecv_uint16(NetworkClientState *cs, Packet *packet)
 
{
 
	uint16 n;
 

	
 
	if (!CanReadFromPacket(cs, packet, sizeof(n))) return 0;
 

	
 
	n  = (uint16)packet->buffer[packet->pos++];
 
	n += (uint16)packet->buffer[packet->pos++] << 8;
 
	return n;
 
}
 

	
 
uint32 NetworkRecv_uint32(NetworkClientState *cs, Packet *packet)
 
{
 
	uint32 n;
 

	
 
	if (!CanReadFromPacket(cs, packet, sizeof(n))) return 0;
 

	
 
	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(NetworkClientState *cs, Packet *packet)
 
{
 
	uint64 n;
 

	
 
	if (!CanReadFromPacket(cs, packet, sizeof(n))) return 0;
 

	
 
	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(NetworkClientState *cs, Packet *p, char *buffer, size_t size)
 
{
 
	PacketSize pos;
 
	char *bufp = buffer;
 

	
 
	/* Don't allow reading from a closed socket */
 
	if (HasClientQuit(cs)) return;
 

	
 
	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 than packet can be read out correctly for the rest */
 
		while (pos < p->size && p->buffer[pos] != '\0') pos++;
 
		pos++;
 
	}
 
	p->pos = pos;
 

	
 
	str_validate(bufp);
 
}
 

	
 
#endif /* ENABLE_NETWORK */
network/core/packet.h
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifndef NETWORK_CORE_PACKET_H
 
#define NETWORK_CORE_PACKET_H
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
/**
 
 * @file packet.h Basic functions to create, fill and read packets.
 
 */
 

	
 
typedef struct NetworkClientState NetworkClientState;
 

	
 
/**
 
 * Queries the network client state struct to determine whether
 
 * the client has quit. It indirectly also queries whether the
 
 * packet is corrupt as the connection will be closed if it is
 
 * reading beyond the boundary of the received packet.
 
 * @param cs the state to query
 
 * @param true if the connection should be considered dropped
 
 */
 
bool HasClientQuit(NetworkClientState *cs);
 

	
 
typedef uint16 PacketSize; ///< Size of the whole packet.
 
typedef uint8  PacketType; ///< Identifier for the packet
 

	
 
/**
 
 * Internal entity of a packet. As everything is sent as a packet,
 
 * all network communication will need to call the functions that
 
 * populate the packet.
 
 * Every packet can be at most SEND_MTU bytes. Overflowing this
 
 * limit will give an assertion when sending (i.e. writing) the
 
 * packet. Reading past the size of the packet when receiving
 
 * will return all 0 values and "" in case of the string.
 
 */
 
typedef struct Packet {
 
	/** The next packet. Used for queueing packets before sending. */
 
	struct Packet *next;
 
	/** The size of the whole packet for received packets. For packets
 
	 * that will be sent, the value is filled in just before the
 
	 * actual transmission. */
 
	PacketSize size;
 
	/** The current read/write position in the packet */
 
	PacketSize pos;
 
	/** The buffer of this packet */
 
	byte buffer[SEND_MTU];
 
} Packet;
 

	
 

	
 
Packet *NetworkSend_Init(PacketType type);
 
void NetworkSend_FillPacketSize(Packet *packet);
 
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 NetworkRecv_ReadPacketSize(Packet *packet);
 
uint8  NetworkRecv_uint8 (NetworkClientState *cs, Packet *packet);
 
uint16 NetworkRecv_uint16(NetworkClientState *cs, Packet *packet);
 
uint32 NetworkRecv_uint32(NetworkClientState *cs, Packet *packet);
 
uint64 NetworkRecv_uint64(NetworkClientState *cs, Packet *packet);
 
void   NetworkRecv_string(NetworkClientState *cs, Packet *packet, char* buffer, size_t size);
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 
#endif /* NETWORK_CORE_PACKET_H */
network/core/tcp.c
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "../../stdafx.h"
 
#include "../../debug.h"
 
#include "../../openttd.h"
 
#include "../../variables.h"
 
#include "../../table/strings.h"
 
#include "../../functions.h"
 

	
 
#include "os_abstraction.h"
 
#include "config.h"
 
#include "packet.h"
 
#include "../network_data.h"
 
#include "tcp.h"
 

	
 
/**
 
 * @file tcp.c Basic functions to receive and send TCP packets.
 
 */
 

	
 
/**
 
 * 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
 
 * @param cs the client to close the connection of
 
 * @return the new status
 
 */
 
NetworkRecvStatus CloseConnection(NetworkClientState *cs)
 
{
 
	NetworkCloseClient(cs);
 

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

	
 
		return NETWORK_RECV_STATUS_CONN_LOST;
 
	}
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Whether the client has quit or not (used in packet.c)
 
 * @param cs the client to check
 
 * @return true if the client has quit
 
 */
 
bool HasClientQuit(NetworkClientState *cs)
 
{
 
	return cs->has_quit;
 
}
 

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

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

	
 
	NetworkSend_FillPacketSize(packet);
 

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

	
 
/**
 
 * 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
 
 * @param cs the client to send the packets for
 
 */
 
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, "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;
 
}
 

	
 
/**
 
 * Receives a packet for the given client
 
 * @param cs     the client to (try to) receive a packet for
 
 * @param status the variable to store the status into
 
 * @return the received packet (or NULL when it didn't receive one)
 
 */
 
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... (104 is connection reset by peer) */
 
					if (err != 104) DEBUG(net, 0, "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 */
 
				*status = CloseConnection(cs);
 
				return NULL;
 
			}
 
			p->pos += res;
 
		}
 

	
 
		NetworkRecv_ReadPacketSize(p);
 

	
 
		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... (104 is connection reset by peer) */
 
				if (err != 104) DEBUG(net, 0, "recv failed with error %d", err);
 
				*status = CloseConnection(cs);
 
				return NULL;
 
			}
 
			/* Connection would block */
 
			return NULL;
 
		}
 
		if (res == 0) {
 
			/* Client/server has left */
 
			*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;
 
}
 

	
 
#endif /* ENABLE_NETWORK */
network/core/tcp.h
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifndef NETWORK_CORE_TCP_H
 
#define NETWORK_CORE_TCP_H
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
/**
 
 * @file tcp.h Basic functions to receive and send TCP packets.
 
 */
 

	
 
/**
 
 * Enum with all types of UDP packets.
 
 * The order of the first 4 packets MUST not be changed, as
 
 * it protects old clients from joining newer servers
 
 * (because SERVER_ERROR is the respond to a wrong revision)
 
 */
 
enum {
 
	PACKET_SERVER_FULL,
 
	PACKET_SERVER_BANNED,
 
	PACKET_CLIENT_JOIN,
 
	PACKET_SERVER_ERROR,
 
	PACKET_CLIENT_COMPANY_INFO,
 
	PACKET_SERVER_COMPANY_INFO,
 
	PACKET_SERVER_CLIENT_INFO,
 
	PACKET_SERVER_NEED_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_SERVER_RCON,
 
	PACKET_CLIENT_RCON,
 
	PACKET_END                   ///< Must ALWAYS be on the end of this list!! (period)
 
};
 

	
 
void NetworkSend_Packet(Packet *packet, NetworkClientState *cs);
 
Packet *NetworkRecv_Packet(NetworkClientState *cs, NetworkRecvStatus *status);
 
bool NetworkSend_Packets(NetworkClientState *cs);
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 
#endif /* NETWORK_CORE_TCP_H */
network/core/udp.c
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "../../stdafx.h"
 
#include "../../date.h"
 
#include "../../debug.h"
 
#include "../../macros.h"
 
#include "../../newgrf_config.h"
 

	
 
#include "os_abstraction.h"
 
#include "config.h"
 
#include "game.h"
 
#include "packet.h"
 
#include "udp.h"
 

	
 
/**
 
 * @file udp.c Basic functions to receive and send UDP packets.
 
 */
 

	
 
/**
 
 * Send a packet over UDP
 
 * @param udp  the socket to send over
 
 * @param p    the packet to send
 
 * @param recv the receiver (target) of the packet
 
 */
 
void NetworkSendUDP_Packet(SOCKET udp, Packet *p, struct sockaddr_in *recv)
 
{
 
	int res;
 

	
 
	NetworkSend_FillPacketSize(p);
 

	
 
	/* Send the buffer */
 
	res = sendto(udp, p->buffer, p->size, 0, (struct sockaddr *)recv, sizeof(*recv));
 

	
 
	/* Check for any errors, but ignore it otherwise */
 
	if (res == -1) DEBUG(net, 1, "[udp] sendto failed with: %i", GET_LAST_ERROR());
 
}
 

	
 
/**
 
 * Start listening on the given host and port.
 
 * @param udp       the place where the (references to the) UDP are stored
 
 * @param host      the host (ip) to listen on
 
 * @param port      the port to listen on
 
 * @param broadcast whether to allow broadcast sending/receiving
 
 * @return true if the listening succeeded
 
 */
 
bool NetworkUDPListen(SOCKET *udp, uint32 host, uint16 port, bool broadcast)
 
{
 
	struct sockaddr_in sin;
 

	
 
	/* Make sure socket is closed */
 
	closesocket(*udp);
 

	
 
	*udp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
 
	if (*udp == INVALID_SOCKET) {
 
		DEBUG(net, 0, "[udp] failed to start UDP listener");
 
		return false;
 
	}
 

	
 
	/* set nonblocking mode for socket */
 
	{
 
		unsigned long blocking = 1;
 
#ifndef BEOS_NET_SERVER
 
		ioctlsocket(*udp, FIONBIO, &blocking);
 
#else
 
		setsockopt(*udp, SOL_SOCKET, SO_NONBLOCK, &blocking, NULL);
 
#endif
 
	}
 

	
 
	sin.sin_family = AF_INET;
 
	/* Listen on all IPs */
 
	sin.sin_addr.s_addr = host;
 
	sin.sin_port = htons(port);
 

	
 
	if (bind(*udp, (struct sockaddr*)&sin, sizeof(sin)) != 0) {
 
		DEBUG(net, 0, "[udp] bind failed on %s:%i", inet_ntoa(*(struct in_addr *)&host), port);
 
		return false;
 
	}
 

	
 
	if (broadcast) {
 
		/* Enable broadcast */
 
		unsigned long val = 1;
 
#ifndef BEOS_NET_SERVER // will work around this, some day; maybe.
 
		setsockopt(*udp, SOL_SOCKET, SO_BROADCAST, (char *) &val , sizeof(val));
 
#endif
 
	}
 

	
 
	DEBUG(net, 1, "[udp] listening on port %s:%d", inet_ntoa(*(struct in_addr *)&host), port);
 

	
 
	return true;
 
}
 

	
 
/**
 
 * Receive a packet at UDP level
 
 * @param udp the socket to receive the packet on
 
 */
 
void NetworkUDPReceive(SOCKET udp)
 
{
 
	struct sockaddr_in client_addr;
 
	socklen_t client_len;
 
	int nbytes;
 
	Packet p;
 
	int packet_len;
 

	
 
	packet_len = sizeof(p.buffer);
 
	client_len = sizeof(client_addr);
 

	
 
	/* Try to receive anything */
 
	nbytes = recvfrom(udp, p.buffer, packet_len, 0, (struct sockaddr *)&client_addr, &client_len);
 

	
 
	/* We got some bytes for the base header of the packet.
 
	 * Assume we received the whole packet. */
 
	if (nbytes > 2) {
 
		NetworkRecv_ReadPacketSize(&p);
 

	
 
		/* Put the position on the right place */
 
		p.pos = 2;
 
		p.next = NULL;
 

	
 
		/* Handle the packet */
 
		NetworkHandleUDPPacket(&p, &client_addr);
 
	}
 
}
 

	
 

	
 
/**
 
 * Serializes the GRFIdentifier (GRF ID and MD5 checksum) to the packet
 
 * @param p the packet to write the data to
 
 * @param c the configuration to write the GRF ID and MD5 checksum from
 
 */
 
void NetworkSend_GRFIdentifier(Packet *p, const GRFConfig *c)
 
{
 
	uint j;
 
	NetworkSend_uint32(p, c->grfid);
 
	for (j = 0; j < sizeof(c->md5sum); j++) {
 
		NetworkSend_uint8 (p, c->md5sum[j]);
 
	}
 
}
 

	
 
/**
 
 * Deserializes the GRFIdentifier (GRF ID and MD5 checksum) from the packet
 
 * @param cs the client state (for closing connect on out-of-bounds reading etc)
 
 * @param p  the packet to read the data from
 
 * @param c  the configuration to write the GRF ID and MD5 checksum to
 
 */
 
void NetworkRecv_GRFIdentifier(NetworkClientState *cs, Packet *p, GRFConfig *c)
 
{
 
	uint j;
 
	c->grfid = NetworkRecv_uint32(cs, p);
 
	for (j = 0; j < sizeof(c->md5sum); j++) {
 
		c->md5sum[j] = NetworkRecv_uint8(cs, p);
 
	}
 
}
 

	
 

	
 
/**
 
 * Serializes the NetworkGameInfo struct to the packet
 
 * @param p    the packet to write the data to
 
 * @param info the NetworkGameInfo struct to serialize
 
 */
 
void NetworkSend_NetworkGameInfo(Packet *p, const NetworkGameInfo *info)
 
{
 
	NetworkSend_uint8 (p, NETWORK_GAME_INFO_VERSION);
 

	
 
	/*
 
	 *              Please observe the order.
 
	 * The parts must be read in the same order as they are sent!
 
	 */
 

	
 

	
 
	/* NETWORK_GAME_INFO_VERSION = 4 */
 
	{
 
		/* Only send the GRF Identification (GRF_ID and MD5 checksum) of
 
		 * the GRFs that are needed, i.e. the ones that the server has
 
		 * selected in the NewGRF GUI and not the ones that are used due
 
		 * to the fact that they are in [newgrf-static] in openttd.cfg */
 
		const GRFConfig *c;
 
		uint count = 0;
 

	
 
		/* Count number of GRFs to send information about */
 
		for (c = info->grfconfig; c != NULL; c = c->next) {
 
			if (!HASBIT(c->flags, GCF_STATIC)) count++;
 
		}
 
		NetworkSend_uint8 (p, count); // Send number of GRFs
 

	
 
		/* Send actual GRF Identifications */
 
		for (c = info->grfconfig; c != NULL; c = c->next) {
 
			if (!HASBIT(c->flags, GCF_STATIC)) NetworkSend_GRFIdentifier(p, c);
 
		}
 
	}
 

	
 
	/* NETWORK_GAME_INFO_VERSION = 3 */
 
	NetworkSend_uint32(p, info->game_date);
 
	NetworkSend_uint32(p, info->start_date);
 

	
 
	/* NETWORK_GAME_INFO_VERSION = 2 */
 
	NetworkSend_uint8 (p, info->companies_max);
 
	NetworkSend_uint8 (p, info->companies_on);
 
	NetworkSend_uint8 (p, info->spectators_max);
 

	
 
	/* NETWORK_GAME_INFO_VERSION = 1 */
 
	NetworkSend_string(p, info->server_name);
 
	NetworkSend_string(p, info->server_revision);
 
	NetworkSend_uint8 (p, info->server_lang);
 
	NetworkSend_uint8 (p, info->use_password);
 
	NetworkSend_uint8 (p, info->clients_max);
 
	NetworkSend_uint8 (p, info->clients_on);
 
	NetworkSend_uint8 (p, info->spectators_on);
 
	NetworkSend_string(p, info->map_name);
 
	NetworkSend_uint16(p, info->map_width);
 
	NetworkSend_uint16(p, info->map_height);
 
	NetworkSend_uint8 (p, info->map_set);
 
	NetworkSend_uint8 (p, info->dedicated);
 
}
 

	
 
/**
 
 * Deserializes the NetworkGameInfo struct from the packet
 
 * @param cs   the client state (for closing connect on out-of-bounds reading etc)
 
 * @param p    the packet to read the data from
 
 * @param info the NetworkGameInfo to deserialize into
 
 */
 
void NetworkRecv_NetworkGameInfo(NetworkClientState *cs, Packet *p, NetworkGameInfo *info)
 
{
 
	info->game_info_version = NetworkRecv_uint8(cs, p);
 

	
 
	/*
 
	 *              Please observe the order.
 
	 * The parts must be read in the same order as they are sent!
 
	 */
 

	
 
	switch (info->game_info_version) {
 
		case 4: {
 
			GRFConfig *c, **dst = &info->grfconfig;
 
			uint i;
 
			uint num_grfs = NetworkRecv_uint8(cs, p);
 

	
 
			for (i = 0; i < num_grfs; i++) {
 
				c = calloc(1, sizeof(*c));
 
				NetworkRecv_GRFIdentifier(cs, p, c);
 
				HandleIncomingNetworkGameInfoGRFConfig(c);
 

	
 
				/* Append GRFConfig to the list */
 
				*dst = c;
 
				dst = &c->next;
 
			}
 
		} /* Fallthrough */
 
		case 3:
 
			info->game_date      = NetworkRecv_uint32(cs, p);
 
			info->start_date     = NetworkRecv_uint32(cs, p);
 
			/* Fallthrough */
 
		case 2:
 
			info->companies_max  = NetworkRecv_uint8 (cs, p);
 
			info->companies_on   = NetworkRecv_uint8 (cs, p);
 
			info->spectators_max = NetworkRecv_uint8 (cs, p);
 
			/* Fallthrough */
 
		case 1:
 
			NetworkRecv_string(cs, p, info->server_name,     sizeof(info->server_name));
 
			NetworkRecv_string(cs, p, info->server_revision, sizeof(info->server_revision));
 
			info->server_lang    = NetworkRecv_uint8 (cs, p);
 
			info->use_password   = NetworkRecv_uint8 (cs, p);
 
			info->clients_max    = NetworkRecv_uint8 (cs, p);
 
			info->clients_on     = NetworkRecv_uint8 (cs, p);
 
			info->spectators_on  = NetworkRecv_uint8 (cs, p);
 
			if (info->game_info_version < 3) { // 16 bits dates got scrapped and are read earlier
 
				info->game_date    = NetworkRecv_uint16(cs, p) + DAYS_TILL_ORIGINAL_BASE_YEAR;
 
				info->start_date   = NetworkRecv_uint16(cs, p) + DAYS_TILL_ORIGINAL_BASE_YEAR;
 
			}
 
			NetworkRecv_string(cs, p, info->map_name, sizeof(info->map_name));
 
			info->map_width      = NetworkRecv_uint16(cs, p);
 
			info->map_height     = NetworkRecv_uint16(cs, p);
 
			info->map_set        = NetworkRecv_uint8 (cs, p);
 
			info->dedicated      = NetworkRecv_uint8 (cs, p);
 
	}
 
}
 

	
 
#endif /* ENABLE_NETWORK */
network/core/udp.h
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifndef NETWORK_CORE_UDP_H
 
#define NETWORK_CORE_UDP_H
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
/**
 
 * @file udp.h Basic functions to receive and send UDP packets.
 
 */
 

	
 
///** Sending/receiving of UDP packets **////
 

	
 
void NetworkSendUDP_Packet(SOCKET udp, Packet *p, struct sockaddr_in *recv);
 
bool NetworkUDPListen(SOCKET *udp, uint32 host, uint16 port, bool broadcast);
 
void NetworkUDPReceive(SOCKET udp);
 

	
 
/**
 
 * Function that is called for every received UDP packet.
 
 * @param packet      the received packet
 
 * @param client_addr the address of the sender of the packet
 
 */
 
void NetworkHandleUDPPacket(Packet *p, struct sockaddr_in *client_addr);
 

	
 

	
 
///** Sending/receiving of (large) chuncks of UDP packets **////
 

	
 

	
 
/** Enum with all types of UDP packets. The order MUST not be changed **/
 
enum {
 
	PACKET_UDP_CLIENT_FIND_SERVER,   ///< Queries a game server for game information
 
	PACKET_UDP_SERVER_RESPONSE,      ///< Reply of the game server with game information
 
	PACKET_UDP_CLIENT_DETAIL_INFO,   ///< Queries a game server about details of the game, such as companies
 
	PACKET_UDP_SERVER_DETAIL_INFO,   ///< Reply of the game server about details of the game, such as companies
 
	PACKET_UDP_SERVER_REGISTER,      ///< Packet to register itself to the master server
 
	PACKET_UDP_MASTER_ACK_REGISTER,  ///< Packet indicating registration has succedeed
 
	PACKET_UDP_CLIENT_GET_LIST,      ///< Request for serverlist from master server
 
	PACKET_UDP_MASTER_RESPONSE_LIST, ///< Response from master server with server ip's + port's
 
	PACKET_UDP_SERVER_UNREGISTER,    ///< Request to be removed from the server-list
 
	PACKET_UDP_CLIENT_GET_NEWGRFS,   ///< Requests the name for a list of GRFs (GRF_ID and MD5)
 
	PACKET_UDP_SERVER_NEWGRFS,       ///< Sends the list of NewGRF's requested.
 
	PACKET_UDP_END                   ///< Must ALWAYS be on the end of this list!! (period)
 
};
 

	
 
void NetworkSend_GRFIdentifier(Packet *p, const GRFConfig *c);
 
void NetworkSend_NetworkGameInfo(Packet *p, const NetworkGameInfo *info);
 

	
 
void NetworkRecv_GRFIdentifier(NetworkClientState *cs, Packet *p, GRFConfig *c);
 
void NetworkRecv_NetworkGameInfo(NetworkClientState *cs, Packet *p, NetworkGameInfo *info);
 

	
 
/**
 
 * Function that is called for every GRFConfig that is read when receiving
 
 * a NetworkGameInfo. Only grfid and md5sum are set, the rest is zero. This
 
 * function must set all appropriate fields. This GRF is later appended to
 
 * the grfconfig list of the NetworkGameInfo.
 
 * @param config the GRF to handle
 
 */
 
void HandleIncomingNetworkGameInfoGRFConfig(GRFConfig *config);
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 
#endif /* NETWORK_CORE_UDP_H */
network/network.c
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#include "../stdafx.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 "../openttd.h"
 
#include "../debug.h"
 
#include "../functions.h"
 
#include "../string.h"
 
#include "../strings.h"
 
#include "../map.h"
 
#include "../command.h"
 
#include "../variables.h"
 
#include "../date.h"
 
#include "../newgrf_config.h"
 
#include "../table/strings.h"
 
#include "network_client.h"
 
#include "network_server.h"
 
#include "network_udp.h"
 
#include "network_gamelist.h"
 
#include "core/udp.h"
 
#include "core/tcp.h"
 
#include "network_gui.h"
 
#include "../console.h" /* IConsoleCmdExec */
 
#include <stdarg.h> /* va_list */
 
#include "../md5.h"
 

	
 
#ifdef __MORPHOS__
 
// the library base is required here
 
struct Library *SocketBase = NULL;
 
#endif
 

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

	
 
// 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 StateGameLoop(void);
 

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

	
 
	for (ci = _network_client_info; ci != endof(_network_client_info); ci++) {
 
		if (ci->client_index == client_index) return ci;
 
	}
 

	
 
	return NULL;
 
}
 

	
 
/** Return the CI for a given IP
 
 * @param ip IP of the client we are looking for. This must be in string-format
 
 * @return return a pointer to the corresponding NetworkClientInfo struct or NULL on failure */
 
NetworkClientInfo *NetworkFindClientInfoFromIP(const char *ip)
 
{
 
	NetworkClientInfo *ci;
 
	uint32 ip_number = inet_addr(ip);
 

	
 
	for (ci = _network_client_info; ci != endof(_network_client_info); ci++) {
 
		if (ci->client_ip == ip_number) return ci;
 
	}
 

	
 
	return NULL;
 
}
 

	
 
// Function that looks up the CS for a given client-index
 
NetworkClientState *NetworkFindClientStateFromIndex(uint16 client_index)
 
{
 
	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, const NetworkClientState *cs)
 
{
 
	const NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs);
 

	
 
	if (ci->client_name[0] == '\0') {
 
		snprintf(client_name, size, "Client #%4d", cs->index);
 
	} else {
 
		ttd_strlcpy(client_name, ci->client_name, size);
 
	}
 
}
 

	
 
byte NetworkSpectatorCount(void)
 
{
 
	const NetworkClientState *cs;
 
	byte count = 0;
 

	
 
	FOR_ALL_CLIENTS(cs) {
 
		if (DEREF_CLIENT_INFO(cs)->client_playas == PLAYER_SPECTATOR) count++;
 
	}
 

	
 
	return count;
 
}
 

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

	
 
	va_start(va, str);
 
	vsnprintf(buf, lengthof(buf), str, va);
 
	va_end(va);
 

	
 
	switch (action) {
 
		case NETWORK_ACTION_SERVER_MESSAGE:
 
			color = 1;
 
			snprintf(message, sizeof(message), "*** %s", buf);
 
			break;
 
		case NETWORK_ACTION_JOIN:
 
			color = 1;
 
			GetString(temp, STR_NETWORK_CLIENT_JOINED, lastof(temp));
 
			snprintf(message, sizeof(message), "*** %s %s", name, temp);
 
			break;
 
		case NETWORK_ACTION_LEAVE:
 
			color = 1;
 
			GetString(temp, STR_NETWORK_ERR_LEFT, lastof(temp));
 
			snprintf(message, sizeof(message), "*** %s %s (%s)", name, temp, buf);
 
			break;
 
		case NETWORK_ACTION_GIVE_MONEY:
 
			if (self_send) {
 
				SetDParamStr(0, name);
 
				SetDParam(1, atoi(buf));
 
				GetString(temp, STR_NETWORK_GAVE_MONEY_AWAY, lastof(temp));
 
				snprintf(message, sizeof(message), "*** %s", temp);
 
			} else {
 
				SetDParam(0, atoi(buf));
 
				GetString(temp, STR_NETWORK_GIVE_MONEY, lastof(temp));
 
				snprintf(message, sizeof(message), "*** %s %s", name, temp);
 
			}
 
			break;
 
		case NETWORK_ACTION_NAME_CHANGE:
 
			GetString(temp, STR_NETWORK_NAME_CHANGE, lastof(temp));
 
			snprintf(message, sizeof(message), "*** %s %s %s", name, temp, buf);
 
			break;
 
		case NETWORK_ACTION_CHAT_COMPANY:
 
			SetDParamStr(0, name);
 
			SetDParamStr(1, buf);
 
			GetString(temp, self_send ? STR_NETWORK_CHAT_TO_COMPANY : STR_NETWORK_CHAT_COMPANY, lastof(temp));
 
			ttd_strlcpy(message, temp, sizeof(message));
 
			break;
 
		case NETWORK_ACTION_CHAT_CLIENT:
 
			SetDParamStr(0, name);
 
			SetDParamStr(1, buf);
 
			GetString(temp, self_send ? STR_NETWORK_CHAT_TO_CLIENT : STR_NETWORK_CHAT_CLIENT, lastof(temp));
 
			ttd_strlcpy(message, temp, sizeof(message));
 
			break;
 
		default:
 
			SetDParamStr(0, name);
 
			SetDParamStr(1, buf);
 
			GetString(temp, STR_NETWORK_CHAT_ALL, lastof(temp));
 
			ttd_strlcpy(message, temp, sizeof(message));
 
			break;
 
	}
 

	
 
	IConsolePrintF(color, "%s", message);
 
	AddTextMessage(color, duration, "%s", message);
 
}
 

	
 
// Calculate the frame-lag of a client
 
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
 
static void NetworkError(StringID error_string)
 
{
 
	_switch_mode = SM_MENU;
 
	_switch_mode_errorstr = error_string;
 
}
 

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

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

	
 
static void NetworkClientError(NetworkRecvStatus 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->has_quit = true;
 
		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; break;
 
	}
 
	// This means we fucked up and the server closed the connection
 
	if (res != NETWORK_RECV_STATUS_SERVER_ERROR && res != NETWORK_RECV_STATUS_SERVER_FULL &&
 
			res != NETWORK_RECV_STATUS_SERVER_BANNED) {
 
		SEND_COMMAND(PACKET_CLIENT_ERROR)(errorno);
 

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

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

	
 
/** Retrieve a string representation of an internal error number
 
 * @param buf buffer where the error message will be stored
 
 * @param err NetworkErrorCode
 
 * @return returns a pointer to the error message (buf) */
 
char* GetNetworkErrorMsg(char* buf, NetworkErrorCode err, const char* last)
 
{
 
	/* List of possible network errors, used by
 
	 * PACKET_SERVER_ERROR and PACKET_CLIENT_ERROR */
 
	static const StringID network_error_strings[] = {
 
		STR_NETWORK_ERR_CLIENT_GENERAL,
 
		STR_NETWORK_ERR_CLIENT_DESYNC,
 
		STR_NETWORK_ERR_CLIENT_SAVEGAME,
 
		STR_NETWORK_ERR_CLIENT_CONNECTION_LOST,
 
		STR_NETWORK_ERR_CLIENT_PROTOCOL_ERROR,
 
		STR_NETWORK_ERR_CLIENT_NOT_AUTHORIZED,
 
		STR_NETWORK_ERR_CLIENT_NOT_EXPECTED,
 
		STR_NETWORK_ERR_CLIENT_WRONG_REVISION,
 
		STR_NETWORK_ERR_CLIENT_NAME_IN_USE,
 
		STR_NETWORK_ERR_CLIENT_WRONG_PASSWORD,
 
		STR_NETWORK_ERR_CLIENT_PLAYER_MISMATCH,
 
		STR_NETWORK_ERR_CLIENT_KICKED,
 
		STR_NETWORK_ERR_CLIENT_CHEATER,
 
		STR_NETWORK_ERR_CLIENT_SERVER_FULL
 
	};
 

	
 
	if (err >= lengthof(network_error_strings)) err = 0;
 

	
 
	return GetString(buf, network_error_strings[err], last);
 
}
 

	
 
/* Count the number of active clients connected */
 
static uint NetworkCountPlayers(void)
 
{
 
	const NetworkClientState *cs;
 
	uint count = 0;
 

	
 
	FOR_ALL_CLIENTS(cs) {
 
		const NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs);
 
		if (IsValidPlayer(ci->client_playas)) count++;
 
	}
 

	
 
	return count;
 
}
 

	
 
static bool _min_players_paused = false;
 

	
 
/* Check if the minimum number of players has been reached and pause or unpause the game as appropriate */
 
void CheckMinPlayers(void)
 
{
 
	if (!_network_dedicated) return;
 

	
 
	if (NetworkCountPlayers() < _network_min_players) {
 
		if (_min_players_paused) return;
 

	
 
		_min_players_paused = true;
 
		DoCommandP(0, 1, 0, NULL, CMD_PAUSE);
 
		NetworkServer_HandleChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "Game paused (not enough players)", NETWORK_SERVER_INDEX);
 
	} else {
 
		if (!_min_players_paused) return;
 

	
 
		_min_players_paused = false;
 
		DoCommandP(0, 0, 0, NULL, CMD_PAUSE);
 
		NetworkServer_HandleChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "Game unpaused (enough players)", NETWORK_SERVER_INDEX);
 
	}
 
}
 

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

	
 
#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
 
	_broadcast_list[0] = 0;
 

	
 
	if (sock < 0) {
 
		DEBUG(net, 0, "[core] error creating socket");
 
		return;
 
	}
 

	
 
	output_length = _netstat(sock, &output_pointer, 1);
 
	if (output_length < 0) {
 
		DEBUG(net, 0, "[core] 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;
 
			uint32 ip;
 
			uint32 netmask;
 

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

	
 
			ip      = (uint32)i1 << 24 | (uint32)i2 << 16 | (uint32)i3 << 8 | (uint32)i4;
 
			netmask = (uint32)j1 << 24 | (uint32)j2 << 16 | (uint32)j3 << 8 | (uint32)j4;
 

	
 
			if (ip != INADDR_LOOPBACK && ip != INADDR_ANY) {
 
				inaddr.s_addr = htonl(ip | ~netmask);
 
				_broadcast_list[i] = inaddr.s_addr;
 
				i++;
 
			}
 
			if (read < 0) {
 
				break;
 
			}
 
			*output += read;
 
		}
 
		/* XXX - Using either one of these crashes openttd heavily? - wber */
 
		/*free(output_pointer);*/
 
		/*free(output);*/
 
		closesocket(sock);
 
	}
 
#elif defined(HAVE_GETIFADDRS)
 
	struct ifaddrs *ifap, *ifa;
 

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

	
 
	if (getifaddrs(&ifap) != 0)
 
		return;
 

	
 
	i = 0;
 
	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
 
		if (!(ifa->ifa_flags & IFF_BROADCAST)) continue;
 
		if (ifa->ifa_broadaddr == NULL) continue;
 
		if (ifa->ifa_broadaddr->sa_family != AF_INET) continue;
 
		_broadcast_list[i] = ((struct sockaddr_in*)ifa->ifa_broadaddr)->sin_addr.s_addr;
 
		i++;
 
	}
 
	freeifaddrs(ifap);
 

	
 
#else /* not HAVE_GETIFADDRS */
 
	SOCKET sock;
 
#ifdef WIN32
 
	DWORD len = 0;
 
	INTERFACE_INFO ifo[MAX_INTERFACES];
 
	uint j;
 
#else
 
	char buf[4 * 1024]; // Arbitrary buffer size
 
	struct ifconf ifconf;
 
	const char* buf_end;
 
	const char* p;
 
#endif
 

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

	
 
	sock = socket(AF_INET, SOCK_DGRAM, 0);
 
	if (sock == INVALID_SOCKET) return;
 

	
 
#ifdef WIN32
 
	memset(&ifo[0], 0, sizeof(ifo));
 
	if ((WSAIoctl(sock, SIO_GET_INTERFACE_LIST, NULL, 0, &ifo[0], sizeof(ifo), &len, NULL, NULL)) != 0) {
 
		closesocket(sock);
 
		return;
 
	}
 

	
 
	i = 0;
 
	for (j = 0; j < len / sizeof(*ifo); j++) {
 
		if (ifo[j].iiFlags & IFF_LOOPBACK) continue;
 
		if (!(ifo[j].iiFlags & IFF_BROADCAST)) continue;
 
		/* iiBroadcast is unusable, because it always seems to be set to
 
		 * 255.255.255.255.
 
		 */
 
		_broadcast_list[i++] =
 
			 ifo[j].iiAddress.AddressIn.sin_addr.s_addr |
 
			~ifo[j].iiNetmask.AddressIn.sin_addr.s_addr;
 
	}
 
#else
 
	ifconf.ifc_len = sizeof(buf);
 
	ifconf.ifc_buf = buf;
 
	if (ioctl(sock, SIOCGIFCONF, &ifconf) == -1) {
 
		closesocket(sock);
 
		return;
 
	}
 

	
 
	i = 0;
 
	buf_end = buf + ifconf.ifc_len;
 
	for (p = buf; p < buf_end;) {
 
		const struct ifreq* req = (const struct ifreq*)p;
 

	
 
		if (req->ifr_addr.sa_family == AF_INET) {
 
			struct ifreq r;
 

	
 
			strncpy(r.ifr_name, req->ifr_name, lengthof(r.ifr_name));
 
			if (ioctl(sock, SIOCGIFFLAGS, &r) != -1 &&
 
					r.ifr_flags & IFF_BROADCAST &&
 
					ioctl(sock, SIOCGIFBRDADDR, &r) != -1) {
 
				_broadcast_list[i++] =
 
					((struct sockaddr_in*)&r.ifr_broadaddr)->sin_addr.s_addr;
 
			}
 
		}
 

	
 
		p += sizeof(struct ifreq);
 
#ifdef AF_LINK
 
		p += req->ifr_addr.sa_len - sizeof(struct sockaddr);
 
#endif
 
	}
 
#endif
 

	
 
	closesocket(sock);
 
#endif /* not HAVE_GETIFADDRS */
 

	
 
	_broadcast_list[i] = 0;
 

	
 
	DEBUG(net, 3, "Detected broadcast addresses:");
 
	// Now display to the debug all the detected ips
 
	for (i = 0; _broadcast_list[i] != 0; i++) {
 
		DEBUG(net, 3, "%d) %s", i, inet_ntoa(*(struct in_addr *)&_broadcast_list[i]));//inet_ntoa(inaddr));
 
	}
 
}
 

	
 
// 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, "Cannot resolve '%s'", hostname);
 
		} else {
 
			struct in_addr addr = *(struct in_addr *)he->h_addr_list[0];
 
			DEBUG(net, 1, "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 char **player, const char **port, char *connection_string)
 
{
 
	char *p;
 
	for (p = connection_string; *p != '\0'; p++) {
 
		if (*p == '#') {
 
			*p = '\0';
 
			*player = ++p;
 
			while (IsValidChar(*p, CS_NUMERAL)) p++;
 
			if (*p == '\0') break;
 
		} else if (*p == ':') {
 
			*port = p + 1;
 
			*p = '\0';
 
		}
 
	}
 
}
 

	
 
// Creates a new client from a socket
 
//   Used both by the server and the client
 
static NetworkClientState *NetworkAllocClient(SOCKET s)
 
{
 
	NetworkClientState *cs;
 
	byte 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 = DEREF_CLIENT(client_no);
 
	memset(cs, 0, sizeof(*cs));
 
	cs->socket = s;
 
	cs->last_frame = 0;
 
	cs->has_quit = false;
 

	
 
	cs->last_frame = _frame_counter;
 
	cs->last_frame_server = _frame_counter;
 

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

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

	
 
		InvalidateWindow(WC_CLIENT_LIST, 0);
 
	}
 

	
 
	return cs;
 
}
 

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

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

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

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

	
 
		GetNetworkErrorMsg(str, errorno, lastof(str));
 

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

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

	
 
	/* When the client was PRE_ACTIVE, the server was in pause mode, so unpause */
 
	if (cs->status == STATUS_PRE_ACTIVE && _network_pause_on_join) {
 
		DoCommandP(0, 0, 0, NULL, CMD_PAUSE);
 
		NetworkServer_HandleChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "Game unpaused", NETWORK_SERVER_INDEX);
 
	}
 

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

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

	
 
	CheckMinPlayers();
 
}
 

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

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

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

	
 
	if (!SetNoDelay(s)) DEBUG(net, 1, "Setting TCP_NODELAY failed");
 

	
 
	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;
 

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

	
 
	if (!SetNonBlocking(s)) DEBUG(net, 0, "Setting non-blocking mode failed"); // XXX should this be an error?
 

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

	
 
	_network_join_status = NETWORK_JOIN_STATUS_CONNECTING;
 
	ShowJoinStatusWindow();
 

	
 
	return true;
 
}
 

	
 
// For the server, to accept new clients
 
static void NetworkAcceptClients(void)
 
{
 
	struct sockaddr_in sin;
 
	NetworkClientState *cs;
 
	uint i;
 
	bool banned;
 

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

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

	
 
		SetNonBlocking(s); // XXX error handling?
 

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

	
 
		SetNoDelay(s); // XXX error handling?
 

	
 
		/* Check if the client is banned */
 
		banned = false;
 
		for (i = 0; i < lengthof(_network_ban_list); i++) {
 
			if (_network_ban_list[i] == NULL) continue;
 

	
 
			if (sin.sin_addr.s_addr == inet_addr(_network_ban_list[i])) {
 
				Packet *p = NetworkSend_Init(PACKET_SERVER_BANNED);
 

	
 
				DEBUG(net, 1, "Banned ip tried to join (%s), refused", _network_ban_list[i]);
 

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

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

	
 
				free(p);
 

	
 
				banned = true;
 
				break;
 
			}
 
		}
 
		/* If this client is banned, continue with next client */
 
		if (banned) continue;
 

	
 
		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;
 

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

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

	
 
	DEBUG(net, 1, "Listening on %s:%d", _network_server_bind_ip_host, _network_server_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;
 
		}
 
	}
 

	
 
	if (!SetNonBlocking(ls)) DEBUG(net, 0, "Setting non-blocking mode 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(_network_server_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
 
static void NetworkClose(void)
 
{
 
	NetworkClientState *cs;
 

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

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

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

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

	
 
	NetworkUDPInitialize();
 
}
 

	
 
// 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
 
NetworkGameList *NetworkQueryServer(const char* host, unsigned short port, bool game_info)
 
{
 
	if (!_network_available) return NULL;
 

	
 
	NetworkDisconnect();
 

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

	
 
	NetworkInitialize();
 

	
 
	_network_server = false;
 

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

	
 
	// We are connected
 
	if (_networking) {
 
		SEND_COMMAND(PACKET_CLIENT_COMPANY_INFO)();
 
	} else { // No networking, close everything down again
 
		NetworkDisconnect();
 
	}
 

	
 
	return NULL;
 
}
 

	
 
/* Validates an address entered as a string and adds the server to
 
 * the list. If you use this function, the games will be marked
 
 * as manually added. */
 
void NetworkAddServer(const char *b)
 
{
 
	if (*b != '\0') {
 
		NetworkGameList *item;
 
		const char *port = NULL;
 
		const char *player = NULL;
 
		char 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, host);
 
		if (port != NULL) rport = atoi(port);
 

	
 
		item = NetworkQueryServer(host, rport, true);
 
		item->manually = true;
 
	}
 
}
 

	
 
/* Generates the list of manually added hosts from NetworkGameList and
 
 * dumps them into the array _network_host_list. This array is needed
 
 * by the function that generates the config file. */
 
void NetworkRebuildHostList(void)
 
{
 
	uint i = 0;
 
	const NetworkGameList *item = _network_game_list;
 
	while (item != NULL && i != lengthof(_network_host_list)) {
 
		if (item->manually) {
 
			free(_network_host_list[i]);
 
			_network_host_list[i++] = str_fmt("%s:%i", item->info.hostname, item->port);
 
		}
 
		item = item->next;
 
	}
 

	
 
	for (; i < lengthof(_network_host_list); i++) {
 
		free(_network_host_list[i]);
 
		_network_host_list[i] = NULL;
 
	}
 
}
 

	
 
// Used by clients, to connect to a server
 
bool NetworkClientConnectGame(const char *host, uint16 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;
 
}
 

	
 
static void NetworkInitGameInfo(void)
 
{
 
	NetworkClientInfo *ci;
 

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

	
 
	ttd_strlcpy(_network_game_info.server_revision, _openttd_revision, sizeof(_network_game_info.server_revision));
 

	
 
	// The server is a client too ;)
 
	if (_network_dedicated) {
 
		_network_game_info.clients_on = 0;
 
		_network_game_info.companies_on = 0;
 
		_network_game_info.dedicated = true;
 
	} else {
 
		_network_game_info.clients_on = 1;
 
		_network_game_info.companies_on = 1;
 
		_network_game_info.dedicated = false;
 
	}
 

	
 
	_network_game_info.spectators_on = 0;
 

	
 
	_network_game_info.game_date = _date;
 
	_network_game_info.start_date = ConvertYMDToDate(_patches.starting_year, 0, 1);
 
	_network_game_info.map_width = MapSizeX();
 
	_network_game_info.map_height = MapSizeY();
 
	_network_game_info.map_set = _opt.landscape;
 

	
 
	_network_game_info.use_password = (_network_server_password[0] != '\0');
 

	
 
	// 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;
 
	ci->client_playas = _network_dedicated ? PLAYER_SPECTATOR : _local_player;
 

	
 
	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(&_udp_server_socket, _network_server_bind_ip, _network_server_port, false);
 

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

	
 
	/* Non-dedicated server will always be player #1 */
 
	if (!_network_dedicated) _network_playas = 0;
 

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

	
 
	_min_players_paused = false;
 
	CheckMinPlayers();
 

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

	
 
// The server is rebooting...
 
// The only difference with NetworkDisconnect, is the packets that is sent
 
void NetworkReboot(void)
 
{
 
	if (_network_server) {
 
		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) {
 
		NetworkClientState *cs;
 
		FOR_ALL_CLIENTS(cs) {
 
			SEND_COMMAND(PACKET_SERVER_SHUTDOWN)(cs);
 
			NetworkSend_Packets(cs);
 
		}
 
	}
 

	
 
	if (_network_advertise) NetworkUDPRemoveAdvertise();
 

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

	
 
	_networking = false;
 
	_network_server = false;
 
}
 

	
 
// Receives something from the network
 
static bool NetworkReceive(void)
 
{
 
	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 {
 
				NetworkRecvStatus res;
 

	
 
				// The client already was quiting!
 
				if (cs->has_quit) return false;
 

	
 
				res = NetworkClient_ReadPackets(cs);
 
				if (res != 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)
 
{
 
	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
 
static void NetworkHandleLocalQueue(void)
 
{
 
	CommandPacket *cp, **cp_prev;
 

	
 
	cp_prev = &_local_command_queue;
 

	
 
	while ( (cp = *cp_prev) != NULL) {
 

	
 
		// The queue is always in order, which means
 
		// that the first element will be executed first.
 
		if (_frame_counter < cp->frame) break;
 

	
 
		if (_frame_counter > cp->frame) {
 
			// If we reach here, it means for whatever reason, we've already executed
 
			// past the command we need to execute.
 
			DEBUG(net, 0, "Trying to execute a packet in the past!");
 
			assert(0);
 
		}
 

	
 
		// We can execute this command
 
		NetworkExecuteCommand(cp);
 

	
 
		*cp_prev = cp->next;
 
		free(cp);
 
	}
 

	
 
	// Just a safety check, to be removed in the future.
 
	// Make sure that no older command appears towards the end of the queue
 
	// In that case we missed executing it. This will never happen.
 
	for (cp = _local_command_queue; cp; cp = cp->next) {
 
		assert(_frame_counter < cp->frame);
 
	}
 

	
 
}
 

	
 
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, "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, "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(_udp_server_socket);
 
		if (_udp_master_socket != INVALID_SOCKET) {
 
			NetworkUDPReceive(_udp_master_socket);
 
		}
 
	} else if (_udp_client_socket != INVALID_SOCKET) {
 
		NetworkUDPReceive(_udp_client_socket);
 
		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) {
 
		bool send_frame = false;
 

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

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

	
 
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, (const md5_byte_t*)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, "[core] starting network...");
 

	
 
#if defined(__MORPHOS__) || defined(__AMIGA__)
 
	/*
 
	 *  IMPORTANT NOTE: SocketBase needs to be initialized before we use _any_
 
	 *  network related function, else: crash.
 
	 */
 
	DEBUG(net, 3, "[core] loading bsd socket library");
 
	SocketBase = OpenLibrary("bsdsocket.library", 4);
 
	if (SocketBase == NULL) {
 
		DEBUG(net, 0, "[core] can't open bsdsocket.library version 4, network unavailable");
 
		_network_available = false;
 
		return;
 
	}
 

	
 
#if defined(__AMIGA__)
 
	// for usleep() implementation (only required for legacy AmigaOS builds)
 
	TimerPort = CreateMsgPort();
 
	if (TimerPort != NULL) {
 
		TimerRequest = (struct timerequest*)CreateIORequest(TimerPort, sizeof(struct timerequest);
 
		if (TimerRequest != NULL) {
 
			if (OpenDevice("timer.device", UNIT_MICROHZ, (struct IORequest*)TimerRequest, 0) == 0) {
 
				TimerBase = TimerRequest->tr_node.io_Device;
 
				if (TimerBase == NULL) {
 
					// free ressources...
 
					DEBUG(net, 0, "[core] can't initialize timer, network unavailable");
 
					_network_available = false;
 
					return;
 
				}
 
			}
 
		}
 
	}
 
#endif // __AMIGA__
 
#endif // __MORPHOS__ / __AMIGA__
 

	
 
	// Network is available
 
	_network_available = true;
 
	_network_dedicated = false;
 
	_network_last_advertise_frame = 0;
 
	_network_need_advertise = true;
 
	_network_advertise_retries = 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();
 

	
 
	{
 
		byte cl_max = _network_game_info.clients_max;
 
		byte cp_max = _network_game_info.companies_max;
 
		byte sp_max = _network_game_info.spectators_max;
 

	
 
		memset(&_network_game_info, 0, sizeof(_network_game_info));
 
		_network_game_info.clients_max = cl_max;
 
		_network_game_info.companies_max = cp_max;
 
		_network_game_info.spectators_max = sp_max;
 
	}
 

	
 
	// Let's load the network in windows
 
	#if defined(WIN32)
 
	{
 
		WSADATA wsa;
 
		DEBUG(net, 3, "[core] loading windows socket library");
 
		if (WSAStartup(MAKEWORD(2,0), &wsa) != 0) {
 
			DEBUG(net, 0, "[core] WSAStartup failed, network unavailable");
 
			_network_available = false;
 
			return;
 
		}
 
	}
 
	#endif // WIN32
 

	
 
	NetworkInitialize();
 
	DEBUG(net, 3, "[core] network online, multiplayer available");
 
	NetworkFindIPs();
 
}
 

	
 
// This shuts the network down
 
void NetworkShutDown(void)
 
{
 
	NetworkDisconnect();
 
	NetworkUDPClose();
 

	
 
	DEBUG(net, 3, "[core] shutting down network");
 

	
 
	_network_available = false;
 

	
 
#if defined(__MORPHOS__) || defined(__AMIGA__)
 
	// free allocated ressources
 
#if defined(__AMIGA__)
 
	if (TimerBase    != NULL) CloseDevice((struct IORequest*)TimerRequest); // XXX This smells wrong
 
	if (TimerRequest != NULL) DeleteIORequest(TimerRequest);
 
	if (TimerPort    != NULL) DeleteMsgPort(TimerPort);
 
#endif
 

	
 
	if (SocketBase != NULL) CloseLibrary(SocketBase);
 
#endif
 

	
 
#if defined(WIN32)
 
	WSACleanup();
 
#endif
 
}
 

	
 
#endif /* ENABLE_NETWORK */
network/network.h
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifndef NETWORK_H
 
#define NETWORK_H
 

	
 
#define NOREV_STRING "norev000"
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "../player.h"
 
#include "core/config.h"
 
#include "core/game.h"
 

	
 
// If this line is enable, every frame will have a sync test
 
//  this is not needed in normal games. Normal is like 1 sync in 100
 
//  frames. You can enable this if you have a lot of desyncs on a certain
 
//  game.
 
// Remember: both client and server have to be compiled with this
 
//  option enabled to make it to work. If one of the two has it disabled
 
//  nothing will happen.
 
//#define ENABLE_NETWORK_SYNC_EVERY_FRAME
 

	
 
// In theory sending 1 of the 2 seeds is enough to check for desyncs
 
//   so in theory, this next define can be left off.
 
//#define NETWORK_SEND_DOUBLE_SEED
 

	
 
// How many clients can we have? Like.. MAX_PLAYERS - 1 is the amount of
 
//  players that can really play.. so.. a max of 4 spectators.. gives us..
 
//  MAX_PLAYERS + 3
 
#define MAX_CLIENTS (MAX_PLAYERS + 3)
 

	
 

	
 
// Do not change this next line. It should _ALWAYS_ be MAX_CLIENTS + 1
 
#define MAX_CLIENT_INFO (MAX_CLIENTS + 1)
 

	
 
#define MAX_INTERFACES 9
 

	
 

	
 
// How many vehicle/station types we put over the network
 
#define NETWORK_VEHICLE_TYPES 5
 
#define NETWORK_STATION_TYPES 5
 

	
 
typedef struct NetworkPlayerInfo {
 
	char company_name[NETWORK_NAME_LENGTH];         // Company name
 
	char password[NETWORK_PASSWORD_LENGTH];         // The password for the player
 
	Year inaugurated_year;                          // What year the company started in
 
	int64 company_value;                            // The company value
 
	int64 money;                                    // The amount of money the company has
 
	int64 income;                                   // How much did the company earned last year
 
	uint16 performance;                             // What was his performance last month?
 
	byte use_password;                              // 0: No password 1: There is a password
 
	uint16 num_vehicle[NETWORK_VEHICLE_TYPES];      // How many vehicles are there of this type?
 
	uint16 num_station[NETWORK_STATION_TYPES];      // How many stations are there of this type?
 
	char players[NETWORK_PLAYERS_LENGTH];           // The players that control this company (Name1, name2, ..)
 
	uint16 months_empty;                            // How many months the company is empty
 
} NetworkPlayerInfo;
 

	
 
typedef struct NetworkClientInfo {
 
	uint16 client_index;                            // Index of the client (same as ClientState->index)
 
	char client_name[NETWORK_CLIENT_NAME_LENGTH];   // Name of the client
 
	byte client_lang;                               // The language of the client
 
	byte client_playas;                             // As which player is this client playing (PlayerID)
 
	uint32 client_ip;                               // IP-address of the client (so he can be banned)
 
	Date join_date;                                 // Gamedate the player has joined
 
	char unique_id[NETWORK_NAME_LENGTH];            // Every play sends an unique id so we can indentify him
 
} NetworkClientInfo;
 

	
 
typedef struct NetworkGameList {
 
	NetworkGameInfo info;
 
	uint32 ip;
 
	uint16 port;
 
	bool online;                                    // False if the server did not respond (default status)
 
	bool manually;                                  // True if the server was added manually
 
	struct NetworkGameList *next;
 
} NetworkGameList;
 

	
 
typedef enum {
 
	NETWORK_JOIN_STATUS_CONNECTING,
 
	NETWORK_JOIN_STATUS_AUTHORIZING,
 
	NETWORK_JOIN_STATUS_WAITING,
 
	NETWORK_JOIN_STATUS_DOWNLOADING,
 
	NETWORK_JOIN_STATUS_PROCESSING,
 
	NETWORK_JOIN_STATUS_REGISTERING,
 

	
 
	NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO,
 
} NetworkJoinStatus;
 

	
 
// language ids for server_lang and client_lang
 
typedef enum {
 
	NETLANG_ANY     = 0,
 
	NETLANG_ENGLISH = 1,
 
	NETLANG_GERMAN  = 2,
 
	NETLANG_FRENCH  = 3,
 
} NetworkLanguage;
 

	
 
VARDEF NetworkGameList *_network_game_list;
 

	
 
VARDEF NetworkGameInfo _network_game_info;
 
VARDEF NetworkPlayerInfo _network_player_info[MAX_PLAYERS];
 
VARDEF NetworkClientInfo _network_client_info[MAX_CLIENT_INFO];
 

	
 
VARDEF char _network_player_name[NETWORK_CLIENT_NAME_LENGTH];
 
VARDEF char _network_default_ip[NETWORK_HOSTNAME_LENGTH];
 

	
 
VARDEF uint16 _network_own_client_index;
 
VARDEF char _network_unique_id[NETWORK_NAME_LENGTH]; // Our own unique ID
 

	
 
VARDEF uint32 _frame_counter_server; // The frame_counter of the server, if in network-mode
 
VARDEF uint32 _frame_counter_max; // To where we may go with our clients
 

	
 
VARDEF uint32 _last_sync_frame; // Used in the server to store the last time a sync packet was sent to clients.
 

	
 
// networking settings
 
VARDEF uint32 _broadcast_list[MAX_INTERFACES + 1];
 

	
 
VARDEF uint16 _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 char _network_server_password[NETWORK_PASSWORD_LENGTH];
 
VARDEF char _network_rcon_password[NETWORK_PASSWORD_LENGTH];
 

	
 
VARDEF uint16 _network_max_join_time;             ///< Time a client can max take to join
 
VARDEF bool _network_pause_on_join;               ///< Pause the game when a client tries to join (more chance of succeeding join)
 

	
 
VARDEF uint16 _redirect_console_to_client;
 

	
 
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 byte _network_lan_internet;
 

	
 
VARDEF bool _network_need_advertise;
 
VARDEF uint32 _network_last_advertise_frame;
 
VARDEF uint8 _network_advertise_retries;
 

	
 
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
 

	
 
VARDEF Year _network_restart_game_year;      // If this year is reached, the server automaticly restarts
 
VARDEF uint8 _network_min_players;           // Minimum number of players for game to unpause
 

	
 
NetworkGameList *NetworkQueryServer(const char* host, unsigned short port, bool game_info);
 

	
 
byte NetworkSpectatorCount(void);
 

	
 
VARDEF char *_network_host_list[10];
 
VARDEF char *_network_ban_list[25];
 

	
 
void ParseConnectionString(const char **player, const char **port, char *connection_string);
 
void NetworkUpdateClientInfo(uint16 client_index);
 
void NetworkAddServer(const char *b);
 
void NetworkRebuildHostList(void);
 
bool NetworkChangeCompanyPassword(byte argc, char *argv[]);
 
void NetworkPopulateCompanyInfo(void);
 
void UpdateNetworkGameWindow(bool unselect);
 
void CheckMinPlayers(void);
 

	
 
void NetworkStartUp(void);
 
void NetworkUDPClose(void);
 
void NetworkShutDown(void);
 
void NetworkGameLoop(void);
 
void NetworkUDPGameLoop(void);
 
bool NetworkServerStart(void);
 
bool NetworkClientConnectGame(const char *host, uint16 port);
 
void NetworkReboot(void);
 
void NetworkDisconnect(void);
 

	
 
VARDEF bool _networking;         ///< are we in networking mode?
 
VARDEF bool _network_server;     ///< network-server is active
 
VARDEF bool _network_available;  ///< is network mode available?
 

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

	
 
static inline void NetworkStartUp(void) {}
 
static inline void NetworkShutDown(void) {}
 

	
 
#define _networking 0
 
#define _network_server 0
 
#define _network_available 0
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 
/* These variables must always be registered! */
 
VARDEF bool _network_dedicated;  ///< are we a dedicated server?
 
VARDEF bool _network_advertise;  ///< is the server advertising to the master server?
 
VARDEF PlayerID _network_playas; ///< an id to play as.. (see players.h:Players)
 

	
 
#endif /* NETWORK_H */
network/network_client.c
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "../stdafx.h"
 
#include "../debug.h"
 
#include "../string.h"
 
#include "../strings.h"
 
#include "network_data.h"
 
#include "core/tcp.h"
 
#include "../date.h"
 
#include "../table/strings.h"
 
#include "../functions.h"
 
#include "network_client.h"
 
#include "network_gamelist.h"
 
#include "network_gui.h"
 
#include "../saveload.h"
 
#include "../command.h"
 
#include "../window.h"
 
#include "../console.h"
 
#include "../variables.h"
 
#include "../ai/ai.h"
 

	
 

	
 
// This file handles all the client-commands
 

	
 

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

	
 
static uint32 last_ack_frame;
 

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

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

	
 
	extern const char _openttd_revision[];
 
	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>
 
	//
 

	
 
	Packet *p = NetworkSend_Init(PACKET_CLIENT_GETMAP);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_MAP_OK)
 
{
 
	//
 
	// Packet: CLIENT_MAP_OK
 
	// Function: Tell the server that we are done receiving/loading the map
 
	// Data:
 
	//    <none>
 
	//
 

	
 
	Packet *p = NetworkSend_Init(PACKET_CLIENT_MAP_OK);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_ACK)
 
{
 
	//
 
	// Packet: CLIENT_ACK
 
	// Function: Tell the server we are done with this frame
 
	// Data:
 
	//    uint32: current FrameCounter of the client
 
	//
 

	
 
	Packet *p = NetworkSend_Init(PACKET_CLIENT_ACK);
 

	
 
	NetworkSend_uint32(p, _frame_counter);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
// Send a command packet to the server
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_COMMAND)(CommandPacket *cp)
 
{
 
	//
 
	// Packet: CLIENT_COMMAND
 
	// Function: Send a DoCommand to the Server
 
	// Data:
 
	//    uint8:  PlayerID (0..MAX_PLAYERS-1)
 
	//    uint32: CommandID (see command.h)
 
	//    uint32: P1 (free variables used in DoCommand)
 
	//    uint32: P2
 
	//    uint32: Tile
 
	//    string: text
 
	//    uint8:  CallBackID (see callback_table.c)
 
	//
 

	
 
	Packet *p = NetworkSend_Init(PACKET_CLIENT_COMMAND);
 

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

	
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
// Send a chat-packet over the network
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_CHAT)(NetworkAction action, DestType type, int dest, const char *msg)
 
{
 
	//
 
	// Packet: CLIENT_CHAT
 
	// Function: Send a chat-packet to the serve
 
	// Data:
 
	//    uint8:  ActionID (see network_data.h, NetworkAction)
 
	//    uint8:  Destination Type (see network_data.h, DestType);
 
	//    uint8:  Destination Player (1..MAX_PLAYERS)
 
	//    String: Message (max MAX_TEXT_MSG_LEN)
 
	//
 

	
 
	Packet *p = NetworkSend_Init(PACKET_CLIENT_CHAT);
 

	
 
	NetworkSend_uint8(p, action);
 
	NetworkSend_uint8(p, type);
 
	NetworkSend_uint8(p, dest);
 
	NetworkSend_string(p, msg);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
// Send an error-packet over the network
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_ERROR)(NetworkErrorCode errorno)
 
{
 
	//
 
	// Packet: CLIENT_ERROR
 
	// Function: The client made an error and is quiting the game
 
	// Data:
 
	//    uint8:  ErrorID (see network_data.h, NetworkErrorCode)
 
	//
 
	Packet *p = NetworkSend_Init(PACKET_CLIENT_ERROR);
 

	
 
	NetworkSend_uint8(p, errorno);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_SET_PASSWORD)(const char *password)
 
{
 
	//
 
	// Packet: PACKET_CLIENT_SET_PASSWORD
 
	// Function: Set the password for the clients current company
 
	// Data:
 
	//    String: Password
 
	//
 
	Packet *p = NetworkSend_Init(PACKET_CLIENT_SET_PASSWORD);
 

	
 
	NetworkSend_string(p, password);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_SET_NAME)(const char *name)
 
{
 
	//
 
	// Packet: PACKET_CLIENT_SET_NAME
 
	// Function: Gives the player a new name
 
	// Data:
 
	//    String: Name
 
	//
 
	Packet *p = NetworkSend_Init(PACKET_CLIENT_SET_NAME);
 

	
 
	NetworkSend_string(p, name);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
// Send an quit-packet over the network
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_QUIT)(const char *leavemsg)
 
{
 
	//
 
	// Packet: CLIENT_QUIT
 
	// Function: The client is quiting the game
 
	// Data:
 
	//    String: leave-message
 
	//
 
	Packet *p = NetworkSend_Init(PACKET_CLIENT_QUIT);
 

	
 
	NetworkSend_string(p, leavemsg);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_RCON)(const char *pass, const char *command)
 
{
 
	Packet *p = NetworkSend_Init(PACKET_CLIENT_RCON);
 
	NetworkSend_string(p, pass);
 
	NetworkSend_string(p, command);
 
	NetworkSend_Packet(p, MY_CLIENT);
 
}
 

	
 

	
 
// **********
 
// Receiving functions
 
//   DEF_CLIENT_RECEIVE_COMMAND has parameter: Packet *p
 
// **********
 

	
 
extern bool SafeSaveOrLoad(const char *filename, int mode, int newgm);
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_FULL)
 
{
 
	// We try to join a server which is full
 
	_switch_mode_errorstr = STR_NETWORK_ERR_SERVER_FULL;
 
	DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 

	
 
	return NETWORK_RECV_STATUS_SERVER_FULL;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_BANNED)
 
{
 
	// We try to join a server where we are banned
 
	_switch_mode_errorstr = STR_NETWORK_ERR_SERVER_BANNED;
 
	DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 

	
 
	return NETWORK_RECV_STATUS_SERVER_BANNED;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_COMPANY_INFO)
 
{
 
	byte company_info_version;
 
	int i;
 

	
 
	company_info_version = NetworkRecv_uint8(MY_CLIENT, p);
 

	
 
	if (!MY_CLIENT->has_quit && company_info_version == NETWORK_COMPANY_INFO_VERSION) {
 
		byte total;
 
		byte current;
 

	
 
		total = NetworkRecv_uint8(MY_CLIENT, p);
 

	
 
		// There is no data at all..
 
		if (total == 0) return NETWORK_RECV_STATUS_CLOSE_QUERY;
 

	
 
		current = NetworkRecv_uint8(MY_CLIENT, p);
 
		if (!IsValidPlayer(current)) return NETWORK_RECV_STATUS_CLOSE_QUERY;
 

	
 
		NetworkRecv_string(MY_CLIENT, p, _network_player_info[current].company_name, sizeof(_network_player_info[current].company_name));
 
		_network_player_info[current].inaugurated_year = NetworkRecv_uint32(MY_CLIENT, p);
 
		_network_player_info[current].company_value = NetworkRecv_uint64(MY_CLIENT, p);
 
		_network_player_info[current].money = NetworkRecv_uint64(MY_CLIENT, p);
 
		_network_player_info[current].income = NetworkRecv_uint64(MY_CLIENT, p);
 
		_network_player_info[current].performance = NetworkRecv_uint16(MY_CLIENT, p);
 
		_network_player_info[current].use_password = NetworkRecv_uint8(MY_CLIENT, p);
 
		for (i = 0; i < NETWORK_VEHICLE_TYPES; i++)
 
			_network_player_info[current].num_vehicle[i] = NetworkRecv_uint16(MY_CLIENT, p);
 
		for (i = 0; i < NETWORK_STATION_TYPES; i++)
 
			_network_player_info[current].num_station[i] = NetworkRecv_uint16(MY_CLIENT, p);
 

	
 
		NetworkRecv_string(MY_CLIENT, p, _network_player_info[current].players, sizeof(_network_player_info[current].players));
 

	
 
		InvalidateWindow(WC_NETWORK_WINDOW, 0);
 

	
 
		return NETWORK_RECV_STATUS_OKAY;
 
	}
 

	
 
	return NETWORK_RECV_STATUS_CLOSE_QUERY;
 
}
 

	
 
// This packet contains info about the client (playas and name)
 
//  as client we save this in NetworkClientInfo, linked via 'index'
 
//  which is always an unique number on a server.
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_CLIENT_INFO)
 
{
 
	NetworkClientInfo *ci;
 
	uint16 index = NetworkRecv_uint16(MY_CLIENT, p);
 
	PlayerID playas = NetworkRecv_uint8(MY_CLIENT, p);
 
	char name[NETWORK_NAME_LENGTH];
 
	char unique_id[NETWORK_NAME_LENGTH];
 

	
 
	NetworkRecv_string(MY_CLIENT, p, name, sizeof(name));
 
	NetworkRecv_string(MY_CLIENT, p, unique_id, sizeof(unique_id));
 

	
 
	if (MY_CLIENT->has_quit) return NETWORK_RECV_STATUS_CONN_LOST;
 

	
 
	/* Do we receive a change of data? Most likely we changed playas */
 
	if (index == _network_own_client_index) _network_playas = playas;
 

	
 
	ci = NetworkFindClientInfoFromIndex(index);
 
	if (ci != NULL) {
 
		if (playas == ci->client_playas && strcmp(name, ci->client_name) != 0) {
 
			// Client name changed, display the change
 
			NetworkTextMessage(NETWORK_ACTION_NAME_CHANGE, 1, false, ci->client_name, "%s", name);
 
		} else if (playas != ci->client_playas) {
 
			// The player changed from client-player..
 
			// Do not display that for now
 
		}
 

	
 
		ci->client_playas = playas;
 
		ttd_strlcpy(ci->client_name, name, sizeof(ci->client_name));
 

	
 
		InvalidateWindow(WC_CLIENT_LIST, 0);
 

	
 
		return NETWORK_RECV_STATUS_OKAY;
 
	}
 

	
 
	// We don't have this index yet, find an empty index, and put the data there
 
	ci = NetworkFindClientInfoFromIndex(NETWORK_EMPTY_INDEX);
 
	if (ci != NULL) {
 
		ci->client_index = index;
 
		ci->client_playas = playas;
 

	
 
		ttd_strlcpy(ci->client_name, name, sizeof(ci->client_name));
 
		ttd_strlcpy(ci->unique_id, unique_id, sizeof(ci->unique_id));
 

	
 
		InvalidateWindow(WC_CLIENT_LIST, 0);
 

	
 
		return NETWORK_RECV_STATUS_OKAY;
 
	}
 

	
 
	// Here the program should never ever come.....
 
	return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_ERROR)
 
{
 
	NetworkErrorCode error = NetworkRecv_uint8(MY_CLIENT, p);
 

	
 
	switch (error) {
 
		/* We made an error in the protocol, and our connection is closed.... */
 
		case NETWORK_ERROR_NOT_AUTHORIZED:
 
		case NETWORK_ERROR_NOT_EXPECTED:
 
		case NETWORK_ERROR_PLAYER_MISMATCH:
 
			_switch_mode_errorstr = STR_NETWORK_ERR_SERVER_ERROR;
 
			break;
 
		case NETWORK_ERROR_FULL:
 
			_switch_mode_errorstr = STR_NETWORK_ERR_SERVER_FULL;
 
			break;
 
		case NETWORK_ERROR_WRONG_REVISION:
 
			_switch_mode_errorstr = STR_NETWORK_ERR_WRONG_REVISION;
 
			break;
 
		case NETWORK_ERROR_WRONG_PASSWORD:
 
			_switch_mode_errorstr = STR_NETWORK_ERR_WRONG_PASSWORD;
 
			break;
 
		case NETWORK_ERROR_KICKED:
 
			_switch_mode_errorstr = STR_NETWORK_ERR_KICKED;
 
			break;
 
		case NETWORK_ERROR_CHEATER:
 
			_switch_mode_errorstr = STR_NETWORK_ERR_CHEATER;
 
			break;
 
		default:
 
			_switch_mode_errorstr = STR_NETWORK_ERR_LOSTCONNECTION;
 
	}
 

	
 
	DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 

	
 
	return NETWORK_RECV_STATUS_SERVER_ERROR;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_NEED_PASSWORD)
 
{
 
	NetworkPasswordType type = NetworkRecv_uint8(MY_CLIENT, p);
 

	
 
	switch (type) {
 
		case NETWORK_GAME_PASSWORD:
 
		case NETWORK_COMPANY_PASSWORD:
 
			ShowNetworkNeedPassword(type);
 
			return NETWORK_RECV_STATUS_OKAY;
 

	
 
		default: return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 
	}
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_WELCOME)
 
{
 
	_network_own_client_index = NetworkRecv_uint16(MY_CLIENT, p);
 

	
 
	// Start receiving the map
 
	SEND_COMMAND(PACKET_CLIENT_GETMAP)();
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_WAIT)
 
{
 
	_network_join_status = NETWORK_JOIN_STATUS_WAITING;
 
	_network_join_waiting = NetworkRecv_uint8(MY_CLIENT, p);
 
	InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0);
 

	
 
	// We are put on hold for receiving the map.. we need GUI for this ;)
 
	DEBUG(net, 1, "The server is currently busy sending the map to someone else, please wait..." );
 
	DEBUG(net, 1, "There are %d clients in front of you", _network_join_waiting);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_MAP)
 
{
 
	static char filename[256];
 
	static FILE *file_pointer;
 

	
 
	byte maptype;
 

	
 
	maptype = NetworkRecv_uint8(MY_CLIENT, p);
 

	
 
	if (MY_CLIENT->has_quit) return NETWORK_RECV_STATUS_CONN_LOST;
 

	
 
	// First packet, init some stuff
 
	if (maptype == MAP_PACKET_START) {
 
		// The name for the temp-map
 
		snprintf(filename, lengthof(filename), "%s%snetwork_client.tmp",  _paths.autosave_dir, PATHSEP);
 

	
 
		file_pointer = fopen(filename, "wb");
 
		if (file_pointer == NULL) {
 
			_switch_mode_errorstr = STR_NETWORK_ERR_SAVEGAMEERROR;
 
			return NETWORK_RECV_STATUS_SAVEGAME;
 
		}
 

	
 
		_frame_counter = _frame_counter_server = _frame_counter_max = NetworkRecv_uint32(MY_CLIENT, p);
 

	
 
		_network_join_status = NETWORK_JOIN_STATUS_DOWNLOADING;
 
		_network_join_kbytes = 0;
 
		_network_join_kbytes_total = NetworkRecv_uint32(MY_CLIENT, p) / 1024;
 
		InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0);
 

	
 
		// The first packet does not contain any more data
 
		return NETWORK_RECV_STATUS_OKAY;
 
	}
 

	
 
	if (maptype == MAP_PACKET_NORMAL) {
 
		// We are still receiving data, put it to the file
 
		fwrite(p->buffer + p->pos, 1, p->size - p->pos, file_pointer);
 

	
 
		_network_join_kbytes = ftell(file_pointer) / 1024;
 
		InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0);
 
	}
 

	
 
	// Check if this was the last packet
 
	if (maptype == MAP_PACKET_END) {
 
		fclose(file_pointer);
 

	
 
		_network_join_status = NETWORK_JOIN_STATUS_PROCESSING;
 
		InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0);
 

	
 
		/* The map is done downloading, load it */
 
		if (!SafeSaveOrLoad(filename, SL_LOAD, GM_NORMAL)) {
 
			DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 
			_switch_mode_errorstr = STR_NETWORK_ERR_SAVEGAMEERROR;
 
			return NETWORK_RECV_STATUS_SAVEGAME;
 
		}
 
		/* If the savegame has successfully loaded, ALL windows have been removed,
 
		 * only toolbar/statusbar and gamefield are visible */
 

	
 
		_opt_ptr = &_opt; // during a network game you are always in-game
 

	
 
		// Say we received the map and loaded it correctly!
 
		SEND_COMMAND(PACKET_CLIENT_MAP_OK)();
 

	
 
		/* New company/spectator (invalid player) or company we want to join is not active
 
		 * Switch local player to spectator and await the server's judgement */
 
		if (_network_playas == PLAYER_NEW_COMPANY || !IsValidPlayer(_network_playas) ||
 
				!GetPlayer(_network_playas)->is_active) {
 

	
 
			SetLocalPlayer(PLAYER_SPECTATOR);
 

	
 
			if (_network_playas != PLAYER_SPECTATOR) {
 
				/* We have arrived and ready to start playing; send a command to make a new player;
 
				 * the server will give us a client-id and let us in */
 
				_network_join_status = NETWORK_JOIN_STATUS_REGISTERING;
 
				ShowJoinStatusWindow();
 
				NetworkSend_Command(0, 0, 0, CMD_PLAYER_CTRL, NULL);
 
			}
 
		} else {
 
			// take control over an existing company
 
			SetLocalPlayer(_network_playas);
 
		}
 
	}
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_FRAME)
 
{
 
	_frame_counter_server = NetworkRecv_uint32(MY_CLIENT, p);
 
	_frame_counter_max = NetworkRecv_uint32(MY_CLIENT, p);
 
#ifdef ENABLE_NETWORK_SYNC_EVERY_FRAME
 
	// Test if the server supports this option
 
	//  and if we are at the frame the server is
 
	if (p->pos < p->size) {
 
		_sync_frame = _frame_counter_server;
 
		_sync_seed_1 = NetworkRecv_uint32(MY_CLIENT, p);
 
#ifdef NETWORK_SEND_DOUBLE_SEED
 
		_sync_seed_2 = NetworkRecv_uint32(MY_CLIENT, p);
 
#endif
 
	}
 
#endif
 
	DEBUG(net, 5, "Received FRAME %d", _frame_counter_server);
 

	
 
	// Let the server know that we received this frame correctly
 
	//  We do this only once per day, to save some bandwidth ;)
 
	if (!_network_first_time && last_ack_frame < _frame_counter) {
 
		last_ack_frame = _frame_counter + DAY_TICKS;
 
		DEBUG(net, 4, "Sent ACK at %d", _frame_counter);
 
		SEND_COMMAND(PACKET_CLIENT_ACK)();
 
	}
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_SYNC)
 
{
 
	_sync_frame = NetworkRecv_uint32(MY_CLIENT, p);
 
	_sync_seed_1 = NetworkRecv_uint32(MY_CLIENT, p);
 
#ifdef NETWORK_SEND_DOUBLE_SEED
 
	_sync_seed_2 = NetworkRecv_uint32(MY_CLIENT, p);
 
#endif
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_COMMAND)
 
{
 
	CommandPacket *cp = malloc(sizeof(CommandPacket));
 
	cp->player = NetworkRecv_uint8(MY_CLIENT, p);
 
	cp->cmd = NetworkRecv_uint32(MY_CLIENT, p);
 
	cp->p1 = NetworkRecv_uint32(MY_CLIENT, p);
 
	cp->p2 = NetworkRecv_uint32(MY_CLIENT, p);
 
	cp->tile = NetworkRecv_uint32(MY_CLIENT, p);
 
	NetworkRecv_string(MY_CLIENT, p, cp->text, sizeof(cp->text));
 
	cp->callback = NetworkRecv_uint8(MY_CLIENT, p);
 
	cp->frame = NetworkRecv_uint32(MY_CLIENT, p);
 
	cp->next = NULL;
 

	
 
	// The server did send us this command..
 
	//  queue it in our own queue, so we can handle it in the upcoming frame!
 

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

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_CHAT)
 
{
 
	char name[NETWORK_NAME_LENGTH], msg[MAX_TEXT_MSG_LEN];
 
	const NetworkClientInfo *ci = NULL, *ci_to;
 

	
 
	NetworkAction action = NetworkRecv_uint8(MY_CLIENT, p);
 
	uint16 index = NetworkRecv_uint16(MY_CLIENT, p);
 
	bool self_send = NetworkRecv_uint8(MY_CLIENT, p);
 
	NetworkRecv_string(MY_CLIENT, p, msg, MAX_TEXT_MSG_LEN);
 

	
 
	ci_to = NetworkFindClientInfoFromIndex(index);
 
	if (ci_to == NULL) return NETWORK_RECV_STATUS_OKAY;
 

	
 
	/* Did we initiate the action locally? */
 
	if (self_send) {
 
		switch (action) {
 
			case NETWORK_ACTION_CHAT_CLIENT:
 
				/* For speaking to client we need the client-name */
 
				snprintf(name, sizeof(name), "%s", ci_to->client_name);
 
				ci = NetworkFindClientInfoFromIndex(_network_own_client_index);
 
				break;
 

	
 
			/* For speaking to company or giving money, we need the player-name */
 
			case NETWORK_ACTION_GIVE_MONEY:
 
				if (!IsValidPlayer(ci_to->client_playas)) return NETWORK_RECV_STATUS_OKAY;
 
				/* fallthrough */
 
			case NETWORK_ACTION_CHAT_COMPANY: {
 
				StringID str = IsValidPlayer(ci_to->client_playas) ? GetPlayer(ci_to->client_playas)->name_1 : STR_NETWORK_SPECTATORS;
 

	
 
				GetString(name, str, lastof(name));
 
				ci = NetworkFindClientInfoFromIndex(_network_own_client_index);
 
			} break;
 

	
 
			default: NOT_REACHED(); break;
 
		}
 
	} else {
 
		/* Display message from somebody else */
 
		snprintf(name, sizeof(name), "%s", ci_to->client_name);
 
		ci = ci_to;
 
	}
 

	
 
	if (ci != NULL)
 
		NetworkTextMessage(action, GetDrawStringPlayerColor(ci->client_playas), self_send, name, "%s", msg);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_ERROR_QUIT)
 
{
 
	char str[100];
 
	uint16 index;
 
	NetworkClientInfo *ci;
 

	
 
	index = NetworkRecv_uint16(MY_CLIENT, p);
 
	GetNetworkErrorMsg(str, NetworkRecv_uint8(MY_CLIENT, p), lastof(str));
 

	
 
	ci = NetworkFindClientInfoFromIndex(index);
 
	if (ci != NULL) {
 
		NetworkTextMessage(NETWORK_ACTION_LEAVE, 1, false, ci->client_name, "%s", str);
 

	
 
		// The client is gone, give the NetworkClientInfo free
 
		ci->client_index = NETWORK_EMPTY_INDEX;
 
	}
 

	
 
	InvalidateWindow(WC_CLIENT_LIST, 0);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_QUIT)
 
{
 
	char str[100];
 
	uint16 index;
 
	NetworkClientInfo *ci;
 

	
 
	index = NetworkRecv_uint16(MY_CLIENT, p);
 
	NetworkRecv_string(MY_CLIENT, p, str, lengthof(str));
 

	
 
	ci = NetworkFindClientInfoFromIndex(index);
 
	if (ci != NULL) {
 
		NetworkTextMessage(NETWORK_ACTION_LEAVE, 1, false, ci->client_name, "%s", str);
 

	
 
		// The client is gone, give the NetworkClientInfo free
 
		ci->client_index = NETWORK_EMPTY_INDEX;
 
	} else {
 
		DEBUG(net, 0, "Unknown client (%d) is leaving the game", index);
 
	}
 

	
 
	InvalidateWindow(WC_CLIENT_LIST, 0);
 

	
 
	// If we come here it means we could not locate the client.. strange :s
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_JOIN)
 
{
 
	uint16 index;
 
	NetworkClientInfo *ci;
 

	
 
	index = NetworkRecv_uint16(MY_CLIENT, p);
 

	
 
	ci = NetworkFindClientInfoFromIndex(index);
 
	if (ci != NULL)
 
		NetworkTextMessage(NETWORK_ACTION_JOIN, 1, false, ci->client_name, "");
 

	
 
	InvalidateWindow(WC_CLIENT_LIST, 0);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_SHUTDOWN)
 
{
 
	_switch_mode_errorstr = STR_NETWORK_SERVER_SHUTDOWN;
 

	
 
	return NETWORK_RECV_STATUS_SERVER_ERROR;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_NEWGAME)
 
{
 
	// To trottle the reconnects a bit, every clients waits
 
	//  his _local_player value before reconnecting
 
	// PLAYER_SPECTATOR is currently 255, so to avoid long wait periods
 
	//  set the max to 10.
 
	_network_reconnect = min(_local_player + 1, 10);
 
	_switch_mode_errorstr = STR_NETWORK_SERVER_REBOOT;
 

	
 
	return NETWORK_RECV_STATUS_SERVER_ERROR;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_RCON)
 
{
 
	char rcon_out[NETWORK_RCONCOMMAND_LENGTH];
 
	uint16 color_code;
 

	
 
	color_code = NetworkRecv_uint16(MY_CLIENT, p);
 
	NetworkRecv_string(MY_CLIENT, p, rcon_out, sizeof(rcon_out));
 

	
 
	IConsolePrint(color_code, rcon_out);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 

	
 

	
 
// The layout for the receive-functions by the client
 
typedef NetworkRecvStatus NetworkClientPacket(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 NetworkClientPacket* const _network_client_packet[] = {
 
	RECEIVE_COMMAND(PACKET_SERVER_FULL),
 
	RECEIVE_COMMAND(PACKET_SERVER_BANNED),
 
	NULL, /*PACKET_CLIENT_JOIN,*/
 
	RECEIVE_COMMAND(PACKET_SERVER_ERROR),
 
	NULL, /*PACKET_CLIENT_COMPANY_INFO,*/
 
	RECEIVE_COMMAND(PACKET_SERVER_COMPANY_INFO),
 
	RECEIVE_COMMAND(PACKET_SERVER_CLIENT_INFO),
 
	RECEIVE_COMMAND(PACKET_SERVER_NEED_PASSWORD),
 
	NULL, /*PACKET_CLIENT_PASSWORD,*/
 
	RECEIVE_COMMAND(PACKET_SERVER_WELCOME),
 
	NULL, /*PACKET_CLIENT_GETMAP,*/
 
	RECEIVE_COMMAND(PACKET_SERVER_WAIT),
 
	RECEIVE_COMMAND(PACKET_SERVER_MAP),
 
	NULL, /*PACKET_CLIENT_MAP_OK,*/
 
	RECEIVE_COMMAND(PACKET_SERVER_JOIN),
 
	RECEIVE_COMMAND(PACKET_SERVER_FRAME),
 
	RECEIVE_COMMAND(PACKET_SERVER_SYNC),
 
	NULL, /*PACKET_CLIENT_ACK,*/
 
	NULL, /*PACKET_CLIENT_COMMAND,*/
 
	RECEIVE_COMMAND(PACKET_SERVER_COMMAND),
 
	NULL, /*PACKET_CLIENT_CHAT,*/
 
	RECEIVE_COMMAND(PACKET_SERVER_CHAT),
 
	NULL, /*PACKET_CLIENT_SET_PASSWORD,*/
 
	NULL, /*PACKET_CLIENT_SET_NAME,*/
 
	NULL, /*PACKET_CLIENT_QUIT,*/
 
	NULL, /*PACKET_CLIENT_ERROR,*/
 
	RECEIVE_COMMAND(PACKET_SERVER_QUIT),
 
	RECEIVE_COMMAND(PACKET_SERVER_ERROR_QUIT),
 
	RECEIVE_COMMAND(PACKET_SERVER_SHUTDOWN),
 
	RECEIVE_COMMAND(PACKET_SERVER_NEWGAME),
 
	RECEIVE_COMMAND(PACKET_SERVER_RCON),
 
	NULL, /*PACKET_CLIENT_RCON,*/
 
};
 

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

	
 
// 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(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(MY_CLIENT, p);
 
		if (type < PACKET_END && _network_client_packet[type] != NULL && !MY_CLIENT->has_quit) {
 
			res = _network_client_packet[type](p);
 
		} else {
 
			res = NETWORK_RECV_STATUS_MALFORMED_PACKET;
 
			DEBUG(net, 0, "[client] received invalid packet type %d", type);
 
		}
 

	
 
		free(p);
 
	}
 

	
 
	return res;
 
}
 

	
 
#endif /* ENABLE_NETWORK */
network/network_client.h
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#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);
 
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_RCON)(const char *pass, const char *command);
 

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

	
 
#endif /* ENABLE_NETWORK */
 

	
 
#endif /* NETWORK_CLIENT_H */
network/network_data.c
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "../stdafx.h"
 
#include "../debug.h"
 
#include "network_data.h"
 
#include "../string.h"
 
#include "network_client.h"
 
#include "../command.h"
 
#include "../callback_table.h"
 

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

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

	
 
// Prepare a DoCommand to be send over the network
 
void NetworkSend_Command(TileIndex 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, "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
 
	}
 

	
 
	ttd_strlcpy(c->text, (_cmd_text != NULL) ? _cmd_text : "", lengthof(c->text));
 

	
 
	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 than others.
 
		//   So to keep the game fair, we delay the command with 1 tick
 
		//   which gives about the same speed as most clients.
 
		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;
 
	_cmd_text = cp->text;
 
	/* cp->callback is unsigned. so we don't need to do lower bounds checking. */
 
	if (cp->callback > _callback_table_count) {
 
		DEBUG(net, 0, "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/network_data.h
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifndef NETWORK_DATA_H
 
#define NETWORK_DATA_H
 

	
 
// Is the network enabled?
 
#ifdef ENABLE_NETWORK
 

	
 
#include "../openttd.h"
 
#include "network.h"
 
#include "core/os_abstraction.h"
 
#include "core/config.h"
 
#include "core/packet.h"
 

	
 
#define MAX_TEXT_MSG_LEN 1024 /* long long long long sentences :-) */
 

	
 
// The client-info-server-index is always 1
 
#define NETWORK_SERVER_INDEX 1
 
#define NETWORK_EMPTY_INDEX 0
 

	
 
typedef struct CommandPacket {
 
	struct CommandPacket *next;
 
	PlayerID player; /// player that is executing the command
 
	uint32 cmd;    /// command being executed
 
	uint32 p1;     /// parameter p1
 
	uint32 p2;     /// parameter p2
 
	TileIndex tile; /// tile command being executed on
 
	char text[80];
 
	uint32 frame;  /// the frame in which this packet is executed
 
	byte callback; /// any callback function executed upon successful completion of the command
 
} CommandPacket;
 

	
 
typedef enum {
 
	STATUS_INACTIVE,
 
	STATUS_AUTH, // This means that the client is authorized
 
	STATUS_MAP_WAIT, // This means that the client is put on hold because someone else is getting the map
 
	STATUS_MAP,
 
	STATUS_DONE_MAP,
 
	STATUS_PRE_ACTIVE,
 
	STATUS_ACTIVE,
 
} ClientStatus;
 

	
 
typedef enum {
 
	MAP_PACKET_START,
 
	MAP_PACKET_NORMAL,
 
	MAP_PACKET_END,
 
} MapPacket;
 

	
 
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_SERVER_BANNED,
 
	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,
 
	NETWORK_ERROR_CHEATER,
 
	NETWORK_ERROR_FULL,
 
} NetworkErrorCode;
 

	
 
// Actions that can be used for NetworkTextMessage
 
typedef enum {
 
	NETWORK_ACTION_JOIN,
 
	NETWORK_ACTION_LEAVE,
 
	NETWORK_ACTION_SERVER_MESSAGE,
 
	NETWORK_ACTION_CHAT,
 
	NETWORK_ACTION_CHAT_COMPANY,
 
	NETWORK_ACTION_CHAT_CLIENT,
 
	NETWORK_ACTION_GIVE_MONEY,
 
	NETWORK_ACTION_NAME_CHANGE,
 
} NetworkAction;
 

	
 
typedef enum {
 
	NETWORK_GAME_PASSWORD,
 
	NETWORK_COMPANY_PASSWORD,
 
} NetworkPasswordType;
 

	
 
// To keep the clients all together
 
struct NetworkClientState { // Typedeffed in network_core/packet.h
 
	SOCKET 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 has_quit;
 

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

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

	
 
typedef enum {
 
	DESTTYPE_BROADCAST, ///< Send message/notice to all players (All)
 
	DESTTYPE_TEAM,    ///< Send message/notice to everyone playing the same company (Team)
 
	DESTTYPE_CLIENT,    ///< Send message/notice to only a certain player (Private)
 
} DestType;
 

	
 
CommandPacket *_local_command_queue;
 

	
 
SOCKET _udp_client_socket; // udp client socket
 
SOCKET _udp_server_socket; // udp server socket
 
SOCKET _udp_master_socket; // udp master socket
 

	
 
// Here we keep track of the clients
 
//  (and the client uses [0] for his own communication)
 
NetworkClientState _clients[MAX_CLIENTS];
 
#define DEREF_CLIENT(i) (&_clients[i])
 
// 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(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 != endof(_clients) && cs->socket != INVALID_SOCKET; cs++)
 
#define FOR_ALL_ACTIVE_CLIENT_INFOS(ci) for (ci = _network_client_info; ci != endof(_network_client_info); ci++) if (ci->client_index != NETWORK_EMPTY_INDEX)
 

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

	
 
// from network.c
 
void NetworkCloseClient(NetworkClientState *cs);
 
void CDECL NetworkTextMessage(NetworkAction action, uint16 color, bool self_send, const char *name, const char *str, ...);
 
void NetworkGetClientName(char *clientname, size_t size, const NetworkClientState *cs);
 
uint NetworkCalculateLag(const NetworkClientState *cs);
 
byte NetworkGetCurrentLanguageIndex(void);
 
NetworkClientInfo *NetworkFindClientInfoFromIndex(uint16 client_index);
 
NetworkClientInfo *NetworkFindClientInfoFromIP(const char *ip);
 
NetworkClientState *NetworkFindClientStateFromIndex(uint16 client_index);
 
unsigned long NetworkResolveHost(const char *hostname);
 
char* GetNetworkErrorMsg(char* buf, NetworkErrorCode err, const char* last);
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 
#endif /* NETWORK_DATA_H */
network/network_gamelist.c
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "../stdafx.h"
 
#include "../debug.h"
 
#include "network_data.h"
 
#include "../newgrf_config.h"
 

	
 
// This file handles the GameList
 
// Also, it handles the request to a server for data about the server
 

	
 
/** Add a new item to the linked gamelist. If the IP and Port match
 
 * return the existing item instead of adding it again
 
 * @param ip the IP-address (inet_addr) of the to-be added item
 
 * @param port the port the server is running on
 
 * @return a point to the newly added or already existing item */
 
NetworkGameList *NetworkGameListAddItem(uint32 ip, uint16 port)
 
{
 
	NetworkGameList *item, *prev_item;
 

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

	
 
	item = malloc(sizeof(*item));
 
	memset(item, 0, sizeof(*item));
 
	item->next = NULL;
 
	item->ip = ip;
 
	item->port = port;
 

	
 
	if (prev_item == NULL) {
 
		_network_game_list = item;
 
	} else {
 
		prev_item->next = item;
 
	}
 
	DEBUG(net, 4, "[gamelist] added server to list");
 

	
 
	UpdateNetworkGameWindow(false);
 

	
 
	return item;
 
}
 

	
 
/** Remove an item from the gamelist linked list
 
 * @param remove pointer to the item to be removed */
 
void NetworkGameListRemoveItem(NetworkGameList *remove)
 
{
 
	NetworkGameList *item, *prev_item;
 

	
 
	prev_item = NULL;
 
	for (item = _network_game_list; item != NULL; item = item->next) {
 
		if (remove == item) {
 
			if (prev_item == NULL) {
 
				_network_game_list = remove->next;
 
			} else {
 
				prev_item->next = remove->next;
 
			}
 

	
 
			/* Remove GRFConfig information */
 
			ClearGRFConfigList(&remove->info.grfconfig);
 
			free(remove);
 
			remove = NULL;
 

	
 
			DEBUG(net, 4, "[gamelist] removed server from list");
 
			UpdateNetworkGameWindow(false);
 
			return;
 
		}
 
		prev_item = item;
 
	}
 
}
 

	
 
#endif /* ENABLE_NETWORK */
network/network_gamelist.h
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifndef NETWORK_GAMELIST_H
 
#define NETWORK_GAMELIST_H
 

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

	
 
#endif /* NETWORK_GAMELIST_H */
network/network_gui.c
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifdef ENABLE_NETWORK
 
#include "../stdafx.h"
 
#include "../openttd.h"
 
#include "../string.h"
 
#include "../strings.h"
 
#include "../table/sprites.h"
 
#include "network.h"
 
#include "../date.h"
 

	
 
#include "../fios.h"
 
#include "../table/strings.h"
 
#include "../functions.h"
 
#include "network_data.h"
 
#include "network_client.h"
 
#include "network_gui.h"
 
#include "network_gamelist.h"
 
#include "../window.h"
 
#include "../gui.h"
 
#include "../gfx.h"
 
#include "../command.h"
 
#include "../variables.h"
 
#include "network_server.h"
 
#include "network_udp.h"
 
#include "../settings.h"
 
#include "../string.h"
 
#include "../town.h"
 
#include "../newgrf.h"
 

	
 
#define BGC 5
 
#define BTC 15
 

	
 
typedef struct network_d {
 
	PlayerID company;        // select company in network lobby
 
	byte field;              // select text-field in start-server and game-listing
 
	NetworkGameList *server; // selected server in lobby and game-listing
 
	FiosItem *map;           // selected map in start-server
 
} network_d;
 
assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(network_d));
 

	
 
typedef struct network_ql_d {
 
	network_d n;                 // see above; general stuff
 
	querystr_d q;                // text-input in start-server and game-listing
 
	NetworkGameList **sort_list; // list of games (sorted)
 
	list_d l;                    // accompanying list-administration
 
} network_ql_d;
 
assert_compile(WINDOW_CUSTOM_SIZE >= sizeof(network_ql_d));
 

	
 
/* Global to remember sorting after window has been closed */
 
static Listing _ng_sorting;
 

	
 
static char _edit_str_buf[150];
 
static bool _chat_tab_completion_active;
 

	
 
static void ShowNetworkStartServerWindow(void);
 
static void ShowNetworkLobbyWindow(NetworkGameList *ngl);
 
extern void SwitchMode(int new_mode);
 

	
 
static const StringID _connection_types_dropdown[] = {
 
	STR_NETWORK_LAN_INTERNET,
 
	STR_NETWORK_INTERNET_ADVERTISE,
 
	INVALID_STRING_ID
 
};
 

	
 
static const StringID _lan_internet_types_dropdown[] = {
 
	STR_NETWORK_LAN,
 
	STR_NETWORK_INTERNET,
 
	INVALID_STRING_ID
 
};
 

	
 
static const StringID _players_dropdown[] = {
 
	STR_NETWORK_0_PLAYERS,
 
	STR_NETWORK_1_PLAYERS,
 
	STR_NETWORK_2_PLAYERS,
 
	STR_NETWORK_3_PLAYERS,
 
	STR_NETWORK_4_PLAYERS,
 
	STR_NETWORK_5_PLAYERS,
 
	STR_NETWORK_6_PLAYERS,
 
	STR_NETWORK_7_PLAYERS,
 
	STR_NETWORK_8_PLAYERS,
 
	STR_NETWORK_9_PLAYERS,
 
	STR_NETWORK_10_PLAYERS,
 
	INVALID_STRING_ID
 
};
 

	
 
static const StringID _language_dropdown[] = {
 
	STR_NETWORK_LANG_ANY,
 
	STR_NETWORK_LANG_ENGLISH,
 
	STR_NETWORK_LANG_GERMAN,
 
	STR_NETWORK_LANG_FRENCH,
 
	INVALID_STRING_ID
 
};
 

	
 
enum {
 
	NET_PRC__OFFSET_TOP_WIDGET          = 54,
 
	NET_PRC__OFFSET_TOP_WIDGET_COMPANY  = 52,
 
	NET_PRC__SIZE_OF_ROW                = 14,
 
};
 

	
 
/** Update the network new window because a new server is
 
 * found on the network.
 
 * @param unselect unselect the currently selected item */
 
void UpdateNetworkGameWindow(bool unselect)
 
{
 
	SendWindowMessage(WC_NETWORK_WINDOW, 0, unselect, 0, 0);
 
}
 

	
 
static bool _internal_sort_order; // Used for Qsort order-flipping
 
typedef int CDECL NGameNameSortFunction(const void*, const void*);
 

	
 
/** Qsort function to sort by name. */
 
static int CDECL NGameNameSorter(const void *a, const void *b)
 
{
 
	const NetworkGameList *cmp1 = *(const NetworkGameList**)a;
 
	const NetworkGameList *cmp2 = *(const NetworkGameList**)b;
 
	int r = strcasecmp(cmp1->info.server_name, cmp2->info.server_name);
 

	
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
/** Qsort function to sort by the amount of clients online on a
 
 * server. If the two servers have the same amount, the one with the
 
 * higher maximum is preferred. */
 
static int CDECL NGameClientSorter(const void *a, const void *b)
 
{
 
	const NetworkGameList *cmp1 = *(const NetworkGameList**)a;
 
	const NetworkGameList *cmp2 = *(const NetworkGameList**)b;
 
	/* Reverse as per default we are interested in most-clients first */
 
	int r = cmp1->info.clients_on - cmp2->info.clients_on;
 

	
 
	if (r == 0) r = cmp1->info.clients_max - cmp2->info.clients_max;
 
	if (r == 0) r = strcasecmp(cmp1->info.server_name, cmp2->info.server_name);
 

	
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
/** Qsort function to sort by joinability. If both servers are the
 
 * same, prefer the non-passworded server first. */
 
static int CDECL NGameAllowedSorter(const void *a, const void *b)
 
{
 
	const NetworkGameList *cmp1 = *(const NetworkGameList**)a;
 
	const NetworkGameList *cmp2 = *(const NetworkGameList**)b;
 
	/* Reverse default as we are interested in compatible clients first */
 
	int r = cmp2->info.compatible - cmp1->info.compatible;
 

	
 
	if (r == 0) r = cmp1->info.use_password - cmp2->info.use_password;
 
	if (r == 0) r = strcasecmp(cmp1->info.server_name, cmp2->info.server_name);
 

	
 
	return _internal_sort_order ? -r : r;
 
}
 

	
 
/** (Re)build the network game list as its amount has changed because
 
 * an item has been added or deleted for example
 
 * @param ngl list_d struct that contains all necessary information for sorting */
 
static void BuildNetworkGameList(network_ql_d *nqld)
 
{
 
	NetworkGameList *ngl_temp;
 
	uint n = 0;
 

	
 
	if (!(nqld->l.flags & VL_REBUILD)) return;
 

	
 
	/* Count the number of games in the list */
 
	for (ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) n++;
 
	if (n == 0) return;
 

	
 
	/* Create temporary array of games to use for listing */
 
	free(nqld->sort_list);
 
	nqld->sort_list = malloc(n * sizeof(nqld->sort_list[0]));
 
	if (nqld->sort_list == NULL) error("Could not allocate memory for the network-game-sorting-list");
 
	nqld->l.list_length = n;
 

	
 
	for (n = 0, ngl_temp = _network_game_list; ngl_temp != NULL; ngl_temp = ngl_temp->next) {
 
		nqld->sort_list[n++] = ngl_temp;
 
	}
 

	
 
	/* Force resort */
 
	nqld->l.flags &= ~VL_REBUILD;
 
	nqld->l.flags |= VL_RESORT;
 
}
 

	
 
static void SortNetworkGameList(network_ql_d *nqld)
 
{
 
	static NGameNameSortFunction * const ngame_sorter[] = {
 
		&NGameNameSorter,
 
		&NGameClientSorter,
 
		&NGameAllowedSorter
 
	};
 

	
 
	NetworkGameList *item;
 
	uint i;
 

	
 
	if (!(nqld->l.flags & VL_RESORT)) return;
 
	if (nqld->l.list_length == 0) return;
 

	
 
	_internal_sort_order = !!(nqld->l.flags & VL_DESC);
 
	qsort(nqld->sort_list, nqld->l.list_length, sizeof(nqld->sort_list[0]), ngame_sorter[nqld->l.sort_type]);
 

	
 
	/* After sorting ngl->sort_list contains the sorted items. Put these back
 
	 * into the original list. Basically nothing has changed, we are only
 
	 * shuffling the ->next pointers */
 
	_network_game_list = nqld->sort_list[0];
 
	for (item = _network_game_list, i = 1; i != nqld->l.list_length; i++) {
 
		item->next = nqld->sort_list[i];
 
		item = item->next;
 
	}
 
	item->next = NULL;
 

	
 
	nqld->l.flags &= ~VL_RESORT;
 
}
 

	
 
/* Uses network_ql_d (network_d, querystr_d and list_d) WP macro */
 
static void NetworkGameWindowWndProc(Window *w, WindowEvent *e)
 
{
 
	network_d *nd = &WP(w, network_ql_d).n;
 
	list_d *ld = &WP(w, network_ql_d).l;
 

	
 
	switch (e->event) {
 
	case WE_CREATE: /* Focus input box */
 
		nd->field = 3;
 
		nd->server = NULL;
 

	
 
		WP(w, network_ql_d).sort_list = NULL;
 
		ld->flags = VL_REBUILD | (_ng_sorting.order << (VL_DESC - 1));
 
		ld->sort_type = _ng_sorting.criteria;
 
		break;
 

	
 
	case WE_PAINT: {
 
		const NetworkGameList *sel = nd->server;
 
		const char *arrow = (ld->flags & VL_DESC) ? DOWNARROW : UPARROW;
 

	
 
		if (ld->flags & VL_REBUILD) {
 
			BuildNetworkGameList(&WP(w, network_ql_d));
 
			SetVScrollCount(w, ld->list_length);
 
		}
 
		if (ld->flags & VL_RESORT) SortNetworkGameList(&WP(w, network_ql_d));
 

	
 
		SetWindowWidgetDisabledState(w, 17, sel == NULL);
 
		/* Join Button disabling conditions */
 
		SetWindowWidgetDisabledState(w, 16, sel == NULL || // no Selected Server
 
				!sel->online || // Server offline
 
				sel->info.clients_on >= sel->info.clients_max || // Server full
 
				!sel->info.compatible); // Revision mismatch
 

	
 
		SetWindowWidgetHiddenState(w, 18, sel == NULL ||
 
				!sel->online ||
 
				sel->info.grfconfig == NULL);
 

	
 
		SetDParam(0, 0x00);
 
		SetDParam(7, _lan_internet_types_dropdown[_network_lan_internet]);
 
		DrawWindowWidgets(w);
 

	
 
		DrawEditBox(w, &WP(w, network_ql_d).q, 3);
 

	
 
		DrawString(9, 23, STR_NETWORK_CONNECTION, 2);
 
		DrawString(210, 23, STR_NETWORK_PLAYER_NAME, 2);
 

	
 
		/* Sort based on widgets: name, clients, compatibility */
 
		switch (ld->sort_type) {
 
			case 6 - 6: DoDrawString(arrow, w->widget[6].right - 10, 42, 0x10); break;
 
			case 7 - 6: DoDrawString(arrow, w->widget[7].right - 10, 42, 0x10); break;
 
			case 8 - 6: DoDrawString(arrow, w->widget[8].right - 10, 42, 0x10); break;
 
		}
 

	
 
		{ // draw list of games
 
			uint16 y = NET_PRC__OFFSET_TOP_WIDGET + 3;
 
			int32 n = 0;
 
			int32 pos = w->vscroll.pos;
 
			uint max_name_width = w->widget[6].right - w->widget[6].left - 5;
 
			const NetworkGameList *cur_item = _network_game_list;
 

	
 
			while (pos > 0 && cur_item != NULL) {
 
				pos--;
 
				cur_item = cur_item->next;
 
			}
 

	
 
			while (cur_item != NULL) {
 
				// show highlighted item with a different colour
 
				if (cur_item == sel) GfxFillRect(w->widget[6].left + 1, y - 2, w->widget[8].right - 1, y + 9, 10);
 

	
 
				SetDParamStr(0, cur_item->info.server_name);
 
				DrawStringTruncated(w->widget[6].left + 5, y, STR_02BD, 16, max_name_width);
 

	
 
				SetDParam(0, cur_item->info.clients_on);
 
				SetDParam(1, cur_item->info.clients_max);
 
				SetDParam(2, cur_item->info.companies_on);
 
				SetDParam(3, cur_item->info.companies_max);
 
				DrawStringCentered(210, y, STR_NETWORK_GENERAL_ONLINE, 2);
 

	
 
				// only draw icons if the server is online
 
				if (cur_item->online) {
 
					// draw a lock if the server is password protected.
 
					if (cur_item->info.use_password) DrawSprite(SPR_LOCK, w->widget[8].left + 5, y - 1);
 

	
 
					// draw red or green icon, depending on compatibility with server.
 
					DrawSprite(SPR_BLOT | (cur_item->info.compatible ? PALETTE_TO_GREEN : (cur_item->info.version_compatible ? PALETTE_TO_YELLOW : PALETTE_TO_RED)), w->widget[8].left + 15, y);
 

	
 
					// draw flag according to server language
 
					DrawSprite(SPR_FLAGS_BASE + cur_item->info.server_lang, w->widget[8].left + 25, y);
 
				}
 

	
 
				cur_item = cur_item->next;
 
				y += NET_PRC__SIZE_OF_ROW;
 
				if (++n == w->vscroll.cap) break; // max number of games in the window
 
			}
 
		}
 

	
 
		/* Draw the right menu */
 
		GfxFillRect(311, 43, 539, 92, 157);
 
		if (sel == NULL) {
 
			DrawStringCentered(425, 58, STR_NETWORK_GAME_INFO, 0);
 
		} else if (!sel->online) {
 
			SetDParamStr(0, sel->info.server_name);
 
			DrawStringCentered(425, 68, STR_ORANGE, 0); // game name
 

	
 
			DrawStringCentered(425, 132, STR_NETWORK_SERVER_OFFLINE, 0); // server offline
 
		} else { // show game info
 
			uint16 y = 100;
 
			const uint16 x = w->widget[15].left + 5;
 

	
 
			DrawStringCentered(425, 48, STR_NETWORK_GAME_INFO, 0);
 

	
 

	
 
			SetDParamStr(0, sel->info.server_name);
 
			DrawStringCenteredTruncated(w->widget[15].left, w->widget[15].right, 62, STR_ORANGE, 16); // game name
 

	
 
			SetDParamStr(0, sel->info.map_name);
 
			DrawStringCenteredTruncated(w->widget[15].left, w->widget[15].right, 74, STR_02BD, 16); // map name
 

	
 
			SetDParam(0, sel->info.clients_on);
 
			SetDParam(1, sel->info.clients_max);
 
			SetDParam(2, sel->info.companies_on);
 
			SetDParam(3, sel->info.companies_max);
 
			DrawString(x, y, STR_NETWORK_CLIENTS, 2);
 
			y += 10;
 

	
 
			SetDParam(0, _language_dropdown[sel->info.server_lang]);
 
			DrawString(x, y, STR_NETWORK_LANGUAGE, 2); // server language
 
			y += 10;
 

	
 
			SetDParam(0, STR_TEMPERATE_LANDSCAPE + sel->info.map_set);
 
			DrawString(x, y, STR_NETWORK_TILESET, 2); // tileset
 
			y += 10;
 

	
 
			SetDParam(0, sel->info.map_width);
 
			SetDParam(1, sel->info.map_height);
 
			DrawString(x, y, STR_NETWORK_MAP_SIZE, 2); // map size
 
			y += 10;
 

	
 
			SetDParamStr(0, sel->info.server_revision);
 
			DrawString(x, y, STR_NETWORK_SERVER_VERSION, 2); // server version
 
			y += 10;
 

	
 
			SetDParamStr(0, sel->info.hostname);
 
			SetDParam(1, sel->port);
 
			DrawString(x, y, STR_NETWORK_SERVER_ADDRESS, 2); // server address
 
			y += 10;
 

	
 
			SetDParam(0, sel->info.start_date);
 
			DrawString(x, y, STR_NETWORK_START_DATE, 2); // start date
 
			y += 10;
 

	
 
			SetDParam(0, sel->info.game_date);
 
			DrawString(x, y, STR_NETWORK_CURRENT_DATE, 2); // current date
 
			y += 10;
 

	
 
			y += 2;
 

	
 
			if (!sel->info.compatible) {
 
				DrawStringCentered(425, y, sel->info.version_compatible ? STR_NETWORK_GRF_MISMATCH : STR_NETWORK_VERSION_MISMATCH, 0); // server mismatch
 
			} else if (sel->info.clients_on == sel->info.clients_max) {
 
				// Show: server full, when clients_on == clients_max
 
				DrawStringCentered(425, y, STR_NETWORK_SERVER_FULL, 0); // server full
 
			} else if (sel->info.use_password) {
 
				DrawStringCentered(425, y, STR_NETWORK_PASSWORD, 0); // password warning
 
			}
 

	
 
			y += 10;
 
		}
 
	}	break;
 

	
 
	case WE_CLICK:
 
		nd->field = e->we.click.widget;
 
		switch (e->we.click.widget) {
 
		case 0: case 14: /* Close 'X' | Cancel button */
 
			DeleteWindowById(WC_NETWORK_WINDOW, 0);
 
			break;
 
		case 4: case 5:
 
			ShowDropDownMenu(w, _lan_internet_types_dropdown, _network_lan_internet, 5, 0, 0); // do it for widget 5
 
			break;
 
		case 6: /* Sort by name */
 
		case 7: /* Sort by connected clients */
 
		case 8: /* Connectivity (green dot) */
 
			if (ld->sort_type == e->we.click.widget - 6) ld->flags ^= VL_DESC;
 
			ld->flags |= VL_RESORT;
 
			ld->sort_type = e->we.click.widget - 6;
 

	
 
			_ng_sorting.order = !!(ld->flags & VL_DESC);
 
			_ng_sorting.criteria = ld->sort_type;
 
			SetWindowDirty(w);
 
			break;
 
		case 9: { /* Matrix to show networkgames */
 
			NetworkGameList *cur_item;
 
			uint32 id_v = (e->we.click.pt.y - NET_PRC__OFFSET_TOP_WIDGET) / NET_PRC__SIZE_OF_ROW;
 

	
 
			if (id_v >= w->vscroll.cap) return; // click out of bounds
 
			id_v += w->vscroll.pos;
 

	
 
			cur_item = _network_game_list;
 
			for (; id_v > 0 && cur_item != NULL; id_v--) cur_item = cur_item->next;
 

	
 
			nd->server = cur_item;
 
			SetWindowDirty(w);
 
		} break;
 
		case 11: /* Find server automatically */
 
			switch (_network_lan_internet) {
 
				case 0: NetworkUDPSearchGame(); break;
 
				case 1: NetworkUDPQueryMasterServer(); break;
 
			}
 
			break;
 
		case 12: { // Add a server
 
				ShowQueryString(
 
				BindCString(_network_default_ip),
 
				STR_NETWORK_ENTER_IP,
 
				31 | 0x1000,  // maximum number of characters OR
 
				250, // characters up to this width pixels, whichever is satisfied first
 
				w, CS_ALPHANUMERAL);
 
		} break;
 
		case 13: /* Start server */
 
			ShowNetworkStartServerWindow();
 
			break;
 
		case 16: /* Join Game */
 
			if (nd->server != NULL) {
 
				snprintf(_network_last_host, sizeof(_network_last_host), "%s", inet_ntoa(*(struct in_addr *)&nd->server->ip));
 
				_network_last_port = nd->server->port;
 
				ShowNetworkLobbyWindow(nd->server);
 
			}
 
			break;
 
		case 17: // Refresh
 
			if (nd->server != NULL)
 
				NetworkQueryServer(nd->server->info.hostname, nd->server->port, true);
 
			break;
 
		case 18: // NewGRF Settings
 
			if (nd->server != NULL) ShowNewGRFSettings(false, false, false, &nd->server->info.grfconfig);
 
			break;
 

	
 
	}	break;
 

	
 
	case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
 
		switch (e->we.dropdown.button) {
 
			case 5:
 
				_network_lan_internet = e->we.dropdown.index;
 
				break;
 
		}
 

	
 
		SetWindowDirty(w);
 
		break;
 

	
 
	case WE_MOUSELOOP:
 
		if (nd->field == 3) HandleEditBox(w, &WP(w, network_ql_d).q, 3);
 
		break;
 

	
 
	case WE_MESSAGE:
 
		if (e->we.message.msg != 0) nd->server = NULL;
 
		ld->flags |= VL_REBUILD;
 
		SetWindowDirty(w);
 
		break;
 

	
 
	case WE_KEYPRESS:
 
		if (nd->field != 3) {
 
			if (nd->server != NULL) {
 
				if (e->we.keypress.keycode == WKC_DELETE) { /* Press 'delete' to remove servers */
 
					NetworkGameListRemoveItem(nd->server);
 
					NetworkRebuildHostList();
 
					nd->server = NULL;
 
				}
 
			}
 
			break;
 
		}
 

	
 
		if (HandleEditBoxKey(w, &WP(w, network_ql_d).q, 3, e) == 1) break; // enter pressed
 

	
 
		// 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:
 
		NetworkAddServer(e->we.edittext.str);
 
		NetworkRebuildHostList();
 
		break;
 

	
 
	case WE_DESTROY: /* Nicely clean up the sort-list */
 
		free(WP(w, network_ql_d).sort_list);
 
		break;
 
	}
 
}
 

	
 
static const Widget _network_game_window_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,   BGC,     0,    10,     0,    13, STR_00C5,                    STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,   BGC,    11,   549,     0,    13, STR_NETWORK_MULTIPLAYER,     STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,   BGC,     0,   549,    14,   263, 0x0,                         STR_NULL},
 

	
 
/* LEFT SIDE */
 
{      WWT_PANEL,   RESIZE_NONE,   BGC,   310,   461,    22,    33, 0x0,                         STR_NETWORK_ENTER_NAME_TIP},
 

	
 
{      WWT_INSET,   RESIZE_NONE,   BGC,    90,   181,    22,    33, STR_NETWORK_COMBO1,          STR_NETWORK_CONNECTION_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,   BGC,   170,   180,    23,    32, STR_0225,                    STR_NETWORK_CONNECTION_TIP},
 

	
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,    10,   170,    42,    53, STR_NETWORK_GAME_NAME,       STR_NETWORK_GAME_NAME_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   171,   250,    42,    53, STR_NETWORK_CLIENTS_CAPTION, STR_NETWORK_CLIENTS_CAPTION_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   251,   290,    42,    53, STR_EMPTY,                   STR_NETWORK_INFO_ICONS_TIP},
 

	
 
{     WWT_MATRIX,   RESIZE_NONE,   BGC,    10,   290,    54,   236, (13 << 8) + 1,               STR_NETWORK_CLICK_GAME_TO_SELECT},
 
{  WWT_SCROLLBAR,   RESIZE_NONE,   BGC,   291,   302,    42,   236, STR_NULL,                    STR_0190_SCROLL_BAR_SCROLLS_LIST},
 

	
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,    30,   130,   246,   257, STR_NETWORK_FIND_SERVER,     STR_NETWORK_FIND_SERVER_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   180,   280,   246,   257, STR_NETWORK_ADD_SERVER,      STR_NETWORK_ADD_SERVER_TIP},
 

	
 
/* RIGHT SIDE */
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   315,   415,   246,   257, STR_NETWORK_START_SERVER,    STR_NETWORK_START_SERVER_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   430,   535,   246,   257, STR_012E_CANCEL,             STR_NULL},
 

	
 
{      WWT_PANEL,   RESIZE_NONE,   BGC,   310,   540,    42,   236, 0x0,                         STR_NULL},
 

	
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   315,   415,   215,   226, STR_NETWORK_JOIN_GAME,       STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   430,   535,   215,   226, STR_NETWORK_REFRESH,         STR_NETWORK_REFRESH_TIP},
 

	
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   430,   535,   197,   208, STR_NEWGRF_SETTINGS_BUTTON,  STR_NULL},
 

	
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _network_game_window_desc = {
 
	WDP_CENTER, WDP_CENTER, 550, 264,
 
	WC_NETWORK_WINDOW,0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_network_game_window_widgets,
 
	NetworkGameWindowWndProc,
 
};
 

	
 
void ShowNetworkGameWindow(void)
 
{
 
	static bool first = true;
 
	Window *w;
 
	DeleteWindowById(WC_NETWORK_WINDOW, 0);
 

	
 
	/* Only show once */
 
	if (first) {
 
		char* const *srv;
 

	
 
		first = false;
 
		// add all servers from the config file to our list
 
		for (srv = &_network_host_list[0]; srv != endof(_network_host_list) && *srv != NULL; srv++) {
 
			NetworkAddServer(*srv);
 
		}
 

	
 
		_ng_sorting.criteria = 2; // sort default by collectivity (green-dots on top)
 
		_ng_sorting.order = 0;    // sort ascending by default
 
	}
 

	
 
	w = AllocateWindowDesc(&_network_game_window_desc);
 
	if (w != NULL) {
 
		querystr_d *querystr = &WP(w, network_ql_d).q;
 

	
 
		ttd_strlcpy(_edit_str_buf, _network_player_name, lengthof(_edit_str_buf));
 
		w->vscroll.cap = 13;
 

	
 
		querystr->afilter = CS_ALPHANUMERAL;
 
		InitializeTextBuffer(&querystr->text, _edit_str_buf, lengthof(_edit_str_buf), 120);
 

	
 
		UpdateNetworkGameWindow(true);
 
	}
 
}
 

	
 
enum {
 
	NSSWND_START = 64,
 
	NSSWND_ROWSIZE = 12
 
};
 

	
 
/* Uses network_ql_d (network_d, querystr_d and list_d) WP macro */
 
static void NetworkStartServerWindowWndProc(Window *w, WindowEvent *e)
 
{
 
	network_d *nd = &WP(w, network_ql_d).n;
 

	
 
	switch (e->event) {
 
	case WE_CREATE: /* focus input box */
 
		nd->field = 3;
 
		_network_game_info.use_password = (_network_server_password[0] != '\0');
 
		break;
 

	
 
	case WE_PAINT: {
 
		int y = NSSWND_START, pos;
 
		const FiosItem *item;
 

	
 
		SetDParam( 7, _connection_types_dropdown[_network_advertise]);
 
		SetDParam( 9, _players_dropdown[_network_game_info.clients_max]);
 
		SetDParam(11, _players_dropdown[_network_game_info.companies_max]);
 
		SetDParam(13, _players_dropdown[_network_game_info.spectators_max]);
 
		SetDParam(15, _language_dropdown[_network_game_info.server_lang]);
 
		DrawWindowWidgets(w);
 

	
 
		GfxFillRect(11, 63, 258, 215, 0xD7);
 
		DrawEditBox(w, &WP(w, network_ql_d).q, 3);
 

	
 
		DrawString(10, 22, STR_NETWORK_NEW_GAME_NAME, 2);
 

	
 
		DrawString(10, 43, STR_NETWORK_SELECT_MAP, 2);
 

	
 
		DrawString(280,  63, STR_NETWORK_CONNECTION, 2);
 
		DrawString(280,  95, STR_NETWORK_NUMBER_OF_CLIENTS, 2);
 
		DrawString(280, 127, STR_NETWORK_NUMBER_OF_COMPANIES, 2);
 
		DrawString(280, 159, STR_NETWORK_NUMBER_OF_SPECTATORS, 2);
 
		DrawString(280, 191, STR_NETWORK_LANGUAGE_SPOKEN, 2);
 

	
 
		if (_network_game_info.use_password) DoDrawString("*", 408, 23, 3);
 

	
 
		// draw list of maps
 
		pos = w->vscroll.pos;
 
		while (pos < _fios_num + 1) {
 
			item = _fios_list + pos - 1;
 
			if (item == nd->map || (pos == 0 && nd->map == NULL))
 
				GfxFillRect(11, y - 1, 258, y + 10, 155); // show highlighted item with a different colour
 

	
 
			if (pos == 0) {
 
				DrawString(14, y, STR_4010_GENERATE_RANDOM_NEW_GAME, 9);
 
			} else {
 
				DoDrawString(item->title, 14, y, _fios_colors[item->type] );
 
			}
 
			pos++;
 
			y += NSSWND_ROWSIZE;
 

	
 
			if (y >= w->vscroll.cap * NSSWND_ROWSIZE + NSSWND_START) break;
 
		}
 
	}	break;
 

	
 
	case WE_CLICK:
 
		nd->field = e->we.click.widget;
 
		switch (e->we.click.widget) {
 
		case 0: /* Close 'X' */
 
		case 19: /* Cancel button */
 
			ShowNetworkGameWindow();
 
			break;
 

	
 
		case 4: /* Set password button */
 
			ShowQueryString(BindCString(_network_server_password), STR_NETWORK_SET_PASSWORD, 20, 250, w, CS_ALPHANUMERAL);
 
			break;
 

	
 
		case 5: { /* Select map */
 
			int y = (e->we.click.pt.y - NSSWND_START) / NSSWND_ROWSIZE;
 

	
 
			y += w->vscroll.pos;
 
			if (y >= w->vscroll.count) return;
 

	
 
			nd->map = (y == 0) ? NULL : _fios_list + y - 1;
 
			SetWindowDirty(w);
 
			} break;
 
		case 7: case 8: /* Connection type */
 
			ShowDropDownMenu(w, _connection_types_dropdown, _network_advertise, 8, 0, 0); // do it for widget 8
 
			break;
 
		case 9: case 10: /* Number of Players (hide 0 and 1 players) */
 
			ShowDropDownMenu(w, _players_dropdown, _network_game_info.clients_max, 10, 0, 3);
 
			break;
 
		case 11: case 12: /* Number of Companies (hide 0, 9 and 10 companies; max is 8) */
 
			ShowDropDownMenu(w, _players_dropdown, _network_game_info.companies_max, 12, 0, 1537);
 
			break;
 
		case 13: case 14: /* Number of Spectators */
 
			ShowDropDownMenu(w, _players_dropdown, _network_game_info.spectators_max, 14, 0, 0);
 
			break;
 
		case 15: case 16: /* Language */
 
			ShowDropDownMenu(w, _language_dropdown, _network_game_info.server_lang, 16, 0, 0);
 
			break;
 
		case 17: /* Start game */
 
			_is_network_server = true;
 

	
 
			if (nd->map == NULL) { // start random new game
 
				ShowGenerateLandscape();
 
			} else { // load a scenario
 
				char *name = FiosBrowseTo(nd->map);
 
				if (name != NULL) {
 
					SetFiosType(nd->map->type);
 
					ttd_strlcpy(_file_to_saveload.name, name, sizeof(_file_to_saveload.name));
 
					ttd_strlcpy(_file_to_saveload.title, nd->map->title, sizeof(_file_to_saveload.title));
 

	
 
					DeleteWindow(w);
 
					SwitchMode(SM_START_SCENARIO);
 
				}
 
			}
 
			break;
 
		case 18: /* Load game */
 
			_is_network_server = true;
 
			/* XXX - WC_NETWORK_WINDOW should stay, but if it stays, it gets
 
			 * copied all the elements of 'load game' and upon closing that, it segfaults */
 
			DeleteWindowById(WC_NETWORK_WINDOW, 0);
 
			ShowSaveLoadDialog(SLD_LOAD_GAME);
 
			break;
 
		}
 
		break;
 

	
 
	case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
 
		switch (e->we.dropdown.button) {
 
			case  8: _network_advertise                = (e->we.dropdown.index != 0); break;
 
			case 10: _network_game_info.clients_max    = e->we.dropdown.index;        break;
 
			case 12: _network_game_info.companies_max  = e->we.dropdown.index;        break;
 
			case 14: _network_game_info.spectators_max = e->we.dropdown.index;        break;
 
			case 16: _network_game_info.server_lang    = e->we.dropdown.index;        break;
 
		}
 

	
 
		SetWindowDirty(w);
 
		break;
 

	
 
	case WE_MOUSELOOP:
 
		if (nd->field == 3) HandleEditBox(w, &WP(w, network_ql_d).q, 3);
 
		break;
 

	
 
	case WE_KEYPRESS:
 
		if (nd->field == 3) {
 
			if (HandleEditBoxKey(w, &WP(w, network_ql_d).q, 3, e) == 1) break; // enter pressed
 

	
 
			ttd_strlcpy(_network_server_name, WP(w, network_ql_d).q.text.buf, sizeof(_network_server_name));
 
			UpdateTextBufferSize(&WP(w, network_ql_d).q.text);
 
		}
 
		break;
 

	
 
	case WE_ON_EDIT_TEXT: {
 
		ttd_strlcpy(_network_server_password, e->we.edittext.str, lengthof(_network_server_password));
 
		_network_game_info.use_password = (_network_server_password[0] != '\0');
 
		SetWindowDirty(w);
 
	} break;
 
	}
 
}
 

	
 
static const Widget _network_start_server_window_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,   BGC,     0,    10,     0,    13, STR_00C5,                      STR_018B_CLOSE_WINDOW },
 
{    WWT_CAPTION,   RESIZE_NONE,   BGC,    11,   419,     0,    13, STR_NETWORK_START_GAME_WINDOW, STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,   BGC,     0,   419,    14,   243, 0x0,                           STR_NULL},
 

	
 
{      WWT_PANEL,   RESIZE_NONE,   BGC,   100,   272,    22,    33, 0x0,                           STR_NETWORK_NEW_GAME_NAME_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   285,   405,    22,    33, STR_NETWORK_SET_PASSWORD,      STR_NETWORK_PASSWORD_TIP},
 

	
 
{      WWT_INSET,   RESIZE_NONE,   BGC,    10,   271,    62,   216, 0x0,                           STR_NETWORK_SELECT_MAP_TIP},
 
{  WWT_SCROLLBAR,   RESIZE_NONE,   BGC,   259,   270,    63,   215, 0x0,                           STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
/* Combo boxes to control Connection Type / Max Clients / Max Companies / Max Observers / Language */
 
{      WWT_INSET,   RESIZE_NONE,   BGC,   280,   410,    77,    88, STR_NETWORK_COMBO1,            STR_NETWORK_CONNECTION_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,   BGC,   399,   409,    78,    87, STR_0225,                      STR_NETWORK_CONNECTION_TIP},
 
{      WWT_INSET,   RESIZE_NONE,   BGC,   280,   410,   109,   120, STR_NETWORK_COMBO2,            STR_NETWORK_NUMBER_OF_CLIENTS_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,   BGC,   399,   409,   110,   119, STR_0225,                      STR_NETWORK_NUMBER_OF_CLIENTS_TIP},
 
{      WWT_INSET,   RESIZE_NONE,   BGC,   280,   410,   141,   152, STR_NETWORK_COMBO3,            STR_NETWORK_NUMBER_OF_COMPANIES_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,   BGC,   399,   409,   142,   151, STR_0225,                      STR_NETWORK_NUMBER_OF_COMPANIES_TIP},
 
{      WWT_INSET,   RESIZE_NONE,   BGC,   280,   410,   173,   184, STR_NETWORK_COMBO4,            STR_NETWORK_NUMBER_OF_SPECTATORS_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,   BGC,   399,   409,   174,   183, STR_0225,                      STR_NETWORK_NUMBER_OF_SPECTATORS_TIP},
 
{      WWT_INSET,   RESIZE_NONE,   BGC,   280,   410,   205,   216, STR_NETWORK_COMBO5,            STR_NETWORK_LANGUAGE_TIP},
 
{    WWT_TEXTBTN,   RESIZE_NONE,   BGC,   399,   409,   206,   215, STR_0225,                      STR_NETWORK_LANGUAGE_TIP},
 

	
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,    40,   140,   224,   235, STR_NETWORK_START_GAME,        STR_NETWORK_START_GAME_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   150,   250,   224,   235, STR_NETWORK_LOAD_GAME,         STR_NETWORK_LOAD_GAME_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   260,   360,   224,   235, STR_012E_CANCEL,               STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _network_start_server_window_desc = {
 
	WDP_CENTER, WDP_CENTER, 420, 244,
 
	WC_NETWORK_WINDOW,0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_network_start_server_window_widgets,
 
	NetworkStartServerWindowWndProc,
 
};
 

	
 
static void ShowNetworkStartServerWindow(void)
 
{
 
	Window *w;
 
	DeleteWindowById(WC_NETWORK_WINDOW, 0);
 

	
 
	w = AllocateWindowDesc(&_network_start_server_window_desc);
 
	ttd_strlcpy(_edit_str_buf, _network_server_name, lengthof(_edit_str_buf));
 

	
 
	_saveload_mode = SLD_NEW_GAME;
 
	BuildFileList();
 
	w->vscroll.cap = 12;
 
	w->vscroll.count = _fios_num+1;
 

	
 
	WP(w, network_ql_d).q.afilter = CS_ALPHANUMERAL;
 
	InitializeTextBuffer(&WP(w, network_ql_d).q.text, _edit_str_buf, lengthof(_edit_str_buf), 160);
 
}
 

	
 
static byte NetworkLobbyFindCompanyIndex(byte pos)
 
{
 
	byte i;
 

	
 
	/* Scroll through all _network_player_info and get the 'pos' item
 
	    that is not empty */
 
	for (i = 0; i < MAX_PLAYERS; i++) {
 
		if (_network_player_info[i].company_name[0] != '\0') {
 
			if (pos-- == 0) return i;
 
		}
 
	}
 

	
 
	return 0;
 
}
 

	
 
/* uses network_d WP macro */
 
static void NetworkLobbyWindowWndProc(Window *w, WindowEvent *e)
 
{
 
	network_d *nd = &WP(w, network_d);
 

	
 
	switch (e->event) {
 
	case WE_CREATE:
 
		nd->company = (byte)-1;
 
		break;
 

	
 
	case WE_PAINT: {
 
		const NetworkGameInfo *gi = &nd->server->info;
 
		int y = NET_PRC__OFFSET_TOP_WIDGET_COMPANY, pos;
 

	
 
		SetWindowWidgetDisabledState(w, 7, nd->company == (byte)-1);
 
		SetWindowWidgetDisabledState(w, 8, gi->companies_on >= gi->companies_max);
 
		/* You can not join a server as spectator when it has no companies active..
 
		 * it causes some nasty crashes */
 
		SetWindowWidgetDisabledState(w, 9, gi->spectators_on >= gi->spectators_max ||
 
				gi->companies_on == 0);
 

	
 
		DrawWindowWidgets(w);
 

	
 
		SetDParamStr(0, gi->server_name);
 
		DrawString(10, 22, STR_NETWORK_PREPARE_TO_JOIN, 2);
 

	
 
		/* Draw company list */
 
		pos = w->vscroll.pos;
 
		while (pos < gi->companies_on) {
 
			byte company = NetworkLobbyFindCompanyIndex(pos);
 
			bool income = false;
 
			if (nd->company == company)
 
				GfxFillRect(11, y - 1, 154, y + 10, 10); // show highlighted item with a different colour
 

	
 
			DoDrawStringTruncated(_network_player_info[company].company_name, 13, y, 16, 135 - 13);
 
			if (_network_player_info[company].use_password != 0) DrawSprite(SPR_LOCK, 135, y);
 

	
 
			/* If the company's income was positive puts a green dot else a red dot */
 
			if (_network_player_info[company].income >= 0) income = true;
 
			DrawSprite(SPR_BLOT | (income ? PALETTE_TO_GREEN : PALETTE_TO_RED), 145, y);
 

	
 
			pos++;
 
			y += NET_PRC__SIZE_OF_ROW;
 
			if (pos >= w->vscroll.cap) break;
 
		}
 

	
 
		/* Draw info about selected company when it is selected in the left window */
 
		GfxFillRect(174, 39, 403, 75, 157);
 
		DrawStringCentered(290, 50, STR_NETWORK_COMPANY_INFO, 0);
 
		if (nd->company != (byte)-1) {
 
			const uint x = 183;
 
			const uint trunc_width = w->widget[6].right - x;
 
			y = 80;
 

	
 
			SetDParam(0, nd->server->info.clients_on);
 
			SetDParam(1, nd->server->info.clients_max);
 
			SetDParam(2, nd->server->info.companies_on);
 
			SetDParam(3, nd->server->info.companies_max);
 
			DrawString(x, y, STR_NETWORK_CLIENTS, 2);
 
			y += 10;
 

	
 
			SetDParamStr(0, _network_player_info[nd->company].company_name);
 
			DrawStringTruncated(x, y, STR_NETWORK_COMPANY_NAME, 2, trunc_width);
 
			y += 10;
 

	
 
			SetDParam(0, _network_player_info[nd->company].inaugurated_year);
 
			DrawString(x, y, STR_NETWORK_INAUGURATION_YEAR, 2); // inauguration year
 
			y += 10;
 

	
 
			SetDParam64(0, _network_player_info[nd->company].company_value);
 
			DrawString(x, y, STR_NETWORK_VALUE, 2); // company value
 
			y += 10;
 

	
 
			SetDParam64(0, _network_player_info[nd->company].money);
 
			DrawString(x, y, STR_NETWORK_CURRENT_BALANCE, 2); // current balance
 
			y += 10;
 

	
 
			SetDParam64(0, _network_player_info[nd->company].income);
 
			DrawString(x, y, STR_NETWORK_LAST_YEARS_INCOME, 2); // last year's income
 
			y += 10;
 

	
 
			SetDParam(0, _network_player_info[nd->company].performance);
 
			DrawString(x, y, STR_NETWORK_PERFORMANCE, 2); // performance
 
			y += 10;
 

	
 
			SetDParam(0, _network_player_info[nd->company].num_vehicle[0]);
 
			SetDParam(1, _network_player_info[nd->company].num_vehicle[1]);
 
			SetDParam(2, _network_player_info[nd->company].num_vehicle[2]);
 
			SetDParam(3, _network_player_info[nd->company].num_vehicle[3]);
 
			SetDParam(4, _network_player_info[nd->company].num_vehicle[4]);
 
			DrawString(x, y, STR_NETWORK_VEHICLES, 2); // vehicles
 
			y += 10;
 

	
 
			SetDParam(0, _network_player_info[nd->company].num_station[0]);
 
			SetDParam(1, _network_player_info[nd->company].num_station[1]);
 
			SetDParam(2, _network_player_info[nd->company].num_station[2]);
 
			SetDParam(3, _network_player_info[nd->company].num_station[3]);
 
			SetDParam(4, _network_player_info[nd->company].num_station[4]);
 
			DrawString(x, y, STR_NETWORK_STATIONS, 2); // stations
 
			y += 10;
 

	
 
			SetDParamStr(0, _network_player_info[nd->company].players);
 
			DrawStringTruncated(x, y, STR_NETWORK_PLAYERS, 2, trunc_width); // players
 
		}
 
	}	break;
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
		case 0: case 11: /* Close 'X' | Cancel button */
 
			ShowNetworkGameWindow();
 
			break;
 
		case 4: { /* Company list */
 
			uint32 id_v = (e->we.click.pt.y - NET_PRC__OFFSET_TOP_WIDGET_COMPANY) / NET_PRC__SIZE_OF_ROW;
 

	
 
			if (id_v >= w->vscroll.cap) return;
 

	
 
			id_v += w->vscroll.pos;
 
			nd->company = (id_v >= nd->server->info.companies_on) ? (byte)-1 : NetworkLobbyFindCompanyIndex(id_v);
 
			SetWindowDirty(w);
 
		}	break;
 
		case 7: /* Join company */
 
			if (nd->company != (byte)-1) {
 
				_network_playas = nd->company;
 
				NetworkClientConnectGame(_network_last_host, _network_last_port);
 
			}
 
			break;
 
		case 8: /* New company */
 
			_network_playas = PLAYER_NEW_COMPANY;
 
			NetworkClientConnectGame(_network_last_host, _network_last_port);
 
			break;
 
		case 9: /* Spectate game */
 
			_network_playas = PLAYER_SPECTATOR;
 
			NetworkClientConnectGame(_network_last_host, _network_last_port);
 
			break;
 
		case 10: /* Refresh */
 
			NetworkQueryServer(_network_last_host, _network_last_port, false); // company info
 
			NetworkUDPQueryServer(_network_last_host, _network_last_port);     // general data
 
			break;
 
		}	break;
 

	
 
	case WE_MESSAGE:
 
		SetWindowDirty(w);
 
		break;
 
	}
 
}
 

	
 
static const Widget _network_lobby_window_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,   BGC,     0,    10,     0,    13, STR_00C5,                  STR_018B_CLOSE_WINDOW },
 
{    WWT_CAPTION,   RESIZE_NONE,   BGC,    11,   419,     0,    13, STR_NETWORK_GAME_LOBBY,    STR_NULL},
 
{      WWT_PANEL,   RESIZE_NONE,   BGC,     0,   419,    14,   234, 0x0,                       STR_NULL},
 

	
 
// company list
 
{      WWT_PANEL,   RESIZE_NONE,   BTC,    10,   155,    38,    49, 0x0,                       STR_NULL},
 
{     WWT_MATRIX,   RESIZE_NONE,   BGC,    10,   155,    50,   190, (10 << 8) + 1,             STR_NETWORK_COMPANY_LIST_TIP},
 
{  WWT_SCROLLBAR,   RESIZE_NONE,   BGC,   156,   167,    38,   190, STR_NULL,                  STR_0190_SCROLL_BAR_SCROLLS_LIST},
 

	
 
// company/player info
 
{      WWT_PANEL,   RESIZE_NONE,   BGC,   173,   404,    38,   190, 0x0,                       STR_NULL},
 

	
 
// buttons
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,    10,   151,   200,   211, STR_NETWORK_JOIN_COMPANY,  STR_NETWORK_JOIN_COMPANY_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,    10,   151,   215,   226, STR_NETWORK_NEW_COMPANY,   STR_NETWORK_NEW_COMPANY_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   158,   268,   200,   211, STR_NETWORK_SPECTATE_GAME, STR_NETWORK_SPECTATE_GAME_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   158,   268,   215,   226, STR_NETWORK_REFRESH,       STR_NETWORK_REFRESH_TIP},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,   278,   388,   200,   211, STR_012E_CANCEL,           STR_NULL},
 

	
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _network_lobby_window_desc = {
 
	WDP_CENTER, WDP_CENTER, 420, 235,
 
	WC_NETWORK_WINDOW,0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_network_lobby_window_widgets,
 
	NetworkLobbyWindowWndProc,
 
};
 

	
 
/* Show the networklobbywindow with the selected server
 
 * @param ngl Selected game pointer which is passed to the new window */
 
static void ShowNetworkLobbyWindow(NetworkGameList *ngl)
 
{
 
	Window *w;
 
	DeleteWindowById(WC_NETWORK_WINDOW, 0);
 

	
 
	NetworkQueryServer(_network_last_host, _network_last_port, false); // company info
 
	NetworkUDPQueryServer(_network_last_host, _network_last_port);     // general data
 

	
 
	w = AllocateWindowDesc(&_network_lobby_window_desc);
 
	if (w != NULL) {
 
		WP(w, network_ql_d).n.server = ngl;
 
		strcpy(_edit_str_buf, "");
 
		w->vscroll.cap = 10;
 
	}
 
}
 

	
 
// The window below gives information about the connected clients
 
//  and also makes able to give money to them, kick them (if server)
 
//  and stuff like that.
 

	
 
extern void DrawPlayerIcon(PlayerID pid, int x, int y);
 

	
 
// Every action must be of this form
 
typedef void ClientList_Action_Proc(byte client_no);
 

	
 
// Max 10 actions per client
 
#define MAX_CLIENTLIST_ACTION 10
 

	
 
// Some standard bullshit.. defines variables ;)
 
static void ClientListWndProc(Window *w, WindowEvent *e);
 
static void ClientListPopupWndProc(Window *w, WindowEvent *e);
 
static byte _selected_clientlist_item = 255;
 
static byte _selected_clientlist_y = 0;
 
static char _clientlist_action[MAX_CLIENTLIST_ACTION][50];
 
static ClientList_Action_Proc *_clientlist_proc[MAX_CLIENTLIST_ACTION];
 

	
 
enum {
 
	CLNWND_OFFSET = 16,
 
	CLNWND_ROWSIZE = 10
 
};
 

	
 
static const Widget _client_list_widgets[] = {
 
{   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                 STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   249,     0,    13, STR_NETWORK_CLIENT_LIST,  STR_018C_WINDOW_TITLE_DRAG_THIS},
 

	
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   249,    14,    14 + CLNWND_ROWSIZE + 1, 0x0, STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const Widget _client_list_popup_widgets[] = {
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   99,     0,     0,     0, STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static WindowDesc _client_list_desc = {
 
	WDP_AUTO, WDP_AUTO, 250, 1,
 
	WC_CLIENT_LIST,0,
 
	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 
	_client_list_widgets,
 
	ClientListWndProc
 
};
 

	
 
// Finds the Xth client-info that is active
 
static const NetworkClientInfo *NetworkFindClientInfo(byte client_no)
 
{
 
	const NetworkClientInfo *ci;
 

	
 
	FOR_ALL_ACTIVE_CLIENT_INFOS(ci) {
 
		if (client_no == 0) return ci;
 
		client_no--;
 
	}
 

	
 
	return NULL;
 
}
 

	
 
// Here we start to define the options out of the menu
 
static void ClientList_Kick(byte client_no)
 
{
 
	if (client_no < MAX_PLAYERS)
 
		SEND_COMMAND(PACKET_SERVER_ERROR)(DEREF_CLIENT(client_no), NETWORK_ERROR_KICKED);
 
}
 

	
 
static void ClientList_Ban(byte client_no)
 
{
 
	uint i;
 
	uint32 ip = NetworkFindClientInfo(client_no)->client_ip;
 

	
 
	for (i = 0; i < lengthof(_network_ban_list); i++) {
 
		if (_network_ban_list[i] == NULL) {
 
			_network_ban_list[i] = strdup(inet_ntoa(*(struct in_addr *)&ip));
 
			break;
 
		}
 
	}
 

	
 
	if (client_no < MAX_PLAYERS)
 
		SEND_COMMAND(PACKET_SERVER_ERROR)(DEREF_CLIENT(client_no), NETWORK_ERROR_KICKED);
 
}
 

	
 
static void ClientList_GiveMoney(byte client_no)
 
{
 
	if (NetworkFindClientInfo(client_no) != NULL)
 
		ShowNetworkGiveMoneyWindow(NetworkFindClientInfo(client_no)->client_playas);
 
}
 

	
 
static void ClientList_SpeakToClient(byte client_no)
 
{
 
	if (NetworkFindClientInfo(client_no) != NULL)
 
		ShowNetworkChatQueryWindow(DESTTYPE_CLIENT, NetworkFindClientInfo(client_no)->client_index);
 
}
 

	
 
static void ClientList_SpeakToCompany(byte client_no)
 
{
 
	if (NetworkFindClientInfo(client_no) != NULL)
 
		ShowNetworkChatQueryWindow(DESTTYPE_TEAM, NetworkFindClientInfo(client_no)->client_playas);
 
}
 

	
 
static void ClientList_SpeakToAll(byte client_no)
 
{
 
	ShowNetworkChatQueryWindow(DESTTYPE_BROADCAST, 0);
 
}
 

	
 
static void ClientList_None(byte client_no)
 
{
 
	// No action ;)
 
}
 

	
 

	
 

	
 
// Help, a action is clicked! What do we do?
 
static void HandleClientListPopupClick(byte index, byte clientno) {
 
	// A click on the Popup of the ClientList.. handle the command
 
	if (index < MAX_CLIENTLIST_ACTION && _clientlist_proc[index] != NULL) {
 
		_clientlist_proc[index](clientno);
 
	}
 
}
 

	
 
// Finds the amount of clients and set the height correct
 
static bool CheckClientListHeight(Window *w)
 
{
 
	int num = 0;
 
	const NetworkClientInfo *ci;
 

	
 
	// Should be replaced with a loop through all clients
 
	FOR_ALL_ACTIVE_CLIENT_INFOS(ci) {
 
		num++;
 
	}
 

	
 
	num *= CLNWND_ROWSIZE;
 

	
 
	// If height is changed
 
	if (w->height != CLNWND_OFFSET + num + 1) {
 
		// XXX - magic unfortunately; (num + 2) has to be one bigger than heigh (num + 1)
 
		SetWindowDirty(w);
 
		w->widget[2].bottom = w->widget[2].top + num + 2;
 
		w->height = CLNWND_OFFSET + num + 1;
 
		SetWindowDirty(w);
 
		return false;
 
	}
 
	return true;
 
}
 

	
 
// Finds the amount of actions in the popup and set the height correct
 
static uint ClientListPopupHeigth(void) {
 
	int i, num = 0;
 

	
 
	// Find the amount of actions
 
	for (i = 0; i < MAX_CLIENTLIST_ACTION; i++) {
 
		if (_clientlist_action[i][0] == '\0') continue;
 
		if (_clientlist_proc[i] == NULL) continue;
 
		num++;
 
	}
 

	
 
	num *= CLNWND_ROWSIZE;
 

	
 
	return num + 1;
 
}
 

	
 
// Show the popup (action list)
 
static Window *PopupClientList(Window *w, int client_no, int x, int y)
 
{
 
	int i, h;
 
	const NetworkClientInfo *ci;
 
	DeleteWindowById(WC_TOOLBAR_MENU, 0);
 

	
 
	// Clean the current actions
 
	for (i = 0; i < MAX_CLIENTLIST_ACTION; i++) {
 
		_clientlist_action[i][0] = '\0';
 
		_clientlist_proc[i] = NULL;
 
	}
 

	
 
	// Fill the actions this client has
 
	// Watch is, max 50 chars long!
 

	
 
	ci = NetworkFindClientInfo(client_no);
 
	if (ci == NULL) return NULL;
 

	
 
	i = 0;
 
	if (_network_own_client_index != ci->client_index) {
 
		GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_SPEAK_TO_CLIENT, lastof(_clientlist_action[i]));
 
		_clientlist_proc[i++] = &ClientList_SpeakToClient;
 
	}
 

	
 
	if (IsValidPlayer(ci->client_playas) || ci->client_playas == PLAYER_SPECTATOR) {
 
		GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_SPEAK_TO_COMPANY, lastof(_clientlist_action[i]));
 
		_clientlist_proc[i++] = &ClientList_SpeakToCompany;
 
	}
 
	GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_SPEAK_TO_ALL, lastof(_clientlist_action[i]));
 
	_clientlist_proc[i++] = &ClientList_SpeakToAll;
 

	
 
	if (_network_own_client_index != ci->client_index) {
 
		/* We are no spectator and the player we want to give money to is no spectator */
 
		if (IsValidPlayer(_network_playas) && IsValidPlayer(ci->client_playas)) {
 
			GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_GIVE_MONEY, lastof(_clientlist_action[i]));
 
			_clientlist_proc[i++] = &ClientList_GiveMoney;
 
		}
 
	}
 

	
 
	// A server can kick clients (but not himself)
 
	if (_network_server && _network_own_client_index != ci->client_index) {
 
		GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_KICK, lastof(_clientlist_action[i]));
 
		_clientlist_proc[i++] = &ClientList_Kick;
 

	
 
		sprintf(_clientlist_action[i],"Ban"); // XXX GetString?
 
		_clientlist_proc[i++] = &ClientList_Ban;
 
	}
 

	
 
	if (i == 0) {
 
		GetString(_clientlist_action[i], STR_NETWORK_CLIENTLIST_NONE, lastof(_clientlist_action[i]));
 
		_clientlist_proc[i++] = &ClientList_None;
 
	}
 

	
 
	/* Calculate the height */
 
	h = ClientListPopupHeigth();
 

	
 
	// Allocate the popup
 
	w = AllocateWindow(x, y, 150, h + 1, ClientListPopupWndProc, WC_TOOLBAR_MENU, _client_list_popup_widgets);
 
	w->widget[0].bottom = w->widget[0].top + h;
 
	w->widget[0].right = w->widget[0].left + 150;
 

	
 
	w->flags4 &= ~WF_WHITE_BORDER_MASK;
 
	WP(w,menu_d).item_count = 0;
 
	// Save our client
 
	WP(w,menu_d).main_button = client_no;
 
	WP(w,menu_d).sel_index = 0;
 
	// We are a popup
 
	_popup_menu_active = true;
 

	
 
	return w;
 
}
 

	
 
/** Main handle for the client popup list
 
 * uses menu_d WP macro */
 
static void ClientListPopupWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		int i, y, sel;
 
		byte colour;
 
		DrawWindowWidgets(w);
 

	
 
		// Draw the actions
 
		sel = WP(w,menu_d).sel_index;
 
		y = 1;
 
		for (i = 0; i < MAX_CLIENTLIST_ACTION; i++, y += CLNWND_ROWSIZE) {
 
			if (_clientlist_action[i][0] == '\0') continue;
 
			if (_clientlist_proc[i] == NULL) continue;
 

	
 
			if (sel-- == 0) { // Selected item, highlight it
 
				GfxFillRect(1, y, 150 - 2, y + CLNWND_ROWSIZE - 1, 0);
 
				colour = 0xC;
 
			} else {
 
				colour = 0x10;
 
			}
 

	
 
			DoDrawString(_clientlist_action[i], 4, y, colour);
 
		}
 
	}	break;
 

	
 
	case WE_POPUPMENU_SELECT: {
 
		// We selected an action
 
		int index = (e->we.popupmenu.pt.y - w->top) / CLNWND_ROWSIZE;
 

	
 
		if (index >= 0 && e->we.popupmenu.pt.y >= w->top)
 
			HandleClientListPopupClick(index, WP(w,menu_d).main_button);
 

	
 
		DeleteWindowById(WC_TOOLBAR_MENU, 0);
 
	}	break;
 

	
 
	case WE_POPUPMENU_OVER: {
 
		// Our mouse hoovers over an action? Select it!
 
		int index = (e->we.popupmenu.pt.y - w->top) / CLNWND_ROWSIZE;
 

	
 
		if (index == -1 || index == WP(w,menu_d).sel_index) return;
 

	
 
		WP(w,menu_d).sel_index = index;
 
		SetWindowDirty(w);
 
	} break;
 

	
 
	}
 
}
 

	
 
// Main handle for clientlist
 
static void ClientListWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		NetworkClientInfo *ci;
 
		int y, i = 0;
 
		byte colour;
 

	
 
		// Check if we need to reset the height
 
		if (!CheckClientListHeight(w)) break;
 

	
 
		DrawWindowWidgets(w);
 

	
 
		y = CLNWND_OFFSET;
 

	
 
		FOR_ALL_ACTIVE_CLIENT_INFOS(ci) {
 
			if (_selected_clientlist_item == i++) { // Selected item, highlight it
 
				GfxFillRect(1, y, 248, y + CLNWND_ROWSIZE - 1, 0);
 
				colour = 0xC;
 
			} else {
 
				colour = 0x10;
 
			}
 

	
 
			if (ci->client_index == NETWORK_SERVER_INDEX) {
 
				DrawString(4, y, STR_NETWORK_SERVER, colour);
 
			} else {
 
				DrawString(4, y, STR_NETWORK_CLIENT, colour);
 
			}
 

	
 
			// Filter out spectators
 
			if (IsValidPlayer(ci->client_playas)) DrawPlayerIcon(ci->client_playas, 64, y + 1);
 

	
 
			DoDrawString(ci->client_name, 81, y, colour);
 

	
 
			y += CLNWND_ROWSIZE;
 
		}
 
	}	break;
 

	
 
	case WE_CLICK:
 
		// Show the popup with option
 
		if (_selected_clientlist_item != 255) {
 
			PopupClientList(w, _selected_clientlist_item, e->we.click.pt.x + w->left, e->we.click.pt.y + w->top);
 
		}
 

	
 
		break;
 

	
 
	case WE_MOUSEOVER:
 
		// -1 means we left the current window
 
		if (e->we.mouseover.pt.y == -1) {
 
			_selected_clientlist_y = 0;
 
			_selected_clientlist_item = 255;
 
			SetWindowDirty(w);
 
			break;
 
		}
 
		// It did not change.. no update!
 
		if (e->we.mouseover.pt.y == _selected_clientlist_y) break;
 

	
 
		// Find the new selected item (if any)
 
		_selected_clientlist_y = e->we.mouseover.pt.y;
 
		if (e->we.mouseover.pt.y > CLNWND_OFFSET) {
 
			_selected_clientlist_item = (e->we.mouseover.pt.y - CLNWND_OFFSET) / CLNWND_ROWSIZE;
 
		} else {
 
			_selected_clientlist_item = 255;
 
		}
 

	
 
		// Repaint
 
		SetWindowDirty(w);
 
		break;
 

	
 
	case WE_DESTROY: case WE_CREATE:
 
		// When created or destroyed, data is reset
 
		_selected_clientlist_item = 255;
 
		_selected_clientlist_y = 0;
 
		break;
 
	}
 
}
 

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

	
 

	
 
static NetworkPasswordType pw_type;
 

	
 

	
 
void ShowNetworkNeedPassword(NetworkPasswordType npt)
 
{
 
	StringID caption;
 

	
 
	pw_type = npt;
 
	switch (npt) {
 
		default: NOT_REACHED();
 
		case NETWORK_GAME_PASSWORD:    caption = STR_NETWORK_NEED_GAME_PASSWORD_CAPTION; break;
 
		case NETWORK_COMPANY_PASSWORD: caption = STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION; break;
 
	}
 
	ShowQueryString(STR_EMPTY, caption, 20, 180, FindWindowById(WC_NETWORK_STATUS_WINDOW, 0), CS_ALPHANUMERAL);
 
}
 

	
 

	
 
static void NetworkJoinStatusWindowWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_PAINT: {
 
		uint8 progress; // used for progress bar
 
		DrawWindowWidgets(w);
 

	
 
		DrawStringCentered(125, 35, STR_NETWORK_CONNECTING_1 + _network_join_status, 14);
 
		switch (_network_join_status) {
 
			case NETWORK_JOIN_STATUS_CONNECTING: case NETWORK_JOIN_STATUS_AUTHORIZING:
 
			case NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO:
 
				progress = 10; // first two stages 10%
 
				break;
 
			case NETWORK_JOIN_STATUS_WAITING:
 
				SetDParam(0, _network_join_waiting);
 
				DrawStringCentered(125, 46, STR_NETWORK_CONNECTING_WAITING, 14);
 
				progress = 15; // third stage is 15%
 
				break;
 
			case NETWORK_JOIN_STATUS_DOWNLOADING:
 
				SetDParam(0, _network_join_kbytes);
 
				SetDParam(1, _network_join_kbytes_total);
 
				DrawStringCentered(125, 46, STR_NETWORK_CONNECTING_DOWNLOADING, 14);
 
				/* Fallthrough */
 
			default: /* Waiting is 15%, so the resting receivement of map is maximum 70% */
 
				progress = 15 + _network_join_kbytes * (100 - 15) / _network_join_kbytes_total;
 
		}
 

	
 
		/* Draw nice progress bar :) */
 
		DrawFrameRect(20, 18, (int)((w->width - 20) * progress / 100), 28, 10, 0);
 
	}	break;
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
			case 2: /* Disconnect button */
 
				NetworkDisconnect();
 
				DeleteWindow(w);
 
				SwitchMode(SM_MENU);
 
				ShowNetworkGameWindow();
 
				break;
 
		}
 
		break;
 

	
 
		/* If the server asks for a password, we need to fill it in */
 
		case WE_ON_EDIT_TEXT_CANCEL:
 
			NetworkDisconnect();
 
			ShowNetworkGameWindow();
 
			break;
 

	
 
		case WE_ON_EDIT_TEXT:
 
			SEND_COMMAND(PACKET_CLIENT_PASSWORD)(pw_type, e->we.edittext.str);
 
			break;
 
	}
 
}
 

	
 
static const Widget _network_join_status_window_widget[] = {
 
{    WWT_CAPTION,   RESIZE_NONE,    14,     0,   249,     0,    13, STR_NETWORK_CONNECTING, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{      WWT_PANEL,   RESIZE_NONE,    14,     0,   249,    14,    84, 0x0,                    STR_NULL},
 
{ WWT_PUSHTXTBTN,   RESIZE_NONE,   BTC,    75,   175,    69,    80, STR_NETWORK_DISCONNECT, STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _network_join_status_window_desc = {
 
	WDP_CENTER, WDP_CENTER, 250, 85,
 
	WC_NETWORK_STATUS_WINDOW, 0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_MODAL,
 
	_network_join_status_window_widget,
 
	NetworkJoinStatusWindowWndProc,
 
};
 

	
 
void ShowJoinStatusWindow(void)
 
{
 
	Window *w;
 
	DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 
	w = AllocateWindowDesc(&_network_join_status_window_desc);
 
	/* Parent the status window to the lobby */
 
	if (w != NULL) w->parent = FindWindowById(WC_NETWORK_WINDOW, 0);
 
}
 

	
 
static void SendChat(const char *buf, DestType type, byte dest)
 
{
 
	if (buf[0] == '\0') return;
 
	if (!_network_server) {
 
		SEND_COMMAND(PACKET_CLIENT_CHAT)(NETWORK_ACTION_CHAT + type, type, dest, buf);
 
	} else {
 
		NetworkServer_HandleChat(NETWORK_ACTION_CHAT + type, type, dest, buf, NETWORK_SERVER_INDEX);
 
	}
 
}
 

	
 
/**
 
 * Find the next item of the list of things that can be auto-completed.
 
 * @param item The current indexed item to return. This function can, and most
 
 *     likely will, alter item, to skip empty items in the arrays.
 
 * @return Returns the char that matched to the index.
 
 */
 
static const char *ChatTabCompletionNextItem(uint *item)
 
{
 
	static char chat_tab_temp_buffer[64];
 

	
 
	/* First, try clients */
 
	if (*item < MAX_CLIENT_INFO) {
 
		/* Skip inactive clients */
 
		while (_network_client_info[*item].client_index == NETWORK_EMPTY_INDEX && *item < MAX_CLIENT_INFO) (*item)++;
 
		if (*item < MAX_CLIENT_INFO) return _network_client_info[*item].client_name;
 
	}
 

	
 
	/* Then, try townnames */
 
	/* Not that the following assumes all town indices are adjacent, ie no
 
	 * towns have been deleted. */
 
	if (*item <= (uint)MAX_CLIENT_INFO + GetMaxTownIndex()) {
 
		const Town *t;
 

	
 
		FOR_ALL_TOWNS_FROM(t, *item - MAX_CLIENT_INFO) {
 
			/* Get the town-name via the string-system */
 
			SetDParam(0, t->townnameparts);
 
			GetString(chat_tab_temp_buffer, t->townnametype, lastof(chat_tab_temp_buffer));
 
			return &chat_tab_temp_buffer[0];
 
		}
 
	}
 

	
 
	return NULL;
 
}
 

	
 
/**
 
 * Find what text to complete. It scans for a space from the left and marks
 
 *  the word right from that as to complete. It also writes a \0 at the
 
 *  position of the space (if any). If nothing found, buf is returned.
 
 */
 
static char *ChatTabCompletionFindText(char *buf)
 
{
 
	char *p = strrchr(buf, ' ');
 
	if (p == NULL) return buf;
 

	
 
	*p = '\0';
 
	return p + 1;
 
}
 

	
 
/**
 
 * See if we can auto-complete the current text of the user.
 
 */
 
static void ChatTabCompletion(Window *w)
 
{
 
	static char _chat_tab_completion_buf[lengthof(_edit_str_buf)];
 
	Textbuf *tb = &WP(w, querystr_d).text;
 
	uint len, tb_len;
 
	uint item;
 
	char *tb_buf, *pre_buf;
 
	const char *cur_name;
 
	bool second_scan = false;
 

	
 
	item = 0;
 

	
 
	/* Copy the buffer so we can modify it without damaging the real data */
 
	pre_buf = (_chat_tab_completion_active) ? strdup(_chat_tab_completion_buf) : strdup(tb->buf);
 

	
 
	tb_buf  = ChatTabCompletionFindText(pre_buf);
 
	tb_len  = strlen(tb_buf);
 

	
 
	while ((cur_name = ChatTabCompletionNextItem(&item)) != NULL) {
 
		item++;
 

	
 
		if (_chat_tab_completion_active) {
 
			/* We are pressing TAB again on the same name, is there an other name
 
			 *  that starts with this? */
 
			if (!second_scan) {
 
				uint offset;
 
				uint length;
 

	
 
				/* If we are completing at the begin of the line, skip the ': ' we added */
 
				if (tb_buf == pre_buf) {
 
					offset = 0;
 
					length = tb->length - 2;
 
				} else {
 
					/* Else, find the place we are completing at */
 
					offset = strlen(pre_buf) + 1;
 
					length = tb->length - offset;
 
				}
 

	
 
				/* Compare if we have a match */
 
				if (strlen(cur_name) == length && strncmp(cur_name, tb->buf + offset, length) == 0) second_scan = true;
 

	
 
				continue;
 
			}
 

	
 
			/* Now any match we make on _chat_tab_completion_buf after this, is perfect */
 
		}
 

	
 
		len = strlen(cur_name);
 
		if (tb_len < len && strncasecmp(cur_name, tb_buf, tb_len) == 0) {
 
			/* Save the data it was before completion */
 
			if (!second_scan) snprintf(_chat_tab_completion_buf, lengthof(_chat_tab_completion_buf), "%s", tb->buf);
 
			_chat_tab_completion_active = true;
 

	
 
			/* Change to the found name. Add ': ' if we are at the start of the line (pretty) */
 
			if (pre_buf == tb_buf) {
 
				snprintf(tb->buf, lengthof(_edit_str_buf), "%s: ", cur_name);
 
			} else {
 
				snprintf(tb->buf, lengthof(_edit_str_buf), "%s %s", pre_buf, cur_name);
 
			}
 

	
 
			/* Update the textbuffer */
 
			UpdateTextBufferSize(&WP(w, querystr_d).text);
 

	
 
			SetWindowDirty(w);
 
			free(pre_buf);
 
			return;
 
		}
 
	}
 

	
 
	if (second_scan) {
 
		/* We walked all posibilities, and the user presses tab again.. revert to original text */
 
		strcpy(tb->buf, _chat_tab_completion_buf);
 
		_chat_tab_completion_active = false;
 

	
 
		/* Update the textbuffer */
 
		UpdateTextBufferSize(&WP(w, querystr_d).text);
 

	
 
		SetWindowDirty(w);
 
	}
 
	free(pre_buf);
 
}
 

	
 
/* uses querystr_d WP macro
 
 * uses querystr_d->caption to store
 
 * - type of chat message (Private/Team/All) in bytes 0-7
 
 * - destination of chat message in the case of Team/Private in bytes 8-15 */
 
static void ChatWindowWndProc(Window *w, WindowEvent *e)
 
{
 
	switch (e->event) {
 
	case WE_CREATE:
 
		SendWindowMessage(WC_NEWS_WINDOW, 0, WE_CREATE, w->height, 0);
 
		SETBIT(_no_scroll, SCROLL_CHAT); // do not scroll the game with the arrow-keys
 
		break;
 

	
 
	case WE_PAINT: {
 
		static const StringID chat_captions[] = {
 
			STR_NETWORK_CHAT_ALL_CAPTION,
 
			STR_NETWORK_CHAT_COMPANY_CAPTION,
 
			STR_NETWORK_CHAT_CLIENT_CAPTION
 
		};
 
		StringID msg;
 

	
 
		DrawWindowWidgets(w);
 

	
 
		assert(GB(WP(w, querystr_d).caption, 0, 8) < lengthof(chat_captions));
 
		msg = chat_captions[GB(WP(w, querystr_d).caption, 0, 8)];
 
		DrawStringRightAligned(w->widget[2].left - 2, w->widget[2].top + 1, msg, 16);
 
		DrawEditBox(w, &WP(w, querystr_d), 2);
 
	} break;
 

	
 
	case WE_CLICK:
 
		switch (e->we.click.widget) {
 
			case 3: { /* Send */
 
				DestType type = GB(WP(w, querystr_d).caption, 0, 8);
 
				byte dest = GB(WP(w, querystr_d).caption, 8, 8);
 
				SendChat(WP(w, querystr_d).text.buf, type, dest);
 
			} /* FALLTHROUGH */
 
			case 0: /* Cancel */ DeleteWindow(w); break;
 
		}
 
		break;
 

	
 
	case WE_MOUSELOOP:
 
		HandleEditBox(w, &WP(w, querystr_d), 2);
 
		break;
 

	
 
	case WE_KEYPRESS:
 
		if (e->we.keypress.keycode == WKC_TAB) {
 
			ChatTabCompletion(w);
 
		} else {
 
			_chat_tab_completion_active = false;
 
			switch (HandleEditBoxKey(w, &WP(w, querystr_d), 2, e)) {
 
				case 1: { /* Return */
 
				DestType type = GB(WP(w, querystr_d).caption, 0, 8);
 
				byte dest = GB(WP(w, querystr_d).caption, 8, 8);
 
				SendChat(WP(w, querystr_d).text.buf, type, dest);
 
			} /* FALLTHROUGH */
 
				case 2: /* Escape */ DeleteWindow(w); break;
 
			}
 
		}
 
		break;
 

	
 
	case WE_DESTROY:
 
		SendWindowMessage(WC_NEWS_WINDOW, 0, WE_DESTROY, 0, 0);
 
		CLRBIT(_no_scroll, SCROLL_CHAT);
 
		break;
 
	}
 
}
 

	
 
static const Widget _chat_window_widgets[] = {
 
{   WWT_CLOSEBOX, RESIZE_NONE, 14,   0,  10,  0, 13, STR_00C5,         STR_018B_CLOSE_WINDOW},
 
{      WWT_PANEL, RESIZE_NONE, 14,  11, 639,  0, 13, 0x0,              STR_NULL}, // background
 
{      WWT_PANEL, RESIZE_NONE, 14,  75, 577,  1, 12, 0x0,              STR_NULL}, // text box
 
{ WWT_PUSHTXTBTN, RESIZE_NONE, 14, 578, 639,  1, 12, STR_NETWORK_SEND, STR_NULL}, // send button
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _chat_window_desc = {
 
	WDP_CENTER, -26, 640, 14, // x, y, width, height
 
	WC_SEND_NETWORK_MSG,0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET,
 
	_chat_window_widgets,
 
	ChatWindowWndProc
 
};
 

	
 
void ShowNetworkChatQueryWindow(DestType type, byte dest)
 
{
 
	Window *w;
 

	
 
	DeleteWindowById(WC_SEND_NETWORK_MSG, 0);
 

	
 
	_edit_str_buf[0] = '\0';
 
	_chat_tab_completion_active = false;
 

	
 
	w = AllocateWindowDesc(&_chat_window_desc);
 

	
 
	LowerWindowWidget(w, 2);
 
	WP(w, querystr_d).caption = GB(type, 0, 8) | (dest << 8); // Misuse of caption
 
	WP(w, querystr_d).afilter = CS_ALPHANUMERAL;
 
	InitializeTextBuffer(&WP(w, querystr_d).text, _edit_str_buf, lengthof(_edit_str_buf), 0);
 
}
 

	
 
#endif /* ENABLE_NETWORK */
network/network_gui.h
Show inline comments
 
new file 100644
 
/* $Id$ */
 

	
 
#ifndef NETWORK_GUI_H
 
#define NETWORK_GUI_H
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "network_data.h"
 

	
 
void ShowNetworkNeedPassword(NetworkPasswordType npt);
 
void ShowNetworkGiveMoneyWindow(byte player); // PlayerID
 
void ShowNetworkChatQueryWindow(DestType type, byte dest);
 
void ShowJoinStatusWindow(void);
 
void ShowNetworkGameWindow(void);
 
void ShowClientList(void);
 

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

	
 
static inline void ShowNetworkChatQueryWindow(byte desttype, byte dest) {}
 
static inline void ShowClientList(void) {}
 
static inline void ShowNetworkGameWindow(void) {}
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 
#endif /* NETWORK_GUI_H */

Changeset was too big and was cut off... Show full diff anyway

0 comments (0 inline, 0 general)