my MMO SERVVER ISSUES!

okay so i have a game, all i want to do is sync the position. it will have one map loaded at a time, and to syncronize the position of the player on the map

currently i am only worried about working with TWO players, not really MMO, but its a start

okay so i have written my own networking code for the client and the server. i know it may not be very secure, but its a start. anyways.

when i play locally i get NO lag. when i have a remote player playing, his game lags like mad, but locally, it does not.

i was thinking it was my code but then again, i cannot figure out why it is lagging for him.

everything works, and works like this

client sends awake command

server checks if user exists, if does not, adds to list, either way, send play players ID number

on a timer, the client will send a command saying SETPOSITION to update the players position on the server

every remote player on the client, his script on the client, calls for a GETPOSITION command on the server, the server sends back the position set by set position. this is called every x seconds on the client from the remote controlelr script

everything works, when i play locally there is no lag. there server seems to receiving the remote players commands super slow. i changed the timers to send really fast, changed the times i sleep in between commands to really small intervals.

still locally it received data much faster, i completley understand why, but my server is getting the player updates very slowly.

i also am running a timer to update the active player list incase i need to instantiate a remote player and his model. i set this on a longer running timer, every 5 seconds.

the server is receiving the data one after anotjer,

GETPLAYERLIST
SETPOSITION
GETPOSITION (for each player)

with out fail, though timers are set to different times.

here is the remote controller script :

using UnityEngine;
using System.Collections;


public class MyRemoteController : MonoBehaviour {	
	private NetworkManager networkManager;
	public string playerName = "My Player";
	
	// Remote players position and rotation
	public Vector3 MyPos;
	public Quaternion MyRot;
	
		private const string NETWORK_MANAGER = "Network Manager";
	
	private float playerUpdateTimer = 0.0f;
	private float playerStartTimer = 0.0f;
	public float playerResetTimer = 1000.0f;
	
	// Use this for initialization
	void Start () {
		// Find the network manager
		networkManager = GameObject.FindGameObjectWithTag(NETWORK_MANAGER).GetComponent<NetworkManager>();	
		if (networkManager == null)
			Debug.LogError("Cannot find the Network Manager!");
		
	}
	
	// Update is called once per frame
	void Update () {
		
		// Get data from server on this mobs position, on a timer
		if (playerUpdateTimer <= playerResetTimer)
		{	
			char[] delimiterChars = {  '|' };
			string loc = networkManager.SendGetLocation(name);
		 	string[] position = loc.Split(delimiterChars);

			int i = 0;
			foreach (string word in position)
			{
				if (word != "GETLOCATION")
				{
			
					float myword = (float)System.Convert.ToSingle(word);
					if (i==1)
						MyPos.x = myword;
					else if (i==2)
						MyPos.y = myword;
					else if (i==3)
						MyPos.z = myword;
					else if (i==4)
						MyRot.x = myword;
					else if (i==5)
						MyRot.y = myword;
					else if (i==6)
						MyRot.x = myword;
				}
				i++;
			}
			playerUpdateTimer = 0.0f;
			playerStartTimer = Time.time;
		}
		else // Update Timer
		{
			playerUpdateTimer = Time.time - playerStartTimer;
		}
		
		// IS the player moving? if he is..
		if (PlayerIsMoving())
		{
			// Animate Moving
			animation.CrossFade("run");
			
			// Move - // GET THE REMOTE PLAYER INDEX AND ITS LOCATION  POSITION
			//           AT ITS INDEX
			transform.position = MyPos;
			transform.rotation = MyRot;	
			
		}
		// If he isnt moving
		else
		{
			// Animate Idle
			animation.CrossFade("idle");	
		}
	}
	
	// Has player changed location in X seconds
	// TODO
	public bool PlayerIsMoving () {
		if (true)
			return true;
		else 
			return false;
	}
}

here is the networjing engine i designed for the client

using UnityEngine;
using System.Collections;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System;
using System.Text;

public class NetworkManager : MonoBehaviour {
	private GameManager gm;
	
	public string ServerAddress = "127.0.0.1";
	public int ServerPort = 3000;
	public int ThreadSleepTime = 12;
	
	private TcpClient client;
	private IPEndPoint serverEndPoint;
	private NetworkStream clientStream;
	
	private const string GAME_MANAGER = "Game Manager";
	
