using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; using HG; using UnityEngine; using UnityEngine.Networking; namespace RoR2; [DisallowMultipleComponent] public class MinionOwnership : NetworkBehaviour { public class MinionGroup : IDisposable { private class MinionGroupDestroyer : MonoBehaviour { public MinionGroup group; private void OnDestroy() { group.Dispose(); group.refCount--; } } private static readonly List instancesList = new List(); public readonly NetworkInstanceId ownerId; private MinionOwnership[] _members; private int _memberCount; private int refCount; private bool resolved; private GameObject resolvedOwnerGameObject; private CharacterMaster resolvedOwnerMaster; public MinionOwnership[] members => _members; public int memberCount => _memberCount; public bool isMinion => ownerId != NetworkInstanceId.Invalid; public static MinionGroup FindGroup(NetworkInstanceId ownerId) { foreach (MinionGroup instances in instancesList) { if (instances.ownerId == ownerId) { return instances; } } return null; } public static void SetMinionOwner(MinionOwnership minion, NetworkInstanceId ownerId) { if (minion.group != null) { if (minion.group.ownerId == ownerId) { return; } RemoveMinion(minion.group.ownerId, minion); } if (ownerId != NetworkInstanceId.Invalid) { AddMinion(ownerId, minion); } } private static void AddMinion(NetworkInstanceId ownerId, MinionOwnership minion) { MinionGroup minionGroup = null; for (int i = 0; i < instancesList.Count; i++) { MinionGroup minionGroup2 = instancesList[i]; if (instancesList[i].ownerId == ownerId) { minionGroup = minionGroup2; break; } } if (minionGroup == null) { minionGroup = new MinionGroup(ownerId); } minionGroup.AddMember(minion); minionGroup.AttemptToResolveOwner(); CharacterMaster component = minion.GetComponent(); if ((bool)component) { component.inventory.GiveItem(RoR2Content.Items.MinionLeash); } } private static void RemoveMinion(NetworkInstanceId ownerId, MinionOwnership minion) { CharacterMaster component = minion.GetComponent(); if ((bool)component) { component.inventory.RemoveItem(RoR2Content.Items.MinionLeash); } MinionGroup minionGroup = null; for (int i = 0; i < instancesList.Count; i++) { MinionGroup minionGroup2 = instancesList[i]; if (instancesList[i].ownerId == ownerId) { minionGroup = minionGroup2; break; } } if (minionGroup == null) { throw new InvalidOperationException(string.Format("{0}.{1} Could not find group to which {2} belongs", "MinionGroup", "RemoveMinion", minion)); } minionGroup.RemoveMember(minion); if (minionGroup.refCount == 0 && !minionGroup.resolvedOwnerGameObject) { minionGroup.Dispose(); } } private MinionGroup(NetworkInstanceId ownerId) { this.ownerId = ownerId; _members = new MinionOwnership[4]; _memberCount = 0; instancesList.Add(this); } public void Dispose() { for (int num = _memberCount - 1; num >= 0; num--) { RemoveMemberAt(num); } instancesList.Remove(this); } private void AttemptToResolveOwner() { if (resolved) { return; } resolvedOwnerGameObject = Util.FindNetworkObject(ownerId); if ((bool)resolvedOwnerGameObject) { resolved = true; resolvedOwnerMaster = resolvedOwnerGameObject.GetComponent(); resolvedOwnerGameObject.AddComponent().group = this; refCount++; for (int i = 0; i < _memberCount; i++) { _members[i].HandleOwnerDiscovery(resolvedOwnerMaster); } } } public void AddMember(MinionOwnership minion) { ArrayUtils.ArrayAppend(ref _members, ref _memberCount, in minion); refCount++; minion.HandleGroupDiscovery(this); if ((bool)resolvedOwnerMaster) { minion.HandleOwnerDiscovery(resolvedOwnerMaster); } } public void RemoveMember(MinionOwnership minion) { RemoveMemberAt(Array.IndexOf(_members, minion)); refCount--; } private void RemoveMemberAt(int i) { MinionOwnership obj = _members[i]; ArrayUtils.ArrayRemoveAt(_members, ref _memberCount, i); obj.HandleOwnerDiscovery(null); obj.HandleGroupDiscovery(null); } [ConCommand(commandName = "minion_dump", flags = ConVarFlags.None, helpText = "Prints debug information about all active minion groups.")] private static void CCMinionPrint(ConCommandArgs args) { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < instancesList.Count; i++) { MinionGroup minionGroup = instancesList[i]; stringBuilder.Append("group [").Append(i).Append("] size=") .Append(minionGroup._memberCount) .Append(" id=") .Append(minionGroup.ownerId) .Append(" resolvedOwnerGameObject=") .Append(minionGroup.resolvedOwnerGameObject) .AppendLine(); for (int j = 0; j < minionGroup._memberCount; j++) { stringBuilder.Append(" ").Append("[").Append(j) .Append("] member.name=") .Append(minionGroup._members[j].name) .AppendLine(); } } } } [SyncVar(hook = "OnSyncOwnerMasterId")] private NetworkInstanceId ownerMasterId = NetworkInstanceId.Invalid; public CharacterMaster ownerMaster { get; private set; } public MinionGroup group { get; private set; } public NetworkInstanceId NetworkownerMasterId { get { return ownerMasterId; } [param: In] set { if (NetworkServer.localClientActive && !base.syncVarHookGuard) { base.syncVarHookGuard = true; OnSyncOwnerMasterId(value); base.syncVarHookGuard = false; } SetSyncVar(value, ref ownerMasterId, 1u); } } public event Action onOwnerDiscovered; public event Action onOwnerLost; public static event Action onMinionGroupChangedGlobal; public static event Action onMinionOwnerChangedGlobal; [Server] public void SetOwner(CharacterMaster newOwnerMaster) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.MinionOwnership::SetOwner(RoR2.CharacterMaster)' called on client"); return; } NetworkownerMasterId = (newOwnerMaster ? newOwnerMaster.netId : NetworkInstanceId.Invalid); MinionGroup.SetMinionOwner(this, ownerMasterId); } private void OnSyncOwnerMasterId(NetworkInstanceId newOwnerMasterId) { MinionGroup.SetMinionOwner(this, ownerMasterId); } public override void OnStartClient() { base.OnStartClient(); if (!NetworkServer.active) { MinionGroup.SetMinionOwner(this, ownerMasterId); } } private void HandleGroupDiscovery(MinionGroup newGroup) { group = newGroup; MinionOwnership.onMinionGroupChangedGlobal?.Invoke(this); } private void HandleOwnerDiscovery(CharacterMaster newOwner) { if ((object)ownerMaster != null) { this.onOwnerLost?.Invoke(ownerMaster); } ownerMaster = newOwner; if ((object)ownerMaster != null) { this.onOwnerDiscovered?.Invoke(ownerMaster); } MinionOwnership.onMinionOwnerChangedGlobal?.Invoke(this); } private void OnDestroy() { MinionGroup.SetMinionOwner(this, NetworkInstanceId.Invalid); } [AssetCheck(typeof(CharacterMaster))] private static void AddMinionOwnershipComponent(AssetCheckArgs args) { CharacterMaster characterMaster = args.asset as CharacterMaster; if (!characterMaster.GetComponent()) { characterMaster.gameObject.AddComponent(); args.UpdatePrefab(); } } private void OnValidate() { if (GetComponents().Length > 1) { Debug.LogError("Only one MinionOwnership is allowed per object!", this); } } private void UNetVersion() { } public override bool OnSerialize(NetworkWriter writer, bool forceAll) { if (forceAll) { writer.Write(ownerMasterId); return true; } bool flag = false; if ((base.syncVarDirtyBits & (true ? 1u : 0u)) != 0) { if (!flag) { writer.WritePackedUInt32(base.syncVarDirtyBits); flag = true; } writer.Write(ownerMasterId); } if (!flag) { writer.WritePackedUInt32(base.syncVarDirtyBits); } return flag; } public override void OnDeserialize(NetworkReader reader, bool initialState) { if (initialState) { ownerMasterId = reader.ReadNetworkId(); return; } int num = (int)reader.ReadPackedUInt32(); if (((uint)num & (true ? 1u : 0u)) != 0) { OnSyncOwnerMasterId(reader.ReadNetworkId()); } } public override void PreStartClient() { } }