Merge pull request #383 from misternebula/authority-sync-again

authority sync (again)
This commit is contained in:
Will Corby 2021-12-02 15:20:37 -08:00 committed by GitHub
commit 916e6653ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 162 additions and 196 deletions

View File

@ -1,5 +1,6 @@
using HarmonyLib; using HarmonyLib;
using QSB.Anglerfish.WorldObjects; using QSB.Anglerfish.WorldObjects;
using QSB.AuthoritySync;
using QSB.Events; using QSB.Events;
using QSB.Patches; using QSB.Patches;
using QSB.Utility; using QSB.Utility;
@ -42,7 +43,7 @@ namespace QSB.Anglerfish.Patches
__instance.gameObject.SetActive(true); __instance.gameObject.SetActive(true);
__instance._anglerBody.Unsuspend(); __instance._anglerBody.Unsuspend();
__instance.RaiseEvent(nameof(__instance.OnAnglerUnsuspended), __instance._currentState); __instance.RaiseEvent(nameof(__instance.OnAnglerUnsuspended), __instance._currentState);
QSBEventManager.FireEvent(EventNames.QSBSuspendChange, qsbAngler.TransformSync.NetIdentity, false); qsbAngler.TransformSync.NetIdentity.FireAuthQueue(true);
return false; return false;
} }
if (__instance.gameObject.activeSelf && !__instance._sector.ContainsAnyOccupants(DynamicOccupant.Player | DynamicOccupant.Probe | DynamicOccupant.Ship)) if (__instance.gameObject.activeSelf && !__instance._sector.ContainsAnyOccupants(DynamicOccupant.Player | DynamicOccupant.Probe | DynamicOccupant.Ship))
@ -50,7 +51,7 @@ namespace QSB.Anglerfish.Patches
__instance._anglerBody.Suspend(); __instance._anglerBody.Suspend();
__instance.gameObject.SetActive(false); __instance.gameObject.SetActive(false);
__instance.RaiseEvent(nameof(__instance.OnAnglerSuspended), __instance._currentState); __instance.RaiseEvent(nameof(__instance.OnAnglerSuspended), __instance._currentState);
QSBEventManager.FireEvent(EventNames.QSBSuspendChange, qsbAngler.TransformSync.NetIdentity, true); qsbAngler.TransformSync.NetIdentity.FireAuthQueue(false);
} }
return false; return false;

View File

@ -1,6 +1,5 @@
using QSB.Anglerfish.TransformSync; using QSB.Anglerfish.TransformSync;
using QSB.Events; using QSB.AuthoritySync;
using QSB.SuspendableSync;
using QSB.WorldSync; using QSB.WorldSync;
using QuantumUNET; using QuantumUNET;
using UnityEngine; using UnityEngine;
@ -24,19 +23,19 @@ namespace QSB.Anglerfish.WorldObjects
{ {
QNetworkServer.Spawn(Object.Instantiate(QSBNetworkManager.Instance.AnglerPrefab)); QNetworkServer.Spawn(Object.Instantiate(QSBNetworkManager.Instance.AnglerPrefab));
QSBCore.UnityEvents.RunWhen(() => TransformSync, () => QSBCore.UnityEvents.RunWhen(() => TransformSync, () =>
SuspendableManager.Register(TransformSync.NetIdentity)); TransformSync.NetIdentity.RegisterAuthQueue());
} }
// for when you host/connect mid-game // for when you host/connect mid-game
QSBCore.UnityEvents.RunWhen(() => TransformSync, () => QSBCore.UnityEvents.RunWhen(() => TransformSync, () =>
QSBEventManager.FireEvent(EventNames.QSBSuspendChange, TransformSync.NetIdentity, AttachedObject._anglerBody.IsSuspended())); TransformSync.NetIdentity.FireAuthQueue(!AttachedObject._anglerBody.IsSuspended()));
} }
public override void OnRemoval() public override void OnRemoval()
{ {
if (QSBCore.IsHost) if (QSBCore.IsHost)
{ {
SuspendableManager.Unregister(TransformSync.NetIdentity); TransformSync.NetIdentity.UnregisterAuthQueue();
QNetworkServer.Destroy(TransformSync.gameObject); QNetworkServer.Destroy(TransformSync.gameObject);
} }
} }

