Jump to content

  • Log In with Google      Sign In   
  • Create Account


Async design


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
13 replies to this topic

#1 NLScotty   Members   -  Reputation: 381

Like
0Likes
Like

Posted 24 December 2013 - 05:04 AM

Hello,

 

I am using async c# code for my client. Now I wonder how you should implent this since sometimes I just read a packet from the server and you need to have information about the game. Do you need to place it inside a queue for the mainthread to check it and let him do the reading and then sending? Or are there better designs, this feels like the same as sync but without handling the thread yourself.



Sponsor:

#2 hplus0603   Moderators   -  Reputation: 4984

Like
0Likes
Like

Posted 24 December 2013 - 02:07 PM

For a ciient, async network completion isn't that helpful, because the amount of I/O will not be enough to matter. However, there's nothing wrong with using async even on the client. In the end, a game loop typically looks like:

 

1) read inputs

2) calculate simulation

3) render graphics

 

With networking, the "read inputs" step includes both keyboard and network, and you typically send the inputs to others right before going to the calculate simulation step.

 

More specifics can't really be known without also knowing what your network mechanism is -- input synchronous? Forward extrapolated? Dumb client? Something else?


enum Bool { True, False, FileNotFound };

#3 NLScotty   Members   -  Reputation: 381

Like
0Likes
Like

Posted 25 December 2013 - 05:28 AM

For a ciient, async network completion isn't that helpful, because the amount of I/O will not be enough to matter. However, there's nothing wrong with using async even on the client. In the end, a game loop typically looks like:

 

1) read inputs

2) calculate simulation

3) render graphics

 

With networking, the "read inputs" step includes both keyboard and network, and you typically send the inputs to others right before going to the calculate simulation step.

 

More specifics can't really be known without also knowing what your network mechanism is -- input synchronous? Forward extrapolated? Dumb client? Something else?

I handle everything sync from the input :)

 

Now the weird thing is, in my game you can start more games vs other players at the same time. I use for every game a new connection, would this mean that async starts to scale better when there are many clients. Else I create 5 or more threads when a user is playing 5 games.



#4 hplus0603   Moderators   -  Reputation: 4984

Like
3Likes
Like

Posted 26 December 2013 - 12:04 AM

On Windows, select() works fine up to 64 sockets (this is an internal implementation detail.)

On Linux, select() works fine up to a little over 1000 sockets (somewhat depending on how many other file descriptors you use.)

The networking part is very, very, seldom an actual bottleneck for games.

On the client side, physics simulation (if used) and graphics will typically be a limitation long before networking even shows up on a profile.

On the server side, interest management (who should see what data,) as well as physics simulation (if used) is typically a lot heavier work than the networking bit, too.


enum Bool { True, False, FileNotFound };

#5 NLScotty   Members   -  Reputation: 381

Like
0Likes
Like

Posted 27 December 2013 - 02:14 PM

On Windows, select() works fine up to 64 sockets (this is an internal implementation detail.)

On Linux, select() works fine up to a little over 1000 sockets (somewhat depending on how many other file descriptors you use.)

The networking part is very, very, seldom an actual bottleneck for games.

On the client side, physics simulation (if used) and graphics will typically be a limitation long before networking even shows up on a profile.

On the server side, interest management (who should see what data,) as well as physics simulation (if used) is typically a lot heavier work than the networking bit, too.

 

This is my code so far, do you see anything weird?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Net;

namespace clb
{
    class Connection
    {
        public enum PacketType
        {
            INIT_PACKET_HEADER,
            INIT_PACKET_DATA,
            PACKET_HEADER,
            PACKET_DATA
        }

        public struct PacketInfo
        {
            public PacketType type;
            public byte[] buffer;
            public int bytesRead;

            public PacketInfo(int len, PacketType pt)
            {
                type = pt;
                buffer = new byte[len];
                bytesRead = 0;
            }
        }

        TcpClient m_client;

        protected GameThread m_thread;

        // constructors
        public Connection()
        {
            m_client = new TcpClient();
        }

        // virtual fcuntions, they get called from my queue again
        public virtual void OnInitPacketReceived(short version)
        {

        }

        public virtual void OnPacketReceived(PacketReader pr)
        {
        }

