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 RagonClient
|
|
|
|
|
{
|
|
|
|
|
private readonly INetworkConnection _connection;
|
2023-07-30 21:19:39 +03:00
|
|
|
private readonly NetworkStatistics _stats;
|
2023-07-09 07:40:06 +03:00
|
|
|
private IRagonEntityListener _entityListener;
|
|
|
|
|
private IRagonSceneCollector _sceneCollector;
|
2023-10-04 14:42:59 +03:00
|
|
|
private IHandler[] _handlers;
|
2023-03-06 10:06:43 +04:00
|
|
|
private RagonBuffer _readBuffer;
|
|
|
|
|
private RagonBuffer _writeBuffer;
|
|
|
|
|
private RagonRoom _room;
|
|
|
|
|
private RagonSession _session;
|
2024-04-13 16:17:31 +03:00
|
|
|
private RagonListenerList _listeners;
|
2023-03-06 10:06:43 +04:00
|
|
|
private RagonPlayerCache _playerCache;
|
|
|
|
|
private RagonEntityCache _entityCache;
|
|
|
|
|
private RagonEventCache _eventCache;
|
|
|
|
|
private RagonStatus _status;
|
2023-07-30 21:46:42 +03:00
|
|
|
|
2023-10-04 14:42:59 +03:00
|
|
|
private double _serverTimestamp;
|
2023-03-06 10:29:22 +04:00
|
|
|
private float _replicationRate = 0;
|
|
|
|
|
private float _replicationTime = 0;
|
2023-03-06 10:06:43 +04:00
|
|
|
|
2023-10-04 14:42:59 +03:00
|
|
|
public double ServerTimestamp => _serverTimestamp;
|
2023-03-06 10:06:43 +04:00
|
|
|
public IRagonConnection Connection => _connection;
|
|
|
|
|
public RagonStatus Status => _status;
|
|
|
|
|
public RagonSession Session => _session;
|
|
|
|
|
public RagonEventCache Event => _eventCache;
|
|
|
|
|
public RagonEntityCache Entity => _entityCache;
|
|
|
|
|
public NetworkStatistics Statistics => _stats;
|
|
|
|
|
public RagonRoom Room => _room;
|
2023-10-16 23:37:35 +03:00
|
|
|
|
2023-03-06 10:06:43 +04:00
|
|
|
internal RagonBuffer Buffer => _writeBuffer;
|
|
|
|
|
internal INetworkChannel Reliable => _connection.Reliable;
|
|
|
|
|
internal INetworkChannel Unreliable => _connection.Unreliable;
|
|
|
|
|
|
|
|
|
|
#region PUBLIC
|
|
|
|
|
|
2023-07-09 07:40:06 +03:00
|
|
|
public RagonClient(INetworkConnection connection, int rate)
|
2023-03-06 10:06:43 +04:00
|
|
|
{
|
2024-04-13 16:17:31 +03:00
|
|
|
_listeners = new RagonListenerList(this);
|
2023-10-11 19:37:50 +03:00
|
|
|
|
2023-03-06 10:06:43 +04:00
|
|
|
_connection = connection;
|
|
|
|
|
_connection.OnData += OnData;
|
|
|
|
|
_connection.OnConnected += OnConnected;
|
|
|
|
|
_connection.OnDisconnected += OnDisconnected;
|
2023-10-11 19:37:50 +03:00
|
|
|
|
2023-03-06 10:29:22 +04:00
|
|
|
_replicationRate = (1000.0f / rate) / 1000.0f;
|
|
|
|
|
_replicationTime = 0;
|
2023-03-06 10:06:43 +04:00
|
|
|
|
|
|
|
|
_eventCache = new RagonEventCache();
|
|
|
|
|
_stats = new NetworkStatistics();
|
|
|
|
|
_status = RagonStatus.DISCONNECTED;
|
|
|
|
|
}
|
2023-07-09 07:40:06 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
public void Configure(IRagonSceneCollector sceneCollector)
|
|
|
|
|
{
|
|
|
|
|
_sceneCollector = sceneCollector;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Configure(IRagonEntityListener listener)
|
|
|
|
|
{
|
|
|
|
|
_entityListener = listener;
|
|
|
|
|
}
|
2023-07-30 21:46:42 +03:00
|
|
|
|
2023-03-06 10:06:43 +04:00
|
|
|
public void Connect(string address, ushort port, string protocol)
|
|
|
|
|
{
|
2023-07-09 07:40:06 +03:00
|
|
|
if (_sceneCollector == null)
|
|
|
|
|
{
|
|
|
|
|
RagonLog.Error("Scene collector is not defined!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_entityListener == null)
|
|
|
|
|
{
|
|
|
|
|
RagonLog.Error("Entity Listener is not defined!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-07-30 21:46:42 +03:00
|
|
|
|
2023-03-06 10:06:43 +04:00
|
|
|
_writeBuffer = new RagonBuffer();
|
|
|
|
|
_readBuffer = new RagonBuffer();
|
2023-10-04 14:42:59 +03:00
|
|
|
_session = new RagonSession(this, _writeBuffer);
|
2023-03-06 10:06:43 +04:00
|
|
|
|
|
|
|
|
_playerCache = new RagonPlayerCache();
|
2023-07-29 10:58:06 +03:00
|
|
|
_entityCache = new RagonEntityCache(this, _playerCache, _sceneCollector);
|
2023-03-06 10:06:43 +04:00
|
|
|
|
2023-10-04 14:42:59 +03:00
|
|
|
_handlers = new IHandler[byte.MaxValue];
|
2024-04-13 16:17:31 +03:00
|
|
|
_handlers[(byte)RagonOperation.AUTHORIZED_SUCCESS] = new AuthorizeSuccessHandler(this, _listeners);
|
|
|
|
|
_handlers[(byte)RagonOperation.AUTHORIZED_FAILED] = new AuthorizeFailedHandler(_listeners);
|
|
|
|
|
_handlers[(byte)RagonOperation.JOIN_SUCCESS] = new JoinSuccessHandler(this, _listeners, _playerCache, _entityCache);
|
|
|
|
|
_handlers[(byte)RagonOperation.JOIN_FAILED] = new JoinFailedHandler(_listeners);
|
|
|
|
|
_handlers[(byte)RagonOperation.LEAVE_ROOM] = new LeaveRoomHandler(this, _listeners, _entityCache);
|
|
|
|
|
_handlers[(byte)RagonOperation.OWNERSHIP_ROOM_CHANGED] = new OwnershipRoomHandler(_listeners, _playerCache, _entityCache);
|
|
|
|
|
_handlers[(byte)RagonOperation.OWNERSHIP_ENTITY_CHANGED] = new EntityOwnershipHandler(_listeners, _playerCache, _entityCache);
|
|
|
|
|
_handlers[(byte)RagonOperation.PLAYER_JOINED] = new PlayerJoinHandler(_playerCache, _listeners);
|
|
|
|
|
_handlers[(byte)RagonOperation.PLAYER_LEAVED] = new PlayerLeftHandler(_entityCache, _playerCache, _listeners);
|
|
|
|
|
_handlers[(byte)RagonOperation.LOAD_SCENE] = new SceneLoadHandler(this, _listeners);
|
2023-07-29 10:58:06 +03:00
|
|
|
_handlers[(byte)RagonOperation.CREATE_ENTITY] = new EntityCreateHandler(this, _playerCache, _entityCache, _entityListener);
|
2023-04-14 14:32:04 +04:00
|
|
|
_handlers[(byte)RagonOperation.REMOVE_ENTITY] = new EntityRemoveHandler(_entityCache);
|
2023-03-06 10:31:42 +04:00
|
|
|
_handlers[(byte)RagonOperation.REPLICATE_ENTITY_STATE] = new StateEntityHandler(_entityCache);
|
2023-10-04 14:42:59 +03:00
|
|
|
_handlers[(byte)RagonOperation.REPLICATE_ENTITY_EVENT] = new EntityEventHandler(_playerCache, _entityCache);
|
2023-10-11 19:37:50 +03:00
|
|
|
_handlers[(byte)RagonOperation.REPLICATE_ROOM_EVENT] = new RoomEventHandler(this, _playerCache);
|
2024-04-13 16:17:31 +03:00
|
|
|
_handlers[(byte)RagonOperation.SNAPSHOT] = new SnapshotHandler(this, _listeners, _entityCache, _playerCache, _entityListener);
|
2023-10-04 14:42:59 +03:00
|
|
|
_handlers[(byte)RagonOperation.TIMESTAMP_SYNCHRONIZATION] = new TimestampHandler(this);
|
2024-04-13 16:17:31 +03:00
|
|
|
_handlers[(byte)RagonOperation.REPLICATE_RAW_DATA] = new RoomDataHandler(_playerCache, _listeners);
|
|
|
|
|
_handlers[(byte)RagonOperation.ROOM_LIST_UPDATED] = new RoomListHandler(_session, _listeners);
|
|
|
|
|
|
2023-03-06 10:06:43 +04:00
|
|
|
var protocolRaw = RagonVersion.Parse(protocol);
|
|
|
|
|
_connection.Connect(address, port, protocolRaw);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Disconnect()
|
|
|
|
|
{
|
|
|
|
|
_status = RagonStatus.DISCONNECTED;
|
|
|
|
|
_room.Cleanup();
|
|
|
|
|
_connection.Disconnect();
|
2023-07-30 21:46:42 +03:00
|
|
|
|
2023-05-08 00:16:43 +03:00
|
|
|
OnDisconnected(RagonDisconnect.MANUAL);
|
2023-03-06 10:06:43 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Update(float dt)
|
|
|
|
|
{
|
2023-03-31 12:56:23 +04:00
|
|
|
if (_status != RagonStatus.DISCONNECTED)
|
2023-03-06 10:06:43 +04:00
|
|
|
{
|
2023-03-31 12:56:23 +04:00
|
|
|
_replicationTime += dt;
|
|
|
|
|
if (_replicationTime >= _replicationRate)
|
|
|
|
|
{
|
|
|
|
|
_replicationTime = 0;
|
2023-10-04 14:42:59 +03:00
|
|
|
_entityCache.WriteState(_writeBuffer);
|
|
|
|
|
|
|
|
|
|
SendTimestamp();
|
2023-03-31 12:56:23 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_stats.Update(_connection.BytesSent, _connection.BytesReceived, _connection.Ping, dt);
|
2023-03-06 10:06:43 +04:00
|
|
|
}
|
2023-07-30 21:46:42 +03:00
|
|
|
|
2024-04-13 16:17:31 +03:00
|
|
|
_listeners.Update();
|
2023-03-06 10:06:43 +04:00
|
|
|
_connection.Update();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
2023-10-11 19:37:50 +03:00
|
|
|
if (_status != RagonStatus.DISCONNECTED)
|
|
|
|
|
{
|
|
|
|
|
_status = RagonStatus.DISCONNECTED;
|
|
|
|
|
_connection.Disconnect();
|
|
|
|
|
}
|
2023-03-06 10:06:43 +04:00
|
|
|
_connection.Dispose();
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-13 16:17:31 +03:00
|
|
|
public void AddListener(IRagonListener listener) => _listeners.Add(listener);
|
|
|
|
|
public void AddListener(IRagonAuthorizationListener listener) => _listeners.Add(listener);
|
|
|
|
|
public void AddListener(IRagonConnectionListener listener) => _listeners.Add(listener);
|
|
|
|
|
public void AddListener(IRagonFailedListener listener) => _listeners.Add(listener);
|
|
|
|
|
public void AddListener(IRagonJoinListener listener) => _listeners.Add(listener);
|
|
|
|
|
public void AddListener(IRagonLeftListener listener) => _listeners.Add(listener);
|
|
|
|
|
public void AddListener(IRagonOwnershipChangedListener listener) => _listeners.Add(listener);
|
|
|
|
|
public void AddListener(IRagonPlayerJoinListener listener) => _listeners.Add(listener);
|
|
|
|
|
public void AddListener(IRagonPlayerLeftListener listener) => _listeners.Add(listener);
|
|
|
|
|
public void AddListener(IRagonSceneListener listener) => _listeners.Add(listener);
|
|
|
|
|
public void AddListener(IRagonSceneRequestListener listener) => _listeners.Add(listener);
|
|
|
|
|
public void AddListener(IRagonDataListener listener) => _listeners.Add(listener);
|
2024-04-13 18:25:39 +03:00
|
|
|
public void AddListener(IRagonRoomListListener listener) => _listeners.Add(listener);
|
2024-04-13 16:17:31 +03:00
|
|
|
public void RemoveListener(IRagonListener listener) => _listeners.Remove(listener);
|
|
|
|
|
public void RemoveListener(IRagonAuthorizationListener listener) => _listeners.Remove(listener);
|
|
|
|
|
public void RemoveListener(IRagonConnectionListener listener) => _listeners.Remove(listener);
|
|
|
|
|
public void RemoveListener(IRagonFailedListener listener) => _listeners.Remove(listener);
|
|
|
|
|
public void RemoveListener(IRagonJoinListener listener) => _listeners.Remove(listener);
|
|
|
|
|
public void RemoveListener(IRagonLeftListener listener) => _listeners.Remove(listener);
|
|
|
|
|
public void RemoveListener(IRagonOwnershipChangedListener listener) => _listeners.Remove(listener);
|
|
|
|
|
public void RemoveListener(IRagonPlayerJoinListener listener) => _listeners.Remove(listener);
|
|
|
|
|
public void RemoveListener(IRagonPlayerLeftListener listener) => _listeners.Remove(listener);
|
|
|
|
|
public void RemoveListener(IRagonSceneListener listener) => _listeners.Remove(listener);
|
|
|
|
|
public void RemoveListener(IRagonSceneRequestListener listener) => _listeners.Remove(listener);
|
|
|
|
|
public void RemoveListener(IRagonDataListener listener) => _listeners.Remove(listener);
|
2024-04-13 18:25:39 +03:00
|
|
|
public void RemoveListener(IRagonRoomListListener listener) => _listeners.Remove(listener);
|
2023-07-30 21:46:42 +03:00
|
|
|
|
2023-03-06 10:06:43 +04:00
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region INTERNAL
|
|
|
|
|
|
|
|
|
|
internal void AssignRoom(RagonRoom room)
|
|
|
|
|
{
|
2023-10-09 09:17:43 +03:00
|
|
|
_room?.Dispose();
|
2023-03-06 10:06:43 +04:00
|
|
|
_room = room;
|
2023-07-30 21:46:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void SetStatus(RagonStatus status)
|
|
|
|
|
{
|
|
|
|
|
_status = status;
|
2023-03-06 10:06:43 +04:00
|
|
|
}
|
|
|
|
|
|
2023-10-04 14:42:59 +03:00
|
|
|
internal void SetTimestamp(double time)
|
|
|
|
|
{
|
|
|
|
|
_serverTimestamp = time;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-06 10:06:43 +04:00
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region PRIVATE
|
|
|
|
|
|
2023-10-04 14:42:59 +03:00
|
|
|
private void SendTimestamp()
|
|
|
|
|
{
|
|
|
|
|
var timestamp = RagonTime.CurrentTimestamp();
|
|
|
|
|
var value = new DoubleToUInt()
|
|
|
|
|
{
|
|
|
|
|
Double = timestamp,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
_writeBuffer.Clear();
|
|
|
|
|
_writeBuffer.WriteOperation(RagonOperation.TIMESTAMP_SYNCHRONIZATION);
|
|
|
|
|
_writeBuffer.Write(value.Int0, 32);
|
|
|
|
|
_writeBuffer.Write(value.Int1, 32);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-06 10:06:43 +04:00
|
|
|
private void OnConnected()
|
|
|
|
|
{
|
|
|
|
|
RagonLog.Trace("Connected");
|
|
|
|
|
|
2024-04-13 16:17:31 +03:00
|
|
|
_listeners.OnConnected();
|
2023-03-06 10:06:43 +04:00
|
|
|
_status = RagonStatus.CONNECTED;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-08 00:16:43 +03:00
|
|
|
private void OnDisconnected(RagonDisconnect reason)
|
2023-03-06 10:06:43 +04:00
|
|
|
{
|
|
|
|
|
RagonLog.Trace($"Disconnected: {reason}");
|
|
|
|
|
|
2024-04-13 16:17:31 +03:00
|
|
|
_listeners.OnDisconnected(reason);
|
2023-03-06 10:06:43 +04:00
|
|
|
_status = RagonStatus.DISCONNECTED;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-04 14:42:59 +03:00
|
|
|
private void OnData(byte[] data)
|
2023-03-06 10:06:43 +04:00
|
|
|
{
|
|
|
|
|
_readBuffer.Clear();
|
|
|
|
|
_readBuffer.FromArray(data);
|
|
|
|
|
|
|
|
|
|
var operation = _readBuffer.ReadByte();
|
2023-03-06 10:31:42 +04:00
|
|
|
_handlers[operation].Handle(_readBuffer);
|
2023-03-06 10:06:43 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
}
|