ddnet/src/engine/e_network.c

979 lines
22 KiB
C
Raw Normal View History

2007-11-25 19:42:40 +00:00
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
#include <string.h>
#include <stdio.h>
2007-07-13 13:40:04 +00:00
2007-12-15 10:24:49 +00:00
#include "e_system.h"
#include "e_config.h"
#include "e_network.h"
2007-07-13 13:40:04 +00:00
/*
header (6 bytes)
2007-07-13 13:40:04 +00:00
unsigned char flags; 1
unsigned char seq_ack[3]; 4
2007-07-25 07:24:57 +00:00
unsigned char token[2]; 6
2007-07-13 13:40:04 +00:00
*/
enum
{
NETWORK_VERSION = 1,
NETWORK_HEADER_SIZE = 6,
NETWORK_MAX_PAYLOAD = 1024,
NETWORK_MAX_PACKET_SIZE = NETWORK_HEADER_SIZE+NETWORK_MAX_PAYLOAD,
2007-07-13 13:40:04 +00:00
NETWORK_MAX_CLIENTS = 16,
NETWORK_CONNSTATE_OFFLINE=0,
NETWORK_CONNSTATE_CONNECT=1,
NETWORK_CONNSTATE_CONNECTACCEPTED=2,
NETWORK_CONNSTATE_ONLINE=3,
NETWORK_CONNSTATE_ERROR=4,
NETWORK_PACKETFLAG_CONNECT=0x01,
NETWORK_PACKETFLAG_ACCEPT=0x02,
NETWORK_PACKETFLAG_CLOSE=0x04,
NETWORK_PACKETFLAG_VITAL=0x08,
NETWORK_PACKETFLAG_RESEND=0x10,
NETWORK_PACKETFLAG_CONNLESS=0x20,
2007-10-06 17:01:06 +00:00
NETWORK_MAX_SEQACK=0x1000
2007-07-13 13:40:04 +00:00
};
2007-07-25 07:24:57 +00:00
static int current_token = 1;
typedef struct
2007-07-13 13:40:04 +00:00
{
unsigned char ID[2];
unsigned char version;
unsigned char flags;
unsigned short seq;
unsigned short ack;
unsigned crc;
2007-07-25 07:24:57 +00:00
int token;
2007-07-13 13:40:04 +00:00
unsigned data_size;
int64 first_send_time;
unsigned char *data;
} NETPACKETDATA;
2007-07-13 13:40:04 +00:00
static void send_packet(NETSOCKET socket, NETADDR4 *addr, NETPACKETDATA *packet)
{
unsigned char buffer[NETWORK_MAX_PACKET_SIZE];
2007-10-06 17:01:06 +00:00
int send_size = NETWORK_HEADER_SIZE+packet->data_size;
2007-07-13 13:40:04 +00:00
buffer[0] = packet->flags;
buffer[1] = ((packet->seq>>4)&0xf0) | ((packet->ack>>8)&0x0f);
buffer[2] = packet->seq;
buffer[3] = packet->ack;
2007-07-25 07:24:57 +00:00
buffer[4] = packet->token>>8;
buffer[5] = packet->token&0xff;
2007-07-13 13:40:04 +00:00
mem_copy(buffer+NETWORK_HEADER_SIZE, packet->data, packet->data_size);
net_udp4_send(socket, addr, buffer, send_size);
}
typedef struct RINGBUFFER_ITEM_t
{
struct RINGBUFFER_ITEM_t *next;
struct RINGBUFFER_ITEM_t *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;
}
static void *rb_item_data(RINGBUFFER_ITEM *item)
{
return (void*)(item+1);
}
static void *rb_alloc(RINGBUFFER *rb, int size)
{
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;
rb->buffer_size += size;
return rb_item_data(item);
}
static void rb_pop_first(RINGBUFFER *rb)
{
if(rb->first)
{
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 = NULL;
else
rb->last = NULL;
}
}
static void rb_clear(RINGBUFFER *rb)
{
while(rb->first)
rb_pop_first(rb);
}
typedef struct
2007-07-13 13:40:04 +00:00
{
unsigned short seq;
unsigned short ack;
2007-07-13 13:40:04 +00:00
unsigned state;
2007-07-25 07:24:57 +00:00
int token;
int remote_closed;
2007-07-25 07:24:57 +00:00
int connected;
int disconnected;
RINGBUFFER buffer;
2007-07-13 13:40:04 +00:00
int64 last_update_time;
2007-07-13 13:40:04 +00:00
int64 last_recv_time;
int64 last_send_time;
char error_string[256];
2007-07-13 13:40:04 +00:00
NETADDR4 peeraddr;
NETSOCKET socket;
NETSTATS stats;
} NETCONNECTION;
2007-07-13 13:40:04 +00:00
typedef struct
2007-07-13 13:40:04 +00:00
{
NETCONNECTION conn;
} NETSLOT;
2007-07-13 13:40:04 +00:00
struct NETSERVER_t
2007-07-13 13:40:04 +00:00
{
NETSOCKET socket;
NETSLOT slots[NETWORK_MAX_CLIENTS];
int max_clients;
NETFUNC_NEWCLIENT new_client;
NETFUNC_NEWCLIENT del_client;
void *user_ptr;
2007-07-13 13:40:04 +00:00
unsigned char recv_buffer[NETWORK_MAX_PACKET_SIZE];
} ;
2007-07-13 13:40:04 +00:00
struct NETCLIENT_t
2007-07-13 13:40:04 +00:00
{
NETADDR4 server_addr;
NETSOCKET socket;
unsigned char recv_buffer[NETWORK_MAX_PACKET_SIZE];
NETCONNECTION conn;
};
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;
2007-07-25 07:24:57 +00:00
if(conn->state == NETWORK_CONNSTATE_ONLINE ||
conn->state == NETWORK_CONNSTATE_ERROR)
{
2007-07-25 07:24:57 +00:00
conn->disconnected++;
}
2007-07-25 07:24:57 +00:00
2007-07-13 13:40:04 +00:00
conn->state = NETWORK_CONNSTATE_OFFLINE;
conn->last_send_time = 0;
conn->last_recv_time = 0;
conn->last_update_time = 0;
2007-07-25 07:24:57 +00:00
conn->token = -1;
mem_zero(&conn->peeraddr, sizeof(conn->peeraddr));
rb_clear(&conn->buffer);
2007-07-13 13:40:04 +00:00
}
2007-07-25 07:24:57 +00:00
2007-07-13 13:40:04 +00:00
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));
}
2007-07-13 13:40:04 +00:00
/*
static int conn_state(NETCONNECTION *conn)
{
return conn->state;
}*/
static void conn_init(NETCONNECTION *conn, NETSOCKET socket)
{
conn_reset(conn);
conn_reset_stats(conn);
conn->socket = socket;
2007-07-25 07:24:57 +00:00
conn->connected = 0;
conn->disconnected = 0;
rb_init(&conn->buffer);
mem_zero(conn->error_string, sizeof(conn->error_string));
2007-07-13 13:40:04 +00:00
}
static void conn_ack(NETCONNECTION *conn, int ack)
{
while(1)
{
RINGBUFFER_ITEM *item = conn->buffer.first;
2007-10-06 17:01:06 +00:00
NETPACKETDATA *resend;
if(!item)
2007-07-13 13:40:04 +00:00
break;
2007-10-06 17:01:06 +00:00
resend = (NETPACKETDATA *)rb_item_data(item);
if(resend->seq <= ack || (ack < NETWORK_MAX_SEQACK/3 && resend->seq > NETWORK_MAX_SEQACK/2))
rb_pop_first(&conn->buffer);
2007-07-13 13:40:04 +00:00
else
break;
}
}
static void conn_send_raw(NETCONNECTION *conn, NETPACKETDATA *data)
{
conn->last_send_time = time_get();
conn->stats.send_packets++;
conn->stats.send_bytes += data->data_size + NETWORK_HEADER_SIZE;
send_packet(conn->socket, &conn->peeraddr, data);
}
static void conn_resend(NETCONNECTION *conn)
{
RINGBUFFER_ITEM *item = conn->buffer.first;
while(item)
2007-07-13 13:40:04 +00:00
{
NETPACKETDATA *resend = (NETPACKETDATA *)rb_item_data(item);
2007-07-13 13:40:04 +00:00
conn->stats.resend_packets++;
conn->stats.resend_bytes += resend->data_size + NETWORK_HEADER_SIZE;
conn_send_raw(conn, resend);
item = item->next;
2007-07-13 13:40:04 +00:00
}
}
static void conn_send(NETCONNECTION *conn, int flags, int data_size, const void *data)
{
2007-10-06 17:01:06 +00:00
NETPACKETDATA p;
2007-07-13 13:40:04 +00:00
if(flags&NETWORK_PACKETFLAG_VITAL)
conn->seq = (conn->seq+1)%NETWORK_MAX_SEQACK;
2007-10-06 17:01:06 +00:00
2007-07-13 13:40:04 +00:00
p.ID[0] = 'T';
p.ID[1] = 'W';
p.version = NETWORK_VERSION;
p.flags = flags;
p.seq = conn->seq;
p.ack = conn->ack;
p.crc = 0;
2007-07-25 07:24:57 +00:00
p.token = conn->token;
2007-07-13 13:40:04 +00:00
p.data_size = data_size;
p.data = (unsigned char *)data;
p.first_send_time = time_get();
2007-07-13 13:40:04 +00:00
if(flags&NETWORK_PACKETFLAG_VITAL)
{
/* save packet if we need to resend */
NETPACKETDATA *resend = (NETPACKETDATA *)rb_alloc(&conn->buffer, sizeof(NETPACKETDATA)+data_size);
2007-07-13 13:40:04 +00:00
*resend = p;
resend->data = (unsigned char *)(resend+1);
mem_copy(resend->data, p.data, p.data_size);
}
/* TODO: calc crc */
2007-07-13 13:40:04 +00:00
conn_send_raw(conn, &p);
}
static int conn_connect(NETCONNECTION *conn, NETADDR4 *addr)
{
if(conn->state != NETWORK_CONNSTATE_OFFLINE)
return -1;
/* init connection */
2007-07-13 13:40:04 +00:00
conn_reset(conn);
conn->peeraddr = *addr;
2007-07-25 07:24:57 +00:00
conn->token = current_token++;
mem_zero(conn->error_string, sizeof(conn->error_string));
2007-07-13 13:40:04 +00:00
conn->state = NETWORK_CONNSTATE_CONNECT;
conn_send(conn, NETWORK_PACKETFLAG_CONNECT, 0, 0);
return 0;
}
static void conn_disconnect(NETCONNECTION *conn, const char *reason)
2007-07-25 07:24:57 +00:00
{
if(conn->remote_closed == 0)
{
if(reason)
conn_send(conn, NETWORK_PACKETFLAG_CLOSE, strlen(reason)+1, reason);
else
conn_send(conn, NETWORK_PACKETFLAG_CLOSE, 0, 0);
2007-12-03 20:35:41 +00:00
conn->error_string[0] = 0;
if(reason)
str_copy(conn->error_string, reason, sizeof(conn->error_string));
}
2007-12-03 18:47:29 +00:00
2007-07-25 07:24:57 +00:00
conn_reset(conn);
}
2007-07-13 13:40:04 +00:00
static int conn_feed(NETCONNECTION *conn, NETPACKETDATA *p, NETADDR4 *addr)
{
2007-12-15 13:53:10 +00:00
int64 now = time_get();
conn->last_recv_time = now;
2007-07-13 13:40:04 +00:00
conn->stats.recv_packets++;
conn->stats.recv_bytes += p->data_size + NETWORK_HEADER_SIZE;
2007-07-25 07:24:57 +00:00
if(p->flags&NETWORK_PACKETFLAG_CLOSE)
{
conn->state = NETWORK_CONNSTATE_ERROR;
conn->remote_closed = 1;
if(p->data_size)
2008-02-24 16:03:58 +00:00
{
/* make sure to sanitize the error string form the other party*/
char str[128];
if(p->data_size < 128)
str_copy(str, (char *)p->data, p->data_size);
else
str_copy(str, (char *)p->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));
2007-07-25 07:24:57 +00:00
return 0;
}
2007-07-13 13:40:04 +00:00
if(conn->state == NETWORK_CONNSTATE_OFFLINE)
{
if(p->flags == NETWORK_PACKETFLAG_CONNECT)
{
/* send response and init connection */
2007-12-15 13:53:10 +00:00
conn_reset(conn);
2007-07-13 13:40:04 +00:00
conn->state = NETWORK_CONNSTATE_ONLINE;
2007-07-25 07:24:57 +00:00
conn->connected++;
2007-07-13 13:40:04 +00:00
conn->peeraddr = *addr;
2007-07-25 07:24:57 +00:00
conn->token = p->token;
2007-12-15 13:53:10 +00:00
conn->last_send_time = now;
conn->last_recv_time = now;
conn->last_update_time = now;
2007-07-13 13:40:04 +00:00
conn_send(conn, NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT, 0, 0);
if(config.debug)
dbg_msg("connection", "got connection, sending connect+accept");
2007-07-13 13:40:04 +00:00
}
}
else if(net_addr4_cmp(&conn->peeraddr, addr) == 0)
{
2007-07-25 07:24:57 +00:00
if(p->token != conn->token)
return 0;
2007-07-13 13:40:04 +00:00
if(conn->state == NETWORK_CONNSTATE_ONLINE)
{
/* remove packages that are acked */
2007-07-13 13:40:04 +00:00
conn_ack(conn, p->ack);
/* check if resend is requested */
2007-07-13 13:40:04 +00:00
if(p->flags&NETWORK_PACKETFLAG_RESEND)
conn_resend(conn);
if(p->flags&NETWORK_PACKETFLAG_VITAL)
{
if(p->seq == (conn->ack+1)%NETWORK_MAX_SEQACK)
2007-07-13 13:40:04 +00:00
{
/* in sequence */
conn->ack = (conn->ack+1)%NETWORK_MAX_SEQACK;
2007-07-13 13:40:04 +00:00
}
else
{
/* out of sequence, request resend */
if(config.debug)
dbg_msg("conn", "asking for resend %d %d", p->seq, (conn->ack+1)%NETWORK_MAX_SEQACK);
2007-07-13 13:40:04 +00:00
conn_send(conn, NETWORK_PACKETFLAG_RESEND, 0, 0);
return 0;
}
}
else
{
if(p->seq > conn->ack)
conn_send(conn, NETWORK_PACKETFLAG_RESEND, 0, 0);
}
2007-07-14 21:37:53 +00:00
if(p->data_size == 0)
return 0;
2007-07-13 13:40:04 +00:00
return 1;
}
else if(conn->state == NETWORK_CONNSTATE_CONNECT)
{
/* connection made */
2007-07-13 21:22:45 +00:00
if(p->flags == (NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT))
2007-07-13 13:40:04 +00:00
{
conn_send(conn, NETWORK_PACKETFLAG_ACCEPT, 0, 0);
conn->state = NETWORK_CONNSTATE_ONLINE;
2007-07-25 07:24:57 +00:00
conn->connected++;
if(config.debug)
dbg_msg("connection", "got connect+accept, sending accept. connection online");
2007-07-13 13:40:04 +00:00
}
}
/*
else if(conn->state == NETWORK_CONNSTATE_CONNECTACCEPTED)
{
// connection made
if(p->flags == NETWORK_PACKETFLAG_ACCEPT)
{
conn->state = NETWORK_CONNSTATE_ONLINE;
dbg_msg("connection", "got accept. connection online");
}
}*/
else
{
/* strange packet, wrong state */
conn->state = NETWORK_CONNSTATE_ERROR;
conn_set_error(conn, "strange state and packet");
2007-07-13 13:40:04 +00:00
}
}
else
{
/* strange packet, not ment for me */
2007-07-13 13:40:04 +00:00
}
return 0;
}
static int conn_update(NETCONNECTION *conn)
2007-07-13 13:40:04 +00:00
{
2007-10-06 17:01:06 +00:00
int64 now = time_get();
if(conn->state == NETWORK_CONNSTATE_OFFLINE || conn->state == NETWORK_CONNSTATE_ERROR)
return 0;
/* watch out for major hitches */
{
2007-10-06 17:01:06 +00:00
int64 delta = now-conn->last_update_time;
if(conn->last_update_time && delta > time_freq()/2)
{
2007-10-06 17:01:06 +00:00
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;
}
}
2007-10-06 17:01:06 +00:00
conn->last_update_time = now;
}
2007-07-13 13:40:04 +00:00
/* check for timeout */
2007-07-13 13:40:04 +00:00
if(conn->state != NETWORK_CONNSTATE_OFFLINE &&
conn->state != NETWORK_CONNSTATE_CONNECT &&
(now-conn->last_recv_time) > time_freq()*10)
2007-07-13 13:40:04 +00:00
{
conn->state = NETWORK_CONNSTATE_ERROR;
2007-09-25 22:44:41 +00:00
conn_set_error(conn, "timeout");
2007-07-13 13:40:04 +00:00
}
/* check for large buffer errors */
if(conn->buffer.buffer_size > 1024*64)
2007-07-13 13:40:04 +00:00
{
conn->state = NETWORK_CONNSTATE_ERROR;
conn_set_error(conn, "too weak connection (out of buffer)");
2007-07-13 13:40:04 +00:00
}
if(conn->buffer.first)
2007-07-13 13:40:04 +00:00
{
NETPACKETDATA *resend = (NETPACKETDATA *)(conn->buffer.first+1);
if(now-resend->first_send_time > time_freq()*10)
2007-07-13 13:40:04 +00:00
{
conn->state = NETWORK_CONNSTATE_ERROR;
conn_set_error(conn, "too weak connection (not acked for 10 seconds)");
2007-07-13 13:40:04 +00:00
}
}
/* send keep alives if nothing has happend for 250ms */
2007-07-13 13:40:04 +00:00
if(conn->state == NETWORK_CONNSTATE_ONLINE)
{
if(time_get()-conn->last_send_time> time_freq()/4)
conn_send(conn, NETWORK_PACKETFLAG_VITAL, 0, 0);
}
else if(conn->state == NETWORK_CONNSTATE_CONNECT)
{
if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect every 500ms */
2007-07-13 13:40:04 +00:00
conn_send(conn, NETWORK_PACKETFLAG_CONNECT, 0, 0);
}
else if(conn->state == NETWORK_CONNSTATE_CONNECTACCEPTED)
{
if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect/accept every 500ms */
2007-07-13 13:40:04 +00:00
conn_send(conn, NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT, 0, 0);
}
return 0;
2007-07-13 13:40:04 +00:00
}
static int check_packet(unsigned char *buffer, int size, NETPACKETDATA *packet)
{
/* check the size */
2007-07-13 13:40:04 +00:00
if(size < NETWORK_HEADER_SIZE || size > NETWORK_MAX_PACKET_SIZE)
return -1;
/* read the packet */
2007-07-13 13:40:04 +00:00
packet->ID[0] = 'T';
packet->ID[1] = 'W';
packet->version = NETWORK_VERSION;
packet->flags = buffer[0];
packet->seq = ((buffer[1]&0xf0)<<4)|buffer[2];
packet->ack = ((buffer[1]&0x0f)<<8)|buffer[3];
2007-07-25 07:24:57 +00:00
packet->crc = 0;
packet->token = (buffer[4]<<8)|buffer[5];
2007-07-13 13:40:04 +00:00
packet->data_size = size - NETWORK_HEADER_SIZE;
packet->data = buffer+NETWORK_HEADER_SIZE;
/* check the packet */
2007-07-13 13:40:04 +00:00
if(packet->ID[0] != 'T' || packet->ID[1] != 'W')
return 1;
if(packet->version != NETWORK_VERSION)
return 1;
/* TODO: perform crc check */
2007-07-13 13:40:04 +00:00
/* return success */
2007-07-13 13:40:04 +00:00
return 0;
}
NETSERVER *netserver_open(NETADDR4 bindaddr, int max_clients, int flags)
2007-07-13 13:40:04 +00:00
{
int i;
NETSERVER *server;
NETSOCKET socket = net_udp4_create(bindaddr);
if(socket == NETSOCKET_INVALID)
return 0;
server = (NETSERVER *)mem_alloc(sizeof(NETSERVER), 1);
2007-07-13 13:40:04 +00:00
mem_zero(server, sizeof(NETSERVER));
server->socket = socket;
server->max_clients = max_clients;
if(server->max_clients > NETWORK_MAX_CLIENTS)
server->max_clients = NETWORK_MAX_CLIENTS;
if(server->max_clients < 1)
server->max_clients = 1;
2007-07-13 13:40:04 +00:00
for(i = 0; i < NETWORK_MAX_CLIENTS; i++)
2007-07-13 13:40:04 +00:00
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)
2007-07-13 13:40:04 +00:00
{
return s->max_clients;
2007-07-13 13:40:04 +00:00
}
int netserver_close(NETSERVER *s)
2007-07-13 13:40:04 +00:00
{
/* TODO: implement me */
return 0;
2007-07-13 13:40:04 +00:00
}
int netserver_drop(NETSERVER *s, int client_id, const char *reason)
2007-07-13 13:40:04 +00:00
{
/* TODO: insert lots of checks here */
2007-07-13 13:40:04 +00:00
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);
2007-07-13 13:40:04 +00:00
return 0;
}
int netserver_update(NETSERVER *s)
2007-07-13 13:40:04 +00:00
{
int i;
for(i = 0; i < s->max_clients; i++)
2007-07-13 13:40:04 +00:00
{
conn_update(&s->slots[i].conn);
if(s->slots[i].conn.state == NETWORK_CONNSTATE_ERROR)
netserver_drop(s, i, conn_error(&s->slots[i].conn));
2007-07-13 13:40:04 +00:00
}
return 0;
}
int netserver_recv(NETSERVER *s, NETPACKET *packet)
2007-07-13 13:40:04 +00:00
{
NETPACKETDATA data;
int i, r, bytes, found;
NETADDR4 addr;
2007-07-13 13:40:04 +00:00
while(1)
{
bytes = net_udp4_recv(s->socket, &addr, s->recv_buffer, NETWORK_MAX_PACKET_SIZE);
2007-07-13 13:40:04 +00:00
/* no more packets for now */
2007-07-13 13:40:04 +00:00
if(bytes <= 0)
break;
r = check_packet(s->recv_buffer, bytes, &data);
2007-07-13 13:40:04 +00:00
if(r == 0)
{
if(data.flags&NETWORK_PACKETFLAG_CONNLESS)
2007-07-13 13:40:04 +00:00
{
/* connection less packets */
packet->client_id = -1;
packet->address = addr;
packet->flags = PACKETFLAG_CONNLESS;
packet->data_size = data.data_size;
packet->data = data.data;
return 1;
}
else
{
/* ok packet, process it */
if(data.flags == NETWORK_PACKETFLAG_CONNECT)
2007-07-14 16:25:46 +00:00
{
found = 0;
/* check if we already got this client */
for(i = 0; i < s->max_clients; i++)
2007-07-14 16:25:46 +00:00
{
if(s->slots[i].conn.state != NETWORK_CONNSTATE_OFFLINE &&
net_addr4_cmp(&s->slots[i].conn.peeraddr, &addr) == 0)
2007-07-14 16:25:46 +00:00
{
found = 1; /* silent ignore.. we got this client already */
2007-07-14 16:25:46 +00:00
break;
}
}
/* client that wants to connect */
if(!found)
{
for(i = 0; i < s->max_clients; i++)
{
if(s->slots[i].conn.state == NETWORK_CONNSTATE_OFFLINE)
{
found = 1;
conn_feed(&s->slots[i].conn, &data, &addr);
if(s->new_client)
s->new_client(i, s->user_ptr);
break;
}
}
}
if(!found)
{
/* send connectionless packet */
const char errstring[] = "server full";
NETPACKETDATA p;
p.ID[0] = 'T';
p.ID[1] = 'W';
p.version = NETWORK_VERSION;
p.flags = NETWORK_PACKETFLAG_CLOSE;
p.seq = 0;
p.ack = 0;
p.crc = 0;
p.token = data.token;
p.data_size = sizeof(errstring);
p.data = (unsigned char *)errstring;
send_packet(s->socket, &addr, &p);
}
2007-07-14 16:25:46 +00:00
}
else
2007-07-13 13:40:04 +00:00
{
/* find matching slot */
for(i = 0; i < s->max_clients; i++)
2007-07-13 13:40:04 +00:00
{
2008-03-29 18:40:18 +00:00
/* must be in some sort of online state */
if(s->slots[i].conn.state == NETWORK_CONNSTATE_OFFLINE)
continue;
if(net_addr4_cmp(&s->slots[i].conn.peeraddr, &addr) == 0)
2007-07-13 13:40:04 +00:00
{
if(conn_feed(&s->slots[i].conn, &data, &addr))
2007-07-13 13:40:04 +00:00
{
if(data.data_size)
{
packet->client_id = i;
packet->address = addr;
packet->flags = 0;
packet->data_size = data.data_size;
packet->data = data.data;
return 1;
}
2007-07-13 13:40:04 +00:00
}
}
}
}
}
}
else
{
/* errornous packet, drop it */
2008-02-24 16:03:58 +00:00
/* dbg_msg("server", "crazy packet"); */
2007-07-13 13:40:04 +00:00
}
/* read header */
/* do checksum */
2007-07-13 13:40:04 +00:00
}
return 0;
}
int netserver_send(NETSERVER *s, NETPACKET *packet)
2007-07-13 13:40:04 +00:00
{
2008-02-24 16:03:58 +00:00
if(packet->data_size >= NETWORK_MAX_PAYLOAD)
{
dbg_msg("netserver", "packet payload too big. %d. dropping packet", packet->data_size);
return -1;
}
if(packet->flags&PACKETFLAG_CONNLESS)
{
/* send connectionless packet */
NETPACKETDATA p;
p.ID[0] = 'T';
p.ID[1] = 'W';
p.version = NETWORK_VERSION;
p.flags = NETWORK_PACKETFLAG_CONNLESS;
p.seq = 0;
p.ack = 0;
p.crc = 0;
p.data_size = packet->data_size;
p.data = (unsigned char *)packet->data;
send_packet(s->socket, &packet->address, &p);
}
else
{
2007-10-06 17:01:06 +00:00
int flags = 0;
dbg_assert(packet->client_id >= 0, "errornous client id");
dbg_assert(packet->client_id < s->max_clients, "errornous client id");
if(packet->flags&PACKETFLAG_VITAL)
flags |= NETWORK_PACKETFLAG_VITAL;
conn_send(&s->slots[packet->client_id].conn, flags, packet->data_size, packet->data);
}
2007-07-13 13:40:04 +00:00
return 0;
}
void netserver_stats(NETSERVER *s, NETSTATS *stats)
2007-07-13 13:40:04 +00:00
{
int num_stats = sizeof(NETSTATS)/sizeof(int);
int *istats = (int *)stats;
int c, i;
2007-10-06 17:01:06 +00:00
mem_zero(stats, sizeof(NETSTATS));
2007-07-13 13:40:04 +00:00
for(c = 0; c < s->max_clients; c++)
2007-07-13 13:40:04 +00:00
{
if(s->slots[c].conn.state != NETWORK_CONNSTATE_OFFLINE)
{
int *sstats = (int *)(&(s->slots[c].conn.stats));
for(i = 0; i < num_stats; i++)
istats[i] += sstats[i];
}
2007-07-13 13:40:04 +00:00
}
}
NETSOCKET netserver_socket(NETSERVER *s)
{
return s->socket;
}
2007-12-18 23:37:22 +00:00
int netserver_client_addr(NETSERVER *s, int client_id, NETADDR4 *addr)
{
*addr = s->slots[client_id].conn.peeraddr;
return 1;
}
NETCLIENT *netclient_open(NETADDR4 bindaddr, int flags)
2007-07-13 13:40:04 +00:00
{
NETCLIENT *client = (NETCLIENT *)mem_alloc(sizeof(NETCLIENT), 1);
mem_zero(client, sizeof(NETCLIENT));
client->socket = net_udp4_create(bindaddr);
2007-07-13 13:40:04 +00:00
conn_init(&client->conn, client->socket);
return client;
}
int netclient_close(NETCLIENT *c)
2007-07-13 13:40:04 +00:00
{
/* TODO: implement me */
2007-07-13 13:40:04 +00:00
return 0;
}
int netclient_update(NETCLIENT *c)
2007-07-13 13:40:04 +00:00
{
conn_update(&c->conn);
if(c->conn.state == NETWORK_CONNSTATE_ERROR)
netclient_disconnect(c, conn_error(&c->conn));
2007-07-13 13:40:04 +00:00
return 0;
}
int netclient_disconnect(NETCLIENT *c, const char *reason)
2007-07-13 13:40:04 +00:00
{
dbg_msg("netclient", "disconnected. reason=\"%s\"", reason);
conn_disconnect(&c->conn, reason);
2007-07-13 13:40:04 +00:00
return 0;
}
int netclient_connect(NETCLIENT *c, NETADDR4 *addr)
2007-07-13 13:40:04 +00:00
{
conn_connect(&c->conn, addr);
return 0;
}
int netclient_recv(NETCLIENT *c, NETPACKET *packet)
2007-07-13 13:40:04 +00:00
{
while(1)
{
NETADDR4 addr;
2007-10-06 17:01:06 +00:00
NETPACKETDATA data;
int r;
2007-07-13 13:40:04 +00:00
int bytes = net_udp4_recv(c->socket, &addr, c->recv_buffer, NETWORK_MAX_PACKET_SIZE);
/* no more packets for now */
2007-07-13 13:40:04 +00:00
if(bytes <= 0)
break;
2007-10-06 17:01:06 +00:00
r = check_packet(c->recv_buffer, bytes, &data);
if(r == 0)
2007-07-13 13:40:04 +00:00
{
if(data.flags&NETWORK_PACKETFLAG_CONNLESS)
{
/* connection less packets */
packet->client_id = -1;
packet->address = addr;
packet->flags = PACKETFLAG_CONNLESS;
packet->data_size = data.data_size;
packet->data = data.data;
return 1;
}
else
{
if(conn_feed(&c->conn, &data, &addr))
{
/* fill in packet */
packet->client_id = 0;
packet->address = addr;
packet->flags = 0;
packet->data_size = data.data_size;
packet->data = data.data;
return 1;
}
else
{
/* errornous packet, drop it */
}
}
2007-07-13 13:40:04 +00:00
}
}
return 0;
}
int netclient_send(NETCLIENT *c, NETPACKET *packet)
2007-07-13 13:40:04 +00:00
{
2008-02-24 16:03:58 +00:00
if(packet->data_size >= NETWORK_MAX_PAYLOAD)
{
dbg_msg("netclient", "packet payload too big. %d. dropping packet", packet->data_size);
return -1;
}
if(packet->flags&PACKETFLAG_CONNLESS)
{
/* send connectionless packet */
NETPACKETDATA p;
p.ID[0] = 'T';
p.ID[1] = 'W';
p.version = NETWORK_VERSION;
p.flags = NETWORK_PACKETFLAG_CONNLESS;
p.seq = 0;
p.ack = 0;
p.crc = 0;
p.token = 0;
p.data_size = packet->data_size;
p.data = (unsigned char *)packet->data;
send_packet(c->socket, &packet->address, &p);
}
else
{
int flags = 0;
2007-10-06 17:01:06 +00:00
dbg_assert(packet->client_id == 0, "errornous client id");
if(packet->flags&PACKETFLAG_VITAL)
flags |= NETWORK_PACKETFLAG_VITAL;
conn_send(&c->conn, flags, packet->data_size, packet->data);
}
2007-07-13 13:40:04 +00:00
return 0;
}
int netclient_state(NETCLIENT *c)
2007-07-13 13:40:04 +00:00
{
if(c->conn.state == NETWORK_CONNSTATE_ONLINE)
return NETSTATE_ONLINE;
if(c->conn.state == NETWORK_CONNSTATE_OFFLINE)
return NETSTATE_OFFLINE;
return NETSTATE_CONNECTING;
}
2007-12-11 21:55:52 +00:00
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)
2007-07-13 13:40:04 +00:00
{
*stats = c->conn.stats;
}
const char *netclient_error_string(NETCLIENT *c)
{
return conn_error(&c->conn);
}