Sync position Issue

Hello,

I’m using Character Controller and State Machine to manage the movement and Animation of my player character. The rotation is coding directly without state machine.
I’m using the tutorial of CodeMonkey Youtube channel to implement multiplayer with Unity Network for GameObject but I encounter a really early issue with syncing the position.
I host a server via a build, let’s say Build01. I move the player in this instance. Then with a second build, Build02, I’m joiining the frist player. I see the actual position of the player1 but if I moving it now, the second player can’t see him moving. And The player1 can’t see the Player 2 moving.
both player can’t see the animation either. Each player can move in its instance but can’t see the other player moving.
BUT player 1 can see the rotation of player 2 and vice versa.
So I think the problem comes from Character Controller and/or my state machine.
The player is in grounded state + Idle Substate and then, if I press WSAD key or a LeftStick Gamepad, the player pass to Grounded State + Walk Substate.

It seems like the character Controller or the State machine don’t communicate the .move function to the Network or receive a zéro vector in the .move function.

Can someone please help me ?

In the “Client Netwtok Transform” component, I’ve checked sync position in x, y and Z. Same with Rotation.

Base State :

using UnityEngine;
public abstract class CharacterBaseState
{
    private bool _isRootState = false; //Variable to tell if the concrete state is at the top of the state hierarchy
    private CharacterStateMachine _ctx;
    private CharacterStateFactory _factory;
    private CharacterBaseState _currentSubState;
    private CharacterBaseState _currentSuperState;

    protected bool IsRootState { set { _isRootState = value; } }
    protected CharacterStateMachine Ctx { get { return _ctx; } }
    protected CharacterStateFactory Factory { get { return _factory; } }

    public CharacterBaseState(CharacterStateMachine currentContext, CharacterStateFactory characterStateFactory)
    {
        _ctx = currentContext;
        _factory = characterStateFactory;
    }

    public abstract void EnterState();

    public abstract void UpdateState();

    public abstract void ExitState();

    public abstract void CheckSwitchState();

    public abstract void InitializeSubState();

    public void UpdateStates()
    {


        UpdateState();
        if (_currentSubState != null)
        {
            _currentSubState.UpdateStates();
        }

    }

    protected void SwitchState(CharacterBaseState newState)
    {


        //First we have to exit the current State
        ExitState();
        //Then we can "Enter" in a new state
        newState.EnterState();
        if (_isRootState) //We only switch tge context's current state if it's at the  top level of the state chain
        {
            //Finally the context set the "_currentState" to be the newState
            _ctx.CurrentState = newState;
        }
        else if (_currentSuperState != null)
        {
            _currentSuperState.SetSubState(newState);
        }
    }

    protected void SetSuperState(CharacterBaseState  newSuperState)
    {
        _currentSuperState = newSuperState;
    }

    protected void SetSubState(CharacterBaseState newSubState)
    {
        _currentSubState = newSubState;
        newSubState.SetSuperState(this);
    }


}

Walk State :

using UnityEngine;

public class CharacterWalkingState : CharacterBaseState
{
    //Vector use to use the right animation regarding the direction where the player is looking at and the direction input
    Vector3 VelocityAnimation;
    public CharacterWalkingState(CharacterStateMachine currentContext, CharacterStateFactory characterStateFactory)
    : base(currentContext, characterStateFactory) { }

    //Increase performance
    int VelocityXHash;
    int VelocityZHash;

    public override void EnterState()
    {
        ////Debug.Log("WELCOME TO THE WALK SUBSTATE2");
        Ctx.Animator.SetBool(Ctx.IsWalkingHash, true);
        VelocityXHash = Animator.StringToHash("Velocity X");
        VelocityZHash = Animator.StringToHash("Velocity Z");
    }