View File

@ -0,0 +1,87 @@
using System.Collections.Generic;
using System.Linq;
using QSB.Events;
using QSB.Utility;
using QuantumUNET;
using QuantumUNET.Components;
namespace QSB.AuthoritySync
{
public static class AuthorityManager
{
#region host only
/// whoever is first gets authority
private static readonly Dictionary<QNetworkIdentity, List<uint>> _authQueue = new();
public static void RegisterAuthQueue(this QNetworkIdentity identity) => _authQueue.Add(identity, new List<uint>());
public static void UnregisterAuthQueue(this QNetworkIdentity identity) => _authQueue.Remove(identity);
public static void UpdateAuthQueue(this QNetworkIdentity identity, uint id, bool queue)
{
var authQueue = _authQueue[identity];
var oldAuthority = authQueue.Contains(id);
if (queue == oldAuthority)
{
return;
}
if (queue)
{
authQueue.Add(id);
}
else
{
authQueue.Remove(id);
}
var newOwner = authQueue.Count != 0 ? authQueue[0] : uint.MaxValue;
SetAuthority(identity, newOwner);
}
/// transfer authority to a different client
public static void OnDisconnect(uint id)
{
foreach (var identity in _authQueue.Keys)
{
identity.UpdateAuthQueue(id, false);
}
}
public static void SetAuthority(this QNetworkIdentity identity, uint id)
{
var oldConn = identity.ClientAuthorityOwner;
var newConn = id != uint.MaxValue
? QNetworkServer.connections.First(x => x.GetPlayerId() == id)
: null;
if (oldConn == newConn)
{
return;
}
if (oldConn != null)
{
identity.RemoveClientAuthority(oldConn);
}
if (newConn != null)
{
identity.AssignClientAuthority(newConn);
}
// DebugLog.DebugWrite($"{identity.NetId}:{identity.gameObject.name} - "
// + $"set authority to {id}");
}
#endregion
#region any client
public static void FireAuthQueue(this QNetworkIdentity identity, bool queue) =>
QSBEventManager.FireEvent(EventNames.QSBAuthorityQueue, identity, queue);
#endregion
}
}

View File

@ -0,0 +1,37 @@
using QSB.Events;
using QuantumUNET.Components;
namespace QSB.AuthoritySync
{
public class AuthorityQueueEvent : QSBEvent<AuthorityQueueMessage>
{
public override void SetupListener() =>
GlobalMessenger<QNetworkIdentity, bool>.AddListener(EventNames.QSBAuthorityQueue, Handler);
public override void CloseListener() =>
GlobalMessenger<QNetworkIdentity, bool>.RemoveListener(EventNames.QSBAuthorityQueue, Handler);
private void Handler(QNetworkIdentity identity, bool queue) => SendEvent(CreateMessage(identity, queue));
private AuthorityQueueMessage CreateMessage(QNetworkIdentity identity, bool queue) => new()
{
OnlySendToHost = true,
Identity = identity,
Queue = queue
};
public override void OnReceiveLocal(bool isHost, AuthorityQueueMessage message) => OnReceive(message);
public override void OnReceiveRemote(bool isHost, AuthorityQueueMessage message) => OnReceive(message);
private static void OnReceive(AuthorityQueueMessage message)
{
if (!QSBCore.WorldObjectsReady)
{
return;
}
message.Identity.UpdateAuthQueue(message.FromId, message.Queue);
}
}
}

View File

@ -2,25 +2,25 @@
using QuantumUNET.Components; using QuantumUNET.Components;
using QuantumUNET.Transport; using QuantumUNET.Transport;
namespace QSB.SuspendableSync namespace QSB.AuthoritySync
{ {
public class SuspendChangeMessage : PlayerMessage public class AuthorityQueueMessage : PlayerMessage
{ {
public QNetworkIdentity Identity; public QNetworkIdentity Identity;
public bool Suspended; public bool Queue;
public override void Deserialize(QNetworkReader reader) public override void Deserialize(QNetworkReader reader)
{ {
base.Deserialize(reader); base.Deserialize(reader);
Identity = reader.ReadNetworkIdentity(); Identity = reader.ReadNetworkIdentity();
Suspended = reader.ReadBoolean(); Queue = reader.ReadBoolean();
} }
public override void Serialize(QNetworkWriter writer) public override void Serialize(QNetworkWriter writer)
{ {
base.Serialize(writer); base.Serialize(writer);
writer.Write(Identity); writer.Write(Identity);
writer.Write(Suspended); writer.Write(Queue);
} }
} }
} }

