wip
This commit is contained in:
@@ -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')))}");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user