    public override void UpdateState()
    {
      

      
        //Movement applied to the character
        Ctx.AppliedMovementX = (Ctx.CurrentMovement.x * (Mathf.Cos(Ctx.CameraRotation * Mathf.PI / 180))) - (Ctx.CurrentMovement.y * (Mathf.Sin(Ctx.CameraRotation * Mathf.PI / 180)));
        Ctx.AppliedMovementZ = (Ctx.CurrentMovement.y * (Mathf.Cos(Ctx.CameraRotation * Mathf.PI / 180))) + (Ctx.CurrentMovement.x * (Mathf.Sin(Ctx.CameraRotation * Mathf.PI / 180)));
       
        float _zDirzChara = Vector3.Angle(new Vector3(0,0,Ctx.AppliedMovementZ), Ctx.TransformForwardChara); //Angle between the Z input and the forward vector of the character
        ////Debug.Log(" Angle _zDirzChara " + _zDirzChara);
        float _xDirzChara = Vector3.Angle(new Vector3(Ctx.AppliedMovementX, 0, 0), Ctx.TransformForwardChara); //Angle between the X input and the forward vector of the character
        ////Debug.Log(" Angle _xDirzChara " + _xDirzChara);
        float _xDirxChara = Vector3.Angle(new Vector3(Ctx.AppliedMovementX, 0, 0), Ctx.TransformRightChara); //Angle between the X input and the right vector of the character
        ////Debug.Log(" Angle _xDirxChara " + _xDirxChara);
        float _zDirxChara = Vector3.Angle(new Vector3(0, 0, Ctx.AppliedMovementZ), Ctx.TransformRightChara); //Angle between the Z input and the right vector of the character
        /*//Debug.Log(" Angle _zDirxChara " + _zDirxChara);
        //Debug.Log("Angle Ctx.AppliedMovementX : " + Ctx.AppliedMovementX);
        //Debug.Log("Angle Ctx.AppliedMovementZ : " + Ctx.AppliedMovementZ);
        //Debug.Log("Angle Ctx.AppliedMovementX * Mathf.Cos(_xDirxChara) : " + Ctx.AppliedMovementX * Mathf.Cos(_xDirxChara * Mathf.PI / 180));
        //Debug.Log("Angle Ctx.AppliedMovementZ * Mathf.Cos(_zDirxChara) : " + Ctx.AppliedMovementZ * Mathf.Cos(_zDirxChara * Mathf.PI / 180));
        //Debug.Log("Angle Ctx.AppliedMovementZ * Mathf.Cos(_zDirzChara) : " + Ctx.AppliedMovementZ * Mathf.Cos(_zDirzChara * Mathf.PI / 180));
        //Debug.Log("Angle Ctx.AppliedMovementX * Mathf.Cos(_xDirzChara) : " + Ctx.AppliedMovementX * Mathf.Cos(_xDirzChara * Mathf.PI / 180));
        */
        //Result on the animation of the direction Input and the direction where the character is looking at
        VelocityAnimation.x = Mathf.Abs(Ctx.AppliedMovementX) * Mathf.Cos(_xDirxChara * Mathf.PI / 180) + Mathf.Abs(Ctx.AppliedMovementZ) * Mathf.Cos(_zDirxChara * Mathf.PI / 180);
        VelocityAnimation.y = 0;
        VelocityAnimation.z = Mathf.Abs(Ctx.AppliedMovementZ) * Mathf.Cos(_zDirzChara * Mathf.PI / 180) + Mathf.Abs(Ctx.AppliedMovementX) * Mathf.Cos(_xDirzChara * Mathf.PI / 180);
        Vector3.Normalize(VelocityAnimation);
       
        Ctx.Animator.SetFloat(VelocityXHash, VelocityAnimation.x);
        Ctx.Animator.SetFloat(VelocityZHash, VelocityAnimation.z);
              
        CheckSwitchState();
    }

    public override void ExitState() { }

    public override void CheckSwitchState()
    {
        if(!Ctx.IsMovementPressed)
        {
            SwitchState(Factory.Idle());
        }
    }

    public override void InitializeSubState() { }
}

And finally the Character State Machine (Where the .Move() function is called )

using Unity.Netcode;
using Cinemachine;
using UnityEngine;
using UnityEngine.InputSystem;


//In the State Machine, this script is the "Context"

public class CharacterStateMachine : NetworkBehaviour
{
    //Declare reference variables
    PlayerInput _playerInput;
    CharacterController _charaController;
    Animator _animator;

    //Variables to store optimized setter/getter parameter Ids
    int _isMovingHash;


    //Variables to store player input values
    Vector2 _currentMovementInput = new Vector2(0, 0);
    Vector3 _currentMovement = new Vector3(0, 0, 0);
    Vector3 _appliedMovement = new Vector3(0, 0, 0);
    bool _isMovementPressed = false;
  

    //Movement variables
    public float _speed;
    public float _rotationSpeed = 15.0f;

    //Third person variable
    Vector3 _cameraRelatioveMovement;
    Quaternion lookRotation;
    Vector3 _rotationRelativeMovement;

    //Gravity variables
    //public float _groundedGravity = -0.05f;
    public float _gravity = -9.81f;

