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

261 lines
6.3 KiB
C#
Raw Permalink 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 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
IRagonSceneCollector sceneCollector
2023-07-01 07:47:57 +03:00
)
2023-03-06 10:06:43 +04:00
{
_client = client;
_sceneCollector = sceneCollector;
_playerCache = playerCache;
}
2023-07-09 07:40:06 +03:00
public bool TryGetEntity(ushort id, out RagonEntity entity)
2023-03-06 10:06:43 +04:00
{
2023-07-09 07:40:06 +03:00
return _entityMap.TryGetValue(id, out entity);
2023-03-06 10:06:43 +04:00
}
2023-07-01 07:47:57 +03:00
2023-07-29 10:58:06 +03:00
public void Create(RagonEntity entity, RagonPayload spawnPayload)
2023-03-06 10:06:43 +04:00
{
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-07-30 16:56:11 +03:00
spawnPayload?.Write(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-07-29 10:58:06 +03:00
public void Destroy(RagonEntity entity, RagonPayload destroyPayload)
2023-03-06 10:06:43 +04:00
{
2023-10-12 11:18:04 +03:00
if (!entity.IsAttached && !entity.HasAuthority)
2023-03-06 10:06:43 +04:00
{
2023-10-12 11:18:04 +03:00
RagonLog.Warn("Can't destroy object");
2023-03-06 10:06:43 +04:00
return;
}
2023-10-11 19:37:50 +03:00
2023-10-12 11:18:04 +03:00
entity.SetReplication(false);
2023-10-11 19:37:50 +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);
2023-07-30 16:56:11 +03:00
destroyPayload?.Write(buffer);
2023-03-06 10:06:43 +04:00
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()
{
2023-07-29 10:58:06 +03:00
var payload = new RagonPayload(0);
2023-03-06 10:06:43 +04:00
foreach (var ent in _entityList)
ent.Detach(payload);
_entityMap.Clear();
_entityList.Clear();
}
2023-07-29 10:58:06 +03:00
internal RagonEntity TryGetEntity(ushort attachId, ushort entityType, ushort sceneId, ushort entityId, bool hasAuthority, out bool hasCreated)
2023-03-06 10:06:43 +04:00
{
if (sceneId > 0)
{
2023-07-29 10:58:06 +03:00
if (_sceneEntities.TryGetValue(sceneId, out var sceneEntity))
2023-03-06 10:06:43 +04:00
{
2023-07-29 10:58:06 +03:00
_entityMap.Add(entityId, sceneEntity);
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
if (hasAuthority)
2023-07-29 10:58:06 +03:00
_entityList.Add(sceneEntity);
hasCreated = false;
return sceneEntity;
2023-03-06 10:06:43 +04:00
}
}
2023-07-01 07:47:57 +03:00
2023-07-29 10:58:06 +03:00
if (_pendingEntities.TryGetValue(attachId, out var pendingEntity))
2023-03-06 10:06:43 +04:00
{
2023-07-29 10:58:06 +03:00
_pendingEntities.Remove(attachId);
_entityMap.Add(entityId, pendingEntity);
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
if (hasAuthority)
2023-07-29 10:58:06 +03:00
_entityList.Add(pendingEntity);
hasCreated = false;
2023-07-01 07:47:57 +03:00
2023-07-29 10:58:06 +03:00
return pendingEntity;
2023-03-06 10:06:43 +04:00
}
2023-07-01 07:47:57 +03:00
2023-07-29 10:58:06 +03:00
var entity = new RagonEntity(entityType, sceneId);
_entityMap.Add(entityId, entity);
if (hasAuthority)
_entityList.Add(entity);
2023-03-06 10:06:43 +04:00
2023-07-29 10:58:06 +03:00
hasCreated = true;
return entity;
2023-03-06 10:06:43 +04:00
}
2023-07-01 07:47:57 +03:00
2023-03-06 10:06:43 +04:00
internal void OnDestroy(ushort entityId, RagonPayload payload)
{
2023-07-29 10:58:06 +03:00
if (_entityMap.TryGetValue(entityId, out var entity))
2023-03-06 10:06:43 +04:00
{
2023-10-09 09:17:43 +03:00
2023-07-29 10:58:06 +03:00
_entityMap.Remove(entityId);
_entityList.Remove(entity);
2023-07-01 07:47:57 +03:00
2023-07-29 10:58:06 +03:00
entity.Detach(payload);
2023-10-09 09:17:43 +03:00
entity.Dispose();
2023-03-06 10:06:43 +04:00
}
}
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-07-29 10:58:06 +03:00
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
}
}