Networking support


#1

Any news on this? Maybe an integration with RakNet or even the lower-level enet?


#2

I’m not sure what your experience or use-case is, but I have found the Lidgren Network Library to be excellent.

It is entirely written in C# and very well engineered, making integration and use very simple. It is also completely open source, using the MIT license.

One thing I would hate to see if more un-managed libraries used in Xenko.


#3

Never heard about it, but will look into it. Thanks! But, as far as integration goes, i was thinking about something like this: http://docs.unity3d.com/Manual/UNet.html

Writing an enet/RakNet wrapper is easy and could be done in a few days. That’s actually what i did. But i wanted a tighter integration into the engine, not just having networking capability. Guess i have explained myself poorly.

So, to sum it up, i wanted to know when high-level networking features are going to land on Xenko.


#4

I’ll have to leave it to the devs to talk about their own networking plans, but I would like to say that I hope they don’t use an unmanaged (C/C++) library for it. While integrating an unmanaged library might be easy to implement they are much more difficult to debug and extend.


#5

I had a crack at this here …

http://answers.xenko.com/questions/1917/networking-within-xenko.html

… the current answer implies that the reason a couple of key pieces are missing is that the classes in question are not meant for this use however I would be happy to offer up my start point as a suggestion here or even clone the repo and build this support myself.

I already have a working socket server with the desired functionality on the server side running on .NET Core 1.0 so using that would be a huge win for me.

I had this working in unity but it would break if I used anything other than a windows build as it was originally sat on System.Net.Sockets, with what Xenko already has and maybe a few hours of dev we can lay the foundations so that devs can build their own networking infrastructure on top of a cross platform socket object.

As you can see, in my case I have built a client based on a 5 byte header for each message, i’m sure if the dev team were up for it I could improve this further to make it more generic for all use cases (e.g. using attributes or something to depict what properties mean with regards to the message) or subclass it for my specific case allowing others to use the base that would just serve up the raw bytes received.

I have an idea about how we could do this using generics already so I could rewrite my client to meet probably everyone’s needs if SimpleSocket or some equivalent was exposed as an entry point that the Xenko platform would support on all platforms going forward.


#6

This is more of a FYI hope it helps someone. Below is a copy of our networking client/server setup. This is just a start to our project but we have accomplished networking for a windows 10/server 08 setup with the following file. If anyone has improvement to this that would be awesome.

TCP Receiver

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

namespace Game_Armament.Network.TCP
{
    public class Receiver : IDisposable
    {
        private int _Port = 8000;
        private IPAddress _IPAddress = IPAddress.Parse("127.0.0.1");
        private TcpListener _Listener = null;
        private Socket _Socket = null;

        public Receiver()
        {
        }

        public void Start()
        {
            Setup();
        }

        private void Setup()
        {
            _Listener = new TcpListener(_IPAddress, _Port);
            _Listener.Start();
            Task.Factory.StartNew(Listener);
        }

        private async void Listener()
        {
            _Socket = await _Listener.AcceptSocketAsync();

            if (_Socket != null)
            {
                byte[] b = new byte[100];
                int k = _Socket.Receive(b);
                Console.WriteLine("Recieved...");
                for (int i = 0; i < k; i++)
                {
                    Console.Write(Convert.ToChar(b[i]));
                }

                ASCIIEncoding asen = new ASCIIEncoding();
                _Socket.Send(asen.GetBytes("The string was recieved by the server."));
                Console.WriteLine("\nSent Acknowledgement");

                //await Task.Factory.StartNew(Do);
            }
        }

        //public async void Do()
        //{
        //    var buffer = new byte[4096];
        //    var bytesRead = await this.networkStream.ReadAsync(buffer, 0, buffer.Length);

        //    Console.WriteLine("Doing some awesome work that takes 5 seconds.");
        //    Thread.Sleep(5000);
        //    Console.WriteLine("Finished doing work.");

        //    await this.networkStream.WriteAsync(buffer, 0, bytesRead);
        //    await this.networkStream.FlushAsync();
        //}

        public void Dispose()
        {
        }
    }
}

TCP Sender

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

namespace Game_Armament.Network.TCP
{
    public class Sender
    {
        private int _Port = 8000;
        private IPAddress _IPAddress = IPAddress.Parse("127.0.0.1");
        private TcpClient Client = new TcpClient();

        public Sender()
        {
        }

        public void Send()
        {
            Setup();
            Data();
        }

        private void Setup()
        {
            Client.ConnectAsync(_IPAddress, _Port);
        }

