173 lines
5.0 KiB
C#
173 lines
5.0 KiB
C#
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<EntitiesNetSyncBatchCommand> _batchCommands = new();
|
|
private readonly ConcurrentQueue<EntitiesNetSyncCommand> _syncCommands = new();
|
|
|
|
private void Start()
|
|
{
|
|
ticker.Add(Tick);
|
|
|
|
clientNetProvider.AddCommandListener<EntitiesNetSyncBatchCommand>(_batchCommands.Enqueue);
|
|
|
|
serverNetProvider.AddCommandListener<EntitiesNetSyncCommand>(_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<IEntityBinaryHeader>(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<IEntityBinaryHeader>(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<GameObject>(addressableId);
|
|
var instance = Instantiate(entity).GetComponent<Entity>();
|
|
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<IEntity, IEntityBinaryHeader, IAddressable>();
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|