	// Use this for initialization
	void Start () {
		// Find the game manager
		gm = GameObject.FindGameObjectWithTag(GAME_MANAGER).GetComponent<GameManager>();	
		if (gm == null)
			Debug.LogError("Cannot find the Game Manager!");
		
		// Create our client, endpoint, and then connect
		client = new TcpClient();
		serverEndPoint = new IPEndPoint(IPAddress.Parse(ServerAddress), ServerPort);
		client.Connect(serverEndPoint);
		
		// Setup our stream..
		clientStream = client.GetStream();
	}
	
	// Update is called once per frame
	void Update () {
	
	}
	
	// Close the connection
	public void CloseConnection (){
		client.Close();
	}
	
	// Send Data to server
	public void SendDataToServer (string MyString){
				
		ASCIIEncoding encoder = new ASCIIEncoding();
		byte[] buffer = encoder.GetBytes(MyString);
		
		clientStream.Write(buffer, 0 , buffer.Length);
		clientStream.Flush();
		
	}
	
	// Send the Awake Command
	// Return True if we successfully connect
	public bool SendServerAwake (){
		try {
				MyPlayerController MYPC	= gm.GetPlayerController().GetComponent<MyPlayerController>();
				string MyString = "AWAKE" + MYPC.playerName +"@";
							
				ASCIIEncoding encoder = new ASCIIEncoding();
				byte[] buffer = encoder.GetBytes(MyString);
				
				clientStream.Write(buffer, 0 , buffer.Length);
				clientStream.Flush();
				Thread.Sleep(ThreadSleepTime);
			
				// Wait for the player list return
				byte[] message = new byte[4096];
		        int bytesRead;
				
				bytesRead = 0;
		
		        //blocks until a client sends a message
		        bytesRead = clientStream.Read(message, 0, 4096);
				
				//message has successfully been received
		        MyString = encoder.GetString(message, 0, bytesRead);
			
			
				char[] delimiterChars = {  '|' };
				string[] players = MyString.Split(delimiterChars);
				MYPC.myPlayerIndex = System.Convert.ToInt32(players[1]);
		
				
				return true;
		}
		catch {
		
				return false;
		}
	}
	
	// Send the Get Player List Command
	public string SendGetPlayerList (){
		string MyString = "GETPLAYERLIST@";		
		ASCIIEncoding encoder = new ASCIIEncoding();
		byte[] buffer = encoder.GetBytes(MyString);
		
		clientStream.Write(buffer, 0 , buffer.Length);
		clientStream.Flush();
		
		// Wait for the player list return
		byte[] message = new byte[4096];
        int bytesRead;
		
		bytesRead = 0;

        //blocks until a client sends a message
        bytesRead = clientStream.Read(message, 0, 4096);
		
		//message has successfully been received
        MyString = encoder.GetString(message, 0, bytesRead);
		
		Thread.Sleep(ThreadSleepTime);
		
		return MyString; // Return the player list
	}
	
	// Send the Get Player Location Command
	public string SendGetLocation (string playerName){
		
		string MyString = "GETLOCATION" + playerName  +"@";		
		ASCIIEncoding encoder = new ASCIIEncoding();
		byte[] buffer = encoder.GetBytes(MyString);
		
		clientStream.Write(buffer, 0 , buffer.Length);
		clientStream.Flush();
		
		// Wait for the player list return
		byte[] message = new byte[4096];
        int bytesRead;
		
		bytesRead = 0;

        //blocks until a client sends a message
        bytesRead = clientStream.Read(message, 0, 4096);
		
		//message has successfully been received
        MyString = "";
		MyString = encoder.GetString(message, 0, bytesRead);
		
		Thread.Sleep(ThreadSleepTime);
		
		return MyString; // Return the player list
	}

	// Send the Set Player Position Command
	// Return True if we successfully send
	public bool SendServerLocation (){
		try {
				MyPlayerController MYPC	= gm.GetPlayerController().GetComponent<MyPlayerController>();
				string MyString = "SETLOCATION" + MYPC.playerName + "|" + MYPC.transform.position.x + "|" + MYPC.transform.position.y + "|" + MYPC.transform.position.z
												 + "|" + MYPC.transform.rotation.x + "|" + MYPC.transform.rotation.y + "|" + MYPC.transform.rotation.z +"@";
							
				ASCIIEncoding encoder = new ASCIIEncoding();
				byte[] buffer = encoder.GetBytes(MyString);
				
				clientStream.Write(buffer, 0 , buffer.Length);
				clientStream.Flush();
		Thread.Sleep(ThreadSleepTime);;
				
				return true;
		}
		catch {
		
				return false;
		}
	}
}