    //Jump variables
    bool _isJumpPressed = false;
    float _initialJumpVelocity;
    public float _maxJumpHeight = 0.75f;
    public float _maxJumpTime = 2f;
    bool _isJumping = false;
    int _isJumpingHash;
    int _isFallingHash;
    //bool _isJumpingAnimating = false;//No more unnecessary variables from the previous implementation, without the State one.
    bool _requireNewJumpPress;

    //State variables
    [SerializeField]
    CharacterBaseState _currentState;
    CharacterStateFactory _states;


    /*
    public bool IsPickUpPressed { get { return GetComponent<PickUpBox>().GetIsPickUpPressed(); } set { GetComponent<PickUpBox>().SetIsPickUpPressed(value); } }

    public bool IsHoldingBox {  get { return GetComponent<PickUpBox>().GetIsHoldingBox(); } set { GetComponent<PickUpBox>().SetIsHoldingBox(value); } }
    public bool IsRequireNewPickUpPress { get { return GetComponent<PickUpBox>().GetRequireNewPickUpPress(); } set { GetComponent<PickUpBox>().SetRequireNewPickUpPress(value); } }
    */
    //Camera variables
    [SerializeField] private CinemachineVirtualCamera _cam;
    bool requireNewCamRotationRightPress = false; //to force player to unpress the camRotation button to press it again to really camRotation
    bool requireNewCamRotationLeftPress = false; //to force player to unpress the camRotation button to press it again to really camRotation

    bool _isCamRotationRightPressed;
    bool _isCamRotationLeftPressed;
    float _cameraRotation;
    Vector2 mousePosition;
    public float CameraRotation { get { return _cameraRotation; } }
    bool _isLockRotToMove;
    bool requireNewLockRotToMove = true;

    //Gamepad variables
    public bool isGamepad;
    Vector2 aimGamepad;
    Vector2 aim;
    float gamepadRotateRatio = 1000f;
    bool _isLookWithRightStick;

    //Knock variables
    float _knockForce;
    Vector3 _knockVelocity;
    Vector3 _dirOfShoot;
    float _knockTime;
  
    //Recoil Impact
    float _recoilImpactForce;
    Vector3 _recoilImpactVelocity;
    Vector3 _dirOfRecoilImpact;
    float _recoilImpactTime;


    //Getter and Setter of the State variables
    public CharacterBaseState CurrentState { get { return _currentState; } set { _currentState = value; } }
    public bool IsJumpPressed { get { return _isJumpPressed; } }
    public Animator Animator { get { return _animator; } }

    public bool IsMovementPressed { get { return _isMovementPressed; } }
    public int IsWalkingHash { get { return _isMovingHash; } set { _isMovingHash = value; } }

    public float AppliedMovementX { get { return _appliedMovement.x; }  set { _appliedMovement.x = value; } }
    public float AppliedMovementZ { get { return _appliedMovement.z; } set { _appliedMovement.z = value; } }
    public Vector2 CurrentMovement { get {return _currentMovementInput; } set { _currentMovementInput = value; } }
    public bool IsJumping { set {_isJumping = value; }}
    public int IsJumpingHash { get { return _isJumpingHash; } }
    public float InitialJumpVelocity { get { return _initialJumpVelocity; } }

    public int IsFallingHash { get { return _isFallingHash; } set { _isFallingHash = value; } }

  
    public float CurrentMovementY { get { return _currentMovement.y; } set { _currentMovement.y = value; } }
    public float AppliedMovementY { get { return _appliedMovement.y; } set { _appliedMovement.y = value; } }

    public Vector3 ApplyMovement { get { return _appliedMovement; } }

  
    public float Gravity { get { return _gravity; } set { _gravity = value; } }

    public bool RequireNewJumpPress { get { return _requireNewJumpPress; } set { _requireNewJumpPress = value; } }

    public CharacterController CharaController { get { return _charaController; } set { _charaController = value; } }

    public Quaternion Rotation { get { return transform.rotation; } }

    public Vector3 TransformForwardChara { get { return transform.forward; } }
    public Vector3 TransformRightChara { get { return transform.right; } }

    public Vector3 TransformRelativeWorldToLocal { get { return transform.InverseTransformDirection(Vector3.forward); } }
    public Vector3 TransformRelativeLocalToWorld { get { return transform.TransformDirection(transform.forward); } }

