예제 #1
0
        /// <summary>
        /// Fill a stream with relevant contents of directory tree for hashing.  Directory tree is filtered by
        /// inputParams if non-null.
        /// </summary>
        private static void BuildCRCStream(string directory, InputParams inputParams, ref MemoryStream memoryStream)
        {
            DirectoryInfo directoryInfo = new DirectoryInfo(directory);
            SyncResults   results       = new SyncResults();

            // get filtered list of files in this directory
            FileInfo[] files = new Sync().GetFiles(directoryInfo, inputParams, ref results);
            // sort by name for deterministic order
            Array.Sort(files, new FileInfoComparer());
            // write information about each file to stream
            foreach (FileInfo fileInfo in files)
            {
                byte[] bytes = ASCIIEncoding.UTF8.GetBytes(fileInfo.Name);
                memoryStream.Write(bytes, 0, bytes.Length);
                bytes = BitConverter.GetBytes(fileInfo.Length);
                memoryStream.Write(bytes, 0, bytes.Length);
                bytes = BitConverter.GetBytes(fileInfo.LastWriteTime.ToBinary());
                memoryStream.Write(bytes, 0, bytes.Length);
                bytes = ASCIIEncoding.UTF8.GetBytes(fileInfo.Attributes.ToString());
                memoryStream.Write(bytes, 0, bytes.Length);
            }

            // get filtered list of subdirectories
            DirectoryInfo[] subdirs = new Sync().GetDirectories(directoryInfo, inputParams, ref results);
            // sort by name for deterministic order
            Array.Sort(subdirs, new DirectoryInfoComparer());

            foreach (DirectoryInfo subdir in subdirs)
            {
                // write information about each subdirectory to stream
                byte[] bytes = ASCIIEncoding.UTF8.GetBytes(subdir.Name);
                memoryStream.Write(bytes, 0, bytes.Length);
                // recurse
                BuildCRCStream(Path.Combine(directory, subdir.Name), inputParams, ref memoryStream);
            }
        }
예제 #2
0
        private void DatabaseUpdated(DatabaseInfo info,
            SyncResults result, string error)
        {
            var listItem = _items.FirstOrDefault(
                x => x.Info == info);
            if (listItem == null)
                return;

            var dispatcher = Dispatcher;
            dispatcher.BeginInvoke(() =>
                listItem.IsUpdating = false);

            switch (result)
            {
                case SyncResults.NoChange:
                case SyncResults.Downloaded:
                    dispatcher.BeginInvoke(() =>
                        UpdateItem(listItem, "updated"));
                    break;

                case SyncResults.Uploaded:

                    dispatcher.BeginInvoke(() =>
                        UpdateItem(listItem, "uploaded"));
                    break;

                case SyncResults.Conflict:
                    dispatcher.BeginInvoke(() =>
                    {
                        UpdateItem(listItem, "uploaded");

                        MessageBox.Show(error,
                            Properties.Resources.ConflictTitle,
                            MessageBoxButton.OK);
                    });
                    break;

                case SyncResults.Failed:
                    var msg = string.Format(
                        Properties.Resources.UpdateFailure,
                        info.Details.Name, error);

                    dispatcher.BeginInvoke(() =>
                    {
                        listItem.UpdatedIcon = null;

                        MessageBox.Show(msg,
                            Properties.Resources.UpdateTitle,
                            MessageBoxButton.OK);
                    });
                    break;
            }
        }
