C#/XNA socket programming question

Started by
6 comments, last by MarlboroKing 11 years, 1 month ago
Hi all. I didn't want to make a post about this but google has proven rather unhelpful.
I am in school for Game Programming and I am working on an XNA based tech demo for a class. I am trying to teach myself UDP socket programming with the goal of eventually making multiplayer game. Right now my grasp on socket programming is rudimentary at best though through example I have written a simple chat server/client. I would love to be able to use Lidgren for this but as it is a tech demo I need to write all the code myself, which I am fine with.
The end goal of my project is to have two sprites moving around the screen, one separate clients controlled by a server. Right now, I just want my server to tell me which key I am sending it. As of now, all I get is:
Received a broadcast from 192.168.x.xxx:59145
Message: NONE
On any key press, which at least means it is getting to my server.

KeyboardState keyboardState = new KeyboardState();
 
            if (Keyboard.GetState().GetPressedKeys().Length > 0)
            {
                // Default movement is none
                if (!keyboardState.IsKeyDown(Keys.W) || !keyboardState.IsKeyDown(Keys.A) || !keyboardState.IsKeyDown(Keys.S) || !keyboardState.IsKeyDown(Keys.D))
                {
                    MoveDir = MoveDirection.NONE;
                }
                if (keyboardState.IsKeyDown(Keys.W))
                {
                    MoveDir = MoveDirection.UP;
                }
                if (keyboardState.IsKeyDown(Keys.A))
                {
                    MoveDir = MoveDirection.LEFT;
                }
                if (keyboardState.IsKeyDown(Keys.S))
                {
                    MoveDir = MoveDirection.DOWN;
                }
                if (keyboardState.IsKeyDown(Keys.D))
                {
                    MoveDir = MoveDirection.RIGHT;
                }
 
                byte[] send_buffer = Encoding.ASCII.GetBytes(MoveDir.ToString()); 
 
                try
                {
                    sending_socket.SendTo(send_buffer, sending_end_point);
 
                }
                catch (Exception send_exception)
                {
                    exception_thrown = true;
                    Console.WriteLine(" Exception {0}", send_exception.Message);
                }
                if (exception_thrown == false)
                {
                    Console.WriteLine("Message has been sent to the broadcast address");
                }
                else
                {
                    exception_thrown = false;
                    Console.WriteLine("The exception indicates the message was not sent.");
                }
            }
That is the chunk of relevant code inside my XNA update function. MoveDir is declared at the start of my file as an instance of Movedirection.

  // Move direction enumerator
    enum MoveDirection
    {
        UP,
        DOWN,
        LEFT,
        RIGHT,
        NONE
    }
is my enum
I don't think it matters but my server code is:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
 
