Browse Source

refactoring

Arnis Lielturks 5 năm trước cách đây
mục cha
commit
a549e9bec1

+ 4 - 1
Source/Samples/17_SceneReplication/SceneReplication.cpp

@@ -378,7 +378,7 @@ void SceneReplication::HandlePostUpdate(StringHash eventType, VariantMap& eventD
         packetsOut_->SetText("Packets out (as client): " + String(GetSubsystem<Network>()->GetServerConnection()->GetPacketsOutPerSec()));
         packetsOut_->SetText("Packets out (as client): " + String(GetSubsystem<Network>()->GetServerConnection()->GetPacketsOutPerSec()));
         packetCounterTimer_.Reset();
         packetCounterTimer_.Reset();
     }
     }
-    if (packetCounterTimer_.GetMSec(false) > 1000 && GetSubsystem<Network>()->GetClientConnections().Size())
+    else if (packetCounterTimer_.GetMSec(false) > 1000 && GetSubsystem<Network>()->IsServerRunning())
     {
     {
         int packetsIn = 0;
         int packetsIn = 0;
         int packetsOut = 0;
         int packetsOut = 0;
@@ -390,6 +390,9 @@ void SceneReplication::HandlePostUpdate(StringHash eventType, VariantMap& eventD
         packetsIn_->SetText("Packets  in (as server)[" + String(connections.Size()) + "] : " + String(packetsIn));
         packetsIn_->SetText("Packets  in (as server)[" + String(connections.Size()) + "] : " + String(packetsIn));
         packetsOut_->SetText("Packets out (as server)[" + String(connections.Size()) + "]: " + String(packetsOut));
         packetsOut_->SetText("Packets out (as server)[" + String(connections.Size()) + "]: " + String(packetsOut));
         packetCounterTimer_.Reset();
         packetCounterTimer_.Reset();
+    } else if (packetCounterTimer_.GetMSec(false) > 1000) {
+        packetsIn_->SetText("Packets in : 0");
+        packetsOut_->SetText("Packets out : 0");
     }
     }
 }
 }
 
 

+ 11 - 5
Source/Urho3D/Network/Connection.cpp

@@ -43,9 +43,10 @@
 #include <SLikeNet/statistics.h>
 #include <SLikeNet/statistics.h>
 
 
 #ifdef URHO3D_WEBSOCKETS
 #ifdef URHO3D_WEBSOCKETS
-#include <libwebsockets.h>
 #include "WS/WSPacket.h"
 #include "WS/WSPacket.h"
 #include "WS/WSHandler.h"
 #include "WS/WSHandler.h"
+#include "WS/WSConnection.h"
+#include "WS/WSClient.h"
 #endif
 #endif
 
 
 #ifdef SendMessage
 #ifdef SendMessage
@@ -96,7 +97,7 @@ Connection::Connection(Context* context, bool isClient, const SLNet::AddressOrGU
 }
 }
 
 
 #ifdef URHO3D_WEBSOCKETS
 #ifdef URHO3D_WEBSOCKETS
-Connection::Connection(Context* context, bool isClient, lws *ws, WSHandler* wsHandler):
+Connection::Connection(Context* context, bool isClient, const WSConnection& ws, WSHandler* wsHandler):
     Object(context),
     Object(context),
     timeStamp_(0),
     timeStamp_(0),
     sendMode_(OPSM_NONE),
     sendMode_(OPSM_NONE),