View File

@ -101,7 +101,7 @@
public static string QSBLearnLaunchCodes = "QSBLearnLaunchCodes"; public static string QSBLearnLaunchCodes = "QSBLearnLaunchCodes";
public static string QSBSatelliteRepairTick = "QSBSatelliteRepairTick"; public static string QSBSatelliteRepairTick = "QSBSatelliteRepairTick";
public static string QSBSatelliteRepaired = "QSBSatelliteRepairTick"; public static string QSBSatelliteRepaired = "QSBSatelliteRepairTick";
public static string QSBSuspendChange = "QSBSuspendChange"; public static string QSBAuthorityQueue = "QSBAuthorityQueue";
public static string QSBJellyfishRising = "QSBJellyfishRising"; public static string QSBJellyfishRising = "QSBJellyfishRising";
} }
} }

View File

@ -3,6 +3,7 @@ using OWML.Common;
using QSB.Anglerfish.Events; using QSB.Anglerfish.Events;
using QSB.Animation.NPC.Events; using QSB.Animation.NPC.Events;
using QSB.Animation.Player.Events; using QSB.Animation.Player.Events;
using QSB.AuthoritySync;
using QSB.CampfireSync.Events; using QSB.CampfireSync.Events;
using QSB.ClientServerStateSync.Events; using QSB.ClientServerStateSync.Events;
using QSB.ConversationSync.Events; using QSB.ConversationSync.Events;
@ -23,7 +24,6 @@ using QSB.ShipSync.Events;
using QSB.ShipSync.Events.Component; using QSB.ShipSync.Events.Component;
using QSB.ShipSync.Events.Hull; using QSB.ShipSync.Events.Hull;
using QSB.StatueSync.Events; using QSB.StatueSync.Events;
using QSB.SuspendableSync;
using QSB.TimeSync.Events; using QSB.TimeSync.Events;
using QSB.Tools.FlashlightTool.Events; using QSB.Tools.FlashlightTool.Events;
using QSB.Tools.ProbeLauncherTool.Events; using QSB.Tools.ProbeLauncherTool.Events;
@ -114,7 +114,7 @@ namespace QSB.Events
new IdentifyFrequencyEvent(), new IdentifyFrequencyEvent(),
new IdentifySignalEvent(), new IdentifySignalEvent(),
new NpcAnimationEvent(), new NpcAnimationEvent(),
new SuspendChangeEvent(), new AuthorityQueueEvent(),
// Ship // Ship
new FlyShipEvent(), new FlyShipEvent(),
new HatchEvent(), new HatchEvent(),

View File

@ -1,4 +1,5 @@
using HarmonyLib; using HarmonyLib;
using QSB.AuthoritySync;
using QSB.Events; using QSB.Events;
using QSB.JellyfishSync.WorldObjects; using QSB.JellyfishSync.WorldObjects;
using QSB.Patches; using QSB.Patches;
@ -24,14 +25,14 @@ namespace QSB.JellyfishSync.Patches
{ {
__instance.gameObject.SetActive(true); __instance.gameObject.SetActive(true);
__instance._jellyfishBody.Unsuspend(); __instance._jellyfishBody.Unsuspend();
QSBEventManager.FireEvent(EventNames.QSBSuspendChange, qsbJellyfish.TransformSync.NetIdentity, false); qsbJellyfish.TransformSync.NetIdentity.FireAuthQueue(true);
return false; return false;
} }
if (__instance.gameObject.activeSelf && !__instance._sector.ContainsAnyOccupants(DynamicOccupant.Player | DynamicOccupant.Probe | DynamicOccupant.Ship)) if (__instance.gameObject.activeSelf && !__instance._sector.ContainsAnyOccupants(DynamicOccupant.Player | DynamicOccupant.Probe | DynamicOccupant.Ship))
{ {
__instance._jellyfishBody.Suspend(); __instance._jellyfishBody.Suspend();
__instance.gameObject.SetActive(false); __instance.gameObject.SetActive(false);
QSBEventManager.FireEvent(EventNames.QSBSuspendChange, qsbJellyfish.TransformSync.NetIdentity, true); qsbJellyfish.TransformSync.NetIdentity.FireAuthQueue(false);
} }
return false; return false;

