2

I'm working on an RTS game using mirror and ran into an issue. I'm working on auto-attack function for the units. Everything works for the host but not on clients. Any help would be very much appreciated, I've been at it for a few days already.. Help! Or at least point me in right direction here. I will optimize this a bit better later on I just need to understand why it's not working on client. I've also noticed that Client will add itself to enemies list, it seems to be ignoring "hasAuthority" check

It's a bit of a write up so I'll try to make it as understandable as possible.

Unit gets instantiated and this is the script attached to it:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;
using System.Linq;

public class UnitFiring : NetworkBehaviour
{

    [SerializeField] private Targeter targeter = null;
    [SerializeField] private GameObject projectilePrefab = null;
    [SerializeField] private Transform projectileSpawnPoint = null;
    [SerializeField] private float fireRange = 10f;
    [SerializeField] private float fireRate = 1f;
    [SerializeField] private float rotationSpeed = 20f;

    private float lastFireTime;


    //auto attack 
    [SerializeField] private Transform ownAimAtPoint = null;
    [SerializeField] private LayerMask layerMask;
    [SerializeField] private int updateFunctionFrequency = 60; //in frames
        

    [ServerCallback]
        private void Update()
        {
    
            
            if (Time.frameCount % this.updateFunctionFrequency != 0) return;
//runs update every 60 frames
            enemyColliders.RemoveAll(Collider => Collider == null);
    //if enemyCollider List has GameObject that was destroyed or null, it removes it from the list.
            
    
        Targetable target = targeter.GetTarget();
               

        if(target == null) 
        {
            ArrayDetect();
                        
            AttackUnit();
            return;  
        }
         

        if (!CanFireAtTarget()) { return; }

        //look at target
        Quaternion targetRotation =
            Quaternion.LookRotation(target.transform.position - transform.position);

        //Rotate
        transform.rotation = Quaternion.RotateTowards
            (transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);

        if(Time.time > (1 / fireRate) +lastFireTime)
        {
            Quaternion projectileRotation = Quaternion.LookRotation(
                target.GetAimAtPoint().position - projectileSpawnPoint.position);

            GameObject projectileInstance = Instantiate(
                projectilePrefab, projectileSpawnPoint.position, projectileRotation);

          
            NetworkServer.Spawn(projectileInstance, connectionToClient);
            
            
            lastFireTime = Time.time;

        }
    }
    [Client]
    private bool CanFireAtTarget()
    {
        return (targeter.GetTarget().transform.position - transform.position).sqrMagnitude 
            <= fireRange * fireRange;
    }

[SerializeField] private Collider[] colliderArray;
[SerializeField] private List<GameObject> enemyColliders;

Next is the ArrayDetect Function Detect unit with Physics.OverlapSphere, make sure it has the authority and add it to enemyCollider List, I also added layerMask to make sure it's only checking for Units.

[Server]
    private void ArrayDetect()
    {
       colliderArray = Physics.OverlapSphere(ownAimAtPoint.position, fireRange, layerMask);
       
              
       foreach (Collider collider in colliderArray)
        {
           
                Debug.Log("we hit a", collider);

                if (!collider.TryGetComponent<Targetable>(out Targetable potentialTarget))
                {

                    return;

                }


                if (potentialTarget.hasAuthority)
                {

                    return;  //if the hit target is the players, do nothing

                }

                else
                {
                    
                    enemyColliders = enemyColliders.Distinct().ToList();
                    enemyColliders.Add(collider.gameObject);
                    Debug.Log("Found an enemy", potentialTarget);
                }
            
        }
    }

Now AttackUnit Function, it will go thought enemyColliders List and set them as target to attack

[ServerCallback]
private void AttackUnit()
{   

    foreach (GameObject enemy in enemyColliders)
    {
    
        Debug.Log("We got confirmed enemy", enemy);
        //GetComponent<Targeter>().CmdSetTarget(enemy);
        targeter.CmdSetTarget(enemy);
            //attack the enemy

    }    
}

This is Targetable Script, it simply returns AimAtPoint:

public class Targetable : NetworkBehaviour
{
    [SerializeField] private Transform aimAtPoint = null;

    public Transform GetAimAtPoint()
    {
        return aimAtPoint;
    }
}

This is the CmdSetTarget Command in Targeter Script:

[Command]
   public void CmdSetTarget(GameObject targetGameObject)
    {
        if(!targetGameObject.TryGetComponent<Targetable>(out Targetable newTarget)) { return; }
        //if game object does not have a target, return

        target = newTarget;
    }

This is the error I get in the console, but only on the client Units, the host runs as it should:

Trying to send command for object without authority. Targeter.CmdSetTarget

Thanks for taking time to read this

Christoph Rackwitz
  • 11,317
  • 4
  • 27
  • 36
IntenseMX
  • 61
  • 1
  • 6
  • Your AttackUnit() is called on a Client without having the authority. Your Host doesn't bother, because its also the Server and therefore has the authority. Also, according to the documentation " [ServerCallback] Only a server can call the method but does not throw warning when called on client." This method shouldn't even be callable from a client. – Senbazuru Jan 29 '21 at 10:59
  • @Senbazuru Tagged AttackUnit() with [Client] AttackUnit() then runs the [Command] line, in theory, should work? but still the same outcome I don't want to discard this fix but I think I tried all [Server] [Client] combinations, none of them seemed to do the trick, must be something else I'm missing, so confused ahhh going back to reading Mirror docs but it's not doing much good right now – IntenseMX Jan 29 '21 at 13:06
  • 1
    @InteseMX probably take a look at the documentation for [Authority](https://mirror-networking.com/docs/Articles/Guides/Authority.html?q=give%20authority) I guess, this is what you need – Senbazuru Feb 02 '21 at 09:56
  • @Senbazuru I been reading and trying all of different authority combinations and checks, but it's still behaving very strangely, sometimes they will attack each other and another time one side will just stop and only side will attack, and after sometime client side units will just wake up and start attacking. so very random. I think it has something to do with command been used by only one client at the time. I will just have to rewrite my attack script and restructure everything again when I have better understanding, thanks for trying to figure it out! – IntenseMX Feb 02 '21 at 16:54

0 Answers0