Announcement

Collapse
No announcement yet.

[Plugin] Socket.io Client

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

    #16
    Yes, I poked around a bit at the interface for the base library you used, didn't even realize the source is out there, that will make things easier. I'll have to take a look at what it takes to compile a plugin this weekend and see if I can get some more functionality going. Returning the object instead of a string would have actually been more convenient anyhow as I could do just about anything with it once I had that! I did see the base library has that and other magic that can come out.

    For the io.sockets.emit(), I think its the same thing, io.emit emits to all active socket connections, but I will take a look and see. I am at work now so I don't have the notes I took when I looked at the base library unfortunately.

    Thanks for the response, sorry for stirring everything up on something that was basically just a prototype

    Comment


      #17
      Hello, first of all thank you very much for your plugin, it's been very useful to me
      Similarly to DIVYA ADITYA, I needed to use this plugin for another platform (Android), and I was trying to recompile your source code for using it as an Android library (I was going to make you a pull request once I had it all builted and tested, if you might be interested ). However, I am having several issues even to compile your code and its dependencies. VS2015 for UE4 does't seem to like websocketpp and boost as dependencies. Did you have to do some particular trick, in order to compile your library with those dependencies?

      Comment


        #18
        Originally posted by ocramot View Post
        Hello, first of all thank you very much for your plugin, it's been very useful to me
        Similarly to DIVYA ADITYA, I needed to use this plugin for another platform (Android), and I was trying to recompile your source code for using it as an Android library (I was going to make you a pull request once I had it all builted and tested, if you might be interested ). However, I am having several issues even to compile your code and its dependencies. VS2015 for UE4 does't seem to like websocketpp and boost as dependencies. Did you have to do some particular trick, in order to compile your library with those dependencies?
        Yeah the original library source is at https://github.com/socketio/socket.io-client-cpp. If I remember right I had to download boost 1.6 and compile it and then grab https://github.com/zaphoyd/websocketpp/network one of the forks in websocketpp that solved some of the compile issues there. Instead of just adding the prebuilt libraries in the repo, maybe I should find the old source for building those libraries for vs2015, I'll dig around!

        If you do get a successful android build, by all means make pull requests! This is a community plugin, not something exclusive.
        Plugins: Node.js - TensorFlow - Socket.io Client - ZipUtility - Leap Motion - Hydra - Myo - RealSense

        Comment


          #19
          Downloaded and I plan on experimenting with your plugin in a little test project. Time to dive back into learning Socket.io and Nodejs!

          Comment


            #20
            [MENTION=548]getnamo[/MENTION] so I found out if the server is unavailable (crashed), the editor completely freezes up and I have to end it.

            Comment


              #21
              Originally posted by DatapawWolf View Post
              [MENTION=548]getnamo[/MENTION] so I found out if the server is unavailable (crashed), the editor completely freezes up and I have to end it.
              Interesting that sounds like the timeout happens on the game thread, the solution is to simply make the connection attempt off-thread. I've added it to issues: https://github.com/getnamo/socketio-client-ue4/issues/3 and will try to address it next time I make a pass on the plugin. Feel free to make pull requests if you fix it before
              Plugins: Node.js - TensorFlow - Socket.io Client - ZipUtility - Leap Motion - Hydra - Myo - RealSense

              Comment


                #22
                Hi,
                thanks for the plugin. It seems I have some trouble getting it to work though. I use the version for UE4.11, socketIO is running on 0.9. I try to connect to socket io with connect node "http://192.168.1.50:5000" then I want to emit a command. The colleague who set up the Server said SocketIO Namespace was "events" and the event I need to send to is called "unityFanSpeedEvent" and receives only a number between 0 and 100.

                I guess the problem is the SocketIO namespace, I'm not sure in what node I need to add the namespace (connect or emit?). Sorry if this is some kind of a stupid question, it's my first time working with socketIO.

                Here is what I'm trying to do:
                Click image for larger version

