Просмотр исходного кода

maximum incomming connections are now configurable, additional network events added, network object lifecycle updated, identity broadcasting updated, p2p create/join flows updated, code cleanup

Arnis Lielturks 7 лет назад
Родитель
Сommit
9c882d57f6

+ 20 - 14
Source/Samples/54_P2PMultiplayer/P2PMultiplayer.cpp

@@ -106,9 +106,8 @@ void P2PMultiplayer::CreateUI()
     auto* uiStyle = cache->GetResource<XMLFile>("UI/DefaultStyle.xml");
     // Set style to the UI root so that elements will inherit it
     root->SetDefaultStyle(uiStyle);
-//
+
     int marginTop = 20;
-//    CreateLabel("1. Start server", IntVector2(20, marginTop-20));
     startSession_ = CreateButton("Start session", 160, IntVector2(20, marginTop));
     nickname_ = CreateLineEdit("Nickname", 160, IntVector2(200, marginTop));
     marginTop += 40;
@@ -149,6 +148,7 @@ void P2PMultiplayer::SubscribeToEvents()
     SubscribeToEvent(E_P2PNEWHOST, URHO3D_HANDLER(P2PMultiplayer, HandleNewHost));
     SubscribeToEvent(E_NETWORKBANNED, URHO3D_HANDLER(P2PMultiplayer, HandleBanned));
     SubscribeToEvent(E_CLIENTIDENTITY, URHO3D_HANDLER(P2PMultiplayer, HandleClientIdentity));
+    SubscribeToEvent(E_SERVERFULL, URHO3D_HANDLER(P2PMultiplayer, HandleServerFull));
 
     GetSubsystem<Network>()->RegisterRemoteEvent("GameStart");
     SubscribeToEvent("GameStart", URHO3D_HANDLER(P2PMultiplayer, HandleGameState));
@@ -190,6 +190,8 @@ void P2PMultiplayer::HandleSessionStarted(StringHash eventType, VariantMap& even
     statusMessage_->SetText("Status: P2P Session started");
 
     gameState_ = GameState::IN_LOBBY;
+
+    //GetSubsystem<Network>()->SetAllowedConnections(0);
 }
 
 void P2PMultiplayer::HandleSessionJoined(StringHash eventType, VariantMap& eventData)
@@ -224,6 +226,8 @@ void P2PMultiplayer::HandleUpdate(StringHash eventType, VariantMap& eventData)
         statusMessage_->SetText("Status: Disconnected");
 
         UpdatePlayerList();