View File

@ -1,6 +1,5 @@
using QSB.Events; using QSB.AuthoritySync;
using QSB.JellyfishSync.TransformSync; using QSB.JellyfishSync.TransformSync;
using QSB.SuspendableSync;
using QSB.WorldSync; using QSB.WorldSync;
using QuantumUNET; using QuantumUNET;
using UnityEngine; using UnityEngine;
@ -22,19 +21,19 @@ namespace QSB.JellyfishSync.WorldObjects
{ {
QNetworkServer.Spawn(Object.Instantiate(QSBNetworkManager.Instance.JellyfishPrefab)); QNetworkServer.Spawn(Object.Instantiate(QSBNetworkManager.Instance.JellyfishPrefab));
QSBCore.UnityEvents.RunWhen(() => TransformSync, () => QSBCore.UnityEvents.RunWhen(() => TransformSync, () =>
SuspendableManager.Register(TransformSync.NetIdentity)); TransformSync.NetIdentity.RegisterAuthQueue());
} }
// for when you host/connect mid-game // for when you host/connect mid-game
QSBCore.UnityEvents.RunWhen(() => TransformSync, () => QSBCore.UnityEvents.RunWhen(() => TransformSync, () =>
QSBEventManager.FireEvent(EventNames.QSBSuspendChange, TransformSync.NetIdentity, AttachedObject._jellyfishBody.IsSuspended())); TransformSync.NetIdentity.FireAuthQueue(!AttachedObject._jellyfishBody.IsSuspended()));
} }
public override void OnRemoval() public override void OnRemoval()
{ {
if (QSBCore.IsHost) if (QSBCore.IsHost)
{ {
SuspendableManager.Unregister(TransformSync.NetIdentity); TransformSync.NetIdentity.UnregisterAuthQueue();
QNetworkServer.Destroy(TransformSync.gameObject); QNetworkServer.Destroy(TransformSync.gameObject);
} }
} }

View File

@ -1,12 +1,11 @@
using OWML.Common; using System.Linq;
using OWML.Common;
using QSB.AuthoritySync;
using QSB.Events; using QSB.Events;
using QSB.OrbSync.TransformSync; using QSB.OrbSync.TransformSync;
using QSB.Utility; using QSB.Utility;
using QSB.WorldSync; using QSB.WorldSync;
using QSB.WorldSync.Events; using QSB.WorldSync.Events;
using QuantumUNET;
using QuantumUNET.Components;
using System.Linq;
namespace QSB.OrbSync.Events namespace QSB.OrbSync.Events
{ {
@ -49,7 +48,6 @@ namespace QSB.OrbSync.Events
private static void HandleServer(WorldObjectMessage message) private static void HandleServer(WorldObjectMessage message)
{ {
var fromPlayer = QNetworkServer.connections.First(x => x.GetPlayerId() == message.FromId);
if (NomaiOrbTransformSync.OrbTransformSyncs == null || NomaiOrbTransformSync.OrbTransformSyncs.Count == 0) if (NomaiOrbTransformSync.OrbTransformSyncs == null || NomaiOrbTransformSync.OrbTransformSyncs.Count == 0)
{ {
DebugLog.ToConsole($"Error - OrbTransformSyncs is empty or null. (ID {message.ObjectId})", MessageType.Error); DebugLog.ToConsole($"Error - OrbTransformSyncs is empty or null. (ID {message.ObjectId})", MessageType.Error);
@ -62,11 +60,6 @@ namespace QSB.OrbSync.Events
return; return;
} }
if (fromPlayer == null)
{
DebugLog.ToConsole("Error - FromPlayer is null!", MessageType.Error);
}
var orbSync = NomaiOrbTransformSync.OrbTransformSyncs.Where(x => x != null) var orbSync = NomaiOrbTransformSync.OrbTransformSyncs.Where(x => x != null)
.FirstOrDefault(x => x.AttachedObject == QSBWorldSync.OldOrbList[message.ObjectId].transform); .FirstOrDefault(x => x.AttachedObject == QSBWorldSync.OldOrbList[message.ObjectId].transform);
if (orbSync == null) if (orbSync == null)
@ -75,27 +68,7 @@ namespace QSB.OrbSync.Events
return; return;
} }
var orbIdentity = orbSync.GetComponent<QNetworkIdentity>(); orbSync.NetIdentity.SetAuthority(message.FromId);
if (orbIdentity == null)
{
DebugLog.ToConsole($"Error - Orb identity is null. (ID {message.ObjectId})", MessageType.Error);
return;
}
var currentOwner = orbIdentity.ClientAuthorityOwner;
var newOwner = fromPlayer;
if (currentOwner == newOwner)
{
return;
}
if (currentOwner != null && currentOwner != fromPlayer)
{
orbIdentity.RemoveClientAuthority(currentOwner);
}
orbIdentity.AssignClientAuthority(fromPlayer);
orbSync.enabled = true; orbSync.enabled = true;
} }

