(svn r942) -Merged branch/network back into the trunk

pull/155/head
truelight 20 years ago
parent c90bba35a2
commit d6a1f3e412

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

@ -830,7 +830,7 @@ static int AiNew_HowManyVehicles(Player *p) {
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 *p) {
// 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 {

@ -25,7 +25,7 @@ static void DrawAircraftImage(Vehicle *v, int x, int y, VehicleID selection)
}
}
static void CcBuildAircraft(bool success, uint tile, uint32 p1, uint32 p2)
void CcBuildAircraft(bool success, uint tile, uint32 p1, uint32 p2)
{
Vehicle *v;

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

@ -23,7 +23,7 @@ extern const PalSpriteID _bridge_sprites[MAX_BRIDGES];
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);
}

@ -0,0 +1,74 @@
#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);

@ -0,0 +1,7 @@
#ifndef CALLBACK_TABLE_H
#define CALLBACK_TABLE_H
extern CommandCallback *_callback_table[];
extern const int _callback_table_count;
#endif

@ -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_table[] = {
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 p1, uint32 p2, CommandCallback *callback,
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 p1, uint32 p2, CommandCallback *callback,
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 p1, uint32 p2, CommandCallback *callback,
(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 p1, uint32 p2, CommandCallback *callback,
}
// 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 p1, uint32 p2, CommandCallback *callback,
_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 {

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

@ -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, WindowEvent* e)
{
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, WindowEvent* e)
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, WindowEvent* e)
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 char direction)
_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, const char* string)
}
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_code, const char* s, ...)
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* name, void* addr, _iconsole_var_types type)
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* name, void* addr, _iconsole_var_types type)
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* var, const char* name)
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 @@ _iconsole_var* IConsoleVarAlloc(_iconsole_var_types type)
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, int value) {
*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* var, int value) {
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* var, const char* dump_desc)
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, _iconsole_hook_types type, iconsole_var_h
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;

@ -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* cmd);
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);

@ -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 char* arg)
/* 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)
DEF_CONSOLE_VAR_HOOK(ConVarHookNoNetClient)
{
if (_networking) {
IConsoleError("This variable is forbidden in multiplayer.");
if (!_network_server) {
IConsoleError("This variable only makes sense for a network server.");
return false;
}
return true;
}
#endif
DEF_CONSOLE_VAR_HOOK(ConVarHookNoNetClient)
DEF_CONSOLE_CMD_HOOK(ConCmdHookNoNetClient)
{
if (!_networking_server) {
IConsoleError("This variable only makes sense for a network server.");
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) {
fgets(cmd, sizeof(cmd), _script_file);
strtok(cmd, "\r\n");
IConsoleCmdExec(cmd);
fgets(cmd, sizeof(cmd), _script_file);
}
_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 */
}

@ -0,0 +1,179 @@
#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 */

@ -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, uint tile, uint32 p1, uint32 p2)
}
}
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);
}

@ -90,7 +90,7 @@ Virtually any settings - train numbers, start date, what vehicles your competito
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.

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

@ -18,11 +18,18 @@ Multiplayer Manual for OpenTTD
- 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 @@ Multiplayer Manual for OpenTTD
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!
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.
- 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

