(svn r942) -Merged branch/network back into the trunk
parent
0434287ef8
commit
b8f6d41418
@ -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
|
@ -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 */
|
@ -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
|
||||
|
@ -1,36 +1,175 @@
|
||||
#ifndef NETWORK_H
|
||||
#define NETWORK_H
|
||||
|
||||
#include "network_core.h"
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
// If this line is enable, every frame will have a sync test
|
||||
// this is not needed in normal games. Normal is like 1 sync in 100
|
||||
// frames. You can enable this if you have a lot of desyncs on a certain
|
||||
// game.
|
||||
// Remember: both client and server have to be compiled with this
|
||||
// option enabled to make it to work. If one of the two has it disabled
|
||||
// nothing will happen.
|
||||
//#define ENABLE_NETWORK_SYNC_EVERY_FRAME
|
||||
|
||||
// In theory sending 1 of the 2 seeds is enough to check for desyncs
|
||||
// so in theory, this next define can be left off.
|
||||
//#define NETWORK_SEND_DOUBLE_SEED
|
||||
|
||||
// How many clients can we have? Like.. MAX_PLAYERS - 1 is the amount of
|
||||
// players that can really play.. so.. a max of 4 spectators.. gives us..
|
||||
// MAX_PLAYERS + 3
|
||||
#define MAX_CLIENTS (MAX_PLAYERS + 3)
|
||||
|
||||
|
||||
// Do not change this next line. It should _ALWAYS_ be MAX_CLIENTS + 1
|
||||
#define MAX_CLIENT_INFO (MAX_CLIENTS + 1)
|
||||
|
||||
#define NETWORK_DISCOVER_PORT 3978
|
||||
#define NETWORK_DEFAULT_PORT 3979
|
||||
|
||||
#define MAX_INTERFACES 9
|
||||
|
||||
|
||||
// How many vehicle/station types we put over the network
|
||||
#define NETWORK_VEHICLE_TYPES 5
|
||||
#define NETWORK_STATION_TYPES 5
|
||||
|
||||
#define NETWORK_NAME_LENGTH 80
|
||||
#define NETWORK_HOSTNAME_LENGTH 80
|
||||
#define NETWORK_REVISION_LENGTH 10
|
||||
#define NETWORK_PASSWORD_LENGTH 20
|
||||
#define NETWORK_PLAYERS_LENGTH 200
|
||||
|
||||
// This is the struct used by both client and server
|
||||
// some fields will be empty on the client (like game_password) by default
|
||||
// and only filled with data a player enters.
|
||||
typedef struct NetworkGameInfo {
|
||||
char server_name[40]; // name of the game
|
||||
char server_revision[8]; // server game version
|
||||
byte server_lang; // langid
|
||||
byte players_max; // max players allowed on server
|
||||
byte players_on; // current count of players on server
|
||||
uint16 game_date; // current date
|
||||
char game_password[10]; // should fit ... 10 chars
|
||||
char map_name[40]; // map which is played ["random" for a randomized map]
|
||||
uint map_width; // map width / 8
|
||||
uint map_height; // map height / 8
|
||||
byte map_set; // graphical set
|
||||
char server_name[NETWORK_NAME_LENGTH]; // Server name
|
||||
char hostname[NETWORK_HOSTNAME_LENGTH]; // Hostname of the server (if any)
|
||||
char server_revision[NETWORK_REVISION_LENGTH]; // The SVN version number the server is using (e.g.: 'r304')
|
||||
// It even shows a SVN version in release-version, so
|
||||
// it is easy to compare if a server is of the correct version
|
||||
byte server_lang; // Language of the server (we should make a nice table for this)
|
||||
byte use_password; // Is set to != 0 if it uses a password
|
||||
char server_password[NETWORK_PASSWORD_LENGTH]; // On the server: the game password, on the client: != "" if server has password
|
||||
byte clients_max; // Max clients allowed on server
|
||||
byte clients_on; // Current count of clients on server
|
||||
byte spectators_on; // How many spectators do we have?
|
||||
uint16 game_date; // Current date
|
||||
uint16 start_date; // When the game started
|
||||
char map_name[NETWORK_NAME_LENGTH]; // Map which is played ["random" for a randomized map]
|
||||
uint16 map_width; // Map width
|
||||
uint16 map_height; // Map height
|
||||
byte map_set; // Graphical set
|
||||
bool dedicated; // Is this a dedicated server?
|
||||
} NetworkGameInfo;
|
||||
|
||||
//typedef struct NetworkGameList;
|
||||
typedef struct NetworkPlayerInfo {
|
||||
char company_name[NETWORK_NAME_LENGTH]; // Company name
|
||||
char password[NETWORK_PASSWORD_LENGTH]; // The password for the player
|
||||
byte inaugurated_year; // What year the company started in
|
||||
int64 company_value; // The company value
|
||||
int64 money; // The amount of money the company has
|
||||
int64 income; // How much did the company earned last year
|
||||
uint16 performance; // What was his performance last month?
|
||||
uint16 num_vehicle[NETWORK_VEHICLE_TYPES]; // How many vehicles are there of this type?
|
||||
uint16 num_station[NETWORK_STATION_TYPES]; // How many stations are there of this type?
|
||||
char players[NETWORK_PLAYERS_LENGTH]; // The players that control this company (Name1, name2, ..)
|
||||
} NetworkPlayerInfo;
|
||||
|
||||
typedef struct NetworkClientInfo {
|
||||
uint16 client_index; // Index of the client (same as ClientState->index)
|
||||
char client_name[NETWORK_NAME_LENGTH]; // Name of the client
|
||||
byte client_lang; // The language of the client
|
||||
byte client_playas; // As which player is this client playing
|
||||
uint32 client_ip; // IP-address of the client (so he can be banned)
|
||||
uint16 join_date; // Gamedate the player has joined
|
||||
} NetworkClientInfo;
|
||||
|
||||
typedef struct NetworkGameList {
|
||||
NetworkGameInfo item;
|
||||
NetworkGameInfo info;
|
||||
uint32 ip;
|
||||
uint16 port;
|
||||
struct NetworkGameList * _next;
|
||||
bool online; // False if the server did not respond (default status)
|
||||
struct NetworkGameList *next;
|
||||
} NetworkGameList;
|
||||
|
||||
enum {
|
||||
NET_EVENT_SUBSIDY = 0,
|
||||
};
|
||||
typedef enum {
|
||||
NETWORK_JOIN_STATUS_CONNECTING,
|
||||
NETWORK_JOIN_STATUS_AUTHORIZING,
|
||||
NETWORK_JOIN_STATUS_WAITING,
|
||||
NETWORK_JOIN_STATUS_DOWNLOADING,
|
||||
NETWORK_JOIN_STATUS_PROCESSING,
|
||||
|
||||
NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO,
|
||||
} NetworkJoinStatus;
|
||||
|
||||
// language ids for server_lang and client_lang
|
||||
typedef enum {
|
||||
NETLANG_ANY = 0,
|
||||
NETLANG_ENGLISH = 1,
|
||||
NETLANG_GERMAN = 2,
|
||||
NETLANG_FRENCH = 3,
|
||||
} NetworkLanguage;
|
||||
|
||||
VARDEF NetworkGameList *_network_game_list;
|
||||
|
||||
VARDEF NetworkGameInfo _network_game_info;
|
||||
VARDEF NetworkPlayerInfo _network_player_info[MAX_PLAYERS];
|
||||
VARDEF NetworkClientInfo _network_client_info[MAX_CLIENT_INFO];
|
||||
|
||||
VARDEF char _network_player_name[NETWORK_NAME_LENGTH];
|
||||
VARDEF char _network_default_ip[NETWORK_HOSTNAME_LENGTH];
|
||||
|
||||
VARDEF uint16 _network_own_client_index;
|
||||
|
||||
VARDEF uint32 _frame_counter_server; // The frame_counter of the server, if in network-mode
|
||||
VARDEF uint32 _frame_counter_max; // To where we may go with our clients
|
||||
|
||||
// networking settings
|
||||
VARDEF uint32 _network_ip_list[MAX_INTERFACES + 1]; // Network IPs
|
||||
VARDEF uint16 _network_game_count;
|
||||
|
||||
VARDEF uint16 _network_lobby_company_count;
|
||||
|
||||
VARDEF uint _network_client_port;
|
||||
VARDEF uint _network_server_port;
|
||||
VARDEF bool _is_network_server; // Does this client wants to be a network-server?
|
||||
VARDEF char _network_server_name[NETWORK_NAME_LENGTH];
|
||||
|
||||
VARDEF uint16 _network_sync_freq;
|
||||
VARDEF uint8 _network_frame_freq;
|
||||
|
||||
VARDEF uint32 _sync_seed_1, _sync_seed_2;
|
||||
VARDEF uint32 _sync_frame;
|
||||
VARDEF bool _network_first_time;
|
||||
// Vars needed for the join-GUI
|
||||
VARDEF NetworkJoinStatus _network_join_status;
|
||||
VARDEF uint8 _network_join_waiting;
|
||||
VARDEF uint16 _network_join_kbytes;
|
||||
VARDEF uint16 _network_join_kbytes_total;
|
||||
|
||||
VARDEF char _network_last_host[NETWORK_HOSTNAME_LENGTH];
|
||||
VARDEF short _network_last_port;
|
||||
VARDEF uint32 _network_last_host_ip;
|
||||
VARDEF uint8 _network_reconnect;
|
||||
|
||||
VARDEF bool _network_udp_server;
|
||||
VARDEF uint16 _network_udp_broadcast;
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
|
||||
NetworkGameInfo _network_game;
|
||||
NetworkGameList * _network_game_list;
|
||||
// Those variables must always be registered!
|
||||
VARDEF bool _networking;
|
||||
VARDEF bool _network_available; // is network mode available?
|
||||
VARDEF bool _network_server; // network-server is active
|
||||
VARDEF bool _network_dedicated; // are we a dedicated server?
|
||||
VARDEF byte _network_playas; // an id to play as..
|
||||
|
||||
void ParseConnectionString(const byte **player, const byte **port, byte *connection_string);
|
||||
void NetworkUpdateClientInfo(uint16 client_index);
|
||||
|
||||
#endif /* NETWORK_H */
|
||||
|
@ -0,0 +1,808 @@
|
||||
#include "stdafx.h"
|
||||
#include "network_data.h"
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
#include "table/strings.h"
|
||||
#include "network_client.h"
|
||||
#include "network_gamelist.h"
|
||||
#include "command.h"
|
||||
#include "gfx.h"
|
||||
#include "window.h"
|
||||
#include "settings.h"
|
||||
|
||||
|
||||
// This file handles all the client-commands
|
||||
|
||||
|
||||
// So we don't make too much typos ;)
|
||||
#define MY_CLIENT DEREF_CLIENT(0)
|
||||
|
||||
static uint32 last_ack_frame;
|
||||
|
||||
void NetworkRecvPatchSettings(Packet *p);
|
||||
|
||||
// **********
|
||||
// Sending functions
|
||||
// DEF_CLIENT_SEND_COMMAND has no parameters
|
||||
// **********
|
||||
|
||||
DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_COMPANY_INFO)
|
||||
{
|
||||
//
|
||||
// Packet: CLIENT_COMPANY_INFO
|
||||
// Function: Request company-info (in detail)
|
||||
// Data:
|
||||
// <none>
|
||||
//
|
||||
Packet *p;
|
||||
_network_join_status = NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO;
|
||||
InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0);
|
||||
|
||||
p = NetworkSend_Init(PACKET_CLIENT_COMPANY_INFO);
|
||||
NetworkSend_Packet(p, MY_CLIENT);
|
||||
}
|
||||
|
||||
DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_JOIN)
|
||||
{
|
||||
//
|
||||
// Packet: CLIENT_JOIN
|
||||
// Function: Try to join the server
|
||||
// Data:
|
||||
// String: OpenTTD Revision (norev000 if no revision)
|
||||
// String: Player Name (max NETWORK_NAME_LENGTH)
|
||||
// uint8: Play as Player id (1..MAX_PLAYERS)
|
||||
// uint8: Language ID
|
||||
//
|
||||
|
||||
#if defined(WITH_REV)
|
||||
extern char _openttd_revision[];
|
||||
#else
|
||||
const char _openttd_revision[] = "norev000";
|
||||
#endif
|
||||
Packet *p;
|
||||
_network_join_status = NETWORK_JOIN_STATUS_AUTHORIZING;
|
||||
InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0);
|
||||
|
||||
p = NetworkSend_Init(PACKET_CLIENT_JOIN);
|
||||
NetworkSend_string(p, _openttd_revision);
|
||||
NetworkSend_string(p, _network_player_name); // Player name
|
||||
NetworkSend_uint8(p, _network_playas); // Password
|
||||
NetworkSend_uint8(p, NETLANG_ANY); // Language
|
||||
NetworkSend_Packet(p, MY_CLIENT);
|
||||
}
|
||||
|
||||
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_PASSWORD)(NetworkPasswordType type, const char *password)
|
||||
{
|
||||
//
|
||||
// Packet: CLIENT_PASSWORD
|
||||
// Function: Send a password to the server to authorize
|
||||
// Data:
|
||||
// uint8: NetworkPasswordType
|
||||
// String: Password
|
||||
//
|
||||
Packet *p = NetworkSend_Init(PACKET_CLIENT_PASSWORD);
|
||||
NetworkSend_uint8(p, type);
|
||||
NetworkSend_string(p, password);
|
||||
NetworkSend_Packet(p, MY_CLIENT);
|
||||
}
|
||||
|
||||
DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_GETMAP)
|
||||
{
|
||||
//
|
||||
// Packet: CLIENT_GETMAP
|
||||
// Function: Request the map from the server
|
||||
// Data:
|
||||
// <none>
|
||||
//
|
||||
|
||||
Packet *p = NetworkSend_Init(PACKET_CLIENT_GETMAP);
|
||||
NetworkSend_Packet(p, MY_CLIENT);
|
||||
}
|
||||
|
||||
DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_MAP_OK)
|
||||
{
|
||||
//
|
||||
// Packet: CLIENT_MAP_OK
|
||||
// Function: Tell the server that we are done receiving/loading the map
|
||||
// Data:
|
||||
// <none>
|
||||
//
|
||||
|
||||
Packet *p = NetworkSend_Init(PACKET_CLIENT_MAP_OK);
|
||||
NetworkSend_Packet(p, MY_CLIENT);
|
||||
}
|
||||
|
||||
DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_ACK)
|
||||
{
|
||||
//
|
||||
// Packet: CLIENT_ACK
|
||||
// Function: Tell the server we are done with this frame
|
||||
// Data:
|
||||
// uint32: current FrameCounter of the client
|
||||
//
|
||||
|
||||
Packet *p = NetworkSend_Init(PACKET_CLIENT_ACK);
|
||||
|
||||
NetworkSend_uint32(p, _frame_counter);
|
||||
NetworkSend_Packet(p, MY_CLIENT);
|
||||
}
|
||||
|
||||
// Send a command packet to the server
|
||||
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_COMMAND)(CommandPacket *cp)
|
||||
{
|
||||
//
|
||||
// Packet: CLIENT_COMMAND
|
||||
// Function: Send a DoCommand to the Server
|
||||
// Data:
|
||||
// uint8: PlayerID (0..MAX_PLAYERS-1)
|
||||
// uint32: CommandID (see command.h)
|
||||
// uint32: P1 (free variables used in DoCommand)
|
||||
// uint32: P2
|
||||
// uint32: Tile
|
||||
// uint32: decode_params
|
||||
// 10 times the last one (lengthof(cp->dp))
|
||||
// uint8: CallBackID (see callback_table.c)
|
||||
//
|
||||
|
||||
int i;
|
||||
Packet *p = NetworkSend_Init(PACKET_CLIENT_COMMAND);
|
||||
|
||||
NetworkSend_uint8(p, cp->player);
|
||||
NetworkSend_uint32(p, cp->cmd);
|
||||
NetworkSend_uint32(p, cp->p1);
|
||||
NetworkSend_uint32(p, cp->p2);
|
||||
NetworkSend_uint32(p, (uint32)cp->tile);
|
||||
for (i = 0; i < lengthof(cp->dp); i++) {
|
||||
NetworkSend_uint32(p, cp->dp[i]);
|
||||
}
|
||||
NetworkSend_uint8(p, cp->callback);
|
||||
|
||||
NetworkSend_Packet(p, MY_CLIENT);
|
||||
}
|
||||
|
||||
// Send a chat-packet over the network
|
||||
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_CHAT)(NetworkAction action, DestType desttype, int dest, const char *msg)
|
||||
{
|
||||
//
|
||||
// Packet: CLIENT_CHAT
|
||||
// Function: Send a chat-packet to the serve
|
||||
// Data:
|
||||
// uint8: ActionID (see network_data.h, NetworkAction)
|
||||
// uint8: Destination Type (see network_data.h, DestType);
|
||||
// uint8: Destination Player (1..MAX_PLAYERS)
|
||||
// String: Message (max MAX_TEXT_MSG_LEN)
|
||||
//
|
||||
|
||||
Packet *p = NetworkSend_Init(PACKET_CLIENT_CHAT);
|
||||
|
||||
NetworkSend_uint8(p, action);
|
||||
NetworkSend_uint8(p, desttype);
|
||||
NetworkSend_uint8(p, dest);
|
||||
NetworkSend_string(p, msg);
|
||||
NetworkSend_Packet(p, MY_CLIENT);
|
||||
}
|
||||
|
||||
// Send an error-packet over the network
|
||||
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_ERROR)(NetworkErrorCode errorno)
|
||||
{
|
||||
//
|
||||
// Packet: CLIENT_ERROR
|
||||
// Function: The client made an error and is quiting the game
|
||||
// Data:
|
||||
// uint8: ErrorID (see network_data.h, NetworkErrorCode)
|
||||
//
|
||||
Packet *p = NetworkSend_Init(PACKET_CLIENT_ERROR);
|
||||
|
||||
NetworkSend_uint8(p, errorno);
|
||||
NetworkSend_Packet(p, MY_CLIENT);
|
||||
}
|
||||
|
||||
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_SET_PASSWORD)(const char *password)
|
||||
{
|
||||
//
|
||||
// Packet: PACKET_CLIENT_SET_PASSWORD
|
||||
// Function: Set the password for the clients current company
|
||||
// Data:
|
||||
// String: Password
|
||||
//
|
||||
Packet *p = NetworkSend_Init(PACKET_CLIENT_SET_PASSWORD);
|
||||
|
||||
NetworkSend_string(p, password);
|
||||
NetworkSend_Packet(p, MY_CLIENT);
|
||||
}
|
||||
|
||||
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_SET_NAME)(const char *name)
|
||||
{
|
||||
//
|
||||
// Packet: PACKET_CLIENT_SET_NAME
|
||||
// Function: Gives the player a new name
|
||||
// Data:
|
||||
// String: Name
|
||||
//
|
||||
Packet *p = NetworkSend_Init(PACKET_CLIENT_SET_NAME);
|
||||
|
||||
NetworkSend_string(p, name);
|
||||
NetworkSend_Packet(p, MY_CLIENT);
|
||||
}
|
||||
|
||||
// Send an quit-packet over the network
|
||||
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_QUIT)(const char *leavemsg)
|
||||
{
|
||||
//
|
||||
// Packet: CLIENT_QUIT
|
||||
// Function: The client is quiting the game
|
||||
// Data:
|
||||
// String: leave-message
|
||||
//
|
||||
Packet *p = NetworkSend_Init(PACKET_CLIENT_QUIT);
|
||||
|
||||
NetworkSend_string(p, leavemsg);
|
||||
NetworkSend_Packet(p, MY_CLIENT);
|
||||
}
|
||||
|
||||
|
||||
// **********
|
||||
// Receiving functions
|
||||
// DEF_CLIENT_RECEIVE_COMMAND has parameter: Packet *p
|
||||
// **********
|
||||
|
||||
extern bool SafeSaveOrLoad(const char *filename, int mode, int newgm);
|
||||
|
||||
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_FULL)
|
||||
{
|
||||
// We try to join a server which is full
|
||||
_switch_mode_errorstr = STR_NETWORK_ERR_SERVER_FULL;
|
||||
DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
|
||||
|
||||
return NETWORK_RECV_STATUS_SERVER_FULL;
|
||||
}
|
||||
|
||||
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_COMPANY_INFO)
|
||||
{
|
||||
byte company_info_version;
|
||||
int i;
|
||||
|
||||
company_info_version = NetworkRecv_uint8(p);
|
||||
|
||||
if (company_info_version == 1) {
|
||||
byte total;
|
||||
byte current;
|
||||
|
||||
total = NetworkRecv_uint8(p);
|
||||
_network_lobby_company_count = total;
|
||||
|
||||
// There is no data at all..
|
||||
if (total == 0)
|
||||
return NETWORK_RECV_STATUS_CLOSE_QUERY;
|
||||
|
||||
current = NetworkRecv_uint8(p) - 1;
|
||||
if (current >= MAX_PLAYERS)
|
||||
return NETWORK_RECV_STATUS_CLOSE_QUERY;
|
||||
|
||||
NetworkRecv_string(p, _network_player_info[current].company_name, sizeof(_network_player_info[current].company_name));
|
||||
_network_player_info[current].inaugurated_year = NetworkRecv_uint8(p);
|
||||
_network_player_info[current].company_value = NetworkRecv_uint64(p);
|
||||
_network_player_info[current].money = NetworkRecv_uint64(p);
|
||||
_network_player_info[current].income = NetworkRecv_uint64(p);
|
||||
_network_player_info[current].performance = NetworkRecv_uint16(p);
|
||||
for (i = 0; i < NETWORK_VEHICLE_TYPES; i++)
|
||||
_network_player_info[current].num_vehicle[i] = NetworkRecv_uint16(p);
|
||||
for (i = 0; i < NETWORK_STATION_TYPES; i++)
|
||||
_network_player_info[current].num_station[i] = NetworkRecv_uint16(p);
|
||||
|
||||
NetworkRecv_string(p, _network_player_info[current].players, sizeof(_network_player_info[current].players));
|
||||
|
||||
InvalidateWindow(WC_NETWORK_WINDOW, 0);
|
||||
|
||||
if (total == current + 1)
|
||||
// This was the last one
|
||||
return NETWORK_RECV_STATUS_CLOSE_QUERY;
|
||||
else
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
return NETWORK_RECV_STATUS_CLOSE_QUERY;
|
||||
}
|
||||
|
||||
// This packet contains info about the client (playas and name)
|
||||
// as client we save this in NetworkClientInfo, linked via 'index'
|
||||
// which is always an unique number on a server.
|
||||
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_CLIENT_INFO)
|
||||
{
|
||||
NetworkClientInfo *ci;
|
||||
uint16 index = NetworkRecv_uint16(p);
|
||||
|
||||
ci = NetworkFindClientInfoFromIndex(index);
|
||||
if (ci != NULL) {
|
||||
byte playas;
|
||||
char name[NETWORK_NAME_LENGTH];
|
||||
|
||||
playas = NetworkRecv_uint8(p);
|
||||
NetworkRecv_string(p, name, sizeof(name));
|
||||
|
||||
if (playas == ci->client_playas && strcmp(name, ci->client_name) != 0) {
|
||||
// Client name changed, display the change
|
||||
NetworkTextMessage(NETWORK_ACTION_NAME_CHANGE, 1, ci->client_name, name);
|
||||
} else if (playas != ci->client_playas) {
|
||||
// The player changed from client-player..
|
||||
// Do not display that for now
|
||||
}
|
||||
|
||||
ci->client_playas = playas;
|
||||
ttd_strlcpy(ci->client_name, name, sizeof(ci->client_name));
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
// We don't have this index yet, find an empty index, and put the data there
|
||||
ci = NetworkFindClientInfoFromIndex(NETWORK_EMPTY_INDEX);
|
||||
if (ci != NULL) {
|
||||
ci->client_index = index;
|
||||
ci->client_playas = NetworkRecv_uint8(p);
|
||||
NetworkRecv_string(p, ci->client_name, sizeof(ci->client_name));
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
// Here the program should never ever come.....
|
||||
return NETWORK_RECV_STATUS_MALFORMED_PACKET;
|
||||
}
|
||||
|
||||
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_ERROR)
|
||||
{
|
||||
NetworkErrorCode error = NetworkRecv_uint8(p);
|
||||
|
||||
if (error == NETWORK_ERROR_NOT_AUTHORIZED || error == NETWORK_ERROR_NOT_EXPECTED ||
|
||||
error == NETWORK_ERROR_PLAYER_MISMATCH) {
|
||||
// We made an error in the protocol, and our connection is closed.... :(
|
||||
_switch_mode_errorstr = STR_NETWORK_ERR_SERVER_ERROR;
|
||||
} else if (error == NETWORK_ERROR_WRONG_REVISION) {
|
||||
// Wrong revision :(
|
||||
_switch_mode_errorstr = STR_NETWORK_ERR_WRONG_REVISION;
|
||||
} else if (error == NETWORK_ERROR_WRONG_PASSWORD) {
|
||||
// Wrong password
|
||||
_switch_mode_errorstr = STR_NETWORK_ERR_WRONG_PASSWORD;
|
||||
} else if (error == NETWORK_ERROR_KICKED) {
|
||||
_switch_mode_errorstr = STR_NETWORK_ERR_KICKED;
|
||||
}
|
||||
|
||||
DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
|
||||
|
||||
return NETWORK_RECV_STATUS_SERVER_ERROR;
|
||||
}
|
||||
|
||||
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_NEED_PASSWORD)
|
||||
{
|
||||
NetworkPasswordType type;
|
||||
type = NetworkRecv_uint8(p);
|
||||
|
||||
if (type == NETWORK_GAME_PASSWORD) {
|
||||
ShowNetworkNeedGamePassword();
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
} else if (type == NETWORK_COMPANY_PASSWORD) {
|
||||
ShowNetworkNeedCompanyPassword();
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
return NETWORK_RECV_STATUS_MALFORMED_PACKET;
|
||||
}
|
||||
|
||||
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_WELCOME)
|
||||
{
|
||||
_network_own_client_index = NetworkRecv_uint16(p);
|
||||
|
||||
// Start receiving the map
|
||||
SEND_COMMAND(PACKET_CLIENT_GETMAP)();
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_WAIT)
|
||||
{
|
||||
_network_join_status = NETWORK_JOIN_STATUS_WAITING;
|
||||
_network_join_waiting = NetworkRecv_uint8(p);
|
||||
InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0);
|
||||
|
||||
// We are put on hold for receiving the map.. we need GUI for this ;)
|
||||
DEBUG(net, 1)("[NET] The server is currently busy sending the map to someone else.. please hold..." );
|
||||
DEBUG(net, 1)("[NET] There are %d clients in front of you", _network_join_waiting);
|
||||
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_MAP)
|
||||
{
|
||||
static char filename[256];
|
||||
static FILE *file_pointer;
|
||||
|
||||
byte maptype;
|
||||
|
||||
maptype = NetworkRecv_uint8(p);
|
||||
|
||||
// First packet, init some stuff
|
||||
if (maptype == MAP_PACKET_START) {
|
||||
// The name for the temp-map
|
||||
sprintf(filename, "%s%snetwork_client.tmp", _path.autosave_dir, PATHSEP);
|
||||
|
||||
file_pointer = fopen(filename, "wb");
|
||||
if (file_pointer == NULL) {
|
||||
_switch_mode_errorstr = STR_NETWORK_ERR_SAVEGAMEERROR;
|
||||
return NETWORK_RECV_STATUS_SAVEGAME;
|
||||
}
|
||||
|
||||
_frame_counter = _frame_counter_server = _frame_counter_max = NetworkRecv_uint32(p);
|
||||
|
||||
_network_join_status = NETWORK_JOIN_STATUS_DOWNLOADING;
|
||||
_network_join_kbytes = 0;
|
||||
_network_join_kbytes_total = NetworkRecv_uint32(p) / 1024;
|
||||
InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0);
|
||||
|
||||
// The first packet does not contain any more data
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
if (maptype == MAP_PACKET_NORMAL) {
|
||||
// We are still receiving data, put it to the file
|
||||
fwrite(p->buffer + p->pos, 1, p->size - p->pos, file_pointer);
|
||||
|
||||
_network_join_kbytes = ftell(file_pointer) / 1024;
|
||||
InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0);
|
||||
}
|
||||
|
||||
if (maptype == MAP_PACKET_PATCH) {
|
||||
NetworkRecvPatchSettings(p);
|
||||
}
|
||||
|
||||
// Check if this was the last packet
|
||||
if (maptype == MAP_PACKET_END) {
|
||||
// We also get, very nice, the player_seeds in this packet
|
||||
int i;
|
||||
for (i = 0; i < MAX_PLAYERS; i++) {
|
||||
_player_seeds[i][0] = NetworkRecv_uint32(p);
|
||||
_player_seeds[i][1] = NetworkRecv_uint32(p);
|
||||
}
|
||||
|
||||
fclose(file_pointer);
|
||||
|
||||
_network_join_status = NETWORK_JOIN_STATUS_PROCESSING;
|
||||
InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0);
|
||||
|
||||
// The map is done downloading, load it
|
||||
// Load the map
|
||||
if (!SafeSaveOrLoad(filename, SL_LOAD, GM_NORMAL)) {
|
||||
DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
|
||||
_switch_mode_errorstr = STR_NETWORK_ERR_SAVEGAMEERROR;
|
||||
return NETWORK_RECV_STATUS_SAVEGAME;
|
||||
}
|
||||
_opt_mod_ptr = &_opt;
|
||||
|
||||
DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0);
|
||||
|
||||
// Say we received the map and loaded it correctly!
|
||||
SEND_COMMAND(PACKET_CLIENT_MAP_OK)();
|
||||
|
||||
if (_network_playas == 0 || _network_playas > MAX_PLAYERS ||
|
||||
!DEREF_PLAYER(_network_playas - 1)->is_active) {
|
||||
|
||||
if (_network_playas == OWNER_SPECTATOR) {
|
||||
// The client wants to be a spectator..
|
||||
_local_player = OWNER_SPECTATOR;
|
||||
} else {
|
||||
// send a command to make a new player
|
||||
_local_player = 0;
|
||||
NetworkSend_Command(0, 0, 0, CMD_PLAYER_CTRL, NULL);
|
||||
_local_player = OWNER_SPECTATOR;
|
||||
}
|
||||
} else {
|
||||
// take control over an existing company
|
||||
_local_player = _network_playas - 1;
|
||||
}
|
||||
|
||||
// Remeber the player
|
||||
if (_local_player != OWNER_SPECTATOR)
|
||||
_network_playas = _local_player + 1;
|
||||
else
|
||||
_network_playas = OWNER_SPECTATOR;
|
||||
}
|
||||
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_FRAME)
|
||||
{
|
||||
_frame_counter_server = NetworkRecv_uint32(p);
|
||||
_frame_counter_max = NetworkRecv_uint32(p);
|
||||
#ifdef ENABLE_NETWORK_SYNC_EVERY_FRAME
|
||||
// Test if the server supports this option
|
||||
// and if we are at the frame the server is
|
||||
if (p->pos < p->size) {
|
||||
_sync_frame = _frame_counter_server;
|
||||
_sync_seed_1 = NetworkRecv_uint32(p);
|
||||
#ifdef NETWORK_SEND_DOUBLE_SEED
|
||||
_sync_seed_2 = NetworkRecv_uint32(p);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
DEBUG(net, 7)("[NET] Received FRAME %d",_frame_counter_server);
|
||||
|
||||
// Let the server know that we received this frame correctly
|
||||
// We do this only once per day, to save some bandwidth ;)
|
||||
if (!_network_first_time && last_ack_frame < _frame_counter) {
|
||||
last_ack_frame = _frame_counter + DAY_TICKS;
|
||||
DEBUG(net,6)("[NET] Sent ACK at %d", _frame_counter);
|
||||
SEND_COMMAND(PACKET_CLIENT_ACK)();
|
||||
}
|
||||
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_SYNC)
|
||||
{
|
||||
_sync_frame = NetworkRecv_uint32(p);
|
||||
_sync_seed_1 = NetworkRecv_uint32(p);
|
||||
#ifdef NETWORK_SEND_DOUBLE_SEED
|
||||
_sync_seed_2 = NetworkRecv_uint32(p);
|
||||
#endif
|
||||
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_COMMAND)
|
||||
{
|
||||
int i;
|
||||
CommandPacket *cp = malloc(sizeof(CommandPacket));
|
||||
cp->player = NetworkRecv_uint8(p);
|
||||
cp->cmd = NetworkRecv_uint32(p);
|
||||
cp->p1 = NetworkRecv_uint32(p);
|
||||
cp->p2 = NetworkRecv_uint32(p);
|
||||
cp->tile = NetworkRecv_uint32(p);
|
||||
for (i = 0; i < lengthof(cp->dp); i++)
|
||||
cp->dp[i] = NetworkRecv_uint32(p);
|
||||
cp->callback = NetworkRecv_uint8(p);
|
||||
cp->frame = NetworkRecv_uint32(p);
|
||||
cp->next = NULL;
|
||||
|
||||
// The server did send us this command..
|
||||
// queue it in our own queue, so we can handle it in the upcoming frame!
|
||||
|
||||
if (_local_command_queue == NULL) {
|
||||
_local_command_queue = cp;
|
||||
} else {
|
||||
// Find last packet
|
||||
CommandPacket *c = _local_command_queue;
|
||||
while (c->next != NULL) c = c->next;
|
||||
c->next = cp;
|
||||
}
|
||||
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_CHAT)
|
||||
{
|
||||
NetworkAction action = NetworkRecv_uint8(p);
|
||||
char msg[MAX_TEXT_MSG_LEN];
|
||||
NetworkClientInfo *ci, *ci_to;
|
||||
uint16 index;
|
||||
char name[NETWORK_NAME_LENGTH];
|
||||
|
||||
index = NetworkRecv_uint16(p);
|
||||
NetworkRecv_string(p, msg, MAX_TEXT_MSG_LEN);
|
||||
|
||||
ci_to = NetworkFindClientInfoFromIndex(index);
|
||||
if (ci_to == NULL) return NETWORK_RECV_STATUS_OKAY;
|
||||
|
||||
if (action == NETWORK_ACTION_CHAT_TO_CLIENT) {
|
||||
snprintf(name, 80, "%s", ci_to->client_name);
|
||||
ci = NetworkFindClientInfoFromIndex(_network_own_client_index);
|
||||
} else if (action == NETWORK_ACTION_CHAT_TO_PLAYER) {
|
||||
GetString(name, DEREF_PLAYER(ci_to->client_playas-1)->name_1);
|
||||
ci = NetworkFindClientInfoFromIndex(_network_own_client_index);
|
||||
} else {
|
||||
snprintf(name, 80, "%s", ci_to->client_name);
|
||||
ci = ci_to;
|
||||
}
|
||||
|
||||
if (ci != NULL)
|
||||
NetworkTextMessage(action, GetDrawStringPlayerColor(ci->client_playas-1), name, "%s", msg);
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_ERROR_QUIT)
|
||||
{
|
||||
int errorno;
|
||||
char str1[100], str2[100];
|
||||
uint16 index;
|
||||
NetworkClientInfo *ci;
|
||||
|
||||
index = NetworkRecv_uint16(p);
|
||||
errorno = NetworkRecv_uint8(p);
|
||||
|
||||
GetString(str1, STR_NETWORK_ERR_LEFT);
|
||||
GetString(str2, STR_NETWORK_ERR_CLIENT_GENERAL + errorno);
|
||||
|
||||
ci = NetworkFindClientInfoFromIndex(index);
|
||||
if (ci != NULL) {
|
||||
NetworkTextMessage(NETWORK_ACTION_JOIN_LEAVE, 1, ci->client_name, "%s (%s)", str1, str2);
|
||||
|
||||
// The client is gone, give the NetworkClientInfo free
|
||||
ci->client_index = NETWORK_EMPTY_INDEX;
|
||||
}
|
||||
|
||||
InvalidateWindow(WC_CLIENT_LIST, 0);
|
||||
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_QUIT)
|
||||
{
|
||||
char str1[100], str2[100];
|
||||
uint16 index;
|
||||
NetworkClientInfo *ci;
|
||||
|
||||
index = NetworkRecv_uint16(p);
|
||||
NetworkRecv_string(p, str2, 100);
|
||||
|
||||
GetString(str1, STR_NETWORK_ERR_LEFT);
|
||||
|
||||
ci = NetworkFindClientInfoFromIndex(index);
|
||||
if (ci != NULL) {
|
||||
NetworkTextMessage(NETWORK_ACTION_JOIN_LEAVE, 1, ci->client_name, "%s (%s)", str1, str2);
|
||||
|
||||
// The client is gone, give the NetworkClientInfo free
|
||||
ci->client_index = NETWORK_EMPTY_INDEX;
|
||||
} else {
|
||||
DEBUG(net, 0)("[NET] Error - unknown client (%d) is leaving the game", index);
|
||||
}
|
||||
|
||||
InvalidateWindow(WC_CLIENT_LIST, 0);
|
||||
|
||||
// If we come here it means we could not locate the client.. strange :s
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_JOIN)
|
||||
{
|
||||
char str1[100];
|
||||
uint16 index;
|
||||
NetworkClientInfo *ci;
|
||||
|
||||
index = NetworkRecv_uint16(p);
|
||||
|
||||
GetString(str1, STR_NETWORK_CLIENT_JOINED);
|
||||
|
||||
ci = NetworkFindClientInfoFromIndex(index);
|
||||
if (ci != NULL) {
|
||||
NetworkTextMessage(NETWORK_ACTION_JOIN_LEAVE, 1, ci->client_name, "%s", str1);
|
||||
}
|
||||
|
||||
InvalidateWindow(WC_CLIENT_LIST, 0);
|
||||
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_SHUTDOWN)
|
||||
{
|
||||
_switch_mode_errorstr = STR_NETWORK_SERVER_SHUTDOWN;
|
||||
|
||||
return NETWORK_RECV_STATUS_SERVER_ERROR;
|
||||
}
|
||||
|
||||
DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_NEWGAME)
|
||||
{
|
||||
// To trottle the reconnects a bit, every clients waits
|
||||
// his _local_player value before reconnecting
|
||||
// OWNER_SPECTATOR is currently 255, so to avoid long wait periods
|
||||
// set the max to 10.
|
||||
_network_reconnect = min(_local_player + 1, 10);
|
||||
_switch_mode_errorstr = STR_NETWORK_SERVER_REBOOT;
|
||||
|
||||
return NETWORK_RECV_STATUS_SERVER_ERROR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// The layout for the receive-functions by the client
|
||||
typedef NetworkRecvStatus NetworkClientPacket(Packet *p);
|
||||
|
||||
// This array matches PacketType. At an incoming
|
||||
// packet it is matches against this array
|
||||
// and that way the right function to handle that
|
||||
// packet is found.
|
||||
static NetworkClientPacket* const _network_client_packet[] = {
|
||||
RECEIVE_COMMAND(PACKET_SERVER_FULL),
|
||||
NULL, /*PACKET_CLIENT_JOIN,*/
|
||||
RECEIVE_COMMAND(PACKET_SERVER_ERROR),
|
||||
NULL, /*PACKET_CLIENT_COMPANY_INFO,*/
|
||||
RECEIVE_COMMAND(PACKET_SERVER_COMPANY_INFO),
|
||||
RECEIVE_COMMAND(PACKET_SERVER_CLIENT_INFO),
|
||||
RECEIVE_COMMAND(PACKET_SERVER_NEED_PASSWORD),
|
||||
NULL, /*PACKET_CLIENT_PASSWORD,*/
|
||||
RECEIVE_COMMAND(PACKET_SERVER_WELCOME),
|
||||
NULL, /*PACKET_CLIENT_GETMAP,*/
|
||||
RECEIVE_COMMAND(PACKET_SERVER_WAIT),
|
||||
RECEIVE_COMMAND(PACKET_SERVER_MAP),
|
||||
NULL, /*PACKET_CLIENT_MAP_OK,*/
|
||||
RECEIVE_COMMAND(PACKET_SERVER_JOIN),
|
||||
RECEIVE_COMMAND(PACKET_SERVER_FRAME),
|
||||
RECEIVE_COMMAND(PACKET_SERVER_SYNC),
|
||||
NULL, /*PACKET_CLIENT_ACK,*/
|
||||
NULL, /*PACKET_CLIENT_COMMAND,*/
|
||||
RECEIVE_COMMAND(PACKET_SERVER_COMMAND),
|
||||
NULL, /*PACKET_CLIENT_CHAT,*/
|
||||
RECEIVE_COMMAND(PACKET_SERVER_CHAT),
|
||||
NULL, /*PACKET_CLIENT_SET_PASSWORD,*/
|
||||
NULL, /*PACKET_CLIENT_SET_NAME,*/
|
||||
NULL, /*PACKET_CLIENT_QUIT,*/
|
||||
NULL, /*PACKET_CLIENT_ERROR,*/
|
||||
RECEIVE_COMMAND(PACKET_SERVER_QUIT),
|
||||
RECEIVE_COMMAND(PACKET_SERVER_ERROR_QUIT),
|
||||
RECEIVE_COMMAND(PACKET_SERVER_SHUTDOWN),
|
||||
RECEIVE_COMMAND(PACKET_SERVER_NEWGAME),
|
||||
};
|
||||
|
||||
// If this fails, check the array above with network_data.h
|
||||
assert_compile(lengthof(_network_client_packet) == PACKET_END);
|
||||
|
||||
extern const SettingDesc patch_settings[];
|
||||
|
||||
// This is a TEMPORARY solution to get the patch-settings
|
||||
// to the client. When the patch-settings are saved in the savegame
|
||||
// this should be removed!!
|
||||
void NetworkRecvPatchSettings(Packet *p)
|
||||
{
|
||||
const SettingDesc *item;
|
||||
|
||||
item = patch_settings;
|
||||
|
||||
while (item->name != NULL) {
|
||||
switch (item->flags) {
|
||||
case SDT_BOOL:
|
||||
case SDT_INT8:
|
||||
case SDT_UINT8:
|
||||
*(uint8 *)(item->ptr) = NetworkRecv_uint8(p);
|
||||
break;
|
||||
case SDT_INT16:
|
||||
case SDT_UINT16:
|
||||
*(uint16 *)(item->ptr) = NetworkRecv_uint16(p);
|
||||
break;
|
||||
case SDT_INT32:
|
||||
case SDT_UINT32:
|
||||
*(uint32 *)(item->ptr) = NetworkRecv_uint32(p);
|
||||
break;
|
||||
}
|
||||
item++;
|
||||
}
|
||||
}
|
||||
|
||||
// Is called after a client is connected to the server
|
||||
void NetworkClient_Connected(void)
|
||||
{
|
||||
// Set the frame-counter to 0 so nothing happens till we are ready
|
||||
_frame_counter = 0;
|
||||
_frame_counter_server = 0;
|
||||
last_ack_frame = 0;
|
||||
// Request the game-info
|
||||
SEND_COMMAND(PACKET_CLIENT_JOIN)();
|
||||
}
|
||||
|
||||
// Reads the packets from the socket-stream, if available
|
||||
NetworkRecvStatus NetworkClient_ReadPackets(ClientState *cs)
|
||||
{
|
||||
Packet *p;
|
||||
NetworkRecvStatus res = NETWORK_RECV_STATUS_OKAY;
|
||||
|
||||
while (res == NETWORK_RECV_STATUS_OKAY && (p = NetworkRecv_Packet(cs, &res)) != NULL) {
|
||||
byte type = NetworkRecv_uint8(p);
|
||||
if (type < PACKET_END && _network_client_packet[type] != NULL) {
|
||||
res = _network_client_packet[type](p);
|
||||
} else {
|
||||
res = NETWORK_RECV_STATUS_MALFORMED_PACKET;
|
||||
DEBUG(net, 0)("[NET][client] Received invalid packet type %d", type);
|
||||
}
|
||||
|
||||
free(p);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
@ -0,0 +1,22 @@
|
||||
#ifndef NETWORK_CLIENT_H
|
||||
#define NETWORK_CLIENT_H
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_GAME_INFO);
|
||||
DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_COMPANY_INFO);
|
||||
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_COMMAND)(CommandPacket *cp);
|
||||
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_ERROR)(NetworkErrorCode errorno);
|
||||
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_QUIT)(const char *leavemsg);
|
||||
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_CHAT)(NetworkAction action, DestType desttype, int dest, const char *msg);
|
||||
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_PASSWORD)(NetworkPasswordType type, const char *password);
|
||||
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_SET_PASSWORD)(const char *password);
|
||||
DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_SET_NAME)(const char *name);
|
||||
DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_ACK);
|
||||
|
||||
NetworkRecvStatus NetworkClient_ReadPackets(ClientState *cs);
|
||||
void NetworkClient_Connected(void);
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
|
||||
#endif // NETWORK_CLIENT_H
|
@ -0,0 +1,83 @@
|
||||
#ifndef NETWORK_CORE_H
|
||||
#define NETWORK_CORE_H
|
||||
|
||||
// Network stuff has many things that needs to be included
|
||||
// by default. All those things are in this file.
|
||||
|
||||
// =============================
|
||||
// Include standard stuff per OS
|
||||
|
||||
// Windows stuff
|
||||
#if defined(WIN32)
|
||||
# include <windows.h>
|
||||
# include <winsock2.h>
|
||||
# include <ws2tcpip.h>
|
||||
# pragma comment (lib, "ws2_32.lib")
|
||||
# define ENABLE_NETWORK // On windows, the network is always enabled
|
||||
# define GET_LAST_ERROR() WSAGetLastError()
|
||||
# define EWOULDBLOCK WSAEWOULDBLOCK
|
||||
// Windows has some different names for some types..
|
||||
typedef SSIZE_T ssize_t;
|
||||
typedef unsigned long in_addr_t;
|
||||
typedef INTERFACE_INFO IFREQ;
|
||||
#endif // WIN32
|
||||
|
||||
// UNIX stuff
|
||||
#if defined(UNIX)
|
||||
# define SOCKET int
|
||||
# define INVALID_SOCKET -1
|
||||
typedef struct ifreq IFREQ;
|
||||
# if !defined(__MORPHOS__) && !defined(__AMIGA__)
|
||||
# define ioctlsocket ioctl
|
||||
# if !defined(BEOS_NET_SERVER)
|
||||
# define closesocket close
|
||||
# endif
|
||||
# define GET_LAST_ERROR() (errno)
|
||||
# endif
|
||||
// Need this for FIONREAD on solaris
|
||||
# define BSD_COMP
|
||||
|
||||
// Includes needed for UNIX-like systems
|
||||
# include <unistd.h>
|
||||
# include <sys/ioctl.h>
|
||||
# if defined(__BEOS__) && defined(BEOS_NET_SERVER)
|
||||
# include <be/net/socket.h>
|
||||
# include <be/kernel/OS.h> // snooze()
|
||||
# include <be/net/netdb.h>
|
||||
typedef unsigned long in_addr_t;
|
||||
# define INADDR_NONE INADDR_BROADCAST
|
||||
# else
|
||||
# include <sys/socket.h>
|
||||
# include <netinet/in.h>
|
||||
# include <netinet/tcp.h>
|
||||
# include <arpa/inet.h>
|
||||
# include <net/if.h>
|
||||
# include <ifaddrs.h>
|
||||
// If for any reason ifaddrs.h does not exist on a system, remove define below
|
||||
// and an other system will be used to fetch ips from the system
|
||||
# define HAVE_GETIFADDRS
|
||||
# endif // BEOS_NET_SERVER
|
||||
# include <errno.h>
|
||||
# include <sys/time.h>
|
||||
# include <netdb.h>
|
||||
#endif // UNIX
|
||||
|
||||
// MorphOS and Amiga stuff
|
||||
#if defined(__MORPHOS__) || defined(__AMIGA__)
|
||||
# include <exec/types.h>
|
||||
# include <proto/exec.h> // required for Open/CloseLibrary()
|
||||
# if defined(__MORPHOS__)
|
||||
# include <sys/filio.h> // FION#? defines
|
||||
# else // __AMIGA__
|
||||
# include <proto/socket.h>
|
||||
# endif
|
||||
|
||||
// Make the names compatible
|
||||
# define closesocket(s) CloseSocket(s)
|
||||
# define GET_LAST_ERROR() Errno()
|
||||
# define ioctlsocket(s,request,status) IoctlSocket((LONG)s,(ULONG)request,(char*)status)
|
||||
|
||||
struct Library *SocketBase = NULL;
|
||||
#endif // __MORPHOS__ || __AMIGA__
|
||||
|
||||
#endif // NETWORK_CORE_H
|
@ -0,0 +1,434 @@
|
||||
#include "stdafx.h"
|
||||
#include "network_data.h"
|
||||
|
||||
// Is the network enabled?
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
#include "table/strings.h"
|
||||
#include "network_client.h"
|
||||
#include "command.h"
|
||||
#include "callback_table.h"
|
||||
|
||||
// This files handles the send/receive of all packets
|
||||
|
||||
// Create a packet for sending
|
||||
Packet *NetworkSend_Init(PacketType type)
|
||||
{
|
||||
Packet *packet = malloc(sizeof(Packet));
|
||||
// An error is inplace here, because it simply means we ran out of memory.
|
||||
if (packet == NULL) error("Failed to allocate Packet");
|
||||
|
||||
// Skip the size so we can write that in before sending the packet
|
||||
packet->size = sizeof(packet->size);
|
||||
packet->buffer[packet->size++] = type;
|
||||
packet->pos = 0;
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
// The next couple of functions make sure we can send
|
||||
// uint8, uint16, uint32 and uint64 endian-safe
|
||||
// over the network. The order it uses is:
|
||||
//
|
||||
// 1 2 3 4
|
||||
//
|
||||
|
||||
void NetworkSend_uint8(Packet *packet, uint8 data)
|
||||
{
|
||||
assert(packet->size < sizeof(packet->buffer) - sizeof(data));
|
||||
packet->buffer[packet->size++] = data & 0xFF;
|
||||
}
|
||||
|
||||
void NetworkSend_uint16(Packet *packet, uint16 data)
|
||||
{
|
||||
assert(packet->size < sizeof(packet->buffer) - sizeof(data));
|
||||
packet->buffer[packet->size++] = data & 0xFF;
|
||||
packet->buffer[packet->size++] = (data >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
void NetworkSend_uint32(Packet *packet, uint32 data)
|
||||
{
|
||||
assert(packet->size < sizeof(packet->buffer) - sizeof(data));
|
||||
packet->buffer[packet->size++] = data & 0xFF;
|
||||
packet->buffer[packet->size++] = (data >>= 8) & 0xFF;
|
||||
packet->buffer[packet->size++] = (data >>= 8) & 0xFF;
|
||||
packet->buffer[packet->size++] = (data >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
void NetworkSend_uint64(Packet *packet, uint64 data)
|
||||
{
|
||||
assert(packet->size < sizeof(packet->buffer) - sizeof(data));
|
||||
packet->buffer[packet->size++] = data & 0xFF;
|
||||
packet->buffer[packet->size++] = (data >>= 8) & 0xFF;
|
||||
packet->buffer[packet->size++] = (data >>= 8) & 0xFF;
|
||||
packet->buffer[packet->size++] = (data >>= 8) & 0xFF;
|
||||
packet->buffer[packet->size++] = (data >>= 8) & 0xFF;
|
||||
packet->buffer[packet->size++] = (data >>= 8) & 0xFF;
|
||||
packet->buffer[packet->size++] = (data >>= 8) & 0xFF;
|
||||
packet->buffer[packet->size++] = (data >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
// Sends a string over the network. It sends out
|
||||
// the string + '\0'. No size-byte or something.
|
||||
void NetworkSend_string(Packet *packet, const char* data)
|
||||
{
|
||||
assert(data != NULL);
|
||||
assert(packet->size < sizeof(packet->buffer) - strlen(data) - 1);
|
||||
while ((packet->buffer[packet->size++] = *data++) != '\0') {}
|
||||
}
|
||||
|
||||
// If PacketSize changes of size, you have to change the 2 packet->size
|
||||
// lines below matching the size of packet->size/PacketSize!
|
||||
// (line 'packet->buffer[0] = packet->size & 0xFF;' and below)
|
||||
assert_compile(sizeof(PacketSize) == 2);
|
||||
|
||||
// This function puts the packet in the send-queue and it is send
|
||||
// as soon as possible
|
||||
// (that is: the next tick, or maybe one tick later if the
|
||||
// OS-network-buffer is full)
|
||||
void NetworkSend_Packet(Packet *packet, ClientState *cs)
|
||||
{
|
||||
Packet *p;
|
||||
assert(packet != NULL);
|
||||
|
||||
packet->pos = 0;
|
||||
packet->next = NULL;
|
||||
|
||||
packet->buffer[0] = packet->size & 0xFF;
|
||||
packet->buffer[1] = packet->size >> 8;
|
||||
|
||||
// Locate last packet buffered for the client
|
||||
p = cs->packet_queue;
|
||||
if (p == NULL) {
|
||||
// No packets yet
|
||||
cs->packet_queue = packet;
|
||||
} else {
|
||||
// Skip to the last packet
|
||||
while (p->next != NULL) p = p->next;
|
||||
p->next = packet;
|
||||
}
|
||||
}
|
||||
|
||||
// Functions to help NetworkRecv_Packet/NetworkSend_Packet a bit
|
||||
// A socket can make errors. When that happens
|
||||
// this handles what to do.
|
||||
// For clients: close connection and drop back to main-menu
|
||||
// For servers: close connection and that is it
|
||||
NetworkRecvStatus CloseConnection(ClientState *cs)
|
||||
{
|
||||
CloseClient(cs);
|
||||
|
||||
// Clients drop back to the main menu
|
||||
if (!_network_server) {
|
||||
_switch_mode = SM_MENU;
|
||||
_networking = false;
|
||||
_switch_mode_errorstr = STR_NETWORK_ERR_LOSTCONNECTION;
|
||||
|
||||
return NETWORK_RECV_STATUS_CONN_LOST;
|
||||
}
|
||||
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
// Sends all the buffered packets out for this client
|
||||
// it stops when:
|
||||
// 1) all packets are send (queue is empty)
|
||||
// 2) the OS reports back that it can not send any more
|
||||
// data right now (full network-buffer, it happens ;))
|
||||
// 3) sending took too long
|
||||
bool NetworkSend_Packets(ClientState *cs)
|
||||
{
|
||||
ssize_t res;
|
||||
Packet *p;
|
||||
|
||||
// We can not write to this socket!!
|
||||
if (!cs->writable) return false;
|
||||
if (cs->socket == INVALID_SOCKET) return false;
|
||||
|
||||
p = cs->packet_queue;
|
||||
while (p != NULL) {
|
||||
res = send(cs->socket, p->buffer + p->pos, p->size - p->pos, 0);
|
||||
if (res == -1) {
|
||||
int err = GET_LAST_ERROR();
|
||||
if (err != EWOULDBLOCK) {
|
||||
// Something went wrong.. close client!
|
||||
DEBUG(net, 0) ("[NET] send() failed with error %d", err);
|
||||
CloseConnection(cs);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (res == 0) {
|
||||
// Client/server has left us :(
|
||||
CloseConnection(cs);
|
||||
return false;
|
||||
}
|
||||
|
||||
p->pos += res;
|
||||
|
||||
// Is this packet sent?
|
||||
if (p->pos == p->size) {
|
||||
// Go to the next packet
|
||||
cs->packet_queue = p->next;
|
||||
free(p);
|
||||
p = cs->packet_queue;
|
||||
} else
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Receiving commands
|
||||
// Again, the next couple of functions are endian-safe
|
||||
// see the comment around NetworkSend_uint8 for more info.
|
||||
uint8 NetworkRecv_uint8(Packet *packet)
|
||||
{
|
||||
return packet->buffer[packet->pos++];
|
||||
}
|
||||
|
||||
uint16 NetworkRecv_uint16(Packet *packet)
|
||||
{
|
||||
uint16 n;
|
||||
n = (uint16)packet->buffer[packet->pos++];
|
||||
n += (uint16)packet->buffer[packet->pos++] << 8;
|
||||
return n;
|
||||
}
|
||||
|
||||
uint32 NetworkRecv_uint32(Packet *packet)
|
||||
{
|
||||
uint32 n;
|
||||
n = (uint32)packet->buffer[packet->pos++];
|
||||
n += (uint32)packet->buffer[packet->pos++] << 8;
|
||||
n += (uint32)packet->buffer[packet->pos++] << 16;
|
||||
n += (uint32)packet->buffer[packet->pos++] << 24;
|
||||
return n;
|
||||
}
|
||||
|
||||
uint64 NetworkRecv_uint64(Packet *packet)
|
||||
{
|
||||
uint64 n;
|
||||
n = (uint64)packet->buffer[packet->pos++];
|
||||
n += (uint64)packet->buffer[packet->pos++] << 8;
|
||||
n += (uint64)packet->buffer[packet->pos++] << 16;
|
||||
n += (uint64)packet->buffer[packet->pos++] << 24;
|
||||
n += (uint64)packet->buffer[packet->pos++] << 32;
|
||||
n += (uint64)packet->buffer[packet->pos++] << 40;
|
||||
n += (uint64)packet->buffer[packet->pos++] << 48;
|
||||
n += (uint64)packet->buffer[packet->pos++] << 56;
|
||||
return n;
|
||||
}
|
||||
|
||||
// Reads a string till it finds a '\0' in the stream
|
||||
void NetworkRecv_string(Packet *p, char* buffer, size_t size)
|
||||
{
|
||||
int pos;
|
||||
pos = p->pos;
|
||||
while (--size > 0 && pos < p->size && (*buffer++ = p->buffer[pos++]) != '\0') {}
|
||||
if (size == 0 || pos == p->size)
|
||||
{
|
||||
*buffer = '\0';
|
||||
// If size was sooner to zero then the string in the stream
|
||||
// skip till the \0, so the packet can be read out correctly for the rest
|
||||
while (pos < p->size && p->buffer[pos] != '\0') ++pos;
|
||||
++pos;
|
||||
}
|
||||
p->pos = pos;
|
||||
}
|
||||
|
||||
// If PacketSize changes of size, you have to change the 2 packet->size
|
||||
// lines below matching the size of packet->size/PacketSize!
|
||||
// (the line: 'p->size = (uint16)p->buffer[0];' and below)
|
||||
assert_compile(sizeof(PacketSize) == 2);
|
||||
|
||||
Packet *NetworkRecv_Packet(ClientState *cs, NetworkRecvStatus *status)
|
||||
{
|
||||
ssize_t res;
|
||||
Packet *p;
|
||||
|
||||
*status = NETWORK_RECV_STATUS_OKAY;
|
||||
|
||||
if (cs->socket == INVALID_SOCKET) return NULL;
|
||||
|
||||
if (cs->packet_recv == NULL) {
|
||||
cs->packet_recv = malloc(sizeof(Packet));
|
||||
if (cs->packet_recv == NULL) error("Failed to allocate packet");
|
||||
// Set pos to zero!
|
||||
cs->packet_recv->pos = 0;
|
||||
cs->packet_recv->size = 0; // Can be ommited, just for safety reasons
|
||||
}
|
||||
|
||||
p = cs->packet_recv;
|
||||
|
||||
// Read packet size
|
||||
if (p->pos < sizeof(PacketSize)) {
|
||||
while (p->pos < sizeof(PacketSize)) {
|
||||
// Read the size of the packet
|
||||
res = recv(cs->socket, p->buffer + p->pos, sizeof(PacketSize) - p->pos, 0);
|
||||
if (res == -1) {
|
||||
int err = GET_LAST_ERROR();
|
||||
if (err != EWOULDBLOCK) {
|
||||
// Something went wrong..
|
||||
if (err != 104) // 104 is Connection Reset by Peer
|
||||
DEBUG(net, 0) ("[NET] recv() failed with error %d", err);
|
||||
*status = CloseConnection(cs);
|
||||
return NULL;
|
||||
}
|
||||
// Connection would block, so stop for now
|
||||
return NULL;
|
||||
}
|
||||
if (res == 0) {
|
||||
// Client/server has left us :(
|
||||
*status = CloseConnection(cs);
|
||||
return NULL;
|
||||
}
|
||||
p->pos += res;
|
||||
}
|
||||
|
||||
p->size = (uint16)p->buffer[0];
|
||||
p->size += (uint16)p->buffer[1] << 8;
|
||||
|
||||
if (p->size > SEND_MTU) {
|
||||
*status = CloseConnection(cs);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Read rest of packet
|
||||
while (p->pos < p->size) {
|
||||
res = recv(cs->socket, p->buffer + p->pos, p->size - p->pos, 0);
|
||||
if (res == -1) {
|
||||
int err = GET_LAST_ERROR();
|
||||
if (err != EWOULDBLOCK) {
|
||||
// Something went wrong..
|
||||
if (err != 104) // 104 is Connection Reset by Peer
|
||||
DEBUG(net, 0) ("[NET] recv() failed with error %d", err);
|
||||
*status = CloseConnection(cs);
|
||||
return NULL;
|
||||
}
|
||||
// Connection would block
|
||||
return NULL;
|
||||
}
|
||||
if (res == 0) {
|
||||
// Client/server has left us :(
|
||||
*status = CloseConnection(cs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p->pos += res;
|
||||
}
|
||||
|
||||
// We have a complete packet, return it!
|
||||
p->pos = 2;
|
||||
p->next = NULL; // Should not be needed, but who knows...
|
||||
|
||||
// Prepare for receiving a new packet
|
||||
cs->packet_recv = NULL;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
// Add a command to the local command queue
|
||||
void NetworkAddCommandQueue(ClientState *cs, CommandPacket *cp)
|
||||
{
|
||||
CommandPacket *new_cp = malloc(sizeof(CommandPacket));
|
||||
|
||||
*new_cp = *cp;
|
||||
|
||||
if (cs->command_queue == NULL)
|
||||
cs->command_queue = new_cp;
|
||||
else {
|
||||
CommandPacket *c = cs->command_queue;
|
||||
while (c->next != NULL) c = c->next;
|
||||
c->next = new_cp;
|
||||
}
|
||||
}
|
||||
|
||||
// If this fails, make sure you change the following line below:
|
||||
// 'memcpy(qp->dp, _decode_parameters, 10 * sizeof(uint32));'
|
||||
// Also, in network_data.h, change the size of CommandPacket->dp!
|
||||
// (this protection is there to make sure in network.h dp is of the right size!)
|
||||
assert_compile(sizeof(_decode_parameters) == 20 * sizeof(uint32));
|
||||
|
||||
// Prepare a DoCommand to be send over the network
|
||||
void NetworkSend_Command(uint32 tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback)
|
||||
{
|
||||
CommandPacket *c = malloc(sizeof(CommandPacket));
|
||||
byte temp_callback;
|
||||
|
||||
c->player = _local_player;
|
||||
c->next = NULL;
|
||||
c->tile = tile;
|
||||
c->p1 = p1;
|
||||
c->p2 = p2;
|
||||
c->cmd = cmd;
|
||||
c->callback = 0;
|
||||
|
||||
temp_callback = 0;
|
||||
|
||||
while (temp_callback < _callback_table_count && _callback_table[temp_callback] != callback)
|
||||
temp_callback++;
|
||||
if (temp_callback == _callback_table_count) {
|
||||
DEBUG(net, 0) ("[NET] Unknown callback. (Pointer: %p) No callback sent.", callback);
|
||||
temp_callback = 0; /* _callback_table[0] == NULL */
|
||||
}
|
||||
|
||||
if (_network_server) {
|
||||
// We are the server, so set the command to be executed next possible frame
|
||||
c->frame = _frame_counter_max + 1;
|
||||
} else {
|
||||
c->frame = 0; // The client can't tell which frame, so just make it 0
|
||||
}
|
||||
|
||||
// Copy the _decode_parameters to dp
|
||||
memcpy(c->dp, _decode_parameters, 20 * sizeof(uint32));
|
||||
|
||||
if (_network_server) {
|
||||
// If we are the server, we queue the command in our 'special' queue.
|
||||
// In theory, we could execute the command right away, but then the
|
||||
// client on the server can do everything 1 tick faster then others.
|
||||
// So to keep the game fair, we delay the command with 1 tick
|
||||
// which gives about the same speed as most clients.
|
||||
ClientState *cs;
|
||||
|
||||
// And we queue it for delivery to the clients
|
||||
FOR_ALL_CLIENTS(cs) {
|
||||
if (cs->status > STATUS_AUTH) {
|
||||
NetworkAddCommandQueue(cs, c);
|
||||
}
|
||||
}
|
||||
|
||||
// Only the server gets the callback, because clients should not get them
|
||||
c->callback = temp_callback;
|
||||
if (_local_command_queue == NULL) {
|
||||
_local_command_queue = c;
|
||||
} else {
|
||||
// Find last packet
|
||||
CommandPacket *cp = _local_command_queue;
|
||||
while (cp->next != NULL) cp = cp->next;
|
||||
cp->next = c;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Clients send their command to the server and forget all about the packet
|
||||
c->callback = temp_callback;
|
||||
SEND_COMMAND(PACKET_CLIENT_COMMAND)(c);
|
||||
}
|
||||
|
||||
// Execute a DoCommand we received from the network
|
||||
void NetworkExecuteCommand(CommandPacket *cp)
|
||||
{
|
||||
_current_player = cp->player;
|
||||
memcpy(_decode_parameters, cp->dp, sizeof(cp->dp));
|
||||
/* cp->callback is unsigned. so we don't need to do lower bounds checking. */
|
||||
if (cp->callback > _callback_table_count) {
|
||||
DEBUG(net,0) ("[NET] Received out-of-bounds callback! (%d)", cp->callback);
|
||||
cp->callback = 0;
|
||||
}
|
||||
DoCommandP(cp->tile, cp->p1, cp->p2, _callback_table[cp->callback], cp->cmd | CMD_NETWORK_COMMAND);
|
||||
}
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
@ -0,0 +1,223 @@
|
||||
#ifndef NETWORK_DATA_H
|
||||
#define NETWORK_DATA_H
|
||||
|
||||
#include "ttd.h"
|
||||
#include "network.h"
|
||||
|
||||
// Is the network enabled?
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
#define SEND_MTU 1460
|
||||
#define MAX_TEXT_MSG_LEN 1024 /* long long long long sentences :-) */
|
||||
|
||||
// The client-info-server-index is always 1
|
||||
#define NETWORK_SERVER_INDEX 1
|
||||
#define NETWORK_EMPTY_INDEX 0
|
||||
|
||||
// What version of game-info do we use?
|
||||
#define NETWORK_GAME_INFO_VERSION 1
|
||||
// What version of company info is this?
|
||||
#define NETWORK_COMPANY_INFO_VERSION 1
|
||||
|
||||
typedef uint16 PacketSize;
|
||||
|
||||
typedef struct Packet {
|
||||
struct Packet *next;
|
||||
PacketSize size;
|
||||
PacketSize pos;
|
||||
byte buffer[SEND_MTU];
|
||||
} Packet;
|
||||
|
||||
typedef struct CommandPacket {
|
||||
struct CommandPacket *next;
|
||||
byte player;
|
||||
uint32 cmd;
|
||||
uint32 p1;
|
||||
uint32 p2;
|
||||
uint32 tile; // Always make it uint32, so it is bigmap compatible
|
||||
uint32 dp[20]; // decode_params
|
||||
uint32 frame; // In which frame must this packet be executed?
|
||||
byte callback;
|
||||
} CommandPacket;
|
||||
|
||||
typedef enum {
|
||||
STATUS_INACTIVE,
|
||||
STATUS_AUTH, // This means that the client is authorized
|
||||
STATUS_MAP_WAIT, // This means that the client is put on hold because someone else is getting the map
|
||||
STATUS_MAP,
|
||||
STATUS_DONE_MAP,
|
||||
STATUS_PRE_ACTIVE,
|
||||
STATUS_ACTIVE,
|
||||
} ClientStatus;
|
||||
|
||||
typedef enum {
|
||||
MAP_PACKET_START,
|
||||
MAP_PACKET_NORMAL,
|
||||
MAP_PACKET_PATCH,
|
||||
MAP_PACKET_END,
|
||||
} MapPacket;
|
||||
|
||||
typedef enum {
|
||||
NETWORK_RECV_STATUS_OKAY,
|
||||
NETWORK_RECV_STATUS_DESYNC,
|
||||
NETWORK_RECV_STATUS_SAVEGAME,
|
||||
NETWORK_RECV_STATUS_CONN_LOST,
|
||||
NETWORK_RECV_STATUS_MALFORMED_PACKET,
|
||||
NETWORK_RECV_STATUS_SERVER_ERROR, // The server told us we made an error
|
||||
NETWORK_RECV_STATUS_SERVER_FULL,
|
||||
NETWORK_RECV_STATUS_CLOSE_QUERY, // Done quering the server
|
||||
} NetworkRecvStatus;
|
||||
|
||||
typedef enum {
|
||||
NETWORK_ERROR_GENERAL, // Try to use thisone like never
|
||||
|
||||
// Signals from clients
|
||||
NETWORK_ERROR_DESYNC,
|
||||
NETWORK_ERROR_SAVEGAME_FAILED,
|
||||
NETWORK_ERROR_CONNECTION_LOST,
|
||||
NETWORK_ERROR_ILLEGAL_PACKET,
|
||||
|
||||
// Signals from servers
|
||||
NETWORK_ERROR_NOT_AUTHORIZED,
|
||||
NETWORK_ERROR_NOT_EXPECTED,
|
||||
NETWORK_ERROR_WRONG_REVISION,
|
||||
NETWORK_ERROR_NAME_IN_USE,
|
||||
NETWORK_ERROR_WRONG_PASSWORD,
|
||||
NETWORK_ERROR_PLAYER_MISMATCH, // Happens in CLIENT_COMMAND
|
||||
NETWORK_ERROR_KICKED,
|
||||
} NetworkErrorCode;
|
||||
|
||||
// Actions that can be used for NetworkTextMessage
|
||||
typedef enum {
|
||||
NETWORK_ACTION_JOIN_LEAVE,
|
||||
NETWORK_ACTION_CHAT,
|
||||
NETWORK_ACTION_CHAT_PLAYER,
|
||||
NETWORK_ACTION_CHAT_CLIENT,
|
||||
NETWORK_ACTION_CHAT_TO_CLIENT,
|
||||
NETWORK_ACTION_CHAT_TO_PLAYER,
|
||||
NETWORK_ACTION_GIVE_MONEY,
|
||||
NETWORK_ACTION_NAME_CHANGE,
|
||||
} NetworkAction;
|
||||
|
||||
typedef enum {
|
||||
NETWORK_GAME_PASSWORD,
|
||||
NETWORK_COMPANY_PASSWORD,
|
||||
} NetworkPasswordType;
|
||||
|
||||
// To keep the clients all together
|
||||
typedef struct ClientState {
|
||||
int socket;
|
||||
uint16 index;
|
||||
uint32 last_frame;
|
||||
uint32 last_frame_server;
|
||||
byte lag_test; // This byte is used for lag-testing the client
|
||||
|
||||
ClientStatus status;
|
||||
bool writable; // is client ready to write to?
|
||||
bool quited;
|
||||
|
||||
Packet *packet_queue; // Packets that are awaiting delivery
|
||||
Packet *packet_recv; // Partially received packet
|
||||
|
||||
CommandPacket *command_queue; // The command-queue awaiting delivery
|
||||
} ClientState;
|
||||
|
||||
// What packet types are there
|
||||
// WARNING: The first 3 packets can NEVER change order again
|
||||
// it protects old clients from joining newer servers (because SERVER_ERROR
|
||||
// is the respond to a wrong revision)
|
||||
typedef enum {
|
||||
PACKET_SERVER_FULL,
|
||||
PACKET_CLIENT_JOIN,
|
||||
PACKET_SERVER_ERROR,
|
||||
PACKET_CLIENT_COMPANY_INFO,
|
||||
PACKET_SERVER_COMPANY_INFO,
|
||||
PACKET_SERVER_CLIENT_INFO,
|
||||
PACKET_SERVER_NEED_PASSWORD,
|
||||
PACKET_CLIENT_PASSWORD,
|
||||
PACKET_SERVER_WELCOME,
|
||||
PACKET_CLIENT_GETMAP,
|
||||
PACKET_SERVER_WAIT,
|
||||
PACKET_SERVER_MAP,
|
||||
PACKET_CLIENT_MAP_OK,
|
||||
PACKET_SERVER_JOIN,
|
||||
PACKET_SERVER_FRAME,
|
||||
PACKET_SERVER_SYNC,
|
||||
PACKET_CLIENT_ACK,
|
||||
PACKET_CLIENT_COMMAND,
|
||||
PACKET_SERVER_COMMAND,
|
||||
PACKET_CLIENT_CHAT,
|
||||
PACKET_SERVER_CHAT,
|
||||
PACKET_CLIENT_SET_PASSWORD,
|
||||
PACKET_CLIENT_SET_NAME,
|
||||
PACKET_CLIENT_QUIT,
|
||||
PACKET_CLIENT_ERROR,
|
||||
PACKET_SERVER_QUIT,
|
||||
PACKET_SERVER_ERROR_QUIT,
|
||||
PACKET_SERVER_SHUTDOWN,
|
||||
PACKET_SERVER_NEWGAME,
|
||||
PACKET_END // Should ALWAYS be on the end of this list!! (period)
|
||||
} PacketType;
|
||||
|
||||
typedef enum {
|
||||
DESTTYPE_BROADCAST,
|
||||
DESTTYPE_PLAYER,
|
||||
DESTTYPE_CLIENT
|
||||
} DestType;
|
||||
|
||||
CommandPacket *_local_command_queue;
|
||||
|
||||
SOCKET _udp_client_socket; // udp client socket
|
||||
|
||||
// Here we keep track of the clients
|
||||
// (and the client uses [0] for his own communication)
|
||||
ClientState _clients[MAX_CLIENTS];
|
||||
#define DEREF_CLIENT(i) (&_clients[i])
|
||||
// This returns the NetworkClientInfo from a ClientState
|
||||
#define DEREF_CLIENT_INFO(cs) (&_network_client_info[cs - _clients])
|
||||
|
||||
// Macros to make life a bit more easier
|
||||
#define DEF_CLIENT_RECEIVE_COMMAND(type) NetworkRecvStatus NetworkPacketReceive_ ## type ## _command(Packet *p)
|
||||
#define DEF_CLIENT_SEND_COMMAND(type) void NetworkPacketSend_ ## type ## _command(void)
|
||||
#define DEF_CLIENT_SEND_COMMAND_PARAM(type) void NetworkPacketSend_ ## type ## _command
|
||||
#define DEF_SERVER_RECEIVE_COMMAND(type) void NetworkPacketReceive_ ## type ## _command(ClientState *cs, Packet *p)
|
||||
#define DEF_SERVER_SEND_COMMAND(type) void NetworkPacketSend_ ## type ## _command(ClientState *cs)
|
||||
#define DEF_SERVER_SEND_COMMAND_PARAM(type) void NetworkPacketSend_ ## type ## _command
|
||||
|
||||
#define SEND_COMMAND(type) NetworkPacketSend_ ## type ## _command
|
||||
#define RECEIVE_COMMAND(type) NetworkPacketReceive_ ## type ## _command
|
||||
|
||||
#define FOR_ALL_CLIENTS(cs) for (cs = _clients; cs != &_clients[MAX_CLIENTS] && cs->socket != INVALID_SOCKET; cs++)
|
||||
|
||||
Packet *NetworkSend_Init(PacketType type);
|
||||
void NetworkSend_uint8(Packet *packet, uint8 data);
|
||||
void NetworkSend_uint16(Packet *packet, uint16 data);
|
||||
void NetworkSend_uint32(Packet *packet, uint32 data);
|
||||
void NetworkSend_uint64(Packet *packet, uint64 data);
|
||||
void NetworkSend_string(Packet *packet, const char* data);
|
||||
void NetworkSend_Packet(Packet *packet, ClientState *cs);
|
||||
|
||||
uint8 NetworkRecv_uint8(Packet *packet);
|
||||
uint16 NetworkRecv_uint16(Packet *packet);
|
||||
uint32 NetworkRecv_uint32(Packet *packet);
|
||||
uint64 NetworkRecv_uint64(Packet *packet);
|
||||
void NetworkRecv_string(Packet *packet, char* buffer, size_t size);
|
||||
Packet *NetworkRecv_Packet(ClientState *cs, NetworkRecvStatus *status);
|
||||
|
||||
bool NetworkSend_Packets(ClientState *cs);
|
||||
void NetworkExecuteCommand(CommandPacket *cp);
|
||||
void NetworkAddCommandQueue(ClientState *cs, CommandPacket *cp);
|
||||
|
||||
// from network.c
|
||||
void CloseClient(ClientState *cs);
|
||||
void NetworkTextMessage(NetworkAction action, uint16 color, const char *name, const char *str, ...);
|
||||
void NetworkGetClientName(char *clientname, size_t size, ClientState *cs);
|
||||
uint NetworkCalculateLag(const ClientState *cs);
|
||||
byte NetworkGetCurrentLanguageIndex();
|
||||
NetworkClientInfo *NetworkFindClientInfoFromIndex(uint16 client_index);
|
||||
ClientState *NetworkFindClientStateFromIndex(uint16 client_index);
|
||||
unsigned long NetworkResolveHost(const char *hostname);
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
|
||||
#endif // NETWORK_DATA_H
|
@ -0,0 +1,83 @@
|
||||
#include "stdafx.h"
|
||||
#include "network_data.h"
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
//
|
||||
// This file handles the GameList
|
||||
// Also, it handles the request to a server for data about the server
|
||||
|
||||
extern void UpdateNetworkGameWindow(bool unselect);
|
||||
|
||||
void NetworkGameListClear(void)
|
||||
{
|
||||
NetworkGameList *item;
|
||||
NetworkGameList *next;
|
||||
|
||||
item = _network_game_list;
|
||||
|
||||
while (item != NULL) {
|
||||
next = item->next;
|
||||
free(item);
|
||||
item = next;
|
||||
}
|
||||
_network_game_list = NULL;
|
||||
_network_game_count = 0;
|
||||
|
||||
UpdateNetworkGameWindow(true);
|
||||
|
||||
DEBUG(net, 4)("[NET][GameList] Cleared list");
|
||||
}
|
||||
|
||||
NetworkGameList *NetworkGameListAddItem(uint32 ip, uint16 port)
|
||||
{
|
||||
NetworkGameList *item;
|
||||
|
||||
item = _network_game_list;
|
||||
if (item != NULL) {
|
||||
while (item->next != NULL) {
|
||||
if (item->ip == ip && item->port == port)
|
||||
return item;
|
||||
item = item->next;
|
||||
}
|
||||
|
||||
if (item->ip == ip && item->port == port)
|
||||
return item;
|
||||
|
||||
item->next = malloc(sizeof(*item));
|
||||
item = item->next;
|
||||
} else {
|
||||
item = malloc(sizeof(*item));
|
||||
_network_game_list = item;
|
||||
}
|
||||
|
||||
DEBUG(net, 4) ("[NET][GameList] Added server to list");
|
||||
|
||||
memset(item, 0, sizeof(*item));
|
||||
|
||||
item->next = NULL;
|
||||
item->ip = ip;
|
||||
item->port = port;
|
||||
_network_game_count++;
|
||||
|
||||
UpdateNetworkGameWindow(false);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
void NetworkGameListAddQueriedItem(NetworkGameInfo *info, bool server_online)
|
||||
{
|
||||
// We queried a server and now we are going to add it to the list
|
||||
NetworkGameList *item;
|
||||
|
||||
item = NetworkGameListAddItem(_network_last_host_ip, _network_last_port);
|
||||
item->online = server_online;
|
||||
memcpy(&item->info, info, sizeof(NetworkGameInfo));
|
||||
ttd_strlcpy(item->info.hostname, _network_last_host, sizeof(item->info.hostname));
|
||||
|
||||
UpdateNetworkGameWindow(false);
|
||||
}
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
|
||||
|
@ -0,0 +1,8 @@
|
||||
#ifndef NETWORK_GAMELIST_H
|
||||
#define NETWORK_GAMELIST_H
|
||||
|
||||
void NetworkGameListClear(void);
|
||||
NetworkGameList *NetworkGameListAddItem(uint32 ip, uint16 port);
|
||||
void NetworkGameListAddQueriedItem(NetworkGameInfo *info, bool server_online);
|
||||
|
||||
#endif /* NETWORK_GAMELIST_H */
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,20 @@
|
||||
#ifndef NETWORK_SERVER_H
|
||||
#define NETWORK_SERVER_H
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
DEF_SERVER_SEND_COMMAND(PACKET_SERVER_MAP);
|
||||
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_ERROR_QUIT)(ClientState *cs, uint16 client_index, NetworkErrorCode errorno);
|
||||
DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_ERROR)(ClientState *cs, NetworkErrorCode error);
|
||||
DEF_SERVER_SEND_COMMAND(PACKET_SERVER_SHUTDOWN);
|
||||
DEF_SERVER_SEND_COMMAND(PACKET_SERVER_NEWGAME);
|
||||
|
||||
bool NetworkFindName(char new_name[NETWORK_NAME_LENGTH]);
|
||||
void NetworkServer_HandleChat(NetworkAction action, DestType desttype, int dest, const char *msg, byte from_index);
|
||||
|
||||
bool NetworkServer_ReadPackets(ClientState *cs);
|
||||
void NetworkServer_Tick();
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
||||
|
||||
#endif // NETWORK_SERVER_H
|
@ -0,0 +1,368 @@
|
||||
#include "stdafx.h"
|
||||
#include "network_data.h"
|
||||
|
||||
#ifdef ENABLE_NETWORK
|
||||
|
||||
#include "network_gamelist.h"
|
||||
|
||||
extern void UpdateNetworkGameWindow(bool unselect);
|
||||
|
||||
//
|
||||
// This file handles all the LAN-stuff
|
||||
// Stuff like:
|
||||
// - UDP search over the network
|
||||
//
|
||||
|
||||
typedef enum {
|
||||
PACKET_UDP_FIND_SERVER,
|
||||
PACKET_UDP_SERVER_RESPONSE,
|
||||
PACKET_UDP_END
|
||||
} PacketUDPType;
|
||||
|
||||
static SOCKET _udp_server_socket; // udp server socket
|
||||
|
||||
#define DEF_UDP_RECEIVE_COMMAND(type) void NetworkPacketReceive_ ## type ## _command(Packet *p, struct sockaddr_in *client_addr)
|
||||
void NetworkSendUDP_Packet(Packet *p, struct sockaddr_in *recv);
|
||||
|
||||
DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_FIND_SERVER)
|
||||
{
|
||||
Packet *packet;
|
||||
// Just a fail-safe.. should never happen
|
||||
if (!_network_udp_server)
|
||||
return;
|
||||
|
||||
packet = NetworkSend_Init(PACKET_UDP_SERVER_RESPONSE);
|
||||
|
||||
// Update some game_info
|
||||
_network_game_info.game_date = _date;
|
||||
_network_game_info.map_set = _opt.landscape;
|
||||
|
||||
NetworkSend_uint8 (packet, NETWORK_GAME_INFO_VERSION);
|
||||
NetworkSend_string(packet, _network_game_info.server_name);
|
||||
NetworkSend_string(packet, _network_game_info.server_revision);
|
||||
NetworkSend_uint8 (packet, _network_game_info.server_lang);
|
||||
NetworkSend_uint8 (packet, _network_game_info.use_password);
|
||||
NetworkSend_uint8 (packet, _network_game_info.clients_max);
|
||||
NetworkSend_uint8 (packet, _network_game_info.clients_on);
|
||||
NetworkSend_uint8 (packet, _network_game_info.spectators_on);
|
||||
NetworkSend_uint16(packet, _network_game_info.game_date);
|
||||
NetworkSend_uint16(packet, _network_game_info.start_date);
|
||||
NetworkSend_string(packet, _network_game_info.map_name);
|
||||
NetworkSend_uint16(packet, _network_game_info.map_width);
|
||||
NetworkSend_uint16(packet, _network_game_info.map_height);
|
||||
NetworkSend_uint8 (packet, _network_game_info.map_set);
|
||||
NetworkSend_uint8 (packet, _network_game_info.dedicated);
|
||||
|
||||
// Let the client know that we are here
|
||||
NetworkSendUDP_Packet(packet, client_addr);
|
||||
|
||||
free(packet);
|
||||
|
||||
DEBUG(net, 2)("[NET][UDP] Queried from %s", inet_ntoa(client_addr->sin_addr));
|
||||
}
|
||||
|
||||
DEF_UDP_RECEIVE_COMMAND(PACKET_UDP_SERVER_RESPONSE)
|
||||
{
|
||||
NetworkGameList *item;
|
||||
byte game_info_version;
|
||||
|
||||
// Just a fail-safe.. should never happen
|
||||
if (_network_udp_server)
|
||||
return;
|
||||
|
||||
game_info_version = NetworkRecv_uint8(p);
|
||||
|
||||
// Find next item
|
||||
item = NetworkGameListAddItem(inet_addr(inet_ntoa(client_addr->sin_addr)), ntohs(client_addr->sin_port));
|
||||
|
||||
if (game_info_version == 1) {
|
||||
NetworkRecv_string(p, item->info.server_name, sizeof(item->info.server_name));
|
||||
NetworkRecv_string(p, item->info.server_revision, sizeof(item->info.server_revision));
|
||||
item->info.server_lang = NetworkRecv_uint8(p);
|
||||
item->info.use_password = NetworkRecv_uint8(p);
|
||||
item->info.clients_max = NetworkRecv_uint8(p);
|
||||
item->info.clients_on = NetworkRecv_uint8(p);
|
||||
item->info.spectators_on = NetworkRecv_uint8(p);
|
||||
item->info.game_date = NetworkRecv_uint16(p);
|
||||
item->info.start_date = NetworkRecv_uint16(p);
|
||||
NetworkRecv_string(p, item->info.map_name, sizeof(item->info.map_name));
|
||||
item->info.map_width = NetworkRecv_uint16(p);
|
||||
item->info.map_height = NetworkRecv_uint16(p);
|
||||
item->info.map_set = NetworkRecv_uint8(p);
|
||||
item->info.dedicated = NetworkRecv_uint8(p);
|
||||
|
||||
if (item->info.hostname[0] == '\0')
|
||||
snprintf(item->info.hostname, sizeof(item->info.hostname), "%s", inet_ntoa(client_addr->sin_addr));
|
||||
}
|
||||
|
||||
item->online = true;
|
||||
|
||||
UpdateNetworkGameWindow(false);
|
||||
}
|
||||
|
||||
|
||||
// The layout for the receive-functions by UDP
|
||||
typedef void NetworkUDPPacket(Packet *p, struct sockaddr_in *client_addr);
|
||||
|
||||
static NetworkUDPPacket* const _network_udp_packet[] = {
|
||||
RECEIVE_COMMAND(PACKET_UDP_FIND_SERVER),
|
||||
RECEIVE_COMMAND(PACKET_UDP_SERVER_RESPONSE),
|
||||
};
|
||||
|
||||
// If this fails, check the array above with network_data.h
|
||||
assert_compile(lengthof(_network_udp_packet) == PACKET_UDP_END);
|
||||
|
||||
|
||||
void NetworkHandleUDPPacket(Packet *p, struct sockaddr_in *client_addr)
|
||||
{
|
||||
byte type;
|
||||
|
||||
type = NetworkRecv_uint8(p);
|
||||
|
||||
if (type < PACKET_UDP_END && _network_udp_packet[type] != NULL) {
|
||||
_network_udp_packet[type](p, client_addr);
|
||||
} else {
|
||||
DEBUG(net, 0)("[NET][UDP] Received invalid packet type %d", type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Send a packet over UDP
|
||||
void NetworkSendUDP_Packet(Packet *p, struct sockaddr_in *recv)
|
||||
{
|
||||
SOCKET udp;
|
||||
int res;
|
||||
|
||||
// Find the correct socket
|
||||
if (_network_udp_server)
|
||||
udp = _udp_server_socket;
|
||||
else
|
||||
udp = _udp_client_socket;
|
||||
|
||||
// Put the length in the buffer
|
||||
p->buffer[0] = p->size & 0xFF;
|
||||
p->buffer[1] = p->size >> 8;
|
||||
|
||||
// Send the buffer
|
||||
res = sendto(udp, p->buffer, p->size, 0, (struct sockaddr *)recv, sizeof(*recv));
|
||||
|
||||
// Check for any errors, but ignore it for the rest
|
||||
if (res == -1) {
|
||||
DEBUG(net, 1)("[NET][UDP] Send error: %i", GET_LAST_ERROR());
|
||||
}
|
||||
}
|
||||
|
||||
// Start UDP listener
|
||||
bool NetworkUDPListen(uint32 host, uint16 port)
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
SOCKET udp;
|
||||
|
||||
// Make sure sockets are closed
|
||||
if (_network_udp_server)
|
||||
closesocket(_udp_server_socket);
|
||||
else
|
||||
closesocket(_udp_client_socket);
|
||||
|
||||
udp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (udp == INVALID_SOCKET) {
|
||||
DEBUG(net, 1)("[NET][UDP] Failed to start UDP support");
|
||||
return false;
|
||||
}
|
||||
|
||||
// set nonblocking mode for socket
|
||||
{
|
||||
unsigned long blocking = 1;
|
||||
ioctlsocket(udp, FIONBIO, &blocking);
|
||||
}
|
||||
|
||||
sin.sin_family = AF_INET;
|
||||
// Listen on all IPs
|
||||
sin.sin_addr.s_addr = host;
|
||||
sin.sin_port = htons(port);
|
||||
|
||||
if (bind(udp, (struct sockaddr*)&sin, sizeof(sin)) != 0) {
|
||||
DEBUG(net, 1) ("[NET][UDP] error: bind failed on port %i", port);
|
||||
return false;
|
||||
}
|
||||
|
||||
// enable broadcasting
|
||||
// allow reusing
|
||||
{
|
||||
unsigned long val = 1;
|
||||
setsockopt(udp, SOL_SOCKET, SO_BROADCAST, (char *) &val , sizeof(val));
|
||||
val = 1;
|
||||
setsockopt(udp, SOL_SOCKET, SO_REUSEADDR, (char *) &val , sizeof(val));
|
||||
}
|
||||
|
||||
if (_network_udp_server)
|
||||
_udp_server_socket = udp;
|
||||
else
|
||||
_udp_client_socket = udp;
|
||||
|
||||
DEBUG(net, 1)("[NET][UDP] Listening on port %d", port);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Close UDP connection
|
||||
void NetworkUDPClose(void)
|
||||
{
|
||||
DEBUG(net, 1) ("[NET][UDP] Closed listener");
|
||||
|
||||
if (_network_udp_server) {
|
||||
closesocket(_udp_server_socket);
|
||||
_udp_server_socket = INVALID_SOCKET;
|
||||
_network_udp_server = false;
|
||||
_network_udp_broadcast = 0;
|
||||
} else {
|
||||
closesocket(_udp_client_socket);
|
||||
_udp_client_socket = INVALID_SOCKET;
|
||||
_network_udp_broadcast = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Receive something on UDP level
|
||||
void NetworkUDPReceive(void)
|
||||
{
|
||||
struct sockaddr_in client_addr;
|
||||
#ifndef __MORPHOS__
|
||||
int client_len;
|
||||
#else
|
||||
LONG client_len; // for some reason we need a 'LONG' under MorphOS
|
||||
#endif
|
||||
int nbytes;
|
||||
static Packet *p = NULL;
|
||||
int packet_len;
|
||||
SOCKET udp;
|
||||
|
||||
if (_network_udp_server)
|
||||
udp = _udp_server_socket;
|
||||
else
|
||||
udp = _udp_client_socket;
|
||||
|
||||
// If p is NULL, malloc him.. this prevents unneeded mallocs
|
||||
if (p == NULL)
|
||||
p = malloc(sizeof(Packet));
|
||||
|
||||
packet_len = sizeof(p->buffer);
|
||||
client_len = sizeof(client_addr);
|
||||
|
||||
// Try to receive anything
|
||||
nbytes = recvfrom(udp, p->buffer, packet_len, 0, (struct sockaddr *)&client_addr, &client_len);
|
||||
|
||||
// We got some bytes.. just asume we receive the whole packet
|
||||
if (nbytes > 0) {
|
||||
// Get the size of the buffer
|
||||
p->size = (uint16)p->buffer[0];
|
||||
p->size += (uint16)p->buffer[1] << 8;
|
||||
// Put the position on the right place
|
||||
p->pos = 2;
|
||||
p->next = NULL;
|
||||
|
||||
// Handle the packet
|
||||
NetworkHandleUDPPacket(p, &client_addr);
|
||||
|
||||
// Free the packet
|
||||
free(p);
|
||||
p = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Broadcast to all ips
|
||||
void NetworkUDPBroadCast(void)
|
||||
{
|
||||
int i;
|
||||
struct sockaddr_in out_addr;
|
||||
byte *bcptr;
|
||||
uint32 bcaddr;
|
||||
Packet *p;
|
||||
|
||||
// Init the packet
|
||||
p = NetworkSend_Init(PACKET_UDP_FIND_SERVER);
|
||||
|
||||
// Go through all the ips on this pc
|
||||
i = 0;
|
||||
while (_network_ip_list[i] != 0) {
|
||||
bcaddr = _network_ip_list[i];
|
||||
bcptr = (byte *)&bcaddr;
|
||||
// Make the address a broadcast address
|
||||
bcptr[3] = 255;
|
||||
|
||||
DEBUG(net, 6)("[NET][UDP] Broadcasting to %s", inet_ntoa(*(struct in_addr *)&bcaddr));
|
||||
|
||||
out_addr.sin_family = AF_INET;
|
||||
out_addr.sin_port = htons(_network_server_port);
|
||||
out_addr.sin_addr.s_addr = bcaddr;
|
||||
|
||||
NetworkSendUDP_Packet(p, &out_addr);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
free(p);
|
||||
}
|
||||
|
||||
// Find all servers
|
||||
void NetworkUDPSearchGame(void)
|
||||
{
|
||||
// We are still searching..
|
||||
if (_network_udp_broadcast > 0)
|
||||
return;
|
||||
|
||||
// No UDP-socket yet..
|
||||
if (_udp_client_socket == INVALID_SOCKET)
|
||||
if (!NetworkUDPListen(0, 0))
|
||||
return;
|
||||
|
||||
DEBUG(net, 0)("[NET][UDP] Searching server");
|
||||
|
||||
NetworkUDPBroadCast();
|
||||
_network_udp_broadcast = 300; // Stay searching for 300 ticks
|
||||
}
|
||||
|
||||
void NetworkUDPQueryServer(const byte* host, unsigned short port)
|
||||
{
|
||||
struct sockaddr_in out_addr;
|
||||
Packet *p;
|
||||
NetworkGameList *item;
|
||||
char hostname[NETWORK_HOSTNAME_LENGTH];
|
||||
|
||||
// No UDP-socket yet..
|
||||
if (_udp_client_socket == INVALID_SOCKET)
|
||||
if (!NetworkUDPListen(0, 0))
|
||||
return;
|
||||
|
||||
ttd_strlcpy(hostname, host, sizeof(hostname));
|
||||
|
||||
out_addr.sin_family = AF_INET;
|
||||
out_addr.sin_port = htons(port);
|
||||
out_addr.sin_addr.s_addr = NetworkResolveHost(host);
|
||||
|
||||
// Clear item in gamelist
|
||||
item = NetworkGameListAddItem(inet_addr(inet_ntoa(out_addr.sin_addr)), ntohs(out_addr.sin_port));
|
||||
memset(&item->info, 0, sizeof(item->info));
|
||||
snprintf(item->info.server_name, sizeof(item->info.server_name), "%s", hostname);
|
||||
snprintf(item->info.hostname, sizeof(item->info.hostname), "%s", hostname);
|
||||
item->online = false;
|
||||
|
||||
// Init the packet
|
||||
p = NetworkSend_Init(PACKET_UDP_FIND_SERVER);
|
||||
|
||||
NetworkSendUDP_Packet(p, &out_addr);
|
||||
|
||||
free(p);
|
||||
|
||||
UpdateNetworkGameWindow(false);
|
||||
}
|
||||
|
||||
void NetworkUDPInitialize(void)
|
||||
{
|
||||
_udp_client_socket = INVALID_SOCKET;
|
||||
_udp_server_socket = INVALID_SOCKET;
|
||||
|
||||
_network_udp_server = false;
|
||||
_network_udp_broadcast = 0;
|
||||
}
|
||||
|
||||
#endif /* ENABLE_NETWORK */
|
@ -0,0 +1,10 @@
|
||||
#ifndef NETWORK_LAN_H
|
||||
#define NETWORK_LAN_H
|
||||
|
||||
void NetworkUDPInitialize(void);
|
||||
bool NetworkUDPListen(uint32 host, uint16 port);
|
||||
void NetworkUDPReceive(void);
|
||||
void NetworkUDPSearchGame(void);
|
||||
void NetworkUDPQueryServer(const byte* host, unsigned short port);
|
||||
|
||||
#endif /* NETWORK_LAN_H */
|
@ -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
|
||||
|
@ -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 */
|
Loading…
Reference in New Issue