Browse Source

additional events and statuses

Arnis Lielturks 7 years ago
parent
commit
7b9b24c123

+ 25 - 17
Source/Samples/54_P2PMultiplayer/P2PMultiplayer.cpp

@@ -109,10 +109,6 @@ void P2PMultiplayer::CreateUI()
     marginTop += 40;
     joinSession_ = CreateButton("Join session", 160, IntVector2(20, marginTop));
 
-    marginTop += 100;
-    readyButton_ = CreateButton("Ready", 160, IntVector2(20, marginTop));
-    marginTop += 40;
-    unreadyButton_ = CreateButton("Unready", 160, IntVector2(20, marginTop));
 
     roleTitle_ = CreateLabel("", IntVector2(GetSubsystem<Graphics>()->GetWidth() / 2, GetSubsystem<Graphics>()->GetHeight() / 2));
     roleTitle_->SetTextAlignment(HA_CENTER);
@@ -136,6 +132,10 @@ void P2PMultiplayer::CreateUI()
     marginTop += 40;
     resetHostButton_ = CreateButton("Reset host", 160, IntVector2(20, marginTop));
 
+    marginTop += 100;
+    readyButton_ = CreateButton("Set ready", 160, IntVector2(20, marginTop));
+    static_cast<Text*>(readyButton_->GetChild(0))->SetColor(Color::GREEN);
+
     // No viewports or scene is defined. However, the default zone's fog color controls the fill color
     //GetSubsystem<Renderer>()->GetDefaultZone()->SetFogColor(Color(0.0f, 0.0f, 0.1f));
 }
@@ -149,13 +149,14 @@ void P2PMultiplayer::SubscribeToEvents()
     SubscribeToEvent(joinSession_, "Released", URHO3D_HANDLER(P2PMultiplayer, HandleJoinP2PSession));
 
     SubscribeToEvent(readyButton_, "Released", URHO3D_HANDLER(P2PMultiplayer, HandleReady));
-    SubscribeToEvent(unreadyButton_, "Released", URHO3D_HANDLER(P2PMultiplayer, HandleUnready));
 
     SubscribeToEvent(resetHostButton_, "Released", URHO3D_HANDLER(P2PMultiplayer, HandleResetHost));
 
     SubscribeToEvent(E_CLIENTCONNECTED, URHO3D_HANDLER(P2PMultiplayer, HandleClientConnected));
     SubscribeToEvent(E_CLIENTDISCONNECTED, URHO3D_HANDLER(P2PMultiplayer, HandleClientDisconnected));
     SubscribeToEvent(E_HTTPREQUESTFINISHED, URHO3D_HANDLER(P2PMultiplayer, HandleHttpResponse));
+    SubscribeToEvent(E_P2PALLREADYCHANGED, URHO3D_HANDLER(P2PMultiplayer, HandleAllReadyChanged));
+
 //    SubscribeToEvent(refreshServerList_, "Released", URHO3D_HANDLER(LANDiscovery, HandleDoNetworkDiscovery));
 }
 