@ -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 *p, bool update)
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 *p)
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 *p)
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;
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);
if (IS_HUMAN_PLAYER(owner)) {
// what does this code do??
InitNewsItemStructs();
DeleteWindowById(WC_NEWS_WINDOW, 0);
switch (p->quarters_of_bankrupcy) {
case 2:
AddNewsItem( (StringID)(owner + 16),
NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
break;
case 3: {
/* XXX - In multiplayer, should we ask other players if it wants to take
over when it is a human company? -- TrueLight */
if (IS_HUMAN_PLAYER(owner)) {
AddNewsItem( (StringID)(owner + 16),
NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
break;
}
// Check if the company has any value.. if not, declare it bankrupt
// right now
val = CalculateCompanyValue(p);
if (val > 0) {
p->bankrupt_value = val;
p->bankrupt_asked = 1 << owner; // Don't ask the owner
p->bankrupt_timeout = 0;
break;
}
// Else, falltrue to case 4...
}
case 4: {
// Close everything the owner has open
DeletePlayerWindows(owner);
// Show bankrupt news
SetDParam(0, p->name_1);
SetDParam(1, p->name_2);
AddNewsItem( (StringID)(owner + 16*3), NEWS_FLAGS(NM_CALLBACK, 0, NT_COMPANY_INFO, DNC_BANKRUPCY),0,0);
// 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(FoundRoute *fr)
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(FoundRoute *fr)
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(FoundRoute *fr)
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(FoundRoute *fr)
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;

@ -783,7 +783,7 @@ int32 CmdRenameEngine(int x, int y, uint32 flags, uint32 p1, uint32 p2)
{
StringID str;
str = AllocateName((byte*)_decode_parameters, 0);
str = AllocateNameUnique((byte*)_decode_parameters, 0);
if (str == 0)
return CMD_ERROR;

@ -1,8 +1,6 @@
#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, int y, uint16 duration);
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, int bridge_len);
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 only_constants);
/* 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, int max);
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);

40
gfx.c

@ -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 width, int height, int xo, int yo) {
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, byte color)
}
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, uint16 str, byte color)
}
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, uint16 str, byte color)
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, int right, int bottom, int ctab, int flags
}
}
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 x, int y, byte color) {
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, int right, int bottom)
UndrawMouseCursor();
}
}
UndrawTextMessage();
#if defined(_DEBUG)
if (_dbg_screen_rect)
@ -1921,9 +1932,18 @@ void ToggleFullScreen(const bool full_screen)
{
_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;
}

11
gfx.h

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

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

@ -71,6 +71,8 @@ extern const HalMusicDriver _extmidi_music_driver;
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

@ -969,7 +969,9 @@ static void MaybePlantFarmField(Industry *i)
{
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 *i, uint tile, int type, const 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;
}
}

@ -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, WindowEvent *e) {
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, WindowEvent *e) {
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, uint32 flags, uint32 p1, uint32 p2)
_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, uint32 flags, uint32 p1, uint32 p2)
_random_seeds[0][0] = p1;
_random_seeds[0][1] = p2;
if (_networking) { NetworkStartSync(true); }
StartScenario();
SwitchMode(SM_START_SCENARIO);
return 0;
}

@ -1062,10 +1062,10 @@ STR_CONFIG_PATCHES_CURRENCY :{CURRENCY}
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: this new AI is still alpha! Currently, o
############ 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}Start new multiplayer game
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}Resume an earlier saved multiplayer game (
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_SPECTATE_GAME :{BLACK}Spectate game
STR_NETWORK_SPECTATE_GAME_TIP :{BLACK}Watch the game as a spectator
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_READY :{BLACK}Ready
STR_NETWORK_SPECTATE_GAME :{BLACK}Spectate game
STR_NETWORK_SPECTATE_GAME_TIP :{BLACK}Watch the game as a spectator
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} The server didn't answer the request
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

@ -170,7 +170,6 @@ static inline int FindFirstBit2x64(int value)
#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))

@ -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 *e) {
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 *cp)
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(Window *w)
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(Window *w)
}
}
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, WindowEvent *e)
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, WindowEvent *e)
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, WindowEvent *e)
}
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, WindowEvent *e)
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(Window *w, int x, int main_button, int g
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, uint tile, uint32 p1, uint32 p2)
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 tile)
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, WindowEvent *e)
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, WindowEvent *e) {
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);
}

@ -1,108 +1,110 @@
# 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
# 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

@ -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
#else
#undef PLAYER_SEED_RANDOM
#endif
// its for now not used at all because it is still desyncing :(
/* 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
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
#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, byte skip)
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,10 +636,10 @@ void IncreaseDate()
/* yeah, increse day counter and call various daily loops */
_date++;
NetworkGameChangeDate(_date);
_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)) {

@ -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, uint32 flags, uint32 p1, uint32 p2)
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 y, uint32 flags, uint32 p1, uint32 p2)
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 flags, uint32 p1, uint32 p2)
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 flags, uint32 p1, uint32 p2)
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, int y, uint32 flags, uint32 p1, uint32 p2)
} 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;

