02061d74c2
(as received from Jonas Echterhoff)
925 lines
24 KiB
C++
925 lines
24 KiB
C++
//network.cpp
|
|
//basic game hosting/joining and message sending code based on Network_Tool
|
|
|
|
#include <network_tool.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "gametime.h"
|
|
#include "config.h"
|
|
#include "network.h"
|
|
#include "error.h"
|
|
#include "gameframe.h"
|
|
#include "controls.h"
|
|
#include "gamesound.h"
|
|
#include "carphysics.h"
|
|
#include "interfaceutil.h"
|
|
#include "gamemem.h"
|
|
#include "Reggie.h"
|
|
#include "gamesystem.h"
|
|
#include "interfacemultiplayer.h"
|
|
#include "initexit.h"
|
|
#include "reg_tool_3.h"
|
|
|
|
extern ReggieRef gReggieRef;
|
|
extern int gReggieConnected;
|
|
int gAllowCompression=true;
|
|
#define kGameName "Redline"
|
|
#define kConnectionTimeOut 5.0
|
|
#define kCompressionSize 32
|
|
#define kCompressionFlag 0x80
|
|
|
|
NT_SessionRef gSessionRef=nil;
|
|
|
|
typedef struct{
|
|
QStub qstub;
|
|
tNetworkPacket packet;
|
|
}tQueuedPacket;
|
|
|
|
Queue gPacketQueue;
|
|
int gInternetAvailable=true;
|
|
int gTerminateSession=false;
|
|
float gLastPingReceived=0,gLastRTT=0;
|
|
char gHistoryBuffer[1024*500]="";
|
|
int gHistoryDumps=0;
|
|
|
|
void NetworkLockOutNewPlayers()
|
|
{
|
|
// HandleError(NT_SetSessionLock(gSessionRef,true));
|
|
int err=NT_SetSessionLock(gSessionRef,true);
|
|
//PrintConsoleString("Locking session. returns %d",err);
|
|
}
|
|
|
|
void NetworkUnlockOutNewPlayers()
|
|
{
|
|
// HandleError(NT_SetSessionLock(gSessionRef,false));
|
|
int err=NT_SetSessionLock(gSessionRef,false);
|
|
//PrintConsoleString("Unlocking session. returns %d",err);
|
|
}
|
|
|
|
int NetworkGetLocalNetID()
|
|
{
|
|
NT_MemberInfo info;
|
|
NT_GetSelfMemberInfo(gSessionRef,&info);
|
|
return info.member;
|
|
}
|
|
|
|
void NetworkClearPacketQueue()
|
|
{
|
|
int temp=gTerminateSession;
|
|
gTerminateSession=false;
|
|
tNetworkPacket packet;
|
|
while(NetworkReceivePacket(&packet))
|
|
if(packet.data)
|
|
MemoryFreeBlock(packet.data);
|
|
gTerminateSession=temp;
|
|
}
|
|
|
|
Result32 CompressPacket(void *buffer, long *length) {
|
|
Result32 error = eCommonErrorNone;
|
|
void ** indirect = NULL;
|
|
|
|
qThrowIfNull(indirect = IndirectInitialize(buffer, *length),
|
|
eCommonErrorOutOfMem, kCommonErrorOutOfMemStr);
|
|
|
|
qThrowIfError(IndirectCompress(indirect,
|
|
kIndirectCompressorZLib, NULL), 0);
|
|
|
|
MemoryCopy(buffer, *indirect, *length = IndirectGetSize(indirect));
|
|
|
|
CLEANUP:
|
|
if (indirect) IndirectDeallocate(indirect);
|
|
return(error);
|
|
}
|
|
|
|
Result32 DecompressPacket(void *buffer, long *length) {
|
|
Result32 error = eCommonErrorNone;
|
|
void ** indirect = NULL;
|
|
|
|
qThrowIfNull(indirect = IndirectInitialize(buffer, *length),
|
|
eCommonErrorOutOfMem, kCommonErrorOutOfMemStr);
|
|
|
|
qThrowIfError(IndirectDecompress(indirect,
|
|
kIndirectCompressorZLib, NULL), 0);
|
|
|
|
MemoryCopy(buffer, *indirect, *length = IndirectGetSize(indirect));
|
|
|
|
CLEANUP:
|
|
if (indirect) IndirectDeallocate(indirect);
|
|
return(error);
|
|
}
|
|
|
|
void NetworkGetStatusString(char *str)
|
|
{
|
|
NT_ProblemInfo prob;
|
|
if(NT_GetProblemInfo(gSessionRef,0,&prob))
|
|
{
|
|
if(prob.timeout<10)
|
|
str[0]='.';
|
|
else if(prob.timeout<50)
|
|
str[0]='t';
|
|
else
|
|
str[0]='T';
|
|
if(prob.latency<10)
|
|
str[1]='.';
|
|
else if(prob.latency<50)
|
|
str[1]='l';
|
|
else
|
|
str[1]='L';
|
|
if(prob.stalled<10)
|
|
str[2]='.';
|
|
else if(prob.stalled<50)
|
|
str[2]='s';
|
|
else
|
|
str[2]='S';
|
|
if(prob.backlog<10)
|
|
str[3]='.';
|
|
else if(prob.backlog<50)
|
|
str[3]='b';
|
|
else
|
|
str[3]='B';
|
|
if(prob.failure<10)
|
|
str[4]='.';
|
|
else if(prob.failure<50)
|
|
str[4]='f';
|
|
else
|
|
str[4]='F';
|
|
if(prob.errors<10)
|
|
str[5]='.';
|
|
else if(prob.errors<50)
|
|
str[5]='e';
|
|
else
|
|
str[5]='E';
|
|
str[6]='\0';
|
|
}
|
|
}
|
|
|
|
void NetworkGetBandwidth(float *rcv,float *snd)
|
|
{
|
|
NT_LatencyInfo lat;
|
|
NT_GetTotalLatencyInfo(gSessionRef,&lat);
|
|
*rcv=lat.recvPackets10Sec*0.1;
|
|
*snd=lat.sendPackets10Sec*0.1;
|
|
}
|
|
|
|
void NetworkQueuePacket(tNetworkPacket *insert)
|
|
{
|
|
tQueuedPacket *q=(tQueuedPacket*)MemoryAllocateZeroedBlock(sizeof(tQueuedPacket));
|
|
q->packet=*insert;
|
|
QueueInsert(&gPacketQueue,(QStubPtr)q);
|
|
}
|
|
|
|
//#define PACKET_DUMP
|
|
|
|
#ifdef PACKET_DUMP
|
|
void DumpPacket(tNetworkPacket *packet)
|
|
{
|
|
char st[256];
|
|
sprintf(st,"what: %d",packet->what);
|
|
PrintConsoleString(st);
|
|
sprintf(st,"from: %d",packet->from);
|
|
PrintConsoleString(st);
|
|
sprintf(st,"size: 0x%x",packet->size);
|
|
PrintConsoleString(st);
|
|
|
|
int line=0;
|
|
|
|
while(line<=packet->size/16)
|
|
{
|
|
char dataStr[256];
|
|
|
|
sprintf(dataStr,"%04x ",line*16);
|
|
|
|
for(int pos=line*16;pos<line*16+16;pos++)
|
|
if(pos<packet->size)
|
|
sprintf(dataStr,"%s%02x",dataStr,*((UInt8*)packet->data+pos));
|
|
else
|
|
sprintf(dataStr,"%s ",dataStr);
|
|
|
|
sprintf(dataStr,"%s ",dataStr);
|
|
|
|
for(int pos=line*16;pos<line*16+16;pos++)
|
|
if(pos<packet->size)
|
|
if(*((UInt8*)packet->data+pos)>=32)
|
|
sprintf(dataStr,"%s%c",dataStr,*((UInt8*)packet->data+pos));
|
|
else
|
|
sprintf(dataStr,"%s.",dataStr);
|
|
else
|
|
sprintf(dataStr,"%s ",dataStr);
|
|
|
|
PrintConsoleString(dataStr);
|
|
line++;
|
|
}
|
|
|
|
PrintConsoleString("");
|
|
}
|
|
#endif
|
|
|
|
void NTEventHandler(NT_SessionRef sessionRef,void *refcon,NT_SessionEventType event,NT_MemberIDType member)
|
|
{
|
|
gAllowCompression=false;
|
|
switch(event)
|
|
{
|
|
case eSessionEventSessionStart:{
|
|
//PrintConsoleString("eSessionEventSessionStart");
|
|
tNetworkPacket packet;
|
|
packet.what=kMessageTypePlayerJoined;
|
|
packet.from=0;
|
|
packet.size=0;
|
|
packet.data=NULL;
|
|
NetworkQueuePacket(&packet);
|
|
}
|
|
break;
|
|
case eSessionEventMemberJoin:{
|
|
//PrintConsoleString("eSessionEventMemberJoin, member=%d",member);
|
|
tNetworkPacket packet;
|
|
packet.what=kMessageTypePlayerJoined;
|
|
packet.from=member;
|
|
packet.size=0;
|
|
packet.data=NULL;
|
|
NetworkQueuePacket(&packet);
|
|
/*NT_ToleranceInfo tolerance;
|
|
NT_GetToleranceInfo(gSessionRef,&tolerance);
|
|
PrintConsoleString("Tolerance info:");
|
|
PrintConsoleString("avgPackets %d.",tolerance.avgPackets);
|
|
PrintConsoleString("avgRequests %d.",tolerance.avgRequests);
|
|
PrintConsoleString("maxRequests %d.",tolerance.maxRequests);
|
|
PrintConsoleString("minHeartbeat %d.",tolerance.minHeartbeat);
|
|
PrintConsoleString("avgHeartbeat %d.",tolerance.avgHeartbeat);
|
|
PrintConsoleString("maxHeartbeat %d.",tolerance.maxHeartbeat);
|
|
PrintConsoleString("maxTimeout %d.",tolerance.maxTimeout);
|
|
PrintConsoleString("maxFailures %d.",tolerance.maxFailures);
|
|
PrintConsoleString("maxErrors %d.",tolerance.maxErrors);
|
|
PrintConsoleString("minLinger %d.",tolerance.minLinger);*/
|
|
}
|
|
break;
|
|
case eSessionEventMemberLeave:{
|
|
//PrintConsoleString("eSessionEventMemberLeave, member=%d",member);
|
|
tNetworkPacket packet;
|
|
if(member==0)
|
|
{
|
|
if(*gDisconnectString=='\0')
|
|
sprintf(gDisconnectString,"Network Failure (Failure on Host side).");
|
|
packet.what=kMessageTypeGameTerminated;
|
|
}
|
|
else
|
|
packet.what=kMessageTypePlayerLeft;
|
|
packet.from=member;
|
|
packet.size=0;
|
|
packet.data=NULL;
|
|
NetworkQueuePacket(&packet);
|
|
NT_LatencyInfo latency;
|
|
if(NT_GetLatencyInfo(gSessionRef,member,&latency))
|
|
{
|
|
PrintConsoleString("Latency info for dropped member:");
|
|
PrintConsoleString("minLinkRTT %d.",latency.minLinkRTT);
|
|
PrintConsoleString("avgLinkRTT %d.",latency.avgLinkRTT);
|
|
PrintConsoleString("p80LinkRTT %d.",latency.p80LinkRTT);
|
|
PrintConsoleString("devLinkRTT %d.",latency.devLinkRTT);
|
|
PrintConsoleString("wAvgLinkRTT %d.",latency.wAvgLinkRTT);
|
|
PrintConsoleString("wDevLinkRTT %d.",latency.wDevLinkRTT);
|
|
PrintConsoleString("minTranRTT %d.",latency.minTranRTT);
|
|
PrintConsoleString("avgTranRTT %d.",latency.avgTranRTT);
|
|
PrintConsoleString("p80TranRTT %d.",latency.p80TranRTT);
|
|
PrintConsoleString("maxTranRTT %d.",latency.maxTranRTT);
|
|
PrintConsoleString("devTranRTT %d.",latency.devTranRTT);
|
|
PrintConsoleString("wAvgTranRTT %d.",latency.wAvgTranRTT);
|
|
PrintConsoleString("wDevTranRTT %d.",latency.wDevTranRTT);
|
|
}
|
|
NT_ProblemInfo prob;
|
|
if(NT_GetProblemInfo(gSessionRef,member,&prob))
|
|
{
|
|
PrintConsoleString("Problem info for dropped member:");
|
|
PrintConsoleString("timeout %d.",prob.timeout);
|
|
PrintConsoleString("latency %d.",prob.latency);
|
|
PrintConsoleString("stalled %d.",prob.stalled);
|
|
PrintConsoleString("backlog %d.",prob.backlog);
|
|
PrintConsoleString("failure %d.",prob.failure);
|
|
PrintConsoleString("errors %d.",prob.errors);
|
|
}
|
|
|
|
NT_PacketHistory(gSessionRef,gHistoryBuffer,1024*500);
|
|
}
|
|
break;
|
|
case eSessionEventSessionEnd:{
|
|
//PrintConsoleString("eSessionEventSessionEnd");
|
|
/* tNetworkPacket packet;
|
|
packet.what=kMessageTypeGameTerminated;
|
|
packet.from=member;
|
|
packet.size=0;
|
|
packet.data=NULL;
|
|
NetworkQueuePacket(&packet);*/
|
|
}
|
|
break;
|
|
case eSessionEventRecvPacket:{
|
|
UInt8 data[kNetworkToolPacketSize];
|
|
UInt16 len;
|
|
if(NT_RecvPacket(gSessionRef,&member,(char*)data,kNetworkToolPacketSize,&len))
|
|
{
|
|
tNetworkPacket packet;
|
|
packet.data=MemoryAllocateBlock(kNetworkToolPacketSize);
|
|
MemoryMove(packet.data,data+1,len-1);
|
|
packet.size=len-1;
|
|
packet.from=member;
|
|
packet.what=*data;
|
|
if(packet.what==kMessageTypeGameTerminated)
|
|
gTerminateSession=true;
|
|
/*#ifdef PACKET_DUMP
|
|
PrintConsoleString("Packet Received:");
|
|
DumpPacket(&packet);
|
|
#endif
|
|
*/
|
|
//got a Ping request?
|
|
if(packet.what==kMessageTypePing)
|
|
//send out a reply packet with the original ping packets time stamp.
|
|
NetworkSendPacket(kMessageTypePong,packet.data,sizeof(float),kMessagePriorityLow,packet.from);
|
|
else if(packet.what==kMessageTypePong)
|
|
{
|
|
gLastPingReceived=TimeGetSeconds();
|
|
gLastRTT=gLastPingReceived-*(float*)packet.data;
|
|
}
|
|
else
|
|
NetworkQueuePacket(&packet);
|
|
}
|
|
}
|
|
break;
|
|
case eSessionEventRecvRequest:{
|
|
UInt8 data[kNetworkToolPacketSize];
|
|
UInt16 len;
|
|
if(NT_RecvRequest(gSessionRef,&member,(char*)data,kNetworkToolPacketSize,&len))
|
|
{
|
|
NT_SendResponse(gSessionRef,NULL,0);
|
|
tNetworkPacket packet;
|
|
packet.data=MemoryAllocateBlock(kNetworkToolPacketSize);
|
|
MemoryMove(packet.data,data+1,len-1);
|
|
packet.size=len-1;
|
|
packet.from=member;
|
|
packet.what=*data;
|
|
if(packet.what==kMessageTypeGameTerminated)
|
|
gTerminateSession=true;
|
|
|
|
#ifdef PACKET_DUMP
|
|
PrintConsoleString("Request Packet Received:");
|
|
DumpPacket(&packet);
|
|
#endif
|
|
|
|
NetworkQueuePacket(&packet);
|
|
}
|
|
}
|
|
break;
|
|
case eSessionEventRecvResponse:{
|
|
UInt8 data[kNetworkToolPacketSize];
|
|
UInt16 len;
|
|
NT_RecvResponse(gSessionRef,&member,(char*)data,kNetworkToolPacketSize,&len);
|
|
}
|
|
break;
|
|
}
|
|
gAllowCompression=true;
|
|
}
|
|
|
|
void NTErrorHandler(NT_SessionRef sessionRef,void *refcon,NT_SessionProblemType problem,NT_SessionSeverityType severity,NT_MemberIDType member,Bool8 *disconnectSelf,Bool8 *disconnectMember)
|
|
{
|
|
gAllowCompression=false;
|
|
if(severity>=100)
|
|
{
|
|
PrintConsoleString("NT Error %d",problem);
|
|
|
|
NT_MemberInfo info;
|
|
NT_GetSelfMemberInfo(gSessionRef,&info);
|
|
if(info.member==0)
|
|
{
|
|
if(member!=0)
|
|
{
|
|
*disconnectMember=true;
|
|
|
|
tChatMessage m;
|
|
m.flags=kChatFlagSystem;
|
|
switch(problem)
|
|
{
|
|
case eSessionProblemTimeout:
|
|
sprintf(m.str,"### Player %d is disconnecting (Connection timed out).",member+1);
|
|
break;
|
|
case eSessionProblemLatency:
|
|
sprintf(m.str,"### Player %d is disconnecting (Latency too high).",member+1);
|
|
break;
|
|
case eSessionProblemStalled:
|
|
sprintf(m.str,"### Player %d is disconnecting (Stalled).",member+1);
|
|
break;
|
|
case eSessionProblemBacklog:
|
|
sprintf(m.str,"### Player %d is disconnecting (Backlog).",member+1);
|
|
break;
|
|
case eSessionProblemFailure:
|
|
sprintf(m.str,"### Player %d is disconnecting (Failure).",member+1);
|
|
break;
|
|
case eSessionProblemErrors:
|
|
sprintf(m.str,"### Player %d is disconnecting (Too many errors).",member+1);
|
|
break;
|
|
default:
|
|
sprintf(m.str,"### Player %d is disconnecting (Unknown error).",member+1);
|
|
break;
|
|
}
|
|
printf("%s\n",m.str);
|
|
//NetworkSendPacket(kMessageTypeChat,&m,sizeof(m),kMessagePriorityHigh,kMessageSendToAll);
|
|
}
|
|
}
|
|
else if(member==0)
|
|
{
|
|
*disconnectMember=false;
|
|
*disconnectSelf=true;
|
|
tNetworkPacket packet;
|
|
packet.data=NULL;
|
|
packet.size=0;
|
|
packet.from=member;
|
|
packet.what=kMessageTypeGameTerminated;
|
|
NetworkQueuePacket(&packet);
|
|
switch(problem)
|
|
{
|
|
case eSessionProblemTimeout:
|
|
sprintf(gDisconnectString,"Network Failure (Connection timed out).");
|
|
break;
|
|
case eSessionProblemLatency:
|
|
sprintf(gDisconnectString,"Network Failure (Latency too high).");
|
|
break;
|
|
case eSessionProblemStalled:
|
|
sprintf(gDisconnectString,"Network Failure (Stalled).");
|
|
break;
|
|
case eSessionProblemBacklog:
|
|
sprintf(gDisconnectString,"Network Failure (Backlog).");
|
|
break;
|
|
case eSessionProblemFailure:
|
|
sprintf(gDisconnectString,"Network Failure (Failure).");
|
|
break;
|
|
case eSessionProblemErrors:
|
|
sprintf(gDisconnectString,"Network Failure (Too many errors).");
|
|
break;
|
|
default:
|
|
sprintf(gDisconnectString,"Network Failure (unknown error).");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
gAllowCompression=true;
|
|
}
|
|
|
|
void NetworkPreSession()
|
|
{
|
|
if(gSessionRef==NULL)
|
|
HandleError(NT_SessionStartParam(&gSessionRef,NTEventHandler,NTErrorHandler,NULL,NULL,6,kGameName,""));
|
|
else
|
|
HandleError(NT_SessionRestart(gSessionRef,6,kGameName,""));
|
|
}
|
|
|
|
float NetworkGetPlayerPing(int netID)
|
|
{
|
|
NT_MemberInfo info;
|
|
NT_GetSelfMemberInfo(gSessionRef,&info);
|
|
if(netID==info.member||netID==kMessageNoRecipients)
|
|
return 0;
|
|
|
|
NT_LatencyInfo latency;
|
|
if(NT_GetLatencyInfo(gSessionRef,netID,&latency));
|
|
{
|
|
/* PrintConsoleString("Player %d latency.",netID);
|
|
PrintConsoleString("minLinkRTT %d.",latency.minLinkRTT);
|
|
PrintConsoleString("avgLinkRTT %d.",latency.avgLinkRTT);
|
|
PrintConsoleString("p80LinkRTT %d.",latency.p80LinkRTT);
|
|
PrintConsoleString("devLinkRTT %d.",latency.devLinkRTT);
|
|
PrintConsoleString("wAvgLinkRTT %d.",latency.wAvgLinkRTT);
|
|
PrintConsoleString("wDevLinkRTT %d.",latency.wDevLinkRTT);
|
|
PrintConsoleString("minTranRTT %d.",latency.minTranRTT);
|
|
PrintConsoleString("avgTranRTT %d.",latency.avgTranRTT);
|
|
PrintConsoleString("p80TranRTT %d.",latency.p80TranRTT);
|
|
PrintConsoleString("maxTranRTT %d.",latency.maxTranRTT);
|
|
PrintConsoleString("devTranRTT %d.",latency.devTranRTT);
|
|
PrintConsoleString("wAvgTranRTT %d.",latency.wAvgTranRTT);
|
|
PrintConsoleString("wDevTranRTT %d.",latency.wDevTranRTT);*/
|
|
return latency.avgLinkRTT*0.001;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//Host a new Network Game
|
|
void HostNetGame(int maxPlayers,char *pass)
|
|
{
|
|
HandleError(NT_SessionRestart(gSessionRef,maxPlayers,kGameName,pass));
|
|
gTerminateSession=false;
|
|
}
|
|
|
|
void NetworkDisconnectPlayer(int netID,UInt8 type)
|
|
{
|
|
NT_MemberInfo info;
|
|
if(NT_GetMemberInfo(gSessionRef,netID,&info))
|
|
{
|
|
if(type!=kNetworkDisconnectLicenseCopies)
|
|
HandleError(NT_SessionBan(gSessionRef,&info.address,false,NULL));
|
|
NetworkSendPacket(kMessageTypeKicked,&type,sizeof(UInt8),kMessagePriorityHigh,netID);
|
|
}
|
|
}
|
|
|
|
void NetworkChangePassword(char *pass)
|
|
{
|
|
HandleError(NT_SetSessionPassword(gSessionRef,pass));
|
|
}
|
|
|
|
//join a network game at the ip address given in the string address.
|
|
//returns an error code or 0. passes the player's id in id.
|
|
int JoinNetGame(char *address,char *errorString)
|
|
{
|
|
if(!*address)
|
|
{
|
|
sprintf(errorString,"No Address Entered.");
|
|
return false;
|
|
}
|
|
//PrintConsoleString(address);
|
|
NetworkAddress addy;
|
|
Result32 err=SimpleResolve(eNetworkStackTCPIP,address,&addy);
|
|
if(err)
|
|
{
|
|
sprintf(errorString,"Can't resolve host. (%d)",err);
|
|
return false;
|
|
}
|
|
|
|
gTerminateSession=false;
|
|
NetworkClearPacketQueue();
|
|
HandleError(NT_SessionRejoin(gSessionRef,&addy,kGameName,NULL,&err));
|
|
|
|
if(err==eNetworkToolErrorBadPass)
|
|
{
|
|
err=0;
|
|
char password[256];
|
|
if(InterfaceGetUserInputString("Please enter Game Password",password,256,true,true))
|
|
{
|
|
NetworkClearPacketQueue();
|
|
HandleError(NT_SessionRejoin(gSessionRef,&addy,kGameName,password,&err));
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
if(err)
|
|
{
|
|
switch(err)
|
|
{
|
|
case eNetworkToolErrorNoServer:
|
|
sprintf(errorString,"No Server Running on that Machine",err);
|
|
break;
|
|
case eNetworkToolErrorTimeout:
|
|
sprintf(errorString,"Network_Tool Timeout",err);
|
|
break;
|
|
case eNetworkToolErrorLatency:
|
|
sprintf(errorString,"Latency is too high.",err);
|
|
break;
|
|
case eNetworkToolErrorBanned:
|
|
sprintf(errorString,"You are banned from this host.",err);
|
|
break;
|
|
case eNetworkToolErrorTooMany:
|
|
sprintf(errorString,"The game is full.",err);
|
|
break;
|
|
case eNetworkToolErrorBadPass:
|
|
sprintf(errorString,"Wrong Password",err);
|
|
break;
|
|
case eNetworkToolErrorLocked:
|
|
sprintf(errorString,"Can't join in the middle of a game.",err);
|
|
break;
|
|
default:
|
|
sprintf(errorString,"Connection Error. (%d)",err);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
float startTime=TimeGetSeconds();
|
|
|
|
while(TimeGetSeconds()<startTime+kConnectionTimeOut)
|
|
{
|
|
tNetworkPacket message;
|
|
while(NetworkReceivePacket(&message))
|
|
{
|
|
//wait for servers response
|
|
if(message.what==kMessageTypeID)
|
|
{
|
|
NetworkQueuePacket(&message);
|
|
return true;
|
|
}
|
|
if(message.what==kMessageTypeVersion)
|
|
{
|
|
UInt32 reply=kVersion;
|
|
U32Swap(reply);
|
|
NetworkSendPacket(kMessageTypeVersion,&reply,sizeof(UInt32),kMessagePriorityHigh,message.from);
|
|
}
|
|
if(message.what==kMessageTypeJoinDenied)
|
|
{
|
|
if(*((UInt8*)message.data)==kJoinDeniedInProgress)
|
|
sprintf(errorString,"Can't join in the middle of a game.",err);
|
|
else if(*((UInt8*)message.data)==kJoinDeniedVersion)
|
|
sprintf(errorString,"Version Mismatch.",err);
|
|
else
|
|
sprintf(errorString,"Host rejected connection.",err);
|
|
//PrintConsoleString("Rejected. Exiting.");
|
|
return false;
|
|
}
|
|
if(message.what==kMessageTypeGameTerminated)
|
|
{
|
|
sprintf(errorString,"Game Terminated.",err);
|
|
return false;
|
|
}
|
|
NetworkDisposePacket(&message);
|
|
}
|
|
}
|
|
sprintf(errorString,"Connection Timed out.",err);
|
|
return false;
|
|
}
|
|
|
|
//exits a network game
|
|
void ExitNetGame()
|
|
{
|
|
if(gSessionRef)
|
|
{
|
|
// HandleError(NT_SessionLeave(gSessionRef));
|
|
// gSessionRef=nil;
|
|
}
|
|
}
|
|
|
|
//sends the data in *message to other players on the network
|
|
void NetworkSendPacket(char messageType,void *message,long size,int priority,int to,int notTo)
|
|
{
|
|
if(messageType==kMessageTypeGameTerminated)
|
|
gTerminateSession=true;
|
|
|
|
if(to==kMessageNoRecipients)
|
|
return;
|
|
|
|
if(size>kNetworkToolPacketSize-1)
|
|
FailWithErrorString("Packet too large.");
|
|
|
|
NT_MemberInfo info;
|
|
NT_GetSelfMemberInfo(gSessionRef,&info);
|
|
NT_MemberListType self=0x00000001<<info.member;
|
|
|
|
UInt8 data[kNetworkToolPacketSize];
|
|
MemoryMove(data+1,message,size);
|
|
*data=messageType;
|
|
if(size>kCompressionSize&&gAllowCompression)
|
|
{
|
|
int oldsize=size;
|
|
if(CompressPacket(data+1,&size)==eCommonErrorNone)
|
|
if(oldsize<size)
|
|
*data|=kCompressionFlag;
|
|
else
|
|
{
|
|
size=oldsize;
|
|
MemoryMove(data+1,message,size);
|
|
}
|
|
}
|
|
// U32Swap(*data);
|
|
|
|
NT_MemberListType members;
|
|
switch(to)
|
|
{
|
|
case kMessageSendToAll: members=eMemberListAll; break;
|
|
case kMessageSendToAllButSelf: members=eMemberListAll^self; break;
|
|
case kMessageSendToHostOnly: members=0x00000001; break;
|
|
default: members=0x00000001<<to;
|
|
}
|
|
if(notTo)
|
|
members&=~(0x00000001<<notTo);
|
|
|
|
if(members&self)
|
|
{
|
|
tNetworkPacket packet;
|
|
packet.data=MemoryAllocateBlock(size);
|
|
MemoryMove(packet.data,message,size);
|
|
packet.size=size;
|
|
packet.from=info.member;
|
|
packet.what=messageType;
|
|
|
|
NetworkQueuePacket(&packet);
|
|
}
|
|
|
|
Result32 err=0;
|
|
if(priority<=kMessagePriorityNormal)
|
|
err=NT_SendPacket(gSessionRef,members,(char*)data,size+4);
|
|
else
|
|
{
|
|
NT_SessionInfo sessInfo;
|
|
NT_MemberInfo member;
|
|
|
|
NT_GetSessionInfo(gSessionRef,&sessInfo);
|
|
for(int i=0;i<32;i++)
|
|
if((members&(0x00000001<<i))&&(i!=info.member))
|
|
if(NT_GetMemberInfo(gSessionRef,i,&member))
|
|
err=NT_SendRequest(gSessionRef,i,(char*)data,size+4);
|
|
}
|
|
if(err)
|
|
{
|
|
PrintConsoleString("Error Sending Packet (%d)",err);
|
|
gTerminateSession=true;
|
|
}
|
|
}
|
|
|
|
void NetworkSendPacket(char messageType,void *message,long size,int priority,int to)
|
|
{
|
|
NetworkSendPacket(messageType,message,size,priority,to,0);
|
|
}
|
|
|
|
int NetworkReceivePacket(tNetworkPacket* packet)
|
|
{
|
|
if(*gHistoryBuffer)
|
|
{
|
|
time_t t;
|
|
time(&t);
|
|
char ct[26];
|
|
ctime_r(&t,ct);
|
|
PrintConsoleString("Dumping Packet History %d. (%s)",gHistoryDumps,ct);
|
|
FILE *f=fopen("/tmp/packethistory.log","a");
|
|
fprintf(f,"Dump %d (%s)\n\n",gHistoryDumps++,ct);
|
|
char *ch=gHistoryBuffer;
|
|
while(*ch)
|
|
fwrite(ch++,1,1,f);
|
|
strcpy(gHistoryBuffer,"");
|
|
|
|
fprintf(f,"Debug Snapshot:");
|
|
NT_MemberListType mt=0xffff;
|
|
char buffer[64*1024];
|
|
NT_DebugSnapshot(gSessionRef,mt,buffer,32*1024);
|
|
ch=buffer;
|
|
while(*ch)
|
|
fwrite(ch++,1,1,f);
|
|
|
|
fclose(f);
|
|
}
|
|
if(gTerminateSession)
|
|
{
|
|
packet->what=kMessageTypeGameTerminated;
|
|
packet->data=NULL;
|
|
return true;
|
|
}
|
|
else if(QStubPtr rcv=QueueRemove(&gPacketQueue))
|
|
{
|
|
*packet=((tQueuedPacket*)rcv)->packet;
|
|
if(packet->what&kCompressionFlag)
|
|
{
|
|
DecompressPacket(packet->data,&packet->size);
|
|
packet->what^=kCompressionFlag;
|
|
}
|
|
MemoryFreeBlock(rcv);
|
|
return true;
|
|
}
|
|
if(gReggieConnected)
|
|
ReggieMessageCheck(gReggieRef);
|
|
return false;
|
|
}
|
|
|
|
void NetworkDisposePacket(tNetworkPacket* packet)
|
|
{
|
|
if(packet->data)
|
|
MemoryFreeBlock(packet->data);
|
|
}
|
|
|
|
void NetworkInit()
|
|
{
|
|
HandleError(NT_Open());
|
|
QueueCreate(&gPacketQueue);
|
|
}
|
|
|
|
void NetworkIdle()
|
|
{
|
|
static float lastStatus=0;
|
|
NT_Idle();
|
|
if(gSessionRef)
|
|
{
|
|
/* if(TimeGetSeconds()-lastStatus<0.5)
|
|
return;
|
|
lastStatus=TimeGetSeconds();
|
|
char str[16];
|
|
NetworkGetStatusString(str);
|
|
// printf("%s\n",str);
|
|
NT_MemberInfo info;
|
|
NT_GetSelfMemberInfo(gSessionRef,&info);
|
|
if(info.member)
|
|
{
|
|
if(gLastRTT<0)
|
|
printf("No Ping Response since %f seconds.\n",lastStatus-gLastPingReceived);
|
|
else
|
|
{
|
|
//printf("Last RTT: %f\n",gLastRTT);
|
|
gLastRTT=-1;
|
|
}
|
|
NetworkSendPacket(kMessageTypePing,&lastStatus,sizeof(float),kMessagePriorityLow,kMessageSendToHostOnly);
|
|
}*/
|
|
}
|
|
}
|
|
|
|
void NetworkExit()
|
|
{
|
|
if(gSessionRef)
|
|
NetworkSendPacket(kMessageTypeBye,0,NULL,kMessagePriorityHigh,kMessageSendToAll);
|
|
if(gReggieConnected)
|
|
ReggieDispose(gReggieRef,true);
|
|
NT_Close();
|
|
while(QStubPtr rcv=QueueRemove(&gPacketQueue))
|
|
MemoryFreeBlock(rcv);
|
|
QueueEmpty(&gPacketQueue);
|
|
}
|
|
|
|
//wait for a response from all players, to synchronize the start of the game.
|
|
int NetworkSynch(int numPlayers)
|
|
{
|
|
//PrintConsoleString("Synching %d...",numPlayers);
|
|
|
|
NetworkSendPacket(kMessageTypeSynch,nil,0,kMessagePriorityHigh,kMessageSendToAll);
|
|
UInt32 lastSynch=0;
|
|
UInt32 startClock;
|
|
int start=false;
|
|
tNetworkPacket leftMessage[kMaxPlayers];
|
|
int numLeftMessages=0;
|
|
|
|
int synchMessagesReceived=0;
|
|
while(synchMessagesReceived<numPlayers&&!start)
|
|
{
|
|
tNetworkPacket message;
|
|
if(NetworkReceivePacket(&message))
|
|
{
|
|
int cancel=false;
|
|
if(message.what==kMessageTypeSynch)
|
|
{
|
|
synchMessagesReceived++;
|
|
//PrintConsoleString("Received Synch packet from player %d",message.from);
|
|
}
|
|
else if(message.what==kMessageTypeEndGame||message.what==kMessageTypeGameTerminated)
|
|
{
|
|
cancel=true;
|
|
PrintConsoleString("Received Termination Packet. Aborting");
|
|
}
|
|
else if(message.what==kMessageTypePlayerLeft)
|
|
{
|
|
//cancel=true;
|
|
leftMessage[numLeftMessages++]=message;
|
|
numPlayers--;
|
|
PrintConsoleString("A Player Left.");
|
|
}
|
|
else if(message.what==kMessageTypeSynchStart)
|
|
{
|
|
//PrintConsoleString("Received Start Signal.");
|
|
|
|
startClock=*(UInt32*)message.data;
|
|
U32Swap(startClock);
|
|
start=true;
|
|
}
|
|
else
|
|
PrintConsoleString("Unexpected Packet");
|
|
|
|
NetworkDisposePacket(&message);
|
|
if(cancel)
|
|
return false;
|
|
}
|
|
SystemPoll(true);
|
|
}
|
|
|
|
|
|
NT_MemberInfo info;
|
|
NT_GetSelfMemberInfo(gSessionRef,&info);
|
|
if(info.member==0)
|
|
{
|
|
|
|
//PrintConsoleString("Sent Start Signal.");
|
|
|
|
startClock=NT_GetSessionClock(gSessionRef)+500;
|
|
U32Swap(startClock);
|
|
NetworkSendPacket(kMessageTypeSynchStart,&startClock,sizeof(UInt32),kMessagePriorityHigh,kMessageSendToAll);
|
|
U32Swap(startClock);
|
|
}
|
|
else
|
|
{
|
|
float startTime=TimeGetSeconds();
|
|
tNetworkPacket message;
|
|
|
|
while(TimeGetSeconds()<startTime+kConnectionTimeOut&&!start)
|
|
{
|
|
if(NetworkReceivePacket(&message))
|
|
{
|
|
if(message.what==kMessageTypeSynchStart)
|
|
{
|
|
//PrintConsoleString("Received Start Signal.");
|
|
|
|
startClock=*(UInt32*)message.data;
|
|
U32Swap(startClock);
|
|
start=true;
|
|
}
|
|
else if(message.what==kMessageTypePlayerLeft)
|
|
{
|
|
leftMessage[numLeftMessages++]=message;
|
|
}
|
|
else
|
|
PrintConsoleString("Unexpected Packet");
|
|
NetworkDisposePacket(&message);
|
|
}
|
|
SystemPoll(false);
|
|
}
|
|
if(!start)
|
|
return false;
|
|
}
|
|
for(int i=0;i<numLeftMessages;i++)
|
|
NetworkQueuePacket(leftMessage+i);
|
|
while(NT_GetSessionClock(gSessionRef)<startClock);
|
|
return true;
|
|
}
|
|
|