Network lag Causes enemy player to die twice.

Options
Nonlin
Nonlin
Basically if a person can shoot faster than Photon can report back that the target has died the shooter gets an extra kill as a result of not knowing soon enough that the player has died.

Here is the code I use and I make sure to check the targets health which doesn't seem to matter because again if they can shoot faster than the network can resolve it would seem to allow them to stack a few extra kills. I'm sure this is a known issue but as my first go of it I'm not sure how to address this.
if(Physics.Raycast(transform.position, transform.forward, out hit, 50f)){

				if(hit.transform.tag == "Player"){

					if(hit.transform.GetComponent<PlayerNetworkMover>().GetHealth() > 0){

						//Tell all we shot a player and call the RPC function GetShot passing damage runs on person shooting
						hit.transform.GetComponent<PhotonView>().RPC ("GetShot", PhotonTargets.All, damage, PhotonNetwork.player); 
						Debug.Log ("<color=red>Target Health</color> " + hit.transform.GetComponent<PlayerNetworkMover>().GetHealth());
					}
				}

Comments

  • vadim
    Options
    How do you count kills?
    If you do it only on confirmation form target that has died, you wold not get extra kills.
  • dontonka
    Options
    Hello, vadim is right, in my FPS I have the same use case.

    When a player hits another one, the "killer" send the damage for the hit player to everybody, and the local player being damaged is the only one that will check if he is dead or not, and if he is dead, he will RPCs his death to all others players.

    This is working fine, but I'm aware about cheating thought, that is why I want to send those critical RPC as encrypted over the network at least.

    Regards,
    Don T.
  • Nonlin
    Options
    Yes I do have all that done on the target's side and I still sometimes can get an extra kill. Here is the code for that.
    [RPC]
    	public void GetShot(float damage, PhotonPlayer enemy){
    		//Take Damage and check for death
    		health -= damage;
    		if(health <=0){
    			Debug.Log ("<color=blue>Checking Health</color>" + health);
    			if (photonView.isMine) {
    				//Tell all enemies HEY Were Dead! So they can't shoot us again
    				//NM.GetComponent<PhotonView>().RPC ("TargetHealthCheck",PhotonTargets.All, health); 
    
    				if(SendNetworkMessage != null){
    					SendNetworkMessage(PhotonNetwork.player.name + " got owned by " + enemy.name);
    				}
    				//Subscribe to the event so that when a player dies 3 sec later respawn
    				if(RespawnMe != null)
    					RespawnMe(3f);
    				//Only owner can remove themselves
    				//Create deaths equal to stored hashtable deaths, increment, Set
    				int totalDeaths = (int)PhotonNetwork.player.customProperties["D"];
    				totalDeaths ++;
    				ExitGames.Client.Photon.Hashtable setPlayerDeaths = new ExitGames.Client.Photon.Hashtable() {{"D", totalDeaths}};
    				PhotonNetwork.player.SetCustomProperties(setPlayerDeaths);
    				//Destroy Object on network
    				PhotonNetwork.Destroy(gameObject);
    			}
    			else{
    
    				if(PhotonNetwork.player == enemy){
    
    					int totalKIlls = (int)PhotonNetwork.player.customProperties["K"];
    					totalKIlls ++;
    					ExitGames.Client.Photon.Hashtable setPlayerKills = new ExitGames.Client.Photon.Hashtable() {{"K", totalKIlls}};
    					Debug.Log ("<color=red>KillCounter Called at </color>" + totalKIlls);
    					PhotonNetwork.player.SetCustomProperties(setPlayerKills);
    				}
    			}
    		}
    	}
    

    The only thing I can see happening is that Target Destroy's itself but before that info can get sent to the shooter shooter shoots again and calls the same GetShot method calling all of it twice.
  • Nonlin
    Options
    So I tried having it disable the game object after it executes all the counting of kills/deaths.

    It seems much more reliable this way. Since you can't shoot what isn't there. Have yet to see it double a score but I'll keep testing.

    However I did see it fail to count a kill. Death still counter but the kill didn't register. Not sure why... Code for that part is.
    [RPC]
    	public void GetShot(float damage, PhotonPlayer enemy){
    		//Take Damage and check for death
    		 
    		health -= damage;
    		if(health <=0){
    			Debug.Log ("<color=blue>Checking Health</color>" + health);
    			if (photonView.isMine) {
    
    				if(SendNetworkMessage != null){
    					SendNetworkMessage(PhotonNetwork.player.name + " got owned by " + enemy.name);
    				}
    				//Subscribe to the event so that when a player dies 3 sec later respawn
    				if(RespawnMe != null)
    					RespawnMe(3f);
    				//Only owner can remove themselves
    				//Create deaths equal to stored hashtable deaths, increment, Set
    				int totalDeaths = (int)PhotonNetwork.player.customProperties["D"];
    				totalDeaths ++;
    				ExitGames.Client.Photon.Hashtable setPlayerDeaths = new ExitGames.Client.Photon.Hashtable() {{"D", totalDeaths}};
    				PhotonNetwork.player.SetCustomProperties(setPlayerDeaths);
    				//Destroy Object on network
    				Debug.Log ("<color=green> Collider State After</color>"+transform.GetComponent<Collider>().enabled.ToString());
    				PhotonNetwork.Destroy(gameObject);
    
    			}
    			else{
    
    				if(PhotonNetwork.player == enemy){
    
    					int totalKIlls = (int)PhotonNetwork.player.customProperties["K"];
    					totalKIlls ++;
    					ExitGames.Client.Photon.Hashtable setPlayerKills = new ExitGames.Client.Photon.Hashtable() {{"K", totalKIlls}};
    					Debug.Log ("<color=red>KillCounter Called at </color>" + totalKIlls);
    					PhotonNetwork.player.SetCustomProperties(setPlayerKills);
    
    				}
    			}
    			transform.GetComponent<Collider>().enabled = false; 
    			gameObject.SetActive(false);
    		}
    	}
    
  • vadim
    Options
    Still not all done on target side in your code. Kills counted on shooter side again.
    Send GetShot to target only. If killed, let target send confirmation RPC to shooter ('enemy' parameter).
    And move code from f(PhotonNetwork.player == enemy) { ...}" block to handler of that confirmation.
  • Nonlin
    Options
    So if RPC method GetShot is executed only on the target that gets shot, that kill code is executing on the shooter? You've confused me.

    I think I get the rest. Basically have the kill count execute on the shooters end. That makes sense. I'll try making that tweak.

    EDIT:
    I feel so silly because I void quite a viable and simple solution. I just made a bool variable called Alive. While alive is true it will call the death part then set it to false so it can't be called again. This works. However in my testing I did notice it not count a kill one time and I'm not sure why that would happen.
  • vadim
    Options
    New variable is not even required. Just check health < 0 before updating health. But this does not eliminate concurrency issues but tries to fix consequences.
    You need run all health logic on target side and all kill count logic on attacker's. And kill count logic should be triggered by health logic with RPC (agree, my previous post was a bit confusing)