:3
This commit is contained in:
@@ -0,0 +1,649 @@
|
||||
using Mocha2023.Classes.DBs;
|
||||
using Mocha2023.Classes.DBs.DBClasses;
|
||||
using static Mocha2023.Classes.DBs.DBClasses.PlayerDBClasses;
|
||||
using static Mocha2023.Classes.DBs.DBClasses.RoomDBClasses;
|
||||
|
||||
namespace Mocha2023.Classes
|
||||
{
|
||||
public class Sessions
|
||||
{
|
||||
private static readonly Random _rng = new();
|
||||
private static readonly object _matchmakingLock = new();
|
||||
private const long ActiveHeartbeatWindowSeconds = 120;
|
||||
private static readonly Dictionary<long, HashSet<long>>
|
||||
_confirmedParticipantsByInstance = new();
|
||||
private static readonly Dictionary<long, long>
|
||||
_instanceOwners = new();
|
||||
private static readonly Dictionary<long, DateTime>
|
||||
_guestDormEnteredAt = new();
|
||||
private static readonly TimeSpan GuestDormHoldWindow =
|
||||
TimeSpan.FromSeconds(15);
|
||||
|
||||
public static Heartbeat? CreateRoom(
|
||||
long playerId,
|
||||
long roomId,
|
||||
string sceneName = "",
|
||||
bool isPrivate = false)
|
||||
{
|
||||
return CreateRoomInternal(
|
||||
playerId,
|
||||
roomId,
|
||||
sceneName,
|
||||
exactSubRoomId: null,
|
||||
isPrivate: isPrivate);
|
||||
}
|
||||
|
||||
public static Heartbeat? CreateRoom(
|
||||
long playerId,
|
||||
long roomId,
|
||||
long subRoomId,
|
||||
bool isPrivate = false)
|
||||
{
|
||||
return CreateRoomInternal(
|
||||
playerId,
|
||||
roomId,
|
||||
sceneName: null,
|
||||
exactSubRoomId: subRoomId,
|
||||
isPrivate: isPrivate);
|
||||
}
|
||||
|
||||
private static Heartbeat? CreateRoomInternal(
|
||||
long playerId,
|
||||
long roomId,
|
||||
string? sceneName,
|
||||
long? exactSubRoomId,
|
||||
bool isPrivate)
|
||||
{
|
||||
var room = RoomDB.GetRoom(roomId);
|
||||
|
||||
if (room?.SubRooms == null || room.SubRooms.Count == 0)
|
||||
return null;
|
||||
|
||||
SubRooms? subRoom = null;
|
||||
|
||||
if (exactSubRoomId.HasValue)
|
||||
{
|
||||
subRoom = room.SubRooms.FirstOrDefault(sub =>
|
||||
sub.SubRoomId == exactSubRoomId.Value);
|
||||
|
||||
if (subRoom == null)
|
||||
return null;
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(sceneName))
|
||||
{
|
||||
subRoom = room.SubRooms.FirstOrDefault(sub =>
|
||||
string.Equals(
|
||||
sub.Name,
|
||||
sceneName,
|
||||
StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
if (!exactSubRoomId.HasValue)
|
||||
{
|
||||
|
||||
subRoom ??= room.SubRooms
|
||||
.Where(sub => sub.Accessibility != RoomAccessibility.Private)
|
||||
.OrderByDescending(sub => string.Equals(
|
||||
sub.Name, "Home", StringComparison.OrdinalIgnoreCase))
|
||||
.FirstOrDefault();
|
||||
|
||||
subRoom ??= room.SubRooms.FirstOrDefault(sub =>
|
||||
string.Equals(
|
||||
sub.Name,
|
||||
"Home",
|
||||
StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
subRoom ??= room.SubRooms.FirstOrDefault();
|
||||
}
|
||||
|
||||
if (subRoom == null)
|
||||
return null;
|
||||
|
||||
lock (_matchmakingLock)
|
||||
{
|
||||
if (!RoomDB.CanPlayerAccessSubRoom(
|
||||
room,
|
||||
subRoom,
|
||||
playerId) ||
|
||||
RoomDB.IsPlayerBannedFromRoom(room.RoomId, playerId))
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[ROOM ACCESS DENIED] player={playerId} " +
|
||||
$"room={room.RoomId} subroom={subRoom.SubRoomId} " +
|
||||
$"isDorm={room.IsDorm} accessibility={room.Accessibility} " +
|
||||
$"subroomAccessibility={subRoom.Accessibility}");
|
||||
return null;
|
||||
}
|
||||
|
||||
bool effectivePrivate =
|
||||
isPrivate ||
|
||||
room.IsDorm ||
|
||||
room.Accessibility == RoomAccessibility.Private ||
|
||||
subRoom.Accessibility == RoomAccessibility.Private;
|
||||
|
||||
if (!effectivePrivate)
|
||||
{
|
||||
var existing = FindBestPublicInstance(
|
||||
playerId,
|
||||
room.RoomId,
|
||||
subRoom.SubRoomId,
|
||||
subRoom.MaxPlayers);
|
||||
|
||||
if (existing != null)
|
||||
{
|
||||
var joinedSession = CloneInstance(existing.Value.Instance);
|
||||
joinedSession.isFull =
|
||||
existing.Value.PlayerCount + 1 >= joinedSession.maxCapacity;
|
||||
RemovePlayerFromConfirmedInstancesNoLock(playerId);
|
||||
TrackParticipantNoLock(
|
||||
playerId,
|
||||
joinedSession.roomInstanceId);
|
||||
|
||||
Console.WriteLine(
|
||||
$"[MATCHMAKING] player={playerId} joined existing " +
|
||||
$"room={room.RoomId} instance={joinedSession.roomInstanceId} " +
|
||||
$"players={existing.Value.PlayerCount + 1}/{joinedSession.maxCapacity}");
|
||||
|
||||
return PlayerDB.UpdatePlayerHeartbeat(playerId, joinedSession);
|
||||
}
|
||||
}
|
||||
|
||||
bool hasPersistencePair = RoomDB.TryGetPersistencePair(
|
||||
room,
|
||||
subRoom,
|
||||
out var dataSave,
|
||||
out string roomDataBlob,
|
||||
out _);
|
||||
long instanceId = _rng.NextInt64(1_000_000L, 1_000_000_000_000L);
|
||||
string roomName = room.Name ?? "UnknownRoom";
|
||||
|
||||
var session = new RoomInstance
|
||||
{
|
||||
Name = $"^{roomName}",
|
||||
|
||||
dataBlob = hasPersistencePair ? roomDataBlob : string.Empty,
|
||||
isPrivate = effectivePrivate,
|
||||
location = subRoom.UnitySceneId,
|
||||
maxCapacity = Math.Max(1, subRoom.MaxPlayers),
|
||||
photonRegion = ServerConfig.PhotonRegion,
|
||||
photonRegionId = ServerConfig.PhotonRegion,
|
||||
photonRoomId = effectivePrivate
|
||||
? $"MochaRoom-{roomName}-private-{instanceId}-{Guid.NewGuid()}"
|
||||
: $"MochaRoom-{roomName}-{instanceId}",
|
||||
roomId = room.RoomId,
|
||||
roomInstanceId = instanceId,
|
||||
roomInstanceType = effectivePrivate
|
||||
? RoomInstanceType.Private
|
||||
: RoomInstanceType.Public,
|
||||
subRoomId = subRoom.SubRoomId,
|
||||
subRoomDataSaveId = dataSave?.SubRoomDataSaveId ?? 0,
|
||||
createdAt = DateTime.UtcNow
|
||||
};
|
||||
RemovePlayerFromConfirmedInstancesNoLock(playerId);
|
||||
TrackParticipantNoLock(playerId, session.roomInstanceId);
|
||||
if (IsRestrictedInstance(session))
|
||||
_instanceOwners[session.roomInstanceId] = playerId;
|
||||
|
||||
Console.WriteLine(
|
||||
$"[ROOM LOAD] room={room.RoomId} " +
|
||||
$"subroom={subRoom.SubRoomId} " +
|
||||
$"name={subRoom.Name} " +
|
||||
$"instance={session.roomInstanceId} new=true " +
|
||||
$"superRoomBlob={room.DataBlob ?? "null"} " +
|
||||
$"subRoomBlob={subRoom.DataBlob ?? "null"} " +
|
||||
$"subRoomDataSaveId={session.subRoomDataSaveId}");
|
||||
|
||||
return PlayerDB.UpdatePlayerHeartbeat(playerId, session);
|
||||
}
|
||||
}
|
||||
|
||||
private static (RoomInstance Instance, int PlayerCount)? FindBestPublicInstance(
|
||||
long playerId,
|
||||
long roomId,
|
||||
long subRoomId,
|
||||
int configuredCapacity)
|
||||
{
|
||||
long activeAfter = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
|
||||
- ActiveHeartbeatWindowSeconds;
|
||||
|
||||
return PlayerDB.Players.FindAll()
|
||||
.Where(player => player.PlayerId != playerId)
|
||||
.Select(player => player.Player?.PlayerExtra?.Heartbeat)
|
||||
.Where(heartbeat =>
|
||||
heartbeat != null &&
|
||||
heartbeat.isOnline &&
|
||||
heartbeat.lastHeartbeatUnixTime >= activeAfter &&
|
||||
heartbeat.roomInstance != null &&
|
||||
|
||||
!heartbeat.roomInstance.isPrivate &&
|
||||
heartbeat.roomInstance.roomId == roomId &&
|
||||
heartbeat.roomInstance.subRoomId == subRoomId &&
|
||||
!string.IsNullOrWhiteSpace(heartbeat.roomInstance.photonRoomId))
|
||||
.GroupBy(heartbeat => heartbeat!.roomInstance!.roomInstanceId)
|
||||
.Select(group =>
|
||||
{
|
||||
var instance = group.First()!.roomInstance!;
|
||||
int playerCount = group
|
||||
.Select(heartbeat => heartbeat!.playerId)
|
||||
.Distinct()
|
||||
.Count();
|
||||
int capacity = Math.Max(
|
||||
1,
|
||||
instance.maxCapacity > 0
|
||||
? instance.maxCapacity
|
||||
: configuredCapacity);
|
||||
|
||||
return new
|
||||
{
|
||||
Instance = instance,
|
||||
PlayerCount = playerCount,
|
||||
Capacity = capacity
|
||||
};
|
||||
})
|
||||
.Where(candidate => candidate.PlayerCount < candidate.Capacity)
|
||||
.OrderByDescending(candidate => candidate.PlayerCount)
|
||||
.ThenBy(candidate => candidate.Instance.roomInstanceId)
|
||||
.Select(candidate => ((RoomInstance Instance, int PlayerCount)?)
|
||||
(candidate.Instance, candidate.PlayerCount))
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
private static RoomInstance CloneInstance(RoomInstance source)
|
||||
{
|
||||
return new RoomInstance
|
||||
{
|
||||
encryptVoiceChat = source.encryptVoiceChat,
|
||||
clubId = source.clubId,
|
||||
dataBlob = source.dataBlob,
|
||||
eventId = source.eventId,
|
||||
isFull = source.isFull,
|
||||
isInProgress = source.isInProgress,
|
||||
isPrivate = source.isPrivate,
|
||||
location = source.location,
|
||||
maxCapacity = source.maxCapacity,
|
||||
Name = source.Name,
|
||||
photonRegion = source.photonRegion,
|
||||
photonRegionId = source.photonRegionId,
|
||||
photonRoomId = source.photonRoomId,
|
||||
roomCode = source.roomCode,
|
||||
roomId = source.roomId,
|
||||
roomInstanceId = source.roomInstanceId,
|
||||
roomInstanceType = source.roomInstanceType,
|
||||
subRoomId = source.subRoomId,
|
||||
subRoomDataSaveId = source.subRoomDataSaveId,
|
||||
createdAt = source.createdAt
|
||||
};
|
||||
}
|
||||
|
||||
public static bool IsRestrictedInstance(RoomInstance? instance) =>
|
||||
instance != null &&
|
||||
(instance.isPrivate ||
|
||||
instance.roomInstanceType is
|
||||
RoomInstanceType.Private or RoomInstanceType.Dormroom);
|
||||
|
||||
public static Heartbeat? JoinRoomInstance(
|
||||
long playerId,
|
||||
long roomInstanceId)
|
||||
{
|
||||
return JoinRoomInstanceInternal(
|
||||
playerId,
|
||||
roomInstanceId,
|
||||
invitedByPlayerId: null,
|
||||
expectedRoomId: null);
|
||||
}
|
||||
|
||||
public static Heartbeat? JoinInvitedRoomInstance(
|
||||
long playerId,
|
||||
long roomInstanceId,
|
||||
long invitedByPlayerId,
|
||||
long expectedRoomId)
|
||||
{
|
||||
if (invitedByPlayerId <= 0 || expectedRoomId <= 0)
|
||||
return null;
|
||||
|
||||
return JoinRoomInstanceInternal(
|
||||
playerId,
|
||||
roomInstanceId,
|
||||
invitedByPlayerId,
|
||||
expectedRoomId);
|
||||
}
|
||||
|
||||
private static Heartbeat? JoinRoomInstanceInternal(
|
||||
long playerId,
|
||||
long roomInstanceId,
|
||||
long? invitedByPlayerId,
|
||||
long? expectedRoomId)
|
||||
{
|
||||
if (playerId <= 0 || roomInstanceId <= 0)
|
||||
return null;
|
||||
|
||||
lock (_matchmakingLock)
|
||||
{
|
||||
long activeAfter = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
|
||||
- ActiveHeartbeatWindowSeconds;
|
||||
|
||||
var occupants = PlayerDB.Players.FindAll()
|
||||
.Select(player => player.Player?.PlayerExtra?.Heartbeat)
|
||||
.Where(heartbeat =>
|
||||
heartbeat != null &&
|
||||
heartbeat.isOnline &&
|
||||
heartbeat.lastHeartbeatUnixTime >= activeAfter &&
|
||||
heartbeat.roomInstance != null &&
|
||||
heartbeat.roomInstance.roomInstanceId == roomInstanceId &&
|
||||
|
||||
!string.IsNullOrWhiteSpace(
|
||||
heartbeat.roomInstance.photonRoomId))
|
||||
.ToList();
|
||||
|
||||
RoomInstance? source = occupants
|
||||
.OrderByDescending(heartbeat =>
|
||||
heartbeat!.lastHeartbeatUnixTime)
|
||||
.Select(heartbeat => heartbeat!.roomInstance)
|
||||
.FirstOrDefault();
|
||||
if (source == null)
|
||||
return null;
|
||||
|
||||
if (expectedRoomId.HasValue &&
|
||||
source.roomId != expectedRoomId.Value)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[PRIVATE JOIN DENIED] player={playerId} " +
|
||||
$"instance={roomInstanceId} reason=invite-room-mismatch");
|
||||
return null;
|
||||
}
|
||||
|
||||
bool alreadyConfirmed = occupants.Any(heartbeat =>
|
||||
heartbeat!.playerId == playerId);
|
||||
bool isOwner = _instanceOwners.TryGetValue(
|
||||
roomInstanceId,
|
||||
out long ownerPlayerId) &&
|
||||
ownerPlayerId == playerId;
|
||||
bool inviterIsPresent = invitedByPlayerId.HasValue &&
|
||||
invitedByPlayerId.Value != playerId &&
|
||||
occupants.Any(heartbeat =>
|
||||
heartbeat!.playerId == invitedByPlayerId.Value);
|
||||
|
||||
bool hasVerifiedInviteGrant =
|
||||
invitedByPlayerId.HasValue &&
|
||||
invitedByPlayerId.Value != playerId &&
|
||||
NotificationDB.HasActiveRoomInvite(
|
||||
playerId,
|
||||
invitedByPlayerId.Value,
|
||||
roomInstanceId,
|
||||
source.roomId);
|
||||
|
||||
var sourceRoom = RoomDB.GetRoom(source.roomId);
|
||||
var sourceSubRoom = sourceRoom?.SubRooms?.FirstOrDefault(
|
||||
subRoom => subRoom.SubRoomId == source.subRoomId);
|
||||
bool persistentRoomIsRestricted =
|
||||
sourceRoom?.IsDorm == true ||
|
||||
sourceRoom?.Accessibility == RoomAccessibility.Private ||
|
||||
sourceSubRoom?.Accessibility == RoomAccessibility.Private;
|
||||
bool hasPersistentRoomAccess =
|
||||
persistentRoomIsRestricted &&
|
||||
RoomDB.CanPlayerAccessSubRoom(
|
||||
sourceRoom,
|
||||
sourceSubRoom,
|
||||
playerId);
|
||||
|
||||
if (sourceRoom != null &&
|
||||
RoomDB.IsPlayerBannedFromRoom(sourceRoom.RoomId, playerId))
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[ROOM ACCESS DENIED] player={playerId} " +
|
||||
$"room={sourceRoom.RoomId} instance={roomInstanceId} " +
|
||||
"reason=room-ban");
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((IsRestrictedInstance(source) ||
|
||||
persistentRoomIsRestricted) &&
|
||||
!alreadyConfirmed &&
|
||||
!isOwner &&
|
||||
!hasVerifiedInviteGrant &&
|
||||
!hasPersistentRoomAccess)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[PRIVATE JOIN DENIED] player={playerId} " +
|
||||
$"instance={roomInstanceId} room={source.roomId} " +
|
||||
"reason=invite-required");
|
||||
return null;
|
||||
}
|
||||
|
||||
int playerCount = occupants
|
||||
.Select(heartbeat => heartbeat!.playerId)
|
||||
.Where(id => id > 0)
|
||||
.Distinct()
|
||||
.Count();
|
||||
int capacity = Math.Max(1, source.maxCapacity);
|
||||
if (!alreadyConfirmed && playerCount >= capacity)
|
||||
return null;
|
||||
|
||||
RoomInstance joinedSession = CloneInstance(source);
|
||||
int joinedPlayerCount = alreadyConfirmed
|
||||
? playerCount
|
||||
: playerCount + 1;
|
||||
joinedSession.isFull = joinedPlayerCount >= capacity;
|
||||
RemovePlayerFromConfirmedInstancesNoLock(playerId);
|
||||
TrackParticipantNoLock(
|
||||
playerId,
|
||||
joinedSession.roomInstanceId);
|
||||
|
||||
if (hasVerifiedInviteGrant &&
|
||||
joinedSession.roomInstanceType == RoomInstanceType.Dormroom)
|
||||
{
|
||||
_guestDormEnteredAt[playerId] = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
Console.WriteLine(
|
||||
$"[MATCHMAKING INSTANCE] player={playerId} " +
|
||||
$"joined room={joinedSession.roomId} " +
|
||||
$"instance={joinedSession.roomInstanceId} " +
|
||||
$"players={joinedPlayerCount}/{capacity} " +
|
||||
$"viaInvite={hasVerifiedInviteGrant.ToString().ToLowerInvariant()} " +
|
||||
$"inviterPresent={inviterIsPresent.ToString().ToLowerInvariant()}");
|
||||
|
||||
return PlayerDB.UpdatePlayerHeartbeat(
|
||||
playerId,
|
||||
joinedSession);
|
||||
}
|
||||
}
|
||||
|
||||
public static void MarkGuestDormEntry(long playerId)
|
||||
{
|
||||
lock (_matchmakingLock)
|
||||
_guestDormEnteredAt[playerId] = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public static bool ShouldHoldInGuestDorm(long playerId, Heartbeat heartbeat)
|
||||
{
|
||||
if (!heartbeat.isOnline ||
|
||||
heartbeat.roomInstance?.roomInstanceType != RoomInstanceType.Dormroom)
|
||||
return false;
|
||||
|
||||
lock (_matchmakingLock)
|
||||
{
|
||||
return _guestDormEnteredAt.TryGetValue(
|
||||
playerId,
|
||||
out DateTime enteredAt) &&
|
||||
DateTime.UtcNow - enteredAt < GuestDormHoldWindow;
|
||||
}
|
||||
}
|
||||
|
||||
public static Heartbeat CreateDorm(long playerId, string name)
|
||||
{
|
||||
var dorm = RoomDB.GetOrCreatePlayerDorm(playerId)
|
||||
?? RoomDB.GetRoom(1);
|
||||
var subRoom =
|
||||
dorm?.SubRooms?.FirstOrDefault(sub =>
|
||||
string.Equals(
|
||||
sub.Name,
|
||||
"Home",
|
||||
StringComparison.OrdinalIgnoreCase))
|
||||
?? dorm?.SubRooms?.FirstOrDefault();
|
||||
|
||||
long dormId = dorm?.RoomId ?? 1;
|
||||
|
||||
long instanceId = _rng.NextInt64(1_000_000L, 1_000_000_000_000L);
|
||||
SubRoomDataSave? dataSave = null;
|
||||
string roomDataBlob = string.Empty;
|
||||
bool hasPersistencePair = false;
|
||||
if (dorm != null && subRoom != null)
|
||||
{
|
||||
hasPersistencePair = RoomDB.TryGetPersistencePair(
|
||||
dorm,
|
||||
subRoom,
|
||||
out dataSave,
|
||||
out roomDataBlob,
|
||||
out _);
|
||||
}
|
||||
|
||||
var session = new RoomInstance
|
||||
{
|
||||
isPrivate = true,
|
||||
|
||||
dataBlob = hasPersistencePair ? roomDataBlob : string.Empty,
|
||||
|
||||
location = subRoom?.UnitySceneId
|
||||
?? "76d98498-60a1-430c-ab76-b54a29b7a163",
|
||||
|
||||
maxCapacity = subRoom?.MaxPlayers ?? 4,
|
||||
Name = $"@{name}'s Dorm",
|
||||
photonRegion = ServerConfig.PhotonRegion,
|
||||
photonRegionId = ServerConfig.PhotonRegion,
|
||||
photonRoomId = $"MochaDorm-{instanceId}-room",
|
||||
roomId = dormId,
|
||||
roomInstanceId = instanceId,
|
||||
subRoomId = subRoom?.SubRoomId ?? 0,
|
||||
subRoomDataSaveId = dataSave?.SubRoomDataSaveId ?? 0,
|
||||
roomInstanceType = RoomInstanceType.Dormroom,
|
||||
createdAt = DateTime.UtcNow
|
||||
};
|
||||
lock (_matchmakingLock)
|
||||
{
|
||||
RemovePlayerFromConfirmedInstancesNoLock(playerId);
|
||||
TrackParticipantNoLock(playerId, session.roomInstanceId);
|
||||
_instanceOwners[session.roomInstanceId] = playerId;
|
||||
_guestDormEnteredAt.Remove(playerId);
|
||||
}
|
||||
|
||||
Console.WriteLine(
|
||||
$"[DORM LOAD] account={playerId} room={session.roomId} " +
|
||||
$"subroom={session.subRoomId} " +
|
||||
$"superRoomBlob={(hasPersistencePair ? session.dataBlob : "null")} " +
|
||||
$"subRoomBlob={(hasPersistencePair ? dataSave?.DataBlob : "null")} " +
|
||||
$"subRoomDataSaveId={session.subRoomDataSaveId} " +
|
||||
$"cv2Pair={hasPersistencePair}");
|
||||
|
||||
return PlayerDB.UpdatePlayerHeartbeat(playerId, session)
|
||||
?? new Heartbeat
|
||||
{
|
||||
playerId = playerId,
|
||||
isOnline = true,
|
||||
roomInstance = session
|
||||
};
|
||||
}
|
||||
|
||||
public static bool ReportJoinResult(
|
||||
long playerId,
|
||||
long roomInstanceId,
|
||||
bool succeeded)
|
||||
{
|
||||
if (playerId <= 0 || roomInstanceId <= 0)
|
||||
return false;
|
||||
|
||||
Heartbeat heartbeat = PlayerDB.GetPlayerHeartbeat(playerId);
|
||||
if (heartbeat.roomInstance?.roomInstanceId != roomInstanceId)
|
||||
{
|
||||
Console.WriteLine(
|
||||
$"[JOIN RESULT] player={playerId} instance={roomInstanceId} " +
|
||||
"ignored=true reason=heartbeat-mismatch");
|
||||
return false;
|
||||
}
|
||||
|
||||
lock (_matchmakingLock)
|
||||
{
|
||||
RemovePlayerFromConfirmedInstancesNoLock(playerId);
|
||||
if (succeeded)
|
||||
TrackParticipantNoLock(playerId, roomInstanceId);
|
||||
}
|
||||
|
||||
if (!succeeded)
|
||||
PlayerDB.LeaveCurrentRoom(playerId);
|
||||
|
||||
Console.WriteLine(
|
||||
$"[JOIN RESULT] player={playerId} instance={roomInstanceId} " +
|
||||
$"success={succeeded.ToString().ToLowerInvariant()}");
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void MarkPlayerLeft(long playerId)
|
||||
{
|
||||
if (playerId <= 0)
|
||||
return;
|
||||
|
||||
lock (_matchmakingLock)
|
||||
{
|
||||
RemovePlayerFromConfirmedInstancesNoLock(playerId);
|
||||
_guestDormEnteredAt.Remove(playerId);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsConfirmedParticipant(
|
||||
long playerId,
|
||||
long roomInstanceId)
|
||||
{
|
||||
lock (_matchmakingLock)
|
||||
{
|
||||
if (IsConfirmedParticipantNoLock(playerId, roomInstanceId))
|
||||
return true;
|
||||
}
|
||||
|
||||
Heartbeat heartbeat = PlayerDB.GetPlayerHeartbeat(playerId);
|
||||
return heartbeat.isOnline &&
|
||||
heartbeat.roomInstance?.roomInstanceId == roomInstanceId;
|
||||
}
|
||||
|
||||
private static void TrackParticipantNoLock(
|
||||
long playerId,
|
||||
long roomInstanceId)
|
||||
{
|
||||
if (playerId <= 0 || roomInstanceId <= 0)
|
||||
return;
|
||||
|
||||
if (!_confirmedParticipantsByInstance.TryGetValue(
|
||||
roomInstanceId,
|
||||
out HashSet<long>? participants))
|
||||
{
|
||||
participants = new HashSet<long>();
|
||||
_confirmedParticipantsByInstance[roomInstanceId] = participants;
|
||||
}
|
||||
|
||||
participants.Add(playerId);
|
||||
}
|
||||
|
||||
private static bool IsConfirmedParticipantNoLock(
|
||||
long playerId,
|
||||
long roomInstanceId)
|
||||
{
|
||||
return _confirmedParticipantsByInstance.TryGetValue(
|
||||
roomInstanceId,
|
||||
out HashSet<long>? participants) &&
|
||||
participants.Contains(playerId);
|
||||
}
|
||||
|
||||
private static void RemovePlayerFromConfirmedInstancesNoLock(
|
||||
long playerId)
|
||||
{
|
||||
foreach (long instanceId in _confirmedParticipantsByInstance
|
||||
.Where(pair => pair.Value.Remove(playerId) &&
|
||||
pair.Value.Count == 0)
|
||||
.Select(pair => pair.Key)
|
||||
.ToList())
|
||||
{
|
||||
_confirmedParticipantsByInstance.Remove(instanceId);
|
||||
_instanceOwners.Remove(instanceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user