View File

@ -2,6 +2,7 @@
using System.Linq; using System.Linq;
using OWML.Common; using OWML.Common;
using OWML.Utils; using OWML.Utils;
using QSB.AuthoritySync;
using QSB.ClientServerStateSync; using QSB.ClientServerStateSync;
using QSB.DeathSync; using QSB.DeathSync;
using QSB.Events; using QSB.Events;
@ -11,7 +12,6 @@ using QSB.Player;
using QSB.Player.TransformSync; using QSB.Player.TransformSync;
using QSB.PoolSync; using QSB.PoolSync;
using QSB.ShipSync.TransformSync; using QSB.ShipSync.TransformSync;
using QSB.SuspendableSync;
using QSB.TimeSync; using QSB.TimeSync;
using QSB.Utility; using QSB.Utility;
using QSB.WorldSync; using QSB.WorldSync;
@ -209,32 +209,33 @@ namespace QSB
{ {
DebugLog.DebugWrite("OnServerDisconnect", MessageType.Info); DebugLog.DebugWrite("OnServerDisconnect", MessageType.Info);
// remove authority for orbs // revert authority for orbs
foreach (var item in NomaiOrbTransformSync.OrbTransformSyncs) foreach (var item in NomaiOrbTransformSync.OrbTransformSyncs)
{ {
if (!item) if (!item)
{ {
DebugLog.ToConsole($"Warning - null transform sync in NomaiOrbTransformSync.OrbTransformSyncs!", MessageType.Warning);
continue; continue;
} }
var identity = item.NetIdentity; var identity = item.NetIdentity;
if (identity.ClientAuthorityOwner == conn) if (identity.ClientAuthorityOwner == conn)
{ {
identity.RemoveClientAuthority(conn); identity.SetAuthority(QSBPlayerManager.LocalPlayerId);
} }
} }
// remove authority from ship // revert authority from ship
if (ShipTransformSync.LocalInstance) if (ShipTransformSync.LocalInstance)
{ {
var identity = ShipTransformSync.LocalInstance.NetIdentity; var identity = ShipTransformSync.LocalInstance.NetIdentity;
if (identity.ClientAuthorityOwner == conn) if (identity.ClientAuthorityOwner == conn)
{ {
identity.RemoveClientAuthority(conn); identity.SetAuthority(QSBPlayerManager.LocalPlayerId);
} }
} }
SuspendableManager.OnDisconnect(conn); AuthorityManager.OnDisconnect(conn.GetPlayerId());
base.OnServerDisconnect(conn); base.OnServerDisconnect(conn);
} }

View File

