Files
Ragon/Ragon.Client/Sources/RagonEntityCache.cs
T

255 lines
6.2 KiB
C#
Raw Normal View History

2023-03-06 10:06:43 +04:00
/*
* Copyright 2023 Eduard Kargin <kargin.eduard@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using Ragon.Protocol;
namespace Ragon.Client;
public sealed class RagonEntityCache
{
private readonly List<RagonEntity> _entityList = new();
private readonly Dictionary<uint, RagonEntity> _entityMap = new();
private readonly Dictionary<uint, RagonEntity> _pendingEntities = new();
private readonly Dictionary<uint, RagonEntity> _sceneEntities = new();
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
private readonly RagonClient _client;
private readonly IRagonEntityListener _entityListener;
private readonly IRagonSceneCollector _sceneCollector;
private readonly RagonPlayerCache _playerCache;
private int _localEntitiesCounter = 0;
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
public RagonEntityCache(
2023-07-01 07:47:57 +03:00
RagonClient client,
RagonPlayerCache playerCache,
2023-03-06 10:06:43 +04:00
IRagonEntityListener listener,
IRagonSceneCollector sceneCollector
2023-07-01 07:47:57 +03:00
)
2023-03-06 10:06:43 +04:00
{
_client = client;
_entityListener = listener;
_sceneCollector = sceneCollector;
_playerCache = playerCache;
}
public RagonEntity FindById(ushort id)
{
return _entityMap[id];
}
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
public void Create(RagonEntity entity, IRagonPayload? spawnPayload)
{
2023-07-01 07:47:57 +03:00
var attachId = (ushort)(_playerCache.Local.PeerId + _localEntitiesCounter++);
2023-03-06 10:06:43 +04:00
var buffer = _client.Buffer;
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
buffer.Clear();
buffer.WriteOperation(RagonOperation.CREATE_ENTITY);
buffer.WriteUShort(attachId);
buffer.WriteUShort(entity.Type);
2023-07-01 07:47:57 +03:00
buffer.WriteByte((byte)entity.Authority);
2023-03-06 10:06:43 +04:00
entity.State.WriteInfo(buffer);
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
spawnPayload?.Serialize(buffer);
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
_pendingEntities.Add(attachId, entity);
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
var sendData = buffer.ToArray();
_client.Reliable.Send(sendData);
}
2023-06-27 23:41:30 +03:00
public void Transfer(RagonEntity entity, RagonPlayer player)
{
var buffer = _client.Buffer;
2023-07-01 07:47:57 +03:00
2023-06-27 23:41:30 +03:00
buffer.Clear();
buffer.WriteOperation(RagonOperation.TRANSFER_ENTITY_OWNERSHIP);
buffer.WriteUShort(entity.Id);
buffer.WriteUShort(player.PeerId);
2023-07-01 07:47:57 +03:00
2023-06-27 23:41:30 +03:00
var sendData = buffer.ToArray();
_client.Reliable.Send(sendData);
}
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
public void Destroy(RagonEntity entity, IRagonPayload? destroyPayload)
{
if (!entity.IsAttached)
{
RagonLog.Warn("Can't destroy object, he is not created");
return;
}
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
var buffer = _client.Buffer;
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
buffer.Clear();
2023-04-09 10:52:18 +04:00
buffer.WriteOperation(RagonOperation.REMOVE_ENTITY);
2023-03-06 10:06:43 +04:00
buffer.WriteUShort(entity.Id);
destroyPayload?.Serialize(buffer);
var sendData = buffer.ToArray();
_client.Reliable.Send(sendData);
}
internal void WriteState(RagonBuffer buffer)
{
var changedEntities = 0u;
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
buffer.Clear();
buffer.WriteOperation(RagonOperation.REPLICATE_ENTITY_STATE);
var offset = buffer.WriteOffset;
buffer.Write(0, 16);
foreach (var ent in _entityList)
{
if (!ent.IsAttached ||
!ent.Replication ||
!ent.PropertiesChanged) continue;
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
ent.Write(buffer);
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
changedEntities++;
}
if (changedEntities <= 0) return;
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
buffer.Write(changedEntities, 16, offset);
var data = buffer.ToArray();
_client.Unreliable.Send(data);
}
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
internal void WriteScene(RagonBuffer buffer)
{
_sceneEntities.Clear();
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
var entities = _sceneCollector.Collect();
2023-07-01 07:47:57 +03:00
buffer.WriteUShort((ushort)entities.Length);
2023-03-06 10:06:43 +04:00
foreach (var entity in entities)
{
buffer.WriteUShort(entity.Type);
2023-07-01 07:47:57 +03:00
buffer.WriteByte((byte)entity.Authority);
2023-03-06 10:06:43 +04:00
buffer.WriteUShort(entity.SceneId);
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
entity.State.WriteInfo(buffer);
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
_sceneEntities.Add(entity.SceneId, entity);
}
}
internal void CacheScene()
{
_sceneEntities.Clear();
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
var entities = _sceneCollector.Collect();
foreach (var entity in entities)
_sceneEntities.Add(entity.SceneId, entity);
}
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
internal void Cleanup()
{
var payload = new RagonPayload();
foreach (var ent in _entityList)
ent.Detach(payload);
_entityMap.Clear();
_entityList.Clear();
}
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
internal RagonEntity OnCreate(ushort attachId, ushort entityType, ushort sceneId, ushort entityId, bool hasAuthority)
{
if (sceneId > 0)
{
if (_sceneEntities.TryGetValue(sceneId, out var entity))
{
_entityMap.Add(entityId, entity);
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
if (hasAuthority)
_entityList.Add(entity);
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
return entity;
}
}
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
if (_pendingEntities.Remove(attachId, out var existsEntity))
{
_entityMap.Add(entityId, existsEntity);
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
if (hasAuthority)
_entityList.Add(existsEntity);
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
return existsEntity;
}
else
{
var entity = new RagonEntity(entityType, sceneId);
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
_entityMap.Add(entityId, entity);
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
if (hasAuthority)
_entityList.Add(entity);
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
_entityListener.OnEntityCreated(entity);
return entity;
}
}
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
internal void OnDestroy(ushort entityId, RagonPayload payload)
{
if (_entityMap.Remove(entityId, out var ragonEntity))
{
_entityList.Remove(ragonEntity);
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
ragonEntity.Detach(payload);
}
}
internal void OnState(ushort entityId, RagonBuffer buffer)
{
if (_entityMap.TryGetValue(entityId, out var entity))
entity.Read(buffer);
2023-07-01 07:47:57 +03:00
else
2023-03-06 10:06:43 +04:00
RagonLog.Warn($"Entity {entityId} not found!");
}
internal void OnEvent(RagonPlayer player, ushort entityId, ushort eventCode, RagonBuffer buffer)
{
if (_entityMap.TryGetValue(entityId, out var entity))
entity.Event(eventCode, player, buffer);
2023-07-01 07:47:57 +03:00
else
2023-03-06 10:06:43 +04:00
RagonLog.Warn($"Entity {entityId} not found!");
}
internal void OnOwnershipChanged(RagonPlayer player, ushort entityId)
{
if (_entityMap.TryGetValue(entityId, out var entity))
2023-07-01 07:47:57 +03:00
{
if (player.IsLocal)
_entityList.Add(entity);
else
_entityList.Remove(entity);
2023-03-06 10:06:43 +04:00
entity.OnOwnershipChanged(player);
2023-07-01 07:47:57 +03:00
}
2023-03-06 10:06:43 +04:00
else
2023-07-01 07:47:57 +03:00
{
2023-03-06 10:06:43 +04:00
RagonLog.Warn($"Entity {entityId} not found!");
2023-07-01 07:47:57 +03:00
}
2023-03-06 10:06:43 +04:00
}
}