@ -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, WindowEvent *e)
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_widgets[] = {
};
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 *w, WindowEvent *e) {
}
}
static void SetFiosType(const byte fiostype)
void SetFiosType(const byte fiostype)
{
switch (fiostype) {
case FIOS_TYPE_FILE: case FIOS_TYPE_SCENARIO:

File diff suppressed because it is too large Load Diff

@ -1,36 +1,175 @@
#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;
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 */
NetworkGameInfo _network_game;
NetworkGameList * _network_game_list;
// 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 */

@ -0,0 +1,808 @@
#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 */

@ -0,0 +1,22 @@
#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

@ -0,0 +1,83 @@
#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

@ -0,0 +1,434 @@
#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 */

@ -0,0 +1,223 @@
#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

@ -0,0 +1,83 @@
#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 */

@ -0,0 +1,8 @@
#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 */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,20 @@
#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

@ -0,0 +1,368 @@
#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 */

@ -0,0 +1,10 @@
#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 */

@ -325,7 +325,7 @@ void RestoreVehicleOrders(Vehicle *v, BackuppedOrders *bak)
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, BackuppedOrders *bak)
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++;
}

@ -490,6 +490,9 @@ static void PlayerCompanyWndProc(Window *w, WindowEvent *e)
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 *w, WindowEvent *e)
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);

@ -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 flags, uint32 p1, uint32 p2)
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

@ -826,7 +826,7 @@ int32 CmdRenameWaypoint(int x, int y, uint32 flags, uint32 p1, uint32 p2)
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) {

@ -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[12] = {
};
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 tile, uint32 p1, uint32 p2)
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, uint tile, uint32 p1, uint32 p2)
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));
}

@ -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, uint tile, uint32 p1, uint32 p2)
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 tile, int direction)
}
}
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 tile, uint32 p1, uint32 p2)
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)