@@ -167,26 +168,27 @@ void P2PMultiplayer::HandleServerConnected(StringHash eventType, VariantMap& eve
 void P2PMultiplayer::HandleStartP2PSession(StringHash eventType, VariantMap& eventData)
 {
     URHO3D_LOGINFO("HandleStartP2PSession");
-    GetSubsystem<Network>()->StartP2PSession(scene_);
+    GetSubsystem<Network>()->P2PStartSession(scene_);
     GetSubsystem<Network>()->MakeHttpRequest("http://frameskippers.com:82/?guid=" + GetSubsystem<Network>()->P2PGetGUID());
 }
 
 void P2PMultiplayer::HandleJoinP2PSession(StringHash eventType, VariantMap& eventData)
 {
     URHO3D_LOGINFO("HandleJoinP2PSession " + guid_->GetText());
-    GetSubsystem<Network>()->JoinP2PSession(guid_->GetText(), scene_);
+    GetSubsystem<Network>()->P2PJoinSession(guid_->GetText(), scene_);
 }
 
 void P2PMultiplayer::HandleReady(StringHash eventType, VariantMap& eventData)
 {
     URHO3D_LOGINFO("Ready");
-    GetSubsystem<Network>()->P2PSetReady(true);
-}
-
-void P2PMultiplayer::HandleUnready(StringHash eventType, VariantMap& eventData)
-{
-    URHO3D_LOGINFO("Unready");
-    GetSubsystem<Network>()->P2PSetReady(false);
+    GetSubsystem<Network>()->P2PSetReady(!GetSubsystem<Network>()->P2PGetReady());
+    if (GetSubsystem<Network>()->P2PGetReady()) {
+        static_cast<Text*>(readyButton_->GetChild(0))->SetText("Set unready");
+        static_cast<Text*>(readyButton_->GetChild(0))->SetColor(Color::RED);
+    } else {
+        static_cast<Text*>(readyButton_->GetChild(0))->SetText("Set ready");
+        static_cast<Text*>(readyButton_->GetChild(0))->SetColor(Color::GREEN);
+    }
 }
 
 void P2PMultiplayer::HandleUpdate(StringHash eventType, VariantMap& eventData)
@@ -201,7 +203,7 @@ void P2PMultiplayer::HandleUpdate(StringHash eventType, VariantMap& eventData)
     static float gametime;
     gametime += timestep * 10;
 
-    if (GetSubsystem<Network>()->P2PIsHostSystem()) {
+    if (GetSubsystem<Network>()->P2PIsHostSystem() && _allReady) {
         //if (body_) {
         //    body_->ApplyImpulse(Vector3(0, 10, 0));
         //}
@@ -409,10 +411,10 @@ void P2PMultiplayer::CreateScene()
     auto* body = ballNode->CreateComponent<RigidBody>();
     body->SetMass(1.0f);
     body->SetFriction(1.0f);
-    body->SetRestitution(1);
+    body->SetRestitution(0.0);
     body_ = body;
     // In addition to friction, use motion damping so that the ball can not accelerate limitlessly
-//    body->SetLinearDamping(0.5f);
+    body->SetLinearDamping(0.5f);
 //    body->SetAngularDamping(0.5f);
     //body->SetLinearVelocity(Vector3(0.1, 1, 0.1));
     auto* shape = ballNode->CreateComponent<CollisionShape>();
@@ -511,4 +513,10 @@ void P2PMultiplayer::HandleHttpResponse(StringHash eventType, VariantMap& eventD
     using namespace HttpRequestFinished;
     //URHO3D_LOGINFO("Response got: " + eventData[P_ADDRESS].GetString() + " => " + eventData[P_RESPONSE].GetString());
     guid_->SetText(eventData[P_RESPONSE].GetString());
+}
+
+void P2PMultiplayer::HandleAllReadyChanged(StringHash eventType, VariantMap& eventData)
+{
+    using namespace P2PAllReadyChanged;
+    _allReady = eventData[P_READY].GetBool();
 }

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

@@ -98,12 +98,13 @@ private:
     void HandleJoinP2PSession(StringHash eventType, VariantMap& eventData);
 
     void HandleReady(StringHash eventType, VariantMap& eventData);
-    void HandleUnready(StringHash eventType, VariantMap& eventData);
 
     void HandleResetHost(StringHash eventType, VariantMap& eventData);
 
     void HandleHttpResponse(StringHash eventType, VariantMap& eventData);
 
+    void HandleAllReadyChanged(StringHash eventType, VariantMap& eventData);
+
     Timer timer_;
 //    /// Stop server
 //	void HandleStopServer(StringHash eventType, VariantMap& eventData);
@@ -116,7 +117,6 @@ private:
     /// Join P2P Session
     SharedPtr<Button> joinSession_;
     SharedPtr<Button> readyButton_;
-    SharedPtr<Button> unreadyButton_;
     SharedPtr<Button> resetHostButton_;
     SharedPtr<LineEdit> guid_;
 //    /// Found server list
@@ -126,4 +126,5 @@ private:
     SharedPtr<Text> hostGuid_;
     SharedPtr<RigidBody> body_;
     SharedPtr<Node> ball_;
+    bool _allReady;
 };

+ 1 - 0
Source/Samples/Sample.inl

@@ -64,6 +64,7 @@ void Sample::Setup()
     engineParameters_[EP_SOUND]        = false;
     engineParameters_[EP_WINDOW_HEIGHT] = 480;
     engineParameters_[EP_WINDOW_WIDTH] = 640;
+    engineParameters_[EP_LOG_LEVEL] = LOG_INFO;
     engineParameters_[EP_MONITOR] = 1;
 
     // Construct a search path to find the resource prefix with two entries:

+ 1 - 0
Source/Urho3D/Engine/Console.cpp

@@ -50,6 +50,7 @@ static const int DEFAULT_HISTORY_SIZE = 16;
 
 const char* logStyles[] =
 {
+    "ConsoleDebugText",
     "ConsoleDebugText",
     "ConsoleInfoText",
     "ConsoleWarningText",

+ 10 - 1
Source/Urho3D/Network/Connection.cpp

@@ -465,6 +465,10 @@ bool Connection::ProcessMessage(int msgID, MemoryBuffer& msg)
         ProcessPackageInfo(msgID, msg);
         break;
 
+    case MSG_P2P_JOIN_REQUEST_DENIED:
+        ProcessP2PRequest(msgID);
+        break;
+
     default:
         processed = false;
         break;
@@ -1592,13 +1596,18 @@ void Connection::ProcessPackageInfo(int msgID, MemoryBuffer& msg)
     RequestNeededPackages(1, msg);
 }
 
+void Connection::ProcessP2PRequest(int msgID)
+{
+    URHO3D_LOGERROR("P2P join request denied!");
+}
+
+
 String Connection::GetAddress() const {
     return String(address_->ToString(false /*write port*/)); 
 }
 
 void Connection::SetAddressOrGUID(const SLNet::AddressOrGUID& addr)
 {
-    URHO3D_LOGINFO("SetAddressOrGUID " + String(addr.rakNetGuid.ToString()));
     delete address_;
     address_ = nullptr;
     address_ = new SLNet::AddressOrGUID(addr);

+ 2 - 1
Source/Urho3D/Network/Connection.h

@@ -234,7 +234,6 @@ public:
 
     /// Set network simulation parameters. Called by Network.
     void ConfigureNetworkSimulator(int latencyMs, float packetLoss);
-
     /// Current controls.
     Controls controls_;
     /// Controls timestamp. Incremented after each sent update.
@@ -282,6 +281,8 @@ private:
     /// Handle all packages loaded successfully. Also called directly on MSG_LOADSCENE if there are none.
     void OnPackagesReady();
 
+    void ProcessP2PRequest(int msgID);
+
     /// Scene.
     WeakPtr<Scene> scene_;
     /// Network replication state of the scene.

+ 69 - 35
Source/Urho3D/Network/Network.cpp

@@ -353,6 +353,7 @@ void Network::HandleMessage(const SLNet::AddressOrGUID& source, int packetID, in
 
 void Network::NewConnectionEstablished(const SLNet::AddressOrGUID& connection)
 {
+    P2PSubscribeForReadyEvents();
     if (clientConnections_[connection]) {
         URHO3D_LOGWARNINGF("Client already in the client list.", connection.rakNetGuid.ToString());
         //TODO proper scene state management
@@ -426,6 +427,7 @@ bool Network::Connect(const String& address, unsigned short port, Scene* scene,
 {
     URHO3D_PROFILE(Connect);
 
+    natPunchtroughAttempt_ = false;
     if (!rakPeerClient_->IsActive())
     {
         URHO3D_LOGINFO("Initializing client connection...");
@@ -875,10 +877,14 @@ void Network::HandleIncomingPacket(SLNet::Packet* packet, bool isServer)
 //            fullyConnectedMesh2_->ResetHostCalculation();
 //                SLNet::ConnectionAttemptResult car = rakPeerClient_->Connect(packet->systemAddress.ToString(false), packet->systemAddress.GetPort(), 0, 0);
 //                OnServerConnected(packet->systemAddress);
+            //TODO this works
             SLNet::BitStream bsOut;
-            bsOut.Write((unsigned char)MSG_P2P_REQUEST);
+            bsOut.Write((unsigned char)MSG_P2P_JOIN_REQUEST);
             rakPeer_->Send(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->guid, false);
-            URHO3D_LOGINFO("ID_CONNECTION_REQUEST_ACCEPTED");
+            //TODO this doesn't, but should. Maybe host GUID is not set correctly
+//            VectorBuffer msg;
+//            msg.WriteBool(true);
+//            serverConnection_->SendMessage(MSG_P2P_JOIN_REQUEST, true, true, msg);
         }
         packetHandled = true;
     }
@@ -977,28 +983,32 @@ void Network::HandleIncomingPacket(SLNet::Packet* packet, bool isServer)
     }
     else if (packetID == ID_READY_EVENT_SET)
     {
-        URHO3D_LOGINFOF("`````````````````````````` Got ID_READY_EVENT_SET from %s", packet->guid.ToString());
-        P2PShowReadyStatus();
+        URHO3D_LOGWARNINGF("Got ID_READY_EVENT_SET from %s", packet->guid.ToString());
+        VariantMap data = GetEventDataMap();
+        data[P2PAllReadyChanged::P_READY] = false;
+        SendEvent(E_P2PALLREADYCHANGED, data);
     }
     else if (packetID == ID_READY_EVENT_UNSET)
     {
-        URHO3D_LOGINFOF("`````````````````````````` Got ID_READY_EVENT_UNSET from %s", packet->guid.ToString());
-        P2PShowReadyStatus();
+        URHO3D_LOGWARNINGF("Got ID_READY_EVENT_UNSET from %s", packet->guid.ToString());
+        VariantMap data = GetEventDataMap();
+        data[P2PAllReadyChanged::P_READY] = false;
+        SendEvent(E_P2PALLREADYCHANGED, data);
     }
     else if (packetID == ID_READY_EVENT_ALL_SET)
     {
-        URHO3D_LOGINFOF("`````````````````````````` Got ID_READY_EVENT_ALL_SET from %s", packet->guid.ToString());
-        P2PShowReadyStatus();
+        VariantMap data = GetEventDataMap();
+        data[P2PAllReadyChanged::P_READY] = true;
+        SendEvent(E_P2PALLREADYCHANGED, data);
+        URHO3D_LOGWARNINGF("ID_READY_EVENT_ALL_SET from %s", packet->guid.ToString());
     }
     else if (packetID == ID_READY_EVENT_QUERY)
     {
         URHO3D_LOGINFOF("`````````````````````````` Got ID_READY_EVENT_QUERY from %s", packet->guid.ToString());
-        P2PShowReadyStatus();
     }
     else if (packetID == ID_READY_EVENT_FORCE_ALL_SET)
     {
         URHO3D_LOGINFOF("`````````````````````````` Got ID_READY_EVENT_FORCE_ALL_SET from %s", packet->guid.ToString());
-        P2PShowReadyStatus();
     }
     else if (packetID == ID_UNCONNECTED_PONG) // Host discovery response
     {
@@ -1052,27 +1062,13 @@ void Network::HandleIncomingPacket(SLNet::Packet* packet, bool isServer)
             for (auto it = clientConnections_.Begin(); it != clientConnections_.End(); ++it) {
                 URHO3D_LOGINFO("Setting new scene for clients");
 //                (*it).second_->SetScene(serverConnection_->GetScene());
+                //TODO decide what to do when we take ownership as the host, should the scene needs to be reloaded?
                 (*it).second_->SetSceneLoaded(true);
             }
-
-//            DataStructures::List<SLNet::RakNetGUID> participantList;
-//            fullyConnectedMesh2_->GetParticipantList(participantList);
-//            for (unsigned int i = 0; i < participantList.Size(); i++) {
-//                if (participantList[i] != rakPeerClient_->GetMyGUID()) {
-//                    readyEvent_->AddToWaitList(0, participantList[i]);
-//                }
-//            }
         }
         else
         {
             isServer_ = false;
-//            DataStructures::List<SLNet::RakNetGUID> participantList;
-//            fullyConnectedMesh2_->GetParticipantList(participantList);
-//            for (unsigned int i = 0; i < participantList.Size(); i++) {
-//                if (participantList[i] != rakPeerClient_->GetMyGUID()) {
-//                    readyEvent_->RemoveFromWaitList(0, participantList[i]);
-//                }
-//            }
             if (oldHost != SLNet::UNASSIGNED_RAKNET_GUID) {
                 URHO3D_LOGINFOF("ID_FCM2_NEW_HOST: A new system %s has become host, GUID=%s", packet->systemAddress.ToString(true), packet->guid.ToString());
             }
@@ -1081,6 +1077,14 @@ void Network::HandleIncomingPacket(SLNet::Packet* packet, bool isServer)
             }
         }
 
+        P2PSubscribeForReadyEvents();
+//        DataStructures::List<SLNet::RakNetGUID> participantList;
+//        fullyConnectedMesh2_->GetParticipantList(participantList);
+//        for (unsigned int i = 0; i < participantList.Size(); i++) {
+//            if (participantList[i] != rakPeerClient_->GetMyGUID()) {
+//                readyEvent_->AddToWaitList(0, participantList[i]);
+//            }
+//        }
         URHO3D_LOGINFO("");
 
 
@@ -1144,12 +1148,14 @@ void Network::HandleIncomingPacket(SLNet::Packet* packet, bool isServer)
     if (packetID >= ID_USER_PACKET_ENUM)
     {
         //URHO3D_LOGINFOF("ID_USER_PACKET_ENUM %i", packetID);
-        if (packetID == MSG_P2P_REQUEST) {
-            URHO3D_LOGINFO("MSG_P2P_REQUEST");
-            URHO3D_LOGINFO("Got request from client to join session. Executing StartVerifiedJoin()");
+        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);
-        } else if (packetID == MSG_P2P_DENY) {
-            URHO3D_LOGERROR("MSG_P2P_DENY");
+            //TODO or deny the join request
+//            VectorBuffer msg;
+//            GetConnection(packet->guid)->SendMessage(MSG_P2P_JOIN_REQUEST_DENIED, true, true, msg);
+//            ClientDisconnected(packet->guid);
         } else if (P2PIsHostSystem())
         {
 //            URHO3D_LOGINFO("Host system handler " + String(packet->guid.ToString()));
@@ -1325,8 +1331,10 @@ void Network::ConfigureNetworkSimulator()
         i->second_->ConfigureNetworkSimulator(simulatedLatency_, simulatedPacketLoss_);
 }
 
-bool Network::StartP2PSession(Scene* scene, const VariantMap& identity)
+bool Network::P2PStartSession(Scene* scene, const VariantMap& identity)
 {
+//    SetSimulatedLatency(500);
+//    SetSimulatedPacketLoss(0.3);
     isServer_ = true;
     if (!serverConnection_) {
         serverConnection_ = new Connection(context_, false, rakPeer_->GetMyBoundAddress(), rakPeer_);
@@ -1345,8 +1353,11 @@ bool Network::StartP2PSession(Scene* scene, const VariantMap& identity)
     return true;
 }
 
-void Network::JoinP2PSession(String guid, Scene* scene, const VariantMap& identity)
+void Network::P2PJoinSession(String guid, Scene* scene, const VariantMap& identity)
 {
+    natPunchtroughAttempt_ = false;
+//    SetSimulatedLatency(500);
+//    SetSimulatedPacketLoss(0.3);
     P2PSetReady(false);
     if (!serverConnection_) {
         serverConnection_ = new Connection(context_, false, rakPeer_->GetMyBoundAddress(), rakPeer_);
@@ -1383,7 +1394,6 @@ bool Network::P2PIsHostSystem()
         return true;
     }
     return false;
-//    return fullyConnectedMesh2_->IsHostSystem();
 }
 
 String Network::P2PGetHostAddress()
@@ -1397,10 +1407,13 @@ void Network::P2PSetReady(bool value)
     readyEvent_->SetEvent(0, value);
 }
 
+bool Network::P2PGetReady()
+{
+    return readyEvent_->IsEventSet(0);
+}
+
 String Network::P2PGetGUID()
 {
-//    URHO3D_LOGINFO("HOST GUID: " + String(fullyConnectedMesh2_->GetHostSystem().ToString()));
-//    URHO3D_LOGINFO("MY GUID: " + String(rakPeerClient_->GetGuidFromSystemAddress(SLNet::UNASSIGNED_SYSTEM_ADDRESS).ToString()));
     return String(rakPeer_->GetGuidFromSystemAddress(SLNet::UNASSIGNED_SYSTEM_ADDRESS).ToString());
 }
 
@@ -1460,6 +1473,27 @@ void Network::HandleTcpResponse()
     }
 }
 
+void Network::P2PSubscribeForReadyEvents() {
+//    if (isServer_) {
+        DataStructures::List<SLNet::RakNetGUID> participantList;
+        fullyConnectedMesh2_->GetParticipantList(participantList);
+        for (unsigned int i = 0; i < participantList.Size(); i++) {
+            if (participantList[i] != rakPeerClient_->GetMyGUID()) {
+                readyEvent_->AddToWaitList(0, participantList[i]);
+            }
+        }
+//    }
+//    else {
+//        DataStructures::List<SLNet::RakNetGUID> participantList;
+//        fullyConnectedMesh2_->GetParticipantList(participantList);
+//        for (unsigned int i = 0; i < participantList.Size(); i++) {
+//            if (participantList[i] != rakPeerClient_->GetMyGUID()) {
+//                readyEvent_->RemoveFromWaitList(0, participantList[i]);
+//            }
+//        }
+//    }
+}
+
 void RegisterNetworkLibrary(Context* context)
 {
     NetworkPriority::RegisterObject(context);

+ 6 - 6
Source/Urho3D/Network/Network.h

@@ -68,8 +68,9 @@ public:
     bool StartServer(unsigned short port);
 
     /// Start P2P session
-    bool StartP2PSession(Scene* scene, const VariantMap& identity = Variant::emptyVariantMap);
-    void JoinP2PSession(String guid, Scene* scene, const VariantMap& identity = Variant::emptyVariantMap);
+    bool P2PStartSession(Scene* scene, const VariantMap& identity = Variant::emptyVariantMap);
+    void P2PJoinSession(String guid, Scene* scene, const VariantMap& identity = Variant::emptyVariantMap);
+    void P2PSubscribeForReadyEvents();
     int GetP2PParticipantCount();
     bool P2PIsConnectedHost();
     bool P2PConnectNAT(const String& address, unsigned short port);
@@ -77,6 +78,7 @@ public:
     String P2PGetHostAddress();
     String P2PGetGUID();
     void P2PSetReady(bool value);
+    bool P2PGetReady();
     void P2PShowReadyStatus();
     void P2PResetHost();
 
@@ -166,10 +168,6 @@ private:
     SLNet::RakPeerInterface* rakPeer_;
     /// SLikeNet peer instance for client connection.
     SLNet::RakPeerInterface* rakPeerClient_;
-
-    /// P2P functionality
-    SLNet::FullyConnectedMesh2 *fullyConnectedMesh2_;
-
     /// Client's server connection.
     SharedPtr<Connection> serverConnection_;
     /// Server's client connections.
@@ -213,6 +211,8 @@ private:
     /// Attempting NAT punchtrough
     bool natPunchtroughAttempt_;
     SLNet::ReadyEvent *readyEvent_;
+    /// P2P functionality
+    SLNet::FullyConnectedMesh2 *fullyConnectedMesh2_;
     SLNet::ConnectionGraph2 *connectionGraph2_;
     SLNet::HTTPConnection2 *httpConnection2_;
     SLNet::TCPInterface *tcp_;

+ 9 - 0
Source/Urho3D/Network/NetworkEvents.h

@@ -149,4 +149,13 @@ URHO3D_EVENT(E_HTTPREQUESTFINISHED, HttpRequestFinished)
     URHO3D_PARAM(P_RESPONSE, Response);         // int
 }
 
+URHO3D_EVENT(E_P2PJOINREQUESTDENIED, P2PJoinRequestDenied)
+{
+}
+
+URHO3D_EVENT(E_P2PALLREADYCHANGED, P2PAllReadyChanged)
+{
+	URHO3D_PARAM(P_READY, Ready);   // String
+}
+
 }

+ 4 - 2
Source/Urho3D/Network/Protocol.h

@@ -63,8 +63,10 @@ static const int MSG_REMOTEEVENT = 0x96;
 static const int MSG_REMOTENODEEVENT = 0x97;
 /// Server->client: info about package.
 static const int MSG_PACKAGEINFO = 0x98;
-static const int MSG_P2P_REQUEST = 0x99;
-static const int MSG_P2P_DENY = 0x9A;
+/// Client->server ask to join P2P network
+static const int MSG_P2P_JOIN_REQUEST = 0x99;
+/// Server->client deny P2P join request
+static const int MSG_P2P_JOIN_REQUEST_DENIED = 0x9A;
 
 /// Fixed content ID for client controls update.
 static const unsigned CONTROLS_CONTENT_ID = 1;