Name:	ue4SocketIO.PNG
Views:	1
Size:	28.2 KB
ID:	1115764

                Comment


                  #23
                  Originally posted by Tyleet View Post
                  Hi,
                  thanks for the plugin. It seems I have some trouble getting it to work though. I use the version for UE4.11, socketIO is running on 0.9. I try to connect to socket io with connect node "http://192.168.1.50:5000" then I want to emit a command. The colleague who set up the Server said SocketIO Namespace was "events" and the event I need to send to is called "unityFanSpeedEvent" and receives only a number between 0 and 100.

                  I guess the problem is the SocketIO namespace, I'm not sure in what node I need to add the namespace (connect or emit?). Sorry if this is some kind of a stupid question, it's my first time working with socketIO.

                  Here is what I'm trying to do:
                  ...
                  So the current plugin is very bare bones, in terms of actual binding meat it's only 40-50 lines of code, enough to have been done in a short presentation as a proof of concept:

                  Code:
                  #include "SocketIOClientPrivatePCH.h"
                  #include "SocketIOClientComponent.h"
                  
                  
                  USocketIOClientComponent::USocketIOClientComponent(const FObjectInitializer &init) : UActorComponent(init)
                  {
                  	bWantsInitializeComponent = true;
                  	bAutoActivate = true;
                  }
                  
                  std::string StdString(FString UEString)
                  {
                  	return std::string(TCHAR_TO_UTF8(*UEString));
                  }
                  FString FStringFromStd(std::string StdString)
                  {
                  	return FString(StdString.c_str());
                  }
                  
                  
                  void USocketIOClientComponent::Connect(FString AddressAndPort)
                  {
                  	if (!AddressAndPort.IsEmpty())
                  	{
                  		PrivateClient.connect(StdString(AddressAndPort));
                  	}
                  	else
                  	{
                  		PrivateClient.connect("http://localhost:3000");
                  	}
                  }
                  
                  void USocketIOClientComponent::Emit(FString Name, FString Data)
                  {
                  	PrivateClient.socket()->emit(StdString(Name), StdString(Data));
                  	//UE_LOG(LogTemp, Log, TEXT("Emit %s with %s"), *Name, *Data);
                  }
                  
                  void USocketIOClientComponent::Bind(FString Name)
                  {
                  	PrivateClient.socket()->on(StdString(Name), sio::socket::event_listener_aux([&](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list &ack_resp) {
                  
                  		const FString SafeName = FStringFromStd(name);
                  		const FString SafeData = FStringFromStd(data->get_string());
                  		FFunctionGraphTask::CreateAndDispatchWhenReady([&, SafeName, SafeData]
                  		{
                  			On.Broadcast(SafeName, SafeData);
                  		}, TStatId(), nullptr, ENamedThreads::GameThread);
                  	}));
                  }
                  That is literally it. But it's apparently useful, so I may have to look into extending it a bit

                  It should be a fairly easy modification to support namespaces. The current code uses

                  Code:
                  PrivateClient.socket()-> ...
                  which fetches the default namespace "/" and the method should just expand to add a namespace variable e.g.

                  Code:
                  FString Namespace = FString(TEXT("events"));
                  PrivateClient.socket(StdString(Namespace))-> ...
                  events.

                  Feel free to modify the source and make a pull request, otherwise it may take a bit of time before I make a pass on the plugin with the fixes. I've added a note about it in github issues on the repo.
                  Plugins: Node.js - TensorFlow - Socket.io Client - ZipUtility - Leap Motion - Hydra - Myo - RealSense

                  Comment


                    #24
                    Hi,
                    thanks for the quick reply, I will try that and let you know the results.

                    Comment


                      #25
                      EDIT: I just tested my theory that it is an socketIO version problem since the legacy server is running 0.9. Set up some local servers on my pc one with 1.x and one for 0.9, 0.9 only throws warnings when Unreal tries to connect, 1.x accepts the connection.


                      Originally posted by getnamo View Post
                      So the current plugin is very bare bones, in terms of actual binding meat it's only 40-50 lines of code, enough to have been done in a short presentation as a proof of concept:

                      Code:
                      #include "SocketIOClientPrivatePCH.h"
                      #include "SocketIOClientComponent.h"
                      
                      
                      USocketIOClientComponent::USocketIOClientComponent(const FObjectInitializer &init) : UActorComponent(init)
                      {
                      	bWantsInitializeComponent = true;
                      	bAutoActivate = true;
                      }
                      
                      std::string StdString(FString UEString)
                      {
                      	return std::string(TCHAR_TO_UTF8(*UEString));
                      }
                      FString FStringFromStd(std::string StdString)
                      {
                      	return FString(StdString.c_str());
                      }
                      
                      
                      void USocketIOClientComponent::Connect(FString AddressAndPort)
                      {
                      	if (!AddressAndPort.IsEmpty())
                      	{
                      		PrivateClient.connect(StdString(AddressAndPort));
                      	}
                      	else
                      	{
                      		PrivateClient.connect("http://localhost:3000");
                      	}
                      }
                      
                      void USocketIOClientComponent::Emit(FString Name, FString Data)
                      {
                      	PrivateClient.socket()->emit(StdString(Name), StdString(Data));
                      	//UE_LOG(LogTemp, Log, TEXT("Emit %s with %s"), *Name, *Data);
                      }
                      
                      void USocketIOClientComponent::Bind(FString Name)
                      {
                      	PrivateClient.socket()->on(StdString(Name), sio::socket::event_listener_aux([&](std::string const& name, sio::message::ptr const& data, bool isAck, sio::message::list &ack_resp) {
                      
                      		const FString SafeName = FStringFromStd(name);
                      		const FString SafeData = FStringFromStd(data->get_string());
                      		FFunctionGraphTask::CreateAndDispatchWhenReady([&, SafeName, SafeData]
                      		{
                      			On.Broadcast(SafeName, SafeData);
                      		}, TStatId(), nullptr, ENamedThreads::GameThread);
                      	}));
                      }
                      That is literally it. But it's apparently useful, so I may have to look into extending it a bit

                      It should be a fairly easy modification to support namespaces. The current code uses

                      Code:
                      PrivateClient.socket()-> ...
                      which fetches the default namespace "/" and the method should just expand to add a namespace variable e.g.

                      Code:
                      FString Namespace = FString(TEXT("events"));
                      PrivateClient.socket(StdString(Namespace))-> ...
                      events.

                      Feel free to modify the source and make a pull request, otherwise it may take a bit of time before I make a pass on the plugin with the fixes. I've added a note about it in github issues on the repo.
                      Hi,
                      so I tried modifying your source code, but it seems I can't get it to run. Even if I hardcode IP, namespace, eventname and data into the source nothing gets through to the server.

                      Code:
                      void USocketIOClientComponent::Connect(FString AddressAndPort, FString nSpace)
                      {
                      	if (!AddressAndPort.IsEmpty())
                      	{
                      		Namespace = nSpace;
                      		PrivateClient.connect("http://192.168.1.50:5000");//StdString(AddressAndPort));
                      		UE_LOG(LogTemp, Log, TEXT("%s"), *nSpace);
                      		
                      	}
                      ...
                      
                      void USocketIOClientComponent::Emit(FString Name, FString Data)
                      {
                      	FString speed = FString(TEXT("100"));
                      	FString nsp = FString(TEXT("/events"));
                      	FString eve = FString(TEXT("unityFanSpeedEvent"));
                      	PrivateClient.socket(StdString(nsp))->emit(StdString(eve),StdString(speed));
                      	//PrivateClient.socket("/servo"/*StdString(Namespace)*/)->emit("enable");//StdString(Name), StdString(Data));
                      	UE_LOG(LogTemp, Log, TEXT("Emit %s with %s"), *Name, *Data);
                      }
                      i also tried "events" or "events/" to make sure its not a problem with the namespace. Here is a code snippet from the Unity Project the Server was originally created for (written in C#):
                      Connect:
                      Code:
                      client = new Client("http://192.168.1.50:5000");
                      
                      		client.Error += SocketError;
                      		client.Message += SocketMessage;
                      
                      		socket = client.Connect ("/events");
                      Emit:
                      Code:
                      public void EventFanSpeed(int speed) {
                      		if (oldSpeed == speed)
                      			return;
                      
                      		socket.Emit("unityFanSpeedEvent", "" + speed, null);
                      		oldSpeed = speed;
                      	}
                      Could it be that the Problem is that the server runs SocketIO 0.9?
                      Last edited by Tyleet; 09-25-2016, 07:07 AM.

                      Comment


                        #26
                        Update to v0.2.0 for 4.13
                        -Added connect/disconnect signatures (these have to be implemented by
                        the server if used)
                        -Added C++ lambda binds do e.g.
                        BindDataLambdaToEvent([&](const FString& EventName, const FString& EventData) { //your code }, Name, Namespace);
                        to use it
                        -Bind changed name to BindEvent
                        -Added Namespace support
                        -Connection runs on background thread (should not be blocking anymore)

                        Grab it at the github repo release
                        https://github.com/getnamo/socketio-...ases/tag/0.2.0


                        Originally posted by Tyleet View Post
                        ...

                        Could it be that the Problem is that the server runs SocketIO 0.9?
                        Try the newest release and let me know if it works. That said I believe the c++ client is using a 1.x version so there may be compatibility issues with a 0.x socket.io server.
                        Last edited by getnamo; 10-01-2016, 10:33 AM.
                        Plugins: Node.js - TensorFlow - Socket.io Client - ZipUtility - Leap Motion - Hydra - Myo - RealSense

                        Comment


                          #27
                          Hello getnamo, amazing plugin, I am just wondering is it possible to do all this socket.io bindings using code instead of BP?

                          Comment


                            #28
                            Update to v0.2.1 for 4.13
                            -added proper binary send/receive support
                            -added functioning onconnect/disconnect events, no longer require
                            special events on your server
                            -added on namespace connect/disconnect events
                            -added on fail event

                            Grab it at the github repo releases url
                            Code changes found in this merged commit


                            Originally posted by Alexander Lean View Post
                            Hello getnamo, amazing plugin, I am just wondering is it possible to do all this socket.io bindings using code instead of BP?
                            Absolutely, I'm using this plugin in C++ for my own projects. Don't have a C++ how to atm, but here's a quick rundown:

                            To use the C++ code from the plugin add it as a dependency module in your project build.cs

                            Code:
                            PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "SocketIOClient" });
                            Add the component to your actor or reference it from another component by getting it on begin play e.g.

                            Code:
                            SIOComponent = Cast<USocketIOClientComponent>(this->GetOwner()->GetComponentByClass(USocketIOClientComponent::StaticClass()));
                            if (!SIOComponent)
                            {
                            	UE_LOG(LogTemp, Warning, TEXT("No sister socket IO component found"));
                            	return;
                            }
                            else
                            {
                            	UE_LOG(LogTemp, Log, TEXT("Found SIOComponent: %s"), *SIOComponent->GetDesc());
                            }

                            Connect:
                            Code:
                            //get a reference or add as subobject in your actor
                            USocketIOClientComponent* SIOComponent;
                            
                            //the component will autoconnect, but you may wish to change the url before it does that via
                            SIOComponent->AddressAndPort = FString("http://127.0.0.1:3000"); //change your address
                            
                            //you can also disable auto connect and connect it at your own time via
                            SIOComponent->ShouldAutoConnect = false;
                            SIOComponent->Connect(); // or SIOComponent->Connect("http://127.0.0.1:3000");

                            String emit:
                            Code:
                            SIOComponent->Emit(FString(TEXT("myevent")), FString(TEXT("some data or stringified json"));
                            note that namespaces are supported as a third optional FString parameter.

                            Raw Data emit:
                            Code:
                            TArray<uint8> Buffer;
                            
                            //fill buffer with your data
                            
                            SIOComponent->EmitBuffer(FString(TEXT("myBinarySendEvent")), Buffer.GetData(), Buffer.Num());
                            to receive events you can bind lambdas which makes things awesomely easy e.g.
                            Code:
                            SIOComponent->BindDataLambdaToEvent([&](const FString& Name, const FString& Data)
                            		{
                            			//do something with your string data
                            		}, FString(TEXT("myStringReceiveEvent")));
                            Raw data receive:
                            Code:
                            SIOComponent->BindBinaryMessageLambdaToEvent([&](const FString& Name, const TArray<uint8>& Buffer)
                            		{
                            			//Do something with your buffer
                            		}, FString(TEXT("myBinaryReceiveEvent")));
                            or if you want to deal with raw socket.io message data (this plugin doesn't have automatic UE json support yet)
                            Code:
                            SIOComponent->BindRawMessageLambdaToEvent([&](const FString& Name, const sio::message::ptr&)
                            		{
                            			//do something with your sio::message::ptr data 
                            		}, FString(TEXT("myArbitraryReceiveEvent")));
                            see https://github.com/socketio/socket.i.../sio_message.h for details on how to deal with raw sio messages.

                            I'll hopefully consolidate the lambda types later on to handle UEJson format instead which will allow you to skip the stringification step on both sides if you want to build json objects. TBC!
                            Last edited by getnamo; 10-02-2016, 12:23 PM.
                            Plugins: Node.js - TensorFlow - Socket.io Client - ZipUtility - Leap Motion - Hydra - Myo - RealSense

                            Comment


                              #29
                              Wow amazing response! I 'm your fan now! Thanks a lot, I am just wondering about one thing, as I know socket.io is TCP based will it work on multiplayer games from 500-5000 players? Will it be as fast as UDP?

                              Thanks!

                              Comment


                                #30
                                Originally posted by Alexander Lean View Post
                                Wow amazing response! I 'm your fan now! Thanks a lot, I am just wondering about one thing, as I know socket.io is TCP based will it work on multiplayer games from 500-5000 players? Will it be as fast as UDP?

                                Thanks!
                                Edit: forgot to mention TCP vs UDP

                                Since Socket.io is based on websockets it can only use TCP. This can be a problem, see here this article: http://gafferongames.com/networking-...rs/udp-vs-tcp/ counterpoints: https://news.ycombinator.com/item?id=7289497

                                In modern networks you don't get as many packet losses as you used to in the past. That said TCP performs worse than UDP for real-time loss tolerant applications because of its in-order setup. As a consequence you will receive old data, even if you have new data that can overwrite it which adds additional latency when you have packet losses. This means that your worst case scenarios will be worse than when using UDP. E.g. in a bad/unreliable connection your characters may appear frozen, and then unfreeze and do all their past movement really quickly vs just teleport to their latest position.

                                In general I personally don't use socket.io for small real-time data e.g. character movement, for that I use UE4's network replication system.
                                What I tend to use socket.io for is big data transfer (e.g. files) or logic events which aren't <1 second time sensitive (new file event, chat, data sharing events). But that doesn't mean it can't be used for that, WoW was notoriously based entirely on TCP!

                                It's definitely easier to write logic in node.js than messing with UE4's dedicated server compilation and the mental gymnastics you have to do to understand the network replication system. It's a question of trade offs, when everything works with no packet losses, both of the solutions will look the same to the end user.


                                In terms of overall throughput and performance, for that I point you to (~4 year old benchmark):
                                http://drewww.github.io/socket.io-benchmarking/

                                key takeaway:
                                If I want to be able to support 5,000 concurrent users, a broadcast every second will use up basically half of my messaging capacity. If I have a chat room with 200 people in it sending a message a second on average, that's only about 2% of my capacity.
                                Also

                                My personal test of around ~ 300 chat windows spawned from the example project suggests its stable for chat traffic for that size and I've seen chats up to ~2k work well.

                                That said, multiplayer traffic as a problem is an exponential function. To get high concurrency you need to cordon off sections of your traffic that don't interact to keep things smooth. Generally this means that if you can't see a user or they are far away, the server shouldn't emit that traffic to you. You can experiment with socket.io rooms/namespaces when you try scaling which will effectively cordon users off to their respective sub-area and you can always add more hardware scaling by having those rooms on different servers. In general though I don't think you would run into a problem until you're transmitting a lot of data or have hundreds of users even on a single server machine.

                                So try to break it and then when you do finally hit the scaling problem, then optimize. Early optimizations usually just waste time as often your expectations != the actual situation.


                                Unrelated to socket.io, but here's someone holding 1 Million long-poll connections active on a single node.js server (~16GB ram). It all depends on what you're doing, ram, connection speed, and overall expected throughput from each client.
                                Last edited by getnamo; 11-06-2016, 07:30 AM.
                                Plugins: Node.js - TensorFlow - Socket.io Client - ZipUtility - Leap Motion - Hydra - Myo - RealSense

                                Comment

                                Working...
                                X