        // Interface functions
        protected async void Connect(string ip, int port)
        {
            try
            {
                await m_client.ConnectAsync(ip, port);

                if(m_client.Connected)
                    StartReadingAsync(new PacketInfo(2, PacketType.INIT_PACKET_HEADER)); 
            }
            catch (Exception e)
            {
                Console.WriteLine("[exception] in Connect:" + e.ToString());
            }
        }

        public void Disconnect()
        {
            if(m_client.Connected)
                m_client.Close();
        }

        public void SendPacket(PacketWriter pw)
        {
            SendPacket(pw.ToArray());
        }

        // The real functions

        private void InitPacketHeader(PacketInfo pi)
        {
            PacketReader pr = new PacketReader(pi.buffer);
            short header = pr.ReadShort();

            StartReadingAsync(new PacketInfo(header, PacketType.INIT_PACKET_DATA));
        }

        private void InitPacketData(PacketInfo pi)
        {
            // read stuff
            StartReadingAsync(new PacketInfo(4, PacketType.PACKET_HEADER));
        }

        private void PacketHeader(PacketInfo pi)
        {
            // read stuff

            StartReadingAsync(new PacketInfo(packetLength, PacketType.PACKET_DATA));
        }

        private void PacketData(PacketInfo pi)
        {
           // read stuff

            if (data.Length != 0)
            {
                m_thread.AddMessage(new Message(MessageID.PacketRecv, new PacketReader(data)));
            }
            StartReadingAsync(new PacketInfo(4, PacketType.PACKET_HEADER));
        }

        private async void StartReadingAsync(PacketInfo pi)
        {
            if (!m_client.Connected)
            {
                Console.WriteLine("Trying to read, but not connected anymore????" );
                return;
            }

            try
            {
                int recv = await m_client.GetStream().ReadAsync(pi.buffer, pi.bytesRead, pi.buffer.Length - pi.bytesRead);

                if(recv == 0)
                {
                    Console.WriteLine("recv 0 in reading, dc?");
                    return;
                }

                pi.bytesRead += recv;

                if (pi.bytesRead != pi.buffer.Length)
                {
                    StartReadingAsync(pi);
                    return;
                }

                switch (pi.type)
                {
                    case PacketType.INIT_PACKET_DATA:
                        InitPacketData(pi);
                        break;

                    case PacketType.INIT_PACKET_HEADER:
                        InitPacketHeader(pi);
                        break;

                    case PacketType.PACKET_DATA:
                        PacketData(pi);
                        break;

                    case PacketType.PACKET_HEADER:
                        PacketHeader(pi);
                        break;
                }
                
            }
            catch (Exception e)
            {
                Console.WriteLine("[exception] in StartReadingAsync:" + e.ToString());
            }
        }

        private async void SendPacket(byte[] buffer)
        {
           // cripto and stuff

            try
            {
               await m_client.GetStream().WriteAsync(sendData, 0, sendData.Length);
            }
            catch (Exception e)
            {
                Console.WriteLine("[exception] in SendPacket:" + e.ToString());
            }
        }

    }
}



#6 zzzz....   Members   -  Reputation: 84

Like
0Likes
Like

Posted 29 January 2014 - 09:21 AM

I am with you on async design. However what you need is more than just threading and polling. In my upcoming distributed interface called Flow I have made it transparent to the caller or sender of messages if the recipient is on the network or in local memory. Like this you can seamlessly distribute tasks and responsibilities as the different message handlers work independently and asynchronously (unless the synchronization is done explicitly with some wait on a response message).

 

You also positively should never have a loop where you poll input with a sub 60 or even 120 Hz frequency as you will miss key events such as a quick key up and down press. The most important here is to always offload heavy calculations to separate threads. Typically simulation can run at 10-20 Hz, visualisation at 25-60 Hz and input at 120 Hz, this makes it a rather poor design choice to use a shared thread for the three of them,

 

It does require that you design your program differently as you will deal with messages rather than state variables in some global shared memory.

 

My current game loop looks like this

 

while (continue)

{

  sleep(1000);

}

 

:-)

 

All the code and logic is distributed and kick started with a set of message handlers.

 

It is rather a beautiful thing to abstract communication be it intra process (same process), inter process (between processes) or inter machine network communication. It also allows parts of your logic run on a mac and another part run on a windows machine as it allows heterogeneous platform processing.

 

The water fall or multi-pass execution model is a technology of ancient times and maladapted to multi-core and multi-processing methodologies.

 

spinningcube


Edited by jbadams, 13 April 2014 - 01:40 AM.
Restored post contents from history.


