mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 01:58:19 +00:00
large change. moved around all source. splitted server and client into separate files
This commit is contained in:
parent
34e3df3966
commit
82023866ab
67
default.bam
67
default.bam
|
@ -31,56 +31,35 @@ function Copy(outputdir, ...)
|
|||
return outputs
|
||||
end
|
||||
|
||||
function makemap(output, input)
|
||||
print("makemap " .. PathFilename(output))
|
||||
os.execute("python scripts/tool.py " .. input .. " " .. output)
|
||||
end
|
||||
|
||||
function MakeMap(output, input)
|
||||
local output = bam_path_fix(output)
|
||||
local input = bam_path_fix(input)
|
||||
bam_add_job("makemap", output, input)
|
||||
bam_add_dependency(output, input)
|
||||
return output
|
||||
end
|
||||
|
||||
--
|
||||
baselib = Import("../baselib/baselib.bam")
|
||||
|
||||
--baselib = Import("src/baselib/baselib.bam")
|
||||
baselib = Import("../baselib/baselib.bam")
|
||||
settings = NewSettings()
|
||||
baselib.use(settings)
|
||||
baselib.apply(settings, "all")
|
||||
|
||||
server_settings = NewSettings()
|
||||
baselib.apply(server_settings, "network")
|
||||
|
||||
settings.cc.debug = 1
|
||||
settings.cc.optimize = 0
|
||||
settings.cc.flags = "-Wall"
|
||||
settings.cc.includes:add("src")
|
||||
--settings.cc.includes:add("src/baselib/include")
|
||||
|
||||
game_src = Collect("src/game/*.cpp")
|
||||
main_src = Collect("src/*.cpp")
|
||||
wavpack_src = Collect("src/wavpack/*.c")
|
||||
exe = Link(settings, "teewars", Compile(settings, game_src, main_src, wavpack_src))
|
||||
--baselib = Compile(settings, Collect("src/baselib/src/*.cpp", "src/baselib/src/*.c"))
|
||||
engine = Compile(settings, Collect("src/engine/*.cpp"))
|
||||
client = Compile(settings, Collect("src/engine/client/*.cpp"))
|
||||
server = Compile(settings, Collect("src/engine/server/*.cpp"))
|
||||
game_shared = Compile(settings, Collect("src/game/*.cpp"))
|
||||
game_client = Compile(settings, Collect("src/game/client/*.cpp"))
|
||||
game_server = Compile(settings, Collect("src/game/server/*.cpp"))
|
||||
editor = Compile(settings, Collect("src/editor/*.cpp"))
|
||||
|
||||
--maps = {
|
||||
-- MakeMap("data/test.map", "data_src/test.txt"),
|
||||
-- MakeMap("data/dm1.map", "data_src/dm1.txt"),
|
||||
-- MakeMap("data/ctf1.map", "data_src/ctf1.txt")
|
||||
-- }
|
||||
|
||||
--data_files = Copy("data",
|
||||
-- {
|
||||
-- "data_src/game_main.tga",
|
||||
-- "data_src/game_weapons.tga",
|
||||
-- "data_src/char_teefault.tga",
|
||||
-- "data_src/sun.tga",
|
||||
-- "data_src/debug_font.tga",
|
||||
-- "data_src/dm1.map"
|
||||
-- })
|
||||
--audio_files = Copy("data/audio",
|
||||
-- {
|
||||
-- "data_src/audio/Music_Menu.wav",
|
||||
-- "data_src/audio/wp_flump_explo-01.wav",
|
||||
-- "data_src/audio/wp_flump_explo-02.wav",
|
||||
-- "data_src/audio/wp_flump_explo-03.wav"
|
||||
-- })
|
||||
client_exe = Link(settings, "teewars", engine, client, game_shared, game_client)
|
||||
server_exe = Link(server_settings, "teewars_srv", engine, server, game_shared, game_server)
|
||||
-- editor_exe = Link(settings, "editor", engine, game_shared, editor)
|
||||
|
||||
game = PseudoTarget("game", exe)
|
||||
|
||||
Target(game)
|
||||
Target(PseudoTarget("client", client_exe))
|
||||
Target(PseudoTarget("server", server_exe))
|
||||
-- Target(PseudoTarget("editor", editor_exe))
|
||||
|
|
|
@ -7,16 +7,15 @@
|
|||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <math.h>
|
||||
#include "interface.h"
|
||||
#include <engine/interface.h>
|
||||
|
||||
|
||||
#include "packet.h"
|
||||
#include "snapshot.h"
|
||||
#include <engine/packet.h>
|
||||
#include <engine/snapshot.h>
|
||||
#include "ui.h"
|
||||
|
||||
#include "lzw.h"
|
||||
#include <engine/lzw.h>
|
||||
|
||||
#include "versions.h"
|
||||
#include <engine/versions.h>
|
||||
|
||||
using namespace baselib;
|
||||
|
||||
|
@ -477,8 +476,8 @@ public:
|
|||
if (server_address)
|
||||
connect(server_address);
|
||||
|
||||
int64 inputs_per_second = 50;
|
||||
int64 time_per_input = time_freq()/inputs_per_second;
|
||||
//int64 inputs_per_second = 50;
|
||||
//int64 time_per_input = time_freq()/inputs_per_second;
|
||||
int64 game_starttime = time_get();
|
||||
int64 last_input = game_starttime;
|
||||
|
||||
|
@ -668,7 +667,7 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
int client_main(int argc, char **argv)
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
dbg_msg("client", "starting...");
|
||||
netaddr4 server_address(127, 0, 0, 1, 8303);
|
|
@ -2,7 +2,7 @@
|
|||
#include <baselib/vmath.h>
|
||||
#include <baselib/stream/file.h>
|
||||
|
||||
#include "interface.h"
|
||||
#include <engine/interface.h>
|
||||
|
||||
using namespace baselib;
|
||||
|
||||
|
@ -222,6 +222,8 @@ int gfx_load_tga(image_info *img, const char *filename)
|
|||
|
||||
bool flipx = (headers[17] >> 4) & 1;
|
||||
bool flipy = !((headers[17] >> 5) & 1);
|
||||
|
||||
(void)flipx; // TODO: make use of this flag
|
||||
|
||||
if(headers[2] != 2) // needs to be uncompressed RGB
|
||||
{
|
||||
|
@ -547,7 +549,7 @@ void gfx_pretty_text(float x, float y, float size, const char *text)
|
|||
|
||||
while (*text)
|
||||
{
|
||||
const char c = *text;
|
||||
const int c = *text;
|
||||
const float width = current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c];
|
||||
|
||||
text++;
|
||||
|
@ -573,7 +575,7 @@ float gfx_pretty_text_width(float size, const char *text)
|
|||
|
||||
while (*text)
|
||||
{
|
||||
const char c = *text++;
|
||||
const int c = *text++;
|
||||
width += size * (current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c] + spacing);
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
#include <baselib/audio.h>
|
||||
#include <baselib/stream/file.h>
|
||||
|
||||
#include "interface.h"
|
||||
#include <engine/interface.h>
|
||||
|
||||
using namespace baselib;
|
||||
|
||||
|
@ -181,10 +181,11 @@ public:
|
|||
static mixer mixer;
|
||||
//static sound_data test_sound;
|
||||
|
||||
/*
|
||||
extern "C"
|
||||
{
|
||||
#include "wavpack/wavpack.h"
|
||||
}
|
||||
}*/
|
||||
|
||||
/*
|
||||
static file_stream *read_func_filestream;
|
|
@ -1,4 +1,4 @@
|
|||
#include "interface.h"
|
||||
#include <engine/interface.h>
|
||||
#include "ui.h"
|
||||
|
||||
/********************************************************
|
|
@ -55,7 +55,7 @@ protected:
|
|||
|
||||
void debug_verify_mark(int type, int size)
|
||||
{
|
||||
if(read_int_raw() == ((type<<16) | size))
|
||||
if(read_int_raw() != ((type<<16) | size))
|
||||
dbg_assert(0, "error during packet disassembly");
|
||||
}
|
||||
|
||||
|
@ -110,8 +110,9 @@ public:
|
|||
const char *read_str()
|
||||
{
|
||||
debug_verify_mark(DEBUG_TYPE_STR, 0);
|
||||
const char *s = (const char *)current;
|
||||
int size = read_int_raw();
|
||||
const char *s = (const char *)current;
|
||||
//dbg_msg("packet", "reading string '%s' (%d)", s, size);
|
||||
current += size;
|
||||
return s;
|
||||
}
|
5
src/engine/server/server.cpp
Normal file
5
src/engine/server/server.cpp
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,6 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "game.h"
|
||||
#include "../game.h"
|
||||
#include "mapres_image.h"
|
||||
#include "mapres_tilemap.h"
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#include <baselib/system.h>
|
||||
#include "../interface.h"
|
||||
#include "../../engine/interface.h"
|
||||
#include "mapres_image.h"
|
||||
#include "mapres.h"
|
||||
#include "../mapres.h"
|
||||
|
||||
static int map_textures[64] = {0};
|
||||
static int count = 0;
|
|
@ -1,7 +1,7 @@
|
|||
#include "../interface.h"
|
||||
#include "../../engine/interface.h"
|
||||
#include "mapres_tilemap.h"
|
||||
#include "mapres_image.h"
|
||||
#include "mapres.h"
|
||||
#include "../mapres.h"
|
||||
|
||||
int tilemap_init()
|
||||
{
|
|
@ -5,13 +5,13 @@
|
|||
#include <baselib/mouse.h>
|
||||
#include <baselib/network.h>
|
||||
|
||||
#include "interface.h"
|
||||
#include "ui.h"
|
||||
#include "versions.h"
|
||||
#include <engine/interface.h>
|
||||
#include <engine/versions.h>
|
||||
#include "../mapres.h"
|
||||
|
||||
#include "game/mapres_image.h"
|
||||
#include "game/mapres_tilemap.h"
|
||||
#include "game/mapres.h"
|
||||
#include <engine/client/ui.h>
|
||||
#include "mapres_image.h"
|
||||
#include "mapres_tilemap.h"
|
||||
|
||||
using namespace baselib;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#include <baselib/system.h>
|
||||
#include <baselib/vmath.h>
|
||||
#include <math.h>
|
||||
#include "../interface.h"
|
||||
#include "../engine/interface.h"
|
||||
#include "mapres_col.h"
|
||||
|
||||
// Don't tweak :)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include <baselib/system.h>
|
||||
#include "../interface.h"
|
||||
#include "../engine/interface.h"
|
||||
#include "mapres_col.h"
|
||||
#include "mapres.h"
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "game.h"
|
||||
#include "../game.h"
|
||||
|
||||
using namespace baselib;
|
||||
|
||||
|
@ -646,7 +646,16 @@ public:
|
|||
if (flags & WEAPON_DRAWSAMMO)
|
||||
numammo--;
|
||||
// Create projectile
|
||||
new projectile(projectileclass, player->client_id, player->pos+vec2(0,projoffsety)+player->direction*projoffsetx, player->direction*projectilevel, projectilespan, player, damage, projectileflags, force, sound_impact_projectile);
|
||||
new projectile(projectileclass,
|
||||
player->client_id,
|
||||
player->pos+vec2(0,projoffsety)+player->direction*projoffsetx,
|
||||
player->direction*projectilevel,
|
||||
(int)projectilespan,
|
||||
player,
|
||||
damage,
|
||||
projectileflags,
|
||||
force,
|
||||
sound_impact_projectile);
|
||||
// recoil force if any
|
||||
if (recoilforce > 0.0f)
|
||||
{
|
668
src/server.cpp
668
src/server.cpp
|
@ -1,668 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <baselib/system.h>
|
||||
#include <baselib/network.h>
|
||||
|
||||
#include "interface.h"
|
||||
|
||||
//#include "socket.h"
|
||||
#include "packet.h"
|
||||
#include "snapshot.h"
|
||||
|
||||
#include "lzw.h"
|
||||
|
||||
#include "versions.h"
|
||||
|
||||
namespace baselib {}
|
||||
using namespace baselib;
|
||||
|
||||
class snapshot_builder
|
||||
{
|
||||
public:
|
||||
static const int MAX_ITEMS = 512;
|
||||
//static const int MAX_DATA_SIZE=1*1024;
|
||||
|
||||
char data[MAX_SNAPSHOT_SIZE];
|
||||
int data_size;
|
||||
|
||||
int offsets[MAX_ITEMS];
|
||||
int num_items;
|
||||
|
||||
int top_size;
|
||||
int top_items;
|
||||
|
||||
int snapnum;
|
||||
|
||||
snapshot_builder()
|
||||
{
|
||||
top_size = 0;
|
||||
top_items = 0;
|
||||
snapnum = 0;
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
data_size = 0;
|
||||
num_items = 0;
|
||||
}
|
||||
|
||||
int finish(void *snapdata)
|
||||
{
|
||||
snapnum++;
|
||||
|
||||
// collect some data
|
||||
/*
|
||||
int change = 0;
|
||||
if(data_size > top_size)
|
||||
{
|
||||
change++;
|
||||
top_size = data_size;
|
||||
}
|
||||
|
||||
if(num_items > top_items)
|
||||
{
|
||||
change++;
|
||||
top_items = num_items;
|
||||
}
|
||||
|
||||
if(change)
|
||||
{
|
||||
dbg_msg("snapshot", "new top, items=%d size=%d", top_items, top_size);
|
||||
}*/
|
||||
|
||||
// flattern and make the snapshot
|
||||
snapshot *snap = (snapshot *)snapdata;
|
||||
snap->num_items = num_items;
|
||||
int offset_size = sizeof(int)*num_items;
|
||||
mem_copy(snap->offsets, offsets, offset_size);
|
||||
mem_copy(snap->data_start(), data, data_size);
|
||||
return sizeof(int) + offset_size + data_size;
|
||||
}
|
||||
|
||||
void *new_item(int type, int id, int size)
|
||||
{
|
||||
snapshot::item *obj = (snapshot::item *)(data+data_size);
|
||||
obj->type_and_id = (type<<16)|id;
|
||||
offsets[num_items] = data_size;
|
||||
data_size += sizeof(int) + size;
|
||||
num_items++;
|
||||
dbg_assert(data_size < MAX_SNAPSHOT_SIZE, "too much data");
|
||||
dbg_assert(num_items < MAX_ITEMS, "too many items");
|
||||
|
||||
return &obj->data;
|
||||
}
|
||||
};
|
||||
|
||||
static snapshot_builder builder;
|
||||
|
||||
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 builder.new_item(type, id, size);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
class client
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
STATE_EMPTY = 0,
|
||||
STATE_CONNECTING = 1,
|
||||
STATE_INGAME = 2,
|
||||
};
|
||||
|
||||
// connection state info
|
||||
int state;
|
||||
|
||||
// (ticks) if lastactivity > 5 seconds kick him
|
||||
int64 lastactivity;
|
||||
connection conn;
|
||||
|
||||
char name[MAX_NAME_LENGTH];
|
||||
char clan[MAX_CLANNAME_LENGTH];
|
||||
/*
|
||||
client()
|
||||
{
|
||||
state = STATE_EMPTY;
|
||||
name[0] = 0;
|
||||
clan[0] = 0;
|
||||
}
|
||||
|
||||
~client()
|
||||
{
|
||||
dbg_assert(state == STATE_EMPTY, "client destoyed while in use");
|
||||
}*/
|
||||
|
||||
bool is_empty() const { return state == STATE_EMPTY; }
|
||||
bool is_ingame() const { return state == STATE_INGAME; }
|
||||
const netaddr4 &address() const { return conn.address(); }
|
||||
};
|
||||
|
||||
static client clients[MAX_CLIENTS];
|
||||
static int current_tick = 0;
|
||||
static int send_heartbeats = 1;
|
||||
|
||||
int server_tick()
|
||||
{
|
||||
return current_tick;
|
||||
}
|
||||
|
||||
int server_tickspeed()
|
||||
{
|
||||
return 50;
|
||||
}
|
||||
|
||||
int server_init()
|
||||
{
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
clients[i].state = client::STATE_EMPTY;
|
||||
clients[i].name[0] = 0;
|
||||
clients[i].clan[0] = 0;
|
||||
clients[i].lastactivity = 0;
|
||||
}
|
||||
|
||||
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].is_ingame())
|
||||
{
|
||||
info->name = clients[client_id].name;
|
||||
info->latency = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
class server
|
||||
{
|
||||
public:
|
||||
|
||||
socket_udp4 game_socket;
|
||||
const char *map_name;
|
||||
const char *server_name;
|
||||
int64 lasttick;
|
||||
int64 lastheartbeat;
|
||||
netaddr4 master_server;
|
||||
|
||||
int biggest_snapshot;
|
||||
|
||||
bool run(const char *servername, const char *mapname)
|
||||
{
|
||||
biggest_snapshot = 0;
|
||||
|
||||
net_init(); // For Windows compatibility.
|
||||
map_name = mapname;
|
||||
server_name = servername;
|
||||
|
||||
// load map
|
||||
if(!map_load(mapname))
|
||||
{
|
||||
dbg_msg("server", "failed to load map. mapname='%s'");
|
||||
return false;
|
||||
}
|
||||
|
||||
// start server
|
||||
if(!game_socket.open(8303))
|
||||
{
|
||||
dbg_msg("network/server", "couldn't open socket");
|
||||
return false;
|
||||
}
|
||||
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
dbg_msg("network/server", "\t%d: %d", i, clients[i].state);
|
||||
|
||||
if (net_host_lookup(MASTER_SERVER_ADDRESS, MASTER_SERVER_PORT, &master_server) != 0)
|
||||
master_server = netaddr4(0, 0, 0, 0, 0);
|
||||
|
||||
mods_init();
|
||||
|
||||
int64 time_per_tick = time_freq()/SERVER_TICK_SPEED;
|
||||
int64 time_per_heartbeat = time_freq() * 30;
|
||||
int64 starttime = time_get();
|
||||
//int64 lasttick = starttime;
|
||||
lasttick = starttime;
|
||||
lastheartbeat = 0;
|
||||
|
||||
int64 reporttime = time_get();
|
||||
int64 reportinterval = time_freq()*3;
|
||||
|
||||
int64 simulationtime = 0;
|
||||
int64 snaptime = 0;
|
||||
int64 networktime = 0;
|
||||
|
||||
while(1)
|
||||
{
|
||||
int64 t = time_get();
|
||||
if(t-lasttick > time_per_tick)
|
||||
{
|
||||
{
|
||||
int64 start = time_get();
|
||||
tick();
|
||||
simulationtime += time_get()-start;
|
||||
}
|
||||
|
||||
{
|
||||
int64 start = time_get();
|
||||
snap();
|
||||
snaptime += time_get()-start;
|
||||
}
|
||||
|
||||
// Check for client timeouts
|
||||
for (int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
if (clients[i].state != client::STATE_EMPTY)
|
||||
{
|
||||
// check last activity time
|
||||
if (((lasttick - clients[i].lastactivity) / time_freq()) > SERVER_CLIENT_TIMEOUT)
|
||||
client_timeout(i);
|
||||
}
|
||||
}
|
||||
|
||||
lasttick += time_per_tick;
|
||||
}
|
||||
|
||||
if(send_heartbeats)
|
||||
{
|
||||
if (t > lastheartbeat+time_per_heartbeat)
|
||||
{
|
||||
if (master_server.port != 0)
|
||||
{
|
||||
int players = 0;
|
||||
|
||||
for (int i = 0; i < MAX_CLIENTS; i++)
|
||||
if (!clients[i].is_empty())
|
||||
players++;
|
||||
|
||||
netaddr4 me(127, 0, 0, 0, 8303);
|
||||
|
||||
send_heartbeat(0, &me, players, MAX_CLIENTS, server_name, mapname);
|
||||
}
|
||||
|
||||
lastheartbeat = t+time_per_heartbeat;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int64 start = time_get();
|
||||
pump_network();
|
||||
networktime += time_get()-start;
|
||||
}
|
||||
|
||||
if(reporttime < time_get())
|
||||
{
|
||||
int64 totaltime = simulationtime+snaptime+networktime;
|
||||
dbg_msg("server/report", "sim=%.02fms snap=%.02fms net=%.02fms total=%.02fms load=%.02f%%",
|
||||
simulationtime/(float)reportinterval*1000,
|
||||
snaptime/(float)reportinterval*1000,
|
||||
networktime/(float)reportinterval*1000,
|
||||
totaltime/(float)reportinterval*1000,
|
||||
(simulationtime+snaptime+networktime)/(float)reportinterval*100.0f);
|
||||
|
||||
unsigned sent_total=0, recv_total=0;
|
||||
for (int i = 0; i < MAX_CLIENTS; i++)
|
||||
if (!clients[i].is_empty())
|
||||
{
|
||||
unsigned s,r;
|
||||
clients[i].conn.counter_get(&s,&r);
|
||||
clients[i].conn.counter_reset();
|
||||
sent_total += s;
|
||||
recv_total += r;
|
||||
}
|
||||
|
||||
|
||||
dbg_msg("server/report", "biggestsnap=%d send=%d recv=%d",
|
||||
biggest_snapshot, sent_total/3, recv_total/3);
|
||||
|
||||
simulationtime = 0;
|
||||
snaptime = 0;
|
||||
networktime = 0;
|
||||
|
||||
reporttime += reportinterval;
|
||||
}
|
||||
|
||||
thread_sleep(1);
|
||||
}
|
||||
|
||||
mods_shutdown();
|
||||
map_unload();
|
||||
}
|
||||
|
||||
void tick()
|
||||
{
|
||||
current_tick++;
|
||||
mods_tick();
|
||||
}
|
||||
|
||||
void snap()
|
||||
{
|
||||
mods_presnap();
|
||||
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
if(clients[i].is_ingame())
|
||||
{
|
||||
char data[MAX_SNAPSHOT_SIZE];
|
||||
char compdata[MAX_SNAPSHOT_SIZE];
|
||||
builder.start();
|
||||
mods_snap(i);
|
||||
|
||||
// finish snapshot
|
||||
int snapshot_size = builder.finish(data);
|
||||
|
||||
// compress it
|
||||
int compsize = lzw_compress(data, snapshot_size, compdata);
|
||||
snapshot_size = compsize;
|
||||
|
||||
if(snapshot_size > biggest_snapshot)
|
||||
biggest_snapshot = snapshot_size;
|
||||
|
||||
const int max_size = MAX_SNAPSHOT_PACKSIZE;
|
||||
int numpackets = (snapshot_size+max_size-1)/max_size;
|
||||
for(int n = 0, left = snapshot_size; left; n++)
|
||||
{
|
||||
int chunk = left < max_size ? left : max_size;
|
||||
left -= chunk;
|
||||
|
||||
packet p(NETMSG_SERVER_SNAP);
|
||||
p.write_int(numpackets);
|
||||
p.write_int(n);
|
||||
p.write_int(chunk);
|
||||
p.write_raw(&compdata[n*max_size], chunk);
|
||||
clients[i].conn.send(&p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mods_postsnap();
|
||||
}
|
||||
|
||||
void send_accept(client *client, const char *map)
|
||||
{
|
||||
packet p(NETMSG_SERVER_ACCEPT);
|
||||
p.write_str(map);
|
||||
client->conn.send(&p);
|
||||
}
|
||||
|
||||
void drop(int cid, const char *reason)
|
||||
{
|
||||
if(clients[cid].state == client::STATE_EMPTY)
|
||||
return;
|
||||
|
||||
clients[cid].state = client::STATE_EMPTY;
|
||||
mods_client_drop(cid);
|
||||
dbg_msg("game", "player dropped. reason='%s' cid=%x name='%s'", reason, cid, clients[cid].name);
|
||||
}
|
||||
|
||||
int find_client(const netaddr4 *addr)
|
||||
{
|
||||
// fetch client
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
if(!clients[i].is_empty() && clients[i].address() == *addr)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void client_process_packet(int cid, packet *p)
|
||||
{
|
||||
clients[cid].lastactivity = lasttick;
|
||||
if(p->msg() == NETMSG_CLIENT_DONE)
|
||||
{
|
||||
dbg_msg("game", "player as entered the game. cid=%x", cid);
|
||||
clients[cid].state = client::STATE_INGAME;
|
||||
mods_client_enter(cid);
|
||||
}
|
||||
else if(p->msg() == NETMSG_CLIENT_INPUT)
|
||||
{
|
||||
int input[MAX_INPUT_SIZE];
|
||||
int size = p->read_int();
|
||||
for(int i = 0; i < size/4; i++)
|
||||
input[i] = p->read_int();
|
||||
if(p->is_good())
|
||||
{
|
||||
//dbg_msg("network/server", "applying input %d %d %d", input[0], input[1], input[2]);
|
||||
mods_client_input(cid, input);
|
||||
}
|
||||
}
|
||||
else if(p->msg() == NETMSG_CLIENT_ERROR)
|
||||
{
|
||||
const char *reason = p->read_str();
|
||||
if(p->is_good())
|
||||
dbg_msg("network/server", "client error. cid=%x reason='%s'", cid, reason);
|
||||
else
|
||||
dbg_msg("network/server", "client error. cid=%x", cid);
|
||||
drop(cid, "client error");
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_msg("network/server", "invalid message. cid=%x msg=%x", cid, p->msg());
|
||||
drop(cid, "invalid message");
|
||||
}
|
||||
}
|
||||
|
||||
void process_packet(packet *p, netaddr4 *from)
|
||||
{
|
||||
if(p->msg() == NETMSG_CLIENT_CONNECT)
|
||||
{
|
||||
// we got no state for this client yet
|
||||
const char *version;
|
||||
const char *name;
|
||||
const char *clan;
|
||||
const char *password;
|
||||
const char *skin;
|
||||
|
||||
version = p->read_str();
|
||||
name = p->read_str();
|
||||
clan = p->read_str();
|
||||
password = p->read_str();
|
||||
skin = p->read_str();
|
||||
|
||||
if(p->is_good())
|
||||
{
|
||||
// check version
|
||||
if(strncmp(version, TEEWARS_NETVERSION, 32) != 0)
|
||||
{
|
||||
// TODO: send error
|
||||
return;
|
||||
}
|
||||
|
||||
// look for empty slot, linear search
|
||||
int id = -1;
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
if(clients[i].is_empty())
|
||||
{
|
||||
id = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if(id != -1)
|
||||
{
|
||||
// slot found
|
||||
// TODO: perform correct copy here
|
||||
mem_copy(clients[id].name, name, MAX_NAME_LENGTH);
|
||||
mem_copy(clients[id].clan, clan, MAX_CLANNAME_LENGTH);
|
||||
clients[id].state = client::STATE_CONNECTING;
|
||||
clients[id].conn.init(&game_socket, from);
|
||||
|
||||
clients[id].lastactivity = lasttick;
|
||||
clients[id].name[MAX_NAME_LENGTH-1] = 0;
|
||||
clients[id].clan[MAX_CLANNAME_LENGTH-1] = 0;
|
||||
|
||||
dbg_msg("network/server", "client connected. '%s' on slot %d", name, id);
|
||||
|
||||
// TODO: return success
|
||||
send_accept(&clients[id], map_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
// no slot found
|
||||
// TODO: send error
|
||||
dbg_msg("network/server", "client connected but server is full");
|
||||
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
dbg_msg("network/server", "\t%d: %d", i, clients[i].state);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int cid = find_client(from);
|
||||
if(cid >= 0)
|
||||
{
|
||||
if(clients[cid].conn.feed(p))
|
||||
{
|
||||
// packet is ok
|
||||
unsigned msg = p->msg();
|
||||
|
||||
// client found, check state
|
||||
if(((msg>>16)&0xff)&clients[cid].state)
|
||||
{
|
||||
// state is ok
|
||||
client_process_packet(cid, p);
|
||||
}
|
||||
else
|
||||
{
|
||||
// invalid state, disconnect the client
|
||||
drop(cid, "invalid message at this state");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
drop(cid, "connection error");
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
dbg_msg("network/server", "packet from strange address.");
|
||||
}
|
||||
}
|
||||
|
||||
void client_timeout(int clientId)
|
||||
{
|
||||
drop(clientId, "client timedout");
|
||||
}
|
||||
|
||||
void pump_network()
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
packet p;
|
||||
netaddr4 from;
|
||||
|
||||
int bytes = game_socket.recv(&from, p.data(), p.max_size());
|
||||
if(bytes <= 0)
|
||||
break;
|
||||
|
||||
process_packet(&p, &from);
|
||||
}
|
||||
// TODO: check for client timeouts
|
||||
}
|
||||
|
||||
char *write_int(char *buffer, int integer)
|
||||
{
|
||||
*buffer++ = integer >> 24;
|
||||
*buffer++ = integer >> 16;
|
||||
*buffer++ = integer >> 8;
|
||||
*buffer++ = integer;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
char *write_netaddr4(char *buffer, netaddr4 *address)
|
||||
{
|
||||
*buffer++ = address->ip[0];
|
||||
*buffer++ = address->ip[1];
|
||||
*buffer++ = address->ip[2];
|
||||
*buffer++ = address->ip[3];
|
||||
|
||||
return write_int(buffer, address->port);
|
||||
}
|
||||
|
||||
void send_heartbeat(int version, netaddr4 *address, int players, int max_players, const char *name, const char *map_name)
|
||||
{
|
||||
char buffer[216] = {0};
|
||||
char *d = buffer;
|
||||
|
||||
d = write_int(d, 'TWHB');
|
||||
d = write_int(d, version);
|
||||
d = write_netaddr4(d, address);
|
||||
d = write_int(d,players);
|
||||
d = write_int(d, max_players);
|
||||
|
||||
int len = strlen(name);
|
||||
if (len > 128)
|
||||
len = 128;
|
||||
|
||||
memcpy(d, name, len);
|
||||
d += 128;
|
||||
|
||||
len = strlen(map_name);
|
||||
if (len > 64)
|
||||
len = 64;
|
||||
|
||||
memcpy(d, map_name, len);
|
||||
d += 64;
|
||||
|
||||
game_socket.send(&master_server, buffer, sizeof(buffer));
|
||||
}
|
||||
};
|
||||
|
||||
int server_main(int argc, char **argv)
|
||||
{
|
||||
dbg_msg("server", "starting...");
|
||||
|
||||
const char *mapname = "data/demo.map";
|
||||
const char *servername = 0;
|
||||
// parse arguments
|
||||
for(int i = 1; i < argc; i++)
|
||||
{
|
||||
if(argv[i][0] == '-' && argv[i][1] == 'm' && argv[i][2] == 0 && argc - i > 1)
|
||||
{
|
||||
// -m map
|
||||
i++;
|
||||
mapname = argv[i];
|
||||
}
|
||||
else if(argv[i][0] == '-' && argv[i][1] == 'n' && argv[i][2] == 0 && argc - i > 1)
|
||||
{
|
||||
// -n server name
|
||||
i++;
|
||||
servername = argv[i];
|
||||
}
|
||||
else if(argv[i][0] == '-' && argv[i][1] == 'p' && argv[i][2] == 0)
|
||||
{
|
||||
// -p (private server)
|
||||
send_heartbeats = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(!mapname)
|
||||
{
|
||||
dbg_msg("server", "no map given (-m MAPNAME)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!servername)
|
||||
{
|
||||
dbg_msg("server", "no server name given (-n \"server name\")");
|
||||
return 0;
|
||||
}
|
||||
|
||||
server_init();
|
||||
server s;
|
||||
s.run(servername, mapname);
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue