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
@@ -29,10 +29,10 @@ namespace Ragon.Server.Handler
private readonly IRagonServer _server;
private readonly RagonContextObserver _observer;
private readonly RagonServerConfiguration _configuration;
private readonly RagonBuffer _writer;
private readonly RagonStream _writer;
public AuthorizationOperation(RagonBuffer reader,
RagonBuffer writer,
public AuthorizationOperation(RagonStream reader,
RagonStream writer,
IRagonServer server,
IServerPlugin serverPlugin,
RagonContextObserver observer,
@@ -21,10 +21,10 @@ namespace Ragon.Server.Handler;
public abstract class BaseOperation
{
protected readonly RagonBuffer Reader;
protected readonly RagonBuffer Writer;
protected readonly RagonStream Reader;
protected readonly RagonStream Writer;
public BaseOperation(RagonBuffer reader, RagonBuffer writer)
public BaseOperation(RagonStream reader, RagonStream writer)
{
Reader = reader;
Writer = writer;
@@ -1,79 +0,0 @@
/*
* 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;
using Ragon.Server.Entity;
using Ragon.Server.IO;
using Ragon.Server.Logging;
namespace Ragon.Server.Handler;
public sealed class EntityCreateOperation : BaseOperation
{
private readonly IRagonLogger _logger = LoggerManager.GetLogger(nameof(EntityCreateOperation));
private RagonServerConfiguration _configuration;
public EntityCreateOperation(RagonBuffer reader, RagonBuffer writer, RagonServerConfiguration configuration) :
base(reader, writer)
{
_configuration = configuration;
}
public override void Handle(RagonContext context, NetworkChannel channel)
{
var player = context.RoomPlayer;
var room = context.Room;
var attachId = Reader.ReadUShort();
var entityType = Reader.ReadUShort();
var eventAuthority = (RagonAuthority) Reader.ReadByte();
var propertiesCount = Reader.ReadUShort();
var entityParameters = new RagonEntityParameters()
{
Type = entityType,
Authority = eventAuthority,
AttachId = attachId,
StaticId = 0,
BufferedEvents = context.LimitBufferedEvents,
};
var entity = new RagonEntity(entityParameters);
for (var i = 0; i < propertiesCount; i++)
{
var propertyType = Reader.ReadBool();
var propertySize = Reader.ReadUShort();
entity.AddProperty(new RagonProperty(propertySize, propertyType, _configuration.LimitPropertySize));
}
if (Reader.Capacity > 0)
entity.Payload.Read(Reader);
var plugin = room.Plugin;
if (!plugin.OnEntityCreate(player, entity))
return;
entity.Attach(player);
room.AttachEntity(entity);
player.AttachEntity(entity);
entity.Create();
_logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} created entity {entity.Id}:{entity.Type}");
}
}
@@ -1,63 +0,0 @@
/*
* 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;
using Ragon.Server.Event;
using Ragon.Server.IO;
using Ragon.Server.Logging;
namespace Ragon.Server.Handler;
public sealed class EntityEventOperation : BaseOperation
{
private readonly IRagonLogger _logger = LoggerManager.GetLogger(nameof(EntityEventOperation));
public EntityEventOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer)
{
}
public override void Handle(RagonContext context, NetworkChannel channel)
{
var player = context.RoomPlayer;
var room = context.Room;
var entityId = Reader.ReadUShort();
if (!room.Entities.TryGetValue(entityId, out var ent))
{
_logger.Warning($"Entity not found for event with Id {entityId}");
return;
}
var eventId = Reader.ReadUShort();
var eventMode = (RagonReplicationMode)Reader.ReadByte();
var targetMode = (RagonTarget)Reader.ReadByte();
var targetPlayerPeerId = (ushort)0;
if (targetMode == RagonTarget.Player)
targetPlayerPeerId = Reader.ReadUShort();
var @event = new RagonEvent(player, eventId);
@event.Read(Reader);
if (targetMode == RagonTarget.Player && room.Players.TryGetValue(targetPlayerPeerId, out var targetPlayer))
{
ent.ReplicateEvent(player, @event, eventMode, targetPlayer);
return;
}
ent.ReplicateEvent(player, @event, eventMode, targetMode);
}
}
@@ -1,75 +0,0 @@
/*
* 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;
using Ragon.Server.IO;
using Ragon.Server.Logging;
namespace Ragon.Server.Handler;
public sealed class EntityOwnershipOperation : BaseOperation
{
private readonly IRagonLogger _logger = LoggerManager.GetLogger(nameof(EntityOwnershipOperation));
public EntityOwnershipOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer)
{
}
public override void Handle(RagonContext context, NetworkChannel channel)
{
var currentOwner = context.RoomPlayer;
var room = context.Room;
var entityId = Reader.ReadUShort();
var playerPeerId = Reader.ReadUShort();
if (!room.Entities.TryGetValue(entityId, out var entity))
{
_logger.Error($"Entity not found with id {entityId}");
return;
}
if (entity.Owner.Connection.Id != currentOwner.Connection.Id)
{
_logger.Error($"Player not owner of entity with id {entityId}");
return;
}
if (!room.Players.TryGetValue(playerPeerId, out var nextOwner))
{
_logger.Error($"Player not found with id {playerPeerId}");
return;
}
currentOwner.Entities.Remove(entity);
nextOwner.Entities.Add(entity);
entity.Attach(nextOwner);
_logger.Trace($"Entity {entity.Id} next owner {nextOwner.Connection.Id}");
Writer.Clear();
Writer.WriteOperation(RagonOperation.OWNERSHIP_ENTITY_CHANGED);
Writer.WriteUShort(playerPeerId);
Writer.WriteUShort(1);
Writer.WriteUShort(entity.Id);
var sendData = Writer.ToArray();
foreach (var player in room.PlayerList)
player.Connection.Reliable.Send(sendData);
}
}
@@ -1,55 +0,0 @@
/*
* 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;
using Ragon.Server.Entity;
using Ragon.Server.IO;
using Ragon.Server.Logging;
namespace Ragon.Server.Handler;
public sealed class EntityDestroyOperation: BaseOperation
{
private readonly IRagonLogger _logger = LoggerManager.GetLogger(nameof(EntityDestroyOperation));
public EntityDestroyOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer)
{
}
public override void Handle(RagonContext context, NetworkChannel channel)
{
var player = context.RoomPlayer;
var room = context.Room;
var entityId = Reader.ReadUShort();
if (room.Entities.TryGetValue(entityId, out var entity) && entity.Owner.Connection.Id == player.Connection.Id)
{
var payload = new RagonPayload();
payload.Read(Reader);
room.DetachEntity(entity);
player.DetachEntity(entity);
entity.Destroy();
_logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} destoyed entity {entity.Id}");
}
else
{
_logger.Trace($"Entity {entity.Id} not found or Player {context.Connection.Id}|{context.LobbyPlayer.Name} have not authority");
}
}
}
@@ -1,50 +0,0 @@
/*
* 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;
using Ragon.Server.IO;
using Ragon.Server.Logging;
namespace Ragon.Server.Handler;
public sealed class EntityStateOperation: BaseOperation
{
private readonly IRagonLogger _logger = LoggerManager.GetLogger(nameof(EntityStateOperation));
public EntityStateOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer)
{
}
public override void Handle(RagonContext context, NetworkChannel channel)
{
var room = context.Room;
var player = context.RoomPlayer;
var entitiesCount = Reader.ReadUShort();
for (var entityIndex = 0; entityIndex < entitiesCount; entityIndex++)
{
var entityId = Reader.ReadUShort();
if (room.Entities.TryGetValue(entityId, out var entity) && entity.TryReadState(player, Reader))
{
room.Track(entity);
}
else
{
_logger.Error($"Entity with Id {entityId} not found, replication interrupted");
}
}
}
}
@@ -11,8 +11,8 @@ namespace Ragon.Server.Handler
private readonly int _userDataLimit;
public PlayerUserDataOperation(
RagonBuffer reader,
RagonBuffer writer,
RagonStream reader,
RagonStream writer,
int userDataLimit
) : base(reader, writer)
{
@@ -32,8 +32,8 @@ namespace Ragon.Server.Handler
private readonly RagonServerConfiguration _configuration;
public RoomCreateOperation(
RagonBuffer reader,
RagonBuffer writer,
RagonStream reader,
RagonStream writer,
IServerPlugin serverPlugin,
RagonServerConfiguration configuration
) : base(reader,
@@ -107,7 +107,7 @@ namespace Ragon.Server.Handler
_logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} joined to room {room.Id}");
}
private void JoinSuccess(RagonRoomPlayer player, RagonRoom room, RagonBuffer writer)
private void JoinSuccess(RagonRoomPlayer player, RagonRoom room, RagonStream writer)
{
writer.Clear();
writer.WriteOperation(RagonOperation.JOIN_SUCCESS);
@@ -21,7 +21,7 @@ namespace Ragon.Server.Handler;
public sealed class RoomDataOperation : BaseOperation
{
public RoomDataOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer)
public RoomDataOperation(RagonStream reader, RagonStream writer) : base(reader, writer)
{
}
@@ -29,8 +29,8 @@ public sealed class RoomDataOperation : BaseOperation
{
var player = context.RoomPlayer;
var room = context.Room;
var data = Reader.RawData;
var data = Reader.ReadBinary(Reader.Lenght);
var dataSize = data.Length - 1;
var headerSize = 3;
var size = headerSize + dataSize;
@@ -10,7 +10,7 @@ public class RoomEventOperation : BaseOperation
{
private readonly IRagonLogger _logger = LoggerManager.GetLogger(nameof(RoomEventOperation));
public RoomEventOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer)
public RoomEventOperation(RagonStream reader, RagonStream writer) : base(reader, writer)
{
}
@@ -26,7 +26,7 @@ public sealed class RoomJoinOperation : BaseOperation
{
private readonly IRagonLogger _logger = LoggerManager.GetLogger(nameof(RoomJoinOperation));
public RoomJoinOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer)
public RoomJoinOperation(RagonStream reader, RagonStream writer) : base(reader, writer)
{
}
@@ -54,7 +54,7 @@ public sealed class RoomJoinOperation : BaseOperation
_logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} joined to {existsRoom.Id}");
}
private void JoinSuccess(RagonContext context, RagonRoom room, RagonBuffer writer)
private void JoinSuccess(RagonContext context, RagonRoom room, RagonStream writer)
{
writer.Clear();
writer.WriteOperation(RagonOperation.JOIN_SUCCESS);
@@ -81,7 +81,7 @@ public sealed class RoomJoinOperation : BaseOperation
context.Connection.Reliable.Send(sendData);
}
private void JoinFailed(RagonContext context, RagonBuffer writer)
private void JoinFailed(RagonContext context, RagonStream writer)
{
writer.Clear();
writer.WriteOperation(RagonOperation.JOIN_FAILED);
@@ -32,8 +32,8 @@ public sealed class RoomJoinOrCreateOperation : BaseOperation
private readonly RagonServerConfiguration _configuration;
public RoomJoinOrCreateOperation(
RagonBuffer reader,
RagonBuffer writer,
RagonStream reader,
RagonStream writer,
IServerPlugin serverPlugin,
RagonServerConfiguration configuration
) : base(reader, writer)
@@ -100,7 +100,7 @@ public sealed class RoomJoinOrCreateOperation : BaseOperation
}
}
private void JoinSuccess(RagonRoomPlayer player, RagonRoom room, RagonBuffer writer)
private void JoinSuccess(RagonRoomPlayer player, RagonRoom room, RagonStream writer)
{
writer.Clear();
writer.WriteOperation(RagonOperation.JOIN_SUCCESS);
@@ -24,7 +24,7 @@ public sealed class RoomLeaveOperation: BaseOperation
{
private readonly IRagonLogger _logger = LoggerManager.GetLogger(nameof(RoomLeaveOperation));
public RoomLeaveOperation(RagonBuffer reader, RagonBuffer writer): base(reader, writer)
public RoomLeaveOperation(RagonStream reader, RagonStream writer): base(reader, writer)
{
}
@@ -23,7 +23,7 @@ public sealed class RoomOwnershipOperation : BaseOperation
{
private readonly IRagonLogger _logger = LoggerManager.GetLogger(nameof(RoomOwnershipOperation));
public RoomOwnershipOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer)
public RoomOwnershipOperation(RagonStream reader, RagonStream writer) : base(reader, writer)
{
}
@@ -27,8 +27,8 @@ public sealed class RoomUserDataOperation : BaseOperation
private readonly int _userDataLimit;
public RoomUserDataOperation(
RagonBuffer reader,
RagonBuffer writer,
RagonStream reader,
RagonStream writer,
int userDataLimit
) : base(reader, writer)
{
@@ -1,52 +0,0 @@
/*
* 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;
using Ragon.Server.IO;
using Ragon.Server.Logging;
namespace Ragon.Server.Handler;
public class SceneLoadOperation: BaseOperation
{
private readonly IRagonLogger _logger = LoggerManager.GetLogger(nameof(SceneLoadOperation));
public SceneLoadOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer) {}
public override void Handle(RagonContext context, NetworkChannel channel)
{
var roomOwner = context.Room.Owner;
var currentPlayer = context.RoomPlayer;
var room = context.Room;
var sceneName = Reader.ReadString();
if (roomOwner.Connection.Id != currentPlayer.Connection.Id)
{
_logger.Warning("Only owner can change scene!");
return;
}
room.UpdateMap(sceneName);
Writer.Clear();
Writer.WriteOperation(RagonOperation.LOAD_SCENE);
Writer.WriteString(sceneName);
var sendData = Writer.ToArray();
foreach (var player in room.PlayerList)
player.Connection.Reliable.Send(sendData);
}
}
@@ -1,168 +0,0 @@
/*
* 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;
using Ragon.Server.Entity;
using Ragon.Server.IO;
using Ragon.Server.Lobby;
using Ragon.Server.Logging;
using Ragon.Server.Room;
namespace Ragon.Server.Handler
{
public sealed class SceneLoadedOperation : BaseOperation
{
private readonly IRagonLogger _logger = LoggerManager.GetLogger(nameof(SceneLoadedOperation));
private RagonServerConfiguration _configuration;
public SceneLoadedOperation(RagonBuffer reader, RagonBuffer writer, RagonServerConfiguration serverConfiguration) : base(reader, writer)
{
_configuration = serverConfiguration;
}
public override void Handle(RagonContext context, NetworkChannel channel)
{
if (context.ConnectionStatus == ConnectionStatus.Unauthorized)
return;
var owner = context.Room.Owner;
var player = context.RoomPlayer;
var room = context.Room;
if (player.IsLoaded)
{
_logger.Warning($"Player {player.Name}:{player.Connection.Id} already ready");
return;
}
if (player == owner)
{
var statics = Reader.ReadUShort();
for (var staticIndex = 0; staticIndex < statics; staticIndex++)
{
var entityType = Reader.ReadUShort();
var eventAuthority = (RagonAuthority)Reader.ReadByte();
var staticId = Reader.ReadUShort();
var propertiesCount = Reader.ReadUShort();
var entityParameters = new RagonEntityParameters()
{
Type = entityType,
Authority = eventAuthority,
AttachId = 0,
StaticId = staticId,
BufferedEvents = context.LimitBufferedEvents,
};
var entity = new RagonEntity(entityParameters);
for (var propertyIndex = 0; propertyIndex < propertiesCount; propertyIndex++)
{
var propertyType = Reader.ReadBool();
var propertySize = Reader.ReadUShort();
entity.AddProperty(new RagonProperty(propertySize, propertyType, _configuration.LimitPropertySize));
}
var roomPlugin = room.Plugin;
if (!roomPlugin.OnEntityCreate(player, entity)) continue;
var playerInfo = $"Player {context.Connection.Id}|{context.LobbyPlayer.Name}";
var entityInfo = $"{entity.Id}:{entity.Type}";
_logger.Trace($"{playerInfo} created static entity {entityInfo}");
entity.Attach(player);
room.AttachEntity(entity);
player.AttachEntity(entity);
}
_logger.Trace($"Player {context.Connection.Id}|{context.LobbyPlayer.Name} loaded");
room.WaitPlayersList.Add(player);
foreach (var roomPlayer in room.WaitPlayersList)
{
DispatchPlayerJoinExcludePlayer(room, roomPlayer, Writer);
roomPlayer.SetReady();
}
room.UpdateReadyPlayerList();
DispatchSnapshot(room, room.WaitPlayersList, Writer);
room.WaitPlayersList.Clear();
}
else if (owner.IsLoaded)
{
player.SetReady();
DispatchPlayerJoinExcludePlayer(room, player, Writer);
room.UpdateReadyPlayerList();
DispatchSnapshot(room, new List<RagonRoomPlayer>() { player }, Writer);
foreach (var entity in room.EntityList)
entity.RestoreBufferedEvents(player, Writer);
}
else
{
_logger.Trace($"Player {player.Connection.Id}|{context.LobbyPlayer.Name} waiting owner of room");
room.WaitPlayersList.Add(player);
}
}
private void DispatchPlayerJoinExcludePlayer(RagonRoom room, RagonRoomPlayer roomPlayer, RagonBuffer writer)
{
writer.Clear();
writer.WriteOperation(RagonOperation.PLAYER_JOINED);
writer.WriteUShort(roomPlayer.Connection.Id);
writer.WriteString(roomPlayer.Id);
writer.WriteString(roomPlayer.Name);
var sendData = writer.ToArray();
foreach (var awaiter in room.ReadyPlayersList)
{
if (awaiter != roomPlayer)
awaiter.Connection.Reliable.Send(sendData);
}
}
private void DispatchSnapshot(RagonRoom room, List<RagonRoomPlayer> receviersList, RagonBuffer writer)
{
writer.Clear();
writer.WriteOperation(RagonOperation.SNAPSHOT);
var dynamicEntities = room.DynamicEntitiesList;
var dynamicEntitiesCount = (ushort)dynamicEntities.Count;
writer.WriteUShort(dynamicEntitiesCount);
foreach (var entity in dynamicEntities)
entity.Snapshot(writer);
var staticEntities = room.StaticEntitiesList;
var staticEntitiesCount = (ushort)staticEntities.Count;
writer.WriteUShort(staticEntitiesCount);
foreach (var entity in staticEntities)
entity.Snapshot(writer);
var sendData = writer.ToArray();
foreach (var player in receviersList)
player.Connection.Reliable.Send(sendData);
}
}
}
@@ -19,18 +19,18 @@ using Ragon.Server.IO;
namespace Ragon.Server.Handler;
public class TimestampSyncOperation: BaseOperation
public class TimestampSyncOperation : BaseOperation
{
public TimestampSyncOperation(RagonBuffer reader, RagonBuffer writer) : base(reader, writer)
public TimestampSyncOperation(RagonStream reader, RagonStream writer) : base(reader, writer)
{
}
public override void Handle(RagonContext context, NetworkChannel channel)
{
var timestamp0 = Reader.Read(32);
var timestamp1 = Reader.Read(32);
var timestamp0 = (uint)Reader.ReadInt();
var timestamp1 = (uint)Reader.ReadInt();
var value = new DoubleToUInt() { Int0 = timestamp0, Int1 = timestamp1 };
context.RoomPlayer?.SetTimestamp(value.Double);
}
}