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

627 lines
18 KiB
C

/*H********************************************************************************/
/*!
\File gamelink.c
\Description
Test NetGameLink
\Copyright
Copyright (c) Electronic Arts 2018.
\Version 02/02/2018 (jbrookes) First Version
*/
/********************************************************************************H*/
/*** Include files ****************************************************************/
#ifdef _WIN32
#pragma warning(push,0)
#include <windows.h>
#pragma warning(pop)
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "DirtySDK/platform.h"
#include "DirtySDK/dirtysock.h"
#include "DirtySDK/dirtysock/dirtymem.h"
#include "DirtySDK/game/netgameutil.h"
#include "DirtySDK/game/netgamelink.h"
#include "DirtySDK/comm/commudp.h"
#if defined(DIRTYCODE_XBOXONE)
#include "DirtySDK/DirtySock/dirtyaddr.h"
#endif
#include "libsample/zlib.h"
#include "libsample/zfile.h"
#include "libsample/zmem.h"
#include "testersubcmd.h"
#include "testermodules.h"
/*** Defines **********************************************************************/
//! gamelink states
enum
{
GAMELINK_STATUS_DISC,
GAMELINK_STATUS_CONN,
GAMELINK_STATUS_OPEN,
GAMELINK_STATUS_ACTV
};
//! default gamelink port
#define GAMELINK_PORT (3658)
/*** Macros ***********************************************************************/
/*** Type Definitions *************************************************************/
typedef struct GameLinkAppT
{
NetGameUtilRefT *pGameUtil;
NetGameLinkRefT *pGameLink;
uint32_t uLoclAddr;
uint8_t bZCallback;
uint8_t uLinkStatus;
uint8_t uPackSendVal;
uint8_t _pad[2];
} GameLinkAppT;
/*** Function Prototypes **********************************************************/
static void _GameLinkCreate(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp);
static void _GameLinkDestroy(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp);
static void _GameLinkConnect(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp);
static void _GameLinkControl(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp);
static void _GameLinkSend(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp);
/*** Variables ********************************************************************/
static T2SubCmdT _GameLink_Commands[] =
{
{ "create", _GameLinkCreate },
{ "destroy", _GameLinkDestroy },
{ "connect", _GameLinkConnect },
{ "ctrl", _GameLinkControl },
{ "send", _GameLinkSend },
{ "", NULL },
};
static GameLinkAppT _GameLink_App = { 0 };
/*** Private Functions ************************************************************/
/*F********************************************************************************/
/*!
\Function _GetIntArg
\Description
Get fourcc/integer from command-line argument
\Input *pArg - pointer to argument
\Version 10/20/2011 (jbrookes)
*/
/********************************************************************************F*/
static int32_t _GetIntArg(const char *pArg)
{
int32_t iValue;
// check for possible fourcc value
if ((strlen(pArg) == 4) && (isalpha(pArg[0]) || isalpha(pArg[1]) || isalpha(pArg[2]) || isalpha(pArg[3])))
{
iValue = pArg[0] << 24;
iValue |= pArg[1] << 16;
iValue |= pArg[2] << 8;
iValue |= pArg[3];
}
else
{
iValue = (signed)strtol(pArg, NULL, 10);
}
return(iValue);
}
/*F********************************************************************************/
/*!
\Function _GetConnectParms
\Description
Create connection string for gamelink connection
\Input *pConnName - [out] connection string
\Input iNameSize - size of connname buffer
\Input uLoclAddr - local address
\Input uLoclPort - local port
\Input uConnAddr - peer address
\Input uConnPort - peer port
\Output
int32_t - connection flags
\Version 02/02/2018 (jbrookes)
*/
/********************************************************************************F*/
static int32_t _GetConnectParms(char *pConnName, int32_t iNameSize, uint32_t uLoclAddr, uint16_t uLoclPort, uint32_t uConnAddr, uint16_t uConnPort)
{
char strLoclAddr[32], strConnAddr[32];
int32_t iConnFlags = NETGAME_CONN_CONNECT;
uint32_t uAddrTemp;
uint8_t bHosting;
ds_snzprintf(strLoclAddr, sizeof(strLoclAddr), "%a:%d", uLoclAddr, uLoclPort);
ds_snzprintf(strConnAddr, sizeof(strConnAddr), "%a:%d", uConnAddr, uConnPort);
bHosting = (strcmp(strLoclAddr, strConnAddr) < 0) ? TRUE : FALSE;
/* set up parms based on whether we are "hosting" or not. the connection name is the
unique address of the "host" concatenated with the unique address of the "client" */
if (bHosting == TRUE)
{
// swap names
uAddrTemp = uConnAddr;
uConnAddr = uLoclAddr;
uLoclAddr = uAddrTemp;
// set conn flags to listen
iConnFlags = NETGAME_CONN_LISTEN;
}
// format connection name and return connection flags
ds_snzprintf(pConnName, iNameSize, "%x-%x", uLoclAddr, uConnAddr);
return(iConnFlags);
}
/*F********************************************************************************/
/*!
\Function _GameUtilConnect
\Description
Connect to peer
\Input *pApp - module state
\Input uLoclAddr - local address
\Input uLoclPort - local port
\Input uConnAddr - peer address
\Input uConnPort - peer port
\Output
int32_t - result of NetGameUtilConnect, or -1 on failure
\Version 02/02/2018 (jbrookes)
*/
/********************************************************************************F*/
static int32_t _GameUtilConnect(GameLinkAppT *pApp, uint32_t uLoclAddr, uint16_t uLoclPort, uint32_t uConnAddr, uint16_t uConnPort)
{
char strConn[128], strConnName[64];
int32_t iConnFlags;
if ((pApp->pGameUtil == NULL) && ((pApp->pGameUtil = NetGameUtilCreate()) == NULL))
{
ZPrintf("gamelink: could not create util ref\n");
return(-1);
}
// set game link options
#if 0
_SetGamelinkOpt(pClient->pGameUtilRef, 'minp', pConnApi->iGameMinp);
_SetGamelinkOpt(pClient->pGameUtilRef, 'mout', pConnApi->iGameMout);
_SetGamelinkOpt(pClient->pGameUtilRef, 'mwid', pConnApi->iGameMwid);
if (pConnApi->iGameUnackLimit != 0)
{
_SetGamelinkOpt(pClient->pGameUtilRef, 'ulmt', pConnApi->iGameUnackLimit);
}
// set our client id (used by gameserver to uniquely identify us)
NetGameUtilControl(pClient->pGameUtilRef, 'clid', pClient->ClientInfo.uLocalClientId);
NetGameUtilControl(pClient->pGameUtilRef, 'rcid', pClient->ClientInfo.uRemoteClientId);
#endif
// determine connection parameters
iConnFlags = _GetConnectParms(strConnName, sizeof(strConnName), uLoclAddr, uLoclPort, uConnAddr, uConnPort);
// format connect string
ds_snzprintf(strConn, sizeof(strConn), "%a:%d:%d#%s", uConnAddr, uLoclPort, uConnPort, strConnName);
// start the connection attempt
return(NetGameUtilConnect(pApp->pGameUtil, iConnFlags, strConn, (CommAllConstructT *)CommUDPConstruct));
}
/*F********************************************************************************/
/*!
\Function _GameLinkDestroyApp
\Description
Destroy app, clearing state
\Input *pApp - app state
\Version 02/02/2018 (jbrookes)
*/
/********************************************************************************F*/
static void _GameLinkDestroyApp(GameLinkAppT *pApp)
{
ds_memclr(pApp, sizeof(*pApp));
}
/*
GameLink Commands
*/
/*F*************************************************************************************/
/*!
\Function _GameLinkCreate
\Description
GameLink subcommand - create gamelink module
\Input *pCmdRef - unused
\Input argc - argument count
\Input *argv[] - argument list
\Input bHelp - TRUE if usage request
\Version 02/02/2018 (jbrookes)
*/
/**************************************************************************************F*/
static void _GameLinkCreate(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp)
{
GameLinkAppT *pApp = &_GameLink_App;
if (bHelp == TRUE)
{
ZPrintf(" usage: %s create\n", argv[0]);
return;
}
pApp->uLoclAddr = SocketInfo(NULL, 'addr', 0, NULL, 0);
}
/*F*************************************************************************************/
/*!
\Function _GameLinkDestroy
\Description
GameLink subcommand - destroy gamelink module
\Input *pCmdRef - unused
\Input argc - argument count
\Input *argv[] - argument list
\Input bHelp - TRUE if usage request
\Version 02/02/2018 (jbrookes)
*/
/**************************************************************************************F*/
static void _GameLinkDestroy(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp)
{
GameLinkAppT *pApp = &_GameLink_App;
if (bHelp == TRUE)
{
ZPrintf(" usage: %s destroy\n", argv[0]);
return;
}
_GameLinkDestroyApp(pApp);
}
/*F*************************************************************************************/
/*!
\Function _GameLinkConnect
\Description
GameLink subcommand - connect to peer
\Input *pCmdRef - unused
\Input argc - argument count
\Input *argv[] - argument list
\Input bHelp - TRUE if usage request
\Version 02/02/2018 (jbrookes)
*/
/**************************************************************************************F*/
static void _GameLinkConnect(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp)
{
GameLinkAppT *pApp = &_GameLink_App;
uint32_t uConnAddr, uConnPort, uLoclPort, uRemoteClientId;
if ((bHelp == TRUE) || (argc > 4))
{
ZPrintf(" usage: %s connect <address[:port:port]> [rclientid]\n", argv[0]);
return;
}
// get remote address, port, and local port (if specified) in addr:port:port2 format
SockaddrInParse2(&uConnAddr, (int32_t *)&uConnPort, (int32_t *)&uLoclPort, argv[2]);
if (uConnPort == 0)
{
uConnPort = GAMELINK_PORT;
}
if (uLoclPort == 0)
{
uLoclPort = GAMELINK_PORT;
}
// get remote client id, or use remote address if unspecified
uRemoteClientId = (argc == 4) ? (uint32_t)strtol(argv[3], NULL, 16) : uConnAddr;
// start the connect
if (_GameUtilConnect(pApp, pApp->uLoclAddr, uLoclPort, uConnAddr, uConnPort) < 0)
{
ZPrintf("%s: error trying to connect\n");
return;
}
// log connection attempt, uptdate status
ZPrintf("%s: connecting to %a:%u:%u (clientId=0x%08x)\n", argv[0], uConnAddr, uConnPort, uLoclPort, uRemoteClientId);
pApp->uLinkStatus = GAMELINK_STATUS_CONN;
}
/*F*************************************************************************************/
/*!
\Function _GameLinkControl
\Description
GameLink control subcommand - set control options
\Input *pCmdRef - unused
\Input argc - argument count
\Input *argv[] - argument list
\Input bHelp - TRUE if usage request
\Version 02/02/2018 (jbrookes)
*/
/**************************************************************************************F*/
static void _GameLinkControl(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp)
{
GameLinkAppT *pApp = &_GameLink_App;
int32_t iCmd, iValue = 0;
if ((argc < 3) && (argc > 4))
{
ZPrintf("%s: invalid ctrl command\n", argv[0]);
bHelp = TRUE;
}
if (bHelp == TRUE)
{
ZPrintf(" usage: %s ctrl <cmd> <arg>\n", argv[0]);
return;
}
// get control command
iCmd = argv[2][0] << 24;
iCmd |= argv[2][1] << 16;
iCmd |= argv[2][2] << 8;
iCmd |= argv[2][3];
// get control argument, if specified
if (argc > 3)
{
iValue = _GetIntArg(argv[3]);
}
// pass it down
NetGameLinkControl(pApp->pGameLink, iCmd, iValue, NULL);
}
/*F*************************************************************************************/
/*!
\Function _GameLinkSend
\Description
GameLink subcommand - send data
\Input *pCmdRef - unused
\Input argc - argument count
\Input *argv[] - argument list
\Input bHelp - TRUE if usage request
\Version 02/02/2018 (jbrookes)
*/
/**************************************************************************************F*/
static void _GameLinkSend(void *pCmdRef, int32_t argc, char *argv[], unsigned char bHelp)
{
GameLinkAppT *pApp = &_GameLink_App;
NetGameMaxPacketT Packet;
int32_t iSize, iResult;
if (argc != 3)
{
ZPrintf("%s: invalid send command\n", argv[0]);
bHelp = TRUE;
}
if (bHelp == TRUE)
{
ZPrintf(" usage: %s send <size>\n", argv[0]);
return;
}
// get size
if (((iSize = _GetIntArg(argv[2])) < 0) || (iSize > NETGAME_DATAPKT_MAXSIZE))
{
ZPrintf("%s: size %d invalid, setting size to %d\n", NETGAME_DATAPKT_MAXSIZE);
iSize = NETGAME_DATAPKT_MAXSIZE;
}
// format packet head
ds_memclr(&Packet.head, sizeof(Packet.head));
Packet.head.kind = GAME_PACKET_USER;
Packet.head.len = iSize;
// format packet data
ds_memset(&Packet.body.data, pApp->uPackSendVal++, iSize);
// send the packet
iResult = NetGameLinkSend(pApp->pGameLink, (NetGamePacketT *)&Packet, 1);
ZPrintf("%s: sent %d bytes\n", argv[0], iResult);
}
/*F********************************************************************************/
/*!
\Function _CmdGameLinkCb
\Description
Update gamelink command
\Input *argz - environment
\Input argc - standard number of arguments
\Input *argv[] - standard arg list
\Output
int32_t - standard return value
\Version 02/02/2018 (jbrookes)
*/
/********************************************************************************F*/
static int32_t _CmdGameLinkCb(ZContext *argz, int32_t argc, char *argv[])
{
GameLinkAppT *pApp = &_GameLink_App;
// check for kill
if (argc == 0)
{
_GameLinkDestroyApp(pApp);
ZPrintf("%s: killed\n", argv[0]);
return(0);
}
// update in connecting state
if (pApp->uLinkStatus == GAMELINK_STATUS_CONN)
{
CommRef *pCommRef;
// check for established connection
if ((pCommRef = NetGameUtilComplete(pApp->pGameUtil)) != NULL)
{
if ((pApp->pGameLink = NetGameLinkCreate(pCommRef, FALSE, 8192)) != NULL)
{
ZPrintf("%s: game connection established\n", argv[0]);
// indicate we've connected
pApp->uLinkStatus = GAMELINK_STATUS_OPEN;
}
else
{
ZPrintf("%s: game connection failed\n", argv[0]);
pApp->uLinkStatus = GAMELINK_STATUS_DISC;
}
}
}
// update in open state
if (pApp->uLinkStatus == GAMELINK_STATUS_OPEN)
{
NetGameLinkStatT Stat;
// give time to NetGameLink to run any connection-related processes
NetGameLinkUpdate(pApp->pGameLink);
// get link stats
NetGameLinkStatus(pApp->pGameLink, 'stat', 0, &Stat, sizeof(NetGameLinkStatT));
// see if we're open
if (Stat.isopen == TRUE)
{
// mark as active
ZPrintf("%s: game connection is active\n", argv[0]);
pApp->uLinkStatus = GAMELINK_STATUS_ACTV;
}
}
// update in active state
if (pApp->uLinkStatus == GAMELINK_STATUS_ACTV)
{
NetGameMaxPacketT Packet;
NetGameLinkStatT Stat;
// get link stats
NetGameLinkStatus(pApp->pGameLink, 'stat', 0, &Stat, sizeof(NetGameLinkStatT));
// make sure connection is still open
if (Stat.isopen == FALSE)
{
ZPrintf("%s: game connection closed\n", argv[0]);
pApp->uLinkStatus = GAMELINK_STATUS_DISC;
}
// see if we've timed out
else if (NetTickDiff(Stat.tick, Stat.rcvdlast) > 20000)
{
ZPrintf("%s: game connection timed out\n", argv[0]);
pApp->uLinkStatus = GAMELINK_STATUS_DISC;
}
// try and receive something; if we get something echo it to output
if (NetGameLinkRecv(pApp->pGameLink, (NetGamePacketT *)&Packet, sizeof(Packet.body.data), FALSE) > 0)
{
// cap max size to echo
#if DIRTYCODE_DEBUG
int32_t iSize = DS_MIN(Packet.head.len, 64);
NetPrintMem(Packet.body.data, iSize, "packet data");
#endif
}
}
// keep running
return(ZCallback(&_CmdGameLinkCb, 16));
}
/*** Public functions *************************************************************/
/*F********************************************************************************/
/*!
\Function CmdGameLink
\Description
Initiate GameLink connection.
\Input *argz - environment
\Input argc - standard number of arguments
\Input *argv[] - standard arg list
\Output
int32_t - standard return value
\Version 02/02/2018 (jbrookes)
*/
/********************************************************************************F*/
int32_t CmdGameLink(ZContext *argz, int32_t argc, char *argv[])
{
GameLinkAppT *pApp = &_GameLink_App;
T2SubCmdT *pCmd;
uint8_t bHelp;
// handle basic help
if ((argc <= 1) || (((pCmd = T2SubCmdParse(_GameLink_Commands, argc, argv, &bHelp)) == NULL)))
{
ZPrintf(" test the gamelink module\n");
T2SubCmdUsage(argv[0], _GameLink_Commands);
return(0);
}
// if no ref yet, make one
if ((pCmd->pFunc != _GameLinkCreate) && (pApp->pGameLink == NULL))
{
char *pCreate = "create";
ZPrintf(" %s: ref has not been created - creating\n", argv[0]);
_GameLinkCreate(pApp, 1, &pCreate, bHelp);
}
// hand off to command
pCmd->pFunc(pApp, argc, argv, bHelp);
// one-time install of periodic callback
if (pApp->bZCallback == FALSE)
{
pApp->bZCallback = TRUE;
return(ZCallback(_CmdGameLinkCb, 16));
}
else
{
return(0);
}
}