diff --git a/Ragon.Protocol/Sources/RagonBuffer.cs b/Ragon.Protocol/Sources/RagonBuffer.cs
index 2b5b468..0a00697 100644
--- a/Ragon.Protocol/Sources/RagonBuffer.cs
+++ b/Ragon.Protocol/Sources/RagonBuffer.cs
@@ -65,6 +65,8 @@ namespace Ragon.Protocol
{
public class RagonBuffer
{
+ private const int MaxBufferSize = 1024 * 1024; // 1MB max buffer size
+
private int _read;
private int _write;
private uint[] _buckets;
@@ -404,6 +406,12 @@ namespace Ragon.Protocol
public void FromArray(byte[] data)
{
var length = data.Length;
+
+ if (length > MaxBufferSize)
+ {
+ throw new InvalidOperationException($"Input data exceeds maximum buffer size: {length} bytes > {MaxBufferSize} bytes");
+ }
+
var bucketsCount = length / 4 + 1;
if (_buckets.Length < bucketsCount)
@@ -493,7 +501,14 @@ namespace Ragon.Protocol
private void Resize(int capacity)
{
- var buckets = new uint[_buckets.Length * 2 + capacity];
+ var newSize = _buckets.Length * 2 + capacity;
+
+ if (newSize * 4 > MaxBufferSize)
+ {
+ throw new InvalidOperationException($"Buffer size limit exceeded: {newSize * 4} bytes > {MaxBufferSize} bytes");
+ }
+
+ var buckets = new uint[newSize];
Array.Copy(_buckets, buckets, _buckets.Length);
_buckets = buckets;
}
diff --git a/Ragon.Relay/Ragon.Relay.csproj b/Ragon.Relay/Ragon.Relay.csproj
index 23b5632..99685ce 100644
--- a/Ragon.Relay/Ragon.Relay.csproj
+++ b/Ragon.Relay/Ragon.Relay.csproj
@@ -3,7 +3,7 @@
Exe
Ragon.Relay
- net8.0
+ net9.0
diff --git a/Ragon.Relay/relay.config.json b/Ragon.Relay/relay.config.json
index f80ef6e..d6c92cc 100644
--- a/Ragon.Relay/relay.config.json
+++ b/Ragon.Relay/relay.config.json
@@ -10,5 +10,6 @@
"limitRooms": 200,
"limitBufferedEvents": 50,
"limitUserDataSize": 1024,
- "limitPropertySize": 512
+ "limitPropertySize": 512,
+ "limitConnectionsPerProject": 100
}
\ No newline at end of file
diff --git a/Ragon.Server.WebSocketServer/Sources/WebSocketServer.cs b/Ragon.Server.WebSocketServer/Sources/WebSocketServer.cs
index 6d5f232..cad4356 100644
--- a/Ragon.Server.WebSocketServer/Sources/WebSocketServer.cs
+++ b/Ragon.Server.WebSocketServer/Sources/WebSocketServer.cs
@@ -43,7 +43,7 @@ public class WebSocketServer : INetworkServer
_executor = new Executor();
}
- public async void StartAccept(CancellationToken cancellationToken)
+ public async ValueTask StartAccept(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
@@ -58,26 +58,26 @@ public class WebSocketServer : INetworkServer
context.Response.Close();
continue;
}
-
+
var webSocketContext = await context.AcceptWebSocketAsync(null);
var webSocket = webSocketContext.WebSocket;
var peerId = _sequencer.Pop();
-
- connection = new WebSocketConnection(webSocket, peerId);
+
+ connection = new WebSocketConnection(webSocket, peerId);
}
catch (Exception ex)
{
_logger.Error(ex);
continue;
}
-
+
_connections[connection.Id] = connection;
-
- StartListen(connection, cancellationToken);
+
+ _ = StartListen(connection, cancellationToken);
}
}
- async void StartListen(WebSocketConnection connection, CancellationToken cancellationToken)
+ async ValueTask StartListen(WebSocketConnection connection, CancellationToken cancellationToken)
{
_activeConnections.Add(connection);
_networkListener.OnConnected(connection);
@@ -86,7 +86,7 @@ public class WebSocketServer : INetworkServer
var rawData = new byte[2048];
var rawDataBuffer = new Memory(rawData);
var data = new ArrayBufferWriter();
-
+
while (webSocket.State == WebSocketState.Open || !cancellationToken.IsCancellationRequested)
{
try
@@ -95,17 +95,17 @@ public class WebSocketServer : INetworkServer
{
var result = await webSocket.ReceiveAsync(rawDataBuffer, cancellationToken);
var payload = rawDataBuffer.Slice(0, result.Count);
-
+
data.Write(payload.Span);
-
+
if (result.EndOfMessage)
break;
}
-
+
if (data.WrittenCount > 0)
{
_networkListener.OnData(connection, NetworkChannel.RELIABLE, data.WrittenMemory.ToArray());
-
+
data.Clear();
}
}
@@ -117,15 +117,15 @@ public class WebSocketServer : INetworkServer
_sequencer.Push(connection.Id);
_activeConnections.Remove(connection);
-
+
_networkListener.OnDisconnected(connection);
}
public void Update()
{
_executor.Update();
-
- Flush();
+
+ _ = Flush();
}
public void Broadcast(byte[] data, NetworkChannel channel)
@@ -134,7 +134,7 @@ public class WebSocketServer : INetworkServer
activeConnection.Reliable.Send(data);
}
- public async void Flush()
+ public async ValueTask Flush()
{
foreach (var conn in _activeConnections)
await conn.Flush();
diff --git a/Ragon.Server/Sources/Data/RagonData.cs b/Ragon.Server/Sources/Data/RagonData.cs
index 992f15f..97a8782 100644
--- a/Ragon.Server/Sources/Data/RagonData.cs
+++ b/Ragon.Server/Sources/Data/RagonData.cs
@@ -12,13 +12,25 @@ public class RagonData
{
}
- public void Read(RagonBuffer buffer)
+ public void Read(RagonBuffer buffer, int maxSize = 0)
{
var len = buffer.ReadUShort();
+ var totalSize = 0;
+
for (int i = 0; i < len; i++)
{
var key = buffer.ReadString();
var valueSize = buffer.ReadUShort();
+
+ if (maxSize > 0)
+ {
+ totalSize += valueSize;
+ if (totalSize > maxSize)
+ {
+ throw new InvalidOperationException($"User data exceeds limit: {totalSize} > {maxSize}");
+ }
+ }
+
if (valueSize > 0)
{
var value = buffer.ReadBytes(valueSize);
@@ -59,8 +71,6 @@ public class RagonData
buffer.WriteString(prop.Key);
buffer.WriteUShort((ushort)prop.Value.Length);
buffer.WriteBytes(prop.Value);
-
- Console.WriteLine($"Key: {prop.Key} Value: {prop.Value.Length}");
}
}
}
\ No newline at end of file
diff --git a/Ragon.Server/Sources/Handler/AuthorizationOperation.cs b/Ragon.Server/Sources/Handler/AuthorizationOperation.cs
index 27cdc7d..45fc4fe 100644
--- a/Ragon.Server/Sources/Handler/AuthorizationOperation.cs
+++ b/Ragon.Server/Sources/Handler/AuthorizationOperation.cs
@@ -19,6 +19,7 @@ using Ragon.Server.IO;
using Ragon.Server.Lobby;
using Ragon.Server.Logging;
using Ragon.Server.Plugin;
+using Ragon.Server.Project;
namespace Ragon.Server.Handler
{
@@ -30,19 +31,22 @@ namespace Ragon.Server.Handler
private readonly RagonContextObserver _observer;
private readonly RagonServerConfiguration _configuration;
private readonly RagonBuffer _writer;
+ private readonly ProjectRegistry _projectRegistry;
public AuthorizationOperation(RagonBuffer reader,
RagonBuffer writer,
IRagonServer server,
IServerPlugin serverPlugin,
RagonContextObserver observer,
- RagonServerConfiguration configuration) : base(reader, writer)
+ RagonServerConfiguration configuration,
+ ProjectRegistry projectRegistry) : base(reader, writer)
{
_serverPlugin = serverPlugin;
_configuration = configuration;
_observer = observer;
_writer = writer;
_server = server;
+ _projectRegistry = projectRegistry;
}
public override void Handle(RagonContext context, NetworkChannel channel)
@@ -59,31 +63,45 @@ namespace Ragon.Server.Handler
return;
}
- var configuration = _configuration;
- var key = Reader.ReadString();
+ var projectKey = Reader.ReadString();
var name = Reader.ReadString();
var payload = Reader.ReadString();
- if (key == configuration.ServerKey)
+ if (!_projectRegistry.ValidateKey(projectKey))
{
- var authorizeViaPlugin = _serverPlugin.OnAuthorize(new ConnectionRequest(_server, context.Connection.Id, payload));
- if (authorizeViaPlugin)
- return;
-
- var id = Guid.NewGuid().ToString();
- Approve(context, new ConnectionResponse(id, name, payload));
- }
- else
- {
- _logger.Warning($"Invalid key for connection {context.Connection.Id}");
-
+ _logger.Warning($"Invalid project key from connection {context.Connection.Id}");
Reject(context);
+ return;
}
+
+ if (!_projectRegistry.CanConnect(projectKey))
+ {
+ _logger.Warning($"Connection limit reached for project key: {projectKey}");
+ Reject(context);
+ return;
+ }
+
+ var authorizeViaPlugin = _serverPlugin.OnAuthorize(new ConnectionRequest(_server, context.Connection.Id, payload));
+ if (authorizeViaPlugin)
+ return;
+
+ var project = _projectRegistry.GetOrCreateProject(projectKey);
+ if (project == null)
+ {
+ _logger.Warning($"Failed to create project for key: {projectKey}");
+ Reject(context);
+ return;
+ }
+
+ var id = Guid.NewGuid().ToString();
+ Approve(context, new ConnectionResponse(id, name, payload), project.Id);
+
+ _projectRegistry.RegisterConnection(project.Id);
}
- public void Approve(RagonContext context, ConnectionResponse result)
+ public void Approve(RagonContext context, ConnectionResponse result, int projectId)
{
- var lobbyPlayer = new RagonLobbyPlayer(context.Connection, result.Id, result.Name, result.Payload);
+ var lobbyPlayer = new RagonLobbyPlayer(context.Connection, result.Id, result.Name, result.Payload, projectId);
context.SetPlayer(lobbyPlayer);
context.ConnectionStatus = ConnectionStatus.Authorized;
@@ -102,7 +120,7 @@ namespace Ragon.Server.Handler
var sendData = _writer.ToArray();
context.Connection.Reliable.Send(sendData);
- _logger.Trace($"Approved {context.Connection.Id} as {playerId}|{context.LobbyPlayer.Name}");
+ _logger.Trace($"Approved {context.Connection.Id} as {playerId}|{context.LobbyPlayer.Name} for project {projectId}");
}
public void Reject(RagonContext context)
diff --git a/Ragon.Server/Sources/Handler/PlayerUserDataOperation.cs b/Ragon.Server/Sources/Handler/PlayerUserDataOperation.cs
index 36d84d8..cca732d 100644
--- a/Ragon.Server/Sources/Handler/PlayerUserDataOperation.cs
+++ b/Ragon.Server/Sources/Handler/PlayerUserDataOperation.cs
@@ -27,7 +27,7 @@ namespace Ragon.Server.Handler
return;
}
- context.UserData.Read(Reader);
+ context.UserData.Read(Reader, _userDataLimit);
}
}
}
\ No newline at end of file
diff --git a/Ragon.Server/Sources/Handler/RoomCreateOperation.cs b/Ragon.Server/Sources/Handler/RoomCreateOperation.cs
index c1b09f0..039ca26 100644
--- a/Ragon.Server/Sources/Handler/RoomCreateOperation.cs
+++ b/Ragon.Server/Sources/Handler/RoomCreateOperation.cs
@@ -88,7 +88,7 @@ namespace Ragon.Server.Handler
var roomPlayer = new RagonRoomPlayer(context, lobbyPlayer.Id, lobbyPlayer.Name);
var roomPlugin = _serverPlugin.CreateRoomPlugin(information);
- var room = new RagonRoom(roomId, information, roomPlugin);
+ var room = new RagonRoom(roomId, information, roomPlugin, lobbyPlayer.ProjectId);
room.Plugin.OnAttached(room);
roomPlayer.OnAttached(room);
diff --git a/Ragon.Server/Sources/Handler/RoomJoinOperation.cs b/Ragon.Server/Sources/Handler/RoomJoinOperation.cs
index 2e38022..12d705f 100644
--- a/Ragon.Server/Sources/Handler/RoomJoinOperation.cs
+++ b/Ragon.Server/Sources/Handler/RoomJoinOperation.cs
@@ -43,6 +43,13 @@ public sealed class RoomJoinOperation : BaseOperation
return;
}
+ if (existsRoom.ProjectId != lobbyPlayer.ProjectId)
+ {
+ JoinFailed(context, Writer);
+ _logger.Warning($"Player {context.Connection.Id}|{lobbyPlayer.Name} tried to join room from different project");
+ return;
+ }
+
var player = new RagonRoomPlayer(context, lobbyPlayer.Id, lobbyPlayer.Name);
context.SetRoom(existsRoom, player);
diff --git a/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs b/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs
index 3cba271..af3e129 100644
--- a/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs
+++ b/Ragon.Server/Sources/Handler/RoomJoinOrCreateOperation.cs
@@ -57,6 +57,12 @@ public sealed class RoomJoinOrCreateOperation : BaseOperation
if (context.Lobby.FindRoomByScene(_roomParameters.Scene, out var existsRoom))
{
+ if (existsRoom.ProjectId != lobbyPlayer.ProjectId)
+ {
+ _logger.Warning($"Player {context.Connection.Id}|{lobbyPlayer.Name} tried to join room from different project");
+ return;
+ }
+
var player = new RagonRoomPlayer(context, lobbyPlayer.Id, lobbyPlayer.Name);
context.SetRoom(existsRoom, player);
@@ -81,7 +87,7 @@ public sealed class RoomJoinOrCreateOperation : BaseOperation
var roomPlayer = new RagonRoomPlayer(context, lobbyPlayer.Id, lobbyPlayer.Name);
var roomPlugin = _serverPlugin.CreateRoomPlugin(information);
- var room = new RagonRoom(roomId, information, roomPlugin);
+ var room = new RagonRoom(roomId, information, roomPlugin, lobbyPlayer.ProjectId);
_serverPlugin.OnRoomCreate(lobbyPlayer, room);
diff --git a/Ragon.Server/Sources/Handler/RoomUserDataOperation.cs b/Ragon.Server/Sources/Handler/RoomUserDataOperation.cs
index 7a020db..0f3c039 100644
--- a/Ragon.Server/Sources/Handler/RoomUserDataOperation.cs
+++ b/Ragon.Server/Sources/Handler/RoomUserDataOperation.cs
@@ -45,6 +45,6 @@ public sealed class RoomUserDataOperation : BaseOperation
var room = context.Room;
if (room != null)
- room.UserData.Read(Reader);
+ room.UserData.Read(Reader, _userDataLimit);
}
}
\ No newline at end of file
diff --git a/Ragon.Server/Sources/Lobby/RagonLobbyDispatcher.cs b/Ragon.Server/Sources/Lobby/RagonLobbyDispatcher.cs
index 1bc0b5d..32e8a04 100644
--- a/Ragon.Server/Sources/Lobby/RagonLobbyDispatcher.cs
+++ b/Ragon.Server/Sources/Lobby/RagonLobbyDispatcher.cs
@@ -12,12 +12,17 @@ public class RagonLobbyDispatcher
_lobby = lobby;
}
- public void Write(RagonBuffer writer)
+ public void Write(RagonBuffer writer, int projectId = 0)
{
writer.Clear();
writer.WriteOperation(RagonOperation.ROOM_LIST_UPDATED);
var rooms = _lobby.Rooms;
+ if (projectId > 0)
+ {
+ rooms = rooms.Where(r => r.ProjectId == projectId).ToList();
+ }
+
writer.WriteUShort((ushort)rooms.Count);
for (int i = 0; i < rooms.Count; i++)
{
diff --git a/Ragon.Server/Sources/Lobby/RagonLobbyPlayer.cs b/Ragon.Server/Sources/Lobby/RagonLobbyPlayer.cs
index 6bd41e1..d51cf5f 100644
--- a/Ragon.Server/Sources/Lobby/RagonLobbyPlayer.cs
+++ b/Ragon.Server/Sources/Lobby/RagonLobbyPlayer.cs
@@ -32,12 +32,14 @@ public class RagonLobbyPlayer
public string Id { get; private set; }
public string Name { get; private set; }
public string Payload { get; private set; }
-
- public RagonLobbyPlayer(INetworkConnection connection, string id, string name, string payload)
+ public int ProjectId { get; private set; }
+
+ public RagonLobbyPlayer(INetworkConnection connection, string id, string name, string payload, int projectId)
{
Id = id;
Name = name;
Connection = connection;
Payload = payload;
+ ProjectId = projectId;
}
}
\ No newline at end of file
diff --git a/Ragon.Server/Sources/Plugin/BaseServerPlugin.cs b/Ragon.Server/Sources/Plugin/BaseServerPlugin.cs
index 3511e90..bbb75d2 100644
--- a/Ragon.Server/Sources/Plugin/BaseServerPlugin.cs
+++ b/Ragon.Server/Sources/Plugin/BaseServerPlugin.cs
@@ -47,7 +47,7 @@ namespace Ragon.Server.Plugin
return;
var operation = (AuthorizationOperation)_server.ResolveHandler(RagonOperation.AUTHORIZE);
- operation.Approve(ctx, new ConnectionResponse(id, name, payload));
+ operation.Approve(ctx, new ConnectionResponse(id, name, payload), 0);
}
public void Reject()
diff --git a/Ragon.Server/Sources/RagonServer.cs b/Ragon.Server/Sources/RagonServer.cs
index 358eadc..8212ec4 100644
--- a/Ragon.Server/Sources/RagonServer.cs
+++ b/Ragon.Server/Sources/RagonServer.cs
@@ -21,6 +21,7 @@ using Ragon.Server.IO;
using Ragon.Server.Lobby;
using Ragon.Server.Logging;
using Ragon.Server.Plugin;
+using Ragon.Server.Project;
using Ragon.Server.Time;
namespace Ragon.Server;
@@ -28,7 +29,7 @@ namespace Ragon.Server;
public class RagonServer : IRagonServer, INetworkListener
{
private const string ServerVersion = "1.4.1";
-
+
private readonly IRagonLogger _logger = LoggerManager.GetLogger(nameof(RagonServer));
private readonly INetworkServer _server;
private readonly BaseOperation[] _handlers;
@@ -40,12 +41,14 @@ public class RagonServer : IRagonServer, INetworkListener
private readonly RagonScheduler _scheduler;
private readonly Dictionary _contextsByConnection;
private readonly Dictionary _contextsByPlayerId;
+ private readonly ProjectRegistry _projectRegistry;
private readonly Stopwatch _timer;
private readonly RagonLobbyDispatcher _lobbySerializer;
private readonly long _tickRate = 0;
private bool _isRunning = false;
public bool IsRunning => _isRunning;
+ public ProjectRegistry ProjectRegistry => _projectRegistry;
public RagonServer(
INetworkServer server,
@@ -57,6 +60,7 @@ public class RagonServer : IRagonServer, INetworkListener
_serverPlugin = plugin;
_contextsByConnection = new Dictionary();
_contextsByPlayerId = new Dictionary();
+ _projectRegistry = new ProjectRegistry(configuration.LimitConnectionsPerProject);
_lobby = new LobbyInMemory();
_lobbySerializer = new RagonLobbyDispatcher(_lobby);
_scheduler = new RagonScheduler();
@@ -73,7 +77,7 @@ public class RagonServer : IRagonServer, INetworkListener
_serverPlugin.OnAttached(this);
_handlers = new BaseOperation[byte.MaxValue];
- _handlers[(byte)RagonOperation.AUTHORIZE] = new AuthorizationOperation(_reader, _writer, this, _serverPlugin, contextObserver, configuration);
+ _handlers[(byte)RagonOperation.AUTHORIZE] = new AuthorizationOperation(_reader, _writer, this, _serverPlugin, contextObserver, configuration, _projectRegistry);
_handlers[(byte)RagonOperation.JOIN_OR_CREATE_ROOM] = new RoomJoinOrCreateOperation(_reader, _writer, plugin, _configuration);
_handlers[(byte)RagonOperation.CREATE_ROOM] = new RoomCreateOperation(_reader, _writer, plugin, _configuration);
_handlers[(byte)RagonOperation.JOIN_ROOM] = new RoomJoinOperation(_reader, _writer);
@@ -152,12 +156,15 @@ public class RagonServer : IRagonServer, INetworkListener
if (room != null)
{
room.DetachPlayer(context.RoomPlayer);
-
+
_lobby.RemoveIfEmpty(room);
}
-
+
if (context.ConnectionStatus == ConnectionStatus.Authorized)
+ {
_contextsByPlayerId.Remove(context.LobbyPlayer.Id);
+ _projectRegistry.UnregisterConnection(context.LobbyPlayer.ProjectId);
+ }
_logger.Trace($"Disconnected: {connection.Id}");
}
@@ -177,10 +184,13 @@ public class RagonServer : IRagonServer, INetworkListener
room.DetachPlayer(context.RoomPlayer);
_lobby.RemoveIfEmpty(room);
}
-
+
if (context.ConnectionStatus == ConnectionStatus.Authorized)
+ {
_contextsByPlayerId.Remove(context.LobbyPlayer.Id);
-
+ _projectRegistry.UnregisterConnection(context.LobbyPlayer.ProjectId);
+ }
+
_logger.Trace($"Timeout: {connection.Id}|{context.LobbyPlayer.Name}|{context.LobbyPlayer.Id}");
}
else
@@ -198,9 +208,16 @@ public class RagonServer : IRagonServer, INetworkListener
_writer.Clear();
_reader.Clear();
_reader.FromArray(data);
-
+
var operation = _reader.ReadByte();
- _handlers[operation]?.Handle(context, channel);
+
+ if (operation >= _handlers.Length || _handlers[operation] == null)
+ {
+ _logger.Warning($"Invalid operation code: {operation} from connection {connection.Id}");
+ return;
+ }
+
+ _handlers[operation].Handle(context, channel);
}
}
catch (Exception ex)
@@ -228,13 +245,14 @@ public class RagonServer : IRagonServer, INetworkListener
public void SendRoomList()
{
- _lobbySerializer.Write(_writer);
-
- var sendData = _writer.ToArray();
- foreach (var (_, value) in _contextsByPlayerId)
+ foreach (var (_, context) in _contextsByPlayerId)
{
- if (value.Room == null) // If only in lobby, then send room list data
- value.Connection.Reliable.Send(sendData);
+ if (context.Room == null) // If only in lobby, then send room list data
+ {
+ _lobbySerializer.Write(_writer, context.LobbyPlayer.ProjectId);
+ var sendData = _writer.ToArray();
+ context.Connection.Reliable.Send(sendData);
+ }
}
}
@@ -287,7 +305,7 @@ public class RagonServer : IRagonServer, INetworkListener
{
return _contextsByPlayerId.TryGetValue(playerId, out var context) ? context : null;
}
-
+
private void CopyrightInfo()
{
_logger.Info($"Server Version: {ServerVersion}");
diff --git a/Ragon.Server/Sources/RagonServerConfiguration.cs b/Ragon.Server/Sources/RagonServerConfiguration.cs
index 5c4cc7d..bb70412 100644
--- a/Ragon.Server/Sources/RagonServerConfiguration.cs
+++ b/Ragon.Server/Sources/RagonServerConfiguration.cs
@@ -36,6 +36,7 @@ public struct RagonServerConfiguration
public int LimitBufferedEvents;
public int LimitUserDataSize;
public int LimitPropertySize;
+ public int LimitConnectionsPerProject;
private static Dictionary _serverTypes = new Dictionary()
{
diff --git a/Ragon.Server/Sources/Room/IRagonRoom.cs b/Ragon.Server/Sources/Room/IRagonRoom.cs
index cd5197e..62389f6 100644
--- a/Ragon.Server/Sources/Room/IRagonRoom.cs
+++ b/Ragon.Server/Sources/Room/IRagonRoom.cs
@@ -24,6 +24,7 @@ public interface IRagonRoom
{
public string Id { get; }
public string Scene { get; }
+ public int ProjectId { get; }
public int PlayerMin { get; }
public int PlayerMax { get; }
public int PlayerCount { get; }
diff --git a/Ragon.Server/Sources/Room/RagonRoom.cs b/Ragon.Server/Sources/Room/RagonRoom.cs
index 7c40f3c..3a77661 100644
--- a/Ragon.Server/Sources/Room/RagonRoom.cs
+++ b/Ragon.Server/Sources/Room/RagonRoom.cs
@@ -31,7 +31,8 @@ public class RagonRoom : IRagonRoom, IRagonAction
public int PlayerMax { get; private set; }
public int PlayerMin { get; private set; }
public int PlayerCount => WaitPlayersList.Count;
-
+ public int ProjectId { get; private set; }
+
public bool IsDone { get; private set; }
public RagonData UserData { get; set; }
@@ -53,13 +54,14 @@ public class RagonRoom : IRagonRoom, IRagonAction
private readonly List _bufferedEvents;
private readonly int _limitBufferedEvents;
- public RagonRoom(string roomId, RoomInformation info, IRoomPlugin roomPlugin)
+ public RagonRoom(string roomId, RoomInformation info, IRoomPlugin roomPlugin, int projectId)
{
Id = roomId;
Scene = info.Scene;
PlayerMax = info.Max;
PlayerMin = info.Min;
Plugin = roomPlugin;
+ ProjectId = projectId;
Players = new Dictionary(info.Max);
WaitPlayersList = new List(info.Max);