@@ -106,14 +107,14 @@ Connection::Connection(Context* context, bool isClient, lws *ws, WSHandler* wsHa
     logStatistics_(false),
     logStatistics_(false),
     address_(nullptr),
     address_(nullptr),
     packedMessageLimit_(1024),
     packedMessageLimit_(1024),
-    ws_(ws),
     peer_(nullptr),
     peer_(nullptr),
+    ws_(ws),
     wsHandler_(wsHandler)
     wsHandler_(wsHandler)
 {
 {
     sceneState_.connection_ = this;
     sceneState_.connection_ = this;
 }
 }
 
 
-void Connection::SetWS(lws* ws)
+void Connection::SetWS(const WSConnection& ws)
 {
 {
     ws_ = ws;
     ws_ = ws;
 }
 }
@@ -294,6 +295,11 @@ void Connection::Disconnect(int waitMSec)
 {
 {
     if (peer_)
     if (peer_)
         peer_->CloseConnection(*address_, true);
         peer_->CloseConnection(*address_, true);
+
+#ifdef URHO3D_WEBSOCKETS
+    if (!isClient_ && wsHandler_)
+        static_cast<WSClient*>(wsHandler_)->Disconnect();
+#endif
 }
 }
 
 
 void Connection::SendServerUpdate()
 void Connection::SendServerUpdate()
@@ -1249,7 +1255,7 @@ String Connection::ToString() const
         return GetAddress() + ":" + String(GetPort());
         return GetAddress() + ":" + String(GetPort());
 
 
 #ifdef URHO3D_WEBSOCKETS
 #ifdef URHO3D_WEBSOCKETS
-    if (ws_)
+    if (ws_.GetWS())
         return "WS connection";
         return "WS connection";
 #endif
 #endif
 
 

+ 8 - 8
Source/Urho3D/Network/Connection.h

@@ -31,6 +31,10 @@
 #include "../IO/VectorBuffer.h"
 #include "../IO/VectorBuffer.h"
 #include "../Scene/ReplicationState.h"
 #include "../Scene/ReplicationState.h"
 
 
+#ifdef URHO3D_WEBSOCKETS
+#include "WS/WSConnection.h"
+#endif
+
 namespace SLNet
 namespace SLNet
 {
 {
     class SystemAddress;
     class SystemAddress;
@@ -41,11 +45,6 @@ namespace SLNet
     class RakPeerInterface;
     class RakPeerInterface;
 }
 }
 
 
-#ifdef URHO3D_WEBSOCKETS
-// libwebsockets struct
-struct lws;
-#endif
-
 namespace Urho3D
 namespace Urho3D
 {
 {
 
 
@@ -56,6 +55,7 @@ class Scene;
 class Serializable;
 class Serializable;
 class PackageFile;
 class PackageFile;
 class WSHandler;
 class WSHandler;
+class WSConnection;
 
 
 /// Queued remote event.
 /// Queued remote event.
 struct RemoteEvent
 struct RemoteEvent
@@ -130,8 +130,8 @@ public:
     Connection(Context* context, bool isClient, const SLNet::AddressOrGUID& address, SLNet::RakPeerInterface* peer);
     Connection(Context* context, bool isClient, const SLNet::AddressOrGUID& address, SLNet::RakPeerInterface* peer);
 #ifdef URHO3D_WEBSOCKETS
 #ifdef URHO3D_WEBSOCKETS
     /// Construct with context, Websocket connection
     /// Construct with context, Websocket connection
-    Connection(Context* context, bool isClient, lws *ws, WSHandler* wsHandler);
-    void SetWS(lws* ws);
+    Connection(Context* context, bool isClient, const WSConnection& ws, WSHandler* wsHandler);
+    void SetWS(const WSConnection& ws);
     void SetWSHandler(WSHandler* server);
     void SetWSHandler(WSHandler* server);
     const WSHandler* GetWSHandler() { return wsHandler_; }
     const WSHandler* GetWSHandler() { return wsHandler_; }
 #endif
 #endif
@@ -364,7 +364,7 @@ private:
     int packedMessageLimit_;
     int packedMessageLimit_;
 #ifdef URHO3D_WEBSOCKETS
 #ifdef URHO3D_WEBSOCKETS
     /// Websocket connection
     /// Websocket connection
-    lws *ws_;
+    WSConnection ws_;
     /// Websocket connection handler
     /// Websocket connection handler
     WSHandler* wsHandler_;
     WSHandler* wsHandler_;
 #endif
 #endif

+ 54 - 6
Source/Urho3D/Network/Network.cpp

@@ -42,6 +42,7 @@
 #include "WS/WSServer.h"
 #include "WS/WSServer.h"
 #include "WS/WSClient.h"
 #include "WS/WSClient.h"
 #include "WS/WSConnection.h"
 #include "WS/WSConnection.h"
+#include "WS/WSPacket.h"
 #endif
 #endif
 
 
 #include <SLikeNet/MessageIdentifiers.h>
 #include <SLikeNet/MessageIdentifiers.h>
@@ -339,7 +340,7 @@ void Network::NewConnectionEstablished(const SLNet::AddressOrGUID& connection)
 }
 }
 
 
 #ifdef URHO3D_WEBSOCKETS
 #ifdef URHO3D_WEBSOCKETS
-void Network::NewConnectionEstablished(lws* ws)
+void Network::NewConnectionEstablished(const WSConnection& ws)
 {
 {
     // Create a new client connection corresponding to this MessageConnection
     // Create a new client connection corresponding to this MessageConnection
     SharedPtr<Connection> newConnection(new Connection(context_, true, ws, wsServer_));
     SharedPtr<Connection> newConnection(new Connection(context_, true, ws, wsServer_));
@@ -349,6 +350,25 @@ void Network::NewConnectionEstablished(lws* ws)
 
 
     NewConnectionEstablished(websocketClientConnections_[WSConnection(ws)]);
     NewConnectionEstablished(websocketClientConnections_[WSConnection(ws)]);
 }
 }
+
+void Network::ClientDisconnected(const WSConnection& ws)
+{
+    // Remove the client connection that corresponds to this MessageConnection
+    HashMap<WSConnection, SharedPtr<Connection> >::Iterator i = websocketClientConnections_.Find(ws);
+    if (i != websocketClientConnections_.End())
+    {
+        Connection* connection = i->second_;
+        URHO3D_LOGINFO("Client " + connection->ToString() + " disconnected");
+
+        using namespace ClientDisconnected;
+
+        VariantMap& eventData = GetEventDataMap();
+        eventData[P_CONNECTION] = connection;
+        connection->SendEvent(E_CLIENTDISCONNECTED, eventData);
+
+        websocketClientConnections_.Erase(i);
+    }
+}
 #endif
 #endif
 
 
 void Network::ClientDisconnected(const SLNet::AddressOrGUID& connection)
 void Network::ClientDisconnected(const SLNet::AddressOrGUID& connection)
@@ -444,7 +464,7 @@ bool Network::Connect(const String& address, unsigned short port, Scene* scene,
 bool Network::ConnectWS(const String& address, unsigned short port, Scene* scene, const VariantMap& identity)
 bool Network::ConnectWS(const String& address, unsigned short port, Scene* scene, const VariantMap& identity)
 {
 {
     URHO3D_PROFILE(ConnectWS);
     URHO3D_PROFILE(ConnectWS);
-    wsClient_ = new WSClient(this);
+    wsClient_ = new WSClient(context_);
     int result = wsClient_->Connect();
     int result = wsClient_->Connect();
 
 
     serverConnection_ = new Connection(context_, false, nullptr, wsClient_);
     serverConnection_ = new Connection(context_, false, nullptr, wsClient_);
@@ -473,7 +493,7 @@ bool Network::StartServer(unsigned short port, unsigned int maxConnections)
 
 
     URHO3D_PROFILE(StartServer);
     URHO3D_PROFILE(StartServer);
 #ifdef URHO3D_WEBSOCKETS
 #ifdef URHO3D_WEBSOCKETS
-    wsServer_ = new WSServer(this);
+    wsServer_ = new WSServer(context_);
     wsServer_->StartServer();
     wsServer_->StartServer();
 #endif
 #endif
     
     
@@ -503,11 +523,18 @@ void Network::StopServer()
 {
 {
     clientConnections_.Clear();
     clientConnections_.Clear();
 
 
+#ifdef URHO3D_WEBSOCKETS
+    if (wsServer_)
+        wsServer_->StopServer();
+        wsServer_.Reset();
+#endif
+
     if (!rakPeer_)
     if (!rakPeer_)
         return;
         return;
 
 
     if (!IsServerRunning())
     if (!IsServerRunning())
         return;
         return;
+
     // Provide 300 ms to notify
     // Provide 300 ms to notify
     rakPeer_->Shutdown(300);
     rakPeer_->Shutdown(300);
 
 
@@ -718,7 +745,7 @@ Connection* Network::GetConnection(const SLNet::AddressOrGUID& connection) const
 }
 }
 
 
 #ifdef URHO3D_WEBSOCKETS
 #ifdef URHO3D_WEBSOCKETS
-Connection* Network::GetConnection(lws* ws) const
+Connection* Network::GetConnection(const WSConnection& ws) const
 {
 {
     if (serverConnection_ && serverConnection_->GetWSHandler() == wsClient_)
     if (serverConnection_ && serverConnection_->GetWSHandler() == wsClient_)
         return serverConnection_;
         return serverConnection_;
@@ -968,8 +995,9 @@ void Network::HandleIncomingPacket(SLNet::Packet* packet, bool isServer)
 
 
 }
 }
 
 
-void Network::HandleIncomingPacket(lws* ws, VectorBuffer& buffer, bool isServer)
+void Network::HandleIncomingPacket(const WSPacket* packet, bool isServer)
 {
 {
+    auto buffer = packet->second_;
     // SLikeNet reserved byte must be ignored
     // SLikeNet reserved byte must be ignored
     int id = buffer.ReadUByte();
     int id = buffer.ReadUByte();
     int messageID = buffer.ReadUInt();
     int messageID = buffer.ReadUInt();
@@ -980,7 +1008,7 @@ void Network::HandleIncomingPacket(lws* ws, VectorBuffer& buffer, bool isServer)
     }
     }
     MemoryBuffer msg(buffer.GetData() + padding, buffer.GetSize() - padding);
     MemoryBuffer msg(buffer.GetData() + padding, buffer.GetSize() - padding);
     // Only process messages from known sources
     // Only process messages from known sources
-    Connection * connection = GetConnection(ws);
+    Connection * connection = GetConnection(packet->first_);
     if (isServer) {
     if (isServer) {
         if (connection) {
         if (connection) {
             if (connection->ProcessMessage((int) messageID, msg)) {
             if (connection->ProcessMessage((int) messageID, msg)) {
@@ -1185,6 +1213,26 @@ void Network::OnServerDisconnected(const SLNet::AddressOrGUID& address)
     }
     }
 }
 }
 
 
+#ifdef URHO3D_WEBSOCKETS
+void Network::OnServerDisconnected(const WSConnection& ws, bool failedConnect)
+{
+    serverConnection_.Reset();
+    if (wsClient_)
+        wsClient_.Reset();
+
+    if (!failedConnect)
+    {
+        URHO3D_LOGINFO("Disconnected from server");
+        SendEvent(E_SERVERDISCONNECTED);
+    }
+    else
+    {
+        URHO3D_LOGERROR("Failed to connect to server");
+        SendEvent(E_CONNECTFAILED);
+    }
+}
+#endif
+
 void Network::ConfigureNetworkSimulator()
 void Network::ConfigureNetworkSimulator()
 {
 {
     if (serverConnection_)
     if (serverConnection_)

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

@@ -26,11 +26,7 @@
 #include "../Core/Object.h"
 #include "../Core/Object.h"
 #include "../IO/VectorBuffer.h"
 #include "../IO/VectorBuffer.h"
 #include "../Network/Connection.h"
 #include "../Network/Connection.h"
-
-#ifdef URHO3D_WEBSOCKETS
-// libwebsockets internal struct
-struct lws;
-#endif
+#include "WS/WSPacket.h"
 
 
 namespace Urho3D
 namespace Urho3D
 {
 {
@@ -40,9 +36,9 @@ class MemoryBuffer;
 class Scene;
 class Scene;
 
 
 #ifdef URHO3D_WEBSOCKETS
 #ifdef URHO3D_WEBSOCKETS
-class WSServer;
 class WSClient;
 class WSClient;
 class WSConnection;
 class WSConnection;
+class WSServer;
 #endif
 #endif
 
 
 /// %Network subsystem. Manages client-server communications using the UDP protocol.
 /// %Network subsystem. Manages client-server communications using the UDP protocol.
@@ -69,7 +65,9 @@ public:
     void NewConnectionEstablished(const SLNet::AddressOrGUID& connection);
     void NewConnectionEstablished(const SLNet::AddressOrGUID& connection);
 #ifdef URHO3D_WEBSOCKETS
 #ifdef URHO3D_WEBSOCKETS
     /// Handle new Websockets client connection.
     /// Handle new Websockets client connection.
-    void NewConnectionEstablished(lws* ws);
+    void NewConnectionEstablished(const WSConnection& ws);
+    /// Handle a client disconnection.
+    void ClientDisconnected(const WSConnection& ws);
 #endif
 #endif
     /// Handle a client disconnection.
     /// Handle a client disconnection.
     void ClientDisconnected(const SLNet::AddressOrGUID& connection);
     void ClientDisconnected(const SLNet::AddressOrGUID& connection);
@@ -88,7 +86,7 @@ public:
     /// Connect to a server using Websockets connection. Return true if connection process successfully started.
     /// Connect to a server using Websockets connection. Return true if connection process successfully started.
     bool ConnectWS(const String& address, unsigned short port, Scene* scene, const VariantMap& identity = Variant::emptyVariantMap);
     bool ConnectWS(const String& address, unsigned short port, Scene* scene, const VariantMap& identity = Variant::emptyVariantMap);
     /// Return a client or server connection by Websocket connection, or null if none exist.
     /// Return a client or server connection by Websocket connection, or null if none exist.
-    Connection* GetConnection(lws* ws) const;
+    Connection* GetConnection(const WSConnection& ws) const;
     /// Handle server connection.
     /// Handle server connection.
     void OnServerConnected(lws* ws);
     void OnServerConnected(lws* ws);
 #endif
 #endif
@@ -177,7 +175,9 @@ private:
     void HandleIncomingPacket(SLNet::Packet* packet, bool isServer);
     void HandleIncomingPacket(SLNet::Packet* packet, bool isServer);
 #ifdef URHO3D_WEBSOCKETS
 #ifdef URHO3D_WEBSOCKETS
     /// All incoming websocket packets are handled here.
     /// All incoming websocket packets are handled here.
-    void HandleIncomingPacket(lws* ws, VectorBuffer& buffer, bool isServer);
+    void HandleIncomingPacket(const WSPacket* packet, bool isServer);
+    /// Handle server disconnection.
+    void OnServerDisconnected(const WSConnection& ws, bool failedConnect = false);
 #endif
 #endif
 
 
     /// SLikeNet peer instance for server connection.
     /// SLikeNet peer instance for server connection.
@@ -230,8 +230,8 @@ private:
     String guid_;
     String guid_;
 
 
 #ifdef URHO3D_WEBSOCKETS
 #ifdef URHO3D_WEBSOCKETS
-    WSServer* wsServer_;
-    WSClient* wsClient_;
+    SharedPtr<WSServer> wsServer_;
+    SharedPtr<WSClient> wsClient_;
 #endif
 #endif
 };
 };
 
 

+ 115 - 35
Source/Urho3D/Network/WS/WSClient.cpp

@@ -1,14 +1,36 @@
+//
+// Copyright (c) 2008-2020 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include "../../Core/WorkQueue.h"
+#include "../../IO/MemoryBuffer.h"
+#include "../../IO/Log.h"
+#include "../Network.h"
 #include "WSClient.h"
 #include "WSClient.h"
 #include "WSHandler.h"
 #include "WSHandler.h"
-#include "../Network.h"
-#include "../../IO/MemoryBuffer.h"
 
 
 #include <libwebsockets.h>
 #include <libwebsockets.h>
 #include <string.h>
 #include <string.h>
 #include <signal.h>
 #include <signal.h>
-#include "../../IO/Log.h"
 
 
-const int ID_USER_PACKET_ENUM = 134;
 static Urho3D::WSClient* WSClientInstance = nullptr;
 static Urho3D::WSClient* WSClientInstance = nullptr;
 static struct lws_context *context;
 static struct lws_context *context;
 
 
@@ -56,23 +78,20 @@ static void connect_cb(lws_sorted_usec_list_t *_sul)
         lws_sul_schedule(context, 0, _sul, connect_cb, 5 * LWS_USEC_PER_SEC);
         lws_sul_schedule(context, 0, _sul, connect_cb, 5 * LWS_USEC_PER_SEC);
 }
 }
 
 
-static int ws_client_callback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
+static int WSCallback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
 {
 {
     URHO3D_LOGINFOF("Incoming client messsage reason %d", reason);
     URHO3D_LOGINFOF("Incoming client messsage reason %d", reason);
     switch (reason) {
     switch (reason) {
-        case LWS_CALLBACK_CLIENT_CLOSED:
-            URHO3D_LOGINFOF("Connection closed from server!");
-            break;
         case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
         case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
-            lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
-                     in ? (char *)in : "(null)");
-            lws_sul_schedule(context, 0, &sul, connect_cb, 5 * LWS_USEC_PER_SEC);
+            if (WSClientInstance) {
+                WSClientInstance->SetState(Urho3D::WCS_CONNECTION_FAILED);
+            }
             break;
             break;
 
 
         case LWS_CALLBACK_CLIENT_ESTABLISHED:
         case LWS_CALLBACK_CLIENT_ESTABLISHED:
             if (WSClientInstance) {
             if (WSClientInstance) {
                 WSClientInstance->SetWSConnection(wsi);
                 WSClientInstance->SetWSConnection(wsi);
-                WSClientInstance->GetNetworkInstance()->OnServerConnected(wsi);
+                WSClientInstance->SetState(Urho3D::WCS_CONNECTED);
             }
             }
             lwsl_user("%s: established\n", __func__);
             lwsl_user("%s: established\n", __func__);
             break;
             break;
@@ -80,7 +99,7 @@ static int ws_client_callback(struct lws *wsi, enum lws_callback_reasons reason,
         case LWS_CALLBACK_CLIENT_RECEIVE: {
         case LWS_CALLBACK_CLIENT_RECEIVE: {
             URHO3D_LOGINFOF("Received buffer of size %d", len);
             URHO3D_LOGINFOF("Received buffer of size %d", len);
             Urho3D::VectorBuffer b((unsigned char*)in, len);
             Urho3D::VectorBuffer b((unsigned char*)in, len);
-            if (b.GetData()[0] == ID_USER_PACKET_ENUM) {
+            if (b.GetData()[0] == URHO3D_MESSAGE) {
                 WSPacket packet(wsi, b);
                 WSPacket packet(wsi, b);
                 if (WSClientInstance) {
                 if (WSClientInstance) {
                     WSClientInstance->AddIncomingPacket(packet);
                     WSClientInstance->AddIncomingPacket(packet);
@@ -94,21 +113,30 @@ static int ws_client_callback(struct lws *wsi, enum lws_callback_reasons reason,
 
 
         case LWS_CALLBACK_CLIENT_WRITEABLE:
         case LWS_CALLBACK_CLIENT_WRITEABLE:
             if (WSClientInstance) {
             if (WSClientInstance) {
-                if(WSClientInstance->GetNumOutgoingPackets()) {
-                    WSPacket& packet = WSClientInstance->GetOutgoingPacket();
-                    unsigned char buf[LWS_PRE + packet.second_.GetSize()];
-                    memcpy(&buf[LWS_PRE], packet.second_.GetData(), packet.second_.GetSize());
-                    int retval = lws_write(wsi, &buf[LWS_PRE], packet.second_.GetSize(), LWS_WRITE_BINARY);
-                    if (retval < packet.second_.GetSize()) {
-                        URHO3D_LOGERRORF("Failed to write to WS, ret = %d", retval);
-                        break;
+                if(WSClientInstance->GetNumOutgoingPackets(wsi)) {
+                    auto packet = WSClientInstance->GetOutgoingPacket(wsi);
+                    if (packet) {
+                        unsigned char buf[LWS_PRE + packet->second_.GetSize()];
+                        memcpy(&buf[LWS_PRE],  packet->second_.GetData(),  packet->second_.GetSize());
+                        int retval = lws_write(wsi, &buf[LWS_PRE],  packet->second_.GetSize(), LWS_WRITE_BINARY);
+                        if (retval <  packet->second_.GetSize()) {
+                            URHO3D_LOGERRORF("Failed to write to WS, ret = %d", retval);
+                            break;
+                        }
+                        WSClientInstance->RemoveOutgoingPacket(wsi);
                     }
                     }
-                    WSClientInstance->RemoveOutgoingPacket();
                 }
                 }
                 lws_callback_on_writable(wsi);
                 lws_callback_on_writable(wsi);
             }
             }
             break;
             break;
 
 
+        case LWS_CALLBACK_CLIENT_CLOSED:
+            if (WSClientInstance) {
+                WSClientInstance->Disconnect();
+            }
+            URHO3D_LOGINFOF("LWS_CALLBACK_CLIENT_CLOSED");
+            break;
+
         default:
         default:
             break;
             break;
     }
     }
@@ -116,10 +144,11 @@ static int ws_client_callback(struct lws *wsi, enum lws_callback_reasons reason,
     return 0;
     return 0;
 }
 }
 
 
+
 static const struct lws_protocols protocols[] = {
 static const struct lws_protocols protocols[] = {
         {
         {
                 "ws_client",
                 "ws_client",
-                ws_client_callback,
+                WSCallback,
                       0,
                       0,
                          0,
                          0,
         },
         },
@@ -133,10 +162,19 @@ static void sigint_handler(int sig)
 
 
 using namespace Urho3D;
 using namespace Urho3D;
 
 
-WSClient::WSClient(Urho3D::Network *networkInstance):
-WSHandler(networkInstance),
+static void RunService(const WorkItem* item, unsigned threadIndex) {
+    int result = lws_service(context, 0);
+    if (result < 0 && WSClientInstance) {
+        WSClientInstance->Disconnect();
+    }
+    URHO3D_LOGINFOF("Running client service");
+}
+
+WSClient::WSClient(Context* context):
+Object(context),
 ws_(nullptr)
 ws_(nullptr)
 {
 {
+    SetState(WCS_DISCONNECTED);
     WSClientInstance = this;
     WSClientInstance = this;
 }
 }
 
 
@@ -183,31 +221,73 @@ int WSClient::Connect()
     }
     }
 
 
     lws_sul_schedule(context, 0, &sul, connect_cb, 50);
     lws_sul_schedule(context, 0, &sul, connect_cb, 50);
+    SubscribeToEvent(E_WORKITEMCOMPLETED, URHO3D_HANDLER(WSClient, HandleWorkItemFinished));
+    SetState(WCS_CONNECTING);
     return 0;
     return 0;
 }
 }
 
 
 void WSClient::Update(float timestep)
 void WSClient::Update(float timestep)
 {
 {
+    if (currentState_ == WCS_CONNECTING && nextState_ == WCS_CONNECTED) {
+        GetSubsystem<Network>()->OnServerConnected(GetWSConnection());
+    }
+    if (nextState_ == WCS_DISCONNECTED) {
+        GetSubsystem<Network>()->OnServerDisconnected(GetWSConnection(), false);
+    }
+    if (nextState_ == WCS_CONNECTION_FAILED) {
+        GetSubsystem<Network>()->OnServerDisconnected(GetWSConnection(), true);
+    }
     if (context) {
     if (context) {
-        int result = lws_service(context, 0);
-        if (result < 0) {
-            Disconnect();
-        }
-        while (GetNumIncomingPackets()) {
-            WSPacket& packet = GetIncomingPacket();
-            networkInstance_->HandleIncomingPacket(packet.first_, packet.second_, false);
-            RemoveIncomingPacket();
+        if (!serviceWorkItem_ && currentState_ != WCS_DISCONNECTED) {
+            WorkQueue *workQueue = GetSubsystem<WorkQueue>();
+            serviceWorkItem_ = workQueue->GetFreeItem();
+            serviceWorkItem_->priority_ = M_MAX_INT;
+            serviceWorkItem_->workFunction_ = RunService;
+            serviceWorkItem_->aux_ = this;
+            serviceWorkItem_->sendEvent_ = true;
+            workQueue->AddWorkItem(serviceWorkItem_);
         }
         }
     }
     }
+
+    while (GetNumIncomingPackets()) {
+        auto packet = GetIncomingPacket();
+        GetSubsystem<Network>()->HandleIncomingPacket(packet, false);
+        RemoveIncomingPacket();
+    }
+
+    if (currentState_ != nextState_) {
+        currentState_ = nextState_;
+    }
 }
 }
 
 
 void WSClient::Disconnect()
 void WSClient::Disconnect()
 {
 {
-    lws_context_destroy(context);
-    context = nullptr;
+    SetState(WCS_DISCONNECTED);
+    if (context)
+    {
+        lws_context_destroy(context);
+        context = nullptr;
+    }
 }
 }
 
 
 void WSClient::SetWSConnection(lws *ws)
 void WSClient::SetWSConnection(lws *ws)
 {
 {
     ws_ = ws;
     ws_ = ws;
 }
 }
+
+void WSClient::SetState(WSClientState state)
+{
+    nextState_ = state;
+}
+
+void WSClient::HandleWorkItemFinished(StringHash eventType, VariantMap& eventData)
+{
+    using namespace WorkItemCompleted;
+    WorkItem *workItem = reinterpret_cast<WorkItem *>(eventData[P_ITEM].GetPtr());
+    if (workItem->aux_ != this) {
+        return;
+    }
+    if (workItem->workFunction_ == RunService) {
+        serviceWorkItem_.Reset();
+    }
+}

+ 42 - 6
Source/Urho3D/Network/WS/WSClient.h

@@ -1,27 +1,63 @@
+//
+// Copyright (c) 2008-2020 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
 #pragma once
 #pragma once
 
 
-#include "../../IO/VectorBuffer.h"
-#include "../../Core/Mutex.h"
 #include "../../Container/List.h"
 #include "../../Container/List.h"
+#include "../../Core/Object.h"
+#include "../../Core/Mutex.h"
+#include "../../IO/VectorBuffer.h"
 #include "WSHandler.h"
 #include "WSHandler.h"
 
 
-struct lws;
-
 namespace Urho3D {
 namespace Urho3D {
 class Network;
 class Network;
+class WorkItem;
+
+enum WSClientState {
+    WCS_CONNECTING,
+    WCS_CONNECTED,
+    WCS_CONNECTION_FAILED,
+    WCS_DISCONNECTED,
+};
 
 
-class WSClient: public WSHandler {
+class WSClient: public WSHandler, public Object {
+    URHO3D_OBJECT(WSClient, Object);
 public:
 public:
-    WSClient(Urho3D::Network *networkInstance);
+    WSClient(Context* context);
     ~WSClient();
     ~WSClient();
     int Connect();
     int Connect();
     void Update(float timestep);
     void Update(float timestep);
     void Disconnect();
     void Disconnect();
 
 
+    void SetState(WSClientState state);
     void SetWSConnection(lws *ws);
     void SetWSConnection(lws *ws);
     lws* GetWSConnection() { return ws_; };
     lws* GetWSConnection() { return ws_; };
 
 
 private:
 private:
+    void HandleWorkItemFinished(StringHash eventType, VariantMap& eventData);
+
     struct lws *ws_;
     struct lws *ws_;
+    SharedPtr<WorkItem> serviceWorkItem_;
+    WSClientState nextState_;
+    WSClientState currentState_;
 };
 };
 }
 }

+ 31 - 2
Source/Urho3D/Network/WS/WSConnection.h

@@ -1,16 +1,45 @@
+//
+// Copyright (c) 2008-2020 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
 #pragma once
 #pragma once
+
+#include <cstdint>
+#include <stddef.h>
+
 struct lws;
 struct lws;
 
 
 namespace Urho3D {
 namespace Urho3D {
 
 
     class WSConnection {
     class WSConnection {
     public:
     public:
-        WSConnection(){}
+        WSConnection():
+        ws_(nullptr)
+        {}
         WSConnection(lws* ws):
         WSConnection(lws* ws):
         ws_(ws)
         ws_(ws)
         {}
         {}
 
 
-        inline bool operator==( WSConnection const& connA) const { return connA.ToHash() == ToHash();}
+        lws* GetWS() const { return ws_; }
+        inline bool operator==( WSConnection const& connA) const { return connA.ws_ == ws_;}
 
 
         unsigned ToHash() const
         unsigned ToHash() const
         {
         {

+ 0 - 52
Source/Urho3D/Network/WS/WSEvents.h

@@ -1,52 +0,0 @@
-//
-// Copyright (c) 2008-2020 the Urho3D project.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-
-#pragma once
-
-#include "../../Core/Object.h"
-
-namespace Urho3D
-{
-
-/// New client connection established.
-URHO3D_EVENT(E_WSCLIENTCONNECTED, WSClientConnected)
-{
-    URHO3D_PARAM(P_SOCKET, Socket); // libwebsocket pointer
-}
-
-/// Client disconnected
-URHO3D_EVENT(E_WSCLIENTDISCONNECTED, WSClientDisconnected)
-{
-    URHO3D_PARAM(P_SOCKET, Socket); // libwebsocket pointer
-}
-
-/// Connected to WS server
-URHO3D_EVENT(E_WSSERVERCONNECTED, WSServerConnected)
-{
-}
-
-/// Disconnected frm WS server
-URHO3D_EVENT(E_WSSERVERDISCONNECTED, WSServerDisconnected)
-{
-}
-
-}

+ 58 - 12
Source/Urho3D/Network/WS/WSHandler.cpp

@@ -1,11 +1,35 @@
-#include "WSHandler.h"
+//
+// Copyright (c) 2008-2020 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+
 #include "../../IO/Log.h"
 #include "../../IO/Log.h"
+#include "WSHandler.h"
+#include "WSPacket.h"
+
 #include <libwebsockets.h>
 #include <libwebsockets.h>
 
 
 using namespace Urho3D;
 using namespace Urho3D;
 
 
-WSHandler::WSHandler(Urho3D::Network *networkInstance):
-    networkInstance_(networkInstance)
+WSHandler::WSHandler()
 {
 {
 
 
 }
 }
@@ -21,9 +45,9 @@ void WSHandler::AddIncomingPacket(const WSPacket& packet)
     incomingPackets_.Push(packet);
     incomingPackets_.Push(packet);
 }
 }
 
 
-WSPacket& WSHandler::GetIncomingPacket()
+WSPacket* WSHandler::GetIncomingPacket()
 {
 {
-    return incomingPackets_.Front();
+    return &incomingPackets_.Front();
 }
 }
 
 
 void WSHandler::RemoveIncomingPacket()
 void WSHandler::RemoveIncomingPacket()
@@ -38,23 +62,45 @@ void WSHandler::RemoveIncomingPacket()
 void WSHandler::AddOutgoingPacket(const WSPacket& packet)
 void WSHandler::AddOutgoingPacket(const WSPacket& packet)
 {
 {
     Urho3D::MutexLock lock(GetOutgoingMutex());
     Urho3D::MutexLock lock(GetOutgoingMutex());
-    outgoingPackets_.Push(packet);
+    outgoingPackets_[packet.first_].Push(packet);
 }
 }
 
 
-WSPacket& WSHandler::GetOutgoingPacket()
+WSPacket* WSHandler::GetOutgoingPacket(const WSConnection& ws)
 {
 {
-    return outgoingPackets_.Front();
+    if (outgoingPackets_.Contains(ws)) {
+        return &outgoingPackets_[ws].Front();
+    }
+
+    return nullptr;
 }
 }
 
 
-void WSHandler::RemoveOutgoingPacket()
+void WSHandler::RemoveOutgoingPacket(const WSConnection& ws)
 {
 {
-    if (GetNumOutgoingPackets() > 0) {
+    if (GetNumOutgoingPackets(ws) > 0) {
         Urho3D::MutexLock lock(GetOutgoingMutex());
         Urho3D::MutexLock lock(GetOutgoingMutex());
-        outgoingPackets_.Front().second_.Clear();
-        outgoingPackets_.PopFront();
+        outgoingPackets_[ws].Front().second_.Clear();
+        outgoingPackets_[ws].PopFront();
     }
     }
 }
 }
 
 
+int WSHandler::GetNumOutgoingPackets(const WSConnection& ws)
+{
+    if (outgoingPackets_.Contains(ws)) {
+        return outgoingPackets_[ws].Size();
+    }
+
+    return 0;
+}
+
+List<WSPacket>* WSHandler::GetOutgoingPackets(const WSConnection& ws)
+{
+    if (outgoingPackets_.Contains(ws)) {
+        return &outgoingPackets_[ws];
+    }
+
+    return nullptr;
+}
+
 void WSHandler::OutputWSLog(int level, const char *line)
 void WSHandler::OutputWSLog(int level, const char *line)
 {
 {
     switch(level) {
     switch(level) {

+ 35 - 14
Source/Urho3D/Network/WS/WSHandler.h

@@ -1,41 +1,62 @@
+//
+// Copyright (c) 2008-2020 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
 #pragma once
 #pragma once
 
 
-#include "../../IO/VectorBuffer.h"
-#include "../../Core/Mutex.h"
 #include "../../Container/List.h"
 #include "../../Container/List.h"
+#include "../../Container/HashMap.h"
+#include "../../Core/Mutex.h"
+#include "../../IO/VectorBuffer.h"
+
+#include "WSConnection.h"
 #include "WSPacket.h"
 #include "WSPacket.h"
 
 
 namespace Urho3D {
 namespace Urho3D {
-    class Network;
-
     class WSHandler {
     class WSHandler {
     public:
     public:
-        WSHandler(Urho3D::Network *networkInstance);
+        WSHandler();
         ~WSHandler();
         ~WSHandler();
 
 
-        Network* GetNetworkInstance() { return networkInstance_; }
-
         Mutex& GetIncomingMutex() { return incomingMutex_; }
         Mutex& GetIncomingMutex() { return incomingMutex_; }
         void AddIncomingPacket(const WSPacket& packet);
         void AddIncomingPacket(const WSPacket& packet);
-        WSPacket& GetIncomingPacket();
+        WSPacket* GetIncomingPacket();
         void RemoveIncomingPacket();
         void RemoveIncomingPacket();
         int GetNumIncomingPackets() { return incomingPackets_.Size(); }
         int GetNumIncomingPackets() { return incomingPackets_.Size(); }
 
 
         Mutex& GetOutgoingMutex() { return outgoingMutex_; }
         Mutex& GetOutgoingMutex() { return outgoingMutex_; }
         void AddOutgoingPacket(const WSPacket& packet);
         void AddOutgoingPacket(const WSPacket& packet);
-        WSPacket& GetOutgoingPacket();
-        void RemoveOutgoingPacket();
-        int GetNumOutgoingPackets() { return outgoingPackets_.Size(); }
-        List<WSPacket>* GetOutgoingPackets() { return &outgoingPackets_; };
+        WSPacket* GetOutgoingPacket(const WSConnection& ws);
+        void RemoveOutgoingPacket(const WSConnection& ws);
+        int GetNumOutgoingPackets(const WSConnection& ws);
+        List<WSPacket>* GetOutgoingPackets(const WSConnection& ws);
+        HashMap<WSConnection, List<WSPacket>>* GetAllOutgoingPackets() { return &outgoingPackets_; };
 
 
         static void OutputWSLog(int level, const char *line);
         static void OutputWSLog(int level, const char *line);
 
 
     protected:
     protected:
-        Network *networkInstance_;
 
 
         Mutex incomingMutex_;
         Mutex incomingMutex_;
         Mutex outgoingMutex_;
         Mutex outgoingMutex_;
         List<WSPacket> incomingPackets_;
         List<WSPacket> incomingPackets_;
-        List<WSPacket> outgoingPackets_;
+        HashMap<WSConnection, List<WSPacket>> outgoingPackets_;
     };
     };
 }
 }

+ 0 - 3
Source/Urho3D/Network/WS/WSPacket.cpp

@@ -1,3 +0,0 @@
-#ifndef __ANDROID__
-#include "WSPacket.h"
-#endif

+ 27 - 3
Source/Urho3D/Network/WS/WSPacket.h

@@ -1,8 +1,32 @@
-#ifndef __ANDROID__
+//
+// Copyright (c) 2008-2020 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
 #pragma once
 #pragma once
 
 
 #include "../../IO/VectorBuffer.h"
 #include "../../IO/VectorBuffer.h"
+#include "WSConnection.h"
 
 
-typedef Urho3D::Pair<struct lws*, Urho3D::VectorBuffer> WSPacket;
+/// Icoming/Outgoing websocket packets and their websocket connections interfaces
+typedef Urho3D::Pair<Urho3D::WSConnection, Urho3D::VectorBuffer> WSPacket;
 
 
-#endif
+/// First byte that SLikeNet library uses to mark custom user messages. Used to filter out non engine generated packets.
+const int URHO3D_MESSAGE = 134;

+ 130 - 60
Source/Urho3D/Network/WS/WSServer.cpp

@@ -1,40 +1,40 @@
-#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__)
-#include "WSServer.h"
-#include "WSHandler.h"
+//
+// Copyright (c) 2008-2020 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
 #include "../Network.h"
 #include "../Network.h"
+#include "../../Core/WorkQueue.h"
+#include "../../IO/Log.h"
 #include "../../IO/MemoryBuffer.h"
 #include "../../IO/MemoryBuffer.h"
+#include "WSHandler.h"
+#include "WSServer.h"
 
 
-//#define LWS_WITH_NETWORK TRUE
-//#define LWS_ROLE_H1 TRUE
-//#define LWS_ROLE_WS TRUE
-//#define LWS_WITH_SERVER TRUE
-
-#include "../../IO/Log.h"
 #include <libwebsockets.h>
 #include <libwebsockets.h>
-#include <string.h>
 #include <signal.h>
 #include <signal.h>
+#include <string.h>
 
 
-#define LWS_PLUGIN_STATIC
-
-#if !defined (LWS_PLUGIN_STATIC)
-#define LWS_DLL
-#define LWS_INTERNAL
-#include <libwebsockets.h>
-#endif
-
-const int ID_USER_PACKET_ENUM = 134;
 static Urho3D::WSServer* WSServerInstance = nullptr;
 static Urho3D::WSServer* WSServerInstance = nullptr;
 static struct lws_context *context;
 static struct lws_context *context;
 
 
-static int ws_server_callback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len);
-
-static struct lws_protocols protocols[] = {
-        { "ws-server", ws_server_callback, 0, 0 },
-        { NULL, NULL, 0, 0 } /* terminator */
-};
-
-static int ws_server_callback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
-{
+static int WSCallback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) {
     URHO3D_LOGINFOF("Incoming server messsage reason %d", reason);
     URHO3D_LOGINFOF("Incoming server messsage reason %d", reason);
 
 
     switch (reason) {
     switch (reason) {
@@ -43,28 +43,20 @@ static int ws_server_callback(struct lws *wsi, enum lws_callback_reasons reason,
 
 
         case LWS_CALLBACK_ESTABLISHED:
         case LWS_CALLBACK_ESTABLISHED:
             if (WSServerInstance) {
             if (WSServerInstance) {
-                WSServerInstance->GetNetworkInstance()->NewConnectionEstablished(wsi);
+                WSServerInstance->AddPendingConnection(wsi);
             }
             }
             break;
             break;
 
 
-        case LWS_CALLBACK_CLOSED:
-            // TODO: Handle client disconnected
-            break;
-
-
         case LWS_CALLBACK_SERVER_WRITEABLE:
         case LWS_CALLBACK_SERVER_WRITEABLE:
         case LWS_CALLBACK_CLIENT_WRITEABLE:
         case LWS_CALLBACK_CLIENT_WRITEABLE:
             if (WSServerInstance) {
             if (WSServerInstance) {
-//                Urho3D::MutexLock lock(WSServerInstance->GetOutgoingMutex());
-                auto* packets = WSServerInstance->GetOutgoingPackets();
+                Urho3D::MutexLock lock(WSServerInstance->GetOutgoingMutex());
+                auto* packets = WSServerInstance->GetOutgoingPackets(wsi);
                 for (auto it = packets->Begin(); it != packets->End(); ++it) {
                 for (auto it = packets->Begin(); it != packets->End(); ++it) {
                     WSPacket& packet = (*it);
                     WSPacket& packet = (*it);
-                    if (wsi != packet.first_) {
-                        continue;
-                    }
                     unsigned char buf[LWS_PRE + packet.second_.GetSize()];
                     unsigned char buf[LWS_PRE + packet.second_.GetSize()];
                     memcpy(&buf[LWS_PRE], packet.second_.GetData(), packet.second_.GetSize());
                     memcpy(&buf[LWS_PRE], packet.second_.GetData(), packet.second_.GetSize());
-                    int retval = lws_write(packet.first_, &buf[LWS_PRE], packet.second_.GetSize(), LWS_WRITE_BINARY);
+                    int retval = lws_write(packet.first_.GetWS(), &buf[LWS_PRE], packet.second_.GetSize(), LWS_WRITE_BINARY);
                     if (retval < packet.second_.GetSize()) {
                     if (retval < packet.second_.GetSize()) {
                         URHO3D_LOGERRORF("Failed to write to WS, ret = %d", retval);
                         URHO3D_LOGERRORF("Failed to write to WS, ret = %d", retval);
                         break;
                         break;
@@ -74,12 +66,11 @@ static int ws_server_callback(struct lws *wsi, enum lws_callback_reasons reason,
                     break;
                     break;
                 }
                 }
             }
             }
-            lws_callback_on_writable(wsi);
             break;
             break;
 
 
         case LWS_CALLBACK_RECEIVE: {
         case LWS_CALLBACK_RECEIVE: {
             Urho3D::VectorBuffer b((unsigned char*)in, len);
             Urho3D::VectorBuffer b((unsigned char*)in, len);
-            if (b.GetData()[0] == ID_USER_PACKET_ENUM) {
+            if (b.GetData()[0] == URHO3D_MESSAGE) {
                 WSPacket packet(wsi, b);
                 WSPacket packet(wsi, b);
                 if (WSServerInstance) {
                 if (WSServerInstance) {
                     WSServerInstance->AddIncomingPacket(packet);
                     WSServerInstance->AddIncomingPacket(packet);
@@ -87,7 +78,14 @@ static int ws_server_callback(struct lws *wsi, enum lws_callback_reasons reason,
             } else {
             } else {
                 URHO3D_LOGINFOF("Received message that is not part of the engine %d", b.GetData()[0]);
                 URHO3D_LOGINFOF("Received message that is not part of the engine %d", b.GetData()[0]);
             }
             }
-            lws_callback_on_writable(wsi);
+            break;
+        }
+
+        case LWS_CALLBACK_CLOSED: {
+            if (WSServerInstance) {
+                WSServerInstance->AddClosedConnection(wsi);
+            }
+            URHO3D_LOGINFOF("LWS_CALLBACK_CLOSED");
             break;
             break;
         }
         }
         default:
         default:
@@ -97,6 +95,12 @@ static int ws_server_callback(struct lws *wsi, enum lws_callback_reasons reason,
     return 0;
     return 0;
 }
 }
 
 
+static struct lws_protocols protocols[] = {
+        { "ws-server", WSCallback, 0, 0 },
+        { NULL, NULL, 0, 0 } /* terminator */
+};
+
+
 static int interrupted;
 static int interrupted;
 
 
 void sigint_handler(int sig)
 void sigint_handler(int sig)
@@ -106,8 +110,25 @@ void sigint_handler(int sig)
 
 
 using namespace Urho3D;
 using namespace Urho3D;
 
 
-WSServer::WSServer(Urho3D::Network* networkInstance):
-    WSHandler(networkInstance)
+static void RunService(const WorkItem* item, unsigned threadIndex) {
+    auto packets = WSServerInstance->GetAllOutgoingPackets();
+    for (auto it = packets->Begin(); it != packets->End(); ++it) {
+        if (!(*it).second_.Empty()) {
+            auto ws = (*it).second_.Front().first_;
+            URHO3D_LOGINFOF("Outgoing packet count (server) %d", WSServerInstance->GetNumOutgoingPackets(ws));
+            lws_callback_on_writable(ws.GetWS());
+        }
+    }
+
+    int result = lws_service(context, 0);
+    if (result < 0 && WSServerInstance) {
+        WSServerInstance->StopServer();
+    }
+    URHO3D_LOGINFOF("Running server service");
+}
+
+WSServer::WSServer(Context* context):
+    Object(context)
 {
 {
     WSServerInstance = this;
     WSServerInstance = this;
 }
 }
@@ -164,6 +185,8 @@ int WSServer::StartServer()
 
 
     lwsl_err("Server started");
     lwsl_err("Server started");
 
 
+    SubscribeToEvent(E_WORKITEMCOMPLETED, URHO3D_HANDLER(WSServer, HandleWorkItemFinished));
+
     return 0;
     return 0;
 //	struct lws_context_creation_info info;
 //	struct lws_context_creation_info info;
 //	memset(&info, 0, sizeof(info));
 //	memset(&info, 0, sizeof(info));
@@ -189,27 +212,74 @@ int WSServer::StartServer()
 
 
 void WSServer::Update(float timestep)
 void WSServer::Update(float timestep)
 {
 {
+    if (currentState_ == WSS_RUNNING && nextState_ == WSS_STOPPED) {
+        GetSubsystem<Network>()->StopServer();
+    }
+    while(!closedConnections.Empty()) {
+        auto ws = closedConnections.Front().GetWS();
+        GetSubsystem<Network>()->ClientDisconnected(closedConnections.Front());
+        auto packets = GetAllOutgoingPackets();
+        packets->Erase(ws);
+        closedConnections.PopFront();
+    }
+    while(!pendingConnections_.Empty()) {
+        auto ws = pendingConnections_.Front();
+        GetSubsystem<Network>()->NewConnectionEstablished(ws);
+        pendingConnections_.PopFront();
+    }
     if (context) {
     if (context) {
-        int result = lws_service(context, 0);
-        if (result < 0) {
-            StopServer();
-        }
-        if (GetNumOutgoingPackets()) {
-            URHO3D_LOGINFOF("Outgoing packet count (server) %d", GetNumOutgoingPackets());
-            lws_callback_on_writable(GetOutgoingPacket().first_);
-        }
-        while (GetNumIncomingPackets()) {
-            WSPacket& packet = GetIncomingPacket();
-            networkInstance_->HandleIncomingPacket(packet.first_, packet.second_, true);
-            RemoveIncomingPacket();
+        if (!serviceWorkItem_) {
+            WorkQueue *workQueue = GetSubsystem<WorkQueue>();
+            serviceWorkItem_ = workQueue->GetFreeItem();
+            serviceWorkItem_->priority_ = M_MAX_INT;
+            serviceWorkItem_->workFunction_ = RunService;
+            serviceWorkItem_->aux_ = this;
+            serviceWorkItem_->sendEvent_ = true;
+            workQueue->AddWorkItem(serviceWorkItem_);
         }
         }
     }
     }
+    while (GetNumIncomingPackets()) {
+        auto packet = GetIncomingPacket();
+        GetSubsystem<Network>()->HandleIncomingPacket(packet, true);
+        RemoveIncomingPacket();
+    }
+    if (currentState_ != nextState_) {
+        currentState_ = nextState_;
+    }
 }
 }
 
 
 void WSServer::StopServer()
 void WSServer::StopServer()
 {
 {
-    lws_context_destroy(context);
-    context = nullptr;
+    SetState(WSS_STOPPED);
+    if (context) {
+        lws_context_destroy(context);
+        context = nullptr;
+    }
 }
 }
 
 
-#endif
+void WSServer::HandleWorkItemFinished(StringHash eventType, VariantMap& eventData)
+{
+    using namespace WorkItemCompleted;
+    WorkItem *workItem = reinterpret_cast<WorkItem *>(eventData[P_ITEM].GetPtr());
+    if (workItem->aux_ != this) {
+        return;
+    }
+    if (workItem->workFunction_ == RunService) {
+        serviceWorkItem_.Reset();
+    }
+}
+
+void WSServer::AddPendingConnection(lws* ws)
+{
+    pendingConnections_.Push(ws);
+}
+
+void WSServer::AddClosedConnection(lws* ws)
+{
+    closedConnections.Push(ws);
+}
+
+void WSServer::SetState(WSServerState state)
+{
+    nextState_ = state;
+}

+ 48 - 6
Source/Urho3D/Network/WS/WSServer.h

@@ -1,22 +1,64 @@
-#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__)
+//
+// Copyright (c) 2008-2020 the Urho3D project.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
 #pragma once
 #pragma once
-#include "WSPacket.h"
-#include "../../Core/Mutex.h"
+
 #include "../../Container/List.h"
 #include "../../Container/List.h"
+#include "../../Core/Mutex.h"
+#include "../../Core/Object.h"
+#include "WSConnection.h"
 #include "WSHandler.h"
 #include "WSHandler.h"
 
 
 namespace Urho3D {
 namespace Urho3D {
+
 class Network;
 class Network;
+class WorkItem;
 
 
-class WSServer: public WSHandler {
+enum WSServerState {
+    WSS_STOPPED,
+    WSS_RUNNING
+};
 
 
+class WSServer: public WSHandler, public Object {
+    URHO3D_OBJECT(WSServer, Object);
 public:
 public:
-    WSServer(Urho3D::Network *networkInstance);
+    WSServer(Context* context);
     ~WSServer();
     ~WSServer();
 
 
     int StartServer();
     int StartServer();
     void StopServer();
     void StopServer();
     void Update(float timestep);
     void Update(float timestep);
+    void AddPendingConnection(lws* ws);
+    void AddClosedConnection(lws* ws);
+    void SetState(WSServerState state);
+
+private:
+    void HandleWorkItemFinished(StringHash eventType, VariantMap& eventData);
+
+    SharedPtr<WorkItem> serviceWorkItem_;
+    List<WSConnection> pendingConnections_;
+    List<WSConnection> closedConnections;
+
+    WSServerState currentState_;
+    WSServerState nextState_;
 };
 };
 }
 }
-#endif