using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Runtime.Serialization.Formatters.Binary; using System.Timers; using BITKit.Entities.Player; using UnityEngine; namespace BITKit.Entities { [Serializable] public struct EntitiesNetSyncCommand { public byte[] Data; } [Serializable] public struct EntitiesNetSyncBatchCommand { public int Length; public int[] Ids; public ulong[] AddressablePaths; public EntitiesNetSyncCommand[] Commands; } public class EntitiesNetService : MonoBehaviour { [Header(Constant.Header.Services)] [SerializeReference, SubclassSelector] private ITicker ticker; [SerializeReference, SubclassSelector] private IEntitiesService entitiesService; [SerializeReference, SubclassSelector] private IPlayerService playerService; [SerializeReference, SubclassSelector]private INetClient client; [SerializeReference, SubclassSelector]private INetServer server; private INetProvider clientNetProvider => client.Source as INetProvider; private INetProvider serverNetProvider => server.Source as INetProvider; [Inject] private IEntityBinaryHeader _playerHeader; [Inject] private IAddressable _playerAddressable; [Inject] private IEntity _playerEntity; private readonly ConcurrentQueue _batchCommands = new(); private readonly ConcurrentQueue _syncCommands = new(); private void Start() { ticker.Add(Tick); clientNetProvider.AddCommandListener(_batchCommands.Enqueue); serverNetProvider.AddCommandListener(_syncCommands.Enqueue); playerService.OnPlayerInitialized+=OnPlayerInitialized; playerService.OnPlayerDisposed += OnPlayerDisposed; destroyCancellationToken.Register(() => { ticker.Remove(Tick); playerService.OnPlayerInitialized-=OnPlayerInitialized; }); } private void OnPlayerDisposed(Entity obj) { _playerHeader = null; _playerAddressable = null; _playerEntity = null; } private void OnPlayerInitialized(Entity obj) { obj.Inject(this); } private void OnSyncCommand(EntitiesNetSyncCommand obj) { using var ms = new MemoryStream(obj.Data); using var reader = new BinaryReader(ms); var id = reader.ReadInt32(); var path = reader.ReadUInt64(); entitiesService.GetOrAdd(id, x => AddEntity(id, path)).TryGetComponent(out var header); header.Deserialize(reader); } private void OnBatchCommand(EntitiesNetSyncBatchCommand obj) { for (var i = 0; i < obj.Length; i++) { var id = obj.Ids[i]; var path = obj.AddressablePaths[i]; var command = obj.Commands[i]; var entity = entitiesService.GetOrAdd(id,x=>AddEntity(id,path)); entity.TryGetComponent(out var header); using var ms = new MemoryStream(command.Data); using var reader = new BinaryReader(ms); header.Deserialize(reader); } } private static IEntity AddEntity(int id,ulong addressableId) { var entity = AddressableHelper.Get(addressableId); var instance = Instantiate(entity).GetComponent(); instance.Id = id; instance.WaitForInitializationComplete(); return instance; } private void Tick(float deltaTime) { while (_batchCommands.TryDequeue(out var command)) { OnBatchCommand(command); } while (_syncCommands.TryDequeue(out var command)) { OnSyncCommand(command); } if (client.IsConnected is false && server.IsRunningServer is false) return; using var memoryStream = new MemoryStream(); if (client.IsConnected && _playerEntity as Entity && _playerHeader != null) { using var writer = new BinaryWriter(memoryStream); writer.Write(_playerEntity.Id); writer.Write(_playerAddressable.AddressableId); _playerHeader.Serialize(writer); var command = new EntitiesNetSyncCommand { Data = memoryStream.ToArray() }; clientNetProvider.ServerCommand(command); } else if (server.IsRunningServer) { using var writer = new BinaryWriter(memoryStream); var headers = entitiesService.QueryComponents(); var batchCommand = new EntitiesNetSyncBatchCommand() { Length = headers.Length, Ids = new int[headers.Length], AddressablePaths = new ulong[headers.Length], Commands = new EntitiesNetSyncCommand[headers.Length] }; var count = -1; foreach (var (entity, header, addressable) in headers) { count++; using var ms = new MemoryStream(); using var entityWriter = new BinaryWriter(ms); header.Serialize(entityWriter); entityWriter.Flush(); batchCommand.Ids[count] = entity.Id; batchCommand.AddressablePaths[count] = addressable.AddressableId; batchCommand.Commands[count] = new EntitiesNetSyncCommand { Data = ms.ToArray() }; } serverNetProvider.AllClientCommand(batchCommand); } } } }