int main(void) { printf("This project demonstrates an in-game lobby using the team manager plugin.\n"); printf("Difficulty: Intermediate\n\n"); rakPeer=RakNet::RakPeerInterface::GetInstance(); fullyConnectedMesh2=FullyConnectedMesh2::GetInstance(); teamManager=TeamManager::GetInstance(); networkIDManager = NetworkIDManager::GetInstance(); replicaManager3=new SampleRM3; // test offline mode /* TM_TeamMember tmTeamMember; TM_Team tmTeam; teamManager->AddWorld(0); teamManager->GetWorldAtIndex(0)->ReferenceTeam(&tmTeam,1,false); teamManager->GetWorldAtIndex(0)->ReferenceTeamMember(&tmTeamMember,0); tmTeamMember.RequestTeam(TeamSelection::AnyAvailable()); RakAssert(tmTeam.GetTeamMembersCount()==1); tmTeam.LeaveTeam(&tmTeamMember, 255); RakAssert(tmTeam.GetTeamMembersCount()==0); */ rakPeer->AttachPlugin(fullyConnectedMesh2); rakPeer->AttachPlugin(teamManager); rakPeer->AttachPlugin(replicaManager3); rakPeer->AttachPlugin(fullyConnectedMesh2); // Make it so all new connections are registered with FullyConnectedMesh2 fullyConnectedMesh2->SetAutoparticipateConnections(true); // Allocate a world instance to be used for team operations teamManager->AddWorld(0); // Tell ReplicaManager3 which networkIDManager to use for object lookup, used for automatic serialization replicaManager3->SetNetworkIDManager(networkIDManager); // Tell ReplicaManager3 and TeamManager to not automatically add new connections, because we wait for host calculation to complete from FullyConnectedMesh2 first replicaManager3->SetAutoManageConnections(false,true); teamManager->SetAutoManageConnections(false); // Just setup user data as an example Team teams[TEAM_TYPES_COUNT]; teams[(int)PLAYER_TEAM_1].teamName="Player_Team_1"; teams[(int)PLAYER_TEAM_2].teamName="Player_Team 2"; teams[(int)REFEREE_TEAM].teamName="Referee_Team"; for (unsigned int i=0; i < TEAM_TYPES_COUNT; i++) { // Static objects require additional setup before calling reference. teams[i].SetNetworkIDManager(networkIDManager); teams[i].SetNetworkID(i); // NetworkID value doesn't matter, just needs to be unique // We serialize teams before team members, this is required by TeamManager during remote object construction. Serialization occurs in the order that Reference() is called on the object replicaManager3->Reference(&teams[i]); // Register the team with the teamManager plugin // Do not apply team balancing to the referee team bool balancingAppliesToThisTeam = i!=REFEREE_TEAM; teamManager->GetWorldAtIndex(0)->ReferenceTeam(&teams[i].tmTeam,teams[i].GetNetworkID(),balancingAppliesToThisTeam); if (i==REFEREE_TEAM) teams[i].tmTeam.SetMemberLimit(1,0); else teams[i].tmTeam.SetMemberLimit(2,0); } // Only join the referee team on specific request teams[REFEREE_TEAM].tmTeam.SetJoinPermissions(ALLOW_JOIN_SPECIFIC_TEAM); // Setup my own User *user = new User; user->userName = rakPeer->GetMyGUID().ToString(); // Inform ReplicaManager3 of my user replicaManager3->Reference(user); // Inform TeamManager of my user's team member info teamManager->GetWorldAtIndex(0)->ReferenceTeamMember(&user->tmTeamMember,user->GetNetworkID()); // Startup RakNet RakNet::SocketDescriptor sd; sd.socketFamily=AF_INET; // Only IPV4 supports broadcast on 255.255.255.255 sd.port=60000; while (SocketLayer::IsPortInUse(sd.port, sd.hostAddress, sd.socketFamily)==true) sd.port++; StartupResult sr = rakPeer->Startup(8,&sd,1); RakAssert(sr==RAKNET_STARTED); rakPeer->SetMaximumIncomingConnections(8); rakPeer->SetTimeoutTime(30000,RakNet::UNASSIGNED_SYSTEM_ADDRESS); printf("Our guid is %s\n", rakPeer->GetGuidFromSystemAddress(RakNet::UNASSIGNED_SYSTEM_ADDRESS).ToString()); printf("Started on %s\n", rakPeer->GetMyBoundAddress().ToString(true)); for (int i=0; i < 32; i++) { if (rakPeer->GetInternalID(RakNet::UNASSIGNED_SYSTEM_ADDRESS,0).GetPort()!=60000+i) rakPeer->AdvertiseSystem("255.255.255.255", 60000+i, 0,0,0); } PrintCommands(); bool success; bool quit=false; char ch; Packet *packet; while (!quit) { for (packet = rakPeer->Receive(); packet; rakPeer->DeallocatePacket(packet), packet = rakPeer->Receive()) { switch (packet->data[0]) { case ID_DISCONNECTION_NOTIFICATION: printf("ID_DISCONNECTION_NOTIFICATION\n"); break; case ID_NEW_INCOMING_CONNECTION: { printf("ID_NEW_INCOMING_CONNECTION from %s. guid=%s.\n", packet->systemAddress.ToString(true), packet->guid.ToString()); // Add mid-game joins to ReplicaManager3 as long as we know who the host is if (fullyConnectedMesh2->GetConnectedHost()!=UNASSIGNED_RAKNET_GUID) { bool success = replicaManager3->PushConnection(replicaManager3->AllocConnection(packet->systemAddress, packet->guid)); RakAssert(success); teamManager->GetWorldAtIndex(0)->AddParticipant(packet->guid); } } break; case ID_CONNECTION_REQUEST_ACCEPTED: { printf("ID_CONNECTION_REQUEST_ACCEPTED from %s. guid=%s.\n", packet->systemAddress.ToString(true), packet->guid.ToString()); // Add mid-game joins to ReplicaManager3 as long as we know who the host is if (fullyConnectedMesh2->GetConnectedHost()!=UNASSIGNED_RAKNET_GUID) { bool success = replicaManager3->PushConnection(replicaManager3->AllocConnection(packet->systemAddress, packet->guid)); RakAssert(success); teamManager->GetWorldAtIndex(0)->AddParticipant(packet->guid); } } break; case ID_CONNECTION_LOST: printf("ID_CONNECTION_LOST\n"); break; case ID_ADVERTISE_SYSTEM: if (packet->guid!=rakPeer->GetMyGUID()) rakPeer->Connect(packet->systemAddress.ToString(false), packet->systemAddress.GetPort(),0,0); break; case ID_FCM2_NEW_HOST: { if (packet->guid==rakPeer->GetMyGUID()) printf("Got new host (ourselves)"); else printf("Got new host %s, GUID=%s", packet->systemAddress.ToString(true), packet->guid.ToString()); RakNet::BitStream bs(packet->data,packet->length,false); bs.IgnoreBytes(1); RakNetGUID oldHost; bs.Read(oldHost); // If the old host is different, then this message was due to losing connection to the host. if (oldHost!=packet->guid) printf(". Oldhost Guid=%s\n", oldHost.ToString()); else printf("\n"); if (oldHost==UNASSIGNED_RAKNET_GUID) { // First time calculated host. Add existing connections to ReplicaManager3 RegisterFCM2Participants(); } } break; case ID_TEAM_BALANCER_TEAM_ASSIGNED: { printf("ID_TEAM_BALANCER_TEAM_ASSIGNED for "); TM_World *world; TM_TeamMember *teamMember; teamManager->DecodeTeamAssigned(packet, &world, &teamMember); printf("worldId=%i teamMember=%s\n", world->GetWorldId(), ((User*)teamMember->GetOwner())->userName.C_String()); } break; case ID_TEAM_BALANCER_REQUESTED_TEAM_FULL: { printf("ID_TEAM_BALANCER_REQUESTED_TEAM_FULL\n"); } break; case ID_TEAM_BALANCER_REQUESTED_TEAM_LOCKED: { printf("ID_TEAM_BALANCER_REQUESTED_TEAM_LOCKED\n"); } break; case ID_TEAM_BALANCER_TEAM_REQUESTED_CANCELLED: { printf("ID_TEAM_BALANCER_TEAM_REQUESTED_CANCELLED\n"); } break; } } if (kbhit()) { ch=getch(); if (ch=='A' || ch=='a') { printf("Request any team\n"); success = user->tmTeamMember.RequestTeam(TeamSelection::AnyAvailable()); printf("Success=%i\n", success); } if (ch=='B' || ch=='b') { printf("Request specific team\n"); char buff1[256]; printf("Enter team index (0-2): "); gets(buff1); if (buff1[0]!=0 && buff1[0]>='0' && buff1[0]<='2') { success = user->tmTeamMember.RequestTeam(TeamSelection::SpecificTeam(&(teams[buff1[0]-'0'].tmTeam))); printf("Success=%i\n", success); } else { printf("Aborted\n"); } } if (ch=='C' || ch=='c') { printf("Request team switch\n"); char buff1[256]; printf("Enter team index to join (0-2): "); gets(buff1); char buff2[256]; printf("Enter team index to leave (0-2) or leave empty for all: "); gets(buff2); if (buff1[0]!=0 && buff1[0]>='0' && buff1[0]<='2' && (buff2[0]==0 || (buff2[0]>='0' && buff2[0]<='2'))) { if (buff2[0]) success = user->tmTeamMember.RequestTeamSwitch(&(teams[buff1[0]-'0'].tmTeam), &teams[buff2[0]-'0'].tmTeam); else success = user->tmTeamMember.RequestTeamSwitch(&(teams[buff1[0]-'0'].tmTeam), 0); printf("Success=%i\n", success); } else { printf("Aborted\n"); } } if (ch=='D' || ch=='d') { printf("Cancel request team\n"); char buff1[256]; printf("Enter team index to cancel (0-2) or leave empty for all: "); gets(buff1); if ((buff1[0]!=0 && buff1[0]>='0' && buff1[0]<='2') || buff1[0]==0) { if (buff1[0]) success = user->tmTeamMember.CancelTeamRequest(&(teams[buff1[0]-'0'].tmTeam)); else success = user->tmTeamMember.CancelTeamRequest(0); printf("Success=%i\n", success); } else { printf("Aborted\n"); } } if (ch=='E' || ch=='e') { printf("Leave specific team\n"); char buff1[256]; printf("Enter team index to leave (0-2): "); gets(buff1); if (buff1[0]!=0 && buff1[0]>='0' && buff1[0]<='2') { success = user->tmTeamMember.LeaveTeam(&(teams[buff1[0]-'0'].tmTeam),0); printf("Success=%i\n", success); } else { printf("Aborted\n"); } } if (ch=='F' || ch=='f') { printf("Leave all teams\n"); success = user->tmTeamMember.LeaveAllTeams(0); printf("Success=%i\n", success); } if (ch=='G' || ch=='g') { printf("Set team member limit\n"); char buff1[256]; printf("Enter team index to operate on (0-2): "); gets(buff1); char buff2[256]; printf("Enter limit (0-9): "); gets(buff2); if (buff1[0]!=0 && buff1[0]>='0' && buff1[0]<='2' && buff2[0]!=0 && buff2[0]>='0' && buff2[0]<='9') { success = teams[buff1[0]-'0'].tmTeam.SetMemberLimit(buff2[0]-'0',0); printf("Success=%i\n", success); } else { printf("Aborted\n"); } } if (ch=='H' || ch=='h') { printf("Turn on balance teams setting\n"); success = teamManager->GetWorldAtIndex(0)->SetBalanceTeams(true,0); printf("Success=%i\n", success); } if (ch=='I' || ch=='i') { printf("Turn off balance teams setting\n"); success = teamManager->GetWorldAtIndex(0)->SetBalanceTeams(false,0); printf("Success=%i\n", success); } if (ch==' ') { if (teamManager->GetWorldAtIndex(0)->GetBalanceTeams()) printf("Team balancing is on\n"); else printf("Team balancing is off\n"); for (unsigned int i=0; i < TEAM_TYPES_COUNT; i++) { printf("Team %i. %s %i/%i members ", i+1, teams[i].teamName.C_String(), teams[i].tmTeam.GetTeamMembersCount(), teams[i].tmTeam.GetMemberLimit()); for (unsigned int j=0; j < teams[i].tmTeam.GetTeamMembersCount(); j++) { User *u = (User *) teams[i].tmTeam.GetTeamMemberByIndex(j)->GetOwner(); printf("%s ", u->userName.C_String()); } printf("\n"); } unsigned int numUsers = teamManager->GetWorldAtIndex(0)->GetTeamMemberCount(); for (unsigned int i=0; i < numUsers; i++) { User *u = (User *) teamManager->GetWorldAtIndex(0)->GetTeamMemberByIndex(i)->GetOwner(); printf("User %i/%i. %s ", i+1, numUsers, u->userName.C_String()); u->PrintTeamStatus(); printf("\n"); } printf("\n"); } else if (ch=='q' || ch=='Q') { printf("Quitting.\n"); quit=true; } } RakSleep(30); } rakPeer->Shutdown(100); replicaManager3->Clear(); RakNet::RakPeerInterface::DestroyInstance(rakPeer); delete replicaManager3; RakNet::FullyConnectedMesh2::DestroyInstance(fullyConnectedMesh2); RakNet::TeamManager::DestroyInstance(teamManager); RakNet::NetworkIDManager::DestroyInstance(networkIDManager); for (unsigned int i=0; i < TEAM_TYPES_COUNT; i++) { // Teams are globally deallocated after NetworkIDManager, so prevent crash on automatic dereference teams[i].SetNetworkIDManager(0); } return 1; }
int main(void) { printf("Demonstrates networking elements for a P2P game on the PC, self-released,\nwith player hosted game servers\n"); printf("Difficulty: Advanced\n\n"); // --------------------------------------------------------------------------------------------------------------------- // Allocate plugins. See declaration in this file for description of each // --------------------------------------------------------------------------------------------------------------------- rakPeer=RakNet::RakPeerInterface::GetInstance(); teamManager=TeamManager::GetInstance(); fullyConnectedMesh2=FullyConnectedMesh2::GetInstance(); networkIDManager = NetworkIDManager::GetInstance(); cloudClient = CloudClient::GetInstance(); natPunchthroughClient = NatPunchthroughClient::GetInstance(); #ifdef NAT_TYPE_DETECTION_SERVER natTypeDetectionClient = NatTypeDetectionClient::GetInstance(); #endif rpc4 = RPC4::GetInstance(); readyEvent = ReadyEvent::GetInstance(); replicaManager3=new SampleRM3; // --------------------------------------------------------------------------------------------------------------------- // Attach plugins // --------------------------------------------------------------------------------------------------------------------- rakPeer->AttachPlugin(fullyConnectedMesh2); rakPeer->AttachPlugin(teamManager); rakPeer->AttachPlugin(cloudClient); rakPeer->AttachPlugin(natPunchthroughClient); #ifdef NAT_TYPE_DETECTION_SERVER rakPeer->AttachPlugin(natTypeDetectionClient); #endif rakPeer->AttachPlugin(rpc4); rakPeer->AttachPlugin(readyEvent); rakPeer->AttachPlugin(replicaManager3); // --------------------------------------------------------------------------------------------------------------------- // Setup plugins: Disable automatically adding new connections. Allocate initial objects and register for replication // --------------------------------------------------------------------------------------------------------------------- // Allocate a world instance to be used for team operations teamManager->AddWorld(0); // Do not automatically count new connections teamManager->SetAutoManageConnections(false); // New connections do not count until after login. fullyConnectedMesh2->SetAutoparticipateConnections(false); // Tell ReplicaManager3 which networkIDManager to use for object lookup, used for automatic serialization replicaManager3->SetNetworkIDManager(networkIDManager); // Do not automatically count new connections, but do drop lost connections automatically replicaManager3->SetAutoManageConnections(false,true); // Reference static game objects that always exist game = new Game; game->SetNetworkIDManager(networkIDManager); game->SetNetworkID(0); replicaManager3->Reference(game); // Setup my own user User *user = new User; user->SetNetworkIDManager(networkIDManager); user->userName = rakPeer->GetMyGUID().ToString(); // Inform TeamManager of my user's team member info teamManager->GetWorldAtIndex(0)->ReferenceTeamMember(&user->tmTeamMember,user->GetNetworkID()); // --------------------------------------------------------------------------------------------------------------------- // Startup RakNet on first available port // --------------------------------------------------------------------------------------------------------------------- RakNet::SocketDescriptor sd; sd.socketFamily=AF_INET; // Only IPV4 supports broadcast on 255.255.255.255 sd.port=0; StartupResult sr = rakPeer->Startup(8,&sd,1); RakAssert(sr==RAKNET_STARTED); rakPeer->SetMaximumIncomingConnections(8); rakPeer->SetTimeoutTime(30000,RakNet::UNASSIGNED_SYSTEM_ADDRESS); printf("Our guid is %s\n", rakPeer->GetGuidFromSystemAddress(RakNet::UNASSIGNED_SYSTEM_ADDRESS).ToString()); printf("Started on %s\n", rakPeer->GetMyBoundAddress().ToString(true)); // Connect to master server game->EnterPhase(Game::CONNECTING_TO_SERVER); // --------------------------------------------------------------------------------------------------------------------- // Read packets loop // --------------------------------------------------------------------------------------------------------------------- char ch; Packet *packet; while (game->phase!=Game::EXIT_SAMPLE) { for (packet = rakPeer->Receive(); packet; rakPeer->DeallocatePacket(packet), packet = rakPeer->Receive()) { switch (packet->data[0]) { case ID_NEW_INCOMING_CONNECTION: { printf("ID_NEW_INCOMING_CONNECTION from %s. guid=%s.\n", packet->systemAddress.ToString(true), packet->guid.ToString()); } break; case ID_CONNECTION_REQUEST_ACCEPTED: { printf("ID_CONNECTION_REQUEST_ACCEPTED from %s,guid=%s\n", packet->systemAddress.ToString(true), packet->guid.ToString()); if (game->phase==Game::CONNECTING_TO_SERVER) { game->masterServerAddress=packet->systemAddress; game->masterServerGuid=packet->guid; // --------------------------------------------------------------------------------------------------------------------- // PC self-hosted servers only: Use master server to determine NAT type. Attempt to open router if needed. // --------------------------------------------------------------------------------------------------------------------- if (NAT_TYPE_DETECTION_SERVER) { game->EnterPhase(Game::DETERMINE_NAT_TYPE); } else { OpenUPNP(); game->EnterPhase(Game::SEARCH_FOR_GAMES); } } else if (game->phase==Game::CONNECTING_TO_GAME_HOST) { printf("Asking host to join session...\n"); // So time in single player does not count towards which system has been running multiplayer the longest fullyConnectedMesh2->ResetHostCalculation(); // Custom message to ask to join the game // We first connect to the game host, and the game host is responsible for calling StartVerifiedJoin() for us to join the session BitStream bsOut; bsOut.Write((MessageID)ID_USER_PACKET_ENUM); rakPeer->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->guid,false); } } break; case ID_CONNECTION_LOST: case ID_DISCONNECTION_NOTIFICATION: if (game->phase==Game::DETERMINE_NAT_TYPE) { printf("Lost connection during NAT type detection. Reason %s. Retrying...\n", PacketLogger::BaseIDTOString(packet->data[0])); game->EnterPhase(Game::CONNECTING_TO_SERVER); } else if (game->phase==Game::NAT_PUNCH_TO_GAME_HOST) { printf("Lost connection during NAT punch to game host. Reason %s.\n", PacketLogger::BaseIDTOString(packet->data[0])); game->EnterPhase(Game::SEARCH_FOR_GAMES); } else { if (packet->guid==game->masterServerGuid) { printf("Server connection lost. Reason %s.\nGame session is no longer searchable.\n", PacketLogger::BaseIDTOString(packet->data[0])); } else { printf("Peer connection lost. Reason %s.\n", PacketLogger::BaseIDTOString(packet->data[0])); } } break; case ID_ALREADY_CONNECTED: printf("ID_ALREADY_CONNECTED with guid %"PRINTF_64_BIT_MODIFIER"u\n", packet->guid); break; case ID_INVALID_PASSWORD: case ID_NO_FREE_INCOMING_CONNECTIONS: case ID_CONNECTION_ATTEMPT_FAILED: case ID_CONNECTION_BANNED: case ID_IP_RECENTLY_CONNECTED: case ID_INCOMPATIBLE_PROTOCOL_VERSION: // Note: Failing to connect to another system does not automatically mean we cannot join a session, since that system may be disconnecting from the host simultaneously // FullyConnectedMesh2::StartVerifiedJoin() internally handles success or failure and notifies the client through ID_FCM2_VERIFIED_JOIN_FAILED if needed. printf("Failed to connect to %s. Reason %s\n", packet->systemAddress.ToString(true), PacketLogger::BaseIDTOString(packet->data[0])); if (game->phase==Game::CONNECTING_TO_SERVER) game->EnterPhase(Game::EXIT_SAMPLE); break; case ID_FCM2_NEW_HOST: { RakNet::BitStream bs(packet->data,packet->length,false); bs.IgnoreBytes(1); RakNetGUID oldHost; bs.Read(oldHost); if (packet->guid==rakPeer->GetMyGUID()) { if (oldHost!=UNASSIGNED_RAKNET_GUID) { PostRoomToCloud(); printf("ID_FCM2_NEW_HOST: Taking over as host from the old host.\nNew options:\n"); } else { // Room not hosted if we become host the first time since this was done in CreateRoom() already printf("ID_FCM2_NEW_HOST: We have become host for the first time. New options:\n"); } printf("(L)ock and unlock game\n"); } else { if (oldHost!=UNASSIGNED_RAKNET_GUID) printf("ID_FCM2_NEW_HOST: A new system %s has become host, GUID=%s\n", packet->systemAddress.ToString(true), packet->guid.ToString()); else printf("ID_FCM2_NEW_HOST: System %s is host, GUID=%s\n", packet->systemAddress.ToString(true), packet->guid.ToString()); } if (oldHost==UNASSIGNED_RAKNET_GUID) { // First time calculated host. Add existing connections to ReplicaManager3 DataStructures::List<RakNetGUID> participantList; fullyConnectedMesh2->GetParticipantList(participantList); for (unsigned int i=0; i < participantList.Size(); i++) RegisterGameParticipant(participantList[i]); // Reference previously created replicated objects, which cannot be serialized until host is known the first time if (packet->guid==rakPeer->GetMyGUID()) { // As host, reference the teams we created for (unsigned int i=0; i < game->teams.Size(); i++) replicaManager3->Reference(game->teams[i]); } // Reference the user we created (host or not) for (unsigned int i=0; i < game->users.Size(); i++) replicaManager3->Reference(game->users[i]); } } break; case ID_TEAM_BALANCER_TEAM_ASSIGNED: { printf("ID_TEAM_BALANCER_TEAM_ASSIGNED for "); TM_World *world; TM_TeamMember *teamMember; teamManager->DecodeTeamAssigned(packet, &world, &teamMember); printf("worldId=%i teamMember=%s", world->GetWorldId(), ((User*)teamMember->GetOwner())->userName.C_String()); if (teamMember->GetCurrentTeam()==0) printf(" not on team\n"); else printf(" on team %s\n", ((Team*)(teamMember->GetCurrentTeam()->GetOwner()))->teamName.C_String()); } break; case ID_TEAM_BALANCER_REQUESTED_TEAM_FULL: { printf("ID_TEAM_BALANCER_REQUESTED_TEAM_FULL\n"); } break; case ID_TEAM_BALANCER_REQUESTED_TEAM_LOCKED: { printf("ID_TEAM_BALANCER_REQUESTED_TEAM_LOCKED\n"); } break; case ID_TEAM_BALANCER_TEAM_REQUESTED_CANCELLED: { printf("ID_TEAM_BALANCER_TEAM_REQUESTED_CANCELLED\n"); } break; case ID_NAT_TARGET_NOT_CONNECTED: case ID_NAT_TARGET_UNRESPONSIVE: case ID_NAT_CONNECTION_TO_TARGET_LOST: case ID_NAT_PUNCHTHROUGH_FAILED: { // As with connection failed, this does not automatically mean we cannot join the session // We only fail on ID_FCM2_VERIFIED_JOIN_FAILED printf("NAT punch to %s failed. Reason %s\n", packet->guid.ToString(), PacketLogger::BaseIDTOString(packet->data[0])); if (game->phase==Game::NAT_PUNCH_TO_GAME_HOST) game->EnterPhase(Game::SEARCH_FOR_GAMES); } case ID_NAT_ALREADY_IN_PROGRESS: // Can ignore this break; case ID_NAT_PUNCHTHROUGH_SUCCEEDED: { if (game->phase==Game::NAT_PUNCH_TO_GAME_HOST || game->phase==Game::VERIFIED_JOIN) { // Connect to the session host ConnectionAttemptResult car = rakPeer->Connect(packet->systemAddress.ToString(false), packet->systemAddress.GetPort(), 0, 0); if (car!=RakNet::CONNECTION_ATTEMPT_STARTED) { printf("Failed connect call to %s. Code=%i\n", packet->systemAddress.ToString(false), car); game->EnterPhase(Game::SEARCH_FOR_GAMES); } else { if (game->phase==Game::NAT_PUNCH_TO_GAME_HOST) { printf("NAT punch completed. Connecting to %s (game host)...\n", packet->systemAddress.ToString(true)); game->EnterPhase(Game::CONNECTING_TO_GAME_HOST); } else { printf("NAT punch completed. Connecting to %s (game client)...\n", packet->systemAddress.ToString(true)); } } } } break; case ID_CLOUD_GET_RESPONSE: { cloudClient->DeallocateWithDefaultAllocator(&cloudQueryResult); cloudClient->OnGetReponse(&cloudQueryResult, packet); unsigned int rowIndex; for (rowIndex=0; rowIndex < cloudQueryResult.rowsReturned.Size(); rowIndex++) { RakNet::CloudQueryRow *row = cloudQueryResult.rowsReturned[rowIndex]; printf("%i. ", rowIndex); PrintRow(row); } printf("(J)oin room\n"); printf("(C)reate room\n"); printf("(S)earch rooms\n"); } break; case ID_NAT_TYPE_DETECTION_RESULT: { game->myNatType = (RakNet::NATTypeDetectionResult) packet->data[1]; printf("NAT Type is %s (%s)\n", NATTypeDetectionResultToString(game->myNatType), NATTypeDetectionResultToStringFriendly(game->myNatType)); if (game->myNatType!=RakNet::NAT_TYPE_NONE) { OpenUPNP(); } if (game->myNatType==RakNet::NAT_TYPE_PORT_RESTRICTED || game->myNatType==RakNet::NAT_TYPE_SYMMETRIC) { printf("Note: Your router must support UPNP or have the user manually forward ports.\n"); printf("Otherwise NATPunchthrough may not always succeed.\n"); } game->EnterPhase(Game::SEARCH_FOR_GAMES); } break; case ID_READY_EVENT_ALL_SET: printf("Got ID_READY_EVENT_ALL_SET from %s\n", packet->systemAddress.ToString(true)); printf("All users ready.\n"); if (fullyConnectedMesh2->IsConnectedHost()) printf("New options:\n(B)egin gameplay\n"); break; case ID_READY_EVENT_SET: printf("Got ID_READY_EVENT_SET from %s\n", packet->systemAddress.ToString(true)); break; case ID_READY_EVENT_UNSET: printf("Got ID_READY_EVENT_UNSET from %s\n", packet->systemAddress.ToString(true)); break; // ID_USER_PACKET_ENUM is used by this sample as a custom message to ask to join a game case ID_USER_PACKET_ENUM: if (game->phase > Game::SEARCH_FOR_GAMES) { printf("Got request from client to join session.\nExecuting StartVerifiedJoin()\n"); fullyConnectedMesh2->StartVerifiedJoin(packet->guid); } else { BitStream bsOut; bsOut.Write((MessageID)(ID_USER_PACKET_ENUM+1)); rakPeer->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,packet->guid,false); } break; // ID_USER_PACKET_ENUM+1 is used by this sample as a custom message to reject a join game request // Requests may also be later rejected through FullyConnectedMesh2::RespondOnVerifiedJoinCapable() to send ID_FCM2_VERIFIED_JOIN_REJECTED case (ID_USER_PACKET_ENUM+1): printf("Join request denied\n"); game->EnterPhase(Game::SEARCH_FOR_GAMES); break; case ID_FCM2_VERIFIED_JOIN_START: { game->EnterPhase(Game::VERIFIED_JOIN); // This message means the session host sent us a list of systems in the session // Once we connect to, or fail to connect to, each of these systems we will get ID_FCM2_VERIFIED_JOIN_FAILED, ID_FCM2_VERIFIED_JOIN_ACCEPTED, or ID_FCM2_VERIFIED_JOIN_REJECTED printf("Host sent us system list. Doing NAT punch to each system...\n"); DataStructures::List<SystemAddress> addresses; DataStructures::List<RakNetGUID> guids; fullyConnectedMesh2->GetVerifiedJoinRequiredProcessingList(packet->guid, addresses, guids); for (unsigned int i=0; i < guids.Size(); i++) natPunchthroughClient->OpenNAT(guids[i], game->masterServerAddress); } break; case ID_FCM2_VERIFIED_JOIN_CAPABLE: printf("Client is capable of joining FullyConnectedMesh2.\n"); if (game->lockGame) { RakNet::BitStream bsOut; bsOut.Write("Game is locked"); fullyConnectedMesh2->RespondOnVerifiedJoinCapable(packet, false, &bsOut); } else fullyConnectedMesh2->RespondOnVerifiedJoinCapable(packet, true, 0); break; case ID_FCM2_VERIFIED_JOIN_ACCEPTED: { DataStructures::List<RakNetGUID> systemsAccepted; bool thisSystemAccepted; fullyConnectedMesh2->GetVerifiedJoinAcceptedAdditionalData(packet, &thisSystemAccepted, systemsAccepted, 0); if (thisSystemAccepted) printf("Game join request accepted\n"); else printf("System %s joined the mesh\n", systemsAccepted[0].ToString()); // Add the new participant to the game if we already know who the host is. Otherwise do this // once ID_FCM2_NEW_HOST arrives if (fullyConnectedMesh2->GetConnectedHost()!=UNASSIGNED_RAKNET_GUID) { // FullyConnectedMesh2 already called AddParticipant() for each accepted system // Still need to add those systems to the other plugins though for (unsigned int i=0; i < systemsAccepted.Size(); i++) RegisterGameParticipant(systemsAccepted[i]); if (thisSystemAccepted) game->EnterPhase(Game::IN_LOBBY_WITH_HOST); } else { if (thisSystemAccepted) game->EnterPhase(Game::IN_LOBBY_WAITING_FOR_HOST); } printf("(E)xit room\n"); } break; case ID_FCM2_VERIFIED_JOIN_REJECTED: { BitStream additionalData; fullyConnectedMesh2->GetVerifiedJoinRejectedAdditionalData(packet, &additionalData); RakString reason; additionalData.Read(reason); printf("Join rejected. Reason=%s\n", reason.C_String()); rakPeer->CloseConnection(packet->guid, true); game->EnterPhase(Game::SEARCH_FOR_GAMES); break; } case ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE: { if (replicaManager3->GetAllConnectionDownloadsCompleted()==true) { printf("Completed all remote downloads\n"); if (game->gameInLobby) game->EnterPhase(Game::IN_LOBBY_WITH_HOST); else game->EnterPhase(Game::IN_GAME); } break; } } } if (kbhit()) { ch=getch(); if (game->phase==Game::SEARCH_FOR_GAMES) { if (ch=='c' || ch=='C') { CreateRoom(); } if (ch=='s' || ch=='S') { game->SearchForGames(); } else if (ch=='j' || ch=='J') { // Join room if (cloudQueryResult.rowsReturned.Size()==0) { printf("No rooms to join.\n"); } else { int index; if (cloudQueryResult.rowsReturned.Size()>1) { printf("Enter index of room to join.\n"); char indexstr[64]; Gets(indexstr,64); index = atoi(indexstr); } else { index = 0; } if (index < 0 || (unsigned int) index >= cloudQueryResult.rowsReturned.Size()) { printf("Index out of range.\n"); } else { CloudQueryRow *row = cloudQueryResult.rowsReturned[index]; // Connect to the session host using NATPunchthrough natPunchthroughClient->OpenNAT(row->clientGUID, game->masterServerAddress); game->EnterPhase(Game::NAT_PUNCH_TO_GAME_HOST); } } } } else { if (game->phase==Game::IN_GAME) { if (ch=='c' || ch=='C') { DataStructures::List<RakNetGUID> participantList; fullyConnectedMesh2->GetParticipantList(participantList); if (participantList.Size()>0) { printf("Enter in-game chat message: "); char str[256]; Gets(str, 256); RakString rs; // Don't use RakString constructor to assign str, or will process % escape characters rs=str; BitStream bsOut; bsOut.Write(rs); for (unsigned int i=0; i < participantList.Size(); i++) rpc4->Signal("InGameChat", &bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, participantList[i], false, false); } } } if (ch=='1') { user->tmTeamMember.RequestTeamSwitch(&game->teams[0]->tmTeam, 0); } else if (ch=='2') { user->tmTeamMember.RequestTeamSwitch(&game->teams[1]->tmTeam, 0); } else if (ch=='r' || ch=='R') { if (readyEvent->SetEvent(0, true)) printf("We are ready to start.\n"); } else if (ch=='u' || ch=='U') { if (readyEvent->SetEvent(0, false)) printf("We are no longer ready to start.\n"); } else if (ch=='l' || ch=='L') { if (fullyConnectedMesh2->IsConnectedHost()) { if (game->lockGame) { printf("Game is no longer locked\n"); game->lockGame=false; } else { printf("Game is now locked\n"); game->lockGame=true; } } } else if (ch=='b' || ch=='B') { if (fullyConnectedMesh2->IsConnectedHost()) { if (game->gameInLobby) { readyEvent->ForceCompletion(0); game->gameInLobby=false; game->EnterPhase(Game::IN_GAME); } else { readyEvent->DeleteEvent(0); printf("Game ended, and now in lobby\n"); game->gameInLobby=true; game->EnterPhase(Game::IN_LOBBY_WITH_HOST); } } } else if (ch=='e' || ch=='E') { // Disconnect from FullyConnectedMesh2 participants DataStructures::List<RakNetGUID> participantList; fullyConnectedMesh2->GetParticipantList(participantList); for (unsigned int i=0; i < participantList.Size(); i++) rakPeer->CloseConnection(participantList[i], true); // User instances are deleted automatically from ReplicaManager3. // However, teams are not deleted since the Team class can migrate between systems. So delete Team instances manually while (game->teams.Size()) delete game->teams[game->teams.Size()-1]; // If we were the host, no longer list this session // The new host will call PostRoomToCloud to reupload under a new IP address on ID_FCM2_NEW_HOST ReleaseRoomFromCloud(); // Clear out state data from plugins fullyConnectedMesh2->Clear(); readyEvent->DeleteEvent(0); replicaManager3->Clear(false); replicaManager3->Reference(game); game->Reset(); game->EnterPhase(Game::SEARCH_FOR_GAMES); } else if (ch=='q' || ch=='Q') { printf("Quitting.\n"); // Disconnecting from the master server automatically releases from cloud game->EnterPhase(Game::EXIT_SAMPLE); } } } RakSleep(30); } rakPeer->Shutdown(100); while (game->teams.Size()) delete game->teams[game->teams.Size()-1]; while (game->users.Size()) delete game->users[game->users.Size()-1]; delete game; cloudClient->DeallocateWithDefaultAllocator(&cloudQueryResult); RakPeerInterface::DestroyInstance(rakPeer); TeamManager::DestroyInstance(teamManager); FullyConnectedMesh2::DestroyInstance(fullyConnectedMesh2); cloudClient->DeallocateWithDefaultAllocator(&cloudQueryResult); CloudClient::DestroyInstance(cloudClient); NatPunchthroughClient::DestroyInstance(natPunchthroughClient); NatTypeDetectionClient::DestroyInstance(natTypeDetectionClient); RPC4::DestroyInstance(rpc4); ReadyEvent::DestroyInstance(readyEvent); delete replicaManager3; NetworkIDManager::DestroyInstance(networkIDManager); return 1; }