diff options
| author | Magnus Auvinen <magnus.auvinen@gmail.com> | 2007-07-13 13:40:04 +0000 |
|---|---|---|
| committer | Magnus Auvinen <magnus.auvinen@gmail.com> | 2007-07-13 13:40:04 +0000 |
| commit | 125d04e51f4e444a38cf038d3ea095d92d3c6dbb (patch) | |
| tree | 2288bbe4b923ab4d695e9f852c177a12f74ea799 /src/engine/packet.h | |
| parent | 7be0ae1b2929a3c5dfedf542bce886deefa0f862 (diff) | |
| download | zcatch-125d04e51f4e444a38cf038d3ea095d92d3c6dbb.tar.gz zcatch-125d04e51f4e444a38cf038d3ea095d92d3c6dbb.zip | |
large rewrite and code cleanup
Diffstat (limited to 'src/engine/packet.h')
| -rw-r--r-- | src/engine/packet.h | 626 |
1 files changed, 270 insertions, 356 deletions
diff --git a/src/engine/packet.h b/src/engine/packet.h index fd93d744..6c92bf31 100644 --- a/src/engine/packet.h +++ b/src/engine/packet.h @@ -1,442 +1,356 @@ +#include <stdarg.h> #include <baselib/stream/file.h> #include <baselib/network.h> #include "versions.h" +#include "ringbuffer.h" +#include "compression.h" +#include "snapshot.h" -#define MACRO_MAKEINT(a,b,c,d) ((a<<24)|(b<<16)|(c<<8)|d) +enum +{ + NETMSG_NULL=0, + + // sent by server + NETMSG_MAP, + NETMSG_SNAP, + NETMSG_SNAPEMPTY, + NETMSG_SNAPSMALL, + + // sent by client + NETMSG_INFO, + NETMSG_ENTERGAME, + NETMSG_INPUT, + NETMSG_SNAPACK, + + // sent by both + NETMSG_ERROR, +}; -// TODO: this is not KISS -class packet + +// this should be revised +enum { - friend class connection; -protected: - enum - { - MAX_PACKET_SIZE = 1024, - }; - - // packet data - struct header + MAX_NAME_LENGTH=32, + MAX_CLANNAME_LENGTH=32, + MAX_INPUT_SIZE=128, + MAX_SNAPSHOT_SIZE=64*1024, + MAX_SNAPSHOT_PACKSIZE=768 +}; + + +class snapshot_storage +{ + struct holder { - unsigned id; - unsigned version; - unsigned size_and_flags; - unsigned crc; - - unsigned msg; - unsigned ack; - unsigned seq; + int tick; + int data_size; + int *data() { return (int *)(this+1); } }; - unsigned char packet_data[MAX_PACKET_SIZE]; - unsigned char *current; + ring_buffer buffer; - // these are used to prepend data in the packet - // used for debugging so we have checks that we - // pack and unpack the same way - enum +public: + void purge_until(int tick) { - DEBUG_TYPE_INT=0x1, - DEBUG_TYPE_STR=0x2, - DEBUG_TYPE_RAW=0x3, - }; + while(1) + { + ring_buffer::item *i = buffer.first(); + if(!i) + break; + holder *h = (holder *)i->data(); + if(h->tick < tick) + buffer.pop_first(); + else + break; + } + } - // writes an int to the packet - void write_int_raw(int i) + void purge_all() { - // TODO: check for overflow - *(int*)current = i; - current += sizeof(int); + buffer.reset(); } - // reads an int from the packet - int read_int_raw() - { - // TODO: check for overflow - int i = *(int*)current; - current += sizeof(int); - return i; - } - - void debug_insert_mark(int type, int size) + void add(int tick, int data_size, void *data) { - write_int_raw((type<<16)|size); + holder *h = (holder *)buffer.alloc(sizeof(holder)+data_size); + h->tick = tick; + h->data_size = data_size; + mem_copy(h->data(), data, data_size); } - void debug_verify_mark(int type, int size) + int get(int tick, void **data) { - if(read_int_raw() != ((type<<16) | size)) - dbg_assert(0, "error during packet disassembly"); + ring_buffer::item *i = buffer.first(); + while(i) + { + holder *h = (holder *)i->data(); + if(h->tick == tick) + { + *data = h->data(); + return h->data_size; + } + + i = i->next; + } + + return -1; } - +}; +/* +class snapshot_delta_builder +{ public: + static const int MAX_ITEMS = 512; - enum - { - FLAG_VITAL=1, - FLAG_RESEND=2 - }; + char data[MAX_SNAPSHOT_SIZE]; + int data_size; - packet(unsigned msg=0) - { - current = packet_data; - current += sizeof(header); + int offsets[MAX_ITEMS]; + int num_items; + + int top_size; + int top_items; - ((header*)packet_data)->id = MACRO_MAKEINT('K','M','A',1); - ((header*)packet_data)->version = TEEWARS_NETVERSION; - ((header*)packet_data)->msg = msg; + int snapnum; + + snapshot_delta_builder() + { + top_size = 0; + top_items = 0; + snapnum = 0; } - - void set_header(unsigned ack, unsigned seq) + + void start() { - ((header*)packet_data)->ack = ack; - ((header*)packet_data)->seq = seq; + data_size = 0; + num_items = 0; } - - // writes an int to the packet - void write_int(int i) + + int finish(void *snapdata) { - debug_insert_mark(DEBUG_TYPE_INT, 4); - write_int_raw(i); + snapnum++; + + // flattern and make the snapshot + snapshot *snap = (snapshot *)snapdata; + snap->data_size = data_size; + 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; } +}; +*/ + +class snapshot_builder +{ +public: + static const int MAX_ITEMS = 512; - void write_raw(const char *raw, int size) + 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() { - debug_insert_mark(DEBUG_TYPE_RAW, size); - while(size--) - *current++ = *raw++; + top_size = 0; + top_items = 0; + snapnum = 0; } - // writes a string to the packet - void write_str(const char *str) + void start() { - debug_insert_mark(DEBUG_TYPE_STR, 0); - int s = strlen(str)+1; - write_int_raw(s); - for(;*str; current++, str++) - *current = *str; - *current = 0; - current++; + data_size = 0; + num_items = 0; } - // reads an int from the packet - int read_int() + snapshot::item *get_item(int index) { - debug_verify_mark(DEBUG_TYPE_INT, 4); - return read_int_raw(); + return (snapshot::item *)&(data[offsets[index]]); } - // reads a string from the packet - const char *read_str() + int *get_item_data(int key) { - debug_verify_mark(DEBUG_TYPE_STR, 0); - int size = read_int_raw(); - const char *s = (const char *)current; - //dbg_msg("packet", "reading string '%s' (%d)", s, size); - current += size; - return s; + for(int i = 0; i < num_items; i++) + { + if(get_item(i)->key() == key) + return (int *)get_item(i)->data(); + } + return 0; } - - const char *read_raw(int size) + + int finish(void *snapdata) { - debug_verify_mark(DEBUG_TYPE_RAW, size); - const char *d = (const char *)current; - current += size; - return d; + snapnum++; + + // flattern and make the snapshot + snapshot *snap = (snapshot *)snapdata; + snap->data_size = data_size; + 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(snapshot) + offset_size + data_size; } - - // TODO: impelement this - bool is_good() const { return true; } - unsigned version() const { return ((header*)packet_data)->version; } - unsigned msg() const { return ((header*)packet_data)->msg; } - unsigned seq() const { return ((header*)packet_data)->seq; } - unsigned ack() const { return ((header*)packet_data)->ack; } - unsigned flags() const { return (((header*)packet_data)->size_and_flags) & 0xffff; } - - // access functions to get the size and data - int size() const { return (int)(current-(unsigned char*)packet_data); } - int max_size() const { return MAX_PACKET_SIZE; } - void *data() { return packet_data; } + 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(snapshot::item) + 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(); + } }; -// TODO: remove all the allocations from this class -class ring_buffer +class data_packer { - struct item + enum { - item *next; - item *prev; - int size; + BUFFER_SIZE=1024*2 }; - item *first; - item *last; - - unsigned buffer_size; + unsigned char buffer[BUFFER_SIZE]; + unsigned char *current; + unsigned char *end; + int error; public: - ring_buffer() - { - first = 0; - last = 0; - buffer_size = 0; - } - - ~ring_buffer() - { - reset(); - } - void reset() { - // clear all - while(peek_data()) - next(); + error = 0; + current = buffer; + end = current + BUFFER_SIZE; } - void *alloc(int size) + void add_int(int i) { - item *i = (item*)mem_alloc(sizeof(item)+size, 1); - i->size = size; - - i->prev = last; - i->next = 0; - if(last) - last->next = i; - else - first = i; - last = i; - - buffer_size += size; - return (void*)(i+1); - } - - unsigned peek_size() - { - if(!first) - return 0; - return first->size; + // TODO: add space check + // TODO: variable length encoding perhaps + // TODO: add debug marker + current = vint_pack(current, i); + //*current++ = (i>>24)&0xff; + //*current++ = (i>>16)&0xff; + //*current++ = (i>>8)&0xff; + //*current++ = i&0xff; } - void *peek_data() + void add_string(const char *p, int limit) { - if(!first) - return 0; - return (void*)(first+1); - } - - void next() - { - if(first) + // TODO: add space check + // TODO: add debug marker + if(limit > 0) { - item *next = first->next; - buffer_size += first->size; - mem_free(first); - first = next; - if(first) - first->prev = 0; - else - last = 0; + while(*p && limit != 0) + { + *current++ = *p++; + limit--; + } + *current++ = 0; + } + else + { + while(*p) + *current++ = *p++; + *current++ = 0; } } - unsigned size() { return buffer_size; } -}; - -// -class connection -{ - baselib::socket_udp4 *socket; - baselib::netaddr4 addr; - unsigned seq; - unsigned ack; - - unsigned counter_sent_bytes; - unsigned counter_recv_bytes; - - int needs_resend; - - /* - struct resend_packet - { - resend_packet *next; - unsigned seq; - unsigned msg; - unsigned size; - char data[1]; - }; - - resend_packet *first_resend; - resend_packet *last_resend; - */ - - ring_buffer resend_buffer; - - void save_for_resend(packet *p) - { - /* - packet *n = (packet *)resend_buffer.alloc(p->size()); - mem_copy(n->data(), p->data(), p->size()); - n->current = (unsigned char*)n->data() + p->size(); - */ - } - - void remove_resends(unsigned ack) + void add_raw(const unsigned char *data, int size) { - /* - while(1) + // TODO: add space check + // TODO: add debug marker + //add_int(size); + while(size) { - packet *p = (packet *)resend_buffer.peek_data(); - if(!p) - break; - - if(p->seq() > ack) - break; - resend_buffer.next(); - }*/ + *current++ = *data++; + size--; + } } -public: - void counter_reset() + int size() const { - counter_sent_bytes = 0; - counter_recv_bytes = 0; + return (const unsigned char *)current-(const unsigned char *)buffer; } - void counter_get(unsigned *sent, unsigned *recved) + const unsigned char *data() { - *sent = counter_sent_bytes; - *recved = counter_recv_bytes; + return (const unsigned char *)buffer; } +}; - void init(baselib::socket_udp4 *socket, const baselib::netaddr4 *addr) +class data_unpacker +{ + const unsigned char *current; + const unsigned char *start; + const unsigned char *end; + int error; + +public: + void reset(const unsigned char *data, int size) { - resend_buffer.reset(); - - this->addr = *addr; - this->socket = socket; - ack = 0; - seq = 0; - needs_resend = 0; - counter_reset(); + error = 0; + start = data; + end = start + size; + current = start; } - void send(packet *p) + int get_int() { - if(p->flags()&packet::FLAG_VITAL) - seq++; - - p->set_header(ack, seq); - - if(p->flags()&packet::FLAG_VITAL) - save_for_resend(p); - - // TODO: request resend if needed, use needs_resend variable - - socket->send(&address(), p->data(), p->size()); - counter_sent_bytes += p->size(); + int i; + current = vint_unpack(current, &i); + // TODO: might be changed into variable width + // TODO: add range check + // TODO: add debug marker + //i = (current[0]<<24) | (current[1]<<16) | (current[2]<<8) | (current[3]); + //current += 4; + return i; } - packet *feed(packet *p) + const char *get_string() { - counter_recv_bytes += p->size(); - - if(p->flags()&packet::FLAG_VITAL) - { - if(p->seq() == ack+1) - { - // packet in seqence, ack it - ack++; - //dbg_msg("network/connection", "packet in sequence. seq/ack=%x", ack); - return p; - } - else if(p->seq() > ack) - { - // packet loss - needs_resend = 1; - dbg_msg("network/connection", "packet loss! seq=%x ack=%x+1", p->seq(), ack); - return p; - } - else - { - // we already got this packet - return 0x0; - } - } - - // remove resends - remove_resends(p->ack()); - - // handle resends - if(p->flags()&packet::FLAG_RESEND) - { - // peer as requested a resend of all non acked packages. - - } - - return p; + // TODO: add range check + // TODO: add debug marker + const char *ptr = (const char *)current; + while(*current) // skip the string + current++; + current++; + return ptr; } - const baselib::netaddr4 &address() const { return addr; } - - void update() + const unsigned char *get_raw(int size) { + // TODO: add range check + // TODO: add debug marker + //int s = get_int(); + //if(size) + //*size = s; + const unsigned char *ptr = current; + current += size; + return ptr; } }; - -//const char *NETWORK_VERSION = "development"; - -enum -{ - NETMSG_CONTEXT_CONNECT=0x00010000, - NETMSG_CONTEXT_GAME=0x00020000, - NETMSG_CONTEXT_GLOBAL=0x00040000, - - // connection phase - NETMSG_CLIENT_CONNECT=NETMSG_CONTEXT_CONNECT|1, - // str32 name - // str32 clan - // str32 password - // str32 skin - - // TODO: These should be implemented to make the server send the map to the client on connect - // NETMSG_CLIENT_FETCH, - // NETMSG_SERVER_MAPDATA, - - NETMSG_SERVER_ACCEPT=NETMSG_CONTEXT_CONNECT|2, - // str32 mapname - - - NETMSG_CLIENT_DONE=NETMSG_CONTEXT_CONNECT|3, - // nothing - - // game phase - NETMSG_SERVER_SNAP = NETMSG_CONTEXT_GAME|1, // server will spam these - // int num_parts - // int part - // int size - // data * - - NETMSG_CLIENT_INPUT = NETMSG_CONTEXT_GAME|1, // client will spam these - // int input[MAX_INPUTS] - - NETMSG_SERVER_EVENT = NETMSG_CONTEXT_GAME|2, - NETMSG_CLIENT_EVENT = NETMSG_CONTEXT_GAME|2, - - NETMSG_CLIENT_CHECKALIVE = NETMSG_CONTEXT_GAME|3, // check if client is alive - - NETMSG_CLIENT_ERROR=0x0fffffff, - // str128 reason - - NETMSG_SERVER_ERROR=0x0fffffff, - // str128 reason -}; - -enum -{ - MAX_NAME_LENGTH=32, - MAX_CLANNAME_LENGTH=32, - MAX_INPUT_SIZE=128, - MAX_SNAPSHOT_SIZE=64*1024, - MAX_SNAPSHOT_PACKSIZE=768 -}; |