예제 #3
0
        public SyncResults Synchronize(HarpFile mapFile)
        {
            var results = new SyncResults();

            var isValid = sql.ConfigureAndTest(mapFile.Config.SqlConnectionString);

            if (!isValid)
            {
                results.Code = SynchronizeResultCode.InvalidSqlConnectionString;
                return(results);
            }

            try
            {
                foreach (var entry in mapFile.Entities)
                {
                    int?tableId;
                    var entity = entry.Value;

                    // Table
                    if (string.IsNullOrWhiteSpace(entity.Table))
                    {
                        // Try match to an existing table
                        var tables = sql.GetAllTables();

                        var matches = tables.Where(
                            t => StringMatcher.IsAFuzzyMatch(
                                getObjectName(t.fullName), entity.Name
                                ));

                        if (matches.Count() > 1)
                        {
                            // Error: too many matches, can't decide.
                            results.Code = SynchronizeResultCode.EntityNameMatchesTooManyTables;
                            return(results);
                        }
                        else if (matches.Count() == 0)
                        {
                            // Error: no matches
                            results.Code = SynchronizeResultCode.EntityNameMatchesNoTables;
                            return(results);
                        }

                        var tableMatch = matches.Single();

                        // Capture table name on entity
                        entity.Table       = tableMatch.fullName;
                        results.WasUpdated = true;
                        trace.AppendLine($"Table match: {entity.Table}");
                    }

                    tableId = sql.GetTableObjectId(entity.Table);
                    if (tableId == null)
                    {
                        results.Code = SynchronizeResultCode.MatchedTableDoesNotExist;
                        return(results);
                    }

                    var columnsForEntity = sql.GetColumnNames(tableId.Value);

                    // Columns, unmapped
                    if (entity.Properties.Any(p => string.IsNullOrWhiteSpace(p.Value)))
                    {
                        for (int x = 0; x < entity.Properties.Count; x++)
                        {
                            var propEntry = entity.Properties.ElementAt(x);

                            // Excludes any already mapped
                            if (propEntry.Value != null)
                            {
                                continue;
                            }

                            var prop = propEntry.Value;

                            var availableMatches = columnsForEntity.Where(c => !entity.Properties.Any(p => p.Value == c));
                            var matches          = availableMatches.Where(c => StringMatcher.IsAFuzzyMatch(c, propEntry.Key));

                            if (matches.Any())
                            {
                                var match = matches.First();
                                entity.Properties[propEntry.Key] = match;
                                results.WasUpdated = true;
                                trace.AppendLine($"Property match: {propEntry.Key} = {match}");
                            }
                            else
                            {
                                // Error: No matches for column
                                results.Code = SynchronizeResultCode.ColumnMatchingError;
                                return(results);
                            }
                        }

                        // Track the unmapped
                        var unmappedCols = columnsForEntity.Except(entity.Properties.Select(p => p.Value));
                        results.UnmappedTableColumns.AddRange(unmappedCols);
                    }
                    else if (entity.Properties.Count() == 0)
                    {
                        // Autopopulate property list with all available columns
                        foreach (var column in columnsForEntity)
                        {
                            var humanName = column.Humanize();
                            entity.AddProperty(humanName, column);

                            results.WasUpdated = true;
                            trace.AppendLine($"Property match: {humanName} = {column}");
                        }
                    }

                    // Columns, mapped
                    var allExistingMappedColumnsExist = entity.Properties.All(p => columnsForEntity.Any(c => string.Equals(c, p.Value, StringComparison.OrdinalIgnoreCase)));
                    if (!allExistingMappedColumnsExist)
                    {
                        results.Code = SynchronizeResultCode.MatchedColumnDoesNotExist;
                        return(results);
                    }


                    var procsForEntity = sql.GetStoredProcsThatRefEntity(entity.Table);

                    // Behaviours, unmapped
                    if (entity.Behaviors.Any(b => string.IsNullOrWhiteSpace(b.Value)))
                    {
                        for (var x = 0; x < entity.Behaviors.Count; x++)
                        {
                            var behaveEntry = entity.Behaviors.ElementAt(x);

                            // ignore already mapped behaviors
                            if (behaveEntry.Value != null)
                            {
                                continue;
                            }

                            var behavior = behaveEntry.Value;

                            // Excludes any already mapped
                            var availableMatches = procsForEntity.Where(p => !entity.Behaviors.Any(b => b.Value == p.fullName));

                            var matches = availableMatches.Select(p => new ProcName(p.fullName))
                                          .OrderByDescending(p =>
                            {
                                // Remove entity name from proc's name, to make it more likely to get matched
                                // since there's less character changes required for a total match.
                                // e.g. 1 = get dogs by id = get by id (becomes the most closest match)
                                //      2 = get cats by id = get cats by id
                                var processed = p.HumanizedName.Replace((" " + entity.Name + " "), string.Empty);
                                return(stringCompareScore(behaveEntry.Key, processed));
                            }).ToArray();
                            if (matches.Any())
                            {
                                var match = matches.First().FullName;
                                entity.Behaviors[behaveEntry.Key] = match;
                                results.WasUpdated = true;
                                trace.AppendLine($"Behavior match: {behaveEntry.Key} = {match}");
                            }
                        }

                        // Track the unmapped
                        var unmappedCols = procsForEntity.Select(p => p.fullName)
                                           .Except(entity.Behaviors.Select(b => b.Value));
                        results.UnmappedStoredProcs.AddRange(unmappedCols);

                        // Ensure all stored procs that have been matched
                        // (either from this process or manually) exist.
                        var allExistingMappedProcsExist = entity.Behaviors.All(b => procsForEntity.Any(pr => string.Equals(pr.fullName, b.Value, StringComparison.OrdinalIgnoreCase)));
                        if (!allExistingMappedProcsExist)
                        {
                            results.Code = SynchronizeResultCode.MatchedProcDoesNotExist;
                            return(results);
                        }
                    }
                    else if (entity.Behaviors.Count() == 0)
                    {
                        // Autopopulate behavior list with all available procs
                        foreach (var proc in procsForEntity)
                        {
                            var humanName = removeWord(entity.Name, getObjectName(proc.fullName).Humanize()).Humanize();

                            if (string.IsNullOrWhiteSpace(humanName))
                            {
                                results.Code = SynchronizeResultCode.ProcMatchingError;
                                return(results);
                            }

                            entity.AddBehavior(humanName, proc.fullName);
                            results.WasUpdated = true;
                            trace.AppendLine($"Behavior match: {humanName} = {proc}");
                        }
                    }
                }

                // Validate
                foreach (var entity in mapFile.Entities.Select(e => e.Value))
                {
                    if (!entity.Behaviors.Any())
                    {
                        results.Code = SynchronizeResultCode.AtLeastOneBehaviorRequired;
                        return(results);
                    }
                }

                results.Code = mapFile.Entities.All(e => e.Value.IsFullyMapped)
                    ? SynchronizeResultCode.OK
                    : SynchronizeResultCode.NotAllMapped;
            }
            catch (Exception ex)
            {
                trace.AppendLine($"{ex.GetType().Name}: {ex.ToString()}");
                results.Code = SynchronizeResultCode.UnknownError;
            }

            return(results);
        }