@ -1,11 +1,9 @@
using OWML.Utils; using OWML.Utils;
using QSB.AuthoritySync;
using QSB.Events; using QSB.Events;
using QSB.Messaging; using QSB.Messaging;
using QSB.Player; using QSB.Player;
using QSB.ShipSync.TransformSync; using QSB.ShipSync.TransformSync;
using QSB.Utility;
using QuantumUNET;
using System.Linq;
using UnityEngine; using UnityEngine;
namespace QSB.ShipSync.Events namespace QSB.ShipSync.Events
@ -57,24 +55,9 @@ namespace QSB.ShipSync.Events
if (QSBCore.IsHost) if (QSBCore.IsHost)
{ {
var newAuthority = ShipManager.Instance.CurrentFlyer == uint.MaxValue ShipTransformSync.LocalInstance.NetIdentity.SetAuthority(isFlying
? QNetworkServer.connections.First(x => x.GetPlayerId() == QSBPlayerManager.LocalPlayerId) ? id
: QNetworkServer.connections.First(x => x.GetPlayerId() == id); : QSBPlayerManager.LocalPlayerId);
var ship = ShipTransformSync.LocalInstance;
var shipNetId = ship.NetIdentity;
if (shipNetId.ClientAuthorityOwner == newAuthority)
{
return;
}
if (shipNetId.ClientAuthorityOwner != null && shipNetId.ClientAuthorityOwner != newAuthority)
{
shipNetId.RemoveClientAuthority(shipNetId.ClientAuthorityOwner);
}
shipNetId.AssignClientAuthority(newAuthority);
} }
} }
} }

View File

@ -1,37 +0,0 @@
using QSB.Events;
using QuantumUNET.Components;
namespace QSB.SuspendableSync
{
public class SuspendChangeEvent : QSBEvent<SuspendChangeMessage>
{
public override void SetupListener() =>
GlobalMessenger<QNetworkIdentity, bool>.AddListener(EventNames.QSBSuspendChange, Handler);
public override void CloseListener() =>
GlobalMessenger<QNetworkIdentity, bool>.RemoveListener(EventNames.QSBSuspendChange, Handler);
private void Handler(QNetworkIdentity identity, bool suspended) => SendEvent(CreateMessage(identity, suspended));
private SuspendChangeMessage CreateMessage(QNetworkIdentity identity, bool unsuspended) => new()
{
OnlySendToHost = true,
Identity = identity,
Suspended = unsuspended
};
public override void OnReceiveLocal(bool isHost, SuspendChangeMessage message) => OnReceive(message);
public override void OnReceiveRemote(bool isHost, SuspendChangeMessage message) => OnReceive(message);
private static void OnReceive(SuspendChangeMessage message)
{
if (!QSBCore.WorldObjectsReady)
{
return;
}
SuspendableManager.UpdateSuspended(message.FromId, message.Identity, message.Suspended);
}
}
}

View File

@ -1,78 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using QSB.Utility;
using QuantumUNET;
using QuantumUNET.Components;
namespace QSB.SuspendableSync
{
/// all of this is host only
public static class SuspendableManager
{
private static readonly Dictionary<QNetworkIdentity, List<uint>> _unsuspendedFor = new();
public static void Register(QNetworkIdentity identity) => _unsuspendedFor.Add(identity, new List<uint>());
public static void Unregister(QNetworkIdentity identity) => _unsuspendedFor.Remove(identity);
public static void UpdateSuspended(uint id, QNetworkIdentity identity, bool suspended)
{
var unsuspendedFor = _unsuspendedFor[identity];
var oldSuspended = !unsuspendedFor.Contains(id);
if (suspended == oldSuspended)
{
return;
}
if (!suspended)
{
unsuspendedFor.Add(id);
}
else
{
unsuspendedFor.Remove(id);
}
var newOwner = unsuspendedFor.Count != 0 ? unsuspendedFor[0] : uint.MaxValue;
SetAuthority(identity, newOwner);
}
private static void SetAuthority(QNetworkIdentity identity, uint id)
{
var oldConn = identity.ClientAuthorityOwner;
var newConn = id != uint.MaxValue
? QNetworkServer.connections.First(x => x.GetPlayerId() == id)
: null;
if (oldConn == newConn)
{
return;
}
if (oldConn != null)
{
identity.RemoveClientAuthority(oldConn);
}
if (newConn != null)
{
identity.AssignClientAuthority(newConn);
}
}
/// transfer authority to a different client
public static void OnDisconnect(QNetworkConnection conn)
{
var id = conn.GetPlayerId();
foreach (var (identity, unsuspendedFor) in _unsuspendedFor)
{
if (unsuspendedFor.Remove(id))
{
var newOwner = unsuspendedFor.Count != 0 ? unsuspendedFor[0] : uint.MaxValue;
SetAuthority(identity, newOwner);
}
}
}
}
}