- message manager

- automatically add patches, events, world object managers, messages
- qsbevent uses message manager
- remove RepeatingManager, merge it into QSBSectorManager since it's only used there
This commit is contained in:
JohnCorby 2021-12-10 23:28:42 -08:00
parent fde9efa599
commit 990f91c80c
16 changed files with 394 additions and 547 deletions

View File

@ -1,10 +0,0 @@
namespace QSB.Events
{
public abstract class BaseQSBEvent : IQSBEvent
{
internal static int _msgType;
public abstract void SetupListener();
public abstract void CloseListener();
}
}

View File

@ -1,35 +1,36 @@
using System;
using OWML.Common;
using QSB.ClientServerStateSync;
using JetBrains.Annotations;
using OWML.Utils;
using QSB.Messaging;
using QSB.Player;
using QSB.Player.Events;
using QSB.Player.TransformSync;
using QSB.Utility;
using QSB.WorldSync;
using QuantumUNET.Components;
using QuantumUNET.Transport;
namespace QSB.Events
{
public abstract class QSBEvent<T> : BaseQSBEvent where T : PlayerMessage, new()
public abstract class QSBEvent<T> : IQSBEvent where T : PlayerMessage, new()
{
public uint LocalPlayerId => QSBPlayerManager.LocalPlayerId;
private readonly MessageHandler<T> _eventHandler;
[UsedImplicitly]
private Type _messageType => typeof(T);
[UsedImplicitly]
private readonly int _msgType;
protected QSBEvent()
{
if (UnitTestDetector.IsInUnitTest)
{
return;
}
_eventHandler = new MessageHandler<T>(_msgType++);
_eventHandler.OnClientReceiveMessage += message => OnReceive(false, message);
_eventHandler.OnServerReceiveMessage += message => OnReceive(true, message);
_msgType = QSBEventManager._eventList.Count;
}
public abstract void SetupListener();
public abstract void CloseListener();
[UsedImplicitly]
public virtual void OnReceiveRemote(bool isHost, T message) { }
[UsedImplicitly]
public virtual void OnReceiveLocal(bool isHost, T message) { }
public abstract bool RequireWorldObjectsReady { get; }
@ -43,83 +44,51 @@ namespace QSB.Events
{
message.ForId = QSBEventManager.ForIdOverride;
}
_eventHandler.SendToServer(message);
if (message.OnlySendToHost)
{
message.ForId = 0;
}
new QSBEventRelay
{
Event = this,
Message = message
}.Send(message.ForId);
});
/// <summary>
/// Checks whether the message should be processed by the executing client.
/// </summary>
/// <returns>True if the message should be processed.</returns>
[UsedImplicitly]
public virtual bool CheckMessage(T message)
=> !RequireWorldObjectsReady || WorldObjectManager.AllObjectsReady;
}
private void OnReceive(bool isServer, T message)
internal class QSBEventRelay : QSBMessage
{
public IQSBEvent Event;
public PlayerMessage Message;
public override void Serialize(QNetworkWriter writer)
{
/* Explanation :
* if <isServer> is true, this message has been received on the server *server*.
* Therefore, we don't want to do any event handling code - that should be dealt
* with on the server *client* and any other client. So just forward the message
* onto all clients. This way, the server *server* just acts as the distribution
* hub for all events.
*/
if (isServer)
{
if (message.OnlySendToHost)
{
_eventHandler.SendToHost(message);
}
else if (message.ForId != uint.MaxValue)
{
_eventHandler.SendTo(message.ForId, message);
}
else
{
_eventHandler.SendToAll(message);
}
return;
}
if (!CheckMessage(message))
{
return;
}
if (PlayerTransformSync.LocalInstance == null || PlayerTransformSync.LocalInstance.GetComponent<QNetworkIdentity>() == null)
{
DebugLog.ToConsole($"Warning - Tried to handle message of type <{GetType().Name}> before localplayer was established.", MessageType.Warning);
return;
}
if (QSBPlayerManager.PlayerExists(message.FromId))
{
var player = QSBPlayerManager.GetPlayer(message.FromId);
if (!player.IsReady
&& player.PlayerId != LocalPlayerId
&& player.State is ClientState.AliveInSolarSystem or ClientState.AliveInEye or ClientState.DeadInSolarSystem
&& this is not PlayerInformationEvent or PlayerReadyEvent)
{
DebugLog.ToConsole($"Warning - Got message from player {message.FromId}, but they were not ready. Asking for state resync, just in case.", MessageType.Warning);
QSBEventManager.FireEvent(EventNames.QSBRequestStateResync);
}
}
try
{
if (QSBPlayerManager.IsBelongingToLocalPlayer(message.FromId))
{
OnReceiveLocal(QSBCore.IsHost, message);
}
else
{
OnReceiveRemote(QSBCore.IsHost, message);
}
}
catch (Exception ex)
{
DebugLog.ToConsole($"Error - Exception handling message {GetType().Name} : {ex}", MessageType.Error);
}
base.Serialize(writer);
var msgType = Event.GetValue<int>("_msgType");
writer.Write(msgType);
Message.Serialize(writer);
}
public override void Deserialize(QNetworkReader reader)
{
base.Deserialize(reader);
var msgType = reader.ReadInt32();
Event = QSBEventManager._eventList[msgType];
var messageType = Event.GetValue<Type>("_messageType");
Message = (PlayerMessage)Activator.CreateInstance(messageType);
Message.Deserialize(reader);
}
public override bool ShouldReceive => Event.Invoke<bool>("CheckMessage", Message);
public override void OnReceiveRemote() => Event.Invoke("OnReceiveRemote", QSBCore.IsHost, Message);
public override void OnReceiveLocal() => Event.Invoke("OnReceiveLocal", QSBCore.IsHost, Message);
}
}