and here is the class i designed, a multi threaded server in c#

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


namespace EuphonicServer
{
    class MyServer
    {
        
        private TcpListener tcpListener;
        private Thread listenThread;
        private IPEndPoint serverEndPoint;

        List<string> playerlist = new List<string>();
        List<string> playerlocation = new List<string>();
        
        public MyServer()
        {

            this.tcpListener = new TcpListener(IPAddress.Any,3000);
            this.listenThread = new Thread(new ThreadStart(ListenForClients));
            this.listenThread.Start();
        }

        private void ListenForClients()
        {
            this.tcpListener.Start();

            while (true)
            {
                //blocks until a client has connected to the server
                TcpClient client = this.tcpListener.AcceptTcpClient();

                //create a thread to handle communication 
                //with connected client
                Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
                clientThread.Start(client);
            }
        }

        private void HandleClientComm(object client)
        {
            TcpClient tcpClient = (TcpClient)client;
            NetworkStream clientStream = tcpClient.GetStream();

            byte[] message = new byte[4096];
            int bytesRead;

            while (tcpClient.Connected)
            {
                bytesRead = 0;

                try
                {
                    //blocks until a client sends a message
                    bytesRead = clientStream.Read(message, 0, 4096);
                }
                catch
                {
                    //a socket error has occured
                    break;
                }

                if (bytesRead == 0)
                {
                    //the client has disconnected from the server
                    break;
                }

                //message has successfully been received
                ASCIIEncoding encoder = new ASCIIEncoding();
                string TheString = encoder.GetString(message, 0, bytesRead);

                // Incase we received more that we are supposed to, check
                char[] delimiterCharz = { '@' };
                string[] positions = TheString.Split(delimiterCharz);
                string MyString = positions[0];


                /// PLAYER AWAKE FUNCTION
                /// ---------------------
                /// Add the player to the game world
                if (MyString.Substring(0, 5) == "AWAKE")
                {
                    // Remove Protocol String, MyString will now contain argument
                    MyString = MyString.Replace("AWAKE", "");

                    bool playerExists = false;

                    int i = 0;
                    /* Is the player already in the game world and get the index */
                    foreach (string player in playerlist)
                    {
                        if (player == MyString){
                            playerExists = true;
                            break;
                        }
                        else
                            i++;
                    }

                    // add player to the game world
                    if (!playerExists)
                    {
                        playerlist.Add(MyString);
                        playerlocation.Add("0|0|0|0|0|0");
                        i++;
                    }
                    
                        // Send the player the players index
                        byte[] buffer = encoder.GetBytes("AWAKE|" + i);
                        clientStream.Write(buffer, 0, buffer.Length);
                        clientStream.Flush();
                    
                        Console.WriteLine("AWAKE: " + MyString);
                }

                /// GET PLAYER LIST
                /// ---------------
                /// Send the client the current player list
                else if (MyString.Substring(0, 13) == "GETPLAYERLIST")
                {
                    string plist = "GETPLAYERLIST";

                    // Build a player list string
                    foreach (string player in playerlist)
                    {
                        plist = plist + "|" + player;
                    }
            
                        // Send the player list string we have built
                        byte[] buffer = encoder.GetBytes(plist);
                        clientStream.Write(buffer, 0, buffer.Length);
                        clientStream.Flush();

                    Console.WriteLine(plist);
                }

                /// GET PLAYER LOCATION
                /// ---------------
                /// Send the client the requested players location
                else if (MyString.Substring(0, 11) == "GETLOCATION")
                {
                    // Remove Protocol String, MyString will now contain argument
                    MyString = MyString.Replace("GETLOCATION", "");

                    // Construct the player location string
                    string plist = "GETLOCATION";

                    int i = 0;
                    foreach (string player in playerlist)
                    {
                        if (player == MyString)
                            plist = plist + "|" + playerlocation[i];

                        i++;
                    }

                    // Send the player list string we have built
                    byte[] buffer = encoder.GetBytes(plist);
                    clientStream.Write(buffer, 0, buffer.Length);
                    clientStream.Flush();

                    Console.WriteLine(MyString + ": " + plist);
                }

                 /// SET PLAYER LOCATION
                /// ---------------
                /// Set the requested players position
                else if (MyString.Substring(0, 11) == "SETLOCATION")
                {
                    // Remove Protocol String, MyString will now contain argument
                    MyString = MyString.Replace("SETLOCATION", "");

                    char[] delimiterChars = { '|' };
                    string[] position = MyString.Split(delimiterChars);

                    string myname = position[0];
                    string myposx = position[1];
                    string myposy = position[2];
                    string myposz = position[3];
                    string myrotx = position[4];
                    string myroty = position[5];
                    string myrotz = position[6];

                    // Set the players location

                    int i = 0;
                    foreach (string player in playerlist)
                    {
                        if (player == myname)
                            playerlocation[i] = myposx + "|" + myposy + "|" + myposz + "|" +myrotx + "|" + myroty + "|" + myrotz ; 

                        i++;
                    }

                    Console.WriteLine("SETLOCATION: " + MyString);
                }
            }
            
            Console.WriteLine("Client Disconnected");
            tcpClient.Close();
        }
    }
}