    public Vector3 CameraRelativeMovement { get { return _cameraRelatioveMovement; } }
  
 
    Vector3 _positionToLookAt;

  
    void Awake()
    {
        //Inityally set reference variable
        _playerInput = new PlayerInput();
        _charaController = GetComponent<CharacterController>();
        _animator = GetComponent<Animator>();
      
        _speed = GetComponent<Stats_Character>().GetSpeed();
      
        _cam = GetComponent<VirtualCameraAssign>().GetVC();
      
    //Setup state
        _states = new CharacterStateFactory(this);
        _currentState = _states.Grounded();
        _currentState.EnterState();

        //Set the parameter hash references
        _isMovingHash = Animator.StringToHash("isMoving");
        _isJumpingHash = Animator.StringToHash("isJumping");
        _isFallingHash = Animator.StringToHash("isFalling");
  
    //Player Input
        _playerInput.CharaControls.Move.started += OnMovementInput;
        _playerInput.CharaControls.Move.canceled += OnMovementInput;
        _playerInput.CharaControls.Move.performed += OnMovementInput;
        _playerInput.CharaControls.Jump.started += OnJump;
        _playerInput.CharaControls.Jump.canceled += OnJump;
        _playerInput.CharaControls.CamRotationLeft.started += onCamRotationLeft;
        _playerInput.CharaControls.CamRotationLeft.canceled += onCamRotationLeft;
        _playerInput.CharaControls.CamRotationRight.started += onCamRotationRight;
        _playerInput.CharaControls.CamRotationRight.canceled += onCamRotationRight;
        _playerInput.CharaControls.Look.started += onRotationInput;
        _playerInput.CharaControls.Look.canceled += onRotationInput;
        _playerInput.CharaControls.Look.performed += onRotationInput;
        _playerInput.CharaControls.LockRotToMove.started += onLockRotToMove;
        _playerInput.CharaControls.LockRotToMove.canceled += onLockRotToMove;

        SetupJumpVariables();

    }

    //Set the intial velocity and gravity to use jump height and duration
    void SetupJumpVariables()
    {
        float timeToApex = _maxJumpTime / 2;
        _gravity = (-2 * _maxJumpHeight) / Mathf.Pow(timeToApex, 2);
        _initialJumpVelocity = (2 * _maxJumpHeight) / timeToApex;
    }

    // Start is called before the first frame update
    void Start()
    {
      
        if (_speed <= 0)
        {
            _speed = GetComponent<Stats_Character>().GetSpeed();
        }
    }

    // Update is called once per frame
    void Update()
    {
      
        if (!IsOwner)
        {

            return;
        }

        if (_cam == null)
        {
            _cam = GetComponent<VirtualCameraAssign>().GetVC();
        }
        HandleRotation();



        HandleMove();

        _currentState.UpdateStates();
    }



    private void HandleMove()
    {

        if(_knockTime > 0) //When player shoot, the character receive a force that make him get backward
        {
            _knockTime -= Time.deltaTime;
            _knockVelocity = _dirOfShoot * (_knockForce * _knockTime);
          
        }
        else if (_knockTime <= 0)
        {
            _knockVelocity = Vector3.zero;
        }

        if (_recoilImpactTime > 0) // When player receive damage, the character receive a force that get him go to the direction opposite of where the force come from
        {
            _recoilImpactTime -= Time.deltaTime;
            _recoilImpactVelocity = _dirOfRecoilImpact * (_recoilImpactForce * _recoilImpactTime);
        }
        else if (_recoilImpactTime <= 0)
        {
            _recoilImpactVelocity = Vector3.zero;
        }

      

        _charaController.Move((_appliedMovement+_knockVelocity + _recoilImpactVelocity) * _speed * Time.deltaTime);

    }