View File

@ -1,40 +1,8 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using OWML.Common;
using QSB.Anglerfish.Events;
using QSB.Animation.NPC.Events;
using QSB.Animation.Player.Events;
using QSB.AuthoritySync;
using QSB.CampfireSync.Events;
using QSB.ClientServerStateSync.Events;
using QSB.ConversationSync.Events;
using QSB.DeathSync.Events;
using QSB.ElevatorSync.Events;
using QSB.GeyserSync.Events;
using QSB.ItemSync.Events;
using QSB.JellyfishSync.Events;
using QSB.LogSync.Events;
using QSB.MeteorSync.Events;
using QSB.OrbSync.Events;
using QSB.Player.Events;
using QSB.QuantumSync.Events;
using QSB.RespawnSync.Events;
using QSB.RoastingSync.Events;
using QSB.SatelliteSync.Events;
using QSB.ShipSync.Events;
using QSB.ShipSync.Events.Component;
using QSB.ShipSync.Events.Hull;
using QSB.StatueSync.Events;
using QSB.TimeSync.Events;
using QSB.Tools.FlashlightTool.Events;
using QSB.Tools.ProbeLauncherTool.Events;
using QSB.Tools.ProbeTool.Events;
using QSB.Tools.SignalscopeTool.Events;
using QSB.Tools.SignalscopeTool.FrequencySync.Events;
using QSB.Tools.TranslatorTool.Events;
using QSB.Tools.TranslatorTool.TranslationSync.Events;
using QSB.Utility;
using QSB.Utility.Events;
using QSB.ZeroGCaveSync.Events;
namespace QSB.Events
{
@ -42,94 +10,15 @@ namespace QSB.Events
{
public static bool Ready { get; private set; }
private static List<IQSBEvent> _eventList = new();
private static readonly Type[] _types = typeof(IQSBEvent).GetDerivedTypes().ToArray();
internal static readonly List<IQSBEvent> _eventList = new();
public static void Init()
{
BaseQSBEvent._msgType = 0;
_eventList = new List<IQSBEvent>
foreach (var type in _types)
{
// Player
new PlayerReadyEvent(),
new PlayerJoinEvent(),
new PlayerSuitEvent(),
new PlayerFlashlightEvent(),
new PlayerSignalscopeEvent(),
new PlayerTranslatorEvent(),
new EquipProbeLauncherEvent(),
new PlayerProbeEvent(),
new PlayerDeathEvent(),
new RequestStateResyncEvent(),
new PlayerInformationEvent(),
new ChangeAnimTypeEvent(),
new ServerTimeEvent(),
new PlayerEntangledEvent(),
new PlayerKickEvent(),
new EnterExitRoastingEvent(),
new MarshmallowEventEvent(),
new AnimationTriggerEvent(),
new PlayerRespawnEvent(),
new ProbeStartRetrieveEvent(),
new RetrieveProbeEvent(),
new LaunchProbeEvent(),
new PlayerRetrieveProbeEvent(),
new PlayerLaunchProbeEvent(),
new EndLoopEvent(),
new StartLoopEvent(),
new ServerStateEvent(),
new ClientStateEvent(),
new DebugEvent(),
new SatelliteProjectorEvent(),
new SatelliteProjectorSnapshotEvent(),
new LaunchCodesEvent(),
// World Objects
new ElevatorEvent(),
new GeyserEvent(),
new OrbSlotEvent(),
new OrbUserEvent(),
new SocketStateChangeEvent(),
new MultiStateChangeEvent(),
new SetAsTranslatedEvent(),
new QuantumShuffleEvent(),
new MoonStateChangeEvent(),
new EnterLeaveEvent(),
new QuantumAuthorityEvent(),
new DropItemEvent(),
new SocketItemEvent(),
new MoveToCarryEvent(),
new StartStatueEvent(),
new CampfireStateEvent(),
new AnglerChangeStateEvent(),
new MeteorPreLaunchEvent(),
new MeteorLaunchEvent(),
new MeteorSpecialImpactEvent(),
new FragmentDamageEvent(),
new FragmentResyncEvent(),
new JellyfishRisingEvent(),
// Conversation/dialogue/exploration
new ConversationEvent(),
new ConversationStartEndEvent(),
new DialogueConditionEvent(),
new RevealFactEvent(),
new IdentifyFrequencyEvent(),
new IdentifySignalEvent(),
new NpcAnimationEvent(),
new AuthorityQueueEvent(),
// Ship
new FlyShipEvent(),
new HatchEvent(),
new FunnelEnableEvent(),
new HullImpactEvent(),
new HullDamagedEvent(),
new HullChangeIntegrityEvent(),
new HullRepairedEvent(),
new HullRepairTickEvent(),
new ComponentDamagedEvent(),
new ComponentRepairedEvent(),
new ComponentRepairTickEvent(),
new SatelliteNodeRepairTick(),
new SatelliteNodeRepaired()
};
_eventList.Add((IQSBEvent)Activator.CreateInstance(type));
}
if (UnitTestDetector.IsInUnitTest)
{
@ -147,7 +36,7 @@ namespace QSB.Events
{
Ready = false;
_eventList.ForEach(ev => ev.CloseListener());
_eventList = new List<IQSBEvent>();
_eventList.Clear();
}
public static void FireEvent(string eventName)

View File

@ -1,108 +0,0 @@
using QSB.Utility;
using QuantumUNET;
using QuantumUNET.Components;
using QuantumUNET.Messages;
using System;
using System.Linq;
namespace QSB.Messaging
{
public class MessageHandler<T> where T : QMessageBase, new()
{
public event Action<T> OnClientReceiveMessage;
public event Action<T> OnServerReceiveMessage;
private readonly short _eventType;
public MessageHandler(int msgType)
{
_eventType = (short)(msgType + QMsgType.Highest + 1);
if (_eventType >= short.MaxValue)
{
DebugLog.ToConsole($"Hey, uh, maybe don't create 32,767 events? You really should never be seeing this." +
$"If you are, something has either gone terrible wrong or QSB somehow needs more events that classes in Outer Wilds." +
$"In either case, I guess something has gone terribly wrong...", OWML.Common.MessageType.Error);
}
if (QSBNetworkManager.Instance.IsReady)
{
Init();
}
else
{
QSBNetworkManager.Instance.OnNetworkManagerReady += Init;
}
}
private void Init()
{
if (QNetworkServer.handlers.Keys.Contains(_eventType))
{
QNetworkServer.handlers.Remove(_eventType);
QNetworkManager.singleton.client.handlers.Remove(_eventType);
}
QNetworkServer.RegisterHandler(_eventType, OnServerReceiveMessageHandler);
QNetworkManager.singleton.client.RegisterHandler(_eventType, OnClientReceiveMessageHandler);
}
public void SendToAll(T message)
{
if (!QSBNetworkManager.Instance.IsReady)
{
return;
}
QNetworkServer.SendToAll(_eventType, message);
}
public void SendToHost(T message)
{
if (!QSBNetworkManager.Instance.IsReady)
{
return;
}
QNetworkServer.SendToClient(0, _eventType, message);
}
public void SendTo(uint id, T message)
{
if (!QSBNetworkManager.Instance.IsReady)
{
return;
}
var conn = QNetworkServer.connections.FirstOrDefault(x => x.GetPlayerId() == id);
if (conn == null)
{
DebugLog.ToConsole($"SendTo unknown player! id: {id}, message: {message.GetType().Name}", OWML.Common.MessageType.Error);
return;
}
QNetworkServer.SendToClient(conn.connectionId, _eventType, message);
}
public void SendToServer(T message)
{
if (!QSBNetworkManager.Instance.IsReady)
{
return;
}
QNetworkManager.singleton.client.Send(_eventType, message);
}
private void OnClientReceiveMessageHandler(QNetworkMessage netMsg)
{
var message = netMsg.ReadMessage<T>();
OnClientReceiveMessage?.SafeInvoke(message);
}
private void OnServerReceiveMessageHandler(QNetworkMessage netMsg)
{
var message = netMsg.ReadMessage<T>();
OnServerReceiveMessage?.SafeInvoke(message);
}
}
}

View File

@ -0,0 +1,80 @@
using System;
using QuantumUNET.Messages;
using QuantumUNET.Transport;
namespace QSB.Messaging
{
public abstract class QSBMessage : QMessageBase
{
public uint From;
public uint To;
public override void Serialize(QNetworkWriter writer)
{
writer.Write(From);
writer.Write(To);
}
public override void Deserialize(QNetworkReader reader)
{
From = reader.ReadUInt32();
To = reader.ReadUInt32();
}
public virtual bool ShouldReceive => true;
public virtual void OnReceiveRemote() { }
public virtual void OnReceiveLocal() { }
}
public abstract class QSBBoolMessage : QSBMessage
{
public bool Value;
public override void Serialize(QNetworkWriter writer)
{
base.Serialize(writer);
writer.Write(Value);
}
public override void Deserialize(QNetworkReader reader)
{
base.Deserialize(reader);
Value = reader.ReadBoolean();
}
}
public abstract class QSBFloatMessage : QSBMessage
{
public float Value;
public override void Serialize(QNetworkWriter writer)
{
base.Serialize(writer);
writer.Write(Value);
}
public override void Deserialize(QNetworkReader reader)
{
base.Deserialize(reader);
Value = reader.ReadSingle();
}
}
public abstract class QSBEnumMessage<E> : QSBMessage where E : Enum
{
public E Value;
public override void Serialize(QNetworkWriter writer)
{
base.Serialize(writer);
writer.Write((int)(object)Value);
}
public override void Deserialize(QNetworkReader reader)
{
base.Deserialize(reader);
Value = (E)(object)reader.ReadInt32();
}
}
}

View File

@ -0,0 +1,124 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OWML.Common;
using QSB.Player;
using QSB.Utility;
using QSB.WorldSync;
using QuantumUNET;
using QuantumUNET.Components;
using QuantumUNET.Messages;
namespace QSB.Messaging
{
public static class QSBMessageManager
{
#region inner workings
private static readonly Dictionary<short, QSBMessage> _msgTypeToMsg = new();
private static readonly Dictionary<Type, short> _typeToMsgType = new();
static QSBMessageManager()
{
var types = typeof(QSBMessage).GetDerivedTypes().ToArray();
for (var i = 0; i < types.Length; i++)
{
var msgType = (short)(QMsgType.Highest + 1 + i);
if (msgType >= short.MaxValue)
{
DebugLog.ToConsole("Hey, uh, maybe don't create 32,767 events? You really should never be seeing this." +
"If you are, something has either gone terrible wrong or QSB somehow needs more events that classes in Outer Wilds." +
"In either case, I guess something has gone terribly wrong...", MessageType.Error);
}
var type = types[i];
var msg = (QSBMessage)Activator.CreateInstance(type);
_msgTypeToMsg.Add(msgType, msg);
_typeToMsgType.Add(type, msgType);
}
}
public static void Init()
{
foreach (var msgType in _msgTypeToMsg.Keys)
{
QNetworkServer.RegisterHandlerSafe(msgType, OnServerReceive);
QNetworkManager.singleton.client.RegisterHandlerSafe(msgType, OnClientReceive);
}
}
private static void OnServerReceive(QNetworkMessage netMsg)
{
var msgType = netMsg.MsgType;
var msg = _msgTypeToMsg[msgType];
netMsg.ReadMessage(msg);
if (msg.To == 0)
{
QNetworkServer.SendToClient(0, msgType, msg);
}
else if (msg.To == uint.MaxValue)
{
QNetworkServer.SendToAll(msgType, msg);
}
else
{
var conn = QNetworkServer.connections.FirstOrDefault(x => msg.To == x.GetPlayerId());
if (conn == null)
{
DebugLog.ToConsole($"SendTo unknown player! id: {msg.To}, message: {msg.GetType().Name}", MessageType.Error);
return;
}
conn.Send(msgType, msg);
}
}
private static void OnClientReceive(QNetworkMessage netMsg)
{
var msgType = netMsg.MsgType;
var msg = _msgTypeToMsg[msgType];
netMsg.ReadMessage(msg);
try
{
if (!msg.ShouldReceive)
{
return;
}
if (msg.From != QSBPlayerManager.LocalPlayerId)
{
msg.OnReceiveRemote();
}
else
{
msg.OnReceiveLocal();
}
}
catch (Exception ex)
{
DebugLog.ToConsole($"Error - Exception handling message {msg.GetType().Name} : {ex}", MessageType.Error);
}
}
#endregion
public static void Send<M>(this M message, uint to = uint.MaxValue)
where M : QSBMessage, new()
{
var msgType = _typeToMsgType[typeof(M)];
message.From = QSBPlayerManager.LocalPlayerId;
message.To = to;
QNetworkManager.singleton.client.Send(msgType, message);
}
public static void SendMessage<T, M>(this T worldObject, M message, uint to = uint.MaxValue)
where M : QSBWorldObjectMessage<T>, new()
where T : IWorldObject
{
message.ObjectId = worldObject.ObjectId;
Send(message, to);
}
}
}

View File

@ -0,0 +1,92 @@
using System;
using QSB.WorldSync;
using QuantumUNET.Transport;
namespace QSB.Messaging
{
public abstract class QSBWorldObjectMessage<T> : QSBMessage where T : IWorldObject
{
public int ObjectId;
public T WorldObject { get; private set; }
public override void Serialize(QNetworkWriter writer)
{
base.Serialize(writer);
writer.Write(ObjectId);
}
public override void Deserialize(QNetworkReader reader)
{
base.Deserialize(reader);
ObjectId = reader.ReadInt32();
}
public override bool ShouldReceive
{
get
{
if (!WorldObjectManager.AllObjectsReady)
{
return false;
}
WorldObject = QSBWorldSync.GetWorldFromId<T>(ObjectId);
return true;
}
}
}
public abstract class QSBBoolWorldObjectMessage<T> : QSBWorldObjectMessage<T> where T : IWorldObject
{
public bool Value;
public override void Serialize(QNetworkWriter writer)
{
base.Serialize(writer);
writer.Write(Value);
}
public override void Deserialize(QNetworkReader reader)
{
base.Deserialize(reader);
Value = reader.ReadBoolean();
}
}
public abstract class QSBFloatWorldObjectMessage<T> : QSBWorldObjectMessage<T> where T : IWorldObject
{
public float Value;
public override void Serialize(QNetworkWriter writer)
{
base.Serialize(writer);
writer.Write(Value);
}
public override void Deserialize(QNetworkReader reader)
{
base.Deserialize(reader);
Value = reader.ReadSingle();
}
}
public abstract class QSBEnumWorldObjectMessage<T, E> : QSBWorldObjectMessage<T>
where T : IWorldObject
where E : Enum
{
public E Value;
public override void Serialize(QNetworkWriter writer)
{
base.Serialize(writer);
writer.Write((int)(object)Value);
}
public override void Deserialize(QNetworkReader reader)
{
base.Deserialize(reader);
Value = (E)(object)reader.ReadInt32();
}
}
}

View File

@ -3,34 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using HarmonyLib;
using OWML.Common;
using QSB.Anglerfish.Patches;
using QSB.Animation.NPC.Patches;
using QSB.Animation.Patches;
using QSB.CampfireSync.Patches;
using QSB.ConversationSync.Patches;
using QSB.DeathSync.Patches;
using QSB.EchoesOfTheEye.LightSensorSync.Patches;
using QSB.ElevatorSync.Patches;
using QSB.GeyserSync.Patches;
using QSB.Inputs.Patches;
using QSB.ItemSync.Patches;
using QSB.JellyfishSync.Patches;
using QSB.LogSync.Patches;
using QSB.MeteorSync.Patches;
using QSB.OrbSync.Patches;
using QSB.Player.Patches;
using QSB.PoolSync.Patches;
using QSB.QuantumSync.Patches;
using QSB.RoastingSync.Patches;
using QSB.SatelliteSync.Patches;
using QSB.ShipSync.Patches;
using QSB.StatueSync.Patches;
using QSB.TimeSync.Patches;
using QSB.Tools.ProbeLauncherTool.Patches;
using QSB.Tools.SignalscopeTool.FrequencySync.Patches;
using QSB.Tools.TranslatorTool.TranslationSync.Patches;
using QSB.Utility;
using QSB.ZeroGCaveSync.Patches;
namespace QSB.Patches
{
@ -39,50 +12,17 @@ namespace QSB.Patches
public static event Action<QSBPatchTypes> OnPatchType;
public static event Action<QSBPatchTypes> OnUnpatchType;
private static List<QSBPatch> _patchList = new();
private static List<QSBPatchTypes> _patchedTypes = new();
private static readonly List<QSBPatch> _patchList = new();
private static readonly List<QSBPatchTypes> _patchedTypes = new();
public static Dictionary<QSBPatchTypes, Harmony> TypeToInstance = new();
public static void Init()
{
_patchList = new List<QSBPatch>
foreach (var type in typeof(QSBPatch).GetDerivedTypes())
{
new ConversationPatches(),
new DeathPatches(),
new ElevatorPatches(),
new OrbPatches(),
new LogPatches(),
new QuantumVisibilityPatches(),
new ServerQuantumPatches(),
new ClientQuantumPatches(),
new FrequencyPatches(),
new SpiralPatches(),
new QuantumPatches(),
new ItemPatches(),
new StatuePatches(),
new GeyserPatches(),
new PoolPatches(),
new CampfirePatches(),
new RoastingPatches(),
new PlayerPatches(),
new PlayerAnimationPatches(),
new CharacterAnimationPatches(),
new ShipPatches(),
new InputPatches(),
new TimePatches(),
new MapPatches(),
new LauncherPatches(),
new SolanumPatches(),
new SatelliteProjectorPatches(),
new LightSensorPatches(),
new AnglerPatches(),
new MeteorClientPatches(),
new MeteorServerPatches(),
new JellyfishPatches(),
new TravelerControllerPatches(),
new ZeroGCavePatches()
};
_patchList.Add((QSBPatch)Activator.CreateInstance(type));
}
TypeToInstance = new Dictionary<QSBPatchTypes, Harmony>
{

View File

@ -2,36 +2,17 @@
using OWML.Common;
using OWML.ModHelper;
using OWML.ModHelper.Input;
using QSB.Anglerfish;
using QSB.Animation.NPC;
using QSB.CampfireSync;
using QSB.ConversationSync;
using QSB.EchoesOfTheEye.AirlockSync;
using QSB.EchoesOfTheEye.LightSensorSync;
using QSB.ElevatorSync;
using QSB.GeyserSync;
using QSB.Inputs;
using QSB.ItemSync;
using QSB.JellyfishSync;
using QSB.Menus;
using QSB.MeteorSync;
using QSB.OrbSync;
using QSB.Patches;
using QSB.Player;
using QSB.Player.TransformSync;
using QSB.PoolSync;
using QSB.QuantumSync;
using QSB.RespawnSync;
using QSB.SatelliteSync;
using QSB.SectorSync;
using QSB.ShipSync;
using QSB.StatueSync;
using QSB.TimeSync;
using QSB.Tools.ProbeLauncherTool;
using QSB.Tools.TranslatorTool.TranslationSync;
using QSB.Utility;
using QSB.WorldSync;
using QSB.ZeroGCaveSync;
using QuantumUNET;
using QuantumUNET.Components;
using UnityEngine;
@ -116,33 +97,18 @@ namespace QSB
gameObject.AddComponent<ConversationManager>();
gameObject.AddComponent<QSBInputManager>();
gameObject.AddComponent<TimeSyncUI>();
gameObject.AddComponent<RepeatingManager>();
gameObject.AddComponent<PlayerEntanglementWatcher>();
gameObject.AddComponent<DebugGUI>();
gameObject.AddComponent<MenuManager>();
gameObject.AddComponent<RespawnManager>();
gameObject.AddComponent<SatelliteProjectorManager>();
gameObject.AddComponent<StatueManager>();
// WorldObject managers
gameObject.AddComponent<QuantumManager>();
gameObject.AddComponent<SpiralManager>();
gameObject.AddComponent<ElevatorManager>();
gameObject.AddComponent<GeyserManager>();
gameObject.AddComponent<OrbManager>();
gameObject.AddComponent<QSBSectorManager>();
gameObject.AddComponent<ItemManager>();
gameObject.AddComponent<StatueManager>();
gameObject.AddComponent<PoolManager>();
gameObject.AddComponent<CampfireManager>();
gameObject.AddComponent<CharacterAnimManager>();
gameObject.AddComponent<ShipManager>();
gameObject.AddComponent<ProbeLauncherManager>();
gameObject.AddComponent<LightSensorManager>();
gameObject.AddComponent<AirlockManager>();
gameObject.AddComponent<AnglerManager>();
gameObject.AddComponent<MeteorManager>();
gameObject.AddComponent<JellyfishManager>();
gameObject.AddComponent<ZeroGCaveManager>();
foreach (var type in typeof(WorldObjectManager).GetDerivedTypes())
{
gameObject.AddComponent(type);
}
DebugBoxManager.Init();

View File

@ -6,6 +6,7 @@ using QSB.AuthoritySync;
using QSB.ClientServerStateSync;
using QSB.DeathSync;
using QSB.Events;
using QSB.Messaging;
using QSB.OrbSync.TransformSync;
using QSB.Patches;
using QSB.Player;
@ -140,6 +141,7 @@ namespace QSB
OnClientConnected?.SafeInvoke();
QSBEventManager.Init();
QSBMessageManager.Init();
gameObject.AddComponent<RespawnOnDeath>();
gameObject.AddComponent<ServerStateManager>();

View File

@ -9,17 +9,34 @@ using UnityEngine;
namespace QSB.SectorSync
{
public class QSBSectorManager : WorldObjectManager, IRepeating
public class QSBSectorManager : WorldObjectManager
{
public static QSBSectorManager Instance { get; private set; }
public bool IsReady { get; private set; }
public List<QSBSector> FakeSectors = new();
private void OnEnable() => RepeatingManager.Repeatings.Add(this);
private void OnDisable() => RepeatingManager.Repeatings.Remove(this);
public List<BaseSectoredSync> SectoredSyncs = new();
#region repeating timer
private const float TimeInterval = 0.4f;
private float _checkTimer = TimeInterval;
private void Update()
{
_checkTimer += Time.unscaledDeltaTime;
if (_checkTimer < TimeInterval)
{
return;
}
Invoke();
_checkTimer = 0;
}
#endregion
public void Invoke()
{
if (!Instance.IsReady || !AllObjectsReady)
@ -91,4 +108,4 @@ namespace QSB.SectorSync
transformSync.SetReferenceSector(closestSector);
}
}
}
}

View File

@ -3,6 +3,7 @@ using QSB.Player;
using QSB.Player.TransformSync;
using QuantumUNET;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
@ -128,6 +129,9 @@ namespace QSB.Utility
multiDelegate.GetInvocationList().ToList().ForEach(dl => dl.DynamicInvoke(args));
}
public static IEnumerable<Type> GetDerivedTypes(this Type type) => type.Assembly.GetTypes()
.Where(x => !x.IsInterface && !x.IsAbstract && type.IsAssignableFrom(x));
// OW
public static Vector3 GetRelativeAngularVelocity(this OWRigidbody baseBody, OWRigidbody relativeBody)

View File

@ -1,7 +0,0 @@
namespace QSB.Utility
{
public interface IRepeating
{
void Invoke();
}
}

View File

@ -1,29 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
namespace QSB.Utility
{
internal class RepeatingManager : MonoBehaviour
{
public static List<IRepeating> Repeatings = new();
private const float TimeInterval = 0.4f;
private float _checkTimer = TimeInterval;
private void Update()
{
_checkTimer += Time.unscaledDeltaTime;
if (_checkTimer < TimeInterval)
{
return;
}
foreach (var repeat in Repeatings)
{
repeat.Invoke();
}
_checkTimer = 0;
}
}
}

View File

@ -1,41 +0,0 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using QSB.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace QSBTests
{
[TestClass]
public class EventTests
{
[TestMethod]
public void CheckUnreferencedEvents()
{
var qsbAssembly = Assembly.Load("QSB");
var allEventTypes = qsbAssembly
.GetTypes()
.Where(x => typeof(IQSBEvent).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract);
QSBEventManager.Init();
var eventInstances = (List<IQSBEvent>)typeof(QSBEventManager)
.GetField("_eventList", BindingFlags.NonPublic | BindingFlags.Static)
.GetValue(typeof(QSBEventManager));
var failedTypes = new List<Type>();
foreach (var type in allEventTypes)
{
if (!eventInstances.Any(x => x.GetType() == type))
{
failedTypes.Add(type);
}
}
if (failedTypes.Count > 0)
{
Assert.Fail(string.Join(", ", failedTypes.Select(x => x.Name)));
}
}
}
}

View File

@ -1,41 +0,0 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using QSB.Patches;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace QSBTests
{
[TestClass]
public class PatchTests
{
[TestMethod]
public void CheckUnreferencedPatches()
{
var qsbAssembly = Assembly.Load("QSB");
var allPatchTypes = qsbAssembly
.GetTypes()
.Where(x => typeof(QSBPatch).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract);
QSBPatchManager.Init();
var patchInstances = (List<QSBPatch>)typeof(QSBPatchManager)
.GetField("_patchList", BindingFlags.NonPublic | BindingFlags.Static)
.GetValue(typeof(QSBPatchManager));
var failedTypes = new List<Type>();
foreach (var type in allPatchTypes)
{
if (!patchInstances.Any(x => x.GetType() == type))
{
failedTypes.Add(type);
}
}
if (failedTypes.Count > 0)
{
Assert.Fail(string.Join(", ", failedTypes.Select(x => x.Name)));
}
}
}
}