2007-11-25 19:42:40 +00:00
|
|
|
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
2007-08-22 07:52:33 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2007-12-15 10:24:49 +00:00
|
|
|
#include <engine/e_system.h>
|
|
|
|
#include <engine/e_config.h>
|
|
|
|
#include <engine/e_engine.h>
|
2008-01-19 10:57:25 +00:00
|
|
|
#include <engine/e_server_interface.h>
|
2007-08-22 07:52:33 +00:00
|
|
|
|
2007-12-15 10:24:49 +00:00
|
|
|
#include <engine/e_protocol.h>
|
|
|
|
#include <engine/e_snapshot.h>
|
2007-08-22 07:52:33 +00:00
|
|
|
|
2007-12-15 10:24:49 +00:00
|
|
|
#include <engine/e_compression.h>
|
2007-08-22 07:52:33 +00:00
|
|
|
|
2007-12-15 10:24:49 +00:00
|
|
|
#include <engine/e_network.h>
|
|
|
|
#include <engine/e_config.h>
|
|
|
|
#include <engine/e_packer.h>
|
|
|
|
#include <engine/e_datafile.h>
|
2007-08-22 07:52:33 +00:00
|
|
|
|
|
|
|
#include <mastersrv/mastersrv.h>
|
|
|
|
|
|
|
|
static SNAPBUILD builder;
|
|
|
|
|
2007-09-09 18:21:14 +00:00
|
|
|
static int64 game_start_time;
|
|
|
|
static int current_tick = 0;
|
|
|
|
|
2007-11-27 19:51:48 +00:00
|
|
|
static int browseinfo_gametype = -1;
|
|
|
|
static int browseinfo_progression = -1;
|
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
static int64 lastheartbeat;
|
|
|
|
static NETADDR4 master_server;
|
|
|
|
|
2007-09-23 22:54:31 +00:00
|
|
|
static char current_map[64];
|
2007-12-03 18:47:29 +00:00
|
|
|
static int current_map_crc;
|
2007-09-23 22:54:31 +00:00
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
void *snap_new_item(int type, int id, int size)
|
|
|
|
{
|
|
|
|
dbg_assert(type >= 0 && type <=0xffff, "incorrect type");
|
|
|
|
dbg_assert(id >= 0 && id <=0xffff, "incorrect id");
|
|
|
|
return snapbuild_new_item(&builder, type, id, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
short next;
|
2007-09-09 18:21:14 +00:00
|
|
|
short state; /* 0 = free, 1 = alloced, 2 = timed */
|
2007-12-12 19:52:57 +00:00
|
|
|
int timeout;
|
2007-08-22 07:52:33 +00:00
|
|
|
} SNAP_ID;
|
|
|
|
|
2007-12-10 18:21:22 +00:00
|
|
|
static const int MAX_IDS = 16*1024; /* should be lowered */
|
|
|
|
static SNAP_ID snap_ids[16*1024];
|
2007-08-22 07:52:33 +00:00
|
|
|
static int snap_first_free_id;
|
|
|
|
static int snap_first_timed_id;
|
|
|
|
static int snap_last_timed_id;
|
|
|
|
static int snap_id_usage;
|
|
|
|
static int snap_id_inusage;
|
|
|
|
static int snap_id_inited = 0;
|
|
|
|
|
2007-09-09 18:21:14 +00:00
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
SRVCLIENT_STATE_EMPTY = 0,
|
2007-11-04 00:19:41 +00:00
|
|
|
SRVCLIENT_STATE_CONNECTING,
|
|
|
|
SRVCLIENT_STATE_READY,
|
2007-12-17 22:14:00 +00:00
|
|
|
SRVCLIENT_STATE_INGAME,
|
|
|
|
|
|
|
|
SRVCLIENT_SNAPRATE_INIT=0,
|
|
|
|
SRVCLIENT_SNAPRATE_FULL,
|
|
|
|
SRVCLIENT_SNAPRATE_RECOVER
|
2007-09-09 18:21:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
int data[MAX_INPUT_SIZE];
|
|
|
|
int pred_tick; /* tick that the client predicted for the input */
|
|
|
|
int game_tick; /* the tick that was chosen for the input */
|
|
|
|
int64 timeleft; /* how much time in ms there were left before this should be applied */
|
|
|
|
} CLIENT_INPUT;
|
|
|
|
|
|
|
|
/* */
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
/* connection state info */
|
|
|
|
int state;
|
|
|
|
int latency;
|
2007-12-17 22:14:00 +00:00
|
|
|
int snap_rate;
|
2007-09-09 18:21:14 +00:00
|
|
|
|
|
|
|
int last_acked_snapshot;
|
|
|
|
SNAPSTORAGE snapshots;
|
|
|
|
|
2008-01-12 12:08:26 +00:00
|
|
|
CLIENT_INPUT latestinput;
|
2007-09-09 18:21:14 +00:00
|
|
|
CLIENT_INPUT inputs[200]; /* TODO: handle input better */
|
|
|
|
int current_input;
|
|
|
|
|
|
|
|
char name[MAX_NAME_LENGTH];
|
|
|
|
char clan[MAX_CLANNAME_LENGTH];
|
2007-12-16 20:16:27 +00:00
|
|
|
int score;
|
2007-09-09 18:21:14 +00:00
|
|
|
} CLIENT;
|
|
|
|
|
|
|
|
static CLIENT clients[MAX_CLIENTS];
|
|
|
|
static NETSERVER *net;
|
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
static void snap_init_id()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for(i = 0; i < MAX_IDS; i++)
|
|
|
|
{
|
|
|
|
snap_ids[i].next = i+1;
|
|
|
|
snap_ids[i].state = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
snap_ids[MAX_IDS-1].next = -1;
|
|
|
|
snap_first_free_id = 0;
|
|
|
|
snap_first_timed_id = -1;
|
|
|
|
snap_last_timed_id = -1;
|
|
|
|
snap_id_usage = 0;
|
|
|
|
snap_id_inusage = 0;
|
|
|
|
|
|
|
|
snap_id_inited = 1;
|
|
|
|
}
|
|
|
|
|
2007-12-10 18:21:22 +00:00
|
|
|
static void snap_remove_first_timeout()
|
|
|
|
{
|
|
|
|
int next_timed = snap_ids[snap_first_timed_id].next;
|
|
|
|
|
|
|
|
/* add it to the free list */
|
|
|
|
snap_ids[snap_first_timed_id].next = snap_first_free_id;
|
|
|
|
snap_ids[snap_first_timed_id].state = 0;
|
|
|
|
snap_first_free_id = snap_first_timed_id;
|
|
|
|
|
|
|
|
/* remove it from the timed list */
|
|
|
|
snap_first_timed_id = next_timed;
|
|
|
|
if(snap_first_timed_id == -1)
|
|
|
|
snap_last_timed_id = -1;
|
|
|
|
|
|
|
|
snap_id_usage--;
|
|
|
|
}
|
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
int snap_new_id()
|
|
|
|
{
|
2007-10-06 17:01:06 +00:00
|
|
|
int id;
|
2007-12-12 19:52:57 +00:00
|
|
|
int64 now = time_get();
|
2007-08-22 07:52:33 +00:00
|
|
|
dbg_assert(snap_id_inited == 1, "requesting id too soon");
|
|
|
|
|
2007-12-12 19:52:57 +00:00
|
|
|
|
2007-09-09 18:21:14 +00:00
|
|
|
/* process timed ids */
|
2007-12-12 19:52:57 +00:00
|
|
|
while(snap_first_timed_id != -1 && snap_ids[snap_first_timed_id].timeout < now)
|
2007-12-10 18:21:22 +00:00
|
|
|
snap_remove_first_timeout();
|
2007-08-22 07:52:33 +00:00
|
|
|
|
2007-10-06 17:01:06 +00:00
|
|
|
id = snap_first_free_id;
|
2007-08-22 07:52:33 +00:00
|
|
|
dbg_assert(id != -1, "id error");
|
|
|
|
snap_first_free_id = snap_ids[snap_first_free_id].next;
|
|
|
|
snap_ids[id].state = 1;
|
|
|
|
snap_id_usage++;
|
|
|
|
snap_id_inusage++;
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2007-12-10 18:21:22 +00:00
|
|
|
void snap_timeout_ids()
|
|
|
|
{
|
|
|
|
/* process timed ids */
|
|
|
|
while(snap_first_timed_id != -1)
|
|
|
|
snap_remove_first_timeout();
|
|
|
|
}
|
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
void snap_free_id(int id)
|
|
|
|
{
|
|
|
|
dbg_assert(snap_ids[id].state == 1, "id is not alloced");
|
|
|
|
|
|
|
|
snap_id_inusage--;
|
|
|
|
snap_ids[id].state = 2;
|
2007-12-12 19:52:57 +00:00
|
|
|
snap_ids[id].timeout = time_get()+time_freq()*5;
|
2007-08-22 07:52:33 +00:00
|
|
|
snap_ids[id].next = -1;
|
|
|
|
|
|
|
|
if(snap_last_timed_id != -1)
|
|
|
|
{
|
|
|
|
snap_ids[snap_last_timed_id].next = id;
|
|
|
|
snap_last_timed_id = id;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
snap_first_timed_id = id;
|
|
|
|
snap_last_timed_id = id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-01-12 12:08:26 +00:00
|
|
|
int *server_latestinput(int client_id, int *size)
|
|
|
|
{
|
|
|
|
if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY)
|
|
|
|
return 0;
|
|
|
|
return clients[client_id].latestinput.data;
|
|
|
|
}
|
|
|
|
|
2007-11-04 00:19:41 +00:00
|
|
|
const char *server_clientname(int client_id)
|
|
|
|
{
|
|
|
|
if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY)
|
|
|
|
return "(invalid client)";
|
|
|
|
return clients[client_id].name;
|
|
|
|
}
|
|
|
|
|
|
|
|
void server_setclientname(int client_id, const char *name)
|
|
|
|
{
|
|
|
|
if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY)
|
|
|
|
return;
|
|
|
|
strncpy(clients[client_id].name, name, MAX_NAME_LENGTH);
|
|
|
|
}
|
|
|
|
|
2007-12-16 20:16:27 +00:00
|
|
|
void server_setclientscore(int client_id, int score)
|
|
|
|
{
|
|
|
|
if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY)
|
|
|
|
return;
|
|
|
|
clients[client_id].score = score;
|
|
|
|
}
|
|
|
|
|
2007-11-27 19:51:48 +00:00
|
|
|
void server_setbrowseinfo(int game_type, int progression)
|
|
|
|
{
|
|
|
|
browseinfo_gametype = game_type;
|
|
|
|
browseinfo_progression = progression;
|
2007-12-18 23:21:57 +00:00
|
|
|
if(browseinfo_progression > 100)
|
|
|
|
browseinfo_progression = 100;
|
|
|
|
if(browseinfo_progression < -1)
|
|
|
|
browseinfo_progression = -1;
|
2007-11-27 19:51:48 +00:00
|
|
|
}
|
|
|
|
|
2007-12-17 00:27:41 +00:00
|
|
|
void server_kick(int client_id, const char *reason)
|
|
|
|
{
|
2008-02-02 12:38:36 +00:00
|
|
|
if(client_id < 0 || client_id > MAX_CLIENTS)
|
|
|
|
return;
|
|
|
|
|
2007-12-17 00:27:41 +00:00
|
|
|
if(clients[client_id].state != SRVCLIENT_STATE_EMPTY)
|
|
|
|
netserver_drop(net, client_id, reason);
|
|
|
|
}
|
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
int server_tick()
|
|
|
|
{
|
|
|
|
return current_tick;
|
|
|
|
}
|
|
|
|
|
2007-09-09 18:21:14 +00:00
|
|
|
int64 server_tick_start_time(int tick)
|
|
|
|
{
|
|
|
|
return game_start_time + (time_freq()*tick)/SERVER_TICK_SPEED;
|
|
|
|
}
|
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
int server_tickspeed()
|
|
|
|
{
|
|
|
|
return SERVER_TICK_SPEED;
|
|
|
|
}
|
|
|
|
|
|
|
|
int server_init()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for(i = 0; i < MAX_CLIENTS; i++)
|
|
|
|
{
|
|
|
|
clients[i].state = SRVCLIENT_STATE_EMPTY;
|
|
|
|
clients[i].name[0] = 0;
|
|
|
|
clients[i].clan[0] = 0;
|
|
|
|
snapstorage_init(&clients[i].snapshots);
|
|
|
|
}
|
|
|
|
|
|
|
|
current_tick = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int server_getclientinfo(int client_id, CLIENT_INFO *info)
|
|
|
|
{
|
|
|
|
dbg_assert(client_id >= 0 && client_id < MAX_CLIENTS, "client_id is not valid");
|
|
|
|
dbg_assert(info != 0, "info can not be null");
|
|
|
|
|
|
|
|
if(clients[client_id].state == SRVCLIENT_STATE_INGAME)
|
|
|
|
{
|
|
|
|
info->name = clients[client_id].name;
|
|
|
|
info->latency = clients[client_id].latency;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int server_send_msg(int client_id)
|
|
|
|
{
|
|
|
|
const MSG_INFO *info = msg_get_info();
|
|
|
|
NETPACKET packet;
|
|
|
|
mem_zero(&packet, sizeof(NETPACKET));
|
|
|
|
|
|
|
|
packet.client_id = client_id;
|
|
|
|
packet.data = info->data;
|
|
|
|
packet.data_size = info->size;
|
|
|
|
|
|
|
|
if(info->flags&MSGFLAG_VITAL)
|
|
|
|
packet.flags = PACKETFLAG_VITAL;
|
|
|
|
|
|
|
|
if(client_id == -1)
|
|
|
|
{
|
2007-09-09 18:21:14 +00:00
|
|
|
/* broadcast */
|
2007-08-22 07:52:33 +00:00
|
|
|
int i;
|
|
|
|
for(i = 0; i < MAX_CLIENTS; i++)
|
|
|
|
if(clients[i].state == SRVCLIENT_STATE_INGAME)
|
|
|
|
{
|
|
|
|
packet.client_id = i;
|
|
|
|
netserver_send(net, &packet);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
netserver_send(net, &packet);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void server_do_snap()
|
|
|
|
{
|
2007-09-09 18:21:14 +00:00
|
|
|
int i, k;
|
2007-12-16 15:33:44 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
static PERFORMACE_INFO scope = {"presnap", 0};
|
|
|
|
perf_start(&scope);
|
|
|
|
mods_presnap();
|
|
|
|
perf_end();
|
|
|
|
}
|
2007-08-22 07:52:33 +00:00
|
|
|
|
2007-09-09 18:21:14 +00:00
|
|
|
for(i = 0; i < MAX_CLIENTS; i++)
|
2007-08-22 07:52:33 +00:00
|
|
|
{
|
2007-12-17 22:14:00 +00:00
|
|
|
/* client must be ingame to recive snapshots */
|
|
|
|
if(clients[i].state != SRVCLIENT_STATE_INGAME)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* this client is trying to recover, don't spam snapshots */
|
|
|
|
if(clients[i].snap_rate == SRVCLIENT_SNAPRATE_RECOVER && (server_tick()%50) != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* this client is trying to recover, don't spam snapshots */
|
|
|
|
if(clients[i].snap_rate == SRVCLIENT_SNAPRATE_INIT && (server_tick()%10) != 0)
|
|
|
|
continue;
|
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
{
|
|
|
|
char data[MAX_SNAPSHOT_SIZE];
|
|
|
|
char deltadata[MAX_SNAPSHOT_SIZE];
|
|
|
|
char compdata[MAX_SNAPSHOT_SIZE];
|
2007-10-06 17:01:06 +00:00
|
|
|
int snapshot_size;
|
|
|
|
int crc;
|
|
|
|
static SNAPSHOT emptysnap;
|
|
|
|
SNAPSHOT *deltashot = &emptysnap;
|
|
|
|
int deltashot_size;
|
|
|
|
int delta_tick = -1;
|
|
|
|
int input_predtick = -1;
|
|
|
|
int64 timeleft = 0;
|
|
|
|
int deltasize;
|
2007-12-16 15:33:44 +00:00
|
|
|
static PERFORMACE_INFO scope = {"build", 0};
|
|
|
|
perf_start(&scope);
|
2007-10-06 17:01:06 +00:00
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
snapbuild_init(&builder);
|
2007-12-16 15:33:44 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
static PERFORMACE_INFO scope = {"modsnap", 0};
|
|
|
|
perf_start(&scope);
|
|
|
|
mods_snap(i);
|
|
|
|
perf_end();
|
|
|
|
}
|
2007-08-22 07:52:33 +00:00
|
|
|
|
2007-09-09 18:21:14 +00:00
|
|
|
/* finish snapshot */
|
2007-10-06 17:01:06 +00:00
|
|
|
snapshot_size = snapbuild_finish(&builder, data);
|
|
|
|
crc = snapshot_crc((SNAPSHOT*)data);
|
2007-08-22 07:52:33 +00:00
|
|
|
|
2007-09-09 18:21:14 +00:00
|
|
|
/* remove old snapshos */
|
|
|
|
/* keep 1 seconds worth of snapshots */
|
2007-08-22 07:52:33 +00:00
|
|
|
snapstorage_purge_until(&clients[i].snapshots, current_tick-SERVER_TICK_SPEED);
|
|
|
|
|
2007-09-09 18:21:14 +00:00
|
|
|
/* save it the snapshot */
|
2007-08-22 07:52:33 +00:00
|
|
|
snapstorage_add(&clients[i].snapshots, current_tick, time_get(), snapshot_size, data);
|
|
|
|
|
2007-09-09 18:21:14 +00:00
|
|
|
/* find snapshot that we can preform delta against */
|
2007-08-22 07:52:33 +00:00
|
|
|
emptysnap.data_size = 0;
|
|
|
|
emptysnap.num_items = 0;
|
|
|
|
|
|
|
|
{
|
|
|
|
deltashot_size = snapstorage_get(&clients[i].snapshots, clients[i].last_acked_snapshot, 0, &deltashot);
|
|
|
|
if(deltashot_size >= 0)
|
|
|
|
delta_tick = clients[i].last_acked_snapshot;
|
2007-12-17 22:14:00 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* no acked package found, force client to recover rate */
|
|
|
|
if(clients[i].snap_rate == SRVCLIENT_SNAPRATE_FULL)
|
|
|
|
clients[i].snap_rate = SRVCLIENT_SNAPRATE_RECOVER;
|
|
|
|
}
|
2007-08-22 07:52:33 +00:00
|
|
|
}
|
|
|
|
|
2007-10-03 21:32:02 +00:00
|
|
|
for(k = 0; k < 200; k++) /* TODO: do this better */
|
2007-09-09 18:21:14 +00:00
|
|
|
{
|
|
|
|
if(clients[i].inputs[k].game_tick == current_tick)
|
|
|
|
{
|
|
|
|
timeleft = clients[i].inputs[k].timeleft;
|
|
|
|
input_predtick = clients[i].inputs[k].pred_tick;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* create delta */
|
2007-12-16 15:33:44 +00:00
|
|
|
{
|
|
|
|
static PERFORMACE_INFO scope = {"delta", 0};
|
|
|
|
perf_start(&scope);
|
|
|
|
deltasize = snapshot_create_delta(deltashot, (SNAPSHOT*)data, deltadata);
|
|
|
|
perf_end();
|
|
|
|
}
|
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
|
|
|
|
if(deltasize)
|
|
|
|
{
|
2007-09-09 18:21:14 +00:00
|
|
|
/* compress it */
|
2007-08-22 07:52:33 +00:00
|
|
|
unsigned char intdata[MAX_SNAPSHOT_SIZE];
|
2007-12-16 15:33:44 +00:00
|
|
|
int intsize;
|
|
|
|
int snapshot_size;
|
2007-08-22 07:52:33 +00:00
|
|
|
const int max_size = MAX_SNAPSHOT_PACKSIZE;
|
2007-12-16 15:33:44 +00:00
|
|
|
int numpackets;
|
2007-08-22 07:52:33 +00:00
|
|
|
int n, left;
|
2007-12-16 15:33:44 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
static PERFORMACE_INFO scope = {"compress", 0};
|
|
|
|
perf_start(&scope);
|
|
|
|
|
|
|
|
{
|
|
|
|
static PERFORMACE_INFO scope = {"int", 0};
|
|
|
|
perf_start(&scope);
|
|
|
|
intsize = intpack_compress(deltadata, deltasize, intdata);
|
|
|
|
perf_end();
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
static PERFORMACE_INFO scope = {"zero", 0};
|
|
|
|
perf_start(&scope);
|
|
|
|
snapshot_size = zerobit_compress(intdata, intsize, compdata);
|
|
|
|
perf_end();
|
|
|
|
}
|
|
|
|
perf_end();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
numpackets = (snapshot_size+max_size-1)/max_size;
|
|
|
|
|
|
|
|
|
2007-12-10 18:21:22 +00:00
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
for(n = 0, left = snapshot_size; left; n++)
|
|
|
|
{
|
|
|
|
int chunk = left < max_size ? left : max_size;
|
|
|
|
left -= chunk;
|
|
|
|
|
2007-12-10 18:21:22 +00:00
|
|
|
if(numpackets == 1)
|
|
|
|
msg_pack_start_system(NETMSG_SNAPSINGLE, 0);
|
|
|
|
else
|
|
|
|
msg_pack_start_system(NETMSG_SNAP, 0);
|
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
msg_pack_int(current_tick);
|
2007-09-09 18:21:14 +00:00
|
|
|
msg_pack_int(current_tick-delta_tick); /* compressed with */
|
|
|
|
msg_pack_int(input_predtick);
|
|
|
|
msg_pack_int((timeleft*1000)/time_freq());
|
2007-12-10 18:21:22 +00:00
|
|
|
|
|
|
|
if(numpackets != 1)
|
|
|
|
{
|
|
|
|
msg_pack_int(numpackets);
|
|
|
|
msg_pack_int(n);
|
|
|
|
}
|
|
|
|
|
2007-08-22 21:21:20 +00:00
|
|
|
msg_pack_int(crc);
|
2007-08-22 07:52:33 +00:00
|
|
|
msg_pack_int(chunk);
|
|
|
|
msg_pack_raw(&compdata[n*max_size], chunk);
|
|
|
|
msg_pack_end();
|
|
|
|
server_send_msg(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
msg_pack_start_system(NETMSG_SNAPEMPTY, 0);
|
|
|
|
msg_pack_int(current_tick);
|
2007-09-09 18:21:14 +00:00
|
|
|
msg_pack_int(current_tick-delta_tick); /* compressed with */
|
|
|
|
msg_pack_int(input_predtick);
|
|
|
|
msg_pack_int((timeleft*1000)/time_freq());
|
2007-08-22 07:52:33 +00:00
|
|
|
msg_pack_end();
|
|
|
|
server_send_msg(i);
|
|
|
|
}
|
2007-12-16 15:33:44 +00:00
|
|
|
|
|
|
|
perf_end();
|
2007-08-22 07:52:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mods_postsnap();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int new_client_callback(int cid, void *user)
|
|
|
|
{
|
2007-09-09 18:21:14 +00:00
|
|
|
int i;
|
2007-08-22 07:52:33 +00:00
|
|
|
clients[cid].state = SRVCLIENT_STATE_CONNECTING;
|
|
|
|
clients[cid].name[0] = 0;
|
|
|
|
clients[cid].clan[0] = 0;
|
2007-09-09 18:21:14 +00:00
|
|
|
|
|
|
|
/* reset input */
|
|
|
|
for(i = 0; i < 200; i++)
|
|
|
|
{
|
|
|
|
clients[cid].inputs[i].game_tick = -1;
|
|
|
|
clients[cid].inputs[i].pred_tick = -1;
|
|
|
|
}
|
|
|
|
clients[cid].current_input = 0;
|
|
|
|
|
2008-01-12 12:08:26 +00:00
|
|
|
mem_zero(&clients[cid].latestinput, sizeof(clients[cid].latestinput));
|
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
snapstorage_purge_all(&clients[cid].snapshots);
|
|
|
|
clients[cid].last_acked_snapshot = -1;
|
2007-12-17 22:14:00 +00:00
|
|
|
clients[cid].snap_rate = SRVCLIENT_SNAPRATE_INIT;
|
2007-12-16 20:16:27 +00:00
|
|
|
clients[cid].score = 0;
|
2007-08-22 07:52:33 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int del_client_callback(int cid, void *user)
|
|
|
|
{
|
2007-12-09 15:46:44 +00:00
|
|
|
/* notify the mod about the drop */
|
|
|
|
if(clients[cid].state == SRVCLIENT_STATE_READY ||
|
|
|
|
clients[cid].state == SRVCLIENT_STATE_INGAME)
|
|
|
|
{
|
|
|
|
mods_client_drop(cid);
|
|
|
|
}
|
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
clients[cid].state = SRVCLIENT_STATE_EMPTY;
|
|
|
|
clients[cid].name[0] = 0;
|
|
|
|
clients[cid].clan[0] = 0;
|
|
|
|
snapstorage_purge_all(&clients[cid].snapshots);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void server_send_map(int cid)
|
|
|
|
{
|
|
|
|
msg_pack_start_system(NETMSG_MAP, MSGFLAG_VITAL);
|
|
|
|
msg_pack_string(config.sv_map, 0);
|
2007-12-03 18:47:29 +00:00
|
|
|
msg_pack_int(current_map_crc);
|
2007-08-22 07:52:33 +00:00
|
|
|
msg_pack_end();
|
|
|
|
server_send_msg(cid);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void server_send_heartbeat()
|
|
|
|
{
|
2007-12-13 20:09:07 +00:00
|
|
|
static unsigned char data[sizeof(SERVERBROWSE_HEARTBEAT) + 2];
|
|
|
|
unsigned short port = config.sv_port;
|
2007-08-22 07:52:33 +00:00
|
|
|
NETPACKET packet;
|
2007-12-13 20:09:07 +00:00
|
|
|
|
|
|
|
mem_copy(data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT));
|
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
packet.client_id = -1;
|
|
|
|
packet.address = master_server;
|
|
|
|
packet.flags = PACKETFLAG_CONNLESS;
|
2007-12-13 20:09:07 +00:00
|
|
|
packet.data_size = sizeof(SERVERBROWSE_HEARTBEAT) + 2;
|
|
|
|
packet.data = &data;
|
|
|
|
|
|
|
|
/* supply the set port that the master can use if it has problems */
|
|
|
|
if(config.sv_external_port)
|
|
|
|
port = config.sv_external_port;
|
|
|
|
data[sizeof(SERVERBROWSE_HEARTBEAT)] = port >> 8;
|
|
|
|
data[sizeof(SERVERBROWSE_HEARTBEAT)+1] = port&0xff;
|
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
netserver_send(net, &packet);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void server_process_client_packet(NETPACKET *packet)
|
|
|
|
{
|
|
|
|
int cid = packet->client_id;
|
|
|
|
int sys;
|
|
|
|
int msg = msg_unpack_start(packet->data, packet->data_size, &sys);
|
|
|
|
if(sys)
|
|
|
|
{
|
2007-09-09 18:21:14 +00:00
|
|
|
/* system message */
|
2007-08-22 07:52:33 +00:00
|
|
|
if(msg == NETMSG_INFO)
|
|
|
|
{
|
|
|
|
char version[64];
|
2007-10-06 17:01:06 +00:00
|
|
|
const char *password;
|
2007-08-22 07:52:33 +00:00
|
|
|
strncpy(version, msg_unpack_string(), 64);
|
2007-08-22 21:13:33 +00:00
|
|
|
if(strcmp(version, mods_net_version()) != 0)
|
2007-08-22 07:52:33 +00:00
|
|
|
{
|
2007-09-09 18:21:14 +00:00
|
|
|
/* OH FUCK! wrong version, drop him */
|
2007-08-22 07:52:33 +00:00
|
|
|
char reason[256];
|
2007-08-22 21:13:33 +00:00
|
|
|
sprintf(reason, "wrong version. server is running %s.", mods_net_version());
|
2007-08-22 07:52:33 +00:00
|
|
|
netserver_drop(net, cid, reason);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
strncpy(clients[cid].name, msg_unpack_string(), MAX_NAME_LENGTH);
|
|
|
|
strncpy(clients[cid].clan, msg_unpack_string(), MAX_CLANNAME_LENGTH);
|
2007-10-06 17:01:06 +00:00
|
|
|
password = msg_unpack_string();
|
2007-10-06 17:19:43 +00:00
|
|
|
|
|
|
|
if(config.password[0] != 0 && strcmp(config.password, password) != 0)
|
|
|
|
{
|
|
|
|
/* wrong password */
|
|
|
|
netserver_drop(net, cid, "wrong password");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
server_send_map(cid);
|
|
|
|
}
|
2007-11-04 00:19:41 +00:00
|
|
|
else if(msg == NETMSG_READY)
|
|
|
|
{
|
|
|
|
if(clients[cid].state == SRVCLIENT_STATE_CONNECTING)
|
|
|
|
{
|
|
|
|
dbg_msg("server", "player is ready. cid=%x", cid);
|
|
|
|
clients[cid].state = SRVCLIENT_STATE_READY;
|
|
|
|
mods_connected(cid);
|
|
|
|
}
|
|
|
|
}
|
2007-08-22 07:52:33 +00:00
|
|
|
else if(msg == NETMSG_ENTERGAME)
|
|
|
|
{
|
|
|
|
if(clients[cid].state != SRVCLIENT_STATE_INGAME)
|
|
|
|
{
|
|
|
|
dbg_msg("server", "player as entered the game. cid=%x", cid);
|
|
|
|
clients[cid].state = SRVCLIENT_STATE_INGAME;
|
|
|
|
mods_client_enter(cid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(msg == NETMSG_INPUT)
|
|
|
|
{
|
2007-10-06 17:01:06 +00:00
|
|
|
int tick, size, i;
|
|
|
|
CLIENT_INPUT *input;
|
2007-09-23 18:27:04 +00:00
|
|
|
int64 tagtime;
|
2007-10-06 17:01:06 +00:00
|
|
|
|
|
|
|
clients[cid].last_acked_snapshot = msg_unpack_int();
|
2007-12-17 22:14:00 +00:00
|
|
|
if(clients[cid].last_acked_snapshot > 0)
|
|
|
|
clients[cid].snap_rate = SRVCLIENT_SNAPRATE_FULL;
|
|
|
|
|
2007-09-23 18:27:04 +00:00
|
|
|
if(snapstorage_get(&clients[cid].snapshots, clients[cid].last_acked_snapshot, &tagtime, 0) >= 0)
|
|
|
|
clients[cid].latency = (int)(((time_get()-tagtime)*1000)/time_freq());
|
|
|
|
|
2007-10-06 17:01:06 +00:00
|
|
|
tick = msg_unpack_int();
|
|
|
|
size = msg_unpack_int();
|
2007-09-09 18:21:14 +00:00
|
|
|
|
2007-10-06 17:01:06 +00:00
|
|
|
input = &clients[cid].inputs[clients[cid].current_input];
|
2007-09-09 18:21:14 +00:00
|
|
|
input->timeleft = server_tick_start_time(tick)-time_get();
|
|
|
|
input->pred_tick = tick;
|
|
|
|
|
2007-10-28 16:48:52 +00:00
|
|
|
if(tick <= server_tick())
|
2007-09-09 18:21:14 +00:00
|
|
|
tick = server_tick()+1;
|
|
|
|
|
|
|
|
input->game_tick = tick;
|
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
for(i = 0; i < size/4; i++)
|
2007-09-09 18:21:14 +00:00
|
|
|
input->data[i] = msg_unpack_int();
|
2008-01-12 12:08:26 +00:00
|
|
|
|
|
|
|
mem_copy(clients[cid].latestinput.data, input->data, MAX_INPUT_SIZE*sizeof(int));
|
|
|
|
|
2007-09-09 18:21:14 +00:00
|
|
|
clients[cid].current_input++;
|
|
|
|
clients[cid].current_input %= 200;
|
2008-02-10 15:32:30 +00:00
|
|
|
|
|
|
|
/* call the mod with the fresh input data */
|
|
|
|
mods_client_direct_input(cid, clients[cid].latestinput.data);
|
2007-08-22 07:52:33 +00:00
|
|
|
}
|
2007-09-23 21:00:57 +00:00
|
|
|
else if(msg == NETMSG_CMD)
|
|
|
|
{
|
|
|
|
const char *pw = msg_unpack_string();
|
|
|
|
const char *cmd = msg_unpack_string();
|
|
|
|
if(config.rcon_password[0] != 0 && strcmp(pw, config.rcon_password) == 0)
|
|
|
|
{
|
|
|
|
dbg_msg("server", "cid=%d rcon='%s'", cid, cmd);
|
2008-02-02 12:38:36 +00:00
|
|
|
console_execute(cmd);
|
2007-09-23 21:00:57 +00:00
|
|
|
}
|
|
|
|
}
|
2008-02-10 15:32:30 +00:00
|
|
|
else if(msg == NETMSG_PING)
|
|
|
|
{
|
|
|
|
msg_pack_start_system(NETMSG_PING_REPLY, 0);
|
|
|
|
msg_pack_end();
|
|
|
|
server_send_msg(cid);
|
|
|
|
}
|
2007-08-22 07:52:33 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
dbg_msg("server", "strange message cid=%d msg=%d data_size=%d", cid, msg, packet->data_size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2007-09-09 18:21:14 +00:00
|
|
|
/* game message */
|
2007-08-22 07:52:33 +00:00
|
|
|
mods_message(msg, cid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-01-20 15:19:33 +00:00
|
|
|
static void server_send_serverinfo(NETADDR4 *addr, int lan)
|
2007-08-22 07:52:33 +00:00
|
|
|
{
|
|
|
|
NETPACKET packet;
|
|
|
|
PACKER p;
|
2007-10-03 21:32:02 +00:00
|
|
|
char buf[128];
|
|
|
|
|
|
|
|
/* count the players */
|
2007-08-22 07:52:33 +00:00
|
|
|
int c = 0;
|
|
|
|
int i;
|
|
|
|
for(i = 0; i < MAX_CLIENTS; i++)
|
|
|
|
{
|
2007-11-08 19:59:19 +00:00
|
|
|
if(clients[i].state != SRVCLIENT_STATE_EMPTY)
|
2007-08-22 07:52:33 +00:00
|
|
|
c++;
|
|
|
|
}
|
2007-10-03 21:32:02 +00:00
|
|
|
|
|
|
|
packer_reset(&p);
|
2008-01-20 15:19:33 +00:00
|
|
|
if(lan)
|
|
|
|
packer_add_raw(&p, SERVERBROWSE_INFO_LAN, sizeof(SERVERBROWSE_INFO_LAN));
|
|
|
|
else
|
|
|
|
packer_add_raw(&p, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO));
|
2007-12-19 19:56:38 +00:00
|
|
|
packer_add_string(&p, mods_version(), 32);
|
2007-10-03 21:32:02 +00:00
|
|
|
packer_add_string(&p, config.sv_name, 64);
|
|
|
|
packer_add_string(&p, config.sv_map, 32);
|
2007-11-27 19:51:48 +00:00
|
|
|
|
|
|
|
/* gametype */
|
|
|
|
sprintf(buf, "%d", browseinfo_gametype);
|
|
|
|
packer_add_string(&p, buf, 2);
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
i = 0;
|
|
|
|
if(strlen(config.password))
|
|
|
|
i |= 1;
|
|
|
|
sprintf(buf, "%d", i);
|
|
|
|
packer_add_string(&p, buf, 2);
|
|
|
|
|
|
|
|
/* progression */
|
|
|
|
sprintf(buf, "%d", browseinfo_progression);
|
|
|
|
packer_add_string(&p, buf, 4);
|
2007-10-03 21:32:02 +00:00
|
|
|
|
|
|
|
sprintf(buf, "%d", c); packer_add_string(&p, buf, 3); /* num players */
|
|
|
|
sprintf(buf, "%d", netserver_max_clients(net)); packer_add_string(&p, buf, 3); /* max players */
|
|
|
|
|
|
|
|
for(i = 0; i < MAX_CLIENTS; i++)
|
|
|
|
{
|
2007-11-26 19:25:55 +00:00
|
|
|
if(clients[i].state != SRVCLIENT_STATE_EMPTY)
|
2007-10-03 21:32:02 +00:00
|
|
|
{
|
|
|
|
packer_add_string(&p, clients[i].name, 48); /* player name */
|
2007-12-16 20:16:27 +00:00
|
|
|
sprintf(buf, "%d", clients[i].score); packer_add_string(&p, buf, 6); /* player score */
|
2007-10-03 21:32:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
|
|
|
|
packet.client_id = -1;
|
|
|
|
packet.address = *addr;
|
|
|
|
packet.flags = PACKETFLAG_CONNLESS;
|
|
|
|
packet.data_size = packer_size(&p);
|
|
|
|
packet.data = packer_data(&p);
|
|
|
|
netserver_send(net, &packet);
|
|
|
|
}
|
|
|
|
|
2007-12-18 23:37:22 +00:00
|
|
|
static void server_dump_status()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
NETADDR4 addr;
|
|
|
|
dbg_msg("server", "-- status --");
|
|
|
|
for(i = 0; i < MAX_CLIENTS; i++)
|
|
|
|
{
|
|
|
|
if(clients[i].state == SRVCLIENT_STATE_INGAME)
|
|
|
|
{
|
|
|
|
netserver_client_addr(net, i, &addr);
|
|
|
|
dbg_msg("server", "id=%d addr=%d.%d.%d.%d:%d name='%s' score=%d",
|
|
|
|
i, addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3], addr.port,
|
|
|
|
clients[i].name, clients[i].score);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dbg_msg("server", "-- end status --");
|
|
|
|
|
|
|
|
config.sv_status = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
|
|
|
|
static void server_send_fwcheckresponse(NETADDR4 *addr)
|
|
|
|
{
|
|
|
|
NETPACKET packet;
|
|
|
|
packet.client_id = -1;
|
|
|
|
packet.address = *addr;
|
|
|
|
packet.flags = PACKETFLAG_CONNLESS;
|
|
|
|
packet.data_size = sizeof(SERVERBROWSE_FWRESPONSE);
|
|
|
|
packet.data = SERVERBROWSE_FWRESPONSE;
|
|
|
|
netserver_send(net, &packet);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void server_pump_network()
|
|
|
|
{
|
2007-10-06 17:01:06 +00:00
|
|
|
NETPACKET packet;
|
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
netserver_update(net);
|
|
|
|
|
2007-09-09 18:21:14 +00:00
|
|
|
/* process packets */
|
2007-08-22 07:52:33 +00:00
|
|
|
while(netserver_recv(net, &packet))
|
|
|
|
{
|
|
|
|
if(packet.client_id == -1)
|
|
|
|
{
|
2007-09-09 18:21:14 +00:00
|
|
|
/* stateless */
|
2007-08-22 07:52:33 +00:00
|
|
|
if(packet.data_size == sizeof(SERVERBROWSE_GETINFO) &&
|
|
|
|
memcmp(packet.data, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0)
|
|
|
|
{
|
2008-01-20 15:19:33 +00:00
|
|
|
server_send_serverinfo(&packet.address, 0);
|
|
|
|
}
|
|
|
|
else if(packet.data_size == sizeof(SERVERBROWSE_GETINFO_LAN) &&
|
|
|
|
memcmp(packet.data, SERVERBROWSE_GETINFO_LAN, sizeof(SERVERBROWSE_GETINFO_LAN)) == 0)
|
|
|
|
{
|
|
|
|
server_send_serverinfo(&packet.address, 1);
|
2007-08-22 07:52:33 +00:00
|
|
|
}
|
|
|
|
else if(packet.data_size == sizeof(SERVERBROWSE_FWCHECK) &&
|
|
|
|
memcmp(packet.data, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0)
|
|
|
|
{
|
|
|
|
server_send_fwcheckresponse(&packet.address);
|
|
|
|
}
|
|
|
|
else if(packet.data_size == sizeof(SERVERBROWSE_FWOK) &&
|
|
|
|
memcmp(packet.data, SERVERBROWSE_FWOK, sizeof(SERVERBROWSE_FWOK)) == 0)
|
|
|
|
{
|
|
|
|
if(config.debug)
|
|
|
|
dbg_msg("server", "no firewall/nat problems detected");
|
|
|
|
}
|
|
|
|
else if(packet.data_size == sizeof(SERVERBROWSE_FWERROR) &&
|
|
|
|
memcmp(packet.data, SERVERBROWSE_FWERROR, sizeof(SERVERBROWSE_FWERROR)) == 0)
|
|
|
|
{
|
|
|
|
dbg_msg("server", "ERROR: the master server reports that clients can not connect to this server.");
|
2007-08-22 18:40:31 +00:00
|
|
|
dbg_msg("server", "ERROR: configure your firewall/nat to let trough udp on port %d.", config.sv_port);
|
2007-08-22 07:52:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
server_process_client_packet(&packet);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-23 22:54:31 +00:00
|
|
|
static int server_load_map(const char *mapname)
|
|
|
|
{
|
|
|
|
DATAFILE *df;
|
|
|
|
char buf[512];
|
|
|
|
sprintf(buf, "data/maps/%s.map", mapname);
|
|
|
|
df = datafile_load(buf);
|
|
|
|
if(!df)
|
|
|
|
return 0;
|
2007-12-03 18:47:29 +00:00
|
|
|
|
2007-12-10 18:21:22 +00:00
|
|
|
/* reinit snapshot ids */
|
|
|
|
snap_timeout_ids();
|
|
|
|
|
2007-12-03 18:47:29 +00:00
|
|
|
/* get the crc of the map */
|
|
|
|
current_map_crc = datafile_crc(buf);
|
|
|
|
dbg_msg("server", "%s crc is %08x", buf, current_map_crc);
|
2007-09-23 22:54:31 +00:00
|
|
|
|
|
|
|
strcpy(current_map, mapname);
|
|
|
|
map_set(df);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
|
|
|
|
static int server_run()
|
|
|
|
{
|
2007-10-06 17:01:06 +00:00
|
|
|
NETADDR4 bindaddr;
|
|
|
|
|
2007-12-03 18:47:29 +00:00
|
|
|
net_init();
|
2007-08-22 07:52:33 +00:00
|
|
|
|
|
|
|
snap_init_id();
|
|
|
|
|
2007-09-09 18:21:14 +00:00
|
|
|
/* load map */
|
2007-09-23 22:54:31 +00:00
|
|
|
if(!server_load_map(config.sv_map))
|
2007-08-22 07:52:33 +00:00
|
|
|
{
|
|
|
|
dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2007-09-09 18:21:14 +00:00
|
|
|
/* start server */
|
2007-08-22 07:52:33 +00:00
|
|
|
if(strlen(config.sv_bindaddr) && net_host_lookup(config.sv_bindaddr, config.sv_port, &bindaddr) != 0)
|
|
|
|
{
|
2007-09-09 18:21:14 +00:00
|
|
|
/* sweet! */
|
2007-08-22 07:52:33 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mem_zero(&bindaddr, sizeof(bindaddr));
|
|
|
|
bindaddr.port = config.sv_port;
|
|
|
|
}
|
|
|
|
|
|
|
|
net = netserver_open(bindaddr, config.sv_max_clients, 0);
|
|
|
|
if(!net)
|
|
|
|
{
|
|
|
|
dbg_msg("server", "couldn't open socket. port might already be in use");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
netserver_set_callbacks(net, new_client_callback, del_client_callback, 0);
|
|
|
|
|
|
|
|
dbg_msg("server", "server name is '%s'", config.sv_name);
|
|
|
|
dbg_msg("server", "masterserver is '%s'", config.masterserver);
|
2007-09-09 18:21:14 +00:00
|
|
|
if(net_host_lookup(config.masterserver, MASTERSERVER_PORT, &master_server) != 0)
|
2007-08-22 07:52:33 +00:00
|
|
|
{
|
2007-09-09 18:21:14 +00:00
|
|
|
/* TODO: fix me */
|
|
|
|
/*master_server = netaddr4(0, 0, 0, 0, 0); */
|
2007-08-22 07:52:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mods_init();
|
2007-08-22 21:13:33 +00:00
|
|
|
dbg_msg("server", "version %s", mods_net_version());
|
2007-08-22 07:52:33 +00:00
|
|
|
|
2007-10-06 17:01:06 +00:00
|
|
|
/* start game */
|
|
|
|
{
|
|
|
|
int64 time_per_heartbeat = time_freq() * 30;
|
|
|
|
int64 reporttime = time_get();
|
|
|
|
int reportinterval = 3;
|
2007-09-09 18:21:14 +00:00
|
|
|
|
2007-10-06 17:01:06 +00:00
|
|
|
lastheartbeat = 0;
|
|
|
|
game_start_time = time_get();
|
|
|
|
|
|
|
|
if(config.debug)
|
|
|
|
dbg_msg("server", "baseline memory usage %dk", mem_allocated()/1024);
|
2007-08-25 08:48:24 +00:00
|
|
|
|
2007-10-06 17:01:06 +00:00
|
|
|
while(1)
|
2007-09-23 22:54:31 +00:00
|
|
|
{
|
2007-12-16 15:33:44 +00:00
|
|
|
static PERFORMACE_INFO rootscope = {"root", 0};
|
2007-10-06 17:01:06 +00:00
|
|
|
int64 t = time_get();
|
2007-12-17 22:14:00 +00:00
|
|
|
int new_ticks = 0;
|
2007-12-16 15:33:44 +00:00
|
|
|
|
|
|
|
perf_start(&rootscope);
|
|
|
|
|
2007-10-06 17:01:06 +00:00
|
|
|
/* load new map TODO: don't poll this */
|
2007-12-09 13:16:18 +00:00
|
|
|
if(strcmp(config.sv_map, current_map) != 0 || config.sv_map_reload)
|
2007-09-23 22:54:31 +00:00
|
|
|
{
|
2007-12-09 13:16:18 +00:00
|
|
|
config.sv_map_reload = 0;
|
|
|
|
|
2007-10-06 17:01:06 +00:00
|
|
|
/* load map */
|
|
|
|
if(server_load_map(config.sv_map))
|
2007-09-23 22:54:31 +00:00
|
|
|
{
|
2007-10-06 17:01:06 +00:00
|
|
|
int c;
|
|
|
|
|
|
|
|
/* new map loaded */
|
|
|
|
mods_shutdown();
|
|
|
|
|
|
|
|
for(c = 0; c < MAX_CLIENTS; c++)
|
|
|
|
{
|
|
|
|
if(clients[c].state == SRVCLIENT_STATE_EMPTY)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
server_send_map(c);
|
|
|
|
clients[c].state = SRVCLIENT_STATE_CONNECTING;
|
|
|
|
clients[c].last_acked_snapshot = -1;
|
2007-12-17 22:14:00 +00:00
|
|
|
clients[c].snap_rate = SRVCLIENT_SNAPRATE_RECOVER;
|
2007-10-06 17:01:06 +00:00
|
|
|
snapstorage_purge_all(&clients[c].snapshots);
|
|
|
|
}
|
2007-09-23 22:54:31 +00:00
|
|
|
|
2007-10-06 17:01:06 +00:00
|
|
|
game_start_time = time_get();
|
|
|
|
current_tick = 0;
|
2007-12-10 21:05:57 +00:00
|
|
|
mods_init();
|
2007-10-06 17:01:06 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map);
|
|
|
|
config_set_sv_map(&config, current_map);
|
2007-09-23 22:54:31 +00:00
|
|
|
}
|
|
|
|
}
|
2007-10-06 17:01:06 +00:00
|
|
|
|
2007-12-17 22:14:00 +00:00
|
|
|
while(t > server_tick_start_time(current_tick+1))
|
2007-09-09 18:21:14 +00:00
|
|
|
{
|
2007-10-28 16:48:52 +00:00
|
|
|
current_tick++;
|
2007-12-17 22:14:00 +00:00
|
|
|
new_ticks++;
|
2007-10-28 16:48:52 +00:00
|
|
|
|
2007-10-06 17:01:06 +00:00
|
|
|
/* apply new input */
|
2007-09-09 18:21:14 +00:00
|
|
|
{
|
2007-12-16 15:33:44 +00:00
|
|
|
static PERFORMACE_INFO scope = {"input", 0};
|
2007-10-06 17:01:06 +00:00
|
|
|
int c, i;
|
2007-12-16 15:33:44 +00:00
|
|
|
|
|
|
|
perf_start(&scope);
|
|
|
|
|
2007-10-06 17:01:06 +00:00
|
|
|
for(c = 0; c < MAX_CLIENTS; c++)
|
2007-09-09 18:21:14 +00:00
|
|
|
{
|
2007-10-06 17:01:06 +00:00
|
|
|
if(clients[c].state == SRVCLIENT_STATE_EMPTY)
|
|
|
|
continue;
|
|
|
|
for(i = 0; i < 200; i++)
|
2007-09-09 18:21:14 +00:00
|
|
|
{
|
2007-10-06 17:01:06 +00:00
|
|
|
if(clients[c].inputs[i].game_tick == server_tick())
|
|
|
|
{
|
2008-02-10 15:32:30 +00:00
|
|
|
mods_client_predicted_input(c, clients[c].inputs[i].data);
|
2007-10-06 17:01:06 +00:00
|
|
|
break;
|
|
|
|
}
|
2007-09-09 18:21:14 +00:00
|
|
|
}
|
|
|
|
}
|
2007-12-16 15:33:44 +00:00
|
|
|
|
|
|
|
perf_end();
|
2007-09-09 18:21:14 +00:00
|
|
|
}
|
2007-10-06 17:01:06 +00:00
|
|
|
|
|
|
|
/* progress game */
|
|
|
|
{
|
2007-12-16 15:33:44 +00:00
|
|
|
static PERFORMACE_INFO scope = {"tick", 0};
|
|
|
|
perf_start(&scope);
|
2007-10-28 16:48:52 +00:00
|
|
|
mods_tick();
|
2007-12-16 15:33:44 +00:00
|
|
|
perf_end();
|
2007-10-06 17:01:06 +00:00
|
|
|
}
|
2007-12-17 22:14:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* snap game */
|
|
|
|
if(new_ticks)
|
|
|
|
{
|
2007-12-18 22:07:57 +00:00
|
|
|
if(config.sv_high_bandwidth || (current_tick%2) == 0)
|
2007-10-06 17:01:06 +00:00
|
|
|
{
|
2007-12-16 15:33:44 +00:00
|
|
|
static PERFORMACE_INFO scope = {"snap", 0};
|
|
|
|
perf_start(&scope);
|
2007-10-06 17:01:06 +00:00
|
|
|
server_do_snap();
|
2007-12-16 15:33:44 +00:00
|
|
|
perf_end();
|
2007-10-06 17:01:06 +00:00
|
|
|
}
|
2007-09-09 18:21:14 +00:00
|
|
|
}
|
2007-10-06 17:01:06 +00:00
|
|
|
|
|
|
|
if(config.sv_sendheartbeats)
|
2007-08-22 07:52:33 +00:00
|
|
|
{
|
2007-10-06 17:01:06 +00:00
|
|
|
if (t > lastheartbeat+time_per_heartbeat)
|
|
|
|
{
|
|
|
|
server_send_heartbeat();
|
|
|
|
lastheartbeat = t+time_per_heartbeat;
|
|
|
|
}
|
2007-08-22 07:52:33 +00:00
|
|
|
}
|
2008-02-10 15:32:30 +00:00
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
{
|
2007-12-16 15:33:44 +00:00
|
|
|
static PERFORMACE_INFO scope = {"net", 0};
|
|
|
|
perf_start(&scope);
|
2007-10-06 17:01:06 +00:00
|
|
|
server_pump_network();
|
2007-12-16 15:33:44 +00:00
|
|
|
perf_end();
|
2007-08-22 07:52:33 +00:00
|
|
|
}
|
2007-12-16 15:33:44 +00:00
|
|
|
|
|
|
|
perf_end();
|
2007-10-06 17:01:06 +00:00
|
|
|
|
|
|
|
if(reporttime < time_get())
|
2007-08-22 07:52:33 +00:00
|
|
|
{
|
2007-10-06 17:01:06 +00:00
|
|
|
if(config.debug)
|
|
|
|
{
|
2007-10-28 11:30:25 +00:00
|
|
|
static NETSTATS prev_stats;
|
|
|
|
NETSTATS stats;
|
|
|
|
netserver_stats(net, &stats);
|
2007-12-16 15:33:44 +00:00
|
|
|
|
|
|
|
perf_next();
|
|
|
|
|
2007-12-17 22:14:00 +00:00
|
|
|
if(config.dbg_pref)
|
|
|
|
perf_dump(&rootscope);
|
2007-10-28 11:30:25 +00:00
|
|
|
|
|
|
|
dbg_msg("server", "send=%8d recv=%8d",
|
|
|
|
(stats.send_bytes - prev_stats.send_bytes)/reportinterval,
|
|
|
|
(stats.recv_bytes - prev_stats.recv_bytes)/reportinterval);
|
|
|
|
|
|
|
|
prev_stats = stats;
|
2007-10-06 17:01:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
reporttime += time_freq()*reportinterval;
|
2007-08-22 07:52:33 +00:00
|
|
|
}
|
2007-12-17 22:14:00 +00:00
|
|
|
|
2007-12-18 23:37:22 +00:00
|
|
|
if(config.sv_status)
|
|
|
|
{
|
|
|
|
server_dump_status();
|
|
|
|
config.sv_status = 0;
|
|
|
|
}
|
|
|
|
|
2008-02-10 15:32:30 +00:00
|
|
|
/* wait for incomming data */
|
|
|
|
net_socket_read_wait(netserver_socket(net), 5);
|
|
|
|
|
|
|
|
/*
|
2007-12-17 22:14:00 +00:00
|
|
|
if(config.dbg_hitch)
|
|
|
|
{
|
|
|
|
thread_sleep(config.dbg_hitch);
|
|
|
|
config.dbg_hitch = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
thread_sleep(1);
|
2008-02-10 15:32:30 +00:00
|
|
|
*/
|
2007-08-22 07:52:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mods_shutdown();
|
|
|
|
map_unload();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-02-02 12:38:36 +00:00
|
|
|
static void server_register_commands()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2007-08-22 07:52:33 +00:00
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
2008-02-02 12:38:36 +00:00
|
|
|
/* init the engine */
|
2007-10-06 17:01:06 +00:00
|
|
|
dbg_msg("server", "starting...");
|
2008-02-02 12:38:36 +00:00
|
|
|
engine_init("Teewars");
|
|
|
|
|
|
|
|
/* register all console commands */
|
|
|
|
server_register_commands();
|
|
|
|
mods_console_init();
|
|
|
|
|
|
|
|
/* parse the command line arguments */
|
|
|
|
engine_parse_arguments(argc, argv);
|
|
|
|
|
|
|
|
/* run the server */
|
2007-08-22 07:52:33 +00:00
|
|
|
server_run();
|
|
|
|
return 0;
|
|
|
|
}
|
2007-08-22 21:13:33 +00:00
|
|
|
|