ManiaDrive/raydium/network.c
2025-01-26 18:33:45 +01:00

1518 lines
39 KiB
C

/*
Raydium - CQFD Corp.
http://raydium.org/
License: GPL - GNU General Public License, see "gpl.txt" file.
*/
#ifndef DONT_INCLUDE_HEADERS
#include "index.h"
#else
#include "headers/network.h"
#endif
// Raydium low-level Network API.
// TODO:
// 1 - protection against duplicate data packets (how ?!)
// 01/07/2004: done ? (need more tests)
// 2 - per client delays for servers
// end of 2004: done too (more test needed)
// need proto
//char raydium_server_accept_new(struct sockaddr *from, char *name);
//void raydium_network_broadcast(char type,char *buff);
//void raydium_network_write(struct sockaddr *to, int from, char type,char *buff);
//char raydium_network_netcall_add(void *ptr, int type, char tcp);
int raydium_network_propag_find(int type)
{
int i;
for(i=0;i<RAYDIUM_NETWORK_MAX_PROPAGS;i++)
if(raydium_network_propag[i].state && raydium_network_propag[i].type==type)
return i;
return -1;
}
void raydium_network_propag_recv(int type, char *buff)
{
int dec;
unsigned int version;
int i;
i=raydium_network_propag_find(type);
if(i<0)
{
raydium_log("network: ERROR: received an invalid propag' type ! (%i)",type);
return;
}
dec=RAYDIUM_NETWORK_PACKET_OFFSET;
memcpy(&version,buff+dec,sizeof(int));
if(version>raydium_network_propag[i].version) // this propag is newer than our
{
dec+=sizeof(int);
raydium_network_propag[i].version=version;
memcpy(raydium_network_propag[i].data,buff+dec,raydium_network_propag[i].size);
}
}
void raydium_network_propag_refresh_id(int i)
{
char buff[RAYDIUM_NETWORK_PACKET_SIZE];
int dec;
if(raydium_network_mode==RAYDIUM_NETWORK_MODE_NONE) return;
if(i<0 || i>=RAYDIUM_NETWORK_MAX_PROPAGS || !raydium_network_propag[i].state)
{
raydium_log("network: ERROR: cannot refresh this propag': invalid id");
return;
}
raydium_network_propag[i].version++;
dec=RAYDIUM_NETWORK_PACKET_OFFSET;
memcpy(buff+dec,&raydium_network_propag[i].version,sizeof(int));
dec+=sizeof(int);
memcpy(buff+dec,raydium_network_propag[i].data,raydium_network_propag[i].size);
raydium_network_write(NULL,raydium_network_uid,raydium_network_propag[i].type,buff);
}
void raydium_network_propag_refresh(int type)
{
int i;
i=raydium_network_propag_find(type);
if(i<0)
{
raydium_log("network: ERROR: cannot refresh this propag': invalid type");
return;
}
raydium_network_propag_refresh_id(i);
}
void raydium_network_propag_refresh_all(void)
{
int i;
for(i=0;i<RAYDIUM_NETWORK_MAX_PROPAGS;i++)
if(raydium_network_propag[i].state)
raydium_network_propag_refresh_id(i);
}
int raydium_network_propag_add(int type, void *data, int size)
{
int i;
if(size>(RAYDIUM_NETWORK_PACKET_SIZE-RAYDIUM_NETWORK_PACKET_OFFSET-RAYDIUM_NETWORK_PROPAG_HEAD))
{
raydium_log("network: ERROR: propag' packet is too big for current network size");
return -1;
}
if(raydium_network_propag_find(type)>=0)
{
raydium_log("network: ERROR: propag' type already added !");
return -1;
}
for(i=0;i<RAYDIUM_NETWORK_MAX_PROPAGS;i++)
if(!raydium_network_propag[i].state)
{
raydium_network_propag[i].state=1;
raydium_network_propag[i].version=0;
raydium_network_propag[i].type=type;
raydium_network_propag[i].size=size;
raydium_network_propag[i].data=data;
raydium_network_netcall_add(raydium_network_propag_recv,type,1);
return i;
}
raydium_log("network: ERROR: no more propag' slots !");
return -1;
}
void raydium_network_queue_element_init(raydium_network_Tcp *e)
{
e->state=0;
}
unsigned short raydium_network_queue_tcpid_gen(void)
{
// not very important.. used for easy client/server tcpid identification
//#ifdef RAYDIUM_NETWORK_ONLY_____
static unsigned short gen=100;
//#else
//static unsigned short gen=1000;
//#endif
gen++;
if(!gen) gen++;
return gen;
}
void raydium_network_queue_tcpid_known_add(int tcpid, int player)
{
raydium_network_tcpid_i[raydium_network_tcpid_index]=tcpid; // mark this TCP-ID as "known"
raydium_network_tcpid_p[raydium_network_tcpid_index]=player; // ... from this player
#ifdef DEBUG_NETWORK
raydium_log("ACK adding tcpid=%i (player %i) to known packets",tcpid,player);
#endif
raydium_network_tcpid_index++;
if(raydium_network_tcpid_index==RAYDIUM_NETWORK_TX_QUEUE_SIZE)
raydium_network_tcpid_index=0;
}
signed char raydium_network_queue_tcpid_known(unsigned short tcpid, unsigned short player)
{
int i;
if(!tcpid) return 0;
for(i=0;i<RAYDIUM_NETWORK_TX_QUEUE_SIZE;i++)
if(raydium_network_tcpid_i[i]==tcpid &&
raydium_network_tcpid_p[i]==player )
return i;
return 0;
}
signed char raydium_network_queue_is_tcpid(int type)
{
int i=0;
if(type >= 0)
for(i=0;i<RAYDIUM_NETWORK_MAX_NETCALLS;i++)
if(raydium_network_netcall_type[i]==type && raydium_network_netcall_tcp[i])
return 1;
return 0;
}
void raydium_network_queue_element_add(char *packet, struct sockaddr *to)
{
unsigned short tcpid;
raydium_network_Tcp *e;
memcpy(&tcpid,packet+2,sizeof(tcpid));
e=&raydium_network_queue[raydium_network_queue_index];
if(e->state)
{
raydium_network_queue_element_init(e);
raydium_network_stat_lost++; // we're erasing an old waiting packet !
}
e->state=1;
e->tcpid=tcpid;
memcpy(e->packet,packet,RAYDIUM_NETWORK_PACKET_SIZE);
e->time=raydium_timecall_clock();
e->retries_left=RAYDIUM_NETWORK_MAX_TRIES;
if(to) memcpy(&e->to,to,sizeof(struct sockaddr));
e->to_player=-1;
// if server mode, search for destination player
if(raydium_network_mode==RAYDIUM_NETWORK_MODE_SERVER)
{
int i;
for(i=0;i<RAYDIUM_NETWORK_MAX_CLIENTS;i++)
if(raydium_network_client[i] && &raydium_network_client_addr[i]==to)
break;
if(i==RAYDIUM_NETWORK_MAX_CLIENTS) // not found
{
raydium_log("ERROR: server: TCP style: cannot find client");
return;
}
e->to_player=i;
}
raydium_network_queue_index++;
if(raydium_network_queue_index==RAYDIUM_NETWORK_TX_QUEUE_SIZE)
raydium_network_queue_index=0;
}
unsigned long *raydium_network_internal_find_delay_addr(int player)
{
//if client
if(raydium_network_mode==RAYDIUM_NETWORK_MODE_CLIENT)
return &raydium_netwok_queue_ack_delay_client;
else // (server)
{
if(player<0 || player>=RAYDIUM_NETWORK_MAX_CLIENTS)
{
raydium_log("ERROR: server: bad client id in resend queue ! SHOULD NEVER APPEND !");
return NULL; // eeeerk !
}
return &raydium_netwok_queue_ack_delay_server[player];
}
}
void raydium_network_queue_check_time(void)
{
unsigned long now;
unsigned long *delay;
raydium_network_Tcp *e;
int i;
// find current delay
for(i=0;i<RAYDIUM_NETWORK_TX_QUEUE_SIZE;i++)
if(raydium_network_queue[i].state)
{
now=raydium_timecall_clock();
e=&raydium_network_queue[i];
delay=raydium_network_internal_find_delay_addr(e->to_player);
// TCP style timeout: timeout=estimatedRTT*2
if( now>(e->time+(*delay)*2) || e->time>now )
{
// resend packet (and do not add this packet to queue again !)
#ifdef DEBUG_NETWORK
raydium_log("ACK re-asking: tcpid=%i",e->tcpid);
#endif
raydium_network_write_notcp=1;
raydium_network_write(&e->to,-1,e->packet[0],e->packet);
raydium_network_stat_reemitted++;
(*delay)*=2; // Karn/Partridge TCP algo
if((*delay)/(double)raydium_timecall_clocks_per_sec>RAYDIUM_NETWORK_ACK_DELAY_MAX)
{
#ifdef DEBUG_NETWORK
raydium_log("ACK: slow network ! max ack delay reached");
#endif
(*delay)=raydium_timecall_clocks_per_sec*RAYDIUM_NETWORK_ACK_DELAY_MAX;
}
e->retries_left--;
e->time=now;
if(e->retries_left==0)
{
#ifdef DEBUG_NETWORK
raydium_log("ACK: packet lost, too many retries: tcpid=%i",e->tcpid);
#endif
raydium_network_queue_element_init(e);
raydium_network_stat_lost++;
}
}
}
}
void raydium_network_queue_ack_send(unsigned short tcpid, struct sockaddr *to)
{
char buff[RAYDIUM_NETWORK_PACKET_SIZE];
//unsigned short port;
memcpy(buff+RAYDIUM_NETWORK_PACKET_OFFSET,&tcpid,sizeof(tcpid));
raydium_network_write(to,raydium_network_uid,RAYDIUM_NETWORK_PACKET_ACK,buff);
//memcpy(&port,&to->sa_data[0],sizeof(port));
//raydium_log("ACK ---> %i.%i.%i.%i:%i",to->sa_data[2],to->sa_data[3],to->sa_data[4],to->sa_data[5],ntohs(port));
}
void raydium_network_queue_ack_recv(int type,char *buff)
{
unsigned short tcpid;
int i;
raydium_network_Tcp *e;
unsigned long now;
// TCP style weight average params
const float a=0.85;
const float b=0.15;
memcpy(&tcpid,buff+RAYDIUM_NETWORK_PACKET_OFFSET,sizeof(tcpid));
for(i=0;i<RAYDIUM_NETWORK_TX_QUEUE_SIZE;i++)
{
e=&raydium_network_queue[i];
if(e->state && e->tcpid==tcpid)
{
// ACK is correct, deleting packet from queue
#ifdef DEBUG_NETWORK
raydium_log("ACK recv ok: tcpid=%i",tcpid);
#endif
now=raydium_timecall_clock();
if(e->time<now)
{
unsigned long *delay;
delay=raydium_network_internal_find_delay_addr(e->to_player);
// Based on original TCP adaptative retransmission algorithm :
*(delay)=a * (*delay) + b*(now - e->time);
#ifdef DEBUG_NETWORK
raydium_log("ACK delay re-eval: %.2f msec (inst=%.2f msec) (client %i)",(*delay)/(double)raydium_timecall_clocks_per_sec*1000,(now - e->time)/(double)raydium_timecall_clocks_per_sec*1000,e->to_player);
#endif
}
raydium_network_queue_element_init(e);
return;
}
}
raydium_network_stat_bogus_ack++;
#ifdef DEBUG_NETWORK
raydium_log("ACK bogus (double ack, probably): tcpid=%i",tcpid);
#endif
}
// -----------------------------------------------------------------------
void raydium_network_player_name(char *str)
{
#ifndef WIN32
{
struct passwd *pn;
pn=getpwuid(geteuid()); // ("old" kernels ok ?)
strncpy(str,pn->pw_name,RAYDIUM_MAX_NAME_LEN-1);
}
#else
{
DWORD s;
s=RAYDIUM_MAX_NAME_LEN-1;
GetUserName(str,&s);
}
#endif
if(!strlen(str))
gethostname(str,RAYDIUM_MAX_NAME_LEN-1);
}
signed char raydium_network_set_socket_block_internal(int socket, int block)
{
int ret=-1;
//int flags = fcntl(sock, F_GETFL); // bof... :)
#ifndef WIN32
if(!block) block=O_NONBLOCK; else block=0;
ret=fcntl(socket, F_SETFL, block);
#else
if(!block) block=1; else block=0;
ret = ioctlsocket(socket, FIONBIO, (unsigned long *)&block);
if(ret) ret=-1;
#endif
if(ret==-1)
{
raydium_log("ERROR ! network: cannot block/unblock socket");
perror("System");
return(0);
}
return(1);
}
signed char raydium_network_set_socket_block(int block)
{
return raydium_network_set_socket_block_internal(raydium_network_socket,block);
}
//int raydium_network_get_socket_block(void)
//{
//return !( fcntl(raydium_network_socket, F_GETFL) & O_NONBLOCK );
//}
int raydium_network_socket_close(int fd)
{
#ifndef WIN32
return close(fd);
#else
return closesocket(fd);
#endif
}
// I've no idea why some version of Dev-CPP are losing this value ...
#ifndef FD_SETSIZE
#define FD_SETSIZE 64
#endif
signed char raydium_network_socket_is_readable(int fd)
{
fd_set readSet;
struct timeval timeVal;
int selectCount;
timeVal.tv_sec=0;
timeVal.tv_usec=0;
FD_ZERO(&readSet);
FD_SET(fd,&readSet);
selectCount=select(fd+1,&readSet,NULL,NULL,&timeVal);
if (selectCount<0)
return 0; // failed
if (selectCount==0)
return 0; // not readable
if (FD_ISSET(fd,&readSet))
return 1; // readable
return 0; // ?!!
}
signed char raydium_network_netcall_add(void *ptr, int type, signed char tcp)
{
int i;
for(i=0;i<RAYDIUM_NETWORK_MAX_NETCALLS;i++)
if(raydium_network_netcall_type[i]<0)
{
raydium_network_netcall_func[i]=ptr;
raydium_network_netcall_type[i]=type;
raydium_network_netcall_tcp[i]=tcp;
break;
}
if(i==RAYDIUM_NETWORK_MAX_NETCALLS)
{
raydium_log("network: ERROR: no more netcalls !");
return 0;
}
return 1;
}
void raydium_network_netcall_exec(int type,char *buff)
{
char tmpbuff[RAYDIUM_NETWORK_PACKET_SIZE];
int i;
void (*f)(int, char*);
if(type >= 0)
for(i=0;i<RAYDIUM_NETWORK_MAX_NETCALLS;i++)
if(raydium_network_netcall_type[i]==type)
{
memcpy(tmpbuff,buff,RAYDIUM_NETWORK_PACKET_SIZE);
f=raydium_network_netcall_func[i];
f(type,tmpbuff);
}
}
signed char raydium_network_timeout_check(void)
{
char str[RAYDIUM_NETWORK_PACKET_SIZE];
int i,n;
time_t now;
void (*f)(int);
time(&now);
if(raydium_network_mode==RAYDIUM_NETWORK_MODE_SERVER)
{
for(i=n=0;i<RAYDIUM_NETWORK_MAX_CLIENTS;i++)
if(raydium_network_client[i] && now>raydium_network_keepalive[i]+RAYDIUM_NETWORK_TIMEOUT)
{
raydium_log("network: TIMEOUT for client %i (%i sec): %s deleted !",i,RAYDIUM_NETWORK_TIMEOUT,raydium_network_name[i]);
raydium_network_client[i]=0;
if(raydium_network_on_disconnect)
{
f=raydium_network_on_disconnect;
f(i);
}
raydium_network_name[i][0]=0;
str[RAYDIUM_NETWORK_PACKET_OFFSET]=i;
str[RAYDIUM_NETWORK_PACKET_OFFSET+1]=0;
raydium_network_broadcast(RAYDIUM_NETWORK_PACKET_INFO_NAME,str);
n++;
}
return n;
}
return 0;
}
void raydium_network_init_sub(void)
{
int i;
if(raydium_network_mode)
raydium_network_socket_close(raydium_network_socket);
raydium_network_uid=-1;
raydium_network_mode=RAYDIUM_NETWORK_MODE_NONE;
raydium_network_socket=-1;
raydium_network_beacon[RAYDIUM_NETWORK_PACKET_OFFSET]=0;
raydium_network_beacon_search.active=0;
for(i=0;i<RAYDIUM_NETWORK_MAX_CLIENTS;i++)
{
raydium_network_client[i]=0;
raydium_network_name[i][0]=0;
}
for(i=0;i<RAYDIUM_NETWORK_MAX_SERVERS;i++)
raydium_network_server_list[i].when=0;
}
signed char raydium_network_init(void)
{
int i;
#ifdef WIN32
int ret;
WSADATA WSAData;
ret = WSAStartup(MAKEWORD(1,1), &WSAData);
if (ret) { raydium_log("network: FAILED ! (Winsock 2 Error: %i while asking for version 1.1)",ret); return(0); }
#endif
raydium_network_init_sub();
for(i=0;i<RAYDIUM_NETWORK_MAX_NETCALLS;i++)
{
raydium_network_netcall_type[i]=-1;
raydium_network_netcall_func[i]=0;
raydium_network_netcall_tcp[i]=0;
}
for(i=0;i<RAYDIUM_NETWORK_TX_QUEUE_SIZE;i++)
raydium_network_queue_element_init(&raydium_network_queue[i]);
for(i=0;i<RAYDIUM_NETWORK_TX_QUEUE_SIZE;i++)
{
raydium_network_tcpid_i[i]=0;
raydium_network_tcpid_p[i]=0;
}
for(i=0;i<RAYDIUM_NETWORK_MAX_PROPAGS;i++)
raydium_network_propag[i].state=0;
raydium_network_queue_index=0;
raydium_network_tcpid_index=0;
raydium_network_on_connect=raydium_network_on_disconnect=NULL;
raydium_network_stat_rx=0;
raydium_network_stat_tx=0;
raydium_network_stat_lost=0;
raydium_network_stat_double=0;
raydium_network_stat_reemitted=0;
raydium_network_stat_bogus_ack=0;
raydium_network_netcall_add(raydium_network_queue_ack_recv,RAYDIUM_NETWORK_PACKET_ACK,0);
raydium_netwok_queue_ack_delay_client=raydium_timecall_clocks_per_sec; // 1sec default delay
for(i=0;i<RAYDIUM_NETWORK_MAX_CLIENTS;i++)
raydium_netwok_queue_ack_delay_server[i]=raydium_timecall_clocks_per_sec; // 1sec default delay
raydium_network_write_notcp=0;
raydium_network_name_local[0]=0;
raydium_network_connected_server[0]=0;
if(raydium_init_cli_option("name",raydium_network_name_local))
{
if(strlen(raydium_network_name_local)==0)
raydium_log("Warning: network: --name option needs an argument");
}
if(strlen(raydium_network_name_local)==0)
raydium_network_player_name(raydium_network_name_local);
raydium_log("network: OK");
return(1);
}
// if from < 0, "from" field is not modified (used for broadcasts, mainly)
void raydium_network_write(struct sockaddr *to, int from, signed char type,char *buff)
{
int ret=-1;
unsigned short tcpid=0;
buff[0]=type;
if(from>=0)
buff[1]=(char)from; // 256 clients MAX for now.
if(raydium_network_write_notcp==0 && raydium_network_queue_is_tcpid(type))
tcpid=raydium_network_queue_tcpid_gen();
if(raydium_network_write_notcp==0) // do not erase tcpid packet's element if it's a re-send
memcpy(buff+2,&tcpid,sizeof(unsigned short));
raydium_network_write_notcp=0;
raydium_network_stat_tx+=RAYDIUM_NETWORK_PACKET_SIZE;
if(raydium_network_mode==RAYDIUM_NETWORK_MODE_CLIENT)
ret=send(raydium_network_socket, buff, RAYDIUM_NETWORK_PACKET_SIZE, 0);
else if(raydium_network_mode==RAYDIUM_NETWORK_MODE_SERVER)
ret=sendto(raydium_network_socket, buff, RAYDIUM_NETWORK_PACKET_SIZE, 0, to, sizeof(struct sockaddr));
raydium_network_timeout_check();
//raydium_log("send ret: %i",ret);
if(ret<0)
{
raydium_log("network: ERROR sending ! (%i)",ret);
return;
}
if(tcpid)
{
raydium_network_queue_element_add(buff,to);
#ifdef DEBUG_NETWORK
raydium_log("ACK asking to peer: tcpid=%i type=%i",tcpid,type);
#endif
}
}
void raydium_network_broadcast(signed char type,char *buff)
{
int i;
for(i=0;i<RAYDIUM_NETWORK_MAX_CLIENTS;i++)
if(raydium_network_client[i])
raydium_network_write(&raydium_network_client_addr[i],-1,type,buff);
}
// buff could be modified, even if return is RAYDIUM_NETWORK_DATA_NONE
signed char raydium_network_read(int *id, signed char *type, char *buff)
{
int i;
struct sockaddr from;
socklen_t len;
int ret;
char dbl=0;
time_t now;
time(&now);
raydium_network_timeout_check();
raydium_network_queue_check_time();
raydium_network_server_broadcast_check();
// search for outdated server slots (should isolate this in a function ?)
for(i=0;i<RAYDIUM_NETWORK_MAX_SERVERS;i++)
if(raydium_network_server_list[i].when!=0)
if(raydium_network_server_list[i].when+RAYDIUM_NETWORK_BEACON_DEFAULT_TTL<now)
raydium_network_server_list[i].when=0;
len=sizeof(struct sockaddr);
ret=recvfrom(raydium_network_socket,buff,RAYDIUM_NETWORK_PACKET_SIZE,0,&from,&len);
if(ret==RAYDIUM_NETWORK_PACKET_SIZE)
{
unsigned short tcpid;
*type=buff[0];
*id=buff[1];
raydium_network_stat_rx+=RAYDIUM_NETWORK_PACKET_SIZE;
memcpy(&tcpid,buff+2,sizeof(unsigned short));
if(tcpid)
{
#ifdef DEBUG_NETWORK
raydium_log("ACK read (and will send): tcpid=%i type=%i",tcpid,*type);
#endif
if(raydium_network_queue_tcpid_known(tcpid,*id))
{
raydium_network_stat_double++;
dbl=1;
#ifdef DEBUG_NETWORK
raydium_log("ACK double: tcpid=%i type=%i",tcpid,*type);
#endif
}
/*else*/ raydium_network_queue_ack_send(tcpid,&from);
raydium_network_queue_tcpid_known_add(tcpid,buff[1]);
}
if(dbl) // discard double packet
return(RAYDIUM_NETWORK_DATA_NONE);
// user must no see this type with netcalls !
if(*type==RAYDIUM_NETWORK_PACKET_SERVER_BEACON)
{
if(raydium_network_mode==RAYDIUM_NETWORK_MODE_DISCOVER &&
raydium_network_beacon_search.active)
{
int id;
int dec;
int version;
char *app_or_mod;
char *name;
char *info;
int player_count;
int player_max;
int slot=-1;
dec=RAYDIUM_NETWORK_PACKET_OFFSET;
dec++; // 1st byte is useless for us (server side flag)
memcpy(&id,buff+dec,sizeof(id));
dec+=sizeof(id);
// search id -> id already found -> update time -> return
for(i=0;i<RAYDIUM_NETWORK_MAX_SERVERS;i++)
if(raydium_network_server_list[i].when!=0)
if(raydium_network_server_list[i].id==id)
{
//raydium_network_server_list[i].when=now;
//return(RAYDIUM_NETWORK_DATA_NONE);
slot=i;
break;
}
memcpy(&version,buff+dec,sizeof(version));
dec+=sizeof(version);
app_or_mod=buff+dec;
dec+=(strlen(app_or_mod)+1);
// else -> id not found -> test game+version
if(version != raydium_network_beacon_search.version ||
strcmp(app_or_mod,raydium_network_beacon_search.app_or_mod))
return(RAYDIUM_NETWORK_DATA_NONE); // not for us ...
name=buff+dec;
dec+=(strlen(name)+1);
info=buff+dec;
dec+=RAYDIUM_NETWORK_BEACON_INFO_MAX_LEN;
memcpy(&player_count,buff+dec,sizeof(player_count));
dec+=sizeof(player_count);
memcpy(&player_max,buff+dec,sizeof(player_max));
dec+=sizeof(player_max);
// true -> search free -> add server
if(slot==-1)
for(i=0;i<RAYDIUM_NETWORK_MAX_SERVERS;i++)
if(raydium_network_server_list[i].when==0)
slot=i;
if(slot<0)
{
raydium_log("network: discover: too much server in this LAN ! (max=%i)",RAYDIUM_NETWORK_MAX_SERVERS);
return(RAYDIUM_NETWORK_DATA_NONE);
}
raydium_network_server_list[slot].id=id;
raydium_network_server_list[slot].when=now;
strcpy(raydium_network_server_list[slot].name,name);
strcpy(raydium_network_server_list[slot].ip,inet_ntoa(((struct sockaddr_in *)(&from))->sin_addr));
strcpy(raydium_network_server_list[slot].info,info);
raydium_network_server_list[slot].player_count=player_count;
raydium_network_server_list[slot].player_max=player_max;
}
return(RAYDIUM_NETWORK_DATA_NONE);
}
raydium_network_netcall_exec(*type,buff);
if( raydium_network_mode==RAYDIUM_NETWORK_MODE_SERVER && (*id>=0) && (*id<RAYDIUM_NETWORK_MAX_CLIENTS) )
time(&raydium_network_keepalive[(*id)]); // update keepalive
if(*type==RAYDIUM_NETWORK_PACKET_REQUEST_UID && raydium_network_mode==RAYDIUM_NETWORK_MODE_SERVER)
{
raydium_server_accept_new(&from,buff+RAYDIUM_NETWORK_PACKET_OFFSET);
return(RAYDIUM_NETWORK_DATA_NONE);
}
if(*type==RAYDIUM_NETWORK_PACKET_ACK)
return(RAYDIUM_NETWORK_DATA_NONE);
if(*type==RAYDIUM_NETWORK_PACKET_INFO_NAME && raydium_network_mode==RAYDIUM_NETWORK_MODE_CLIENT)
{
i=buff[RAYDIUM_NETWORK_PACKET_OFFSET];
strcpy(raydium_network_name[i],buff+RAYDIUM_NETWORK_PACKET_OFFSET+1);
raydium_log("network: client %i is %s",i,raydium_network_name[i]);
if(strlen(raydium_network_name[i]))
raydium_network_propag_refresh_all(); // spread propags to this new client
return(RAYDIUM_NETWORK_DATA_NONE);
}
return(RAYDIUM_NETWORK_DATA_OK);
}
else if(errno==EAGAIN) return(RAYDIUM_NETWORK_DATA_NONE); // POSIX
else {
#ifdef WIN32
ret=WSAGetLastError();
if(ret==WSAEWOULDBLOCK) return(RAYDIUM_NETWORK_DATA_NONE); // NON POSIX (GRRrrr)
#else
// perror("System");
#endif
// raydium_log("ERROR ! network: error receiving ! (%i)",ret);
return(RAYDIUM_NETWORK_DATA_ERROR);
}
}
signed char raydium_network_read_flushed(int *id, signed char *type, char *buff)
{
char ret,data=0;
char save_buff[RAYDIUM_NETWORK_PACKET_SIZE];
int save_id;
char save_type;
do
{
// erreur !: meme si le retour est NONE, buff peut avoir ete modifie !
// (et donc, on le lit pas tt a fait le dernier paquet de donnes)
// solution: sauver buff avant et le restauter au final ? (lent ?)
// 13/04/2004: solution en question mise en place: en test, premier resultats
// probants
ret=raydium_network_read(id,type,buff);
if(ret==RAYDIUM_NETWORK_DATA_OK)
{
data++;
memcpy(save_buff,buff,RAYDIUM_NETWORK_PACKET_SIZE);
save_id=*id;
save_type=*type;
}
}while(ret==RAYDIUM_NETWORK_DATA_OK);
if(data)
{
memcpy(buff,save_buff,RAYDIUM_NETWORK_PACKET_SIZE);
*id=save_id;
*type=save_type;
return RAYDIUM_NETWORK_DATA_OK;
}
else return ret;
}
void raydium_network_read_faked(void)
{
int id;
signed char type;
char buff[RAYDIUM_NETWORK_PACKET_SIZE];
raydium_network_read_flushed(&id,&type,buff);
}
signed char raydium_network_server_broadcast(char *name, char *app_or_mod, int version)
{
int dec=0;
int id;
int player_count;
int player_max;
if(raydium_network_mode!=RAYDIUM_NETWORK_MODE_SERVER)
{
raydium_log("network: ERROR: cannot set server broadcast attributes: not a server");
return 0;
}
dec=RAYDIUM_NETWORK_PACKET_OFFSET;
if( strlen(name)+strlen(app_or_mod)+dec+
sizeof(version)+sizeof(id) +
sizeof(player_count) + sizeof(player_max) +
RAYDIUM_NETWORK_BEACON_INFO_MAX_LEN >=
RAYDIUM_NETWORK_PACKET_SIZE-10)
{
raydium_log("network: ERROR: cannot set server attributes: packet's too small");
return 0;
}
player_count=0;
player_max=0;
id=rand();
dec=RAYDIUM_NETWORK_PACKET_OFFSET;
raydium_network_beacon[dec]=1;
dec++;
memcpy(raydium_network_beacon+dec,&id,sizeof(id)); // server id
dec+=sizeof(id);
memcpy(raydium_network_beacon+dec,&version,sizeof(version));
dec+=sizeof(version);
strcpy(raydium_network_beacon+dec,app_or_mod);
dec+=(strlen(app_or_mod)+1);
strcpy(raydium_network_beacon+dec,name);
dec+=(strlen(name)+1);
raydium_network_beacon_info_offset=dec;
raydium_network_beacon[dec]=0; // no info yet
dec+=RAYDIUM_NETWORK_BEACON_INFO_MAX_LEN;
memcpy(raydium_network_beacon+dec,&player_count,sizeof(player_count));
dec+=sizeof(player_count);
memcpy(raydium_network_beacon+dec,&player_max,sizeof(player_max));
dec+=sizeof(player_max);
raydium_log("network: now broadcasting : '%s' (%s) version %i",name,app_or_mod,version);
return 1;
}
void raydium_network_server_broadcast_info(char *info)
{
if(raydium_network_mode!=RAYDIUM_NETWORK_MODE_SERVER)
{
raydium_log("network: ERROR: cannot set server broadcast infos: not a server");
return;
}
if(strlen(info)<RAYDIUM_NETWORK_BEACON_INFO_MAX_LEN-1)
strcpy(raydium_network_beacon+raydium_network_beacon_info_offset,info);
else
raydium_log("network: ERROR: cannot set server broadcast info: string's too long");
}
void raydium_network_server_broadcast_check(void)
{
static time_t last=0;
time_t now;
if(raydium_network_mode!=RAYDIUM_NETWORK_MODE_SERVER)
return;
// no beacon is ready ? (see _server_broadcast())
if(!raydium_network_beacon[RAYDIUM_NETWORK_PACKET_OFFSET])
return;
time(&now);
if(now>last+RAYDIUM_NETWORK_BEACON_DELAY)
{
int player_count;
int player_max;
int dec;
int i;
#ifndef linux
struct sockaddr_in sock;
#endif
player_max=RAYDIUM_NETWORK_MAX_CLIENTS;
player_count=0;
for(i=0;i<RAYDIUM_NETWORK_MAX_CLIENTS;i++)
if(raydium_network_client[i])
player_count++;
dec=raydium_network_beacon_info_offset+RAYDIUM_NETWORK_BEACON_INFO_MAX_LEN;
memcpy(raydium_network_beacon+dec,&player_count,sizeof(player_count));
dec+=sizeof(player_count);
memcpy(raydium_network_beacon+dec,&player_max,sizeof(player_max));
dec+=sizeof(player_max);
#ifdef linux
for(i=0;i<raydium_network_broadcast_interface_index;i++)
raydium_network_write((struct sockaddr *)&raydium_network_broadcast_interfaces[i],
255,RAYDIUM_NETWORK_PACKET_SERVER_BEACON,raydium_network_beacon);
#else
sock.sin_family=AF_INET;
sock.sin_addr.s_addr=htonl(INADDR_BROADCAST);
sock.sin_port=htons(RAYDIUM_NETWORK_BEACON_PORT);
raydium_network_write((struct sockaddr *)&sock,255,RAYDIUM_NETWORK_PACKET_SERVER_BEACON,raydium_network_beacon);
#endif
last=now;
}
}
signed char raydium_network_server_create(void)
{
struct sockaddr_in sock;
int on=1;
int ret;
if(raydium_network_mode!=RAYDIUM_NETWORK_MODE_NONE)
{
raydium_log("network: ERROR: cannot create server : already connected");
return 0;
}
raydium_network_start=time(NULL);
raydium_network_socket=socket(AF_INET,RAYDIUM_NETWORK_UDP,0);
if(raydium_network_socket==-1)
{
raydium_log("ERROR ! network: cannot create server socket");
perror("System");
return(0);
}
raydium_log("network: server socket created");
sock.sin_family=AF_INET;
sock.sin_addr.s_addr=htonl(INADDR_ANY);
sock.sin_port=htons(RAYDIUM_NETWORK_PORT);
ret=bind(raydium_network_socket,(struct sockaddr *)&sock,sizeof(sock));
if(ret)
{
raydium_log("ERROR ! network: cannot open server socket (already used ?)");
perror("System");
return(0);
}
#ifdef linux
raydium_network_linux_find_broadcast_interfaces();
#endif
raydium_log("network: server OK: waiting for clients (%i max) at udp port %i",RAYDIUM_NETWORK_MAX_CLIENTS,RAYDIUM_NETWORK_PORT);
raydium_network_mode=RAYDIUM_NETWORK_MODE_SERVER;
setsockopt(raydium_network_socket,SOL_SOCKET,SO_BROADCAST,(char *)&on,sizeof(on));
raydium_network_set_socket_block(0);
return(1);
}
// will resolv "server" name
signed char raydium_network_client_connect_to(char *server)
{
struct sockaddr_in sock;
int ret,empty;
char str[RAYDIUM_NETWORK_PACKET_SIZE];
signed char type;
struct hostent *server_addr;
int on=1;
// should automaticaly stop discover mode here ...
if(raydium_network_mode==RAYDIUM_NETWORK_MODE_DISCOVER)
{
raydium_network_socket_close(raydium_network_socket);
raydium_network_mode=RAYDIUM_NETWORK_MODE_NONE;
}
if(raydium_network_mode!=RAYDIUM_NETWORK_MODE_NONE)
{
raydium_log("network: ERROR: cannot create client : already connected");
return 0;
}
raydium_network_start=time(NULL);
raydium_network_socket=socket(AF_INET,RAYDIUM_NETWORK_UDP,0);
if(raydium_network_socket==-1)
{
raydium_log("ERROR ! network: cannot create client socket");
perror("System");
return(0);
}
raydium_log("network: client socket created");
server_addr = gethostbyname(server);
if(!server_addr)
{
raydium_log("ERROR ! DNS/resolv error with \"%s\"",server);
perror("System");
return(0);
}
//inet_pton(AF_INET,server,&sock.sin_addr);
memcpy((char*)(&(sock.sin_addr.s_addr)), server_addr->h_addr, server_addr->h_length);
sock.sin_family=AF_INET;
sock.sin_port=htons(RAYDIUM_NETWORK_PORT);
ret=connect(raydium_network_socket,(struct sockaddr *)&sock,sizeof(sock));
if(ret)
{
raydium_log("ERROR ! local UDP socket error (contacting %s)",server);
perror("System");
return(0);
}
raydium_log("network: connecting to %s and waiting UID...",server);
raydium_network_set_socket_block(1);
setsockopt(raydium_network_socket,SOL_SOCKET,SO_BROADCAST,(char *)&on,sizeof(on));
// needed now, because we use network_write
raydium_network_mode=RAYDIUM_NETWORK_MODE_CLIENT;
// we need to send request for uid (and send our name)
strcpy(str+RAYDIUM_NETWORK_PACKET_OFFSET,raydium_network_name_local);
raydium_network_write(NULL,-1,RAYDIUM_NETWORK_PACKET_REQUEST_UID,str);
// probably needs timeouts, here ...
if (raydium_network_read(&empty,&type,str)!=RAYDIUM_NETWORK_DATA_OK)
{
raydium_network_mode=RAYDIUM_NETWORK_MODE_NONE;
raydium_log("ERROR ! network: cannot connect to server %s",server);
perror("System");
raydium_network_socket_close(raydium_network_socket);
return(0);
}
if(type==RAYDIUM_NETWORK_PACKET_ATTRIB_UID)
{
raydium_network_uid=str[RAYDIUM_NETWORK_PACKET_OFFSET];
raydium_log("network: accepted as client %i",raydium_network_uid);
raydium_network_set_socket_block(0);
strcpy(raydium_network_connected_server,server);
return(1);
}
if(type==RAYDIUM_NETWORK_PACKET_ERROR_NO_MORE_PLACE)
{
raydium_network_mode=RAYDIUM_NETWORK_MODE_NONE;
raydium_log("ERROR ! network: no more room (server said: %s)",str+RAYDIUM_NETWORK_PACKET_OFFSET);
raydium_network_socket_close(raydium_network_socket);
return(0);
}
raydium_network_mode=RAYDIUM_NETWORK_MODE_NONE;
raydium_log("ERROR ! network: unknow server message type (%i). abort.",type);
raydium_network_socket_close(raydium_network_socket);
return(0);
}
signed char raydium_network_client_discover(char *game,int version)
{
struct sockaddr_in sock;
int on=1;
int ret;
if(raydium_network_mode!=RAYDIUM_NETWORK_MODE_NONE)
{
raydium_log("network: ERROR: cannot create discover : already connected");
return 0;
}
raydium_network_start=time(NULL);
raydium_network_socket=socket(AF_INET,RAYDIUM_NETWORK_UDP,0);
if(raydium_network_socket==-1)
{
raydium_log("ERROR ! network: cannot create discover socket");
perror("System");
return(0);
}
raydium_log("network: discover socket created");
sock.sin_family=AF_INET;
sock.sin_addr.s_addr=htonl(INADDR_ANY);
sock.sin_port=htons(RAYDIUM_NETWORK_BEACON_PORT);
ret=bind(raydium_network_socket,(struct sockaddr *)&sock,sizeof(sock));
if(ret)
{
raydium_log("ERROR ! network: cannot open discover socket (already used ?)");
perror("System");
return(0);
}
raydium_network_beacon_search.active=1;
strcpy(raydium_network_beacon_search.app_or_mod,game);
raydium_network_beacon_search.version=version;
raydium_network_mode=RAYDIUM_NETWORK_MODE_DISCOVER;
setsockopt(raydium_network_socket,SOL_SOCKET,SO_BROADCAST,(char *)&on,sizeof(on));
raydium_network_set_socket_block(0);
raydium_log("network: discover OK: waiting for server beacons with '%s' (version %i)",game,version);
return 1;
}
// return :
// -1 : not in discover mode
// 0 and more : number of broadcasting servers
int raydium_network_discover_numservers(void)
{
int i;
int cpt=0;
if(raydium_network_mode!=RAYDIUM_NETWORK_MODE_DISCOVER ||
!raydium_network_beacon_search.active)
return -1;
for(i=0;i<RAYDIUM_NETWORK_MAX_SERVERS;i++)
if(raydium_network_server_list[i].when!=0)
cpt++;
return cpt;
}
// "num" starts from 0 to raydium_network_discover_numservers()-1
// return :
// -1 : not in discover mode
// 0 : "num" is invalid
// 1 : ok
signed char raydium_network_discover_getserver(int num, char *name, char *ip, char *info, int *player_count, int *player_max)
{
int i,cpt;
int slot=-1;
if( raydium_network_mode!=RAYDIUM_NETWORK_MODE_DISCOVER ||
!raydium_network_beacon_search.active)
return -1;
for(cpt=0,i=0;i<RAYDIUM_NETWORK_MAX_SERVERS;i++)
if(raydium_network_server_list[i].when!=0)
{
if(cpt==num)
{
slot=i;
break;
}
cpt++;
}
if(slot<0)
return 0;
// printf(".\n");
strcpy(name,raydium_network_server_list[slot].name);
strcpy(ip,raydium_network_server_list[slot].ip);
strcpy(info,raydium_network_server_list[slot].info);
*player_count=raydium_network_server_list[slot].player_count;
*player_max=raydium_network_server_list[slot].player_max;
return 1;
}
void raydium_network_client_disconnect(void)
{
raydium_network_init_sub();
raydium_log("network: disconnected");
}
signed char raydium_server_accept_new(struct sockaddr *from, char *name)
{
int socklen,i,n;
char str[RAYDIUM_NETWORK_PACKET_SIZE];
void (*f)(int);
socklen=sizeof(struct sockaddr);
for(i=0,n=-1;i<RAYDIUM_NETWORK_MAX_CLIENTS;i++)
if(!raydium_network_client[i]) {n=i; break;}
if(n<0)
{
// no more room in this server
sprintf(str+RAYDIUM_NETWORK_PACKET_OFFSET,"Server limited to %i client(s)",RAYDIUM_NETWORK_MAX_CLIENTS);
raydium_network_write(from,-1,RAYDIUM_NETWORK_PACKET_ERROR_NO_MORE_PLACE,str);
return(0);
}
memcpy(&raydium_network_client_addr[n],from,sizeof(struct sockaddr));
raydium_network_client[n]=1;
time(&raydium_network_keepalive[n]); // first keepalive
strcpy(raydium_network_name[n],name);
raydium_netwok_queue_ack_delay_server[n]=raydium_timecall_clocks_per_sec; // 1sec default delay
raydium_log("network: client %i connected as %s"/*,inet_ntoa(from->sin_addr)*/,n,name);
/* send uid to client */
str[RAYDIUM_NETWORK_PACKET_OFFSET]=n;
raydium_network_write(from,-1,RAYDIUM_NETWORK_PACKET_ATTRIB_UID,str);
// OnConnect:
for(i=0;i<RAYDIUM_NETWORK_MAX_CLIENTS;i++)
if(i!=n && raydium_network_client[i])
{
strcpy(str+RAYDIUM_NETWORK_PACKET_OFFSET+1,raydium_network_name[i]);
str[RAYDIUM_NETWORK_PACKET_OFFSET]=i;
raydium_network_write(from,i,RAYDIUM_NETWORK_PACKET_INFO_NAME,str);
}
strcpy(str+RAYDIUM_NETWORK_PACKET_OFFSET+1,raydium_network_name[n]); // send name to all others...
str[RAYDIUM_NETWORK_PACKET_OFFSET]=n;
raydium_network_broadcast(RAYDIUM_NETWORK_PACKET_INFO_NAME,str);
if(raydium_network_on_connect)
{
f=raydium_network_on_connect;
f(n);
}
return(n);
}
void raydium_network_close(void)
{
raydium_network_socket_close(raydium_network_socket);
#ifdef WIN32
WSACleanup();
#endif
}
void raydium_network_internal_server_delays_dump(void)
{
int i;
raydium_log("Network server delays:");
for(i=0;i<RAYDIUM_NETWORK_MAX_CLIENTS;i++)
if(raydium_network_client[i])
raydium_log("player %i : %.2f msec (%s)",
i,
raydium_netwok_queue_ack_delay_server[i]/(double)raydium_timecall_clocks_per_sec*1000,
raydium_network_name[i]);
}
void raydium_network_internal_dump(void)
{
time_t diff;
diff=time(NULL)-raydium_network_start;
raydium_log("Network stats:");
raydium_log("Rx: %i byte(s) / Tx: %i bytes(s) / %.2f min",raydium_network_stat_rx,raydium_network_stat_tx,diff/60.f);
raydium_log("Transfert rates: Rx: %.2f KB/s / Tx: %.2f KB/s",raydium_network_stat_rx/(float)diff/1024.f,raydium_network_stat_tx/(float)diff/1024.f);
raydium_log("Packets (err): Tx: %i re-emitted, Rx: %i doubles",raydium_network_stat_reemitted,raydium_network_stat_double);
raydium_log("Packets (err): Tx: %i erased or lost, bogus ACK: %i",raydium_network_stat_lost,raydium_network_stat_bogus_ack);
}
/*
// Test internet connection using a root dns server ... shame ? :)
#define RAYDIUM_NETWORK_INTERNET_TEST_HOST "198.41.0.4"
#define RAYDIUM_NETWORK_INTERNET_TEST_PORT 53
#define RAYDIUM_NETWORK_INTERNET_TEST_TIMEOUT 8
*/
signed char raydium_network_internet_test(void)
{
/*
int sockfd;
struct sockaddr_in serv_addr;
struct hostent *server;
//struct timeval sv;
//int svlen;
struct timeval timeout;
fd_set writable;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
server = gethostbyname(RAYDIUM_NETWORK_INTERNET_TEST_HOST);
//server = gethostbyname("192.168.2.1");
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
memcpy(&(serv_addr.sin_addr.s_addr),server->h_addr,server->h_length);
serv_addr.sin_port = htons(RAYDIUM_NETWORK_INTERNET_TEST_PORT);
raydium_network_set_socket_block_internal(sockfd,0);
// i'm tired of win32's sockets bad behaviours ... let's trust connect() ...
connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
// dont ask me why ... (Dev-CPP's mingw needs it)
#ifdef WIN32
#ifndef FD_SETSIZE
#define FD_SETSIZE 64
#endif
#endif
FD_ZERO(&writable);
FD_SET(sockfd, &writable);
timeout.tv_sec=RAYDIUM_NETWORK_INTERNET_TEST_TIMEOUT;
timeout.tv_usec=0;
#ifndef ENETUNREACH
#define ENETUNREACH WSAENETUNREACH
#endif
if (select(sockfd + 1, NULL, &writable, NULL, &timeout) <= 0 || errno==ENETUNREACH)
{
raydium_log("network: cannot contact remote server, no internet connection detected");
raydium_network_socket_close(sockfd);
return 0; // not writable
}
//raydium_log("network: internet link is ok");
raydium_network_socket_close(sockfd);
return 1; // writable
*/
// may use a cache system ? (time-limited ? with an arg ?)
return raydium_rayphp_http_test();
}
#ifdef linux
signed char raydium_network_linux_find_broadcast_interfaces(void)
{
int fd,len, ifflags;
struct sockaddr_in addr;
struct ifconf ifconf;
struct ifreq *ifreqp, ifreq, ifbuf[RAYDIUM_NETWORK_BROADCAST_INTERFACE_MAX];
char name[RAYDIUM_MAX_NAME_LEN];
char msg[RAYDIUM_MAX_NAME_LEN];
msg[0]=0;
raydium_network_broadcast_interface_index=0;
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
raydium_log("network: linux broadcast find interfaces: ERROR");
perror("socket");
return 0;
}
ifconf.ifc_len = sizeof(ifbuf);
ifconf.ifc_buf = (caddr_t)ifbuf;
memset((void *)ifbuf, 0, sizeof(ifbuf));
if (ioctl(fd, SIOCGIFCONF, (char *)&ifconf) == -1)
{
raydium_log("network: linux broadcast find interfaces: ERROR");
perror("ioctl SIOCGIFCONF");
close(fd);
return 0;
}
for (len = 0; len + (int)sizeof(struct ifreq) <= ifconf.ifc_len;)
{
ifreqp = (struct ifreq *)&ifconf.ifc_buf[len];
len += sizeof(struct ifreq);
if (ifreqp->ifr_addr.sa_family != AF_INET)
continue;
addr = *(struct sockaddr_in *)&ifreqp->ifr_addr;
strcpy(name,ifreqp->ifr_name);
//printf("\taddress %s\n", inet_ntoa(addr.sin_addr));
ifreq = *ifreqp;
if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifreq) == -1)
{
raydium_log("network: linux broadcast find interfaces: ERROR");
perror("ioctl SIOCGIFFLAGS");
continue;
}
ifflags = ifreq.ifr_flags;
// interface up ?
if ((ifflags & IFF_UP) == 0)
continue;
// running ?
if ((ifflags & IFF_RUNNING) == 0)
continue;
// is loopback ?
if ((ifflags & IFF_LOOPBACK) != 0)
continue;
// can broadcast ?
if ((ifflags & IFF_BROADCAST) == 0)
continue;
ifreq = *ifreqp;
if (ioctl(fd, SIOCGIFBRDADDR, (char *)&ifreq) == -1)
{
perror("ioctl SIOCGIFBRDADDR");
continue;
}
addr = *(struct sockaddr_in *)&ifreq.ifr_addr;
addr.sin_family = AF_INET;
addr.sin_port=htons(RAYDIUM_NETWORK_BEACON_PORT);
memcpy( &raydium_network_broadcast_interfaces[raydium_network_broadcast_interface_index],
&addr,sizeof(addr)); // broadcast address
strcat(msg,name);
strcat(msg," ");
raydium_network_broadcast_interface_index++;
}
close(fd);
raydium_log("network: linux broadcast interface(s): %s",msg);
return 1;
}
#endif