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
+569
View File
@@ -0,0 +1,569 @@
using DeluxeBackend.Extensions;
using DeluxeBackend.Models;
using DeluxeBackend.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Linq;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Numerics;
using System.Security.Principal;
using System.Text.Json;
using System.Text.Json.Serialization;
using static DeluxeBackend.Enums;
using static DeluxeBackend.Extensions.RoomExtensions;
namespace DeluxeBackend.Controllers
{
[Route("Rooms")]
[ApiController]
public class RoomsController : ControllerBase
{
private readonly ILiteDbService db;
private readonly IJwtService jwt;
private readonly ICdnService cdn;
private readonly DiscordBotService discord;
private readonly bool setup = false;
public RoomsController(ILiteDbService _db, IJwtService _jwtService, DiscordBotService _discord, ICdnService _cdn)
{
db = _db;
jwt = _jwtService;
discord = _discord;
cdn = _cdn;
if (!setup)
{
setup = true;
var json = System.IO.File.ReadAllText(Path.Combine("data", "AGRoomRuntimeConfig.json"));
AGRoomRuntimeConfig activityRuntimeConfig = JsonSerializer.Deserialize<AGRoomRuntimeConfig>(json) ?? throw new Exception("Failed to deserialize");
Account mainPlayer = db.Accounts.FindById(1) ?? throw new Exception("player not found");
foreach (AGRoomRuntimeConfig.Room room in activityRuntimeConfig.Rooms)
{
if (db.Rooms.FindOne(x => x.Name == room.Name) != null)
{
continue;
}
Room newRoom = new Room
{
Accessibility = room.Accessibility,
Name = room.Name,
Description = room.Description,
CloningAllowed = room.CloningAllowed,
Creator = mainPlayer,
CustomWarning = room.CustomRoomWarning,
WarningMask = room.RoomWarningMask,
DisableMicAutoMute = room.DisableMicAutoMute,
DisableRoomComments = room.DisableRoomComments,
EncryptVoiceChat = false,
IsDorm = room.Name == "DormRoom",
IsRRO = true,
};
RoomSupports RoomSup = RoomSupports.None;
if (room.SupportsScreens) RoomSup |= RoomSupports.Screens;
if (room.SupportsWalkVR) RoomSup |= RoomSupports.WalkVR;
if (room.SupportsTeleportVR) RoomSup |= RoomSupports.TeleportVR;
RoomSup |= RoomSupports.Juniors;
newRoom.SupportedPlayerTypes = RoomSup;
db.Rooms.Insert(newRoom);
foreach (var scene in room.Scenes)
{
SubRoom newScene = new()
{
Room = newRoom,
LocationId = scene.RoomSceneLocationId,
Name = scene.Name,
IsSandbox = scene.IsSandbox,
MaxPlayers = scene.MaxPlayers,
CanMatchmakeInto = scene.CanMatchmakeInto,
SupportsJoinInProgress = scene.SupportsJoinInProgress,
UseLevelBasedMatchmaking = scene.UseLevelBasedMatchmaking,
UseAgeBasedMatchmaking = scene.UseAgeBasedMatchmaking,
UseRecRoyaleMatchmaking = scene.UseRecRoyaleMatchmaking
};
db.SubRooms.Insert(newScene);
}
}
}
}
[HttpGet("rooms/hot")]
public async Task<IActionResult> Hot([FromQuery] string? tag, [FromQuery] int skip = 0, [FromQuery] int take = 32)
{
var query = db.Rooms.Query().Where(x => x.Accessibility == RoomAccessibility.Public);
if (!string.IsNullOrWhiteSpace(tag))
{
if (tag.ToLower() == "rro")
{
query = query.Where(x => x.IsRRO == true);
}
}
var roomEntities = query.OrderByDescending(x => x.Stats.VisitCount).Skip(skip).Limit(take).ToList();
var tasks = roomEntities.Select(async p => await p.ToDictionary(discord, db));
var rooms = (await Task.WhenAll(tasks)).ToList();
return Ok(new
{
Results = rooms,
TotalResults = rooms.Count
});
}
[HttpGet("rooms/{roomId}/subrooms/{subroomId}/saves")]
[Authorize]
public async Task<IActionResult> RoomSaves([FromRoute] int roomId, [FromRoute] int subroomId, [FromQuery] int skip, [FromQuery] int take)
{
Account? account = await jwt.GetLogin(User);
if (account == null) return NotFound("User not found in database");
Room? room = db.Rooms.Include(x => x.Creator).FindById(roomId);
if (room == null) return NotFound();
if (!room.HasRole(account.Id, RoomRoleType.CoOwner))
{
return StatusCode(403);
}
SubRoom? subRoom = db.SubRooms.Include(x => x.Room).FindById(subroomId);
if (subRoom == null) return NotFound();
if (subRoom.Room.Id != roomId)
{
return NotFound();
}
var saves = db.SubRoomSave.Find(x => x.SubRoomId == subroomId).OrderByDescending(x => x.Id);
List<SubRoomSave> subRoomSaves = saves.Skip(skip).Take(take).ToList();
return Ok(new
{
Results = subRoomSaves.Select(x => x.ToDictionary()).ToList(),
TotalResults = saves.Count()
});
}
[HttpPut("rooms/{roomId}/subrooms/{subroomId}/permissions")]
[Authorize(Roles = "gameClient")]
public async Task<IActionResult> Permissions([FromRoute] int roomId, [FromRoute] int subroomId)
{
return Ok(new
{
Success = true
});
}
/*[HttpGet("subroom/{subroomId}/MakeSAVE")]
public async Task<IActionResult> MakeSave([FromRoute] int subroomId, [FromQuery] string DataBlob, [FromQuery] string? UnityAssetId = null)
{
SubRoom? room = db.SubRooms.FindById(subroomId);
if (room == null) return NotFound();
SubRoomSave Save = new SubRoomSave()
{
DataBlob = DataBlob,
SubRoomId = subroomId,
UnityAssetId = UnityAssetId
};
db.SubRoomSave.Insert(Save);
room.CurrentSave = Save;
db.SubRooms.Update(room);
return Ok(new { hiii = "meow :3" });
}*/
[HttpGet("rooms/createdby/me")]
[Authorize]
public async Task<IActionResult> CreatedbyMe()
{
Account? account = await jwt.GetLogin(User);
if (account == null) return NotFound("User not found in database");
List<Dictionary<string, object>> response = [];
var foundRooms = db.Rooms.Include(x => x.Creator).Find(x => x.Creator != null && x.Creator.Id == account.Id);
foreach (var room in foundRooms)
{
response.Add(await room.ToDictionary(discord, db, RoomDetailsMask.None));
}
return Ok(response);
}
[HttpGet("rooms/ownedby/me")]
[Authorize]
public async Task<IActionResult> OwnedbybyMe()
{
Account? account = await jwt.GetLogin(User);
if (account == null) return NotFound("User not found in database");
List<Dictionary<string, object>> response = [];
var foundRooms = db.Rooms.Include(x => x.Creator)
.FindAll()
.Where(x =>
(x.Creator != null && x.Creator.Id == account.Id) ||
(x.Roles != null && x.Roles.Any(r => r.AccountId == account.Id && r.Role >= RoomRoleType.CoOwner))
);
foreach (var room in foundRooms)
{
response.Add(await room.ToDictionary(discord, db, RoomDetailsMask.None));
}
return Ok(response);
}
[HttpGet("photon_access_token")]
[Authorize(Roles = "gameClient")]
public async Task<IActionResult> PhotonAccessToken()
{
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) return NotFound("");
if (presence.Instance == null) return BadRequest("Not in a Instance");
return Ok(new
{
RoomInstanceId = presence.Instance.Id,
PhotonAccessToken = "Meow",
Permissions = new List<object>()
});
}
[HttpGet("unity_assets/{UnityAssetId}/{Target}/{Version}")]
[Authorize(Roles = "gameClient")]
public async Task<IActionResult> UnityAssetss([FromRoute] Guid UnityAssetId, [FromRoute] AssetBundleType Target, AssetBundleVersion Version)
{
if (Target == AssetBundleType.OculusQuest)
{
Target = AssetBundleType.MobileAndroid;
}
UnityAssets unityAssets = db.UnityAssets.FindById(UnityAssetId);
if (unityAssets == null) return NotFound();
UnityAsset? unityAsset = unityAssets.Assets.FirstOrDefault(u => u.Target == Target && u.Version == Version);
if (unityAsset == null) return NotFound();
return Ok(new
{
UnityAssetId,
Target,
Version,
unityAsset.Filename
});
}
[HttpGet("rooms/ownedby/{accId}")]
public async Task<IActionResult> RoomsOwnedby(long accId)
{
List<Dictionary<string, object>> response = [];
var foundRooms = db.Rooms.Include(x => x.Creator)
.FindAll()
.Where(x =>
x.Accessibility == RoomAccessibility.Public &&
(
(x.Creator != null && x.Creator.Id == accId) ||
(x.Roles != null && x.Roles.Any(r => r.AccountId == accId && r.Role >= RoomRoleType.CoOwner))
)
);
foreach (var room in foundRooms)
{
response.Add(await room.ToDictionary(discord, db, RoomDetailsMask.None));
}
return Ok(response);
}
[HttpGet("rooms/base")]
[Authorize(Roles = "gameClient")]
public async Task<IActionResult> Base()
{
List<Dictionary<string, object>> response = [];
var foundRooms = db.Rooms.Include(x => x.Creator).Find(x => x.Accessibility == RoomAccessibility.Unlisted && x.CloningAllowed == true);
foreach (var room in foundRooms)
{
response.Add(await room.ToDictionary(discord, db, RoomDetailsMask.None));
}
return Ok(response);
}
[HttpPost("rooms/{roomId}/clone")]
[Authorize(Roles = "gameClient")]
public async Task<IActionResult> Clone(long roomId, [FromForm, Required] string name)
{
LoginWithInfoResult? login = await jwt.GetLoginWithInfo(User);
if (login == null) return Unauthorized();
Account account = login.Account;
Room? room = db.Rooms.Include(x => x.Creator).FindById(roomId);
if (room == null) return NotFound();
if (string.IsNullOrWhiteSpace(name) || name.Length < 3 || name.Length > 32)
{
return BadRequest(new { Success = false, Error = "Room name must be between 3 and 32 characters." });
}
if (!room.CloningAllowed && !room.HasRole(account.Id, RoomRoleType.CoOwner))
{
return Forbid();
}
if (db.Rooms.FindOne(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase)) != null)
{
return Conflict(new { Success = false, Error = $"The room name '{name}' is already taken." });
}
Room newRoom = new()
{
Name = name.Trim(),
SupportedPlayerTypes = room.SupportedPlayerTypes,
Accessibility = RoomAccessibility.Private,
ImageName = room.ImageName,
Description = room.Description,
Creator = account,
CustomWarning = room.CustomWarning,
WarningMask = room.WarningMask,
DataBlob = room.DataBlob,
DisableMicAutoMute = room.DisableMicAutoMute,
DisableRoomComments = room.DisableRoomComments,
EncryptVoiceChat = room.EncryptVoiceChat,
MinLevel = room.MinLevel,
PersistenceVersion = room.PersistenceVersion
};
db.Rooms.Insert(newRoom);
foreach (SubRoom subRoom in db.SubRooms.Include(x => x.Room).Include(x => x.CurrentSave).Find(x => x.Room.Id == roomId))
{
SubRoom newSubroom = new()
{
Room = newRoom,
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)
{
SubRoomSave newSave = new()
{
SubRoomId = newSubroom.Id,
DataBlob = subRoom.CurrentSave.DataBlob,
SavedByAccountId = account.Id,
SavedOnPlatform = login.Platform,
SavedOnDeviceClass = login.DeviceClass,
Description = $"Cloned From ^{room.Name}",
UnityAssetId = subRoom.CurrentSave.UnityAssetId,
PersistenceVersion = subRoom.CurrentSave.PersistenceVersion
};
db.SubRoomSave.Insert(newSave);
newSubroom.CurrentSave = newSave;
db.SubRooms.Update(newSubroom);
}
}
return Ok(new { Success = true, Value = await newRoom.ToDictionary(discord, db, RoomDetailsMask.StandardDetails) });
}
[HttpGet("rooms/moderatedby/me")]
public async Task<IActionResult> ModerateByNe()
{
Account? account = await jwt.GetLogin(User);
if (account == null) return NotFound("User not found in database");
List<Dictionary<string, object>> response = [];
var foundRooms = db.Rooms.Include(x => x.Creator)
.FindAll()
.Where(x =>
(x.Creator != null && x.Creator.Id == account.Id) ||
(x.Roles != null && x.Roles.Any(r => r.AccountId == account.Id && r.Role >= RoomRoleType.Moderator))
);
foreach (var room in foundRooms)
{
response.Add(await room.ToDictionary(discord, db, RoomDetailsMask.None));
}
return Ok(response);
}
public class UploadDTO
{
[JsonPropertyName("Filename")]
public required string filename { get; set; }
[JsonPropertyName("Hash")]
public required string hash { get; set; }
[JsonPropertyName("OwnershipProof")]
public required string ownershipProof { get; set; }
}
public class SaveSubroomDTO
{
[JsonPropertyName("AutoPublish")]
public bool autoPublish { get; set; } = false;
[JsonPropertyName("Description")]
public string? description { get; set; } = null;
[JsonPropertyName("RoomData")]
public required UploadDTO roomData { get; set; }
[JsonPropertyName("SubRoomData")]
public required UploadDTO SubRoomData { get; set; }
[JsonPropertyName("UnityAssetId")]
public string? unityAssetId { get; set; } = null;
[JsonPropertyName("PersistenceVersion")]
public int persistenceVersion { get; set; }
}
[HttpPost("rooms/{roomId}/subrooms/{subroomId}/data")]
[Authorize(Roles = "gameClient")]
public async Task<IActionResult> SubRoonData(long roomId, long subroomId, [FromBody, Required] SaveSubroomDTO request)
{
LoginWithInfoResult? login = await jwt.GetLoginWithInfo(User);
if (login == null) return Unauthorized();
Account account = login.Account;
Room? room = db.Rooms.Include(x => x.Creator).FindById(roomId);
if (room == null) return NotFound();
if (!room.HasRole(account.Id, RoomRoleType.CoOwner))
{
return StatusCode(403);
}
SubRoom? subRoom = db.SubRooms.Include(x => x.Room).FindById(subroomId);
if (subRoom == null) return NotFound();
if (subRoom.Room.Id != roomId)
{
return NotFound();
}
if (request.roomData.ownershipProof != CdnService.GenerateOwnershipProof(account, request.roomData.filename))
{
return Ok(new { Success = false, Error = "Room Ownership Proof Mismatch!" });
}
if (request.SubRoomData.ownershipProof != CdnService.GenerateOwnershipProof(account, request.SubRoomData.filename))
{
return Ok(new { Success = false, Error = "Subroom Ownership Proof Mismatch!" });
}
if (room.Accessibility == RoomAccessibility.Private)
{
request.autoPublish = true;
}
SubRoomSave save = new()
{
SubRoomId = subroomId,
DataBlob = request.SubRoomData.filename,
SavedByAccountId = account.Id,
Description = request.description,
UnityAssetId = request.unityAssetId,
SavedOnPlatform=login.Platform,
SavedOnDeviceClass=login.DeviceClass,
PersistenceVersion = request.persistenceVersion,
};
db.SubRoomSave.Insert(save);
if (request.autoPublish)
{
subRoom.CurrentSave = save;
db.SubRooms.Update(subRoom);
}
room.DataBlob = request.roomData.filename;
room.PersistenceVersion = request.persistenceVersion;
db.Rooms.Update(room);
return Ok(new { Success = true, Value = new Dictionary<string, object> { ["Room"] = await room.ToDictionary(discord, db, RoomDetailsMask.StandardDetails), ["SubRoomDataSave"] = save.ToDictionary()} });
}
[HttpGet("rooms/visitedby/{accId}")]
[Authorize]
public async Task<IActionResult> VisitedBy(long accId)
{
Account? account = db.Accounts.FindById(accId);
if (account == null) return NotFound();
if (!account.IsRecentHistoryVisible)
{
return StatusCode(403);
}
List<Dictionary<string, object>> response = [];
var visitedRoomIds = account.VisitedRooms.Keys.ToList();
if (visitedRoomIds.Count > 0)
{
var foundRooms = db.Rooms.Include(x => x.Creator)
.FindAll()
.Where(x => visitedRoomIds.Contains(x.Id))
.ToList().Where(room => room.Accessibility == RoomAccessibility.Public).OrderByDescending(room => account.VisitedRooms.TryGetValue(room.Id, out var date) ? date : DateTime.MinValue);
foreach (var room in foundRooms)
{
response.Add(await room.ToDictionary(discord, db, RoomDetailsMask.None));
}
}
return Ok(response);
}
[HttpGet("rooms/visitedby/me")]
[Authorize]
public async Task<IActionResult> VisitedByMe()
{
Account? account = await jwt.GetLogin(User);
if (account == null) return Unauthorized();
List<Dictionary<string, object>> response = [];
var visitedRoomIds = account.VisitedRooms.Keys.ToList();
if (visitedRoomIds.Count > 0)
{
var foundRooms = db.Rooms.Include(x => x.Creator)
.FindAll()
.Where(x => visitedRoomIds.Contains(x.Id))
.ToList().Where(room => room.Accessibility == RoomAccessibility.Public || room.HasRole(account.Id, RoomRoleType.CoOwner)).OrderByDescending(room => account.VisitedRooms.TryGetValue(room.Id, out var date) ? date : DateTime.MinValue);
foreach (var room in foundRooms)
{
response.Add(await room.ToDictionary(discord, db, RoomDetailsMask.None));
}
}
return Ok(response);
}
}
}