Add remaining project files
This commit is contained in:
@@ -0,0 +1,458 @@
|
||||
using DeluxeBackend.Controllers.Auth;
|
||||
using DeluxeBackend.Models;
|
||||
using DeluxeBackend.Services;
|
||||
using Discord;
|
||||
using Discord.Net;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using static DeluxeBackend.Enums;
|
||||
|
||||
namespace DeluxeBackend.Controllers.Matchmaking
|
||||
{
|
||||
[Token(Token = "0x2000BB7")]
|
||||
public enum PLBMBHKLCHD
|
||||
{
|
||||
[Token(Token = "0x4002DF5")] UnknownError = -1,
|
||||
[Token(Token = "0x4002DF6")] Success,
|
||||
[Token(Token = "0x4002DF7")] NoSuchGame,
|
||||
[Token(Token = "0x4002DF8")] PlayerNotOnline,
|
||||
[Token(Token = "0x4002DF9")] InsufficientSpace,
|
||||
[Token(Token = "0x4002DFA")] EventNotStarted,
|
||||
[Token(Token = "0x4002DFB")] EventAlreadyFinished,
|
||||
[Token(Token = "0x4002DFC")] BlockedFromRoom = 7,
|
||||
[Token(Token = "0x4002DFD")] JuniorNotAllowed = 11,
|
||||
[Token(Token = "0x4002DFE")] Banned,
|
||||
[Token(Token = "0x4002DFF")] AlreadyInBestInstance,
|
||||
[Token(Token = "0x4002E00")] InsufficientRelationship,
|
||||
[Token(Token = "0x4002E01")] UpdateRequired = 16,
|
||||
[Token(Token = "0x4002E02")] AlreadyInTargetInstance,
|
||||
[Token(Token = "0x4002E03")] UGCNotAllowed = 19,
|
||||
[Token(Token = "0x4002E04")] NoSuchRoom,
|
||||
[Token(Token = "0x4002E05")] RoomIsNotActive = 22,
|
||||
[Token(Token = "0x4002E06")] RoomBlockedByCreator,
|
||||
[Token(Token = "0x4002E07")] RoomIsPrivate = 25,
|
||||
[Token(Token = "0x4002E08")] RoomInstanceIsPrivate,
|
||||
[Token(Token = "0x4002E09")] DeviceClassNotSupported = 30,
|
||||
[Token(Token = "0x4002E0A")] DeviceClassNotSupportedByRoomOwner,
|
||||
[Token(Token = "0x4002E0B")] MovementModeNotSupportedByRoomOwner,
|
||||
[Token(Token = "0x4002E0C")] EventIsPrivate = 35,
|
||||
[Token(Token = "0x4002E0D")] EventIsFull,
|
||||
[Token(Token = "0x4002E0E")] RoomInviteExpired = 40,
|
||||
[Token(Token = "0x4002E0F")] NoAvailableRegion = 45,
|
||||
[Token(Token = "0x4002E10")] NotorietyTooPoor = 50,
|
||||
[Token(Token = "0x4002E11")] BannedFromRoom = 55,
|
||||
[Token(Token = "0x4002E12")] NoSuchClub = 70,
|
||||
[Token(Token = "0x4002E13")] ClubHasNoClubhouse,
|
||||
[Token(Token = "0x4002E14")] ClubIsNotActive = 73,
|
||||
[Token(Token = "0x4002E15")] NotAMemberOfClub,
|
||||
[Token(Token = "0x4002E16")] BannedFromClub,
|
||||
[Token(Token = "0x4002E17")] InstanceJoinNotPermitted,
|
||||
[Token(Token = "0x4002E18")] LevelTooLow,
|
||||
[Token(Token = "0x4002E19")] ChatPartyInviteNotFound,
|
||||
[Token(Token = "0x4002E1A")] ChatPartyInviteModerated,
|
||||
[Token(Token = "0x4002E1B")] ChatMessageNotAnInvite,
|
||||
[Token(Token = "0x4002E1C")] DeveloperOnly,
|
||||
[Token(Token = "0x4002E1D")] RRPlusRequired,
|
||||
[Token(Token = "0x4002E1F")] MetaJuniorAccountRestriction,
|
||||
[Token(Token = "0x4002E1F")] NotExclusivelyLoggedIn,
|
||||
[Token(Token = "0x4002E20")] AccountDoesNotExist
|
||||
}
|
||||
|
||||
public enum JoinType
|
||||
{
|
||||
[Token(Token = "0x4002DF1")] PublicMatchmaking,
|
||||
[Token(Token = "0x4002DF2")] PublicNewInstance,
|
||||
[Token(Token = "0x4002DF3")] PrivateNewInstance
|
||||
}
|
||||
|
||||
public class JoinRoomRequestDto
|
||||
{
|
||||
[Required] public bool BypassMovementModeRestriction { get; set; }
|
||||
[Required] public int MaxPersistenceVersion { get; set; }
|
||||
[Required] public JoinType JoinMode { get; set; }
|
||||
public string? ClientJoinData { get; set; }
|
||||
}
|
||||
|
||||
[Route("Matchmaking/matchmake")]
|
||||
[ApiController]
|
||||
[Authorize(Roles = "gameClient")]
|
||||
public class MatchmakeController(IJwtService jwt, ILiteDbService db, INotificationService ws) : ControllerBase
|
||||
{
|
||||
private static readonly object _statsLock = new();
|
||||
|
||||
[HttpPost("none")]
|
||||
public async Task<IActionResult> None()
|
||||
{
|
||||
var login = await jwt.GetLoginWithInfo(User);
|
||||
if (login == null) return Unauthorized();
|
||||
|
||||
var presence = GetOrCreatePresence(login.Account);
|
||||
presence.Instance = null;
|
||||
presence.AppVersion = login.AppVersion;
|
||||
|
||||
await SaveAndNotifyPresenceAsync(presence);
|
||||
return Ok(presence.ToDictionary());
|
||||
}
|
||||
|
||||
[HttpPost("dorm")]
|
||||
public async Task<IActionResult> Dorm([FromForm, Required] JoinRoomRequestDto request)
|
||||
{
|
||||
var login = await jwt.GetLoginWithInfo(User);
|
||||
if (login == null) return Unauthorized();
|
||||
|
||||
var account = login.Account;
|
||||
var presence = GetOrCreatePresence(account);
|
||||
|
||||
var room = db.Rooms.Include(x => x.Creator).FindOne(x => x.IsDorm == true && x.Creator.Id == account.Id);
|
||||
if (room == null)
|
||||
{
|
||||
var baseDorm = db.Rooms.Include(x => x.Creator).FindOne(x => x.IsDorm == true && x.Creator.Id == 1);
|
||||
if (baseDorm == null)
|
||||
{
|
||||
return Ok(new { errorCode = PLBMBHKLCHD.RoomIsNotActive });
|
||||
}
|
||||
|
||||
room = InitializeNewDormRoom(account, baseDorm);
|
||||
db.Rooms.Insert(room);
|
||||
CloneSubRooms(room, baseDorm, account, login);
|
||||
}
|
||||
|
||||
if (room.Accessibility == RoomAccessibility.Private && !room.HasRole(account.Id, RoomRoleType.CoOwner))
|
||||
{
|
||||
return Ok(new { errorCode = PLBMBHKLCHD.RoomIsPrivate });
|
||||
}
|
||||
|
||||
var subRooms = db.SubRooms.Include(x => x.Room).Find(x => x.Room.Id == room.Id && x.CanMatchmakeInto).ToList();
|
||||
if (subRooms.Count == 0)
|
||||
{
|
||||
return Ok(new { errorCode = PLBMBHKLCHD.DeveloperOnly });
|
||||
}
|
||||
|
||||
var subRoom = subRooms[Random.Shared.Next(subRooms.Count)];
|
||||
var privateInstances = db.RoomInstances
|
||||
.Include(x => x.SubRoom)
|
||||
.Include(x => x.SubRoom!.Room)
|
||||
.Include(x => x.SubRoom!.Room!.Creator)
|
||||
.Find(x => x.SubRoom.Id == subRoom.Id && x.InstanceType == Enums.RoomInstanceType.Dormroom && x.EventId == -1)
|
||||
.ToList();
|
||||
|
||||
var roomInstance = privateInstances.Count == 0
|
||||
? CreateNewInstance(subRoom, Enums.RoomInstanceType.Dormroom, true)
|
||||
: privateInstances[0];
|
||||
|
||||
return await ProcessPresenceTransitionAsync(presence, roomInstance, account, room, login.AppVersion);
|
||||
}
|
||||
|
||||
[HttpPost("room/{RoomId}")]
|
||||
public async Task<IActionResult> GoToRoom([FromRoute] long RoomId, [FromForm, Required] JoinRoomRequestDto request)
|
||||
{
|
||||
var login = await jwt.GetLoginWithInfo(User);
|
||||
if (login == null) return Unauthorized();
|
||||
var room = db.Rooms.Include(x => x.Creator).FindOne(x => x.Id == RoomId);
|
||||
if (RoomId == 3 && DateTime.UtcNow.Month == 6)
|
||||
{
|
||||
room = db.Rooms.Include(x => x.Creator).FindOne(x => x.Name == "PrideCenter");
|
||||
}
|
||||
if (room == null) return Ok(new { errorCode = PLBMBHKLCHD.NoSuchRoom });
|
||||
|
||||
var account = login.Account;
|
||||
if (room.Accessibility == RoomAccessibility.Private && !room.HasRole(account.Id, RoomRoleType.CoOwner))
|
||||
{
|
||||
return Ok(new { errorCode = PLBMBHKLCHD.RoomIsPrivate });
|
||||
}
|
||||
|
||||
var subRooms = db.SubRooms.Include(x => x.Room).Find(x => x.Room.Id == room.Id && x.CanMatchmakeInto).ToList();
|
||||
if (subRooms.Count == 0) return Ok(new { errorCode = PLBMBHKLCHD.DeveloperOnly });
|
||||
|
||||
var subRoom = subRooms[Random.Shared.Next(subRooms.Count)];
|
||||
var presence = GetOrCreatePresence(account);
|
||||
|
||||
var roomInstance = ResolveRoomInstance(subRoom, room.Accessibility, request.JoinMode);
|
||||
if (roomInstance == null) return Ok(new { errorCode = PLBMBHKLCHD.NoAvailableRegion });
|
||||
|
||||
return await ProcessPresenceTransitionAsync(presence, roomInstance, account, room, login.AppVersion);
|
||||
}
|
||||
|
||||
[HttpPost("room/{RoomId}/{SubRoomId}")]
|
||||
public async Task<IActionResult> GoToSubRoom([FromRoute] long RoomId, [FromRoute] long SubRoomId, [FromForm, Required] JoinRoomRequestDto request)
|
||||
{
|
||||
var login = await jwt.GetLoginWithInfo(User);
|
||||
if (login == null) return Unauthorized();
|
||||
|
||||
var room = db.Rooms.Include(x => x.Creator).FindOne(x => x.Id == RoomId);
|
||||
if (RoomId == 3 && DateTime.UtcNow.Month == 6)
|
||||
{
|
||||
room = db.Rooms.Include(x => x.Creator).FindOne(x => x.Name == "PrideCenter");
|
||||
}
|
||||
if (room == null) return Ok(new { errorCode = PLBMBHKLCHD.NoSuchRoom });
|
||||
|
||||
var account = login.Account;
|
||||
if (room.Accessibility == RoomAccessibility.Private && !room.HasRole(account.Id, RoomRoleType.CoOwner))
|
||||
{
|
||||
return Ok(new { errorCode = PLBMBHKLCHD.RoomIsPrivate });
|
||||
}
|
||||
|
||||
var subRoom = db.SubRooms.Include(x => x.Room).FindOne(x => x.Room.Id == room.Id && x.Id == SubRoomId);
|
||||
if (subRoom == null) return Ok(new { errorCode = PLBMBHKLCHD.NoSuchGame });
|
||||
|
||||
var presence = GetOrCreatePresence(account);
|
||||
|
||||
var roomInstance = ResolveRoomInstance(subRoom, room.Accessibility, request.JoinMode);
|
||||
if (roomInstance == null) return Ok(new { errorCode = PLBMBHKLCHD.NoAvailableRegion });
|
||||
|
||||
return await ProcessPresenceTransitionAsync(presence, roomInstance, account, room, login.AppVersion);
|
||||
}
|
||||
|
||||
[HttpPost("event/{EventId}")]
|
||||
public async Task<IActionResult> GoToEvent([FromRoute] long EventId, [FromForm, Required] JoinRoomRequestDto request)
|
||||
{
|
||||
var login = await jwt.GetLoginWithInfo(User);
|
||||
if (login == null) return Unauthorized();
|
||||
|
||||
var playerEvent = db.PlayerEvents.Include(x => x.Room).Include(x => x.SubRoom).Include(x => x.Creator).FindById(EventId);
|
||||
if (playerEvent == null) return NotFound();
|
||||
|
||||
var room = db.Rooms.Include(x => x.Creator).FindOne(x => x.Id == playerEvent.Room.Id);
|
||||
if (room == null) return Ok(new { errorCode = PLBMBHKLCHD.NoSuchRoom });
|
||||
|
||||
var account = login.Account;
|
||||
if (room.Accessibility == RoomAccessibility.Private && !room.HasRole(account.Id, RoomRoleType.CoOwner))
|
||||
{
|
||||
return Ok(new { errorCode = PLBMBHKLCHD.RoomIsPrivate });
|
||||
}
|
||||
|
||||
var subRoom = db.SubRooms.Include(x => x.Room).FindOne(x => x.Room.Id == room.Id && x.Id == playerEvent.SubRoom.Id);
|
||||
if (subRoom == null) return Ok(new { errorCode = PLBMBHKLCHD.NoSuchGame });
|
||||
|
||||
RoomInstance? roomInstance = null;
|
||||
if (playerEvent.IsMultiInstance)
|
||||
{
|
||||
var roomInstances = db.RoomInstances.Include(x => x.SubRoom)
|
||||
.Find(x => x.SubRoom.Id == playerEvent.SubRoom.Id && x.Id != playerEvent.BroadcastingRoomInstanceId && x.EventId == playerEvent.Id && !x.Private).ToList();
|
||||
|
||||
roomInstance = roomInstances.Count == 0
|
||||
? CreateNewInstance(subRoom, Enums.RoomInstanceType.MultiInstanceEvent, false, EventId)
|
||||
: roomInstances[Random.Shared.Next(roomInstances.Count)];
|
||||
}
|
||||
|
||||
if (roomInstance == null) return Ok(new { errorCode = PLBMBHKLCHD.NoAvailableRegion });
|
||||
|
||||
var presence = GetOrCreatePresence(account);
|
||||
return await ProcessPresenceTransitionAsync(presence, roomInstance, account, room, login.AppVersion);
|
||||
}
|
||||
|
||||
[HttpPost("invite/{InviteId}")]
|
||||
public async Task<IActionResult> GoToInvite([FromRoute] long InviteId)
|
||||
{
|
||||
var login = await jwt.GetLoginWithInfo(User);
|
||||
if (login == null) return Unauthorized();
|
||||
|
||||
var account = login.Account;
|
||||
var playerInvite = db.PlayerInvites.FindById(InviteId);
|
||||
|
||||
if (playerInvite == null || playerInvite.Account.Id != account.Id || !playerInvite.IsValid)
|
||||
{
|
||||
return Ok(new { errorCode = PLBMBHKLCHD.RoomInviteExpired });
|
||||
}
|
||||
|
||||
var roomInstance = db.RoomInstances.Include(x => x.SubRoom).Include(x => x.SubRoom!.Room).Include(x => x.SubRoom!.Room!.Creator).FindById(playerInvite.InstanceId);
|
||||
if (roomInstance == null) return Ok(new { errorCode = PLBMBHKLCHD.NoAvailableRegion });
|
||||
|
||||
var presence = GetOrCreatePresence(account);
|
||||
return await ProcessPresenceTransitionAsync(presence, roomInstance, account, roomInstance.SubRoom.Room, login.AppVersion);
|
||||
}
|
||||
|
||||
[HttpPost("instance/{instanceId}")]
|
||||
public async Task<IActionResult> GoToInstance([FromRoute] long instanceId)
|
||||
{
|
||||
var login = await jwt.GetLoginWithInfo(User);
|
||||
if (login == null) return Unauthorized();
|
||||
|
||||
var roomInstance = db.RoomInstances.Include(x => x.SubRoom).Include(x => x.SubRoom!.Room).Include(x => x.SubRoom!.Room!.Creator).FindById(instanceId);
|
||||
if (roomInstance == null) return NotFound();
|
||||
if (roomInstance.Private) return Ok(new { errorCode = PLBMBHKLCHD.RoomInstanceIsPrivate });
|
||||
|
||||
var account = login.Account;
|
||||
var presence = GetOrCreatePresence(account);
|
||||
return await ProcessPresenceTransitionAsync(presence, roomInstance, account, roomInstance.SubRoom.Room, login.AppVersion);
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
private AccountPresence GetOrCreatePresence(Account account)
|
||||
{
|
||||
var presence = db.Presences
|
||||
.Include(x => x.Account)
|
||||
.Include(x => x.Instance)
|
||||
.Include(x => x.Instance!.SubRoom)
|
||||
.Include(x => x.Instance!.SubRoom!.Room)
|
||||
.Include(x => x.Instance!.SubRoom!.Room!.Creator)
|
||||
.FindOne(x => x.Account.Id == account.Id);
|
||||
|
||||
return presence ?? new AccountPresence { Account = account };
|
||||
}
|
||||
|
||||
private async Task SaveAndNotifyPresenceAsync(AccountPresence presence)
|
||||
{
|
||||
presence.LastOnline = DateTime.UtcNow;
|
||||
presence.IsOnline = true;
|
||||
db.Presences.Upsert(presence);
|
||||
await ws.SendToAllPlayer("PresenceUpdate", presence.ToDictionary());
|
||||
}
|
||||
|
||||
private RoomInstance CreateNewInstance(SubRoom subRoom, RoomInstanceType type, bool isPrivate, long eventId = -1)
|
||||
{
|
||||
var instance = new RoomInstance
|
||||
{
|
||||
SubRoom = subRoom,
|
||||
InstanceType = type,
|
||||
Private = isPrivate,
|
||||
PhotonRegion = "eu",
|
||||
PhotonRoom = Guid.NewGuid().ToString(),
|
||||
EventId = eventId,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
db.RoomInstances.Insert(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
private RoomInstance? ResolveRoomInstance(SubRoom subRoom, RoomAccessibility accessibility, JoinType joinMode)
|
||||
{
|
||||
if (accessibility == RoomAccessibility.Private)
|
||||
{
|
||||
var privateInstances = db.RoomInstances.Include(x => x.SubRoom).Include(x => x.SubRoom!.Room)
|
||||
.Find(x => x.SubRoom.Id == subRoom.Id && x.InstanceType == Enums.RoomInstanceType.Private && x.EventId == -1).ToList();
|
||||
|
||||
return privateInstances.Count == 0
|
||||
? CreateNewInstance(subRoom, Enums.RoomInstanceType.Private, true)
|
||||
: privateInstances[0];
|
||||
}
|
||||
|
||||
return joinMode switch
|
||||
{
|
||||
JoinType.PublicMatchmaking => GetOrCreatePublicInstance(subRoom),
|
||||
JoinType.PublicNewInstance => CreateNewInstance(subRoom, Enums.RoomInstanceType.Public, false),
|
||||
JoinType.PrivateNewInstance => CreateNewInstance(subRoom, Enums.RoomInstanceType.Private, true),
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
private RoomInstance GetOrCreatePublicInstance(SubRoom subRoom)
|
||||
{
|
||||
var roomInstances = db.RoomInstances.Include(x => x.SubRoom).Include(x => x.SubRoom!.Room)
|
||||
.Find(x => x.SubRoom.Id == subRoom.Id && x.InstanceType == Enums.RoomInstanceType.Public && x.EventId == -1).ToList();
|
||||
|
||||
return roomInstances.Count == 0
|
||||
? CreateNewInstance(subRoom, Enums.RoomInstanceType.Public, false)
|
||||
: roomInstances[Random.Shared.Next(roomInstances.Count)];
|
||||
}
|
||||
|
||||
private async Task<IActionResult> ProcessPresenceTransitionAsync(AccountPresence presence, RoomInstance targetInstance, Account account, Room room, string appVersion)
|
||||
{
|
||||
if (presence.Instance?.Id == targetInstance.Id)
|
||||
{
|
||||
return Ok(new { errorCode = PLBMBHKLCHD.AlreadyInTargetInstance });
|
||||
}
|
||||
|
||||
UpdateRoomStats(account, room);
|
||||
|
||||
presence.Instance = targetInstance;
|
||||
presence.AppVersion = appVersion;
|
||||
await SaveAndNotifyPresenceAsync(presence);
|
||||
|
||||
return Ok(new
|
||||
{
|
||||
errorCode = PLBMBHKLCHD.Success,
|
||||
roomInstance = targetInstance.ToDictionary()
|
||||
});
|
||||
}
|
||||
|
||||
private static Room InitializeNewDormRoom(Account account, Room baseDorm)
|
||||
{
|
||||
return new Room
|
||||
{
|
||||
Name = Guid.NewGuid().ToString(),
|
||||
IsDorm = true,
|
||||
SupportedPlayerTypes = baseDorm.SupportedPlayerTypes,
|
||||
Accessibility = RoomAccessibility.Private,
|
||||
ImageName = baseDorm.ImageName,
|
||||
Description = baseDorm.Description,
|
||||
Creator = account,
|
||||
CustomWarning = baseDorm.CustomWarning,
|
||||
WarningMask = baseDorm.WarningMask,
|
||||
DataBlob = baseDorm.DataBlob,
|
||||
DisableMicAutoMute = baseDorm.DisableMicAutoMute,
|
||||
DisableRoomComments = baseDorm.DisableRoomComments,
|
||||
EncryptVoiceChat = baseDorm.EncryptVoiceChat,
|
||||
MinLevel = baseDorm.MinLevel,
|
||||
PersistenceVersion = baseDorm.PersistenceVersion
|
||||
};
|
||||
}
|
||||
|
||||
private void CloneSubRooms(Room targetRoom, Room sourceRoom, Account account, LoginWithInfoResult login)
|
||||
{
|
||||
var sourceSubRooms = db.SubRooms.Include(x => x.Room).Include(x => x.CurrentSave).Find(x => x.Room.Id == sourceRoom.Id);
|
||||
|
||||
foreach (var subRoom in sourceSubRooms)
|
||||
{
|
||||
var newSubRoom = new SubRoom
|
||||
{
|
||||
Room = targetRoom,
|
||||
LocationId = subRoom.LocationId,
|
||||
Name = subRoom.Name,
|
||||
IsSandbox = subRoom.IsSandbox,
|
||||
MaxPlayers = subRoom.MaxPlayers,
|
||||
CanMatchmakeInto = subRoom.CanMatchmakeInto,
|
||||
SupportsJoinInProgress = subRoom.SupportsJoinInProgress,
|
||||
UseLevelBasedMatchmaking = subRoom.UseLevelBasedMatchmaking,
|
||||
UseAgeBasedMatchmaking = subRoom.UseAgeBasedMatchmaking,
|
||||
UseRecRoyaleMatchmaking = subRoom.UseRecRoyaleMatchmaking
|
||||
};
|
||||
db.SubRooms.Insert(newSubRoom);
|
||||
|
||||
if (subRoom.CurrentSave != null)
|
||||
{
|
||||
var newSave = new SubRoomSave
|
||||
{
|
||||
SubRoomId = newSubRoom.Id,
|
||||
DataBlob = subRoom.CurrentSave.DataBlob,
|
||||
SavedByAccountId = account.Id,
|
||||
SavedOnPlatform = login.Platform,
|
||||
SavedOnDeviceClass = login.DeviceClass,
|
||||
Description = $"Cloned From ^{targetRoom.Name}",
|
||||
UnityAssetId = subRoom.CurrentSave.UnityAssetId,
|
||||
PersistenceVersion = subRoom.CurrentSave.PersistenceVersion
|
||||
};
|
||||
db.SubRoomSave.Insert(newSave);
|
||||
|
||||
newSubRoom.CurrentSave = newSave;
|
||||
db.SubRooms.Update(newSubRoom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateRoomStats(Account account, Room room)
|
||||
{
|
||||
lock (_statsLock)
|
||||
{
|
||||
if (!room.Stats.VisitorIds.Contains(account.Id))
|
||||
{
|
||||
room.Stats.VisitorIds.Add(account.Id);
|
||||
}
|
||||
room.Stats.VisitCount += 1;
|
||||
db.Rooms.Update(room);
|
||||
|
||||
account.VisitedRooms[room.Id] = DateTime.UtcNow;
|
||||
db.Accounts.Update(account);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user