Changeset - r543:efdb197f91ad
[Not reviewed]
Makefile
Show inline comments
 
@@ -69,7 +69,7 @@
 
#
 
# Paths:
 
# INSTALL: If not set, the game uses the directory of the binary to
 
# store everything (lang, data, gm, save and openttd.cfg), this is the `old' behaviour. 
 
# store everything (lang, data, gm, save and openttd.cfg), this is the `old' behaviour.
 
# In this case, none of the following paths are used, you also should _not_
 
# use `make install', but copy the required stuff yourself (or just play out
 
# of you source directory, which should work fine).
 
@@ -83,7 +83,7 @@
 
# PREFIX:	Normally /usr/local
 
# BINARY_DIR:	The location of the binary, normally games. (Will be prefixed
 
#		with $PREFIX)
 
# DATA_DIR: 	The location of the data (lang, data and gm), normally 
 
# DATA_DIR: 	The location of the data (lang, data and gm), normally
 
#		share/games/openttd. (Will be prefixed with $PREFIX)
 
# PERSONAL_DIR:	The directory where openttd.cfg and the save folder will be
 
#		stored. You cannot use ~ here, define USE_HOMEDIR for that.
 
@@ -157,7 +157,7 @@ endif
 
# this is used if there aren't any makefile.config
 
ifndef CONFIG_INCLUDED
 
# sets network on by default if there aren't any config file
 
ENABLE_NETWORK:=1   
 
ENABLE_NETWORK:=1
 

	
 
# paths for make install
 
# disabled as they would break it for some (many?) people if they were default
 
@@ -287,6 +287,7 @@ BASECFLAGS += -g
 
else
 
ifdef PROFILE
 
BASECFLAGS += -pg
 
LDFLAGS += -pg
 
else
 
# Release mode
 
ifndef MORPHOS
 
@@ -457,9 +458,11 @@ CDEFS += -DMIDI_ARG=\"$(MIDI_ARG)\"
 
endif
 
endif
 

	
 
# Experimental
 
ifdef WITH_NETWORK
 
CDEFS += -DENABLE_NETWORK
 
ifdef QNX
 
LIBS += -lsocket
 
endif
 
ifdef UNIX
 
ifndef OSX
 
ifndef MORPHOS
 
@@ -516,18 +519,21 @@ endif
 
C_SOURCES = \
 
	ai.c ai_build.c ai_new.c ai_pathfinder.c ai_shared.c aircraft_cmd.c \
 
	aircraft_gui.c airport.c airport_gui.c aystar.c bridge_gui.c \
 
	clear_cmd.c command.c console.c console_cmds.c disaster_cmd.c dock_gui.c dummy_land.c economy.c \
 
	callback_table.c clear_cmd.c command.c console.c console_cmds.c \
 
	dedicated.c disaster_cmd.c dock_gui.c dummy_land.c economy.c \
 
	engine.c engine_gui.c fileio.c gfx.c graph_gui.c newgrf.c \
 
	industry_cmd.c industry_gui.c intro_gui.c landscape.c main_gui.c \
 
	minilzo.c misc.c misc_cmd.c misc_gui.c music_gui.c namegen.c network.c \
 
	network_gui.c news_gui.c oldloader.c order_cmd.c order_gui.c \
 
	pathfind.c player_gui.c players.c queue.c rail_cmd.c rail_gui.c rev.c \
 
	road_cmd.c road_gui.c roadveh_cmd.c roadveh_gui.c saveload.c \
 
	screenshot.c settings.c settings_gui.c ship_cmd.c ship_gui.c \
 
	smallmap_gui.c sound.c sprite.c spritecache.c station_cmd.c station_gui.c \
 
	strings.c subsidy_gui.c terraform_gui.c texteff.c town_cmd.c \
 
	town_gui.c train_cmd.c train_gui.c tree_cmd.c ttd.c tunnelbridge_cmd.c \
 
	unmovable_cmd.c vehicle.c vehicle_gui.c viewport.c water_cmd.c widget.c window.c
 
	network_client.c network_data.c network_gamelist.c network_gui.c \
 
	network_server.c network_udp.c news_gui.c oldloader.c order_cmd.c \
 
	order_gui.c pathfind.c player_gui.c players.c queue.c rail_cmd.c \
 
	rail_gui.c rev.c road_cmd.c road_gui.c roadveh_cmd.c roadveh_gui.c \
 
	saveload.c screenshot.c settings.c settings_gui.c ship_cmd.c \
 
	ship_gui.c smallmap_gui.c sound.c sprite.c spritecache.c station_cmd.c \
 
	station_gui.c strings.c subsidy_gui.c terraform_gui.c texteff.c \
 
	town_cmd.c town_gui.c train_cmd.c train_gui.c tree_cmd.c ttd.c \
 
	tunnelbridge_cmd.c unmovable_cmd.c vehicle.c vehicle_gui.c viewport.c \
 
	water_cmd.c widget.c window.c
 
CXX_SOURCES =
 

	
 
ifdef WITH_SDL
 
@@ -670,8 +676,8 @@ ifeq ($(INSTALL),)
 
	is set correctly up - run \"make upgradeconf\")
 
endif
 

	
 
ifeq ($(PREFIX), ) 
 
	$(error no prefix set - check makefile.config) 
 
ifeq ($(PREFIX), )
 
	$(error no prefix set - check makefile.config)
 
endif
 
#	We compare against the non prefixed version here, so we won't install
 
#	if only the prefix has been set
ai_new.c
Show inline comments
 
@@ -830,7 +830,7 @@ static int AiNew_HowManyVehicles(Player 
 
    	tiles_a_day = RoadVehInfo(i)->max_speed * DAY_TICKS / 256 / 16;
 
    	// We want a vehicle in a station once a month at least, so, calculate it!
 
    	// (the * 2 is because we have 2 stations ;))
 
    	amount = ((int)(((float)length / (float)tiles_a_day / 30 * 2))) * 2;
 
			amount = length * 2 * 2 / tiles_a_day / 30;
 
    	if (amount == 0) amount = 1;
 
   		return amount;
 
	} else if (p->ainew.tbt == AI_TRUCK) {
 
@@ -853,7 +853,7 @@ static int AiNew_HowManyVehicles(Player 
 
    	// We want all the cargo to be gone in a month.. so, we know the cargo it delivers
 
    	//  we know what the vehicle takes with him, and we know the time it takes him
 
    	//  to get back here.. now let's do some math!
 
    	amount = (int)(((float)length / (float)tiles_a_day / 30 * 2) * ((float)max_cargo / (float)RoadVehInfo(i)->capacity));
 
			amount = 2 * length * max_cargo / tiles_a_day / 30 / RoadVehInfo(i)->capacity;
 
    	amount += 1;
 
    	return amount;
 
	} else {
aircraft_gui.c
Show inline comments
 
@@ -25,7 +25,7 @@ static void DrawAircraftImage(Vehicle *v
 
	}
 
}
 

	
 
static void CcBuildAircraft(bool success, uint tile, uint32 p1, uint32 p2)
 
void CcBuildAircraft(bool success, uint tile, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 

	
airport_gui.c
Show inline comments
 
@@ -16,7 +16,7 @@ static byte _selected_airport_type;
 
static void ShowBuildAirportPicker();
 

	
 

	
 
static void CcBuildAirport(bool success, uint tile, uint32 p1, uint32 p2)
 
void CcBuildAirport(bool success, uint tile, uint32 p1, uint32 p2)
 
{
 
	if (success) {
 
		SndPlayTileFx(SND_1F_SPLAT, tile);
bridge_gui.c
Show inline comments
 
@@ -23,7 +23,7 @@ extern const PalSpriteID _bridge_sprites
 
extern const uint16 _bridge_speeds[MAX_BRIDGES];
 
extern const StringID _bridge_material[MAX_BRIDGES];
 

	
 
static void CcBuildBridge(bool success, uint tile, uint32 p1, uint32 p2)
 
void CcBuildBridge(bool success, uint tile, uint32 p1, uint32 p2)
 
{
 
	if (success) SndPlayTileFx(SND_27_BLACKSMITH_ANVIL, tile);
 
}
callback_table.c
Show inline comments
 
new file 100644
 
#include "stdafx.h"
 
#include "ttd.h"
 
#include "functions.h"
 

	
 
// If you add a callback for DoCommandP, also add the callback in here
 
//   see below for the full list!
 
// If you don't do it, it won't work across the network!!
 

	
 
/* aircraft_gui.c */
 
CommandCallback CcBuildAircraft;
 

	
 
/* airport_gui.c */
 
CommandCallback CcBuildAirport;
 

	
 
/* bridge_gui.c */
 
CommandCallback CcBuildBridge;
 

	
 
/* dock_gui.c */
 
CommandCallback CcBuildDocks;
 
CommandCallback CcBuildCanal;
 

	
 
/* main_gui.c */
 
CommandCallback CcPlaySound10;
 
CommandCallback CcPlaceSign;
 
CommandCallback CcTerraform;
 
//CommandCallback CcDemolish;
 
CommandCallback CcBuildTown;
 

	
 
/* rail_gui.c */
 
CommandCallback CcPlaySound1E;
 
CommandCallback CcRailDepot;
 
CommandCallback CcStation;
 
CommandCallback CcBuildRailTunnel;
 

	
 
/* road_gui.c */
 
CommandCallback CcPlaySound1D;
 
CommandCallback CcBuildRoadTunnel;
 
CommandCallback CcRoadDepot;
 

	
 
/* roadveh_gui.c */
 
CommandCallback CcBuildRoadVeh;
 

	
 
/* ship_gui.c */
 
CommandCallback CcBuildShip;
 

	
 
/* train_gui.c */
 
CommandCallback CcBuildWagon;
 
CommandCallback CcBuildLoco;
 

	
 
CommandCallback *_callback_table[] = {
 
	/* 0x00 */ NULL,
 
	/* 0x01 */ CcBuildAircraft,
 
	/* 0x02 */ CcBuildAirport,
 
	/* 0x03 */ CcBuildBridge,
 
	/* 0x04 */ CcBuildCanal,
 
	/* 0x05 */ CcBuildDocks,
 
	/* 0x06 */ CcBuildLoco,
 
	/* 0x07 */ CcBuildRoadVeh,
 
	/* 0x08 */ CcBuildShip,
 
	/* 0x09 */ CcBuildTown,
 
	/* 0x0A */ CcBuildRoadTunnel,
 
	/* 0x0B */ CcBuildRailTunnel,
 
	/* 0x0C */ CcBuildWagon,
 
	/* 0x0D */ CcRoadDepot,
 
	/* 0x0E */ CcRailDepot,
 
	/* 0x0F */ CcPlaceSign,
 
	/* 0x10 */ CcPlaySound10,
 
	/* 0x11 */ CcPlaySound1D,
 
	/* 0x12 */ CcPlaySound1E,
 
	/* 0x13 */ CcStation,
 
	/* 0x14 */ CcTerraform
 
};
 

	
 
const int _callback_table_count = sizeof (_callback_table) / sizeof (*_callback_table);
callback_table.h
Show inline comments
 
new file 100644
 
#ifndef CALLBACK_TABLE_H
 
#define CALLBACK_TABLE_H
 

	
 
extern CommandCallback *_callback_table[];
 
extern const int _callback_table_count;
 

	
 
#endif
command.c
Show inline comments
 
@@ -4,6 +4,7 @@
 
#include "gui.h"
 
#include "command.h"
 
#include "player.h"
 
#include "network.h"
 

	
 
#define DEF_COMMAND(yyyy) int32 yyyy(int x, int y, uint32 flags, uint32 p1, uint32 p2)
 

	
 
@@ -128,6 +129,7 @@ DEF_COMMAND(CmdSetRoadDriveSide);
 
DEF_COMMAND(CmdSetTownNameType);
 

	
 
DEF_COMMAND(CmdChangeDifficultyLevel);
 
DEF_COMMAND(CmdChangePatchSetting);
 

	
 
DEF_COMMAND(CmdStartStopShip);
 
DEF_COMMAND(CmdSellShip);
 
@@ -149,6 +151,7 @@ DEF_COMMAND(CmdCloneOrder);
 

	
 
DEF_COMMAND(CmdClearArea);
 

	
 
DEF_COMMAND(CmdGiveMoney);
 
DEF_COMMAND(CmdMoneyCheat);
 
DEF_COMMAND(CmdBuildCanal);
 
DEF_COMMAND(CmdBuildLock);
 
@@ -301,6 +304,8 @@ static CommandProc * const _command_proc
 
	CmdBuildManySignals,					/* 110 */
 
	//CmdDestroyIndustry,						/* 109 */
 
	CmdDestroyCompanyHQ,					/* 111 */
 
	CmdGiveMoney,									/* 112 */
 
	CmdChangePatchSetting,				/* 113 */
 
};
 

	
 
int32 DoCommandByTile(TileIndex tile, uint32 p1, uint32 p2, uint32 flags, uint procc)
 
@@ -386,15 +391,6 @@ bool DoCommandP(TileIndex tile, uint32 p
 

	
 
	assert(_docommand_recursive == 0);
 

	
 
	if (_networking && !(cmd & CMD_NET_INSTANT) && _pause) {
 
		// When the game is paused, and we are in a network game
 
		//  we do not allow any commands. This is because
 
		//  of some technical reasons
 
		ShowErrorMessage(-1, STR_MULTIPLAYER_PAUSED, x, y);
 
		_docommand_recursive = 0;
 
		return true;
 
	}
 

	
 
	_error_message = INVALID_STRING_ID;
 
	_error_message_2 = cmd >> 16;
 
	_additional_cash_required = 0;
 
@@ -413,7 +409,10 @@ bool DoCommandP(TileIndex tile, uint32 p
 
	assert((cmd & 0xFF) < lengthof(_command_proc_table));
 
	proc = _command_proc_table[cmd & 0xFF];
 

	
 
	// this command is a notest command?
 
	// Some commands have a different output in dryrun then the realrun
 
	//  e.g.: if you demolish a whole town, the dryrun would say okay.
 
	//  but by really destroying, your rating drops and at a certain point
 
	//  it will fail. so res and res2 are different
 
	// CMD_REMOVE_ROAD: This command has special local authority
 
	// restrictions which may cause the test run to fail (the previous
 
	// road fragments still stay there and the town won't let you
 
@@ -426,12 +425,10 @@ bool DoCommandP(TileIndex tile, uint32 p
 
		(cmd & 0xFF) == CMD_TRAIN_GOTO_DEPOT ||
 
		(cmd & 0xFF) == CMD_REMOVE_ROAD;
 

	
 
	if (_networking && (cmd & CMD_ASYNC)) notest = true;
 

	
 
	_docommand_recursive = 1;
 

	
 
	// cost estimation only?
 
	if (_shift_pressed && _current_player == _local_player && !(cmd & CMD_DONT_NETWORK)) {
 
	if (_shift_pressed && _current_player == _local_player && !(cmd & CMD_NETWORK_COMMAND)) {
 
		// estimate the cost.
 
		res = proc(x, y, flags, p1, p2);
 
		if ((uint32)res >> 16 == 0x8000) {
 
@@ -446,30 +443,26 @@ bool DoCommandP(TileIndex tile, uint32 p
 
	}
 

	
 

	
 

	
 
	// unless the command is a notest command, check if it can be executed.
 
	if (!notest) {
 
	if (!((cmd & CMD_NO_TEST_IF_IN_NETWORK) && _networking)) {
 
		// first test if the command can be executed.
 
		res = proc(x,y, flags, p1, p2);
 
		if ((uint32)res >> 16 == 0x8000) {
 
			if (res & 0xFFFF) _error_message = res & 0xFFFF;
 
			goto show_error;
 
		}
 
		// no money?
 
		if (res != 0 && !CheckPlayerHasMoney(res)) goto show_error;
 
		// no money? Only check if notest is off
 
		if (!notest && res != 0 && !CheckPlayerHasMoney(res)) goto show_error;
 
	}
 

	
 
	// put the command in a network queue and execute it later?
 
	if (_networking && !(cmd & CMD_DONT_NETWORK)) {
 
		if (!(cmd & CMD_NET_INSTANT)) {
 
			NetworkSendCommand(tile, p1, p2, cmd, callback);
 
			_docommand_recursive = 0;
 
			return true;
 
		} else {
 
			// Instant Command ... Relay and Process then
 
			NetworkSendCommand(tile, p1, p2, cmd, callback);
 
		}
 
#ifdef ENABLE_NETWORK
 
	// If we are in network, and the command is not from the network
 
	//   send it to the command-queue and abort execution
 
	if (_networking && !(cmd & CMD_NETWORK_COMMAND)) {
 
		NetworkSend_Command(tile, p1, p2, cmd, callback);
 
		_docommand_recursive = 0;
 
		return true;
 
	}
 
#endif /* ENABLE_NETWORK */
 

	
 
	// update last build coordinate of player.
 
	if ( tile != 0 && _current_player < MAX_PLAYERS) DEREF_PLAYER(_current_player)->last_build_coordinate = tile;
 
@@ -478,6 +471,8 @@ bool DoCommandP(TileIndex tile, uint32 p
 
	_yearly_expenses_type = 0;
 
	res2 = proc(x,y, flags|DC_EXEC, p1, p2);
 

	
 
	// If notest is on, it means the result of the test can be different then
 
	//   the real command.. so ignore the test
 
	if (!notest) {
 
		assert(res == res2); // sanity check
 
	} else {
command.h
Show inline comments
 
@@ -146,6 +146,8 @@ enum {
 

	
 
	//CMD_DESTROY_INDUSTRY = 109,
 
	CMD_DESTROY_COMPANY_HQ = 111,
 
	CMD_GIVE_MONEY = 112,
 
	CMD_CHANGE_PATCH_SETTING = 113,
 
};
 

	
 
enum {
 
@@ -166,9 +168,8 @@ enum {
 
enum {
 
	CMD_AUTO = 0x200,
 
	CMD_NO_WATER = 0x400,
 
	CMD_DONT_NETWORK = 0x800,		// execute the command without sending it on the network
 
	CMD_ASYNC = 0x1000,					// execute the command asynchronously without testing first in networking
 
	CMD_NET_INSTANT = 0x2000,
 
	CMD_NETWORK_COMMAND = 0x800,		// execute the command without sending it on the network
 
	CMD_NO_TEST_IF_IN_NETWORK = 0x1000, // When enabled, the command will bypass the no-DC_EXEC round if in network
 
};
 

	
 
//#define return_cmd_error(errcode) do { _error_message=(errcode); return CMD_ERROR; } while(0)
console.c
Show inline comments
 
@@ -10,6 +10,7 @@
 
#include <stdarg.h>
 
#include <string.h>
 
#include "console.h"
 
#include "network.h"
 

	
 
#ifdef WIN32
 
#include <windows.h>
 
@@ -19,19 +20,15 @@
 
#define ICON_CMDBUF_SIZE 20
 
#define ICON_CMDLN_SIZE 255
 
#define ICON_LINE_HEIGHT 12
 

	
 
typedef enum {
 
	ICONSOLE_OPENED,
 
	ICONSOLE_CLOSED
 
} _iconsole_modes;
 
#define ICON_RIGHT_BORDERWIDTH 10
 
#define ICON_BOTTOM_BORDERWIDTH 12
 

	
 
// ** main console ** //
 
static bool _iconsole_inited;
 
static char* _iconsole_buffer[ICON_BUFFER + 1];
 
static char _iconsole_cbuffer[ICON_BUFFER + 1];
 
static uint16 _iconsole_cbuffer[ICON_BUFFER + 1];
 
static char _iconsole_cmdline[ICON_CMDLN_SIZE];
 
static byte _iconsole_cmdpos;
 
static _iconsole_modes _iconsole_mode = ICONSOLE_CLOSED;
 
static Window* _iconsole_win = NULL;
 
static byte _iconsole_scroll;
 

	
 
@@ -104,14 +101,20 @@ static void IConsoleWndProc(Window* w, W
 
		{
 
			int i = _iconsole_scroll;
 
			int max = (w->height / ICON_LINE_HEIGHT) - 1;
 
			int delta = 0;
 
			GfxFillRect(w->left, w->top, w->width, w->height - 1, 0);
 
			while ((i > _iconsole_scroll - max) && (_iconsole_buffer[i] != NULL)) {
 
				DoDrawString(_iconsole_buffer[i], 5,
 
					w->height - (_iconsole_scroll + 2 - i) * ICON_LINE_HEIGHT, _iconsole_cbuffer[i]);
 
				i--;
 
			}
 
			DoDrawString("]", 5, w->height - ICON_LINE_HEIGHT, _iconsole_color_commands);
 
			DoDrawString(_iconsole_cmdline, 10, w->height - ICON_LINE_HEIGHT, _iconsole_color_commands);
 
			delta = w->width - 10 - GetStringWidth(_iconsole_cmdline) - ICON_RIGHT_BORDERWIDTH;
 
			if (delta > 0) {
 
				DoDrawString("]", 5, w->height - ICON_LINE_HEIGHT, _iconsole_color_commands);
 
				delta = 0;
 
			}
 

	
 
			DoDrawString(_iconsole_cmdline, 10 + delta, w->height - ICON_LINE_HEIGHT, _iconsole_color_commands);
 
			break;
 
		}
 
		case WE_TICK:
 
@@ -119,11 +122,15 @@ static void IConsoleWndProc(Window* w, W
 
			if (_icursor_counter > _icursor_rate) {
 
				int posx;
 
				int posy;
 
				int delta;
 

	
 
				_icursor_state = !_icursor_state;
 

	
 
				_cur_dpi = &_screen;
 
				posx = 10 + GetStringWidth(_iconsole_cmdline);
 
				delta = w->width - 10 - GetStringWidth(_iconsole_cmdline) - ICON_RIGHT_BORDERWIDTH;
 
				if (delta > 0)
 
					delta = 0;
 
				posx = 10 + GetStringWidth(_iconsole_cmdline) + delta;
 
				posy = w->height - 3;
 
				GfxFillRect(posx, posy, posx + 5, posy + 1, _icursor_state ? 14 : 0);
 
				_video_driver->make_dirty(posx, posy, 5, 1);
 
@@ -183,10 +190,20 @@ static void IConsoleWndProc(Window* w, W
 
					break;
 
				case WKC_RETURN:
 
					IConsolePrintF(_iconsole_color_commands, "] %s", _iconsole_cmdline);
 
					_iconsole_cmdbufferpos = 19;
 
					IConsoleCmdBufferAdd(_iconsole_cmdline);
 
					IConsoleCmdExec(_iconsole_cmdline);
 
					IConsoleClearCommand();
 
					break;
 
				case WKC_CTRL | WKC_RETURN:
 
					if (_iconsole_mode == ICONSOLE_FULL) {
 
						_iconsole_mode = ICONSOLE_OPENED;
 
					} else {
 
						_iconsole_mode = ICONSOLE_FULL;
 
					}
 
					IConsoleResize();
 
					MarkWholeScreenDirty();
 
					break;
 
				case WKC_BACKSPACE:
 
					if (_iconsole_cmdpos != 0) _iconsole_cmdpos--;
 
					_iconsole_cmdline[_iconsole_cmdpos] = 0;
 
@@ -239,9 +256,9 @@ void IConsoleInit(void)
 
	}
 
	IConsoleStdLibRegister();
 
	#if defined(WITH_REV)
 
	IConsolePrintF(13, "OpenTTD Game Console Revision 4 - %s", _openttd_revision);
 
	IConsolePrintF(13, "OpenTTD Game Console Revision 5 - %s", _openttd_revision);
 
	#else
 
	IConsolePrint(13, "OpenTTD Game Console Revision 4");
 
	IConsolePrint(13, "OpenTTD Game Console Revision 5");
 
	#endif
 
	IConsolePrint(12, "---------------------------------");
 
	IConsolePrint(12, "use \"help\" for more info");
 
@@ -266,9 +283,17 @@ void IConsoleFree(void)
 

	
 
void IConsoleResize(void)
 
{
 
	if (_iconsole_win != NULL) {
 
		_iconsole_win->height = _screen.height / 3;
 
		_iconsole_win->width = _screen.width;
 
	switch (_iconsole_mode) {
 
		case ICONSOLE_OPENED:
 
			_iconsole_win->height = _screen.height / 3;
 
			_iconsole_win->width = _screen.width;
 
			break;
 
		case ICONSOLE_FULL:
 
			_iconsole_win->height = _screen.height - ICON_BOTTOM_BORDERWIDTH;
 
			_iconsole_win->width = _screen.width;
 
			break;
 
		default:
 
			break;
 
	}
 
}
 

	
 
@@ -286,6 +311,11 @@ void IConsoleSwitch(void)
 
			_iconsole_win = NULL;
 
			_iconsole_mode = ICONSOLE_CLOSED;
 
			break;
 
		case ICONSOLE_FULL:
 
			DeleteWindowById(WC_CONSOLE, 0);
 
			_iconsole_win = NULL;
 
			_iconsole_mode = ICONSOLE_CLOSED;
 
			break;
 
	}
 
}
 

	
 
@@ -331,15 +361,20 @@ void IConsoleCmdBufferNavigate(signed ch
 
	_iconsole_cmdpos = strlen(_iconsole_cmdbuffer[i]);
 
}
 

	
 
void IConsolePrint(byte color_code, const char* string)
 
void IConsolePrint(uint16 color_code, const char* string)
 
{
 
	char* _ex;
 
	char* _new;
 
	char _exc;
 
	char _newc;
 
	uint16 _exc;
 
	uint16 _newc;
 
	char* i;
 
	int j;
 

	
 
	if (_network_dedicated) {
 
		printf("%s\n", string);
 
		return;
 
	}
 

	
 
	if (!_iconsole_inited) return;
 

	
 
	_newc = color_code;
 
@@ -362,7 +397,7 @@ void IConsolePrint(byte color_code, cons
 
}
 

	
 

	
 
void CDECL IConsolePrintF(byte color_code, const char* s, ...)
 
void CDECL IConsolePrintF(uint16 color_code, const char* s, ...)
 
{
 
	va_list va;
 
	char buf[1024];
 
@@ -384,7 +419,7 @@ void CDECL IConsolePrintF(byte color_cod
 
void IConsoleDebug(const char* string)
 
{
 
	if (_stdlib_developer > 1)
 
		IConsolePrintF(_iconsole_color_debug, "DEBUG: %s", string);
 
		IConsolePrintF(_iconsole_color_debug, "dbg: %s", string);
 
}
 

	
 
void IConsoleError(const char* string)
 
@@ -446,7 +481,7 @@ void IConsoleVarRegister(const char* nam
 
	item_new = malloc(sizeof(_iconsole_var)); /* XXX unchecked malloc */
 

	
 
	item_new->name = malloc(strlen(name) + 2); /* XXX unchecked malloc */
 
	sprintf(item_new->name, "*%s", name);
 
	sprintf(item_new->name, "%s", name);
 

	
 
	item_new->_next = NULL;
 
	switch (type) {
 
@@ -454,6 +489,7 @@ void IConsoleVarRegister(const char* nam
 
			item_new->data.bool_ = addr;
 
			break;
 
		case ICONSOLE_VAR_BYTE:
 
		case ICONSOLE_VAR_UINT8:
 
			item_new->data.byte_ = addr;
 
			break;
 
		case ICONSOLE_VAR_UINT16:
 
@@ -507,7 +543,7 @@ void IConsoleVarInsert(_iconsole_var* va
 
	if (var->_next != NULL) return;
 

	
 
	var->name = malloc(strlen(name) + 2); /* XXX unchecked malloc */
 
	sprintf(var->name, "*%s", name);
 
	sprintf(var->name, "%s", name);
 

	
 
	item = _iconsole_vars;
 
	if (item == NULL) {
 
@@ -540,6 +576,7 @@ void IConsoleVarInsert(_iconsole_var* va
 
			item->_malloc = true;
 
			break;
 
		case ICONSOLE_VAR_BYTE:
 
		case ICONSOLE_VAR_UINT8:
 
			item->data.byte_ = malloc(sizeof(*item->data.byte_));
 
			*item->data.byte_ = 0;
 
			item->_malloc = true;
 
@@ -607,6 +644,7 @@ void IConsoleVarSetValue(_iconsole_var* 
 
			*var->data.bool_ = (value != 0);
 
			break;
 
		case ICONSOLE_VAR_BYTE:
 
		case ICONSOLE_VAR_UINT8:
 
			*var->data.byte_ = value;
 
			break;
 
		case ICONSOLE_VAR_UINT16:
 
@@ -629,6 +667,7 @@ void IConsoleVarSetValue(_iconsole_var* 
 

	
 
void IConsoleVarDump(const _iconsole_var* var, const char* dump_desc)
 
{
 
	if (var == NULL) return;
 
	if (dump_desc == NULL) dump_desc = var->name;
 

	
 
	switch (var->type) {
 
@@ -638,6 +677,7 @@ void IConsoleVarDump(const _iconsole_var
 
			break;
 
		break;
 
		case ICONSOLE_VAR_BYTE:
 
		case ICONSOLE_VAR_UINT8:
 
			IConsolePrintF(_iconsole_color_default, "%s = %u",
 
				dump_desc, *var->data.byte_);
 
			break;
 
@@ -703,7 +743,10 @@ void IConsoleVarHook(const char* name, _
 

	
 
bool IConsoleVarHookHandle(_iconsole_var* hook_var, _iconsole_hook_types type)
 
{
 
	iconsole_var_hook proc = NULL;
 
	iconsole_var_hook proc;
 
	if (hook_var == NULL) return false;
 

	
 
	proc = NULL;
 
	switch (type) {
 
		case ICONSOLE_HOOK_BEFORE_CHANGE:
 
			proc = hook_var->hook_before_change;
 
@@ -802,6 +845,7 @@ void IConsoleCmdExec(const char* cmdstr)
 
	i = 0;
 
	c = 0;
 
	tokens[c] = tokenstream;
 
	tokentypes[c] = ICONSOLE_VAR_UNKNOWN;
 
	while (i < l && c < lengthof(tokens) - 1) {
 
		if (cmdstr[i] == '"') {
 
			if (longtoken) {
 
@@ -812,9 +856,12 @@ void IConsoleCmdExec(const char* cmdstr)
 
					skip_lt_change = true;
 
				} else {
 
					longtoken = !longtoken;
 
					tokentypes[c] = ICONSOLE_VAR_STRING;
 
				}
 
			} else
 
			} else {
 
				longtoken = !longtoken;
 
				tokentypes[c] = ICONSOLE_VAR_STRING;
 
			}
 
			if (!skip_lt_change) {
 
				if (!longtoken) {
 
					if (valid_token) {
 
@@ -822,6 +869,7 @@ void IConsoleCmdExec(const char* cmdstr)
 
						*tokenstream = '\0';
 
						tokenstream++;
 
						tokens[c] = tokenstream;
 
						tokentypes[c] = ICONSOLE_VAR_UNKNOWN;
 
						valid_token = false;
 
					}
 
				}
 
@@ -833,6 +881,7 @@ void IConsoleCmdExec(const char* cmdstr)
 
				*tokenstream = '\0';
 
				tokenstream++;
 
				tokens[c] = tokenstream;
 
				tokentypes[c] = ICONSOLE_VAR_UNKNOWN;
 
				valid_token = false;
 
			}
 
		} else {
 
@@ -853,28 +902,29 @@ void IConsoleCmdExec(const char* cmdstr)
 
	//** interpreting **//
 

	
 
	for (i = 0; i < c; i++) {
 
		tokentypes[i] = ICONSOLE_VAR_UNKNOWN;
 
		if (tokens[i] != NULL && i > 0 && strlen(tokens[i]) > 0) {
 
			if (tokens[i][0] == '*') {
 
			if (IConsoleVarGet((char *)tokens[i]) != NULL) {
 
				// change the variable to an pointer if execution_mode != 4 is
 
				// being prepared. execution_mode 4 is used to assign 
 
				// being prepared. execution_mode 4 is used to assign
 
				// one variables data to another one
 
				// [token 0 and 2]
 
				if (!((i == 2) && (tokentypes[1] == ICONSOLE_VAR_UNKNOWN) &&
 
					(strcmp(tokens[1], "<<") == 0))) {
 
					var = IConsoleVarGet(tokens[i]);
 
					// only look for another variable if it isnt an longtoken == string with ""
 
					var = NULL;
 
					if (tokentypes[i]!=ICONSOLE_VAR_STRING) var = IConsoleVarGet(tokens[i]);
 
					if (var != NULL) {
 
						// pointer to the data --> token
 
						tokens[i] = (char *) var->data.addr; /* XXX: maybe someone finds an cleaner way to do this */ 
 
						tokens[i] = (char *) var->data.addr; /* XXX: maybe someone finds an cleaner way to do this */
 
						tokentypes[i] = var->type;
 
					}
 
				}
 
			}
 
			if (tokens[i] != NULL && tokens[i][0] == '@' && tokens[i][1] == '*') {
 
				var = IConsoleVarGet(tokens[i] + 1);
 
			if (tokens[i] != NULL && tokens[i][0] == '@' && (IConsoleVarGet(tokens[i]+1) != NULL)) {
 
				var = IConsoleVarGet(tokens[i]+1);
 
				if (var != NULL) {
 
					// pointer to the _iconsole_var struct --> token
 
					tokens[i] = (char *) var; /* XXX: maybe someone finds an cleaner way to do this */ 
 
					tokens[i] = (char *) var; /* XXX: maybe someone finds an cleaner way to do this */
 
					tokentypes[i] = ICONSOLE_VAR_REFERENCE;
 
				}
 
			}
 
@@ -960,6 +1010,7 @@ void IConsoleCmdExec(const char* cmdstr)
 
						break;
 
					}
 
					case ICONSOLE_VAR_BYTE:
 
					case ICONSOLE_VAR_UINT8:
 
					{
 
						if (strcmp(tokens[1], "=") == 0) {
 
							if (c == 3)
 
@@ -1088,9 +1139,9 @@ void IConsoleCmdExec(const char* cmdstr)
 
							IConsoleError("operation not supported");
 
						break;
 
					}
 
					case ICONSOLE_VAR_NONE: 
 
					case ICONSOLE_VAR_REFERENCE: 
 
					case ICONSOLE_VAR_UNKNOWN: 
 
					case ICONSOLE_VAR_NONE:
 
					case ICONSOLE_VAR_REFERENCE:
 
					case ICONSOLE_VAR_UNKNOWN:
 
						IConsoleError("operation not supported");
 
						break;
 
				}
 
@@ -1140,6 +1191,7 @@ void IConsoleCmdExec(const char* cmdstr)
 
							IConsoleVarDump(var, NULL);
 
							break;
 
						case ICONSOLE_VAR_BYTE:
 
						case ICONSOLE_VAR_UINT8:
 
							*var->data.byte_ = *result->data.byte_;
 
							IConsoleVarDump(var, NULL);
 
							break;
console.h
Show inline comments
 
@@ -7,6 +7,7 @@ typedef enum _iconsole_var_types {
 
	ICONSOLE_VAR_NONE,
 
	ICONSOLE_VAR_BOOLEAN,
 
	ICONSOLE_VAR_BYTE,
 
	ICONSOLE_VAR_UINT8,
 
	ICONSOLE_VAR_UINT16,
 
	ICONSOLE_VAR_UINT32,
 
	ICONSOLE_VAR_INT16,
 
@@ -17,6 +18,12 @@ typedef enum _iconsole_var_types {
 
	ICONSOLE_VAR_UNKNOWN
 
} _iconsole_var_types;
 

	
 
typedef enum {
 
	ICONSOLE_FULL,
 
	ICONSOLE_OPENED,
 
	ICONSOLE_CLOSED
 
} _iconsole_modes;
 

	
 
typedef enum _iconsole_hook_types {
 
	ICONSOLE_HOOK_ACCESS,
 
	ICONSOLE_HOOK_BEFORE_CHANGE,
 
@@ -80,6 +87,7 @@ VARDEF byte _iconsole_color_error;
 
VARDEF byte _iconsole_color_warning;
 
VARDEF byte _iconsole_color_debug;
 
VARDEF byte _iconsole_color_commands;
 
VARDEF _iconsole_modes _iconsole_mode;
 

	
 
// ** ttd.c functions ** //
 

	
 
@@ -100,8 +108,8 @@ void IConsoleCmdBufferAdd(const char* cm
 
void IConsoleCmdBufferNavigate(signed char direction);
 

	
 
// ** console output ** //
 
void IConsolePrint(byte color_code, const char* string);
 
void CDECL IConsolePrintF(byte color_code, const char* s, ...);
 
void IConsolePrint(uint16 color_code, const char* string);
 
void CDECL IConsolePrintF(uint16 color_code, const char* s, ...);
 
void IConsoleDebug(const char* string);
 
void IConsoleError(const char* string);
 
void IConsoleWarning(const char* string);
console_cmds.c
Show inline comments
 
@@ -5,10 +5,10 @@
 
#include "engine.h"
 
#include "functions.h"
 
#include "variables.h"
 

	
 
#if defined(WIN32)
 
#	define ENABLE_NETWORK
 
#endif
 
#include "network_data.h"
 
#include "network_client.h"
 
#include "network_server.h"
 
#include "command.h"
 

	
 

	
 
// ** scriptfile handling ** //
 
@@ -39,6 +39,8 @@ static uint32 GetArgumentInteger(const c
 
/* variable and command hooks   */
 
/* **************************** */
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
DEF_CONSOLE_CMD_HOOK(ConCmdHookNoNetwork)
 
{
 
	if (_networking) {
 
@@ -48,26 +50,44 @@ DEF_CONSOLE_CMD_HOOK(ConCmdHookNoNetwork
 
	return true;
 
}
 

	
 
#if 0 /* Not used atm */
 
DEF_CONSOLE_VAR_HOOK(ConVarHookNoNetwork)
 
{
 
	if (_networking) {
 
		IConsoleError("This variable is forbidden in multiplayer.");
 
		return false;
 
	}
 
	return true;
 
}
 
#endif
 

	
 
DEF_CONSOLE_VAR_HOOK(ConVarHookNoNetClient)
 
{
 
	if (!_networking_server) {
 
	if (!_network_server) {
 
		IConsoleError("This variable only makes sense for a network server.");
 
		return false;
 
	}
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD_HOOK(ConCmdHookNoNetClient)
 
{
 
	if (!_networking || !_network_server) {
 
		IConsoleError("This command is only available for a network server.");
 
		return false;
 
	}
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD_HOOK(ConCmdHookNoNetServer)
 
{
 
	if (!_networking || _network_server) {
 
		IConsoleError("You can not use this command for you are a network-server.");
 
		return false;
 
	}
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD_HOOK(ConCmdHookNeedNetwork)
 
{
 
	if (!_networking) {
 
		IConsoleError("Not connected. Multiplayer only command.");
 
		return false;
 
	}
 
	return true;
 
}
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 
/* **************************** */
 
/* reset commands               */
 
/* **************************** */
 
@@ -100,41 +120,130 @@ DEF_CONSOLE_CMD(ConScrollToTile)
 
	return 0;
 
}
 

	
 

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

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

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

	
 
	return NULL;
 
}
 

	
 
DEF_CONSOLE_CMD(ConKick)
 
{
 
	NetworkClientInfo *ci;
 

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

	
 
		ci = NetworkFindClientInfoFromIndex(index);
 

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

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

	
 
	return NULL;
 
}
 

	
 
DEF_CONSOLE_CMD(ConNetworkClients)
 
{
 
	NetworkClientInfo *ci;
 
	for (ci = _network_client_info; ci != &_network_client_info[MAX_CLIENT_INFO]; ci++) {
 
		if (ci->client_index != NETWORK_EMPTY_INDEX) {
 
			IConsolePrintF(8,"Client #%d   name: %s  play-as: %d", ci->client_index, ci->client_name, ci->client_playas);
 
		}
 
	}
 

	
 
	return NULL;
 
}
 

	
 
DEF_CONSOLE_CMD(ConNetworkConnect)
 
{
 
	char* ip;
 
	const char *port = NULL;
 
	const char *player = NULL;
 
	const byte *port = NULL;
 
	const byte *player = NULL;
 
	uint16 rport;
 

	
 
	if (argc<2) return NULL;
 

	
 
	if (_networking) {
 
		// We are in network-mode, first close it!
 
		NetworkDisconnect();
 
	}
 

	
 
	ip = argv[1];
 
	rport = _network_server_port;
 
	rport = NETWORK_DEFAULT_PORT;
 

	
 
	ParseConnectionString(&player, &port, ip);
 

	
 
	IConsolePrintF(_iconsole_color_default, "Connecting to %s...", ip);
 
	if (player!=NULL) {
 
	if (player != NULL) {
 
		_network_playas = atoi(player);
 
		IConsolePrintF(_iconsole_color_default, "    player-no: %s", player);
 
	}
 
	if (port!=NULL) {
 
	if (port != NULL) {
 
		rport = atoi(port);
 
		IConsolePrintF(_iconsole_color_default, "    port: %s", port);
 
	}
 

	
 
	NetworkCoreConnectGame(ip, rport);
 
	NetworkClientConnectGame(ip, rport);
 

	
 
	return NULL;
 
}
 

	
 
#endif
 
#endif /* ENABLE_NETWORK */
 

	
 
/* ******************************** */
 
/*   script file console commands   */
 
@@ -148,7 +257,7 @@ DEF_CONSOLE_CMD(ConExec)
 
	if (argc<2) return NULL;
 

	
 
	doerror = true;
 
	_script_file = fopen(argv[1], "rb");
 
	_script_file = fopen(argv[1], "r");
 

	
 
	if (_script_file == NULL) {
 
		if (argc>2) if (atoi(argv[2])==0) doerror=false;
 
@@ -158,9 +267,11 @@ DEF_CONSOLE_CMD(ConExec)
 

	
 
	_script_running = true;
 

	
 
	fgets(cmd, sizeof(cmd), _script_file);
 
	while (!feof(_script_file) && _script_running) {
 
		strtok(cmd, "\r\n");
 
		IConsoleCmdExec(cmd);
 
		fgets(cmd, sizeof(cmd), _script_file);
 
		IConsoleCmdExec(cmd);
 
	}
 

	
 
	_script_running = false;
 
@@ -209,6 +320,19 @@ DEF_CONSOLE_CMD(ConEchoC)
 
	return NULL;
 
}
 

	
 
extern void SwitchMode(int new_mode);
 

	
 
DEF_CONSOLE_CMD(ConNewGame)
 
{
 
	_docommand_recursive = 0;
 

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

	
 
	SwitchMode(SM_NEWGAME);
 
	return NULL;
 
}
 

	
 
DEF_CONSOLE_CMD(ConPrintF)
 
{
 
	if (argc < 3) return NULL;
 
@@ -299,15 +423,15 @@ DEF_CONSOLE_CMD(ConHelp)
 
{
 
	IConsolePrint(13, " -- console help -- ");
 
	IConsolePrint( 1, " variables: [command to list them: list_vars]");
 
	IConsolePrint( 1, " *temp_string = \"my little \"");
 
	IConsolePrint( 1, " temp_string = \"my little \"");
 
	IConsolePrint( 1, "");
 
	IConsolePrint( 1, " commands: [command to list them: list_cmds]");
 
	IConsolePrint( 1, " [command] [\"string argument with spaces\"] [argument 2] ...");
 
	IConsolePrint( 1, " printf \"%s world\" *temp_string");
 
	IConsolePrint( 1, " printf \"%s world\" temp_string");
 
	IConsolePrint( 1, "");
 
	IConsolePrint( 1, " command/variable returning a value into an variable:");
 
	IConsolePrint( 1, " *temp_uint16 << random");
 
	IConsolePrint( 1, " *temp_uint16 << *temp_uint16_2");
 
	IConsolePrint( 1, " temp_uint16 << random");
 
	IConsolePrint( 1, " temp_uint16 << temp_uint16_2");
 
	IConsolePrint( 1, "");
 
	return NULL;
 
}
 
@@ -362,6 +486,131 @@ DEF_CONSOLE_CMD(ConListDumpVariables)
 
	return NULL;
 
}
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
DEF_CONSOLE_CMD(ConSay)
 
{
 
	if (argc == 2) {
 
		if (!_network_server)
 
			SEND_COMMAND(PACKET_CLIENT_CHAT)(NETWORK_ACTION_CHAT, DESTTYPE_BROADCAST, 0 /* param does not matter */, argv[1]);
 
		else
 
			NetworkServer_HandleChat(NETWORK_ACTION_CHAT, DESTTYPE_BROADCAST, 0, argv[1], NETWORK_SERVER_INDEX);
 
	} else
 
		IConsolePrint(_iconsole_color_default, "Unknown usage. Usage: say \"<msg>\"");
 
	return NULL;
 
}
 

	
 
DEF_CONSOLE_CMD(ConSayPlayer)
 
{
 
	if (argc == 3) {
 
		if (atoi(argv[1]) < 1 || atoi(argv[1]) > MAX_PLAYERS) {
 
			IConsolePrintF(_iconsole_color_default, "Unknown player. Player range is between 1 and %d.", MAX_PLAYERS);
 
			return NULL;
 
		}
 

	
 
		if (!_network_server)
 
			SEND_COMMAND(PACKET_CLIENT_CHAT)(NETWORK_ACTION_CHAT_PLAYER, DESTTYPE_PLAYER, atoi(argv[1]), argv[2]);
 
		else
 
			NetworkServer_HandleChat(NETWORK_ACTION_CHAT_PLAYER, DESTTYPE_PLAYER, atoi(argv[1]), argv[2], NETWORK_SERVER_INDEX);
 
	} else
 
		IConsolePrint(_iconsole_color_default, "Unknown usage. Usage: say_player <playerno> \"<msg>\"");
 
	return NULL;
 
}
 

	
 
DEF_CONSOLE_CMD(ConSayClient)
 
{
 
	if (argc == 3) {
 
		if (!_network_server)
 
			SEND_COMMAND(PACKET_CLIENT_CHAT)(NETWORK_ACTION_CHAT_CLIENT, DESTTYPE_CLIENT, atoi(argv[1]), argv[2]);
 
		else
 
			NetworkServer_HandleChat(NETWORK_ACTION_CHAT_CLIENT, DESTTYPE_CLIENT, atoi(argv[1]), argv[2], NETWORK_SERVER_INDEX);
 
	} else
 
		IConsolePrint(_iconsole_color_default, "Unknown usage. Usage: say_client <clientno> \"<msg>\"");
 
	return NULL;
 
}
 

	
 
DEF_CONSOLE_CMD(ConSetServerName) {
 
	if (argc == 2) {
 
		strncpy(_network_server_name, argv[1], 40);
 
		IConsolePrintF(_iconsole_color_default, "Server-name changed to '%s'", _network_server_name);
 
		ttd_strlcpy(_network_game_info.server_name, _network_server_name, 40);
 
	} else if (argc == 1) {
 
		IConsolePrintF(_iconsole_color_default, "Current server-name is '%s'", _network_server_name);
 
		IConsolePrint(_iconsole_color_default, "  Usage: setservername \"<GameName>\".");
 
	} else {
 
		IConsolePrint(_iconsole_color_default, "Unknow usage. Usage: setservername \"<ServerName>\".");
 
	}
 
	return NULL;
 
}
 

	
 
DEF_CONSOLE_CMD(ConClientName) {
 
	NetworkClientInfo *ci;
 
	ci = NetworkFindClientInfoFromIndex(_network_own_client_index);
 

	
 
	if (argc == 2 && ci != NULL) {
 
		if (!_network_server)
 
			SEND_COMMAND(PACKET_CLIENT_SET_NAME)(argv[1]);
 
		else {
 
			if (NetworkFindName(argv[1])) {
 
				NetworkTextMessage(NETWORK_ACTION_NAME_CHANGE, 1, ci->client_name, argv[1]);
 
				ttd_strlcpy(ci->client_name, argv[1], 40);
 
				NetworkUpdateClientInfo(NETWORK_SERVER_INDEX);
 
			}
 
		}
 
	} else {
 
		IConsolePrint(_iconsole_color_default, "With 'name' you can change your network-player name.");
 
		IConsolePrint(_iconsole_color_default, "  Usage: name \"<name>\".");
 
	}
 

	
 
	return NULL;
 
}
 

	
 
DEF_CONSOLE_CMD(ConProtect) {
 
	// Protect a company with a password
 
	if (_local_player >= MAX_PLAYERS) {
 
		IConsolePrintF(_iconsole_color_default, "You have to own a company to make use of this command.");
 
		return NULL;
 
	}
 
	if (argc == 2) {
 
		if (strncmp(argv[1], "*", 20) == 0) {
 
			_network_player_info[_local_player].password[0] = '\0';
 
		} else {
 
			strncpy(_network_player_info[_local_player].password, argv[1], 20);
 
		}
 
		if (!_network_server)
 
			SEND_COMMAND(PACKET_CLIENT_SET_PASSWORD)(_network_player_info[_local_player].password);
 
		IConsolePrintF(_iconsole_color_default, "Company protected with '%s'", _network_player_info[_local_player].password);
 
	} else {
 
		IConsolePrint(_iconsole_color_default, "Protect sets a password on the company, so no-one without the correct password can join.");
 
		IConsolePrint(_iconsole_color_default, "  Usage: protect \"<password>\".   Use * as <password> to set no password.");
 
	}
 

	
 
	return NULL;
 
}
 

	
 
DEF_CONSOLE_CMD(ConSetPassword) {
 
	if (argc == 2) {
 
		// Change server password
 
		if (strncmp(argv[1], "*", 20) == 0) {
 
			_network_game_info.server_password[0] = '\0';
 
			_network_game_info.use_password = 0;
 
		} else {
 
			strncpy(_network_game_info.server_password, argv[1], 20);
 
			_network_game_info.use_password = 1;
 
		}
 
		IConsolePrintF(_iconsole_color_default, "Game-password changed to '%s'", _network_game_info.server_password);
 
	} else if (argc == 1) {
 
		IConsolePrintF(_iconsole_color_default, "Current game-password is set to '%s'", _network_game_info.server_password);
 
		IConsolePrint(_iconsole_color_default, "  Usage: setpassword \"<password>\".   Use * as <password> to set no password.");
 
	} else {
 
		IConsolePrint(_iconsole_color_default, "Unknow usage. Usage: setpassword \"<password>\".   Use * as <password> to set no password.");
 
	}
 

	
 
	return 0;
 
}
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 
#ifdef _DEBUG
 
/* ****************************************** */
 
/*  debug commands and variables */
 
@@ -390,7 +639,7 @@ void IConsoleDebugLibRegister()
 
/*  console command and variable registration */
 
/* ****************************************** */
 

	
 
void IConsoleStdLibRegister()
 
void IConsoleStdLibRegister(void)
 
{
 
	// stdlib
 
	extern byte _stdlib_developer; /* XXX extern in .c */
 
@@ -402,8 +651,9 @@ void IConsoleStdLibRegister()
 
	// functions [please add them alphabetically]
 
#ifdef ENABLE_NETWORK
 
	IConsoleCmdRegister("connect", ConNetworkConnect);
 
	IConsoleCmdHook("connect", ICONSOLE_HOOK_ACCESS, ConCmdHookNoNetwork);
 
#endif
 
	IConsoleCmdHook("connect", ICONSOLE_HOOK_ACCESS, ConCmdHookNoNetServer);
 
	IConsoleCmdRegister("clients", ConNetworkClients);
 
#endif /* ENABLE_NETWORK */
 
	IConsoleCmdRegister("debug_level",  ConDebugLevel);
 
	IConsoleCmdRegister("dump_vars",    ConListDumpVariables);
 
	IConsoleCmdRegister("echo",         ConEcho);
 
@@ -415,26 +665,50 @@ void IConsoleStdLibRegister()
 
	IConsoleCmdRegister("info_var",     ConInfoVar);
 
	IConsoleCmdRegister("list_cmds",    ConListCommands);
 
	IConsoleCmdRegister("list_vars",    ConListVariables);
 
#ifdef ENABLE_NETWORK
 
	IConsoleCmdRegister("kick",         ConKick);
 
	IConsoleCmdHook("kick", ICONSOLE_HOOK_ACCESS, ConCmdHookNoNetClient);
 
	IConsoleCmdRegister("protect", ConProtect);
 
	IConsoleCmdRegister("name",         ConClientName);
 
#endif
 
	IConsoleCmdRegister("newgame",         ConNewGame);
 
	IConsoleCmdRegister("printf",       ConPrintF);
 
	IConsoleCmdRegister("printfc",      ConPrintFC);
 
	IConsoleCmdRegister("quit",         ConExit);
 
	IConsoleCmdRegister("random",       ConRandom);
 
	IConsoleCmdRegister("resetengines", ConResetEngines);
 
#ifdef ENABLE_NETWORK
 
	IConsoleCmdHook("resetengines", ICONSOLE_HOOK_ACCESS, ConCmdHookNoNetwork);
 
#endif /* ENABLE_NETWORK */
 
	IConsoleCmdRegister("return",     ConReturn);
 
#ifdef ENABLE_NETWORK
 
	IConsoleCmdRegister("say",        ConSay);
 
	IConsoleCmdHook("say", ICONSOLE_HOOK_ACCESS, ConCmdHookNeedNetwork);
 
	IConsoleCmdRegister("say_player", ConSayPlayer);
 
	IConsoleCmdHook("say_player", ICONSOLE_HOOK_ACCESS, ConCmdHookNeedNetwork);
 
	IConsoleCmdRegister("say_client", ConSayClient);
 
	IConsoleCmdHook("say_client", ICONSOLE_HOOK_ACCESS, ConCmdHookNeedNetwork);
 
#endif /* ENABLE_NETWORK */
 
	IConsoleCmdRegister("screenshot", ConScreenShot);
 
	IConsoleCmdRegister("script",     ConScript);
 
	IConsoleCmdRegister("scrollto",   ConScrollToTile);
 
#ifdef ENABLE_NETWORK
 
	IConsoleCmdRegister("setservername", ConSetServerName);
 
	IConsoleCmdHook("setservername", ICONSOLE_HOOK_ACCESS, ConCmdHookNoNetClient);
 
	IConsoleCmdRegister("setpassword", ConSetPassword);
 
	IConsoleCmdHook("setpassword", ICONSOLE_HOOK_ACCESS, ConCmdHookNoNetClient);
 
	IConsoleCmdRegister("status",   ConStatus);
 
	IConsoleCmdHook("status", ICONSOLE_HOOK_ACCESS, ConCmdHookNoNetClient);
 
#endif /* ENABLE_NETWORK */
 

	
 
	// variables [please add them alphabeticaly]
 
	IConsoleVarRegister("developer", &_stdlib_developer, ICONSOLE_VAR_BYTE);
 
#ifdef ENABLE_NETWORK
 
	IConsoleVarRegister("net_client_timeout", &_network_client_timeout, ICONSOLE_VAR_UINT16);
 
	IConsoleVarHook("*net_client_timeout", ICONSOLE_HOOK_ACCESS, ConVarHookNoNetClient);
 
	IConsoleVarRegister("net_ready_ahead", &_network_ready_ahead, ICONSOLE_VAR_UINT16);
 
	IConsoleVarRegister("net_frame_freq", &_network_frame_freq, ICONSOLE_VAR_UINT8);
 
	IConsoleVarHook("*net_frame_freq", ICONSOLE_HOOK_ACCESS, ConVarHookNoNetClient);
 
	IConsoleVarRegister("net_sync_freq", &_network_sync_freq, ICONSOLE_VAR_UINT16);
 
	IConsoleVarHook("*net_sync_freq", ICONSOLE_HOOK_ACCESS, ConVarHookNoNetClient);
 
#endif
 
#endif /* ENABLE_NETWORK */
 

	
 

	
 
}
dedicated.c
Show inline comments
 
new file 100644
 
#include "stdafx.h"
 
#include "ttd.h"
 
#include "network.h"
 
#include "hal.h"
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "gfx.h"
 
#include "window.h"
 
#include "command.h"
 
#include "console.h"
 
#ifdef WIN32
 
#	include <windows.h> /* GetTickCount */
 
#	include <conio.h>
 
#endif
 
#ifdef UNIX
 
#	include <sys/time.h> /* gettimeofday */
 
#	include <sys/types.h>
 
#	include <unistd.h>
 
#	define STDIN 0  /* file descriptor for standard input */
 
#endif
 

	
 
// This file handles all dedicated-server in- and outputs
 

	
 
static void *_dedicated_video_mem;
 

	
 
static const char *DedicatedVideoStart(char **parm) {
 
	_screen.width = _screen.pitch = _cur_resolution[0];
 
	_screen.height = _cur_resolution[1];
 
	_dedicated_video_mem = malloc(_cur_resolution[0]*_cur_resolution[1]);
 

	
 
	_debug_net_level = 6;
 
	_debug_misc_level = 0;
 

	
 
	DEBUG(misc,0)("Loading dedicated server...");
 
	return NULL;
 
}
 
static void DedicatedVideoStop() { free(_dedicated_video_mem); }
 
static void DedicatedVideoMakeDirty(int left, int top, int width, int height) {}
 
static bool DedicatedVideoChangeRes(int w, int h) { return false; }
 

	
 
#ifdef UNIX
 

	
 
bool InputWaiting()
 
{
 
	struct timeval tv;
 
	fd_set readfds;
 

	
 
	tv.tv_sec = 0;
 
	tv.tv_usec = 1;
 

	
 
	FD_ZERO(&readfds);
 
	FD_SET(STDIN, &readfds);
 

	
 
	/* don't care about writefds and exceptfds: */
 
	select(STDIN+1, &readfds, NULL, NULL, &tv);
 

	
 
	if (FD_ISSET(STDIN, &readfds))
 
		return true;
 
	else
 
		return false;
 
}
 
#else
 
bool InputWaiting()
 
{
 
	return kbhit();
 
}
 
#endif
 

	
 
static int DedicatedVideoMainLoop() {
 
#ifndef WIN32
 
	struct timeval tim;
 
#else
 
	char input;
 
#endif
 
	uint32 next_tick;
 
	uint32 cur_ticks;
 
	char input_line[200];
 

	
 
#ifdef WIN32
 
	next_tick = GetTickCount() + 30;
 
#else
 
	gettimeofday(&tim, NULL);
 
	next_tick = (tim.tv_usec / 1000) + 30 + (tim.tv_sec * 1000);
 
#endif
 

	
 
	// Load the dedicated server stuff
 
	_is_network_server = true;
 
	_network_dedicated = true;
 
	_switch_mode = SM_NONE;
 
	_network_playas = OWNER_SPECTATOR;
 
	_local_player = OWNER_SPECTATOR;
 
	DoCommandP(0, Random(), InteractiveRandom(), NULL, CMD_GEN_RANDOM_NEW_GAME);
 
	// Done loading, start game!
 

	
 
	if (!_networking) {
 
		DEBUG(net, 1)("Dedicated server could not be launced. Aborting..");
 
		return ML_QUIT;
 
	}
 

	
 
	while (true) {
 
		InteractiveRandom(); // randomness
 

	
 
#ifdef UNIX
 
		if (InputWaiting()) {
 
			fgets(input_line, 200, stdin);
 
			// Forget about the final \n (or \r)
 
			strtok(input_line, "\r\n");
 
			IConsoleCmdExec(input_line);
 
		}
 
#else
 
		if (InputWaiting()) {
 
			input = getch();
 
			printf("%c", input);
 
			if (input != '\r')
 
				snprintf(input_line, 200, "%s%c", input_line, input);
 
			else {
 
				printf("\n");
 
				IConsoleCmdExec(input_line);
 
				sprintf(input_line, "");
 
			}
 
		}
 
#endif
 

	
 
		if (_exit_game) return ML_QUIT;
 

	
 
#ifdef WIN32
 
		cur_ticks = GetTickCount();
 
#else
 
		gettimeofday(&tim, NULL);
 
		cur_ticks = (tim.tv_usec / 1000) + (tim.tv_sec * 1000);
 
#endif
 

	
 
		if (cur_ticks >= next_tick) {
 
			next_tick += 30;
 

	
 
			GameLoop();
 
			_screen.dst_ptr = _dedicated_video_mem;
 
			UpdateWindows();
 
		}
 
		CSleep(1);
 
	}
 

	
 
	return ML_QUIT;
 
}
 

	
 

	
 
const HalVideoDriver _dedicated_video_driver = {
 
	DedicatedVideoStart,
 
	DedicatedVideoStop,
 
	DedicatedVideoMakeDirty,
 
	DedicatedVideoMainLoop,
 
	DedicatedVideoChangeRes,
 
};
 

	
 
#else
 

	
 
static void *_dedicated_video_mem;
 

	
 
static const char *DedicatedVideoStart(char **parm) {
 
	DEBUG(misc,0)("OpenTTD compiled without network-support, quiting...");
 

	
 
	return NULL;
 
}
 

	
 
static void DedicatedVideoStop() { free(_dedicated_video_mem); }
 
static void DedicatedVideoMakeDirty(int left, int top, int width, int height) {}
 
static bool DedicatedVideoChangeRes(int w, int h) { return false; }
 
static int DedicatedVideoMainLoop() { return ML_QUIT; }
 

	
 
const HalVideoDriver _dedicated_video_driver = {
 
	DedicatedVideoStart,
 
	DedicatedVideoStop,
 
	DedicatedVideoMakeDirty,
 
	DedicatedVideoMainLoop,
 
	DedicatedVideoChangeRes,
 
};
 

	
 
#endif /* ENABLE_NETWORK */
dock_gui.c
Show inline comments
 
@@ -13,7 +13,7 @@ static void ShowBuildDocksDepotPicker();
 

	
 
static byte _ship_depot_direction;
 

	
 
static void CcBuildDocks(bool success, uint tile, uint32 p1, uint32 p2)
 
void CcBuildDocks(bool success, uint tile, uint32 p1, uint32 p2)
 
{
 
	if (success) {
 
		SndPlayTileFx(SND_02_SPLAT, tile);
 
@@ -21,7 +21,7 @@ static void CcBuildDocks(bool success, u
 
	}
 
}
 

	
 
static void CcBuildCanal(bool success, uint tile, uint32 p1, uint32 p2)
 
void CcBuildCanal(bool success, uint tile, uint32 p1, uint32 p2)
 
{
 
	if (success) SndPlayTileFx(SND_02_SPLAT, tile);
 
}
docs/Manual.txt
Show inline comments
 
@@ -90,7 +90,7 @@ Virtually any settings - train numbers, 
 

	
 
2.11 Network Play 
 

	
 
OpenTTD now supports rudimentary TCP/IP based network play. This is not supported on all platforms. To start a server, use the '-n' CLI switch, and start a client with '-n' and the servers IP adress. The OpenTTD network play runs over port 12345, so you may need to open this on your firewall.
 
See multiplayer.txt for more info.
 

	
 
2.12 Rail Recycling.
 

	
docs/console.txt
Show inline comments
 
@@ -40,13 +40,13 @@ VARIABLES:
 
VARIABLE HANDLING:
 
------------------
 

	
 
*developer = 0
 
*developer ++
 
developer = 0
 
developer ++
 

	
 
*temp_string = test
 
*temp_string = "my little"
 
temp_string = test
 
temp_string = "my little"
 

	
 
printf "%s world" *temp_string
 
printf "%s world" temp_string
 

	
 

	
 
---------------------------------------------------
docs/multiplayer.txt
Show inline comments
 
@@ -18,11 +18,18 @@ 2. Connecting to a Server
 
   - select one in the list below the buttons
 
   - click on "join game".
 

	
 
 - if you want to play over the internet you should have the ip of the game server you want connect to.
 
   - click direct connect
 
 - if you want to play over the internet you should have the ip or hostname of the game server you want connect to.
 
   - click add server
 
   - type in the ip address or hostname
 
   - if you want to add a port use :<port>
 
   - if you want to connect as an special player use #<player-no>
 
   
 
 - now you can select a company and press: "Join company", to help that company
 
 - or you can press "Spectate game", to spectate the game
 
 - or you can press "New company", and start your own company (if there are slots free)
 
 
 
 - you see a progressbar how far you are with joining the server.
 
 
 
 - happy playing
 
   
 
3. Connecting to a Server over the Console
 

	
 
@@ -33,38 +40,32 @@ 3. Connecting to a Server over the Conso
 

	
 
4. Playing Internet-Games
 

	
 
 - since OpenTTD 0.3.4 you can also play internet games over higher latency connections.
 
 - to do this the gameservers sync frequency should be highered to a decent value.
 
   - open the console [on the server]
 
   - type in the following command:
 
   
 
    ] *net_sync_freq = <4-80>
 
    
 
    default value: 4
 
    
 
   - this is lowering the sync frequency of the server and your game should be less laggy.
 
   - this is a server variable: it has nothing to do with the clients
 
   
 
 - you can also change when the clients ready packet is sent if you still have lags.
 
   - open the console
 
   - type in the following command:
 
   
 
    ] *net_ready_ahead = <1-8>
 
    
 
    default value: 1
 
    
 
   - in that way your client is sending its "i am ready for next sync" a bit earlier
 
     thats quite good for games where some players have higher latencies than the others.
 
   - this is a client variable: it has nothing to do with the server
 
 - since OpenTTD 0.3.5 the network protocol has been rewritten and is very stable, even over slow connections.
 
 
 
 - it can happen that a connection is that slow, or you have that many clients connected to your server, that your clients start to loose their connection. Some things you can do about it:
 
 
 
 - net_frame_freq:
 
    change it in console with: net_frame_freq = <number>
 
	the number should be between the 0 and 10, not much higher. It indicates the delay between clicking and showing up. The higher, the more you notice it, but the less bandwidth you use.
 
	
 
 - net_sync_freq:
 
    change it in console with: net_sync_freq = <number>
 
	the number should be between the 50 and 1000, not much lower, not much higer. It indicates the time between sync-frames. A sync-frame is a frame which checks if all clients are still in sync. When the value it too high, clients can desync in 1960, but the server detects it in 1970. Not really handy. The lower the value, the more bandwidth it uses.
 
	
 
 NB: changing net_frame_freq has more effect on the bandwidth then net_sync_freq. You should never change net_sync_freq!
 
 
 
 - to change the client timeout time
 
   - open the console [on the server]
 
   - type in the following command:
 
   
 
    ] *net_client_timeout = <30-x>
 
    
 
    default value: 300
 
   
 
   - warning: a too low value will disconnect your clients if they have a short lag
 
   
 
  
 
\ No newline at end of file
 

	
 
5. Some useful things
 

	
 
 - You can protect your company so nobody else can join uninvited. You do this with opening the console and then enter: protect <password>, where <password> is your password.
 
 
 
 - You can give other players some money via the ClientList (under the 'head' in the mainbar).
 
 
 
 - You can chat with other players via SHIFT+T or via the ClientList
 
 
 
 - Servers can now kick players, so don't make them use it!
 
 
 
 - From 0.3.5, desyncs should not happen anymore
 
 
 
 - From 0.3.5, patch-settings are also synced. You can now play without deleting openttd.cfg, and with, for example, extra large trains enabled.
 
 
economy.c
Show inline comments
 
@@ -15,6 +15,7 @@
 
#include "network.h"
 
#include "sound.h"
 
#include "engine.h"
 
#include "network_data.h"
 

	
 
void UpdatePlayerHouse(Player *p, uint score)
 
{
 
@@ -139,7 +140,7 @@ int UpdateCompanyRatingAndValue(Player *
 
		PlayerEconomyEntry *pee;
 
		int numec;
 
		int32 min_income;
 
		uint32 max_income;
 
		int32 max_income;
 

	
 
		numec = min(p->num_valid_stat_ent, 12);
 
		if (numec != 0) {
 
@@ -346,6 +347,7 @@ static void PlayersCheckBankrupt(Player 
 
	int owner;
 
	int64 val;
 

	
 
	// If the player has money again, it does not go bankrupt
 
	if (p->player_money >= 0) {
 
		p->quarters_of_bankrupcy = 0;
 
		return;
 
@@ -355,43 +357,65 @@ static void PlayersCheckBankrupt(Player 
 

	
 
	owner = p->index;
 

	
 
	if (p->quarters_of_bankrupcy == 2) {
 
year_2:
 
		AddNewsItem( (StringID)(owner + 16),
 
			NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
 

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

	
 
		val = CalculateCompanyValue(p);
 
		if (val == 0) goto year_4;
 

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

	
 
		if (IS_HUMAN_PLAYER(owner)) {
 
// what does this code do??
 
			InitNewsItemStructs();
 
			DeleteWindowById(WC_NEWS_WINDOW, 0);
 
		}
 
//		Show bankrupt news
 
			SetDParam(0, p->name_1);
 
			SetDParam(1, p->name_2);
 
			AddNewsItem( (StringID)(owner + 16*3), NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
 

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

	
 
		if (IS_HUMAN_PLAYER(owner)) {
 
			p->bankrupt_asked = 255;
 
			p->bankrupt_timeout = 0x456;
 
		} else {
 
			p->money64 = p->player_money = 100000000;
 
			ChangeOwnershipOfPlayerItems(owner, 0xFF); // 255 is no owner
 
			p->is_active = false;
 
			// If the player is human, and it is no network play, leave the player playing
 
			if (IS_HUMAN_PLAYER(owner) && !_networking) {
 
				p->bankrupt_asked = 255;
 
				p->bankrupt_timeout = 0x456;
 
			} else {
 
				// If we are the server, make sure it is clear that his player is no
 
				//  longer with us!
 
				if (IS_HUMAN_PLAYER(owner) && _network_server) {
 
					NetworkClientInfo *ci;
 
					ci = NetworkFindClientInfoFromIndex(_network_own_client_index);
 
					ci->client_playas = (byte)(OWNER_SPECTATOR + 1);
 
					// Send the new info to all the clients
 
					NetworkUpdateClientInfo(_network_own_client_index);
 
				}
 
				// Make sure the player no longer controls the company
 
				if (IS_HUMAN_PLAYER(owner) && owner == _local_player) {
 
					// Switch the player to spectator..
 
					_local_player = OWNER_SPECTATOR;
 
				}
 
				// Convert everything the player owns to NO_OWNER
 
				p->money64 = p->player_money = 100000000;
 
				ChangeOwnershipOfPlayerItems(owner, 0xFF); // 255 is no owner
 
				// Register the player as not-active
 
				p->is_active = false;
 
			}
 
		}
 
	}
 
}
 
@@ -811,11 +835,11 @@ static void FindSubsidyPassengerRoute(Fo
 

	
 
	fr->distance = (uint)-1;
 

	
 
	fr->from = from = DEREF_TOWN(InteractiveRandomRange(_total_towns));
 
	fr->from = from = DEREF_TOWN(RandomRange(_total_towns));
 
	if (from->xy == 0 || from->population < 400)
 
		return;
 

	
 
	fr->to = to = DEREF_TOWN(InteractiveRandomRange(_total_towns));
 
	fr->to = to = DEREF_TOWN(RandomRange(_total_towns));
 
	if (from==to || to->xy == 0 || to->population < 400 || to->pct_pass_transported > 42)
 
		return;
 

	
 
@@ -830,12 +854,12 @@ static void FindSubsidyCargoRoute(FoundR
 

	
 
	fr->distance = (uint)-1;
 

	
 
	fr->from = i = DEREF_INDUSTRY(InteractiveRandomRange(_total_industries));
 
	fr->from = i = DEREF_INDUSTRY(RandomRange(_total_industries));
 
	if (i->xy == 0)
 
		return;
 

	
 
	// Randomize cargo type
 
	if (InteractiveRandom()&1 && i->produced_cargo[1] != 0xFF) {
 
	if (Random()&1 && i->produced_cargo[1] != 0xFF) {
 
		cargo = i->produced_cargo[1];
 
		trans = i->pct_transported[1];
 
		total = i->total_production[1];
 
@@ -855,7 +879,7 @@ static void FindSubsidyCargoRoute(FoundR
 

	
 
	if (cargo == CT_GOODS || cargo == CT_FOOD) {
 
		// The destination is a town
 
		Town *t = DEREF_TOWN(InteractiveRandomRange(_total_towns));
 
		Town *t = DEREF_TOWN(RandomRange(_total_towns));
 

	
 
		// Only want big towns
 
		if (t->xy == 0 || t->population < 900)
 
@@ -864,7 +888,7 @@ static void FindSubsidyCargoRoute(FoundR
 
		fr->to = t;
 
	} else {
 
		// The destination is an industry
 
		Industry *i2 = DEREF_INDUSTRY(InteractiveRandomRange(_total_industries));
 
		Industry *i2 = DEREF_INDUSTRY(RandomRange(_total_industries));
 

	
 
		// The industry must accept the cargo
 
		if (i == i2 || i2->xy == 0 ||
 
@@ -943,10 +967,8 @@ static void SubsidyMonthlyHandler()
 
		}
 
	}
 

	
 
	if ((_networking) && (!_networking_server)) return;
 

	
 
	// 25% chance to go on
 
	if (ICHANCE16(1,4)) {
 
	if (CHANCE16(1,4)) {
 
		// Find a free slot
 
		s = _subsidies;
 
		while (s->cargo_type != 0xFF) {
 
@@ -972,7 +994,6 @@ static void SubsidyMonthlyHandler()
 
				if (!CheckSubsidyDuplicate(s)) {
 
					s->age = 0;
 
					pair = SetupSubsidyDecodeParam(s, 0);
 
					if (_networking_server) NetworkSendEvent(NET_EVENT_SUBSIDY,sizeof(Subsidy),s);
 
					AddNewsItem(STR_2030_SERVICE_SUBSIDY_OFFERED, NEWS_FLAGS(NM_NORMAL, NF_TILE, NT_SUBSIDIES, 0), pair.a, pair.b);
 
					modified = true;
 
					break;
engine.c
Show inline comments
 
@@ -783,7 +783,7 @@ int32 CmdRenameEngine(int x, int y, uint
 
{
 
	StringID str;
 

	
 
	str = AllocateName((byte*)_decode_parameters, 0);
 
	str = AllocateNameUnique((byte*)_decode_parameters, 0);
 
	if (str == 0)
 
		return CMD_ERROR;
 

	
functions.h
Show inline comments
 
#ifndef FUNCTIONS_H
 
#define FUNCTIONS_H
 

	
 
#include "network.h"
 

	
 
/* vehicle.c */
 

	
 
/* window.c */
 
@@ -96,8 +94,28 @@ void CDECL ShowInfoF(const char *str, ..
 
void NORETURN CDECL error(const char *str, ...);
 

	
 
/* ttd.c */
 
uint32 Random();
 
uint RandomRange(uint max);
 

	
 
// **************
 
// * Warning: DO NOT enable this unless you understand what it does
 
// *
 
// * If enabled, in a network game all randoms will be dumped to the
 
// *  stdout if the first client joins (or if you are a client). This
 
// *  is to help finding desync problems.
 
// *
 
// * Warning: DO NOT enable this unless you understand what it does
 
// **************
 

	
 
//#define RANDOM_DEBUG
 

	
 
#ifdef RANDOM_DEBUG
 
	#define Random() DoRandom(__LINE__, __FILE__)
 
	uint32 DoRandom(uint line, char *file);
 
	#define RandomRange(max) DoRandomRange(max, __LINE__, __FILE__)
 
	uint DoRandomRange(uint max, uint line, char *file);
 
#else
 
	uint32 Random();
 
	uint RandomRange(uint max);
 
#endif
 

	
 
void InitPlayerRandoms();
 

	
 
@@ -114,6 +132,12 @@ void AddTextEffect(StringID msg, int x, 
 
void InitTextEffects();
 
void DrawTextEffects(DrawPixelInfo *dpi);
 

	
 
void InitTextMessage();
 
void DrawTextMessage();
 
void AddTextMessage(uint16 color, uint8 duration, const char *message, ...);
 
void UndrawTextMessage();
 
void TextMessageDailyLoop();
 

	
 
bool AddAnimatedTile(uint tile);
 
void DeleteAnimatedTile(uint tile);
 
void AnimateAnimatedTiles();
 
@@ -125,46 +149,21 @@ bool CheckBridge_Stuff(byte bridge_type,
 
uint32 GetBridgeLength(TileIndex begin, TileIndex end);
 
int CalcBridgeLenCostFactor(int x);
 

	
 
/* network.c */
 
typedef void CommandCallback(bool success, uint tile, uint32 p1, uint32 p2);
 
bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback, uint32 cmd);
 

	
 
void NetworkReceive();
 
void NetworkSend();
 
void NetworkProcessCommands();
 
void NetworkListen();
 
void NetworkInitialize();
 
void NetworkShutdown();
 
void NetworkSendCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback);
 
void NetworkSendEvent(uint16 type, uint16 data_len, void * data);
 
void NetworkStartSync(bool fcreset);
 
void NetworkClose(bool client);
 
void NetworkSendReadyPacket();
 
void NetworkSendSyncPackets();
 
void NetworkSendFrameSyncPackets();
 
bool NetworkCheckClientReady();
 

	
 
void NetworkIPListInit();
 

	
 
void NetworkCoreInit();
 
void NetworkCoreShutdown();
 
void NetworkCoreDisconnect();
 
void NetworkCoreLoop(bool incomming);
 
bool NetworkCoreConnectGame(const byte* b, unsigned short port);
 
bool NetworkCoreConnectGameStruct(NetworkGameList * item);
 
bool NetworkCoreStartGame();
 

	
 
void NetworkLobbyShutdown();
 
void NetworkLobbyInit();
 

	
 
void NetworkGameListClear();
 
NetworkGameList * NetworkGameListAdd();
 
void NetworkGameListFromLAN();
 
void NetworkGameListFromInternet();
 
NetworkGameList * NetworkGameListItem(uint16 index);
 

	
 
void NetworkGameFillDefaults();
 
void NetworkGameChangeDate(uint16 newdate);
 
/* network.c */
 
void NetworkUDPClose(void);
 
void NetworkStartUp();
 
void NetworkShutDown(void);
 
void NetworkGameLoop(void);
 
void NetworkUDPGameLoop(void);
 
bool NetworkServerStart(void);
 
bool NetworkClientConnectGame(const byte* host, unsigned short port);
 
void NetworkQueryServer(const byte* host, unsigned short port, bool game_info);
 
void NetworkReboot();
 
void NetworkDisconnect();
 
void NetworkSend_Command(uint32 tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback);
 

	
 
/* misc_cmd.c */
 
void PlaceTreesRandomly();
 
@@ -180,11 +179,16 @@ void InitializeLandscapeVariables(bool o
 
/* misc.c */
 
void DeleteName(StringID id);
 
byte *GetName(int id, byte *buff);
 
StringID AllocateName(const byte *name, byte skip);
 

	
 
// AllocateNameUnique also tests if the name used is not used anywere else
 
//  and if it is used, it returns an error.
 
#define AllocateNameUnique(name, skip) RealAllocateName(name, skip, true)
 
#define AllocateName(name, skip) RealAllocateName(name, skip, false)
 
StringID RealAllocateName(const byte *name, byte skip, bool check_double);
 
void ConvertDayToYMD(YearMonthDay *ymd, uint16 date);
 
uint ConvertYMDToDay(uint year, uint month, uint day);
 
uint ConvertIntDate(uint date);
 

	
 
void CSleep(int milliseconds);
 

	
 

	
 
/* misc functions */
 
@@ -221,6 +225,10 @@ void ChangeTownRating(Town *t, int add, 
 
uint GetRoadBitsByTile(TileIndex tile);
 
int GetTownRadiusGroup(Town *t, uint tile);
 
int32 GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, byte cargo_type);
 
void ShowNetworkChatQueryWindow(byte desttype, byte dest);
 
void ShowNetworkGiveMoneyWindow(byte player);
 
void ShowNetworkNeedGamePassword();
 
void ShowNetworkNeedCompanyPassword();
 
void ShowRenameSignWindow(SignStruct *ss);
 
void ShowRenameWaypointWindow(Waypoint *cp);
 
int FindFirstBit(uint32 x);
gfx.c
Show inline comments
 
@@ -17,7 +17,8 @@ static byte _string_colorremap[3];
 
static byte _dirty_blocks[DIRTY_BYTES_PER_LINE * MAX_SCREEN_HEIGHT / 8];
 

	
 

	
 
static void memcpy_pitch(void *d, void *s, int w, int h, int spitch, int dpitch)
 

	
 
void memcpy_pitch(void *d, void *s, int w, int h, int spitch, int dpitch)
 
{
 
	byte *dp = (byte*)d;
 
	byte *sp = (byte*)s;
 
@@ -41,6 +42,7 @@ void GfxScroll(int left, int top, int wi
 

	
 
	if (_cursor.visible)
 
		UndrawMouseCursor();
 
	UndrawTextMessage();
 

	
 
	p = _screen.pitch;
 

	
 
@@ -254,7 +256,7 @@ enum {
 

	
 

	
 
/* returns right coordinate */
 
int DrawString(int x, int y, uint16 str, byte color)
 
int DrawString(int x, int y, uint16 str, uint16 color)
 
{
 
	GetString(str_buffr, str);
 
	assert(strlen(str_buffr) < sizeof(str_buffr) - 1);
 
@@ -262,7 +264,7 @@ int DrawString(int x, int y, uint16 str,
 
}
 

	
 

	
 
void DrawStringRightAligned(int x, int y, uint16 str, byte color)
 
void DrawStringRightAligned(int x, int y, uint16 str, uint16 color)
 
{
 
	GetString(str_buffr, str);
 
	assert(strlen(str_buffr) < sizeof(str_buffr) - 1);
 
@@ -270,7 +272,7 @@ void DrawStringRightAligned(int x, int y
 
}
 

	
 

	
 
int DrawStringCentered(int x, int y, uint16 str, byte color)
 
int DrawStringCentered(int x, int y, uint16 str, uint16 color)
 
{
 
	int w;
 

	
 
@@ -283,7 +285,7 @@ int DrawStringCentered(int x, int y, uin
 
	return w;
 
}
 

	
 
void DrawStringCenterUnderline(int x, int y, uint16 str, byte color)
 
void DrawStringCenterUnderline(int x, int y, uint16 str, uint16 color)
 
{
 
	int w = DrawStringCentered(x, y, str, color);
 
	GfxFillRect(x-(w>>1), y+10, x-(w>>1)+w, y+10, _string_colorremap[1]);
 
@@ -472,12 +474,15 @@ void DrawFrameRect(int left, int top, in
 
	}
 
}
 

	
 
int DoDrawString(const byte *string, int x, int y, byte color) {
 
int DoDrawString(const byte *string, int x, int y, uint16 real_color) {
 
	DrawPixelInfo *dpi = _cur_dpi;
 
	int base = _stringwidth_base;
 
	byte c;
 
	byte color;
 
	int xo = x, yo = y;
 

	
 
	color = real_color & 0xFF;
 

	
 
	if (color != 0xFE) {
 
		if (x >= dpi->left + dpi->width ||
 
				x + _screen.width*2 <= dpi->left ||
 
@@ -487,8 +492,13 @@ int DoDrawString(const byte *string, int
 

	
 
		if (color != 0xFF) {
 
switch_color:;
 
			_string_colorremap[1] = _string_colormap[color].text;
 
			_string_colorremap[2] = _string_colormap[color].shadow;
 
			if (real_color & 0x100) {
 
				_string_colorremap[1] = color;
 
				_string_colorremap[2] = 215;
 
			} else {
 
				_string_colorremap[1] = _string_colormap[color].text;
 
				_string_colorremap[2] = _string_colormap[color].shadow;
 
			}
 
			_color_remap_ptr = _string_colorremap;
 
		}
 
	}
 
@@ -1684,6 +1694,7 @@ void RedrawScreenRect(int left, int top,
 
			UndrawMouseCursor();
 
		}
 
	}
 
	UndrawTextMessage();
 

	
 
#if defined(_DEBUG)
 
	if (_dbg_screen_rect)
 
@@ -1921,9 +1932,18 @@ void ToggleFullScreen(const bool full_sc
 
{
 
	_fullscreen = full_screen;
 
	/* use preset resolutions, not _screen.height and _screen.width. On windows for example
 
	   if Desktop-size is 1280x1024, and gamesize is also 1280x1024, _screen.height will be 
 
		 only 1000 because of possible start-bar. For this reason you cannot switch to 
 
	   if Desktop-size is 1280x1024, and gamesize is also 1280x1024, _screen.height will be
 
		 only 1000 because of possible start-bar. For this reason you cannot switch to
 
		 fullscreen mode from this resolution. Use of preset resolution will fix this */
 
	if (!_video_driver->change_resolution(_cur_resolution[0], _cur_resolution[1]))
 
		_fullscreen ^= true; // switching resolution failed, put back full_screen to original status
 
}
 

	
 
uint16 GetDrawStringPlayerColor(byte player)
 
{
 
	// Get the color for DrawString-subroutines which matches the color
 
	//  of the player
 
	if (player == OWNER_SPECTATOR || player == OWNER_SPECTATOR - 1)
 
			return 1;
 
	return (_color_list[_player_colors[player]].window_color_1b) | 0x100;
 
}
gfx.h
Show inline comments
 
@@ -42,14 +42,15 @@ typedef struct CursorVars {
 

	
 
void RedrawScreenRect(int left, int top, int right, int bottom);
 
void GfxScroll(int left, int top, int width, int height, int xo, int yo);
 
int DrawStringCentered(int x, int y, uint16 str, byte color);
 
int DrawString(int x, int y, uint16 str, byte color);
 
void DrawStringCenterUnderline(int x, int y, uint16 str, byte color);
 
int DoDrawString(const byte *string, int x, int y, byte color);
 
void DrawStringRightAligned(int x, int y, uint16 str, byte color);
 
int DrawStringCentered(int x, int y, uint16 str, uint16 color);
 
int DrawString(int x, int y, uint16 str, uint16 color);
 
void DrawStringCenterUnderline(int x, int y, uint16 str, uint16 color);
 
int DoDrawString(const byte *string, int x, int y, uint16 color);
 
void DrawStringRightAligned(int x, int y, uint16 str, uint16 color);
 
void GfxFillRect(int left, int top, int right, int bottom, int color);
 
void GfxDrawLine(int left, int top, int right, int bottom, int color);
 
void DrawFrameRect(int left, int top, int right, int bottom, int color, int flags);
 
uint16 GetDrawStringPlayerColor(byte player);
 

	
 
int GetStringWidth(const byte *str);
 
void LoadStringWidthTable();
gui.h
Show inline comments
 
@@ -93,6 +93,12 @@ void AskForNewGameToStart();
 
void DrawEditBox(Window *w, int wid);
 
void HandleEditBox(Window *w, int wid);
 

	
 
void BuildFileList();
 
void SetFiosType(const byte fiostype);
 

	
 
/*	FIOS_TYPE_FILE, FIOS_TYPE_OLDFILE etc. different colours */
 
static const byte _fios_colors[] = {13, 9, 9, 6, 5, 6, 5};
 

	
 

	
 
/* network gui */
 
void ShowNetworkGameWindow();
hal.h
Show inline comments
 
@@ -71,6 +71,8 @@ extern const HalMusicDriver _extmidi_mus
 
extern const HalMusicDriver _bemidi_music_driver;
 
#endif
 

	
 
extern const HalVideoDriver _dedicated_video_driver;
 

	
 
enum DriverType {
 
	VIDEO_DRIVER = 0,
 
	SOUND_DRIVER = 1,
 
@@ -119,6 +121,12 @@ enum {
 
	FIOS_TYPE_OLD_SCENARIO = 6,
 
};
 

	
 

	
 
// Variables to display file lists
 
FiosItem *_fios_list;
 
int _fios_num;
 
int _saveload_mode;
 

	
 
// get the name of an oldstyle savegame
 
void GetOldSaveGameName(char *title, const char *file);
 
// get the name of an oldstyle scenario
industry_cmd.c
Show inline comments
 
@@ -969,7 +969,9 @@ static void MaybePlantFarmField(Industry
 
{
 
	uint tile;
 
	if (CHANCE16(1,8)) {
 
		tile = TileAddWrap(i->xy, ((i->width>>1) + Random() % 31 - 16), ((i->height>>1) + Random() % 31 - 16));
 
		int x = (i->width>>1) + Random() % 31 - 16;
 
		int y = (i->height>>1) + Random() % 31 - 16;
 
		tile = TileAddWrap(i->xy, x, y);
 
		if (tile != TILE_WRAPPED)
 
			PlantFarmField(tile);
 
	}
 
@@ -1391,7 +1393,7 @@ static Industry *AllocateIndustry()
 
	for(i=_industries; i != endof(_industries); i++) {
 
		if (i->xy == 0) {
 
			int index = i - _industries;
 
		    if (index > _total_industries) _total_industries++;
 
		    if (index > _total_industries) _total_industries = index;
 
			return i;
 
		}
 
	}
 
@@ -1476,7 +1478,9 @@ static void DoCreateNewIndustry(Industry
 
	if (i->type == IT_FARM || i->type == IT_FARM_2) {
 
		tile = i->xy + TILE_XY((i->width >> 1), (i->height >> 1));
 
		for(j=0; j!=50; j++) {
 
			uint new_tile = TileAddWrap(tile, Random() % 31 - 16, Random() % 31 - 16);
 
			int x = Random() % 31 - 16;
 
			int y = Random() % 31 - 16;
 
			uint new_tile = TileAddWrap(tile, x, y);
 
			if (new_tile != TILE_WRAPPED)
 
				PlantFarmField(new_tile);
 
		}
 
@@ -1898,8 +1902,7 @@ static void Load_INDY()
 
	_total_industries = 0;
 
	while ((index = SlIterateArray()) != -1) {
 
		SlObject(DEREF_INDUSTRY(index), _industry_desc);
 
		if (index + 1 > _total_industries)
 
		    _total_industries = index + 1;
 
		if (index > _total_industries) _total_industries = index;
 
	}
 
}
 

	
intro_gui.c
Show inline comments
 
@@ -8,9 +8,9 @@
 
#include "player.h"
 
#include "command.h"
 
#include "console.h"
 
#include "network.h"
 

	
 
extern void MakeNewGame();
 
extern void StartScenario();
 
extern void SwitchMode(int new_mode);
 

	
 
/*
 
static void ShowSelectTutorialWindow()
 
@@ -39,11 +39,13 @@ static const Widget _select_game_widgets
 
{   WIDGETS_END},
 
};
 

	
 
extern void HandleOnEditText(WindowEvent *e);
 
extern void HandleOnEditTextCancel();
 

	
 
static void SelectGameWndProc(Window *w, WindowEvent *e) {
 
	switch(e->event) {
 
	case WE_PAINT:
 
		w->click_state = (w->click_state & ~(0xC0) & ~(0xF << 12)) | (1 << (_new_opt.landscape+12)) | (!_networking?(1<<6):(1<<7));
 
		w->disabled_state = _networking ? 0x30 : 0;
 
		w->click_state = (w->click_state & ~(0xC0) & ~(0xF << 12)) | (1 << (_new_opt.landscape+12)) | (1<<6);
 
		SetDParam(0, STR_6801_EASY + _new_opt.diff_level);
 
		DrawWindowWidgets(w);
 
		break;
 
@@ -54,17 +56,16 @@ static void SelectGameWndProc(Window *w,
 
		case 3: ShowSaveLoadDialog(SLD_LOAD_GAME); break;
 
		case 4: ShowPatchesSelection(); break;
 
		case 5: DoCommandP(0, InteractiveRandom(), 0, NULL, CMD_CREATE_SCENARIO); break;
 
		case 6:
 
			if (_networking)
 
				DoCommandP(0, 0, 0, NULL, CMD_SET_SINGLE_PLAYER);
 
			break;
 
		case 7:
 
#ifdef ENABLE_NETWORK
 
			if (!_network_available) {
 
				ShowErrorMessage(-1,STR_NETWORK_ERR_NOTAVAILABLE, 0, 0);
 
			} else {
 
				ShowNetworkGameWindow();
 
				ShowErrorMessage(-1, TEMP_STRING_NO_NETWORK, 0, 0);
 
			}
 
#else
 
			ShowErrorMessage(-1,STR_NETWORK_ERR_NOTAVAILABLE, 0, 0);
 
#endif /* ENABLE_NETWORK */
 
			break;
 
		case 8: ShowGameOptions(); break;
 
		case 9: ShowGameDifficulty(); break;
 
@@ -79,7 +80,11 @@ static void SelectGameWndProc(Window *w,
 
		case WKC_BACKQUOTE: IConsoleSwitch(); break;
 
		}
 
		break;
 

	
 
	case WE_ON_EDIT_TEXT: HandleOnEditText(e); break;
 
	case WE_ON_EDIT_TEXT_CANCEL: HandleOnEditTextCancel(); break;
 
	}
 

	
 
}
 

	
 
static const WindowDesc _select_game_desc = {
 
@@ -128,9 +133,7 @@ int32 CmdGenRandomNewGame(int x, int y, 
 
	_random_seeds[0][0] = p1;
 
	_random_seeds[0][1] = p2;
 

	
 
	if (_networking) { NetworkStartSync(true); }
 

	
 
	MakeNewGame();
 
	SwitchMode(SM_NEWGAME);
 
	return 0;
 
}
 

	
 
@@ -169,9 +172,7 @@ int32 CmdStartScenario(int x, int y, uin
 
	_random_seeds[0][0] = p1;
 
	_random_seeds[0][1] = p2;
 

	
 
	if (_networking) { NetworkStartSync(true); }
 

	
 
	StartScenario();
 
	SwitchMode(SM_START_SCENARIO);
 
	return 0;
 
}
 

	
lang/english.txt
Show inline comments
 
@@ -1062,10 +1062,10 @@ STR_CONFIG_PATCHES_CURRENCY				:{CURRENC
 
STR_CONFIG_PATCHES_QUERY_CAPT			:{WHITE}Change setting value
 
STR_CONFIG_PATCHES_SERVICE_INTERVAL_INCOMPATIBLE	:{WHITE}Some or all of the default service interval(s) below are incompatible with chosen setting! 5-90% and 30-800 days are valid
 

	
 
STR_TEMPERATE_LANDSCAPE			:temperate landscape
 
STR_SUB_ARCTIC_LANDSCAPE		:sub-arctic landscape
 
STR_SUB_TROPICAL_LANDSCAPE	:sub-tropical landscape
 
STR_TOYLAND_LANDSCAPE				:toyland landscape
 
STR_TEMPERATE_LANDSCAPE			:Temperate landscape
 
STR_SUB_ARCTIC_LANDSCAPE		:Sub-arctic landscape
 
STR_SUB_TROPICAL_LANDSCAPE	:Sub-tropical landscape
 
STR_TOYLAND_LANDSCAPE				:Toyland landscape
 

	
 
STR_CHEATS										:{WHITE}Cheats
 
STR_CHEATS_TIP								:{BLACK}Checkboxes indicate if you have used this cheat before
 
@@ -1184,37 +1184,46 @@ TEMP_AI_ACTIVATED						:{WHITE}Warning: 
 

	
 
############ network gui strings
 

	
 
TEMP_STRING_NO_NETWORK						:{WHITE}Network interface is not yet fully operational!{}Not working items have been disabled.
 

	
 
STR_NETWORK_MULTIPLAYER						:{WHITE}Multiplayer
 

	
 
STR_NETWORK_FIND_SERVER						:{BLACK}Find server
 
STR_NETWORK_FIND_SERVER_TIP				:{BLACK}Search network for a server
 
STR_NETWORK_DIRECT_CONNECT				:{BLACK}Direct connect
 
STR_NETWORK_ENTER_IP							:{BLACK}Enter the IP address of the server
 
STR_NETWORK_DIRECT_CONNECT_TIP		:{BLACK}Connect to a known IP
 
STR_NETWORK_START_SERVER					:{BLACK}Start server
 
STR_NETWORK_START_SERVER_TIP			:{BLACK}Start an own server
 

	
 
STR_NETWORK_PLAYER_NAME						:{BLACK}Player name:
 
STR_NETWORK_ENTER_NAME_TIP				:{BLACK}This is the name other players will identify you by
 

	
 
STR_NETWORK_SELECT_CONNECTION			:{BLACK}Select connection type:
 
STR_NETWORK_CONNECTION_TYPE_TIP		:{BLACK}Choose between an internet game or a local area nework game
 
STR_NETWORK_CONNECTION						:{BLACK}Connection:
 
STR_NETWORK_CONNECTION_TIP				:{BLACK}Choose between an internet game or a local area nework game
 
STR_NETWORK_COMBO1								:{BLACK}{SKIP}{SKIP}{STRING}
 
STR_NETWORK_LAN										:LAN
 
STR_NETWORK_INTERNET							:Internet
 

	
 
STR_NETWORK_START_SERVER					:{BLACK}Start server
 
STR_NETWORK_START_SERVER_TIP			:{BLACK}Start an own server
 

	
 
STR_NETWORK_GAME_NAME							:{BLACK}Name
 
STR_NETWORK_GAME_NAME_TIP					:{BLACK}Name of the game
 
STR_NETWORK_PLAYERS								:{BLACK}#/#
 
STR_NETWORK_PLAYERS_TIP						:{BLACK}Players currently in this game / Maximum number of players
 
STR_NETWORK_MAP_SIZE							:{BLACK}Size
 
STR_NETWORK_MAP_SIZE_TIP					:{BLACK}Size of the map
 
STR_NETWORK_INFO_ICONS_TIP				:{BLACK}Language, server version, etc.
 
STR_NETWORK_CLICK_GAME_TO_SELECT	:{BLACK}Click a game from the list to select it
 

	
 
STR_NETWORK_PLAYERS_VAL						:{BLACK}{COMMA8}/{COMMA8}
 
STR_NETWORK_FIND_SERVER						:{BLACK}Find server
 
STR_NETWORK_FIND_SERVER_TIP				:{BLACK}Search network for a server
 
STR_NETWORK_ADD_SERVER						:{BLACK}Add server
 
STR_NETWORK_ADD_SERVER_TIP				:{BLACK}Adds a server to the list which will always be checked for running games.
 
STR_NETWORK_ENTER_IP							:{BLACK}Enter the address of the host
 

	
 
STR_NETWORK_CLIENTS_ONLINE				:{BLACK}{COMMA16}/{COMMA16}
 
STR_NETWORK_CLIENTS_CAPTION 			:{BLACK}Clients
 
STR_NETWORK_CLIENTS_CAPTION_TIP		:{BLACK}Clients online / clients max
 
STR_NETWORK_GAME_INFO							:{SILVER}GAME INFO
 
STR_ORANGE												:{ORANGE}{STRING}
 
STR_NETWORK_CLIENTS								:{SILVER}Clients:  {WHITE}{COMMA8} / {COMMA8}
 
STR_NETWORK_LANGUAGE							:{SILVER}Language:  {WHITE}{STRING}
 
STR_NETWORK_TILESET								:{SILVER}Tileset:  {WHITE}{STRING}
 
STR_NETWORK_MAP_SIZE							:{SILVER}Map size:  {WHITE}{COMMA16}x{COMMA16}
 
STR_NETWORK_SERVER_VERSION				:{SILVER}Server version:  {WHITE}{STRING}
 
STR_NETWORK_SERVER_ADDRESS				:{SILVER}Server address:  {WHITE}{STRING}
 
STR_NETWORK_START_DATE						:{SILVER}Start date:  {WHITE}{DATE_SHORT}
 
STR_NETWORK_CURRENT_DATE					:{SILVER}Current date:  {WHITE}{DATE_SHORT}
 
STR_NETWORK_PASSWORD							:{SILVER}Password protected!
 
STR_NETWORK_SERVER_OFFLINE				:{SILVER}SERVER OFFLINE
 
STR_NETWORK_SERVER_FULL						:{SILVER}SERVER FULL
 

	
 
STR_NETWORK_JOIN_GAME							:{BLACK}Join game
 

	
 
@@ -1223,20 +1232,25 @@ STR_NETWORK_START_GAME_WINDOW			:{WHITE}
 

	
 
STR_NETWORK_NEW_GAME_NAME					:{BLACK}Game name:
 
STR_NETWORK_NEW_GAME_NAME_TIP			:{BLACK}The game name will be displayed to other players in the multiplayer game selection menu
 
STR_NETWORK_PASSWORD							:{BLACK}Password:
 
STR_NETWORK_SET_PASSWORD					:{BLACK}Set password
 
STR_NETWORK_PASSWORD_TIP					:{BLACK}Protect your game with a password if you don't want other people to join it
 
STR_NETWORK_SELECT_MAP						:{BLACK}Select a map:
 
STR_NETWORK_SELECT_MAP_TIP				:{BLACK}Which map do you want to play?
 
STR_NETWORK_NUMBER_OF_PLAYERS			:{BLACK}Number of players:
 
STR_NETWORK_NUMBER_OF_PLAYERS_TIP	:{BLACK}Choose a maximum number of players. Not all slots need to be filled.
 
STR_NETWORK_NUMBER_OF_CLIENTS			:{BLACK}Maximum allowed clients:
 
STR_NETWORK_NUMBER_OF_CLIENTS_TIP	:{BLACK}Choose a maximum number of clients. Not all slots need to be filled.
 
STR_NETWORK_COMBO2								:{BLACK}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{STRING}
 
STR_NETWORK_2_PLAYERS							:2 players
 
STR_NETWORK_3_PLAYERS							:3 players
 
STR_NETWORK_4_PLAYERS							:4 players
 
STR_NETWORK_5_PLAYERS							:5 players
 
STR_NETWORK_6_PLAYERS							:6 players
 
STR_NETWORK_7_PLAYERS							:7 players
 
STR_NETWORK_8_PLAYERS							:8 players
 
STR_NETWORK_2_CLIENTS							:2 clients
 
STR_NETWORK_3_CLIENTS							:3 clients
 
STR_NETWORK_4_CLIENTS							:4 clients
 
STR_NETWORK_5_CLIENTS							:5 clients
 
STR_NETWORK_6_CLIENTS							:6 clients
 
STR_NETWORK_7_CLIENTS							:7 clients
 
STR_NETWORK_8_CLIENTS							:8 clients
 
STR_NETWORK_9_CLIENTS							:9 clients
 
STR_NETWORK_10_CLIENTS						:10 clients
 
STR_NETWORK_LANGUAGE_SPOKEN				:{BLACK}Language spoken:
 
STR_NETWORK_LANGUAGE_TIP					:{BLACK}Other players will know which language is spoken on the server.
 
STR_NETWORK_COMBO3								:{BLACK}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{SKIP}{STRING}
 
STR_NETWORK_START_GAME						:{BLACK}Start Game
 
STR_NETWORK_START_GAME_TIP				:{BLACK}Start a new network game from a random map, or scenario
 
STR_NETWORK_LOAD_GAME							:{BLACK}Load Game
 
@@ -1244,17 +1258,61 @@ STR_NETWORK_LOAD_GAME_TIP					:{BLACK}Re
 
STR_NETWORK_LOAD_SCENARIO					:{BLACK}Load Scenario
 
STR_NETWORK_LOAD_SCENARIO_TIP			:{BLACK}Start a new network game from a scenario
 

	
 
############ Leave those lines in this order!!
 
STR_NETWORK_LANG_ANY							:Any
 
STR_NETWORK_LANG_ENGLISH					:English
 
STR_NETWORK_LANG_GERMAN						:German
 
STR_NETWORK_LANG_FRENCH						:French
 
############ End of leave-in-this-order
 

	
 
STR_NETWORK_GAME_LOBBY						:{WHITE}Multiplayer game lobby
 

	
 
STR_NETWORK_SEND									:{BLACK}Send
 
STR_NETWORK_SEND_TIP							:{BLACK}Send a message to the other players
 
STR_NETWORK_COMPANY_NAME					:{BLACK}Company name:
 
STR_NETWORK_COMPANY_NAME_TIP			:{BLACK}Change the name of your company. Hit enter to apply changes
 
STR_NETWORK_PREPARE_TO_JOIN				:{BLACK}Preparing to join:   {ORANGE}{STRING}
 
STR_NETWORK_COMPANY_LIST_TIP			:{BLACK}A list of all companies currently in this game. You can either join one or start a
 
STR_NETWORK_NEW_COMPANY						:{BLACK}New company
 
STR_NETWORK_NEW_COMPANY_TIP				:{BLACK}Open a new company
 
STR_NETWORK_SPECTATE_GAME					:{BLACK}Spectate game
 
STR_NETWORK_SPECTATE_GAME_TIP			:{BLACK}Watch the game as a spectator
 
STR_NETWORK_NEW_COMPANY						:{BLACK}New company
 
STR_NETWORK_NEW_COMPANY_TIP				:{BLACK}Open a new company
 
STR_NETWORK_READY									:{BLACK}Ready
 
STR_NETWORK_JOIN_COMPANY					:{BLACK}Join company
 
STR_NETWORK_JOIN_COMPANY_TIP			:{BLACK}Help managing this company
 
STR_NETWORK_REFRESH								:{BLACK}Refresh server
 
STR_NETWORK_REFRESH_TIP						:{BLACK}Refresh the server info
 

	
 
STR_NETWORK_COMPANY_INFO					:{SILVER}COMPANY INFO
 

	
 
STR_NETWORK_COMPANY_NAME					:{SILVER}Company name:  {WHITE}{STRING}
 
STR_NETWORK_INAUGURATION_YEAR			:{SILVER}Inauguration:  {WHITE}{NUMU16}
 
STR_NETWORK_VALUE									:{SILVER}Company value:  {WHITE}{CURRENCY64}
 
STR_NETWORK_CURRENT_BALANCE				:{SILVER}Current balance:  {WHITE}{CURRENCY64}
 
STR_NETWORK_LAST_YEARS_INCOME			:{SILVER}Last year's income:  {WHITE}{CURRENCY64}
 
STR_NETWORK_PERFORMANCE						:{SILVER}Performance:  {WHITE}{NUMU16}
 

	
 
STR_NETWORK_VEHICLES							:{SILVER}Vehicles:  {WHITE}{NUMU16} {TRAIN}, {NUMU16} {LORRY}, {NUMU16} {BUS}, {NUMU16} {PLANE}, {NUMU16} {SHIP}
 
STR_NETWORK_STATIONS							:{SILVER}Stations:  {WHITE}{NUMU16} {TRAIN}, {NUMU16} {LORRY}, {NUMU16} {BUS}, {NUMU16} {PLANE}, {NUMU16} {SHIP}
 
STR_NETWORK_PLAYERS								:{SILVER}Players:  {WHITE}{STRING}
 

	
 
STR_NETWORK_CONNECTING			:{WHITE}Connecting...
 

	
 
############ Leave those lines in this order!!
 
STR_NETWORK_CONNECTING_1	:{BLACK}(1/6) Connecting..
 
STR_NETWORK_CONNECTING_2	:{BLACK}(2/6) Authorizing..
 
STR_NETWORK_CONNECTING_3	:{BLACK}(3/6) Waiting..
 
STR_NETWORK_CONNECTING_4	:{BLACK}(4/6) Downloading map..
 
STR_NETWORK_CONNECTING_5	:{BLACK}(5/6) Processing data..
 

	
 
STR_NETWORK_CONNECTING_SPECIAL_1	:{BLACK}Fetching game info..
 
STR_NETWORK_CONNECTING_SPECIAL_2	:{BLACK}Fetching company info..
 
############ End of leave-in-this-order
 
STR_NETWORK_CONNECTING_WAITING			:{BLACK}{INT32} client(s) in front of us
 
STR_NETWORK_CONNECTING_DOWNLOADING	:{BLACK}{INT32} / {INT32} kbytes downloaded so far
 

	
 
STR_NETWORK_DISCONNECT			:{BLACK}Disconnect
 

	
 
STR_NETWORK_CHAT_QUERY_CAPTION			:{WHITE}Enter your text message to send
 
STR_NETWORK_GIVE_MONEY_CAPTION			:{WHITE}Enter the amount of money you want to give
 
STR_NETWORK_NEED_GAME_PASSWORD_CAPTION		:{WHITE}Server is protected. Enter password
 
STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION	:{WHITE}Company is protected. Enter password
 
STR_NETWORK_CLIENT_LIST						:{WHITE}Client List
 

	
 
STR_NETWORK_ERR_NOTAVAILABLE			:{WHITE} No network devices found or compiled without ENABLE_NETWORK
 
STR_NETWORK_ERR_NOSERVER			:{WHITE} Could not find any network games
 
@@ -1262,7 +1320,34 @@ STR_NETWORK_ERR_NOCONNECTION			:{WHITE} 
 
STR_NETWORK_ERR_DESYNC				:{WHITE} Network-Game synchronization failed.
 
STR_NETWORK_ERR_LOSTCONNECTION			:{WHITE} Network-Game connection lost.
 
STR_NETWORK_ERR_SAVEGAMEERROR			:{WHITE} Could not load server-savegame.
 
STR_NETWORK_ERR_SERVER_START			:{WHITE} Could not start the server.
 
STR_NETWORK_ERR_CLIENT_START			:{WHITE} Could not connect.
 
STR_NETWORK_ERR_TIMEOUT					:{WHITE} Connection #{NUMU16} timed out.
 
STR_NETWORK_ERR_SERVER_ERROR		:{WHITE} We made a protocol-error and our connection is closed.
 
STR_NETWORK_ERR_WRONG_REVISION	:{WHITE} The revision of this client does not match the revision of the server.
 
STR_NETWORK_ERR_WRONG_PASSWORD	:{WHITE} Wrong password.
 
STR_NETWORK_ERR_SERVER_FULL	:{WHITE} The server is full
 
STR_NETWORK_ERR_KICKED	:{WHITE} You are kicked out of the server
 

	
 
STR_NETWORK_ERR_LEFT							:has left the game
 
############ Leave those lines in this order!!
 
STR_NETWORK_ERR_CLIENT_GENERAL					:general error
 
STR_NETWORK_ERR_CLIENT_DESYNC						:desync error
 
STR_NETWORK_ERR_CLIENT_SAVEGAME					:could not load map
 
STR_NETWORK_ERR_CLIENT_CONNECTION_LOST	:connection lost
 
STR_NETWORK_ERR_CLIENT_PROTOCOL_ERROR		:protocol error
 
STR_NETWORK_ERR_CLIENT_NOT_AUTHORIZED		:not authorized
 
STR_NETWORK_ERR_CLIENT_NOT_EXPECTED			:received strange packet
 
STR_NETWORK_ERR_CLIENT_WRONG_REVISION		:wrong revision
 
STR_NETWORK_ERR_CLIENT_NAME_IN_USE			:name already in use
 
STR_NETWORK_ERR_CLIENT_WRONG_PASSWORD		:wrong game-password
 
STR_NETWORK_ERR_CLIENT_PLAYER_MISMATCH	:wrong player-id in DoCommand
 
STR_NETWORK_ERR_CLIENT_KICKED						:kicked by server
 
############ End of leave-in-this-order
 
STR_NETWORK_CLIENT_JOINED					:has joined the game
 
STR_NETWORK_GIVE_MONEY						:gave you some money ({CURRENCY})
 
STR_NETWORK_SERVER_SHUTDOWN				:{WHITE} The server closed the session
 
STR_NETWORK_SERVER_REBOOT					:{WHITE} The server is restarting...{}Please wait...
 

	
 
############ end network gui strings
 

	
macros.h
Show inline comments
 
@@ -170,7 +170,6 @@ static inline int FindFirstBit2x64(int v
 

	
 

	
 
#define CHANCE16(a,b) ((uint16)Random() <= (uint16)((65536 * a) / b))
 
#define ICHANCE16(a,b) ((uint16)InteractiveRandom() <= (uint16)((65536 * a) / b))
 
#define CHANCE16R(a,b,r) ((uint16)(r=Random()) <= (uint16)((65536 * a) / b))
 
#define CHANCE16I(a,b,v) ((uint16)(v) <= (uint16)((65536 * a) / b))
 

	
main_gui.c
Show inline comments
 
@@ -12,6 +12,13 @@
 
#include "vehicle.h"
 
#include "console.h"
 
#include "sound.h"
 
#include "network.h"
 

	
 
#ifdef ENABLE_NETWORK
 
#include "network_data.h"
 
#include "network_client.h"
 
#include "network_server.h"
 
#endif /* ENABLE_NETWORK */
 

	
 
#include "table/animcursors.h"
 

	
 
@@ -35,7 +42,19 @@ extern void GenerateWorld(int mode);
 
extern void GenerateIndustries();
 
extern void GenerateTowns();
 

	
 
static void HandleOnEditText(WindowEvent *e) {
 
extern uint GetCurrentCurrencyRate();
 

	
 
void HandleOnEditTextCancel() {
 
	switch(_rename_what) {
 
#ifdef ENABLE_NETWORK
 
	case 4:
 
		NetworkDisconnect();
 
		break;
 
#endif /* ENABLE_NETWORK */
 
	}
 
}
 

	
 
void HandleOnEditText(WindowEvent *e) {
 
	byte *b = e->edittext.str;
 
	int id;
 
	memcpy(_decode_parameters, b, 32);
 
@@ -52,6 +71,36 @@ static void HandleOnEditText(WindowEvent
 
			return;
 
		DoCommandP(0, id, 0, NULL, CMD_RENAME_WAYPOINT | CMD_MSG(STR_CANT_CHANGE_WAYPOINT_NAME));
 
		break;
 
#ifdef ENABLE_NETWORK
 
	case 2:
 
		// Speak to..
 
		if (!_network_server)
 
			SEND_COMMAND(PACKET_CLIENT_CHAT)(NETWORK_ACTION_CHAT + (id & 0xFF), id & 0xFF, (id >> 8) & 0xFF, e->edittext.str);
 
		else
 
			NetworkServer_HandleChat(NETWORK_ACTION_CHAT + (id & 0xFF), id & 0xFF, (id >> 8) & 0xFF, e->edittext.str, NETWORK_SERVER_INDEX);
 
		break;
 
	case 3: {
 
		// Give money
 
		int32 money = atoi(e->edittext.str) / GetCurrentCurrencyRate();
 
		char msg[100];
 
		// Give 'id' the money, and substract it from ourself
 
		if (!DoCommandP(0, money, id, NULL, CMD_GIVE_MONEY)) break;
 

	
 
		// Inform the player of this action
 
		SetDParam(0, money);
 
		GetString(msg, STR_NETWORK_GIVE_MONEY);
 

	
 
		if (!_network_server)
 
			SEND_COMMAND(PACKET_CLIENT_CHAT)(NETWORK_ACTION_GIVE_MONEY, DESTTYPE_PLAYER, id + 1, msg);
 
		else
 
			NetworkServer_HandleChat(NETWORK_ACTION_GIVE_MONEY, DESTTYPE_PLAYER, id + 1, msg, NETWORK_SERVER_INDEX);
 
		break;
 
	}
 
	case 4: {// Game-Password and Company-Password
 
		SEND_COMMAND(PACKET_CLIENT_PASSWORD)(id, e->edittext.str);
 
		break;
 
	}
 
#endif /* ENABLE_NETWORK */
 
	}
 
}
 

	
 
@@ -88,9 +137,9 @@ typedef void ToolbarButtonProc(Window *w
 

	
 
static void ToolbarPauseClick(Window *w)
 
{
 
	if (_networking && !_networking_server) { return;} // only server can pause the game
 
	if (_networking && !_network_server) { return;} // only server can pause the game
 

	
 
	if (DoCommandP(0, _pause?0:1, 0, NULL, CMD_PAUSE | CMD_NET_INSTANT))
 
	if (DoCommandP(0, _pause?0:1, 0, NULL, CMD_PAUSE))
 
		SndPlayFx(SND_15_BEEP);
 
}
 

	
 
@@ -111,7 +160,7 @@ static void MenuClickSettings(int index)
 
	case 1: ShowGameDifficulty(); return;
 
	case 2: ShowPatchesSelection(); return;
 
	case 3: ShowNewgrf(); return;
 
	
 

	
 
	case 5: _display_opt ^= DO_SHOW_TOWN_NAMES; MarkWholeScreenDirty(); return;
 
	case 6: _display_opt ^= DO_SHOW_STATION_NAMES; MarkWholeScreenDirty(); return;
 
	case 7: _display_opt ^= DO_SHOW_SIGNS; MarkWholeScreenDirty(); return;
 
@@ -194,9 +243,20 @@ static void MenuClickFinances(int index)
 
	ShowPlayerFinances(index);
 
}
 

	
 
#ifdef ENABLE_NETWORK
 
extern void ShowClientList();
 
#endif /* ENABLE_NETWORK */
 

	
 
static void MenuClickCompany(int index)
 
{
 
	ShowPlayerCompany(index);
 
	if (_networking && index == 0) {
 
#ifdef ENABLE_NETWORK
 
		ShowClientList();
 
#endif /* ENABLE_NETWORK */
 
	} else {
 
		if (_networking) index--;
 
		ShowPlayerCompany(index);
 
	}
 
}
 

	
 

	
 
@@ -270,6 +330,37 @@ static void MenuClickBuildAir(int index)
 
	ShowBuildAirToolbar();
 
}
 

	
 
#ifdef ENABLE_NETWORK
 
void ShowNetworkChatQueryWindow(byte desttype, byte dest)
 
{
 
	_rename_id = desttype + (dest << 8);
 
	_rename_what = 2;
 
	ShowQueryString(STR_EMPTY, STR_NETWORK_CHAT_QUERY_CAPTION, 60, 250, 1, 0);
 
}
 

	
 
void ShowNetworkGiveMoneyWindow(byte player)
 
{
 
	_rename_id = player;
 
	_rename_what = 3;
 
	ShowQueryString(STR_EMPTY, STR_NETWORK_GIVE_MONEY_CAPTION, 30, 180, 1, 0);
 
}
 

	
 
void ShowNetworkNeedGamePassword()
 
{
 
	_rename_id = NETWORK_GAME_PASSWORD;
 
	_rename_what = 4;
 
	ShowQueryString(STR_EMPTY, STR_NETWORK_NEED_GAME_PASSWORD_CAPTION, 20, 180, WC_SELECT_GAME, 0);
 
}
 

	
 
void ShowNetworkNeedCompanyPassword()
 
{
 
	_rename_id = NETWORK_COMPANY_PASSWORD;
 
	_rename_what = 4;
 
	ShowQueryString(STR_EMPTY, STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION, 20, 180, WC_SELECT_GAME, 0);
 
}
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 
void ShowRenameSignWindow(SignStruct *ss)
 
{
 
	_rename_id = ss - _sign_list;
 
@@ -286,7 +377,7 @@ void ShowRenameWaypointWindow(Waypoint *
 
	ShowQueryString(STR_WAYPOINT_RAW, STR_EDIT_WAYPOINT_NAME, 30, 180, 1, 0);
 
}
 

	
 
static void CcPlaceSign(bool success, uint tile, uint32 p1, uint32 p2)
 
void CcPlaceSign(bool success, uint tile, uint32 p1, uint32 p2)
 
{
 
	if (success) {
 
		ShowRenameSignWindow(_new_sign_struct);
 
@@ -483,6 +574,12 @@ static void UpdatePlayerMenuHeight(Windo
 
			num++;
 
	}
 

	
 
	// Increase one to fit in PlayerList in the menu when
 
	//  in network
 
	if (_networking && WP(w,menu_d).main_button == 9) {
 
		num++;
 
	}
 

	
 
	if (WP(w,menu_d).item_count != num) {
 
		WP(w,menu_d).item_count = num;
 
		SetWindowDirty(w);
 
@@ -493,6 +590,8 @@ static void UpdatePlayerMenuHeight(Windo
 
	}
 
}
 

	
 
extern void DrawPlayerIcon(int p, int x, int y);
 

	
 
static void PlayerMenuWndProc(Window *w, WindowEvent *e)
 
{
 
	switch(e->event) {
 
@@ -510,12 +609,23 @@ static void PlayerMenuWndProc(Window *w,
 
		sel = WP(w,menu_d).sel_index;
 
		chk = WP(w,menu_d).checked_items; // let this mean gray items.
 

	
 
		// 9 = playerlist
 
		if (_networking && WP(w,menu_d).main_button == 9) {
 
			if (sel == 0) {
 
				GfxFillRect(x, y, x + 238, y + 9, 0);
 
			}
 
			DrawString(x + 19, y, STR_NETWORK_CLIENT_LIST, 0x0);
 
			y += 10;
 
			sel--;
 
		}
 

	
 
		FOR_ALL_PLAYERS(p) {
 
			if (p->is_active) {
 
				if (p->index == sel) {
 
					GfxFillRect(x, y, x + 0xEE, y + 9, 0);
 
					GfxFillRect(x, y, x + 238, y + 9, 0);
 
				}
 
				DrawSprite( ((p->player_color + 0x307)<<16)+0x82EB, x+2, y+1);
 

	
 
				DrawPlayerIcon(p->index, x + 2, y + 1);
 

	
 
				SetDParam(0, p->name_1);
 
				SetDParam(1, p->name_2);
 
@@ -523,12 +633,13 @@ static void PlayerMenuWndProc(Window *w,
 

	
 
				color = (byte)((p->index==sel) ? 0xC : 0x10);
 
				if (chk&1) color = 14;
 
				DrawString(x+0x13, y, STR_7021, color);
 
				DrawString(x + 19, y, STR_7021, color);
 

	
 
				y += 10;
 
			}
 
			chk >>= 1;
 
		}
 

	
 
		break;
 
		}
 

	
 
@@ -540,9 +651,15 @@ static void PlayerMenuWndProc(Window *w,
 
		}
 

	
 
	case WE_POPUPMENU_SELECT: {
 
		int index = GetPlayerIndexFromMenu(GetMenuItemIndex(w, e->popupmenu.pt.x, e->popupmenu.pt.y));
 
		int index = GetMenuItemIndex(w, e->popupmenu.pt.x, e->popupmenu.pt.y);
 
		int action_id = WP(w,menu_d).action_id;
 

	
 
		// We have a new entry at the top of the list of menu 9 when networking
 
		//  so keep that in count
 
		if (!_networking || (WP(w,menu_d).main_button == 9 && index > 0)) {
 
			index = GetPlayerIndexFromMenu(index-1)+1;
 
		}
 

	
 
		if (index < 0) {
 
			Window *w2 = FindWindowById(WC_MAIN_TOOLBAR,0);
 
			if (GetWidgetFromPos(w2, e->popupmenu.pt.x - w2->left, e->popupmenu.pt.y - w2->top) == WP(w,menu_d).main_button)
 
@@ -560,7 +677,15 @@ static void PlayerMenuWndProc(Window *w,
 
	case WE_POPUPMENU_OVER: {
 
		int index;
 
		UpdatePlayerMenuHeight(w);
 
		index = GetPlayerIndexFromMenu(GetMenuItemIndex(w, e->popupmenu.pt.x, e->popupmenu.pt.y));
 
		index = GetMenuItemIndex(w, e->popupmenu.pt.x, e->popupmenu.pt.y);
 

	
 
		// We have a new entry at the top of the list of menu 9 when networking
 
		//  so keep that in count
 
		if (index != -1) {
 
			if (!_networking || (WP(w,menu_d).main_button == 9 && index != 0)) {
 
				index = GetPlayerIndexFromMenu(index-1)+1;
 
			}
 
		}
 

	
 
		if (index == -1 || index == WP(w,menu_d).sel_index)
 
			return;
 
@@ -612,7 +737,10 @@ static Window *PopupMainPlayerToolbMenu(
 
	w = AllocateWindow(x, 0x16, 0xF1, 0x52, PlayerMenuWndProc, WC_TOOLBAR_MENU, _player_menu_widgets);
 
	w->flags4 &= ~WF_WHITE_BORDER_MASK;
 
	WP(w,menu_d).item_count = 0;
 
	WP(w,menu_d).sel_index = _local_player != OWNER_SPECTATOR ? _local_player : 0;
 
	WP(w,menu_d).sel_index = (_local_player != OWNER_SPECTATOR) ? _local_player : 0;
 
	if (_networking && main_button == 9 && _local_player != OWNER_SPECTATOR) {
 
		WP(w,menu_d).sel_index++;
 
	}
 
	WP(w,menu_d).action_id = main_button;
 
	WP(w,menu_d).main_button = main_button;
 
	WP(w,menu_d).checked_items = gray;
 
@@ -988,7 +1116,7 @@ static void AskResetLandscape(uint mode)
 
	AllocateWindowDescFront(&_ask_reset_landscape_desc, mode);
 
}
 

	
 
static void CcTerraform(bool success, uint tile, uint32 p1, uint32 p2)
 
void CcTerraform(bool success, uint tile, uint32 p1, uint32 p2)
 
{
 
	if (success) {
 
		SndPlayTileFx(SND_1F_SPLAT, tile);
 
@@ -1000,7 +1128,7 @@ static void CcTerraform(bool success, ui
 
static void CommonRaiseLowerBigLand(uint tile, int mode)
 
{
 
	int size;
 
	uint h;
 
	byte h;
 

	
 
	_error_message_2 = mode ? STR_0808_CAN_T_RAISE_LAND_HERE : STR_0809_CAN_T_LOWER_LAND_HERE;
 

	
 
@@ -1049,7 +1177,7 @@ static void PlaceProc_LowerBigLand(uint 
 
	CommonRaiseLowerBigLand(tile, 0);
 
}
 

	
 
//static void CcDemolish(bool success, uint tile, uint32 p1, uint32 p2)
 
//void CcDemolish(bool success, uint tile, uint32 p1, uint32 p2)
 
//{
 
//	if (success) {
 
		//SndPlayTileFx(0x10, tile);
 
@@ -1257,7 +1385,7 @@ static void ToolbarScenGenLand(Window *w
 
	AllocateWindowDescFront(&_scen_edit_land_gen_desc, 0);
 
}
 

	
 
static void CcBuildTown(bool success, uint tile, uint32 p1, uint32 p2)
 
void CcBuildTown(bool success, uint tile, uint32 p1, uint32 p2)
 
{
 
	if (success) {
 
		SndPlayTileFx(SND_1F_SPLAT, tile);
 
@@ -1713,7 +1841,7 @@ static void MainToolbarWndProc(Window *w
 
		case WKC_CTRL  | 'S': _make_screenshot = 1; break;
 
		case WKC_CTRL  | 'G': _make_screenshot = 2; break;
 
		case WKC_BACKQUOTE: IConsoleSwitch(); e->keypress.cont=false; break;
 
		case WKC_CTRL | WKC_ALT | 'C': if(!_networking) ShowCheatWindow(); break;
 
		case WKC_CTRL | WKC_ALT | 'C': if (!_networking) ShowCheatWindow(); break;
 
		}
 
	} break;
 

	
 
@@ -2201,6 +2329,12 @@ static void MainWindowWndProc(Window *w,
 
			MarkWholeScreenDirty();
 
			break;
 

	
 
#ifdef ENABLE_NETWORK
 
		case 'T' | WKC_SHIFT:
 
			ShowNetworkChatQueryWindow(DESTTYPE_BROADCAST, 0);
 
			break;
 
#endif /* ENABLE_NETWORK */
 

	
 
		default:
 
			return;
 
		}
 
@@ -2247,7 +2381,7 @@ void SetupColorsAndInitialWindow()
 

	
 
		if (_networking) { // if networking, disable fast-forward button
 
			w->disabled_state |= (1 << 1);
 
			if (!_networking_server) // if not server, disable pause button
 
			if (!_network_server) // if not server, disable pause button
 
				w->disabled_state |= (1 << 0);
 
		}
 

	
makefiledir/Makefile.libdetection
Show inline comments
 
# this file detects what OS and libs the computer have/are running
 
 
# Automatically recognize if building on Win32
 
ifdef WINDIR
 
ifndef UNIX
 
WIN32:=1
 
CYGWIN:=1
 
MINGW:=1
 
STATIC:=1
 
SKIP_STATIC_CHECK:=1
 
endif
 
else
 
UNIX:=1
 
endif
 
 
# Automatically recognize if building on FreeBSD
 
ifeq ($(shell uname),FreeBSD)
 
FREEBSD:=1
 
endif
 
 
# Automatically recognize if building on MacOSX
 
ifeq ($(VENDOR), apple)
 
OSX:=1
 
# OSX uses the unix setup too
 
UNIX:=1
 
endif
 
 
# Automatically recognize if building on MorphOS
 
ifeq ($(shell uname), MorphOS)
 
MORPHOS:=1
 
# MorphOS uses UNIX setup too
 
UNIX:=1
 
endif
 
 
# Automatically recognize if building on BeOS
 
ifeq ($(shell uname), BeOS)
 
BEOS:=1
 
# BeOS uses UNIX setup too
 
UNIX:=1
 
# Except that in BeOS 5.0 we need to use net_server, not BONE networking
 
ifeq ($(shell uname -r), 5.0)
 
BEOS_NET_SERVER:=1
 
endif
 
endif
 
 
# Automatically recognize if building on SunOS/Solaris
 
ifeq ($(shell uname), SunOS)
 
SUNOS:=1
 
# SunOS uses UNIX setup too
 
UNIX:=1
 
endif
 
 
# FreeBSD uses sdl11 instead of sdl
 
ifdef FREEBSD
 
SDL-CONFIG:=sdl11-config
 
else
 
SDL-CONFIG:=sdl-config
 
endif
 
 
 
# Library detections
 
WITH_SDL:=$(shell $(SDL-CONFIG) --version 2>/dev/null)
 
 
# libpng detection
 
ifdef FREEBSD
 
# a little hack was needed for FreeBSD because it misses libpng-config
 
WITH_PNG:=$(shell ls /usr/lib | grep "libpng" 2>/dev/null) $(shell \
 
ls /usr/local/lib | grep "libpng" 2>/dev/null)
 
ifdef WITH_PNG
 
# makes the flag look nicer in makefile.config
 
WITH_PNG:=1
 
endif
 
else
 
WITH_PNG:=$(shell libpng-config --version 2>/dev/null)
 
endif
 
 
ifdef WITH_PNG
 
# LibPNG depends on Zlib
 
WITH_ZLIB:=1
 
else
 
# We go looking for zlib with a little hack
 
WITH_ZLIB:=$(shell ls /usr/include | grep "zlib.h" 2>/dev/null) \
 
$(shell ls /usr/local/include | grep "zlib.h" 2>/dev/null)
 
ifdef WITH_ZLIB
 
WITH_ZLIB:=1
 
endif
 
endif
 
 
 
# sets the default paths
 
ifdef UNIX
 
ifndef OSX
 
ifndef MORPHOS
 
ifndef BIN_DIR
 
#BINARY_DIR:=
 
#DATA_DIR_PREFIX:=
 
#INSTALL_DIR:=/usr/local/
 
#USE_HOMEDIR:=
 
endif
 
endif
 
endif
 
endif
 
 
# workaround
 
# cygwin have problems with libpng, so we will just disable it for now until the problem is solved
 
ifdef CYGWIN
 
WITH_PNG:=
 
endif
 
\ No newline at end of file
 
# this file detects what OS and libs the computer have/are running
 

	
 
# Automatically recognize if building on Win32
 
ifdef WINDIR
 
ifndef UNIX
 
WIN32:=1
 
CYGWIN:=1
 
MINGW:=1
 
STATIC:=1
 
SKIP_STATIC_CHECK:=1
 
endif
 
else
 
UNIX:=1
 
endif
 

	
 
# Automatically recognize if building on FreeBSD
 
ifeq ($(shell uname),FreeBSD)
 
FREEBSD:=1
 
endif
 

	
 
# Automatically recognize if building on MacOSX
 
ifeq ($(VENDOR), apple)
 
OSX:=1
 
# OSX uses the unix setup too
 
UNIX:=1
 
endif
 

	
 
# Automatically recognize if building on MorphOS
 
ifeq ($(shell uname), MorphOS)
 
MORPHOS:=1
 
# MorphOS uses UNIX setup too
 
UNIX:=1
 
endif
 

	
 
# Automatically recognize if building on BeOS
 
ifeq ($(shell uname), BeOS)
 
BEOS:=1
 
# BeOS uses UNIX setup too
 
UNIX:=1
 
# Except that in BeOS 5.0 we need to use net_server, not BONE networking
 
ifeq ($(shell uname -r), 5.0)
 
BEOS_NET_SERVER:=1
 
endif
 
endif
 

	
 
# Automatically recognize if building on SunOS/Solaris
 
ifeq ($(shell uname), SunOS)
 
SUNOS:=1
 
# SunOS uses UNIX setup too
 
UNIX:=1
 
endif
 

	
 
# FreeBSD uses sdl11 instead of sdl
 
ifdef FREEBSD
 
SDL-CONFIG:=sdl11-config
 
else
 
SDL-CONFIG:=sdl-config
 
endif
 

	
 
# Networking, enabled by default
 
WITH_NETWORK:=1
 

	
 
# Library detections
 
WITH_SDL:=$(shell $(SDL-CONFIG) --version 2>/dev/null)
 

	
 
# libpng detection
 
ifdef FREEBSD
 
# a little hack was needed for FreeBSD because it misses libpng-config
 
WITH_PNG:=$(shell ls /usr/lib | grep "libpng" 2>/dev/null) $(shell \
 
ls /usr/local/lib | grep "libpng" 2>/dev/null)
 
ifdef WITH_PNG
 
# makes the flag look nicer in makefile.config
 
WITH_PNG:=1
 
endif
 
else
 
WITH_PNG:=$(shell libpng-config --version 2>/dev/null)
 
endif
 

	
 
ifdef WITH_PNG
 
# LibPNG depends on Zlib
 
WITH_ZLIB:=1
 
else
 
# We go looking for zlib with a little hack
 
WITH_ZLIB:=$(shell ls /usr/include | grep "zlib.h" 2>/dev/null) \
 
$(shell ls /usr/local/include | grep "zlib.h" 2>/dev/null)
 
ifdef WITH_ZLIB
 
WITH_ZLIB:=1
 
endif
 
endif
 

	
 

	
 
# sets the default paths
 
ifdef UNIX
 
ifndef OSX
 
ifndef MORPHOS
 
ifndef BIN_DIR
 
#BINARY_DIR:=
 
#DATA_DIR_PREFIX:=
 
#INSTALL_DIR:=/usr/local/
 
#USE_HOMEDIR:=
 
endif
 
endif
 
endif
 
endif
 

	
 
# workaround
 
# cygwin have problems with libpng, so we will just disable it for now until the problem is solved
 
ifdef CYGWIN
 
WITH_PNG:=
 
endif
misc.c
Show inline comments
 
@@ -5,6 +5,7 @@
 
#include "gfx.h"
 
#include "assert.h"
 
#include "saveload.h"
 
#include "network.h"
 

	
 
extern void StartupEconomy();
 
extern void InitNewsItemStructs();
 
@@ -16,21 +17,24 @@ static inline uint32 ROR(uint32 x, int n
 
	return (x >> n) + (x << ((sizeof(x)*8)-n));
 
}
 

	
 
// For multiplayer, we introduced this new way of random-seeds
 
//  It is player-based, so 2 clients can do 2 commands at the same time
 
//  without the game desyncing.
 
// It is not used for non-multiplayer games
 
#ifdef ENABLE_NETWORK
 
	#define PLAYER_SEED_RANDOM
 
/* XXX - Player-seeds don't seem to be used anymore.. which is a good thing
 
     so I just disabled them for now. If there are no problems, we can remove
 
     it completely! -- TrueLight */
 
#undef PLAYER_SEED_RANDOM
 

	
 
#ifdef RANDOM_DEBUG
 
#include "network_data.h"
 

	
 
uint32 DoRandom(uint line, char *file)
 
#else
 
	#undef PLAYER_SEED_RANDOM
 
uint32 Random()
 
#endif
 
{
 
#ifdef RANDOM_DEBUG
 
	if (_networking && (DEREF_CLIENT(0)->status != STATUS_INACTIVE || !_network_server))
 
		printf("Random [%d/%d] %s:%d\n",_frame_counter, _current_player, file, line);
 
#endif
 

	
 
// its for now not used at all because it is still desyncing :(
 
#undef PLAYER_SEED_RANDOM
 

	
 
uint32 Random()
 
{
 
#ifdef PLAYER_SEED_RANDOM
 
	if (_current_player>=MAX_PLAYERS || !_networking) {
 
		uint32 s = _random_seeds[0][0];
 
@@ -41,6 +45,7 @@ uint32 Random()
 
		uint32 s = _player_seeds[_current_player][0];
 
		uint32 t = _player_seeds[_current_player][1];
 
		_player_seeds[_current_player][0] = s + ROR(t ^ 0x1234567F, 7);
 
		DEBUG(net, 1)("[NET-Seeds] Player seed called!");
 
		return _player_seeds[_current_player][1] = ROR(s, 3);
 
	}
 
#else
 
@@ -51,10 +56,17 @@ uint32 Random()
 
#endif
 
}
 

	
 
#ifdef RANDOM_DEBUG
 
uint DoRandomRange(uint max, uint line, char *file)
 
{
 
	return (uint16)DoRandom(line, file) * max >> 16;
 
}
 
#else
 
uint RandomRange(uint max)
 
{
 
	return (uint16)Random() * max >> 16;
 
}
 
#endif
 

	
 
uint32 InteractiveRandom()
 
{
 
@@ -75,7 +87,7 @@ void InitPlayerRandoms()
 
	for (i=0; i<MAX_PLAYERS; i++) {
 
		_player_seeds[i][0]=InteractiveRandom();
 
		_player_seeds[i][1]=InteractiveRandom();
 
		}
 
	}
 
}
 

	
 
void SetDate(uint date)
 
@@ -86,6 +98,51 @@ void SetDate(uint date)
 
	_cur_month = ymd.month;
 
}
 

	
 

	
 
// multi os compatible sleep function
 

	
 
#if defined(__AMIGA__)
 
// usleep() implementation
 
#	include <devices/timer.h>
 
#	include <dos/dos.h>
 

	
 
	static struct Device      *TimerBase    = NULL;
 
	static struct MsgPort     *TimerPort    = NULL;
 
	static struct timerequest *TimerRequest = NULL;
 
#endif // __AMIGA__
 

	
 
void CSleep(int milliseconds)
 
{
 
	#if defined(WIN32)
 
		Sleep(milliseconds);
 
	#endif
 
	#if defined(UNIX)
 
		#if !defined(__BEOS__) && !defined(__AMIGAOS__)
 
			usleep(milliseconds * 1000);
 
		#endif
 
		#ifdef __BEOS__
 
			snooze(milliseconds * 1000);
 
		#endif
 
		#if defined(__AMIGAOS__) && !defined(__MORPHOS__)
 
		{
 
			ULONG signals;
 
			ULONG TimerSigBit = 1 << TimerPort->mp_SigBit;
 

	
 
			// send IORequest
 
			TimerRequest->tr_node.io_Command = TR_ADDREQUEST;
 
			TimerRequest->tr_time.tv_secs    = (milliseconds * 1000) / 1000000;
 
			TimerRequest->tr_time.tv_micro   = (milliseconds * 1000) % 1000000;
 
			SendIO((struct IORequest *)TimerRequest);
 

	
 
			if (!((signals = Wait(TimerSigBit | SIGBREAKF_CTRL_C)) & TimerSigBit) ) {
 
				AbortIO((struct IORequest *)TimerRequest);
 
			}
 
			WaitIO((struct IORequest *)TimerRequest);
 
		}
 
		#endif // __AMIGAOS__ && !__MORPHOS__
 
	#endif
 
}
 

	
 
void InitializeClearLand();
 
void InitializeRail();
 
void InitializeRailGui();
 
@@ -160,6 +217,7 @@ void InitializeGame()
 
	InitializeCheats();
 

	
 
	InitTextEffects();
 
	InitTextMessage();
 
	InitializeAnimatedTiles();
 

	
 
	InitializeLandscapeVariables(false);
 
@@ -171,6 +229,9 @@ void GenerateWorld(int mode)
 
{
 
	int i;
 

	
 
	// Make sure everything is done via OWNER_NONE
 
	_current_player = OWNER_NONE;
 

	
 
	_generating_world = true;
 
	InitializeGame();
 
	SetObjectToPlace(1, 0, 0, 0);
 
@@ -252,7 +313,7 @@ static void InitializeNameMgr()
 
	memset(_name_array, 0, sizeof(_name_array));
 
}
 

	
 
StringID AllocateName(const byte *name, byte skip)
 
StringID RealAllocateName(const byte *name, byte skip, bool check_double)
 
{
 
	int free_item = -1;
 
	const byte *names;
 
@@ -266,7 +327,7 @@ StringID AllocateName(const byte *name, 
 
			if (free_item == -1)
 
				free_item = i;
 
		} else {
 
			if (str_eq(names, name)) {
 
			if (check_double && str_eq(names, name)) {
 
				_error_message = STR_0132_CHOSEN_NAME_IN_USE_ALREADY;
 
				return 0;
 
			}
 
@@ -575,9 +636,9 @@ void IncreaseDate()
 
	/* yeah, increse day counter and call various daily loops */
 
	_date++;
 

	
 
	NetworkGameChangeDate(_date);
 
	_vehicle_id_ctr_day = 0;
 

	
 
	_vehicle_id_ctr_day = 0;
 
	TextMessageDailyLoop();
 

	
 
	DisasterDailyLoop();
 
	WaypointsDailyLoop();
 
@@ -593,8 +654,6 @@ void IncreaseDate()
 
		return;
 
	_cur_month = ymd.month;
 

	
 
//	printf("Month %d, %X\n", ymd.month, _random_seeds[0][0]);
 

	
 
	/* yes, call various monthly loops */
 
	if (_game_mode != GM_MENU) {
 
		if (HASBIT(_autosave_months[_opt.autosave], _cur_month)) {
misc_cmd.c
Show inline comments
 
@@ -8,6 +8,7 @@
 
#include "window.h"
 
#include "saveload.h"
 
#include "economy.h"
 
#include "network.h"
 

	
 
/* p1 = player
 
   p2 = face
 
@@ -124,7 +125,7 @@ int32 CmdChangeCompanyName(int x, int y,
 
	StringID str,old_str;
 
	Player *p;
 

	
 
	str = AllocateName((byte*)_decode_parameters, 4);
 
	str = AllocateNameUnique((byte*)_decode_parameters, 4);
 
	if (str == 0)
 
		return CMD_ERROR;
 

	
 
@@ -146,7 +147,7 @@ int32 CmdChangePresidentName(int x, int 
 
	StringID str,old_str;
 
	Player *p;
 

	
 
	str = AllocateName((byte*)_decode_parameters, 4);
 
	str = AllocateNameUnique((byte*)_decode_parameters, 4);
 
	if (str == 0)
 
		return CMD_ERROR;
 

	
 
@@ -228,7 +229,7 @@ int32 CmdRenameSign(int x, int y, uint32
 
	SignStruct *ss;
 

	
 
	if (_decode_parameters[0] != 0 && !p2) {
 
		str = AllocateName((byte*)_decode_parameters, 0);
 
		str = AllocateNameUnique((byte*)_decode_parameters, 0);
 
		if (str == 0)
 
			return CMD_ERROR;
 

	
 
@@ -280,6 +281,22 @@ int32 CmdMoneyCheat(int x, int y, uint32
 
	return (int32)p1;
 
}
 

	
 
int32 CmdGiveMoney(int x, int y, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	SET_EXPENSES_TYPE(EXPENSES_OTHER);
 

	
 
	if (flags & DC_EXEC) {
 
		// Add money to player
 
		byte old_cp = _current_player;
 
		_current_player = p2;
 
		SubtractMoneyFromPlayer(-(int32)p1);
 
		_current_player = old_cp;
 
	}
 

	
 
	// Subtract money from local-player
 
	return (int32)p1;
 
}
 

	
 
int32 CmdChangeDifficultyLevel(int x, int y, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	if (flags & DC_EXEC) {
 
@@ -289,6 +306,9 @@ int32 CmdChangeDifficultyLevel(int x, in
 
		} else {
 
			_opt_mod_ptr->diff_level = p2;
 
		}
 
		// If we are a network-client, update the difficult setting (if it is open)
 
		if (_networking && !_network_server && FindWindowById(WC_GAME_OPTIONS, 0) != NULL)
 
			memcpy(&_opt_mod_temp, _opt_mod_ptr, sizeof(GameOptions));
 
		InvalidateWindow(WC_GAME_OPTIONS, 0);
 
	}
 
	return 0;
misc_gui.c
Show inline comments
 
@@ -10,11 +10,12 @@
 
#include "player.h"
 
#include "town.h"
 
#include "sound.h"
 
#include "network.h"
 

	
 
#include "hal.h" // Fios items
 
#include "hal.h" // for file list
 

	
 
bool _query_string_active;
 
static void SetFiosType(const byte fiostype);
 
void SetFiosType(const byte fiostype);
 

	
 
typedef struct LandInfoData {
 
	Town *town;
 
@@ -24,7 +25,6 @@ typedef struct LandInfoData {
 
	TileDesc td;
 
} LandInfoData;
 

	
 

	
 
static void LandInfoWndProc(Window *w, WindowEvent *e)
 
{
 
	LandInfoData *lid;
 
@@ -764,6 +764,7 @@ void DrawEditBox(Window *w, int wid)
 

	
 
static void QueryStringWndProc(Window *w, WindowEvent *e)
 
{
 
	static bool closed = false;
 
	switch(e->event) {
 
	case WE_PAINT: {
 
//		int x;
 
@@ -779,7 +780,7 @@ static void QueryStringWndProc(Window *w
 
		case 3: DeleteWindow(w); break;
 
		case 4:
 
press_ok:;
 
			if (str_eq(WP(w,querystr_d).buf, WP(w,querystr_d).buf + MAX_QUERYSTR_LEN)) {
 
			if (str_eq(WP(w,querystr_d).buf, WP(w,querystr_d).buf + MAX_QUERYSTR_LEN) && (WP(w,querystr_d).maxlen & 0x1000) == 0) {
 
				DeleteWindow(w);
 
			} else {
 
				byte *buf = WP(w,querystr_d).buf;
 
@@ -788,6 +789,10 @@ press_ok:;
 
				Window *parent;
 

	
 
				DeleteWindow(w);
 

	
 
				// Mask the edit-box as closed, so we don't send out a CANCEL
 
				closed = true;
 

	
 
				parent = FindWindowById(wnd_class, wnd_num);
 
				if (parent != NULL) {
 
					WindowEvent e;
 
@@ -818,7 +823,20 @@ press_ok:;
 
		}
 
	} break;
 

	
 
	case WE_CREATE:
 
		closed = false;
 
		break;
 

	
 
	case WE_DESTROY:
 
		// If the window is not closed yet, it means it still needs to send a CANCEL
 
		if (!closed) {
 
			Window *parent = FindWindowById(WP(w,querystr_d).wnd_class, WP(w,querystr_d).wnd_num);
 
			if (parent != NULL) {
 
				WindowEvent e;
 
				e.event = WE_ON_EDIT_TEXT_CANCEL;
 
				parent->wndproc(parent, &e);
 
			}
 
		}
 
		_query_string_active = false;
 
		break;
 
	}
 
@@ -933,11 +951,7 @@ static const Widget _save_dialog_scen_wi
 
};
 

	
 

	
 
static FiosItem *_fios_list;
 
static int _fios_num;
 
static int _saveload_mode;
 

	
 
static void BuildFileList()
 
void BuildFileList()
 
{
 
	FiosFreeSavegameList();
 
	if(_saveload_mode==SLD_NEW_GAME || _saveload_mode==SLD_LOAD_SCENARIO || _saveload_mode==SLD_SAVE_SCENARIO)
 
@@ -957,9 +971,6 @@ static void DrawFiosTexts()
 
	DoDrawString(path, 2, 27, 16);
 
}
 

	
 
/*	FIOS_TYPE_FILE, FIOS_TYPE_OLDFILE etc. different colours */
 
static const byte _fios_colors[] = {13, 9, 9, 6, 5, 6, 5};
 

	
 
#if defined(_WIN32)
 
	extern int CDECL compare_FiosItems (const void *a, const void *b);
 
#else
 
@@ -1185,7 +1196,7 @@ void ShowSaveLoadDialog(int mode)
 
	}
 

	
 
	// pause is only used in single-player, non-editor mode
 
	if(_game_mode != GM_MENU && !_networking && (_game_mode != GM_EDITOR))
 
	if(_game_mode != GM_MENU && !_networking && _game_mode != GM_EDITOR)
 
		DoCommandP(0, 1, 0, NULL, CMD_PAUSE);
 

	
 
	BuildFileList();
 
@@ -1282,7 +1293,7 @@ static void SelectScenarioWndProc(Window
 
	}
 
}
 

	
 
static void SetFiosType(const byte fiostype)
 
void SetFiosType(const byte fiostype)
 
{
 
	switch (fiostype) {
 
	case FIOS_TYPE_FILE: case FIOS_TYPE_SCENARIO:
network.c
Show inline comments
 
#include "stdafx.h"
 
#include "ttd.h"
 
#include "network_data.h"
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "table/strings.h"
 
#include "gui.h"
 
#include "command.h"
 
#include "player.h"
 
#include "console.h"
 
#include "economy.h"
 

	
 
#if defined(WIN32)
 
#	include <windows.h>
 
#	include <winsock.h>
 
#include "network_client.h"
 
#include "network_server.h"
 
#include "network_udp.h"
 
#include "network_gamelist.h"
 
#include "console.h" /* IConsoleCmdExec */
 
#include <stdarg.h> /* va_list */
 

	
 
# pragma comment (lib, "ws2_32.lib")
 
# define ENABLE_NETWORK
 
# define GET_LAST_ERROR() WSAGetLastError()
 
# define EWOULDBLOCK WSAEWOULDBLOCK
 
#endif
 
// The listen socket for the server
 
static SOCKET _listensocket;
 

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

	
 
#if defined(UNIX)
 
// Make compatible with WIN32 names
 
#	define SOCKET int
 
#	define INVALID_SOCKET -1
 
// we need different defines for MorphOS and AmigaOS
 
#if !defined(__MORPHOS__) && !defined(__AMIGA__)
 
#	define ioctlsocket ioctl
 
# define closesocket close
 
# define GET_LAST_ERROR() errno
 
#endif
 
// Need this for FIONREAD on solaris
 
#	define BSD_COMP
 
#	include <unistd.h>
 
#	include <sys/ioctl.h>
 
// 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;
 

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

	
 
// Socket stuff
 
#	include <sys/socket.h>
 
#	include <netinet/in.h>
 
#	include <arpa/inet.h>
 
# 	include <errno.h>
 
# 	include <sys/time.h>
 
// NetDB
 
#   include <netdb.h>
 
	for (ci = _network_client_info; ci != &_network_client_info[MAX_CLIENT_INFO]; ci++)
 
		if (ci->client_index == client_index)
 
			return ci;
 

	
 
# ifndef TCP_NODELAY
 
#  define TCP_NODELAY 0x0001
 
# endif
 
	return NULL;
 
}
 

	
 
#endif
 

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

	
 
#if defined(__MORPHOS__) || defined(__AMIGA__)
 
#	include <exec/types.h>
 
#	include <proto/exec.h> 		// required for Open/CloseLibrary()
 
#	if defined(__MORPHOS__)
 
#		include <sys/filio.h> 	// FION#? defines
 
#	else // __AMIGA__
 
#		include	<proto/socket.h>
 
#	endif
 
	for (cs = _clients; cs != &_clients[MAX_CLIENT_INFO]; cs++)
 
		if (cs->index == client_index)
 
			return cs;
 

	
 
// make source compatible with bsdsocket.library functions
 
# define closesocket(s)     						CloseSocket(s)
 
# define GET_LAST_ERROR() 							Errno()
 
#	define ioctlsocket(s,request,status)  IoctlSocket((LONG)s,(ULONG)request,(char*)status)
 

	
 
struct Library *SocketBase = NULL;
 
	return NULL;
 
}
 

	
 
#if !defined(__MORPHOS__)
 
// usleep() implementation
 
#include <devices/timer.h>
 
#include <dos/dos.h>
 

	
 
struct Device       *TimerBase    = NULL;
 
struct MsgPort      *TimerPort    = NULL;
 
struct timerequest  *TimerRequest = NULL;
 
#endif
 

	
 
#endif /* __MORPHOS__ || __AMIGA__ */
 

	
 

	
 
#define SEND_MTU 1460
 

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

	
 
enum {
 
	PACKET_TYPE_WELCOME = 0,
 
	PACKET_TYPE_READY,
 
	PACKET_TYPE_ACK,
 
	PACKET_TYPE_SYNC,
 
	PACKET_TYPE_FSYNC,
 
	PACKET_TYPE_XMIT,
 
	PACKET_TYPE_COMMAND,
 
	PACKET_TYPE_EVENT,
 
};
 

	
 
// sent from client -> server whenever the client wants to exec a command.
 
// send from server -> client when another player execs a command.
 
typedef struct CommandPacket {
 
	byte packet_length;
 
	byte packet_type;
 
	uint16 cmd;
 
	uint32 p1,p2;
 
	TileIndex tile;
 
	byte player;// player id, this is checked by the server.
 
	byte when;  // offset from the current max_frame value minus 1. this is set by the server.
 
	uint32 dp[8];
 
} CommandPacket;
 
// This puts a text-message to the console, or in the future, the chat-box,
 
//  (to keep it all a bit more general)
 
void NetworkTextMessage(NetworkAction action, uint16 color, const char *name, const char *str, ...)
 
{
 
	char buf[1024];
 
	va_list va;
 
	const int duration = 10; // Game days the messages stay visible
 

	
 
typedef struct EventPacket {
 
	byte packet_length;
 
	byte packet_type;
 
	byte event_type;
 
	byte data_start;
 
} EventPacket;
 

	
 
#define COMMAND_PACKET_BASE_SIZE (sizeof(CommandPacket) - 8 * sizeof(uint32))
 

	
 
// sent from server -> client periodically to tell the client about the current tick in the server
 
// and how far the client may progress.
 
typedef struct SyncPacket {
 
	byte packet_length;
 
	byte packet_type;
 
	byte frames; // how many more frames may the client execute? this is relative to the old value of max.
 
	byte server; // where is the server currently executing? this is negatively relative to the old value of max.
 
	uint32 random_seed_1; // current random state at server. used to detect out of sync.
 
	uint32 random_seed_2;
 
} SyncPacket;
 

	
 
typedef struct FrameSyncPacket {
 
	byte packet_length;
 
	byte packet_type;
 
	byte frames; // where is the server currently executing? this is negatively relative to the old value of max.
 
} FrameSyncPacket;
 
	va_start(va, str);
 
	vsprintf(buf, str, va);
 
	va_end(va);
 

	
 
// sent from server -> client as an acknowledgement that the server received the command.
 
// the command will be executed at the current value of "max".
 
typedef struct AckPacket {
 
	byte packet_length;
 
	byte packet_type;
 
	int16 when;
 
} AckPacket;
 

	
 
typedef struct ReadyPacket {
 
	byte packet_length;
 
	byte packet_type;
 
} ReadyPacket;
 

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

	
 
// sent from server to client when the client has joined.
 
typedef struct WelcomePacket {
 
	byte packet_length;
 
	byte packet_type;
 
	uint32 player_seeds[MAX_PLAYERS][2];
 
	uint32 frames_max;
 
	uint32 frames_srv;
 
	uint32 frames_cnt;
 
} WelcomePacket;
 
// Calculate the frame-lag of a client
 
uint NetworkCalculateLag(const ClientState *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);
 

	
 
typedef struct Packet Packet;
 
struct Packet {
 
	Packet *next; // this one has to be the first element.
 
	uint siz;
 
	byte buf[SEND_MTU]; // packet payload
 
};
 

	
 
typedef struct ClientState {
 
	int socket;
 
	bool inactive; // disable sending of commands/syncs to client
 
	bool writable;
 
	bool ready;
 
	uint timeout;
 
	uint xmitpos;
 

	
 
	uint eaten;
 
	Packet *head, **last;
 

	
 
	uint buflen;											// receive buffer len
 
	byte buf[1024];										// receive buffer
 
} ClientState;
 
	return lag;
 
}
 

	
 

	
 
typedef struct QueuedCommand QueuedCommand;
 
struct QueuedCommand {
 
	QueuedCommand *next;
 
	CommandPacket cp;
 
	CommandCallback *callback;
 
	uint32 cmd;
 
	uint32 frame;
 
};
 

	
 
typedef struct CommandQueue CommandQueue;
 
struct CommandQueue {
 
	QueuedCommand *head, **last;
 
};
 

	
 
#define MAX_CLIENTS (MAX_PLAYERS + 1)
 

	
 
// packets waiting to be executed, for each of the players.
 
// this list is sorted in frame order, so the item on the front will be executed first.
 
static CommandQueue _command_queue;
 

	
 
// in the client, this is the list of commands that have not yet been acked.
 
// when it is acked, it will be moved to the appropriate position at the end of the player queue.
 
static CommandQueue _ack_queue;
 
// There was a non-recoverable error, drop back to the main menu with a nice
 
//  error
 
void NetworkError(StringID error_string)
 
{
 
	_switch_mode = SM_MENU;
 
	_switch_mode_errorstr = error_string;
 
}
 

	
 
static ClientState _clients[MAX_CLIENTS];
 
static int _num_clients;
 

	
 
// keep a history of the 16 most recent seeds to be able to capture out of sync errors.
 
static uint32 _my_seed_list[16][2];
 
static bool _network_ready_sent;
 
static uint32 _frame_fsync_last;
 

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

	
 
// remember some future seeds that the server sent to us.
 
static FutureSeeds _future_seed[8];
 
static uint _num_future_seed;
 

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

	
 
static SOCKET _udp_client_socket; // udp server socket
 
static SOCKET _udp_server_socket; // udp client socket
 

	
 
typedef struct UDPPacket {
 
	byte command_code;
 
	byte data_len;
 
	byte command_check;
 
	byte data[255];
 
} UDPPacket;
 
void NetworkClientError(byte res, ClientState *cs) {
 
	// First, send a CLIENT_ERROR to the server, so he knows we are
 
	//  disconnection (and why!)
 
	NetworkErrorCode errorno;
 

	
 
enum {
 
	NET_UDPCMD_SERVERSEARCH = 1,
 
	NET_UDPCMD_SERVERACTIVE,
 
	NET_UDPCMD_GETSERVERINFO,
 
	NET_UDPCMD_SERVERINFO,
 
};
 
	// We just want to close the connection..
 
	if (res == NETWORK_RECV_STATUS_CLOSE_QUERY) {
 
		cs->quited = true;
 
		CloseClient(cs);
 
		_networking = false;
 

	
 
void NetworkUDPSend(bool client, struct sockaddr_in recv,struct UDPPacket packet);
 
static void HandleCommandPacket(ClientState *cs, CommandPacket *np);
 
static void CloseClient(ClientState *cs);
 
void NetworkSendWelcome(ClientState *cs, bool direct);
 

	
 
uint32 _network_ip_list[10]; // network ip list
 

	
 
// this is set to point to the savegame
 
static byte *_transmit_file;
 
static size_t _transmit_file_size;
 

	
 
static FILE *_recv_file;
 
		DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 
		return;
 
	}
 

	
 
/* multi os compatible sleep function */
 
void CSleep(int milliseconds) {
 
#if defined(WIN32)
 
Sleep(milliseconds);
 
#endif
 
#if defined(UNIX)
 
#if !defined(__BEOS__) && !defined(__MORPHOS__) && !defined(__AMIGAOS__)
 
usleep(milliseconds*1000);
 
#endif
 
#ifdef __BEOS__
 
snooze(milliseconds*1000);
 
#endif
 
#if defined(__MORPHOS__)
 
usleep(milliseconds*1000);
 
#endif
 
#if defined(__AMIGAOS__) && !defined(__MORPHOS__)
 
{
 
	ULONG signals;
 
	ULONG TimerSigBit = 1 << TimerPort->mp_SigBit;
 
	switch(res) {
 
		case NETWORK_RECV_STATUS_DESYNC: errorno = NETWORK_ERROR_DESYNC; break;
 
		case NETWORK_RECV_STATUS_SAVEGAME: errorno = NETWORK_ERROR_SAVEGAME_FAILED; break;
 
		default: errorno = NETWORK_ERROR_GENERAL;
 
	}
 
	// This means we fucked up and the server closed the connection
 
	if (res != NETWORK_RECV_STATUS_SERVER_ERROR && res != NETWORK_RECV_STATUS_SERVER_FULL) {
 
		SEND_COMMAND(PACKET_CLIENT_ERROR)(errorno);
 

	
 
	// send IORequest
 
	TimerRequest->tr_node.io_Command = TR_ADDREQUEST;
 
	TimerRequest->tr_time.tv_secs    = (milliseconds * 1000) / 1000000;
 
	TimerRequest->tr_time.tv_micro   = (milliseconds * 1000) % 1000000;
 
	SendIO((struct IORequest *)TimerRequest);
 
		// Dequeue all commands before closing the socket
 
		NetworkSend_Packets(DEREF_CLIENT(0));
 
	}
 

	
 
	if ( !((signals = Wait(TimerSigBit|SIGBREAKF_CTRL_C)) & TimerSigBit) ) {
 
		AbortIO((struct IORequest *)TimerRequest);
 
	}
 
	WaitIO((struct IORequest *)TimerRequest);
 
}
 
#endif // __AMIGAOS__ && !__MORPHOS__
 
#endif
 
	_switch_mode = SM_MENU;
 
	CloseClient(cs);
 
	_networking = false;
 
}
 

	
 
//////////////////////////////////////////////////////////////////////
 

	
 
// ****************************** //
 
// * Network Error Handlers     * //
 
// ****************************** //
 

	
 
static void NetworkHandleSaveGameError()
 
{
 
		_networking_sync = false;
 
		_networking_queuing = true;
 
		_switch_mode = SM_MENU;
 
		_switch_mode_errorstr = STR_NETWORK_ERR_SAVEGAMEERROR;
 
}
 

	
 
static void NetworkHandleConnectionLost()
 
{
 
		_networking_sync = false;
 
		_networking_queuing = true;
 
		_switch_mode = SM_MENU;
 
		_switch_mode_errorstr = STR_NETWORK_ERR_LOSTCONNECTION;
 
}
 

	
 
static void NetworkHandleDeSync()
 
// Find all IP-aliases for this host
 
void NetworkFindIPs(void)
 
{
 
	DEBUG(net, 0) ("NET: error: network sync error at frame %i", _frame_counter);
 
	{
 
		int i;
 
		for (i=15; i>=0; i--) DEBUG(net,0) ("NET frame %i: [0]=%i, [1]=%i",_frame_counter-(i+1),_my_seed_list[i][0],_my_seed_list[i][1]);
 
		for (i=0; i<8; i++) DEBUG(net,0) ("NET frame %i: [0]=%i, [1]=%i",_frame_counter+i,_future_seed[i].seed[0],_future_seed[i].seed[1]);
 
	int i, last;
 

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

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

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

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

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

	
 
	output_length = _netstat(sock, &output_pointer, 1);
 
	if (output_length < 0) {
 
		DEBUG(net, 0)("Error running _netstat!");
 
		return;
 
	}
 
	_networking_sync = false;
 
	_networking_queuing = true;
 
	_switch_mode = SM_MENU;
 
	_switch_mode_errorstr = STR_NETWORK_ERR_DESYNC;
 
}
 

	
 
	output = &output_pointer;
 
	if (seek_past_header(output, "IP Interfaces:") == B_OK) {
 
		for (;;) {
 
			uint32 n, fields, read;
 
			uint8 i1, i2, i3, i4, j1, j2, j3, j4;
 
			struct in_addr inaddr;
 
			fields = sscanf(*output, "%u: %hhu.%hhu.%hhu.%hhu, netmask %hhu.%hhu.%hhu.%hhu%n",
 
												&n, &i1,&i2,&i3,&i4, &j1,&j2,&j3,&j4, &read);
 
			read += 1;
 
			if (fields != 9) {
 
				break;
 
			}
 
			inaddr.s_addr = htonl((uint32)i1 << 24 | (uint32)i2 << 16 | (uint32)i3 << 8 | (uint32)i4);
 
			if (inaddr.s_addr != 0) {
 
				_network_ip_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
 
	_network_ip_list[0] = 0;
 

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

	
 
// ****************************** //
 
// * TCP Packets and Handlers   * //
 
// ****************************** //
 
	i = 0;
 
	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
 
		if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_INET)
 
			continue;
 
		_network_ip_list[i] = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
 
		i++;
 
	}
 
	freeifaddrs(ifap);
 

	
 
#else /* not HAVE_GETIFADDRS */
 

	
 
	unsigned long len = 0;
 
	SOCKET sock;
 
	IFREQ ifo[MAX_INTERFACES];
 

	
 
#ifndef WIN32
 
	struct ifconf if_conf;
 
#endif
 

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

	
 
	if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
 
		return;
 
	}
 

	
 
static QueuedCommand *AllocQueuedCommand(CommandQueue *nq)
 
{
 
	QueuedCommand *qp = (QueuedCommand*)calloc(sizeof(QueuedCommand), 1);
 
	assert(qp);
 
	*nq->last = qp;
 
	nq->last = &qp->next;
 
	return qp;
 
#ifdef WIN32
 
	// On windows it is easy
 
	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;
 
	}
 
#else
 
	// On linux a bit harder
 
	if_conf.ifc_len = (sizeof (struct ifreq)) * MAX_INTERFACES;
 
	if_conf.ifc_buf = (char *)&ifo[0];
 
	if ((ioctl(sock, SIOCGIFCONF, &if_conf)) == -1) {
 
		closesocket(sock);
 
		return;
 
	}
 
	len = if_conf.ifc_len;
 
#endif /* WIN32 */
 

	
 
	// Now walk through all IPs and list them
 
	for (i = 0; i < (int)(len / sizeof(IFREQ)); i++) {
 
		// Request IP for this interface
 
#ifdef WIN32
 
		_network_ip_list[i] = *(&ifo[i].iiAddress.AddressIn.sin_addr.s_addr);
 
#else
 
		if ((ioctl(sock, SIOCGIFADDR, &ifo[i])) != 0) {
 
			closesocket(sock);
 
			return;
 
		}
 

	
 
		_network_ip_list[i] = ((struct sockaddr_in *)&ifo[i].ifr_addr)->sin_addr.s_addr;
 
#endif
 
	}
 

	
 
	closesocket(sock);
 

	
 
#endif /* not HAVE_GETIFADDRS */
 

	
 
	_network_ip_list[i] = 0;
 
	last = i - 1;
 

	
 
	DEBUG(net, 3)("Detected IPs:");
 
	// Now display to the debug all the detected ips
 
	i = 0;
 
	while (_network_ip_list[i] != 0) {
 
		// Also check for non-used ips (127.0.0.1)
 
		if (_network_ip_list[i] == inet_addr("127.0.0.1")) {
 
			// If there is an ip after thisone, put him in here
 
			if (last > i)
 
				_network_ip_list[i] = _network_ip_list[last];
 
			// Clear the last ip
 
			_network_ip_list[last] = 0;
 
			// And we have 1 ip less
 
			last--;
 
			continue;
 
		}
 

	
 
		DEBUG(net, 3)(" %d) %s", i, inet_ntoa(*(struct in_addr *)&_network_ip_list[i]));//inet_ntoa(inaddr));
 
		i++;
 
	}
 
}
 

	
 
static void QueueClear(CommandQueue *nq)
 
// Resolve a hostname to a inet_addr
 
unsigned long NetworkResolveHost(const char *hostname)
 
{
 
	QueuedCommand *qp;
 
	while ((qp=nq->head)) {
 
		// unlink it.
 
		if (!(nq->head = qp->next)) nq->last = &nq->head;
 
		free(qp);
 
	in_addr_t ip;
 

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

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

	
 
static int GetNextSyncFrame()
 
{
 
	uint32 newframe;
 
	if (_frame_fsync_last == 0) return -11;
 
	newframe = (_frame_fsync_last + 11); // do not use a multiple of 4 since that screws up sync-packets
 
	return (_frame_counter_max - newframe);
 

	
 
	}
 
	return ip;
 
}
 

	
 
// go through the player queues for each player and see if there are any pending commands
 
// that should be executed this frame. if there are, execute them.
 
void NetworkProcessCommands()
 
// Converts a string to ip/port/player
 
//  Format: IP#player:port
 
//
 
// connection_string will be re-terminated to seperate out the hostname, and player and port will
 
// be set to the player and port strings given by the user, inside the memory area originally
 
// occupied by connection_string.
 
void ParseConnectionString(const byte **player, const byte **port, byte *connection_string)
 
{
 
	CommandQueue *nq;
 
	QueuedCommand *qp;
 
	byte old_player;
 

	
 
	// queue mode ?
 
	if (_networking_queuing)
 
		return;
 

	
 
	nq = &_command_queue;
 
	while ( (qp=nq->head) && (!_networking_sync || qp->frame <= _frame_counter)) {
 
		// unlink it.
 
		if (!(nq->head = qp->next)) nq->last = &nq->head;
 

	
 
		if (qp->frame < _frame_counter && _networking_sync) {
 
			DEBUG(net,0) ("warning: !qp->cp.frame < _frame_counter, %d < %d [%d]\n", qp->frame, _frame_counter, _frame_counter_srv+4);
 
		}
 

	
 
		// run the command
 
		old_player = _current_player;
 
		_current_player = qp->cp.player;
 
		memcpy(_decode_parameters, qp->cp.dp, (qp->cp.packet_length - COMMAND_PACKET_BASE_SIZE));
 

	
 
		DoCommandP(qp->cp.tile, qp->cp.p1, qp->cp.p2, qp->callback, qp->cmd | CMD_DONT_NETWORK);
 
		free(qp);
 
		_current_player = old_player;
 
	}
 

	
 
	if (!_networking_server) {
 
		// remember the random seed so we can check if we're out of sync.
 
		_my_seed_list[_frame_counter & 15][0] = _sync_seed_1;
 
		_my_seed_list[_frame_counter & 15][1] = _sync_seed_2;
 

	
 
		while (_num_future_seed) {
 
			assert(_future_seed[0].frame >= _frame_counter);
 
			if (_future_seed[0].frame != _frame_counter) break;
 
			if (_future_seed[0].seed[0] != _sync_seed_1 ||_future_seed[0].seed[1] != _sync_seed_2) NetworkHandleDeSync();
 
			memmove(_future_seed, _future_seed + 1, --_num_future_seed * sizeof(FutureSeeds));
 
	byte *p;
 
	for (p = connection_string; *p != '\0'; p++) {
 
		if (*p == '#') {
 
			*player = p + 1;
 
			*p = '\0';
 
		} else if (*p == ':') {
 
			*port = p + 1;
 
			*p = '\0';
 
		}
 
	}
 
}
 

	
 
// send a packet to a client
 
static void SendBytes(ClientState *cs, void *bytes, uint len)
 
// Creates a new client from a socket
 
//   Used both by the server and the client
 
static ClientState *AllocClient(SOCKET s)
 
{
 
	byte *b = (byte*)bytes;
 
	uint n;
 
	Packet *p;
 

	
 
	assert(len != 0);
 
	ClientState *cs;
 
	NetworkClientInfo *ci;
 
	byte client_no;
 

	
 
	// see if there's space in the last packet?
 
	if (!cs->head || (p = (Packet*)cs->last, p->siz == sizeof(p->buf)))
 
		p = NULL;
 
	client_no = 0;
 

	
 
	do {
 
		if (!p) {
 
			// need to allocate a new packet buffer.
 
			p = (Packet*)malloc(sizeof(Packet));
 
	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;
 

	
 
			// insert at the end of the linked list.
 
			*cs->last = p;
 
			cs->last = &p->next;
 
			p->next = NULL;
 
			p->siz = 0;
 
		}
 
		// Register the login
 
		client_no = _network_clients_connected++;
 
	}
 

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

	
 
		// copy bytes to packet.
 
		n = minu(sizeof(p->buf) - p->siz, len);
 
		memcpy(p->buf + p->siz, b, n);
 
		p->siz += n;
 
		b += n;
 
		p = NULL;
 
	} while (len -= n);
 
}
 
	if (_network_server) {
 
		ci = &_network_client_info[client_no];
 
		memset(ci, 0, sizeof(*ci));
 

	
 
// send data direct to a client
 
static void SendDirectBytes(ClientState *cs, void *bytes, uint len)
 
{
 
	char *buf = (char*)bytes;
 
	uint n;
 
		cs->index = _network_client_index++;
 
		ci->client_index = cs->index;
 
		ci->join_date = _date;
 

	
 
	n = send(cs->socket, buf, len, 0);
 
	if (n == -1) {
 
				int err = GET_LAST_ERROR();
 
				DEBUG(net, 0) ("NET: %i] send() failed with error %d", _frame_counter, err);
 
				CloseClient(cs);
 
			}
 
		InvalidateWindow(WC_CLIENT_LIST, 0);
 
	}
 

	
 
	return cs;
 
}
 

	
 
// client:
 
//   add it to the client's ack queue, and send the command to the server
 
// server:
 
//   add it to the server's player queue, and send it to all clients.
 
void NetworkSendCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback)
 
// Close a connection
 
void CloseClient(ClientState *cs)
 
{
 
	int nump;
 
	QueuedCommand *qp;
 
	ClientState *cs;
 
	CommandPacket cp;
 

	
 
	if (!(cmd & CMD_NET_INSTANT)) {
 
		qp = AllocQueuedCommand(_networking_server ? &_command_queue : &_ack_queue);
 
	} else {
 
		qp = (QueuedCommand*)calloc(sizeof(QueuedCommand), 1);
 
		}
 
	qp->cp.packet_type = PACKET_TYPE_COMMAND;
 
	qp->cp.tile = tile;
 
	qp->cp.p1 = p1;
 
	qp->cp.p2 = p2;	
 
	qp->cp.cmd = (uint16)cmd;
 
	qp->cp.player = _local_player;
 
	qp->cp.when = 0;
 
	qp->cmd = cmd;
 
	qp->callback = callback;
 
	NetworkClientInfo *ci;
 
	// Socket is already dead
 
	if (cs->socket == INVALID_SOCKET) return;
 

	
 
	// so the server knows when to execute it.
 
	qp->frame = _frame_counter_max - GetNextSyncFrame();
 

	
 
	// calculate the amount of extra bytes.
 
	nump = 8;
 
	while ( nump != 0 && ((uint32*)_decode_parameters)[nump-1] == 0) nump--;
 
	qp->cp.packet_length = COMMAND_PACKET_BASE_SIZE + nump * sizeof(uint32);
 
	if (nump != 0) memcpy(qp->cp.dp, _decode_parameters, nump * sizeof(uint32));
 

	
 
	cp = qp->cp;
 

	
 
	// convert to little endian
 
	cp.tile = TO_LE16(cp.tile);
 
	cp.p1 = TO_LE32(cp.p1);
 
	cp.p2 = TO_LE32(cp.p2);
 
	cp.cmd = TO_LE16(cp.cmd);
 

	
 
	// send it to the peers
 
	for(cs=_clients; cs->socket != INVALID_SOCKET; cs++) if (!cs->inactive) SendBytes(cs, &cp, cp.packet_length);
 

	
 
	if (cmd & CMD_NET_INSTANT) {
 
		free(qp);
 
	}
 
}
 

	
 
void NetworkSendEvent(uint16 type, uint16 data_len, void * data)
 
{
 
	EventPacket * ep;
 
	ClientState *cs;
 
	DEBUG(net, 1) ("[NET] Closed client connection");
 

	
 
	// encode the event ... add its data
 
	ep=malloc(data_len+sizeof(EventPacket)-1);
 
	ep->event_type = type;
 
	ep->packet_length = data_len+sizeof(EventPacket)-1;
 
	ep->packet_type = PACKET_TYPE_EVENT;
 
	memcpy(&ep->data_start,data,data_len);
 

	
 
	// send it to the peers
 
	for(cs=_clients; cs->socket != INVALID_SOCKET; cs++) if (!cs->inactive) SendBytes(cs, ep, ep->packet_length);
 

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

	
 
// client:
 
//   server sends a command from another player that we should execute.
 
//   put it in the command queue.
 
//
 
// server:
 
//   client sends a command that it wants to execute.
 
//   fill the when field so the client knows when to execute it.
 
//   put it in the appropriate player queue.
 
//   send it to all other clients.
 
//   send an ack packet to the actual client.
 

	
 
static void HandleCommandPacket(ClientState *cs, CommandPacket *np)
 
{
 
	QueuedCommand *qp;
 
	ClientState *c;
 
	AckPacket ap;
 
	uint16 cmd;
 
		NetworkGetClientName(client_name, sizeof(client_name), cs);
 

	
 
	DEBUG(net, 2) ("NET: %i] cmd size %d", _frame_counter, np->packet_length);
 
	assert(np->packet_length >= COMMAND_PACKET_BASE_SIZE);
 

	
 
	cmd = FROM_LE16(np->cmd);
 
		GetString(str1, STR_NETWORK_ERR_LEFT);
 
		GetString(str2, STR_NETWORK_ERR_CLIENT_GENERAL + errorno);
 

	
 
	if (!(cmd & CMD_NET_INSTANT)) {
 
		// put it into the command queue
 
		qp = AllocQueuedCommand(&_command_queue);
 
	} else {
 
		qp = (QueuedCommand*)calloc(sizeof(QueuedCommand), 1);
 
	}
 
	qp->cp = *np;
 

	
 
	qp->frame = _frame_counter_max - GetNextSyncFrame();
 
		NetworkTextMessage(NETWORK_ACTION_JOIN_LEAVE, 1, client_name, "%s (%s)", str1, str2);
 

	
 
	qp->callback = NULL;
 

	
 
	// extra params
 
	memcpy(&qp->cp.dp, np->dp, np->packet_length - COMMAND_PACKET_BASE_SIZE);
 

	
 
	ap.packet_type = PACKET_TYPE_ACK;
 
	ap.when = TO_LE16(GetNextSyncFrame());
 
	ap.packet_length = sizeof(AckPacket);
 
	DEBUG(net,4)("NET: %i] NewACK: frame=%i %i",_frame_counter, ap.when,_frame_counter_max - GetNextSyncFrame());
 

	
 
	// send it to the peers
 
	if (_networking_server) {
 
		for(c=_clients; c->socket != INVALID_SOCKET; c++) {
 
			if (c == cs) {
 
				if (!(cmd & CMD_NET_INSTANT)) SendDirectBytes(c, &ap, ap.packet_length);
 
			} else {
 
				if (!cs->inactive) SendBytes(c, &qp->cp, qp->cp.packet_length);
 
		// 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);
 
			}
 
		}
 
	}
 

	
 
// convert from little endian to big endian?
 
#if defined(TTD_BIG_ENDIAN)
 
	qp->cp.cmd = FROM_LE16(qp->cp.cmd);
 
	qp->cp.tile = FROM_LE16(qp->cp.tile);
 
	qp->cp.p1 = FROM_LE32(qp->cp.p1);
 
	qp->cp.p2 = FROM_LE32(qp->cp.p2);
 
#endif
 

	
 
	qp->cmd = qp->cp.cmd;
 

	
 
	if (cmd & CMD_NET_INSTANT) {
 
		byte p = _current_player;
 
		_current_player = qp->cp.player;
 
		memcpy(_decode_parameters, qp->cp.dp, (qp->cp.packet_length - COMMAND_PACKET_BASE_SIZE));
 
		DoCommandP(qp->cp.tile, qp->cp.p1, qp->cp.p2, qp->callback, qp->cmd | CMD_DONT_NETWORK);
 
		free(qp);
 
		_current_player = p;
 
		}
 
}
 
	closesocket(cs->socket);
 
	cs->writable = false;
 

	
 
static void HandleEventPacket(EventPacket *ep)
 
{
 
	switch (ep->event_type) {
 
		case NET_EVENT_SUBSIDY:
 
			RemoteSubsidyAdd((Subsidy *)&ep->data_start);
 
			break;
 
	// 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;
 
	}
 
}
 

	
 
// sent from server -> client periodically to tell the client about the current tick in the server
 
// and how far the client may progress.
 
static void HandleSyncPacket(SyncPacket *sp)
 
{
 
	uint32 s1,s2;
 

	
 
	_frame_counter_srv = _frame_counter_max - sp->server;
 
	_frame_counter_max += sp->frames;
 

	
 
	// reset network ready packet state
 
	_network_ready_sent = false;
 

	
 
	// queueing only?
 
	if (_networking_queuing || _frame_counter == 0)
 
		return;
 

	
 
	s1 = FROM_LE32(sp->random_seed_1);
 
	s2 = FROM_LE32(sp->random_seed_2);
 

	
 
	DEBUG(net, 3) ("NET: %i] sync seeds: 1=%i 2=%i",_frame_counter, sp->random_seed_1, sp->random_seed_2);
 
	free(cs->packet_recv);
 
	cs->packet_recv = NULL;
 

	
 
	if (_frame_counter_srv <= _frame_counter) {
 
		// we are ahead of the server check if the seed is in our list.
 
		if (_frame_counter_srv + 16 > _frame_counter) {
 
			// the random seed exists in our array check it.
 
			if (s1 != _my_seed_list[_frame_counter_srv & 0xF][0] || s2 != _my_seed_list[_frame_counter_srv & 0xF][1]) NetworkHandleDeSync();
 
		}
 
	} else {
 
		// the server's frame has not been executed yet. store the server's seed in a list.
 
		if (_num_future_seed < lengthof(_future_seed)) {
 
			_future_seed[_num_future_seed].frame = _frame_counter_srv;
 
			_future_seed[_num_future_seed].seed[0] = s1;
 
			_future_seed[_num_future_seed].seed[1] = s2;
 
			_num_future_seed++;
 
		}
 
	while (cs->command_queue != NULL) {
 
		CommandPacket *p = cs->command_queue->next;
 
		free(cs->command_queue);
 
		cs->command_queue = p;
 
	}
 
}
 

	
 
static void HandleFSyncPacket(FrameSyncPacket *fsp)
 
{
 
	DEBUG(net,3)("NET: %i] FSYNC: srv=%i %i",_frame_counter, fsp->frames,(_frame_counter_max - fsp->frames));
 
	if (fsp->frames < 1) return;
 
	_frame_fsync_last = _frame_counter_srv = _frame_counter_max - fsp->frames;
 
}
 

	
 
// sent from server -> client as an acknowledgement that the server received the command.
 
// the command will be executed at the current value of "max".
 
static void HandleAckPacket(AckPacket * ap)
 
{
 
	QueuedCommand *q;
 
	// move a packet from the ack queue to the end of this player's queue.
 
	q = _ack_queue.head;
 
	assert(q);
 
	if (!(_ack_queue.head = q->next)) _ack_queue.last = &_ack_queue.head;
 
	q->next = NULL;
 

	
 
	q->frame = (_frame_counter_max - (FROM_LE16(ap->when)));
 
	// Close the gap in the client-list
 
	ci = DEREF_CLIENT_INFO(cs);
 

	
 
	*_command_queue.last = q;
 
	_command_queue.last = &q->next;
 

	
 
	DEBUG(net, 2) ("NET %i] ack [frame=%i]",_frame_counter,q->frame);
 
}
 

	
 
static void HandleFilePacket(FilePacketHdr *fp)
 
{
 
	int n = fp->packet_length - sizeof(FilePacketHdr);
 
	char tempfile[512];
 

	
 
	sprintf(tempfile, "%s/networkc.tmp",  _path.personal_dir);
 

	
 
	if (n == 0) {
 
		assert(_networking_queuing);
 
		assert(!_networking_sync);
 
		// eof
 
		if (_recv_file) { fclose(_recv_file); _recv_file = NULL; }
 
	if (_network_server) {
 
		// We just lost one client :(
 
		if (cs->status > STATUS_INACTIVE)
 
			_network_game_info.clients_on--;
 
		_network_clients_connected--;
 

	
 
		// attempt loading the game.
 
		_game_mode = GM_NORMAL;
 
		if (SaveOrLoad(tempfile, SL_LOAD) != SL_OK) {
 
				NetworkCoreDisconnect();
 
				NetworkHandleSaveGameError();
 
				return;
 
				}
 
		// sync to server.
 
		_networking_queuing = false;
 
		NetworkStartSync(false);
 

	
 
		if (_network_playas == 0) {
 
			// send a command to make a new player
 
			_local_player = 0;
 
			NetworkSendCommand(0, 0, 0, CMD_PLAYER_CTRL, NULL);
 
			_local_player = OWNER_SPECTATOR;
 
		} else {
 
			// take control over an existing company
 
			if (DEREF_PLAYER(_network_playas-1)->is_active)
 
				_local_player = _network_playas-1;
 
			else
 
				_local_player = OWNER_SPECTATOR;
 
		while ((cs + 1) != DEREF_CLIENT(MAX_CLIENTS) && (cs + 1)->socket != INVALID_SOCKET) {
 
			*cs = *(cs + 1);
 
			*ci = *(ci + 1);
 
			cs++;
 
			ci++;
 
		}
 

	
 
	} else {
 
		if(!_recv_file) {
 
			_recv_file = fopen(tempfile, "wb");
 
			if (!_recv_file) error("can't open savefile");
 
		}
 
		fwrite( (char*)fp + sizeof(*fp), n, 1, _recv_file);
 
	}
 
}
 

	
 
static void HandleWelcomePacket(WelcomePacket *wp)
 
{
 
	int i;
 
	for (i=0; i<MAX_PLAYERS; i++) {
 

	
 
		_player_seeds[i][0] = FROM_LE32(wp->player_seeds[i][0]);
 
		_player_seeds[i][1] = FROM_LE32(wp->player_seeds[i][1]);
 
		}
 
	if (wp->frames_srv != 0) {
 
		_frame_counter_max = FROM_LE32(wp->frames_max);
 
		_frame_counter_srv = FROM_LE32(wp->frames_srv);
 
	}
 
	if (wp->frames_cnt != 0) {
 
		_frame_counter = FROM_LE32(wp->frames_cnt);
 
	}
 
}
 

	
 
static void HandleReadyPacket(ReadyPacket *rp, ClientState *cs)
 
{
 
	cs->ready=true;
 
	cs->timeout=_network_client_timeout;
 
	DEBUG(net,1) ("NET: %i] ready packet recv", _frame_counter);
 
}
 

	
 

	
 
static void CloseClient(ClientState *cs)
 
{
 
	Packet *p, *next;
 

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

	
 
	assert(cs->socket != INVALID_SOCKET);
 

	
 
	closesocket(cs->socket);
 

	
 
	// free buffers
 
	for(p = cs->head; p; p=next) {
 
		next = p->next;
 
		free(p);
 
		InvalidateWindow(WC_CLIENT_LIST, 0);
 
	}
 

	
 
	// copy up structs...
 
	while ((cs+1)->socket != INVALID_SOCKET) {
 
		*cs = *(cs+1);
 
		cs++;
 
	}
 
	// Reset the status of the last socket
 
	cs->socket = INVALID_SOCKET;
 

	
 
	if (_networking_server) _network_game.players_on--;
 

	
 
	_num_clients--;
 
	cs->status = STATUS_INACTIVE;
 
	cs->index = NETWORK_EMPTY_INDEX;
 
	ci->client_index = NETWORK_EMPTY_INDEX;
 
}
 

	
 
#define NETWORK_BUFFER_SIZE 4096
 
static bool ReadPackets(ClientState *cs)
 
{
 
	byte network_buffer[NETWORK_BUFFER_SIZE];
 
	uint pos,size;
 
	unsigned long recv_bytes;
 

	
 
	size = cs->buflen;
 

	
 
	for(;;) {
 
		if (size != 0) memcpy(network_buffer, cs->buf, size);
 
extern void ShowJoinStatusWindow();
 

	
 
		recv_bytes = recv(cs->socket, (char*)network_buffer + size, sizeof(network_buffer) - size, 0);
 
		if ( recv_bytes == (unsigned long)-1) {
 
			int err = GET_LAST_ERROR();
 
			if (err == EWOULDBLOCK) break;
 
			DEBUG(net, 0) ("[NET] recv() failed with error %d", err);
 
			CloseClient(cs);
 
			return false;
 
		}
 
		// no more bytes for now?
 
		if (recv_bytes == 0)
 
			break;
 
// A client wants to connect to a server
 
bool NetworkConnect(const char *hostname, int port)
 
{
 
	SOCKET s;
 
	struct sockaddr_in sin;
 

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

	
 
		size += recv_bytes; // number of bytes read.
 
		pos = 0;
 
		while (size >= 2) {
 
			byte *packet = network_buffer + pos;
 
			// whole packet not there yet?
 
			if (size < packet[0]) break;
 
			size -= packet[0];
 
			pos += packet[0];
 
			switch(packet[1]) {
 
			case PACKET_TYPE_WELCOME:
 
				HandleWelcomePacket((WelcomePacket *)packet);
 
				break;
 
			case PACKET_TYPE_COMMAND:
 
				HandleCommandPacket(cs, (CommandPacket*)packet);
 
				break;
 
			case PACKET_TYPE_SYNC:
 
				assert(_networking_sync || _networking_queuing);
 
				assert(!_networking_server);
 
				HandleSyncPacket((SyncPacket*)packet);
 
				break;
 
			case PACKET_TYPE_FSYNC:
 
				HandleFSyncPacket((FrameSyncPacket *)packet);
 
				break;
 
			case PACKET_TYPE_ACK:
 
				assert(!_networking_server);
 
				HandleAckPacket((AckPacket*)packet);
 
				break;
 
			case PACKET_TYPE_XMIT:
 
				HandleFilePacket((FilePacketHdr*)packet);
 
				break;
 
			case PACKET_TYPE_READY:
 
				HandleReadyPacket((ReadyPacket*)packet, cs);
 
				break;
 
			case PACKET_TYPE_EVENT:
 
				HandleEventPacket((EventPacket*)packet);
 
				break;
 
			default:
 
				DEBUG (net,0) ("NET: %i] unknown packet type",_frame_counter);
 
			}
 
		}
 
	s = socket(AF_INET, SOCK_STREAM, 0);
 
	if (s == INVALID_SOCKET) {
 
		ClientStartError("socket() failed");
 
		return false;
 
	}
 

	
 
		assert(size < sizeof(cs->buf));
 

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

	
 
	cs->buflen = size;
 
	sin.sin_family = AF_INET;
 
	sin.sin_addr.s_addr = NetworkResolveHost(hostname);
 
	sin.sin_port = htons(port);
 
	_network_last_host_ip = sin.sin_addr.s_addr;
 

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

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

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

	
 
	ShowJoinStatusWindow();
 

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

	
 
	return true;
 
}
 

	
 

	
 
static bool SendPackets(ClientState *cs)
 
{
 
	Packet *p;
 
	int n;
 
	uint nskip = cs->eaten, nsent = nskip;
 

	
 
	// try sending as much as possible.
 
	for(p=cs->head; p ;p = p->next) {
 
		if (p->siz) {
 
			assert(nskip < p->siz);
 

	
 
			n = send(cs->socket, p->buf + nskip, p->siz - nskip, 0);
 
			if (n == -1) {
 
				int err = GET_LAST_ERROR();
 
				if (err == EWOULDBLOCK) break;
 
				DEBUG(net, 0) ("[NET] send() failed with error %d", err);
 
				CloseClient(cs);
 
				return false;
 
			}
 
			nsent += n;
 
			// send was not able to send it all? then we assume that the os buffer is full and break.
 
			if (nskip + n != p->siz)
 
				break;
 
			nskip = 0;
 
		}
 
	}
 

	
 
	// nsent bytes in the linked list are not invalid. free as many buffers as possible.
 
	// don't actually free the last buffer.
 
	while (nsent) {
 
		p = cs->head;
 
		assert(p->siz != 0);
 

	
 
		// some bytes of the packet are still unsent.
 
		if ( (int)(nsent - p->siz) < 0)
 
			break;
 
		nsent -= p->siz;
 
		p->siz = 0;
 
		if (p->next) {
 
			cs->head = p->next;
 
			free(p);
 
		}
 
	}
 

	
 
	cs->eaten = nsent;
 

	
 
	return true;
 
}
 

	
 
// transmit the file..
 
static void SendXmit(ClientState *cs)
 
{
 
	uint pos, n;
 
	FilePacketHdr hdr;
 
	int p;
 

	
 
	// if too many unsent bytes left in buffer, don't send more.
 
	if (cs->head && cs->head->next)
 
		return;
 

	
 
	pos = cs->xmitpos - 1;
 

	
 
	p = 20;
 
	do {
 
		// compute size of data to xmit
 
		n = minu(_transmit_file_size - pos, 248);
 

	
 
		hdr.packet_length = n + sizeof(hdr);
 
		hdr.packet_type = PACKET_TYPE_XMIT;
 
		SendBytes(cs, &hdr, sizeof(hdr));
 

	
 
		if (n == 0) {
 
			pos = -1; // eof
 
			break;
 
		}
 
		SendBytes(cs, _transmit_file + pos, n);
 
		pos += n;
 
	} while (--p);
 

	
 
	cs->xmitpos = pos + 1;
 

	
 
	if (cs->xmitpos == 0) {
 
		NetworkSendWelcome(cs,false);
 
	}
 

	
 
	DEBUG(net, 2) ("[NET] client xmit at %d", pos + 1);
 
}
 

	
 
static ClientState *AllocClient(SOCKET s)
 
{
 
	ClientState *cs;
 

	
 
	if (_num_clients == MAX_CLIENTS)
 
		return NULL;
 

	
 
	if (_networking_server) _network_game.players_on++;
 

	
 
	cs = &_clients[_num_clients++];
 
	memset(cs, 0, sizeof(*cs));
 
	cs->last = &cs->head;
 
	cs->socket = s;
 
	cs->timeout = _network_client_timeout;
 
	return cs;
 
}
 

	
 
void NetworkSendReadyPacket()
 
{
 
	if ((!_network_ready_sent) && (_frame_counter + _network_ready_ahead >= _frame_counter_max)) {
 
		ReadyPacket rp;
 

	
 
		DEBUG(net,1) ("NET: %i] ready packet sent", _frame_counter);
 

	
 
		rp.packet_type = PACKET_TYPE_READY;
 
		rp.packet_length = sizeof(rp);
 
		SendBytes(_clients, &rp, sizeof(rp));
 
		_network_ready_sent = true;
 
	}
 
}
 

	
 
void NetworkSendSyncPackets()
 
{
 
	ClientState *cs;
 
	uint32 new_max;
 
	SyncPacket sp;
 

	
 
	new_max = _frame_counter + (int)_network_sync_freq;
 

	
 
	DEBUG(net,3) ("NET: %i] serv: sync max=%i, seed1=%i, seed2=%i",_frame_counter,new_max,_sync_seed_1,_sync_seed_2);
 

	
 
	sp.packet_length = sizeof(sp);
 
	sp.packet_type = PACKET_TYPE_SYNC;
 
	sp.frames = new_max - _frame_counter_max;
 
	sp.server = _frame_counter_max - _frame_counter;
 
	sp.random_seed_1 = TO_LE32(_sync_seed_1);
 
	sp.random_seed_2 = TO_LE32(_sync_seed_2);
 
	_frame_counter_max = new_max;
 

	
 
	// send it to all the clients and mark them unready
 
	for(cs=_clients;cs->socket != INVALID_SOCKET; cs++) {
 
		cs->ready=false;
 
		SendBytes(cs, &sp, sp.packet_length);
 
	}
 

	
 
}
 

	
 
void NetworkSendFrameSyncPackets()
 
{
 
	ClientState *cs;
 
	FrameSyncPacket fsp;
 
	if ((_frame_counter + 4) < _frame_counter_max) if ((_frame_fsync_last + 4 < _frame_counter)) {
 
		// this packet mantains some information about on which frame the server is
 
		fsp.frames = _frame_counter_max - _frame_counter;
 
		fsp.packet_type = PACKET_TYPE_FSYNC;
 
		fsp.packet_length = sizeof (FrameSyncPacket);
 
		// send it to all the clients and mark them unready
 
		for(cs=_clients;cs->socket != INVALID_SOCKET; cs++) {
 
			SendBytes(cs, &fsp, fsp.packet_length);
 
		}
 
		_frame_fsync_last = _frame_counter;
 
	}
 

	
 
}
 

	
 
void NetworkSendWelcome(ClientState *cs, bool direct) {
 
	WelcomePacket wp;
 
	int i;
 
	wp.packet_type = PACKET_TYPE_WELCOME;
 
	wp.packet_length = sizeof(WelcomePacket);
 
	for (i=0; i<MAX_PLAYERS; i++) {
 
		wp.player_seeds[i][0]=TO_LE32(_player_seeds[i][0]);
 
		wp.player_seeds[i][1]=TO_LE32(_player_seeds[i][1]);
 
		}
 
	if (direct) {
 
		wp.frames_max=0;
 
		wp.frames_srv=0;
 
		wp.frames_cnt=TO_LE32(_frame_counter);
 
		SendDirectBytes(cs,(void *)&wp,wp.packet_length);
 
	} else {
 
		wp.frames_max=TO_LE32(_frame_counter_max);
 
		wp.frames_srv=TO_LE32(_frame_counter_srv);
 
		wp.frames_cnt=0;
 
		SendBytes(cs,(void *)&wp,wp.packet_length);
 
	}
 
}
 

	
 
static void NetworkAcceptClients()
 
// For the server, to accept new clients
 
static void NetworkAcceptClients(void)
 
{
 
	struct sockaddr_in sin;
 
	SOCKET s;
 
@@ -1040,200 +559,362 @@ static void NetworkAcceptClients()
 
	LONG sin_len; // for some reason we need a 'LONG' under MorphOS
 
#endif
 

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

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

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

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

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

	
 
		cs = AllocClient(s);
 
		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;
 
		}
 

	
 
		if (_networking_sync) {
 
			// a new client has connected. it needs a snapshot.
 
			cs->inactive = true;
 
		// a new client has connected. We set him at inactive for now
 
		//  maybe he is only requesting server-info. Till he has sent a PACKET_CLIENT_MAP_OK
 
		//  the client stays inactive
 
		cs->status = STATUS_INACTIVE;
 

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

	
 
	// when a new client has joined. it needs different information depending on if it's at the game menu or in an active game.
 
	// Game menu:
 
	//  - list of players already in the game (name, company name, face, color)
 
	//  - list of game settings and patch settings
 
	// Active game:
 
	//  - the state of the world (includes player name, company name, player face, player color)
 
	//  - list of the patch settings
 

	
 
	// Networking can be in several "states".
 
	//  * not sync - games don't need to be in sync, and frame counter is not synced. for example intro screen. all commands are executed immediately.
 
	//  * sync - games are in sync
 
}
 

	
 
static void SendQueuedCommandsToNewClient(ClientState *cs)
 
{
 
	// send the commands in the server queue to the new client.
 
	QueuedCommand *qp;
 
	SyncPacket sp;
 
	uint32 frame;
 

	
 
	DEBUG(net, 2) ("NET: %i] sending queued commands to client",_frame_counter);
 

	
 
	sp.packet_length = sizeof(sp);
 
	sp.packet_type = PACKET_TYPE_SYNC;
 
	sp.random_seed_1 = sp.random_seed_2 = 0;
 
	sp.server = 0;
 

	
 
	frame = _frame_counter;
 

	
 
	for(qp=_command_queue.head; qp; qp = qp->next) {
 
		DEBUG(net, 4) ("NET: %i] sending cmd to be executed at %d (old %d)", _frame_counter, qp->frame, frame);
 
		if (qp->frame > frame) {
 
			assert(qp->frame <= _frame_counter_max);
 
			sp.frames = qp->frame - frame;
 
			frame = qp->frame;
 
			SendBytes(cs, &sp, sizeof(sp));
 
		}
 
		SendBytes(cs, &qp->cp, qp->cp.packet_length);
 
	}
 

	
 
	if (frame < _frame_counter_max) {
 
		DEBUG(net, 4) ("NET: %i] sending queued sync %d (%d)",_frame_counter, _frame_counter_max, frame);
 
		sp.frames = _frame_counter_max - frame;
 
		SendBytes(cs, &sp, sizeof(sp));
 
	}
 

	
 
}
 

	
 
bool NetworkCheckClientReady()
 
{
 
	bool ready_all = true;
 
	uint16 count = 0;
 
	ClientState *cs;
 

	
 
	for(cs=_clients;cs->socket != INVALID_SOCKET; cs++) {
 
		count++;
 
		ready_all = ready_all && (cs->ready || cs->inactive || (cs->xmitpos>0));
 
		if (!cs->ready) cs->timeout-=1;
 
		if (cs->timeout == 0) {
 
			SetDParam(0,count);
 
			ShowErrorMessage(-1,STR_NETWORK_ERR_TIMEOUT,0,0);
 
			CloseClient(cs);
 
			}
 
		}
 
	return ready_all;
 
}
 

	
 
// ************************** //
 
// * TCP Networking         * //
 
// ************************** //
 

	
 
unsigned long NetworkResolveHost(const char *hostname)
 
// Set up the listen socket for the server
 
bool NetworkListen(void)
 
{
 
	struct hostent* remotehost;
 

	
 
	if ((hostname[0]<0x30) || (hostname[0]>0x39)) {
 
		// seems to be an hostname [first character is no number]
 
		remotehost = gethostbyname(hostname);
 
		if (remotehost == NULL) {
 
			DEBUG(net, 1) ("[NET][IP] cannot resolve %s", hostname);
 
			return 0;
 
		} else {
 
			DEBUG(net, 1) ("[NET][IP] resolved %s to %s",hostname, inet_ntoa(*(struct in_addr *) remotehost->h_addr_list[0]));
 
			return inet_addr(inet_ntoa(*(struct in_addr *) remotehost->h_addr_list[0]));
 
			}
 
	} else {
 
		// seems to be an ip [first character is a number]
 
		return inet_addr(hostname);
 
		}
 

	
 
}
 

	
 
bool NetworkConnect(const char *hostname, int port)
 
{
 
	SOCKET s;
 
	struct sockaddr_in sin;
 
	int b;
 

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

	
 
	s = socket(AF_INET, SOCK_STREAM, 0);
 
	if (s == INVALID_SOCKET) error("socket() failed");
 

	
 
	b = 1;
 
	setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (const char*)&b, sizeof(b));
 

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

	
 
	if (connect(s, (struct sockaddr*) &sin, sizeof(sin)) != 0) {
 
		NetworkClose(true);
 
		return false;
 
		}
 

	
 
	// set nonblocking mode for socket..
 
	{ unsigned long blocking = 1; ioctlsocket(s, FIONBIO, &blocking); }
 

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

	
 
	// queue packets.. because we're waiting for the savegame.
 
	_networking_queuing = true;
 
	_frame_counter_max = 0;
 

	
 
	return true;
 
}
 

	
 
void NetworkListen()
 
{
 

	
 
	SOCKET ls;
 
	struct sockaddr_in sin;
 
	int port;
 

	
 
	port = _network_server_port;
 

	
 
	DEBUG(net, 1) ("[NET][TCP] listening on port %d", port);
 
	DEBUG(net, 1) ("[NET] Listening on port %d", port);
 

	
 
	ls = socket(AF_INET, SOCK_STREAM, 0);
 
	if (ls == INVALID_SOCKET)
 
		error("socket() on listen socket failed");
 

	
 
	// reuse the socket
 
	{
 
		int reuse = 1; if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) == -1)
 
			error("setsockopt() on listen socket failed");
 
	if (ls == INVALID_SOCKET) {
 
		ServerStartError("socket() on listen socket failed");
 
		return false;
 
	}
 

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

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

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

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

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

	
 
	_listensocket = ls;
 

	
 
	return true;
 
}
 

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

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

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

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

	
 
	_local_command_queue = NULL;
 

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

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

	
 
	_sync_frame = 0;
 
	_network_first_time = true;
 

	
 
	_network_reconnect = 0;
 

	
 
	InitPlayerRandoms();
 

	
 
	NetworkUDPInitialize();
 
}
 

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

	
 
	NetworkDisconnect();
 

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

	
 
	NetworkInitialize();
 

	
 
	_network_server = false;
 

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

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

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

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

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

	
 
	if (port == 0) return false;
 

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

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

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

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

	
 
	return _networking;
 
}
 

	
 
void NetworkReceive()
 
void NetworkInitGameInfo(void)
 
{
 
#if defined(WITH_REV)
 
		extern char _openttd_revision[];
 
#else
 
		const char _openttd_revision[] = "norev000";
 
#endif
 
	NetworkClientInfo *ci;
 

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

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

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

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

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

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

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

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

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

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

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

	
 
	NetworkClose();
 

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

	
 
	_networking = false;
 
	_network_server = false;
 
}
 

	
 
// We want to disconnect from the host/clients
 
void NetworkDisconnect(void)
 
{
 
	if (_network_server) {
 
		ClientState *cs;
 
		FOR_ALL_CLIENTS(cs) {
 
			SEND_COMMAND(PACKET_SERVER_SHUTDOWN)(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);
 
	}
 

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

	
 
	_networking = false;
 
	_network_server = false;
 
}
 

	
 
// Receives something from the network
 
bool NetworkReceive(void)
 
{
 
	ClientState *cs;
 
	int n;
 
@@ -1243,13 +924,13 @@ void NetworkReceive()
 
	FD_ZERO(&read_fd);
 
	FD_ZERO(&write_fd);
 

	
 
	for(cs=_clients;cs->socket != INVALID_SOCKET; cs++) {
 
	FOR_ALL_CLIENTS(cs) {
 
		FD_SET(cs->socket, &read_fd);
 
		FD_SET(cs->socket, &write_fd);
 
	}
 

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

	
 
@@ -1259,428 +940,257 @@ void NetworkReceive()
 
#else
 
	n = WaitSelect(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv, NULL);
 
#endif
 
	if ((n == -1) && (!_networking_server)) NetworkHandleConnectionLost();
 
	if (n == -1 && !_network_server) NetworkError(STR_NETWORK_ERR_LOSTCONNECTION);
 

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

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

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

	
 
	// if we're a server, and any client needs a snapshot, create a snapshot and send all commands from the server queue to the client.
 
	if (_networking_server && _transmit_file == NULL) {
 
		bool didsave = false;
 
// This sends all buffered commands (if possible)
 
static void NetworkSend(void)
 
{
 
	ClientState *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);
 
			}
 
		}
 
	}
 
}
 

	
 
		for(cs=_clients;cs->socket != INVALID_SOCKET; cs++) {
 
			if (cs->inactive) {
 
				cs->inactive = false;
 
				// found a client waiting for a snapshot. make a snapshot.
 
				if (!didsave) {
 
					char filename[256];
 
					sprintf(filename, "%snetwork.tmp",  _path.autosave_dir);
 
					didsave = true;
 
					if (SaveOrLoad(filename, SL_SAVE) != SL_OK) error("network savedump failed");
 
					_transmit_file = ReadFileToMem(filename, &_transmit_file_size, 500000);
 
					if (_transmit_file == NULL) error("network savedump failed to load");
 
// Handle the local-command-queue
 
void NetworkHandleLocalQueue(void)
 
{
 
	if (_local_command_queue != NULL) {
 
		CommandPacket *cp;
 
		CommandPacket *cp_prev;
 

	
 
		cp = _local_command_queue;
 
		cp_prev = NULL;
 

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

	
 
				if (cp_prev != NULL) {
 
					cp_prev->next = cp->next;
 
					free(cp);
 
					cp = cp_prev->next;
 
				} else {
 
					// This means we are at our first packet
 
					_local_command_queue = cp->next;
 
					free(cp);
 
					cp = _local_command_queue;
 
				}
 
				// and start sending the file..
 
				cs->xmitpos = 1;
 

	
 
				// send queue of commands to client.
 
				SendQueuedCommandsToNewClient(cs);
 

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

	
 
void NetworkSend()
 

	
 
extern void StateGameLoop();
 

	
 
bool NetworkDoClientLoop(void)
 
{
 
	ClientState *cs;
 
	void *free_xmit;
 
	_frame_counter++;
 

	
 
	free_xmit = _transmit_file;
 
	NetworkHandleLocalQueue();
 

	
 
	StateGameLoop();
 

	
 
	// send stuff to all clients
 
	for(cs=_clients;cs->socket != INVALID_SOCKET; cs++) {
 
		if (cs->xmitpos) {
 
			if (cs->writable)
 
				SendXmit(cs);
 
			free_xmit = NULL;
 
		}
 
		if (cs->writable)	{
 
			if (!SendPackets(cs)) cs--;
 
	// Check if we are in sync!
 
	if (_sync_frame != 0) {
 
		if (_sync_frame == _frame_counter) {
 
#ifdef NETWORK_SEND_DOUBLE_SEED
 
			if (_sync_seed_1 != _random_seeds[0][0] || _sync_seed_2 != _random_seeds[0][1]) {
 
#else
 
			if (_sync_seed_1 != _random_seeds[0][0]) {
 
#endif
 
				NetworkError(STR_NETWORK_ERR_DESYNC);
 
				DEBUG(net, 0)("[NET] Sync error detected!");
 
				NetworkClientError(NETWORK_RECV_STATUS_DESYNC, DEREF_CLIENT(0));
 
				return false;
 
			}
 

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

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

	
 
	// no clients left that xmit the file, free it.
 
	if (free_xmit) {
 
		_transmit_file = NULL;
 
		free(free_xmit);
 
	}
 
	return true;
 
}
 

	
 

	
 
void NetworkInitialize()
 
// We have to do some UDP checking
 
void NetworkUDPGameLoop(void)
 
{
 
	ClientState *cs;
 

	
 
	QueueClear(&_command_queue);
 
	QueueClear(&_ack_queue);
 
	_command_queue.last = &_command_queue.head;
 
	_network_game_list = NULL;
 

	
 
	// invalidate all clients
 
	for(cs=_clients; cs != &_clients[MAX_CLIENTS]; cs++)
 
		cs->socket = INVALID_SOCKET;
 

	
 
}
 

	
 
void NetworkClose(bool client)
 
{
 

	
 
	ClientState *cs;
 
	// invalidate all clients
 

	
 
	for(cs=_clients; cs != &_clients[MAX_CLIENTS]; cs++) if (cs->socket != INVALID_SOCKET) {
 
		CloseClient(cs);
 
		}
 

	
 
	if (!client) {
 
		// if in servermode --> close listener
 
		closesocket(_listensocket);
 
		_listensocket= INVALID_SOCKET;
 
		DEBUG(net, 1) ("[NET][TCP] closed listener on port %i", _network_server_port);
 
	if (_network_udp_server)
 
		NetworkUDPReceive();
 
	else if (_udp_client_socket != INVALID_SOCKET) {
 
		NetworkUDPReceive();
 
		if (_network_udp_broadcast > 0)
 
			_network_udp_broadcast--;
 
	}
 
}
 

	
 
void NetworkShutdown()
 
// The main loop called from ttd.c
 
//  Here we also have to do StateGameLoop if needed!
 
void NetworkGameLoop(void)
 
{
 
	_networking_server = false;
 
	_networking = false;
 
	_networking_sync = false;
 
	_frame_counter = 0;
 
	_frame_counter_max = 0;
 
	_frame_counter_srv = 0;
 
}
 
	if (!_networking) return;
 

	
 
// switch to synced mode.
 
void NetworkStartSync(bool fcreset)
 
{
 
	DEBUG(net, 3) ("[NET][SYNC] switching to synced game mode");
 
	_networking_sync = true;
 
	_frame_counter = 0;
 
	if (!NetworkReceive()) return;
 

	
 
	if (fcreset) {
 
		_frame_counter_max = 0;
 
		_frame_counter_srv = 0;
 
		_frame_fsync_last = 0;
 
		}
 
	_num_future_seed = 0;
 
	_sync_seed_1 = _sync_seed_2 = 0;
 
	memset(_my_seed_list, 0, sizeof(_my_seed_list));
 
}
 
	if (_network_server) {
 
		// We first increase the _frame_counter
 
		_frame_counter++;
 

	
 
// ************************** //
 
// * UDP Network Extensions * //
 
// ************************** //
 
		NetworkHandleLocalQueue();
 

	
 
void NetworkUDPListen(bool client)
 
{
 
	SOCKET udp;
 
	struct sockaddr_in sin;
 
	int port;
 

	
 
	if (client) { port = _network_client_port; } else { port = _network_server_port; };
 
		// Then we make the frame
 
		StateGameLoop();
 

	
 
	DEBUG(net, 1) ("[NET][UDP] listening on port %i", port);
 

	
 
	udp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
 

	
 
	// this disables network
 
	_network_available = !(udp == INVALID_SOCKET);
 
		_sync_seed_1 = _random_seeds[0][0];
 
#ifdef NETWORK_SEND_DOUBLE_SEED
 
		_sync_seed_2 = _random_seeds[0][1];
 
#endif
 

	
 
	// set nonblocking mode for socket
 
	{ unsigned long blocking = 1; ioctlsocket(udp, FIONBIO, &blocking); }
 

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

	
 
	if (bind(udp, (struct sockaddr*)&sin, sizeof(sin)) != 0)
 
		DEBUG(net, 1) ("[NET][UDP] error: bind failed on port %i", port);
 

	
 
		NetworkServer_Tick();
 
	} else {
 
		// Client
 

	
 
	// enable broadcasting
 
	{ unsigned long val=1; setsockopt(udp, SOL_SOCKET, SO_BROADCAST, (char *) &val , sizeof(val)); }
 
	// allow reusing
 
	{ unsigned long val=1; setsockopt(udp, SOL_SOCKET, SO_REUSEADDR, (char *) &val , sizeof(val)); }
 

	
 
	if (client) { _udp_client_socket = udp; } else { _udp_server_socket = udp; } ;
 

	
 
}
 

	
 
void NetworkUDPClose(bool client)
 
{
 
	if (client) {
 
		DEBUG(net, 1) ("[NET][UDP] closed listener on port %i", _network_client_port);
 
		closesocket(_udp_client_socket);
 
		_udp_client_socket = INVALID_SOCKET;
 
		// 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 {
 
		DEBUG(net, 1) ("[NET][UDP] closed listener on port %i", _network_server_port);
 
		closesocket(_udp_server_socket);
 
		_udp_server_socket = INVALID_SOCKET;
 
		};
 
			// Else, keep on going till _frame_counter_max
 
			if (_frame_counter_max > _frame_counter) {
 
				NetworkDoClientLoop();
 
			}
 
		}
 
	}
 

	
 
void NetworkUDPReceive(bool client)
 
{
 
	struct sockaddr_in client_addr;
 
#ifndef __MORPHOS__
 
	int client_len;
 
#else
 
	LONG client_len; // for some reason we need a 'LONG' under MorphOS
 
#endif
 
	int nbytes;
 
	struct UDPPacket packet;
 
	int packet_len;
 

	
 
	SOCKET udp;
 
	if (client) udp=_udp_client_socket; else udp=_udp_server_socket;
 

	
 
	packet_len = sizeof(packet);
 
	client_len = sizeof(client_addr);
 

	
 
	nbytes = recvfrom(udp, (char *) &packet, packet_len , 0, (struct sockaddr *) &client_addr, &client_len);
 
	if (nbytes>0) {
 
		if (packet.command_code==packet.command_check) switch (packet.command_code) {
 

	
 
 		case NET_UDPCMD_SERVERSEARCH:
 
 			if (!client) {
 
				packet.command_check=packet.command_code=NET_UDPCMD_SERVERINFO;
 
				memcpy(&packet.data,&_network_game,sizeof(_network_game));
 
				packet.data_len=sizeof(_network_game);
 
 				NetworkUDPSend(client,client_addr, packet);
 
 			}
 
 			break;
 
		case NET_UDPCMD_GETSERVERINFO:
 
			if (!client) {
 
				packet.command_check=packet.command_code=NET_UDPCMD_SERVERINFO;
 
				memcpy(&packet.data,&_network_game,sizeof(_network_game));
 
				packet.data_len=sizeof(_network_game);
 
				NetworkUDPSend(client,client_addr, packet);
 
			}
 
			break;
 
		case NET_UDPCMD_SERVERINFO:
 
 			if (client) {
 
				NetworkGameList * item;
 

	
 
				item = (NetworkGameList *) NetworkGameListAdd();
 
				item -> ip = inet_addr(inet_ntoa(client_addr.sin_addr));
 
				item -> port = ntohs(client_addr.sin_port);
 

	
 
				memcpy(item,&packet.data,packet.data_len);
 
 			}
 
 			break;
 
		}
 
	}
 
}
 

	
 
void NetworkUDPBroadCast(bool client, struct UDPPacket packet)
 
{
 
	int i=0, res;
 
	struct sockaddr_in out_addr;
 
	uint32 bcaddr;
 
	byte * bcptr;
 

	
 
	SOCKET udp;
 
	if (client) udp=_udp_client_socket; else udp=_udp_server_socket;
 

	
 
	while (_network_ip_list[i]!=0) {
 
		bcaddr=_network_ip_list[i];
 
		out_addr.sin_family = AF_INET;
 
		if (client) { out_addr.sin_port = htons(_network_server_port); } else { out_addr.sin_port = htons(_network_client_port); };
 
		bcptr = (byte *) &bcaddr;
 
		bcptr[3]=255;
 
		out_addr.sin_addr.s_addr = bcaddr;
 
		res=sendto(udp,(char *) &packet,sizeof(packet),0,(struct sockaddr *) &out_addr,sizeof(out_addr));
 
		if (res==-1) DEBUG(net, 1)("udp: broadcast error: %i",GET_LAST_ERROR());
 
		i++;
 
	}
 

	
 
}
 

	
 
void NetworkUDPSend(bool client, struct sockaddr_in recv,struct UDPPacket packet)
 
{
 
	SOCKET udp;
 
	if (client) udp=_udp_client_socket; else udp=_udp_server_socket;
 

	
 
	sendto(udp,(char *) &packet,sizeof(packet),0,(struct sockaddr *) &recv,sizeof(recv));
 
	NetworkSend();
 
}
 

	
 

	
 
bool NetworkUDPSearchGame(const byte ** _network_detected_serverip, unsigned short * _network_detected_serverport)
 
{
 
	struct UDPPacket packet;
 
	int timeout=3000;
 

	
 
	NetworkGameListClear();
 

	
 
	DEBUG(net, 0) ("[NET][UDP] searching server");
 
	*_network_detected_serverip = "255.255.255.255";
 
	*_network_detected_serverport = 0;
 

	
 
	packet.command_check=packet.command_code=NET_UDPCMD_SERVERSEARCH;
 
	packet.data_len=0;
 
	NetworkUDPBroadCast(true, packet);
 
	while (timeout>=0) {
 
		CSleep(100);
 
		timeout-=100;
 
	    NetworkUDPReceive(true);
 

	
 
		if (_network_game_count>0) {
 
			NetworkGameList * item;
 
			item = (NetworkGameList *) NetworkGameListItem(0);
 
			*_network_detected_serverip=inet_ntoa(*(struct in_addr *) &item->ip);
 
			*_network_detected_serverport=item->port;
 
 			timeout=-1;
 
 			DEBUG(net, 0) ("[NET][UDP] server found on %s", *_network_detected_serverip);
 
 			}
 

	
 
		}
 

	
 
	return (*_network_detected_serverport>0);
 

	
 
}
 

	
 

	
 
// *************************** //
 
// * New Network Core System * //
 
// *************************** //
 

	
 
void NetworkIPListInit()
 
// This tries to launch the network for a given OS
 
void NetworkStartUp(void)
 
{
 
	struct hostent* he = NULL;
 
	char hostname[250];
 
	uint32 bcaddr;
 
	int i=0;
 

	
 
	gethostname(hostname,250);
 
	DEBUG(net, 2) ("[NET][IP] init for host %s", hostname);
 
	he=gethostbyname((char *) hostname);
 

	
 
	if (he == NULL) {
 
		he = gethostbyname("localhost");
 
		}
 

	
 
	if (he == NULL) {
 
		bcaddr = inet_addr("127.0.0.1");
 
		he = gethostbyaddr(inet_ntoa(*(struct in_addr *) &bcaddr), sizeof(bcaddr), AF_INET);
 
		}
 
	DEBUG(net, 3) ("[NET][Core] Starting network...");
 
	// Network is available
 
	_network_available = true;
 
	_network_dedicated = false;
 

	
 
	if (he == NULL) {
 
		DEBUG(net, 2) ("[NET][IP] cannot resolve %s", hostname);
 
	} else {
 
		while(he->h_addr_list[i]) {
 
			bcaddr = inet_addr(inet_ntoa(*(struct in_addr *) he->h_addr_list[i]));
 
			_network_ip_list[i]=bcaddr;
 
			DEBUG(net, 2) ("[NET][IP] add %s",inet_ntoa(*(struct in_addr *) he->h_addr_list[i]));
 
			i++;
 
		}
 

	
 
	}
 
	_network_ip_list[i]=0;
 
	memset(&_network_game_info, 0, sizeof(_network_game_info));
 

	
 
}
 

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

	
 
void NetworkCoreInit()
 
{
 
	DEBUG(net, 3) ("[NET][Core] init()");
 
	_network_available = true;
 
	_network_client_timeout = 300;
 
	_network_ready_ahead = 1;
 

	
 
	// [win32] winsock startup
 

	
 
	// Let's load the network in windows
 
	#if defined(WIN32)
 
	{
 
		WSADATA wsa;
 
		DEBUG(net, 3) ("[NET][Core] using windows socket library");
 
		DEBUG(net, 3) ("[NET][Core] Loading windows socket library");
 
		if (WSAStartup(MAKEWORD(2,0), &wsa) != 0) {
 
			DEBUG(net, 3) ("[NET][Core] error: WSAStartup failed");
 
			_network_available=false;
 
			}
 
			DEBUG(net, 0) ("[NET][Core] Error: WSAStartup failed. Network not available.");
 
			_network_available = false;
 
			return;
 
		}
 
	}
 
	#else
 

	
 
	// [morphos/amigaos] bsd-socket startup
 

	
 
	#if defined(__MORPHOS__) || defined(__AMIGA__)
 
	{
 
		DEBUG(misc,3) ("[NET][Core] using bsd socket library");
 
		if (!(SocketBase = OpenLibrary("bsdsocket.library", 4))) {
 
			DEBUG(net, 3) ("[NET][Core] Couldn't open bsdsocket.library version 4.");
 
			_network_available=false;
 
		#if defined(__MORPHOS__) || defined(__AMIGA__)
 
		{
 
			DEBUG(misc,3) ("[NET][Core] Loading bsd socket library");
 
			if (!(SocketBase = OpenLibrary("bsdsocket.library", 4))) {
 
				DEBUG(net, 0) ("[NET][Core] Error: couldn't open bsdsocket.library version 4. Network not available.");
 
				_network_available = false;
 
				return;
 
			}
 

	
 
		#if !defined(__MORPHOS__)
 
		// for usleep() implementation (only required for legacy AmigaOS builds)
 
		if ( (TimerPort = CreateMsgPort()) ) {
 
			if ( (TimerRequest = (struct timerequest *) CreateIORequest(TimerPort, sizeof(struct timerequest))) ) {
 
				if ( OpenDevice("timer.device", UNIT_MICROHZ, (struct IORequest *) TimerRequest, 0) == 0 ) {
 
					if ( !(TimerBase = TimerRequest->tr_node.io_Device) ) {
 
						// free ressources...
 
						DEBUG(net, 3) ("[NET][Core] Couldn't initialize timer.");
 
						_network_available=false;
 
			#if defined(__AMIGA__)
 
			// for usleep() implementation (only required for legacy AmigaOS builds)
 
			if ( (TimerPort = CreateMsgPort()) ) {
 
				if ( (TimerRequest = (struct timerequest *) CreateIORequest(TimerPort, sizeof(struct timerequest))) ) {
 
					if ( OpenDevice("timer.device", UNIT_MICROHZ, (struct IORequest *) TimerRequest, 0) == 0 ) {
 
						if ( !(TimerBase = TimerRequest->tr_node.io_Device) ) {
 
							// free ressources...
 
							DEBUG(net, 0) ("[NET][Core] Error: couldn't initialize timer. Network not available.");
 
							_network_available = false;
 
							return;
 
						}
 
					}
 
				}
 
			}
 
			#endif // __AMIGA__
 
		}
 
		#endif
 

	
 
	}
 
	#else
 

	
 
	// [linux/macos] unix-socket startup
 

	
 
		DEBUG(net, 3) ("[NET][Core] using unix socket library");
 
		#endif // __MORPHOS__ / __AMIGA__
 
	#endif // WIN32
 

	
 
	#endif
 

	
 
	#endif
 

	
 

	
 
	if (_network_available) {
 
		DEBUG(net, 3) ("[NET][Core] OK: multiplayer available");
 
		// initiate network ip list
 
		NetworkIPListInit();
 
	} else
 
		DEBUG(net, 3) ("[NET][Core] FAILED: multiplayer not available");
 
	NetworkInitialize();
 
	DEBUG(net, 3) ("[NET][Core] Network online. Multiplayer available.");
 
	NetworkFindIPs();
 
}
 

	
 
/* *************************************************** */
 
// This shuts the network down
 
void NetworkShutDown(void)
 
{
 
	DEBUG(net, 3) ("[NET][Core] Shutting down the network.");
 

	
 
void NetworkCoreShutdown()
 
{
 
	DEBUG(net, 3) ("[NET][Core] shutdown()");
 
	_network_available = false;
 

	
 
	#if defined(__MORPHOS__) || defined(__AMIGA__)
 
	{
 
		// free allocated ressources
 
		#if !defined(__MORPHOS__)
 
		if (TimerBase)    { CloseDevice((struct IORequest *) TimerRequest); }
 
		if (TimerRequest) { DeleteIORequest(TimerRequest); }
 
		if (TimerPort)    { DeleteMsgPort(TimerPort); }
 
			if (TimerBase)    { CloseDevice((struct IORequest *) TimerRequest); }
 
			if (TimerRequest) { DeleteIORequest(TimerRequest); }
 
			if (TimerPort)    { DeleteMsgPort(TimerPort); }
 
		#endif
 

	
 
		if (SocketBase) {
 
@@ -1690,290 +1200,15 @@ void NetworkCoreShutdown()
 
	#endif
 

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

	
 
/* *************************************************** */
 

	
 
void ParseConnectionString(const byte **player, const byte **port, byte *connection_string)
 
{
 
	byte c = 0;
 
	while (connection_string[c] != '\0') {
 
		if (connection_string[c] == '#') {
 
			*player = &connection_string[c+1];
 
			connection_string[c] = '\0';
 
		}
 
		if (connection_string[c] == ':') {
 
			*port = &connection_string[c+1];
 
			connection_string[c] = '\0';
 
		}
 
		c++;
 
	}
 
}
 

	
 
bool NetworkCoreConnectGame(const byte* b, unsigned short port)
 
{
 
	if (!_network_available) return false;
 

	
 
	if (strcmp(b,"auto")==0) {
 
		// do autodetect
 
		NetworkUDPSearchGame(&b, &port);
 
	}
 

	
 
	if (port==0) {
 
		// autodetection failed
 
		if (_networking_override) NetworkLobbyShutdown();
 
		ShowErrorMessage(-1, STR_NETWORK_ERR_NOSERVER, 0, 0);
 
		_switch_mode_errorstr = STR_NETWORK_ERR_NOSERVER;
 
		return false;
 
	}
 

	
 
	NetworkInitialize();
 
	_networking = NetworkConnect(b, port);
 
	if (_networking) {
 
		NetworkLobbyShutdown();
 
		IConsoleCmdExec("exec scripts/on_client.scr 0");
 
	} else {
 
		if (_networking_override)
 
			NetworkLobbyShutdown();
 

	
 
		ShowErrorMessage(-1, STR_NETWORK_ERR_NOCONNECTION,0,0);
 
		_switch_mode_errorstr = STR_NETWORK_ERR_NOCONNECTION;
 
	}
 
	return _networking;
 
}
 

	
 
/* *************************************************** */
 

	
 
bool NetworkCoreConnectGameStruct(NetworkGameList * item)
 
{
 
	return NetworkCoreConnectGame(inet_ntoa(*(struct in_addr *) &item->ip),item->port);
 
}
 

	
 
/* *************************************************** */
 

	
 
bool NetworkCoreStartGame()
 
{
 
	if (!_network_available) return false;
 
	NetworkLobbyShutdown();
 
	NetworkInitialize();
 
	NetworkListen();
 
	NetworkUDPListen(false);
 
	_networking_server = true;
 
	_networking = true;
 
	NetworkGameFillDefaults(); // clears the network game info
 
	_network_game.players_on++; // the serverplayer is online
 
	// execute server initialization script
 
	IConsoleCmdExec("exec scripts/on_server.scr 0");
 
	return true;
 
}
 

	
 
/* *************************************************** */
 

	
 
void NetworkCoreDisconnect()
 
{
 
	/* terminate server */
 
	if (_networking_server) {
 
		NetworkUDPClose(false);
 
		NetworkClose(false);
 
		}
 

	
 
	/* terminate client connection */
 
	else if (_networking) {
 
		NetworkClose(true);
 
		}
 

	
 
	NetworkShutdown();
 
}
 

	
 
/* *************************************************** */
 

	
 
void NetworkCoreLoop(bool incomming)
 
{
 
	if (incomming) {
 
		// incomming
 
		if ( _udp_client_socket != INVALID_SOCKET ) NetworkUDPReceive(true);
 
		if ( _udp_server_socket != INVALID_SOCKET ) NetworkUDPReceive(false);
 

	
 
		if (_networking)
 
			NetworkReceive();
 

	
 
	} else {
 
		if ( _udp_client_socket != INVALID_SOCKET ) NetworkUDPReceive(true);
 
		if ( _udp_server_socket != INVALID_SOCKET ) NetworkUDPReceive(false);
 

	
 
		if (_networking)
 
			NetworkSend();
 
	}
 
}
 

	
 
void NetworkLobbyInit()
 
{
 
	DEBUG(net, 3) ("[NET][Lobby] init()");
 
	NetworkUDPListen(true);
 
}
 

	
 
void NetworkLobbyShutdown()
 
{
 
	DEBUG(net, 3) ("[NET][Lobby] shutdown()");
 
	NetworkUDPClose(true);
 
}
 

	
 

	
 
// ******************************** //
 
// * Network Game List Extensions * //
 
// ******************************** //
 

	
 
void NetworkGameListClear()
 
{
 
	NetworkGameList * item;
 
	NetworkGameList * next;
 

	
 
	DEBUG(net, 4) ("[NET][G-List] cleared server list");
 

	
 
	item = _network_game_list;
 
#else
 

	
 
	while (item != NULL) {
 
		next = (NetworkGameList *) item -> _next;
 
		free (item);
 
		item = next;
 
	}
 
	_network_game_list=NULL;
 
	_network_game_count=0;
 
}
 

	
 
NetworkGameList * NetworkGameListAdd()
 
{
 
	NetworkGameList * item;
 
	NetworkGameList * before;
 

	
 
	DEBUG(net, 4) ("[NET][G-List] added server to list");
 

	
 
	item = _network_game_list;
 
	before = item;
 
	while (item != NULL) {
 
		before = item;
 
		item = (NetworkGameList *) item -> _next;
 
	}
 

	
 
	item = malloc(sizeof(NetworkGameList));
 
	item -> _next = NULL;
 

	
 
	if (before == NULL) {
 
		_network_game_list = item;
 
	} else
 
		before -> _next = item;
 

	
 
	_network_game_count++;
 
	return item;
 
}
 

	
 
void NetworkGameListFromLAN()
 
{
 
	struct UDPPacket packet;
 
	DEBUG(net, 2) ("[NET][G-List] searching server over lan");
 
	NetworkGameListClear();
 
	packet.command_check=packet.command_code=NET_UDPCMD_SERVERSEARCH;
 
	packet.data_len=0;
 
	NetworkUDPBroadCast(true,packet);
 
}
 

	
 
void NetworkGameListFromInternet()
 
{
 
	DEBUG(net, 2) ("[NET][G-List] searching servers over internet");
 
	NetworkGameListClear();
 

	
 
	// **TODO** masterserver communication [internet protocol list]
 
}
 

	
 
NetworkGameList * NetworkGameListItem(uint16 index)
 
{
 
	NetworkGameList * item;
 
	NetworkGameList * next;
 
	uint16 cnt = 0;
 

	
 
	item = _network_game_list;
 

	
 
	while ((item != NULL) && (cnt != index)) {
 
		next = (NetworkGameList *) item -> _next;
 
		item = next;
 
		cnt++;
 
	}
 

	
 
	return item;
 
}
 
void ParseConnectionString(const byte **player, const byte **port, byte *connection_string) {}
 
void NetworkUpdateClientInfo(uint16 client_index) {}
 

	
 
// *************************** //
 
// * Network Game Extensions * //
 
// *************************** //
 

	
 
void NetworkGameFillDefaults()
 
{
 
	NetworkGameInfo * game = &_network_game;
 
	#if defined(WITH_REV)
 
		extern char _openttd_revision[];
 
	#else
 
		const char _openttd_revision[] = "norev000";
 
	#endif
 

	
 
	DEBUG(net, 4) ("[NET][G-Info] setting defaults");
 

	
 
	ttd_strlcpy(game->server_name, "OpenTTD Game", sizeof(game->server_name));
 
	game->game_password[0]='\0';
 
	game->map_name[0]='\0';
 
	ttd_strlcpy(game->server_revision, _openttd_revision, sizeof(game->server_revision));
 
	game->game_date=0;
 

	
 
	game->map_height=0;
 
	game->map_width=0;
 
	game->map_set=0;
 

	
 
	game->players_max=8;
 
	game->players_on=0;
 

	
 
	game->server_lang=_dynlang.curr;
 
}
 

	
 
void NetworkGameChangeDate(uint16 newdate)
 
{
 
	if (_networking_server)
 
		_network_game.game_date = newdate;
 
}
 

	
 
#else // not ENABLE_NETWORK
 

	
 
// stubs
 
void NetworkInitialize() {}
 
void NetworkShutdown() {}
 
void NetworkListen() {}
 
void NetworkConnect(const char *hostname, int port) {}
 
void NetworkReceive() {}
 
void NetworkSend() {}
 
void NetworkSendCommand(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback) {}
 
void NetworkSendEvent(uint16 type, uint16 data_len, void * data) {};
 
void NetworkProcessCommands() {}
 
void NetworkStartSync(bool fcreset) {}
 
void NetworkSendReadyPacket() {}
 
void NetworkSendSyncPackets() {}
 
void NetworkSendFrameSyncPackets() {}
 
bool NetworkCheckClientReady() { return true; }
 
void NetworkCoreInit() { _network_available=false; };
 
void NetworkCoreShutdown() {};
 
void NetworkCoreDisconnect() {};
 
void NetworkCoreLoop(bool incomming) {};
 
void ParseConnectionString(const byte **player, const byte **port, byte *connection_string) {};
 
bool NetworkCoreConnectGame(const byte* b, unsigned short port) {return false;};
 
bool NetworkCoreStartGame() {return false;};
 
void NetworkLobbyShutdown() {};
 
void NetworkLobbyInit() {};
 
void NetworkGameListClear() {};
 
NetworkGameList * NetworkGameListAdd() {return NULL;};
 
void NetworkGameListFromLAN() {};
 
void NetworkGameListFromInternet() {};
 
void NetworkGameFillDefaults() {};
 
NetworkGameList * NetworkGameListItem(uint16 index) {return NULL;};
 
bool NetworkCoreConnectGameStruct(NetworkGameList * item) {return false;};
 
void NetworkGameChangeDate(uint16 newdate) {};
 

	
 
#endif
 
#endif /* ENABLE_NETWORK */
network.h
Show inline comments
 
#ifndef NETWORK_H
 
#define NETWORK_H
 

	
 
#include "network_core.h"
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
// 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 NETWORK_DISCOVER_PORT 3978
 
#define NETWORK_DEFAULT_PORT 3979
 

	
 
#define MAX_INTERFACES 9
 

	
 

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

	
 
#define NETWORK_NAME_LENGTH 80
 
#define NETWORK_HOSTNAME_LENGTH 80
 
#define NETWORK_REVISION_LENGTH 10
 
#define NETWORK_PASSWORD_LENGTH 20
 
#define NETWORK_PLAYERS_LENGTH 200
 

	
 
// 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 {
 
	char server_name[40];			// name of the game
 
	char server_revision[8];	// server game version
 
	byte server_lang;					// langid
 
	byte players_max;					// max players allowed on server
 
	byte players_on;					// current count of players on server
 
	uint16 game_date;					// current date
 
	char game_password[10];		// should fit ... 10 chars
 
	char map_name[40];				// map which is played ["random" for a randomized map]
 
	uint map_width;						// map width / 8
 
	uint map_height;					// map height / 8
 
	byte map_set;							// graphical set
 
	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 SVN version number the server is using (e.g.: 'r304')
 
																									//  It even shows a SVN version in release-version, so
 
																									//  it is easy to compare if a server is of the correct version
 
	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 spectators_on;															// How many spectators do we have?
 
	uint16 game_date;																// Current date
 
	uint16 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?
 
} NetworkGameInfo;
 

	
 
//typedef struct NetworkGameList;
 
typedef struct NetworkPlayerInfo {
 
	char company_name[NETWORK_NAME_LENGTH];					// Company name
 
	char password[NETWORK_PASSWORD_LENGTH];					// The password for the player
 
	byte 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?
 
	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, ..)
 
} NetworkPlayerInfo;
 

	
 
typedef struct NetworkClientInfo {
 
	uint16 client_index;														// Index of the client (same as ClientState->index)
 
	char client_name[NETWORK_NAME_LENGTH];					// Name of the client
 
	byte client_lang;																// The language of the client
 
	byte client_playas;															// As which player is this client playing
 
	uint32 client_ip;																// IP-address of the client (so he can be banned)
 
	uint16 join_date;																// Gamedate the player has joined
 
} NetworkClientInfo;
 

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

	
 
enum {
 
	NET_EVENT_SUBSIDY = 0,
 
};
 
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_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_NAME_LENGTH];
 
VARDEF char _network_default_ip[NETWORK_HOSTNAME_LENGTH];
 

	
 
VARDEF uint16 _network_own_client_index;
 

	
 
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
 

	
 
// networking settings
 
VARDEF uint32 _network_ip_list[MAX_INTERFACES + 1]; // Network IPs
 
VARDEF uint16 _network_game_count;
 

	
 
NetworkGameInfo _network_game;
 
NetworkGameList * _network_game_list;
 
VARDEF uint16 _network_lobby_company_count;
 

	
 
VARDEF uint _network_client_port;
 
VARDEF uint _network_server_port;
 
VARDEF bool _is_network_server; // Does this client wants to be a network-server?
 
VARDEF char _network_server_name[NETWORK_NAME_LENGTH];
 

	
 
VARDEF uint16 _network_sync_freq;
 
VARDEF uint8 _network_frame_freq;
 

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

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

	
 
VARDEF bool _network_udp_server;
 
VARDEF uint16 _network_udp_broadcast;
 

	
 
#endif /* ENABLE_NETWORK */
 

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

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

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

	
 
#ifdef ENABLE_NETWORK
 

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

	
 

	
 
// This file handles all the client-commands
 

	
 

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

	
 
static uint32 last_ack_frame;
 

	
 
void NetworkRecvPatchSettings(Packet *p);
 

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

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

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

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

	
 
#if defined(WITH_REV)
 
		extern char _openttd_revision[];
 
#else
 
		const char _openttd_revision[] = "norev000";
 
#endif
 
	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); // Password
 
	NetworkSend_uint8(p, NETLANG_ANY); // Language
 
	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
 
	//    uint32: decode_params
 
	//      10 times the last one (lengthof(cp->dp))
 
	//    uint8:  CallBackID (see callback_table.c)
 
	//
 

	
 
	int i;
 
	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);
 
	for (i = 0; i < lengthof(cp->dp); i++) {
 
		NetworkSend_uint32(p, cp->dp[i]);
 
	}
 
	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 desttype, 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, desttype);
 
	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);
 
}
 

	
 

	
 
// **********
 
// 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_COMPANY_INFO)
 
{
 
	byte company_info_version;
 
	int i;
 

	
 
	company_info_version = NetworkRecv_uint8(p);
 

	
 
	if (company_info_version == 1) {
 
		byte total;
 
		byte current;
 

	
 
		total = NetworkRecv_uint8(p);
 
		_network_lobby_company_count = total;
 

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

	
 
		current = NetworkRecv_uint8(p) - 1;
 
		if (current >= MAX_PLAYERS)
 
			return NETWORK_RECV_STATUS_CLOSE_QUERY;
 

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

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

	
 
		InvalidateWindow(WC_NETWORK_WINDOW, 0);
 

	
 
		if (total == current + 1)
 
			// This was the last one
 
			return NETWORK_RECV_STATUS_CLOSE_QUERY;
 
		else
 
			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(p);
 

	
 
	ci = NetworkFindClientInfoFromIndex(index);
 
	if (ci != NULL) {
 
		byte playas;
 
		char name[NETWORK_NAME_LENGTH];
 

	
 
		playas = NetworkRecv_uint8(p);
 
		NetworkRecv_string(p, name, sizeof(name));
 

	
 
		if (playas == ci->client_playas && strcmp(name, ci->client_name) != 0) {
 
			// Client name changed, display the change
 
			NetworkTextMessage(NETWORK_ACTION_NAME_CHANGE, 1, ci->client_name, 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));
 
		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 = NetworkRecv_uint8(p);
 
		NetworkRecv_string(p, ci->client_name, sizeof(ci->client_name));
 
		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(p);
 

	
 
	if (error == NETWORK_ERROR_NOT_AUTHORIZED || error == NETWORK_ERROR_NOT_EXPECTED ||
 
			error == NETWORK_ERROR_PLAYER_MISMATCH) {
 
		// We made an error in the protocol, and our connection is closed.... :(
 
		_switch_mode_errorstr = STR_NETWORK_ERR_SERVER_ERROR;
 
	} else if (error == NETWORK_ERROR_WRONG_REVISION) {
 
		// Wrong revision :(
 
		_switch_mode_errorstr = STR_NETWORK_ERR_WRONG_REVISION;
 
	} else if (error == NETWORK_ERROR_WRONG_PASSWORD) {
 
		// Wrong password
 
		_switch_mode_errorstr = STR_NETWORK_ERR_WRONG_PASSWORD;
 
	} else if (error == NETWORK_ERROR_KICKED) {
 
		_switch_mode_errorstr = STR_NETWORK_ERR_KICKED;
 
	}
 

	
 
	DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 

	
 
	return NETWORK_RECV_STATUS_SERVER_ERROR;
 
}
 

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

	
 
	if (type == NETWORK_GAME_PASSWORD) {
 
		ShowNetworkNeedGamePassword();
 
		return NETWORK_RECV_STATUS_OKAY;
 
	} else if (type == NETWORK_COMPANY_PASSWORD) {
 
		ShowNetworkNeedCompanyPassword();
 
		return NETWORK_RECV_STATUS_OKAY;
 
	}
 

	
 
	return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_WELCOME)
 
{
 
	_network_own_client_index = NetworkRecv_uint16(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(p);
 
	InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0);
 

	
 
	// We are put on hold for receiving the map.. we need GUI for this ;)
 
	DEBUG(net, 1)("[NET] The server is currently busy sending the map to someone else.. please hold..." );
 
	DEBUG(net, 1)("[NET]  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(p);
 

	
 
	// First packet, init some stuff
 
	if (maptype == MAP_PACKET_START) {
 
		// The name for the temp-map
 
		sprintf(filename, "%s%snetwork_client.tmp",  _path.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(p);
 

	
 
		_network_join_status = NETWORK_JOIN_STATUS_DOWNLOADING;
 
		_network_join_kbytes = 0;
 
		_network_join_kbytes_total = NetworkRecv_uint32(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);
 
	}
 

	
 
	if (maptype == MAP_PACKET_PATCH) {
 
		NetworkRecvPatchSettings(p);
 
	}
 

	
 
	// Check if this was the last packet
 
	if (maptype == MAP_PACKET_END) {
 
		// We also get, very nice, the player_seeds in this packet
 
		int i;
 
		for (i = 0; i < MAX_PLAYERS; i++) {
 
			_player_seeds[i][0] = NetworkRecv_uint32(p);
 
			_player_seeds[i][1] = NetworkRecv_uint32(p);
 
		}
 

	
 
		fclose(file_pointer);
 

	
 
		_network_join_status = NETWORK_JOIN_STATUS_PROCESSING;
 
		InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0);
 

	
 
		// The map is done downloading, load it
 
		// Load the map
 
		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;
 
		}
 
		_opt_mod_ptr = &_opt;
 

	
 
		DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 

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

	
 
		if (_network_playas == 0 || _network_playas > MAX_PLAYERS ||
 
				!DEREF_PLAYER(_network_playas - 1)->is_active) {
 

	
 
			if (_network_playas == OWNER_SPECTATOR) {
 
				// The client wants to be a spectator..
 
				_local_player = OWNER_SPECTATOR;
 
			} else {
 
				// send a command to make a new player
 
				_local_player = 0;
 
				NetworkSend_Command(0, 0, 0, CMD_PLAYER_CTRL, NULL);
 
				_local_player = OWNER_SPECTATOR;
 
			}
 
		} else {
 
			// take control over an existing company
 
			_local_player = _network_playas - 1;
 
		}
 

	
 
		// Remeber the player
 
		if (_local_player != OWNER_SPECTATOR)
 
			_network_playas = _local_player + 1;
 
		else
 
			_network_playas = OWNER_SPECTATOR;
 
	}
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_FRAME)
 
{
 
	_frame_counter_server = NetworkRecv_uint32(p);
 
	_frame_counter_max = NetworkRecv_uint32(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(p);
 
#ifdef NETWORK_SEND_DOUBLE_SEED
 
		_sync_seed_2 = NetworkRecv_uint32(p);
 
#endif
 
	}
 
#endif
 
	DEBUG(net, 7)("[NET] 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,6)("[NET] 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(p);
 
	_sync_seed_1 = NetworkRecv_uint32(p);
 
#ifdef NETWORK_SEND_DOUBLE_SEED
 
	_sync_seed_2 = NetworkRecv_uint32(p);
 
#endif
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_COMMAND)
 
{
 
	int i;
 
	CommandPacket *cp = malloc(sizeof(CommandPacket));
 
	cp->player = NetworkRecv_uint8(p);
 
	cp->cmd = NetworkRecv_uint32(p);
 
	cp->p1 = NetworkRecv_uint32(p);
 
	cp->p2 = NetworkRecv_uint32(p);
 
	cp->tile = NetworkRecv_uint32(p);
 
	for (i = 0; i < lengthof(cp->dp); i++)
 
		cp->dp[i] = NetworkRecv_uint32(p);
 
	cp->callback = NetworkRecv_uint8(p);
 
	cp->frame = NetworkRecv_uint32(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)
 
{
 
	NetworkAction action = NetworkRecv_uint8(p);
 
	char msg[MAX_TEXT_MSG_LEN];
 
	NetworkClientInfo *ci, *ci_to;
 
	uint16 index;
 
	char name[NETWORK_NAME_LENGTH];
 

	
 
	index = NetworkRecv_uint16(p);
 
	NetworkRecv_string(p, msg, MAX_TEXT_MSG_LEN);
 

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

	
 
	if (action == NETWORK_ACTION_CHAT_TO_CLIENT) {
 
		snprintf(name, 80, "%s", ci_to->client_name);
 
		ci = NetworkFindClientInfoFromIndex(_network_own_client_index);
 
	} else if (action == NETWORK_ACTION_CHAT_TO_PLAYER) {
 
		GetString(name, DEREF_PLAYER(ci_to->client_playas-1)->name_1);
 
		ci = NetworkFindClientInfoFromIndex(_network_own_client_index);
 
	} else {
 
		snprintf(name, 80, "%s", ci_to->client_name);
 
		ci = ci_to;
 
	}
 

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

	
 
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_ERROR_QUIT)
 
{
 
	int errorno;
 
	char str1[100], str2[100];
 
	uint16 index;
 
	NetworkClientInfo *ci;
 

	
 
	index = NetworkRecv_uint16(p);
 
	errorno = NetworkRecv_uint8(p);
 

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

	
 
	ci = NetworkFindClientInfoFromIndex(index);
 
	if (ci != NULL) {
 
		NetworkTextMessage(NETWORK_ACTION_JOIN_LEAVE, 1, ci->client_name, "%s (%s)", str1, str2);
 

	
 
		// 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 str1[100], str2[100];
 
	uint16 index;
 
	NetworkClientInfo *ci;
 

	
 
	index = NetworkRecv_uint16(p);
 
	NetworkRecv_string(p, str2, 100);
 

	
 
	GetString(str1, STR_NETWORK_ERR_LEFT);
 

	
 
	ci = NetworkFindClientInfoFromIndex(index);
 
	if (ci != NULL) {
 
		NetworkTextMessage(NETWORK_ACTION_JOIN_LEAVE, 1, ci->client_name, "%s (%s)", str1, str2);
 

	
 
		// The client is gone, give the NetworkClientInfo free
 
		ci->client_index = NETWORK_EMPTY_INDEX;
 
	} else {
 
		DEBUG(net, 0)("[NET] Error - 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)
 
{
 
	char str1[100];
 
	uint16 index;
 
	NetworkClientInfo *ci;
 

	
 
	index = NetworkRecv_uint16(p);
 

	
 
	GetString(str1, STR_NETWORK_CLIENT_JOINED);
 

	
 
	ci = NetworkFindClientInfoFromIndex(index);
 
	if (ci != NULL) {
 
		NetworkTextMessage(NETWORK_ACTION_JOIN_LEAVE, 1, ci->client_name, "%s", str1);
 
	}
 

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

	
 

	
 

	
 

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

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

	
 
extern const SettingDesc patch_settings[];
 

	
 
// This is a TEMPORARY solution to get the patch-settings
 
//  to the client. When the patch-settings are saved in the savegame
 
//  this should be removed!!
 
void NetworkRecvPatchSettings(Packet *p)
 
{
 
	const SettingDesc *item;
 

	
 
	item = patch_settings;
 

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

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

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

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

	
 
		free(p);
 
	}
 

	
 
	return res;
 
}
 

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

	
 
#ifdef ENABLE_NETWORK
 

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

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

	
 
#endif /* ENABLE_NETWORK */
 

	
 
#endif // NETWORK_CLIENT_H
network_core.h
Show inline comments
 
new file 100644
 
#ifndef NETWORK_CORE_H
 
#define NETWORK_CORE_H
 

	
 
// Network stuff has many things that needs to be included
 
//  by default. All those things are in this file.
 

	
 
// =============================
 
// Include standard stuff per OS
 

	
 
// Windows stuff
 
#if defined(WIN32)
 
#	include <windows.h>
 
#	include <winsock2.h>
 
#	include <ws2tcpip.h>
 
#	pragma comment (lib, "ws2_32.lib")
 
#	define ENABLE_NETWORK // On windows, the network is always enabled
 
#	define GET_LAST_ERROR() WSAGetLastError()
 
#	define EWOULDBLOCK WSAEWOULDBLOCK
 
// Windows has some different names for some types..
 
typedef SSIZE_T ssize_t;
 
typedef unsigned long in_addr_t;
 
typedef INTERFACE_INFO IFREQ;
 
#endif // WIN32
 

	
 
// UNIX stuff
 
#if defined(UNIX)
 
#	define SOCKET int
 
#	define INVALID_SOCKET -1
 
typedef struct ifreq IFREQ;
 
#	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>
 
#		include <ifaddrs.h>
 
// If for any reason ifaddrs.h does not exist on a system, remove define below
 
//   and an other system will be used to fetch ips from the system
 
#		define HAVE_GETIFADDRS
 
#	endif // BEOS_NET_SERVER
 
#	include <errno.h>
 
#	include <sys/time.h>
 
#	include <netdb.h>
 
#endif // UNIX
 

	
 
// 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> 	// FION#? defines
 
#	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)
 

	
 
	struct Library *SocketBase = NULL;
 
#endif // __MORPHOS__ || __AMIGA__
 

	
 
#endif // NETWORK_CORE_H
network_data.c
Show inline comments
 
new file 100644
 
#include "stdafx.h"
 
#include "network_data.h"
 

	
 
// Is the network enabled?
 
#ifdef ENABLE_NETWORK
 

	
 
#include "table/strings.h"
 
#include "network_client.h"
 
#include "command.h"
 
#include "callback_table.h"
 

	
 
// This files handles the send/receive of all packets
 

	
 
// Create a packet for sending
 
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;
 
}
 

	
 
// The next couple of functions make sure we can send
 
//  uint8, uint16, uint32 and uint64 endian-safe
 
//  over the network. The order it uses is:
 
//
 
//  1 2 3 4
 
//
 

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

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

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

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

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

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

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

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

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

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

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

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

	
 
		return NETWORK_RECV_STATUS_CONN_LOST;
 
	}
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

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

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

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

	
 
		p->pos += res;
 

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

	
 
	return true;
 
}
 

	
 

	
 
// Receiving commands
 
// Again, the next couple of functions are endian-safe
 
//  see the comment around NetworkSend_uint8 for more info.
 
uint8 NetworkRecv_uint8(Packet *packet)
 
{
 
	return packet->buffer[packet->pos++];
 
}
 

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

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

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

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

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

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

	
 
	*status = NETWORK_RECV_STATUS_OKAY;
 

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

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

	
 
	p = cs->packet_recv;
 

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

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

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

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

	
 
		p->pos += res;
 
	}
 

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

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

	
 
	return p;
 
}
 

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

	
 
	*new_cp = *cp;
 

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

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

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

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

	
 
	temp_callback = 0;
 

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

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

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

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

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

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

	
 
		return;
 
	}
 

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

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

	
 
#endif /* ENABLE_NETWORK */
network_data.h
Show inline comments
 
new file 100644
 
#ifndef NETWORK_DATA_H
 
#define NETWORK_DATA_H
 

	
 
#include "ttd.h"
 
#include "network.h"
 

	
 
// Is the network enabled?
 
#ifdef ENABLE_NETWORK
 

	
 
#define SEND_MTU 1460
 
#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
 

	
 
// What version of game-info do we use?
 
#define NETWORK_GAME_INFO_VERSION 1
 
// What version of company info is this?
 
#define NETWORK_COMPANY_INFO_VERSION 1
 

	
 
typedef uint16 PacketSize;
 

	
 
typedef struct Packet {
 
	struct Packet *next;
 
	PacketSize size;
 
	PacketSize pos;
 
	byte buffer[SEND_MTU];
 
} Packet;
 

	
 
typedef struct CommandPacket {
 
	struct CommandPacket *next;
 
	byte player;
 
	uint32 cmd;
 
	uint32 p1;
 
	uint32 p2;
 
	uint32 tile; // Always make it uint32, so it is bigmap compatible
 
	uint32 dp[20]; // decode_params
 
	uint32 frame; // In which frame must this packet be executed?
 
	byte callback;
 
} 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_PATCH,
 
	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_CLOSE_QUERY, // Done quering the server
 
} NetworkRecvStatus;
 

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

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

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

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

	
 
typedef enum {
 
	NETWORK_GAME_PASSWORD,
 
	NETWORK_COMPANY_PASSWORD,
 
} NetworkPasswordType;
 

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

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

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

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

	
 
// What packet types are there
 
// WARNING: The first 3 packets can NEVER change order again
 
//   it protects old clients from joining newer servers (because SERVER_ERROR
 
//   is the respond to a wrong revision)
 
typedef enum {
 
	PACKET_SERVER_FULL,
 
	PACKET_CLIENT_JOIN,
 
	PACKET_SERVER_ERROR,
 
	PACKET_CLIENT_COMPANY_INFO,
 
	PACKET_SERVER_COMPANY_INFO,
 
	PACKET_SERVER_CLIENT_INFO,
 
	PACKET_SERVER_NEED_PASSWORD,
 
	PACKET_CLIENT_PASSWORD,
 
	PACKET_SERVER_WELCOME,
 
	PACKET_CLIENT_GETMAP,
 
	PACKET_SERVER_WAIT,
 
	PACKET_SERVER_MAP,
 
	PACKET_CLIENT_MAP_OK,
 
	PACKET_SERVER_JOIN,
 
	PACKET_SERVER_FRAME,
 
	PACKET_SERVER_SYNC,
 
	PACKET_CLIENT_ACK,
 
	PACKET_CLIENT_COMMAND,
 
	PACKET_SERVER_COMMAND,
 
	PACKET_CLIENT_CHAT,
 
	PACKET_SERVER_CHAT,
 
	PACKET_CLIENT_SET_PASSWORD,
 
	PACKET_CLIENT_SET_NAME,
 
	PACKET_CLIENT_QUIT,
 
	PACKET_CLIENT_ERROR,
 
	PACKET_SERVER_QUIT,
 
	PACKET_SERVER_ERROR_QUIT,
 
	PACKET_SERVER_SHUTDOWN,
 
	PACKET_SERVER_NEWGAME,
 
	PACKET_END // Should ALWAYS be on the end of this list!! (period)
 
} PacketType;
 

	
 
typedef enum {
 
	DESTTYPE_BROADCAST,
 
	DESTTYPE_PLAYER,
 
	DESTTYPE_CLIENT
 
} DestType;
 

	
 
CommandPacket *_local_command_queue;
 

	
 
SOCKET _udp_client_socket; // udp client socket
 

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

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

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

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

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

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

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

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

	
 
#endif /* ENABLE_NETWORK */
 

	
 
#endif // NETWORK_DATA_H
network_gamelist.c
Show inline comments
 
new file 100644
 
#include "stdafx.h"
 
#include "network_data.h"
 

	
 
#ifdef ENABLE_NETWORK
 

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

	
 
extern void UpdateNetworkGameWindow(bool unselect);
 

	
 
void NetworkGameListClear(void)
 
{
 
	NetworkGameList *item;
 
	NetworkGameList *next;
 

	
 
	item = _network_game_list;
 

	
 
	while (item != NULL) {
 
		next = item->next;
 
		free(item);
 
		item = next;
 
	}
 
	_network_game_list = NULL;
 
	_network_game_count = 0;
 

	
 
	UpdateNetworkGameWindow(true);
 

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

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

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

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

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

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

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

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

	
 
	UpdateNetworkGameWindow(false);
 

	
 
	return item;
 
}
 

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

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

	
 
	UpdateNetworkGameWindow(false);
 
}
 

	
 
#endif /* ENABLE_NETWORK */
 

	
 

	
network_gamelist.h
Show inline comments
 
new file 100644
 
#ifndef NETWORK_GAMELIST_H
 
#define NETWORK_GAMELIST_H
 

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

	
 
#endif /* NETWORK_GAMELIST_H */
network_gui.c
Show inline comments
 
#include "stdafx.h"
 
#include "ttd.h"
 
#include "network.h"
 
#include "saveload.h"
 

	
 
#include "hal.h" // for file list
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "table/strings.h"
 
#include "network_data.h"
 
#include "window.h"
 
#include "gui.h"
 
#include "gfx.h"
 
#include "command.h"
 
#include "network.h"
 
#include "functions.h"
 
#include "variables.h"
 
#include "network_server.h"
 
#include "network_udp.h"
 

	
 
#define BGC 5
 
#define BTC 15
 
#define MAX_QUERYSTR_LEN 64
 
static byte _edit_str_buf[MAX_QUERYSTR_LEN*2];
 
static void ShowNetworkStartServerWindow();
 
#if 0
 
static void ShowNetworkLobbyWindow();
 
#endif
 
static void ShowNetworkStartServerWindow(void);
 
static void ShowNetworkLobbyWindow(void);
 

	
 
static byte _selected_field;
 

	
 
@@ -24,148 +33,293 @@ static const StringID _connection_types_
 
	INVALID_STRING_ID
 
};
 

	
 
/* Should be _network_game->players_max but since network is not yet really done
 
* we'll just use some dummy here
 
* network.c -->> static NetworkGameInfo _network_game;
 
*/
 
static byte _players_max;
 
/* Should be ??????????? (something) but since network is not yet really done
 
* we'll just use some dummy here
 
*/
 
static byte _network_connection;
 
static uint16 _network_game_count_last;
 

	
 
static StringID _str_map_name, _str_game_name, _str_server_version, _str_server_address;
 

	
 
enum {
 
  NET_PRC__OFFSET_TOP_WIDGET	= 93,
 
	NET_PRC__SIZE_OF_ROW				= 14,
 
	NET_PRC__OFFSET_TOP_WIDGET					= 74,
 
	NET_PRC__OFFSET_TOP_WIDGET_COMPANY	= 42,
 
	NET_PRC__SIZE_OF_ROW								= 14,
 
	NET_PRC__SIZE_OF_ROW_COMPANY				= 12,
 
};
 

	
 
static NetworkGameList *selected_item = NULL;
 
static NetworkGameList *_selected_item = NULL;
 
static int8 _selected_company_item = -1;
 

	
 
#ifdef WITH_REV
 
extern char _openttd_revision[];
 
#endif
 

	
 
// Truncates a string to max_width (via GetStringWidth) and adds 3 dots
 
//  at the end of the name.
 
static void NetworkTruncateString(char *name, const int max_width)
 
{
 
	char temp[NETWORK_NAME_LENGTH];
 
	char internal_name[NETWORK_NAME_LENGTH];
 

	
 
	ttd_strlcpy(internal_name, name, sizeof(internal_name));
 

	
 
	if (GetStringWidth(internal_name) > max_width) {
 
		// Servername is too long, trunc it!
 
		snprintf(temp, sizeof(temp), "%s...", internal_name);
 
		// Continue to delete 1 char of the string till it is in range
 
		while (GetStringWidth(temp) > max_width) {
 
			internal_name[strlen(internal_name) - 1] = '\0';
 
			snprintf(temp, sizeof(temp), "%s...", internal_name);
 
		}
 
		ttd_strlcpy(name, temp, sizeof(temp));
 
	}
 
}
 

	
 
static void NetworkGameWindowWndProc(Window *w, WindowEvent *e)
 
{
 
	switch(e->event) {
 
	case WE_PAINT: {
 
		if (_selected_item == NULL)
 
			w->disabled_state = (1<<17) | (1<<18);
 
		else if (!_selected_item->online)
 
			w->disabled_state = (1<<17); // Server offline, join button disabled
 
		else if (_selected_item->info.clients_on == _selected_item->info.clients_max)
 
			w->disabled_state = (1<<17); // Server full, join button disabled
 
		else
 
			w->disabled_state = 0;
 

	
 
		SetDParam(0, 0x00);
 
		SetDParam(2, STR_NETWORK_LAN + _network_connection);
 
		DrawWindowWidgets(w);
 

	
 
		DrawEditBox(w, 6);
 
		DrawEditBox(w, 3);
 

	
 
		DrawString(9, 43, STR_NETWORK_PLAYER_NAME, 2);
 
		DrawString(9, 63, STR_NETWORK_SELECT_CONNECTION, 2);
 
		DrawString(9, 23, STR_NETWORK_PLAYER_NAME, 2);
 
		DrawString(9, 43, STR_NETWORK_CONNECTION, 2);
 

	
 
		DrawString(15, 82, STR_NETWORK_GAME_NAME, 2);
 
		DrawString(238, 82, STR_NETWORK_PLAYERS, 2);
 
		DrawString(288, 82, STR_NETWORK_MAP_SIZE, 2);
 
		DrawString(15, 63, STR_NETWORK_GAME_NAME, 2);
 
		DrawString(135, 63, STR_NETWORK_CLIENTS_CAPTION, 2);
 

	
 
		{ // draw list of games
 
			uint16 y = NET_PRC__OFFSET_TOP_WIDGET + 3;
 
			int32 n = 0;
 
			NetworkGameList *cur_item = _network_game_list;
 
			char servername[NETWORK_NAME_LENGTH];
 
			const NetworkGameList *cur_item = _network_game_list;
 
			while (cur_item != NULL) {
 
				if (cur_item == selected_item)
 
					GfxFillRect(11, y - 2, 380, y + 9, 10); // show highlighted item with a different colour
 
#ifdef WITH_REV
 
				bool compatible = (strncmp(cur_item->info.server_revision, _openttd_revision, 10) == 0);
 
#else
 
				bool compatible = true; // We have no idea if we are compatible...
 
#endif
 
				if (strncmp(cur_item->info.server_revision, "norev000", sizeof(cur_item->info.server_revision)) == 0)
 
					compatible = true;
 

	
 
				DoDrawString(cur_item->item.server_name, 15, y, 16); // server name
 
				if (cur_item == _selected_item)
 
					GfxFillRect(11, y - 2, 218, y + 9, 10); // show highlighted item with a different colour
 

	
 
				SetDParam(0, cur_item->item.players_on);
 
				SetDParam(1, cur_item->item.players_max);
 
				DrawString(238, y, STR_NETWORK_PLAYERS_VAL, 2); // #/#
 
				snprintf(servername, sizeof(servername), "%s", cur_item->info.server_name);
 
				NetworkTruncateString(servername, 110);
 
				DoDrawString(servername, 15, y, 16); // server name
 

	
 
				DoDrawString(cur_item->item.map_name, 288, y, 16); // map size
 
				cur_item = cur_item->_next;
 
				SetDParam(0, cur_item->info.clients_on);
 
				SetDParam(1, cur_item->info.clients_max);
 
				DrawString(135, y, STR_NETWORK_CLIENTS_ONLINE, 2);
 

	
 
				// draw red or green icon, depending on compatibility with server. TODO: needs new icons
 
				DrawSprite((SPR_OPENTTD_BASE + 10) | (compatible?0x30d8000:0x30b8000), 185, y);
 
				// draw red or green flag, to show if the server is password protected. TODO: needs new icons
 
				DrawSprite((cur_item->info.use_password)? 0xC12 : 0xC13, 195, 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
 
			}
 
		}
 

	
 
		// right menu
 
		GfxFillRect(252, 23, 468, 65, 157);
 
		if (_selected_item == NULL) {
 
			DrawStringMultiCenter(360, 40, STR_NETWORK_GAME_INFO, 0);
 
		} else if (!_selected_item->online) {
 
			SetDParam(0, _str_game_name);
 
			DrawStringMultiCenter(360, 42, STR_ORANGE, 2); // game name
 

	
 
			DrawStringMultiCenter(360, 110, STR_NETWORK_SERVER_OFFLINE, 2); // server offline
 
		} else { // show game info
 
			uint16 y = 70;
 

	
 
			DrawStringMultiCenter(360, 30, STR_NETWORK_GAME_INFO, 0);
 

	
 
			SetDParam(0, _str_game_name);
 
			DrawStringMultiCenter(360, 42, STR_ORANGE, 2); // game name
 

	
 
			SetDParam(0, _str_map_name);
 
			DrawStringMultiCenter(360, 54, STR_02BD, 2); // map name
 

	
 
			SetDParam(0, _selected_item->info.clients_on);
 
			SetDParam(1, _selected_item->info.clients_max);
 
			DrawString(260, y, STR_NETWORK_CLIENTS, 2); // clients on the server / maximum slots
 
			y+=10;
 

	
 
			SetDParam(0, STR_NETWORK_LANG_ANY+_selected_item->info.server_lang);
 
			DrawString(260, y, STR_NETWORK_LANGUAGE, 2); // server language
 
			y+=10;
 

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

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

	
 
			SetDParam(0, _str_server_version);
 
			DrawString(260, y, STR_NETWORK_SERVER_VERSION, 2); // server version
 
			y+=10;
 

	
 
			SetDParam(0, _str_server_address);
 
			DrawString(260, y, STR_NETWORK_SERVER_ADDRESS, 2); // server address
 
			y+=10;
 

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

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

	
 
			if (_selected_item->info.clients_on == _selected_item->info.clients_max)
 
				// Show: server full, when clients_on == clients_max
 
				DrawStringMultiCenter(360, y, STR_NETWORK_SERVER_FULL, 2); // server full
 
			else if (_selected_item->info.use_password)
 
				DrawStringMultiCenter(360, y, STR_NETWORK_PASSWORD, 2); // password warning
 
			y+=10;
 
		}
 
	}	break;
 

	
 
	case WE_CLICK:
 
		_selected_field = e->click.widget;
 
		switch(e->click.widget) {
 
		case 0: case 15:  /* Close 'X' | Cancel button */
 
		case 0: case 14: /* Close 'X' | Cancel button */
 
			DeleteWindowById(WC_NETWORK_WINDOW, 0);
 
			NetworkLobbyShutdown();
 
			break;
 
		case 3: { /* Find server automaticaly */
 
			NetworkCoreConnectGame("auto", _network_server_port);
 
		}	break;
 
		case 4: { /* Connect via direct ip */
 
				StringID str;
 
				str = AllocateName((byte*)_decode_parameters, 0);
 

	
 
				ShowQueryString(
 
				str,
 
				STR_NETWORK_ENTER_IP,
 
				50,  // maximum 50 characters OR
 
				250, // characters up to width 250 pixels, whichever is satisfied first
 
				w->window_class,
 
				w->window_number);
 
				DeleteName(str);
 
		} break;
 
		case 5: /* Start server */
 
			ShowNetworkStartServerWindow();
 
			break;
 
		case 7: case 8: /* Connection type */
 
			ShowDropDownMenu(w, _connection_types_dropdown, _network_connection, 8, 0); // do it for widget 8
 
		case 4: case 5: /* Connection type */
 
			ShowDropDownMenu(w, _connection_types_dropdown, _network_connection, 5, 0); // do it for widget 5
 
			return;
 
		case 14: { /* Matrix to show networkgames */
 
		case 10: { /* Matrix to show networkgames */
 
			uint32 id_v = (e->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;
 

	
 
			{
 
				NetworkGameList *cur_item = _network_game_list;
 
				for (; id_v > 0 && cur_item != NULL; id_v--)
 
					cur_item = cur_item->_next;
 
					cur_item = cur_item->next;
 

	
 
				if (cur_item == NULL) {
 
					// click out of vehicle bounds
 
					_selected_item = NULL;
 
					SetWindowDirty(w);
 
					return;
 
				}
 
				_selected_item = cur_item;
 

	
 
				if (cur_item == NULL) { return;} // click out of vehicle bounds
 
				DeleteName(_str_game_name);
 
				DeleteName(_str_map_name);
 
				DeleteName(_str_server_version);
 
				DeleteName(_str_server_address);
 
				if (_selected_item->info.server_name[0] != '\0')
 
					_str_game_name = AllocateName((byte*) _selected_item->info.server_name, 0);
 
				else
 
					_str_game_name = STR_EMPTY;
 

	
 
				selected_item = cur_item;
 
				if (_selected_item->info.map_name[0] != '\0')
 
					_str_map_name = AllocateName((byte*) _selected_item->info.map_name, 0);
 
				else
 
					_str_map_name = STR_EMPTY;
 

	
 
				if (_selected_item->info.server_revision[0] != '\0')
 
					_str_server_version = AllocateName((byte*) _selected_item->info.server_revision, 0);
 
				else
 
					_str_server_version = STR_EMPTY;
 

	
 
				if (_selected_item->info.hostname[0] != '\0')
 
					_str_server_address = AllocateName((byte*) _selected_item->info.hostname, 0);
 
				else
 
					_str_server_address = STR_EMPTY;
 
			}
 
			SetWindowDirty(w);
 
		} break;
 
		case 16: /* Join Game */
 
			if (selected_item != NULL)
 
				NetworkCoreConnectGameStruct(selected_item);
 
		case 11: /* Find server automatically */
 
			NetworkUDPSearchGame();
 
			break;
 
		}
 
		break;
 
		case 12: { // Add a server
 
				StringID str = AllocateName((byte*)_network_default_ip, 0);
 

	
 
				ShowQueryString(
 
				str,
 
				STR_NETWORK_ENTER_IP,
 
				31 | 0x1000,  // maximum number of characters OR
 
				250, // characters up to this width pixels, whichever is satisfied first
 
				w->window_class,
 
				w->window_number);
 
				DeleteName(str);
 
		} break;
 
		case 13: /* Start server */
 
			ShowNetworkStartServerWindow();
 
			break;
 
		case 17: /* Join Game */
 
			if (_selected_item != NULL) {
 
				memcpy(&_network_game_info, &_selected_item->info, sizeof(NetworkGameInfo));
 
				snprintf(_network_last_host, sizeof(_network_last_host), "%s", inet_ntoa(*(struct in_addr *)&_selected_item->ip));
 
				_network_last_port = _selected_item->port;
 
				ShowNetworkLobbyWindow();
 
			}
 
			break;
 
		case 18: // Refresh
 
			if (_selected_item != NULL) {
 
				NetworkQueryServer(_selected_item->info.hostname, _selected_item->port, true);
 
			}
 
			break;
 

	
 
	}	break;
 

	
 
	case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
 
		_network_connection = e->dropdown.index;
 
		switch (_network_connection) {
 
		case 0: /* LAN */
 
			NetworkGameListFromLAN();
 
//			NetworkGameListFromLAN();
 
			break;
 
		case 1: /* Internet */
 
			NetworkGameListFromInternet();
 
//			NetworkGameListFromInternet();
 
			break;
 
		}
 

	
 
		_network_game_count_last = _network_game_count;
 
		SetWindowDirty(w);
 

	
 
		break;
 

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

	
 
		break;
 

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

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

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

	
 
		break;
 

	
 
	case WE_ON_EDIT_TEXT: {
 
@@ -175,147 +329,225 @@ static void NetworkGameWindowWndProc(Win
 
			const byte *player = NULL;
 
			uint16 rport;
 

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

	
 
			ParseConnectionString(&player, &port, b);
 

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

	
 
			NetworkCoreConnectGame(b, rport);
 
			NetworkQueryServer(b, rport, true);
 
		}
 
	} break;
 

	
 
	case WE_TICK: {
 
		if (_network_game_count_last != _network_game_count)
 
			SetWindowDirty(w);
 
	case WE_CREATE: {
 
		_selected_item = NULL;
 
	} break;
 

	
 
	}
 
}
 

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

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

	
 
{ WWT_PUSHTXTBTN,   BTC,    20,   130,    22,    33, STR_NETWORK_FIND_SERVER,			STR_NETWORK_FIND_SERVER_TIP},
 
{ WWT_PUSHTXTBTN,   BTC,   145,   255,    22,    33, STR_NETWORK_DIRECT_CONNECT,	STR_NETWORK_DIRECT_CONNECT_TIP},
 
{ WWT_PUSHTXTBTN,   BTC,   270,   380,    22,    33, STR_NETWORK_START_SERVER,		STR_NETWORK_START_SERVER_TIP},
 
{          WWT_6,   BGC,    90,   230,    42,    53, STR_NETWORK_COMBO1,					STR_NETWORK_CONNECTION_TIP},
 
{   WWT_CLOSEBOX,   BGC,   219,   229,    43,    52, STR_0225,										STR_NETWORK_CONNECTION_TIP},
 

	
 
{     WWT_IMGBTN,   BGC,   250,   394,    42,    53, 0x0,													STR_NETWORK_ENTER_NAME_TIP},
 
{  WWT_SCROLLBAR,   BGC,   220,   230,    62,   185, 0x0,													STR_0190_SCROLL_BAR_SCROLLS_LIST},
 

	
 
{          WWT_6,   BGC,   250,   393,    62,    73, STR_NETWORK_COMBO1,					STR_NETWORK_CONNECTION_TYPE_TIP},
 
{   WWT_CLOSEBOX,   BGC,   382,   392,    63,    72, STR_0225,										STR_NETWORK_CONNECTION_TYPE_TIP},
 
{     WWT_IMGBTN,   BTC,    10,   130,    62,    73, 0x0,													STR_NETWORK_GAME_NAME_TIP },
 
{     WWT_IMGBTN,   BTC,   131,   180,    62,    73, 0x0,													STR_NETWORK_CLIENTS_CAPTION_TIP },
 
{     WWT_IMGBTN,   BTC,   181,   219,    62,    73, 0x0,													STR_NETWORK_INFO_ICONS_TIP },
 

	
 
{  WWT_SCROLLBAR,   BGC,   382,   392,    81,   176, 0x0,													STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{     WWT_MATRIX,   BGC,    10,   219,    74,   185, 0x801,												STR_NETWORK_CLICK_GAME_TO_SELECT},
 

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

	
 
{     WWT_IMGBTN,   BTC,    10,   231,    81,    92, 0x0,													STR_NETWORK_GAME_NAME_TIP },
 
{     WWT_IMGBTN,   BTC,   232,   281,    81,    92, 0x0,													STR_NETWORK_PLAYERS_TIP },
 
{     WWT_IMGBTN,   BTC,   282,   331,    81,    92, 0x0,													STR_NETWORK_MAP_SIZE_TIP },
 
{     WWT_IMGBTN,   BTC,   332,   381,    81,    92, 0x0,													STR_NETWORK_INFO_ICONS_TIP },
 
/* RIGHT SIDE */
 
{     WWT_IMGBTN,   BGC,   250,   470,    22,   185, 0x0,					STR_NULL},
 
{          WWT_6,   BGC,   251,   469,    23,   184, 0x0,					STR_NULL},
 

	
 
{     WWT_MATRIX,   BGC,    10,   381,    93,   176, 0x601,												STR_NETWORK_CLICK_GAME_TO_SELECT},
 
{ WWT_PUSHTXTBTN,   BTC,   260,   355,   164,   175, STR_NETWORK_JOIN_GAME,					STR_NULL},
 
{ WWT_PUSHTXTBTN,   BTC,   365,   460,   164,   175, STR_NETWORK_REFRESH,					STR_NETWORK_REFRESH_TIP},
 

	
 
{ WWT_PUSHTXTBTN,   BTC,   145,   255,   180,   191, STR_012E_CANCEL,							STR_NULL},
 
{ WWT_PUSHTXTBTN,   BTC,   270,   392,   180,   191, STR_NETWORK_JOIN_GAME,				STR_NULL},
 
{   WIDGETS_END},
 
};
 

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

	
 
static FiosItem *selected_map = NULL; // to highlight slected map
 

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

	
 
	NetworkLobbyInit();
 

	
 
//	NetworkLobbyInit();
 
	w = AllocateWindowDesc(&_network_game_window_desc);
 
	strcpy(_edit_str_buf, "Your name");
 
	w->vscroll.cap = 6;
 
	w->disabled_state = (1<<6) | (1<<7) | (1<<8); // disable buttons not yet working
 
	NetworkGameListFromLAN(); // default dropdown item is LAN, so fill that array
 

	
 
	_network_game_count_last = _network_game_count;
 
	ttd_strlcpy(_edit_str_buf, _network_player_name, MAX_QUERYSTR_LEN);
 
	w->vscroll.cap = 8;
 
	w->disabled_state = (1<<4) | (1<<5); // disable buttons not yet working
 
//	NetworkGameListFromLAN(); // default dropdown item is LAN, so fill that array
 

	
 
	WP(w,querystr_d).caret = 1;
 
	WP(w,querystr_d).maxlen = MAX_QUERYSTR_LEN;
 
	WP(w,querystr_d).maxwidth = 240;
 
	WP(w,querystr_d).maxwidth = 120;
 
	WP(w,querystr_d).buf = _edit_str_buf;
 
}
 

	
 
// called when a new server is found on the network
 
void UpdateNetworkGameWindow(bool unselect)
 
{
 
	Window *w;
 
	w = FindWindowById(WC_NETWORK_WINDOW, 0);
 
	if (w != NULL) {
 
		if (unselect)
 
			_selected_item = NULL;
 
		w->vscroll.count = _network_game_count;
 
		SetWindowDirty(w);
 
	}
 
}
 

	
 
static const StringID _players_dropdown[] = {
 
	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_2_CLIENTS,
 
	STR_NETWORK_3_CLIENTS,
 
	STR_NETWORK_4_CLIENTS,
 
	STR_NETWORK_5_CLIENTS,
 
	STR_NETWORK_6_CLIENTS,
 
	STR_NETWORK_7_CLIENTS,
 
	STR_NETWORK_8_CLIENTS,
 
	STR_NETWORK_9_CLIENTS,
 
	STR_NETWORK_10_CLIENTS,
 
	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 {
 
	NSSWND_START = 64,
 
	NSSWND_ROWSIZE = 12
 
};
 

	
 
static void NetworkStartServerWindowWndProc(Window *w, WindowEvent *e)
 
{
 
	switch(e->event) {
 
	case WE_PAINT: {
 
		int y = NSSWND_START, pos;
 
		const FiosItem *item;
 

	
 
		SetDParam(7, STR_NETWORK_2_PLAYERS + _players_max);
 
		SetDParam(7, STR_NETWORK_2_CLIENTS + _network_game_info.clients_max - 2);
 
		SetDParam(9, STR_NETWORK_LANG_ANY + _network_game_info.server_lang);
 
		DrawWindowWidgets(w);
 

	
 
		GfxFillRect(11, 63, 237, 168, 0xD7);
 
		GfxFillRect(11, 63, 239, 165, 0xD7);
 

	
 
		DrawEditBox(w, 3);
 
		DrawEditBox(w, 4);
 

	
 
		DrawString(10, 22, STR_NETWORK_NEW_GAME_NAME, 2);
 
		DrawString(210, 22, STR_NETWORK_PASSWORD, 2);
 

	
 
		DrawString(10, 43, STR_NETWORK_SELECT_MAP, 2);
 
		DrawString(260, 63, STR_NETWORK_NUMBER_OF_PLAYERS, 2);
 
		DrawString(260, 63, STR_NETWORK_NUMBER_OF_CLIENTS, 2);
 
		DrawString(260, 105, STR_NETWORK_LANGUAGE_SPOKEN, 2);
 

	
 
		// draw list of maps
 
		pos = w->vscroll.pos;
 
		while (pos < _fios_num + 1) {
 
			item = _fios_list + pos - 1;
 
			if (item == selected_map || (pos == 0 && selected_map == NULL))
 
				GfxFillRect(11, y - 1, 239, 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[0] ? item->title : item->name, 14, y, _fios_colors[item->type] );
 
			pos++;
 
			y += NSSWND_ROWSIZE;
 

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

	
 
	case WE_CLICK:
 
		_selected_field = e->click.widget;
 
		switch(e->click.widget) {
 
		case 0: case 12: /* Close 'X' | Cancel button */
 
		case 0: case 13: /* Close 'X' | Cancel button */
 
			ShowNetworkGameWindow();
 
			break;
 
		case 4: { /* Set password button */
 
			StringID str;
 
			str = AllocateName(_network_game_info.server_password, 0);
 
			ShowQueryString(str, STR_NETWORK_SET_PASSWORD, 20, 250, w->window_class, w->window_number);
 
			DeleteName(str);
 
			} break;
 
		case 5: { /* Select map */
 
			int y = (e->click.pt.y - NSSWND_START) / NSSWND_ROWSIZE;
 
			if ((y += w->vscroll.pos) >= w->vscroll.count)
 
				return;
 
			if (y == 0) selected_map = NULL;
 
			else selected_map = _fios_list + y-1;
 
			SetWindowDirty(w);
 
			} break;
 
		case 7: case 8: /* Number of Players */
 
			ShowDropDownMenu(w, _players_dropdown, _players_max, 8, 0); // do it for widget 8
 
			ShowDropDownMenu(w, _players_dropdown, _network_game_info.clients_max - 2, 8, 0); // do it for widget 8
 
			return;
 
		case  9: /* Start game */
 
			NetworkCoreStartGame();
 
			strcpy(_network_game.server_name,	WP(w,querystr_d).buf);
 
			//ShowNetworkLobbyWindow();
 
			DoCommandP(0, 0, 0, NULL, CMD_START_NEW_GAME);
 
		case 9: case 10: /* Language */
 
			ShowDropDownMenu(w, _language_dropdown, _network_game_info.server_lang, 10, 0); // do it for widget 10
 
			return;
 
		case  11: /* Start game */
 
			_is_network_server = true;
 
			ttd_strlcpy(_network_server_name, WP(w,querystr_d).buf, sizeof(_network_server_name));
 
			if(selected_map==NULL) { // start random new game
 
				DoCommandP(0, Random(), InteractiveRandom(), NULL, CMD_GEN_RANDOM_NEW_GAME);
 
			} else { // load a scenario
 
				char *name;
 
				if ((name = FiosBrowseTo(selected_map)) != NULL) {
 
					SetFiosType(selected_map->type);
 
					strcpy(_file_to_saveload.name, name);
 
					snprintf(_network_game_info.map_name, sizeof(_network_game_info.map_name), "Loaded scenario");
 
					DeleteWindow(w);
 
					DoCommandP(0, Random(), InteractiveRandom(), NULL, CMD_START_SCENARIO);
 
				}
 
			}
 
			break;
 
		case 10: /* Load game */
 
			NetworkCoreStartGame();
 
			strcpy(_network_game.server_name,	WP(w,querystr_d).buf);
 
			//ShowNetworkLobbyWindow();
 
		case 12: /* Load game */
 
			_is_network_server = true;
 
			ttd_strlcpy(_network_server_name, WP(w,querystr_d).buf, sizeof(_network_server_name));
 
			snprintf(_network_game_info.map_name, sizeof(_network_game_info.map_name), "Loaded game");
 
			/* 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;
 
		case 11: /* Load scenario */
 
			NetworkCoreStartGame();
 
			strcpy(_network_game.server_name,	WP(w,querystr_d).buf);
 
			//ShowNetworkLobbyWindow();
 
			ShowSaveLoadDialog(SLD_LOAD_SCENARIO);
 
			break;
 
		}
 
		break;
 

	
 
	case WE_DROPDOWN_SELECT: /* we have selected a dropdown item in the list */
 
		_players_max = e->dropdown.index;
 
		switch(e->dropdown.button) {
 
			case 8:
 
				_network_game_info.clients_max = e->dropdown.index + 2;
 
				break;
 
			case 10:
 
				_network_game_info.server_lang = e->dropdown.index;
 
				break;
 
		}
 

	
 
		SetWindowDirty(w);
 
		break;
 
@@ -327,7 +559,7 @@ static void NetworkStartServerWindowWndP
 
		break;
 

	
 
	case WE_KEYPRESS:
 
		if(_selected_field != 3 && _selected_field != 4)
 
		if(_selected_field != 3)
 
			break;
 
		switch (HandleEditBoxKey(w, _selected_field, e)) {
 
		case 1:
 
@@ -336,104 +568,661 @@ static void NetworkStartServerWindowWndP
 
		}
 
		break;
 

	
 
	case WE_ON_EDIT_TEXT: {
 
		byte *b = e->edittext.str;
 
		ttd_strlcpy(_network_game_info.server_password, b, sizeof(_network_game_info.server_password));
 
		if (_network_game_info.server_password[0] == '\0') {
 
			_network_game_info.use_password = 0;
 
		} else {
 
			_network_game_info.use_password = 1;
 
		}
 
	} break;
 
	}
 
}
 

	
 
static const Widget _network_start_server_window_widgets[] = {
 
{   WWT_CLOSEBOX,   BGC,     0,    10,     0,    13, STR_00C5,											STR_018B_CLOSE_WINDOW },
 
{    WWT_CAPTION,   BGC,    10,   399,     0,    13, STR_NETWORK_START_GAME_WINDOW,	STR_NULL},
 
{    WWT_CAPTION,   BGC,    11,   399,     0,    13, STR_NETWORK_START_GAME_WINDOW,	STR_NULL},
 
{     WWT_IMGBTN,   BGC,     0,   399,    14,   199, 0x0,														STR_NULL},
 

	
 
{     WWT_IMGBTN,   BGC,    80,   190,    22,    33, 0x0,														STR_NETWORK_NEW_GAME_NAME_TIP},
 
{     WWT_IMGBTN,   BGC,   280,   390,    22,    33, 0x0,														STR_NETWORK_PASSWORD_TIP},
 
{     WWT_IMGBTN,   BGC,    80,   251,    22,    33, 0x0,														STR_NETWORK_NEW_GAME_NAME_TIP},
 
{ WWT_PUSHTXTBTN,   BTC,   270,   380,    22,    33, STR_NETWORK_SET_PASSWORD,			STR_NETWORK_PASSWORD_TIP},
 

	
 
{     WWT_IMGBTN,   BGC,    10,   240,    62,   170, 0x0,														STR_NETWORK_SELECT_MAP_TIP},
 
{  WWT_SCROLLBAR,   BGC,   241,   251,    62,   170, 0x0,														STR_0190_SCROLL_BAR_SCROLLS_LIST},
 
{          WWT_6,   BGC,    10,   251,    62,   166, 0x0,														STR_NETWORK_SELECT_MAP_TIP},
 
{  WWT_SCROLLBAR,   BGC,   240,   250,    63,   165, 0x0,														STR_0190_SCROLL_BAR_SCROLLS_LIST},
 

	
 
{          WWT_6,   BGC,   260,   390,    81,    92, STR_NETWORK_COMBO2,						STR_NETWORK_NUMBER_OF_PLAYERS_TIP},
 
{   WWT_CLOSEBOX,   BGC,   379,   389,    82,    91, STR_0225,											STR_NETWORK_NUMBER_OF_PLAYERS_TIP},
 
{          WWT_6,   BGC,   260,   390,    77,    88, STR_NETWORK_COMBO2,						STR_NETWORK_NUMBER_OF_CLIENTS_TIP},
 
{   WWT_CLOSEBOX,   BGC,   379,   389,    78,    87, STR_0225,											STR_NETWORK_NUMBER_OF_CLIENTS_TIP},
 

	
 
{ WWT_PUSHTXTBTN,   BTC,    10,   100,   180,   191, STR_NETWORK_START_GAME,				STR_NETWORK_START_GAME_TIP},
 
{ WWT_PUSHTXTBTN,   BTC,   110,   200,   180,   191, STR_NETWORK_LOAD_GAME,					STR_NETWORK_LOAD_GAME_TIP},
 
{ WWT_PUSHTXTBTN,   BTC,   210,   300,   180,   191, STR_NETWORK_LOAD_SCENARIO,			STR_NETWORK_LOAD_SCENARIO_TIP},
 
{ WWT_PUSHTXTBTN,   BTC,   310,   390,   180,   191, STR_012E_CANCEL,								STR_NULL},
 
{          WWT_6,   BGC,   260,   390,   119,   130, STR_NETWORK_COMBO3,						STR_NETWORK_LANGUAGE_TIP},
 
{   WWT_CLOSEBOX,   BGC,   379,   389,   120,   129, STR_0225,											STR_NETWORK_LANGUAGE_TIP},
 

	
 
{ WWT_PUSHTXTBTN,   BTC,    55,   145,   180,   191, STR_NETWORK_START_GAME,				STR_NETWORK_START_GAME_TIP},
 
{ WWT_PUSHTXTBTN,   BTC,   155,   245,   180,   191, STR_NETWORK_LOAD_GAME,					STR_NETWORK_LOAD_GAME_TIP},
 
{ WWT_PUSHTXTBTN,   BTC,   255,   345,   180,   191, STR_012E_CANCEL,								STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _network_start_server_window_desc = {
 
	WDP_CENTER, WDP_CENTER, 400, 200,
 
	WC_NETWORK_WINDOW,0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_RESTORE_DPARAM,
 
	_network_start_server_window_widgets,
 
	NetworkStartServerWindowWndProc,
 
};
 

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

	
 
	w = AllocateWindowDesc(&_network_start_server_window_desc);
 
	strcpy(_edit_str_buf, "");
 
	w->disabled_state = (1<<4) | (1<<5) | (1<<6) | (1<<7) | (1<<8); // disable buttons not yet working
 
	ttd_strlcpy(_edit_str_buf, _network_server_name, MAX_QUERYSTR_LEN);
 

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

	
 
	WP(w,querystr_d).caret = 1;
 
	WP(w,querystr_d).maxlen = MAX_QUERYSTR_LEN;
 
	WP(w,querystr_d).maxwidth = 240;
 
	WP(w,querystr_d).maxwidth = 160;
 
	WP(w,querystr_d).buf = _edit_str_buf;
 
}
 

	
 
#if 0
 
static void NetworkLobbyWindowWndProc(Window *w, WindowEvent *e)
 
{
 
	switch(e->event) {
 
	case WE_PAINT: {
 
		int y = NET_PRC__OFFSET_TOP_WIDGET_COMPANY, pos;
 
		StringID str;
 

	
 
		SetDParam(7, STR_NETWORK_2_PLAYERS + _opt_mod_ptr->road_side);
 
		if (_selected_company_item == -1) {
 
			w->disabled_state = (1<<7);
 
		} else
 
			w->disabled_state = 0;
 

	
 
		DrawWindowWidgets(w);
 

	
 
		GfxFillRect( 11,  31, 239, 239, 0xD7);
 
		GfxFillRect(261,  31, 378, 220, 0xD7);
 
		SetDParam(0, _str_game_name);
 
		DrawString(10, 22, STR_NETWORK_PREPARE_TO_JOIN, 2);
 

	
 
		// draw company list
 
		GfxFillRect(11, 41, 139, 165, 0xD7);
 
		pos = w->vscroll.pos;
 
		while (pos < _network_lobby_company_count) {
 
			if (_selected_company_item == pos)
 
				GfxFillRect(11, y - 1, 139, y + 10, 155); // show highlighted item with a different colour
 

	
 
			DoDrawString(_network_player_info[pos].company_name, 13, y, 2);
 

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

	
 
		// draw info about selected company
 
		DrawStringMultiCenter(270, 48, STR_NETWORK_COMPANY_INFO, 0);
 
		if (_selected_company_item != -1) { // if a company is selected...
 
			// show company info
 
			const uint x = 168;
 
			uint xm;
 
			y = 65;
 

	
 
			str = AllocateName(_network_player_info[_selected_company_item].company_name, 0);
 
			SetDParam(0, str);
 
			DrawString(x, y, STR_NETWORK_COMPANY_NAME, 2);
 
			DeleteName(str);
 
			y += 10;
 

	
 
			SetDParam(0, _network_player_info[_selected_company_item].inaugurated_year + 1920);
 
			DrawString(x, y, STR_NETWORK_INAUGURATION_YEAR, 2); // inauguration year
 
			y += 10;
 

	
 
		DrawEditBox(w, 5);
 
		DrawEditBox(w, 7);
 
			SetDParam64(0, _network_player_info[_selected_company_item].company_value);
 
			DrawString(x, y, STR_NETWORK_VALUE, 2); // company value
 
			y += 10;
 

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

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

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

	
 
		DrawString(10, 255, STR_NETWORK_COMPANY_NAME, 2);
 
			SetDParam(0, _network_player_info[_selected_company_item].num_vehicle[0]);
 
			SetDParam(1, _network_player_info[_selected_company_item].num_vehicle[1]);
 
			SetDParam(2, _network_player_info[_selected_company_item].num_vehicle[2]);
 
			SetDParam(3, _network_player_info[_selected_company_item].num_vehicle[3]);
 
			SetDParam(4, _network_player_info[_selected_company_item].num_vehicle[4]);
 
			DrawString(x, y, STR_NETWORK_VEHICLES, 2); // vehicles
 
			y += 10;
 

	
 
		break;
 
	}
 
			SetDParam(0, _network_player_info[_selected_company_item].num_station[0]);
 
			SetDParam(1, _network_player_info[_selected_company_item].num_station[1]);
 
			SetDParam(2, _network_player_info[_selected_company_item].num_station[2]);
 
			SetDParam(3, _network_player_info[_selected_company_item].num_station[3]);
 
			SetDParam(4, _network_player_info[_selected_company_item].num_station[4]);
 
			DrawString(x, y, STR_NETWORK_STATIONS, 2); // stations
 
			y += 10;
 

	
 
			str = AllocateName(_network_player_info[_selected_company_item].players, 0);
 
			SetDParam(0, str);
 
			xm = DrawString(x, y, STR_NETWORK_PLAYERS, 2); // players
 
			DeleteName(str);
 
			y += 10;
 
		}
 
	}	break;
 

	
 
	case WE_CLICK:
 
		_selected_field = e->click.widget;
 
		switch(e->click.widget) {
 

	
 
		case 0: // close X
 
		case 13: // cancel button
 
		case 0: case 11: /* Close 'X' | Cancel button */
 
			ShowNetworkGameWindow();
 
			break;
 
		case 3: /* Company list */
 
			_selected_company_item = (e->click.pt.y - NET_PRC__OFFSET_TOP_WIDGET_COMPANY) / NET_PRC__SIZE_OF_ROW_COMPANY;
 

	
 
		}
 
			if (_selected_company_item >= w->vscroll.cap) {
 
				// click out of bounds
 
				_selected_company_item = -1;
 
				SetWindowDirty(w);
 
				return;
 
			}
 
			_selected_company_item += w->vscroll.pos;
 
			if (_selected_company_item >= _network_lobby_company_count) {
 
				_selected_company_item = -1;
 
				SetWindowDirty(w);
 
				return;
 
			}
 

	
 
			SetWindowDirty(w);
 
			break;
 
		case 7: /* Join company */
 
			if (_selected_company_item != -1) {
 
				_network_playas = _selected_company_item + 1;
 
				NetworkClientConnectGame(_network_last_host, _network_last_port);
 
			}
 
			break;
 
		case 8: /* New company */
 
			_network_playas = 0;
 
			NetworkClientConnectGame(_network_last_host, _network_last_port);
 
			break;
 
		case 9: /* Spectate game */
 
			_network_playas = OWNER_SPECTATOR;
 
			NetworkClientConnectGame(_network_last_host, _network_last_port);
 
			break;
 
		case 10: /* Refresh */
 
			NetworkQueryServer(_network_last_host, _network_last_port, false);
 
			break;
 
		}	break;
 

	
 
	case WE_CREATE:
 
		_selected_company_item = -1;
 
	}
 
}
 

	
 
static const Widget _network_lobby_window_widgets[] = {
 
{   WWT_CLOSEBOX,   BGC,     0,    10,     0,    13, STR_00C5,									STR_018B_CLOSE_WINDOW },
 
{    WWT_CAPTION,   BGC,    11,   399,     0,    13, STR_NETWORK_GAME_LOBBY,		STR_NULL},
 
{     WWT_IMGBTN,   BGC,     0,   399,    14,   209, 0x0,												STR_NULL},
 

	
 
// company list
 
{          WWT_6,   BGC,    10,   151,    40,   166, 0x0,												STR_NETWORK_COMPANY_LIST_TIP},
 
{  WWT_SCROLLBAR,   BGC,   140,   150,    41,   165, 0x1,												STR_0190_SCROLL_BAR_SCROLLS_LIST},
 

	
 
// company/player info
 
{     WWT_IMGBTN,   BGC,   158,   389,    38,   165, 0x0,					STR_NULL},
 
{          WWT_6,   BGC,   159,   388,    39,   164, 0x0,					STR_NULL},
 

	
 
// buttons
 
{ WWT_PUSHTXTBTN,   BTC,    10,   150,   175,   186, STR_NETWORK_JOIN_COMPANY,	STR_NETWORK_JOIN_COMPANY_TIP},
 
{ WWT_PUSHTXTBTN,   BTC,    10,   150,   190,   201, STR_NETWORK_NEW_COMPANY,		STR_NETWORK_NEW_COMPANY_TIP},
 
{ WWT_PUSHTXTBTN,   BTC,   158,   268,   175,   186, STR_NETWORK_SPECTATE_GAME,	STR_NETWORK_SPECTATE_GAME_TIP},
 
{ WWT_PUSHTXTBTN,   BTC,   158,   268,   190,   201, STR_NETWORK_REFRESH,				STR_NETWORK_REFRESH_TIP},
 
{ WWT_PUSHTXTBTN,   BTC,   278,   388,   175,   186, STR_012E_CANCEL,						STR_NULL},
 

	
 
{   WIDGETS_END},
 
};
 

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

	
 

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

	
 
	_network_lobby_company_count = 0;
 

	
 
	NetworkQueryServer(_network_last_host, _network_last_port, false);
 

	
 
	w = AllocateWindowDesc(&_network_lobby_window_desc);
 
	strcpy(_edit_str_buf, "");
 
	w->vscroll.pos = 0;
 
	w->vscroll.cap = 8;
 
}
 

	
 

	
 

	
 

	
 
// 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(int p, 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 uint16 _client_list_popup_height = 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 Widget _client_list_widgets[] = {
 
{    WWT_TEXTBTN,    14,     0,    10,     0,    13, STR_00C5,                 STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,    14,    11,   249,     0,    13, STR_NETWORK_CLIENT_LIST,  STR_018C_WINDOW_TITLE_DRAG_THIS},
 

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

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

	
 
static WindowDesc _client_list_desc = {
 
	-1, -1, 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 NetworkClientInfo *NetworkFindClientInfo(byte client_no)
 
{
 
	NetworkClientInfo *ci;
 
	for (ci = _network_client_info; ci != &_network_client_info[MAX_CLIENT_INFO]; ci++) {
 
		// Skip non-active items
 
		if (ci->client_index == NETWORK_EMPTY_INDEX) continue;
 
		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)(&_clients[client_no], NETWORK_ERROR_KICKED);
 
}
 

	
 
/*static void ClientList_Ban(byte client_no)
 
{
 
// TODO
 
}*/
 

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

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

	
 
static void ClientList_SpeakToPlayer(byte client_no)
 
{
 
	if (NetworkFindClientInfo(client_no) != NULL)
 
		ShowNetworkChatQueryWindow(DESTTYPE_PLAYER, 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;
 
	NetworkClientInfo *ci;
 

	
 
	case WE_MOUSELOOP:
 
		if(_selected_field == 5)
 
		{
 
			HandleEditBox(w, 5);
 
			break;
 
	// Should be replaced with a loop through all clients
 
	for (ci = _network_client_info; ci != &_network_client_info[MAX_CLIENT_INFO]; ci++) {
 
		// Skip non-active items
 
		if (ci->client_index == NETWORK_EMPTY_INDEX) continue;
 
		num++;
 
	}
 

	
 
	num *= CLNWND_ROWSIZE;
 

	
 
	// If height is changed
 
	if (_client_list_desc.height != CLNWND_OFFSET + num + 1) {
 
		// XXX - magic unfortunately; (num + 2) has to be one bigger than heigh (num + 1)
 
		_client_list_widgets[2].bottom = _client_list_widgets[2].top + num + 2;
 
		_client_list_desc.height = CLNWND_OFFSET + num + 1;
 
		_client_list_desc.left = w->left;
 
		_client_list_desc.top = w->top;
 
		// Delete the window and reallocate.. else we can not change the height ;)
 
		DeleteWindow(w);
 
		w = AllocateWindowDescFront(&_client_list_desc, 0);
 
		return false;
 
	}
 
	return true;
 
}
 

	
 
// Finds the amount of actions in the popup and set the height correct
 
static void UpdateClientListPopupHeigth(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;
 
	// Set the height
 
	_client_list_popup_height = num + 2; // XXX - magic, has to be one more than the value below (num + 1)
 
	_client_list_popup_widgets[0].bottom = _client_list_popup_widgets[0].top + num + 1;
 
}
 

	
 
// Show the popup (action list)
 
static Window *PopupClientList(Window *w, int client_no, int x, int y)
 
{
 
	int i;
 
	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) {
 
		sprintf(_clientlist_action[i],"Private message");
 
		_clientlist_proc[i++] = &ClientList_SpeakToClient;
 
	}
 

	
 
	if (ci->client_playas >= 1 && ci->client_playas <= MAX_PLAYERS) {
 
		sprintf(_clientlist_action[i],"Speak to company");
 
		_clientlist_proc[i++] = &ClientList_SpeakToPlayer;
 
	}
 
	sprintf(_clientlist_action[i],"Speak to all");
 
	_clientlist_proc[i++] = &ClientList_SpeakToAll;
 

	
 
	if (_network_own_client_index != ci->client_index) {
 
		if (_network_playas >= 1 && _network_playas <= MAX_PLAYERS) {
 
			// We are no spectator
 
			if (ci->client_playas >= 1 && ci->client_playas <= MAX_PLAYERS) {
 
				sprintf(_clientlist_action[i],"Give money");
 
				_clientlist_proc[i++] = &ClientList_GiveMoney;
 
			}
 
		}
 
		if(_selected_field == 7)
 
		{
 
			HandleEditBox(w, 7);
 
			break;
 
	}
 

	
 
	// A server can kick clients (but not hisself)
 
	if (_network_server && _network_own_client_index != ci->client_index) {
 
		sprintf(_clientlist_action[i],"Kick");
 
		_clientlist_proc[i++] = &ClientList_Kick;
 

	
 
/*		sprintf(clientlist_action[i],"Ban");
 
		clientlist_proc[i++] = &ClientList_Ban;*/
 
	}
 

	
 
	if (i == 0) {
 
		sprintf(_clientlist_action[i],"(none)");
 
		_clientlist_proc[i++] = &ClientList_None;
 
	}
 

	
 

	
 
	// Find the right height for the popup
 
	UpdateClientListPopupHeigth();
 

	
 
	// Allocate the popup
 
	w = AllocateWindow(x, y, 100, _client_list_popup_height, ClientListPopupWndProc, WC_TOOLBAR_MENU, _client_list_popup_widgets);
 
	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 popup
 
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, 98, 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->popupmenu.pt.y - w->top) / CLNWND_ROWSIZE;
 

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

	
 
		// Sometimes, because of the bad DeleteWindow-proc, the 'w' pointer is
 
		//  invalid after the last functions (mostly because it kills a window
 
		//  that is in front of 'w', and because of a silly memmove, the address
 
		//  'w' was pointing to becomes invalid), so we need to refetch
 
		//  the right address...
 
		DeleteWindowById(WC_TOOLBAR_MENU, 0);
 
	}	break;
 

	
 
	case WE_POPUPMENU_OVER: {
 
		// Our mouse hoovers over an action? Select it!
 
		int index = (e->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 (ci = _network_client_info; ci != &_network_client_info[MAX_CLIENT_INFO]; ci++) {
 
			// Skip non-active items
 
			if (ci->client_index == NETWORK_EMPTY_INDEX) continue;
 

	
 
			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) {
 
				DoDrawString("Server", 4, y, colour);
 
			} else
 
				DoDrawString("Client", 4, y, colour);
 

	
 
			// Filter out spectators
 
			if (ci->client_playas > 0 && ci->client_playas <= MAX_PLAYERS)
 
				DrawPlayerIcon(ci->client_playas - 1, 44, y + 1);
 

	
 
			DoDrawString(ci->client_name, 61, 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->click.pt.x + w->left, e->click.pt.y + w->top);
 
		}
 

	
 
		break;
 

	
 
	case WE_KEYPRESS:
 
		if(_selected_field != 5 && _selected_field != 7)
 
	case WE_MOUSEOVER:
 
		// -1 means we left the current window
 
		if (e->mouseover.pt.y == -1) {
 
			_selected_clientlist_y = 0;
 
			_selected_clientlist_item = 255;
 
			SetWindowDirty(w);
 
			break;
 
		switch (HandleEditBoxKey(w, _selected_field, e)) {
 
		case 1:
 
			HandleButtonClick(w, 12);
 
		}
 
		// It did not change.. no update!
 
		if (e->mouseover.pt.y == _selected_clientlist_y) break;
 

	
 
		// Find the new selected item (if any)
 
		_selected_clientlist_y = e->mouseover.pt.y;
 
		if (e->mouseover.pt.y > CLNWND_OFFSET) {
 
			_selected_clientlist_item = (e->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()
 
{
 
	AllocateWindowDesc(&_client_list_desc);
 
}
 

	
 
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->click.widget) {
 
		case 0: case 3: /* Close 'X' | Disconnect button */
 
			NetworkDisconnect();
 
			ShowNetworkGameWindow();
 
			DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 
			break;
 
		}
 
		break;
 
@@ -441,56 +1230,28 @@ static void NetworkLobbyWindowWndProc(Wi
 
	}
 
}
 

	
 
static const Widget _network_lobby_window_widgets[] = {
 
{   WWT_CLOSEBOX,   BGC,     0,    10,     0,    13, STR_00C5,									STR_018B_CLOSE_WINDOW },
 
{    WWT_CAPTION,   BGC,    10,   399,     0,    13, STR_NETWORK_GAME_LOBBY,		STR_NULL},
 
{     WWT_IMGBTN,   BGC,     0,   399,    14,   299, 0x0,												STR_NULL},
 

	
 
// chat widget
 
{     WWT_IMGBTN,   BGC,    10,   240,    30,   240, 0x0,												STR_NULL},
 
{  WWT_SCROLLBAR,   BGC,   241,   251,    30,   240, 0x0,												STR_0190_SCROLL_BAR_SCROLLS_LIST},
 

	
 
// send message prompt
 
{     WWT_IMGBTN,   BGC,    10,   200,   241,   252, 0x0,												STR_NETWORK_ENTER_NAME_TIP},
 
{ WWT_PUSHTXTBTN,   BTC,   201,   251,   241,   252, STR_NETWORK_SEND,					STR_NETWORK_SEND_TIP},
 

	
 
// company name
 
{     WWT_IMGBTN,   BGC,   100,   251,   254,   265, 0x0,												STR_NETWORK_COMPANY_NAME_TIP},
 

	
 
// player information
 
{     WWT_IMGBTN,   BGC,   260,   379,    30,   221, 0x0,												STR_NULL},
 
{  WWT_SCROLLBAR,   BGC,   380,   390,    30,   221, 0x1,												STR_0190_SCROLL_BAR_SCROLLS_LIST},
 

	
 
// buttons
 
{ WWT_PUSHTXTBTN,   BTC,   260,   390,   233,   244, STR_NETWORK_NEW_COMPANY,		STR_NETWORK_NEW_COMPANY_TIP},
 
{ WWT_PUSHTXTBTN,   BTC,   260,   390,   254,   265, STR_NETWORK_SPECTATE_GAME,	STR_NETWORK_SPECTATE_GAME_TIP},
 

	
 
{ WWT_PUSHTXTBTN,   BTC,    80,   180,   280,   291, STR_NETWORK_READY,					STR_NULL},
 
{ WWT_PUSHTXTBTN,   BTC,   220,   320,   280,   291, STR_012E_CANCEL,						STR_NULL},
 
static const Widget _network_join_status_window_widget[] = {
 
{    WWT_TEXTBTN,    14,     0,    10,     0,    13, STR_00C5, STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,    14,    11,   249,     0,    13, STR_NETWORK_CONNECTING, STR_018C_WINDOW_TITLE_DRAG_THIS},
 
{     WWT_IMGBTN,    14,     0,   249,    14,    84, 0x0,STR_NULL},
 
{ WWT_PUSHTXTBTN,   BTC,    75,   175,    69,    80, STR_NETWORK_DISCONNECT, STR_NULL},
 
{   WIDGETS_END},
 
};
 

	
 
static const WindowDesc _network_lobby_window_desc = {
 
	WDP_CENTER, WDP_CENTER, 400, 300,
 
	WC_NETWORK_WINDOW,0,
 
	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 
	_network_lobby_window_widgets,
 
	NetworkLobbyWindowWndProc,
 
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,
 
	_network_join_status_window_widget,
 
	NetworkJoinStatusWindowWndProc,
 
};
 

	
 
void ShowJoinStatusWindow()
 
{
 
	DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
 
	_network_join_status = NETWORK_JOIN_STATUS_CONNECTING;
 
	AllocateWindowDesc(&_network_join_status_window_desc);
 
}
 

	
 

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

	
 
	w = AllocateWindowDesc(&_network_lobby_window_desc);
 
	strcpy(_edit_str_buf, "");
 

	
 

	
 
	WP(w,querystr_d).caret = 1;
 
	WP(w,querystr_d).maxlen = MAX_QUERYSTR_LEN;
 
	WP(w,querystr_d).maxwidth = 240;
 
	WP(w,querystr_d).buf = _edit_str_buf;
 
}
 
#endif
 
#endif /* ENABLE_NETWORK */
network_server.c
Show inline comments
 
new file 100644
 
#include "stdafx.h"
 
#include "network_data.h"
 

	
 
#ifdef ENABLE_NETWORK
 

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

	
 
// This file handles all the server-commands
 

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

	
 
// Is the network enabled?
 

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

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

	
 
	Packet *p;
 

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

	
 
		NetworkSend_Packet(p, cs);
 
	}
 
}
 

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

	
 
	int i;
 

	
 
	Player *player;
 
	Packet *p;
 

	
 
	byte active = 0;
 
	byte current = 0;
 

	
 

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

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

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

	
 
		NetworkSend_Packet(p, cs);
 
		return;
 
	}
 

	
 
	NetworkPopulateCompanyInfo();
 

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

	
 
		current++;
 

	
 
		p = NetworkSend_Init(PACKET_SERVER_COMPANY_INFO);
 

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

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

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

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

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

	
 
		NetworkSend_Packet(p, cs);
 
	}
 
}
 

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

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

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

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

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

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

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

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

	
 
				SEND_COMMAND(PACKET_SERVER_ERROR_QUIT)(new_cs, cs->index, error);
 
			}
 
		}
 
	} else {
 
		DEBUG(net, 2)("[NET] Clientno %d has made an error and his connection is closed", cs->index);
 
	}
 

	
 
	cs->quited = true;
 

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

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

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

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

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

	
 
	Packet *p;
 
	ClientState *new_cs;
 

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

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

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

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

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

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

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

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

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

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

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

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

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

	
 
		fseek(file_pointer, 0, SEEK_SET);
 

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

	
 
		cs->status = STATUS_MAP;
 
	}
 

	
 
	if (cs->status == STATUS_MAP) {
 
		uint i;
 
		int res;
 
		for (i = 0; i < sent_packets; i++) {
 
			Packet *p = NetworkSend_Init(PACKET_SERVER_MAP);
 
			NetworkSend_uint8(p, MAP_PACKET_NORMAL);
 
			res = fread(p->buffer + p->size, 1, SEND_MTU - p->size, file_pointer);
 
			if (ferror(file_pointer)) {
 
				error("Error reading temporary network savegame!");
 
			}
 
			p->size += res;
 
			NetworkSend_Packet(p, cs);
 
			if (feof(file_pointer)) {
 
				// Done reading!
 
				int i;
 
				Packet *p;
 

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

	
 
				p = NetworkSend_Init(PACKET_SERVER_MAP);
 
				NetworkSend_uint8(p, MAP_PACKET_END);
 
				// Send the player_seeds in this packet
 
				for (i = 0; i < MAX_PLAYERS; i++) {
 
					NetworkSend_uint32(p, _player_seeds[i][0]);
 
					NetworkSend_uint32(p, _player_seeds[i][1]);
 
				}
 
				NetworkSend_Packet(p, cs);
 

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

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

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

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

	
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_JOIN)(ClientState *cs, uint16 client_index)
 
{
 
	//
 
	// Packet: SERVER_JOIN
 
	// Function: A client is joined (all active clients receive this after a
 
	//     PACKET_CLIENT_MAP_OK) Mostly what directly follows is a
 
	//     PACKET_SERVER_CLIENT_INFO
 
	// Data:
 
	//    uint16:  Client-Index
 
	//
 

	
 
	Packet *p = NetworkSend_Init(PACKET_SERVER_JOIN);
 

	
 
	NetworkSend_uint16(p, client_index);
 

	
 
	NetworkSend_Packet(p, cs);
 
}
 

	
 

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

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

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

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

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

	
 
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_COMMAND)(ClientState *cs, CommandPacket *cp)
 
{
 
	//
 
	// Packet: SERVER_COMMAND
 
	// Function: Sends a DoCommand to the client
 
	// Data:
 
	//    uint8:  PlayerID (0..MAX_PLAYERS-1)
 
	//    uint32: CommandID (see command.h)
 
	//    uint32: P1 (free variables used in DoCommand)
 
	//    uint32: P2
 
	//    uint32: Tile
 
	//    uint32: decode_params
 
	//      10 times the last one (lengthof(cp->dp))
 
	//    uint8:  CallBackID (see callback_table.c)
 
	//    uint32: Frame of execution
 
	//
 

	
 
	int i;
 
	Packet *p = NetworkSend_Init(PACKET_SERVER_COMMAND);
 

	
 
	NetworkSend_uint8(p, cp->player);
 
	NetworkSend_uint32(p, cp->cmd);
 
	NetworkSend_uint32(p, cp->p1);
 
	NetworkSend_uint32(p, cp->p2);
 
	NetworkSend_uint32(p, cp->tile);
 
	for (i = 0; i < lengthof(cp->dp); i++) {
 
		NetworkSend_uint32(p, cp->dp[i]);
 
	}
 
	NetworkSend_uint8(p, cp->callback);
 
	NetworkSend_uint32(p, cp->frame);
 

	
 
	NetworkSend_Packet(p, cs);
 
}
 

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

	
 
	Packet *p = NetworkSend_Init(PACKET_SERVER_CHAT);
 

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

	
 
	NetworkSend_Packet(p, cs);
 
}
 

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

	
 
	Packet *p = NetworkSend_Init(PACKET_SERVER_ERROR_QUIT);
 

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

	
 
	NetworkSend_Packet(p, cs);
 
}
 

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

	
 
	Packet *p = NetworkSend_Init(PACKET_SERVER_QUIT);
 

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

	
 
	NetworkSend_Packet(p, cs);
 
}
 

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

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

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

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

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

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

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

	
 

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

	
 
	//  Too bad, when WITH_REV is disabled, we can not compare the version.
 
#if defined(WITH_REV)
 
	// Check if the client has WITH_REV enabled
 
	if (strncmp("norev000", client_revision, sizeof(client_revision)) != 0) {
 
		if (strncmp(_network_game_info.server_revision, client_revision, sizeof(_network_game_info.server_revision)) != 0) {
 
			// Different revisions!!
 
			SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_WRONG_REVISION);
 

	
 
			return;
 
		}
 
	}
 
#endif
 

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

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

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

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

	
 
	ci = DEREF_CLIENT_INFO(cs);
 

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

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

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

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

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

	
 
		ci = DEREF_CLIENT_INFO(cs);
 

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

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

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

	
 
		SEND_COMMAND(PACKET_SERVER_WELCOME)(cs);
 
		return;
 
	}
 

	
 

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

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_GETMAP)
 
{
 
	ClientState *new_cs;
 

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

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

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

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_MAP_OK)
 
{
 
	// Client has the map, now start syncing
 
	if (cs->status == STATUS_DONE_MAP && !cs->quited) {
 
		char client_name[NETWORK_NAME_LENGTH];
 
		char str[100];
 
		ClientState *new_cs;
 
		GetString(str, STR_NETWORK_CLIENT_JOINED);
 

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

	
 
		NetworkTextMessage(NETWORK_ACTION_JOIN_LEAVE, 1, client_name, str);
 

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

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

	
 
		FOR_ALL_CLIENTS(new_cs) {
 
			if (new_cs->status > STATUS_AUTH) {
 
				SEND_COMMAND(PACKET_SERVER_CLIENT_INFO)(new_cs, DEREF_CLIENT_INFO(cs));
 
				SEND_COMMAND(PACKET_SERVER_JOIN)(new_cs, cs->index);
 
			}
 
		}
 
	} else {
 
		// Wrong status for this packet, give a warning to client, and close connection
 
		SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_NOT_EXPECTED);
 
	}
 
}
 

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

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

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

	
 
	cp->player = NetworkRecv_uint8(p);
 
	cp->cmd = NetworkRecv_uint32(p);
 
	cp->p1 = NetworkRecv_uint32(p);
 
	cp->p2 = NetworkRecv_uint32(p);
 
	cp->tile = NetworkRecv_uint32(p);
 
	for (i = 0; i < lengthof(cp->dp); i++)
 
		cp->dp[i] = NetworkRecv_uint32(p);
 

	
 
	callback = NetworkRecv_uint8(p);
 

	
 
	ci = DEREF_CLIENT_INFO(cs);
 
	// Only CMD_PLAYER_CTRL is always allowed, for the rest, playas needs
 
	//  to match the player in the packet
 
	if (cp->cmd != CMD_PLAYER_CTRL && ci->client_playas-1 != cp->player) {
 
		// The player did a command with the wrong player_id.. bad!!
 
		SEND_COMMAND(PACKET_SERVER_ERROR)(cs, NETWORK_ERROR_PLAYER_MISMATCH);
 
		return;
 
	}
 
	if (cp->cmd == CMD_PLAYER_CTRL) {
 
		// UGLY! p1 is mis-used to get the client-id in CmdPlayerCtrl
 
		cp->p2 = cs - _clients;
 
	}
 

	
 

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

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

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

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_ERROR)
 
{
 
	// This packets means a client noticed an error and is reporting this
 
	//  to us. Display the error and report it to the other clients
 
	ClientState *new_cs;
 
	byte errorno = NetworkRecv_uint8(p);
 
	char str1[100], str2[100];
 
	char client_name[NETWORK_NAME_LENGTH];
 

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

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

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

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

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

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

	
 
	cs->quited = true;
 
}
 

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_QUIT)
 
{
 
	// The client wants to leave. Display this and report it to the other
 
	//  clients.
 
	ClientState *new_cs;
 
	char str1[100], str2[100];
 
	char client_name[NETWORK_NAME_LENGTH];
 

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

	
 
	NetworkRecv_string(p, str2, 100);
 

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

	
 
	GetString(str1, STR_NETWORK_ERR_LEFT);
 

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

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

	
 
	cs->quited = true;
 
}
 

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

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

	
 

	
 

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

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

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

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

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

	
 
	NetworkRecv_string(p, msg, MAX_TEXT_MSG_LEN);
 

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

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

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

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

	
 
DEF_SERVER_RECEIVE_COMMAND(PACKET_CLIENT_SET_NAME)
 
{
 
	char name[NETWORK_NAME_LENGTH];
 
	NetworkClientInfo *ci;
 

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

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

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

	
 

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

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

	
 

	
 
extern const SettingDesc patch_settings[];
 

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

	
 
	item = patch_settings;
 

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

	
 
	NetworkSend_Packet(p, cs);
 
}
 

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

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

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

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

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

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

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

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

	
 
	ci = NetworkFindClientInfoFromIndex(NETWORK_SERVER_INDEX);
 
	// Register local player (if not dedicated)
 
	if (ci != NULL && _local_player < MAX_PLAYERS) {
 
		snprintf(_network_player_info[ci->client_playas-1].players, sizeof(_network_player_info[ci->client_playas-1].players), "%s", ci->client_name);
 
	}
 

	
 
	FOR_ALL_CLIENTS(cs) {
 
		char client_name[NETWORK_NAME_LENGTH];
 
		char temp[NETWORK_PLAYERS_LENGTH];
 

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

	
 
		ci = DEREF_CLIENT_INFO(cs);
 
		if (ci != NULL && ci->client_playas <= MAX_PLAYERS) {
 
			if (_network_player_info[ci->client_playas-1].players[0] == '\0')
 
				snprintf(_network_player_info[ci->client_playas-1].players, sizeof(_network_player_info[ci->client_playas-1].players), "%s", client_name);
 
			else {
 
				snprintf(temp, sizeof(temp), "%s, %s", _network_player_info[ci->client_playas-1].players, client_name);
 
				snprintf(_network_player_info[ci->client_playas-1].players, sizeof(_network_player_info[ci->client_playas-1].players), "%s", temp);
 
			}
 
		}
 
	}
 
}
 

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

	
 
	ci = NetworkFindClientInfoFromIndex(client_index);
 

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

	
 
// This function changes new_name to a name that is unique (by adding #1 ...)
 
//  and it returns true if that succeeded.
 
bool NetworkFindName(char new_name[NETWORK_NAME_LENGTH])
 
{
 
	ClientState *new_cs;
 
	NetworkClientInfo *ci;
 
	bool found_name = false;
 
	byte number = 0;
 
	char original_name[NETWORK_NAME_LENGTH];
 

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

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

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

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

	
 
	return found_name;
 
}
 

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

	
 
	return true;
 
}
 

	
 
// Handle the local command-queue
 
void NetworkHandleCommandQueue(ClientState *cs) {
 
	if (cs->command_queue != NULL) {
 
		CommandPacket *cp;
 
		CommandPacket *cp_prev;
 

	
 
		cp = cs->command_queue;
 
		cp_prev = NULL;
 

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

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

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

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

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

	
 
				// Report once per time we detect the lag
 
				if (cs->lag_test == 0) {
 
					IConsolePrintF(_iconsole_color_warning,"[%d] Client #%d is slow, try increasing *net_frame_freq to a higher value!", _frame_counter, cs->index);
 
					cs->lag_test = 1;
 
				}
 
			} else {
 
				cs->lag_test = 0;
 
			}
 
		}
 

	
 

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

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

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

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

	
 
#ifdef ENABLE_NETWORK
 

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

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

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

	
 
#endif /* ENABLE_NETWORK */
 

	
 
#endif // NETWORK_SERVER_H
network_udp.c
Show inline comments
 
new file 100644
 
#include "stdafx.h"
 
#include "network_data.h"
 

	
 
#ifdef ENABLE_NETWORK
 

	
 
#include "network_gamelist.h"
 

	
 
extern void UpdateNetworkGameWindow(bool unselect);
 

	
 
//
 
// This file handles all the LAN-stuff
 
// Stuff like:
 
//   - UDP search over the network
 
//
 

	
 
typedef enum {
 
	PACKET_UDP_FIND_SERVER,
 
	PACKET_UDP_SERVER_RESPONSE,
 
	PACKET_UDP_END
 
} PacketUDPType;
 

	
 
static SOCKET _udp_server_socket; // udp server socket
 

	
 
#define DEF_UDP_RECEIVE_COMMAND(type) void NetworkPacketReceive_ ## type ## _command(Packet *p, struct sockaddr_in *client_addr)
 
void NetworkSendUDP_Packet(Packet *p, struct sockaddr_in *recv);
 

	
 
DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_FIND_SERVER)
 
{
 
	Packet *packet;
 
	// Just a fail-safe.. should never happen
 
	if (!_network_udp_server)
 
		return;
 

	
 
	packet = NetworkSend_Init(PACKET_UDP_SERVER_RESPONSE);
 

	
 
	// Update some game_info
 
	_network_game_info.game_date = _date;
 
	_network_game_info.map_set = _opt.landscape;
 

	
 
	NetworkSend_uint8 (packet, NETWORK_GAME_INFO_VERSION);
 
	NetworkSend_string(packet, _network_game_info.server_name);
 
	NetworkSend_string(packet, _network_game_info.server_revision);
 
	NetworkSend_uint8 (packet, _network_game_info.server_lang);
 
	NetworkSend_uint8 (packet, _network_game_info.use_password);
 
	NetworkSend_uint8 (packet, _network_game_info.clients_max);
 
	NetworkSend_uint8 (packet, _network_game_info.clients_on);
 
	NetworkSend_uint8 (packet, _network_game_info.spectators_on);
 
	NetworkSend_uint16(packet, _network_game_info.game_date);
 
	NetworkSend_uint16(packet, _network_game_info.start_date);
 
	NetworkSend_string(packet, _network_game_info.map_name);
 
	NetworkSend_uint16(packet, _network_game_info.map_width);
 
	NetworkSend_uint16(packet, _network_game_info.map_height);
 
	NetworkSend_uint8 (packet, _network_game_info.map_set);
 
	NetworkSend_uint8 (packet, _network_game_info.dedicated);
 

	
 
	// Let the client know that we are here
 
	NetworkSendUDP_Packet(packet, client_addr);
 

	
 
	free(packet);
 

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

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

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

	
 
	game_info_version = NetworkRecv_uint8(p);
 

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

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

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

	
 
	item->online = true;
 

	
 
	UpdateNetworkGameWindow(false);
 
}
 

	
 

	
 
// The layout for the receive-functions by UDP
 
typedef void NetworkUDPPacket(Packet *p, struct sockaddr_in *client_addr);
 

	
 
static NetworkUDPPacket* const _network_udp_packet[] = {
 
	RECEIVE_COMMAND(PACKET_UDP_FIND_SERVER),
 
	RECEIVE_COMMAND(PACKET_UDP_SERVER_RESPONSE),
 
};
 

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

	
 

	
 
void NetworkHandleUDPPacket(Packet *p, struct sockaddr_in *client_addr)
 
{
 
	byte type;
 

	
 
	type = NetworkRecv_uint8(p);
 

	
 
	if (type < PACKET_UDP_END && _network_udp_packet[type] != NULL) {
 
		_network_udp_packet[type](p, client_addr);
 
	}	else {
 
		DEBUG(net, 0)("[NET][UDP] Received invalid packet type %d", type);
 
	}
 
}
 

	
 

	
 
// Send a packet over UDP
 
void NetworkSendUDP_Packet(Packet *p, struct sockaddr_in *recv)
 
{
 
	SOCKET udp;
 
	int res;
 

	
 
	// Find the correct socket
 
	if (_network_udp_server)
 
		udp = _udp_server_socket;
 
	else
 
		udp = _udp_client_socket;
 

	
 
	// Put the length in the buffer
 
	p->buffer[0] = p->size & 0xFF;
 
	p->buffer[1] = p->size >> 8;
 

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

	
 
	// Check for any errors, but ignore it for the rest
 
	if (res == -1) {
 
		DEBUG(net, 1)("[NET][UDP] Send error: %i", GET_LAST_ERROR());
 
	}
 
}
 

	
 
// Start UDP listener
 
bool NetworkUDPListen(uint32 host, uint16 port)
 
{
 
	struct sockaddr_in sin;
 
	SOCKET udp;
 

	
 
	// Make sure sockets are closed
 
	if (_network_udp_server)
 
		closesocket(_udp_server_socket);
 
	else
 
		closesocket(_udp_client_socket);
 

	
 
	udp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
 
	if (udp == INVALID_SOCKET) {
 
		DEBUG(net, 1)("[NET][UDP] Failed to start UDP support");
 
		return false;
 
	}
 

	
 
	// set nonblocking mode for socket
 
	{
 
		unsigned long blocking = 1;
 
		ioctlsocket(udp, FIONBIO, &blocking);
 
	}
 

	
 
	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, 1) ("[NET][UDP] error: bind failed on port %i", port);
 
		return false;
 
	}
 

	
 
	// enable broadcasting
 
	// allow reusing
 
	{
 
		unsigned long val = 1;
 
		setsockopt(udp, SOL_SOCKET, SO_BROADCAST, (char *) &val , sizeof(val));
 
		val = 1;
 
		setsockopt(udp, SOL_SOCKET, SO_REUSEADDR, (char *) &val , sizeof(val));
 
	}
 

	
 
	if (_network_udp_server)
 
		_udp_server_socket = udp;
 
	else
 
		_udp_client_socket = udp;
 

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

	
 
	return true;
 
}
 

	
 
// Close UDP connection
 
void NetworkUDPClose(void)
 
{
 
	DEBUG(net, 1) ("[NET][UDP] Closed listener");
 

	
 
	if (_network_udp_server) {
 
		closesocket(_udp_server_socket);
 
		_udp_server_socket = INVALID_SOCKET;
 
		_network_udp_server = false;
 
		_network_udp_broadcast = 0;
 
	} else {
 
		closesocket(_udp_client_socket);
 
		_udp_client_socket = INVALID_SOCKET;
 
		_network_udp_broadcast = 0;
 
	}
 
}
 

	
 
// Receive something on UDP level
 
void NetworkUDPReceive(void)
 
{
 
	struct sockaddr_in client_addr;
 
#ifndef __MORPHOS__
 
	int client_len;
 
#else
 
	LONG client_len; // for some reason we need a 'LONG' under MorphOS
 
#endif
 
	int nbytes;
 
	static Packet *p = NULL;
 
	int packet_len;
 
	SOCKET udp;
 

	
 
	if (_network_udp_server)
 
		udp = _udp_server_socket;
 
	else
 
		udp = _udp_client_socket;
 

	
 
	// If p is NULL, malloc him.. this prevents unneeded mallocs
 
	if (p == NULL)
 
		p = malloc(sizeof(Packet));
 

	
 
	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.. just asume we receive the whole packet
 
	if (nbytes > 0) {
 
		// Get the size of the buffer
 
		p->size = (uint16)p->buffer[0];
 
		p->size += (uint16)p->buffer[1] << 8;
 
		// Put the position on the right place
 
		p->pos = 2;
 
		p->next = NULL;
 

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

	
 
		// Free the packet
 
		free(p);
 
		p = NULL;
 
	}
 
}
 

	
 
// Broadcast to all ips
 
void NetworkUDPBroadCast(void)
 
{
 
	int i;
 
	struct sockaddr_in out_addr;
 
	byte *bcptr;
 
	uint32 bcaddr;
 
	Packet *p;
 

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

	
 
	// Go through all the ips on this pc
 
	i = 0;
 
	while (_network_ip_list[i] != 0) {
 
		bcaddr = _network_ip_list[i];
 
		bcptr = (byte *)&bcaddr;
 
		// Make the address a broadcast address
 
		bcptr[3] = 255;
 

	
 
		DEBUG(net, 6)("[NET][UDP] Broadcasting to %s", inet_ntoa(*(struct in_addr *)&bcaddr));
 

	
 
		out_addr.sin_family = AF_INET;
 
		out_addr.sin_port = htons(_network_server_port);
 
		out_addr.sin_addr.s_addr = bcaddr;
 

	
 
		NetworkSendUDP_Packet(p, &out_addr);
 

	
 
		i++;
 
	}
 

	
 
	free(p);
 
}
 

	
 
// Find all servers
 
void NetworkUDPSearchGame(void)
 
{
 
	// We are still searching..
 
	if (_network_udp_broadcast > 0)
 
		return;
 

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

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

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

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

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

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

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

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

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

	
 
	NetworkSendUDP_Packet(p, &out_addr);
 

	
 
	free(p);
 

	
 
	UpdateNetworkGameWindow(false);
 
}
 

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

	
 
	_network_udp_server = false;
 
	_network_udp_broadcast = 0;
 
}
 

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

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

	
 
#endif /* NETWORK_LAN_H */
order_cmd.c
Show inline comments
 
@@ -325,7 +325,7 @@ void RestoreVehicleOrders(Vehicle *v, Ba
 
		DoCommandP(0, v->index, 0, NULL, CMD_NAME_VEHICLE);
 
	}
 

	
 
	DoCommandP(0, v->index, bak->orderindex|(bak->service_interval<<16) , NULL, CMD_RESTORE_ORDER_INDEX | CMD_ASYNC);
 
	DoCommandP(0, v->index, bak->orderindex|(bak->service_interval<<16) , NULL, CMD_RESTORE_ORDER_INDEX);
 

	
 
	os = bak->order;
 
	if (os[0] == 0xFFFF) {
 
@@ -333,9 +333,13 @@ void RestoreVehicleOrders(Vehicle *v, Ba
 
		return;
 
	}
 

	
 
	// CMD_NO_TEST_IF_IN_NETWORK is used here, because CMD_INSERT_ORDER checks if the
 
	//  order number is one more then the current amount of orders, and because
 
	//  in network the commands are queued before send, the second insert always
 
	//  fails in test mode. By bypassing the test-mode, that no longer is a problem.
 
	ind = 0;
 
	while ((ord = *os++) != 0) {
 
		if (!DoCommandP(0, v->index + (ind << 16), ord, NULL, CMD_INSERT_ORDER | CMD_ASYNC))
 
		if (!DoCommandP(0, v->index + (ind << 16), ord, NULL, CMD_INSERT_ORDER | CMD_NO_TEST_IF_IN_NETWORK))
 
			break;
 
		ind++;
 
	}
player_gui.c
Show inline comments
 
@@ -490,6 +490,9 @@ static void PlayerCompanyWndProc(Window 
 

	
 
		dis = 0;
 
		if (GetAmountOwnedBy(p, 0xFF) == 0) dis |= 1 << 9;
 
		// Also disable the buy button if 25% is not-owned by someone
 
		//   and the player is not an AI
 
		if (GetAmountOwnedBy(p, 0xFF) == 1 && !p->is_ai) dis |= 1 << 9;
 
		if (GetAmountOwnedBy(p, _local_player) == 0) dis |= 1 << 10;
 

	
 
		w->disabled_state = dis;
 
@@ -501,7 +504,8 @@ static void PlayerCompanyWndProc(Window 
 
		DrawPlayerVehiclesAmount(w->window_number);
 

	
 
		DrawString(110,48, STR_7006_COLOR_SCHEME, 0);
 
		DrawSprite((p->player_color<<16) + 0x3078C19, 215,49);
 
		// Draw company-colour bus (0xC19)
 
		DrawSprite(PLAYER_SPRITE_COLOR(p->index) + 0x8C19, 215, 49);
 

	
 
		DrawPlayerFace(p->face, p->player_color, 2, 16);
 

	
players.c
Show inline comments
 
@@ -11,6 +11,7 @@
 
#include "command.h"
 
#include "ai.h"
 
#include "sound.h"
 
#include "network.h"
 

	
 
extern void StartupEconomy();
 

	
 
@@ -489,6 +490,7 @@ Player *DoStartupNewPlayer(bool is_ai)
 

	
 
	InvalidateWindow(WC_GRAPH_LEGEND, 0);
 
	InvalidateWindow(WC_TOOLBAR_MENU, 0);
 
	InvalidateWindow(WC_CLIENT_LIST, 0);
 

	
 
	return p;
 
}
 
@@ -538,7 +540,7 @@ void OnTick_Players()
 
	_cur_player_tick_index = (_cur_player_tick_index + 1) % MAX_PLAYERS;
 
	if (p->name_1 != 0) GenerateCompanyName(p);
 

	
 
	if (_game_mode != GM_MENU && !--_next_competitor_start) {
 
	if (!_networking && _game_mode != GM_MENU && !--_next_competitor_start) {
 
		MaybeStartNewPlayer();
 
	}
 
}
 
@@ -636,12 +638,44 @@ int32 CmdPlayerCtrl(int x, int y, uint32
 
	if (!(flags & DC_EXEC))
 
		return 0;
 

	
 
	_current_player = OWNER_NONE;
 

	
 
	switch(p1 & 0xff) {
 
	case 0: // make new player
 
		p = DoStartupNewPlayer(false);
 
		if (_local_player == OWNER_SPECTATOR && p != NULL) {
 
			_local_player = p->index;
 
			MarkWholeScreenDirty();
 
		if (p != NULL) {
 
			if (_local_player == OWNER_SPECTATOR) {
 
				_local_player = p->index;
 
				MarkWholeScreenDirty();
 
			}
 
#ifdef ENABLE_NETWORK
 
			if (_network_server) {
 
				NetworkClientInfo *ci;
 
				// UGLY! p2 is mis-used to fetch the client-id
 
				ci = &_network_client_info[p2];
 
				ci->client_playas = p->index + 1;
 
				NetworkUpdateClientInfo(ci->client_index);
 

	
 
				if (ci->client_playas != 0 && ci->client_playas <= MAX_PLAYERS) {
 
					memcpy(_decode_parameters, ci->client_name, 32);
 
					/* XXX - What are the consequents of this? It is needed, but is it bad? */
 
					_docommand_recursive = 0;
 
					DoCommandP(0, ci->client_playas-1, 0, NULL, CMD_CHANGE_PRESIDENT_NAME | CMD_MSG(STR_700D_CAN_T_CHANGE_PRESIDENT));
 
				}
 
			} else {
 
				_network_playas = p->index + 1;
 
			}
 
		} else {
 
			if (_network_server) {
 
				NetworkClientInfo *ci;
 
				// UGLY! p2 is mis-used to fetch the client-id
 
				ci = &_network_client_info[p2];
 
				ci->client_playas = OWNER_SPECTATOR;
 
				NetworkUpdateClientInfo(ci->client_index);
 
			} else {
 
				_network_playas = OWNER_SPECTATOR;
 
			}
 
#endif /* ENABLE_NETWORK */
 
		}
 
		break;
 
	case 1: // make new ai player
rail_cmd.c
Show inline comments
 
@@ -826,7 +826,7 @@ int32 CmdRenameWaypoint(int x, int y, ui
 
	StringID str;
 

	
 
	if (_decode_parameters[0] != 0) {
 
		str = AllocateName((byte*)_decode_parameters, 0);
 
		str = AllocateNameUnique((byte*)_decode_parameters, 0);
 
		if (str == 0) return CMD_ERROR;
 

	
 
		if (flags & DC_EXEC) {
rail_gui.c
Show inline comments
 
@@ -31,7 +31,7 @@ static void ShowStationBuilder();
 

	
 
typedef void OnButtonClick(Window *w);
 

	
 
static void CcPlaySound1E(bool success, uint tile, uint32 p1, uint32 p2)
 
void CcPlaySound1E(bool success, uint tile, uint32 p1, uint32 p2)
 
{
 
	if (success) SndPlayTileFx(SND_20_SPLAT_2, tile);
 
}
 
@@ -91,7 +91,7 @@ static const uint16 _place_depot_extra[1
 
};
 

	
 

	
 
static void CcDepot(bool success, uint tile, uint32 p1, uint32 p2)
 
void CcRailDepot(bool success, uint tile, uint32 p1, uint32 p2)
 
{
 
	if (success) {
 
		int dir = p2;
 
@@ -111,7 +111,7 @@ static void CcDepot(bool success, uint t
 

	
 
static void PlaceRail_Depot(uint tile)
 
{
 
	DoCommandP(tile, _cur_railtype, _build_depot_direction, CcDepot,
 
	DoCommandP(tile, _cur_railtype, _build_depot_direction, CcRailDepot,
 
		CMD_BUILD_TRAIN_DEPOT | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_100E_CAN_T_BUILD_TRAIN_DEPOT));
 
}
 

	
 
@@ -124,7 +124,7 @@ static void PlaceRail_Waypoint(uint tile
 
	}
 
}
 

	
 
static void CcStation(bool success, uint tile, uint32 p1, uint32 p2)
 
void CcStation(bool success, uint tile, uint32 p1, uint32 p2)
 
{
 
	if (success) {
 
		SndPlayTileFx(SND_20_SPLAT_2, tile);
 
@@ -180,7 +180,7 @@ static void PlaceRail_Bridge(uint tile)
 
	VpStartPlaceSizing(tile, VPM_X_OR_Y);
 
}
 

	
 
static void CcBuildTunnel(bool success, uint tile, uint32 p1, uint32 p2)
 
void CcBuildRailTunnel(bool success, uint tile, uint32 p1, uint32 p2)
 
{
 
	if (success) {
 
		SndPlayTileFx(SND_20_SPLAT_2, tile);
 
@@ -192,7 +192,7 @@ static void CcBuildTunnel(bool success, 
 

	
 
static void PlaceRail_Tunnel(uint tile)
 
{
 
	DoCommandP(tile, _cur_railtype, 0, CcBuildTunnel,
 
	DoCommandP(tile, _cur_railtype, 0, CcBuildRailTunnel,
 
		CMD_BUILD_TUNNEL | CMD_AUTO | CMD_MSG(STR_5016_CAN_T_BUILD_TUNNEL_HERE));
 
}
 

	
road_gui.c
Show inline comments
 
@@ -20,7 +20,7 @@ static byte _place_road_flag;
 
static byte _road_depot_orientation;
 
static byte _road_station_picker_orientation;
 

	
 
static void CcPlaySound1D(bool success, uint tile, uint32 p1, uint32 p2)
 
void CcPlaySound1D(bool success, uint tile, uint32 p1, uint32 p2)
 
{
 
	if (success) SndPlayTileFx(SND_1F_SPLAT, tile);
 
}
 
@@ -43,7 +43,7 @@ static void PlaceRoad_Bridge(uint tile)
 
}
 

	
 

	
 
static void CcBuildTunnel(bool success, uint tile, uint32 p1, uint32 p2)
 
void CcBuildRoadTunnel(bool success, uint tile, uint32 p1, uint32 p2)
 
{
 
	if (success) {
 
		SndPlayTileFx(SND_20_SPLAT_2, tile);
 
@@ -55,7 +55,7 @@ static void CcBuildTunnel(bool success, 
 

	
 
static void PlaceRoad_Tunnel(uint tile)
 
{
 
	DoCommandP(tile, 0x200, 0, CcBuildTunnel, CMD_BUILD_TUNNEL | CMD_AUTO | CMD_MSG(STR_5016_CAN_T_BUILD_TUNNEL_HERE));
 
	DoCommandP(tile, 0x200, 0, CcBuildRoadTunnel, CMD_BUILD_TUNNEL | CMD_AUTO | CMD_MSG(STR_5016_CAN_T_BUILD_TUNNEL_HERE));
 
}
 

	
 
static void BuildRoadOutsideStation(uint tile, int direction)
 
@@ -68,7 +68,7 @@ static void BuildRoadOutsideStation(uint
 
	}
 
}
 

	
 
static void CcDepot(bool success, uint tile, uint32 p1, uint32 p2)
 
void CcRoadDepot(bool success, uint tile, uint32 p1, uint32 p2)
 
{
 
	if (success) {
 
		SndPlayTileFx(SND_1F_SPLAT, tile);
 
@@ -79,17 +79,17 @@ static void CcDepot(bool success, uint t
 

	
 
static void PlaceRoad_Depot(uint tile)
 
{
 
	DoCommandP(tile, _road_depot_orientation, 0, CcDepot, CMD_BUILD_ROAD_DEPOT | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1807_CAN_T_BUILD_ROAD_VEHICLE));
 
	DoCommandP(tile, _road_depot_orientation, 0, CcRoadDepot, CMD_BUILD_ROAD_DEPOT | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1807_CAN_T_BUILD_ROAD_VEHICLE));
 
}
 

	
 
static void PlaceRoad_BusStation(uint tile)
 
{
 
	DoCommandP(tile, _road_station_picker_orientation, 0, CcDepot, CMD_BUILD_BUS_STATION | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1808_CAN_T_BUILD_BUS_STATION));
 
	DoCommandP(tile, _road_station_picker_orientation, 0, CcRoadDepot, CMD_BUILD_BUS_STATION | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1808_CAN_T_BUILD_BUS_STATION));
 
}
 

	
 
static void PlaceRoad_TruckStation(uint tile)
 
{
 
	DoCommandP(tile, _road_station_picker_orientation, 0, CcDepot, CMD_BUILD_TRUCK_STATION | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1809_CAN_T_BUILD_TRUCK_STATION));
 
	DoCommandP(tile, _road_station_picker_orientation, 0, CcRoadDepot, CMD_BUILD_TRUCK_STATION | CMD_AUTO | CMD_NO_WATER | CMD_MSG(STR_1809_CAN_T_BUILD_TRUCK_STATION));
 
}
 

	
 
static void PlaceRoad_DemolishArea(uint tile)
roadveh_gui.c
Show inline comments
 
@@ -368,7 +368,7 @@ static void DrawNewRoadVehWindow(Window 
 
	}
 
}
 

	
 
static void CcBuildRoadVeh(bool success, uint tile, uint32 p1, uint32 p2)
 
void CcBuildRoadVeh(bool success, uint tile, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 

	
scripts/on_client.scr
Show inline comments
 
echo "Setting default network client settings..."
 
*net_ready_ahead = 1
 
\ No newline at end of file
scripts/on_dedicated.scr
Show inline comments
 
new file 100644
 
echo "Setting dedicated network server settings..."
 
setpassword "*"
 
setservername "My example dedicated gameserver"
scripts/on_server.scr
Show inline comments
 
echo "Setting default network server settings..."
 
*net_sync_freq = 4
 
*net_client_timeout = 300
 
\ No newline at end of file
 
net_sync_freq = 100
 
net_frame_freq = 0
sdl.c
Show inline comments
 
@@ -8,6 +8,7 @@
 
#include <SDL.h>
 
#include "player.h"
 
#include "hal.h"
 
#include "network.h"
 

	
 
#ifdef UNIX
 
#include <signal.h>
 
@@ -607,6 +608,7 @@ static int SdlVideoMainLoop(void)
 
		} else {
 
			SDL_CALL SDL_Delay(1);
 
			_screen.dst_ptr = _sdl_screen->pixels;
 
			DrawTextMessage();
 
			DrawMouseCursor();
 
			DrawSurfaceToScreen();
 
		}
settings.c
Show inline comments
 
#include "stdafx.h"
 
#include "ttd.h"
 
#include "sound.h"
 

	
 
enum SettingDescType {
 
	SDT_INTX, // must be 0
 
	SDT_ONEOFMANY,
 
	SDT_MANYOFMANY,
 
	SDT_BOOLX,
 
	SDT_STRING,
 
	SDT_STRINGBUF,
 
	SDT_INTLIST,
 

	
 
	SDT_INT8 = 0 << 4,
 
	SDT_UINT8 = 1 << 4,
 
	SDT_INT16 = 2 << 4,
 
	SDT_UINT16 = 3 << 4,
 
	SDT_INT32 = 4 << 4,
 
	SDT_UINT32 = 5 << 4,
 
	SDT_CALLBX = 6 << 4,
 

	
 
	SDT_UINT = SDT_UINT32,
 
	SDT_INT = SDT_INT32,
 

	
 
	SDT_NOSAVE = 1 << 8,
 

	
 
	SDT_CALLB = SDT_INTX | SDT_CALLBX,
 

	
 
	SDT_BOOL = SDT_BOOLX | SDT_UINT8,
 
};
 
#include "network.h"
 
#include "settings.h"
 

	
 
typedef struct IniFile IniFile;
 
typedef struct IniItem IniItem;
 
typedef struct IniGroup IniGroup;
 
typedef struct SettingDesc SettingDesc;
 
typedef struct MemoryPool MemoryPool;
 

	
 
static void pool_init(MemoryPool **pool);
 
@@ -322,15 +296,6 @@ static void ini_free(IniFile *ini)
 
	pool_free(&ini->pool);
 
}
 

	
 
struct SettingDesc {
 
	const char *name;
 
	int flags;
 
	const void *def;
 
	void *ptr;
 
	const void *b;
 

	
 
};
 

	
 
static int lookup_oneofmany(const char *many, const char *one, int onelen)
 
{
 
	const char *s;
 
@@ -532,7 +497,7 @@ static const void *string_to_val(const S
 
	return NULL;
 
}
 

	
 
static void load_setting_desc(IniFile *ini, const SettingDesc *desc, const void *grpname, void *base)
 
static void load_setting_desc(IniFile *ini, const SettingDesc *desc, const void *grpname)
 
{
 
	IniGroup *group_def = ini_getgroup(ini, grpname, -1), *group;
 
	IniItem *item;
 
@@ -559,8 +524,6 @@ static void load_setting_desc(IniFile *i
 

	
 
		// get ptr to array
 
		ptr = desc->ptr;
 
		if ( (uint32)ptr < 0x10000)
 
			ptr = (byte*)base + (uint32)ptr;
 

	
 
		switch(desc->flags & 0xF) {
 
		// all these are stored in the same way
 
@@ -603,7 +566,7 @@ static void load_setting_desc(IniFile *i
 
	}
 
}
 

	
 
static void save_setting_desc(IniFile *ini, const SettingDesc *desc, const void *grpname, void *base)
 
static void save_setting_desc(IniFile *ini, const SettingDesc *desc, const void *grpname)
 
{
 
	IniGroup *group_def = NULL, *group;
 
	IniItem *item;
 
@@ -633,8 +596,6 @@ static void save_setting_desc(IniFile *i
 

	
 
		// get ptr to array
 
		ptr = desc->ptr;
 
		if ( (uint32)ptr < 0x10000)
 
			ptr = (byte*)base + (uint32)ptr;
 

	
 
		if (item->value != NULL) {
 
			// check if the value is the same as the old value
 
@@ -726,13 +687,13 @@ static void save_setting_desc(IniFile *i
 
//***************************
 

	
 
static const SettingDesc music_settings[] = {
 
	{"playlist",	SDT_UINT8,	(void*)0,			(void*)offsetof(MusicFileSettings,	playlist), NULL},
 
	{"music_vol", SDT_UINT8,	(void*)128,		(void*)offsetof(MusicFileSettings,	music_vol), NULL},
 
	{"effect_vol",SDT_UINT8,	(void*)128,		(void*)offsetof(MusicFileSettings,	effect_vol), NULL},
 
	{"custom_1",	SDT_INTLIST | SDT_UINT8 | lengthof(msf.custom_1) << 16, NULL, (void*)offsetof(MusicFileSettings, custom_1), NULL},
 
	{"custom_2",	SDT_INTLIST | SDT_UINT8 | lengthof(msf.custom_2) << 16, NULL, (void*)offsetof(MusicFileSettings, custom_2), NULL},
 
	{"playing",		SDT_BOOL,		(void*)true,	(void*)offsetof(MusicFileSettings,	btn_down), NULL},
 
	{"shuffle",		SDT_BOOL,		(void*)false, (void*)offsetof(MusicFileSettings,	shuffle), NULL},
 
	{"playlist",	SDT_UINT8,	(void*)0,			&msf.playlist, NULL},
 
	{"music_vol", SDT_UINT8,	(void*)128,		&msf.music_vol, NULL},
 
	{"effect_vol",SDT_UINT8,	(void*)128,		&msf.effect_vol, NULL},
 
	{"custom_1",	SDT_INTLIST | SDT_UINT8 | lengthof(msf.custom_1) << 16, NULL, &msf.custom_1, NULL},
 
	{"custom_2",	SDT_INTLIST | SDT_UINT8 | lengthof(msf.custom_2) << 16, NULL, &msf.custom_2, NULL},
 
	{"playing",		SDT_BOOL,		(void*)true,	&msf.btn_down, NULL},
 
	{"shuffle",		SDT_BOOL,		(void*)false, &msf.shuffle, NULL},
 
	{NULL,				0,					NULL,					NULL,																NULL}
 
};
 

	
 
@@ -760,13 +721,19 @@ static const SettingDesc misc_settings[]
 
	{NULL,								0,						NULL,					NULL,									NULL}
 
};
 

	
 
#ifdef ENABLE_NETWORK
 
static const SettingDesc network_settings[] = {
 
	{"port",					SDT_UINT | SDT_NOSAVE,	(void*)3978,	&_network_client_port,	NULL},
 
	{"server_port",		SDT_UINT | SDT_NOSAVE,	(void*)3979,	&_network_server_port,	NULL},
 
	{"sync_freq",			SDT_UINT16 | SDT_NOSAVE,	(void*)4,			&_network_sync_freq,		NULL},
 
	{"ahead_frames",	SDT_UINT16 | SDT_NOSAVE,	(void*)5,			&_network_ahead_frames, NULL},
 
	{"port",					SDT_UINT | SDT_NOSAVE,	(void*)NETWORK_DISCOVER_PORT,	&_network_client_port,	NULL},
 
	{"server_port",		SDT_UINT | SDT_NOSAVE,	(void*)NETWORK_DEFAULT_PORT,	&_network_server_port,	NULL},
 
	{"sync_freq",			SDT_UINT16 | SDT_NOSAVE,	(void*)100,			&_network_sync_freq,		NULL},
 
	{"frame_freq",			SDT_UINT8 | SDT_NOSAVE,	(void*)0,			&_network_frame_freq,		NULL},
 
	{"player_name",		SDT_STRINGBUF | (lengthof(_network_player_name) << 16), NULL, &_network_player_name, NULL},
 
	{"server_password",		SDT_STRINGBUF | (lengthof(_network_game_info.server_password) << 16), NULL, &_network_game_info.server_password, NULL},
 
	{"server_name",		SDT_STRINGBUF | (lengthof(_network_server_name) << 16), NULL, &_network_server_name, NULL},
 
	{"connect_to_ip",		SDT_STRINGBUF | (lengthof(_network_default_ip) << 16), NULL, &_network_default_ip, NULL},
 
	{NULL,						0,											NULL,					NULL,										NULL}
 
};
 
#endif /* ENABLE_NETWORK */
 

	
 
static const SettingDesc debug_settings[] = {
 
	{"savedump_path",		SDT_STRINGBUF | (lengthof(_savedump_path)<<16) | SDT_NOSAVE, NULL, _savedump_path, NULL},
 
@@ -778,126 +745,141 @@ static const SettingDesc debug_settings[
 

	
 

	
 
static const SettingDesc gameopt_settings[] = {
 
	{"diff_level",	SDT_UINT8,									(void*)9,		(void*)offsetof(GameOptions, diff_level), NULL},
 
	{"diff_custom", SDT_INTLIST | SDT_UINT32 | (sizeof(GameDifficulty)/4) << 16, NULL, (void*)offsetof(GameOptions, diff), NULL},
 
	{"currency",		SDT_UINT8 | SDT_ONEOFMANY,	(void*)21,	(void*)offsetof(GameOptions, currency),		"GBP|USD|FF|DM|YEN|PT|FT|ZL|ATS|BEF|DKK|FIM|GRD|CHF|NLG|ITL|SEK|RUR|CZK|ISK|NOK|EUR|ROL" },
 
	{"distances",		SDT_UINT8 | SDT_ONEOFMANY,	(void*)1,		(void*)offsetof(GameOptions, kilometers), "imperial|metric" },
 
	{"town_names",	SDT_UINT8 | SDT_ONEOFMANY,	(void*)0,		(void*)offsetof(GameOptions, town_name),	"english|french|german|american|latin|silly|swedish|dutch|finnish|polish|slovakish|hungarian|romanian|czech" },
 
	{"landscape",		SDT_UINT8 | SDT_ONEOFMANY,	(void*)0,		(void*)offsetof(GameOptions, landscape),	"normal|hilly|desert|candy" },
 
	{"autosave",		SDT_UINT8 | SDT_ONEOFMANY,	(void*)1,		(void*)offsetof(GameOptions, autosave),		"off|monthly|quarterly|half year|yearly" },
 
	{"road_side",		SDT_UINT8 | SDT_ONEOFMANY,	(void*)1,		(void*)offsetof(GameOptions, road_side),	"left|right" },
 
	{"diff_level",	SDT_UINT8,									(void*)9,		&_new_opt.diff_level, NULL},
 
	{"diff_custom", SDT_INTLIST | SDT_UINT32 | (sizeof(GameDifficulty)/4) << 16, NULL, &_new_opt.diff, NULL},
 
	{"currency",		SDT_UINT8 | SDT_ONEOFMANY,	(void*)21,	&_new_opt.currency,		"GBP|USD|FF|DM|YEN|PT|FT|ZL|ATS|BEF|DKK|FIM|GRD|CHF|NLG|ITL|SEK|RUR|CZK|ISK|NOK|EUR|ROL" },
 
	{"distances",		SDT_UINT8 | SDT_ONEOFMANY,	(void*)1,		&_new_opt.kilometers, "imperial|metric" },
 
	{"town_names",	SDT_UINT8 | SDT_ONEOFMANY,	(void*)0,		&_new_opt.town_name,	"english|french|german|american|latin|silly|swedish|dutch|finnish|polish|slovakish|hungarian|romanian|czech" },
 
	{"landscape",		SDT_UINT8 | SDT_ONEOFMANY,	(void*)0,		&_new_opt.landscape,	"normal|hilly|desert|candy" },
 
	{"autosave",		SDT_UINT8 | SDT_ONEOFMANY,	(void*)1,		&_new_opt.autosave,		"off|monthly|quarterly|half year|yearly" },
 
	{"road_side",		SDT_UINT8 | SDT_ONEOFMANY,	(void*)1,		&_new_opt.road_side,	"left|right" },
 
	{NULL,					0,													NULL,				NULL,																			NULL}
 
};
 

	
 
static const SettingDesc patch_settings[] = {
 
	{"vehicle_speed",				SDT_BOOL,		(void*)true,	(void*)offsetof(Patches, vehicle_speed),				NULL},
 
	{"build_on_slopes",			SDT_BOOL,		(void*)true,	(void*)offsetof(Patches, build_on_slopes),			NULL},
 
	{"mammoth_trains",			SDT_BOOL,		(void*)true,	(void*)offsetof(Patches, mammoth_trains),				NULL},
 
	{"join_stations",				SDT_BOOL,		(void*)true,	(void*)offsetof(Patches, join_stations),				NULL},
 
	{"station_spread",			SDT_UINT8,	(void*)12,		(void*)offsetof(Patches, station_spread),				NULL},
 
	{"full_load_any",				SDT_BOOL,		(void*)true,	(void*)offsetof(Patches, full_load_any),				NULL},
 
	{"improved_load",				SDT_BOOL,		(void*)false, (void*)offsetof(Patches, improved_load),				NULL},
 
	{"order_review_system", SDT_UINT8,	(void*)2,			(void*)offsetof(Patches, order_review_system),	NULL},
 

	
 
	{"inflation",						SDT_BOOL,		(void*)true,	(void*)offsetof(Patches, inflation),						NULL},
 
	{"selectgoods",					SDT_BOOL,		(void*)true,	(void*)offsetof(Patches, selectgoods),					NULL},
 
	{"longbridges",					SDT_BOOL,		(void*)false, (void*)offsetof(Patches, longbridges),					NULL},
 
	{"gotodepot",						SDT_BOOL,		(void*)true,	(void*)offsetof(Patches, gotodepot),						NULL},
 

	
 
	{"build_rawmaterial_ind",	SDT_BOOL, (void*)false, (void*)offsetof(Patches, build_rawmaterial_ind),NULL},
 
	{"multiple_industry_per_town",SDT_BOOL, (void*)false, (void*)offsetof(Patches, multiple_industry_per_town), NULL},
 
	{"same_industry_close",	SDT_BOOL,		(void*)false, (void*)offsetof(Patches, same_industry_close),	NULL},
 

	
 
	{"lost_train_days",			SDT_UINT16, (void*)180,		(void*)offsetof(Patches, lost_train_days),			NULL},
 
	{"train_income_warn",		SDT_BOOL,		(void*)true,	(void*)offsetof(Patches, train_income_warn),		NULL},
 

	
 
	{"status_long_date",		SDT_BOOL,		(void*)true,	(void*)offsetof(Patches, status_long_date),			NULL},
 
	{"signal_side",					SDT_BOOL,		(void*)true,	(void*)offsetof(Patches, signal_side),					NULL},
 
	{"show_finances",				SDT_BOOL,		(void*)true,	(void*)offsetof(Patches, show_finances),				NULL},
 

	
 
	{"new_nonstop",					SDT_BOOL,		(void*)false, (void*)offsetof(Patches, new_nonstop),					NULL},
 
	{"roadveh_queue",				SDT_BOOL,		(void*)false, (void*)offsetof(Patches, roadveh_queue),				NULL},
 

	
 
	{"autoscroll",					SDT_BOOL,		(void*)false, (void*)offsetof(Patches, autoscroll),						NULL},
 
	{"errmsg_duration",			SDT_UINT8,	(void*)5,			(void*)offsetof(Patches, errmsg_duration),			NULL},
 
	{"snow_line_height",		SDT_UINT8,	(void*)7,			(void*)offsetof(Patches, snow_line_height),			NULL},
 

	
 
	{"bribe",								SDT_BOOL,		(void*)false, (void*)offsetof(Patches, bribe),								NULL},
 
	{"new_depot_finding",		SDT_BOOL,		(void*)false, (void*)offsetof(Patches, new_depot_finding),		NULL},
 

	
 
	{"nonuniform_stations",	SDT_BOOL,		(void*)false, (void*)offsetof(Patches, nonuniform_stations),	NULL},
 
	{"always_small_airport",SDT_BOOL,		(void*)false, (void*)offsetof(Patches, always_small_airport),	NULL},
 
	{"realistic_acceleration",SDT_BOOL, (void*)false, (void*)offsetof(Patches, realistic_acceleration),	NULL},
 

	
 
	{"toolbar_pos",					SDT_UINT8,	(void*)0,			(void*)offsetof(Patches, toolbar_pos),					NULL},
 
	{"window_snap_radius",  SDT_UINT8,  (void*)10,    (void*)offsetof(Patches, window_snap_radius),   NULL},
 
// The player-based settings (are not send over the network)
 
// Not everything can just be added to this list. For example, service_interval
 
//  can not be done, because every client assigns the service_interval value to the
 
//  v->service_interval, meaning that every client assigns his value to the interval.
 
//  If the setting was player-based, that would mean that vehicles could deside on
 
//  different moments that they are heading back to a service depot, causing desyncs
 
//  on a massive scale.
 
// Short, you can only add settings that does stuff for the screen, GUI, that kind
 
//  of stuff.
 
static const SettingDesc patch_player_settings[] = {
 
	{"vehicle_speed",				SDT_BOOL,		(void*)true,	&_patches.vehicle_speed,				NULL},
 

	
 
	{"max_trains",					SDT_UINT8,	(void*)80,		(void*)offsetof(Patches, max_trains),						NULL},
 
	{"max_roadveh",					SDT_UINT8,	(void*)80,		(void*)offsetof(Patches, max_roadveh),					NULL},
 
	{"max_aircraft",				SDT_UINT8,	(void*)40,		(void*)offsetof(Patches, max_aircraft),					NULL},
 
	{"max_ships",						SDT_UINT8,	(void*)50,		(void*)offsetof(Patches, max_ships),						NULL},
 

	
 
	{"servint_ispercent",		SDT_BOOL,		(void*)false,	(void*)offsetof(Patches, servint_ispercent),		NULL},
 
	{"servint_trains",			SDT_UINT16, (void*)150,		(void*)offsetof(Patches, servint_trains),				NULL},
 
	{"servint_roadveh",			SDT_UINT16, (void*)150,		(void*)offsetof(Patches, servint_roadveh),			NULL},
 
	{"servint_ships",				SDT_UINT16, (void*)360,		(void*)offsetof(Patches, servint_ships),				NULL},
 
	{"servint_aircraft",		SDT_UINT16, (void*)100,		(void*)offsetof(Patches, servint_aircraft),			NULL},
 

	
 
	{"autorenew",						SDT_BOOL,		(void*)false,	(void*)offsetof(Patches, autorenew),						NULL},
 
	{"autorenew_months",		SDT_INT16,	(void*)-6,		(void*)offsetof(Patches, autorenew_months),			NULL},
 
	{"autorenew_money",			SDT_INT32,	(void*)100000,(void*)offsetof(Patches, autorenew_money),			NULL},
 

	
 
	{"new_pathfinding",			SDT_BOOL,		(void*)false, (void*)offsetof(Patches, new_pathfinding),			NULL},
 
	{"pf_maxlength",				SDT_UINT16, (void*)512,		(void*)offsetof(Patches, pf_maxlength),					NULL},
 
	{"pf_maxdepth",					SDT_UINT8,	(void*)16,		(void*)offsetof(Patches, pf_maxdepth),					NULL},
 

	
 
	{"lost_train_days",			SDT_UINT16, (void*)180,		&_patches.lost_train_days,			NULL},
 
	{"train_income_warn",		SDT_BOOL,		(void*)true,	&_patches.train_income_warn,		NULL},
 
	{"order_review_system", SDT_UINT8,	(void*)2,			&_patches.order_review_system,	NULL},
 

	
 
	{"ai_disable_veh_train",SDT_BOOL,		(void*)false, (void*)offsetof(Patches, ai_disable_veh_train),	NULL},
 
	{"ai_disable_veh_roadveh",SDT_BOOL,	(void*)false, (void*)offsetof(Patches, ai_disable_veh_roadveh),	NULL},
 
	{"ai_disable_veh_aircraft",SDT_BOOL,(void*)false, (void*)offsetof(Patches, ai_disable_veh_aircraft),NULL},
 
	{"ai_disable_veh_ship",	SDT_BOOL,		(void*)false, (void*)offsetof(Patches, ai_disable_veh_ship),	NULL},
 
	{"starting_date",				SDT_UINT32, (void*)1950,	(void*)offsetof(Patches, starting_date),				NULL},
 

	
 
	{"colored_news_date",		SDT_UINT32, (void*)2000,	(void*)offsetof(Patches, colored_news_date),		NULL},
 

	
 
	{"bridge_pillars",			SDT_BOOL,		(void*)true,	(void*)offsetof(Patches, bridge_pillars),				NULL},
 
	{"invisible_trees",			SDT_BOOL,		(void*)false, (void*)offsetof(Patches, invisible_trees),			NULL},
 

	
 
	{"keep_all_autosave",		SDT_BOOL,		(void*)false, (void*)offsetof(Patches, keep_all_autosave),		NULL},
 

	
 
	{"extra_dynamite",			SDT_BOOL,		(void*)false, (void*)offsetof(Patches, extra_dynamite),				NULL},
 
	{"status_long_date",		SDT_BOOL,		(void*)true,	&_patches.status_long_date,			NULL},
 
	{"show_finances",				SDT_BOOL,		(void*)true,	&_patches.show_finances,				NULL},
 
	{"autoscroll",					SDT_BOOL,		(void*)false,	&_patches.autoscroll,						NULL},
 
	{"errmsg_duration",			SDT_UINT8,	(void*)5,			&_patches.errmsg_duration,			NULL},
 
	{"toolbar_pos",					SDT_UINT8,	(void*)0,			&_patches.toolbar_pos,					NULL},
 
	{"keep_all_autosave",		SDT_BOOL,		(void*)false, &_patches.keep_all_autosave,		NULL},
 

	
 
	{"never_expire_vehicles",SDT_BOOL,	(void*)false, (void*)offsetof(Patches, never_expire_vehicles),NULL},
 
	{"extend_vehicle_life",	SDT_UINT8,	(void*)0,			(void*)offsetof(Patches, extend_vehicle_life),	NULL},
 

	
 
	{"auto_euro",						SDT_BOOL,		(void*)true,	(void*)offsetof(Patches, auto_euro),						NULL},
 

	
 
	{"serviceathelipad",		SDT_BOOL,		(void*)true,	(void*)offsetof(Patches, serviceathelipad),			NULL},
 
	{"smooth_economy",			SDT_BOOL,		(void*)false, (void*)offsetof(Patches, smooth_economy),				NULL},
 
	{"dist_local_authority",SDT_UINT8,	(void*)20,		(void*)offsetof(Patches, dist_local_authority), NULL},
 

	
 
	{"wait_oneway_signal",	SDT_UINT8,	(void*)15,		(void*)offsetof(Patches, wait_oneway_signal),		NULL},
 
	{"wait_twoway_signal",	SDT_UINT8,	(void*)41,		(void*)offsetof(Patches, wait_twoway_signal),		NULL},
 

	
 
	{"ainew_active",				SDT_BOOL,		(void*)false, (void*)offsetof(Patches, ainew_active),					NULL},
 

	
 
	{"drag_signals_density",SDT_UINT8,	(void*)4,			(void*)offsetof(Patches, drag_signals_density), NULL},
 
	{"bridge_pillars",			SDT_BOOL,		(void*)true,	&_patches.bridge_pillars,				NULL},
 
	{"invisible_trees",			SDT_BOOL,		(void*)false, &_patches.invisible_trees,			NULL},
 
	{"drag_signals_density",SDT_UINT8,	(void*)4,			&_patches.drag_signals_density, NULL},
 

	
 
	{NULL,									0,					NULL,					NULL,																						NULL}
 
};
 

	
 
typedef void SettingDescProc(IniFile *ini, const SettingDesc *desc, const void *grpname, void *base);
 
// Non-static, needed in network_server.c
 
const SettingDesc patch_settings[] = {
 
	{"build_on_slopes",			SDT_BOOL,		(void*)true,	&_patches.build_on_slopes,			NULL},
 
	{"mammoth_trains",			SDT_BOOL,		(void*)true,	&_patches.mammoth_trains,				NULL},
 
	{"join_stations",				SDT_BOOL,		(void*)true,	&_patches.join_stations,				NULL},
 
	{"station_spread",			SDT_UINT8,	(void*)12,		&_patches.station_spread,				NULL},
 
	{"full_load_any",				SDT_BOOL,		(void*)true,	&_patches.full_load_any,				NULL},
 

	
 
	{"inflation",						SDT_BOOL,		(void*)true,	&_patches.inflation,						NULL},
 
	{"selectgoods",					SDT_BOOL,		(void*)true,	&_patches.selectgoods,					NULL},
 
	{"longbridges",					SDT_BOOL,		(void*)true, &_patches.longbridges,					NULL},
 
	{"gotodepot",						SDT_BOOL,		(void*)true,	&_patches.gotodepot,						NULL},
 

	
 
	{"build_rawmaterial_ind",	SDT_BOOL, (void*)false, &_patches.build_rawmaterial_ind,NULL},
 
	{"multiple_industry_per_town",SDT_BOOL, (void*)false, &_patches.multiple_industry_per_town, NULL},
 
	{"same_industry_close",	SDT_BOOL,		(void*)false, &_patches.same_industry_close,	NULL},
 

	
 
	{"signal_side",					SDT_BOOL,		(void*)true,	&_patches.signal_side,					NULL},
 

	
 
	{"new_nonstop",					SDT_BOOL,		(void*)false,	&_patches.new_nonstop,					NULL},
 
	{"roadveh_queue",				SDT_BOOL,		(void*)true,	&_patches.roadveh_queue,				NULL},
 
	{"window_snap_radius",  SDT_UINT8,  (void*)10,    &_patches.window_snap_radius,   NULL},
 

	
 
	{"snow_line_height",		SDT_UINT8,	(void*)7,			&_patches.snow_line_height,			NULL},
 

	
 
	{"bribe",								SDT_BOOL,		(void*)true,	&_patches.bribe,								NULL},
 
	{"new_depot_finding",		SDT_BOOL,		(void*)false,	&_patches.new_depot_finding,		NULL},
 

	
 
	{"nonuniform_stations",	SDT_BOOL,		(void*)true,	&_patches.nonuniform_stations,	NULL},
 
	{"always_small_airport",SDT_BOOL,		(void*)false,	&_patches.always_small_airport,	NULL},
 
	{"realistic_acceleration",SDT_BOOL, (void*)false,	&_patches.realistic_acceleration,	NULL},
 

	
 
	{"max_trains",					SDT_UINT8,	(void*)80,		&_patches.max_trains,						NULL},
 
	{"max_roadveh",					SDT_UINT8,	(void*)80,		&_patches.max_roadveh,					NULL},
 
	{"max_aircraft",				SDT_UINT8,	(void*)40,		&_patches.max_aircraft,					NULL},
 
	{"max_ships",						SDT_UINT8,	(void*)50,		&_patches.max_ships,						NULL},
 

	
 
	{"servint_ispercent",		SDT_BOOL,		(void*)false,	&_patches.servint_ispercent,		NULL},
 
	{"servint_trains",			SDT_UINT16, (void*)150,		&_patches.servint_trains,				NULL},
 
	{"servint_roadveh",			SDT_UINT16, (void*)150,		&_patches.servint_roadveh,			NULL},
 
	{"servint_ships",				SDT_UINT16, (void*)360,		&_patches.servint_ships,				NULL},
 
	{"servint_aircraft",		SDT_UINT16, (void*)100,		&_patches.servint_aircraft,			NULL},
 

	
 
	{"autorenew",						SDT_BOOL,		(void*)false,	&_patches.autorenew,						NULL},
 
	{"autorenew_months",		SDT_INT16,	(void*)-6,		&_patches.autorenew_months,			NULL},
 
	{"autorenew_money",			SDT_INT32,	(void*)100000,&_patches.autorenew_money,			NULL},
 

	
 
	{"new_pathfinding",			SDT_BOOL,		(void*)true,	&_patches.new_pathfinding,			NULL},
 
	{"pf_maxlength",				SDT_UINT16, (void*)512,		&_patches.pf_maxlength,					NULL},
 
	{"pf_maxdepth",					SDT_UINT8,	(void*)16,		&_patches.pf_maxdepth,					NULL},
 

	
 

	
 
	{"ai_disable_veh_train",SDT_BOOL,		(void*)false, &_patches.ai_disable_veh_train,	NULL},
 
	{"ai_disable_veh_roadveh",SDT_BOOL,	(void*)false, &_patches.ai_disable_veh_roadveh,	NULL},
 
	{"ai_disable_veh_aircraft",SDT_BOOL,(void*)false, &_patches.ai_disable_veh_aircraft,NULL},
 
	{"ai_disable_veh_ship",	SDT_BOOL,		(void*)false, &_patches.ai_disable_veh_ship,	NULL},
 
	{"starting_date",				SDT_UINT32, (void*)1950,	&_patches.starting_date,				NULL},
 

	
 
	{"colored_news_date",		SDT_UINT32, (void*)2000,	&_patches.colored_news_date,		NULL},
 

	
 
	{"extra_dynamite",			SDT_BOOL,		(void*)false, &_patches.extra_dynamite,				NULL},
 

	
 
	{"never_expire_vehicles",SDT_BOOL,	(void*)false, &_patches.never_expire_vehicles,NULL},
 
	{"extend_vehicle_life",	SDT_UINT8,	(void*)0,			&_patches.extend_vehicle_life,	NULL},
 

	
 
	{"auto_euro",						SDT_BOOL,		(void*)true,	&_patches.auto_euro,						NULL},
 

	
 
	{"serviceathelipad",		SDT_BOOL,		(void*)true,	&_patches.serviceathelipad,			NULL},
 
	{"smooth_economy",			SDT_BOOL,		(void*)true,	&_patches.smooth_economy,				NULL},
 
	{"dist_local_authority",SDT_UINT8,	(void*)20,		&_patches.dist_local_authority, NULL},
 

	
 
	{"wait_oneway_signal",	SDT_UINT8,	(void*)15,		&_patches.wait_oneway_signal,		NULL},
 
	{"wait_twoway_signal",	SDT_UINT8,	(void*)41,		&_patches.wait_twoway_signal,		NULL},
 

	
 
	{"ainew_active",				SDT_BOOL,		(void*)false, &_patches.ainew_active,					NULL},
 

	
 
	{NULL,									0,					NULL,					NULL,																						NULL}
 
};
 

	
 
typedef void SettingDescProc(IniFile *ini, const SettingDesc *desc, const void *grpname);
 

	
 
static void HandleSettingDescs(IniFile *ini, SettingDescProc *proc)
 
{
 
	proc(ini, misc_settings,		"misc",			NULL);
 
	proc(ini, win32_settings,		"win32",		NULL);
 
	proc(ini, network_settings, "network",	NULL);
 
	proc(ini, music_settings,		"music",		&msf);
 
	proc(ini, gameopt_settings, "gameopt",	&_new_opt);
 
	proc(ini, patch_settings,		"patches",	&_patches);
 
	proc(ini, misc_settings,		"misc");
 
	proc(ini, win32_settings,		"win32");
 
#ifdef ENABLE_NETWORK
 
	proc(ini, network_settings, "network");
 
#endif /* ENABLE_NETWORK */
 
	proc(ini, music_settings,		"music");
 
	proc(ini, gameopt_settings, "gameopt");
 
	proc(ini, patch_settings,		"patches");
 
	proc(ini, patch_player_settings,		"patches");
 

	
 
	proc(ini, debug_settings,		"debug",		NULL);
 
	proc(ini, debug_settings,		"debug");
 
}
 

	
 
static void LoadGrfSettings(IniFile *ini)
settings.h
Show inline comments
 
new file 100644
 
#ifndef SETTINGS_H
 
#define SETTINGS_H
 

	
 
enum SettingDescType {
 
	SDT_INTX, // must be 0
 
	SDT_ONEOFMANY,
 
	SDT_MANYOFMANY,
 
	SDT_BOOLX,
 
	SDT_STRING,
 
	SDT_STRINGBUF,
 
	SDT_INTLIST,
 

	
 
	SDT_INT8 = 0 << 4,
 
	SDT_UINT8 = 1 << 4,
 
	SDT_INT16 = 2 << 4,
 
	SDT_UINT16 = 3 << 4,
 
	SDT_INT32 = 4 << 4,
 
	SDT_UINT32 = 5 << 4,
 
	SDT_CALLBX = 6 << 4,
 

	
 
	SDT_UINT = SDT_UINT32,
 
	SDT_INT = SDT_INT32,
 

	
 
	SDT_NOSAVE = 1 << 8,
 

	
 
	SDT_CALLB = SDT_INTX | SDT_CALLBX,
 

	
 
	SDT_BOOL = SDT_BOOLX | SDT_UINT8,
 
};
 

	
 
typedef struct SettingDesc {
 
	const char *name;
 
	int flags;
 
	const void *def;
 
	void *ptr;
 
	const void *b;
 
} SettingDesc;
 

	
 
#endif /* SETTINGS_H */
settings_gui.c
Show inline comments
 
@@ -8,6 +8,7 @@
 
#include "engine.h"
 
#include "screenshot.h"
 
#include "newgrf.h"
 
#include "network.h"
 

	
 
static uint32 _difficulty_click_a;
 
static uint32 _difficulty_click_b;
 
@@ -295,8 +296,6 @@ static inline bool GetBitAndShift(uint32
 
	return (x&1) != 0;
 
}
 

	
 
static GameOptions _opt_mod_temp;
 

	
 
static const int16 _default_game_diff[3][GAME_DIFFICULTY_NUM] = {
 
	{2, 2, 1, 3, 300, 2, 0, 2, 0, 1, 2, 0, 1, 0, 0, 0, 0, 0},
 
	{4, 1, 1, 2, 150, 3, 1, 3, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1},
 
@@ -327,20 +326,29 @@ static void GameDifficultyWndProc(Window
 

	
 
		w->click_state = (1 << 4) << _opt_mod_temp.diff_level;
 
		w->disabled_state = (_game_mode != GM_NORMAL) ? 0 : (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7);
 
		// Disable save-button in multiplayer (and if client)
 
		if (_networking && !_network_server)
 
			w->disabled_state |= (1 << 10);
 
		DrawWindowWidgets(w);
 

	
 
		click_a = _difficulty_click_a;
 
		click_b = _difficulty_click_b;
 

	
 
		/* XXX - This is most likely the worst way I have ever seen
 
		     to disable some buttons and to enable others.
 
		     What the value means, is this:
 
		       if bit1 is enabled, setting 1 is disabled
 
		       then it is shifted to the left, and the story
 
		       repeats....
 
		   -- TrueLight */
 
		disabled = _game_mode == GM_NORMAL ? 0x383E : 0;
 
		// XXX
 

	
 
		x = 0;
 
		y = 32;
 
		for (i = 0; i != GAME_DIFFICULTY_NUM; i++) {
 
			DrawFrameRect(x+5, y+1, x+5+9, y+9, 3, GetBitAndShift(&click_a)?0x20:0);
 
			DrawFrameRect(x+15, y+1, x+15+9, y+9, 3, GetBitAndShift(&click_b)?0x20:0);
 
			if (GetBitAndShift(&disabled)) {
 
			if (GetBitAndShift(&disabled) || (_networking && !_network_server)) {
 
				int color = 0x8000 | _color_list[3].unk2;
 
				GfxFillRect(x+6, y+2, x+6+8, y+9, color);
 
				GfxFillRect(x+16, y+2, x+16+8, y+9, color);
 
@@ -367,6 +375,10 @@ static void GameDifficultyWndProc(Window
 
			int val;
 
			const GameSettingData *info;
 

	
 
			// Don't allow clients to make any changes
 
			if  (_networking && !_network_server)
 
				return;
 

	
 
			x = e->click.pt.x - 5;
 
			if (!IS_INT_INSIDE(x, 0, 21))
 
				return;
 
@@ -563,19 +575,21 @@ enum {
 
	PF_0ISDIS				= 1,
 
	PF_NOCOMMA			= 2,
 
	PF_MULTISTRING	= 4,
 
	PF_PLAYERBASED	= 8, // This has to match the entries that are in settings.c, patch_player_settings
 
};
 

	
 
static const PatchEntry _patches_ui[] = {
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_VEHICLESPEED,			&_patches.vehicle_speed,						0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_LONGDATE,					&_patches.status_long_date,					0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_SHOWFINANCES,			&_patches.show_finances,						0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_AUTOSCROLL,				&_patches.autoscroll,								0,  0,  0, NULL},
 
	{PE_BOOL,		PF_PLAYERBASED, STR_CONFIG_PATCHES_VEHICLESPEED,			&_patches.vehicle_speed,						0,  0,  0, NULL},
 
	{PE_BOOL,		PF_PLAYERBASED, STR_CONFIG_PATCHES_LONGDATE,					&_patches.status_long_date,					0,  0,  0, NULL},
 
	{PE_BOOL,		PF_PLAYERBASED, STR_CONFIG_PATCHES_SHOWFINANCES,			&_patches.show_finances,						0,  0,  0, NULL},
 
	{PE_BOOL,		PF_PLAYERBASED, STR_CONFIG_PATCHES_AUTOSCROLL,				&_patches.autoscroll,								0,  0,  0, NULL},
 

	
 
	{PE_UINT8,	0, STR_CONFIG_PATCHES_ERRMSG_DURATION,	&_patches.errmsg_duration,					0, 20,  1, NULL},
 
	{PE_UINT8,	PF_PLAYERBASED, STR_CONFIG_PATCHES_ERRMSG_DURATION,	&_patches.errmsg_duration,					0, 20,  1, NULL},
 

	
 
	{PE_UINT8,	PF_MULTISTRING, STR_CONFIG_PATCHES_TOOLBAR_POS, &_patches.toolbar_pos,			0,  2,  1, &v_PositionMainToolbar},
 
	{PE_UINT8, PF_0ISDIS, STR_CONFIG_PATCHES_SNAP_RADIUS, &_patches.window_snap_radius,     1, 32,  1, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_INVISIBLE_TREES,	&_patches.invisible_trees,					0,  1,  1, &InvisibleTreesActive},
 
	{PE_UINT8,	PF_MULTISTRING | PF_PLAYERBASED, STR_CONFIG_PATCHES_TOOLBAR_POS, &_patches.toolbar_pos,			0,  2,  1, &v_PositionMainToolbar},
 
	{PE_UINT8,	PF_0ISDIS, STR_CONFIG_PATCHES_SNAP_RADIUS, &_patches.window_snap_radius,     1, 32,  1, NULL},
 
	{PE_BOOL,		PF_PLAYERBASED, STR_CONFIG_PATCHES_INVISIBLE_TREES,	&_patches.invisible_trees,					0,  1,  1, &InvisibleTreesActive},
 
};
 

	
 
static const PatchEntry _patches_construction[] = {
 
@@ -585,7 +599,7 @@ static const PatchEntry _patches_constru
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_SIGNALSIDE,				&_patches.signal_side,							0,  0,  0, NULL},
 

	
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_SMALL_AIRPORTS,		&_patches.always_small_airport,			0,  0,  0, NULL},
 
	{PE_UINT8,	0, STR_CONFIG_PATCHES_DRAG_SIGNALS_DENSITY, &_patches.drag_signals_density, 1, 20,  1, NULL},
 
	{PE_UINT8,	PF_PLAYERBASED, STR_CONFIG_PATCHES_DRAG_SIGNALS_DENSITY, &_patches.drag_signals_density, 1, 20,  1, NULL},
 

	
 
};
 

	
 
@@ -597,11 +611,11 @@ static const PatchEntry _patches_vehicle
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_NEW_DEPOT_FINDING,&_patches.new_depot_finding,				0,  0,  0, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_NEW_TRAIN_PATHFIND,				&_patches.new_pathfinding,	0,  0,  0, NULL},
 

	
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_WARN_INCOME_LESS, &_patches.train_income_warn,				0,  0,  0, NULL},
 
	{PE_UINT8,	PF_MULTISTRING, STR_CONFIG_PATCHES_ORDER_REVIEW,&_patches.order_review_system,0,2,  1, NULL},
 
	{PE_BOOL,		PF_PLAYERBASED, STR_CONFIG_PATCHES_WARN_INCOME_LESS, &_patches.train_income_warn,				0,  0,  0, NULL},
 
	{PE_UINT8,	PF_MULTISTRING | PF_PLAYERBASED, STR_CONFIG_PATCHES_ORDER_REVIEW,&_patches.order_review_system,0,2,  1, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_NEVER_EXPIRE_VEHICLES,		&_patches.never_expire_vehicles,0,0,0, NULL},
 

	
 
	{PE_UINT16, PF_0ISDIS, STR_CONFIG_PATCHES_LOST_TRAIN_DAYS, &_patches.lost_train_days,	180,720, 60, NULL},
 
	{PE_UINT16, PF_0ISDIS | PF_PLAYERBASED, STR_CONFIG_PATCHES_LOST_TRAIN_DAYS, &_patches.lost_train_days,	180,720, 60, NULL},
 
	{PE_BOOL,		0, STR_CONFIG_PATCHES_AUTORENEW_VEHICLE,&_patches.autorenew,								0,  0,  0, NULL},
 
	{PE_INT16,	0, STR_CONFIG_PATCHES_AUTORENEW_MONTHS, &_patches.autorenew_months,				-12, 12,  1, NULL},
 
	{PE_CURRENCY, 0, STR_CONFIG_PATCHES_AUTORENEW_MONEY,&_patches.autorenew_money,					0, 2000000, 100000, NULL},
 
@@ -728,8 +742,7 @@ static void WritePE(const PatchEntry *pe
 
									*(int32*)pe->variable = val;
 
								break;
 

	
 
	case PE_CURRENCY: val /= GetCurrentCurrencyRate();
 
								if ((int64)val > (int64)pe->max)
 
	case PE_CURRENCY: if ((int64)val > (int64)pe->max)
 
									*(int64*)pe->variable = (int64)pe->max;
 
								else if ((int64)val < (int64)pe->min)
 
									*(int64*)pe->variable = (int64)pe->min;
 
@@ -762,12 +775,24 @@ static void PatchesSelectionWndProc(Wind
 
		page = &_patches_page[WP(w,def_d).data_1];
 
		for(i=0,pe=page->entries; i!=page->num; i++,pe++) {
 
			bool disabled = false;
 
			bool editable = true;
 
			// We do not allow changes of some items when we are a client in a networkgame
 
			if (!(pe->flags & PF_PLAYERBASED) && _networking && !_network_server)
 
				editable = false;
 
			if (pe->type == PE_BOOL) {
 
				DrawFrameRect(x+5, y+1, x+15+9, y+9, (*(bool*)pe->variable)?6:4, (*(bool*)pe->variable)?0x20:0);
 
				if (editable)
 
					DrawFrameRect(x+5, y+1, x+15+9, y+9, (*(bool*)pe->variable)?6:4, (*(bool*)pe->variable)?0x20:0);
 
				else
 
					DrawFrameRect(x+5, y+1, x+15+9, y+9, (*(bool*)pe->variable)?7:9, (*(bool*)pe->variable)?0x20:0);
 
				SetDParam(0, *(bool*)pe->variable ? STR_CONFIG_PATCHES_ON : STR_CONFIG_PATCHES_OFF);
 
			} else {
 
				DrawFrameRect(x+5, y+1, x+5+9, y+9, 3, clk == i*2+1 ? 0x20 : 0);
 
				DrawFrameRect(x+15, y+1, x+15+9, y+9, 3, clk == i*2+2 ? 0x20 : 0);
 
				if (!editable) {
 
					int color = 0x8000 | _color_list[3].unk2;
 
					GfxFillRect(x+6, y+2, x+6+8, y+9, color);
 
					GfxFillRect(x+16, y+2, x+16+8, y+9, color);
 
				}
 
				DrawStringCentered(x+10, y+1, STR_6819, 0);
 
				DrawStringCentered(x+20, y+1, STR_681A, 0);
 

	
 
@@ -816,6 +841,9 @@ static void PatchesSelectionWndProc(Wind
 
			x = e->click.pt.x - 5;
 
			if (x < 0) return;
 

	
 
			if (!(pe->flags & PF_PLAYERBASED) && _networking && !_network_server)
 
				return;
 

	
 
			if (x < 21) { // clicked on the icon on the left side. Either scroller or bool on/off
 
				int32 val = ReadPE(pe), oval = val;
 

	
 
@@ -859,7 +887,17 @@ static void PatchesSelectionWndProc(Wind
 
					break;
 
				}
 
				if (val != oval) {
 
					WritePE(pe, val);
 
					// To make patch-changes network-safe
 
					if (pe->type == PE_CURRENCY) {
 
						val /= GetCurrentCurrencyRate();
 
					}
 
					// If an item is playerbased, we do not send it over the network (if any)
 
					if (pe->flags & PF_PLAYERBASED) {
 
						WritePE(pe, val);
 
					} else {
 
						// Else we do
 
						DoCommandP(0, (byte)WP(w,def_d).data_1 + ((byte)btn << 8), val, NULL, CMD_CHANGE_PATCH_SETTING);
 
					}
 
					SetWindowDirty(w);
 

	
 
					if (pe->click_proc != NULL) // call callback function
 
@@ -892,7 +930,18 @@ static void PatchesSelectionWndProc(Wind
 
		if (*e->edittext.str) {
 
			const PatchPage *page = &_patches_page[WP(w,def_d).data_1];
 
			const PatchEntry *pe = &page->entries[WP(w,def_d).data_3];
 
			WritePE(pe, atoi(e->edittext.str));
 
			int32 val;
 
			val = atoi(e->edittext.str);
 
			if (pe->type == PE_CURRENCY) {
 
				val /= GetCurrentCurrencyRate();
 
			}
 
			// If an item is playerbased, we do not send it over the network (if any)
 
			if (pe->flags & PF_PLAYERBASED) {
 
				WritePE(pe, val);
 
			} else {
 
				// Else we do
 
				DoCommandP(0, (byte)WP(w,def_d).data_1 + ((byte)WP(w,def_d).data_3 << 8), val, NULL, CMD_CHANGE_PATCH_SETTING);
 
			}
 
			SetWindowDirty(w);
 

	
 
			if (pe->click_proc != NULL) // call callback function
 
@@ -907,6 +956,25 @@ static void PatchesSelectionWndProc(Wind
 
	}
 
}
 

	
 
int32 CmdChangePatchSetting(int x, int y, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	const PatchPage *page;
 
	const PatchEntry *pe;
 

	
 
	if (flags & DC_EXEC) {
 
		page = &_patches_page[(byte)p1];
 
		if (page == NULL) return 0;
 
		pe = &page->entries[(byte)(p1 >> 8)];
 
		if (pe == NULL) return 0;
 

	
 
		WritePE(pe, (int32)p2);
 

	
 
		InvalidateWindow(WC_GAME_OPTIONS, 0);
 
	}
 

	
 
	return 0;
 
}
 

	
 
static const Widget _patches_selection_widgets[] = {
 
{   WWT_CLOSEBOX,    10,     0,    10,     0,    13, STR_00C5,												STR_018B_CLOSE_WINDOW},
 
{    WWT_CAPTION,    10,    11,   369,     0,    13, STR_CONFIG_PATCHES_CAPTION,			STR_018C_WINDOW_TITLE_DRAG_THIS},
 
@@ -952,7 +1020,7 @@ static void NewgrfWndProc(Window *w, Win
 
		struct GRFFile *c = _first_grffile;
 

	
 
		DrawWindowWidgets(w);
 
		
 

	
 
		if (_first_grffile == NULL) { // no grf sets installed
 
			DrawStringMultiCenter(140, 210, STR_NEWGRF_NO_FILES_INSTALLED, 250);
 
			break;
 
@@ -963,13 +1031,13 @@ static void NewgrfWndProc(Window *w, Win
 
			if (i >= w->vscroll.pos) { // draw files according to scrollbar position
 
				bool h = (_sel_grffile==c);
 
				// show highlighted item with a different background and highlighted text
 
				if(h) GfxFillRect(1, y + 1, 267, y + 12, 156); 
 
				if(h) GfxFillRect(1, y + 1, 267, y + 12, 156);
 
				// XXX - will be grf name later
 
				DoDrawString(c->filename, 25, y + 2, h ? 0xC : 0x10); 
 
				DoDrawString(c->filename, 25, y + 2, h ? 0xC : 0x10);
 
				DrawSprite(SPRITE_PALETTE(0x2EB | 0x30b8000), 5, y + 3);
 
				y += NEWGRF_WND_PROC_ROWSIZE;
 
			}
 
			
 

	
 
			c = c->next;
 
			if (++i == w->vscroll.cap + w->vscroll.pos) break; // stop after displaying 12 items
 
		}
 
@@ -982,7 +1050,7 @@ static void NewgrfWndProc(Window *w, Win
 
			// draw filename
 
			x = DrawString(5, 199, STR_NEWGRF_FILENAME, 0);
 
			DoDrawString(_sel_grffile->filename, x + 2, 199, 0x01);
 
	
 

	
 
			// draw grf id
 
			x = DrawString(5, 209, STR_NEWGRF_GRF_ID, 0);
 
			snprintf(_userstring, USERSTRING_LEN, "%08X", _sel_grffile->grfid);
ship_gui.c
Show inline comments
 
@@ -301,7 +301,7 @@ static void ShowShipDetailsWindow(Vehicl
 
	w->caption_color = v->owner;
 
}
 

	
 
static void CcBuildShip(bool success, uint tile, uint32 p1, uint32 p2)
 
void CcBuildShip(bool success, uint tile, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 
	if (!success) return;
station_cmd.c
Show inline comments
 
@@ -2415,7 +2415,7 @@ int32 CmdRenameStation(int x, int y, uin
 
	StringID str,old_str;
 
	Station *st;
 

	
 
	str = AllocateName((byte*)_decode_parameters, 6);
 
	str = AllocateNameUnique((byte*)_decode_parameters, 6);
 
	if (str == 0)
 
		return CMD_ERROR;
 

	
 
@@ -2654,7 +2654,7 @@ static int32 ClearTile_Station(uint tile
 
void InitializeStations()
 
{
 
	int i;
 
	
 

	
 
	memset(_stations, 0, sizeof(_stations));
 
	for(i = 0; i != lengthof(_stations); i++)
 
		_stations[i].index=i;
stdafx.h
Show inline comments
 
@@ -64,8 +64,8 @@
 
#	define inline _inline
 
#	define CDECL _cdecl
 
# define NOT_REACHED() _assume(0)
 
# define snprintf _snprintf
 
# define vsnprintf _vsnprintf
 
int snprintf(char *str, size_t size, const char *format, ...);
 
int vsnprintf(char *str, size_t size, const char *format, va_list ap);
 
# undef TTD_ALIGNMENT_4
 
# undef TTD_ALIGNMENT_2
 
# define GCC_PACK
 
@@ -97,6 +97,10 @@ typedef unsigned int uint32;
 
#if !defined(UNIX) && !defined(__CYGWIN__)
 
	typedef unsigned int uint;
 
#endif
 
// Not defined in QNX Neutrino (6.x)
 
#if defined(__QNXNTO__)
 
	typedef unsigned int uint;
 
#endif
 

	
 
#ifndef __BEOS__
 
typedef signed char int8;
 
@@ -175,6 +179,7 @@ assert_compile(sizeof(uint8)	== 1);
 
#define GetString OTTD_GetString
 
#define DrawString OTTD_DrawString
 
#define Random OTTD_Random
 
#define CloseConnection OTTD_CloseConnection
 
#endif
 

	
 
#endif // !defined(_STDAFX_H)
texteff.c
Show inline comments
 
@@ -3,6 +3,9 @@
 
#include "gfx.h"
 
#include "viewport.h"
 
#include "saveload.h"
 
#include "hal.h"
 
#include "console.h"
 
#include <stdarg.h> /* va_list */
 

	
 
typedef struct TextEffect {
 
	StringID string_id;
 
@@ -12,9 +15,187 @@ typedef struct TextEffect {
 
	uint32 params_2;
 
} TextEffect;
 

	
 
#define MAX_TEXTMESSAGE_LENGTH 250
 

	
 
typedef struct TextMessage {
 
	char message[MAX_TEXTMESSAGE_LENGTH];
 
	uint16 color;
 
	uint16 end_date;
 
} TextMessage;
 

	
 
#define MAX_CHAT_MESSAGES 10
 
static TextEffect _text_effect_list[30];
 
static TextMessage _text_message_list[MAX_CHAT_MESSAGES];
 
TileIndex _animated_tile_list[256];
 

	
 

	
 
int _textmessage_width = 0;
 
bool _textmessage_dirty = true;
 
bool _textmessage_visible = false;
 

	
 
const int _textmessage_box_left = 10; // Pixels from left
 
const int _textmessage_box_y = 150;  // Height of box
 
const int _textmessage_box_bottom = 20; // Pixels from bottom
 
const int _textmessage_box_max_width = 400; // Max width of box
 

	
 
static byte _textmessage_backup[150*400]; // (y * max_width)
 

	
 
extern void memcpy_pitch(void *d, void *s, int w, int h, int spitch, int dpitch);
 

	
 
// Duration is in game-days
 
void AddTextMessage(uint16 color, uint8 duration, const char *message, ...)
 
{
 
	int i;
 
	char buf[1024];
 
	char buf2[MAX_TEXTMESSAGE_LENGTH];
 
	va_list va;
 
	int length;
 

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

	
 
	if ((color & 0xFF) == 0xC9) color = 0x1CA;
 

	
 
	length = MAX_TEXTMESSAGE_LENGTH;
 
	snprintf(buf2, length, "%s", buf);
 
	while (GetStringWidth(buf2) > _textmessage_width - 9) {
 
		snprintf(buf2, --length, "%s", buf);
 
	}
 

	
 
	for (i = 0; i < MAX_CHAT_MESSAGES; i++) {
 
		if (_text_message_list[i].message[0] == '\0') {
 
			// Empty spot
 
			snprintf(_text_message_list[i].message, MAX_TEXTMESSAGE_LENGTH, "%s", buf2);
 
			_text_message_list[i].color = color;
 
			_text_message_list[i].end_date = _date + duration;
 

	
 
			_textmessage_dirty = true;
 
			return;
 
		}
 
	}
 

	
 
	// We did not found a free spot, trash the first one, and add to the end
 
	memmove(&_text_message_list[0], &_text_message_list[1], sizeof(TextMessage) * (MAX_CHAT_MESSAGES - 1));
 
	snprintf(_text_message_list[MAX_CHAT_MESSAGES - 1].message, MAX_TEXTMESSAGE_LENGTH, "%s", buf2);
 
	_text_message_list[MAX_CHAT_MESSAGES - 1].color = color;
 
	_text_message_list[i].end_date = _date + duration;
 

	
 
	_textmessage_dirty = true;
 
}
 

	
 
void InitTextMessage()
 
{
 
	int i;
 
	for (i = 0; i < MAX_CHAT_MESSAGES; i++) {
 
		_text_message_list[i].message[0] = '\0';
 
	}
 

	
 
	_textmessage_width = _textmessage_box_max_width;
 
}
 

	
 
// Hide the textbox
 
void UndrawTextMessage()
 
{
 
	if (_textmessage_visible) {
 
		// Sometimes we also need to hide the cursor
 
		//   This is because both textmessage and the cursor take a shot of the
 
		//   screen before drawing.
 
		//   Now the textmessage takes his shot and paints his data before the cursor
 
		//   does, so in the shot of the cursor is the screen-data of the textmessage
 
		//   included when the cursor hangs somewhere over the textmessage. To
 
		//   avoid wrong repaints, we undraw the cursor in that case, and everything
 
		//   looks nicely ;)
 
		// (and now hope this story above makes sense to you ;))
 

	
 
		if (_cursor.visible) {
 
			if (_cursor.draw_pos.x + _cursor.draw_size.x >= _textmessage_box_left &&
 
				_cursor.draw_pos.x <= _textmessage_box_left + _textmessage_width &&
 
				_cursor.draw_pos.y + _cursor.draw_size.y >= _screen.height - _textmessage_box_bottom - _textmessage_box_y &&
 
				_cursor.draw_pos.y <= _screen.height - _textmessage_box_bottom) {
 
				UndrawMouseCursor();
 
			}
 
		}
 

	
 
		_textmessage_visible = false;
 
		// Put our 'shot' back to the screen
 
		memcpy_pitch(
 
			_screen.dst_ptr + _textmessage_box_left + (_screen.height-_textmessage_box_bottom-_textmessage_box_y) * _screen.pitch,
 
			_textmessage_backup,
 
			_textmessage_width, _textmessage_box_y, _textmessage_width, _screen.pitch);
 

	
 
		// And make sure it is updated next time
 
		_video_driver->make_dirty(_textmessage_box_left, _screen.height-_textmessage_box_bottom-_textmessage_box_y, _textmessage_width, _textmessage_box_y);
 

	
 
		_textmessage_dirty = true;
 
	}
 
}
 

	
 
// Check if a message is expired every day
 
void TextMessageDailyLoop()
 
{
 
	int i = 0;
 
	while (i < MAX_CHAT_MESSAGES) {
 
		if (_text_message_list[i].message[0] == '\0') break;
 
		if (_date > _text_message_list[i].end_date) {
 
			memmove(&_text_message_list[i], &_text_message_list[i+1], sizeof(TextMessage) * ((MAX_CHAT_MESSAGES - 1) - i));
 
			_text_message_list[MAX_CHAT_MESSAGES - 1].message[0] = '\0';
 
			i--;
 
			_textmessage_dirty = true;
 
		}
 
		i++;
 
	}
 
}
 

	
 
// Draw the textmessage-box
 
void DrawTextMessage()
 
{
 
	int i, j;
 
	bool has_message;
 

	
 
	if (!_textmessage_dirty)
 
		return;
 

	
 
	// First undraw if needed
 
	UndrawTextMessage();
 

	
 
	if (_iconsole_mode == ICONSOLE_FULL)
 
		return;
 

	
 
	has_message = false;
 
	for ( i = 0; i < MAX_CHAT_MESSAGES; i++) {
 
		if (_text_message_list[i].message[0] == '\0') break;
 
		has_message = true;
 
	}
 
	if (!has_message) return;
 

	
 
	// Make a copy of the screen as it is before painting (for undraw)
 
	memcpy_pitch(
 
		_textmessage_backup,
 
		_screen.dst_ptr + _textmessage_box_left + (_screen.height-_textmessage_box_bottom-_textmessage_box_y) * _screen.pitch,
 
		_textmessage_width, _textmessage_box_y, _screen.pitch, _textmessage_width);
 

	
 
	// Switch to _screen painting
 
	_cur_dpi = &_screen;
 

	
 
	j = 0;
 
	// Paint the messages
 
	for (i = MAX_CHAT_MESSAGES - 1; i >= 0; i--) {
 
		if (_text_message_list[i].message[0] == '\0') continue;
 
		j++;
 
		GfxFillRect(_textmessage_box_left, _screen.height-_textmessage_box_bottom-j*13-2, _textmessage_box_left+_textmessage_width - 1, _screen.height-_textmessage_box_bottom-j*13+10, /* black, but with some alpha */ 0x4322);
 

	
 
		DoDrawString(_text_message_list[i].message, _textmessage_box_left + 2, _screen.height - _textmessage_box_bottom - j * 13 - 1, 0x10);
 
		DoDrawString(_text_message_list[i].message, _textmessage_box_left + 3, _screen.height - _textmessage_box_bottom - j * 13, _text_message_list[i].color);
 
	}
 

	
 
	// Make sure the data is updated next flush
 
	_video_driver->make_dirty(_textmessage_box_left, _screen.height-_textmessage_box_bottom-_textmessage_box_y, _textmessage_width, _textmessage_box_y);
 

	
 
	_textmessage_visible = true;
 
	_textmessage_dirty = false;
 
}
 

	
 
static void MarkTextEffectAreaDirty(TextEffect *te)
 
{
 
	MarkAllViewportsDirty(
town_cmd.c
Show inline comments
 
@@ -13,6 +13,7 @@
 
#include "saveload.h"
 
#include "economy.h"
 
#include "gui.h"
 
#include "network.h"
 

	
 
enum {
 
	TOWN_HAS_CHURCH     = 0x02,
 
@@ -928,7 +929,7 @@ static Town *AllocateTown()
 
	Town *t;
 
	FOR_ALL_TOWNS(t) {
 
		if (t->xy == 0) {
 
			_total_towns++;
 
			if (t->index > _total_towns) _total_towns = t->index;
 
			return t;
 
		}
 
	}
 
@@ -1342,7 +1343,7 @@ int32 CmdRenameTown(int x, int y, uint32
 
	StringID str;
 
	Town *t = DEREF_TOWN(p1);
 

	
 
	str = AllocateName((byte*)_decode_parameters, 4);
 
	str = AllocateNameUnique((byte*)_decode_parameters, 4);
 
	if (str == 0)
 
		return CMD_ERROR;
 

	
 
@@ -1910,7 +1911,7 @@ static void Load_TOWN()
 
	while ((index = SlIterateArray()) != -1) {
 
		Town *t = DEREF_TOWN(index);
 
		SlObject(t, _town_desc);
 
		_total_towns++;
 
		if (index > _total_towns) _total_towns = index;
 
	}
 
}
 

	
train_gui.c
Show inline comments
 
@@ -15,7 +15,7 @@
 
int _traininfo_vehicle_pitch = 0;
 

	
 

	
 
static void CcBuildWagon(bool success, uint tile, uint32 p1, uint32 p2)
 
void CcBuildWagon(bool success, uint tile, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v,*found;
 

	
 
@@ -43,7 +43,7 @@ static void CcBuildWagon(bool success, u
 
	}
 
}
 

	
 
static void CcBuildLoco(bool success, uint tile, uint32 p1, uint32 p2)
 
void CcBuildLoco(bool success, uint tile, uint32 p1, uint32 p2)
 
{
 
	Vehicle *v;
 

	
ttd.c
Show inline comments
 
@@ -24,6 +24,7 @@
 
#include "ai.h"
 
#include "console.h"
 
#include "screenshot.h"
 
#include "network.h"
 

	
 
#include <stdarg.h>
 

	
 
@@ -53,7 +54,7 @@ extern void HalGameLoop();
 

	
 
uint32 _pixels_redrawn;
 
bool _dbg_screen_rect;
 
bool disable_computer;
 
bool disable_computer; // We should get ride of this thing.. is only used for a debug-cheat
 
static byte _os_version = 0;
 

	
 
void CDECL error(const char *s, ...) {
 
@@ -299,17 +300,21 @@ static void showhelp()
 

	
 
	p = strecpy(buf,
 
		"Command line options:\n"
 
		"  -v drv = Set video driver (see below)\n"
 
		"  -s drv = Set sound driver (see below)\n"
 
		"  -m drv = Set music driver (see below)\n"
 
		"  -r res = Set resolution (for instance 800x600)\n"
 
		"  -h     = Display this help text\n"
 
		"  -t date= Set starting date\n"
 
		"  -d dbg = Debug mode\n"
 
		"  -l lng = Select Language\n"
 
		"  -e     = Start Editor\n"
 
		"  -g     = Start new game immediately (can optionally take a game to load)\n"
 
		"  -G seed= Set random seed\n"
 
		"  -v drv              = Set video driver (see below)\n"
 
		"  -s drv              = Set sound driver (see below)\n"
 
		"  -m drv              = Set music driver (see below)\n"
 
		"  -r res              = Set resolution (for instance 800x600)\n"
 
		"  -h                  = Display this help text\n"
 
		"  -t date             = Set starting date\n"
 
		"  -d [dbg]            = Debug mode\n"
 
		"  -l lng              = Select Language\n"
 
		"  -e                  = Start Editor\n"
 
		"  -g [savegame]       = Start new/save game immediately\n"
 
		"  -G seed             = Set random seed\n"
 
		"  -n [ip#player:port] = Start networkgame\n"
 
		"  -D                  = Start dedicated server\n"
 
		"  -i                  = Ignore wrong grf\n"
 
		"  -p #player          = Player as #player (deprecated) (network only)\n"
 
	);
 

	
 
	for(i=0; i!=lengthof(_driver_classes); i++,dc++) {
 
@@ -474,11 +479,40 @@ void ParseResolution(int res[2], char *s
 
	res[1] = strtoul(t + 1, NULL, 0);
 
}
 

	
 
void LoadIntroGame()
 
{
 
	char filename[256];
 
	_game_mode = GM_MENU;
 
	_display_opt &= ~DO_TRANS_BUILDINGS; // don't make buildings transparent in intro
 

	
 
	_opt_mod_ptr = &_new_opt;
 
	GfxLoadSprites();
 
	LoadStringWidthTable();
 

	
 
	// Setup main window
 
	InitWindowSystem();
 
	SetupColorsAndInitialWindow();
 

	
 
	// Generate a world.
 
	sprintf(filename, "%sopntitle.dat",  _path.data_dir);
 
	if (SaveOrLoad(filename, SL_LOAD) != SL_OK)
 
		GenerateWorld(1); // if failed loading, make empty world.
 

	
 
	_opt.currency = _new_opt.currency;
 

	
 
	_pause = 0;
 
	_local_player = 0;
 
	MarkWholeScreenDirty();
 

	
 
	// Play main theme
 
	if (_music_driver->is_song_playing()) ResetMusic();
 
}
 

	
 
int ttd_main(int argc, char* argv[])
 
{
 
	MyGetOptData mgo;
 
	int i;
 
	int network = 0;
 
	bool network = false;
 
	char *network_conn = NULL;
 
	char *language = NULL;
 
	char musicdriver[16], sounddriver[16], videodriver[16];
 
@@ -486,25 +520,31 @@ int ttd_main(int argc, char* argv[])
 
	uint startdate = -1;
 
	_ignore_wrong_grf = false;
 
	musicdriver[0] = sounddriver[0] = videodriver[0] = 0;
 
	_networking_override=false;
 

	
 
	_game_mode = GM_MENU;
 
	_switch_mode = SM_MENU;
 
	_switch_mode_errorstr = INVALID_STRING_ID;
 

	
 
	MyGetOptInit(&mgo, argc-1, argv+1, "m:s:v:hn::l:eit:d::r:g::G:cp:");
 
	// The last param of the following function means this:
 
	//   a letter means: it accepts that param (e.g.: -h)
 
	//   a ':' behind it means: it need a param (e.g.: -m<driver>)
 
	//   a '::' behind it means: it can optional have a param (e.g.: -d<debug>)
 
	MyGetOptInit(&mgo, argc-1, argv+1, "m:s:v:hDn::l:eit:d::r:g::G:p:");
 
	while ((i = MyGetOpt(&mgo)) != -1) {
 
		switch(i) {
 
		case 'm': ttd_strlcpy(musicdriver, mgo.opt, sizeof(musicdriver)); break;
 
		case 's': ttd_strlcpy(sounddriver, mgo.opt, sizeof(sounddriver)); break;
 
		case 'v': ttd_strlcpy(videodriver, mgo.opt, sizeof(videodriver)); break;
 
		case 'D': {
 
				sprintf(musicdriver,"null");
 
				sprintf(sounddriver,"null");
 
				sprintf(videodriver,"dedicated");
 
			} break;
 
		case 'n': {
 
				network = 1;
 
				_networking_override=true;
 
				if (mgo.opt) {
 
				network = true;
 
				if (mgo.opt)
 
					// Optional, you can give an IP
 
					network_conn = mgo.opt;
 
					network++;
 
				}
 
				else
 
					network_conn = NULL;
 
			} break;
 
@@ -536,7 +576,8 @@ int ttd_main(int argc, char* argv[])
 
			break;
 
		case 'p': {
 
			int i = atoi(mgo.opt);
 
			if (IS_INT_INSIDE(i, 0, MAX_PLAYERS))	_network_playas = i + 1;
 
			// Play as an other player in network games
 
			if (IS_INT_INSIDE(i, 1, MAX_PLAYERS)) _network_playas = i;
 
			break;
 
		}
 
		case -2:
 
@@ -556,9 +597,6 @@ int ttd_main(int argc, char* argv[])
 
	if (resolution[0]) { _cur_resolution[0] = resolution[0]; _cur_resolution[1] = resolution[1]; }
 
	if (startdate != -1) _patches.starting_date = startdate;
 

	
 
	// initialize network-core
 
	NetworkCoreInit();
 

	
 
	// enumerate language files
 
	InitializeLanguagePacks();
 

	
 
@@ -586,6 +624,11 @@ int ttd_main(int argc, char* argv[])
 
	MusicLoop();
 
	_savegame_sort_order = 1; // default sorting of savegames is by date, newest first
 

	
 
#ifdef ENABLE_NETWORK
 
	// initialize network-core
 
	NetworkStartUp();
 
#endif /* ENABLE_NETWORK */
 

	
 
	// Default difficulty level
 
	_opt_mod_ptr = &_new_opt;
 

	
 
@@ -593,27 +636,45 @@ int ttd_main(int argc, char* argv[])
 
	if (_opt_mod_ptr->diff_level == 9)
 
		SetDifficultyLevel(0, _opt_mod_ptr);
 

	
 
	if ((network) && (_network_available)) {
 
		NetworkLobbyInit();
 
		if (network_conn!=NULL) {
 
			NetworkCoreConnectGame(network_conn,_network_server_port);
 
			} else {
 
			NetworkCoreConnectGame("auto",_network_server_port);
 
			}
 
		}
 

	
 
	// initialize the ingame console
 
	IConsoleInit();
 
	InitPlayerRandoms();
 

	
 
#ifdef ENABLE_NETWORK
 
	if ((network) && (_network_available)) {
 
		if (network_conn != NULL) {
 
			const byte *port = NULL;
 
			const byte *player = NULL;
 
			uint16 rport;
 

	
 
			rport = NETWORK_DEFAULT_PORT;
 

	
 
			ParseConnectionString(&player, &port, network_conn);
 

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

	
 
			LoadIntroGame();
 
			_switch_mode = SM_NONE;
 
			NetworkClientConnectGame(network_conn, rport);
 
		} else {
 
//			NetworkCoreConnectGame("auto", _network_server_port);
 
		}
 
	}
 
#endif /* ENABLE_NETWORK */
 

	
 
	while (_video_driver->main_loop() == ML_SWITCHDRIVER) {}
 

	
 
	IConsoleFree();
 

	
 
#ifdef ENABLE_NETWORK
 
	if (_network_available) {
 
		// shutdown network-core
 
		NetworkCoreShutdown();
 
		}
 
		// Shut down the network and close any open connections
 
		NetworkDisconnect();
 
		NetworkUDPClose();
 
		NetworkShutDown();
 
	}
 
#endif /* ENABLE_NETWORK */
 

	
 
	_video_driver->stop();
 
	_music_driver->stop();
 
@@ -638,35 +699,6 @@ static void ShowScreenshotResult(bool b)
 

	
 
}
 

	
 
static void LoadIntroGame()
 
{
 
	char filename[256];
 
	_game_mode = GM_MENU;
 
	_display_opt &= ~DO_TRANS_BUILDINGS; // don't make buildings transparent in intro
 

	
 
	_opt_mod_ptr = &_new_opt;
 
	GfxLoadSprites();
 
	LoadStringWidthTable();
 

	
 
	// Setup main window
 
	InitWindowSystem();
 
	SetupColorsAndInitialWindow();
 

	
 
	// Generate a world.
 
	sprintf(filename, "%sopntitle.dat",  _path.data_dir);
 
	if (SaveOrLoad(filename, SL_LOAD) != SL_OK)
 
		GenerateWorld(1); // if failed loading, make empty world.
 

	
 
	_opt.currency = _new_opt.currency;
 

	
 
	_pause = 0;
 
	_local_player = 0;
 
	MarkWholeScreenDirty();
 

	
 
	// Play main theme
 
	if (_music_driver->is_song_playing()) ResetMusic();
 
}
 

	
 
void MakeNewGame()
 
{
 
	_game_mode = GM_NORMAL;
 
@@ -686,10 +718,15 @@ void MakeNewGame()
 
	// Randomize world
 
	GenerateWorld(0);
 

	
 
	// Create a single player
 
	DoStartupNewPlayer(false);
 
	// In a dedicated server, the server does not play
 
	if (_network_dedicated) {
 
		_local_player = OWNER_SPECTATOR;
 
	}	else {
 
		// Create a single player
 
		DoStartupNewPlayer(false);
 

	
 
	_local_player = 0;
 
		_local_player = 0;
 
	}
 

	
 
	MarkWholeScreenDirty();
 
}
 
@@ -766,7 +803,7 @@ void StartScenario()
 
	MarkWholeScreenDirty();
 
}
 

	
 
static bool SafeSaveOrLoad(const char *filename, int mode, int newgm)
 
bool SafeSaveOrLoad(const char *filename, int mode, int newgm)
 
{
 
	byte ogm = _game_mode;
 
	int r;
 
@@ -788,25 +825,55 @@ static bool SafeSaveOrLoad(const char *f
 
		return true;
 
}
 

	
 
static void SwitchMode(int new_mode)
 
void SwitchMode(int new_mode)
 
{
 
	_in_state_game_loop = true;
 

	
 
#ifdef ENABLE_NETWORK
 
	// If we are saving something, the network stays in his current state
 
	if (new_mode != SM_SAVE) {
 
		// If the network is active, make it not-active
 
		if (_networking) {
 
			if (_network_server && (new_mode == SM_LOAD || new_mode == SM_NEWGAME)) {
 
				NetworkReboot();
 
				NetworkUDPClose();
 
			} else {
 
				NetworkDisconnect();
 
				NetworkUDPClose();
 
			}
 
		}
 

	
 
		// If we are a server, we restart the server
 
		if (_is_network_server) {
 
			// But not if we are going to the menu
 
			if (new_mode != SM_MENU) {
 
				NetworkServerStart();
 
			} else {
 
				// This client no longer wants to be a network-server
 
				_is_network_server = false;
 
			}
 
		}
 
	}
 
#endif /* ENABLE_NETWORK */
 

	
 
	switch(new_mode) {
 
	case SM_EDITOR: // Switch to scenario editor
 
		MakeNewEditorWorld();
 
		break;
 

	
 
	case SM_NEWGAME:
 
		if (_networking) { NetworkStartSync(true); } // UGLY HACK HACK HACK
 
		if (_network_server)
 
			snprintf(_network_game_info.map_name, 40, "Random");
 
		MakeNewGame();
 
		break;
 

	
 
	case SM_START_SCENARIO:
 
		StartScenario();
 
		break;
 

	
 
normal_load:
 
	case SM_LOAD: { // Load game
 

	
 
		if (_networking) { NetworkStartSync(true); } // UGLY HACK HACK HACK
 

	
 
		_error_message = INVALID_STRING_ID;
 
		if (!SafeSaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_NORMAL)) {
 
			ShowErrorMessage(_error_message, STR_4009_GAME_LOAD_FAILED, 0, 0);
 
@@ -814,6 +881,8 @@ normal_load:
 
			_opt_mod_ptr = &_opt;
 
			_local_player = 0;
 
			DoCommandP(0, 0, 0, NULL, CMD_PAUSE); // decrease pause counter (was increased from opening load dialog)
 
			if (_network_server)
 
				snprintf(_network_game_info.map_name, 40, "Loaded game");
 
		}
 
		break;
 
	}
 
@@ -836,6 +905,9 @@ normal_load:
 
			_generating_world = false;
 
			// delete all stations owned by a player
 
			DeleteAllPlayerStations();
 

	
 
			if (_network_server)
 
				snprintf(_network_game_info.map_name, 40, "Loaded scenario");
 
		} else
 
			ShowErrorMessage(INVALID_STRING_ID, STR_4009_GAME_LOAD_FAILED, 0, 0);
 

	
 
@@ -844,10 +916,6 @@ normal_load:
 

	
 

	
 
	case SM_MENU: // Switch to game menu
 

	
 
		if ((_networking) && (!_networking_override)) NetworkCoreDisconnect();
 
		_networking_override=false;
 

	
 
		LoadIntroGame();
 
		break;
 

	
 
@@ -877,18 +945,17 @@ normal_load:
 
// The state must not be changed from anywhere
 
// but here.
 
// That check is enforced in DoCommand.
 
static void StateGameLoop()
 
void StateGameLoop()
 
{
 
	// dont execute the state loop during pause
 
	if (_pause) return;
 

	
 
	_in_state_game_loop = true;
 
	_frame_counter++;
 

	
 
	// store the random seed to be able to detect out of sync errors
 
	_sync_seed_1 = _random_seeds[0][0];
 
	_sync_seed_2 = _random_seeds[0][1];
 
 	if (_networking) disable_computer=true;
 
	// _frame_counter is increased somewhere else when in network-mode
 
	//  Sidenote: _frame_counter is ONLY used for _savedump in non-MP-games
 
	//    Should that not be deleted? If so, the next 2 lines can also be deleted
 
	if (!_networking)
 
		_frame_counter++;
 

	
 
	if (_savedump_path[0] && (uint)_frame_counter >= _savedump_first && (uint)(_frame_counter -_savedump_first) % _savedump_freq == 0 ) {
 
		char buf[100];
 
@@ -915,13 +982,16 @@ static void StateGameLoop()
 
		CallVehicleTicks();
 
		CallLandscapeTick();
 

	
 
		if (!disable_computer)
 
		// To bad the AI does not work in multiplayer, because states are not saved
 
		//  perfectly
 
		if (!disable_computer && !_networking)
 
			RunOtherPlayersLoop();
 

	
 
		CallWindowTickEvent();
 
		NewsLoop();
 
		_current_player = p;
 
	}
 

	
 
	_in_state_game_loop = false;
 
}
 

	
 
@@ -992,43 +1062,25 @@ void GameLoop()
 
	_timer_counter+=8;
 
	CursorTick();
 

	
 
	// incomming packets
 
	NetworkCoreLoop(true);
 
#ifdef ENABLE_NETWORK
 
	// Check for UDP stuff
 
	NetworkUDPGameLoop();
 

	
 
	if (_networking_sync) {
 
		// client: make sure client's time is synched to the server by running frames quickly up to where the server is.
 
		if (!_networking_server) {
 
			while (_frame_counter < _frame_counter_srv) {
 
				NetworkCoreLoop(true);
 
				StateGameLoop();
 
				NetworkProcessCommands(); // need to process queue to make sure that packets get executed.
 
				NetworkCoreLoop(false);
 
			}
 
		// client: don't exceed the max count told by the server
 
		if (_frame_counter < _frame_counter_max) {
 
			StateGameLoop();
 
			NetworkProcessCommands();
 
			}
 
		// client: send the ready packet
 
		NetworkSendReadyPacket();
 
		} else {
 
		// server: work on to the frame max
 
		if (_frame_counter < _frame_counter_max) {
 
			StateGameLoop();
 
			NetworkProcessCommands(); // to check if we got any new commands belonging to the current frame before we increase it.
 
			NetworkSendFrameSyncPackets();
 
			}
 
		// server: wait until all clients were ready for going on
 
		if (_frame_counter == _frame_counter_max) {
 
			if (NetworkCheckClientReady()) NetworkSendSyncPackets();
 
			}
 
	if (_networking) {
 
		// Multiplayer
 
		NetworkGameLoop();
 
	} else {
 
		if (_network_reconnect > 0 && --_network_reconnect == 0) {
 
			// This means that we want to reconnect to the last host
 
			// We do this here, because it means that the network is really closed
 
			NetworkClientConnectGame(_network_last_host, _network_last_port);
 
		}
 
	} else {
 
		// server/client/standalone: not synced --> state game loop
 
		// Singleplayer
 
		StateGameLoop();
 
		// server/client: process queued network commands
 
		if (_networking) NetworkProcessCommands();
 
	}
 
#else
 
	StateGameLoop();
 
#endif /* ENABLE_NETWORK */
 

	
 
	if (!_pause && _display_opt&DO_FULL_ANIMATION)
 
		DoPaletteAnimations();
 
@@ -1038,10 +1090,6 @@ void GameLoop()
 

	
 
	MouseLoop();
 

	
 
	// send outgoing packets.
 
	NetworkCoreLoop(false);
 

	
 

	
 
	if (_game_mode != GM_MENU)
 
		MusicLoop();
 
}
 
@@ -1174,7 +1222,9 @@ bool AfterLoadGame(uint version)
 

	
 
	// If Load Scenario / New (Scenario) Game is used,
 
	//  a player does not exist yet. So create one here.
 
	if (!_players[0].is_active)
 
	// 1 exeption: network-games. Those can have 0 players
 
	//   But this exeption is not true for network_servers!
 
	if (!_players[0].is_active && (!_networking || (_networking && _network_server)))
 
		DoStartupNewPlayer(false);
 

	
 
	DoZoomInOutWindow(ZOOM_NONE, w); // update button status
ttd.h
Show inline comments
 
@@ -80,7 +80,7 @@ enum SwitchModes {
 
	SM_SAVE = 5,
 
	SM_GENRANDLAND = 6,
 
	SM_LOAD_SCENARIO = 9,
 

	
 
	SM_START_SCENARIO = 10,
 
};
 

	
 
enum MapTileTypes {
 
@@ -436,6 +436,8 @@ enum {
 
	WC_PERFORMANCE_DETAIL = 0x46,
 
	WC_CONSOLE = 0x47,
 
	WC_EXTRA_VIEW_PORT = 0x48,
 
	WC_CLIENT_LIST = 0x49,
 
	WC_NETWORK_STATUS_WINDOW = 0x4A,
 
};
 

	
 

	
ttd.vcproj
Show inline comments
 
@@ -268,6 +268,9 @@
 
				RelativePath=".\aystar.c">
 
			</File>
 
			<File
 
				RelativePath=".\callback_table.c">
 
			</File>
 
			<File
 
				RelativePath="command.c">
 
				<FileConfiguration
 
					Name="Release|Win32">
 
@@ -303,6 +306,9 @@
 
				RelativePath=".\console_cmds.c">
 
			</File>
 
			<File
 
				RelativePath=".\dedicated.c">
 
			</File>
 
			<File
 
				RelativePath="economy.c">
 
				<FileConfiguration
 
					Name="Release|Win32">
 
@@ -570,6 +576,21 @@
 
				RelativePath=".\newgrf.c">
 
			</File>
 
			<File
 
				RelativePath=".\network_client.c">
 
			</File>
 
			<File
 
				RelativePath=".\network_data.c">
 
			</File>
 
			<File
 
				RelativePath=".\network_gamelist.c">
 
			</File>
 
			<File
 
				RelativePath=".\network_udp.c">
 
			</File>
 
			<File
 
				RelativePath=".\network_server.c">
 
			</File>
 
			<File
 
				RelativePath="oldloader.c">
 
				<FileConfiguration
 
					Name="Release|Win32">
 
@@ -1148,6 +1169,24 @@
 
				RelativePath=".\newgrf.h">
 
			</File>
 
			<File
 
				RelativePath=".\network_client.h">
 
			</File>
 
			<File
 
				RelativePath=".\network_core.h">
 
			</File>
 
			<File
 
				RelativePath=".\network_data.h">
 
			</File>
 
			<File
 
				RelativePath=".\network_gamelist.h">
 
			</File>
 
			<File
 
				RelativePath=".\network_udp.h">
 
			</File>
 
			<File
 
				RelativePath=".\network_server.h">
 
			</File>
 
			<File
 
				RelativePath="news.h">
 
			</File>
 
			<File
unix.c
Show inline comments
 
@@ -331,6 +331,7 @@ const DriverDesc _video_driver_descs[] =
 
#if defined(WITH_SDL)
 
	{ "sdl",	"SDL Video Driver",		&_sdl_video_driver,		1},
 
#endif
 
	{ "dedicated", "Dedicated Video Driver", &_dedicated_video_driver, 0},
 
	{ NULL,		NULL,									NULL,									0}
 
};
 

	
variables.h
Show inline comments
 
@@ -205,6 +205,7 @@ VARDEF Paths _path;
 

	
 
// Which options struct does options modify?
 
VARDEF GameOptions *_opt_mod_ptr;
 
VARDEF GameOptions _opt_mod_temp;
 

	
 
// NOSAVE: Used in palette animations only, not really important.
 
VARDEF int _timer_counter;
 
@@ -220,24 +221,6 @@ VARDEF byte _player_colors[MAX_PLAYERS];
 
VARDEF bool _in_state_game_loop;
 
VARDEF uint32 _frame_counter;
 

	
 
VARDEF uint32 _frame_counter_max; // for networking, this is the frame that we are not allowed to execute yet.
 
VARDEF uint32 _frame_counter_srv; // for networking, this is the last known framecounter of the server. it is always less than frame_counter_max.
 

	
 
// networking settings
 
VARDEF bool _network_available;  // is network mode available?
 
VARDEF uint32 _network_ip_list[10]; // Network IPs
 
VARDEF uint16 _network_game_count;
 

	
 
VARDEF uint _network_client_port;
 
VARDEF uint _network_server_port;
 

	
 
VARDEF uint16 _network_sync_freq;
 
VARDEF uint16 _network_ahead_frames;
 
VARDEF uint16 _network_ready_ahead;
 
VARDEF uint16 _network_client_timeout;
 

	
 
VARDEF uint32 _sync_seed_1, _sync_seed_2;
 

	
 
VARDEF bool _is_ai_player; // current player is an AI player? - Can be removed if new AI is done
 

	
 
VARDEF bool _do_autosave;
 
@@ -293,15 +276,6 @@ VARDEF bool _exit_game;
 
VARDEF SmallFiosItem _file_to_saveload;
 
VARDEF byte _make_screenshot;
 

	
 
VARDEF bool _networking;
 
VARDEF bool _networking_override; // dont shutdown network core when the GameMenu appears.
 

	
 
VARDEF bool _networking_sync; // if we use network mode and the games must stay in sync.
 
VARDEF bool _networking_server;
 
VARDEF bool _networking_queuing; // queueing only?
 

	
 
VARDEF byte _network_playas; // an id to play as..
 

	
 
VARDEF byte _get_z_hint; // used as a hint to getslopez to return the right height at a bridge.
 

	
 
VARDEF char *_newgrf_files[32];
vehicle.c
Show inline comments
 
@@ -1069,6 +1069,12 @@ static const byte * const _effecttick9_d
 

	
 
static void EffectTick_9(Vehicle *v)
 
{
 
	/*
 
	 * Warning: those effects can NOT use Random(), and have to use
 
	 *  InteractiveRandom(), because somehow someone forgot to save
 
	 *  spritenum to the savegame, and so it will cause desyncs in
 
	 *  multiplayer!! (that is: in ToyLand)
 
	 */
 
	int et;
 
	const byte *b;
 

	
 
@@ -1086,7 +1092,7 @@ static void EffectTick_9(Vehicle *v)
 
			return;
 
		}
 
		if (v->u.special.unk2 != 0) {
 
			v->spritenum = (byte)((Random()&3)+1);
 
			v->spritenum = (byte)((InteractiveRandom()&3)+1);
 
		} else {
 
			v->spritenum = 6;
 
		}
 
@@ -1104,7 +1110,7 @@ again:
 
	}
 

	
 
	if (*b == 0x81) {
 
		if (v->z_pos > 180 || CHANCE16(1,96)) {
 
		if (v->z_pos > 180 || CHANCE16I(1,96, InteractiveRandom())) {
 
			v->spritenum = 5;
 
			SndPlayVehicleFx(SND_2F_POP, v);
 
		}
 
@@ -1399,7 +1405,7 @@ int32 CmdNameVehicle(int x, int y, uint3
 
	if (!CheckOwnership(v->owner))
 
		return CMD_ERROR;
 

	
 
	str = AllocateName((byte*)_decode_parameters, 2);
 
	str = AllocateNameUnique((byte*)_decode_parameters, 2);
 
	if (str == 0)
 
		return CMD_ERROR;
 

	
win32.c
Show inline comments
 
@@ -11,6 +11,7 @@
 
#include <wininet.h>
 
#include <io.h>
 
#include <fcntl.h>
 
#include "network.h"
 

	
 
#define SMART_PALETTE_ANIM
 

	
 
@@ -719,6 +720,7 @@ static int Win32GdiMainLoop()
 
			Sleep(1);
 
			GdiFlush();
 
			_screen.dst_ptr = _wnd.buffer_bits;
 
			DrawTextMessage();
 
			DrawMouseCursor();
 
		}
 
	}
 
@@ -1805,6 +1807,7 @@ const DriverDesc _video_driver_descs[] =
 
	{"sdl", "SDL Video Driver",					&_sdl_video_driver,			1},
 
#endif
 
	{"win32", "Win32 GDI Video Driver",	&_win32_video_driver,		Windows_NT3_51},
 
	{ "dedicated", "Dedicated Video Driver", &_dedicated_video_driver, 0},
 
	{NULL}
 
};
 

	
 
@@ -1946,12 +1949,12 @@ void CreateConsole()
 
	// redirect unbuffered STDIN, STDOUT, STDERR to the console
 
#if !defined(__CYGWIN__)
 
	*stdout = *_fdopen( _open_osfhandle((long)hand, _O_TEXT), "w" );
 
	*stdin = *_fdopen(_open_osfhandle((long)GetStdHandle(STD_INPUT_HANDLE), _O_TEXT), "w" );
 
	*stdin = *_fdopen(_open_osfhandle((long)GetStdHandle(STD_INPUT_HANDLE), _O_TEXT), "r" );
 
	*stderr = *_fdopen(_open_osfhandle((long)GetStdHandle(STD_ERROR_HANDLE), _O_TEXT), "w" );
 
#else
 
	// open_osfhandle is not in cygwin
 
	*stdout = *fdopen(1, "w" );
 
	*stdin = *fdopen(0, "w" );
 
	*stdin = *fdopen(0, "r" );
 
	*stderr = *fdopen(2, "w" );
 
#endif
 

	
 
@@ -2052,3 +2055,21 @@ void DeterminePaths()
 
	CreateDirectory(_path.scenario_dir, NULL);
 
}
 

	
 
int snprintf(char *str, size_t size, const char *format, ...)
 
{
 
	va_list ap;
 
	int ret;
 

	
 
	va_start(ap, format);
 
	ret = vsnprintf(str, size, format, ap);
 
	va_end(ap);
 
	return ret;
 
}
 

	
 
int vsnprintf(char *str, size_t size, const char *format, va_list ap)
 
{
 
	int ret;
 
	ret = _vsnprintf(str, size, format, ap);
 
	if (ret < 0) str[size - 1] = '\0';
 
	return ret;
 
}
window.c
Show inline comments
 
@@ -170,6 +170,7 @@ void DrawOverlappedWindow(Window *w, int
 
void CallWindowEventNP(Window *w, int event)
 
{
 
	WindowEvent e;
 

	
 
	e.event = event;
 
	w->wndproc(w, &e);
 
}
 
@@ -691,7 +692,40 @@ static bool HandlePopupMenu()
 
	return false;
 
}
 

	
 
static bool HandleWindowDragging()
 
bool HandleMouseOver()
 
{
 
	Window *w;
 
	WindowEvent e;
 
	static Window *last_w = NULL;
 

	
 
	w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
 

	
 
	// We changed window, put a MOUSEOVER event to the last window
 
	if (last_w && last_w != w) {
 
		e.event = WE_MOUSEOVER;
 
		e.mouseover.pt.x = -1;
 
		e.mouseover.pt.y = -1;
 
		if (last_w->wndproc)
 
			last_w->wndproc(last_w, &e);
 
	}
 
	last_w = w;
 

	
 
	if (w) {
 
		// send an event in client coordinates.
 
		e.event = WE_MOUSEOVER;
 
		e.mouseover.pt.x = _cursor.pos.x - w->left;
 
		e.mouseover.pt.y = _cursor.pos.y - w->top;
 
		if (w->widget != NULL) {
 
			e.mouseover.widget = GetWidgetFromPos(w, e.mouseover.pt.x, e.mouseover.pt.y);
 
		}
 
		w->wndproc(w, &e);
 
	}
 

	
 
	// Mouseover never stops execution
 
	return true;
 
}
 

	
 
bool HandleWindowDragging()
 
{
 
	Window *w;
 
	// Get out immediately if no window is being dragged at all.
 
@@ -1083,6 +1117,9 @@ void MouseLoop()
 
	if (!HandleViewportScroll())
 
		return;
 

	
 
	if (!HandleMouseOver())
 
		return;
 

	
 
	x = _cursor.pos.x;
 
	y = _cursor.pos.y;
 

	
 
@@ -1188,6 +1225,7 @@ void UpdateWindows()
 
		if (w->viewport != NULL)
 
			UpdateViewportPosition(w);
 
	}
 
	DrawTextMessage();
 
	// Redraw mouse cursor in case it was hidden
 
	DrawMouseCursor();
 
}
window.h
Show inline comments
 
@@ -55,6 +55,12 @@ union WindowEvent {
 

	
 
	struct {
 
		byte event;
 
		Point pt;
 
		int widget;
 
	} mouseover;
 

	
 
	struct {
 
		byte event;
 
		bool cont;   // continue the search? (default true)
 
		byte ascii;  // 8-bit ASCII-value of the key
 
		uint16 keycode;// untranslated key (including shift-state)
 
@@ -303,6 +309,8 @@ enum WindowEvents {
 
	WE_RCLICK = 17,
 
	WE_KEYPRESS = 18,
 
	WE_CREATE = 19,
 
	WE_MOUSEOVER = 20,
 
	WE_ON_EDIT_TEXT_CANCEL = 21,
 
};
 

	
 

	
0 comments (0 inline, 0 general)