diff options
Diffstat (limited to 'src/engine/server/server.cpp')
| -rw-r--r-- | src/engine/server/server.cpp | 571 |
1 files changed, 193 insertions, 378 deletions
diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index d0ae80f0..5632689e 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -1,138 +1,23 @@ -#include <stdio.h> -#include <string.h> +#include <cstdio> +#include <cstring> +#include <cstdlib> #include <baselib/system.h> #include <engine/interface.h> -//#include "socket.h" #include <engine/packet.h> #include <engine/snapshot.h> -#include <engine/lzw.h> +#include <engine/compression.h> #include <engine/versions.h> -namespace baselib {} -using namespace baselib; - -int net_addr4_cmp(const NETADDR4 *a, const NETADDR4 *b) -{ - if( - a->ip[0] != b->ip[0] || - a->ip[1] != b->ip[1] || - a->ip[2] != b->ip[2] || - a->ip[3] != b->ip[3] || - a->port != b->port - ) - return 1; - return 0; -} - -// --- string handling (MOVE THESE!!) --- -void snap_encode_string(const char *src, int *dst, int length, int max_length) -{ - const unsigned char *p = (const unsigned char *)src; - - // handle whole int - for(int i = 0; i < length/4; i++) - { - *dst = (p[0]<<24|p[1]<<16|p[2]<<8|p[3]); - p += 4; - dst++; - } - - // take care of the left overs - int left = length%4; - if(left) - { - unsigned last = 0; - switch(left) - { - case 3: last |= p[2]<<8; - case 2: last |= p[1]<<16; - case 1: last |= p[0]<<24; - } - *dst = last; - } -} - - -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; - } +#include <engine/network.h> +#include <engine/config.h> - 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; - } -}; +namespace baselib {} +using namespace baselib; static snapshot_builder builder; @@ -143,7 +28,6 @@ void *snap_new_item(int type, int id, int size) return builder.new_item(type, id, size); } - // class client { @@ -157,34 +41,21 @@ public: // connection state info int state; - - // (ticks) if lastactivity > 5 seconds kick him - int64 lastactivity; - connection conn; + + int last_acked_snapshot; + snapshot_storage snapshots; 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; +static net_server net; int server_tick() { @@ -193,7 +64,7 @@ int server_tick() int server_tickspeed() { - return 50; + return SERVER_TICK_SPEED; } int server_init() @@ -203,7 +74,7 @@ int server_init() clients[i].state = client::STATE_EMPTY; clients[i].name[0] = 0; clients[i].clan[0] = 0; - clients[i].lastactivity = 0; + //clients[i].lastactivity = 0; } current_tick = 0; @@ -225,12 +96,27 @@ int server_getclientinfo(int client_id, client_info *info) return 0; } -// + +int server_send_msg(int client_id) +{ + const msg_info *info = msg_get_info(); + NETPACKET packet; + packet.client_id = client_id; + packet.data = info->data; + packet.data_size = info->size; + + if(info->flags&MSGFLAG_VITAL) + packet.flags = PACKETFLAG_VITAL; + + net.send(&packet); + return 0; +} + +// TODO: remove this class class server { public: - - socket_udp4 game_socket; + //socket_udp4 game_socket; const char *map_name; const char *server_name; @@ -256,14 +142,14 @@ public: } // start server - if(!game_socket.open(8303)) + if(!net.open(8303, 0, 0)) { 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); + //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) { @@ -304,17 +190,6 @@ public: 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; } @@ -332,8 +207,7 @@ public: // TODO: fix me netaddr4 me(127, 0, 0, 0, 8303); - - send_heartbeat(0, &me, players, MAX_CLIENTS, server_name, mapname); + //send_heartbeat(0, &me, players, MAX_CLIENTS, server_name, mapname); } lastheartbeat = t+time_per_heartbeat; @@ -357,6 +231,7 @@ public: (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()) { @@ -366,7 +241,7 @@ public: sent_total += s; recv_total += r; } - + */ dbg_msg("server/report", "biggestsnap=%d send=%d recv=%d", biggest_snapshot, sent_total/3, recv_total/3); @@ -393,6 +268,8 @@ public: void snap() { + //if(current_tick&1) + // return; mods_presnap(); for(int i = 0; i < MAX_CLIENTS; i++) @@ -400,33 +277,86 @@ public: if(clients[i].is_ingame()) { char data[MAX_SNAPSHOT_SIZE]; + char deltadata[MAX_SNAPSHOT_SIZE]; char compdata[MAX_SNAPSHOT_SIZE]; + //char intdata[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++) + // remove old snapshos + // keep 1 seconds worth of snapshots + clients[i].snapshots.purge_until(current_tick-SERVER_TICK_SPEED); + + // save it the snapshot + clients[i].snapshots.add(current_tick, snapshot_size, data); + + // find snapshot that we can preform delta against + static snapshot emptysnap; + emptysnap.data_size = 0; + emptysnap.num_items = 0; + + snapshot *deltashot = &emptysnap; + int deltashot_size; + int delta_tick = -1; + { + void *delta_data; + deltashot_size = clients[i].snapshots.get(clients[i].last_acked_snapshot, (void **)&delta_data); + if(deltashot_size >= 0) + { + delta_tick = clients[i].last_acked_snapshot; + deltashot = (snapshot *)delta_data; + } + } + + // create delta + int deltasize = snapshot_create_delta(deltashot, (snapshot*)data, deltadata); + + if(deltasize) { - 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); + // compress it + //int intsize = -1; + unsigned char intdata[MAX_SNAPSHOT_SIZE]; + int intsize = intpack_compress(deltadata, deltasize, intdata); + + int compsize = zerobit_compress(intdata, intsize, compdata); + //dbg_msg("compress", "%5d --delta-> %5d --int-> %5d --zero-> %5d %5d", + //snapshot_size, deltasize, intsize, compsize, intsize-compsize); + 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; + + if(numpackets == 1) + msg_pack_start(NETMSG_SNAPSMALL, 0); + else + msg_pack_start(NETMSG_SNAP, 0); + msg_pack_int(current_tick); + msg_pack_int(current_tick-delta_tick); // compressed with + msg_pack_int(chunk); + msg_pack_raw(&compdata[n*max_size], chunk); + msg_pack_end(); + //const msg_info *info = msg_get_info(); + //dbg_msg("server", "size=%d", info->size); + server_send_msg(i); + } + } + else + { + msg_pack_start(NETMSG_SNAPEMPTY, 0); + msg_pack_int(current_tick); + msg_pack_int(current_tick-delta_tick); // compressed with + msg_pack_end(); + server_send_msg(i); } } } @@ -434,11 +364,12 @@ public: mods_postsnap(); } - void send_accept(client *client, const char *map) + void send_map(int cid) { - packet p(NETMSG_SERVER_ACCEPT); - p.write_str(map); - client->conn.send(&p); + msg_pack_start(NETMSG_MAP, MSGFLAG_VITAL); + msg_pack_string(map_name, 0); + msg_pack_end(); + server_send_msg(cid); } void drop(int cid, const char *reason) @@ -451,161 +382,47 @@ public: dbg_msg("game", "player dropped. reason='%s' cid=%x name='%s'", reason, cid, clients[cid].name); } - int find_client(const netaddr4 *addr) + void process_client_packet(NETPACKET *packet) { - // fetch client - for(int i = 0; i < MAX_CLIENTS; i++) + int cid = packet->client_id; + int msg = msg_unpack_start(packet->data, packet->data_size); + if(msg == NETMSG_INFO) { - if(!clients[i].is_empty() && clients[i].address() == *addr) - return i; + strncpy(clients[cid].name, msg_unpack_string(), MAX_NAME_LENGTH); + strncpy(clients[cid].clan, msg_unpack_string(), MAX_CLANNAME_LENGTH); + const char *password = msg_unpack_string(); + const char *skin = msg_unpack_string(); + (void)password; // ignore these variables + (void)skin; + send_map(cid); } - return -1; - } - - void client_process_packet(int cid, packet *p) - { - clients[cid].lastactivity = lasttick; - if(p->msg() == NETMSG_CLIENT_DONE) + else if(msg == NETMSG_ENTERGAME) { 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) + else if(msg == NETMSG_INPUT) { int input[MAX_INPUT_SIZE]; - int size = p->read_int(); + int size = msg_unpack_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); - } + input[i] = msg_unpack_int(); + mods_client_input(cid, input); } - else if(p->msg() == NETMSG_CLIENT_ERROR) + else if(msg == NETMSG_SNAPACK) { - 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"); + clients[cid].last_acked_snapshot = msg_unpack_int(); } else { - dbg_msg("network/server", "invalid message. cid=%x msg=%x", cid, p->msg()); - drop(cid, "invalid message"); + dbg_msg("server", "strange message cid=%d msg=%d data_size=%d", cid, msg, packet->data_size); } + } - void process_packet(packet *p, netaddr4 *from) + void process_packet(NETPACKET *packet) { - // do version check - if(p->version() != TEEWARS_NETVERSION) - { - // send an empty packet back. - // this will allow the client to check the version - packet p; - game_socket.send(from, p.data(), p.size()); - return; - } - - 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(strcmp(version, TEEWARS_NETVERSION) != 0) - { - dbg_msg("network/server", "wrong version connecting '%s'", version); - // 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) @@ -615,67 +432,53 @@ public: void pump_network() { + net.update(); + + // process packets + NETPACKET packet; + while(net.recv(&packet)) + { + + if(packet.client_id == -1) + { + // stateless + } + else + process_client_packet(&packet); + } + + // check for removed clients while(1) { - packet p; - netaddr4 from; - - //int bytes = net_udp4_recv( - int bytes = game_socket.recv(&from, p.data(), p.max_size()); - //int bytes = game_socket.recv(&from, p.data(), p.max_size()); - if(bytes <= 0) + int cid = net.delclient(); + if(cid == -1) break; - - process_packet(&p, &from); + + clients[cid].state = client::STATE_EMPTY; + clients[cid].name[0] = 0; + clients[cid].clan[0] = 0; + clients[cid].snapshots.purge_all(); + + mods_client_drop(cid); + + dbg_msg("server", "del client %d", cid); + } + + // check for new clients + while(1) + { + int cid = net.newclient(); + if(cid == -1) + break; + + clients[cid].state = client::STATE_CONNECTING; + clients[cid].name[0] = 0; + clients[cid].clan[0] = 0; + clients[cid].snapshots.purge_all(); + clients[cid].last_acked_snapshot = -1; + + dbg_msg("server", "new client %d", cid); } - // 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)); } }; @@ -683,8 +486,14 @@ int main(int argc, char **argv) { dbg_msg("server", "starting..."); + dbg_msg("server", "%d %d", sizeof(snapshot), sizeof(snapshot::item)); + + config_reset(); + config_load("server.cfg"); + const char *mapname = "data/demo.map"; const char *servername = 0; + // parse arguments for(int i = 1; i < argc; i++) { @@ -705,6 +514,12 @@ int main(int argc, char **argv) // -p (private server) send_heartbeats = 0; } + else if(argv[i][0] == '-' && argv[i][1] == 'o' && argv[i][2] == 0) + { + // -o port + i++; + config_set_sv_port(&config, atol(argv[i])); + } } if(!mapname) |