/// RakNet stuff void CDemo::UpdateRakNet(void) { RakNet::SystemAddress facilitatorSystemAddress(DEFAULT_NAT_PUNCHTHROUGH_FACILITATOR_IP, DEFAULT_NAT_PUNCHTHROUGH_FACILITATOR_PORT); RakNet::Packet *packet; RakNet::TimeMS curTime = RakNet::GetTimeMS(); RakNet::RakString targetName; for (packet=rakPeer->Receive(); packet; rakPeer->DeallocatePacket(packet), packet=rakPeer->Receive()) { if (strcmp(packet->systemAddress.ToString(false),DEFAULT_NAT_PUNCHTHROUGH_FACILITATOR_IP)==0) { targetName="NATPunchthroughServer"; } else { targetName=packet->systemAddress.ToString(true); } switch (packet->data[0]) { case ID_IP_RECENTLY_CONNECTED: { PushMessage(RakNet::RakString("This IP address recently connected from ") + targetName + RakNet::RakString(".")); if (packet->systemAddress==facilitatorSystemAddress) PushMessage("Multiplayer will not work without the NAT punchthrough server!"); } break; case ID_INCOMPATIBLE_PROTOCOL_VERSION: { PushMessage(RakNet::RakString("Incompatible protocol version from ") + targetName + RakNet::RakString(".")); if (packet->systemAddress==facilitatorSystemAddress) PushMessage("Multiplayer will not work without the NAT punchthrough server!"); } break; case ID_DISCONNECTION_NOTIFICATION: { PushMessage(RakNet::RakString("Disconnected from ") + targetName + RakNet::RakString(".")); if (packet->systemAddress==facilitatorSystemAddress) isConnectedToNATPunchthroughServer=false; } break; case ID_CONNECTION_LOST: { PushMessage(RakNet::RakString("Connection to ") + targetName + RakNet::RakString(" lost.")); if (packet->systemAddress==facilitatorSystemAddress) isConnectedToNATPunchthroughServer=false; } break; case ID_NO_FREE_INCOMING_CONNECTIONS: { PushMessage(RakNet::RakString("No free incoming connections to ") + targetName + RakNet::RakString(".")); if (packet->systemAddress==facilitatorSystemAddress) PushMessage("Multiplayer will not work without the NAT punchthrough server!"); } break; case ID_NEW_INCOMING_CONNECTION: { if (fullyConnectedMesh2->IsHostSystem()) { PushMessage(RakNet::RakString("Sending player list to new connection")); fullyConnectedMesh2->StartVerifiedJoin(packet->guid); } } break; case ID_FCM2_VERIFIED_JOIN_START: { DataStructures::List<RakNet::SystemAddress> addresses; DataStructures::List<RakNet::RakNetGUID> guids; fullyConnectedMesh2->GetVerifiedJoinRequiredProcessingList(packet->guid, addresses, guids); for (unsigned int i=0; i < guids.Size(); i++) natPunchthroughClient->OpenNAT(guids[i], facilitatorSystemAddress); } break; case ID_FCM2_VERIFIED_JOIN_FAILED: { PushMessage(RakNet::RakString("Failed to join game session")); } break; case ID_FCM2_VERIFIED_JOIN_CAPABLE: { fullyConnectedMesh2->RespondOnVerifiedJoinCapable(packet, true, 0); } break; case ID_FCM2_VERIFIED_JOIN_ACCEPTED: { DataStructures::List<RakNet::RakNetGUID> systemsAccepted; bool thisSystemAccepted; fullyConnectedMesh2->GetVerifiedJoinAcceptedAdditionalData(packet, &thisSystemAccepted, systemsAccepted, 0); if (thisSystemAccepted) PushMessage("Game join request accepted\n"); else PushMessage(RakNet::RakString("System %s joined the mesh\n", systemsAccepted[0].ToString())); // DataStructures::List<RakNetGUID> participantList; // fullyConnectedMesh2->GetParticipantList(participantList); for (unsigned int i=0; i < systemsAccepted.Size(); i++) replicaManager3->PushConnection(replicaManager3->AllocConnection(rakPeer->GetSystemAddressFromGuid(systemsAccepted[i]), systemsAccepted[i])); } break; case ID_CONNECTION_REQUEST_ACCEPTED: { PushMessage(RakNet::RakString("Connection request to ") + targetName + RakNet::RakString(" accepted.")); if (packet->systemAddress==facilitatorSystemAddress) { isConnectedToNATPunchthroughServer=true; // Open UPNP. struct UPNPDev * devlist = 0; devlist = upnpDiscover(1000, 0, 0, 0); if (devlist) { char lanaddr[64]; /* my ip address on the LAN */ struct UPNPUrls urls; struct IGDdatas data; if (UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr))==1) { // External port is the port people will be connecting to us on. This is our port as seen by the directory server // Internal port is the port RakNet was internally started on char eport[32], iport[32]; natPunchthroughClient->GetUPNPPortMappings(eport, iport, facilitatorSystemAddress); int r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, eport, iport, lanaddr, 0, "UDP", 0); if(r==UPNPCOMMAND_SUCCESS) { // UPNP done } } } // Query cloud for other running game instances RakNet::CloudQuery cloudQuery; cloudQuery.keys.Push(RakNet::CloudKey("IrrlichtDemo",0),_FILE_AND_LINE_); cloudClient->Get(&cloudQuery, packet->guid); } } break; case ID_FCM2_NEW_HOST: { if (packet->guid==rakPeer->GetMyGUID()) { // Original host dropped. I am the new session host. Upload to the cloud so new players join this system. RakNet::CloudKey cloudKey("IrrlichtDemo",0); cloudClient->Post(&cloudKey, 0, 0, rakPeer->GetGuidFromSystemAddress(facilitatorSystemAddress)); } } break; case ID_CLOUD_GET_RESPONSE: { RakNet::CloudQueryResult cloudQueryResult; cloudClient->OnGetReponse(&cloudQueryResult, packet); if (cloudQueryResult.rowsReturned.Size()>0) { PushMessage(RakNet::RakString("NAT punch to existing game instance")); natPunchthroughClient->OpenNAT(cloudQueryResult.rowsReturned[0]->clientGUID, facilitatorSystemAddress); } else { PushMessage(RakNet::RakString("Publishing new game instance")); // Start as a new game instance because no other games are running RakNet::CloudKey cloudKey("IrrlichtDemo",0); cloudClient->Post(&cloudKey, 0, 0, packet->guid); } cloudClient->DeallocateWithDefaultAllocator(&cloudQueryResult); } break; case ID_CONNECTION_ATTEMPT_FAILED: { PushMessage(RakNet::RakString("Connection attempt to ") + targetName + RakNet::RakString(" failed.")); if (packet->systemAddress==facilitatorSystemAddress) PushMessage("Multiplayer will not work without the NAT punchthrough server!"); } break; case ID_NAT_TARGET_NOT_CONNECTED: { RakNet::RakNetGUID recipientGuid; RakNet::BitStream bs(packet->data,packet->length,false); bs.IgnoreBytes(sizeof(RakNet::MessageID)); bs.Read(recipientGuid); targetName=recipientGuid.ToString(); PushMessage(RakNet::RakString("NAT target ") + targetName + RakNet::RakString(" not connected.")); } break; case ID_NAT_TARGET_UNRESPONSIVE: { RakNet::RakNetGUID recipientGuid; RakNet::BitStream bs(packet->data,packet->length,false); bs.IgnoreBytes(sizeof(RakNet::MessageID)); bs.Read(recipientGuid); targetName=recipientGuid.ToString(); PushMessage(RakNet::RakString("NAT target ") + targetName + RakNet::RakString(" unresponsive.")); } break; case ID_NAT_CONNECTION_TO_TARGET_LOST: { RakNet::RakNetGUID recipientGuid; RakNet::BitStream bs(packet->data,packet->length,false); bs.IgnoreBytes(sizeof(RakNet::MessageID)); bs.Read(recipientGuid); targetName=recipientGuid.ToString(); PushMessage(RakNet::RakString("NAT target connection to ") + targetName + RakNet::RakString(" lost.")); } break; case ID_NAT_ALREADY_IN_PROGRESS: { RakNet::RakNetGUID recipientGuid; RakNet::BitStream bs(packet->data,packet->length,false); bs.IgnoreBytes(sizeof(RakNet::MessageID)); bs.Read(recipientGuid); targetName=recipientGuid.ToString(); PushMessage(RakNet::RakString("NAT punchthrough to ") + targetName + RakNet::RakString(" in progress (skipping).")); } break; case ID_NAT_PUNCHTHROUGH_SUCCEEDED: { if (packet->data[1]==1) { PushMessage(RakNet::RakString("Connecting to existing game instance")); RakNet::ConnectionAttemptResult car = rakPeer->Connect(packet->systemAddress.ToString(false), packet->systemAddress.GetPort(), 0, 0); RakAssert(car==RakNet::CONNECTION_ATTEMPT_STARTED); } } break; case ID_ADVERTISE_SYSTEM: if (packet->guid!=rakPeer->GetGuidFromSystemAddress(RakNet::UNASSIGNED_SYSTEM_ADDRESS)) { char hostIP[32]; packet->systemAddress.ToString(false,hostIP); RakNet::ConnectionAttemptResult car = rakPeer->Connect(hostIP,packet->systemAddress.GetPort(),0,0); RakAssert(car==RakNet::CONNECTION_ATTEMPT_STARTED); } break; } } // Call the Update function for networked game objects added to BaseIrrlichtReplica once the game is ready if (currentScene>=1) { unsigned int idx; for (idx=0; idx < replicaManager3->GetReplicaCount(); idx++) ((BaseIrrlichtReplica*)(replicaManager3->GetReplicaAtIndex(idx)))->Update(curTime);; } }
/// RakNet stuff void CDemo::UpdateRakNet(void) { SystemAddress facilitatorSystemAddress(DEFAULT_NAT_PUNCHTHROUGH_FACILITATOR_IP, DEFAULT_NAT_PUNCHTHROUGH_FACILITATOR_PORT); Packet *packet; RakNetTime curTime = RakNet::GetTime(); RakNet::RakString targetName; for (packet=rakPeer->Receive(); packet; rakPeer->DeallocatePacket(packet), packet=rakPeer->Receive()) { if (strcmp(packet->systemAddress.ToString(false),DEFAULT_NAT_PUNCHTHROUGH_FACILITATOR_IP)==0) { targetName="NATPunchthroughServer"; } else { targetName=packet->systemAddress.ToString(true); } switch (packet->data[0]) { case ID_IP_RECENTLY_CONNECTED: { PushMessage(RakNet::RakString("This IP address recently connected from ") + targetName + RakNet::RakString(".")); if (packet->systemAddress==facilitatorSystemAddress) PushMessage("Multiplayer will not work without the NAT punchthrough server!"); } break; case ID_INCOMPATIBLE_PROTOCOL_VERSION: { PushMessage(RakNet::RakString("Incompatible protocol version from ") + targetName + RakNet::RakString(".")); if (packet->systemAddress==facilitatorSystemAddress) PushMessage("Multiplayer will not work without the NAT punchthrough server!"); } break; case ID_DISCONNECTION_NOTIFICATION: { PushMessage(RakNet::RakString("Disconnected from ") + targetName + RakNet::RakString(".")); if (packet->systemAddress==facilitatorSystemAddress) isConnectedToNATPunchthroughServer=false; } break; case ID_CONNECTION_LOST: { PushMessage(RakNet::RakString("Connection to ") + targetName + RakNet::RakString(" lost.")); if (packet->systemAddress==facilitatorSystemAddress) isConnectedToNATPunchthroughServer=false; } break; case ID_NO_FREE_INCOMING_CONNECTIONS: { PushMessage(RakNet::RakString("No free incoming connections to ") + targetName + RakNet::RakString(".")); if (packet->systemAddress==facilitatorSystemAddress) PushMessage("Multiplayer will not work without the NAT punchthrough server!"); } break; case ID_NEW_INCOMING_CONNECTION: { PushMessage(RakNet::RakString("New incoming connection from ") + targetName + RakNet::RakString(".")); // Add this system as a new player replicaManager3->PushConnection(replicaManager3->AllocConnection(packet->systemAddress, packet->guid)); } break; case ID_CONNECTION_REQUEST_ACCEPTED: { PushMessage(RakNet::RakString("Connection request to ") + targetName + RakNet::RakString(" accepted.")); if (packet->systemAddress==facilitatorSystemAddress) { isConnectedToNATPunchthroughServer=true; } else { // Add this system as a new player replicaManager3->PushConnection(replicaManager3->AllocConnection(packet->systemAddress, packet->guid)); } } break; case ID_CONNECTION_ATTEMPT_FAILED: { PushMessage(RakNet::RakString("Connection attempt to ") + targetName + RakNet::RakString(" failed.")); if (packet->systemAddress==facilitatorSystemAddress) PushMessage("Multiplayer will not work without the NAT punchthrough server!"); } break; case ID_NAT_TARGET_NOT_CONNECTED: { RakNetGUID recipientGuid; RakNet::BitStream bs(packet->data,packet->length,false); bs.IgnoreBytes(sizeof(MessageID)); bs.Read(recipientGuid); targetName=recipientGuid.ToString(); PushMessage(RakNet::RakString("NAT target ") + targetName + RakNet::RakString(" not connected.")); } break; case ID_NAT_TARGET_UNRESPONSIVE: { RakNetGUID recipientGuid; RakNet::BitStream bs(packet->data,packet->length,false); bs.IgnoreBytes(sizeof(MessageID)); bs.Read(recipientGuid); targetName=recipientGuid.ToString(); PushMessage(RakNet::RakString("NAT target ") + targetName + RakNet::RakString(" unresponsive.")); } break; case ID_NAT_CONNECTION_TO_TARGET_LOST: { RakNetGUID recipientGuid; RakNet::BitStream bs(packet->data,packet->length,false); bs.IgnoreBytes(sizeof(MessageID)); bs.Read(recipientGuid); targetName=recipientGuid.ToString(); PushMessage(RakNet::RakString("NAT target connection to ") + targetName + RakNet::RakString(" lost.")); } break; case ID_NAT_ALREADY_IN_PROGRESS: { RakNetGUID recipientGuid; RakNet::BitStream bs(packet->data,packet->length,false); bs.IgnoreBytes(sizeof(MessageID)); bs.Read(recipientGuid); targetName=recipientGuid.ToString(); PushMessage(RakNet::RakString("NAT punchthrough to ") + targetName + RakNet::RakString(" in progress (skipping).")); } break; case ID_NAT_PUNCHTHROUGH_FAILED: { targetName=packet->guid.ToString(); unsigned char weAreSender=packet->data[1]; if (weAreSender) { PushMessage(RakNet::RakString("Punchthrough to ") + targetName + RakNet::RakString(" failed. Using proxy.")); udpProxyClient->RequestForwarding(facilitatorSystemAddress, UNASSIGNED_SYSTEM_ADDRESS, packet->guid, 7000); } else { PushMessage(RakNet::RakString("Punchthrough to ") + targetName + RakNet::RakString(" failed. Remote system should connect via proxy.")); } } break; case ID_NAT_PUNCHTHROUGH_SUCCEEDED: { unsigned char weAreSender=packet->data[1]; if (weAreSender) { PushMessage(RakNet::RakString("Punchthrough to ") + targetName + RakNet::RakString(" succeeded. Connecting.")); rakPeer->Connect(packet->systemAddress.ToString(false), packet->systemAddress.port,0,0); } else { PushMessage(RakNet::RakString("Punchthrough to ") + targetName + RakNet::RakString(" succeeded.")); } } break; case ID_ADVERTISE_SYSTEM: if (packet->guid!=rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)) { char hostIP[32]; packet->systemAddress.ToString(false,hostIP); rakPeer->Connect(hostIP,packet->systemAddress.port,0,0); } break; } } for (packet=tcpInterface->Receive(); packet; tcpInterface->DeallocatePacket(packet), packet=tcpInterface->Receive()) { httpConnection->ProcessTCPPacket(packet); } if (httpConnection->HasRead()) { RakNet::RakString httpResult = httpConnection->Read(); // Good response, let the PHPDirectoryServer2 class handle the data // If resultCode is not an empty string, then we got something other than a table // (such as delete row success notification, or the message is for HTTP only and not for this class). HTTPReadResult readResult = phpDirectoryServer2->ProcessHTTPRead(httpResult); if (readResult==HTTP_RESULT_GOT_TABLE) { /// Got a table which was stored internally. Print it out const DataStructures::Table *games = phpDirectoryServer2->GetLastDownloadedTable(); // __GAME_NAME is an automatic column passed to PHPDirectoryServer::UploadTable unsigned int gameNameIndex = games->ColumnIndex("__GAME_NAME"); // RakNetGUID is a column we manually added using our own GUID with PHPDirectoryServer::SetField // We need to use RakNetGUID because NAT punchthrough refers to systems by RakNetGUID, rather than by SystemAddress unsigned int guidIndex = games->ColumnIndex("RakNetGUID"); DataStructures::Table::Row *row; unsigned int i; unsigned int tableSize=0, connectionCount=0; DataStructures::Page<unsigned, DataStructures::Table::Row*, _TABLE_BPLUS_TREE_ORDER> *cur = games->GetRows().GetListHead(); while (cur) { for (i=0; i < (unsigned)cur->size; i++) { RakAssert(gameNameIndex!=-1); row = cur->data[i]; // Make sure it's the same game, since the PHPDirectoryServer can return other games too if (gameNameIndex!=-1 && strcmp(row->cells[gameNameIndex]->c, "IrrlichtDemo")==0) { tableSize++; RakNet::RakString guidStr = row->cells[guidIndex]->c; RakNetGUID guid; guid.FromString(guidStr.C_String()); // Connect as long as I'm not connecting to myself if (guid!=rakPeer->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS)) { connectionCount++; systemsToConnectTo.Push(guid); } } } cur=cur->next; } PushMessage(RakNet::RakString("DirectoryServer returned %i rows, of which we are connecting to %i",tableSize,connectionCount)); } } // Update our two classes so they can do time-based updates httpConnection->Update(); phpDirectoryServer2->Update(); // Start punchthrough to all pending systems, which we got from the PHP directory server code about 20 lines above if (isConnectedToNATPunchthroughServer) { while (systemsToConnectTo.GetSize()>0) { RakNetGUID g = systemsToConnectTo.Pop(__FILE__,__LINE__); natPunchthroughClient->OpenNAT(g, facilitatorSystemAddress); targetName=g.ToString(); PushMessage(RakNet::RakString("Punchthrough to ") + targetName + RakNet::RakString(" started.")); } } // Update non-plugin helper classes. // This keeps our system listed on the PHP directory server httpConnection->Update(); phpDirectoryServer2->Update(); // Call the Update function for networked game objects added to BaseIrrlichtReplica once the game is ready if (currentScene>=1) { DataStructures::DefaultIndexType idx; for (idx=0; idx < replicaManager3->GetReplicaCount(); idx++) ((BaseIrrlichtReplica*)(replicaManager3->GetReplicaAtIndex(idx)))->Update(curTime);; } }