        private void Data()
        {
            using (var stm = Client.GetStream())
            {
                ASCIIEncoding asen = new ASCIIEncoding();
                byte[] ba = asen.GetBytes("Hello World");

                stm.Write(ba, 0, ba.Length);

                byte[] bb = new byte[100];
                int k = stm.Read(bb, 0, 100);

                for (int i = 0; i < k; i++)
                {
                    Console.Write(Convert.ToChar(bb[i]));
                }
            }
        }
    }
}

UDP Receiver
https://social.msdn.microsoft.com/Forums/en-US/92846ccb-fad3-469a-baf7-bb153ce2d82b/simple-udp-example-code?forum=netfxnetcom

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

namespace Game_Armament.Network.UDP
{
    public class Reciever
    {
        private int _Port = 7000;
        private IPAddress _IPAddress = IPAddress.Parse("127.0.0.1");
        IPEndPoint _IPEndPoint = null;

        public Reciever()
        {
        }

        public void Start()
        {
            Setup();
        }

        private void Setup()
        {
            _IPEndPoint = new IPEndPoint(_IPAddress, _Port);
            Listener();
        }

        private async void Listener()
        {
            bool done = false;
            UdpClient Listener = new UdpClient(_Port);
            string received_data;
            while (!done)
            {
                Console.WriteLine("Waiting for broadcast");
                UdpReceiveResult Result = await Listener.ReceiveAsync();
                Console.WriteLine("Received a broadcast from {0}", _IPEndPoint.ToString());
                received_data = Encoding.ASCII.GetString(Result.Buffer, 0, Result.Buffer.Length);
                Console.WriteLine("data follows \n{0}\n\n", received_data);
            }
        }
    }
}

UDP Sender

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

namespace Game_Armament.Network.UDP
{
    public class Sender
    {
        private int _Port = 7000;
        private IPAddress _IPAddress = IPAddress.Parse("127.0.0.1");
        private Socket _Socket = null;
        IPEndPoint _IPEndPoint = null;

        public Sender()
        {
        }

        public void Send()
        {
            Setup();
            Data();
        }

        private void Setup()
        {
            _Socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            _IPEndPoint = new IPEndPoint(_IPAddress, _Port);
        }

        private void Data()
        {
            while (true)
            {
                byte[] Buffer = Encoding.ASCII.GetBytes("Hello WOrld");
                _Socket.SendTo(Buffer, _IPEndPoint);
            }
        }
    }
}

SQL Server Side Only

using System.Data.SqlClient;
using System.Data;

namespace Website.Thou.Curator.Data
{
    public class SystemDataBaseData
    {
        private string _Connection = "";

        public SystemDataBaseData()
        {
            _Connection = "Data Source=127.0.0.1, 1433; Initial Catalog=TCSDB; User Id=curator; Password=logitech; Pooling=true; Max Pool Size = 300";
        }

        public DataTable Query(SqlCommand sqlCommand)
        {
            DataTable dataTable = new DataTable();
            SqlDataAdapter sqlDataAdapter = null;

            using (SqlConnection sqlConnection = new SqlConnection(_Connection))
            {
                sqlCommand.Connection = sqlConnection.CreateCommand().Connection;
                sqlConnection.Open();
                sqlDataAdapter = new SqlDataAdapter(sqlCommand);
                sqlDataAdapter.Fill(dataTable);
                sqlConnection.Close();
            }

            return dataTable;
        }

        public int Update(SqlCommand sqlCommand)
        {
            int rowAffected = 0;
            using (SqlConnection sqlConnection = new SqlConnection(_Connection))
            {
                sqlCommand.Connection = sqlConnection.CreateCommand().Connection;
                sqlConnection.Open();
                rowAffected = sqlCommand.ExecuteNonQuery();
                sqlConnection.Close();
            }

            return rowAffected;
        }

        public string Insert(SqlCommand sqlCommand)
        {
            using (SqlConnection sqlConnection = new SqlConnection(_Connection))
            {
                sqlCommand.Connection = sqlConnection.CreateCommand().Connection;
                SqlParameter sqlParameterId = new SqlParameter("@Id", SqlDbType.Int);
                sqlParameterId.Direction = ParameterDirection.Output;
                sqlCommand.Parameters.Add(sqlParameterId);
                sqlCommand.CommandText += " SET @Id = SCOPE_IDENTITY()";

                sqlConnection.Open();
                sqlCommand.ExecuteNonQuery();
                sqlConnection.Close();

                return sqlParameterId.Value.ToString();
            }
        }

        public void Delete(SqlCommand sqlCommand)
        {
            using (SqlConnection sqlConnection = new SqlConnection(_Connection))
            {
                sqlCommand.Connection = sqlConnection.CreateCommand().Connection;
                sqlConnection.Open();
                sqlCommand.ExecuteNonQuery();
                sqlConnection.Close();
            }
        }
    }
}

James Fleming
http://www.thoucurator.com