Jump to content
  • Advertisement

Ian Reed

Member
  • Content Count

    12
  • Joined

  • Last visited

Community Reputation

183 Neutral

About Ian Reed

  • Rank
    Member

Personal Information

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. Ian Reed

    Questions about WaitHandles in C#

    Thanks for the advice. My code evolved around using the WaitHandle from BeginReceive and EndReceive, and I think that sent me down a confusing road. I've reworked things and I think the new setup will solve my important issues and be a lot cleaner. I'm now using WaitForWork and DoWork methods. Here is the rough code for the main thread: var server = new UDPServer(port); byte[] buffer = new byte[UDPServer.MAX_BUFFER_SIZE]; while (listening) { if (!server.WaitForWork(timeUntilNextTick)) OnTick(); else { IPEndPoint remoteEP; ConnectionOperation op; int read; while (server.DoWork(buffer, out read, out remoteEP, out op)) { if (op != ConnectionOperation.Receive) continue; // we could alternatively log a count of acks, keep alive, duplicate packets, etc. // otherwise we can read the payload // implementation elided } } } DoWork returns a bool telling me if there is more work because I only pass in one buffer, so it can only process one packet for me at a time, even if the first one it processes is an in order packet that makes previously cached out of order packets now ready for processing as well. Here are the rough implementations: public bool WaitForWork(int callerTimeout) { // returns true if work is available, false if the caller's timeout elapsed. int ackTimeout = GetTimeUntilEndOfAckGroupingWindow(); int timeout = Math.Min(ackTimeout, callerTimeout); bool usingAckTimeout = ackTimeout <= callerTimeout; if (mPendingReceiveAsyncResult == null) { var remoteEP = mAnyEP; mPendingReceiveAsyncResult = mSocket.BeginReceiveFrom(mReceiveBuffer, 0, mReceiveBuffer.Length, SocketFlags.None, ref remoteEP, null, null); } bool signalled = mPendingReceiveAsyncResult.AsyncWaitHandle.WaitOne(timeout); if (!signalled && usingAckTimeout) mTimeToSendAcks = true; // there is work to do if receive signalled, or the ack timeout elapsed. return signalled || usingAckTimeout; } public bool DoWork(byte[] writeBuffer, out int size, out IPEndPoint remoteEP, out ConnectionOperation op) { // returns true if more work can be done on a subsequent call. // attempt to handle any out of order packets that can now be processed if (mOutOfOrderPacketsCanBeProcessed) { // implementation elided } if (mTimeToSendAcks) { // implementation elided } if (mPendingReceiveAsyncResult.IsCompleted) { var tempRemoteEP = mAnyEP; int read = mSocket.EndReceiveFrom(mPendingReceiveAsyncResult, ref tempRemoteEP); mPendingReceiveAsyncResult = null; remoteEP = (IPEndPoint)tempRemoteEP; // process my reliable UDP headers, write the rest of the payload to the writeBuffer, and set mOutOfOrderPacketsCanBeProcessed and related member variables as needed. // implementation elided } // otherwise we were called when there was no work to be done, which is an invalid operation since you always want to be calling WaitForWork until it is true before calling DoWork. throw new InvalidOperationException("DoWork was called when there was no work to be done."); } public enum ConnectionOperation { // this is returned to callers of DoWork Connect, Disconnect, Receive, KeepAlive, Ack, OutOfOrder, Duplicate, Invalid, } I think that is going to work pretty well for me, and feels a lot cleaner than the previous direction I was headed. Thanks again for the feedback. I've been following your posts for a long time and they have taught me a lot. Edit: I'm blind and using a screen reader, and from what I can tell my code tags didn't work correctly. Sorry if the formatting is bad for you. I'll try to figure out what I did wrong. Edit2: Ah, I was supposed to use square brackets, not angle brackets.
  2. I am using C#. I have a UDPServer class with methods called BeginWork and EndWork. When BeginWork is called, I want it to detect type A work that can be completed immediately, type B work that can be completed after a timeout, and type C work that can be completed when a WaitHandle is set (the wait handle is retrieved from a built in .NET BeginReceive call on a socket.) I am trying to avoid using an additional thread. I have a main game server thread that wants to wait for network related work that needs to be done, in which case it would call EndWork, or just wait for its own timeout value to elapse and then call its own Update method. I would like to have BeginWork return a WaitHandle so that the main thread can call WaitOne or WaitAny, passing in its own timeout. How can I combine the check for types A, B, and C work into a single WaitHandle that can be returned to the main thread? A few more details just in case they are helpful: My UDPServer class is implementing reliable UDP. The type A work is detecting out of order packets that can now be processed because the in order packets leading up to them were finally received and processed. The type B work is needing to send ack packets when we have waited long enough to group a few acks, but not so long that the other side will think it needs to resend an unacknowledged packet. The type C work is just waiting for additional packets to come in. If I create a MyWaitHandle class to wrap the logic of waiting on the network timeout and receive WaitHandle, then the main thread can not combine that with any other wait handles. It might work for this particular scenario, but if the main thread ever needs to wait on something else at the same time then it won't work. I would rather not expose multiple WaitHandles and a timeout value to my caller, because that complicates their logic for something that should be self contained. They should only need a WaitHandle that they can wait on to know if any work is ready to be done in UDPServer or not. I might be able to make it work by passing callbacks to BeginReceive and a System.Threading.Timer. Then having those callbacks set a ManualResetEvent that corresponds to the WaitHandle I return, along with some internal state to note which work is ready. This does introduce some ThreadPool threads, which is not as bad as me creating a dedicated thread, but the synchronization logic still seems to get hairy. I guess I wanted something like this to exist: var waitHandle = WaitHandle.CreateWaitAny(waitHandleArray, timeout); But then I'd still need to internally determine which of the waitHandles was ready, or if the timeout occurred, so maybe that isn't really the right solution either. Thanks for any help.
  3. Ah, I see. Yes, that makes sense. The WaitHandle on IAsyncResult does seem pretty helpful. I'll play with this for a bit and see if I hit any other issues. Thank you very much.
  4. Thanks, but I am wanting to both wait on incoming socket data and messages from other threads. WaitHandle.WaitAny and Task.WhenAny work just as well as my BlockingCollection.TryTake solution for receiving messages from other threads, but none of them help me wait on incoming socket data at the same time. Is there a way to get a WaitHandle that will trigger when a socket has data to be read?
  5. I am working in C#. I am creating a multiplayer game/chat server. The game server can run multiple games at a time, with no more than 10 participants in each game. I intend to only use 1 "game thread" per CPU core, which means a single game thread will run the tick logic for multiple games, then go to sleep until the next tick. In addition to these game threads, I would like to create a single "network thread" within this multiplayer server that can listen for new TCP connections, incoming TCP data, incoming UDP data, and also receive messages from the game threads which are running tick logic periodically. The network thread will pass incoming data to the game thread that is dealing with that player endpoint. It needs to receive messages from the game threads so they can tell it to close a socket for a disconnected player, possibly to send outgoing data through the network thread, and probably other communication I haven't thought of yet. MSDN says the C# Socket class is thread safe, so perhaps the game threads can send out directly on the sockets, rather than passing their outgoing messages to the network thread to be sent out. Should I have the game threads pass their outgoing messages to the network thread, or just send them out directly on the sockets? Game communication is handled over TCP. I need the incoming UDP data because the game server will also support voice chat between friends (a chat party), linked to a specific game, or linked to a specific team within a game. I've already got mic capture, encoding to Opus, transmitting via UDP, receiving via UDP, decoding from Opus with its packet loss concealment and forward error correction, and voice sample playback working in a separate test application. I could send UDP voice data directly between peers, but I don't want to deal with NAT punch through between peers yet, plus it seems like while sending directly between peers could give lower latency, it also has a trade off of sending more upstream data from each client, over the client's possibly podunk network. The way I usually handle cross thread communication is to use a BlockingCollection which external threads add their messages to, and the receiving thread calls either Take or TryTake, depending on whether it wants to specify a timeout. For instance, a game thread would call TryTake so it can receive messages from the network thread, but also specify a timeout so it wakes up for the next tick. For communication where the sender actually wants to block waiting for a result, I group the message with a ManualResetEvent so the sending thread can wait on it until the receiving thread has set the result and triggered it. I want the network thread to be able to call Socket.Select passing in all its sockets, so it can respond as soon as a new TCP client has connected, or TCP or UDP data is available for reading. But I also want it to be able to handle messages coming from game threads immediately. Is there a way to wait on both incoming data from sockets, and incoming messages from other threads? I currently plan on calling Socket.Select passing in the bound TCP listening socket, the bound UDP socket, and all TCP sockets created by new connections. For reference, its prototype is: public static void Select(IList checkRead, IList checkWrite, IList checkError, int microSeconds); I will pass them in the checkRead parameter, and pass an infinite timeout. (Unless I end up needing to wake up in order to check for messages from other threads.) It seems like I should also pass them in the checkError parameter, so I can handle connection errors immediately. Will Socket.Select return when a new UDP packet comes in if I have bound that UDP socket to an EndPoint? If not, do you have a recommendation for how to wait on both TCP and UDP data at the same time? Thanks.
  6. I see what you mean. There are definitely a lot of network factors outside of my control. I just wanted to make sure the asset downloader that is within my control would not be the cause of a poor gameplay experience, but perhaps I am over thinking it. Thanks for the advice.
  7. @swiftcoder, thanks. I guess I was wondering how much that margin needs to be, whether that be a percentage of total bandwidth, or just a set amount. I intended on subtracting the exact bandwidth requirements of the gameplay from the total available bandwidth as you say. Then the decision is how much of that remaining bandwidth can I use for downloading assets in the background without negatively affecting the gameplay traffic. I suppose my main concern is that using 90% of the available bandwidth would result in a greater than linear increase in lost packets as compared with using 10%, even though both are clearly under 100% with a margin. My hope would be that I can expect a linear increase (or less) in lost packets as I saturate the the available bandwidth. If the increase in lost packets is more than linear, then that might make it worth staying at much lower bandwidth usage for the background asset downloader, even though more bandwidth is available. I hope that better explains my concern. Thanks for the help. @khawk, thanks, I will probably be using Digital Ocean "spaces" / object storage. They may have a rate limit setting built in to their admin interface. I prefer to limit on the client, since I'm writing the client anyway, and each player's available bandwidth will be different, so they can rate limit at different speeds based on that. The rate limiting is to protect the client's gameplay experience while still downloading assets in the background, not to protect the server from being overwhelmed.
  8. Thanks, that makes sense. I expect this background asset downloader to run at the same time as network game play. If I know the max download bandwidth for a client, is there a percent threshold I should keep the combined asset download and game play traffic under in order to avoid lost game play packets? Is running an asset downloader in the background a bad idea? I want to utilize the extra bandwidth by downloading assets ahead of time and not making the player wait at download screens as often or as long, but not if it will negatively affect network game play.
  9. Ok, thanks to both of you. I did some reading on the TCP receive window, window scaling, TCP congestion control, and HTTP ranges. Wikipedia said: Windows Vista and Windows 7 have a fixed default TCP receive buffer of 64 kB, scaling up to 16 MB through "autotuning", limiting manual TCP tuning over long fat networks. https://en.wikipedia.org/wiki/TCP_window_scale_option I am using C# and see that the HttpWebRequest.ServicePoint.ReceiveBufferSize is set to -1 after constructing a new HttpWebRequest (despite the method documentation saying the default is 8192). I assume the -1 indicates "unset" and allows the operating system to auto tune it. My game clients will be run on Windows, and the download servers and game servers will be run on linux. Would I be better off letting the OS auto tune the window scale / receive buffer size, rather than choosing it myself?
  10. I'd like my game client to download some extra assets in the background, but I want it to rate limit itself so it has less effect on any other network traffic. I imagine I could rate limit on the server side through a web server setting, but I'd rather the client gets to choose its own rate limit so it can also have times when it downloads at a faster rate. What is the right way to do this? I'll be using HTTPS for the downloads. Is there some way to inform the TCP or HTTP stack that you only want to receive data at a certain speed for a particular connection? Or do I have to just slow down the rate at which I am reading it from the HTTP stream? Slowing down my rate of reading seems like I am relying on TCP fallback logic to handle my slow processing speed, rather than informing it up front that I want to be a good citizen and use less bandwidth.
  11. Kylotan or hplus0603: If you have the clients send their own timestamp, does that mean you would also do some server rewind and replay? Or would you run the server's timeline behind the clients so that incoming timestamps have usually not occurred on the server's timeline yet? Or maybe another way of saying that is having the clients timestamp their actions slightly in the future?
  12. Blind gamers still enjoy muds, though you will need to somehow get your mud to stand out from the others already available. One of the most popular muds among blind gamers is Alter Aeon. A big part of its popularity is due to a client called Mush-Z. Mush-Z is the MushClient.exe cclient preconfigured with addons to setup text to speech (TTS) for screen readers, and many scripts to add sounds in place of or in addition to text, music triggered by entering a certain area of the mud, and hot keys to make navigation faster and getting stats about yourself or your group faster. The sounds and music really help the immersiveness a blind player feels. Blind players only get information through sound, music, and TTS anyway, so the lack of graphics does not bother them. Find info about Mush-Z here: http://www.blindsoftware.com/ Cyber Assault and Empire mud are a couple others that have been popular among blind gamers. Iron Realms is a company that develops muds commercially: https://www.ironrealms.com/ They have a heavy focus on role playing, meaning players must remain in character when chatting and interacting with others. Posting on the audiogames.net forum is a good way to advertise to blind players: http://forum.audiogames.net/ Blind gamers are a niche market, but also one that is starved for good games. I'm not saying that building a mud that blind players can play will be commercially successful, just that if you are going to build a mud as an exercise anyway, blind players are a possible target audience.
  13. Ian Reed

    Java Open AL questions

    I'm personally using C# instead of Java to access OpenAL, but my answers should still be relevant. AL10.alSourcei(sources.get(index), AL10.AL_BUFFER,buffers.get(index)); That line associates a source with a buffer. A buffer holds all the sound data while a source is the object that holds position, gain, and other properties. They are separated so you can load sound data into a single buffer, then setup multiple sources to use it without needing to have the sound data in memory more than once. It doesn't create the source, in C# I use AL.GenSource() to create the source, then call the equivalent of your line to associate it with a buffer I've already loaded with sound data. AL10.alSource3f(sources.get(index), AL10.AL_POSITION, 0,0,0); That line sets the position of the source. You mentioned not wanting to load sources until a particular level is loaded. You'd want to do this for buffers as well since they take much more memory than sources do. When unloading a level you should also delete the sources and buffers it used unless you will re-use them on the next level. Not sure about ALint and ALfloat. The API I use in C# just has me pass int and float instead. HTH  
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!