/*
    Raydium - CQFD Corp.
    http://raydium.org/
    Released under both BSD license and Lesser GPL library license.
    See "license.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);

// utility function to workaround strict-aliasing rules
void *get_in_addr(struct sockaddr *sa)
{
    union {
        struct sockaddr     *sa;
        struct sockaddr_in  *sa_in;
        struct sockaddr_in6 *sa_in6;
    } u;
    u.sa = sa;
    if (sa->sa_family == AF_INET)
        return &(u.sa_in->sin_addr);
    else
        return &(u.sa_in6->sin6_addr);
}


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);
if(raydium_network_mode==RAYDIUM_NETWORK_MODE_SERVER)
    raydium_network_broadcast(raydium_network_propag[i].type,buff);
else
    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'(%i): invalid type",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>(int)(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_network_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_network_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
unsigned long ulblock;
if(!block) ulblock=1; else ulblock=0;
ret = ioctlsocket(socket, FIONBIO, &ulblock);
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;
}

// returns 1 if a netcall was found, 0 otherwise
signed char raydium_network_netcall_exec(int type,char *buff)
{
char tmpbuff[RAYDIUM_NETWORK_PACKET_SIZE];
int i;
void (*f)(int, char*);
signed char done=0;

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);
      done=1;
   }
return done;
}

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;

}

void raydium_network_port_set(unsigned int port){
    raydium_network_close();
    raydium_network_init_port(port);
}

signed char raydium_network_init(void){
char tmp[128];
char strport[16];
    sprintf(strport,"%d",RAYDIUM_NETWORK_DEFAULT_PORT);
    raydium_init_cli_option_default("port",tmp,strport);
    return raydium_network_init_port(atoi(tmp));
}

signed char raydium_network_init_port(int port)
{
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_port=port;
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_network_queue_ack_delay_client=raydium_timecall_clocks_per_sec; // 1sec default delay

for(i=0;i<RAYDIUM_NETWORK_MAX_CLIENTS;i++)
    raydium_network_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){
    //raydium_network_set_socket_block(1);
    ret=send(raydium_network_socket, buff, RAYDIUM_NETWORK_PACKET_SIZE, 0);
    //raydium_network_set_socket_block(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("socket %i send ret: %i to: %x",raydium_network_socket,ret,to);
if(ret<0)
    {
    raydium_log("network: ERROR sending ! (%i)",ret);
//raydium_log("network: ERROR sending ! (%i) on socket %i to : %x [%02x %02x %02x %02x %02x]",WSAGetLastError(),raydium_network_socket,to,(unsigned char)buff[0],(unsigned char)buff[1],(unsigned char)buff[2],(unsigned char)buff[3],(unsigned char)buff[4]);
    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((time_t)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 in_addr *)get_in_addr(&from))); // not IPv6 ready ...
        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);
    }

 if(raydium_network_netcall_exec(*type,buff))
    return RAYDIUM_NETWORK_DATA_NONE;

 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);
    if(strlen(raydium_network_name[i]))
        {
        raydium_log("network: client %i is %s",i,raydium_network_name[i]);
        raydium_network_propag_refresh_all(); // spread propags to this new client
        }
    else
        raydium_log("network: client %i is Disconnected",i);
    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=0;
char save_type=0;

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, Beacon port %i",name,app_or_mod,version,RAYDIUM_NETWORK_BEACON_PORT);
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;
int retry;

// should automaticaly stop discover mode here ...
if(raydium_network_mode==RAYDIUM_NETWORK_MODE_DISCOVER)
    {
    raydium_network_socket_close(raydium_network_socket);
    raydium_network_socket=-1;
    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 %i",raydium_network_socket);


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 socket %i and waiting UID...",server,raydium_network_socket);
//For timeout raison, not a blocking socket.
raydium_network_set_socket_block(0);
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);

retry=5;
while(retry--){
    if (raydium_network_read(&empty,&type,str)!=RAYDIUM_NETWORK_DATA_OK && !retry)
        {
        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);
        }
#ifndef WIN32
        usleep(100000);
#else
        Sleep(100);
#endif
    raydium_log("network: connecting to %s and waiting UID keep trying (%d)...",server,retry);
}

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 port %d",RAYDIUM_NETWORK_BEACON_PORT);

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) Port %i",game,version,RAYDIUM_NETWORK_BEACON_PORT);
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 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_network_queue_ack_delay_server[n]=raydium_timecall_clocks_per_sec; // 1sec default delay

 raydium_log("network: client %i connected as %s (%s)",n,name,inet_ntoa(((struct sockaddr_in *)from)->sin_addr));

 /* 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_network_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