using FishNet.Connection; using FishNet.Managing; using FishNet.Serializing; using FishNet.Transporting; using GameKit.Dependencies.Utilities; using System; using System.Collections.Generic; namespace FishNet.Broadcast.Helping { internal static class BroadcastsSerializers { /// /// Writes a broadcast to writer. /// internal static PooledWriter WriteBroadcast(NetworkManager networkManager, PooledWriter writer, T message, ref Channel channel) { writer.WritePacketIdUnpacked(PacketId.Broadcast); writer.WriteUInt16(typeof(T).FullName.GetStableHashU16()); //Write data to a new writer. PooledWriter dataWriter = WriterPool.Retrieve(); dataWriter.Write(message); //Write length of data. writer.WriteInt32(dataWriter.Length); //Write data. writer.WriteArraySegment(dataWriter.GetArraySegment()); //Update channel to reliable if needed. networkManager.TransportManager.CheckSetReliableChannel(writer.Length, ref channel); dataWriter.Store(); return writer; } } internal static class BroadcastExtensions { /// /// Gets the key for a broadcast type. /// /// /// /// internal static ushort GetKey() { return typeof(T).FullName.GetStableHashU16(); } } /// /// Implemented by server and client broadcast handlers. /// public abstract class BroadcastHandlerBase { /// /// Current index when iterating invokes. /// This value will be -1 when not iterating. /// protected int IteratingIndex; public abstract void RegisterHandler(object obj); public abstract void UnregisterHandler(object obj); public virtual void InvokeHandlers(PooledReader reader, Channel channel) { } public virtual void InvokeHandlers(NetworkConnection conn, PooledReader reader, Channel channel) { } public virtual bool RequireAuthentication => false; } /// /// Handles broadcasts received on server, from clients. /// internal class ClientBroadcastHandler : BroadcastHandlerBase { /// /// Action handlers for the broadcast. /// private List> _handlers = new(); /// /// True to require authentication for the broadcast type. /// private bool _requireAuthentication; public ClientBroadcastHandler(bool requireAuthentication) { _requireAuthentication = requireAuthentication; } /// /// Invokes handlers after reading broadcast. /// /// True if a rebuild was required. public override void InvokeHandlers(NetworkConnection conn, PooledReader reader, Channel channel) { T result = reader.Read(); for (base.IteratingIndex = 0; base.IteratingIndex < _handlers.Count; base.IteratingIndex++) { Action item = _handlers[base.IteratingIndex]; if (item != null) { item.Invoke(conn, result, channel); } else { _handlers.RemoveAt(base.IteratingIndex); base.IteratingIndex--; } } base.IteratingIndex = -1; } /// /// Adds a handler for this type. /// public override void RegisterHandler(object obj) { Action handler = (Action)obj; _handlers.AddUnique(handler); } /// /// Removes a handler from this type. /// /// public override void UnregisterHandler(object obj) { Action handler = (Action)obj; int indexOf = _handlers.IndexOf(handler); //Not registered. if (indexOf == -1) return; /* Has already been iterated over, need to subtract * 1 from iteratingIndex to accomodate * for the entry about to be removed. */ if (base.IteratingIndex >= 0 && (indexOf <= base.IteratingIndex)) base.IteratingIndex--; //Remove entry. _handlers.RemoveAt(indexOf); } /// /// True to require authentication for the broadcast type. /// public override bool RequireAuthentication => _requireAuthentication; } /// /// Handles broadcasts received on client, from server. /// internal class ServerBroadcastHandler : BroadcastHandlerBase { /// /// Action handlers for the broadcast. /// Even though List lookups are slower this allows easy adding and removing of entries during iteration. /// private List> _handlers = new(); /// /// Invokes handlers after reading broadcast. /// /// True if a rebuild was required. public override void InvokeHandlers(PooledReader reader, Channel channel) { T result = reader.Read(); for (base.IteratingIndex = 0; base.IteratingIndex < _handlers.Count; base.IteratingIndex++) { Action item = _handlers[base.IteratingIndex]; if (item != null) { item.Invoke(result, channel); } else { _handlers.RemoveAt(base.IteratingIndex); base.IteratingIndex--; } } base.IteratingIndex = -1; } /// /// Adds a handler for this type. /// public override void RegisterHandler(object obj) { Action handler = (Action)obj; _handlers.AddUnique(handler); } /// /// Removes a handler from this type. /// /// public override void UnregisterHandler(object obj) { Action handler = (Action)obj; int indexOf = _handlers.IndexOf(handler); //Not registered. if (indexOf == -1) return; /* Has already been iterated over, need to subtract * 1 from iteratingIndex to accomodate * for the entry about to be removed. */ if (base.IteratingIndex >= 0 && (indexOf <= base.IteratingIndex)) base.IteratingIndex--; //Remove entry. _handlers.RemoveAt(indexOf); } /// /// True to require authentication for the broadcast type. /// public override bool RequireAuthentication => false; } }