@ -368,7 +368,7 @@ static void DrawNewRoadVehWindow(Window *w)
}
}
static void CcBuildRoadVeh(bool success, uint tile, uint32 p1, uint32 p2)
void CcBuildRoadVeh(bool success, uint tile, uint32 p1, uint32 p2)
{
Vehicle *v;

@ -1,2 +1 @@
echo "Setting default network client settings..."
*net_ready_ahead = 1

@ -0,0 +1,3 @@
echo "Setting dedicated network server settings..."
setpassword "*"
setservername "My example dedicated gameserver"

@ -1,3 +1,3 @@
echo "Setting default network server settings..."
*net_sync_freq = 4
*net_client_timeout = 300
net_sync_freq = 100
net_frame_freq = 0

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

@ -1,38 +1,12 @@
#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 SettingDesc *desc, const char *str)
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 *ini, const SettingDesc *desc, const void
// 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 *ini, const SettingDesc *desc, const void
}
}
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 *ini, const SettingDesc *desc, const void
// 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 *ini, const SettingDesc *desc, const void
//***************************
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},
// 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},
{"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},
{"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},
{"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},
{"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},
{NULL, 0, NULL, NULL, 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},
// 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},
{"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},
{"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},
{"bribe", SDT_BOOL, (void*)false, (void*)offsetof(Patches, bribe), NULL},
{"new_depot_finding", SDT_BOOL, (void*)false, (void*)offsetof(Patches, new_depot_finding), 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},
{"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},
{"signal_side", SDT_BOOL, (void*)true, &_patches.signal_side, 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},
{"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},
{"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},
{"snow_line_height", SDT_UINT8, (void*)7, &_patches.snow_line_height, 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},
{"bribe", SDT_BOOL, (void*)true, &_patches.bribe, NULL},
{"new_depot_finding", SDT_BOOL, (void*)false, &_patches.new_depot_finding, 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},
{"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},
{"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},
{"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},
{"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},
{"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},
{"colored_news_date", SDT_UINT32, (void*)2000, (void*)offsetof(Patches, colored_news_date), 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},
{"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},
{"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},
{"extra_dynamite", SDT_BOOL, (void*)false, (void*)offsetof(Patches, extra_dynamite), NULL},
{"colored_news_date", SDT_UINT32, (void*)2000, &_patches.colored_news_date, 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},
{"extra_dynamite", SDT_BOOL, (void*)false, &_patches.extra_dynamite, NULL},
{"auto_euro", SDT_BOOL, (void*)true, (void*)offsetof(Patches, auto_euro), 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},
{"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},
{"auto_euro", SDT_BOOL, (void*)true, &_patches.auto_euro, 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},
{"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},
{"ainew_active", SDT_BOOL, (void*)false, (void*)offsetof(Patches, ainew_active), 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},
{"drag_signals_density",SDT_UINT8, (void*)4, (void*)offsetof(Patches, drag_signals_density), 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, void *base);
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, debug_settings, "debug", NULL);
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");
}
static void LoadGrfSettings(IniFile *ini)

@ -0,0 +1,39 @@
#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 */

@ -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 *b)
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, WindowEvent *e)
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 *w, WindowEvent *e)
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_construction[] = {
{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_vehicles[] = {
{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 val)
*(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(Window *w, WindowEvent *e)
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(Window *w, WindowEvent *e)
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(Window *w, WindowEvent *e)
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(Window *w, WindowEvent *e)
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(Window *w, WindowEvent *e)
}
}
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, WindowEvent *e)
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, WindowEvent *e)
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, WindowEvent *e)
// 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);

@ -301,7 +301,7 @@ static void ShowShipDetailsWindow(Vehicle *v)
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;

@ -2415,7 +2415,7 @@ int32 CmdRenameStation(int x, int y, uint32 flags, uint32 p1, uint32 p2)
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, byte flags) {
void InitializeStations()
{
int i;
memset(_stations, 0, sizeof(_stations));
for(i = 0; i != lengthof(_stations); i++)
_stations[i].index=i;

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

@ -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(

@ -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 flags, uint32 p1, uint32 p2)
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;
}
}

@ -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, uint tile, uint32 p1, uint32 p2)
}
}
static void CcBuildLoco(bool success, uint tile, uint32 p1, uint32 p2)
void CcBuildLoco(bool success, uint tile, uint32 p1, uint32 p2)
{
Vehicle *v;

298
ttd.c

@ -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 *filename, int mode, int newgm)
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);
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();
}
}
#ifdef ENABLE_NETWORK
// Check for UDP stuff
NetworkUDPGameLoop();
if (_networking) {
// Multiplayer
NetworkGameLoop();
} else {
// server/client/standalone: not synced --> state game loop
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);
}
// 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

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

@ -267,6 +267,9 @@
<File
RelativePath=".\aystar.c">
</File>
<File
RelativePath=".\callback_table.c">
</File>
<File
RelativePath="command.c">
<FileConfiguration
@ -302,6 +305,9 @@
<File
RelativePath=".\console_cmds.c">
</File>
<File
RelativePath=".\dedicated.c">
</File>
<File
RelativePath="economy.c">
<FileConfiguration
@ -569,6 +575,21 @@
<File
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
@ -1147,6 +1168,24 @@
<File
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>

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

@ -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];

@ -1069,6 +1069,12 @@ static const byte * const _effecttick9_data[6] = {
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, uint32 flags, uint32 p1, uint32 p2)
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;

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

@ -170,6 +170,7 @@ void DrawOverlappedWindow(Window *w, int left, int top, int right, int bottom)
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();
}

@ -53,6 +53,12 @@ union WindowEvent {
int index;
} dropdown;
struct {
byte event;
Point pt;
int widget;
} mouseover;
struct {
byte event;
bool cont; // continue the search? (default true)
@ -303,6 +309,8 @@ enum WindowEvents {
WE_RCLICK = 17,
WE_KEYPRESS = 18,
WE_CREATE = 19,
WE_MOUSEOVER = 20,
WE_ON_EDIT_TEXT_CANCEL = 21,
};

Loading…
Cancel
Save