using System; using System.Collections.Generic; using System.Linq; using OWML.Common; using QSB.ClientServerStateSync; using QSB.ClientServerStateSync.Events; using QSB.Events; using QSB.Player; using QSB.Player.Events; using QSB.Player.TransformSync; 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 _msgTypeToType = new(); private static readonly Dictionary _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); } _msgTypeToType.Add(msgType, types[i]); _typeToMsgType.Add(types[i], msgType); } } public static void Init() { foreach (var msgType in _msgTypeToType.Keys) { QNetworkServer.RegisterHandlerSafe(msgType, OnServerReceive); QNetworkManager.singleton.client.RegisterHandlerSafe(msgType, OnClientReceive); } } private static void OnServerReceive(QNetworkMessage netMsg) { var msgType = netMsg.MsgType; var msg = (QSBMessage)Activator.CreateInstance(_msgTypeToType[msgType]); netMsg.ReadMessage(msg); if (msg.To == uint.MaxValue) { QNetworkServer.SendToAll(msgType, msg); } else if (msg.To == 0) { OnReceive(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}", MessageType.Error); return; } conn.Send(msgType, msg); } } private static void OnClientReceive(QNetworkMessage netMsg) { var msgType = netMsg.MsgType; var msg = (QSBMessage)Activator.CreateInstance(_msgTypeToType[msgType]); netMsg.ReadMessage(msg); OnReceive(msg); } private static void OnReceive(QSBMessage msg) { if (PlayerTransformSync.LocalInstance == null) { DebugLog.ToConsole($"Warning - Tried to handle message {msg} before localplayer was established.", MessageType.Warning); return; } if (QSBPlayerManager.PlayerExists(msg.From)) { var player = QSBPlayerManager.GetPlayer(msg.From); if (!player.IsReady && player.PlayerId != QSBPlayerManager.LocalPlayerId && player.State is ClientState.AliveInSolarSystem or ClientState.AliveInEye or ClientState.DeadInSolarSystem && msg is not QSBEventRelay { Event: PlayerInformationEvent or PlayerReadyEvent or RequestStateResyncEvent or ServerStateEvent }) { DebugLog.ToConsole($"Warning - Got message {msg} from player {msg.From}, but they were not ready. Asking for state resync, just in case.", MessageType.Warning); QSBEventManager.FireEvent(EventNames.QSBRequestStateResync); } } 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} : {ex}", MessageType.Error); } } #endregion public static void Send(this M msg) where M : QSBMessage, new() { if (!QSBCore.IsInMultiplayer) { DebugLog.ToConsole($"Warning - Tried to send message {msg} while not connected to/hosting server.", MessageType.Warning); return; } msg.From = QSBPlayerManager.LocalPlayerId; var msgType = _typeToMsgType[typeof(M)]; QNetworkManager.singleton.client.Send(msgType, msg); } public static void SendMessage(this T worldObject, M msg) where T : IWorldObject where M : QSBWorldObjectMessage, new() { msg.ObjectId = worldObject.ObjectId; Send(msg); } } }