    void HandleRotation()
    {

        //Vector3 _positionToLookAt;
        //Verify if the player is playing with a Gamepad
        if (isGamepad)
        {
            //Debug.Log("Mathf.Abs(aim.x) : " + Mathf.Abs(aim.x));
            // /Debug.Log("Mathf.Abs(aim.y) : " + Mathf.Abs(aim.y));
            //Debug.Log("controllerDeadzone : " + controllerDeadzone);
            //Debug.Log("isLookWithRightStick : " + _isLookWithRightStick);
            //If the player is orienting the right joystick, the character will look to the direction poiting by this joystick
            if (_isLookWithRightStick)
            {
                aimGamepad.x = /*_isRunPressed ? */(aim.x) * (Mathf.Cos(_cameraRotation * Mathf.PI / 180)) - (aim.y) * (Mathf.Sin(_cameraRotation * Mathf.PI / 180));/* : _currentMovementInput.x;*/
                aimGamepad.y = /*_isRunPressed ? */(aim.y) * (Mathf.Cos(_cameraRotation * Mathf.PI / 180)) + (aim.x) * (Mathf.Sin(_cameraRotation * Mathf.PI / 180));/* : _currentMovementInput.y;*/

                Vector3 playerDirection = Vector3.right * aimGamepad.x + Vector3.forward * aimGamepad.y;
                if (playerDirection.sqrMagnitude > 0.0f)
                {
                    Quaternion newRotation = Quaternion.LookRotation(playerDirection, Vector3.up);
                    transform.rotation = Quaternion.RotateTowards(transform.rotation, newRotation, gamepadRotateRatio * Time.deltaTime);
                }
            }
            //If Player doesn't use the Joystick, the character will look in the direction of the movement
            else if (!_isLookWithRightStick)
            {
                _positionToLookAt.x = (_currentMovementInput.x * (Mathf.Cos(_cameraRotation * Mathf.PI / 180))) - (_currentMovementInput.y * (Mathf.Sin(_cameraRotation * Mathf.PI / 180))); //_cameraRelatioveMovement.x instead of _currentMovementInput.x for a third person view
                _positionToLookAt.y = 0.0f;
                _positionToLookAt.z = (_currentMovementInput.y * (Mathf.Cos(_cameraRotation * Mathf.PI / 180))) + (_currentMovementInput.x * (Mathf.Sin(_cameraRotation * Mathf.PI / 180))); ;  //_cameraRelatioveMovement.y instead of _currentMovementInput.y for a third person view
              
                Quaternion _currentRotation = transform.rotation;

                if (_isMovementPressed)
                {
                    //Create a new rotation based on where the player is currently pressing
                    Quaternion _targetRotation = Quaternion.LookRotation(_positionToLookAt);
                    transform.rotation = Quaternion.Slerp(_currentRotation, _targetRotation, _rotationSpeed * Time.deltaTime);
                }
            }
          
        }
        //If player is using a keyboard
        else if (!isGamepad)
        {

            //If RequireNewLockRotToMove is true the player will look to direction of the mouse
            if (requireNewLockRotToMove)
            {
                Debug.Log("2 Cam : " + _cam.transform.name);
                Ray ray = Camera.main.ScreenPointToRay(mousePosition);
              
                if (Physics.Raycast(ray, out RaycastHit raycastHit, 100.0f, ~(1<<11)))
                {
                   _positionToLookAt = raycastHit.point;
                   //_positionToLookAt.y = 1.5f;

                   LookAt(_positionToLookAt);
                }
            }
            //If RequireNewLockRotToMove is false, the character will look in the direction of the movement
            else if (!requireNewLockRotToMove)
            {
                Debug.Log("4 Cam : " + _cam.transform.name);
                _positionToLookAt.x = (_currentMovementInput.x * (Mathf.Cos(_cameraRotation * Mathf.PI / 180))) - (_currentMovementInput.y * (Mathf.Sin(_cameraRotation * Mathf.PI / 180))); //_cameraRelatioveMovement.x instead of _currentMovementInput.x for a third person view
                _positionToLookAt.y = 0.0f;
                _positionToLookAt.z = (_currentMovementInput.y * (Mathf.Cos(_cameraRotation * Mathf.PI / 180))) + (_currentMovementInput.x * (Mathf.Sin(_cameraRotation * Mathf.PI / 180))); ;  //_cameraRelatioveMovement.y instead of _currentMovementInput.y for a third person view

                Quaternion _currentRotation = transform.rotation;

                if (_isMovementPressed)
                {
                    //Create a new rotation based on where the player is currently pressing
                    Quaternion _targetRotation = Quaternion.LookRotation(_positionToLookAt);
                    transform.rotation = Quaternion.Slerp(_currentRotation, _targetRotation, _rotationSpeed * Time.deltaTime);
                }
            }
            //Debug.Log("Camera Rotation : " + _cameraRotation);
        }
    }

  
  
    public void LookAt(Vector3 lookPoint) // Use to have the player looking where the mouse is
    {
        Vector3 direction = (lookPoint - transform.position);
      
        lookRotation = Quaternion.LookRotation(new Vector3(direction.x, 0.0f, direction.z));
      
      
        transform.rotation = Quaternion.Slerp(transform.rotation, lookRotation, Time.deltaTime * _rotationSpeed);
    }

