This commit is contained in:
2024-09-28 20:11:56 +03:00
parent 5136f08dab
commit 672bb1ff6d
95 changed files with 741 additions and 601 deletions
@@ -0,0 +1,16 @@
using Ragon.Protocol;
using Ragon.Server.Room;
namespace Ragon.Server.Entity;
public interface IRagonEntity
{
public ushort Id { get; }
public ushort Type { get; }
public ushort StaticId { get; }
public ushort AttachId { get; }
public RagonRoomPlayer Owner { get; }
public RagonAuthority Authority { get; }
public RagonPayload Payload { get; }
public IRagonEntityState State { get; }
}
@@ -0,0 +1,8 @@
using Ragon.Protocol;
namespace Ragon.Server.Entity;
public interface IRagonEntityState
{
}
@@ -0,0 +1,246 @@
/*
* Copyright 2023-2024 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 System.Collections.Generic;
using Ragon.Protocol;
using Ragon.Server.Event;
using Ragon.Server.Room;
namespace Ragon.Server.Entity;
public class RagonEntity : IRagonEntity
{
private static ushort _idGenerator = 100;
public ushort Id { get; private set; }
public ushort Type { get; private set; }
public ushort StaticId { get; private set; }
public ushort AttachId { get; private set; }
public RagonRoomPlayer Owner { get; private set; }
public RagonAuthority Authority { get; private set; }
public RagonPayload Payload { get; private set; }
public IRagonEntityState State => _state;
private readonly List<RagonEvent> _bufferedEvents;
private readonly int _limitBufferedEvents;
private readonly RagonEntityState _state;
public RagonEntity(RagonEntityParameters parameters)
{
Id = _idGenerator++;
StaticId = parameters.StaticId;
Type = parameters.Type;
AttachId = parameters.AttachId;
Authority = parameters.Authority;
Payload = new RagonPayload();
_state = new RagonEntityState(this);
_bufferedEvents = new List<RagonEvent>();
_limitBufferedEvents = parameters.BufferedEvents;
}
public void Attach(RagonRoomPlayer owner)
{
Owner = owner;
}
public void Detach()
{
}
public void RestoreBufferedEvents(RagonRoomPlayer roomPlayer, RagonStream writer)
{
foreach (var evnt in _bufferedEvents)
{
writer.Clear();
writer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT);
writer.WriteUShort(evnt.EventCode);
writer.WriteUShort(evnt.Invoker.Connection.Id);
writer.WriteByte((byte)RagonReplicationMode.Server);
writer.WriteUShort(Id);
evnt.Write(writer);
var sendData = writer.ToArray();
roomPlayer.Connection.Reliable.Send(sendData);
}
}
public void Create()
{
var room = Owner.Room;
var buffer = room.Writer;
buffer.Clear();
buffer.WriteOperation(RagonOperation.CREATE_ENTITY);
buffer.WriteUShort(AttachId);
buffer.WriteUShort(Type);
buffer.WriteUShort(Id);
buffer.WriteUShort(Owner.Connection.Id);
Payload.Write(buffer);
var sendData = buffer.ToArray();
foreach (var player in room.ReadyPlayersList)
player.Connection.Reliable.Send(sendData);
}
public void Destroy()
{
var room = Owner.Room;
var buffer = room.Writer;
buffer.Clear();
buffer.WriteOperation(RagonOperation.REMOVE_ENTITY);
buffer.WriteUShort(Id);
Payload.Write(buffer);
var sendData = buffer.ToArray();
foreach (var player in room.ReadyPlayersList)
player.Connection.Reliable.Send(sendData);
}
public void Snapshot(RagonStream buffer)
{
buffer.WriteUShort(Type);
buffer.WriteUShort(Id);
if (StaticId != 0)
buffer.WriteUShort(StaticId);
buffer.WriteUShort(Owner.Connection.Id);
buffer.WriteUShort(Payload.Size);
Payload.Write(buffer);
_state.Snapshot(buffer);
}
public void ReplicateEvent(
RagonRoomPlayer invoker,
RagonEvent evnt,
RagonReplicationMode eventMode,
RagonRoomPlayer targetPlayer
)
{
if (Authority == RagonAuthority.OwnerOnly && invoker.Connection.Id != Owner.Connection.Id)
{
return;
}
var room = Owner.Room;
var buffer = room.Writer;
buffer.Clear();
buffer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT);
buffer.WriteUShort(evnt.EventCode);
buffer.WriteUShort(invoker.Connection.Id);
buffer.WriteByte((byte)eventMode);
buffer.WriteUShort(Id);
evnt.Write(buffer);
var sendData = buffer.ToArray();
targetPlayer.Connection.Reliable.Send(sendData);
}
public void ReplicateEvent(
RagonRoomPlayer invoker,
RagonEvent evnt,
RagonReplicationMode eventMode,
RagonTarget targetMode
)
{
if (Authority == RagonAuthority.OwnerOnly && invoker.Connection.Id != Owner.Connection.Id)
{
return;
}
if (eventMode == RagonReplicationMode.Buffered && targetMode != RagonTarget.Owner && _bufferedEvents.Count < _limitBufferedEvents)
{
_bufferedEvents.Add(evnt);
}
var room = Owner.Room;
var buffer = room.Writer;
buffer.Clear();
buffer.WriteOperation(RagonOperation.REPLICATE_ENTITY_EVENT);
buffer.WriteUShort(evnt.EventCode);
buffer.WriteUShort(invoker.Connection.Id);
buffer.WriteByte((byte)eventMode);
buffer.WriteUShort(Id);
evnt.Write(buffer);
var sendData = buffer.ToArray();
switch (targetMode)
{
case RagonTarget.Owner:
{
Owner.Connection.Reliable.Send(sendData);
break;
}
case RagonTarget.ExceptOwner:
{
foreach (var roomPlayer in room.ReadyPlayersList)
{
if (roomPlayer.Connection.Id != Owner.Connection.Id)
roomPlayer.Connection.Reliable.Send(sendData);
}
break;
}
case RagonTarget.ExceptInvoker:
{
foreach (var roomPlayer in room.ReadyPlayersList)
{
if (roomPlayer.Connection.Id != invoker.Connection.Id)
roomPlayer.Connection.Reliable.Send(sendData);
}
break;
}
case RagonTarget.All:
{
foreach (var roomPlayer in room.ReadyPlayersList)
roomPlayer.Connection.Reliable.Send(sendData);
break;
}
}
}
public void AddProperty(RagonProperty property)
{
_state.AddProperty(property);
}
public void WriteState(RagonStream writer)
{
_state.Write(writer);
}
public bool TryReadState(RagonRoomPlayer player, RagonBuffer reader)
{
if (Owner.Connection.Id != player.Connection.Id)
return false;
_state.Read(reader);
return true;
}
}
@@ -0,0 +1,53 @@
/*
* Copyright 2023-2024 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 System.Collections.Generic;
using Ragon.Server.Entity;
namespace Ragon.Server;
public class RagonEntityCache
{
private readonly List<RagonEntity> _dynamicEntitiesList = new List<RagonEntity>();
private readonly List<RagonEntity> _staticEntitiesList = new List<RagonEntity>();
private readonly Dictionary<ushort, RagonEntity> _entitiesMap = new Dictionary<ushort, RagonEntity>();
public IReadOnlyList<RagonEntity> StaticList => _staticEntitiesList;
public IReadOnlyList<RagonEntity> DynamicList => _dynamicEntitiesList;
public IReadOnlyDictionary<ushort, RagonEntity> Map => _entitiesMap;
public void Add(RagonEntity entity)
{
if (entity.StaticId != 0)
_staticEntitiesList.Add(entity);
else
_dynamicEntitiesList.Add(entity);
_entitiesMap.Add(entity.Id, entity);
}
public bool Remove(RagonEntity entity)
{
if (_entitiesMap.Remove(entity.Id, out var existEntity))
{
_staticEntitiesList.Remove(entity);
_dynamicEntitiesList.Remove(entity);
return true;
}
return false;
}
}
@@ -0,0 +1,28 @@
/*
* Copyright 2023-2024 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.Server.Entity;
public ref struct RagonEntityParameters
{
public ushort Type;
public ushort StaticId;
public ushort AttachId;
public RagonAuthority Authority;
public int BufferedEvents;
}
@@ -0,0 +1,78 @@
/*
* Copyright 2023-2024 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 System.Collections.Generic;
using Ragon.Protocol;
namespace Ragon.Server.Entity;
public class RagonEntityState: IRagonEntityState
{
private readonly List<RagonProperty> _properties;
private readonly RagonEntity _entity;
public RagonEntityState(RagonEntity entity, int capacity = 10)
{
_entity = entity;
_properties = new List<RagonProperty>(capacity);
}
public void AddProperty(RagonProperty property)
{
_properties.Add(property);
}
public void Write(RagonStream buffer)
{
buffer.WriteUShort(_entity.Id);
foreach (var property in _properties)
{
if (property.IsDirty)
{
buffer.WriteBool(true);
property.Write(buffer);
property.Clear();
continue;
}
buffer.WriteBool(false);
}
}
public void Read(RagonBuffer buffer)
{
foreach (var property in _properties)
{
if (buffer.ReadBool())
property.Read(buffer);
}
}
public void Snapshot(RagonStream buffer)
{
foreach (var property in _properties)
{
if (property.HasData)
{
buffer.WriteBool(true);
property.Write(buffer);
continue;
}
buffer.WriteBool(false);
}
}
}
@@ -0,0 +1,40 @@
/*
* Copyright 2023-2024 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.Server.Entity;
public class RagonPayload
{
private uint[] _data = new uint[128];
private int _size = 0;
public ushort Size => (ushort) _size;
public void Read(RagonStream buffer)
{
// _size = buffer.Capacity;
// buffer.ReadArray(_data, _size);
}
public void Write(RagonStream buffer)
{
// if (_size == 0) return;
// buffer.WriteArray(_data, _size);
}
}
@@ -0,0 +1,78 @@
/*
* Copyright 2023-2024 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 System;
using System.Linq;
using Ragon.Protocol;
namespace Ragon.Server.Entity;
public class RagonProperty : RagonPayload
{
public int Size { get; set; }
public bool IsDirty { get; private set; }
public bool IsFixed { get; private set; }
public bool HasData { get; private set; }
private uint[] _data;
public RagonProperty(int size, bool isFixed, int limit)
{
Size = size;
IsFixed = isFixed;
IsDirty = false;
_data = new uint[limit / 4 + 1];
}
public void Read(RagonBuffer buffer)
{
if (IsFixed)
{
buffer.ReadArray(_data, Size);
}
else
{
Size = (int) buffer.Read();
buffer.ReadArray(_data, Size);
}
HasData = true;
IsDirty = true;
}
public void Write(RagonBuffer buffer)
{
if (IsFixed)
{
buffer.WriteArray(_data, Size);
return;
}
buffer.Write((ushort) Size);
buffer.WriteArray(_data, Size);
}
public void Clear()
{
IsDirty = false;
}
public void Dump()
{
Console.WriteLine( $"[{Size.ToString("00")}] {string.Join("", _data.Take(8).Reverse().Select(b => Convert.ToString(b, 2).PadLeft(32, '0')))}");
}
}