diff options
| author | Magnus Auvinen <magnus.auvinen@gmail.com> | 2008-10-02 12:29:19 +0000 |
|---|---|---|
| committer | Magnus Auvinen <magnus.auvinen@gmail.com> | 2008-10-02 12:29:19 +0000 |
| commit | cebda9790bfa7d109014d5871d79e9ecd88c7d50 (patch) | |
| tree | d315b73baf879c0414cf652b89690796cc2f1f3b /src | |
| parent | bddc6ec6cc369e87d9dd0c0d1749bb871e1537a8 (diff) | |
| download | zcatch-cebda9790bfa7d109014d5871d79e9ecd88c7d50.tar.gz zcatch-cebda9790bfa7d109014d5871d79e9ecd88c7d50.zip | |
some cleanups. splitted e_network.c into several files. continued on the ban support
Diffstat (limited to 'src')
| -rw-r--r-- | src/base/system.c | 45 | ||||
| -rw-r--r-- | src/base/system.h | 10 | ||||
| -rw-r--r-- | src/engine/client/ec_client.c | 6 | ||||
| -rw-r--r-- | src/engine/e_network.c | 1044 | ||||
| -rw-r--r-- | src/engine/e_network.h | 24 | ||||
| -rw-r--r-- | src/engine/e_network_client.c | 148 | ||||
| -rw-r--r-- | src/engine/e_network_conn.c | 364 | ||||
| -rw-r--r-- | src/engine/e_network_internal.h | 149 | ||||
| -rw-r--r-- | src/engine/e_network_server.c | 387 | ||||
| -rw-r--r-- | src/engine/e_ringbuffer.c | 61 | ||||
| -rw-r--r-- | src/engine/e_ringbuffer.h | 20 | ||||
| -rw-r--r-- | src/engine/server/es_server.c | 2 | ||||
| -rw-r--r-- | src/game/client/components/console.cpp | 4 |
13 files changed, 1224 insertions, 1040 deletions
diff --git a/src/base/system.c b/src/base/system.c index e2c37bbd..5fb48e12 100644 --- a/src/base/system.c +++ b/src/base/system.c @@ -58,6 +58,7 @@ static DBG_LOGGER loggers[16]; static int num_loggers = 0; static NETSTATS network_stats = {0}; +static MEMSTATS memory_stats = {0}; void dbg_logger(DBG_LOGGER logger) { @@ -140,41 +141,39 @@ void dbg_logger_file(const char *filename) int memory_alloced = 0; -struct memheader +typedef struct MEMHEADER { const char *filename; int line; int size; - struct memheader *prev; - struct memheader *next; -}; + struct MEMHEADER *prev; + struct MEMHEADER *next; +} MEMHEADER; -struct memtail +typedef struct MEMTAIL { int guard; -}; +} MEMTAIL; -static struct memheader *first = 0; - -int mem_allocated() -{ - return memory_alloced; -} +static struct MEMHEADER *first = 0; void *mem_alloc_debug(const char *filename, int line, unsigned size, unsigned alignment) { /* TODO: fix alignment */ /* TODO: add debugging */ - struct memheader *header = (struct memheader *)malloc(size+sizeof(struct memheader)+sizeof(struct memtail)); - struct memtail *tail = (struct memtail *)(((char*)(header+1))+size); + MEMHEADER *header = (struct MEMHEADER *)malloc(size+sizeof(MEMHEADER)+sizeof(MEMTAIL)); + MEMTAIL *tail = (struct MEMTAIL *)(((char*)(header+1))+size); header->size = size; header->filename = filename; header->line = line; - memory_alloced += header->size; + + memory_stats.allocated += header->size; + memory_stats.total_allocations++; + memory_stats.active_allocations++; tail->guard = 0xbaadc0de; - header->prev = (struct memheader *)0; + header->prev = (MEMHEADER *)0; header->next = first; if(first) first->prev = header; @@ -188,13 +187,14 @@ void mem_free(void *p) { if(p) { - struct memheader *header = (struct memheader *)p - 1; - struct memtail *tail = (struct memtail *)(((char*)(header+1))+header->size); + MEMHEADER *header = (MEMHEADER *)p - 1; + MEMTAIL *tail = (MEMTAIL *)(((char*)(header+1))+header->size); if(tail->guard != 0xbaadc0de) dbg_msg("mem", "!! %p", p); /* dbg_msg("mem", "-- %p", p); */ - memory_alloced -= header->size; + memory_stats.allocated -= header->size; + memory_stats.active_allocations--; if(header->prev) header->prev->next = header->next; @@ -210,7 +210,7 @@ void mem_free(void *p) void mem_debug_dump() { char buf[1024]; - struct memheader *header = first; + MEMHEADER *header = first; IOHANDLE f = io_open("memory.txt", IOFLAG_WRITE); while(header) @@ -1109,6 +1109,11 @@ int mem_comp(const void *a, const void *b, int size) return memcmp(a,b,size); } +const MEMSTATS *mem_stats() +{ + return &memory_stats; +} + void net_stats(NETSTATS *stats_inout) { *stats_inout = network_stats; diff --git a/src/base/system.h b/src/base/system.h index fdbfe72d..e7c3586f 100644 --- a/src/base/system.h +++ b/src/base/system.h @@ -885,7 +885,6 @@ int net_would_block(); int net_socket_read_wait(NETSOCKET sock, int time); void mem_debug_dump(); -int mem_allocated(); void swap_endian(void *data, unsigned elem_size, unsigned num); @@ -897,6 +896,15 @@ void dbg_logger_file(const char *filename); typedef struct { + int allocated; + int active_allocations; + int total_allocations; +} MEMSTATS; + +const MEMSTATS *mem_stats(); + +typedef struct +{ int sent_packets; int sent_bytes; int recv_packets; diff --git a/src/engine/client/ec_client.c b/src/engine/client/ec_client.c index 1a6a24f1..8d8bdad7 100644 --- a/src/engine/client/ec_client.c +++ b/src/engine/client/ec_client.c @@ -586,12 +586,12 @@ static void client_debug_render() udp = 8 total = 42 */ - frametime_avg = frametime_avg*0.9f + frametime*0.1f; - str_format(buffer, sizeof(buffer), "ticks: %8d %8d snaploss: %d mem %dk gfxmem: %dk fps: %3d", + str_format(buffer, sizeof(buffer), "ticks: %8d %8d snaploss: %d mem %dk %d gfxmem: %dk fps: %3d", current_tick, current_predtick, snaploss, - mem_allocated()/1024, + mem_stats()->allocated/1024, + mem_stats()->total_allocations, gfx_memory_usage()/1024, (int)(1.0f/frametime_avg)); gfx_quads_text(2, 2, 16, buffer); diff --git a/src/engine/e_network.c b/src/engine/e_network.c index 85a9117c..2ed45707 100644 --- a/src/engine/e_network.c +++ b/src/engine/e_network.c @@ -5,213 +5,77 @@ #include "e_config.h" #include "e_network.h" +#include "e_network_internal.h" #include "e_huffman.h" -/* - -CURRENT: - packet header: 3 bytes - unsigned char flags_ack; // 4bit flags, 4bit ack - unsigned char ack; // 8 bit ack - unsigned char num_chunks; // 8 bit chunks - - (unsigned char padding[3]) // 24 bit extra incase it's a connection less packet - // this is to make sure that it's compatible with the - // old protocol - - chunk header: 2-3 bytes - unsigned char flags_size; // 2bit flags, 6 bit size - unsigned char size_seq; // 4bit size, 4bit seq - (unsigned char seq;) // 8bit seq, if vital flag is set -*/ - -enum +void recvinfo_clear(NETRECVINFO *info) { - NET_VERSION = 2, - - NET_MAX_CHUNKSIZE = 1024, - NET_MAX_PAYLOAD = NET_MAX_CHUNKSIZE+16, - NET_MAX_PACKETSIZE = NET_MAX_PAYLOAD+16, - NET_MAX_CHUNKHEADERSIZE = 5, - NET_PACKETHEADERSIZE = 3, - NET_MAX_CLIENTS = 128, - NET_MAX_SEQUENCE = 1<<10, - NET_SEQUENCE_MASK = NET_MAX_SEQUENCE-1, - - NET_CONNSTATE_OFFLINE=0, - NET_CONNSTATE_CONNECT=1, - NET_CONNSTATE_CONNECTACCEPTED=2, - NET_CONNSTATE_ONLINE=3, - NET_CONNSTATE_ERROR=4, - - NET_PACKETFLAG_CONTROL=1, - NET_PACKETFLAG_CONNLESS=2, - NET_PACKETFLAG_RESEND=4, - NET_PACKETFLAG_COMPRESSION=8, - - NET_CHUNKFLAG_VITAL=1, - NET_CHUNKFLAG_RESEND=2, - - NET_CTRLMSG_KEEPALIVE=0, - NET_CTRLMSG_CONNECT=1, - NET_CTRLMSG_CONNECTACCEPT=2, - NET_CTRLMSG_ACCEPT=3, - NET_CTRLMSG_CLOSE=4, - - NET_ENUM_TERMINATOR -}; - -typedef struct -{ - int flags; - int ack; - int num_chunks; - int data_size; - unsigned char chunk_data[NET_MAX_PAYLOAD]; -} NETPACKETCONSTRUCT; - -typedef struct -{ - int flags; - int size; - int sequence; -} NETCHUNKHEADER; - -typedef struct -{ - int flags; - int data_size; - unsigned char *data; - - int sequence; - int64 first_send_time; -} NETCHUNKDATA; - -typedef struct RINGBUFFER_ITEM -{ - struct RINGBUFFER_ITEM *next; - struct RINGBUFFER_ITEM *prev; - int size; -} RINGBUFFER_ITEM; - -typedef struct -{ - RINGBUFFER_ITEM *first; - RINGBUFFER_ITEM *last; - unsigned buffer_size; -} RINGBUFFER; - -static void rb_init(RINGBUFFER *rb) -{ - rb->first = 0; - rb->last = 0; - rb->buffer_size = 0; + info->valid = 0; } -static void *rb_item_data(RINGBUFFER_ITEM *item) +void recvinfo_start(NETRECVINFO *info, NETADDR *addr, NETCONNECTION *conn, int cid) { - return (void*)(item+1); + info->addr = *addr; + info->conn = conn; + info->client_id = cid; + info->current_chunk = 0; + info->valid = 1; } -static void *rb_alloc(RINGBUFFER *rb, int size) +/* TODO: rename this function */ +int recvinfo_fetch_chunk(NETRECVINFO *info, NETCHUNK *chunk) { - RINGBUFFER_ITEM *item = (RINGBUFFER_ITEM*)mem_alloc(sizeof(RINGBUFFER_ITEM)+size, 1); - item->size = size; - - item->prev = rb->last; - item->next = 0; - if(rb->last) - rb->last->next = item; - else - rb->first = item; - rb->last = item; + NETCHUNKHEADER header; + unsigned char *data = info->data.chunk_data; + int i; - rb->buffer_size += size; - return rb_item_data(item); -} - -static void rb_pop_first(RINGBUFFER *rb) -{ - if(rb->first) + while(1) { - RINGBUFFER_ITEM *next = rb->first->next; - rb->buffer_size -= rb->first->size; - mem_free(rb->first); - rb->first = next; - if(rb->first) - rb->first->prev = (void*)0; - else - rb->last = (void*)0; + /* check for old data to unpack */ + if(!info->valid || info->current_chunk >= info->data.num_chunks) + { + recvinfo_clear(info); + return 0; + } + + /* TODO: add checking here so we don't read too far */ + for(i = 0; i < info->current_chunk; i++) + { + data = unpack_chunk_header(data, &header); + data += header.size; + } + + /* unpack the header */ + data = unpack_chunk_header(data, &header); + info->current_chunk++; + + /* handle sequence stuff */ + if(info->conn && (header.flags&NET_CHUNKFLAG_VITAL)) + { + if(header.sequence == (info->conn->ack+1)%NET_MAX_SEQUENCE) + { + /* in sequence */ + info->conn->ack = (info->conn->ack+1)%NET_MAX_SEQUENCE; + } + else + { + /* out of sequence, request resend */ + dbg_msg("conn", "asking for resend %d %d", header.sequence, (info->conn->ack+1)%NET_MAX_SEQUENCE); + conn_want_resend(info->conn); + continue; /* take the next chunk in the packet */ + } + } + + /* fill in the info */ + chunk->client_id = info->client_id; + chunk->address = info->addr; + chunk->flags = 0; + chunk->data_size = header.size; + chunk->data = data; + return 1; } } -static void rb_clear(RINGBUFFER *rb) -{ - while(rb->first) - rb_pop_first(rb); -} - - -typedef struct -{ - unsigned short seq; - unsigned short ack; - unsigned state; - - int token; - int remote_closed; - - RINGBUFFER buffer; - - int64 last_update_time; - int64 last_recv_time; - int64 last_send_time; - - char error_string[256]; - - NETPACKETCONSTRUCT construct; - - NETADDR peeraddr; - NETSOCKET socket; - NETSTATS stats; -} NETCONNECTION; - -typedef struct -{ - NETCONNECTION conn; -} NETSLOT; - -typedef struct -{ - NETADDR addr; - NETCONNECTION *conn; - int current_chunk; - int client_id; - int valid; - NETPACKETCONSTRUCT data; - unsigned char buffer[NET_MAX_PACKETSIZE]; -} NETRECVINFO; - -struct NETSERVER -{ - NETSOCKET socket; - NETSLOT slots[NET_MAX_CLIENTS]; - int max_clients; - NETFUNC_NEWCLIENT new_client; - NETFUNC_NEWCLIENT del_client; - void *user_ptr; - - NETRECVINFO recv; -}; - -struct NETCLIENT -{ - NETADDR server_addr; - NETSOCKET socket; - - NETRECVINFO recv; - NETCONNECTION conn; -}; static IOHANDLE datalog = 0; static HUFFMAN_STATE huffmanstate; @@ -219,7 +83,7 @@ static HUFFMAN_STATE huffmanstate; #define COMPRESSION 1 /* packs the data tight and sends it */ -static void send_packet_connless(NETSOCKET socket, NETADDR *addr, const void *data, int data_size) +void send_packet_connless(NETSOCKET socket, NETADDR *addr, const void *data, int data_size) { unsigned char buffer[NET_MAX_PACKETSIZE]; buffer[0] = 0xff; @@ -232,7 +96,7 @@ static void send_packet_connless(NETSOCKET socket, NETADDR *addr, const void *da net_udp_send(socket, addr, buffer, 6+data_size); } -static void send_packet(NETSOCKET socket, NETADDR *addr, NETPACKETCONSTRUCT *packet) +void send_packet(NETSOCKET socket, NETADDR *addr, NETPACKETCONSTRUCT *packet) { unsigned char buffer[NET_MAX_PACKETSIZE]; int compressed_size = -1; @@ -274,7 +138,7 @@ static void send_packet(NETSOCKET socket, NETADDR *addr, NETPACKETCONSTRUCT *pac } /* TODO: rename this function */ -static int unpack_packet(unsigned char *buffer, int size, NETPACKETCONSTRUCT *packet) +int unpack_packet(unsigned char *buffer, int size, NETPACKETCONSTRUCT *packet) { /* check the size */ if(size < NET_PACKETHEADERSIZE || size > NET_MAX_PACKETSIZE) @@ -311,7 +175,7 @@ static int unpack_packet(unsigned char *buffer, int size, NETPACKETCONSTRUCT *pa /* TODO: change the arguments of this function */ -static unsigned char *pack_chunk_header(unsigned char *data, int flags, int size, int sequence) +unsigned char *pack_chunk_header(unsigned char *data, int flags, int size, int sequence) { data[0] = ((flags&3)<<6)|((size>>4)&0x3f); data[1] = (size&0xf); @@ -324,7 +188,7 @@ static unsigned char *pack_chunk_header(unsigned char *data, int flags, int size return data + 2; } -static unsigned char *unpack_chunk_header(unsigned char *data, NETCHUNKHEADER *header) +unsigned char *unpack_chunk_header(unsigned char *data, NETCHUNKHEADER *header) { header->flags = (data[0]>>6)&3; header->size = ((data[0]&0x3f)<<4) | (data[1]&0xf); @@ -338,790 +202,6 @@ static unsigned char *unpack_chunk_header(unsigned char *data, NETCHUNKHEADER *h } -static void conn_reset_stats(NETCONNECTION *conn) -{ - mem_zero(&conn->stats, sizeof(conn->stats)); -} - -static void conn_reset(NETCONNECTION *conn) -{ - conn->seq = 0; - conn->ack = 0; - conn->remote_closed = 0; - - conn->state = NET_CONNSTATE_OFFLINE; - conn->state = NET_CONNSTATE_OFFLINE; - conn->last_send_time = 0; - conn->last_recv_time = 0; - conn->last_update_time = 0; - conn->token = -1; - mem_zero(&conn->peeraddr, sizeof(conn->peeraddr)); - - rb_clear(&conn->buffer); - - mem_zero(&conn->construct, sizeof(conn->construct)); -} - - -static const char *conn_error(NETCONNECTION *conn) -{ - return conn->error_string; -} - -static void conn_set_error(NETCONNECTION *conn, const char *str) -{ - str_copy(conn->error_string, str, sizeof(conn->error_string)); -} - -static void conn_init(NETCONNECTION *conn, NETSOCKET socket) -{ - conn_reset(conn); - conn_reset_stats(conn); - conn->socket = socket; - rb_init(&conn->buffer); - mem_zero(conn->error_string, sizeof(conn->error_string)); -} - - -static void conn_ack(NETCONNECTION *conn, int ack) -{ - while(1) - { - RINGBUFFER_ITEM *item = conn->buffer.first; - NETCHUNKDATA *resend; - if(!item) - break; - - resend = (NETCHUNKDATA *)rb_item_data(item); - if(resend->sequence <= ack || (ack < NET_MAX_SEQUENCE/3 && resend->sequence > NET_MAX_SEQUENCE/2)) - rb_pop_first(&conn->buffer); - else - break; - } -} - -static void conn_want_resend(NETCONNECTION *conn) -{ - conn->construct.flags |= NET_PACKETFLAG_RESEND; -} - -static int conn_flush(NETCONNECTION *conn) -{ - int num_chunks = conn->construct.num_chunks; - if(!num_chunks && !conn->construct.flags) - return 0; - - conn->construct.ack = conn->ack; - send_packet(conn->socket, &conn->peeraddr, &conn->construct); - conn->last_send_time = time_get(); - - /* clear construct so we can start building a new package */ - mem_zero(&conn->construct, sizeof(conn->construct)); - return num_chunks; -} - -/*NETCHUNKDATA *data*/ - -static void conn_queue_chunk(NETCONNECTION *conn, int flags, int data_size, const void *data) -{ - unsigned char *chunk_data; - /* check if we have space for it, if not, flush the connection */ - if(conn->construct.data_size + data_size + NET_MAX_CHUNKHEADERSIZE > sizeof(conn->construct.chunk_data)) - conn_flush(conn); - - if(flags&NET_CHUNKFLAG_VITAL && !(flags&NET_CHUNKFLAG_RESEND)) - conn->seq = (conn->seq+1)%NET_MAX_SEQUENCE; - - /* pack all the data */ - chunk_data = &conn->construct.chunk_data[conn->construct.data_size]; - chunk_data = pack_chunk_header(chunk_data, flags, data_size, conn->seq); - mem_copy(chunk_data, data, data_size); - chunk_data += data_size; - - /* */ - conn->construct.num_chunks++; - conn->construct.data_size = (int)(chunk_data-conn->construct.chunk_data); - - /* set packet flags aswell */ - - if(flags&NET_CHUNKFLAG_VITAL && !(flags&NET_CHUNKFLAG_RESEND)) - { - /* save packet if we need to resend */ - NETCHUNKDATA *resend = (NETCHUNKDATA *)rb_alloc(&conn->buffer, sizeof(NETCHUNKDATA)+data_size); - resend->sequence = conn->seq; - resend->flags = flags; - resend->data_size = data_size; - resend->data = (unsigned char *)(resend+1); - resend->first_send_time = time_get(); - mem_copy(resend->data, data, data_size); - } -} - -static void conn_send_control(NETCONNECTION *conn, int controlmsg, const void *extra, int extra_size) -{ - NETPACKETCONSTRUCT construct; - construct.flags = NET_PACKETFLAG_CONTROL; - construct.ack = conn->ack; - construct.num_chunks = 0; - construct.data_size = 1+extra_size; - construct.chunk_data[0] = controlmsg; - mem_copy(&construct.chunk_data[1], extra, extra_size); - - /* send the control message */ - send_packet(conn->socket, &conn->peeraddr, &construct); - conn->last_send_time = time_get(); -} - -static void conn_resend(NETCONNECTION *conn) -{ - int resend_count = 0; - int max = 10; - RINGBUFFER_ITEM *item = conn->buffer.first; - while(item) - { - NETCHUNKDATA *resend = (NETCHUNKDATA *)rb_item_data(item); - conn_queue_chunk(conn, resend->flags|NET_CHUNKFLAG_RESEND, resend->data_size, resend->data); - item = item->next; - max--; - resend_count++; - if(!max) - break; - } - - dbg_msg("conn", "resent %d packets", resend_count); -} - -static int conn_connect(NETCONNECTION *conn, NETADDR *addr) -{ - if(conn->state != NET_CONNSTATE_OFFLINE) - return -1; - - /* init connection */ - conn_reset(conn); - conn->peeraddr = *addr; - mem_zero(conn->error_string, sizeof(conn->error_string)); - conn->state = NET_CONNSTATE_CONNECT; - conn_send_control(conn, NET_CTRLMSG_CONNECT, 0, 0); - return 0; -} - -static void conn_disconnect(NETCONNECTION *conn, const char *reason) -{ - if(conn->state == NET_CONNSTATE_OFFLINE) - return; - - if(conn->remote_closed == 0) - { - if(reason) - conn_send_control(conn, NET_CTRLMSG_CLOSE, reason, strlen(reason)+1); - else - conn_send_control(conn, NET_CTRLMSG_CLOSE, 0, 0); - - conn->error_string[0] = 0; - if(reason) - str_copy(conn->error_string, reason, sizeof(conn->error_string)); - } - - conn_reset(conn); -} - -static int conn_feed(NETCONNECTION *conn, NETPACKETCONSTRUCT *packet, NETADDR *addr) -{ - int64 now = time_get(); - conn->last_recv_time = now; - - /* check if resend is requested */ - if(packet->flags&NET_PACKETFLAG_RESEND) - conn_resend(conn); - - /* */ - if(packet->flags&NET_PACKETFLAG_CONTROL) - { - int ctrlmsg = packet->chunk_data[0]; - - if(ctrlmsg == NET_CTRLMSG_CLOSE) - { - conn->state = NET_CONNSTATE_ERROR; - conn->remote_closed = 1; - - if(packet->data_size) - { - /* make sure to sanitize the error string form the other party*/ - char str[128]; - if(packet->data_size < 128) - str_copy(str, (char *)packet->chunk_data, packet->data_size); - else - str_copy(str, (char *)packet->chunk_data, 128); - str_sanitize_strong(str); - - /* set the error string */ - conn_set_error(conn, str); - } - else - conn_set_error(conn, "no reason given"); - - if(config.debug) - dbg_msg("conn", "closed reason='%s'", conn_error(conn)); - return 0; - } - else - { - if(conn->state == NET_CONNSTATE_OFFLINE) - { - if(ctrlmsg == NET_CTRLMSG_CONNECT) - { - /* send response and init connection */ - conn_reset(conn); - conn->state = NET_CONNSTATE_ONLINE; - conn->peeraddr = *addr; - conn->last_send_time = now; - conn->last_recv_time = now; - conn->last_update_time = now; - conn_send_control(conn, NET_CTRLMSG_CONNECTACCEPT, 0, 0); - if(config.debug) - dbg_msg("connection", "got connection, sending connect+accept"); - } - } - else if(conn->state == NET_CONNSTATE_CONNECT) - { - /* connection made */ - if(ctrlmsg == NET_CTRLMSG_CONNECTACCEPT) - { - conn_send_control(conn, NET_CTRLMSG_ACCEPT, 0, 0); - conn->state = NET_CONNSTATE_ONLINE; - if(config.debug) - dbg_msg("connection", "got connect+accept, sending accept. connection online"); - } - } - else if(conn->state == NET_CONNSTATE_ONLINE) - { - /* connection made */ - /* - if(ctrlmsg == NET_CTRLMSG_CONNECTACCEPT) - { - - }*/ - } - } - } - - if(conn->state == NET_CONNSTATE_ONLINE) - { - conn_ack(conn, packet->ack); - } - - return 1; -} - -static int conn_update(NETCONNECTION *conn) -{ - int64 now = time_get(); - - if(conn->state == NET_CONNSTATE_OFFLINE || conn->state == NET_CONNSTATE_ERROR) - return 0; - - /* watch out for major hitches */ - { - /* TODO: fix this */ - /* - int64 delta = now-conn->last_update_time; - if(conn->last_update_time && delta > time_freq()/2) - { - RINGBUFFER_ITEM *item = conn->buffer.first; - - dbg_msg("conn", "hitch %d", (int)((delta*1000)/time_freq())); - conn->last_recv_time += delta; - - while(item) - { - NETPACKETDATA *resend = (NETPACKETDATA *)rb_item_data(item); - resend->first_send_time += delta; - item = item->next; - } - } - - conn->last_update_time = now; - */ - } - - - /* check for timeout */ - if(conn->state != NET_CONNSTATE_OFFLINE && - conn->state != NET_CONNSTATE_CONNECT && - (now-conn->last_recv_time) > time_freq()*10) - { - conn->state = NET_CONNSTATE_ERROR; - conn_set_error(conn, "timeout"); - } - - /* check for large buffer errors */ - if(conn->buffer.buffer_size > 1024*64) - { - conn->state = NET_CONNSTATE_ERROR; - conn_set_error(conn, "too weak connection (out of buffer)"); - } - - if(conn->buffer.first) - { - /* TODO: fix this */ - NETCHUNKDATA *resend = (NETCHUNKDATA *)(conn->buffer.first+1); - if(now-resend->first_send_time > time_freq()*10) - { - conn->state = NET_CONNSTATE_ERROR; - conn_set_error(conn, "too weak connection (not acked for 10 seconds)"); - } - } - - /* send keep alives if nothing has happend for 1000ms */ - if(conn->state == NET_CONNSTATE_ONLINE) - { - if(time_get()-conn->last_send_time > time_freq()/2) /* flush connection after 250ms if needed */ - { - int num_flushed_chunks = conn_flush(conn); - if(num_flushed_chunks && config.debug) - dbg_msg("connection", "flushed connection due to timeout. %d chunks.", num_flushed_chunks); - } - - if(time_get()-conn->last_send_time > time_freq()) - conn_send_control(conn, NET_CTRLMSG_KEEPALIVE, 0, 0); - } - else if(conn->state == NET_CONNSTATE_CONNECT) - { - if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect every 500ms */ - conn_send_control(conn, NET_CTRLMSG_CONNECT, 0, 0); - /*conn_send(conn, NETWORK_PACKETFLAG_CONNECT, 0, 0);*/ - } - else if(conn->state == NET_CONNSTATE_CONNECTACCEPTED) - { - - if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect/accept every 500ms */ - conn_send_control(conn, NET_CTRLMSG_CONNECTACCEPT, 0, 0); - /*conn_send(conn, NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT, 0, 0);*/ - } - - return 0; -} - -NETSERVER *netserver_open(NETADDR bindaddr, int max_clients, int flags) -{ - int i; - NETSERVER *server; - NETSOCKET socket = net_udp_create(bindaddr); - if(socket == NETSOCKET_INVALID) - return 0; - - server = (NETSERVER *)mem_alloc(sizeof(NETSERVER), 1); - mem_zero(server, sizeof(NETSERVER)); - server->socket = socket; - server->max_clients = max_clients; - if(server->max_clients > NET_MAX_CLIENTS) - server->max_clients = NET_MAX_CLIENTS; - if(server->max_clients < 1) - server->max_clients = 1; - - for(i = 0; i < NET_MAX_CLIENTS; i++) - conn_init(&server->slots[i].conn, server->socket); - - return server; -} - -int netserver_set_callbacks(NETSERVER *s, NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user) -{ - s->new_client = new_client; - s->del_client = del_client; - s->user_ptr = user; - return 0; -} - -int netserver_max_clients(NETSERVER *s) -{ - return s->max_clients; -} - -int netserver_close(NETSERVER *s) -{ - /* TODO: implement me */ - return 0; -} - -int netserver_drop(NETSERVER *s, int client_id, const char *reason) -{ - /* TODO: insert lots of checks here */ - dbg_msg("net_server", "client dropped. cid=%d reason=\"%s\"", client_id, reason); - conn_disconnect(&s->slots[client_id].conn, reason); - - if(s->del_client) - s->del_client(client_id, s->user_ptr); - - return 0; -} - -int netserver_update(NETSERVER *s) -{ - int i; - for(i = 0; i < s->max_clients; i++) - { - conn_update(&s->slots[i].conn); - if(s->slots[i].conn.state == NET_CONNSTATE_ERROR) - netserver_drop(s, i, conn_error(&s->slots[i].conn)); - } - return 0; -} - -static void recvinfo_clear(NETRECVINFO *info) -{ - info->valid = 0; -} - -static void recvinfo_start(NETRECVINFO *info, NETADDR *addr, NETCONNECTION *conn, int cid) -{ - info->addr = *addr; - info->conn = conn; - info->client_id = cid; - info->current_chunk = 0; - info->valid = 1; -} - -/* TODO: rename this function */ -static int recvinfo_fetch_chunk(NETRECVINFO *info, NETCHUNK *chunk) -{ - NETCHUNKHEADER header; - unsigned char *data = info->data.chunk_data; - int i; - - while(1) - { - /* check for old data to unpack */ - if(!info->valid || info->current_chunk >= info->data.num_chunks) - { - recvinfo_clear(info); - return 0; - } - - /* TODO: add checking here so we don't read too far */ - for(i = 0; i < info->current_chunk; i++) - { - data = unpack_chunk_header(data, &header); - data += header.size; - } - - /* unpack the header */ - data = unpack_chunk_header(data, &header); - info->current_chunk++; - - /* handle sequence stuff */ - if(info->conn && (header.flags&NET_CHUNKFLAG_VITAL)) - { - if(header.sequence == (info->conn->ack+1)%NET_MAX_SEQUENCE) - { - /* in sequence */ - info->conn->ack = (info->conn->ack+1)%NET_MAX_SEQUENCE; - } - else - { - /* out of sequence, request resend */ - dbg_msg("conn", "asking for resend %d %d", header.sequence, (info->conn->ack+1)%NET_MAX_SEQUENCE); - conn_want_resend(info->conn); - continue; /* take the next chunk in the packet */ - } - } - - /* fill in the info */ - chunk->client_id = info->client_id; - chunk->address = info->addr; - chunk->flags = 0; - chunk->data_size = header.size; - chunk->data = data; - return 1; - } -} - -int netserver_recv(NETSERVER *s, NETCHUNK *chunk) -{ - while(1) - { - NETADDR addr; - int i, bytes, found; - - /* check for a chunk */ - if(recvinfo_fetch_chunk(&s->recv, chunk)) - return 1; - - /* TODO: empty the recvinfo */ - bytes = net_udp_recv(s->socket, &addr, s->recv.buffer, NET_MAX_PACKETSIZE); - - /* no more packets for now */ - if(bytes <= 0) - break; - - if(unpack_packet(s->recv.buffer, bytes, &s->recv.data) == 0) - { - if(s->recv.data.flags&NET_PACKETFLAG_CONNLESS) - { - chunk->flags = NETSENDFLAG_CONNLESS; - chunk->client_id = -1; - chunk->address = addr; - chunk->data_size = s->recv.data.data_size; - chunk->data = s->recv.data.chunk_data; - return 1; - } - else - { - /* TODO: check size here */ - if(s->recv.data.flags&NET_PACKETFLAG_CONTROL && s->recv.data.chunk_data[0] == NET_CTRLMSG_CONNECT) - { - found = 0; - - /* check if we already got this client */ - for(i = 0; i < s->max_clients; i++) - { - if(s->slots[i].conn.state != NET_CONNSTATE_OFFLINE && - net_addr_comp(&s->slots[i].conn.peeraddr, &addr) == 0) - { - found = 1; /* silent ignore.. we got this client already */ - break; - } - } - - /* client that wants to connect */ - if(!found) - { - found = 0; - - for(i = 0; i < s->max_clients; i++) - { - if(s->slots[i].conn.state == NET_CONNSTATE_OFFLINE) - { - found = 1; - conn_feed(&s->slots[i].conn, &s->recv.data, &addr); - if(s->new_client) - s->new_client(i, s->user_ptr); - break; - } - } - - if(!found) - { - /* TODO: send server full emssage */ - } - } - } - else - { - /* normal packet, find matching slot */ - for(i = 0; i < s->max_clients; i++) - { - if(net_addr_comp(&s->slots[i].conn.peeraddr, &addr) == 0) - { - if(conn_feed(&s->slots[i].conn, &s->recv.data, &addr)) - { - if(s->recv.data.data_size) - recvinfo_start(&s->recv, &addr, &s->slots[i].conn, i); - } - } - } - } - } - } - } - return 0; -} - -int netserver_send(NETSERVER *s, NETCHUNK *chunk) -{ - if(chunk->data_size >= NET_MAX_PAYLOAD) - { - dbg_msg("netserver", "packet payload too big. %d. dropping packet", chunk->data_size); - return -1; - } - - if(chunk->flags&NETSENDFLAG_CONNLESS) - { - /* send connectionless packet */ - send_packet_connless(s->socket, &chunk->address, chunk->data, chunk->data_size); - } - else - { - int f = 0; - dbg_assert(chunk->client_id >= 0, "errornous client id"); - dbg_assert(chunk->client_id < s->max_clients, "errornous client id"); - - if(chunk->flags&NETSENDFLAG_VITAL) - f = NET_CHUNKFLAG_VITAL; - - conn_queue_chunk(&s->slots[chunk->client_id].conn, f, chunk->data_size, chunk->data); - - if(chunk->flags&NETSENDFLAG_FLUSH) - conn_flush(&s->slots[chunk->client_id].conn); - } - return 0; -} - -void netserver_stats(NETSERVER *s, NETSTATS *stats) -{ - int num_stats = sizeof(NETSTATS)/sizeof(int); - int *istats = (int *)stats; - int c, i; - - mem_zero(stats, sizeof(NETSTATS)); - - for(c = 0; c < s->max_clients; c++) - { - if(s->slots[c].conn.state != NET_CONNSTATE_OFFLINE) - { - int *sstats = (int *)(&(s->slots[c].conn.stats)); - for(i = 0; i < num_stats; i++) - istats[i] += sstats[i]; - } - } -} - -NETSOCKET netserver_socket(NETSERVER *s) -{ - return s->socket; -} - -int netserver_client_addr(NETSERVER *s, int client_id, NETADDR *addr) -{ - *addr = s->slots[client_id].conn.peeraddr; - return 1; -} - -NETCLIENT *netclient_open(NETADDR bindaddr, int flags) -{ - NETCLIENT *client = (NETCLIENT *)mem_alloc(sizeof(NETCLIENT), 1); - mem_zero(client, sizeof(NETCLIENT)); - client->socket = net_udp_create(bindaddr); - conn_init(&client->conn, client->socket); - return client; -} - -int netclient_close(NETCLIENT *c) -{ - /* TODO: implement me */ - return 0; -} - -int netclient_update(NETCLIENT *c) -{ - conn_update(&c->conn); - if(c->conn.state == NET_CONNSTATE_ERROR) - netclient_disconnect(c, conn_error(&c->conn)); - return 0; -} - -int netclient_disconnect(NETCLIENT *c, const char *reason) -{ - dbg_msg("netclient", "disconnected. reason=\"%s\"", reason); - conn_disconnect(&c->conn, reason); - return 0; -} - -int netclient_connect(NETCLIENT *c, NETADDR *addr) -{ - conn_connect(&c->conn, addr); - return 0; -} - -int netclient_recv(NETCLIENT *c, NETCHUNK *chunk) -{ - while(1) - { - NETADDR addr; - int bytes; - - /* check for a chunk */ - if(recvinfo_fetch_chunk(&c->recv, chunk)) - return 1; - - /* TODO: empty the recvinfo */ - bytes = net_udp_recv(c->socket, &addr, c->recv.buffer, NET_MAX_PACKETSIZE); - - /* no more packets for now */ - if(bytes <= 0) - break; - - if(unpack_packet(c->recv.buffer, bytes, &c->recv.data) == 0) - { - if(c->recv.data.flags&NET_PACKETFLAG_CONNLESS) - { - chunk->flags = NETSENDFLAG_CONNLESS; - chunk->client_id = -1; - chunk->address = addr; - chunk->data_size = c->recv.data.data_size; - chunk->data = c->recv.data.chunk_data; - return 1; - } - else - { - if(conn_feed(&c->conn, &c->recv.data, &addr)) - recvinfo_start(&c->recv, &addr, &c->conn, 0); - } - } - } - return 0; -} - -int netclient_send(NETCLIENT *c, NETCHUNK *chunk) -{ - if(chunk->data_size >= NET_MAX_PAYLOAD) - { - dbg_msg("netclient", "chunk payload too big. %d. dropping chunk", chunk->data_size); - return -1; - } - - if(chunk->flags&NETSENDFLAG_CONNLESS) - { - /* send connectionless packet */ - send_packet_connless(c->socket, &chunk->address, chunk->data, chunk->data_size); - } - else - { - int f = 0; - dbg_assert(chunk->client_id == 0, "errornous client id"); - - if(chunk->flags&NETSENDFLAG_VITAL) - f = NET_CHUNKFLAG_VITAL; - - conn_queue_chunk(&c->conn, f, chunk->data_size, chunk->data); - - if(chunk->flags&NETSENDFLAG_FLUSH) - conn_flush(&c->conn); - } - return 0; -} - -int netclient_state(NETCLIENT *c) -{ - if(c->conn.state == NET_CONNSTATE_ONLINE) - return NETSTATE_ONLINE; - if(c->conn.state == NET_CONNSTATE_OFFLINE) - return NETSTATE_OFFLINE; - return NETSTATE_CONNECTING; -} - -int netclient_flush(NETCLIENT *c) -{ - return conn_flush(&c->conn); -} - -int netclient_gotproblems(NETCLIENT *c) -{ - if(time_get() - c->conn.last_recv_time > time_freq()) - return 1; - return 0; -} - -void netclient_stats(NETCLIENT *c, NETSTATS *stats) -{ - *stats = c->conn.stats; -} - -const char *netclient_error_string(NETCLIENT *c) -{ - return conn_error(&c->conn); -} - void netcommon_openlog(const char *filename) { datalog = io_open(filename, IOFLAG_WRITE); diff --git a/src/engine/e_network.h b/src/engine/e_network.h index efacc9fa..1ac02708 100644 --- a/src/engine/e_network.h +++ b/src/engine/e_network.h @@ -1,5 +1,8 @@ +#ifndef ENGINE_NETWORK_H +#define ENGINE_NETWORK_H /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + typedef struct { /* -1 means that it's a stateless packet */ @@ -11,6 +14,14 @@ typedef struct const void *data; } NETCHUNK; + +typedef struct +{ + NETADDR addr; + int type; + int expires; +} NETBANINFO; + /*typedef struct { int send_bytes; @@ -34,7 +45,10 @@ enum NETSTATE_OFFLINE=0, NETSTATE_CONNECTING, - NETSTATE_ONLINE + NETSTATE_ONLINE, + + NETBANTYPE_SOFT=1, + NETBANTYPE_DROP=2 }; typedef int (*NETFUNC_DELCLIENT)(int cid, void *user); @@ -55,6 +69,12 @@ NETSOCKET netserver_socket(NETSERVER *s); int netserver_drop(NETSERVER *s, int client_id, const char *reason); int netserver_client_addr(NETSERVER *s, int client_id, NETADDR *addr); int netserver_max_clients(NETSERVER *s); + +int netserver_ban_add(NETSERVER *s, NETADDR addr, int type, int seconds); +int netserver_ban_remove(NETSERVER *s, NETADDR addr); +int netserver_ban_num(NETSERVER *s); /* caution, slow */ +int netserver_ban_get(NETSERVER *s, int index, NETBANINFO *info); /* caution, slow */ + /*void netserver_stats(NETSERVER *s, NETSTATS *stats);*/ /* client side */ @@ -120,3 +140,5 @@ public: }; #endif + +#endif diff --git a/src/engine/e_network_client.c b/src/engine/e_network_client.c new file mode 100644 index 00000000..df7e4c26 --- /dev/null +++ b/src/engine/e_network_client.c @@ -0,0 +1,148 @@ +#include <base/system.h> +#include "e_network.h" +#include "e_network_internal.h" + +struct NETCLIENT +{ + NETADDR server_addr; + NETSOCKET socket; + + NETRECVINFO recv; + NETCONNECTION conn; +}; + +NETCLIENT *netclient_open(NETADDR bindaddr, int flags) +{ + NETCLIENT *client = (NETCLIENT *)mem_alloc(sizeof(NETCLIENT), 1); + mem_zero(client, sizeof(NETCLIENT)); + client->socket = net_udp_create(bindaddr); + conn_init(&client->conn, client->socket); + return client; +} + +int netclient_close(NETCLIENT *c) +{ + /* TODO: implement me */ + return 0; +} + + +int netclient_disconnect(NETCLIENT *c, const char *reason) +{ + dbg_msg("netclient", "disconnected. reason=\"%s\"", reason); + conn_disconnect(&c->conn, reason); + return 0; +} + +int netclient_update(NETCLIENT *c) +{ + conn_update(&c->conn); + if(c->conn.state == NET_CONNSTATE_ERROR) + netclient_disconnect(c, conn_error(&c->conn)); + return 0; +} + +int netclient_connect(NETCLIENT *c, NETADDR *addr) +{ + conn_connect(&c->conn, addr); + return 0; +} + +int netclient_recv(NETCLIENT *c, NETCHUNK *chunk) +{ + while(1) + { + NETADDR addr; + int bytes; + + /* check for a chunk */ + if(recvinfo_fetch_chunk(&c->recv, chunk)) + return 1; + + /* TODO: empty the recvinfo */ + bytes = net_udp_recv(c->socket, &addr, c->recv.buffer, NET_MAX_PACKETSIZE); + + /* no more packets for now */ + if(bytes <= 0) + break; + + if(unpack_packet(c->recv.buffer, bytes, &c->recv.data) == 0) + { + if(c->recv.data.flags&NET_PACKETFLAG_CONNLESS) + { + chunk->flags = NETSENDFLAG_CONNLESS; + chunk->client_id = -1; + chunk->address = addr; + chunk->data_size = c->recv.data.data_size; + chunk->data = c->recv.data.chunk_data; + return 1; + } + else + { + if(conn_feed(&c->conn, &c->recv.data, &addr)) + recvinfo_start(&c->recv, &addr, &c->conn, 0); + } + } + } + return 0; +} + +int netclient_send(NETCLIENT *c, NETCHUNK *chunk) +{ + if(chunk->data_size >= NET_MAX_PAYLOAD) + { + dbg_msg("netclient", "chunk payload too big. %d. dropping chunk", chunk->data_size); + return -1; + } + + if(chunk->flags&NETSENDFLAG_CONNLESS) + { + /* send connectionless packet */ + send_packet_connless(c->socket, &chunk->address, chunk->data, chunk->data_size); + } + else + { + int f = 0; + dbg_assert(chunk->client_id == 0, "errornous client id"); + + if(chunk->flags&NETSENDFLAG_VITAL) + f = NET_CHUNKFLAG_VITAL; + + conn_queue_chunk(&c->conn, f, chunk->data_size, chunk->data); + + if(chunk->flags&NETSENDFLAG_FLUSH) + conn_flush(&c->conn); + } + return 0; +} + +int netclient_state(NETCLIENT *c) +{ + if(c->conn.state == NET_CONNSTATE_ONLINE) + return NETSTATE_ONLINE; + if(c->conn.state == NET_CONNSTATE_OFFLINE) + return NETSTATE_OFFLINE; + return NETSTATE_CONNECTING; +} + +int netclient_flush(NETCLIENT *c) +{ + return conn_flush(&c->conn); +} + +int netclient_gotproblems(NETCLIENT *c) +{ + if(time_get() - c->conn.last_recv_time > time_freq()) + return 1; + return 0; +} + +void netclient_stats(NETCLIENT *c, NETSTATS *stats) +{ + *stats = c->conn.stats; +} + +const char *netclient_error_string(NETCLIENT *c) +{ + return conn_error(&c->conn); +} diff --git a/src/engine/e_network_conn.c b/src/engine/e_network_conn.c new file mode 100644 index 00000000..05ced197 --- /dev/null +++ b/src/engine/e_network_conn.c @@ -0,0 +1,364 @@ +#include <base/system.h> +#include <string.h> +#include "e_config.h" +#include "e_network_internal.h" + +static void conn_reset_stats(NETCONNECTION *conn) +{ + mem_zero(&conn->stats, sizeof(conn->stats)); +} + +static void conn_reset(NETCONNECTION *conn) +{ + conn->seq = 0; + conn->ack = 0; + conn->remote_closed = 0; + + conn->state = NET_CONNSTATE_OFFLINE; + conn->state = NET_CONNSTATE_OFFLINE; + conn->last_send_time = 0; + conn->last_recv_time = 0; + conn->last_update_time = 0; + conn->token = -1; + mem_zero(&conn->peeraddr, sizeof(conn->peeraddr)); + + conn->buffer = ringbuf_init(conn->buffer_memory, sizeof(conn->buffer_memory), 0); + + mem_zero(&conn->construct, sizeof(conn->construct)); +} + + +const char *conn_error(NETCONNECTION *conn) +{ + return conn->error_string; +} + +static void conn_set_error(NETCONNECTION *conn, const char *str) +{ + str_copy(conn->error_string, str, sizeof(conn->error_string)); +} + +void conn_init(NETCONNECTION *conn, NETSOCKET socket) +{ + conn_reset(conn); + conn_reset_stats(conn); + conn->socket = socket; + mem_zero(conn->error_string, sizeof(conn->error_string)); +} + + +static void conn_ack(NETCONNECTION *conn, int ack) +{ + while(1) + { + NETCHUNKDATA *resend = (NETCHUNKDATA *)ringbuf_first(conn->buffer); + if(!resend) + break; + + if(resend->sequence <= ack || (ack < NET_MAX_SEQUENCE/3 && resend->sequence > NET_MAX_SEQUENCE/2)) + ringbuf_popfirst(conn->buffer); + else + break; + } +} + +void conn_want_resend(NETCONNECTION *conn) +{ + conn->construct.flags |= NET_PACKETFLAG_RESEND; +} + +int conn_flush(NETCONNECTION *conn) +{ + int num_chunks = conn->construct.num_chunks; + if(!num_chunks && !conn->construct.flags) + return 0; + + conn->construct.ack = conn->ack; + send_packet(conn->socket, &conn->peeraddr, &conn->construct); + conn->last_send_time = time_get(); + + /* clear construct so we can start building a new package */ + mem_zero(&conn->construct, sizeof(conn->construct)); + return num_chunks; +} + +void conn_queue_chunk(NETCONNECTION *conn, int flags, int data_size, const void *data) +{ + unsigned char *chunk_data; + /* check if we have space for it, if not, flush the connection */ + if(conn->construct.data_size + data_size + NET_MAX_CHUNKHEADERSIZE > sizeof(conn->construct.chunk_data)) + conn_flush(conn); + + if(flags&NET_CHUNKFLAG_VITAL && !(flags&NET_CHUNKFLAG_RESEND)) + conn->seq = (conn->seq+1)%NET_MAX_SEQUENCE; + + /* pack all the data */ + chunk_data = &conn->construct.chunk_data[conn->construct.data_size]; + chunk_data = pack_chunk_header(chunk_data, flags, data_size, conn->seq); + mem_copy(chunk_data, data, data_size); + chunk_data += data_size; + + /* */ + conn->construct.num_chunks++; + conn->construct.data_size = (int)(chunk_data-conn->construct.chunk_data); + + /* set packet flags aswell */ + + if(flags&NET_CHUNKFLAG_VITAL && !(flags&NET_CHUNKFLAG_RESEND)) + { + /* save packet if we need to resend */ + NETCHUNKDATA *resend = (NETCHUNKDATA *)ringbuf_allocate(conn->buffer, sizeof(NETCHUNKDATA)+data_size); + if(resend) + { + resend->sequence = conn->seq; + resend->flags = flags; + resend->data_size = data_size; + resend->data = (unsigned char *)(resend+1); + resend->first_send_time = time_get(); + mem_copy(resend->data, data, data_size); + } + else + { + /* out of buffer */ + conn_disconnect(conn, "too weak connection (out of buffer)"); + } + } +} + +static void conn_send_control(NETCONNECTION *conn, int controlmsg, const void *extra, int extra_size) +{ + NETPACKETCONSTRUCT construct; + construct.flags = NET_PACKETFLAG_CONTROL; + construct.ack = conn->ack; + construct.num_chunks = 0; + construct.data_size = 1+extra_size; + construct.chunk_data[0] = controlmsg; + mem_copy(&construct.chunk_data[1], extra, extra_size); + + /* send the control message */ + send_packet(conn->socket, &conn->peeraddr, &construct); + conn->last_send_time = time_get(); +} + +static void conn_resend(NETCONNECTION *conn) +{ + int resend_count = 0; + int max = 10; + void *item = ringbuf_first(conn->buffer); + while(item) + { + NETCHUNKDATA *resend = item; + conn_queue_chunk(conn, resend->flags|NET_CHUNKFLAG_RESEND, resend->data_size, resend->data); + item = ringbuf_next(conn->buffer, item); + max--; + resend_count++; + if(!max) + break; + } + + dbg_msg("conn", "resent %d packets", resend_count); +} + +int conn_connect(NETCONNECTION *conn, NETADDR *addr) +{ + if(conn->state != NET_CONNSTATE_OFFLINE) + return -1; + + /* init connection */ + conn_reset(conn); + conn->peeraddr = *addr; + mem_zero(conn->error_string, sizeof(conn->error_string)); + conn->state = NET_CONNSTATE_CONNECT; + conn_send_control(conn, NET_CTRLMSG_CONNECT, 0, 0); + return 0; +} + +void conn_disconnect(NETCONNECTION *conn, const char *reason) +{ + if(conn->state == NET_CONNSTATE_OFFLINE) + return; + + if(conn->remote_closed == 0) + { + if(reason) + conn_send_control(conn, NET_CTRLMSG_CLOSE, reason, strlen(reason)+1); + else + conn_send_control(conn, NET_CTRLMSG_CLOSE, 0, 0); + + conn->error_string[0] = 0; + if(reason) + str_copy(conn->error_string, reason, sizeof(conn->error_string)); + } + + conn_reset(conn); +} + +int conn_feed(NETCONNECTION *conn, NETPACKETCONSTRUCT *packet, NETADDR *addr) +{ + int64 now = time_get(); + conn->last_recv_time = now; + + /* check if resend is requested */ + if(packet->flags&NET_PACKETFLAG_RESEND) + conn_resend(conn); + + /* */ + if(packet->flags&NET_PACKETFLAG_CONTROL) + { + int ctrlmsg = packet->chunk_data[0]; + + if(ctrlmsg == NET_CTRLMSG_CLOSE) + { + conn->state = NET_CONNSTATE_ERROR; + conn->remote_closed = 1; + + if(packet->data_size) + { + /* make sure to sanitize the error string form the other party*/ + char str[128]; + if(packet->data_size < 128) + str_copy(str, (char *)packet->chunk_data, packet->data_size); + else + str_copy(str, (char *)packet->chunk_data, 128); + str_sanitize_strong(str); + + /* set the error string */ + conn_set_error(conn, str); + } + else + conn_set_error(conn, "no reason given"); + + if(config.debug) + dbg_msg("conn", "closed reason='%s'", conn_error(conn)); + return 0; + } + else + { + if(conn->state == NET_CONNSTATE_OFFLINE) + { + if(ctrlmsg == NET_CTRLMSG_CONNECT) + { + /* send response and init connection */ + conn_reset(conn); + conn->state = NET_CONNSTATE_ONLINE; + conn->peeraddr = *addr; + conn->last_send_time = now; + conn->last_recv_time = now; + conn->last_update_time = now; + conn_send_control(conn, NET_CTRLMSG_CONNECTACCEPT, 0, 0); + if(config.debug) + dbg_msg("connection", "got connection, sending connect+accept"); + } + } + else if(conn->state == NET_CONNSTATE_CONNECT) + { + /* connection made */ + if(ctrlmsg == NET_CTRLMSG_CONNECTACCEPT) + { + conn_send_control(conn, NET_CTRLMSG_ACCEPT, 0, 0); + conn->state = NET_CONNSTATE_ONLINE; + if(config.debug) + dbg_msg("connection", "got connect+accept, sending accept. connection online"); + } + } + else if(conn->state == NET_CONNSTATE_ONLINE) + { + /* connection made */ + /* + if(ctrlmsg == NET_CTRLMSG_CONNECTACCEPT) + { + + }*/ + } + } + } + + if(conn->state == NET_CONNSTATE_ONLINE) + { + conn_ack(conn, packet->ack); + } + + return 1; +} + +int conn_update(NETCONNECTION *conn) +{ + int64 now = time_get(); + + if(conn->state == NET_CONNSTATE_OFFLINE || conn->state == NET_CONNSTATE_ERROR) + return 0; + + /* watch out for major hitches */ + { + /* TODO: fix this */ + /* + int64 delta = now-conn->last_update_time; + if(conn->last_update_time && delta > time_freq()/2) + { + RINGBUFFER_ITEM *item = conn->buffer.first; + + dbg_msg("conn", "hitch %d", (int)((delta*1000)/time_freq())); + conn->last_recv_time += delta; + + while(item) + { + NETPACKETDATA *resend = (NETPACKETDATA *)rb_item_data(item); + resend->first_send_time += delta; + item = item->next; + } + } + + conn->last_update_time = now; + */ + } + + + /* check for timeout */ + if(conn->state != NET_CONNSTATE_OFFLINE && + conn->state != NET_CONNSTATE_CONNECT && + (now-conn->last_recv_time) > time_freq()*10) + { + conn->state = NET_CONNSTATE_ERROR; + conn_set_error(conn, "timeout"); + } + + if(ringbuf_first(conn->buffer)) + { + /* TODO: fix this */ + NETCHUNKDATA *resend = (NETCHUNKDATA *)ringbuf_first(conn->buffer); + if(now-resend->first_send_time > time_freq()*10) + { + conn->state = NET_CONNSTATE_ERROR; + conn_set_error(conn, "too weak connection (not acked for 10 seconds)"); + } + } + + /* send keep alives if nothing has happend for 1000ms */ + if(conn->state == NET_CONNSTATE_ONLINE) + { + if(time_get()-conn->last_send_time > time_freq()/2) /* flush connection after 250ms if needed */ + { + int num_flushed_chunks = conn_flush(conn); + if(num_flushed_chunks && config.debug) + dbg_msg("connection", "flushed connection due to timeout. %d chunks.", num_flushed_chunks); + } + + if(time_get()-conn->last_send_time > time_freq()) + conn_send_control(conn, NET_CTRLMSG_KEEPALIVE, 0, 0); + } + else if(conn->state == NET_CONNSTATE_CONNECT) + { + if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect every 500ms */ + conn_send_control(conn, NET_CTRLMSG_CONNECT, 0, 0); + /*conn_send(conn, NETWORK_PACKETFLAG_CONNECT, 0, 0);*/ + } + else if(conn->state == NET_CONNSTATE_CONNECTACCEPTED) + { + + if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect/accept every 500ms */ + conn_send_control(conn, NET_CTRLMSG_CONNECTACCEPT, 0, 0); + /*conn_send(conn, NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT, 0, 0);*/ + } + + return 0; +} diff --git a/src/engine/e_network_internal.h b/src/engine/e_network_internal.h new file mode 100644 index 00000000..b4b7e794 --- /dev/null +++ b/src/engine/e_network_internal.h @@ -0,0 +1,149 @@ +#include <base/system.h> +#include "e_network.h" +#include "e_ringbuffer.h" + +/* + +CURRENT: + packet header: 3 bytes + unsigned char flags_ack; // 4bit flags, 4bit ack + unsigned char ack; // 8 bit ack + unsigned char num_chunks; // 8 bit chunks + + (unsigned char padding[3]) // 24 bit extra incase it's a connection less packet + // this is to make sure that it's compatible with the + // old protocol + + chunk header: 2-3 bytes + unsigned char flags_size; // 2bit flags, 6 bit size + unsigned char size_seq; // 4bit size, 4bit seq + (unsigned char seq;) // 8bit seq, if vital flag is set +*/ + +enum +{ + NET_VERSION = 2, + + NET_MAX_CHUNKSIZE = 1024, + NET_MAX_PAYLOAD = NET_MAX_CHUNKSIZE+16, + NET_MAX_PACKETSIZE = NET_MAX_PAYLOAD+16, + NET_MAX_CHUNKHEADERSIZE = 5, + NET_PACKETHEADERSIZE = 3, + NET_MAX_CLIENTS = 16, + NET_MAX_SEQUENCE = 1<<10, + NET_SEQUENCE_MASK = NET_MAX_SEQUENCE-1, + + NET_CONNSTATE_OFFLINE=0, + NET_CONNSTATE_CONNECT=1, + NET_CONNSTATE_CONNECTACCEPTED=2, + NET_CONNSTATE_ONLINE=3, + NET_CONNSTATE_ERROR=4, + + NET_PACKETFLAG_CONTROL=1, + NET_PACKETFLAG_CONNLESS=2, + NET_PACKETFLAG_RESEND=4, + NET_PACKETFLAG_COMPRESSION=8, + + NET_CHUNKFLAG_VITAL=1, + NET_CHUNKFLAG_RESEND=2, + + NET_CTRLMSG_KEEPALIVE=0, + NET_CTRLMSG_CONNECT=1, + NET_CTRLMSG_CONNECTACCEPT=2, + NET_CTRLMSG_ACCEPT=3, + NET_CTRLMSG_CLOSE=4, + + NET_SERVER_MAXBANS=1024, + + NET_CONN_BUFFERSIZE=1024*16, + + NET_ENUM_TERMINATOR +}; + + +typedef struct NETPACKETCONSTRUCT +{ + int flags; + int ack; + int num_chunks; + int data_size; + unsigned char chunk_data[NET_MAX_PAYLOAD]; +} NETPACKETCONSTRUCT; + +typedef struct NETCHUNKHEADER +{ + int flags; + int size; + int sequence; +} NETCHUNKHEADER; + +typedef struct +{ + int flags; + int data_size; + unsigned char *data; + + int sequence; + int64 first_send_time; +} NETCHUNKDATA; + +typedef struct +{ + unsigned short seq; + unsigned short ack; + unsigned state; + + int token; + int remote_closed; + + RINGBUFFER *buffer; + + int64 last_update_time; + int64 last_recv_time; + int64 last_send_time; + + char error_string[256]; + + NETPACKETCONSTRUCT construct; + + NETADDR peeraddr; + NETSOCKET socket; + NETSTATS stats; + + char buffer_memory[NET_CONN_BUFFERSIZE]; +} NETCONNECTION; + +typedef struct NETRECVINFO +{ + NETADDR addr; + NETCONNECTION *conn; + int current_chunk; + int client_id; + int valid; + NETPACKETCONSTRUCT data; + unsigned char buffer[NET_MAX_PACKETSIZE]; +} NETRECVINFO; + + +/* connection functions */ +void conn_init(NETCONNECTION *conn, NETSOCKET socket); +int conn_connect(NETCONNECTION *conn, NETADDR *addr); +void conn_disconnect(NETCONNECTION *conn, const char *reason); +int conn_update(NETCONNECTION *conn); +int conn_feed(NETCONNECTION *conn, NETPACKETCONSTRUCT *packet, NETADDR *addr); +void conn_queue_chunk(NETCONNECTION *conn, int flags, int data_size, const void *data); +const char *conn_error(NETCONNECTION *conn); +void conn_want_resend(NETCONNECTION *conn); +int conn_flush(NETCONNECTION *conn); + +/* recvinfo functions */ +void recvinfo_clear(NETRECVINFO *info); +void recvinfo_start(NETRECVINFO *info, NETADDR *addr, NETCONNECTION *conn, int cid); +int recvinfo_fetch_chunk(NETRECVINFO *info, NETCHUNK *chunk); + +/* misc helper functions */ +void send_packet_connless(NETSOCKET socket, NETADDR *addr, const void *data, int data_size); +void send_packet(NETSOCKET socket, NETADDR *addr, NETPACKETCONSTRUCT *packet); +int unpack_packet(unsigned char *buffer, int size, NETPACKETCONSTRUCT *packet); +unsigned char *pack_chunk_header(unsigned char *data, int flags, int size, int sequence); +unsigned char *unpack_chunk_header(unsigned char *data, NETCHUNKHEADER *header); diff --git a/src/engine/e_network_server.c b/src/engine/e_network_server.c new file mode 100644 index 00000000..98d162a5 --- /dev/null +++ b/src/engine/e_network_server.c @@ -0,0 +1,387 @@ +#include <base/system.h> +#include "e_network.h" +#include "e_network_internal.h" + +typedef struct +{ + NETCONNECTION conn; +} NETSLOT; + +typedef struct NETBAN +{ + NETBANINFO info; + + struct NETBAN *hashnext; + struct NETBAN *hashprev; + + struct NETBAN *next; + struct NETBAN *prev; +} NETBAN; + +struct NETSERVER +{ + NETSOCKET socket; + NETSLOT slots[NET_MAX_CLIENTS]; + int max_clients; + + NETBAN *bans[256]; + NETBAN banpool[NET_SERVER_MAXBANS]; + NETBAN *banpool_firstfree; + NETBAN *banpool_firstused; + + NETFUNC_NEWCLIENT new_client; + NETFUNC_NEWCLIENT del_client; + void *user_ptr; + + NETRECVINFO recv; +}; + +NETSERVER *netserver_open(NETADDR bindaddr, int max_clients, int flags) +{ + int i; + NETSERVER *server; + NETSOCKET socket = net_udp_create(bindaddr); + if(socket == NETSOCKET_INVALID) + return 0; + + server = (NETSERVER *)mem_alloc(sizeof(NETSERVER), 1); + mem_zero(server, sizeof(NETSERVER)); + server->socket = socket; + server->max_clients = max_clients; + if(server->max_clients > NET_MAX_CLIENTS) + server->max_clients = NET_MAX_CLIENTS; + if(server->max_clients < 1) + server->max_clients = 1; + + for(i = 0; i < NET_MAX_CLIENTS; i++) + conn_init(&server->slots[i].conn, server->socket); + + /* setup all pointers for bans */ + for(i = 1; i < NET_SERVER_MAXBANS-1; i++) + { + server->banpool[i].next = &server->banpool[i+1]; + server->banpool[i].prev = &server->banpool[i-1]; + } + + server->banpool[0].next = &server->banpool[1]; + server->banpool[NET_SERVER_MAXBANS-1].prev = &server->banpool[NET_SERVER_MAXBANS-2]; + server->banpool_firstfree = &server->banpool[0]; + + return server; +} + +int netserver_set_callbacks(NETSERVER *s, NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user) +{ + s->new_client = new_client; + s->del_client = del_client; + s->user_ptr = user; + return 0; +} + +int netserver_max_clients(NETSERVER *s) +{ + return s->max_clients; +} + +int netserver_close(NETSERVER *s) +{ + /* TODO: implement me */ + return 0; +} + +int netserver_drop(NETSERVER *s, int client_id, const char *reason) +{ + /* TODO: insert lots of checks here */ + dbg_msg("net_server", "client dropped. cid=%d reason=\"%s\"", client_id, reason); + conn_disconnect(&s->slots[client_id].conn, reason); + + if(s->del_client) + s->del_client(client_id, s->user_ptr); + + return 0; +} + +int netserver_ban_get(NETSERVER *s, int index, NETBANINFO *info) +{ + NETBAN *ban; + for(ban = s->banpool_firstused; ban && index; ban = ban->next, index--) + {} + + if(!ban) + return 0; + *info = ban->info; + return 1; +} + +int netserver_ban_num(NETSERVER *s) +{ + int count = 0; + NETBAN *ban; + for(ban = s->banpool_firstused; ban; ban = ban->next) + count++; + return count; +} + +int netserver_ban_remove(NETSERVER *s, NETADDR addr) +{ + return 0; +} + +int netserver_ban_add(NETSERVER *s, NETADDR addr, int type, int seconds) +{ + int iphash = (addr.ip[0]+addr.ip[1]+addr.ip[2]+addr.ip[3])&0xff; + unsigned stamp = time_timestamp() + seconds; + NETBAN *ban; + NETBAN *insert_after; + + /* search to see if it already exists */ + for(ban = s->bans[iphash]; ban; ban = ban->hashnext) + { + if(net_addr_comp(&ban->info.addr, &addr) == 0) + { + if(ban->info.expires < stamp) + { + /* decide what to do here */ + } + return -1; + } + } + + if(!s->banpool_firstfree) + return -1; + + /* fetch and clear the new ban */ + ban = s->banpool_firstfree; + s->banpool_firstfree->prev = 0; + ban->next = 0; + ban->prev = 0; + ban->info.expires = stamp; + ban->info.type = type; + + /* add it to the ban hash */ + if(s->bans[iphash]) + s->bans[iphash]->hashprev = ban; + ban->hashnext = s->bans[iphash]; + ban->hashprev = 0; + s->bans[iphash] = ban; + + /* insert it into the used list */ + insert_after = s->banpool_firstused; + while(1) + { + if(!insert_after->next) + break; + if(insert_after->next->info.expires < stamp) + break; + insert_after = insert_after->next; + } + + if(!insert_after || insert_after->info.expires > stamp) + { + /* insert first */ + insert_after->prev = ban; + s->banpool_firstused = ban; + ban->next = insert_after; + ban->prev = 0; + } + else + { + /* insert after */ + ban->next = insert_after->next; + ban->prev = insert_after; + if(ban->next) + ban->next->prev = ban; + insert_after->next = ban; + } + + return 0; +} + +int netserver_update(NETSERVER *s) +{ + int i; + for(i = 0; i < s->max_clients; i++) + { + conn_update(&s->slots[i].conn); + if(s->slots[i].conn.state == NET_CONNSTATE_ERROR) + netserver_drop(s, i, conn_error(&s->slots[i].conn)); + } + return 0; +} + +/* + TODO: chopp up this function into smaller working parts +*/ +int netserver_recv(NETSERVER *s, NETCHUNK *chunk) +{ + unsigned now = time_timestamp(); + + while(1) + { + NETADDR addr; + int i, bytes, found; + + /* check for a chunk */ + if(recvinfo_fetch_chunk(&s->recv, chunk)) + return 1; + + /* TODO: empty the recvinfo */ + bytes = net_udp_recv(s->socket, &addr, s->recv.buffer, NET_MAX_PACKETSIZE); + + /* no more packets for now */ + if(bytes <= 0) + break; + + if(unpack_packet(s->recv.buffer, bytes, &s->recv.data) == 0) + { + NETBAN *ban = 0; + int iphash = (addr.ip[0]+addr.ip[1]+addr.ip[2]+addr.ip[3])&0xff; + found = 0; + + /* search a ban */ + for(ban = s->bans[iphash]; ban; ban = ban->hashnext) + { + if(net_addr_comp(&ban->info.addr, &addr) == 0) + break; + } + + /* check if we just should drop the packet */ + if(ban && ban->info.type == NETBANTYPE_DROP && ban->info.expires > now) + continue; + + if(s->recv.data.flags&NET_PACKETFLAG_CONNLESS) + { + chunk->flags = NETSENDFLAG_CONNLESS; + chunk->client_id = -1; + chunk->address = addr; + chunk->data_size = s->recv.data.data_size; + chunk->data = s->recv.data.chunk_data; + return 1; + } + else + { + /* TODO: check size here */ + if(s->recv.data.flags&NET_PACKETFLAG_CONTROL && s->recv.data.chunk_data[0] == NET_CTRLMSG_CONNECT) + { + found = 0; + + if(ban && ban->info.expires > now) + { + /* TODO: soft ban, reply with a message */ + } + else + { + /* check if we already got this client */ + for(i = 0; i < s->max_clients; i++) + { + if(s->slots[i].conn.state != NET_CONNSTATE_OFFLINE && + net_addr_comp(&s->slots[i].conn.peeraddr, &addr) == 0) + { + found = 1; /* silent ignore.. we got this client already */ + break; + } + } + + /* client that wants to connect */ + if(!found) + { + for(i = 0; i < s->max_clients; i++) + { + if(s->slots[i].conn.state == NET_CONNSTATE_OFFLINE) + { + found = 1; + conn_feed(&s->slots[i].conn, &s->recv.data, &addr); + if(s->new_client) + s->new_client(i, s->user_ptr); + break; + } + } + + if(!found) + { + /* TODO: send server full message */ + } + } + } + } + else + { + /* normal packet, find matching slot */ + for(i = 0; i < s->max_clients; i++) + { + if(net_addr_comp(&s->slots[i].conn.peeraddr, &addr) == 0) + { + if(conn_feed(&s->slots[i].conn, &s->recv.data, &addr)) + { + if(s->recv.data.data_size) + recvinfo_start(&s->recv, &addr, &s->slots[i].conn, i); + } + } + } + } + } + } + } + return 0; +} + +int netserver_send(NETSERVER *s, NETCHUNK *chunk) +{ + if(chunk->data_size >= NET_MAX_PAYLOAD) + { + dbg_msg("netserver", "packet payload too big. %d. dropping packet", chunk->data_size); + return -1; + } + + if(chunk->flags&NETSENDFLAG_CONNLESS) + { + /* send connectionless packet */ + send_packet_connless(s->socket, &chunk->address, chunk->data, chunk->data_size); + } + else + { + int f = 0; + dbg_assert(chunk->client_id >= 0, "errornous client id"); + dbg_assert(chunk->client_id < s->max_clients, "errornous client id"); + + if(chunk->flags&NETSENDFLAG_VITAL) + f = NET_CHUNKFLAG_VITAL; + + conn_queue_chunk(&s->slots[chunk->client_id].conn, f, chunk->data_size, chunk->data); + + if(chunk->flags&NETSENDFLAG_FLUSH) + conn_flush(&s->slots[chunk->client_id].conn); + } + return 0; +} + +void netserver_stats(NETSERVER *s, NETSTATS *stats) +{ + int num_stats = sizeof(NETSTATS)/sizeof(int); + int *istats = (int *)stats; + int c, i; + + mem_zero(stats, sizeof(NETSTATS)); + + for(c = 0; c < s->max_clients; c++) + { + if(s->slots[c].conn.state != NET_CONNSTATE_OFFLINE) + { + int *sstats = (int *)(&(s->slots[c].conn.stats)); + for(i = 0; i < num_stats; i++) + istats[i] += sstats[i]; + } + } +} + +NETSOCKET netserver_socket(NETSERVER *s) +{ + return s->socket; +} + + +int netserver_client_addr(NETSERVER *s, int client_id, NETADDR *addr) +{ + *addr = s->slots[client_id].conn.peeraddr; + return 1; +} diff --git a/src/engine/e_ringbuffer.c b/src/engine/e_ringbuffer.c index 281c3751..ed3326d4 100644 --- a/src/engine/e_ringbuffer.c +++ b/src/engine/e_ringbuffer.c @@ -1,10 +1,26 @@ #include <base/system.h> +#include "e_ringbuffer.h" + enum { RBFLAG_FREE=1 }; +/* + +*/ +struct RINGBUFFER +{ + struct RBITEM_t *next_alloc; + struct RBITEM_t *last_alloc; + struct RBITEM_t *first; + struct RBITEM_t *last; + void *memory; + int size; + int flags; +}; + typedef struct RBITEM_t { struct RBITEM_t *prev; @@ -12,19 +28,8 @@ typedef struct RBITEM_t int flags; int size; } RBITEM; - -typedef struct -{ - /* what you need */ - RBITEM *next_alloc; - RBITEM *last_alloc; - RBITEM *first; - RBITEM *last; - void *memory; - int size; -} RINGBUFFER; -RINGBUFFER *ringbuf_init(void *memory, int size) +RINGBUFFER *ringbuf_init(void *memory, int size, int flags) { RINGBUFFER *rb = (RINGBUFFER *)memory; mem_zero(memory, size); @@ -37,6 +42,8 @@ RINGBUFFER *ringbuf_init(void *memory, int size) rb->last = rb->first; rb->next_alloc = rb->first; + rb->flags = flags; + return rb; } @@ -45,6 +52,10 @@ static RBITEM *ringbuf_free(RINGBUFFER *rb, RBITEM *item) dbg_assert(!(item->flags&RBFLAG_FREE), "trying to free element that is already freed"); item->flags |= RBFLAG_FREE; + /* TODO: this should be handled better */ + if(item == rb->last_alloc) + rb->last_alloc = 0; + /* merge with all free items backwards */ while(item->prev && (item->prev->flags&RBFLAG_FREE)) { @@ -66,7 +77,7 @@ static RBITEM *ringbuf_free(RINGBUFFER *rb, RBITEM *item) if(!item->next) rb->last = item; - + return item; } @@ -126,6 +137,17 @@ static RBITEM *ringbuf_try_allocate(RINGBUFFER *rb, int wanted_size) return item; } +void ringbuf_popfirst(RINGBUFFER *rb) +{ + if(rb->next_alloc->next) + rb->next_alloc = ringbuf_free(rb, rb->next_alloc->next); + else + { + rb->next_alloc = rb->first; + rb->next_alloc = ringbuf_free(rb, rb->next_alloc); + } +} + void *ringbuf_allocate(RINGBUFFER *rb, int size) { int wanted_size = (size+sizeof(RBITEM)+sizeof(RBITEM)-1)/sizeof(RBITEM)*sizeof(RBITEM); @@ -143,17 +165,16 @@ void *ringbuf_allocate(RINGBUFFER *rb, int size) block = ringbuf_try_allocate(rb, wanted_size); if(block) return block+1; + + /* check if we just should return null */ + if(!(rb->flags&RINGBUF_FLAG_RECYCLE)) + return 0; /* ok, we need to wipe some blocks in order to get space */ while(1) { - if(rb->next_alloc->next) - rb->next_alloc = ringbuf_free(rb, rb->next_alloc->next); - else - { - rb->next_alloc = rb->first; - rb->next_alloc = ringbuf_free(rb, rb->next_alloc); - } + /* pop one */ + ringbuf_popfirst(rb); /* try allocate again */ block = ringbuf_try_allocate(rb, wanted_size); diff --git a/src/engine/e_ringbuffer.h b/src/engine/e_ringbuffer.h index 40043492..71b5831b 100644 --- a/src/engine/e_ringbuffer.h +++ b/src/engine/e_ringbuffer.h @@ -1,18 +1,16 @@ #ifndef _RINGBUFFER_H #define _RINGBUFFER_H -typedef struct RINGBUFFER +typedef struct RINGBUFFER RINGBUFFER; + +enum { - /* what you need */ - struct RBITEM_t *next_alloc; - struct RBITEM_t *last_alloc; - struct RBITEM_t *first; - struct RBITEM_t *last; - void *memory; - int size; -} RINGBUFFER; + /* Will start to destroy items to try to fit the next one */ + RINGBUF_FLAG_RECYCLE=1 +}; -RINGBUFFER *ringbuf_init(void *memory, int size); +RINGBUFFER *ringbuf_init(void *memory, int size, int flags); +void ringbuf_clear(RINGBUFFER *rb); void *ringbuf_allocate(RINGBUFFER *rb, int size); void ringbuf_validate(RINGBUFFER *rb); @@ -23,4 +21,6 @@ void *ringbuf_next(RINGBUFFER *rb, void *current); void *ringbuf_first(RINGBUFFER *rb); void *ringbuf_last(RINGBUFFER *rb); +void ringbuf_popfirst(RINGBUFFER *rb); + #endif diff --git a/src/engine/server/es_server.c b/src/engine/server/es_server.c index 58866e09..dd4fe81f 100644 --- a/src/engine/server/es_server.c +++ b/src/engine/server/es_server.c @@ -963,7 +963,7 @@ static int server_run() game_start_time = time_get(); if(config.debug) - dbg_msg("server", "baseline memory usage %dk", mem_allocated()/1024); + dbg_msg("server", "baseline memory usage %dk", mem_stats()->allocated/1024); while(run_server) { diff --git a/src/game/client/components/console.cpp b/src/game/client/components/console.cpp index a5098ce1..c8fe018e 100644 --- a/src/game/client/components/console.cpp +++ b/src/game/client/components/console.cpp @@ -34,8 +34,8 @@ enum CONSOLE::INSTANCE::INSTANCE(int t) { // init ringbuffers - history = ringbuf_init(history_data, sizeof(history_data)); - backlog = ringbuf_init(backlog_data, sizeof(backlog_data)); + history = ringbuf_init(history_data, sizeof(history_data), RINGBUF_FLAG_RECYCLE); + backlog = ringbuf_init(backlog_data, sizeof(backlog_data), RINGBUF_FLAG_RECYCLE); history_entry = 0x0; |