I have been going round in circles of how to ultimately synchronize the movement of 20 objects through 60 chosen points each. I had several options the main were
- Using sync vars to synchronize their progress between points (1 to 0), but that is open to lag and fairly intensive
- Using unity's built in network transform, but that would seem costly in terms of networking ability as the other device only need know the points and when to start
- First, sending the list of points, which i have successfully done and then making the devices start movement at a certain time at a fixed rate
So this is how i came to the conclusion that i needed to have a networked universal time accurate to around 0.1 seconds so that the users would see the same movement. Please if you see this as the wrong approach to this networking please let me know as I very much a beginner to networking.
So far I have tried three methods to get this synced time.
I used
System.DateTime
believing that it was an accurate and universal time but found variation beyond a second between devicesI tried to calculate the latency between the two devices so i could calulate and remove the device time variation, by
- (A) Using the in built method of
GetAveragePing(NetworkPlayer player);
but this was old and depreciated as the NetworkPlayer class seemed to be no longer functional and compatible - (B) Using another in built method of
GetCurrentRtt(int hostId, int connectionId, out byte error);
which returned 0 and again i believe may be depreciated - (C) By sending a message from the Server to the Client and back then dividing the time taken by two but this seemed inaccurate as I was trying to calculate latency between the server and client which was not the same as between the client and server so not exactly half
- (A) Using the in built method of
I tried to access a form of synced network time from a server, by
- (A) Using code from here to get a network time then worked out the System.DateTime difference between the network date time at a certain point so that i could synchronize them. The scripts which I used to do this are below.
- (B) Getting network time stamps from
GetNetworkTimestamp();
which I thought may be what i wanted
So for all of these methods have failed so today I am asking you;
Is there another method for syncing time?
Should one of these methods work, so have I gone wrong, in which case i can give further details on my issues and code used?
Is my approach to networking totally wrong, or at least mostly wrong and how do you suggest i achieve my desired goal?
Thank you very much for reading this I hope it is detailed and still clear and if i can help you advance your understanding of my question i will be very happy to assist. Thanks for any answers / enlightening comments.
Code for Networked Time
Script A (Gets Network Time)
using UnityEngine;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using UnityEngine.UI;
public class GetNetworkTime : MonoBehaviour {
public static System.DateTime NetworkTime()
{
//default Windows time server
const string ntpServer = "time.windows.com";
// NTP message size - 16 bytes of the digest (RFC 2030)
var ntpData = new byte[48];
//Setting the Leap Indicator, Version Number and Mode values
ntpData[0] = 0x1B; //LI = 0 (no warning), VN = 3 (IPv4 only), Mode = 3 (Client Mode)
var addresses = Dns.GetHostEntry(ntpServer).AddressList;
//The UDP port number assigned to NTP is 123
var ipEndPoint = new IPEndPoint(addresses[0], 123);
//NTP uses UDP
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.Connect(ipEndPoint);
//Stops code hang if NTP is blocked
socket.ReceiveTimeout = 3000;
socket.Send(ntpData);
socket.Receive(ntpData);
socket.Close();
//Offset to get to the "Transmit Timestamp" field (time at which the reply
//departed the server for the client, in 64-bit timestamp format."
const byte serverReplyTime = 40;
//Get the seconds part
ulong intPart = System.BitConverter.ToUInt32(ntpData, serverReplyTime);
//Get the seconds fraction
ulong fractPart = System.BitConverter.ToUInt32(ntpData, serverReplyTime + 4);
//Convert From big-endian to little-endian
intPart = SwapEndianness(intPart);
fractPart = SwapEndianness(fractPart);
var milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);
//**UTC** time
var networkDateTime = (new System.DateTime(1900, 1, 1, 0, 0, 0, System.DateTimeKind.Utc)).AddMilliseconds((long)milliseconds);
//return networkDateTime.ToLocalTime();
return networkDateTime;
}
// stackoverflow.com/a/3294698/162671
static uint SwapEndianness(ulong x)
{
return (uint)(((x & 0x000000ff) << 24) +
((x & 0x0000ff00) << 8) +
((x & 0x00ff0000) >> 8) +
((x & 0xff000000) >> 24));
}
}
Script B (Difference Calculator and Time Logger)
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
using UnityEngine.UI;
public class SyncTime2 : NetworkBehaviour {
public float serverTimeDif;
public float clientTimeDif;
GetNetworkTime GetNetworkTime;
GameObject time;
Text TimeLog;
// Use this for initialization
void Start () {
GetNetworkTime = GetComponent<GetNetworkTime>();
time = GameObject.Find("time");
TimeLog = time.GetComponent<Text>();
if (isServer)
{
serverTimeDif = (float)System.DateTime.UtcNow.TimeOfDay.TotalSeconds - (float) GetNetworkTime.NetworkTime().TimeOfDay.TotalSeconds;
StartCoroutine("DisplayTime", serverTimeDif);
}
else
{
clientTimeDif = (float)System.DateTime.UtcNow.TimeOfDay.TotalSeconds - (float)GetNetworkTime.NetworkTime().TimeOfDay.TotalSeconds;
StartCoroutine("DisplayTime", clientTimeDif);
}
}
IEnumerator DisplayTime (float TimeDif)
{
for (;;)
{
TimeLog.text = "" + ((float)System.DateTime.UtcNow.TimeOfDay.TotalSeconds + TimeDif);
// Log time so i can see if it is different on different devices
yield return new WaitForSeconds(0.01f);
}
}
}