using System; using System.Collections; using System.Collections.Generic; using System.Linq; using ExitGames.Client.Photon; using Photon.Pun; using Photon.Realtime; using ShiningGames.UFS2; using UFS2.Gameplay; using UnityEngine; public class MultiplayerFishSpawner : MonoBehaviour, IOnEventCallback { public class NetworkedFish { public string guid; public int fishID; public float fishWeight; public FishEntity fishEntity; public Vector3 position; public int targetAreaID; public int actorInControl; public MultiplayerFishEntity multiplayerFishEntity; public Guid Guid => new Guid(guid); public NetworkedFish(string guid, int fishID, float fishWeight) { this.guid = guid; this.fishID = fishID; this.fishWeight = fishWeight; fishEntity = null; position = Vector3.zero; targetAreaID = 0; actorInControl = -1; multiplayerFishEntity = null; } } public static MultiplayerFishSpawner Instance; [SerializeField] [Tooltip("Delay in seconds between sending fish synchronization packets to slave clients")] [Range(0f, 1f)] private float fishSyncDelay = 0.2f; [SerializeField] [Tooltip("Number of fishes contained in one synchronization packet")] [Range(1f, 30f)] private int fishSyncCount = 3; [SerializeField] private MultiplayerFishEntity multiplayerFishEntity_prefab; private List networkedFishList; private Coroutine fishSyncCoroutine; public static bool IsOnline => GetIsOnline(); public static bool IsMaster => GetIsMaster(); public static bool IsSlave => GetIsSlave(); public static event Action OnNetworkedFishSpawn; public static event Action OnNetworkedFishDelete; public static event Action OnFishDeleteAfterInteraction; public static bool GetIsOnline() { if (Instance != null) { return MultiplayerManager.InRoomLocationStatic; } return false; } public static bool GetIsMaster() { bool result = false; if (GetIsOnline()) { result = PhotonNetwork.IsMasterClient; } return result; } public static bool GetIsSlave() { bool result = false; if (GetIsOnline()) { result = !PhotonNetwork.IsMasterClient; } return result; } public static void CheckFishInteractionWithPlayer(FishEntity fishEntity, out bool withLocalPlayer, out bool withRemotePlayer) { withLocalPlayer = false; withRemotePlayer = false; NetworkedFish networkedFish = GetNetworkedFish(fishEntity); if (networkedFish?.multiplayerFishEntity != null) { PhotonView photonView = networkedFish.multiplayerFishEntity.photonView; withLocalPlayer = photonView.IsMine; withRemotePlayer = !photonView.IsMine; } } public static bool CheckFishInteractionWithPlayer(FishEntity fishEntity) { return GetNetworkedFish(fishEntity)?.multiplayerFishEntity != null; } public static NetworkedFish GetNetworkedFish(FishEntity fishEntity) { NetworkedFish result = null; if (Instance != null && Instance.networkedFishList != null) { result = Instance.networkedFishList.FirstOrDefault((NetworkedFish fish) => fish.fishEntity == fishEntity); } return result; } public static NetworkedFish GetNetworkedFish(string guid) { NetworkedFish result = null; if (Instance != null && Instance.networkedFishList != null) { result = Instance.networkedFishList.FirstOrDefault((NetworkedFish fish) => fish.guid == guid); } return result; } public static void FishControlRequest(FishEntity fishEntity) { NetworkedFish networkedFish = GetNetworkedFish(fishEntity); PhotonNetwork.RaiseEvent(2, new object[1] { networkedFish.Guid.ToByteArray() }, new RaiseEventOptions { Receivers = ReceiverGroup.MasterClient }, SendOptions.SendReliable); } public static void FishControlRequestCancel(FishEntity fishEntity) { NetworkedFish networkedFish = GetNetworkedFish(fishEntity); PhotonNetwork.RaiseEvent(3, new object[1] { networkedFish.Guid.ToByteArray() }, new RaiseEventOptions { Receivers = ReceiverGroup.MasterClient }, SendOptions.SendReliable); } public static MultiplayerFishEntity SpawnMultiplayerFishEntity(FishEntity fishEntity) { MultiplayerFishEntity multiplayerFishEntity = null; NetworkedFish networkedFish = GetNetworkedFish(fishEntity); if (!CheckFishInteractionWithPlayer(fishEntity) && networkedFish.actorInControl == PhotonNetwork.LocalPlayer.ActorNumber) { multiplayerFishEntity = PhotonNetwork.Instantiate(Instance.multiplayerFishEntity_prefab.name, fishEntity.transform.position, fishEntity.transform.rotation, 0).GetComponent(); multiplayerFishEntity.Init(networkedFish); } return multiplayerFishEntity; } private void FishSpawner_OnDespawnFish(FishEntity fishEntity) { if (IsMaster) { NetworkedFish networkedFish = GetNetworkedFish(fishEntity); if (networkedFish != null) { networkedFishList.Remove(networkedFish); PhotonNetwork.RaiseEvent(1, new object[1] { networkedFish.Guid.ToByteArray() }, RaiseEventOptions.Default, SendOptions.SendReliable); } } } private void FishSpawner_OnSpawnFish(FishEntity fishEntity, int levelFishDataIndex) { if (IsMaster) { NetworkedFish networkedFish = new NetworkedFish(Guid.NewGuid().ToString(), levelFishDataIndex, fishEntity.Weight); networkedFish.fishEntity = fishEntity; networkedFish.position = fishEntity.transform.position; networkedFish.targetAreaID = FishTargetBaker.Instance.Areas.IndexOf(fishEntity.CurrentArea); networkedFish.actorInControl = -1; networkedFishList.Add(networkedFish); SendFishUpdateEvent(networkedFish); } } private void SceneLoader_OnBeginSceneLoad(string sceneLoadName) { networkedFishList = new List(); } private void SceneLoader_OnSceneLoaded(string sceneLoadName) { ToggleFishSyncCoroutine(MultiplayerManager.InRoomLocationStatic); } private void Awake() { if (Instance != null && Instance != this) { UnityEngine.Object.Destroy(base.gameObject); } else { Instance = this; } } private void OnEnable() { PhotonNetwork.AddCallbackTarget(this); FishSpawner.OnSpawnFish += FishSpawner_OnSpawnFish; FishSpawner.OnDespawnFish += FishSpawner_OnDespawnFish; SceneLoader.OnBeginSceneLoad += SceneLoader_OnBeginSceneLoad; SceneLoader.OnSceneLoaded += SceneLoader_OnSceneLoaded; } private void OnDisable() { PhotonNetwork.RemoveCallbackTarget(this); FishSpawner.OnSpawnFish -= FishSpawner_OnSpawnFish; FishSpawner.OnDespawnFish -= FishSpawner_OnDespawnFish; SceneLoader.OnBeginSceneLoad -= SceneLoader_OnBeginSceneLoad; SceneLoader.OnSceneLoaded -= SceneLoader_OnSceneLoaded; } private void Event_FishUpdate(string guid, int fishID, float fishWeight, Vector3 position, int targetAreaID, int actorInControl) { if (!IsSlave) { return; } NetworkedFish networkedFish = GetNetworkedFish(guid); if (networkedFish != null) { networkedFish.position = position; networkedFish.targetAreaID = targetAreaID; networkedFish.fishEntity.CurrentArea = FishTargetBaker.Instance.Areas[targetAreaID]; networkedFish.actorInControl = actorInControl; if (fishWeight != networkedFish.fishWeight) { networkedFish.fishWeight = fishWeight; networkedFish.fishEntity.TryGrowFish(fishWeight); } } else { NetworkedFish networkedFish2 = new NetworkedFish(guid, fishID, fishWeight); networkedFish2.position = position; networkedFish2.targetAreaID = targetAreaID; networkedFish2.actorInControl = actorInControl; networkedFishList.Add(networkedFish2); MultiplayerFishSpawner.OnNetworkedFishSpawn?.Invoke(networkedFish2); } } private void Event_FishDelete(string guid) { if (IsSlave) { NetworkedFish networkedFish = GetNetworkedFish(guid); if (networkedFish != null) { networkedFishList.Remove(networkedFish); MultiplayerFishSpawner.OnNetworkedFishDelete?.Invoke(networkedFish); } } } private void Event_FishControlRequest(string guid, int sender) { if (IsMaster) { NetworkedFish networkedFish = GetNetworkedFish(guid); if (networkedFish != null && PhotonNetwork.CurrentRoom.Players.TryGetValue(sender, out var requestingPlayer) && !networkedFishList.Exists((NetworkedFish element) => element.actorInControl == requestingPlayer.ActorNumber) && !CheckFishInteractionWithPlayer(networkedFish.fishEntity) && networkedFish.actorInControl == -1) { networkedFish.actorInControl = requestingPlayer.ActorNumber; SendFishUpdateEvent(networkedFish); } } } private void Event_FishControlRequestCancel(string guid, int sender) { if (IsMaster) { NetworkedFish networkedFish = GetNetworkedFish(guid); if (networkedFish != null && PhotonNetwork.CurrentRoom.Players.TryGetValue(sender, out var value) && !CheckFishInteractionWithPlayer(networkedFish.fishEntity) && networkedFish.actorInControl == value.ActorNumber) { networkedFish.actorInControl = -1; SendFishUpdateEvent(networkedFish); } } } private void Event_FishDeleteAfterInteraction(string guid) { if (IsMaster) { NetworkedFish networkedFish = GetNetworkedFish(guid); if (networkedFish != null && networkedFish.fishEntity != null) { FishSpawner.Despawn(networkedFish.fishEntity); UnityEngine.Object.Destroy(networkedFish.fishEntity.gameObject); MultiplayerFishSpawner.OnFishDeleteAfterInteraction?.Invoke(); } } } public void OnEvent(EventData photonEvent) { switch (photonEvent.Code) { case 0: { object[] obj = (object[])photonEvent.CustomData; byte[][] array = (byte[][])obj[0]; byte[] array2 = (byte[])obj[1]; float[] array3 = (float[])obj[2]; Vector3[] array4 = (Vector3[])obj[3]; byte[] array5 = (byte[])obj[4]; byte[] array6 = (byte[])obj[5]; for (int i = 0; i < array.Length; i++) { if (!array[i].All((byte b) => b == 0)) { string guid5 = new Guid(array[i]).ToString(); Event_FishUpdate(guid5, array2[i], array3[i], array4[i], array5[i], array6[i]); } } break; } case 1: { string guid4 = new Guid((byte[])((object[])photonEvent.CustomData)[0]).ToString(); Event_FishDelete(guid4); break; } case 2: { string guid3 = new Guid((byte[])((object[])photonEvent.CustomData)[0]).ToString(); Event_FishControlRequest(guid3, photonEvent.Sender); break; } case 3: { string guid2 = new Guid((byte[])((object[])photonEvent.CustomData)[0]).ToString(); Event_FishControlRequestCancel(guid2, photonEvent.Sender); break; } case 4: { string guid = new Guid((byte[])((object[])photonEvent.CustomData)[0]).ToString(); Event_FishDeleteAfterInteraction(guid); break; } } } private void SendFishUpdateEvent(NetworkedFish networkedFish) { if (IsMaster) { networkedFish.position = networkedFish.fishEntity.transform.position; networkedFish.targetAreaID = FishTargetBaker.Instance.Areas.IndexOf(networkedFish.fishEntity.CurrentArea); byte[][] array = new byte[1][] { networkedFish.Guid.ToByteArray() }; byte[] array2 = new byte[1] { (byte)networkedFish.fishID }; float[] array3 = new float[1] { networkedFish.fishWeight }; Vector3[] array4 = new Vector3[1] { networkedFish.position }; byte[] array5 = new byte[1] { (byte)networkedFish.targetAreaID }; byte[] array6 = new byte[1] { (byte)networkedFish.actorInControl }; PhotonNetwork.RaiseEvent(0, new object[6] { array, array2, array3, array4, array5, array6 }, RaiseEventOptions.Default, SendOptions.SendReliable); } } private void ToggleFishSyncCoroutine(bool toggle = false) { if (fishSyncCoroutine != null) { StopCoroutine(fishSyncCoroutine); fishSyncCoroutine = null; } if (toggle) { fishSyncCoroutine = StartCoroutine(FishSyncCoroutine()); } } private void ValidateFishes() { List list = new List(); foreach (NetworkedFish networkedFish in networkedFishList) { if (networkedFish.actorInControl != -1 && !PhotonNetwork.CurrentRoom.Players.ContainsKey(networkedFish.actorInControl)) { networkedFish.actorInControl = -1; } if (networkedFish.fishEntity == null) { list.Add(networkedFish); } } list.ForEach(delegate(NetworkedFish element) { networkedFishList.Remove(element); }); } private IEnumerator FishSyncCoroutine() { int index = 0; while (true) { yield return new WaitForSeconds(fishSyncDelay); if (!IsMaster || MultiplayerManager.Instance.CurrentState != MultiplayerManager.State.InRoomLocation || networkedFishList.Count <= 0) { continue; } byte[][] array = Enumerable.Repeat(Enumerable.Repeat((byte)0, 16).ToArray(), fishSyncCount).ToArray(); byte[] array2 = new byte[fishSyncCount]; float[] array3 = new float[fishSyncCount]; Vector3[] array4 = new Vector3[fishSyncCount]; byte[] array5 = new byte[fishSyncCount]; byte[] array6 = new byte[fishSyncCount]; for (int i = 0; i < fishSyncCount; i++) { if (index >= networkedFishList.Count) { index = 0; } NetworkedFish networkedFish = networkedFishList[index]; if (!CheckFishInteractionWithPlayer(networkedFish.fishEntity)) { networkedFish.fishWeight = networkedFish.fishEntity.Weight; networkedFish.position = networkedFish.fishEntity.transform.position; networkedFish.targetAreaID = FishTargetBaker.Instance.Areas.IndexOf(networkedFish.fishEntity.CurrentArea); array[i] = networkedFish.Guid.ToByteArray(); array2[i] = (byte)networkedFish.fishID; array3[i] = networkedFish.fishWeight; array4[i] = networkedFish.position; array5[i] = (byte)networkedFish.targetAreaID; array6[i] = (byte)networkedFish.actorInControl; } int num = index + 1; index = num; } PhotonNetwork.RaiseEvent(0, new object[6] { array, array2, array3, array4, array5, array6 }, RaiseEventOptions.Default, SendOptions.SendReliable); ValidateFishes(); } } }