    void OnMovementInput(InputAction.CallbackContext context) //Use to detect the WSAD or leftstick input
    {

        _currentMovementInput = context.ReadValue<Vector2>();
        _currentMovement.x = _currentMovementInput.x;
        _currentMovement.z = _currentMovementInput.y;
        _isMovementPressed = _currentMovementInput.x != 0 || _currentMovementInput.y != 0;
    }
    void OnJump(InputAction.CallbackContext context) //Use to detect jump button pressed
    {
        _isJumpPressed = context.ReadValueAsButton();
        _requireNewJumpPress = false;
    }

    void onCamRotationRight(InputAction.CallbackContext context) //Rotate the camera around player anti-clockwise
    {

        _isCamRotationRightPressed = context.ReadValueAsButton();
        requireNewCamRotationRightPress = false;
        _cameraRotation = _cam.GetComponent<CameraControl>().GetCameraRotation();


    }
    void onCamRotationLeft(InputAction.CallbackContext context) //Rotate the Camera around player clockwise
    {

        _isCamRotationLeftPressed = context.ReadValueAsButton();
        requireNewCamRotationLeftPress = false;
        _cameraRotation = _cam.GetComponent<CameraControl>().GetCameraRotation();


    }

    void onRotationInput(InputAction.CallbackContext context) //If player use the right stick, the character  will look ind the direction of the stick and if player don't use the right stick, the character will look in the direction of the movement
    {
        mousePosition = context.ReadValue<Vector2>();
        aim = context.ReadValue<Vector2>();
      
        if (context.performed && isGamepad)
        {
            _isLookWithRightStick = true;
        }
        else if (context.canceled && isGamepad)
        {
            _isLookWithRightStick = false;
        }
    }

    void onLockRotToMove(InputAction.CallbackContext context)
    {
        _isLockRotToMove = context.ReadValueAsButton();
        if (_isLockRotToMove && requireNewLockRotToMove)
        {
            ////Debug.Log("Require new lock to move 1 : " + requireNewLockRotToMove);
            requireNewLockRotToMove = false;
            ////Debug.Log("Require new lock to move 2 : " + requireNewLockRotToMove);
            return;
        }
        else if (_isLockRotToMove && !requireNewLockRotToMove)
        {
            ////Debug.Log("Require new lock to move 3 : " + requireNewLockRotToMove);
            requireNewLockRotToMove = true;
            ////Debug.Log("Require new lock to move 4 : " + requireNewLockRotToMove);
            return;
        }
      
    }

    //Setting of knock due shooting
    public void SetKnockVariables(float knockForce, float knocTime)
    {
        _knockForce = knockForce;
        _knockTime = knocTime;
        _dirOfShoot = -transform.forward;
    }

    public void SetImpactRecoil(float impactRecoilForce, float impactRecoilTime, Vector3 impactDir)
    {

        _recoilImpactForce = impactRecoilForce;
        _recoilImpactTime = impactRecoilTime;
        _dirOfRecoilImpact = impactDir;

    }



    void OnEnable()
    {
        //Enable the chara controls action map
        _playerInput.CharaControls.Enable();
    }

    void OnDisable()
    {
        //Disable the chara controls action map
        _playerInput.CharaControls.Disable();
    }
}

I forget to say that the “Character Controller” component of player1 is detected by player2 and vice versa because Player 2 can’t go through player 1, there is bodyblock and that’s what I want .

Please can someone help me ?

That’s a lot of code we don’t need to read.

NetworkTransform has no client authority by default. See here for a solution.

To add to what LaneFox posted,
With CharacterController you will want to disable the CharacterController on non-owners you will want to disable the CharacterController. Personally, I typically handle this in the owner/client authoritative NetworkTransform and that would look something like:

    [DisallowMultipleComponent]
    public class ClientNetworkTransform : NetworkTransform
    {
        private CharacterController m_CharacterController;

        private void Awake()
        {
            m_CharacterController = GetComponent<CharacterController>();
        }
        /// <summary>
        /// Used to determine who can write to this transform. Owner client only.
        /// This imposes state to the server. This is putting trust on your clients. Make sure no security-sensitive features use this transform.
        /// </summary>
        protected override bool OnIsServerAuthoritative()
        {
            return false;
        }

        public override void OnNetworkSpawn()
        {
            if (!IsOwner)
            {
                m_CharacterController.enabled = false;
            }
            base.OnNetworkSpawn();
        }
    }