예제 #4
0
        public static CSyncReturn Syncronize(Type T)
        {
            CScriptConstSyncAttribute[] attributes = (CScriptConstSyncAttribute[])T.GetCustomAttributes(typeof(CScriptConstSyncAttribute), false);

            string FullPath = attributes[0].FullPathOfJavaScript;

            if (FullPath.StartsWith("/"))
            {
                FullPath = HttpContext.Current.Server.MapPath(FullPath);
            }

            if (!File.Exists(FullPath))
            {
                throw new Exception(string.Format("{0} is not exists.", FullPath));
            }

            string JsGenerated = ConvertToJavaScript(T, 0);

            string FileContent    = CFile.GetTextInFile(FullPath);
            string LogDateTime    = DateTime.Now.ToString(CConst.Format_yyyyMMddHHmmss);
            string FullPathBackup = GetFullPathBackup(FullPath, LogDateTime);

            string Declare = "var " + T.Name + " ";

            SyncResults SyncResult = SyncResults.NoChange;

            string JsAlready = GetJavaScriptByDeclare(FileContent, Declare);

            if (!string.IsNullOrEmpty(JsAlready))
            {
                if (CFindRep.TrimWhiteSpace(JsGenerated) != CFindRep.TrimWhiteSpace(JsAlready))
                {
                    SyncResult = SyncResults.Update;
                }
            }
            else
            {
                SyncResult = SyncResults.Append;
            }

            if (SyncResult != SyncResults.NoChange)
            {
                string Line = string.Format("//{0} {1} by CScriptConstSyncAttribute", LogDateTime, SyncResult);

                string FileContentNew = "";
                if (SyncResult == SyncResults.Update)
                {
                    FileContentNew = FileContent.Replace(JsAlready, Line + "\r\n" + JsGenerated);
                }
                else if (SyncResult == SyncResults.Append)
                {
                    FileContentNew = FileContent + "\r\n" + Line + "\r\n" + JsGenerated;
                }

                CFile.WriteTextToFile(FullPath, FileContentNew);
                CFile.WriteTextToFile(FullPathBackup, FileContent);
            }

            return(new CSyncReturn()
            {
                SyncResult = SyncResult,
                FullPathOfJavaScript = FullPath,
                FullPathOfJavaScriptBackup = FullPathBackup
            });
        }
