435 lines
11 KiB
C#
435 lines
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Security.Cryptography;
|
|
using FishNet;
|
|
using FishNet.Connection;
|
|
using FishNet.Managing.Scened;
|
|
using FishNet.Object;
|
|
using FishNet.Transporting;
|
|
using log4net;
|
|
using Unity.VisualScripting;
|
|
using UnityEngine;
|
|
using UnityEngine.SceneManagement;
|
|
|
|
public class SSOLobby : NetworkBehaviour
|
|
{
|
|
private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
|
|
|
#region Fields
|
|
[SerializeField] private NetworkObject clientPrefab;
|
|
public List<Room> createdRooms = new();
|
|
public Dictionary<NetworkConnection, Room> connectionRooms = new();
|
|
#endregion
|
|
|
|
#region Init & Update
|
|
protected virtual void Awake()
|
|
{
|
|
Locator.RegisterService<SSOLobby>(this);
|
|
|
|
// Register connection state events
|
|
InstanceFinder.ServerManager.OnServerConnectionState += OnServerStateChanged;
|
|
InstanceFinder.ServerManager.OnRemoteConnectionState += OnClienStateChanged;
|
|
InstanceFinder.ClientManager.OnClientConnectionState += OnLocalClientStateChanged;
|
|
|
|
}
|
|
|
|
|
|
public override void OnStartClient()
|
|
{
|
|
if (clientPrefab == null)
|
|
{
|
|
Log.Error("There is no client prefab for the client to spawn.");
|
|
return;
|
|
}
|
|
|
|
// // The client gets a client instance upon entering the lobby
|
|
// NetworkObject nob = Instantiate(clientPrefab);
|
|
// Scene scene = UnityEngine.SceneManagement.SceneManager.GetSceneByName("OnlineLobby");
|
|
// UnityEngine.SceneManagement.SceneManager.MoveGameObjectToScene(nob.gameObject, scene);
|
|
// InstanceFinder.ServerManager.Spawn(nob.gameObject, LocalConnection);
|
|
Locator.GetService<LobbyManager>().InitClient(this);
|
|
}
|
|
|
|
public override void OnStartServer()
|
|
{
|
|
// TODO: check if the mirror timing issues still persist
|
|
ChangeSubscription(false);
|
|
ChangeSubscription(true);
|
|
}
|
|
|
|
public override void OnStopServer()
|
|
{
|
|
base.OnStopServer();
|
|
ChangeSubscription(false);
|
|
}
|
|
|
|
public void ChangeSubscription(bool subscribe)
|
|
{
|
|
if (base.NetworkManager == null)
|
|
return;
|
|
|
|
if (subscribe)
|
|
{
|
|
base.NetworkManager.SceneManager.OnLoadEnd += OnClientLoadedScene;
|
|
base.NetworkManager.SceneManager.OnClientPresenceChangeEnd += OnClientSceneState;
|
|
}
|
|
else
|
|
{
|
|
base.NetworkManager.SceneManager.OnLoadEnd -= OnClientLoadedScene;
|
|
base.NetworkManager.SceneManager.OnClientPresenceChangeEnd -= OnClientSceneState;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Actions
|
|
public event Action<Room, SceneLoadEndEventArgs> OnServerLoadedScenes;
|
|
|
|
public event Action<Room, NetworkObject> OnClientCreatedRoom;
|
|
public event Action<Room, NetworkObject> OnClientLeftRoom;
|
|
public event Action<Room, NetworkObject> OnClientJoinedRoom;
|
|
public event Action<Room, NetworkObject> OnClientStarted;
|
|
|
|
// Events for the client
|
|
public event Action<Room> OnRoomCreated;
|
|
public event Action<Room> OnRoomDeleted;
|
|
public event Action<Room> OnRoomStarted;
|
|
|
|
public event Action<NetworkObject> OnMemberLeft;
|
|
public event Action<NetworkObject> OnMemberStarted;
|
|
public event Action<NetworkObject> OnMemberJoined;
|
|
public event Action<NetworkObject> OnClientLoggedIn;
|
|
|
|
#endregion
|
|
|
|
#region Events
|
|
private void OnClientSceneState(ClientPresenceChangeEventArgs args)
|
|
{
|
|
}
|
|
|
|
private void OnClientLoadedScene(SceneLoadEndEventArgs args)
|
|
{
|
|
}
|
|
|
|
private void OnLocalClientStateChanged(ClientConnectionStateArgs args)
|
|
{
|
|
}
|
|
|
|
private void OnClienStateChanged(NetworkConnection connection, RemoteConnectionStateArgs args)
|
|
{
|
|
}
|
|
|
|
private void OnServerStateChanged(ServerConnectionStateArgs args)
|
|
{
|
|
}
|
|
#endregion
|
|
|
|
#region SignIn
|
|
[Client]
|
|
public void SignIn()
|
|
{
|
|
ServerSignIn();
|
|
}
|
|
|
|
[ServerRpc(RequireOwnership = false)]
|
|
private void ServerSignIn(NetworkConnection sender = null)
|
|
{
|
|
// Assign a username to this new connection
|
|
bool success =
|
|
OnSignIn(sender.ClientId, out string username, out string failedReason);
|
|
|
|
// Get client instance from that user
|
|
ClientInstance.ReturnClientInstance(sender).Username = username;
|
|
|
|
|
|
if (success)
|
|
{
|
|
TargetSignInSuccess(sender, username);
|
|
}
|
|
else
|
|
{
|
|
TargetSignInFailed(sender, failedReason);
|
|
}
|
|
}
|
|
|
|
// TODO: Username input options and sanitization
|
|
private bool OnSignIn(int clientId, out string username, out string failedReason)
|
|
{
|
|
username = "Ship " + clientId;
|
|
failedReason = "cannot fail yet";
|
|
return true;
|
|
}
|
|
|
|
[TargetRpc]
|
|
private void TargetSignInSuccess(NetworkConnection conn, string username)
|
|
{
|
|
OnClientLoggedIn?.Invoke(ClientInstance.ReturnClientInstance(conn).NetworkObject);
|
|
Locator.GetService<LobbyManager>().SignInSuccess(username);
|
|
}
|
|
|
|
[TargetRpc]
|
|
private void TargetSignInFailed(NetworkConnection conn, string failedReason)
|
|
{
|
|
Locator.GetService<LobbyManager>().SignInFailed(failedReason);
|
|
}
|
|
#endregion
|
|
|
|
#region Create Room
|
|
[Client]
|
|
public void CreateRoom(string roomName = "")
|
|
{
|
|
ServerCreateRoom(roomName);
|
|
}
|
|
[ServerRpc(RequireOwnership = false)]
|
|
private void ServerCreateRoom(string roomName, NetworkConnection sender = null)
|
|
{
|
|
string failedReason = "";
|
|
ClientInstance ci = ClientInstance.ReturnClientInstance(sender);
|
|
if (ci == null)
|
|
{
|
|
failedReason = "Unable to find Client Instance for incoming rpc call.";
|
|
Log.Error(failedReason);
|
|
TargetCreateRoomFailed(sender, failedReason);
|
|
return;
|
|
}
|
|
if (roomName == "")
|
|
{
|
|
roomName = ci.Username + "'s Room";
|
|
}
|
|
|
|
Room match = ReturnRoom(ci.NetworkObject);
|
|
if (match != null)
|
|
{
|
|
failedReason = "User is already in a room.";
|
|
Log.Error(failedReason);
|
|
TargetCreateRoomFailed(sender, failedReason);
|
|
return;
|
|
}
|
|
|
|
match = ReturnRoom(roomName);
|
|
if (match != null)
|
|
{
|
|
failedReason = "Room was already created.";
|
|
TargetCreateRoomFailed(sender, failedReason);
|
|
return;
|
|
}
|
|
// TODO: Manage room player count
|
|
Room room = new Room(roomName, "", true, 2);
|
|
room.AddMember(ci.NetworkObject);
|
|
createdRooms.Add(room);
|
|
connectionRooms[sender] = room;
|
|
Log.Info($"New room: {roomName} created successfully.");
|
|
OnClientCreatedRoom?.Invoke(room, ci.NetworkObject);
|
|
/*
|
|
RpcUpdateRooms(new Room[] {room});
|
|
*/
|
|
ObserverRoomChange(room, "Create");
|
|
TargetCreateRoomSuccess(sender, room);
|
|
}
|
|
[TargetRpc]
|
|
private void TargetCreateRoomSuccess(NetworkConnection conn, Room room)
|
|
{
|
|
// Local first object instead
|
|
OnMemberJoined?.Invoke(ClientManager.Connection.FirstObject);
|
|
Locator.GetService<LobbyManager>().OnCreateRoom(room);
|
|
}
|
|
[TargetRpc]
|
|
private void TargetCreateRoomFailed(NetworkConnection conn, string failedReason)
|
|
{
|
|
Log.Error(failedReason);
|
|
}
|
|
#endregion
|
|
|
|
#region Join Room
|
|
[Client]
|
|
public void JoinRoom(string roomName)
|
|
{
|
|
ServerJoinRoom(roomName);
|
|
}
|
|
[ServerRpc(RequireOwnership = false)]
|
|
private void ServerJoinRoom(string roomName, NetworkConnection sender = null)
|
|
{
|
|
string failedReason = "";
|
|
var ci = ClientInstance.ReturnClientInstance(sender);
|
|
if (ci == null)
|
|
{
|
|
failedReason = "Unable to find Client Instance for incoming rpc call.";
|
|
Log.Error(failedReason);
|
|
TargetJoindRoomFailed(sender, failedReason);
|
|
return;
|
|
}
|
|
|
|
Room room = ReturnRoom(roomName);
|
|
if (room == null)
|
|
{
|
|
failedReason = $"No room named: {roomName} was found.";
|
|
Log.Error(failedReason);
|
|
TargetJoindRoomFailed(sender, failedReason);
|
|
return;
|
|
}
|
|
if (ReturnRoom(ci.NetworkObject) != null)
|
|
{
|
|
failedReason = "User is already in a room.";
|
|
Log.Error(failedReason);
|
|
TargetJoindRoomFailed(sender, failedReason);
|
|
return;
|
|
}
|
|
if (room.MemberIds.Count >= room.maxPlayers)
|
|
{
|
|
failedReason = "The room is already full.";
|
|
Log.Error(failedReason);
|
|
TargetJoindRoomFailed(sender, failedReason);
|
|
return;
|
|
}
|
|
if (room.isStarted && room.lockOnStart)
|
|
{
|
|
failedReason = "Room has already started a game.";
|
|
Log.Error(failedReason);
|
|
TargetJoindRoomFailed(sender, failedReason);
|
|
return;
|
|
}
|
|
room.AddMember(ci.NetworkObject);
|
|
connectionRooms[ci.Owner] = room;
|
|
OnClientJoinedRoom?.Invoke(room, ci.NetworkObject);
|
|
|
|
foreach (NetworkObject item in room.MemberIds)
|
|
{
|
|
TargetMemberJoined(item.Owner, ci.NetworkObject);
|
|
if (item.Owner != ci.Owner)
|
|
{
|
|
TargetMemberJoined(ci.Owner, item);
|
|
}
|
|
}
|
|
|
|
TargetJoinRoomSuccess(sender, room);
|
|
}
|
|
[TargetRpc]
|
|
private void TargetJoinRoomSuccess(NetworkConnection conn, Room room)
|
|
{
|
|
OnMemberJoined?.Invoke(ClientManager.Connection.FirstObject);
|
|
Locator.GetService<LobbyManager>().OnJoinRoom(room);
|
|
}
|
|
[TargetRpc]
|
|
private void TargetJoindRoomFailed(NetworkConnection conn, string failedReason)
|
|
{
|
|
Log.Error(failedReason);
|
|
}
|
|
#endregion
|
|
|
|
#region Leave Rooom
|
|
[Client]
|
|
public void LeaveRoom()
|
|
{
|
|
ServerLeaveRoom();
|
|
}
|
|
[ServerRpc(RequireOwnership = false)]
|
|
private void ServerLeaveRoom(NetworkConnection sender = null)
|
|
{
|
|
var ci = ClientInstance.ReturnClientInstance(sender);
|
|
Room room = RemoveFromRoom(ci.NetworkObject, false);
|
|
if (room == null)
|
|
{
|
|
TargetLeaveRoomFailed(sender);
|
|
return;
|
|
}
|
|
TargetLeaveRoomSuccess(sender);
|
|
}
|
|
[TargetRpc]
|
|
private void TargetLeaveRoomSuccess(NetworkConnection conn)
|
|
{
|
|
Locator.GetService<LobbyManager>().OnLeaveRoom();
|
|
}
|
|
[TargetRpc]
|
|
private void TargetLeaveRoomFailed(NetworkConnection conn)
|
|
{
|
|
Log.Error("Leave room failed.");
|
|
}
|
|
#endregion
|
|
|
|
#region Manage Rooms
|
|
[Server]
|
|
private Room RemoveFromRoom(NetworkObject clientId, bool clientDisconnected)
|
|
{
|
|
Room room = ReturnRoom(clientId);
|
|
if (room == null)
|
|
return null;
|
|
//Let members know someone left
|
|
foreach (NetworkObject item in room.MemberIds)
|
|
{
|
|
if (clientDisconnected && item == clientId)
|
|
continue;
|
|
TargetMemberLeft(item.Owner, clientId);
|
|
}
|
|
|
|
//Remove the member from the room
|
|
room.RemoveMember(clientId);
|
|
connectionRooms.Remove(clientId.Owner);
|
|
OnClientLeftRoom?.Invoke(room, clientId);
|
|
|
|
//If not disconnectiong tell client to unload scenes
|
|
if (!clientDisconnected)
|
|
{
|
|
SceneLookupData[] lookups = SceneLookupData.CreateData(room.Scenes.ToArray());
|
|
SceneUnloadData sud = new SceneUnloadData(lookups);
|
|
if (lookups.Length > 0)
|
|
InstanceFinder.SceneManager.UnloadConnectionScenes(clientId.Owner, sud);
|
|
}
|
|
//If room is empty remove room
|
|
if (room.MemberIds.Count == 0)
|
|
{
|
|
createdRooms.Remove(room);
|
|
ObserverRoomChange(room, "Delete");
|
|
}
|
|
return room;
|
|
}
|
|
|
|
public Room ReturnRoom(string roomName)
|
|
{
|
|
return createdRooms.FirstOrDefault
|
|
(r => r.name.Equals(roomName, StringComparison.CurrentCultureIgnoreCase));
|
|
}
|
|
public Room ReturnRoom(NetworkObject clientId)
|
|
{
|
|
foreach (Room r in createdRooms)
|
|
{
|
|
if (r.MemberIds.Contains(clientId))
|
|
{
|
|
return r;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
[TargetRpc]
|
|
private void TargetMemberJoined(NetworkConnection conn, NetworkObject member)
|
|
{
|
|
OnMemberJoined?.Invoke(member);
|
|
Log.Debug("Member joined");
|
|
}
|
|
[TargetRpc]
|
|
private void TargetMemberLeft(NetworkConnection conn, NetworkObject member)
|
|
{
|
|
OnMemberLeft?.Invoke(member);
|
|
Log.Debug("Member left");
|
|
}
|
|
|
|
[ObserversRpc]
|
|
public void ObserverRoomChange(Room room, string action)
|
|
{
|
|
if (action == "Create")
|
|
{
|
|
OnRoomCreated?.Invoke(room);
|
|
}
|
|
else if (action == "Delete")
|
|
{
|
|
OnRoomDeleted?.Invoke(room);
|
|
}
|
|
else if (action == "Started")
|
|
{
|
|
OnRoomStarted?.Invoke(room);
|
|
}
|
|
}
|
|
#endregion
|
|
} |