Queue Outgoing Unreliable Warning

Options
artefacto
edited April 2011 in DotNet
Hi.
Based on the code for the Unity demo, I created a multiplayer application using Photon as our server. It works great so far : I can retrieve avatar, position, animation of the remote clients and also chat messages.

Now, what I have to do is to implement a way to send a 2D Texture from one client to another. So I decided to create a "texture member" in the Player class and share it over the server, the same way I did for the avatar type for example. Basically, this "texture member" is actually an array of int values, calculated using the ColorToInt given function.

But the problem I have is I get a "QueueOutgoingUnreliableWarning". Additionnaly the framerate drops to 0.

The only topic I found about this warning message is http://forum.exitgames.com/viewtopic.php?f=6&t=99 but it doesn't seem to be the same cause.

Can someone explain me what this means and how to solve this problem ?

Thanks a lot.

Comments

  • Boris
    Options
    What's the size of the int[] you send?
  • Tobias
    Options
    You could try to call SendOutgoingCommands() in smaller intervals. Also send the texture data in channel 1, if your other events are in channel 0.
    The ChannelCount is 2 by default, so that's OK for 2 channels without changing it.
  • Thanks for your quick replies.
    @Boris : I started with a 256x256 texture (that would make an array of length 65536), but since it seems to be too much, I chose a 64x64 texture instead (array length : 4096). It "works" better since my application now runs at the amazing speed of 2 fps :roll:
    @Tobias : I tried sending on channel 1, it didn't do much. So I increased my "viewSendInterval" to 5000 and now it's much better with a 64x64 texture, but as soon as I got back to a 256x256, the application becomes very slow when I send the texture info.

    If it's of any help, here's the code I added for sending the texture. I mostly copied and pasted the given code and changed variable types to what I wanted.

    In the Update loop function, I added that (in the "lock(this)" section) :
    if (Environment.TickCount - this.lastView > this.intervalView)
    {
        this.lastView = Environment.TickCount;
        this.SendPlayerView();
    }
    
    And my SendPlayerView function :
    public void SendPlayerView()
    {
    	lock (this)
    	{
    		if (this.LocalPlayer == null || this.LocalPlayer.playerID == 0 || this.peer == null)
    		{
    			return;
    		}
    
    		if (this.LocalPlayer.view == null)
    		{
    			return;
    		}
    		
    		if (this.LocalPlayer.hasMoved)
    		{
    			DebugReturn("view is updated");
    			this.LocalPlayer.SendEvView(this.peer);
    		}
    	}
    }
    
    And finally, the Player.SendEvView function is like that
    internal void SendEvView(LitePeer peer)
    {
    	if (peer == null)
    	{
    		return;
    	}
    
    	Hashtable evInfo = new Hashtable();
    
    	evInfo.Add((System.Object)STATUS_PLAYER_VIEW, (int[])this.view);
    
    	peer.OpRaiseEvent(EV_VIEW, evInfo, isSendReliable, 1);
    }
    

    Thanks.
  • Tobias
    Options
    So, you try to send an uncompressed image every ... how often?
    You should be calling SendOutgoingCommands() 10..20 times a second. Each prepares a UDP package and might carry your view data.
    Any idea where the time is lost, when you call it that often? Sending the data is done asynchronously, so it should not take up time of your game loop in general.
  • I raise my EV_VIEW event manually approximately every 5 sec so far. Between these 5 seconds I can move my player correctly without framerate loss.
    But since I left the sendInterval variable to 50, does it mean the image is sent several times without having been updated ? Does the SendOutgoingCommands() raise events for all the player information?

    From what I understand, or at least I think I do, the different events raised by the SendPosition or SendView functions are queued every 1 or 10 cycles (depending on the moveInterval or viewInterval values). Then, the SendOutgoingCommands will actually send the events over the network each 50 cycles (sendInterval). Am I right?
  • Tobias
    Options
    Any operation call you do is queued as "command". When you call Service() (which internally calls SendOutgoingCommands() for you), a single UDP package with ~1200bytes of space is filled up with the commands that were queued before. If nothing was queued in the mean time, no packet is sent of course.
    If you send more than 1200 bytes in one operation, this is fragmented and spread across multiple commands. Each fragment command fits exactly in one UDP package. If a fragment is sent, no other commands are send along in the same package. If a command is already assigned to the UDP package, no fragment fits in anymore.
    This mechanism is meant to give you control over the number of packages sent. If you send more data, to send it in the same time, you can try to call SendOutgoingCommands() more often. Of course, this is limited by the bandwidth available.

    I'm not fully sure I understand why your game slows down. SendEvView does not actually send anything. It just queues a command that will carry the operation. So your interval in which you call Service() defines how often something goes out and the size of the image defines how many calls to Service() you need just to get this image on it's way.


    Any operation call you make is only sent once, so the view is not sent multiple times when you don't update it. Just once.
  • Boris
    Options
    The 64x64 image is 16KB = ~14 reliable network packages.
    As Tobias said: the network package queuing itself won't cost a lot.
    I wonder if the image to int[] translation is what the real work is here?
  • dreamora
    Options
    Whats the chance that the impact comes from a combination of that + the fact that it ends in a lock block potentially heavily impacting "normal things" as a consequence?
  • Kaiserludi
    Options
    If there is any chance to do so, I would strongly recommend to not send the image itself as in most cases this will be a waste of bandwith which can be quite expensive on mobile devices like the iPhone, as you are not guaranteed to always have 3G avaiable. Well, 16kb every 5secs isn't that much, but it still does not seem to be the optimal approach to me.
    The alternative, you normally would go, would be, to nummerize your images and just send the number of the image, you want to show, a single short should be enough for that and if you want to, you could send that info dozens of times a second and still be at far less than 1% of the traffic, which you are causing by sending the image itself every 5 seconds.
    If that is not impossible, as the image is not a file, but created on the fly by the sending client, then you still could send the values, which you have used, to create that image. That way you could send 4096x4096 textures or even more (also the iPhone graphics card can't handle textures over 2048x2048 anyway and even those not on the old generations) multiple times a second without having appreciating performance-impacts.

    It is always a good idea to use the approach, which is costing you to send less amoutn of data, from all fitting approaches, if you are dealing with the combiantion of realtime applications and mobile networks, so you can get the most out of the small ressources, you have in terms of network hardware and infrastructure.
  • Thanks for all your input. Really appreciate it.
    I've rewriting part of my code, and now I'm getting better results. Still not perfect though, but it's better.
    As Boris pointed out, it seems the Color[] to int[] is indeed requiring a lot of work. So I'll try to look for an alternative here.
    Unfortunately, I have to send the image itself. My initial idea was to send the remote position and orientation of the camera, and then recalculate the image on the local PC, but the project constraints require the "real" image to be sent across the network.
  • Tobias
    Options
    You are sending raw per-pixel values now (at least 3 int's per pixel). If you could somehow get a JPEG compressed version of it, you would save considerable bandwidth with it.

    Edit: Are you using Unity? Then check out: http://unity3d.com/support/documentatio ... ToPNG.html
  • Using a compressed byte[] version of the view has significantly improved frame rate. Also, I have separated my connected peers in two : one manager, and several players. This way, only the manager deals with the player images, and so, it doesn't have to update and display info about the world. And for the players, they don't have to access the other players' image, so they can focus on the 3D world and the position and animation of other players.
    I still may encounter problems later on, but for now, I think I'm heading in a better direction than before.
    Thanks a lot for your help.