예제 #5
0
    void RecieveResults(SyncResults syncResults)
    {
        //Convering values back
        Results results;

        results.rotation  = Quaternion.Euler((float)syncResults.pitch / 182, (float)syncResults.yaw / 182, 0);
        results.position  = syncResults.position;
        results.sprinting = syncResults.sprinting;
        results.crouching = syncResults.crouching;
        results.timeStamp = syncResults.timeStamp;

        //Discard out of order results
        if (results.timeStamp <= _lastTimeStamp)
        {
            return;
        }
        _lastTimeStamp = results.timeStamp;
        //Non-owner client
        if (!isLocalPlayer && !hasAuthority)
        {
            //Adding results to the results list so they can be used in interpolation process
            results.timeStamp = Time.time;
            _resultsList.Add(results);
        }

        //Owner client
        //Server client reconciliation process should be executed in order to client's rotation and position with server values but do it without jittering
        if (isLocalPlayer && !hasAuthority)
        {
            //Update client's position and rotation with ones from server
            _results.rotation = results.rotation;
            _results.position = results.position;
            int foundIndex = -1;
            //Search recieved time stamp in client's inputs list
            for (int index = 0; index < _inputsList.Count; index++)
            {
                //If time stamp found run through all inputs starting from needed time stamp
                if (_inputsList[index].timeStamp > results.timeStamp)
                {
                    foundIndex = index;
                    break;
                }
            }
            if (foundIndex == -1)
            {
                //Clear Inputs list if no needed records found
                while (_inputsList.Count != 0)
                {
                    _inputsList.RemoveAt(0);
                }
                return;
            }
            //Replay recorded inputs
            for (int subIndex = foundIndex; subIndex < _inputsList.Count; subIndex++)
            {
                _results.rotation  = Rotate(_inputsList[subIndex], _results);
                _results.crouching = Crouch(_inputsList[subIndex], _results);
                _results.sprinting = Sprint(_inputsList[subIndex], _results);

                _results.position = Move(_inputsList[subIndex], _results);
            }
            //Remove all inputs before time stamp
            int targetCount = _inputsList.Count - foundIndex;
            while (_inputsList.Count > targetCount)
            {
                _inputsList.RemoveAt(0);
            }
        }
    }
