Search Unity

Unity Multiplayer [SOLVED] ClientCallBack not Executing

Discussion in 'Multiplayer' started by DRRosen3, Dec 30, 2015.

  1. DRRosen3

    DRRosen3

    Joined:
    Jan 30, 2014
    Posts:
    667
    Can't figure out why my ClientCallBack isn't firing. I got the framework for this from another thread...HERE. I tested the original and it works just fine. I really haven't done anything different other than take out unneeded variables and change how rotation is handled.

    EDIT - After a bit more investigating, I'm seeing that my Commands aren't even being called, so of course the ReceiveResults isn't executing, because the syncResults aren't being modified. However, I can't see why the Commands aren't being called.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using UnityEngine.Networking;
    4. using System.Collections.Generic;
    5. //Server-authoritative movement with Client-side prediction and reconciliation
    6. //Author:gennadiy.shvetsov@gmail.com
    7. //QoS channels used:
    8. //channel #0: Reliable Sequenced
    9. //channel #1: Unreliable Sequenced
    10. [NetworkSettings(channel=1,sendInterval=0.05f)]
    11. public class Network_Movement : NetworkBehaviour
    12. {
    13.     //This struct would be used to collect player inputs
    14.     [System.Serializable]
    15.     public struct Inputs        
    16.     {
    17.         public float forwardBackward;
    18.         public float leftRight;
    19.         public float vertical;
    20.         public float rotationAngle;
    21.         public Vector3 forward;
    22.         public Vector3 right;
    23.         public bool walk;
    24.      
    25.         public float timeStamp;
    26.     }
    27.     [System.Serializable]
    28.     public struct SyncInputs        
    29.     {
    30.         public sbyte forwardBackward;
    31.         public sbyte leftRight;
    32.         public sbyte vertical;
    33.         public sbyte rotationAngle;
    34.         public bool walk;
    35.      
    36.         public float timeStamp;
    37.     }
    38.     //This struct would be used to collect results of Move and Rotate functions
    39.     [System.Serializable]
    40.     public struct Results
    41.     {
    42.         public Quaternion rotation;
    43.         public Vector3 position;
    44.         public bool walking;
    45.         public float timeStamp;
    46.     }
    47.     [System.Serializable]
    48.     public struct SyncResults
    49.     {
    50.         public Vector3 position;
    51.         public ushort rotationAngle;
    52.         public bool walking;
    53.         public float timeStamp;
    54.     }
    55.  
    56.  
    57.     private Inputs inputs;
    58.     //Synced from server to all clients
    59.     [SyncVar(hook="RecieveResults")]
    60.     public SyncResults syncResults;
    61.     public Results results;
    62.     //Owner client and server would store it's inputs in this list
    63.     public List<Inputs> InputList = new List<Inputs>();
    64.     //This list stores results of movement and rotation. Needed for non-owner client interpolation
    65.     public List<Results> ResultsList = new List<Results>();
    66.     //Interpolation related variables
    67.     private bool playData = false;
    68.     private float dataStep = 0.0f;
    69.     private float lastTimeStamp = 0.0f;
    70.     private bool jumping = false;
    71.     private Vector3 startPosition;
    72.     private Quaternion startRotation;
    73.     private float step = 0.0f;
    74.  
    75.     public void SetStartPosition(Vector3 position)
    76.     {
    77.         results.position = position;
    78.     }
    79.     public void SetStartRotation(Quaternion rotation)
    80.     {
    81.         results.rotation = rotation;
    82.     }
    83.  
    84.     void Start()
    85.     {
    86.         SetStartPosition(transform.position);
    87.         SetStartRotation(transform.rotation);
    88.     }
    89.     void Update()
    90.     {
    91.         if(isLocalPlayer)
    92.             //Getting clients inputs
    93.             GetInputs (ref inputs);
    94.     }
    95.     void FixedUpdate()
    96.     {
    97.         if(isLocalPlayer)
    98.         {
    99.             inputs.timeStamp = Time.time;
    100.  
    101.             //Client side prediction for non-authoritative client or plane movement and rotation for listen server/host
    102.             Vector3 lastPosition = results.position;
    103.             Quaternion lastRotation = results.rotation;
    104.  
    105.             results.rotation = Rotate(inputs, results);
    106.             results.walking = Walk(inputs, results);
    107.             results.position = Move(inputs, results);
    108.  
    109.             if(hasAuthority)
    110.             {
    111.                 //Listen server/host part
    112.                 //Sending results to other clients(state sync)
    113.                 if(dataStep >= GetNetworkSendInterval())
    114.                 {
    115.                     if(Vector3.Distance(results.position, lastPosition) > 0 || Quaternion.Angle(results.rotation, lastRotation) > 0)
    116.                     {
    117.                         results.timeStamp = inputs.timeStamp;
    118.                         //Struct need to be fully new to count as dirty
    119.                         //Convering some of the values to get less traffic
    120.                         SyncResults tempResults;
    121.  
    122.                         tempResults.rotationAngle = (ushort)(results.rotation.eulerAngles.y * 182);
    123.                         tempResults.position = results.position;
    124.                         tempResults.walking = results.walking;
    125.                         tempResults.timeStamp = results.timeStamp;
    126.                         syncResults = tempResults;
    127.                     }
    128.                     dataStep = 0;
    129.                 }
    130.  
    131.                 dataStep += Time.fixedDeltaTime;
    132.             }
    133.             else
    134.             {
    135.                 //Owner client. Non-authoritative part
    136.                 //Add inputs to the inputs list so they could be used during reconciliation process
    137.                 if(Vector3.Distance(results.position,lastPosition) > 0 || Quaternion.Angle(results.rotation,lastRotation) > 0)
    138.                     InputList.Add(inputs);
    139.  
    140.                 //Sending inputs to the server
    141.                 //Unfortunately there is now method overload for [Command] so I need to write several almost similar functions
    142.                 //This one is needed to save on network traffic
    143.                 SyncInputs syncInputs;
    144.  
    145.                 syncInputs.forwardBackward = (sbyte)(inputs.forwardBackward * 127);
    146.                 syncInputs.leftRight = (sbyte)(inputs.leftRight * 127);
    147.                 syncInputs.vertical = (sbyte)(inputs.vertical * 127);
    148.  
    149.                 if(Vector3.Distance(results.position, lastPosition) > 0)
    150.                 {
    151.                     if(Quaternion.Angle(results.rotation, lastRotation) > 0)
    152.                         Cmd_MovementRotationInputs(syncInputs.forwardBackward, syncInputs.leftRight, syncInputs.vertical, inputs.rotationAngle, inputs.walk, inputs.timeStamp);
    153.                     else
    154.                         Cmd_MovementInputs(syncInputs.forwardBackward, syncInputs.leftRight, syncInputs.vertical, inputs.walk, inputs.timeStamp);
    155.                 }
    156.                 else
    157.                 {
    158.                     if(Quaternion.Angle(results.rotation, lastRotation) > 0)
    159.                         Cmd_RotationInputs(inputs.rotationAngle, inputs.timeStamp);
    160.                     else
    161.                         Cmd_OnlyStances(inputs.timeStamp);
    162.                 }
    163.             }
    164.         }
    165.         else
    166.         {
    167.             if(hasAuthority)
    168.             {
    169.                 //Server
    170.                 //Check if there is atleast one record in inputs list
    171.                 if(InputList.Count == 0)
    172.                     return;
    173.  
    174.                 //Move and rotate part. Nothing interesting here
    175.                 Inputs inputs = InputList[0];
    176.  
    177.                 InputList.RemoveAt(0);
    178.  
    179.                 Vector3 lastPosition = results.position;
    180.                 Quaternion lastRotation = results.rotation;
    181.  
    182.                 results.rotation = Rotate(inputs, results);
    183.                 results.walking = Walk(inputs, results);
    184.                 results.position = Move(inputs, results);
    185.                 //Sending results to other clients(state sync)
    186.              
    187.                 if(dataStep >= GetNetworkSendInterval())
    188.                 {
    189.                     if(Vector3.Distance(results.position, lastPosition) > 0 || Quaternion.Angle(results.rotation, lastRotation) > 0)
    190.                     {
    191.                         //Struct need to be fully new to count as dirty
    192.                         //Convering some of the values to get less traffic
    193.                         results.timeStamp = inputs.timeStamp;
    194.  
    195.                         SyncResults tempResults;
    196.  
    197.                         tempResults.rotationAngle = (ushort)(results.rotation.eulerAngles.y * 182);
    198.                         tempResults.position = results.position;
    199.                         tempResults.walking = results.walking;
    200.                         tempResults.timeStamp = results.timeStamp;
    201.                         syncResults = tempResults;
    202.                     }
    203.  
    204.                     dataStep = 0;
    205.                 }
    206.  
    207.                 dataStep += Time.fixedDeltaTime;
    208.             }
    209.             else
    210.             {
    211.                 //Non-owner client a.k.a. dummy client
    212.                 //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
    213.                 //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
    214.                 if(ResultsList.Count == 0)
    215.                     playData = false;
    216.  
    217.                 if(ResultsList.Count >= 2)
    218.                     playData = true;
    219.  
    220.                 if(playData)
    221.                 {
    222.                     if(dataStep == 0)
    223.                     {
    224.                         startPosition = results.position;
    225.                         startRotation = results.rotation;
    226.                     }
    227.  
    228.                     step = 1/(GetNetworkSendInterval()) ;
    229.                     results.rotation = Quaternion.Slerp(startRotation, ResultsList[0].rotation, dataStep);
    230.                     results.position = Vector3.Lerp(startPosition, ResultsList[0].position, dataStep);
    231.                     results.walking = ResultsList[0].walking;
    232.                     dataStep += step * Time.fixedDeltaTime;
    233.  
    234.                     if(dataStep >= 1)
    235.                     {
    236.                         dataStep = 0;
    237.                         ResultsList.RemoveAt(0);
    238.                     }
    239.                 }
    240.  
    241.                 UpdateRotation(results.rotation);
    242.                 UpdatePosition(results.position);
    243.                 UpdateWalking(results.walking);
    244.             }
    245.         }
    246.     }
    247.  
    248.     #region Commands
    249.     //Standing on spot
    250.     [Command(channel = 0)]
    251.     void Cmd_OnlyStances(float timeStamp)
    252.     {
    253.         if(hasAuthority && !isLocalPlayer)
    254.         {
    255.             Inputs inputs;
    256.  
    257.             inputs.forwardBackward = 0.0f;
    258.             inputs.leftRight = 0.0f;
    259.             inputs.vertical = 0.0f;
    260.             inputs.rotationAngle = 0.0f;
    261.             inputs.forward = Vector3.zero;
    262.             inputs.right = Vector3.zero;
    263.             inputs.walk = false;
    264.             inputs.timeStamp = timeStamp;
    265.             InputList.Add(inputs);
    266.         }
    267.     }
    268.     //Only rotation inputs sent
    269.     [Command(channel = 0)]
    270.     void Cmd_RotationInputs(float rotationAngle, float timeStamp)
    271.     {
    272.         if(hasAuthority && !isLocalPlayer)
    273.         {
    274.             Inputs inputs;
    275.             inputs.forwardBackward = 0.0f;
    276.             inputs.leftRight = 0.0f;
    277.             inputs.vertical = 0.0f;
    278.             inputs.rotationAngle = rotationAngle;
    279.             inputs.forward = Vector3.zero;
    280.             inputs.right = Vector3.zero;
    281.             inputs.walk = false;
    282.             inputs.timeStamp = timeStamp;
    283.             InputList.Add(inputs);
    284.         }
    285.     }
    286.     //Rotation and movement inputs sent
    287.     [Command(channel = 0)]
    288.     void Cmd_MovementRotationInputs(sbyte forwardBackward, sbyte leftRight, sbyte vertical, float rotationAngle, bool walk,float timeStamp)
    289.     {
    290.         if(hasAuthority && !isLocalPlayer)
    291.         {
    292.             Inputs inputs;
    293.  
    294.             inputs.forwardBackward = Mathf.Clamp((float)forwardBackward / 127, -1, 1);
    295.             inputs.leftRight = Mathf.Clamp((float)leftRight / 127, -1, 1);
    296.             inputs.vertical = Mathf.Clamp((float)vertical / 127, -1, 1);
    297.             inputs.rotationAngle = rotationAngle;
    298.             inputs.forward = Vector3.zero;
    299.             inputs.right = Vector3.zero;
    300.             inputs.walk = walk;
    301.             inputs.timeStamp = timeStamp;
    302.             InputList.Add(inputs);
    303.         }
    304.     }
    305.     //Only movements inputs sent
    306.     [Command(channel = 0)]
    307.     void Cmd_MovementInputs(sbyte forwardBackward, sbyte leftRight, sbyte vertical, bool walk, float timeStamp)
    308.     {
    309.         if(hasAuthority && !isLocalPlayer)
    310.         {
    311.             Inputs inputs;
    312.  
    313.             inputs.forwardBackward = Mathf.Clamp((float)forwardBackward / 127, -1, 1);
    314.             inputs.leftRight = Mathf.Clamp((float)leftRight / 127, -1, 1);
    315.             inputs.vertical = Mathf.Clamp((float)vertical / 127, -1, 1);
    316.             inputs.rotationAngle = 0.0f;
    317.             inputs.forward = Vector3.zero;
    318.             inputs.right = Vector3.zero;
    319.             inputs.walk = walk;
    320.             inputs.timeStamp = timeStamp;
    321.             InputList.Add(inputs);
    322.         }
    323.     }
    324.     #endregion
    325.  
    326.     //Self explanatory
    327.     //Can be changed in inherited class
    328.     public virtual void GetInputs(ref Inputs inputs)
    329.     {
    330.         //Don't use one frame events in this part
    331.         //It would be processed incorrectly
    332.         inputs.leftRight = Input.GetAxisRaw("Horizontal");
    333.         inputs.forwardBackward = Input.GetAxisRaw("Vertical");
    334.         inputs.forward = Camera.main.transform.TransformDirection(Vector3.forward);
    335.         inputs.forward.y = 0.0f;
    336.         inputs.forward = inputs.forward.normalized;
    337.         inputs.right = new Vector3(inputs.forward.z, 0.0f, -inputs.forward.x);
    338.         inputs.rotationAngle = Physics_Calculations.AngleAroundAxis(transform.forward, (inputs.leftRight * inputs.right + inputs.forwardBackward * inputs.forward),
    339.                                                                     Vector3.up);
    340.         inputs.walk = Input.GetButton("Walk");
    341.  
    342.         if (Input.GetButtonDown ("Jump") && inputs.vertical <=-0.9f)
    343.             jumping = true;
    344.  
    345.         float verticalTarget = -1;
    346.  
    347.         if(jumping)
    348.         {
    349.             verticalTarget = 1;
    350.  
    351.             if(inputs.vertical >= 0.9f)
    352.                 jumping = false;
    353.         }
    354.  
    355.         inputs.vertical = Mathf.Lerp(inputs.vertical, verticalTarget, 20 * Time.deltaTime);
    356.     }
    357.  
    358.     //Next virtual functions can be changed in inherited class for custom movement and rotation mechanics
    359.     //So it would be possible to control for example humanoid or vehicle from one script just by changing controlled pawn
    360.     public virtual void UpdatePosition(Vector3 newPosition)
    361.     {
    362.         transform.position = newPosition;
    363.     }
    364.  
    365.     public virtual void UpdateRotation(Quaternion newRotation)
    366.     {
    367.         transform.rotation = newRotation;
    368.     }
    369.  
    370.     public virtual void UpdateWalking(bool walking)
    371.     {
    372.      
    373.     }
    374.  
    375.     public virtual Vector3 Move(Inputs inputs, Results current)
    376.     {
    377.         transform.position = current.position;
    378.  
    379.         float speed = 2;
    380.  
    381.         if(current.walking)
    382.             speed = 1;
    383.  
    384.         transform.position += (inputs.leftRight * inputs.right + inputs.forwardBackward * inputs.forward) * speed * Time.fixedDeltaTime;
    385.  
    386.         return transform.position;
    387.     }
    388.     public virtual bool Walk(Inputs inputs, Results current)
    389.     {
    390.         return inputs.walk;
    391.     }
    392.  
    393.     public virtual Quaternion Rotate(Inputs inputs, Results current)
    394.     {
    395.         transform.rotation = current.rotation;
    396.  
    397.         Vector3 lookDirection = (inputs.leftRight * inputs.right + inputs.forwardBackward * inputs.forward);
    398.  
    399.         if(lookDirection != Vector3.zero)
    400.         {
    401.             Quaternion newRotation = Quaternion.LookRotation(lookDirection);
    402.             transform.rotation = Quaternion.Lerp(transform.rotation, newRotation, Time.deltaTime * 5.0f);
    403.         }
    404.  
    405.         return transform.rotation;
    406.     }
    407.  
    408.     //Updating Clients with server states
    409.     [ClientCallback]
    410.     void RecieveResults(SyncResults syncResults)
    411.     {
    412.         //Convering values back
    413.         Results results;
    414.  
    415.         results.rotation = Quaternion.Euler(0.0f, (float)syncResults.rotationAngle / 182.0f, 0.0f);
    416.         results.position = syncResults.position;
    417.         results.walking = syncResults.walking;
    418.         results.timeStamp = syncResults.timeStamp;
    419.      
    420.         //Discard out of order results
    421.         if(results.timeStamp <= lastTimeStamp)
    422.             return;
    423.  
    424.         lastTimeStamp = results.timeStamp;
    425.  
    426.         //Non-owner client
    427.         if (!isLocalPlayer /*&& !hasAuthority*/)
    428.         {
    429.             //Adding results to the results list so they can be used in interpolation process
    430.             results.timeStamp = Time.time;
    431.             ResultsList.Add(results);
    432.         }
    433.      
    434.         //Owner client
    435.         //Server client reconciliation process should be executed in order to client's rotation and position with server values but do it without jittering
    436.         if (isLocalPlayer && !hasAuthority)
    437.         {
    438.             //Update client's position and rotation with ones from server
    439.             results.rotation = results.rotation;
    440.             results.position = results.position;
    441.  
    442.             int foundIndex = -1;
    443.  
    444.             //Search recieved time stamp in client's inputs list
    445.             for(int index = 0; index < InputList.Count; index++)
    446.             {
    447.                 //If time stamp found run through all inputs starting from needed time stamp
    448.                 if(InputList[index].timeStamp > results.timeStamp)
    449.                 {
    450.                     foundIndex = index;
    451.                     break;
    452.                 }
    453.             }
    454.  
    455.             if(foundIndex == -1)
    456.             {
    457.                 //Clear Inputs list if no needed records found
    458.                 while(InputList.Count != 0)
    459.                 {
    460.                     InputList.RemoveAt(0);
    461.                 }
    462.              
    463.                 return;
    464.             }
    465.             //Replay recorded inputs
    466.             for(int subIndex = foundIndex; subIndex < InputList.Count; subIndex++)
    467.             {
    468.                 results.rotation = Rotate(InputList[subIndex],results);
    469.                 results.walking = Walk(InputList[subIndex],results);
    470.              
    471.                 results.position = Move(InputList[subIndex],results);
    472.             }
    473.  
    474.             //Remove all inputs before time stamp
    475.             int targetCount = InputList.Count - foundIndex;
    476.  
    477.             while(InputList.Count > targetCount)
    478.             {
    479.                 InputList.RemoveAt(0);
    480.             }
    481.         }
    482.     }
    483. }
     
    Last edited: Dec 30, 2015
unityunity