namespace Server
{
    public class UDPListener
    {
        private const int listenPort = 11000;
 
 
        static int Main(string[] args)
        {
            bool done = false;
 
            UdpClient listener = new UdpClient(listenPort);
            IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, listenPort);
            string recieved_userName;
            string received_data;
            byte[] receive_name_array;
            byte[] receive_byte_array;
            receive_name_array = listener.Receive(ref groupEP);
           
 
            try
            {
                while (!done)
                {
                    
                    receive_byte_array = listener.Receive(ref groupEP);
                    Console.WriteLine("Received a broadcast from {0}", groupEP.ToString());
                    received_data = Encoding.ASCII.GetString(receive_byte_array, 0, receive_byte_array.Length);
                    Console.Write("Message: {0}\n", received_data);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
            listener.Close();
            return 0;
        }
 
    }
}
Anyways, like I said I am new to this and trying to figure it all out. My level of experience is a college level data structures course. XNA does not support "true keyboard input" (http://dream-forever.net/Blog/2011/08/29/getting-true-keyboard-input-into-your-xna-games/) and I know there HAS to be a simpler way to get this done besides what is suggested in that post. I don't need it to be pretty, just to work.
If this is a dumb question, I apologize for wasting peoples time. Thanks for any help.
Edit: I was unsure if I should post this here or in the networking section, since it has XNA specific stuff I chose here, please move if it is out of place.
cout << "hi!n";
Advertisement

From past experience I don't think there is a easy way to getting actual keyboard char presses from the keyboard. Last time I successfully achieved it on XNA I had to use unmanaged code (PInvoke) etc... However, the Keyboard.GetState() is fast enough if you call it at the beginning of each tick/update since its as fast as the game is simulating.

Anyway I'm guessing your question is how to make two sprites move on the screen, 1 sprite per player? Your server will need a list to store all clients that connect, you will also need to remember the IP for each client as a way as separating up different clients (or you could just use TCP which I think would be easier for beginning networking).

Then you need to do all these required things:
- Handle disbatching of new client connections (add them to the list), which then means...

- Once a client connects you need to send data to all other currently connected clients to indicate that someone has connected (so the existing clients can add the new player to their list aswell).

- Handle the disconnections/time outs of connections, and do the same as the point above, but to remove clients from lists.

- Handle receiving messages, and then relaying the messages to all connected clients.

Hey Mr.C,

I wrote the article you referred to in your post. You really only need to worry about XNA's input limitations if you are building some sort of text editor or real-time text input within XNA, such as an in-game password field or entry form. XNA's input handling system was built with game development in mind, not the sort of 'raw-text-input' scenario I was needing at the time, that's why one has to hack around a bit when requiring this sort of precision. That said, you appear to be using the Keyboard class to move an object around and not for text input. If that's the case your code should run fine, as that's the type of stuff it's built for!

If you don't mind me commenting on your code a bit more...

It would be more efficient to send a single char over the network rather than an entire string. For example, if the user pressed 'W', simply send the byte 'W' to your server. Encoding.ASCII.GetBytes(MoveDir.ToString()) is sort of a nightmare (in terms of garbage collection) to put in the update loop. It likely won't have too big of an impact on the PC (though running similar code on the Xbox would be devastating) but these sort of things can add up over time and eventually lead to general performance issues.

The end goal of my project is to have two sprites moving around the screen, one separate clients controlled by a server. Right now, I just want my server to tell me which key I am sending it. As of now, all I get is:

Received a broadcast from 192.168.x.xxx:59145
Message: NONE

I just realized this. My derp.

You have a logical error in this if statement:

if (!keyboardState.IsKeyDown(Keys.W) || !keyboardState.IsKeyDown(Keys.A) || !keyboardState.IsKeyDown(Keys.S) || !keyboardState.IsKeyDown(Keys.D))

You are saying, if the W is not down or A is not down or S is not down or D is not down, then say we are not moving. You likely want this to be AND. If W And A And S And D are not down THEN send None. As it is now you will have to press WASD all at once to get something other than NONE to be sent.

The main problem though:

KeyboardState keyboardState = new KeyboardState();

should be

KeyboardState keyboardState = Keyboard.GetState();

Thank you both for your replies, I will add the fixes and see what I end up with. Although TCP might be "easier" for this, my Prof. wants me to use UDP. Sadly I am at his mercy :) Thanks again.

cout << "hi!n";

From past experience I don't think there is a easy way to getting actual keyboard char presses from the keyboard. Last time I successfully achieved it on XNA I had to use unmanaged code (PInvoke) etc... However, the Keyboard.GetState() is fast enough if you call it at the beginning of each tick/update since its as fast as the game is simulating.

Anyway I'm guessing your question is how to make two sprites move on the screen, 1 sprite per player? Your server will need a list to store all clients that connect, you will also need to remember the IP for each client as a way as separating up different clients (or you could just use TCP which I think would be easier for beginning networking).

Then you need to do all these required things:
- Handle disbatching of new client connections (add them to the list), which then means...

- Once a client connects you need to send data to all other currently connected clients to indicate that someone has connected (so the existing clients can add the new player to their list aswell).

- Handle the disconnections/time outs of connections, and do the same as the point above, but to remove clients from lists.

- Handle receiving messages, and then relaying the messages to all connected clients.

I was wondering, since UDP is connection-less how do you store new connections? I haven't found a clear example of this. With TCP there is a connection made which you could store in a struct and/or put into a list easily.

cout << "hi!n";

It's more of just "I was here the whole time".

I generally found that a server will have a setup of all the packets clumped together. This is to help ease the need to rewrite your buffer again and again.

With that being said, most have a ID set to a "OnConnect" packet that your client must send on initialization in order to get inside. EG;

ClientA -> OnSetupInternals() -> RequestConnectionToServer(maybeDataHere)

Server -> OnRequestConnectionToServer(maybeDataHere) -> IsClientAlreadyConnected() -> AddToList() -> ResponseConnectionToServer(RESULT_OK)

This topic is closed to new replies.

Advertisement