예제 #6
0
    void FixedUpdate()
    {
        if (isLocalPlayer)
        {
            _inputs.timeStamp = Time.time;
            //Client side prediction for non-authoritative client or plane movement and rotation for listen server/host
            Vector3    lastPosition = _results.position;
            Quaternion lastRotation = _results.rotation;
            bool       lastCrouch   = _results.crouching;
            _results.rotation  = Rotate(_inputs, _results);
            _results.crouching = Crouch(_inputs, _results);
            _results.sprinting = Sprint(_inputs, _results);
            _results.position  = Move(_inputs, _results);
            if (hasAuthority)
            {
                //Listen server/host part
                //Sending results to other clients(state sync)
                if (_dataStep >= GetNetworkSendInterval())
                {
                    if (Vector3.Distance(_results.position, lastPosition) > 0 || Quaternion.Angle(_results.rotation, lastRotation) > 0 || _results.crouching != lastCrouch)
                    {
                        _results.timeStamp = _inputs.timeStamp;
                        //Struct need to be fully new to count as dirty
                        //Convering some of the values to get less traffic
                        SyncResults tempResults;
                        tempResults.yaw       = (ushort)(_results.rotation.eulerAngles.y * 182);
                        tempResults.pitch     = (ushort)(_results.rotation.eulerAngles.x * 182);
                        tempResults.position  = _results.position;
                        tempResults.sprinting = _results.sprinting;
                        tempResults.crouching = _results.crouching;
                        tempResults.timeStamp = _results.timeStamp;
                        syncResults           = tempResults;
                    }
                    _dataStep = 0;
                }
                _dataStep += Time.fixedDeltaTime;
            }
            else
            {
                //Owner client. Non-authoritative part
                //Add inputs to the inputs list so they could be used during reconciliation process
                if (Vector3.Distance(_results.position, lastPosition) > 0 || Quaternion.Angle(_results.rotation, lastRotation) > 0 || _results.crouching != lastCrouch)
                {
                    _inputsList.Add(_inputs);
                }
                //Sending inputs to the server
                //Unfortunately there is now method overload for [Command] so I need to write several almost similar functions
                //This one is needed to save on network traffic
                SyncInputs syncInputs;
                syncInputs.forward  = (sbyte)(_inputs.forward * 127);
                syncInputs.sides    = (sbyte)(_inputs.sides * 127);
                syncInputs.vertical = (sbyte)(_inputs.vertical * 127);
                if (Vector3.Distance(_results.position, lastPosition) > 0)
                {
                    if (Quaternion.Angle(_results.rotation, lastRotation) > 0)
                    {
                        Cmd_MovementRotationInputs(syncInputs.forward, syncInputs.sides, syncInputs.vertical, _inputs.pitch, _inputs.yaw, _inputs.sprint, _inputs.crouch, _inputs.timeStamp);
                    }
                    else
                    {
                        Cmd_MovementInputs(syncInputs.forward, syncInputs.sides, syncInputs.vertical, _inputs.sprint, _inputs.crouch, _inputs.timeStamp);
                    }
                }
                else
                {
                    if (Quaternion.Angle(_results.rotation, lastRotation) > 0)
                    {
                        Cmd_RotationInputs(_inputs.pitch, _inputs.yaw, _inputs.crouch, _inputs.timeStamp);
                    }
                    else
                    {
                        Cmd_OnlyStances(_inputs.crouch, _inputs.timeStamp);
                    }
                }
            }
        }
        else
        {
            if (hasAuthority)
            {
                //Server

                //Check if there is atleast one record in inputs list
                if (_inputsList.Count == 0)
                {
                    return;
                }
                //Move and rotate part. Nothing interesting here
                Inputs inputs = _inputsList[0];
                _inputsList.RemoveAt(0);
                Vector3    lastPosition = _results.position;
                Quaternion lastRotation = _results.rotation;
                bool       lastCrouch   = _results.crouching;
                _results.rotation  = Rotate(inputs, _results);
                _results.crouching = Crouch(inputs, _results);
                _results.sprinting = Sprint(inputs, _results);
                _results.position  = Move(inputs, _results);
                //Sending results to other clients(state sync)

                if (_dataStep >= GetNetworkSendInterval())
                {
                    if (Vector3.Distance(_results.position, lastPosition) > 0 || Quaternion.Angle(_results.rotation, lastRotation) > 0 || _results.crouching != lastCrouch)
                    {
                        //Struct need to be fully new to count as dirty
                        //Convering some of the values to get less traffic
                        _results.timeStamp = inputs.timeStamp;
                        SyncResults tempResults;
                        tempResults.yaw       = (ushort)(_results.rotation.eulerAngles.y * 182);
                        tempResults.pitch     = (ushort)(_results.rotation.eulerAngles.x * 182);
                        tempResults.position  = _results.position;
                        tempResults.sprinting = _results.sprinting;
                        tempResults.crouching = _results.crouching;
                        tempResults.timeStamp = _results.timeStamp;
                        syncResults           = tempResults;
                    }
                    _dataStep = 0;
                }
                _dataStep += Time.fixedDeltaTime;
            }
            else
            {
                //Non-owner client a.k.a. dummy client
                //there should be at least two records in the results list so it would be possible to interpolate between them in case if there would be some dropped packed or latency spike
                //And yes this stupid structure should be here because it should start playing data when there are at least two records and continue playing even if there is only one record left
                if (_resultsList.Count == 0)
                {
                    _playData = false;
                }
                if (_resultsList.Count >= 2)
                {
                    _playData = true;
                }
                if (_playData)
                {
                    if (_dataStep == 0)
                    {
                        _startPosition = _results.position;
                        _startRotation = _results.rotation;
                    }
                    _step              = 1 / (GetNetworkSendInterval());
                    _results.rotation  = Quaternion.Slerp(_startRotation, _resultsList[0].rotation, _dataStep);
                    _results.position  = Vector3.Lerp(_startPosition, _resultsList[0].position, _dataStep);
                    _results.crouching = _resultsList[0].crouching;
                    _results.sprinting = _resultsList[0].sprinting;
                    _dataStep         += _step * Time.fixedDeltaTime;
                    if (_dataStep >= 1)
                    {
                        _dataStep = 0;
                        _resultsList.RemoveAt(0);
                    }
                }
                UpdateRotation(_results.rotation);
                UpdatePosition(_results.position);
                UpdateCrouch(_results.crouching);
                UpdateSprinting(_results.sprinting);
            }
        }
    }