#7 hplus0603   Moderators   -  Reputation: 4984

Like
3Likes
Like

Posted 29 January 2014 - 10:25 AM

Why would you use a polling loop for main? This means that, after I decide to quit, "nothing" may be happening for up to a second. I'd presume that, if you have a good queuing API, the main loop could blocking wait on a queue where you send it a "quit" message (rather than setting a boolean to false.) And if you don't have blocking waits, you could at least use the OS-specific mutex/condition variable/event to achieve the same thing.

 

Other than that, mostly-transparent network/memory messaging APIs have some nice properties. ZeroMQ for example took that to heart and had significant success with it. The two main drawbacks, in my mind, are that sometimes, you NEED to know, for example when implementing and requiring authentication. And sometimes, you NEED to know, for performance reasons. As long as those don't get in the way, the code can be very nice and clean.


enum Bool { True, False, FileNotFound };

#8 zzzz....   Members   -  Reputation: 84

Like
-5Likes
Like

Posted 29 January 2014 - 11:23 AM

Why would you use a polling loop for main? This means that, after I decide to quit, "nothing" may be happening for up to a second. I'd presume that, if you have a good queuing API, the main loop could blocking wait on a queue where you send it a "quit" message (rather than setting a boolean to false.) And if you don't have blocking waits, you could at least use the OS-specific mutex/condition variable/event to achieve the same thing.

 

Other than that, mostly-transparent network/memory messaging APIs have some nice properties. ZeroMQ for example took that to heart and had significant success with it. The two main drawbacks, in my mind, are that sometimes, you NEED to know, for example when implementing and requiring authentication. And sometimes, you NEED to know, for performance reasons. As long as those don't get in the way, the code can be very nice and clean.

 

Seriously? It is an example and a joke at that to exemplify how a truly distributed system does not care about order as the components negotiate their communication without a central authority. I like anarchy :-) But sure you could make the main thread just wait or sleep on a signal. I guess you will gain 0.0000001 seconds performance ;-) But ok if your aim is to exit as fast as possible then sure a long sleep and then a poll will delay that. I give you that.

 

Nice straw man argument. Here is a pat on the back.

 

Transparent message passing is the way to go and I am quite amased why not everyone has transitioned to the paradigm already. I guess laziness. And I totally can't stand Intel TBB it just plainly over complicates things (yes all programmers know this is not a message passing interface, next...)

 

What do you mean that you NEED to know? I don't understand. Do you mean knowing the clock cycles that were spent? Curious.

 

spinningcube

 

PS - and people wonder why coders have zero personal skills. You hung up on a detail on a polling loop when 99% of the post was about how to design a proper distributed game environment. Sheesh.

 

PS2 - the moderator gives obvious BS advice and gets plussed and I get a negative when I tell the truth. Awesome. Shows how many here are actual professional coders...


Edited by jbadams, 13 April 2014 - 01:40 AM.
Restored post contents from history.


#9 frob   Moderators   -  Reputation: 19040

Like
3Likes
Like

Posted 29 January 2014 - 12:14 PM

Seriously? It is an example and a joke at that to exemplify how a truly distributed system does not care about order as the components negotiate their communication without a central authority. I like anarchy :-) But sure you could make the main thread just wait or sleep on a signal. I guess you will gain 0.0000001 seconds performance ;-) But ok if your aim is to exit as fast as possible then sure a long sleep and then a poll will delay that. I give you that.

Polling for main game loops may be fine for turn-based or event-based games and can be used for them. However, most modern games do not rely on that method, instead operating on a tight main loop that runs "as fast as possible". These tight main loops decouple rendering, animation, simulation, and input because that allows them to bypass steps that are not required during that instance of the loop.

As for gaining 0.0000001 seconds (100 nanoseconds), there have absolutely been times when profiling and optimizing code that a 100 nanosecond improvement was a godsend. When you start multiplying things by 60 or 75 or 120 frames per second, multiplied again by hundreds or thousands of elements to update every frame, those nanoseconds become important pretty quickly.

Nice straw man argument. Here is a pat on the back.
 
Transparent message passing is the way to go and I am quite amased why not everyone has transitioned to the paradigm already. I guess laziness. And I totally can't stand Intel TBB it just plainly over complicates things (yes all programmers know this is not a message passing interface, next...)
 
What do you mean that you NEED to know? I don't understand. Do you mean knowing the clock cycles that were spent? Curious.
  
