569 lines
22 KiB
C#
569 lines
22 KiB
C#
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);
|
|
}
|
|
}
|
|
} |