예제 #7
0
        /// <summary>
        /// Runs a test case
        /// </summary>
        private static void TestOneCase(string[] srcDirectories, string[] srcFiles, string[] destDirectories, string[] destFiles, InputParams inputParams, SyncResults expectedResults)
        {
            // delete base directories in case they were hanging around from a previous failed test
            DeleteTestDirectory(baseDirSrc);
            DeleteTestDirectory(baseDirDest);

            // create source directories and files specified by test
            CreateTestDirectories(baseDirSrc, srcDirectories);
            CreateTestFiles(baseDirSrc, null, srcFiles);
            // create destination directories and files specified by test
            if (destDirectories != null)
            {
                CreateTestDirectories(baseDirDest, destDirectories);
            }
            if (destFiles != null)
            {
                CreateTestFiles(baseDirDest, baseDirSrc, destFiles);
            }

            // perform the directory sync
            SyncResults results = new SyncResults();

            results = new Sync(baseDirSrc, baseDirDest).Start(inputParams);

            // Assert we have expected results
            Assert.IsTrue(SyncTools.CompareTo(expectedResults, results));

            // If we are deleting extra files from destination, verify we have exactly the same files as filtered source files
            if (inputParams.DeleteFromDest &&
                (!(inputParams.DeleteExcludeFiles != null) && !(inputParams.DeleteExcludeDirs != null)))
            {
                // calc hash of filtered files & directories in source tree
                byte[] hashSrc = CalcHash(baseDirSrc, inputParams);
                // calc hash of all files & directories in destination tree
                byte[] hashDest = CalcHash(baseDirDest, null);
                // hashes must match
                bool hashesMatch = SyncTools.CompareByteArrays(hashSrc, hashDest);
                Assert.IsTrue(hashesMatch);
            }

            DeleteTestDirectory(baseDirSrc);
            DeleteTestDirectory(baseDirDest);
        }
예제 #8
0
 public void Initialization()
 {
     inputParams     = new InputParams();
     expectedResults = new SyncResults();
 }