+
+        gameState_ = IN_MENU;
     }
 
     if (input->GetKeyPress(KEY_H)) {
@@ -257,8 +261,10 @@ void P2PMultiplayer::HandleUpdate(StringHash eventType, VariantMap& eventData)
     pitch_ += MOUSE_SENSITIVITY * mouseMove.y_;
     pitch_ = Clamp(pitch_, 1.0f, 90.0f);
 
-    // Construct new orientation for the camera scene node from yaw and pitch. Roll is fixed to zero
-    cameraNode_->SetRotation(Quaternion(pitch_, yaw_, 0.0f));
+    if (gameState_ == IN_GAME) {
+        // Construct new orientation for the camera scene node from yaw and pitch. Roll is fixed to zero
+        cameraNode_->SetRotation(Quaternion(pitch_, yaw_, 0.0f));
+    }
 
     if (GetSubsystem<Network>()->GetServerConnection()) {
         Controls controls;
@@ -277,14 +283,7 @@ void P2PMultiplayer::HandleUpdate(StringHash eventType, VariantMap& eventData)
         timer_.Reset();
 
         //TODO fix this
-        if (GetSubsystem<Network>()->GetServerConnection()) {
-            auto clients = GetSubsystem<Network>()->GetClientConnections();
-            for (auto it = clients.Begin(); it != clients.End(); ++it) {
-                VectorBuffer msg;
-                msg.WriteVariantMap(GetSubsystem<Network>()->GetServerConnection()->GetIdentity());
-                (*it)->SendMessage(MSG_IDENTITY, true, true, msg);
-            }
-        }
+
 
         if (GetSubsystem<Network>()->GetClientConnections().Size() != peers_.Size() - 1) {
             UpdateClientObjects();
@@ -453,7 +452,6 @@ void P2PMultiplayer::CreateScene()
 
 void P2PMultiplayer::SetupViewport()
 {
-//    return;
     auto* renderer = GetSubsystem<Renderer>();
 
     // Set up a viewport to the Renderer subsystem so that the 3D scene can be seen
@@ -573,6 +571,9 @@ void P2PMultiplayer::HandleBanned(StringHash eventType, VariantMap& eventData)
 
     GetSubsystem<Input>()->SetMouseVisible(true);
     GetSubsystem<Input>()->SetMouseGrabbed(false);
+
+    gameState_ = IN_MENU;
+
 //    GetSubsystem<Input>()->SetMouseMode(MouseMode::MM_FREE);
 
     using namespace NetworkBanned;
@@ -585,7 +586,7 @@ void P2PMultiplayer::HandleClientIdentity(StringHash eventType, VariantMap& even
 {
     using namespace ClientIdentity;
     auto connection = static_cast<Connection*>(eventData[P_CONNECTION].GetPtr());
-    URHO3D_LOGINFO("Client identity: Client " + connection->GetGUID() + " => " + connection->GetIdentity()["Name"].GetString());
+//    URHO3D_LOGINFO("Client identity: Client " + connection->GetGUID() + " => " + connection->GetIdentity()["Name"].GetString());
     UpdatePlayerList();
 }
 
@@ -727,4 +728,9 @@ void P2PMultiplayer::CreatePlayerListWindow()
     playerList_->SetAlignment(HA_CENTER, VA_CENTER);
     playerList_->SetSize(IntVector2(GetSubsystem<Graphics>()->GetWidth() - 200, GetSubsystem<Graphics>()->GetHeight() - 400));
     playerList_->BringToFront();
+}
+
+void P2PMultiplayer::HandleServerFull(StringHash eventType, VariantMap& eventData)
+{
+    UpdatePlayerList();
 }

+ 2 - 0
Source/Samples/54_P2PMultiplayer/P2PMultiplayer.h

@@ -106,6 +106,8 @@ private:
     void HandleStartP2PSession(StringHash eventType, VariantMap& eventData);
     void HandleJoinP2PSession(StringHash eventType, VariantMap& eventData);
 
+    void HandleServerFull(StringHash eventType, VariantMap& eventData);
+
     void HandleAllReadyChanged(StringHash eventType, VariantMap& eventData);
 
     void HandleSessionStarted(StringHash eventType, VariantMap& eventData);

+ 186 - 102
Source/Urho3D/Network/Network.cpp

@@ -210,19 +210,15 @@ Network::Network(Context* context) :
     natPunchServerAddress_(nullptr),
     remoteGUID_(nullptr),
     networkMode_(SERVER_CLIENT),
-    natAutoReconnect_(true)
+    natAutoReconnect_(true),
+    allowedConnectionCount_(128),
+    fullyConnectedMesh2_(nullptr),
+    connectionGraph2_(nullptr),
+    readyEvent_(nullptr)
 {
     rakPeer_ = SLNet::RakPeerInterface::GetInstance();
     rakPeerClient_ = SLNet::RakPeerInterface::GetInstance();
 
-    fullyConnectedMesh2_ = SLNet::FullyConnectedMesh2::GetInstance();
-    rakPeer_->AttachPlugin(fullyConnectedMesh2_);
-    fullyConnectedMesh2_->SetAutoparticipateConnections(false);
-    readyEvent_ = SLNet::ReadyEvent::GetInstance();
-    rakPeer_->AttachPlugin(readyEvent_);
-    connectionGraph2_ = SLNet::ConnectionGraph2::GetInstance();
-    rakPeer_->AttachPlugin(connectionGraph2_);
-
     rakPeer_->SetTimeoutTime(SERVER_TIMEOUT_TIME, SLNet::UNASSIGNED_SYSTEM_ADDRESS);
     rakPeerClient_->SetTimeoutTime(SERVER_TIMEOUT_TIME, SLNet::UNASSIGNED_SYSTEM_ADDRESS);
     SetPassword("");
@@ -300,22 +296,39 @@ Network::~Network()
     // If server connection exists, disconnect, but do not send an event because we are shutting down
     Disconnect(1000);
 
-    fullyConnectedMesh2_->ResetHostCalculation();
+    if (fullyConnectedMesh2_)
+        fullyConnectedMesh2_->ResetHostCalculation();
 
     rakPeer_->DetachPlugin(natPunchthroughServerClient_);
     rakPeerClient_->DetachPlugin(natPunchthroughClient_);
-    rakPeer_->DetachPlugin(fullyConnectedMesh2_);
-    rakPeer_->DetachPlugin(connectionGraph2_);
-    rakPeer_->DetachPlugin(readyEvent_);
+
+    SLNet::NatPunchthroughClient::DestroyInstance(natPunchthroughServerClient_);
+    natPunchthroughServerClient_ = nullptr;
+    SLNet::NatPunchthroughClient::DestroyInstance(natPunchthroughClient_);
+    natPunchthroughClient_ = nullptr;
+
+    if (fullyConnectedMesh2_) {
+        rakPeer_->DetachPlugin(fullyConnectedMesh2_);
+        SLNet::FullyConnectedMesh2::DestroyInstance(fullyConnectedMesh2_);
+        fullyConnectedMesh2_ = nullptr;
+    }
+
+    if (connectionGraph2_) {
+        rakPeer_->DetachPlugin(connectionGraph2_);
+        SLNet::ConnectionGraph2::DestroyInstance(connectionGraph2_);
+        connectionGraph2_ = nullptr;
+    }
+
+    if (readyEvent_) {
+        rakPeer_->DetachPlugin(readyEvent_);
+        SLNet::ReadyEvent::DestroyInstance(readyEvent_);
+        readyEvent_ = nullptr;
+    }
 
     serverConnection_.Reset();
 
     clientConnections_.Clear();
 
-    delete natPunchthroughServerClient_;
-    natPunchthroughServerClient_ = nullptr;
-    delete natPunchthroughClient_;
-    natPunchthroughClient_ = nullptr;
     delete remoteGUID_;
     remoteGUID_ = nullptr;
     delete natPunchServerAddress_;
@@ -324,10 +337,6 @@ Network::~Network()
     SLNet::RakPeerInterface::DestroyInstance(rakPeer_);
     SLNet::RakPeerInterface::DestroyInstance(rakPeerClient_);
 
-    SLNet::FullyConnectedMesh2::DestroyInstance(fullyConnectedMesh2_);
-    SLNet::ReadyEvent::DestroyInstance(readyEvent_);
-    SLNet::ConnectionGraph2::DestroyInstance(connectionGraph2_);
-
     rakPeer_ = nullptr;
     rakPeerClient_ = nullptr;
 }
@@ -382,12 +391,17 @@ void Network::HandleMessageClient(const SLNet::AddressOrGUID& source, int packet
 
 void Network::NewConnectionEstablished(const SLNet::AddressOrGUID& connection, const char* address)
 {
+    if (allowedConnectionCount_ == 0 || GetClientConnections().Size() >= allowedConnectionCount_) {
+        URHO3D_LOGWARNING("Server is full, dropping incoming connection!");
+        rakPeer_->CloseConnection(connection, true);
+        return;
+    }
 
-    URHO3D_LOGINFO("NewConnectionEstablished: " + String(connection.ToString()));
     ReadyStatusChanged();
     if (networkMode_ == PEER_TO_PEER && clientConnections_[connection]) {
         //TODO proper scene state management
         clientConnections_[connection]->SetSceneLoaded(true);
+        SendOutIdentityToPeers();
         return;
     }
 
@@ -408,18 +422,23 @@ void Network::NewConnectionEstablished(const SLNet::AddressOrGUID& connection, c
     eventData[P_CONNECTION] = newConnection;
     newConnection->SendEvent(E_CLIENTCONNECTED, eventData);
 
-//    if (GetSubsystem<Network>()->GetServerConnection()) {
-//        auto clients = GetSubsystem<Network>()->GetClientConnections();
-//        for (auto it = clients.Begin(); it != clients.End(); ++it) {
-//        if (serverConnection_) {
-//            VectorBuffer msg;
-//            msg.WriteVariantMap(serverConnection_->GetIdentity());
-//            newConnection->SendMessage(MSG_IDENTITY, true, true, msg);
-//        }
-//        }
-//    }
-
+    // Let all our peers know who we are
+    if (networkMode_ == PEER_TO_PEER) {
+        SendOutIdentityToPeers();
+    }
+}
 
+void Network::SendOutIdentityToPeers()
+{
+    if (serverConnection_) {
+        URHO3D_LOGINFO("Sending out identity to all peers");
+        auto clients = GetClientConnections();
+        for (auto it = clients.Begin(); it != clients.End(); ++it) {
+            VectorBuffer msg;
+            msg.WriteVariantMap(serverConnection_->GetIdentity());
+            (*it)->SendMessage(MSG_IDENTITY, true, true, msg);
+        }
+    }
 }
 
 void Network::ClientDisconnected(const SLNet::AddressOrGUID& connection)
@@ -460,7 +479,7 @@ void Network::DiscoverHosts(unsigned port)
         SLNet::SocketDescriptor socket;
         // Startup local connection with max 1 incoming connection(first param) and 1 socket description (third param)
         rakPeerClient_->Startup(32, &socket, 1);
-        rakPeerClient_->SetMaximumIncomingConnections(32);
+        rakPeerClient_->SetMaximumIncomingConnections(allowedConnectionCount_);
     }
     rakPeerClient_->Ping("255.255.255.255", port, false);
 }
@@ -477,11 +496,10 @@ bool Network::Connect(const String& address, unsigned short port, Scene* scene,
 
     if (!rakPeerClient_->IsActive())
     {
-        URHO3D_LOGINFO("Initializing client connection...");
         SLNet::SocketDescriptor socket;
         // Startup local connection with max 2 incoming connections(first param) and 1 socket description (third param)
         rakPeerClient_->Startup(32, &socket, 1);
-        rakPeerClient_->SetMaximumIncomingConnections(32);
+        rakPeerClient_->SetMaximumIncomingConnections(allowedConnectionCount_);
     } else {
         OnServerDisconnected();
     }
@@ -513,20 +531,16 @@ bool Network::ConnectNAT(const String& address, unsigned short port)
 
     if (!rakPeer_->IsActive())
     {
-        URHO3D_LOGINFO("Initializing client connection...");
         SLNet::SocketDescriptor socket;
         // Startup local connection with max 2 incoming connections(first param) and 1 socket description (third param)
         rakPeer_->Startup(128, &socket, 1);
-        rakPeer_->SetMaximumIncomingConnections(128);
+        rakPeer_->SetMaximumIncomingConnections(allowedConnectionCount_);
         rakPeer_->AttachPlugin(natPunchthroughServerClient_);
     }
-    else {
-        OnServerDisconnected();
-    }
 
-    //isServer_ = false;
     SLNet::ConnectionAttemptResult connectResult = rakPeer_->Connect(address.CString(), port, nullptr, 0);
     if (connectResult == SLNet::ALREADY_CONNECTED_TO_ENDPOINT) {
+        SendEvent(E_NATMASTERCONNECTIONSUCCEEDED);
         URHO3D_LOGWARNING("Already connected to server " + address + ":" + String(port) + ", error code: " + String((int)connectResult));
         return false;
     }
@@ -545,17 +559,17 @@ bool Network::ConnectNAT(const String& address, unsigned short port)
 
 void Network::Disconnect(int waitMSec)
 {
-    if (!serverConnection_)
-        return;
-
     URHO3D_PROFILE(Disconnect);
-    serverConnection_->Disconnect(waitMSec);
-    serverConnection_.Reset();
 
     if (networkMode_ == PEER_TO_PEER) {
         rakPeer_->Shutdown(waitMSec);
         clientConnections_.Clear();
     }
+
+    if (serverConnection_) {
+        serverConnection_->Disconnect(waitMSec);
+        serverConnection_.Reset();
+    }
 }
 
 bool Network::StartServer(unsigned short port)
@@ -573,7 +587,7 @@ bool Network::StartServer(unsigned short port)
     if (startResult == SLNet::RAKNET_STARTED)
     {
         URHO3D_LOGINFO("Started server on port " + String(port));
-        rakPeer_->SetMaximumIncomingConnections(128);
+        rakPeer_->SetMaximumIncomingConnections(allowedConnectionCount_);
         isServer_ = true;
         rakPeer_->SetOccasionalPing(true);
         rakPeer_->SetUnreliableTimeout(1000);
@@ -645,7 +659,7 @@ void Network::AttemptNATPunchtrough(const String& guid, Scene* scene, const Vari
         SLNet::SocketDescriptor socket;
         // Startup local connection with max 2 incoming connections(first param) and 1 socket description (third param)
         rakPeerClient_->Startup(32, &socket, 1);
-        rakPeerClient_->SetMaximumIncomingConnections(32);
+        rakPeerClient_->SetMaximumIncomingConnections(allowedConnectionCount_);
     }
 
     rakPeerClient_->Connect(natPunchServerAddress_->ToString(false), natPunchServerAddress_->GetPort(), nullptr, 0);
@@ -904,7 +918,6 @@ void Network::HandleIncomingPacket(SLNet::Packet* packet, bool isServer)
             bsIn.Read(count);
             SLNet::SystemAddress remoteAddress;
             SLNet::RakNetGUID remoteGuid;
-            URHO3D_LOGINFO("AAAA GUID " + String(packet->guid.ToString()));
             NewConnectionEstablished(packet->guid, packet->systemAddress.ToString(false));
 
 //            for (unsigned int i=0; i < count; i++)
@@ -931,6 +944,7 @@ void Network::HandleIncomingPacket(SLNet::Packet* packet, bool isServer)
     {
         if (natPunchServerAddress_ && packet->systemAddress == *natPunchServerAddress_) {
             URHO3D_LOGINFO("Already connected to NAT server! ");
+            SendEvent(E_NATMASTERCONNECTIONSUCCEEDED);
             if (!isServer && networkMode_ == SERVER_CLIENT)
             {
                 natPunchthroughClient_->OpenNAT(*remoteGUID_, *natPunchServerAddress_);
@@ -951,13 +965,12 @@ void Network::HandleIncomingPacket(SLNet::Packet* packet, bool isServer)
             if (networkMode_ == SERVER_CLIENT) {
                 OnServerConnected(packet->guid);
             } else {
-                URHO3D_LOGERROR(">>>>>>>>>>>>>>>>>>>.");
                 URHO3D_LOGINFOF("ID_CONNECTION_REQUEST_ACCEPTED from %s,guid=%s", packet->systemAddress.ToString(true), packet->guid.ToString());
                 // Assume that we're connecting to the P2P host
 //                serverConnection_->SetAddressOrGUID(packet->guid);
-                SLNet::BitStream bsOut;
-                bsOut.Write((unsigned char) MSG_P2P_JOIN_REQUEST);
-                rakPeer_->Send(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->guid, false);
+//                SLNet::BitStream bsOut;
+//                bsOut.Write((unsigned char) MSG_P2P_JOIN_REQUEST);
+//                rakPeer_->Send(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->guid, false);
 
             }
         }
@@ -1174,17 +1187,24 @@ void Network::HandleIncomingPacket(SLNet::Packet* packet, bool isServer)
         URHO3D_LOGINFO("ID_FCM2_VERIFIED_JOIN_START");
         packetHandled = true;
     }
+    else if (packetID == ID_NO_FREE_INCOMING_CONNECTIONS)
+    {
+        URHO3D_LOGERROR("Server is full!");
+        Disconnect(1000);
+        SendEvent(E_SERVERFULL);
+        packetHandled = true;
+    }
     else if (packetID == ID_FCM2_VERIFIED_JOIN_CAPABLE)
     {
         URHO3D_LOGINFO("ID_FCM2_VERIFIED_JOIN_CAPABLE");
-        fullyConnectedMesh2_->RespondOnVerifiedJoinCapable(packet, true, nullptr);
+//        fullyConnectedMesh2_->RespondOnVerifiedJoinCapable(packet, true, nullptr);
         packetHandled = true;
     }
     else if (packetID == ID_FCM2_VERIFIED_JOIN_ACCEPTED)
     {
         DataStructures::List<SLNet::RakNetGUID> systemsAccepted;
         bool thisSystemAccepted;
-        fullyConnectedMesh2_->GetVerifiedJoinAcceptedAdditionalData(packet, &thisSystemAccepted, systemsAccepted, nullptr);
+//        fullyConnectedMesh2_->GetVerifiedJoinAcceptedAdditionalData(packet, &thisSystemAccepted, systemsAccepted, nullptr);
         if (thisSystemAccepted) {
             URHO3D_LOGINFO("Game join request accepted");
         }
@@ -1221,12 +1241,15 @@ void Network::HandleIncomingPacket(SLNet::Packet* packet, bool isServer)
         //URHO3D_LOGINFOF("ID_USER_PACKET_ENUM %i", packetID);
         if (packetID == MSG_P2P_JOIN_REQUEST) {
             URHO3D_LOGINFO("MSG_P2P_JOIN_REQUEST");
-            //TODO decide if the client is able to join
-            fullyConnectedMesh2_->StartVerifiedJoin(packet->guid);
-            //TODO or deny the join request
-//            VectorBuffer msg;
-//            GetConnection(packet->guid)->SendMessage(MSG_P2P_JOIN_REQUEST_DENIED, true, true, msg);
-//            ClientDisconnected(packet->guid);
+//            if (allowedConnectionCount_ > 0 && GetClientConnections().Size() < allowedConnectionCount_) {
+////                fullyConnectedMesh2_->StartVerifiedJoin(packet->guid);
+//                URHO3D_LOGWARNING("StartVerifiedJoin SUCCESS");
+//            } else {
+//                VectorBuffer msg;
+//                GetConnection(packet->guid)->SendMessage(MSG_P2P_JOIN_REQUEST_DENIED, true, true, msg);
+//                ClientDisconnected(packet->guid);
+//                URHO3D_LOGWARNING("StartVerifiedJoin FAILED");
+//            }
         } else if (networkMode_ == PEER_TO_PEER && IsHostSystem())
         {
             // We are the host in the P2P server, parse the message accordingly
@@ -1269,9 +1292,11 @@ void Network::Update(float timeStep)
         while (SLNet::Packet* packet = rakPeer_->Receive())
         {
             if (IsHostSystem() || networkMode_ == SERVER_CLIENT) {
+                // Process all messages as server/host
                 HandleIncomingPacket(packet, true);
             }
             else {
+                // Process messages as a client/peer
                 HandleIncomingPacket(packet, false);
             }
             // Check if interface is still active, ban could happen while the message was processing
@@ -1281,7 +1306,7 @@ void Network::Update(float timeStep)
         }
     }
 
-    // Process all incoming messages for the client
+    // Process all incoming messages for the client, server-client mode only
     if (rakPeerClient_->IsActive())
     {
         while (SLNet::Packet* packet = rakPeerClient_->Receive())
@@ -1340,6 +1365,7 @@ void Network::PostUpdate(float timeStep)
 
         if (serverConnection_)
         {
+            // No need to send out updates if we're the host system
             if (networkMode_ == PEER_TO_PEER && !IsHostSystem()) {
                 // Send the client update
                 serverConnection_->SendClientUpdate();
@@ -1417,22 +1443,24 @@ void Network::ConfigureNetworkSimulator()
 
 bool Network::StartSession(Scene* scene, const VariantMap& identity)
 {
-    Disconnect(1000);
-    if (!natPunchServerAddress_) {
-        URHO3D_LOGERROR("Set the NAT server info first!");
-        return false;
-    }
-    ConnectNAT(natPunchServerAddress_->ToString(false), natPunchServerAddress_->GetPort());
     if (networkMode_ == SERVER_CLIENT) {
         URHO3D_LOGERROR("P2P sessions are not available for SERVER_CLIENT mode!");
         return false;
     }
 
+    if (!natPunchServerAddress_) {
+        URHO3D_LOGERROR("Set the NAT server info first!");
+        return false;
+    }
+
     UnsubscribeFromEvent(E_NATMASTERCONNECTIONSUCCEEDED);
     SubscribeToEvent(E_NATMASTERCONNECTIONSUCCEEDED, URHO3D_HANDLER(Network, HandleNATStartSession));
+
     scene_ = scene;
     identity_ = identity;
 
+    ConnectNAT(natPunchServerAddress_->ToString(false), natPunchServerAddress_->GetPort());
+
     guid_ = String(rakPeer_->GetGuidFromSystemAddress(SLNet::UNASSIGNED_SYSTEM_ADDRESS).ToString());
 
     return true;
@@ -1442,15 +1470,18 @@ bool Network::StartSession(Scene* scene, const VariantMap& identity)
 void Network::HandleNATStartSession(StringHash eventType, VariantMap& eventData)
 {
     UnsubscribeFromEvent(E_NATMASTERCONNECTIONSUCCEEDED);
-    isServer_ = false;
-    if (!serverConnection_) {
-        serverConnection_ = new Connection(context_, false, rakPeer_->GetMyGUID(), rakPeer_);
-        serverConnection_->SetScene(scene_);
-        serverConnection_->SetSceneLoaded(true);
-        serverConnection_->SetIdentity(identity_);
-        serverConnection_->SetConnectPending(true);
-        serverConnection_->ConfigureNetworkSimulator(simulatedLatency_, simulatedPacketLoss_);
+
+    if (serverConnection_) {
+        serverConnection_.Reset();
     }
+
+    serverConnection_ = new Connection(context_, false, rakPeer_->GetMyGUID(), rakPeer_);
+    serverConnection_->SetScene(scene_);
+    serverConnection_->SetSceneLoaded(true);
+    serverConnection_->SetIdentity(identity_);
+    serverConnection_->SetConnectPending(true);
+    serverConnection_->ConfigureNetworkSimulator(simulatedLatency_, simulatedPacketLoss_);
+
     rakPeer_->SetOccasionalPing(true);
     fullyConnectedMesh2_->Clear();
     fullyConnectedMesh2_->ResetHostCalculation();
@@ -1460,27 +1491,28 @@ void Network::HandleNATStartSession(StringHash eventType, VariantMap& eventData)
     SetReady(false);
 
     SendEvent(E_P2PSESSIONSTARTED);
-
-    URHO3D_LOGINFO("P2P Session started");
 }
 
 void Network::JoinSession(const String& guid, Scene* scene, const VariantMap& identity)
 {
-    Disconnect(1000);
-    if (!natPunchServerAddress_) {
-        URHO3D_LOGERROR("Set the NAT server info first!");
-        return;
-    }
-    ConnectNAT(natPunchServerAddress_->ToString(false), natPunchServerAddress_->GetPort());
     if (networkMode_ == SERVER_CLIENT) {
         URHO3D_LOGERROR("P2P sessions are not available for SERVER_CLIENT mode!");
         return;
     }
 
+    if (!natPunchServerAddress_) {
+        URHO3D_LOGERROR("Set the NAT server info first!");
+        return;
+    }
+
+    UnsubscribeFromEvent(E_NATMASTERCONNECTIONSUCCEEDED);
+    SubscribeToEvent(E_NATMASTERCONNECTIONSUCCEEDED, URHO3D_HANDLER(Network, HandleNATJoinSession));
+
     if (remoteGUID_) {
         delete remoteGUID_;
         remoteGUID_ = nullptr;
     }
+
     remoteGUID_ = new SLNet::RakNetGUID;
     remoteGUID_->FromString(guid.CString());
     hostGuid_ = guid;
@@ -1488,43 +1520,46 @@ void Network::JoinSession(const String& guid, Scene* scene, const VariantMap& id
     scene_ = scene;
     identity_ = identity;
 
-    guid_ = String(rakPeer_->GetGuidFromSystemAddress(SLNet::UNASSIGNED_SYSTEM_ADDRESS).ToString());
-
-    UnsubscribeFromEvent(E_NATMASTERCONNECTIONSUCCEEDED);
-    SubscribeToEvent(E_NATMASTERCONNECTIONSUCCEEDED, URHO3D_HANDLER(Network, HandleNATJoinSession));
+    ConnectNAT(natPunchServerAddress_->ToString(false), natPunchServerAddress_->GetPort());
 
+    guid_ = String(rakPeer_->GetGuidFromSystemAddress(SLNet::UNASSIGNED_SYSTEM_ADDRESS).ToString());
 }
 
 void Network::HandleNATJoinSession(StringHash eventType, VariantMap& eventData)
 {
     UnsubscribeFromEvent(E_NATMASTERCONNECTIONSUCCEEDED);
     SetReady(false);
-    if (!serverConnection_) {
-        serverConnection_ = new Connection(context_, false, rakPeer_->GetMyBoundAddress(), rakPeer_);
-        serverConnection_->SetScene(scene_);
-        serverConnection_->SetSceneLoaded(true);
-        serverConnection_->SetIdentity(identity_);
-        serverConnection_->SetConnectPending(true);
-        serverConnection_->SetAddressOrGUID(*remoteGUID_);
-        serverConnection_->ConfigureNetworkSimulator(simulatedLatency_, simulatedPacketLoss_);
+
+    if (serverConnection_) {
+        serverConnection_.Reset();
     }
 
+    serverConnection_ = new Connection(context_, false, rakPeer_->GetMyBoundAddress(), rakPeer_);
+    serverConnection_->SetScene(scene_);
+    serverConnection_->SetSceneLoaded(true);
+    serverConnection_->SetIdentity(identity_);
+    serverConnection_->SetConnectPending(true);
+    serverConnection_->SetAddressOrGUID(*remoteGUID_);
+    serverConnection_->ConfigureNetworkSimulator(simulatedLatency_, simulatedPacketLoss_);
+
     rakPeer_->SetOccasionalPing(true);
     fullyConnectedMesh2_->ResetHostCalculation();
     fullyConnectedMesh2_->Clear();
 
-    URHO3D_LOGINFO("Attempting to Join P2P Session : " + String(remoteGUID_->ToString()));
-    natPunchthroughServerClient_->OpenNAT(*remoteGUID_, *natPunchServerAddress_);
+    if (natPunchthroughServerClient_->OpenNAT(*remoteGUID_, *natPunchServerAddress_)) {
+        SendEvent(E_P2PSESSIONJOINED);
+    } else {
+        URHO3D_LOGERROR("P2P join attempt failed!");
+        SendEvent(E_P2PSESSIONJOINFAILED);
+    }
 
     ReadyStatusChanged();
-
-    SendEvent(E_P2PSESSIONJOINED);
 }
 
 int Network::GetParticipantCount()
 {
     if (networkMode_ == SERVER_CLIENT) {
-        return 0;
+        return GetClientConnections().Size();
     }
     return fullyConnectedMesh2_->GetParticipantCount();
 }
@@ -1638,6 +1673,7 @@ void Network::SetMode(NetworkMode mode, bool force)
         URHO3D_LOGERROR("Failed to change network mode! Shutdown networking system first or use forced mode changing!");
         return;
     }
+
     if (force) {
         if (rakPeer_->IsActive()) {
             rakPeer_->Shutdown(100);
@@ -1649,6 +1685,47 @@ void Network::SetMode(NetworkMode mode, bool force)
         serverConnection_.Reset();
     }
     networkMode_ = mode;
+
+    if (networkMode_ == PEER_TO_PEER) {
+        if (fullyConnectedMesh2_) {
+            rakPeer_->DetachPlugin(fullyConnectedMesh2_);
+            SLNet::FullyConnectedMesh2::DestroyInstance(fullyConnectedMesh2_);
+        }
+        fullyConnectedMesh2_ = SLNet::FullyConnectedMesh2::GetInstance();
+        rakPeer_->AttachPlugin(fullyConnectedMesh2_);
+        fullyConnectedMesh2_->SetAutoparticipateConnections(true);
+
+        if (readyEvent_) {
+            rakPeer_->DetachPlugin(readyEvent_);
+            SLNet::ReadyEvent::DestroyInstance(readyEvent_);
+        }
+        readyEvent_ = SLNet::ReadyEvent::GetInstance();
+        rakPeer_->AttachPlugin(readyEvent_);
+
+        if (connectionGraph2_) {
+            rakPeer_->DetachPlugin(connectionGraph2_);
+            SLNet::ConnectionGraph2::DestroyInstance(connectionGraph2_);
+        }
+        connectionGraph2_ = SLNet::ConnectionGraph2::GetInstance();
+        rakPeer_->AttachPlugin(connectionGraph2_);
+
+    } else {
+        if (fullyConnectedMesh2_) {
+            rakPeer_->DetachPlugin(fullyConnectedMesh2_);
+            SLNet::FullyConnectedMesh2::DestroyInstance(fullyConnectedMesh2_);
+            fullyConnectedMesh2_ = nullptr;
+        }
+        if (readyEvent_) {
+            rakPeer_->DetachPlugin(readyEvent_);
+            SLNet::ReadyEvent::DestroyInstance(readyEvent_);
+            readyEvent_ = nullptr;
+        }
+        if (connectionGraph2_) {
+            rakPeer_->DetachPlugin(connectionGraph2_);
+            SLNet::ConnectionGraph2::DestroyInstance(connectionGraph2_);
+            connectionGraph2_ = nullptr;
+        }
+    }
 }
 
 void Network::SetNATAutoReconnect(bool retry)
@@ -1656,6 +1733,13 @@ void Network::SetNATAutoReconnect(bool retry)
     natAutoReconnect_ = retry;
 }
 
+void Network::SetAllowedConnections(unsigned short connectionCount)
+{
+    URHO3D_LOGINFO("Max incoming connection changed to " + String(connectionCount));
+    rakPeer_->SetMaximumIncomingConnections(connectionCount);
+    allowedConnectionCount_ = connectionCount;
+}
+
 void RegisterNetworkLibrary(Context* context)
 {
     NetworkPriority::RegisterObject(context);

+ 7 - 0
Source/Urho3D/Network/Network.h

@@ -173,8 +173,12 @@ public:
     void SetMode(NetworkMode mode, bool force = false);
     /// Get current network mode
     const NetworkMode  GetMode() const { return networkMode_; }
+    /// Set incoming connection count. If connectionCount < current connection count, no new connections will be made
+    void SetAllowedConnections(unsigned short connectionCount);
 
 private:
+    /// Send out identity to all connected peers
+    void SendOutIdentityToPeers();
     /// Connect to NAT server which will handle P2P session
     bool ConnectNAT(const String& address, unsigned short port);
 
@@ -248,7 +252,10 @@ private:
     String hostGuid_;
     /// current network mode - P2P or server-client mode
     NetworkMode networkMode_;
+    /// Automatically reconnect to NAT punchtrough master server
     bool natAutoReconnect_{};
+    /// Max allowed connections, if exceeded, no new connections will be made
+    unsigned short allowedConnectionCount_;
 };
 
 /// Register Network library objects.

+ 11 - 1
Source/Urho3D/Network/NetworkEvents.h

@@ -160,11 +160,21 @@ URHO3D_EVENT(E_P2PSESSIONSTARTED, P2PSessionStarted)
 {
 }
 
-/// P2P session join succesfull
+/// P2P session join successfull
 URHO3D_EVENT(E_P2PSESSIONJOINED, P2PSessionJoined)
 {
 }
 
+/// P2P session join succesfull
+URHO3D_EVENT(E_P2PSESSIONJOINFAILED, P2PSessionJoinFailed)
+{
+}
+
+/// Server/Host doesn't allow any new connections
+URHO3D_EVENT(E_SERVERFULL, ServerFull)
+{
+}
+
 /// When host system is elected
 URHO3D_EVENT(E_P2PNEWHOST, P2PNewHost)
 {