PS - and people wonder why coders have zero personal skills. You hung up on a detail on a polling loop when 99% of the post was about how to design a proper distributed game environment. Sheesh.

 

 

True, a few coders have zero personal skills. Fortunately they make up a small portion of the community. There are many books that might be useful on that subject to such people, such as this famous one, that teach techniques such as avoiding direct criticism and condemnation, being sympathetic with others' ideas even when they are different from yours, and allowing other people to save face when they (rightly or wrongly) feel wronged.


Check out my personal indie blog at bryanwagstaff.com.

#10 zzzz....   Members   -  Reputation: 84

Like
-2Likes
Like

Posted 29 January 2014 - 12:30 PM

 

Seriously? It is an example and a joke at that to exemplify how a truly distributed system does not care about order as the components negotiate their communication without a central authority. I like anarchy :-) But sure you could make the main thread just wait or sleep on a signal. I guess you will gain 0.0000001 seconds performance ;-) But ok if your aim is to exit as fast as possible then sure a long sleep and then a poll will delay that. I give you that.

Polling for main game loops may be fine for turn-based or event-based games and can be used for them. However, most modern games do not rely on that method, instead operating on a tight main loop that runs "as fast as possible". These tight main loops decouple rendering, animation, simulation, and input because that allows them to bypass steps that are not required during that instance of the loop. As for gaining 0.0000001 seconds (100 nanoseconds), there have absolutely been times when profiling and optimizing code that a 100 nanosecond improvement was a godsend. When you start multiplying things by 60 or 75 or 120 frames per second, multiplied again by hundreds or thousands of elements to update every frame, those nanoseconds become important pretty quickly.

Nice straw man argument. Here is a pat on the back.   Transparent message passing is the way to go and I am quite amased why not everyone has transitioned to the paradigm already. I guess laziness. And I totally can't stand Intel TBB it just plainly over complicates things (yes all programmers know this is not a message passing interface, next...)   What do you mean that you NEED to know? I don't understand. Do you mean knowing the clock cycles that were spent? Curious. PS - and people wonder why coders have zero personal skills. You hung up on a detail on a polling loop when 99% of the post was about how to design a proper distributed game environment. Sheesh.

 

 

True, a few coders have zero personal skills. Fortunately they make up a small portion of the community. There are many books that might be useful on that subject to such people, such as this famous one, that teach techniques such as avoiding direct criticism and condemnation, being sympathetic with others' ideas even when they are different from yours, and allowing other people to save face when they (rightly or wrongly) feel wronged.

 

 

Again with the poll. It was shown as an example that you no longer have to use a main thread at all. Yes you can replace that with whatever other sleep/signal that you prefer.

 

It is not always true though. Unfortunately when working on OS X and iPad some parts have to be polled in the main thread. I hate that :-)

 

For the rest. Yeah maybe I should have been kinder but it puts my pants on fire when I spend a long post to explain the benefits of asynch and distributed design, to then make an obvious fun remark about how the main loop can now just sleep, that hplus spends all his energy and focus on that joke of a line. It's ironic and black humour. But it also shows how some think they can get out of a stupid comment (by advocating a multi-pass approach for things that should not, like hplus did) by attacking something without rime nor reason. It's weak and he should be the one pointing out his own mistake, not spreading bad information. 

 

This site still is about better not worse coding practices right?

 

spinningcube

 

PS I've read that book and usually self help is just some honey for the soul, not really the solution. Sometimes anger is good and helps you get things done. I am sure hplus knows his shit, but why then play stupid and point out something like that, when he should be re-assessing his first answer. We all make mistakes and mine is sometimes to be a bit too confrontational, of which if I hurt anyone, apologize.

 

PS2 - and I am genuinely curious what would be the NEED in the message passing approach. I am designing such a system and would like to know. Maybe hplus has a good insight on this.


Edited by jbadams, 13 April 2014 - 01:41 AM.
Restored post contents from history.


#11 hplus0603   Moderators   -  Reputation: 4984

Like
1Likes
Like

Posted 30 January 2014 - 03:04 PM

Transparent message passing is the way to go


Except when it isn't.

and I am quite amased why not everyone has transitioned to the paradigm already.


I can think of many reasons, such as perhaps having tried it and it turns out that "transparency" always comes at a cost. I gave a few other reasons in my original response, such as when you have a need for authentication over networks. As I also said in my original response, ZeroMQ has done this for a long time, and there are successful systems built on top of that.