예제 #9
0
	void FixedUpdate(){
		if (isLocalPlayer) {

			_inputs.timeStamp = Time.time;
			//Client side prediction for non-authoritative client or plane movement and rotation for listen server/host
			Vector3 lastPosition = _results.position;
			Quaternion lastRotation = _results.rotation;
			bool lastCrouch = _results.crouching;
			_results.rotation = Rotate(_inputs,_results);
			_results.crouching = Crouch(_inputs,_results);
			_results.sprinting = Sprint(_inputs,_results);
			_results.position = Move(_inputs,_results);
			if(hasAuthority){
				//Listen server/host part
				//Sending results to other clients(state sync)
				if(_dataStep >= GetNetworkSendInterval()){
					if(Vector3.Distance(_results.position,lastPosition) > 0 || Quaternion.Angle(_results.rotation,lastRotation) > 0 || _results.crouching != lastCrouch ){
						_results.timeStamp = _inputs.timeStamp;
						//Struct need to be fully new to count as dirty 
						//Convering some of the values to get less traffic
						SyncResults tempResults;
						tempResults.yaw = (ushort)(_results.rotation.eulerAngles.y * 182);
						tempResults.pitch = (ushort)(_results.rotation.eulerAngles.x * 182);
						tempResults.position = _results.position;
						tempResults.sprinting = _results.sprinting;
						tempResults.crouching = _results.crouching;
						tempResults.timeStamp = _results.timeStamp;
						syncResults = tempResults;
					}
					_dataStep = 0;
				}
				_dataStep += Time.fixedDeltaTime;
			}else{
				//Owner client. Non-authoritative part
				//Add inputs to the inputs list so they could be used during reconciliation process
				if(Vector3.Distance(_results.position,lastPosition) > 0 || Quaternion.Angle(_results.rotation,lastRotation) > 0 || _results.crouching != lastCrouch ){
					_inputsList.Add(_inputs);
				}
				//Sending inputs to the server
				//Unfortunately there is now method overload for [Command] so I need to write several almost similar functions
				//This one is needed to save on network traffic
				SyncInputs syncInputs;
				syncInputs.forward = (sbyte)(_inputs.forward * 127);
				syncInputs.sides = (sbyte)(_inputs.sides * 127);
				syncInputs.vertical = (sbyte)(_inputs.vertical * 127);
				if(Vector3.Distance(_results.position,lastPosition) > 0 ){
					if(Quaternion.Angle(_results.rotation,lastRotation) > 0){
						Cmd_MovementRotationInputs(syncInputs.forward,syncInputs.sides,syncInputs.vertical,_inputs.pitch,_inputs.yaw,_inputs.sprint,_inputs.crouch,_inputs.timeStamp);
					}else{
						Cmd_MovementInputs(syncInputs.forward,syncInputs.sides,syncInputs.vertical,_inputs.sprint,_inputs.crouch,_inputs.timeStamp);
					}
				}else{
					if(Quaternion.Angle(_results.rotation,lastRotation) > 0){
						Cmd_RotationInputs(_inputs.pitch,_inputs.yaw,_inputs.crouch,_inputs.timeStamp);
					}else{
						Cmd_OnlyStances(_inputs.crouch,_inputs.timeStamp);
					}
				}
			}
		} else {
			if(hasAuthority){
				//Server

				//Check if there is atleast one record in inputs list
				if(_inputsList.Count == 0){
					return;
				}
				//Move and rotate part. Nothing interesting here
				Inputs inputs = _inputsList[0];
				_inputsList.RemoveAt(0);
				Vector3 lastPosition = _results.position;
				Quaternion lastRotation = _results.rotation;
				bool lastCrouch = _results.crouching;
				_results.rotation = Rotate(inputs,_results);
				_results.crouching = Crouch(inputs,_results);
				_results.sprinting = Sprint(inputs,_results);
				_results.position = Move(inputs,_results);
				//Sending results to other clients(state sync)

				if(_dataStep >= GetNetworkSendInterval()){
					if(Vector3.Distance(_results.position,lastPosition) > 0 || Quaternion.Angle(_results.rotation,lastRotation) > 0 || _results.crouching != lastCrouch){
						//Struct need to be fully new to count as dirty 
						//Convering some of the values to get less traffic
						_results.timeStamp = inputs.timeStamp;
						SyncResults tempResults;
						tempResults.yaw = (ushort)(_results.rotation.eulerAngles.y * 182);
						tempResults.pitch = (ushort)(_results.rotation.eulerAngles.x * 182);
						tempResults.position = _results.position;
						tempResults.sprinting = _results.sprinting;
						tempResults.crouching = _results.crouching;
						tempResults.timeStamp = _results.timeStamp;
						syncResults = tempResults;
					}
					_dataStep = 0;
				}
				_dataStep += Time.fixedDeltaTime;
			}else{
				//Non-owner client a.k.a. dummy client
				//there should be at least two records in the results list so it would be possible to interpolate between them in case if there would be some dropped packed or latency spike
				//And yes this stupid structure should be here because it should start playing data when there are at least two records and continue playing even if there is only one record left 
				if(_resultsList.Count == 0){
					_playData = false;
				}
				if(_resultsList.Count >=2){
					_playData = true;
				}
				if(_playData){
					if(_dataStep==0){
						_startPosition = _results.position;
						_startRotation = _results.rotation;
					}
					_step = 1/(GetNetworkSendInterval()) ;
					_results.rotation = Quaternion.Slerp(_startRotation,_resultsList[0].rotation,_dataStep);
					_results.position = Vector3.Lerp(_startPosition,_resultsList[0].position,_dataStep);
					_results.crouching = _resultsList[0].crouching;
					_results.sprinting = _resultsList[0].sprinting;
					_dataStep += _step * Time.fixedDeltaTime;
					if(_dataStep>= 1){
						_dataStep = 0;
						_resultsList.RemoveAt(0);
					}
				}
				UpdateRotation(_results.rotation);
				UpdatePosition(_results.position);
				UpdateCrouch(_results.crouching );
				UpdateSprinting(_results.sprinting );
			}
		}
	}
