├── README.md ├── Source └── HTML5Networking │ ├── Classes │ ├── WebSocketConnection.h │ └── WebSocketNetDriver.h │ ├── HTML5Networking.Build.cs │ └── Private │ ├── HTML5NetworkingPCH.h │ ├── HTML5NetworkingPlugin.cpp │ ├── WebSocket.cpp │ ├── WebSocket.h │ ├── WebSocketNetDriver.cpp │ ├── WebSocketServer.cpp │ ├── WebSocketServer.h │ └── WebsocketConnection.cpp └── html5networking.uplugin /README.md: -------------------------------------------------------------------------------- 1 | ### Unreal Engine 4 Networking over Websockets Plugin 2 | 3 | - Provides websocket transport layer for unreal engine 4. 4 | - Uses [libwebsockets](http://libwebsocket.org) for the server side and client side for non HTML5 clients. 5 | - HTML5 clients use emscripten's sockets abstraction. 6 | 7 | #### How to 8 | 9 | - Clone this in Engine\Plugins\Experimental directory. 10 | - Add the following section in `BaseEngine.ini` 11 | ``` 12 | [/Script/HTML5Networking.WebSocketNetDriver] 13 | AllowPeerConnections=False 14 | AllowPeerVoice=False 15 | ConnectionTimeout=6000.0 16 | InitialConnectTimeout=6000.0 17 | AckTimeout=10.0 18 | KeepAliveTime=20.2 19 | MaxClientRate=15000 20 | MaxInternetClientRate=10000 21 | RelevantTimeout=5.0 22 | SpawnPrioritySeconds=1.0 23 | ServerTravelPause=4.0 24 | NetServerMaxTickRate=30 25 | LanServerMaxTickRate=35 26 | WebSocketPort=8889 27 | NetConnectionClassName="/Script/HTML5Networking.WebSocketConnection" 28 | MaxPortCountToTry=512 29 | ``` 30 | In section [/Script/Engine.Engine] disable comment out NetDriverDefinitions and add 31 | ``` 32 | NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="/Script/HTML5Networking.WebSocketNetDriver",DriverClassNameFallback="/Script/HTML5Networking.IpNetDriver") 33 | ``` 34 | To enable this Net Driver. 35 | 36 | Build! and follow existing Unreal Networking documentation to setup servers/clients. 37 | 38 | #### Issues/Todo 39 | 40 | Disconnect events on client or server side are not handled properly yet 41 | 42 | Copyright 2015 Epic Games. 43 | -------------------------------------------------------------------------------- /Source/HTML5Networking/Classes/WebSocketConnection.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "WebSocketConnection.generated.h" 6 | 7 | 8 | UCLASS(transient, config = Engine) 9 | class HTML5NETWORKING_API UWebSocketConnection : public UNetConnection 10 | { 11 | GENERATED_UCLASS_BODY() 12 | 13 | class FWebSocket* WebSocket; 14 | 15 | // Begin NetConnection Interface 16 | virtual void InitBase(UNetDriver* InDriver, class FSocket* InSocket, const FURL& InURL, EConnectionState InState, int32 InMaxPacket = 0, int32 InPacketOverhead = 0) override; 17 | virtual void InitRemoteConnection(UNetDriver* InDriver, class FSocket* InSocket, const FURL& InURL, const class FInternetAddr& InRemoteAddr, EConnectionState InState, int32 InMaxPacket = 0, int32 InPacketOverhead = 0) override; 18 | virtual void InitLocalConnection(UNetDriver* InDriver, class FSocket* InSocket, const FURL& InURL, EConnectionState InState, int32 InMaxPacket = 0, int32 InPacketOverhead = 0) override; 19 | virtual void LowLevelSend(void* Data, int32 Count) override; 20 | FString LowLevelGetRemoteAddress(bool bAppendPort = false) override; 21 | FString LowLevelDescribe() override; 22 | virtual void Tick(); 23 | virtual void FinishDestroy(); 24 | // End NetConnection Interface 25 | 26 | 27 | void SetWebSocket(FWebSocket* InWebSocket); 28 | FWebSocket* GetWebSocket(); 29 | }; 30 | -------------------------------------------------------------------------------- /Source/HTML5Networking/Classes/WebSocketNetDriver.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. 2 | // 3 | // websocket based implementation of the net driver 4 | // 5 | 6 | #pragma once 7 | #include "WebSocketNetDriver.generated.h" 8 | 9 | UCLASS(transient, config = Engine) 10 | class HTML5NETWORKING_API UWebSocketNetDriver : public UNetDriver 11 | { 12 | GENERATED_UCLASS_BODY() 13 | 14 | /** Websocket server port*/ 15 | UPROPERTY(Config) 16 | int32 WebSocketPort; 17 | 18 | // Begin UNetDriver interface. 19 | virtual bool IsAvailable() const override; 20 | virtual bool InitBase(bool bInitAsClient, FNetworkNotify* InNotify, const FURL& URL, bool bReuseAddressAndPort, FString& Error) override; 21 | virtual bool InitConnect(FNetworkNotify* InNotify, const FURL& ConnectURL, FString& Error) override; 22 | virtual bool InitListen(FNetworkNotify* InNotify, FURL& LocalURL, bool bReuseAddressAndPort, FString& Error) override; 23 | virtual void ProcessRemoteFunction(class AActor* Actor, class UFunction* Function, void* Parameters, struct FOutParmRec* OutParms, struct FFrame* Stack, class UObject* SubObject = NULL) override; 24 | virtual void TickDispatch(float DeltaTime) override; 25 | virtual FString LowLevelGetNetworkNumber() override; 26 | virtual void LowLevelDestroy() override; 27 | virtual bool IsNetResourceValid(void) override; 28 | 29 | // stub implementation because for websockets we don't use any underlying socket sub system. 30 | virtual class ISocketSubsystem* GetSocketSubsystem() override; 31 | virtual FSocket * CreateSocket(); 32 | 33 | // End UNetDriver interface. 34 | 35 | // Begin FExec Interface 36 | virtual bool Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar = *GLog) override; 37 | // End FExec Interface 38 | 39 | /** 40 | * Exec command handlers 41 | */ 42 | bool HandleSocketsCommand(const TCHAR* Cmd, FOutputDevice& Ar, UWorld* InWorld); 43 | 44 | /** @return connection to server */ 45 | class UWebSocketConnection* GetServerConnection(); 46 | 47 | /************************************************************************/ 48 | /* FWebSocketServer */ 49 | /************************************************************************/ 50 | 51 | FWebSocketServer* WebSocketServer; 52 | 53 | /******************************************************************************/ 54 | /* Callback Function for New Connection from a client is accepted by this server */ 55 | /******************************************************************************/ 56 | 57 | void OnWebSocketClientConnected(FWebSocket*); // to the server. 58 | 59 | 60 | /************************************************************************/ 61 | /* Callback Function for when this client Connects to the server */ 62 | /************************************************************************/ 63 | 64 | void OnWebSocketServerConnected(); // to the client. 65 | 66 | 67 | }; 68 | -------------------------------------------------------------------------------- /Source/HTML5Networking/HTML5Networking.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. 2 | 3 | namespace UnrealBuildTool.Rules 4 | { 5 | public class HTML5Networking : ModuleRules 6 | { 7 | public HTML5Networking(TargetInfo Target) 8 | { 9 | PrivateDependencyModuleNames.AddRange( 10 | new string[] { 11 | "Core", 12 | "CoreUObject", 13 | "Engine", 14 | "ImageCore", 15 | "Sockets", 16 | "OnlineSubSystemUtils", 17 | "WebSockets" 18 | } 19 | ); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/HTML5Networking/Private/HTML5NetworkingPCH.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. 2 | #pragma once 3 | 4 | #include "Core.h" 5 | #include "Engine.h" 6 | #include "Sockets.h" 7 | #include "SocketSubsystem.h" 8 | #include "ModuleManager.h" 9 | 10 | class FWebSocket; 11 | class FWebSocketServer; 12 | 13 | typedef struct libwebsocket_context WebSocketInternalContext; 14 | typedef struct libwebsocket WebSocketInternal; 15 | typedef struct libwebsocket_protocols WebSocketInternalProtocol; 16 | 17 | DECLARE_DELEGATE_TwoParams(FWebsocketPacketRecievedCallBack, void* /*Data*/, int32 /*Data Size*/); 18 | DECLARE_DELEGATE_OneParam(FWebsocketClientConnectedCallBack, FWebSocket* /*Socket*/); 19 | DECLARE_DELEGATE(FWebsocketInfoCallBack); 20 | 21 | DECLARE_LOG_CATEGORY_EXTERN(LogHTML5Networking, Warning, All); -------------------------------------------------------------------------------- /Source/HTML5Networking/Private/HTML5NetworkingPlugin.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "HTML5NetworkingPCH.h" 4 | 5 | 6 | class FHTML5NetworkingPlugin : public IModuleInterface 7 | { 8 | /** IModuleInterface implementation */ 9 | virtual void StartupModule() override; 10 | virtual void ShutdownModule() override; 11 | }; 12 | 13 | IMPLEMENT_MODULE(FHTML5NetworkingPlugin, HTML5Networking) 14 | 15 | void FHTML5NetworkingPlugin::StartupModule() 16 | { 17 | } 18 | 19 | 20 | void FHTML5NetworkingPlugin::ShutdownModule() 21 | { 22 | } 23 | 24 | DEFINE_LOG_CATEGORY(LogHTML5Networking); 25 | 26 | 27 | -------------------------------------------------------------------------------- /Source/HTML5Networking/Private/WebSocket.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. 2 | #include "HTML5NetworkingPCH.h" 3 | #include "WebSocket.h" 4 | 5 | #if PLATFORM_HTML5 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #endif 20 | 21 | #if PLATFORM_WINDOWS 22 | #include "AllowWindowsPlatformTypes.h" 23 | #endif 24 | 25 | 26 | #if !PLATFORM_HTML5 27 | #include "libwebsockets.h" 28 | #include "private-libwebsockets.h" 29 | #endif 30 | 31 | #if PLATFORM_WINDOWS 32 | #include "HideWindowsPlatformTypes.h" 33 | #endif 34 | 35 | 36 | #if !PLATFORM_HTML5 37 | uint8 PREPADDING[LWS_SEND_BUFFER_PRE_PADDING]; 38 | uint8 POSTPADDING[LWS_SEND_BUFFER_POST_PADDING]; 39 | #endif 40 | 41 | 42 | #if !PLATFORM_HTML5 43 | static void libwebsocket_debugLogS(int level, const char *line) 44 | { 45 | UE_LOG(LogHTML5Networking, Log, TEXT("client: %s"), ANSI_TO_TCHAR(line)); 46 | } 47 | #endif 48 | 49 | FWebSocket::FWebSocket( 50 | const FInternetAddr& ServerAddress 51 | ) 52 | :IsServerSide(false) 53 | { 54 | 55 | #if !PLATFORM_HTML5_BROWSER 56 | 57 | #if !UE_BUILD_SHIPPING 58 | lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_DEBUG | LLL_INFO, libwebsocket_debugLogS); 59 | #endif 60 | 61 | Protocols = new libwebsocket_protocols[3]; 62 | FMemory::Memzero(Protocols, sizeof(libwebsocket_protocols) * 3); 63 | 64 | Protocols[0].name = "binary"; 65 | Protocols[0].callback = FWebSocket::unreal_networking_client; 66 | Protocols[0].per_session_data_size = 0; 67 | Protocols[0].rx_buffer_size = 10 * 1024 * 1024; 68 | 69 | Protocols[1].name = nullptr; 70 | Protocols[1].callback = nullptr; 71 | Protocols[1].per_session_data_size = 0; 72 | 73 | struct lws_context_creation_info Info; 74 | memset(&Info, 0, sizeof Info); 75 | 76 | Info.port = CONTEXT_PORT_NO_LISTEN; 77 | Info.protocols = &Protocols[0]; 78 | Info.gid = -1; 79 | Info.uid = -1; 80 | Info.user = this; 81 | 82 | Context = libwebsocket_create_context(&Info); 83 | 84 | check(Context); 85 | 86 | 87 | Wsi = libwebsocket_client_connect_extended 88 | (Context, 89 | TCHAR_TO_ANSI(*ServerAddress.ToString(false)), 90 | ServerAddress.GetPort(), 91 | false, "/", TCHAR_TO_ANSI(*ServerAddress.ToString(false)), TCHAR_TO_ANSI(*ServerAddress.ToString(false)), Protocols[1].name, -1,this); 92 | 93 | check(Wsi); 94 | 95 | #endif 96 | 97 | #if PLATFORM_HTML5_BROWSER 98 | 99 | struct sockaddr_in addr; 100 | int res; 101 | 102 | SockFd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 103 | if (SockFd == -1) { 104 | UE_LOG(LogHTML5Networking, Error, TEXT("Socket creationg failed ")); 105 | } 106 | else 107 | { 108 | UE_LOG(LogHTML5Networking, Warning, TEXT(" Socked %d created "), SockFd); 109 | } 110 | 111 | fcntl(SockFd, F_SETFL, O_NONBLOCK); 112 | 113 | memset(&addr, 0, sizeof(addr)); 114 | addr.sin_family = AF_INET; 115 | addr.sin_port = htons(ServerAddress.GetPort()); 116 | 117 | if (inet_pton(AF_INET, TCHAR_TO_ANSI(*ServerAddress.ToString(false)), &addr.sin_addr) != 1) { 118 | UE_LOG(LogHTML5Networking, Warning, TEXT("inet_pton failed ")); 119 | return; 120 | } 121 | 122 | int Ret = connect(SockFd, (struct sockaddr *)&addr, sizeof(addr)); 123 | UE_LOG(LogHTML5Networking, Warning, TEXT(" Connect socket returned %d"), Ret); 124 | 125 | #endif 126 | } 127 | 128 | FWebSocket::FWebSocket(WebSocketInternalContext* InContext, WebSocketInternal* InWsi) 129 | : Context(InContext), 130 | Wsi(InWsi), 131 | IsServerSide(true) 132 | { 133 | } 134 | 135 | 136 | bool FWebSocket::Send(uint8* Data, uint32 Size) 137 | { 138 | TArray Buffer; 139 | // insert size. 140 | 141 | #if !PLATFORM_HTML5 142 | Buffer.Append((uint8*)&PREPADDING, sizeof(PREPADDING)); 143 | #endif 144 | 145 | Buffer.Append((uint8*)&Size, sizeof (uint32)); 146 | Buffer.Append((uint8*)Data, Size); 147 | 148 | #if !PLATFORM_HTML5 149 | Buffer.Append((uint8*)&POSTPADDING, sizeof(POSTPADDING)); 150 | #endif 151 | 152 | OutgoingBuffer.Add(Buffer); 153 | 154 | return true; 155 | } 156 | 157 | void FWebSocket::SetRecieveCallBack(FWebsocketPacketRecievedCallBack CallBack) 158 | { 159 | RecievedCallBack = CallBack; 160 | } 161 | 162 | FString FWebSocket::RemoteEndPoint() 163 | { 164 | #if !PLATFORM_HTML5 165 | ANSICHAR Peer_Name[128]; 166 | ANSICHAR Peer_Ip[128]; 167 | libwebsockets_get_peer_addresses(Context, Wsi, libwebsocket_get_socket_fd(Wsi), Peer_Name, sizeof Peer_Name, Peer_Ip, sizeof Peer_Ip); 168 | return FString(Peer_Name); 169 | #endif 170 | 171 | #if PLATFORM_HTML5 172 | return FString(TEXT("TODO:REMOTEENDPOINT")); 173 | #endif 174 | } 175 | 176 | 177 | FString FWebSocket::LocalEndPoint() 178 | { 179 | #if !PLATFORM_HTML5 180 | return FString(ANSI_TO_TCHAR(libwebsocket_canonical_hostname(Context))); 181 | #endif 182 | 183 | #if PLATFORM_HTML5 184 | return FString(TEXT("TODO:LOCALENDPOINT")); 185 | #endif 186 | 187 | } 188 | 189 | void FWebSocket::Tick() 190 | { 191 | 192 | #if !PLATFORM_HTML5 193 | { 194 | libwebsocket_service(Context, 0); 195 | if (!IsServerSide) 196 | libwebsocket_callback_on_writable_all_protocol(&Protocols[0]); 197 | } 198 | #endif 199 | 200 | #if PLATFORM_HTML5 201 | 202 | fd_set Fdr; 203 | fd_set Fdw; 204 | int Res; 205 | 206 | // make sure that server.fd is ready to read / write 207 | FD_ZERO(&Fdr); 208 | FD_ZERO(&Fdw); 209 | FD_SET(SockFd, &Fdr); 210 | FD_SET(SockFd, &Fdw); 211 | Res = select(64, &Fdr, &Fdw, NULL, NULL); 212 | 213 | if (Res == -1) { 214 | UE_LOG(LogHTML5Networking, Warning, TEXT("Select Failed!")); 215 | return; 216 | } 217 | 218 | if (FD_ISSET(SockFd, &Fdr)) { 219 | // we can read! 220 | OnRawRecieve(NULL, NULL); 221 | } 222 | 223 | if (FD_ISSET(SockFd, &Fdw)) { 224 | // we can write 225 | OnRawWebSocketWritable(NULL); 226 | } 227 | #endif 228 | } 229 | 230 | void FWebSocket::SetConnectedCallBack(FWebsocketInfoCallBack CallBack) 231 | { 232 | ConnectedCallBack = CallBack; 233 | } 234 | 235 | void FWebSocket::SetErrorCallBack(FWebsocketInfoCallBack CallBack) 236 | { 237 | ErrorCallBack = CallBack; 238 | } 239 | 240 | void FWebSocket::OnRawRecieve(void* Data, uint32 Size) 241 | { 242 | #if !PLATFORM_HTML5 243 | RecievedBuffer.Append((uint8*)Data, Size); 244 | 245 | while (RecievedBuffer.Num()) 246 | { 247 | uint32 BytesToBeRead = *(uint32*)RecievedBuffer.GetData(); 248 | if (BytesToBeRead <= ((uint32)RecievedBuffer.Num() - sizeof(uint32))) 249 | { 250 | RecievedCallBack.Execute((void*)((uint8*)RecievedBuffer.GetData() + sizeof(uint32)), BytesToBeRead); 251 | RecievedBuffer.RemoveAt(0, sizeof(uint32) + BytesToBeRead ); 252 | } 253 | else 254 | { 255 | break; 256 | } 257 | } 258 | #endif 259 | 260 | #if PLATFORM_HTML5 261 | 262 | uint8 Buffer[1024]; // should be at MAX PACKET SIZE. 263 | int Result = recv(SockFd, Buffer, sizeof(uint32), 0); 264 | 265 | uint32 DataToBeRead = 0;*(uint32*)Buffer; 266 | 267 | if (Result == -1) 268 | { 269 | UE_LOG(LogHTML5Networking, Log, TEXT("Read message size failed!")); 270 | this->ErrorCallBack.ExecuteIfBound(); 271 | } 272 | else 273 | { 274 | DataToBeRead = *(uint32*)Buffer; 275 | UE_LOG(LogHTML5Networking, Log, TEXT("Read 4 bytes showing the size"), DataToBeRead); 276 | } 277 | 278 | check(Result == sizeof(uint32)); 279 | 280 | // read rest of the data. 281 | Result = recv(SockFd, Buffer, DataToBeRead, 0); 282 | 283 | if(Result < 0 ) 284 | { 285 | UE_LOG(LogHTML5Networking, Log, TEXT("Read message failed!")); 286 | this->ErrorCallBack.ExecuteIfBound(); 287 | } 288 | else 289 | { 290 | UE_LOG(LogHTML5Networking, Log, TEXT("Read %d bytes and Executing."), DataToBeRead); 291 | check(DataToBeRead == Result); 292 | RecievedCallBack.Execute(Buffer, DataToBeRead); 293 | } 294 | 295 | #endif 296 | 297 | } 298 | 299 | void FWebSocket::OnRawWebSocketWritable(WebSocketInternal* wsi) 300 | { 301 | if (OutgoingBuffer.Num() == 0) 302 | return; 303 | 304 | TArray & Packet = OutgoingBuffer[0]; 305 | 306 | #if !PLATFORM_HTML5_BROWSER 307 | 308 | uint32 TotalDataSize = Packet.Num() - LWS_SEND_BUFFER_PRE_PADDING - LWS_SEND_BUFFER_POST_PADDING; 309 | 310 | int Sent = libwebsocket_write(Wsi, Packet.GetData() + LWS_SEND_BUFFER_PRE_PADDING, TotalDataSize, (libwebsocket_write_protocol)LWS_WRITE_BINARY); 311 | 312 | check(Sent == TotalDataSize); // if this fires we need a slightly more complicated buffering mechanism. 313 | check(Wsi == wsi); 314 | 315 | #endif 316 | 317 | #if PLATFORM_HTML5_BROWSER 318 | 319 | // send actual data in one go. 320 | int Result = send(SockFd, (uint32*)Packet.GetData(),Packet.Num(), 0); 321 | 322 | if (Result == -1) 323 | { 324 | // we are caught with our pants down. fail. 325 | UE_LOG(LogHTML5Networking, Error, TEXT("Could not write %d bytes"), Packet.Num()); 326 | ErrorCallBack.ExecuteIfBound(); 327 | } 328 | else 329 | { 330 | check(Result == Packet.Num()); 331 | UE_LOG(LogHTML5Networking, Log, TEXT("Wrote %d bytes"), Packet.Num()); 332 | } 333 | 334 | #endif 335 | 336 | // this is very inefficient we need a constant size circular buffer to efficiently not do unnecessary allocations/deallocations. 337 | OutgoingBuffer.RemoveAt(0); 338 | 339 | } 340 | 341 | FWebSocket::~FWebSocket() 342 | { 343 | #if !PLATFORM_HTML5 344 | if ( !IsServerSide) 345 | { 346 | libwebsocket_context_destroy(Context); 347 | Context = NULL; 348 | delete Protocols; 349 | Protocols = NULL; 350 | } 351 | #endif 352 | 353 | #if PLATFORM_HTML5 354 | close(SockFd); 355 | #endif 356 | 357 | } 358 | 359 | #if !PLATFORM_HTML5 360 | int FWebSocket::unreal_networking_client( 361 | struct libwebsocket_context *Context, 362 | struct libwebsocket *Wsi, 363 | enum libwebsocket_callback_reasons Reason, 364 | void *User, 365 | void *In, 366 | size_t Len) 367 | { 368 | FWebSocket* Socket = (FWebSocket*)libwebsocket_context_user(Context);; 369 | switch (Reason) 370 | { 371 | case LWS_CALLBACK_CLIENT_ESTABLISHED: 372 | { 373 | Socket->ConnectedCallBack.ExecuteIfBound(); 374 | libwebsocket_set_timeout(Wsi, NO_PENDING_TIMEOUT, 0); 375 | check(Socket->Wsi == Wsi); 376 | } 377 | break; 378 | case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: 379 | { 380 | Socket->ErrorCallBack.ExecuteIfBound(); 381 | return -1; 382 | } 383 | break; 384 | case LWS_CALLBACK_CLIENT_RECEIVE: 385 | { 386 | // push it on the socket. 387 | Socket->OnRawRecieve(In, (uint32)Len); 388 | check(Socket->Wsi == Wsi); 389 | libwebsocket_set_timeout(Wsi, NO_PENDING_TIMEOUT, 0); 390 | break; 391 | } 392 | case LWS_CALLBACK_CLIENT_WRITEABLE: 393 | { 394 | check(Socket->Wsi == Wsi); 395 | Socket->OnRawWebSocketWritable(Wsi); 396 | libwebsocket_callback_on_writable(Context, Wsi); 397 | libwebsocket_set_timeout(Wsi, NO_PENDING_TIMEOUT, 0); 398 | break; 399 | } 400 | case LWS_CALLBACK_CLOSED: 401 | { 402 | Socket->ErrorCallBack.ExecuteIfBound(); 403 | return -1; 404 | } 405 | } 406 | 407 | return 0; 408 | } 409 | #endif 410 | 411 | -------------------------------------------------------------------------------- /Source/HTML5Networking/Private/WebSocket.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. 2 | // 3 | // libwebsocket client wrapper. 4 | // 5 | #pragma once 6 | #include "HTML5NetworkingPCH.h" 7 | 8 | class FWebSocket 9 | { 10 | 11 | public: 12 | 13 | // Initialize as client side socket. 14 | FWebSocket(const FInternetAddr& ServerAddress); 15 | 16 | // Initialize as server side socket. 17 | FWebSocket(WebSocketInternalContext* InContext, WebSocketInternal* Wsi); 18 | 19 | // clean up. 20 | ~FWebSocket(); 21 | 22 | /************************************************************************/ 23 | /* Set various callbacks for Socket Events */ 24 | /************************************************************************/ 25 | void SetConnectedCallBack(FWebsocketInfoCallBack CallBack); 26 | void SetErrorCallBack(FWebsocketInfoCallBack CallBack); 27 | void SetRecieveCallBack(FWebsocketPacketRecievedCallBack CallBack); 28 | 29 | /** Send raw data to remote end point. */ 30 | bool Send(uint8* Data, uint32 Size); 31 | 32 | /** service libwebsocket. */ 33 | void Tick(); 34 | 35 | /** Helper functions to describe end points. */ 36 | FString RemoteEndPoint(); 37 | FString LocalEndPoint(); 38 | 39 | private: 40 | 41 | void OnRawRecieve(void* Data, uint32 Size); 42 | void OnRawWebSocketWritable(WebSocketInternal* wsi); 43 | 44 | /************************************************************************/ 45 | /* Various Socket callbacks */ 46 | /************************************************************************/ 47 | FWebsocketPacketRecievedCallBack RecievedCallBack; 48 | FWebsocketInfoCallBack ConnectedCallBack; 49 | FWebsocketInfoCallBack ErrorCallBack; 50 | 51 | /** Recv and Send Buffers, serviced during the Tick */ 52 | TArray RecievedBuffer; 53 | TArray> OutgoingBuffer; 54 | 55 | /** libwebsocket internal context*/ 56 | WebSocketInternalContext* Context; 57 | 58 | /** libwebsocket web socket */ 59 | WebSocketInternal* Wsi; 60 | 61 | /** libwebsocket Protocols that can be serviced by this implemenation*/ 62 | WebSocketInternalProtocol* Protocols; 63 | 64 | /** Server side socket or client side*/ 65 | bool IsServerSide; 66 | 67 | #if !PLATFORM_HTML5 68 | /* libwebsocket service functions */ 69 | static int unreal_networking_server(struct libwebsocket_context *, struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, void *user, void *in, size_t len); 70 | static int unreal_networking_client(struct libwebsocket_context *, struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, void *user, void *in, size_t len); 71 | #endif 72 | 73 | friend class FWebSocketServer; 74 | int SockFd; 75 | }; 76 | 77 | 78 | -------------------------------------------------------------------------------- /Source/HTML5Networking/Private/WebSocketNetDriver.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. 2 | 3 | /*============================================================================= 4 | Unreal Websocket network driver. 5 | =============================================================================*/ 6 | 7 | #include "HTML5NetworkingPCH.h" 8 | #include "Engine/Channel.h" 9 | 10 | #include "IPAddress.h" 11 | #include "Sockets.h" 12 | 13 | #include "WebSocketConnection.h" 14 | #include "WebSocketNetDriver.h" 15 | #include "WebSocketServer.h" 16 | #include "WebSocket.h" 17 | 18 | 19 | /*----------------------------------------------------------------------------- 20 | Declarations. 21 | -----------------------------------------------------------------------------*/ 22 | 23 | /** Size of the network recv buffer */ 24 | #define NETWORK_MAX_PACKET (576) 25 | 26 | UWebSocketNetDriver::UWebSocketNetDriver(const FObjectInitializer& ObjectInitializer) 27 | : Super(ObjectInitializer) 28 | { 29 | } 30 | 31 | bool UWebSocketNetDriver::IsAvailable() const 32 | { 33 | // Websocket driver always valid for now 34 | return true; 35 | } 36 | 37 | 38 | 39 | ISocketSubsystem* UWebSocketNetDriver::GetSocketSubsystem() 40 | { 41 | return ISocketSubsystem::Get(); 42 | } 43 | 44 | FSocket * UWebSocketNetDriver::CreateSocket() 45 | { 46 | return NULL; 47 | } 48 | 49 | bool UWebSocketNetDriver::InitBase(bool bInitAsClient, FNetworkNotify* InNotify, const FURL& URL, bool bReuseAddressAndPort, FString& Error) 50 | { 51 | if (!Super::InitBase(bInitAsClient, InNotify, URL, bReuseAddressAndPort, Error)) 52 | { 53 | return false; 54 | } 55 | 56 | return true; 57 | } 58 | 59 | bool UWebSocketNetDriver::InitConnect(FNetworkNotify* InNotify, const FURL& ConnectURL, FString& Error) 60 | { 61 | if (!InitBase(true, InNotify, ConnectURL, false, Error)) 62 | { 63 | return false; 64 | } 65 | 66 | // Create new connection. 67 | ServerConnection = NewObject(NetConnectionClass); 68 | 69 | TSharedRef InternetAddr = GetSocketSubsystem()->CreateInternetAddr(); 70 | bool Ok; 71 | InternetAddr->SetIp(*ConnectURL.Host, Ok); 72 | InternetAddr->SetPort(WebSocketPort); 73 | 74 | FWebSocket* WebSocket = new FWebSocket(*InternetAddr); 75 | UWebSocketConnection* Connection = (UWebSocketConnection*)ServerConnection; 76 | Connection->SetWebSocket(WebSocket); 77 | 78 | FWebsocketPacketRecievedCallBack CallBack; 79 | CallBack.BindUObject(Connection, &UWebSocketConnection::ReceivedRawPacket); 80 | WebSocket->SetRecieveCallBack(CallBack); 81 | 82 | FWebsocketInfoCallBack ConnectedCallBack; 83 | ConnectedCallBack.BindUObject(this, &UWebSocketNetDriver::OnWebSocketServerConnected); 84 | WebSocket->SetConnectedCallBack(ConnectedCallBack); 85 | 86 | ServerConnection->InitLocalConnection(this, NULL, ConnectURL, USOCK_Pending); 87 | 88 | // Create channel zero. 89 | GetServerConnection()->CreateChannel(CHTYPE_Control, 1, 0); 90 | 91 | return true; 92 | } 93 | 94 | bool UWebSocketNetDriver::InitListen(FNetworkNotify* InNotify, FURL& LocalURL, bool bReuseAddressAndPort, FString& Error) 95 | { 96 | if (!InitBase(false, InNotify, LocalURL, bReuseAddressAndPort, Error)) 97 | { 98 | return false; 99 | } 100 | 101 | WebSocketServer = new class FWebSocketServer(); 102 | 103 | FWebsocketClientConnectedCallBack CallBack; 104 | CallBack.BindUObject(this, &UWebSocketNetDriver::OnWebSocketClientConnected); 105 | 106 | if(!WebSocketServer->Init(WebSocketPort, CallBack)) 107 | return false; 108 | 109 | WebSocketServer->Tick(); 110 | LocalURL.Port = WebSocketPort; 111 | UE_LOG(LogHTML5Networking, Log, TEXT("%s WebSocketNetDriver listening on port %i"), *GetDescription(), LocalURL.Port); 112 | 113 | // server has no server connection. 114 | ServerConnection = NULL; 115 | return true; 116 | } 117 | 118 | void UWebSocketNetDriver::TickDispatch(float DeltaTime) 119 | { 120 | Super::TickDispatch(DeltaTime); 121 | 122 | if (WebSocketServer) 123 | WebSocketServer->Tick(); 124 | } 125 | 126 | void UWebSocketNetDriver::ProcessRemoteFunction(class AActor* Actor, UFunction* Function, void* Parameters, FOutParmRec* OutParms, FFrame* Stack, class UObject* SubObject) 127 | { 128 | bool bIsServer = IsServer(); 129 | 130 | UNetConnection* Connection = NULL; 131 | if (bIsServer) 132 | { 133 | if ((Function->FunctionFlags & FUNC_NetMulticast)) 134 | { 135 | // Multicast functions go to every client 136 | TArray UniqueRealConnections; 137 | for (int32 i = 0; iFunctionFlags & FUNC_NetReliable) == 0) 151 | { 152 | if (Connection->ViewTarget) 153 | { 154 | FNetViewer Viewer(Connection, 0.f); 155 | IsRelevant = Actor->IsNetRelevantFor(Viewer.InViewer, Viewer.ViewTarget, Viewer.ViewLocation); 156 | } 157 | else 158 | { 159 | // No viewer for this connection(?), just let it go through. 160 | UE_LOG(LogHTML5Networking, Log, TEXT("Multicast function %s called on actor %s when a connection has no Viewer"), *Function->GetName(), *Actor->GetName()); 161 | } 162 | } 163 | 164 | if (IsRelevant) 165 | { 166 | if (Connection->GetUChildConnection() != NULL) 167 | { 168 | Connection = ((UChildConnection*)Connection)->Parent; 169 | } 170 | 171 | InternalProcessRemoteFunction(Actor, SubObject, Connection, Function, Parameters, OutParms, Stack, bIsServer); 172 | } 173 | } 174 | } 175 | 176 | // Return here so we don't call InternalProcessRemoteFunction again at the bottom of this function 177 | return; 178 | } 179 | } 180 | 181 | // Send function data to remote. 182 | Connection = Actor->GetNetConnection(); 183 | if (Connection) 184 | { 185 | InternalProcessRemoteFunction(Actor, SubObject, Connection, Function, Parameters, OutParms, Stack, bIsServer); 186 | } 187 | } 188 | 189 | FString UWebSocketNetDriver::LowLevelGetNetworkNumber() 190 | { 191 | return WebSocketServer->Info(); 192 | } 193 | 194 | void UWebSocketNetDriver::LowLevelDestroy() 195 | { 196 | Super::LowLevelDestroy(); 197 | delete WebSocketServer; 198 | } 199 | 200 | bool UWebSocketNetDriver::HandleSocketsCommand(const TCHAR* Cmd, FOutputDevice& Ar, UWorld* InWorld) 201 | { 202 | Ar.Logf(TEXT("")); 203 | if (WebSocketServer != NULL) 204 | { 205 | Ar.Logf(TEXT("Running WebSocket Server %s"), *WebSocketServer->Info()); 206 | } 207 | else 208 | { 209 | check(GetServerConnection()); 210 | Ar.Logf(TEXT("WebSocket client's EndPoint %s"), *(GetServerConnection()->WebSocket->RemoteEndPoint())); 211 | } 212 | return UNetDriver::Exec(InWorld, TEXT("SOCKETS"), Ar); 213 | } 214 | 215 | bool UWebSocketNetDriver::Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar) 216 | { 217 | if (FParse::Command(&Cmd, TEXT("SOCKETS"))) 218 | { 219 | return HandleSocketsCommand(Cmd, Ar, InWorld); 220 | } 221 | return UNetDriver::Exec(InWorld, Cmd, Ar); 222 | } 223 | 224 | UWebSocketConnection* UWebSocketNetDriver::GetServerConnection() 225 | { 226 | return (UWebSocketConnection*)ServerConnection; 227 | } 228 | 229 | void UWebSocketNetDriver::OnWebSocketClientConnected(FWebSocket* ClientWebSocket) 230 | { 231 | // Determine if allowing for client/server connections 232 | const bool bAcceptingConnection = Notify->NotifyAcceptingConnection() == EAcceptConnection::Accept; 233 | if (bAcceptingConnection) 234 | { 235 | 236 | UWebSocketConnection* Connection = NewObject(NetConnectionClass); 237 | check(Connection); 238 | 239 | TSharedRef InternetAddr = GetSocketSubsystem()->CreateInternetAddr(); 240 | bool Ok; 241 | 242 | InternetAddr->SetIp(*(ClientWebSocket->RemoteEndPoint()),Ok); 243 | InternetAddr->SetPort(0); 244 | Connection->SetWebSocket(ClientWebSocket); 245 | Connection->InitRemoteConnection(this, NULL, FURL(), *InternetAddr, USOCK_Open); 246 | 247 | Notify->NotifyAcceptedConnection(Connection); 248 | 249 | AddClientConnection(Connection); 250 | 251 | FWebsocketPacketRecievedCallBack CallBack; 252 | CallBack.BindUObject(Connection, &UWebSocketConnection::ReceivedRawPacket); 253 | ClientWebSocket->SetRecieveCallBack(CallBack); 254 | 255 | UE_LOG(LogHTML5Networking, Log, TEXT(" Websocket server running on %s Accepted Connection from %s "), *WebSocketServer->Info(),*ClientWebSocket->RemoteEndPoint()); 256 | } 257 | } 258 | 259 | bool UWebSocketNetDriver::IsNetResourceValid(void) 260 | { 261 | if ( (WebSocketServer && !ServerConnection)// Server 262 | || (!WebSocketServer && ServerConnection) // client 263 | ) 264 | { 265 | return true; 266 | } 267 | 268 | return false; 269 | } 270 | 271 | // Just logging, not yet attached to html5 clients. 272 | void UWebSocketNetDriver::OnWebSocketServerConnected() 273 | { 274 | check(GetServerConnection()); 275 | UE_LOG(LogHTML5Networking, Log, TEXT(" %s Websocket Client connected to server %s "), *GetDescription(), *GetServerConnection()->WebSocket->RemoteEndPoint()); 276 | } 277 | 278 | -------------------------------------------------------------------------------- /Source/HTML5Networking/Private/WebSocketServer.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "HTML5NetworkingPCH.h" 4 | #include "WebSocketServer.h" 5 | #include "WebSocket.h" 6 | 7 | #if PLATFORM_WINDOWS 8 | #include "AllowWindowsPlatformTypes.h" 9 | #endif 10 | 11 | #if !PLATFORM_HTML5 12 | #include "libwebsockets.h" 13 | #endif 14 | 15 | #if PLATFORM_WINDOWS 16 | #include "HideWindowsPlatformTypes.h" 17 | #endif 18 | 19 | // a object of this type is associated by libwebsocket to every connected session. 20 | struct PerSessionDataServer 21 | { 22 | FWebSocket *Socket; // each session is actually a socket to a client 23 | }; 24 | 25 | 26 | #if !PLATFORM_HTML5 27 | // real networking handler. 28 | static int unreal_networking_server( 29 | struct libwebsocket_context *, 30 | struct libwebsocket *wsi, 31 | enum libwebsocket_callback_reasons reason, 32 | void *user, 33 | void *in, 34 | size_t 35 | len 36 | ); 37 | 38 | #if !UE_BUILD_SHIPPING 39 | void libwebsocket_debugLog(int level, const char *line) 40 | { 41 | UE_LOG(LogHTML5Networking, Log, TEXT("websocket server: %s"), ANSI_TO_TCHAR(line)); 42 | } 43 | #endif 44 | #endif 45 | 46 | bool FWebSocketServer::Init(uint32 Port, FWebsocketClientConnectedCallBack CallBack) 47 | { 48 | #if !PLATFORM_HTML5 49 | // setup log level. 50 | #if !UE_BUILD_SHIPPING 51 | lws_set_log_level(LLL_ERR | LLL_WARN | LLL_NOTICE | LLL_DEBUG | LLL_INFO, libwebsocket_debugLog); 52 | #endif 53 | 54 | Protocols = new libwebsocket_protocols[3]; 55 | FMemory::Memzero(Protocols, sizeof(libwebsocket_protocols) * 3); 56 | 57 | Protocols[0].name = "binary"; 58 | Protocols[0].callback = FWebSocket::unreal_networking_server; 59 | Protocols[0].per_session_data_size = sizeof(PerSessionDataServer); 60 | Protocols[0].rx_buffer_size = 10 * 1024 * 1024; 61 | 62 | Protocols[1].name = nullptr; 63 | Protocols[1].callback = nullptr; 64 | Protocols[1].per_session_data_size = 0; 65 | 66 | struct lws_context_creation_info Info; 67 | memset(&Info, 0, sizeof(lws_context_creation_info)); 68 | // look up libwebsockets.h for details. 69 | Info.port = Port; 70 | // we listen on all available interfaces. 71 | Info.iface = NULL; 72 | Info.protocols = &Protocols[0]; 73 | // no extensions 74 | Info.extensions = NULL; 75 | Info.gid = -1; 76 | Info.uid = -1; 77 | Info.options = 0; 78 | // tack on this object. 79 | Info.user = this; 80 | Info.port = Port; 81 | Context = libwebsocket_create_context(&Info); 82 | 83 | if (Context == NULL) 84 | { 85 | return false; // couldn't create a server. 86 | } 87 | 88 | ConnectedCallBack = CallBack; 89 | 90 | #endif 91 | return true; 92 | } 93 | 94 | bool FWebSocketServer::Tick() 95 | { 96 | #if !PLATFORM_HTML5 97 | { 98 | libwebsocket_service(Context, 0); 99 | libwebsocket_callback_on_writable_all_protocol(&Protocols[0]); 100 | } 101 | #endif 102 | return true; 103 | } 104 | 105 | FWebSocketServer::FWebSocketServer() 106 | {} 107 | 108 | FWebSocketServer::~FWebSocketServer() 109 | { 110 | #if !PLATFORM_HTML5 111 | if (Context) 112 | { 113 | 114 | libwebsocket_context_destroy(Context); 115 | Context = NULL; 116 | } 117 | 118 | delete Protocols; 119 | Protocols = NULL; 120 | #endif 121 | } 122 | 123 | FString FWebSocketServer::Info() 124 | { 125 | 126 | #if !PLATFORM_HTML5 127 | return FString(ANSI_TO_TCHAR(libwebsocket_canonical_hostname(Context))); 128 | #else 129 | return FString(TEXT("NOT SUPPORTED")); 130 | #endif 131 | 132 | } 133 | 134 | // callback. 135 | #if !PLATFORM_HTML5 136 | int FWebSocket::unreal_networking_server 137 | ( 138 | struct libwebsocket_context *Context, 139 | struct libwebsocket *Wsi, 140 | enum libwebsocket_callback_reasons Reason, 141 | void *User, 142 | void *In, 143 | size_t Len 144 | ) 145 | { 146 | PerSessionDataServer* BufferInfo = (PerSessionDataServer*)User; 147 | FWebSocketServer* Server = (FWebSocketServer*)libwebsocket_context_user(Context); 148 | 149 | switch (Reason) 150 | { 151 | case LWS_CALLBACK_ESTABLISHED: 152 | { 153 | BufferInfo->Socket = new FWebSocket(Context, Wsi); 154 | Server->ConnectedCallBack.ExecuteIfBound(BufferInfo->Socket); 155 | libwebsocket_set_timeout(Wsi, NO_PENDING_TIMEOUT, 0); 156 | } 157 | break; 158 | 159 | case LWS_CALLBACK_RECEIVE: 160 | { 161 | BufferInfo->Socket->OnRawRecieve(In, Len); 162 | libwebsocket_set_timeout(Wsi, NO_PENDING_TIMEOUT, 0); 163 | } 164 | break; 165 | 166 | case LWS_CALLBACK_SERVER_WRITEABLE: 167 | { 168 | BufferInfo->Socket->OnRawWebSocketWritable(Wsi); 169 | libwebsocket_set_timeout(Wsi, NO_PENDING_TIMEOUT, 0); 170 | } 171 | break; 172 | case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: 173 | { 174 | BufferInfo->Socket->ErrorCallBack.ExecuteIfBound(); 175 | } 176 | break; 177 | } 178 | 179 | return 0; 180 | } 181 | #endif -------------------------------------------------------------------------------- /Source/HTML5Networking/Private/WebSocketServer.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. 2 | // 3 | // Read http://lucumr.pocoo.org/2012/9/24/websockets-101/ for a nice intro to web sockets. 4 | // This uses https://libwebsockets.org/trac/libwebsockets 5 | #pragma once 6 | #include "HTML5NetworkingPCH.h" 7 | 8 | class FWebSocketServer 9 | { 10 | public: 11 | 12 | FWebSocketServer(); 13 | ~FWebSocketServer(); 14 | 15 | /** Create a web socket server*/ 16 | bool Init(uint32 Port, FWebsocketClientConnectedCallBack); 17 | 18 | /** Service libwebsocket */ 19 | bool Tick(); 20 | 21 | /** Describe this libwebsocket server */ 22 | FString Info(); 23 | 24 | private: 25 | 26 | /** Callback for a new websocket connection to the server */ 27 | FWebsocketClientConnectedCallBack ConnectedCallBack; 28 | 29 | /** Internal libwebsocket context */ 30 | WebSocketInternalContext* Context; 31 | 32 | /** Protocols serviced by this implementation */ 33 | WebSocketInternalProtocol* Protocols; 34 | 35 | friend class FWebSocket; 36 | }; 37 | 38 | 39 | -------------------------------------------------------------------------------- /Source/HTML5Networking/Private/WebsocketConnection.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "HTML5NetworkingPCH.h" 4 | 5 | #include "IPAddress.h" 6 | #include "Sockets.h" 7 | #include "Net/NetworkProfiler.h" 8 | #include "Net/DataChannel.h" 9 | 10 | #include "WebSocketConnection.h" 11 | #include "WebSocketNetDriver.h" 12 | #include "WebSocket.h" 13 | /*----------------------------------------------------------------------------- 14 | Declarations. 15 | -----------------------------------------------------------------------------*/ 16 | 17 | // Size of a UDP header. 18 | #define IP_HEADER_SIZE (20) 19 | #define UDP_HEADER_SIZE (IP_HEADER_SIZE+8) 20 | #define SLIP_HEADER_SIZE (UDP_HEADER_SIZE+4) 21 | #define WINSOCK_MAX_PACKET (512) 22 | 23 | UWebSocketConnection::UWebSocketConnection(const FObjectInitializer& ObjectInitializer) : 24 | Super(ObjectInitializer), 25 | WebSocket(NULL) 26 | { 27 | } 28 | 29 | void UWebSocketConnection::InitBase(UNetDriver* InDriver, class FSocket* InSocket, const FURL& InURL, EConnectionState InState, int32 InMaxPacket, int32 InPacketOverhead) 30 | { 31 | // Pass the call up the chain 32 | Super::InitBase(InDriver, InSocket, InURL, InState, 33 | // Use the default packet size/overhead unless overridden by a child class 34 | InMaxPacket == 0 ? WINSOCK_MAX_PACKET : InMaxPacket, 35 | InPacketOverhead == 0 ? SLIP_HEADER_SIZE : InPacketOverhead); 36 | 37 | } 38 | 39 | void UWebSocketConnection::InitLocalConnection(UNetDriver* InDriver, class FSocket* InSocket, const FURL& InURL, EConnectionState InState, int32 InMaxPacket, int32 InPacketOverhead) 40 | { 41 | InitBase(InDriver, InSocket, InURL, InState, 42 | // Use the default packet size/overhead unless overridden by a child class 43 | InMaxPacket == 0 ? WINSOCK_MAX_PACKET : InMaxPacket, 44 | InPacketOverhead == 0 ? SLIP_HEADER_SIZE : InPacketOverhead); 45 | 46 | // Figure out IP address from the host URL 47 | 48 | // Initialize our send bunch 49 | InitSendBuffer(); 50 | } 51 | 52 | void UWebSocketConnection::InitRemoteConnection(UNetDriver* InDriver, class FSocket* InSocket, const FURL& InURL, const class FInternetAddr& InRemoteAddr, EConnectionState InState, int32 InMaxPacket, int32 InPacketOverhead) 53 | { 54 | InitBase(InDriver, InSocket, InURL, InState, 55 | // Use the default packet size/overhead unless overridden by a child class 56 | InMaxPacket == 0 ? WINSOCK_MAX_PACKET : InMaxPacket, 57 | InPacketOverhead == 0 ? SLIP_HEADER_SIZE : InPacketOverhead); 58 | 59 | // Initialize our send bunch 60 | InitSendBuffer(); 61 | 62 | // This is for a client that needs to log in, setup ClientLoginState and ExpectedClientLoginMsgType to reflect that 63 | SetClientLoginState(EClientLoginState::LoggingIn); 64 | SetExpectedClientLoginMsgType(NMT_Hello); 65 | } 66 | 67 | void UWebSocketConnection::LowLevelSend(void* Data, int32 Count) 68 | { 69 | int32 BytesSent = 0; 70 | WebSocket->Send((uint8*)Data, Count); 71 | } 72 | 73 | FString UWebSocketConnection::LowLevelGetRemoteAddress(bool bAppendPort) 74 | { 75 | return WebSocket->RemoteEndPoint(); 76 | } 77 | 78 | FString UWebSocketConnection::LowLevelDescribe() 79 | { 80 | return FString::Printf 81 | ( 82 | TEXT(" remote=%s local=%s state: %s"), 83 | *WebSocket->RemoteEndPoint(), 84 | *WebSocket->LocalEndPoint(), 85 | State == USOCK_Pending ? TEXT("Pending") 86 | : State == USOCK_Open ? TEXT("Open") 87 | : State == USOCK_Closed ? TEXT("Closed") 88 | : TEXT("Invalid") 89 | ); 90 | } 91 | 92 | 93 | void UWebSocketConnection::SetWebSocket(FWebSocket* InWebSocket) 94 | { 95 | WebSocket = InWebSocket; 96 | } 97 | 98 | FWebSocket* UWebSocketConnection::GetWebSocket() 99 | { 100 | return WebSocket; 101 | } 102 | 103 | 104 | void UWebSocketConnection::Tick() 105 | { 106 | UNetConnection::Tick(); 107 | WebSocket->Tick(); 108 | } 109 | 110 | void UWebSocketConnection::FinishDestroy() 111 | { 112 | Super::FinishDestroy(); 113 | delete WebSocket; 114 | WebSocket = NULL; 115 | 116 | } 117 | 118 | -------------------------------------------------------------------------------- /html5networking.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion" : 3, 3 | 4 | "FriendlyName" : "Experimental HTML5 Networking Plugin", 5 | "Version" : 1, 6 | "VersionName" : "1.0", 7 | "CreatedBy" : "Epic Games, Inc.", 8 | "CreatedByURL" : "http://epicgames.com", 9 | "EngineVersion" : "4.8.0", 10 | "Description" : "HTML5 Networking", 11 | "Category" : "Networking", 12 | "EnabledByDefault" : false, 13 | 14 | "Modules" : 15 | [ 16 | { 17 | "Name" : "HTML5Networking", 18 | "Type" : "Runtime", 19 | "WhitelistPlatforms" : [ "Win64", "HTML5" ] 20 | } 21 | ] 22 | } 23 | --------------------------------------------------------------------------------