my connection is fast enough to run apache and server websites with ease. why is my server getting this informaton so slowly? what can i do to prevent this lag?

should i maybe put a flag and let the update function know we are still waiting for data so the thread does not pause?

im thinking this will stop thread from stopping while waiting for data on the players position, but it will not decrease the lag that i know about… but i am also using one tcp listener, and if i receive data for a command that another class is waiting for, it could bug out my system…

just looking for some pointers, please help

A few pointers:

You use one thread per client, this does not scale and creates a horrible over-head. It will just not work past a small number of connected clients. You need to look into something called IOCP (assuming you’re on Windows), more specifically look up a class called SocketAsyncEventArgs on MSDN.

Secondly, you’re sending text commands (as an actual text string) to the server, is not needed. The server does not need to know the name of the operation, you can just send a number between 0 and 255 (basically one byte) that designates what type of operation you want to perform.

Also the “GETPOSITION” command is redundant, this should not exist as the server should always pump the current position of every player to every other player at some set interval, say ten times per second.

It also looks like you’re running the server without the simulation having any pre-defined steps, as in - it will run as FAST as it can, consuming as much CPU as it needs. This does not scale and will not work for a real game or server, what you need is to “step” (as in move forward) the simulation at a set interval, somewhere between 10 and 30 times per second is usually ok. But do notice that an MMO usually has a step time of once or twice per second (for example, world of warcraft, only moves the simulation one step forward every second). The “delays” this cause you have to smooth over with nice prediction and client site eye candy.

Also, another note. I see that you’re using TCP, and while it’s perfectly viable for large MMO games (due to the QoS gurantuees of TCP/IP and a bunch of other things) you need to take great care with your prediction algorithms so they can handle very sudden jitter in the data flow. You also need to set the NODELAY flag on every socket so data is sent instantly and not buffered (look up Nagle’s algorithm on wikipedia).

Using UDP instead has it’s own pitfalls of course, and you need to do a lot of manual plumbing compared to TCP/IP.

Edit:

Also, about multi-threading (as I see you are using a lot of threads): Making an application multi-threaded is not something undertaken lightly, and even large MMOs (like EVE) are single-threaded and communicate using IPC (between processes) and TCP (between servers) instead.

It’s also a matter of scale, if your game (or at least one “zone”, like one continent in WoW) will be able to run on one machine you might get better performance using multi-threading, but it’s also a lot harder to get right.

But if your world will be very large, you will most likely not be able to run it on one server, and then you need to deal with the between-server communication problem anyway so you might aswell do it right from the start and not rely on multi-threading (at least not as much as you do right now).

Also, multi-threading the way you have done it (one thread per client) will likely not scale well with more then 32 players since the context switching on the CPU between 32+ active threads will consume a significant part of your CPUs power.

I think you can do yourself a major favor, and look into using Photon, uLink or some other of the plethora of networking solutions available for Unity. Good networking is hard, really really hard, use something built by experts.

yeah i do have some experience. it was late. what i did was moved my code for the timer to a coroutine and added flags to my sockets to catch the right size packets and time out receiving data. works like a charm now. now i need to work on interpolation :slight_smile:

thank yuou for the replu

if i have the remote players position, should i put the remote player object in its position and interpolate to the REAL location every X seconds?

how should i go about interpolation? i understand i should be using lerp but i am trying to figure out how? any more pointers? :slight_smile:

i have fully integrated networking now, i have no more issues. after finishing interpolation, and logging off, i have a fully working client/master server based networking system , or atleast a start to one.

thanks for the help!

edit: i have posted a topic in worked under progress. you can test my engine out or see information screenies. enjoy!

http://forum.unity3d.com/threads/118432-Euphonic-MMORPG