Add remaining project files
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
using DeluxeBackend.Models;
|
||||
using DeluxeBackend.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Data;
|
||||
using static DeluxeBackend.Enums;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace DeluxeBackend.Controllers.Matchmaking
|
||||
{
|
||||
[Route("Matchmaking/invite")]
|
||||
[ApiController]
|
||||
[Authorize(Roles = "gameClient")]
|
||||
public class InviteController(IJwtService jwt, ILiteDbService db, IMessageService message) : ControllerBase
|
||||
{
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Invite([FromForm(Name = "roomInstanceId")] long RoomInstanceId, [FromForm(Name = "playerId")] long PlayerId)
|
||||
{
|
||||
LoginWithInfoResult? login = await jwt.GetLoginWithInfo(User);
|
||||
|
||||
if (login == null) return Unauthorized();
|
||||
Account account = login.Account;
|
||||
Account? account1 = db.Accounts.FindById(PlayerId);
|
||||
if (account1 == null)
|
||||
{
|
||||
return Ok(new { Success = false });
|
||||
}
|
||||
|
||||
RoomInstance roomInstance = db.RoomInstances.Include(x => x.SubRoom).Include(x => x.SubRoom!.Room).Include(x => x.SubRoom!.Room!.Creator).FindById(RoomInstanceId);
|
||||
if (roomInstance == null)
|
||||
{
|
||||
return Ok(new { Success = false });
|
||||
}
|
||||
|
||||
PlayerInvite playerInvite = new()
|
||||
{
|
||||
Account = account1,
|
||||
InstanceId = roomInstance.Id,
|
||||
InvitedBy = account,
|
||||
ExpiresAt = DateTime.UtcNow.AddMinutes(10)
|
||||
};
|
||||
db.PlayerInvites.Insert(playerInvite);
|
||||
|
||||
await message.SendMessage(account, account1, MessageType.GameInviteV2, JsonSerializer.Serialize(new Dictionary<string, object>()
|
||||
{
|
||||
["inviteId"] = playerInvite.Id,
|
||||
["name"] = "meow",
|
||||
["roomInstanceId"] = roomInstance.Id
|
||||
}));
|
||||
|
||||
return Ok(new { Success = true });
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
using DeluxeBackend.Models;
|
||||
using DeluxeBackend.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using static DeluxeBackend.Enums;
|
||||
|
||||
namespace DeluxeBackend.Controllers.Matchmaking
|
||||
{
|
||||
[Route("Matchmaking/player")]
|
||||
[ApiController]
|
||||
public class PlayerController : ControllerBase
|
||||
{
|
||||
private readonly IJwtService jwt;
|
||||
private readonly ILiteDbService db;
|
||||
private readonly INotificationService ws;
|
||||
|
||||
public PlayerController(IJwtService _jwt, ILiteDbService _db, INotificationService _ws) {
|
||||
jwt = _jwt;
|
||||
db = _db;
|
||||
ws = _ws;
|
||||
}
|
||||
[HttpPost("login")]
|
||||
[Authorize(Roles = "gameClient")]
|
||||
public async Task<IActionResult> Login()
|
||||
{
|
||||
return Ok("");
|
||||
}
|
||||
[HttpPost("exclusivelogin")]
|
||||
[Authorize(Roles = "gameClient")]
|
||||
public async Task<IActionResult> ExclusiveLogin()
|
||||
{
|
||||
Account? account = await jwt.GetLogin(User);
|
||||
|
||||
if (account == null)
|
||||
{
|
||||
return NotFound("User not found in database");
|
||||
}
|
||||
|
||||
AccountPresence 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);
|
||||
if (presence == null)
|
||||
{
|
||||
presence = new AccountPresence
|
||||
{
|
||||
Account = account,
|
||||
};
|
||||
}
|
||||
presence.Instance = null;
|
||||
presence.LastOnline = DateTime.UtcNow;
|
||||
presence.IsOnline = true;
|
||||
db.Presences.Upsert(presence);
|
||||
await ws.SendToAllPlayer("PresenceUpdate", presence.ToDictionary());
|
||||
return Ok("");
|
||||
}
|
||||
[HttpGet("connection-info")]
|
||||
[Authorize(Roles = "gameClient")]
|
||||
public async Task<IActionResult> ConnectioniInfo([FromQuery(Name ="roomInstanceId")] long? roomInstanceId = null)
|
||||
{
|
||||
//return Ok(new { success = false });
|
||||
string? photonRegion = null;
|
||||
string? photonRoomId = null;
|
||||
if (roomInstanceId.HasValue)
|
||||
{
|
||||
RoomInstance? roomInstance = db.RoomInstances.Include(x => x.SubRoom).Include(x => x.SubRoom!.Room).FindById(roomInstanceId.Value);
|
||||
if (roomInstance != null)
|
||||
{
|
||||
photonRegion = roomInstance.PhotonRegion;
|
||||
photonRoomId = roomInstance.PhotonRoom;
|
||||
}
|
||||
}
|
||||
#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
|
||||
#pragma warning disable CS8601 // Possible null reference assignment.
|
||||
return Ok(new { success =true, value = new Dictionary<string, object>() {
|
||||
|
||||
["photonAuthToken"] = "Meow",
|
||||
["photonRealtimeAppId"] = "1b822489-5923-4d99-a0c9-7ca8b39e3481",
|
||||
["photonVoiceAppId"] = "5e54ba56-1c6d-4d7d-95b3-000c1b4f1d82",
|
||||
["photonChatAppId"] = "df1cd903-1d6a-4d74-ab85-19cb5ba38760",
|
||||
["photonRegion"] = photonRegion,
|
||||
["photonRoomId"] = photonRoomId,
|
||||
//["VoiceServerId"] = "test-voice-5",
|
||||
//["VoiceConnectionInfo"] = "192.168.1.171:6791"
|
||||
|
||||
} });
|
||||
#pragma warning restore CS8601
|
||||
#pragma warning restore CS8625
|
||||
}
|
||||
|
||||
[HttpPost("heartbeat")]
|
||||
[Authorize(Roles = "gameClient")]
|
||||
public async Task<IActionResult> Heartbeat()
|
||||
{
|
||||
LoginWithInfoResult? login = await jwt.GetLoginWithInfo(User);
|
||||
if (login == null) return Unauthorized();
|
||||
|
||||
Account account = login.Account;
|
||||
|
||||
AccountPresence 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);
|
||||
if (presence == null)
|
||||
{
|
||||
presence = new AccountPresence
|
||||
{
|
||||
Account = account,
|
||||
};
|
||||
}
|
||||
presence.LastOnline = DateTime.UtcNow;
|
||||
presence.IsOnline = true;
|
||||
presence.AppVersion = login.AppVersion;
|
||||
db.Presences.Upsert(presence);
|
||||
await ws.SendToAllPlayer("PresenceUpdate", presence.ToDictionary());
|
||||
return Ok(presence.ToDictionary());
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> Player([FromQuery(Name = "id")] List<long> accountIds)
|
||||
{
|
||||
Account? account = await jwt.GetLogin(User);
|
||||
|
||||
if (account == null)
|
||||
{
|
||||
return NotFound("User not found in database");
|
||||
}
|
||||
List<Dictionary<string, object>> presences = new List<Dictionary<string, object>>();
|
||||
foreach (var accountId in accountIds)
|
||||
{
|
||||
AccountPresence 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 == accountId);
|
||||
if (presence == null)
|
||||
{
|
||||
presence = new AccountPresence { Account = db.Accounts.FindById(accountId) };
|
||||
}
|
||||
presences.Add(presence.ToDictionary());
|
||||
|
||||
}
|
||||
return Ok(presences);
|
||||
}
|
||||
[HttpPut("gameserverregionpings")]
|
||||
[Authorize]
|
||||
public async Task<IActionResult> gameserverregionpings()
|
||||
{
|
||||
return Ok(new { Success = true });
|
||||
}
|
||||
|
||||
[HttpPut("statusvisibility")]
|
||||
[Authorize(Roles = "gameClient")]
|
||||
public async Task<IActionResult> StatusVisibility([FromForm] StatusVisibilityType statusVisibility)
|
||||
{
|
||||
Account? account = await jwt.GetLogin(User);
|
||||
|
||||
if (account == null)
|
||||
{
|
||||
return NotFound("User not found in database");
|
||||
}
|
||||
|
||||
AccountPresence 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);
|
||||
if (presence == null)
|
||||
{
|
||||
presence = new AccountPresence
|
||||
{
|
||||
Account = account,
|
||||
};
|
||||
}
|
||||
presence.StatusVisibility = statusVisibility;
|
||||
db.Presences.Upsert(presence);
|
||||
await ws.SendToAllPlayer("PresenceUpdate", presence.ToDictionary());
|
||||
return Ok(presence.ToDictionary());
|
||||
}
|
||||
[HttpPut("vrmovementmode")]
|
||||
[Authorize(Roles = "gameClient")]
|
||||
public async Task<IActionResult> VrMovementMode([FromForm] VrMovementModeType vrMovementMode)
|
||||
{
|
||||
Account? account = await jwt.GetLogin(User);
|
||||
|
||||
if (account == null)
|
||||
{
|
||||
return NotFound("User not found in database");
|
||||
}
|
||||
|
||||
AccountPresence 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);
|
||||
if (presence == null)
|
||||
{
|
||||
presence = new AccountPresence
|
||||
{
|
||||
Account = account,
|
||||
};
|
||||
}
|
||||
presence.VrMovementMode = vrMovementMode;
|
||||
db.Presences.Upsert(presence);
|
||||
await ws.SendToPlayerSubs(account.Id, "PresenceUpdate", presence.ToDictionary());
|
||||
return Ok(presence.ToDictionary());
|
||||
}
|
||||
|
||||
[HttpPost("logout")]
|
||||
[Authorize(Roles = "gameClient")]
|
||||
public async Task<IActionResult> Logout()
|
||||
{
|
||||
Account? account = await jwt.GetLogin(User);
|
||||
|
||||
if (account == null)
|
||||
{
|
||||
return NotFound("User not found in database");
|
||||
}
|
||||
|
||||
AccountPresence 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);
|
||||
if (presence == null)
|
||||
{
|
||||
presence = new AccountPresence
|
||||
{
|
||||
Account = account,
|
||||
};
|
||||
}
|
||||
presence.Instance = null;
|
||||
presence.LastOnline = DateTime.UtcNow;
|
||||
presence.IsOnline = false;
|
||||
db.Presences.Upsert(presence);
|
||||
await ws.SendToAllPlayer("PresenceUpdate", presence.ToDictionary());
|
||||
return Ok("");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
using DeluxeBackend.Models;
|
||||
using DeluxeBackend.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using static DeluxeBackend.Enums;
|
||||
|
||||
namespace DeluxeBackend.Controllers.Matchmaking
|
||||
{
|
||||
[Route("Matchmaking/room")]
|
||||
[ApiController]
|
||||
[Authorize(Roles = "gameClient")]
|
||||
public class RoomController(IJwtService jwt, ILiteDbService db, DiscordBotService discord) : ControllerBase
|
||||
{
|
||||
[HttpGet("{roomId}/instances")]
|
||||
public async Task<IActionResult> Instances(long roomId)
|
||||
{
|
||||
var account = await jwt.GetLogin(User);
|
||||
if (account == null)
|
||||
{
|
||||
return Unauthorized();
|
||||
}
|
||||
|
||||
var room = db.Rooms.FindById(roomId);
|
||||
if (room == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
bool isMod = await account.HasRoleAsync(discord, "moderator") || await account.HasRoleAsync(discord, "developer");
|
||||
if (!isMod && !room.HasRole(account.Id, RoomRoleType.Moderator))
|
||||
{
|
||||
return StatusCode(StatusCodes.Status403Forbidden, "Permission Denied");
|
||||
}
|
||||
|
||||
var query = db.RoomInstances.Query()
|
||||
.Include(x => x.SubRoom)
|
||||
.Include(x => x.SubRoom!.Room)
|
||||
.Where(x => x.SubRoom.Room.Id == roomId);
|
||||
|
||||
if (!isMod)
|
||||
{
|
||||
query = query.Where(x => x.InstanceType == RoomInstanceType.Public);
|
||||
}
|
||||
|
||||
var instances = query.ToList();
|
||||
if (instances.Count == 0)
|
||||
{
|
||||
return Ok(Array.Empty<object>());
|
||||
}
|
||||
|
||||
var instanceIds = instances.Select(x => x.Id).ToList();
|
||||
|
||||
var presences = db.Presences.Query()
|
||||
.Include(x => x.Account)
|
||||
.Include(x => x.Instance)
|
||||
.Where(x => x.Instance != null && instanceIds.Contains(x.Instance.Id))
|
||||
.ToList();
|
||||
|
||||
var playersByInstance = presences
|
||||
.Where(x => x.Instance != null && x.Account != null)
|
||||
.GroupBy(x => x.Instance!.Id)
|
||||
.ToDictionary(
|
||||
g => g.Key,
|
||||
g => g.Select(p => p.Account.Id).ToList()
|
||||
);
|
||||
|
||||
var result = instances
|
||||
.OrderByDescending(x =>
|
||||
playersByInstance.TryGetValue(x.Id, out var ids) ? ids.Count : 0)
|
||||
.Select(x =>
|
||||
{
|
||||
var playerIds = playersByInstance.TryGetValue(x.Id, out var ids) ? ids : [];
|
||||
return MapToDictionary(x, playerIds);
|
||||
});
|
||||
|
||||
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
private static Dictionary<string, object> MapToDictionary(RoomInstance roomInstance, List<long> playerIds)
|
||||
{
|
||||
return new Dictionary<string, object>
|
||||
{
|
||||
["RoomInstanceId"] = roomInstance.Id,
|
||||
["RoomId"] = roomInstance.SubRoom?.Room?.Id ?? 0,
|
||||
["SubRoomId"] = roomInstance.SubRoom?.Id ?? 0,
|
||||
["IsFull"] = false,
|
||||
["CreatedAt"] = roomInstance.CreatedAt.ToString("O"),
|
||||
["PlayerIds"] = playerIds
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
using DeluxeBackend.Models;
|
||||
using DeluxeBackend.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using static DeluxeBackend.Enums;
|
||||
|
||||
namespace DeluxeBackend.Controllers.Matchmaking
|
||||
{
|
||||
[Route("Matchmaking/roominstance")]
|
||||
[ApiController]
|
||||
[Authorize(Roles = "gameClient")]
|
||||
public class RoomInstanceController(IJwtService jwt, ILiteDbService db, INotificationService ws) : ControllerBase
|
||||
{
|
||||
[HttpPut("{roominstanceId}/markprivate")]
|
||||
public async Task<IActionResult> MakePrivate(long roominstanceId)
|
||||
{
|
||||
LoginWithInfoResult? login = await jwt.GetLoginWithInfo(User);
|
||||
if (login == null) return Unauthorized();
|
||||
|
||||
Account account = login.Account;
|
||||
AccountPresence presence = GetOrCreatePresence(account);
|
||||
if (presence.Instance == null)
|
||||
{
|
||||
return BadRequest("Not in a Instance");
|
||||
}
|
||||
|
||||
RoomInstance? roomInstance = db.RoomInstances.Include(x => x.SubRoom).Include(x => x.SubRoom!.Room).Include(x => x.SubRoom!.Room!.Creator).FindById(roominstanceId);
|
||||
if (roomInstance == null) return NotFound();
|
||||
if (roomInstance.Id != presence.Instance!.Id)
|
||||
{
|
||||
return StatusCode(403);
|
||||
}
|
||||
if (roomInstance.InstanceType == RoomInstanceType.Public)
|
||||
{
|
||||
roomInstance.InstanceType = RoomInstanceType.Private;
|
||||
}
|
||||
roomInstance.Private= true;
|
||||
db.RoomInstances.Update(roomInstance);
|
||||
await ws.SendToPlayerSubs(account.Id, "RoomInstanceUpdate", roomInstance.ToDictionary());
|
||||
|
||||
return Ok(new { Success = true });
|
||||
}
|
||||
[HttpPut("{roominstanceId}/inprogress")]
|
||||
public async Task<IActionResult> SetInProgress(long roominstanceId, [FromForm] bool inprogress)
|
||||
{
|
||||
LoginWithInfoResult? login = await jwt.GetLoginWithInfo(User);
|
||||
if (login == null) return Unauthorized();
|
||||
|
||||
Account account = login.Account;
|
||||
AccountPresence presence = GetOrCreatePresence(account);
|
||||
if (presence.Instance == null)
|
||||
{
|
||||
return BadRequest("Not in a Instance");
|
||||
}
|
||||
|
||||
RoomInstance? roomInstance = db.RoomInstances.Include(x => x.SubRoom).Include(x => x.SubRoom!.Room).Include(x => x.SubRoom!.Room!.Creator).FindById(roominstanceId);
|
||||
if (roomInstance == null) return NotFound();
|
||||
if (roomInstance.Id != presence.Instance!.Id)
|
||||
{
|
||||
return StatusCode(403);
|
||||
}
|
||||
if (roomInstance.GameInProgress == inprogress)
|
||||
{
|
||||
return Ok(new { Success = true });
|
||||
}
|
||||
roomInstance.GameInProgress= inprogress;
|
||||
db.RoomInstances.Update(roomInstance);
|
||||
await ws.SendToPlayerSubs(account.Id, "RoomInstanceUpdate", roomInstance.ToDictionary());
|
||||
|
||||
return Ok(new { Success = true });
|
||||
}
|
||||
[HttpPut("{roominstanceId}/roomCode")]
|
||||
public async Task<IActionResult> SetRoomCode(long roominstanceId, [FromForm, Required] string roomCode, [FromForm, Required] bool forceChange)
|
||||
{
|
||||
return StatusCode(501);
|
||||
}
|
||||
[HttpPost("{roominstanceId}/reportjoinresult")]
|
||||
public async Task<IActionResult> ReportJoinResult(long roominstanceId)
|
||||
{
|
||||
return StatusCode(501);
|
||||
}
|
||||
|
||||
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 };
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user