Add remaining project files

This commit is contained in:
Marco Baldwin
2026-07-23 18:21:43 -07:00
parent c12d8ac35d
commit 6e15e89a9d
453 changed files with 64265 additions and 0 deletions
@@ -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
}
}
+221
View File
@@ -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("");
}
}
}
+103
View File
@@ -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 };
}
}
}