the moderator gives obvious BS advice


Specifics would be much more useful.
enum Bool { True, False, FileNotFound };

#12 zzzz....   Members   -  Reputation: 84

Like
-2Likes
Like

Posted 30 January 2014 - 07:51 PM

 

Transparent message passing is the way to go

Except when it isn't.

and I am quite amased why not everyone has transitioned to the paradigm already.

I can think of many reasons, such as perhaps having tried it and it turns out that "transparency" always comes at a cost. I gave a few other reasons in my original response, such as when you have a need for authentication over networks. As I also said in my original response, ZeroMQ has done this for a long time, and there are successful systems built on top of that.

the moderator gives obvious BS advice

Specifics would be much more useful.

 

 

Well if you really need to know that you are doing authentication from a certain place you are "screwed". Anyone can hack your binary and make you think you are wherever they want you to believe you are. There is no protection against that. As a programmer you are blind and deaf to the world in your little black box of assumptions i.e code.

 

To the BS I found this at the top

 

start_quote

 

1) read inputs

2) calculate simulation

3) render graphics

 

With networking, the "read inputs" step includes both keyboard and network, and you typically send the inputs to others right before going to the calculate simulation step.

 

end_quote

 

And I answered exactly that by saying no, that is very bad practice as you have different frequencies and workloads in these different steps. In most OSs though you are obliged to keep graphics rendering in the main thread which is a shame. At least as long as you use the legacy languages OpenGL or DirectX. GPUs bye-bye...

 

I had a look at ZeroMQ thanks to your comments and it seems quite interesting. It is intriguing although since I am almost done (on my third re-write) I just will go with the current architecture. It does deserve a closer inspection as the authors of ZeroMQ definitely have put a lot of good engineering into their product.

 

Lets just say that they have one major fallacy. You can handle reliability over UDP. Ponder on that.

 

spinningcube


Edited by jbadams, 13 April 2014 - 01:41 AM.
Restored post contents from history.


#13 hplus0603   Moderators   -  Reputation: 4984

Like
0Likes
Like

Posted 30 January 2014 - 10:16 PM

Well if you really need to know that you are doing authentication from a certain place you are "screwed".


I think you got it backwards. To be able to DO authentication, the network-receiving part needs to know that it's talking to a network, and needs information like what the remote IP address is.

Anyone can hack your binary


I don't understand why you're discussing clients at all. The client does not have multiple networked nodes to use, so I don't understand how network-versus-local-node transparency would help you there at all.

To the BS I found this at the top

1) read inputs
2) calculate simulation
3) render graphics


Just to make it clear: You state that you think the game structure from almost every real-time game, ever, is bullshit? (Note that the specifics of reading/calculating/rendering may vary greatly, but clearly that has to be the data dependency.)
enum Bool { True, False, FileNotFound };

#14 zzzz....   Members   -  Reputation: 84

Like
-5Likes
Like

Posted 31 January 2014 - 03:31 AM

 

Well if you really need to know that you are doing authentication from a certain place you are "screwed".

I think you got it backwards. To be able to DO authentication, the network-receiving part needs to know that it's talking to a network, and needs information like what the remote IP address is.

Anyone can hack your binary

I don't understand why you're discussing clients at all. The client does not have multiple networked nodes to use, so I don't understand how network-versus-local-node transparency would help you there at all.

To the BS I found this at the top  

1) read inputs 2) calculate simulation 3) render graphics

 

Just to make it clear: You state that you think the game structure from almost every real-time game, ever, is bullshit? (Note that the specifics of reading/calculating/rendering may vary greatly, but clearly that has to be the data dependency.)

 

 

If you don;t understand the client thing no big deal.

 

No. You have obviously not worked on a commercial game as most have input in a separate thread at a high frequency. What you describe here is the beginner way of programming straight out of high school class. The OP asked about async design and you did give him bonafide BS advice and keep on doing it.

 

But please keep on the pretenses. Maybe like that you feel better. Do not try to help others, just make yourself be right. That's what is important after all, not telling how to do thing in a good way, just that people accept the crappy advice as gold. After all if people don't respect the authority of the moderator, what would happen? Oh gosh.

 

Since you have no intention in bending your mind to understand I simply stop this for my own minimisation of time waste.

 

spinningcube


Edited by jbadams, 13 April 2014 - 01:42 AM.
Restored post contents from history.





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS