Kawe Mazidjatari b3a68ed095 Add EABase, EAThread and DirtySDK to R5sdk
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
2024-04-05 18:29:03 +02:00

712 lines
20 KiB
C

/*H*************************************************************************************************/
/*!
\File netgameutil.c
\Description
This module provides the setup required to bring peer-peer networking
online.
\Copyright
Copyright (c) Tiburon Entertainment / Electronic Arts 2001-2002. ALL RIGHTS RESERVED.
\Version 1.0 01/09/01 (GWS) First Version
*/
/*************************************************************************************************H*/
/*** Include files *********************************************************************/
#include <stdio.h>
#include <string.h>
#include "DirtySDK/dirtysock.h"
#include "DirtySDK/dirtysock/dirtymem.h"
#include "DirtySDK/comm/commall.h"
#include "DirtySDK/comm/commudp.h"
#include "DirtySDK/comm/commsrp.h"
#include "DirtySDK/proto/protoadvt.h"
#include "DirtySDK/game/netgameutil.h"
#include "DirtySDK/game/netgamepkt.h"
/*** Defines ***************************************************************************/
/*** Macros ****************************************************************************/
/*** Type Definitions ******************************************************************/
//! netgameutil internal state
struct NetGameUtilRefT
{
//! module memory group
int32_t memgroup;
void *memgrpusrdata;
//! mac->name translation table
char *table;
//! class (unique to app)
char kind[32];
//! service address list (64-->128 7/25/05 to fix ConnApi overrun GWS)
char addr[128];
//! advert ref, for broadcasting hosting info
ProtoAdvtRef *advt;
//! advertising ref, for connecting
ProtoAdvtRef *find;
//! hosting status: 0=hosting, 1=joining
int32_t hosting;
//! host ip
uint32_t hostip;
//! host port
uint32_t hostport;
//! peer ip
uint32_t peerip;
//! peer port
uint32_t peerport;
//! socket ref
SocketT *pSocket;
//! max packet width
int32_t maxwid;
//! size of send buffer in packets
int32_t maxout;
//! size of receive buffer in packets
int32_t maxinp;
//! unacknowledged packet window
int32_t unacklimit;
//! advertising frequency, in seconds
int32_t advtfreq;
//! client identifier (zero == none)
int32_t clientid;
//! remote client identifier
int32_t rclientid;
//! metatype
int32_t metatype;
//! construct function (used for AUTO mode)
CommAllConstructT *pConstruct;
//! commref of connection, or NULL if no connection
CommRef *comm;
uint8_t uLocalAdvt;
};
/*** Function Prototypes ***************************************************************/
/*** Variables *************************************************************************/
// Private variables
// Constants
// Public variables
/*** Private Functions *****************************************************************/
/*F*************************************************************************************/
/*!
\Function _NetGameUtilAdvtConstruct
\Description
Create the ProtoAdvt module.
\Input *pRef - pointer to module state
\Input iSize - ProtoAdvtConstruct() parameter
\Input bConnect - TRUE if constructing advt ref for connecting, FALSE if constructing advt ref for broadcasting hosting info
\Output
ProtoAdvtRefT * - pointer to new advertising module
\Version 10/13/2005 (jbrookes)
*/
/*************************************************************************************F*/
static ProtoAdvtRef *_NetGameUtilAdvtConstruct(NetGameUtilRefT *pRef, int32_t iSize, uint32_t bConnect)
{
ProtoAdvtRef **ppAdvt = bConnect ? &pRef->find : &pRef->advt;
if (*ppAdvt == NULL)
{
DirtyMemGroupEnter(pRef->memgroup, pRef->memgrpusrdata);
*ppAdvt = ProtoAdvtConstruct(iSize);
DirtyMemGroupLeave();
}
return(*ppAdvt);
}
/*** Public Functions ******************************************************************/
/*F*************************************************************************************************/
/*!
\Function NetGameUtilCreate
\Description
Construct the game setup module
\Output
NetGameUtilRefT * - reference pointer
\Version 01/09/01 (GWS)
*/
/*************************************************************************************************F*/
NetGameUtilRefT *NetGameUtilCreate(void)
{
NetGameUtilRefT *pRef;
int32_t iMemGroup;
void *pMemGroupUserData;
// Query current mem group data
DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData);
// allocate and init module state
if ((pRef = DirtyMemAlloc(sizeof(*pRef), NETGAMEUTIL_MEMID, iMemGroup, pMemGroupUserData)) == NULL)
{
NetPrintf(("netgameutil: unable to allocate module state\n"));
return(NULL);
}
ds_memclr(pRef, sizeof(*pRef));
pRef->memgroup = iMemGroup;
pRef->memgrpusrdata = pMemGroupUserData;
// set default comm buffer parameters
NetGameUtilControl(pRef, 'mwid', NETGAME_DATAPKT_DEFSIZE);
NetGameUtilControl(pRef, 'minp', NETGAME_DATABUF_MAXSIZE);
NetGameUtilControl(pRef, 'mout', NETGAME_DATABUF_MAXSIZE);
// return state
return(pRef);
}
/*F*************************************************************************************************/
/*!
\Function NetGameUtilDestroy
\Description
Destroy the game setup module
\Input *ref - reference pointer
\Version 01/09/01 (GWS)
*/
/*************************************************************************************************F*/
void NetGameUtilDestroy(NetGameUtilRefT *ref)
{
// reset
NetGameUtilReset(ref);
// done with local state
DirtyMemFree(ref, NETGAMEUTIL_MEMID, ref->memgroup, ref->memgrpusrdata);
}
/*F*************************************************************************************************/
/*!
\Function NetGameUtilReset
\Description
Reset the game setup module
\Input *ref - reference pointer
\Version 01/09/01 (GWS)
*/
/*************************************************************************************************F*/
void NetGameUtilReset(NetGameUtilRefT *ref)
{
// release comm module
if (ref->comm != NULL)
{
ref->comm->Destroy(ref->comm);
ref->comm = NULL;
}
// kill advertisements
if (ref->find != NULL)
{
ProtoAdvtDestroy(ref->find);
ref->find = NULL;
}
if (ref->advt != NULL)
{
ProtoAdvtDestroy(ref->advt);
ref->advt = NULL;
}
// clear construct ref, if any
ref->pConstruct = NULL;
}
/*F*************************************************************************************************/
/*!
\Function NetGameUtilControl
\Description
Set internal GameUtil parameters.
\Input *pRef - reference pointer
\Input iKind - selector
\Input iValue - value to set
\Notes
Selectors:
\verbatim
'advf': set advertising frequency, in seconds (call before calling NetGameUtilAdvert)
'clid': set client identifier* (for use with gameservers)
'locl': enable or disable query of local adverts
'meta': set metatype (default=0)
'minp': set receive buffer size, in packets*
'mout': set send buffer size, in packets*
'mwid': set maximum packet width (must be <= NETGAME_DATAPKT_MAXSIZE)*
* must be set before NetGameLinkConnect() is called to be effective.
\endverbatim
\Version 11/12/03 (JLB)
*/
/*************************************************************************************************F*/
void NetGameUtilControl(NetGameUtilRefT *pRef, int32_t iKind, int32_t iValue)
{
if (iKind == 'clid')
{
pRef->clientid = iValue;
NetPrintf(("netgameutil: setting clid=0x%08x\n", pRef->clientid));
}
if (iKind == 'rcid')
{
pRef->rclientid = iValue;
NetPrintf(("netgameutil: setting rcid=0x%08x\n", pRef->rclientid));
}
if (iKind == 'locl')
{
pRef->uLocalAdvt = iValue;
NetPrintf(("netgameutil: setting uLocalAdvt=%d\n", pRef->uLocalAdvt));
}
if (iKind == 'meta')
{
pRef->metatype = iValue;
NetPrintf(("netgameutil: setting meta=0x%08x\n", pRef->metatype));
}
if (iKind == 'mwid')
{
if (iValue <= NETGAME_DATAPKT_MAXSIZE)
{
pRef->maxwid = iValue+NETGAME_DATAPKT_MAXTAIL;
NetPrintf(("netgameutil: setting mwid=%d\n", pRef->maxwid));
}
else
{
NetPrintf(("netgameutil: mwid value of %d is too large\n", iValue));
}
}
if (iKind == 'minp')
{
pRef->maxinp = iValue;
NetPrintf(("netgameutil: setting minp=%d\n", pRef->maxinp));
}
if (iKind == 'mout')
{
pRef->maxout = iValue;
NetPrintf(("netgameutil: setting mout=%d\n", pRef->maxout));
}
if (iKind == 'ulmt')
{
pRef->unacklimit = iValue;
NetPrintf(("netgameutil: setting unacklimit=%d\n", pRef->unacklimit));
}
if (iKind == 'advf')
{
pRef->advtfreq = iValue;
NetPrintf(("netgameutil: setting advf=%d\n", pRef->advtfreq));
}
// if selector is unhandled, and a comm func is available, pass it on down
if ((pRef->comm != NULL) && (pRef->comm->Control != NULL))
{
pRef->comm->Control(pRef->comm, iKind, iValue, NULL);
}
}
/*F*************************************************************************************************/
/*!
\Function NetGameUtilConnect
\Description
Establish a connection (connect/listen)
\Input *ref - reference pointer
\Input conn - connect mode (NETGAME_CONN_*) | comm type (NETGAME_CONN_*)
\Input *addr - service address list
\Input *pConstruct - comm construct function
\Output
int32_t - zero=success, negative=failure
\Version 01/09/01 (GWS)
*/
/*************************************************************************************************F*/
int32_t NetGameUtilConnect(NetGameUtilRefT *ref, int32_t conn, const char *addr, CommAllConstructT *pConstruct)
{
int32_t iErr = 0;
// make sure user specified connect and/or listen, and a valid protocol
if (((conn & NETGAME_CONN_AUTO) == 0) || (pConstruct == NULL))
{
NetPrintf(("netgameutil: invalid conn param\n"));
return(-100); // using hundred range to not conflic with COMM_* error codes that iErr can be assigned with.
}
// save the address for later
ds_strnzcpy(ref->addr, addr, sizeof(ref->addr));
// save host/join mode
ref->hosting = ((conn & NETGAME_CONN_CONNECT) ? 1 : 0);
NetPrintf(("netgameutil: connect %d %s\n", conn, addr));
// release previous comm module
if (ref->comm != NULL)
{
ref->comm->Destroy(ref->comm);
}
// handle auto mode
if ((conn & NETGAME_CONN_AUTO) == NETGAME_CONN_AUTO)
{
// save construct ref
ref->pConstruct = pConstruct;
// make sure advertising is running
if (_NetGameUtilAdvtConstruct(ref, 8, TRUE) == NULL)
{
NetPrintf(("netgameutil: NetGameUtilConnect() failed to create the ProtoAdvt module.\n"));
return(-101); // using hundred range to not conflic with COMM_* error codes that iErr can be assigned with.
}
ProtoAdvtAnnounce(ref->find, "GmUtil", addr, "", "TCP:~1:1024\tUDP:~1:1024", 0);
return(0);
}
// mark modules as created by us with our memgroup
DirtyMemGroupEnter(ref->memgroup, ref->memgrpusrdata);
// create comm module
ref->comm = pConstruct(ref->maxwid, ref->maxinp, ref->maxout);
// start connect/listen
if (ref->comm != NULL)
{
if (ref->comm->Control != NULL)
{
ref->comm->Control(ref->comm, 'clid', ref->clientid, NULL);
ref->comm->Control(ref->comm, 'rcid', ref->rclientid, NULL);
ref->comm->Control(ref->comm, 'meta', ref->metatype, NULL);
if (ref->unacklimit != 0)
{
ref->comm->Control(ref->comm, 'ulmt', ref->unacklimit, NULL);
}
}
if (conn & NETGAME_CONN_CONNECT)
{
iErr = ref->comm->Connect(ref->comm, addr);
}
else if (conn & NETGAME_CONN_LISTEN)
{
iErr = ref->comm->Listen(ref->comm, addr);
}
ref->pSocket = ref->comm->sockptr;
// get host ip/port info from commref
ref->hostip = ref->comm->hostip;
ref->hostport = ref->comm->hostport;
}
DirtyMemGroupLeave();
return(iErr);
}
/*F*************************************************************************************************/
/*!
\Function NetGameUtilComplete
\Description
Check for connection complete
\Input *ref - reference pointer
\Output
void * - connection pointer (NULL is no connection)
\Version 01/09/01 (GWS)
*/
/*************************************************************************************************F*/
void *NetGameUtilComplete(NetGameUtilRefT *ref)
{
// see if we are in find mode
if (ref->find != NULL)
{
// if not connecting, see if we can locate someone
if (ref->comm == NULL)
{
char text[256];
uint32_t peer, host;
peer = ProtoAdvtLocate(ref->find, "GmUtil", ref->addr, &host, 0);
if (peer != 0)
{
NetPrintf(("netgameutil: located peer=%08x, host=%08x\n", peer, host));
if (peer > host)
{
ref->hostip = peer;
ref->peerip = host;
ds_snzprintf(text, sizeof(text), "%d.%d.%d.%d%s",
(unsigned char)(peer>>24), (unsigned char)(peer>>16),
(unsigned char)(peer>> 8), (unsigned char)(peer>>0),
ref->addr);
NetGameUtilConnect(ref, NETGAME_CONN_CONNECT, text, ref->pConstruct);
}
else
{
ref->hostip = host;
ref->peerip = peer;
ds_strnzcpy(text, ref->addr, sizeof(text));
NetGameUtilConnect(ref, NETGAME_CONN_LISTEN, text, ref->pConstruct);
}
}
}
}
// check for a connect
if ((ref->comm != NULL) && (ref->comm->Status(ref->comm) == COMM_ONLINE))
{
// get peer ip/port info from commref
ref->peerip = ref->comm->peerip;
ref->peerport = ref->comm->peerport;
// stop any advertising
if (ref->advt != NULL)
{
ProtoAdvtDestroy(ref->advt);
ref->advt = NULL;
}
if (ref->find != NULL)
{
ProtoAdvtDestroy(ref->find);
ref->find = NULL;
}
NetPrintf(("netgameutil: connection complete\n"));
return(ref->comm);
}
else
{
return(NULL);
}
}
/*F*************************************************************************************************/
/*!
\Function NetGameUtilStatus
\Description
Return status info
\Input *ref - reference pointer
\Input iSelect - info selector
\Input *pBuf - [out] output buffer
\Input iBufSize - size of output buffer
\Output
int32_t - status info
\Notes
iSelect can be one of the following:
\verbatim
'host' - TRUE if hosting, else FALSE
'join' - TRUE if joining, else FALSE
'hoip' - host ip
'hprt' - host port
'peip' - peer ip
'pprt' - peer port
'pkrc' - packet received
'sock' - SocketT socket pointer (in pBuf)
\endverbatim
\Version 01/09/01 (GWS)
*/
/*************************************************************************************************F*/
int32_t NetGameUtilStatus(NetGameUtilRefT *ref, int32_t iSelect, void *pBuf, int32_t iBufSize)
{
// return host status
if (iSelect == 'host')
{
return(ref->hosting == 0);
}
if (iSelect == 'join')
{
return(ref->hosting == 1);
}
if (iSelect == 'hoip')
{
return(ref->hostip);
}
if (iSelect == 'hprt')
{
return(ref->hostport);
}
if (iSelect == 'pkrc')
{
return(ref->comm->bpackrcvd);
}
if (iSelect == 'peip')
{
return(ref->peerip);
}
if (iSelect == 'pprt')
{
return(ref->peerport);
}
if ((iSelect == 'sock') && (iBufSize == (signed)sizeof(ref->pSocket)))
{
ds_memcpy(pBuf, &ref->pSocket, sizeof(ref->pSocket));
return(sizeof(ref->pSocket));
}
return(-1);
}
/*F*************************************************************************************************/
/*!
\Function NetGameUtilAdvert
\Description
Send out an advertisement
\Input *ref - reference pointer
\Input *kind - class (unique to app)
\Input *name - name to broadcast
\Input *note - notes
\Version 01/09/01 (GWS)
*/
/*************************************************************************************************F*/
void NetGameUtilAdvert(NetGameUtilRefT *ref, const char *kind, const char *name, const char *note)
{
// see if we need to create module
if (_NetGameUtilAdvtConstruct(ref, 16, FALSE) == NULL)
{
NetPrintf(("netgameutil: NetGameUtilAdvert() failed to create the ProtoAdvt module.\n"));
return;
}
// save the kind for future queries
ds_strnzcpy(ref->kind, kind, sizeof(ref->kind));
// start advertising
ProtoAdvtAnnounce(ref->advt, kind, name, note, "TCP:~1:1024\tUDP:~1:1024", ref->advtfreq);
}
/*F*************************************************************************************************/
/*!
\Function NetGameUtilWithdraw
\Description
Withdraw given advertisement
\Input *ref - reference pointer
\Input *kind - advert kind
\Input *name - advert name
\Version 01/09/01 (GWS)
*/
/*************************************************************************************************F*/
void NetGameUtilWithdraw(NetGameUtilRefT *ref, const char *kind, const char *name)
{
if (ref->advt != NULL)
{
ProtoAdvtCancel(ref->advt, kind, name);
}
}
/*F*************************************************************************************************/
/*!
\Function NetGameUtilLocate
\Description
Find ip address of a specific advertisement
\Input *ref - reference pointer
\Input *kind - class (unique to app)
\Input *name - advertisement to look for
\Output
uint32_t - ip address of advertiser or zero if no match
\Version 01/09/01 (GWS)
*/
/*************************************************************************************************F*/
uint32_t NetGameUtilLocate(NetGameUtilRefT *ref, const char *kind, const char *name)
{
// auto-create the advert module if needed
if (_NetGameUtilAdvtConstruct(ref, 16, FALSE) == NULL)
{
NetPrintf(("netgameutil: NetGameUtilLocate() failed to create the ProtoAdvt module.\n"));
return(0);
}
// allow use of default kind
if (kind == NULL)
{
kind = ref->kind;
}
// pass to advertising module
return(ProtoAdvtLocate(ref->advt, kind, name, NULL, 0));
}
/*F*************************************************************************************************/
/*!
\Function NetGameUtilQuery
\Description
Return a list of all advertisements
\Input *ref - reference pointer
\Input *kind - class (unique to app)
\Input *buf - target buffer
\Input max - target buffer length
\Output
int32_t - number of matching ads
\Version 01/09/01 (GWS)
*/
/*************************************************************************************************F*/
int32_t NetGameUtilQuery(NetGameUtilRefT *ref, const char *kind, char *buf, int32_t max)
{
// auto-create the advert module if needed
if (_NetGameUtilAdvtConstruct(ref, 16, FALSE) == NULL)
{
NetPrintf(("netgameutil: NetGameUtilQuery() failed to create the ProtoAdvt module.\n"));
return(0);
}
// allow use of default kind
if (kind == NULL)
{
kind = ref->kind;
}
// pass to advert module
return(ProtoAdvtQuery(ref->advt, kind, "", buf, max, ref->uLocalAdvt));
}