예제 #10
0
	void RecieveResults(SyncResults syncResults){ 
		//Convering values back
		Results results;
		results.rotation = Quaternion.Euler ((float)syncResults.pitch/182,(float)syncResults.yaw/182,0);
		results.position = syncResults.position;
		results.sprinting = syncResults.sprinting;
		results.crouching = syncResults.crouching;
		results.timeStamp = syncResults.timeStamp;

		//Discard out of order results
		if (results.timeStamp <= _lastTimeStamp) {
			return;
		}
		_lastTimeStamp = results.timeStamp;
		//Non-owner client
		if (!isLocalPlayer && !hasAuthority) {
			//Adding results to the results list so they can be used in interpolation process
			results.timeStamp = Time.time;
			_resultsList.Add(results);
		}

		//Owner client
		//Server client reconciliation process should be executed in order to client's rotation and position with server values but do it without jittering
		if (isLocalPlayer && !hasAuthority) {
			//Update client's position and rotation with ones from server 
			_results.rotation = results.rotation;
			_results.position = results.position;
			int foundIndex = -1;
			//Search recieved time stamp in client's inputs list
			for(int index = 0; index < _inputsList.Count; index++){
				//If time stamp found run through all inputs starting from needed time stamp 
				if(_inputsList[index].timeStamp > results.timeStamp){
					foundIndex = index;
					break;
				}
			}
			if(foundIndex ==-1){
				//Clear Inputs list if no needed records found 
				while(_inputsList.Count != 0){
					_inputsList.RemoveAt(0);
				}
				return;
			}
			//Replay recorded inputs
			for(int subIndex = foundIndex; subIndex < _inputsList.Count;subIndex++){
				_results.rotation = Rotate(_inputsList[subIndex],_results);
				_results.crouching = Crouch(_inputsList[subIndex],_results);
				_results.sprinting = Sprint(_inputsList[subIndex],_results);

				_results.position = Move(_inputsList[subIndex],_results);
			}
			//Remove all inputs before time stamp
			int targetCount = _inputsList.Count - foundIndex;
			while(_inputsList.Count > targetCount){
				_inputsList.RemoveAt(0);
			}
		}
	}