mirror of https://github.com/OpenTTD/OpenTTD
(svn r942) -Merged branch/network back into the trunk
parent
0434287ef8
commit
b8f6d41418
36
Makefile
36
Makefile
|
@ -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
|
||||
|
|
4
ai_new.c
4
ai_new.c
|
@ -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
|
51
command.c
51
command.c
|
@ -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)
|
||||
|
|
122
console.c
122
console.c
|
@ -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;
|
||||
|
|
12
console.h
12
console.h
|
@ -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);
|
||||
|
|
378
console_cmds.c
378
console_cmds.c
|
@ -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)
|
||||
{
|
||||
if (_networking) {
|
||||
IConsoleError("This variable is forbidden in multiplayer.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
DEF_CONSOLE_VAR_HOOK(ConVarHookNoNetClient)
|
||||
{
|
||||
if (!_networking_server) {
|
||||
if (!_network_server) {
|
||||
IConsoleError("This variable only makes sense for a network server.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
DEF_CONSOLE_CMD_HOOK(ConCmdHookNoNetClient)
|
||||
{
|
||||
if (!_networking || !_network_server) {
|
||||
IConsoleError("This command is only available for a network server.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
DEF_CONSOLE_CMD_HOOK(ConCmdHookNoNetServer)
|
||||
{
|
||||
if (!_networking || _network_server) {
|
||||
IConsoleError("You can not use this command for you are a network-server.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
DEF_CONSOLE_CMD_HOOK(ConCmdHookNeedNetwork)
|
||||
{
|
||||
if (!_networking) {
|
||||
IConsoleError("Not connected. Multiplayer only command.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
|
||||
/* **************************** */
|
||||
/* reset commands */
|
||||
/* **************************** */
|
||||
|
@ -100,41 +120,130 @@ DEF_CONSOLE_CMD(ConScrollToTile)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// ********************************* //
|
||||
// * Network Core Console Commands * //
|
||||
// ********************************* //
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
DEF_CONSOLE_CMD(ConNetworkConnect)
|
||||
DEF_CONSOLE_CMD(ConStatus)
|
||||
{
|
||||
char* ip;
|
||||
const char *port = NULL;
|
||||
const char *player = NULL;
|
||||
uint16 rport;
|
||||
const char *status;
|
||||
int lag;
|
||||
const ClientState *cs;
|
||||
const NetworkClientInfo *ci;
|
||||
FOR_ALL_CLIENTS(cs) {
|
||||
lag = NetworkCalculateLag(cs);
|
||||
ci = DEREF_CLIENT_INFO(cs);
|
||||
|
||||
if (argc<2) return NULL;
|
||||
|
||||
ip = argv[1];
|
||||
rport = _network_server_port;
|
||||
|
||||
ParseConnectionString(&player, &port, ip);
|
||||
|
||||
IConsolePrintF(_iconsole_color_default, "Connecting to %s...", ip);
|
||||
if (player!=NULL) {
|
||||
_network_playas = atoi(player);
|
||||
IConsolePrintF(_iconsole_color_default, " player-no: %s", player);
|
||||
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);
|
||||
}
|
||||
if (port!=NULL) {
|
||||
rport = atoi(port);
|
||||
IConsolePrintF(_iconsole_color_default, " port: %s", port);
|
||||
}
|
||||
|
||||
NetworkCoreConnectGame(ip, rport);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
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 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_DEFAULT_PORT;
|
||||
|
||||
ParseConnectionString(&player, &port, ip);
|
||||
|
||||
IConsolePrintF(_iconsole_color_default, "Connecting to %s...", ip);
|
||||
if (player != NULL) {
|
||||
_network_playas = atoi(player);
|
||||
IConsolePrintF(_iconsole_color_default, " player-no: %s", player);
|
||||
}
|
||||
if (port != NULL) {
|
||||
rport = atoi(port);
|
||||
IConsolePrintF(_iconsole_color_default, " port: %s", port);
|
||||
}
|
||||
|
||||
NetworkClientConnectGame(ip, rport);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#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
|
||||
|
||||
|
109
economy.c
109
economy.c
|
@ -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);
|
||||
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;
|
||||
}
|
||||
|
||||
} 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);
|
||||
// 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;
|
||||
|
|
2
engine.c
2
engine.c
|
@ -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;
|
||||
|
||||
|
|
94
functions.h
94
functions.h
|
@ -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
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
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();
|
||||
|
|
6
gui.h
6
gui.h
|
@ -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();
|
||||
|
|
8
hal.h
8
hal.h
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
31
intro_gui.c
31
intro_gui.c
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
163
lang/english.txt
163
lang/english.txt
|
@ -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
|
||||
|
||||
|
|
1
macros.h
1
macros.h
|
@ -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))
|
||||
|
||||
|
|
170
main_gui.c
170
main_gui.c
|
@ -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
|
||||
|
|
95
misc.c
95
misc.c
|
@ -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)) {
|
||||
|
|
26
misc_cmd.c
26
misc_cmd.c
|
@ -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;
|
||||
|
|
39
misc_gui.c
39
misc_gui.c
|
@ -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:
|
||||
|
|
177
network.h
177
network.h
|
@ -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,
|
||||
|
||||
NetworkGameInfo _network_game;
|
||||
NetworkGameList * _network_game_list;
|
||||
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 */
|
||||
|
||||
// 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 */
|
1195
network_gui.c
1195
network_gui.c
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);
|
||||
|
||||
|
|
42
players.c
42
players.c
|
@ -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) {
|
||||
|
|
12
rail_gui.c
12
rail_gui.c
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
14
road_gui.c
14
road_gui.c
|
@ -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
|
||||
|
|
2
sdl.c
2
sdl.c
|
@ -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();
|
||||
}
|
||||
|
|
302
settings.c
302
settings.c
|
@ -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},
|
||||
// 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},
|
||||
|
||||
{"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},
|
||||
{"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},
|
||||
|
||||
{"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},
|
||||
{"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},
|
||||
|
||||
{"lost_train_days", SDT_UINT16, (void*)180, (void*)offsetof(Patches, lost_train_days), NULL},
|
||||
{"train_income_warn", SDT_BOOL, (void*)true, (void*)offsetof(Patches, train_income_warn), NULL},
|
||||
|
||||
{"status_long_date", SDT_BOOL, (void*)true, (void*)offsetof(Patches, status_long_date), NULL},
|
||||
{"signal_side", SDT_BOOL, (void*)true, (void*)offsetof(Patches, signal_side), NULL},
|
||||
{"show_finances", SDT_BOOL, (void*)true, (void*)offsetof(Patches, show_finances), NULL},
|
||||
|
||||
{"new_nonstop", SDT_BOOL, (void*)false, (void*)offsetof(Patches, new_nonstop), NULL},
|
||||
{"roadveh_queue", SDT_BOOL, (void*)false, (void*)offsetof(Patches, roadveh_queue), NULL},
|
||||
|
||||
{"autoscroll", SDT_BOOL, (void*)false, (void*)offsetof(Patches, autoscroll), NULL},
|
||||
{"errmsg_duration", SDT_UINT8, (void*)5, (void*)offsetof(Patches, errmsg_duration), NULL},
|
||||
{"snow_line_height", SDT_UINT8, (void*)7, (void*)offsetof(Patches, snow_line_height), NULL},
|
||||
|
||||
{"bribe", SDT_BOOL, (void*)false, (void*)offsetof(Patches, bribe), NULL},
|
||||
{"new_depot_finding", SDT_BOOL, (void*)false, (void*)offsetof(Patches, new_depot_finding), NULL},
|
||||
|
||||
{"nonuniform_stations", SDT_BOOL, (void*)false, (void*)offsetof(Patches, nonuniform_stations), NULL},
|
||||
{"always_small_airport",SDT_BOOL, (void*)false, (void*)offsetof(Patches, always_small_airport), NULL},
|
||||
{"realistic_acceleration",SDT_BOOL, (void*)false, (void*)offsetof(Patches, realistic_acceleration), NULL},
|
||||
|
||||
{"toolbar_pos", SDT_UINT8, (void*)0, (void*)offsetof(Patches, toolbar_pos), NULL},
|
||||
{"window_snap_radius", SDT_UINT8, (void*)10, (void*)offsetof(Patches, window_snap_radius), NULL},
|
||||
|
||||
{"max_trains", SDT_UINT8, (void*)80, (void*)offsetof(Patches, max_trains), NULL},
|
||||
{"max_roadveh", SDT_UINT8, (void*)80, (void*)offsetof(Patches, max_roadveh), NULL},
|
||||
{"max_aircraft", SDT_UINT8, (void*)40, (void*)offsetof(Patches, max_aircraft), NULL},
|
||||
{"max_ships", SDT_UINT8, (void*)50, (void*)offsetof(Patches, max_ships), NULL},
|
||||
|
||||
{"servint_ispercent", SDT_BOOL, (void*)false, (void*)offsetof(Patches, servint_ispercent), NULL},
|
||||
{"servint_trains", SDT_UINT16, (void*)150, (void*)offsetof(Patches, servint_trains), NULL},
|
||||
{"servint_roadveh", SDT_UINT16, (void*)150, (void*)offsetof(Patches, servint_roadveh), NULL},
|
||||
{"servint_ships", SDT_UINT16, (void*)360, (void*)offsetof(Patches, servint_ships), NULL},
|
||||
{"servint_aircraft", SDT_UINT16, (void*)100, (void*)offsetof(Patches, servint_aircraft), NULL},
|
||||
|
||||
{"autorenew", SDT_BOOL, (void*)false, (void*)offsetof(Patches, autorenew), NULL},
|
||||
{"autorenew_months", SDT_INT16, (void*)-6, (void*)offsetof(Patches, autorenew_months), NULL},
|
||||
{"autorenew_money", SDT_INT32, (void*)100000,(void*)offsetof(Patches, autorenew_money), NULL},
|
||||
|
||||
{"new_pathfinding", SDT_BOOL, (void*)false, (void*)offsetof(Patches, new_pathfinding), NULL},
|
||||
{"pf_maxlength", SDT_UINT16, (void*)512, (void*)offsetof(Patches, pf_maxlength), NULL},
|
||||
{"pf_maxdepth", SDT_UINT8, (void*)16, (void*)offsetof(Patches, pf_maxdepth), NULL},
|
||||
|
||||
|
||||
{"ai_disable_veh_train",SDT_BOOL, (void*)false, (void*)offsetof(Patches, ai_disable_veh_train), NULL},
|
||||
{"ai_disable_veh_roadveh",SDT_BOOL, (void*)false, (void*)offsetof(Patches, ai_disable_veh_roadveh), NULL},
|
||||
{"ai_disable_veh_aircraft",SDT_BOOL,(void*)false, (void*)offsetof(Patches, ai_disable_veh_aircraft),NULL},
|
||||
{"ai_disable_veh_ship", SDT_BOOL, (void*)false, (void*)offsetof(Patches, ai_disable_veh_ship), NULL},
|
||||
{"starting_date", SDT_UINT32, (void*)1950, (void*)offsetof(Patches, starting_date), NULL},
|
||||
|
||||
{"colored_news_date", SDT_UINT32, (void*)2000, (void*)offsetof(Patches, colored_news_date), NULL},
|
||||
|
||||
{"bridge_pillars", SDT_BOOL, (void*)true, (void*)offsetof(Patches, bridge_pillars), NULL},
|
||||
{"invisible_trees", SDT_BOOL, (void*)false, (void*)offsetof(Patches, invisible_trees), NULL},
|
||||
|
||||
{"keep_all_autosave", SDT_BOOL, (void*)false, (void*)offsetof(Patches, keep_all_autosave), NULL},
|
||||
|
||||
{"extra_dynamite", SDT_BOOL, (void*)false, (void*)offsetof(Patches, extra_dynamite), NULL},
|
||||
|
||||
{"never_expire_vehicles",SDT_BOOL, (void*)false, (void*)offsetof(Patches, never_expire_vehicles),NULL},
|
||||
{"extend_vehicle_life", SDT_UINT8, (void*)0, (void*)offsetof(Patches, extend_vehicle_life), NULL},
|
||||
|
||||
{"auto_euro", SDT_BOOL, (void*)true, (void*)offsetof(Patches, auto_euro), NULL},
|
||||
|
||||
{"serviceathelipad", SDT_BOOL, (void*)true, (void*)offsetof(Patches, serviceathelipad), NULL},
|
||||
{"smooth_economy", SDT_BOOL, (void*)false, (void*)offsetof(Patches, smooth_economy), NULL},
|
||||
{"dist_local_authority",SDT_UINT8, (void*)20, (void*)offsetof(Patches, dist_local_authority), NULL},
|
||||
|
||||
{"wait_oneway_signal", SDT_UINT8, (void*)15, (void*)offsetof(Patches, wait_oneway_signal), NULL},
|
||||
{"wait_twoway_signal", SDT_UINT8, (void*)41, (void*)offsetof(Patches, wait_twoway_signal), NULL},
|
||||
|
||||
{"ainew_active", SDT_BOOL, (void*)false, (void*)offsetof(Patches, ainew_active), NULL},
|
||||
|
||||
{"drag_signals_density",SDT_UINT8, (void*)4, (void*)offsetof(Patches, drag_signals_density), NULL},
|
||||
{"bridge_pillars", SDT_BOOL, (void*)true, &_patches.bridge_pillars, NULL},
|
||||
{"invisible_trees", SDT_BOOL, (void*)false, &_patches.invisible_trees, NULL},
|
||||
{"drag_signals_density",SDT_UINT8, (void*)4, &_patches.drag_signals_density, NULL},
|
||||
|
||||
{NULL, 0, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
typedef void SettingDescProc(IniFile *ini, const SettingDesc *desc, const void *grpname, void *base);
|
||||
// Non-static, needed in network_server.c
|
||||
const SettingDesc patch_settings[] = {
|
||||
{"build_on_slopes", SDT_BOOL, (void*)true, &_patches.build_on_slopes, NULL},
|
||||
{"mammoth_trains", SDT_BOOL, (void*)true, &_patches.mammoth_trains, NULL},
|
||||
{"join_stations", SDT_BOOL, (void*)true, &_patches.join_stations, NULL},
|
||||
{"station_spread", SDT_UINT8, (void*)12, &_patches.station_spread, NULL},
|
||||
{"full_load_any", SDT_BOOL, (void*)true, &_patches.full_load_any, NULL},
|
||||
|
||||
{"inflation", SDT_BOOL, (void*)true, &_patches.inflation, NULL},
|
||||
{"selectgoods", SDT_BOOL, (void*)true, &_patches.selectgoods, NULL},
|
||||
{"longbridges", SDT_BOOL, (void*)true, &_patches.longbridges, NULL},
|
||||
{"gotodepot", SDT_BOOL, (void*)true, &_patches.gotodepot, NULL},
|
||||
|
||||
{"build_rawmaterial_ind", SDT_BOOL, (void*)false, &_patches.build_rawmaterial_ind,NULL},
|
||||
{"multiple_industry_per_town",SDT_BOOL, (void*)false, &_patches.multiple_industry_per_town, NULL},
|
||||
{"same_industry_close", SDT_BOOL, (void*)false, &_patches.same_industry_close, NULL},
|
||||
|
||||
{"signal_side", SDT_BOOL, (void*)true, &_patches.signal_side, NULL},
|
||||
|
||||
{"new_nonstop", SDT_BOOL, (void*)false, &_patches.new_nonstop, NULL},
|
||||
{"roadveh_queue", SDT_BOOL, (void*)true, &_patches.roadveh_queue, NULL},
|
||||
{"window_snap_radius", SDT_UINT8, (void*)10, &_patches.window_snap_radius, NULL},
|
||||
|
||||
{"snow_line_height", SDT_UINT8, (void*)7, &_patches.snow_line_height, NULL},
|
||||
|
||||
{"bribe", SDT_BOOL, (void*)true, &_patches.bribe, NULL},
|
||||
{"new_depot_finding", SDT_BOOL, (void*)false, &_patches.new_depot_finding, NULL},
|
||||
|
||||
{"nonuniform_stations", SDT_BOOL, (void*)true, &_patches.nonuniform_stations, NULL},
|
||||
{"always_small_airport",SDT_BOOL, (void*)false, &_patches.always_small_airport, NULL},
|
||||
{"realistic_acceleration",SDT_BOOL, (void*)false, &_patches.realistic_acceleration, NULL},
|
||||
|
||||
{"max_trains", SDT_UINT8, (void*)80, &_patches.max_trains, NULL},
|
||||
{"max_roadveh", SDT_UINT8, (void*)80, &_patches.max_roadveh, NULL},
|
||||
{"max_aircraft", SDT_UINT8, (void*)40, &_patches.max_aircraft, NULL},
|
||||
{"max_ships", SDT_UINT8, (void*)50, &_patches.max_ships, NULL},
|
||||
|
||||
{"servint_ispercent", SDT_BOOL, (void*)false, &_patches.servint_ispercent, NULL},
|
||||
{"servint_trains", SDT_UINT16, (void*)150, &_patches.servint_trains, NULL},
|
||||
{"servint_roadveh", SDT_UINT16, (void*)150, &_patches.servint_roadveh, NULL},
|
||||
{"servint_ships", SDT_UINT16, (void*)360, &_patches.servint_ships, NULL},
|
||||
{"servint_aircraft", SDT_UINT16, (void*)100, &_patches.servint_aircraft, NULL},
|
||||
|
||||
{"autorenew", SDT_BOOL, (void*)false, &_patches.autorenew, NULL},
|
||||
{"autorenew_months", SDT_INT16, (void*)-6, &_patches.autorenew_months, NULL},
|
||||
{"autorenew_money", SDT_INT32, (void*)100000,&_patches.autorenew_money, NULL},
|
||||
|
||||
{"new_pathfinding", SDT_BOOL, (void*)true, &_patches.new_pathfinding, NULL},
|
||||
{"pf_maxlength", SDT_UINT16, (void*)512, &_patches.pf_maxlength, NULL},
|
||||
{"pf_maxdepth", SDT_UINT8, (void*)16, &_patches.pf_maxdepth, NULL},
|
||||
|
||||
|
||||
{"ai_disable_veh_train",SDT_BOOL, (void*)false, &_patches.ai_disable_veh_train, NULL},
|
||||
{"ai_disable_veh_roadveh",SDT_BOOL, (void*)false, &_patches.ai_disable_veh_roadveh, NULL},
|
||||
{"ai_disable_veh_aircraft",SDT_BOOL,(void*)false, &_patches.ai_disable_veh_aircraft,NULL},
|
||||
{"ai_disable_veh_ship", SDT_BOOL, (void*)false, &_patches.ai_disable_veh_ship, NULL},
|
||||
{"starting_date", SDT_UINT32, (void*)1950, &_patches.starting_date, NULL},
|
||||
|
||||
{"colored_news_date", SDT_UINT32, (void*)2000, &_patches.colored_news_date, NULL},
|
||||
|
||||
{"extra_dynamite", SDT_BOOL, (void*)false, &_patches.extra_dynamite, NULL},
|
||||
|
||||
{"never_expire_vehicles",SDT_BOOL, (void*)false, &_patches.never_expire_vehicles,NULL},
|
||||
{"extend_vehicle_life", SDT_UINT8, (void*)0, &_patches.extend_vehicle_life, NULL},
|
||||
|
||||
{"auto_euro", SDT_BOOL, (void*)true, &_patches.auto_euro, NULL},
|
||||
|
||||
{"serviceathelipad", SDT_BOOL, (void*)true, &_patches.serviceathelipad, NULL},
|
||||
{"smooth_economy", SDT_BOOL, (void*)true, &_patches.smooth_economy, NULL},
|
||||
{"dist_local_authority",SDT_UINT8, (void*)20, &_patches.dist_local_authority, NULL},
|
||||
|
||||
{"wait_oneway_signal", SDT_UINT8, (void*)15, &_patches.wait_oneway_signal, NULL},
|
||||
{"wait_twoway_signal", SDT_UINT8, (void*)41, &_patches.wait_twoway_signal, NULL},
|
||||
|
||||
{"ainew_active", SDT_BOOL, (void*)false, &_patches.ainew_active, NULL},
|
||||
|
||||
{NULL, 0, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
typedef void SettingDescProc(IniFile *ini, const SettingDesc *desc, const void *grpname);
|
||||
|
||||
static void HandleSettingDescs(IniFile *ini, SettingDescProc *proc)
|
||||
{
|
||||
proc(ini, misc_settings, "misc", NULL);
|
||||
proc(ini, win32_settings, "win32", NULL);
|
||||
proc(ini, network_settings, "network", NULL);
|
||||
proc(ini, music_settings, "music", &msf);
|
||||
proc(ini, gameopt_settings, "gameopt", &_new_opt);
|
||||
proc(ini, patch_settings, "patches", &_patches);
|
||||
proc(ini, misc_settings, "misc");
|
||||
proc(ini, win32_settings, "win32");
|
||||
#ifdef ENABLE_NETWORK
|
||||
proc(ini, network_settings, "network");
|
||||
#endif /* ENABLE_NETWORK */
|
||||
proc(ini, music_settings, "music");
|
||||
proc(ini, gameopt_settings, "gameopt");
|
||||
proc(ini, patch_settings, "patches");
|
||||
proc(ini, patch_player_settings, "patches");
|
||||
|
||||
proc(ini, debug_settings, "debug", NULL);
|
||||
proc(ini, debug_settings, "debug");
|
||||
}
|
||||
|
||||
static void LoadGrfSettings(IniFile *ini)
|
||||
|
|
|
@ -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 */
|
118
settings_gui.c
118
settings_gui.c
|
@ -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;
|
||||
|
|
9
stdafx.h
9
stdafx.h
|
@ -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)
|
||||
|
|
181
texteff.c
181
texteff.c
|
@ -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;
|
||||
|
||||
|
|
296
ttd.c
296
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);
|
||||
#ifdef ENABLE_NETWORK
|
||||
// Check for UDP stuff
|
||||
NetworkUDPGameLoop();
|
||||
|
||||
if (_networking_sync) {
|
||||
// client: make sure client's time is synched to the server by running frames quickly up to where the server is.
|
||||
if (!_networking_server) {
|
||||
while (_frame_counter < _frame_counter_srv) {
|
||||
NetworkCoreLoop(true);
|
||||
StateGameLoop();
|
||||
NetworkProcessCommands(); // need to process queue to make sure that packets get executed.
|
||||
NetworkCoreLoop(false);
|
||||
}
|
||||
// client: don't exceed the max count told by the server
|
||||
if (_frame_counter < _frame_counter_max) {
|
||||
StateGameLoop();
|
||||
NetworkProcessCommands();
|
||||
}
|
||||
// client: send the ready packet
|
||||
NetworkSendReadyPacket();
|
||||
} else {
|
||||
// server: work on to the frame max
|
||||
if (_frame_counter < _frame_counter_max) {
|
||||
StateGameLoop();
|
||||
NetworkProcessCommands(); // to check if we got any new commands belonging to the current frame before we increase it.
|
||||
NetworkSendFrameSyncPackets();
|
||||
}
|
||||
// server: wait until all clients were ready for going on
|
||||
if (_frame_counter == _frame_counter_max) {
|
||||
if (NetworkCheckClientReady()) NetworkSendSyncPackets();
|
||||
}
|
||||
}
|
||||
if (_networking) {
|
||||
// Multiplayer
|
||||
NetworkGameLoop();
|
||||
} else {
|
||||
// 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
|
||||
|
|
4
ttd.h
4
ttd.h
|
@ -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,
|
||||
};
|
||||
|
||||
|
||||
|
|
39
ttd.vcproj
39
ttd.vcproj
|
@ -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>
|
||||
|
|
1
unix.c
1
unix.c
|
@ -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}
|
||||
};
|
||||
|
||||
|
|
28
variables.h
28
variables.h
|
@ -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];
|
||||
|
|
12
vehicle.c
12
vehicle.c
|
@ -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;
|
||||
|
||||
|
|
25
win32.c
25
win32.c
|
@ -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;
|
||||
}
|
||||
|
|
40
window.c
40
window.c
|
@ -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();
|
||||
}
|
||||
|
|
8
window.h
8
window.h
|
@ -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…
Reference in New Issue