mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
1657 lines
47 KiB
C
1657 lines
47 KiB
C
/*H*************************************************************************************************/
|
|
/*!
|
|
|
|
\File commsrp.c
|
|
|
|
\Description
|
|
This is CommSRP (Selectively Reliable Protocol), a datagram packet-based
|
|
transport class.
|
|
|
|
\Copyright
|
|
Copyright (c) Tiburon Entertainment / Electronic Arts 1999-2003. ALL RIGHTS RESERVED.
|
|
|
|
\Version 0.5 01/03/03 (JLB) Initial Version, based on CommTCP
|
|
\Version 0.7 01/07/03 (JLB) Working unreliable transport, based on CommUDP
|
|
\Version 0.8 01/08/03 (JLB) Working reliable transport.
|
|
\Version 0.9 02/09/03 (JLB) Added support for sending zero-byte packets, and
|
|
fixed PS2 alignment issue.
|
|
*/
|
|
/*************************************************************************************************H*/
|
|
|
|
/*** Include files *********************************************************************/
|
|
|
|
#define ESC_CAUSES_LOSS (0)
|
|
|
|
#if ESC_CAUSES_LOSS
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "DirtySDK/dirtysock.h"
|
|
#include "DirtySDK/dirtysock/dirtymem.h"
|
|
#include "DirtySDK/comm/commall.h"
|
|
#include "DirtySDK/comm/commsrp.h"
|
|
|
|
/*** Defines ***************************************************************************/
|
|
|
|
//! enable debug spam
|
|
#define COMMSRP_VERBOSE (COMM_PRINT && FALSE)
|
|
|
|
//! control packet types
|
|
enum
|
|
{
|
|
CTRL_PACKET_FIRST = 16, //!< 16: beginning of packet code range
|
|
|
|
CTRL_PACKET_INIT = CTRL_PACKET_FIRST, //!< 16: init packet
|
|
CTRL_PACKET_POKE, //!< 17: firewall poke packet
|
|
CTRL_PACKET_CONN, //!< 18: connection confirmation packet
|
|
CTRL_PACKET_KEEP, //!< 19: keep-alive packet
|
|
CTRL_PACKET_DISC, //!< 20: connection terminated packet
|
|
|
|
CTRL_PACKET_LAST = 63 //!< 63: end of packet code range
|
|
};
|
|
|
|
//! size of sequence set
|
|
#define SEQN_SIZE (64)
|
|
//! sequence set mask
|
|
#define SEQN_MASK (SEQN_SIZE-1)
|
|
|
|
//! base of unreliable sequence set
|
|
#define UNRELSEQN_BASE (64)
|
|
//! base of reliable sequence set
|
|
#define RELSEQN_BASE (128)
|
|
//! base of reliable packet reception acknowledgement sequence set
|
|
#define RELSEQNACK_BASE (192)
|
|
|
|
//! rate at which to resend unacknoweldged packets
|
|
#define RELIABLE_RESEND_RATE (250)
|
|
|
|
//! percentage of packets in receive buffer reserved for reliable packets (divisor)
|
|
#define RELIABLE_PCT_RESERVED (8)
|
|
|
|
|
|
/*** Macros ****************************************************************************/
|
|
|
|
/*** Type Definitions ******************************************************************/
|
|
|
|
//! raw protocol packet format
|
|
/*! Notes:
|
|
The RawSRPPacket code field looks like this:
|
|
|
|
value(s) meaning
|
|
~~~~~~~~~~~~~~~~~~~~~~
|
|
0 reserved
|
|
1-15 bundled packet count
|
|
16 ctrl packet: init connection (CTRL_PACKET_INIT)
|
|
17 ctrl packet: poke firewall (CTRL_PACKET_POKE)
|
|
18 ctrl packet: connection established (CTRL_PACKET_CONN)
|
|
19 ctrl packet: keep-alive (CTRL_PACKET_KEEP)
|
|
20 ctrl packet: disconnect (CTRL_PACKET_DISC)
|
|
21-63 reserved
|
|
64-127 unreliable packet: sequence number is code-64
|
|
128-191 reliable packet: sequence number is code-128
|
|
192-255 reliable packet acknowledgement: sequence number is code-192
|
|
~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
If the code field is 1-15, then the value represents the number of packets
|
|
bundled together into the same UDP frame minus one (a value of one is
|
|
unsupported, as it would simply make the single packet bulkier). The
|
|
bundle format looks as follows:
|
|
|
|
offset value meaning
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
0 code bundle count-1 (1-15)
|
|
|
|
1 len0-1 length of first bundled packet
|
|
2 code0 code of first packet
|
|
3 data0 data for first packet
|
|
|
|
4+len0 len1-1 length of second bundled packet
|
|
5+len0 code1 code of second packet
|
|
6+len0 data1 data for second packet
|
|
|
|
[...]
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
*/
|
|
|
|
typedef struct
|
|
{
|
|
//! packet header which is not sent/received
|
|
//! (this data is used internally)
|
|
struct
|
|
{
|
|
uint32_t when; //!< tick when a packet was received
|
|
uint32_t len; //!< size of packet
|
|
} head;
|
|
//! packet body which is sent/received
|
|
struct
|
|
{
|
|
unsigned char code; //!< packet code (see packet codes above)
|
|
unsigned char data[1]; //!< packet user data (variable length)
|
|
} body;
|
|
} RawSRPPacketT;
|
|
|
|
|
|
//! private module storage
|
|
struct CommSRPRef
|
|
{
|
|
//! common header
|
|
CommRef common;
|
|
//! linked list of all instances
|
|
CommSRPRef *link;
|
|
//! comm socket
|
|
SocketT *socket;
|
|
//! peer address
|
|
struct sockaddr peeraddr;
|
|
|
|
//! port state
|
|
enum {
|
|
ST_IDLE, //!< no connection
|
|
ST_CONN, //!< connecting
|
|
ST_LIST, //!< listening
|
|
ST_OPEN, //!< connection established
|
|
ST_CLOSE //!< connection closed
|
|
} state;
|
|
|
|
//! identifier to keep from getting spoofed
|
|
uint32_t connident;
|
|
|
|
//! width of receive records (same as width of send)
|
|
int32_t rcvwid;
|
|
//! number of packets reserved for reliable transport
|
|
int32_t rcvrelresv;
|
|
//! length of receive buffer (multiple of rcvwid)
|
|
int32_t rcvlen;
|
|
//! fifo input offset
|
|
int32_t rcvinp;
|
|
//! fifo output offset
|
|
int32_t rcvout;
|
|
//! pointer to buffer storage
|
|
char *rcvbuf;
|
|
|
|
//! width of send record (same as width of receive)
|
|
int32_t sndwid;
|
|
//! length of send buffer (multiple of sndwid)
|
|
int32_t sndlen;
|
|
//! fifo input offset
|
|
int32_t sndinp;
|
|
//! fifo output offset
|
|
int32_t sndout;
|
|
//! pointer to buffer storage
|
|
char *sndbuf;
|
|
|
|
//! tick at which last packet was sent
|
|
uint32_t sendtick;
|
|
//! tick at which last packet was received
|
|
uint32_t recvtick;
|
|
|
|
//! unreliable sequence number
|
|
uint32_t unrelseqn;
|
|
//! unreliable sequence number
|
|
uint32_t unrelrecvseqn;
|
|
//! reliable sequence number
|
|
uint32_t relseqn;
|
|
//! reliable received sequence number
|
|
uint32_t relrecvseqn;
|
|
|
|
//! allow for dns lookup
|
|
uint32_t dnsid;
|
|
char dnsquery[256];
|
|
char *dnsbuf;
|
|
int32_t dnslen;
|
|
char dnsdiv;
|
|
|
|
//! semaphore to synchronize thread access
|
|
NetCritT crit;
|
|
//! callback synchronization
|
|
volatile int32_t callback;
|
|
//! indcate pending event
|
|
int32_t gotevent;
|
|
//! callback routine pointer
|
|
void (*callproc)(CommRef *ref, int32_t event);
|
|
};
|
|
|
|
/*** Function Prototypes ***************************************************************/
|
|
|
|
/*** Variables *************************************************************************/
|
|
|
|
// Private variables
|
|
|
|
// Public variables
|
|
|
|
|
|
/*** Private Functions *****************************************************************/
|
|
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function _CommSRPSetAddrInfo
|
|
|
|
\Description
|
|
Sets peer/host addr/port info in common ref.
|
|
|
|
\Input *ref - reference pointer
|
|
\Input sin - address pointer
|
|
|
|
\Version 04/16/04 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
static void _CommSRPSetAddrInfo(CommSRPRef *ref, struct sockaddr *sin)
|
|
{
|
|
struct sockaddr SockAddr;
|
|
|
|
// save peer addr/port info in common ref
|
|
ref->common.peerip = SockaddrInGetAddr(sin);
|
|
ref->common.peerport = SockaddrInGetPort(sin);
|
|
|
|
// save host addr/port info in common ref
|
|
SocketInfo(ref->socket, 'bind', 0, &SockAddr, sizeof(SockAddr));
|
|
ref->common.hostip = SocketGetLocalAddr();
|
|
ref->common.hostport = SockaddrInGetPort(&SockAddr);
|
|
|
|
// debug output
|
|
NetPrintf(("commsrp: peer=0x%08x:%d, host=0x%08x:%d\n",
|
|
ref->common.peerip,
|
|
ref->common.peerport,
|
|
ref->common.hostip,
|
|
ref->common.hostport));
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function _CommSRPSetSocket
|
|
|
|
\Description
|
|
Sets socket in ref and socketref in common portion of ref.
|
|
|
|
\Input *pRef - reference pointer
|
|
\Input *pSocket - socket to set
|
|
|
|
\Version 08/24/04 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
static void _CommSRPSetSocket(CommSRPRef *pRef, SocketT *pSocket)
|
|
{
|
|
pRef->socket = pSocket;
|
|
pRef->common.sockptr = pSocket;
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function _CommSRPResetTransfer
|
|
|
|
\Description
|
|
Reset the transfer state
|
|
|
|
\Input *ref - reference pointer
|
|
|
|
\Version 01/03/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
static void _CommSRPResetTransfer(CommSRPRef *ref)
|
|
{
|
|
// reset the send queue
|
|
ref->sndinp = 0;
|
|
ref->sndout = 0;
|
|
|
|
// reset the receive queue
|
|
ref->rcvinp = 0;
|
|
ref->rcvout = 0;
|
|
|
|
// make sendtick really old (in protocol terms)
|
|
ref->sendtick = NetTick()-5000;
|
|
|
|
// start reliable received sequence at an invalid sequence number
|
|
ref->relrecvseqn = (uint32_t)-1;
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function _CommSRPSendImmediate
|
|
|
|
\Description
|
|
Send an packet to the peer.
|
|
|
|
\Input *ref - reference pointer
|
|
\Input *packet - packet pointer
|
|
|
|
\Output
|
|
int32_t - negative=error, else amount of data sent
|
|
|
|
\Version 01/07/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
static int32_t _CommSRPSendImmediate(CommSRPRef *ref, RawSRPPacketT *packet)
|
|
{
|
|
int32_t len, err;
|
|
|
|
#if ESC_CAUSES_LOSS
|
|
// lose packets when escape is pressed
|
|
if (GetAsyncKeyState(VK_ESCAPE) < 0)
|
|
{
|
|
ref->sendtick = NetTick();
|
|
return(0);
|
|
}
|
|
#endif
|
|
|
|
// figure out amount to send
|
|
len = sizeof(packet->body)-sizeof(packet->body.data); // packet framing
|
|
len += packet->head.len; // variable data
|
|
|
|
#if COMMSRP_VERBOSE
|
|
{
|
|
char addrbuf[32];
|
|
|
|
SockaddrInGetAddrText(&ref->peeraddr,addrbuf,sizeof(addrbuf)),
|
|
NetPrintf(("_CommSRPSendImmediate: Sending %d bytes to %s:%d\n",len, addrbuf,
|
|
SockaddrInGetPort(&ref->peeraddr)));
|
|
}
|
|
#endif
|
|
|
|
// send some data
|
|
err = SocketSendto(ref->socket,(char *)&packet->body,len,0,
|
|
&ref->peeraddr,sizeof(ref->peeraddr));
|
|
|
|
// check for send failure
|
|
if (err != len)
|
|
{
|
|
NetPrintf(("_CommSRPSendImmediate: SocketSendto returned %d\n", err));
|
|
return(-1);
|
|
}
|
|
|
|
// update last send time
|
|
ref->sendtick = NetTick();
|
|
ref->common.datasent += len;
|
|
ref->common.packsent += 1;
|
|
|
|
return(len);
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function _CommSRPSendCtrl
|
|
|
|
\Description
|
|
Send a control packet.
|
|
|
|
\Input *ref - reference pointer
|
|
\Input code - control code to send
|
|
|
|
\Notes
|
|
Control packets are sent unreliably.
|
|
|
|
\Version 01/08/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
static void _CommSRPSendCtrl(CommSRPRef *ref, unsigned char code)
|
|
{
|
|
RawSRPPacketT packet;
|
|
|
|
#if COMMSRP_VERBOSE
|
|
NetPrintf(("_CommSRPSendCtrl: Sending control packet id %d\n", code));
|
|
#endif
|
|
|
|
packet.head.len = 0;
|
|
packet.body.code = code;
|
|
|
|
_CommSRPSendImmediate(ref, &packet);
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function _CommSRPProcessInitRequest
|
|
|
|
\Description
|
|
Send an INIT control packet if we know our peer.
|
|
|
|
\Input *ref - reference pointer
|
|
|
|
\Version 01/08/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
static void _CommSRPProcessInitRequest(CommSRPRef *ref)
|
|
{
|
|
// see if we know our peer
|
|
if ((SockaddrInGetAddr(&ref->peeraddr) == 0) || (SockaddrInGetPort(&ref->peeraddr) == 0))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// send connect message once a second
|
|
if ((NetTick() - ref->sendtick) >= 1000)
|
|
{
|
|
if (ref->state == ST_CONN)
|
|
{
|
|
// send connection initiation packet
|
|
_CommSRPSendCtrl(ref, CTRL_PACKET_INIT);
|
|
}
|
|
else if (ref->state == ST_LIST)
|
|
{
|
|
// send poke packet
|
|
_CommSRPSendCtrl(ref, CTRL_PACKET_POKE);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function _CommSRPProcessACK
|
|
|
|
\Description
|
|
Process a reliable packet acknowledgement
|
|
|
|
\Input *ref - reference pointer
|
|
\Input *packet - ack packet
|
|
|
|
\Version 01/08/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
static void _CommSRPProcessACK(CommSRPRef *ref, RawSRPPacketT *packet)
|
|
{
|
|
uint32_t ackseqnid, refseqnid;
|
|
RawSRPPacketT *refpkt;
|
|
|
|
// get a pointer to packet this should be an ack for
|
|
refpkt = (RawSRPPacketT *)&ref->sndbuf[ref->sndout];
|
|
|
|
// decode sequence ids
|
|
ackseqnid = packet->body.code - RELSEQNACK_BASE;
|
|
refseqnid = refpkt->body.code - RELSEQN_BASE;
|
|
|
|
// compare sequence ids
|
|
if (ackseqnid == refseqnid)
|
|
{
|
|
// packet was successfully transmitted, so dequeue it
|
|
ref->sndout = (ref->sndout+ref->sndwid)%ref->sndlen;
|
|
|
|
#if COMMSRP_VERBOSE
|
|
NetPrintf(("_CommSRPProcessACK: Success: confirmed delivery of packet %d\n",
|
|
refseqnid));
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#if COMMSRP_VERBOSE
|
|
NetPrintf(("_CommSRPProcessACK: Got old ack %d, wanted ack %d\n",
|
|
ackseqnid,refseqnid));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function _CommSRPProcessCtrl
|
|
|
|
\Description
|
|
Process an incoming control message.
|
|
|
|
\Input *ref - reference pointer
|
|
\Input *packet - control packet to process
|
|
\Input *pFrom - sender's socket address
|
|
|
|
\Version 01/07/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
static void _CommSRPProcessCtrl(CommSRPRef *ref, RawSRPPacketT *packet, struct sockaddr *pFrom)
|
|
{
|
|
// update valid receive time
|
|
// must put into past to avoid race condition
|
|
ref->recvtick = NetTick()-1000;
|
|
|
|
switch(packet->body.code)
|
|
{
|
|
// response to connection request packet
|
|
case CTRL_PACKET_INIT:
|
|
{
|
|
#if COMMSRP_VERBOSE
|
|
NetPrintf(("_CommSRPProcessSetup: Received INIT\n"));
|
|
#endif
|
|
|
|
if (ref->state == ST_LIST)
|
|
{
|
|
// set the peer
|
|
ds_memcpy_s(&ref->peeraddr, sizeof(ref->peeraddr), pFrom, sizeof(*pFrom));
|
|
|
|
// set peer/host addr/port info
|
|
_CommSRPSetAddrInfo(ref, pFrom);
|
|
|
|
// update state
|
|
ref->state = ST_OPEN;
|
|
}
|
|
|
|
// always send a response
|
|
_CommSRPSendCtrl(ref, CTRL_PACKET_CONN);
|
|
}
|
|
break;
|
|
|
|
// response to poke packet
|
|
case CTRL_PACKET_POKE:
|
|
{
|
|
#if COMMSRP_VERBOSE
|
|
NetPrintf(("_CommSRPProcessSetup: Received POKE\n"));
|
|
#endif
|
|
|
|
if (ref->state == ST_CONN)
|
|
{
|
|
// set the peer
|
|
ds_memcpy_s(&ref->peeraddr, sizeof(ref->peeraddr), pFrom, sizeof(*pFrom));
|
|
}
|
|
}
|
|
break;
|
|
|
|
// response to a connect confirmation
|
|
case CTRL_PACKET_CONN:
|
|
{
|
|
#if COMMSRP_VERBOSE
|
|
NetPrintf(("_CommSRPProcessSetup: Received CONN\n"));
|
|
#endif
|
|
|
|
// change to open if not already there
|
|
if (ref->state == ST_CONN)
|
|
{
|
|
// set peer/host addr/port info
|
|
_CommSRPSetAddrInfo(ref, pFrom);
|
|
|
|
ref->state = ST_OPEN;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// response to disconnect message
|
|
case CTRL_PACKET_DISC:
|
|
{
|
|
#if COMMSRP_VERBOSE
|
|
NetPrintf(("_CommSRPProcessSetup: Received DISC\n"));
|
|
#endif
|
|
|
|
// close the connection
|
|
if (ref->state == ST_OPEN)
|
|
{
|
|
ref->state = ST_CLOSE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
// response to keepalive message
|
|
case CTRL_PACKET_KEEP:
|
|
{
|
|
#if COMMSRP_VERBOSE
|
|
NetPrintf(("_CommSRPProcessSetup: Received KEEP\n"));
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
// this case should not happen
|
|
default:
|
|
{
|
|
NetPrintf(("_CommSRPProcessSetup: Unrecognized control packet type %d\n",packet->body.code));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function _CommSRPProcessData
|
|
|
|
\Description
|
|
Process incoming data packet.
|
|
|
|
\Input *ref - reference pointer
|
|
\Input *packet - incoming packet
|
|
|
|
\Output
|
|
int32_t - <0 = error, 0 = packet discarded (duplicate resend), 1 = packet added to buffer
|
|
|
|
\Version 01/07/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
static int32_t _CommSRPProcessData(CommSRPRef *ref, RawSRPPacketT *packet)
|
|
{
|
|
RawSRPPacketT *buffer;
|
|
|
|
if ((packet->body.code >= UNRELSEQN_BASE) && (packet->body.code < RELSEQN_BASE))
|
|
{
|
|
int32_t queuepos, pktsfree;
|
|
|
|
// calculate the number of free packets in rcvbuf
|
|
queuepos = ((ref->rcvinp+ref->rcvlen)-ref->rcvout)%ref->rcvlen;
|
|
pktsfree = (ref->rcvlen - queuepos)/ref->rcvwid;
|
|
|
|
// see if there is room in buffer for packet (leave extra space for reliable packets)
|
|
if (pktsfree <= ref->rcvrelresv)
|
|
{
|
|
// no room in buffer -- just drop packet
|
|
#if COMMSRP_VERBOSE // (we expect lots of these, so don't spam)
|
|
NetPrintf(("_CommSRPProcessData: Unreliable packet %d discarded due to input buffer overrun.\n",ref->relrecvseqn));
|
|
#endif
|
|
return(-1);
|
|
}
|
|
|
|
#if COMMSRP_VERBOSE
|
|
NetPrintf(("_CommSRPProcessData: Received packet %d\n",packet->body.code - UNRELSEQN_BASE));
|
|
#endif
|
|
|
|
// update unreliably received sequence number
|
|
ref->unrelrecvseqn = packet->body.code - UNRELSEQN_BASE;
|
|
}
|
|
else
|
|
{
|
|
uint32_t uPacketId;
|
|
|
|
// see if room in buffer for packet
|
|
if ((ref->rcvinp+ref->rcvwid)%ref->rcvlen == ref->rcvout)
|
|
{
|
|
// no room in buffer -- just drop packet
|
|
NetPrintf(("_CommSRPProcessData: Reliable packet discarded due to input buffer overrun\n"));
|
|
return(-1);
|
|
}
|
|
|
|
// get packet ID
|
|
uPacketId = (uint32_t)(packet->body.code - RELSEQN_BASE);
|
|
|
|
// is this the packet we're expecting?
|
|
if (uPacketId == ((ref->relrecvseqn+1) % SEQN_SIZE))
|
|
{
|
|
// update reliably received sequence number
|
|
ref->relrecvseqn = uPacketId;
|
|
|
|
#if COMMSRP_VERBOSE
|
|
NetPrintf(("_CommSRPProcessData: Received packet %d\n", ref->relrecvseqn));
|
|
#endif
|
|
}
|
|
else if (uPacketId == ref->relrecvseqn)
|
|
{
|
|
// this is the previously received packet - return TRUE to ack it in case our last ack got lost
|
|
return(1);
|
|
}
|
|
else
|
|
{
|
|
#if COMMSRP_VERBOSE
|
|
NetPrintf(("_CommSRPProcessData: Discarding duplicate packet %d\n",ref->relrecvseqn));
|
|
#endif
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
// add the packet to the buffer
|
|
buffer = (RawSRPPacketT *) &ref->rcvbuf[ref->rcvinp];
|
|
ds_memcpy(buffer, packet, ref->rcvwid);
|
|
|
|
// limit receive access for callbacks
|
|
ref->callback += 1;
|
|
// add item to receive buffer
|
|
ref->rcvinp = (ref->rcvinp+ref->rcvwid) % ref->rcvlen;
|
|
// indicate we got an event
|
|
ref->gotevent |= 1;
|
|
// let the callback process it
|
|
if (ref->common.RecvCallback != NULL)
|
|
{
|
|
ref->common.RecvCallback((CommRef *)ref, buffer->body.data, buffer->head.len, buffer->head.when);
|
|
}
|
|
// release access to receive
|
|
ref->callback -= 1;
|
|
return(1);
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function _CommSRPProcessAlive
|
|
|
|
\Description
|
|
Send out a keep-alive packet (CTRL_PACKET_KEEP) periodically
|
|
|
|
\Input *ref - reference pointer
|
|
|
|
\Version 01/07/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
static void _CommSRPProcessAlive(CommSRPRef *ref)
|
|
{
|
|
if ((ref->state == ST_OPEN) && (ref->sndinp == ref->sndout))
|
|
{
|
|
// see if time has elapsed
|
|
if (NetTick() > ref->sendtick+5000)
|
|
{
|
|
// queue up a keepalive packet
|
|
_CommSRPSendCtrl(ref, CTRL_PACKET_KEEP);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function _CommSRPProcessRecvQueue
|
|
|
|
\Description
|
|
Attempt to read data from peer and add it to the receive queue.
|
|
|
|
\Input *ref - reference pointer
|
|
|
|
\Version 01/08/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
static void _CommSRPProcessRecvQueue(CommSRPRef *ref)
|
|
{
|
|
int32_t len, sinlen, iResult = 0;
|
|
char *buffer;
|
|
struct sockaddr sin;
|
|
RawSRPPacketT *packet;
|
|
|
|
while((ref->state >= ST_CONN) && (ref->state <= ST_OPEN) && (iResult >= 0))
|
|
{
|
|
// get pointer to recv queue
|
|
packet = (RawSRPPacketT *) &ref->rcvbuf[ref->rcvinp];
|
|
buffer = (char *) &packet->body;
|
|
|
|
// attempt to get a packet
|
|
sinlen = sizeof(sin);
|
|
len = SocketRecvfrom(ref->socket, buffer, ref->rcvwid, 0, &sin, &sinlen);
|
|
|
|
// if we got data add it to recv queue
|
|
if (len > 0)
|
|
{
|
|
#if COMMSRP_VERBOSE
|
|
NetPrintf(("_CommSRPProcessRecvQueue: Received %d bytes of data\n",len));
|
|
#endif
|
|
|
|
packet->head.len = len;
|
|
packet->head.when = SockaddrInGetMisc(&sin);
|
|
|
|
// process packet
|
|
if ((packet->body.code >= CTRL_PACKET_FIRST) && (packet->body.code <= CTRL_PACKET_LAST))
|
|
{
|
|
// handle control packets
|
|
_CommSRPProcessCtrl(ref, packet, &sin);
|
|
}
|
|
else if (packet->body.code >= RELSEQNACK_BASE)
|
|
{
|
|
// handle ack of reliable packet
|
|
_CommSRPProcessACK(ref, packet);
|
|
}
|
|
else
|
|
{
|
|
// handle data packets
|
|
iResult = _CommSRPProcessData(ref, packet);
|
|
if (iResult == 1)
|
|
{
|
|
// check to see if this is a reliable packet and requires an acknowledgement
|
|
if ((packet->body.code >= RELSEQN_BASE) && (packet->body.code < RELSEQNACK_BASE))
|
|
{
|
|
// send an ack (seqid +64)
|
|
#if COMMSRP_VERBOSE
|
|
NetPrintf(("_CommSRPProcessRecvQueue: Sending ack for packet %d\n",packet->body.code));
|
|
#endif
|
|
|
|
_CommSRPSendCtrl(ref,packet->body.code + SEQN_SIZE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (len < 0)
|
|
{
|
|
// error in recv - close connection
|
|
NetPrintf(("_CommSRPProcessRecvQueue: Error %d - closing connection\n",len));
|
|
ref->state = ST_CLOSE;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// no data to receive
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function _CommSRPProcessSendQueue
|
|
|
|
\Description
|
|
Send data to peer from send queue.
|
|
|
|
\Input *ref - reference pointer
|
|
|
|
\Version 01/08/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
static void _CommSRPProcessSendQueue(CommSRPRef *ref)
|
|
{
|
|
RawSRPPacketT *packet;
|
|
|
|
if ((ref->state == ST_OPEN) && (ref->sndinp != ref->sndout))
|
|
{
|
|
// ref next packet to send
|
|
packet = (RawSRPPacketT *) &ref->sndbuf[ref->sndout];
|
|
|
|
// send packet
|
|
if ((packet->head.when == 0) || ((NetTick() - packet->head.when) >= RELIABLE_RESEND_RATE))
|
|
{
|
|
#if COMMSRP_VERBOSE
|
|
if (packet->head.when != 0)
|
|
{
|
|
NetPrintf(("_CommSRPProcessSendQueue: Resending sequence id %d\n",
|
|
packet->body.code - RELSEQN_BASE));
|
|
}
|
|
#endif
|
|
|
|
#if COMMSRP_VERBOSE
|
|
if (packet->head.when == 0)
|
|
{
|
|
NetPrintf(("_CommSRPProcessSendQueue: Sending sequence id %d\n",
|
|
packet->body.code - RELSEQN_BASE));
|
|
}
|
|
#endif
|
|
|
|
// send the packet
|
|
if (_CommSRPSendImmediate(ref, packet) < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// update sent tick
|
|
packet->head.when = NetTick();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function _CommSRPClose
|
|
|
|
\Description
|
|
Close the connection
|
|
|
|
\Input *ref - reference pointer
|
|
|
|
\Output
|
|
int32_t - zero
|
|
|
|
\Version 01/03/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
static int32_t _CommSRPClose(CommSRPRef *ref)
|
|
{
|
|
// see if we are even connected
|
|
if (ref->state != ST_OPEN)
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
// send a disconnect message
|
|
_CommSRPSendCtrl(ref, CTRL_PACKET_DISC);
|
|
|
|
// set to disconnect state
|
|
ref->connident = 0;
|
|
ref->state = ST_CLOSE;
|
|
return(0);
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function _CommSRPEvent0
|
|
|
|
\Description
|
|
Private socket callback function for CommSRP event processing.
|
|
|
|
\Input *ref - reference pointer
|
|
|
|
\Notes
|
|
This function should only be called by _CommSRPEvent()
|
|
|
|
\Version 01/08/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
static void _CommSRPEvent0(CommSRPRef *ref)
|
|
{
|
|
// send init request if we're connecting
|
|
if ((ref->state == ST_CONN) || (ref->state == ST_LIST))
|
|
{
|
|
_CommSRPProcessInitRequest(ref);
|
|
}
|
|
|
|
// receive data into recv queue
|
|
_CommSRPProcessRecvQueue(ref);
|
|
|
|
// periodically send a keep-alive
|
|
_CommSRPProcessAlive(ref);
|
|
|
|
// send all queued data
|
|
_CommSRPProcessSendQueue(ref);
|
|
|
|
// do callback if needed
|
|
if ((ref->callback == 0) && (ref->gotevent != 0))
|
|
{
|
|
// limit callback access
|
|
ref->callback += 1;
|
|
// notify user
|
|
if (ref->callproc != NULL)
|
|
{
|
|
ref->callproc((CommRef *)ref, ref->gotevent);
|
|
}
|
|
// limit callback access
|
|
ref->callback -= 1;
|
|
// done with event
|
|
ref->gotevent = 0;
|
|
}
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function _CommSRPEvent
|
|
|
|
\Description
|
|
Socket callback function for CommSRP event processing, protected
|
|
with critical section.
|
|
|
|
\Input *sock - unused
|
|
\Input flags - unused
|
|
\Input *_ref - reference pointer
|
|
|
|
\Output
|
|
int32_t - zero
|
|
|
|
\Version 01/08/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
static int32_t _CommSRPEvent(SocketT *sock, int32_t flags, void *_ref)
|
|
{
|
|
CommSRPRef *ref = _ref;
|
|
|
|
// see if we have exclusive access
|
|
if (NetCritTry(&ref->crit))
|
|
{
|
|
// update
|
|
_CommSRPEvent0(ref);
|
|
|
|
// free access
|
|
NetCritLeave(&ref->crit);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function _CommSRPSendQueued
|
|
|
|
\Description
|
|
Add packet to send queue.
|
|
|
|
\Input *ref - reference pointer
|
|
\Input *packet - packet to add to send queue
|
|
|
|
\Output
|
|
int32_t - buffer depth
|
|
|
|
\Notes
|
|
Only reliable packets should be added to the send queue; use _CommSRPSendImmediate() for
|
|
unreliable packet data.
|
|
|
|
\Version 01/08/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
static int32_t _CommSRPSendQueued(CommSRPRef *ref, RawSRPPacketT *packet)
|
|
{
|
|
int32_t pos;
|
|
|
|
// mark timestamp=0 for immediate send
|
|
packet->head.when = 0;
|
|
|
|
// add the packet to the queue
|
|
ref->sndinp = (ref->sndinp+ref->sndwid) % ref->sndlen;
|
|
|
|
// try and send immediately
|
|
_CommSRPEvent(ref->socket, 0, ref);
|
|
|
|
// return buffer depth
|
|
pos = (((ref->sndinp+ref->sndlen)-ref->sndout)%ref->sndlen)/ref->sndwid;
|
|
return(pos);
|
|
}
|
|
|
|
|
|
/*** Public Functions ******************************************************************/
|
|
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function CommSRPConstruct
|
|
|
|
\Description
|
|
Construct the class
|
|
|
|
\Input maxwid - max record width
|
|
\Input maxinp - input packet buffer size
|
|
\Input maxout - output packet buffer size
|
|
|
|
\Output
|
|
CommSRPRef - reference pointer
|
|
|
|
\Notes
|
|
Initialized winsock for first class. also creates linked
|
|
list of all current instances of the class and worker thread
|
|
to do most udp stuff.
|
|
|
|
\Version 01/03/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
CommSRPRef *CommSRPConstruct(int32_t maxwid, int32_t maxinp, int32_t maxout)
|
|
{
|
|
CommSRPRef *ref;
|
|
int32_t iMemGroup;
|
|
void *pMemGroupUserData;
|
|
|
|
// Query current mem group data
|
|
DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData);
|
|
|
|
// allocate class storage
|
|
ref = DirtyMemAlloc(sizeof(*ref), COMMSRP_MEMID, iMemGroup, pMemGroupUserData);
|
|
if (ref == NULL)
|
|
return(NULL);
|
|
ds_memclr(ref, sizeof(*ref));
|
|
ref->common.memgroup = iMemGroup;
|
|
ref->common.memgrpusrdata = pMemGroupUserData;
|
|
|
|
// initialize the callback routines
|
|
ref->common.Construct = (CommAllConstructT *)CommSRPConstruct;
|
|
ref->common.Destroy = (CommAllDestroyT *)CommSRPDestroy;
|
|
ref->common.Resolve = (CommAllResolveT *)CommSRPResolve;
|
|
ref->common.Unresolve = (CommAllUnresolveT *)CommSRPUnresolve;
|
|
ref->common.Listen = (CommAllListenT *)CommSRPListen;
|
|
ref->common.Unlisten = (CommAllUnlistenT *)CommSRPUnlisten;
|
|
ref->common.Connect = (CommAllConnectT *)CommSRPConnect;
|
|
ref->common.Unconnect = (CommAllUnconnectT *)CommSRPUnconnect;
|
|
ref->common.Callback = (CommAllCallbackT *)CommSRPCallback;
|
|
ref->common.Status = (CommAllStatusT *)CommSRPStatus;
|
|
ref->common.Tick = (CommAllTickT *)CommSRPTick;
|
|
ref->common.Send = (CommAllSendT *)CommSRPSend;
|
|
ref->common.Peek = (CommAllPeekT *)CommSRPPeek;
|
|
ref->common.Recv = (CommAllRecvT *)CommSRPRecv;
|
|
|
|
// remember max packet width
|
|
ref->common.maxwid = maxwid;
|
|
ref->common.maxinp = maxinp;
|
|
ref->common.maxout = maxout;
|
|
|
|
// reset access to shared resources
|
|
NetCritInit(&ref->crit, "commsrp");
|
|
|
|
// allocate the buffers
|
|
ref->rcvwid = sizeof(RawSRPPacketT)-sizeof(((RawSRPPacketT *)0)->body.data)+maxwid;
|
|
ref->rcvwid = (ref->rcvwid+3) & 0x7ffc;
|
|
ref->rcvlen = ref->rcvwid * maxinp;
|
|
ref->rcvbuf = DirtyMemAlloc(ref->rcvlen, COMMSRP_MEMID, ref->common.memgroup, ref->common.memgrpusrdata);
|
|
ref->sndwid = sizeof(RawSRPPacketT)-sizeof(((RawSRPPacketT *)0)->body.data)+maxwid;
|
|
ref->sndwid = (ref->sndwid+3) & 0x7ffc;
|
|
ref->sndlen = ref->sndwid * maxout;
|
|
ref->sndbuf = DirtyMemAlloc(ref->sndlen, COMMSRP_MEMID, ref->common.memgroup, ref->common.memgrpusrdata);
|
|
|
|
// calculate number of packets reserved for reliable transport
|
|
ref->rcvrelresv = maxinp/RELIABLE_PCT_RESERVED;
|
|
|
|
// reset the socket
|
|
_CommSRPSetSocket(ref, NULL);
|
|
|
|
// reset the state
|
|
ref->state = ST_IDLE;
|
|
ref->connident = 0;
|
|
|
|
// return the reference
|
|
return(ref);
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function CommSRPDestroy
|
|
|
|
\Description
|
|
Destruct the class
|
|
|
|
\Input *ref - reference pointer
|
|
|
|
\Version 01/03/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
void CommSRPDestroy(CommSRPRef *ref)
|
|
{
|
|
// if port is open, close it
|
|
if (ref->state == ST_OPEN)
|
|
{
|
|
_CommSRPClose(ref);
|
|
}
|
|
|
|
// kill the socket
|
|
if (ref->socket != NULL)
|
|
{
|
|
SocketClose(ref->socket);
|
|
_CommSRPSetSocket(ref, NULL);
|
|
}
|
|
|
|
// get rid of sockets critical section
|
|
NetCritKill(&ref->crit);
|
|
|
|
// release resources
|
|
DirtyMemFree(ref->rcvbuf, COMMSRP_MEMID, ref->common.memgroup, ref->common.memgrpusrdata);
|
|
DirtyMemFree(ref->sndbuf, COMMSRP_MEMID, ref->common.memgroup, ref->common.memgrpusrdata);
|
|
DirtyMemFree(ref, COMMSRP_MEMID, ref->common.memgroup, ref->common.memgrpusrdata);
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function CommSRPCallback
|
|
|
|
\Description
|
|
Set upper layer callback
|
|
|
|
\Input *ref - reference pointer
|
|
\Input *callback - socket generating callback
|
|
|
|
\Version 01/03/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
void CommSRPCallback(CommSRPRef *ref, void (*callback)(CommRef *ref, int32_t event))
|
|
{
|
|
ref->callproc = callback;
|
|
ref->gotevent |= 2;
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function CommSRPResolve
|
|
|
|
\Description
|
|
Resolve an address (unimplemented)
|
|
|
|
\Input *ref - endpoint
|
|
\Input *addr - resolve address
|
|
\Input *buf - target buffer
|
|
\Input len - target length (min 64 bytes)
|
|
\Input div - divider char
|
|
|
|
\Output
|
|
int32_t - <0=error, 0=complete (COMM_NOERROR), >0=in progress (COMM_PENDING)
|
|
|
|
\Notes
|
|
Target list is always double null terminated allowing null
|
|
to be used as the divider character if desired. when COMM_PENDING
|
|
is returned, target buffer is set to "~" until completion
|
|
|
|
\Version 01/03/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
int32_t CommSRPResolve(CommSRPRef *ref, const char *addr, char *buf, int32_t len, char div)
|
|
{
|
|
NetPrintf(("commsrp: resolve functionality not supported\n"));
|
|
return(-1);
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function CommSRPUnresolve
|
|
|
|
\Description
|
|
Stop the resolver
|
|
|
|
\Input *ref - endpoint ref
|
|
|
|
\Version 01/03/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
void CommSRPUnresolve(CommSRPRef *ref)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function CommSRPUnlisten
|
|
|
|
\Description
|
|
Stop listening.
|
|
|
|
\Input *ref - reference pointer
|
|
|
|
\Output
|
|
int32_t - negative=error, zero=ok
|
|
|
|
\Version 01/03/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
int32_t CommSRPUnlisten(CommSRPRef *ref)
|
|
{
|
|
// get rid of socket if presernt
|
|
if (ref->socket != NULL)
|
|
{
|
|
SocketClose(ref->socket);
|
|
_CommSRPSetSocket(ref, NULL);
|
|
}
|
|
|
|
// return to idle mode
|
|
ref->state = ST_IDLE;
|
|
return(0);
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function CommSRPUnconnect
|
|
|
|
\Description
|
|
Terminate a connection
|
|
|
|
\Input *ref - reference pointer
|
|
|
|
\Output
|
|
int32_t - negative=error, zero=ok
|
|
|
|
\Version 01/03/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
int32_t CommSRPUnconnect(CommSRPRef *ref)
|
|
{
|
|
// get rid of socket if presernt
|
|
if (ref->socket != NULL)
|
|
{
|
|
SocketClose(ref->socket);
|
|
_CommSRPSetSocket(ref, NULL);
|
|
}
|
|
|
|
// return to idle mode
|
|
ref->state = ST_IDLE;
|
|
return(0);
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function CommSRPStatus
|
|
|
|
\Description
|
|
Return current stream status
|
|
|
|
\Input *ref - reference pointer
|
|
|
|
\Output
|
|
int32_t - COMM_CONNECTING, COMM_OFFLINE, COMM_ONLINE or COMM_FAILURE
|
|
|
|
\Version 01/03/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
int32_t CommSRPStatus(CommSRPRef *ref)
|
|
{
|
|
// return state
|
|
if ((ref->state == ST_CONN) || (ref->state == ST_LIST))
|
|
return(COMM_CONNECTING);
|
|
if ((ref->state == ST_IDLE) || (ref->state == ST_CLOSE))
|
|
return(COMM_OFFLINE);
|
|
if (ref->state == ST_OPEN)
|
|
return(COMM_ONLINE);
|
|
return(COMM_FAILURE);
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function CommSRPTick
|
|
|
|
\Description
|
|
Return current tick
|
|
|
|
\Input *ref - reference pointer
|
|
|
|
\Output
|
|
uint32_t - elapsed milliseconds
|
|
|
|
\Version 01/03/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
uint32_t CommSRPTick(CommSRPRef *ref)
|
|
{
|
|
return(NetTick());
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function CommSRPSend
|
|
|
|
\Description
|
|
Send a packet
|
|
|
|
\Input *ref - reference pointer
|
|
\Input *buffer - pointer to data
|
|
\Input length - length of data
|
|
\Input flags - COMM_FLAGS_*
|
|
|
|
\Output
|
|
int32_t - negative=error, zero=buffer full (temp fail), positive=queue position (ok)
|
|
|
|
\Version 01/03/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
int32_t CommSRPSend(CommSRPRef *ref, const void *buffer, int32_t length, uint32_t flags)
|
|
{
|
|
RawSRPPacketT *packet;
|
|
int32_t pos;
|
|
|
|
// make sure port is open
|
|
if (ref->state != ST_OPEN)
|
|
{
|
|
return(COMM_BADSTATE);
|
|
}
|
|
|
|
// see if input queue full
|
|
if ((ref->sndinp+ref->sndwid)%ref->sndlen == ref->sndout)
|
|
{
|
|
NetPrintf(("commsrp: input queue full\n"));
|
|
return(0);
|
|
}
|
|
|
|
// return error for oversized packets
|
|
if (length > (signed)(ref->sndwid-(sizeof(RawSRPPacketT)-sizeof((RawSRPPacketT *)0)->body.data)))
|
|
{
|
|
NetPrintf(("commsrp: oversized packet send (%d bytes)\n", length));
|
|
return(COMM_MINBUFFER);
|
|
}
|
|
|
|
// zero sized packet cannot be sent (they are used for acks)
|
|
// instead, treat them as successful which means the queue
|
|
// position is returned
|
|
if (length == 0)
|
|
{
|
|
pos = (((ref->sndinp+ref->sndlen)-ref->sndout)%ref->sndlen)/ref->sndwid;
|
|
return(pos+1);
|
|
}
|
|
|
|
// add packet to send queue
|
|
packet = (RawSRPPacketT *) &(ref->sndbuf[ref->sndinp]);
|
|
|
|
// set packet length
|
|
packet->head.len = length;
|
|
|
|
// copy user data into queue
|
|
ds_memcpy(packet->body.data, buffer, length);
|
|
|
|
// send reliable or unreliable?
|
|
if (flags & COMM_FLAGS_UNRELIABLE)
|
|
{
|
|
// unreliable sequence tracking
|
|
packet->body.code = (unsigned char)ref->unrelseqn++ + UNRELSEQN_BASE;
|
|
ref->unrelseqn &= SEQN_MASK;
|
|
|
|
#if COMMSRP_VERBOSE
|
|
NetPrintf(("commsrp: sending unreliable sequence number %d (len=%d)\n",packet->body.code-UNRELSEQN_BASE,length));
|
|
#endif
|
|
|
|
// unreliable packets are sent immediately
|
|
if ((pos = _CommSRPSendImmediate(ref,packet)) > 0)
|
|
{
|
|
pos = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// reliable sequence tracking
|
|
packet->body.code = (unsigned char)ref->relseqn++ + RELSEQN_BASE;
|
|
ref->relseqn &= SEQN_MASK;
|
|
|
|
#if COMMSRP_VERBOSE
|
|
NetPrintf(("CommSRPSend: Sending reliable sequence number %d (len=%d)\n",packet->body.code-RELSEQN_BASE,length));
|
|
#endif
|
|
|
|
// reliable packets are added to the send queue
|
|
pos = _CommSRPSendQueued(ref,packet);
|
|
}
|
|
|
|
// return buffer depth
|
|
return((pos > 0) ? pos : 1);
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function CommSRPPeek
|
|
|
|
\Description
|
|
Peek at waiting packet, and copy to target buffer if present.
|
|
|
|
\Input *ref - reference pointer
|
|
\Input *target - target buffer
|
|
\Input length - buffer length
|
|
\Input *when - tick received at
|
|
|
|
\Output
|
|
int32_t - negative=nothing pending, else packet length
|
|
|
|
\Version 01/03/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
int32_t CommSRPPeek(CommSRPRef *ref, void *target, int32_t length, uint32_t *when)
|
|
{
|
|
RawSRPPacketT *packet;
|
|
int32_t packetlen;
|
|
|
|
// see if a packet is available
|
|
if (ref->rcvout == ref->rcvinp)
|
|
{
|
|
return(COMM_NODATA);
|
|
}
|
|
|
|
// point to the packet
|
|
packet = (RawSRPPacketT *) &(ref->rcvbuf[ref->rcvout]);
|
|
|
|
#if COMMSRP_VERBOSE
|
|
NetPrintf(("commsrp: received packet, sequence number %d (len=%d)\n",packet->body.code,packet->head.len));
|
|
#endif
|
|
|
|
// packet length minus code byte
|
|
packetlen = packet->head.len - (sizeof(packet->body)-sizeof(packet->body.data));
|
|
|
|
// copy over the data portion
|
|
ds_memcpy(target, packet->body.data, (packetlen < length ? packetlen : length));
|
|
|
|
// get the timestamp
|
|
if (when != NULL)
|
|
{
|
|
*when = packet->head.when;
|
|
}
|
|
|
|
// return packet data length
|
|
return(packetlen);
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function CommSRPRecv
|
|
|
|
\Description
|
|
Receive a packet from the buffer
|
|
|
|
\Input *ref - reference pointer
|
|
\Input *target - target buffer
|
|
\Input length - buffer length
|
|
\Input *when - tick received at
|
|
|
|
\Output
|
|
int32_t - negative=error, else packet length
|
|
|
|
\Version 01/03/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
int32_t CommSRPRecv(CommSRPRef *ref, void *target, int32_t length, uint32_t *when)
|
|
{
|
|
// use peek to remove the data
|
|
int32_t len = CommSRPPeek(ref, target, length, when);
|
|
|
|
if (len >= 0)
|
|
{
|
|
ref->rcvout = (ref->rcvout+ref->rcvwid)%ref->rcvlen;
|
|
}
|
|
|
|
// all done
|
|
return(len);
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function CommSRPListen
|
|
|
|
\Description
|
|
Listen for a connection
|
|
|
|
\Input *ref - reference pointer
|
|
\Input *addr - port to listen on (only :port portion used)
|
|
|
|
\Output
|
|
int32_t - negative=error, zero=ok
|
|
|
|
\Version 01/03/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
int32_t CommSRPListen(CommSRPRef *ref, const char *addr)
|
|
{
|
|
int32_t err, iListenPort, iConnPort;
|
|
uint32_t uPokeAddr;
|
|
struct sockaddr bindaddr;
|
|
SocketT *sock;
|
|
|
|
// make sure in valid state
|
|
if ((ref->state != ST_IDLE) || (ref->socket != NULL))
|
|
{
|
|
return(COMM_BADSTATE);
|
|
}
|
|
|
|
// setup bind points
|
|
SockaddrInit(&bindaddr, AF_INET);
|
|
|
|
// parse at least port
|
|
if ((SockaddrInParse2(&uPokeAddr, &iListenPort, &iConnPort, addr) & 0x2) != 0x2)
|
|
{
|
|
return(COMM_BADADDRESS);
|
|
}
|
|
SockaddrInSetPort(&bindaddr, iListenPort);
|
|
|
|
// reset to unresolved
|
|
_CommSRPResetTransfer(ref);
|
|
|
|
// create socket in case its needed
|
|
sock = SocketOpen(AF_INET, SOCK_DGRAM, 0);
|
|
_CommSRPSetSocket(ref, sock);
|
|
if (ref->socket == NULL)
|
|
{
|
|
return(COMM_NORESOURCE);
|
|
}
|
|
|
|
// bind locally
|
|
#if COMMSRP_VERBOSE
|
|
NetPrintf(("commsrp: binding to port %d\n", SockaddrInGetPort(&bindaddr)));
|
|
#endif
|
|
if ((err = SocketBind(ref->socket, &bindaddr, sizeof(bindaddr))) < 0)
|
|
{
|
|
NetPrintf(("commsrp: error %d binding socket\n", err));
|
|
SocketClose(ref->socket);
|
|
_CommSRPSetSocket(ref, NULL);
|
|
return(COMM_UNEXPECTED);
|
|
}
|
|
|
|
// setup for callbacks
|
|
SocketCallback(ref->socket, CALLB_RECV, 100, ref, &_CommSRPEvent);
|
|
|
|
// see if we should setup peer address
|
|
if (uPokeAddr != 0)
|
|
{
|
|
if (iConnPort == 0)
|
|
{
|
|
iConnPort = iListenPort+1;
|
|
}
|
|
|
|
SockaddrInit(&ref->peeraddr, AF_INET);
|
|
SockaddrInSetAddr(&ref->peeraddr, uPokeAddr);
|
|
SockaddrInSetPort(&ref->peeraddr, iConnPort);
|
|
}
|
|
|
|
// put into init mode
|
|
ref->state = ST_LIST;
|
|
return(0);
|
|
}
|
|
|
|
/*F*************************************************************************************************/
|
|
/*!
|
|
\Function CommSRPConnect
|
|
|
|
\Description
|
|
Initiate a connection to a peer
|
|
|
|
\Input *ref - reference pointer
|
|
\Input *pAddr - address in ip-address:port form
|
|
|
|
\Output
|
|
int32_t - negative=error, zero=ok
|
|
|
|
\Notes
|
|
Does not perform dns translation
|
|
|
|
\Version 01/03/03 (JLB)
|
|
*/
|
|
/*************************************************************************************************F*/
|
|
int32_t CommSRPConnect(CommSRPRef *ref, const char *pAddr)
|
|
{
|
|
struct sockaddr bindaddr;
|
|
int32_t iErr, iConnPort, iListenPort;
|
|
uint32_t uAddr;
|
|
SocketT *sock;
|
|
|
|
// make sure in valid state
|
|
if ((ref->state != ST_IDLE) || (ref->socket != NULL))
|
|
{
|
|
return(COMM_BADSTATE);
|
|
}
|
|
|
|
// parse address and port from addr string
|
|
if ((SockaddrInParse2(&uAddr, &iListenPort, &iConnPort, pAddr) & 0x3) != 0x3)
|
|
{
|
|
return(COMM_BADADDRESS);
|
|
}
|
|
|
|
// if we don't have an alternate connect port: connect=listen, listen=listen+1
|
|
if (iConnPort == 0)
|
|
{
|
|
iConnPort = iListenPort++;
|
|
}
|
|
|
|
// reset to unresolved
|
|
_CommSRPResetTransfer(ref);
|
|
|
|
// create the actual socket
|
|
sock = SocketOpen(AF_INET, SOCK_DGRAM, 0);
|
|
_CommSRPSetSocket(ref, sock);
|
|
if (ref->socket == NULL)
|
|
{
|
|
return(COMM_NORESOURCE);
|
|
}
|
|
|
|
// set listen port
|
|
SockaddrInit(&bindaddr, AF_INET);
|
|
SockaddrInSetPort(&bindaddr, iListenPort);
|
|
|
|
// bind to port
|
|
#if COMMSRP_VERBOSE
|
|
NetPrintf(("commsrp: binding to port %d\n", iListenPort));
|
|
#endif
|
|
if ((iErr = SocketBind(ref->socket, &bindaddr, sizeof(bindaddr))) < 0)
|
|
{
|
|
NetPrintf(("commsrp: error %d binding socket\n", iErr));
|
|
return(COMM_UNEXPECTED);
|
|
}
|
|
|
|
// setup target info
|
|
SockaddrInit(&ref->peeraddr, AF_INET);
|
|
SockaddrInSetAddr(&ref->peeraddr, uAddr);
|
|
SockaddrInSetPort(&ref->peeraddr, iConnPort);
|
|
|
|
// setup for callbacks
|
|
SocketCallback(ref->socket, CALLB_RECV, 100, ref, &_CommSRPEvent);
|
|
|
|
// change the state
|
|
ref->state = ST_CONN;
|
|
return(0);
|
|
}
|