Update Crescendo image references: replace old image name with new identifier across multiple files

This commit is contained in:
Alexa
2026-06-03 11:42:42 +03:00
parent 2b7246abe9
commit 3f4a1b0eb0
4 changed files with 52 additions and 25 deletions
@@ -21,6 +21,9 @@ namespace DorkNet.Server.Controllers.Match;
public class MatchPlayerController( public class MatchPlayerController(
PlayerPresenceService presence, PlayerPresenceService presence,
NotificationService notifications, NotificationService notifications,
RoomService rooms,
PrivateInstanceService privateInstances,
IConfiguration config,
DorkNetDbContext db, DorkNetDbContext db,
ILogger<MatchPlayerController> log) : ControllerBase ILogger<MatchPlayerController> log) : ControllerBase
{ {
@@ -298,34 +301,58 @@ public class MatchPlayerController(
/// <summary>Build a RoomInstance that matches the shape /// <summary>Build a RoomInstance that matches the shape
/// <c>/goto/room/DormRoom</c> would have produced for this player /// <c>/goto/room/DormRoom</c> would have produced for this player
/// — used as a heartbeat fallback when presence is empty (boot /// — used as a heartbeat fallback when presence is empty (boot
/// race, replica restart, Redis TTL expiry). Same RoomInstanceId /// race, replica restart, Redis TTL expiry).
/// math, same PhotonRoomId pattern, same location GUID; the ///
/// watch's RoomInstancesAreDifferent finds the fallback identical /// CRITICAL: this MUST reproduce <c>/goto</c>'s RoomInstance
/// to its own cached state, so no out-of-sync. Returns null only /// byte-for-byte (RoomInstanceId, PhotonRoomId, Location, DataBlob),
/// if we can't find the player's dorm row in the DB.</summary> /// or the watch's RoomInstancesAreDifferent fires
/// "presence heartbeat response indicates local presence is
/// out-of-sync!" and boot-loops the player. The previous version
/// computed <c>RoomInstanceId = roomId * 100_000</c>, but
/// <c>GoToController</c> stamps the dorm with
/// <c>EnsureForDormAsync</c>'s id (<c>0xDD0000 ^ …</c>) — so the two
/// disagreed and every cache-miss heartbeat (e.g. right after a
/// server restart) kicked the player. We now go through the exact
/// same <see cref="RoomService.EnsurePersonalDormAsync"/> +
/// <see cref="PrivateInstanceService.EnsureForDormAsync"/> path
/// <c>/goto/room/DormRoom</c> uses, so the id math can never drift
/// again. Returns null only for anonymous callers.</summary>
private async Task<RoomInstanceDto?> BuildDefaultDormPresenceAsync(int playerId) private async Task<RoomInstanceDto?> BuildDefaultDormPresenceAsync(int playerId)
{ {
var dormRow = await db.Rooms.AsNoTracking() if (playerId == 0) return null;
.Where(r => r.IsDormRoom && r.CreatorPlayerId == playerId)
.Select(r => new { r.Id, r.CurrentDataBlobName, r.LocationReplicationId })
.FirstOrDefaultAsync();
// Fall back to the canonical dorm template row if the player // Same resolution /goto does: the caller's personal dorm row is
// doesn't yet have their own dorm record — same id space // the source of truth for RoomId + LocationReplicationId.
// /goto uses. var personal = await rooms.EnsurePersonalDormAsync(playerId);
long roomId = dormRow?.Id ?? 1; var roomId = personal.Id;
string dataBlob = dormRow?.CurrentDataBlobName ?? string.Empty; var location = personal.LocationReplicationId;
if (string.IsNullOrWhiteSpace(dataBlob))
dataBlob = await ResolveDataBlobForPresenceAsync(roomId, playerId); var dataBlob = await ResolveDataBlobForPresenceAsync(roomId, playerId);
var photonRegion = (config["Photon:CloudRegion"] ?? "us").ToLowerInvariant();
var photonRoomId = $"^dormroom_p{playerId}";
// EnsureForDormAsync owns the deterministic dorm instance-id
// formula; reusing it here guarantees the heartbeat's
// RoomInstanceId equals the one /goto handed the watch.
var dorm = await privateInstances.EnsureForDormAsync(
ownerPlayerId: playerId,
roomId: roomId,
subRoomId: 0,
baseName: "DormRoom",
location: location,
dataBlob: dataBlob,
photonRegion: photonRegion,
maxCapacity: 8,
photonRoomId: photonRoomId);
return new RoomInstanceDto return new RoomInstanceDto
{ {
RoomInstanceId = roomId * 100_000L, RoomInstanceId = dorm.InstanceId,
RoomId = roomId, RoomId = roomId,
SubRoomId = 0, SubRoomId = 0,
Location = dormRow?.LocationReplicationId ?? "76d98498-60a1-430c-ab76-b54a29b7a163", Location = location,
PhotonRegionId = "us", PhotonRegionId = photonRegion,
PhotonRoomId = $"^dormroom_p{playerId}", PhotonRoomId = photonRoomId,
Name = "DormRoom", Name = "DormRoom",
MaxCapacity = 8, MaxCapacity = 8,
IsFull = false, IsFull = false,
+1 -1
View File
@@ -128,7 +128,7 @@ public static class LegacyUpgrades
{ {
bloodMoon.Name = "Crescendo"; bloodMoon.Name = "Crescendo";
bloodMoon.Description = "Brave the haunted halls of Castle Dracula and survive the night."; bloodMoon.Description = "Brave the haunted halls of Castle Dracula and survive the night.";
bloodMoon.ImageName = "image_Crescendo.png"; bloodMoon.ImageName = "by3mjs9jbozpdvu6g9aje7jgz.png";
bloodMoon.HiddenFromBrowse = false; bloodMoon.HiddenFromBrowse = false;
await db.SaveChangesAsync(ct); await db.SaveChangesAsync(ct);
logger.LogInformation( logger.LogInformation(
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"3DCharades": "aj7w8q5nrfyt2ssfoovawtz4l.png", "3DCharades": "aj7w8q5nrfyt2ssfoovawtz4l.png",
"BowlingAlley": "8gwqibu0anm4j0rw54xo2y0yd.png", "BowlingAlley": "8gwqibu0anm4j0rw54xo2y0yd.png",
"Crescendo": "image_Crescendo.png", "Crescendo": "by3mjs9jbozpdvu6g9aje7jgz.png",
"CrimsonCauldron": "ae6yh8p6y8p3cuyqkc4l8wi4z.png", "CrimsonCauldron": "ae6yh8p6y8p3cuyqkc4l8wi4z.png",
"DiscGolfLake": "86s8y4ihyhx7n9ac41gwahwze.png", "DiscGolfLake": "86s8y4ihyhx7n9ac41gwahwze.png",
"DiscGolfPropulsion": "6wvnxmrmsw98wsttajp0nreci.png", "DiscGolfPropulsion": "6wvnxmrmsw98wsttajp0nreci.png",
+3 -3
View File
@@ -104,7 +104,7 @@ public class RoomService(DorkNetDbContext db)
// IsleOfLostSkulls was previously pointing at Quarry's GUID; // IsleOfLostSkulls was previously pointing at Quarry's GUID;
// real Location ReplicationId is 7e01cfe0-… (scene Quest_Pirate1_additive). // real Location ReplicationId is 7e01cfe0-… (scene Quest_Pirate1_additive).
("IsleOfLostSkulls", "The Isle of Lost Skulls", "Can your pirate crew get to the Isle, defeat its fearsome guardian, and escape with the gold?", "7e01cfe0-820a-406f-b1b3-0a5bf575235c", "recroomoriginal,quest,co-op,adventure", "image_IsleOfLostSkulls.png"), ("IsleOfLostSkulls", "The Isle of Lost Skulls", "Can your pirate crew get to the Isle, defeat its fearsome guardian, and escape with the gold?", "7e01cfe0-820a-406f-b1b3-0a5bf575235c", "recroomoriginal,quest,co-op,adventure", "image_IsleOfLostSkulls.png"),
("Crescendo", "Crescendo of the Blood Moon", "Brave the haunted halls of Castle Dracula and survive the night.", "49cb8993-a956-43e2-86f4-1318f279b22a", "recroomoriginal,quest,co-op,adventure", "image_Crescendo.png"), ("Crescendo", "Crescendo of the Blood Moon", "Brave the haunted halls of Castle Dracula and survive the night.", "49cb8993-a956-43e2-86f4-1318f279b22a", "recroomoriginal,quest,co-op,adventure", "by3mjs9jbozpdvu6g9aje7jgz.png"),
("StuntRunner", "Stunt Runner", "A solo platforming gauntlet — sprint, climb and dodge to reach the trophy at the top.", "b7281665-a715-4051-826b-8e08e69c6172", "recroomoriginal,sport,quest,stuntrunner,parkour", "image_StuntRunner.png"), ("StuntRunner", "Stunt Runner", "A solo platforming gauntlet — sprint, climb and dodge to reach the trophy at the top.", "b7281665-a715-4051-826b-8e08e69c6172", "recroomoriginal,sport,quest,stuntrunner,parkour", "image_StuntRunner.png"),
("Drive-In", "Rec Drive-In", "Watch movies, hang out with friends, or chill at the bar.", "65ddbb48-5a01-4e3e-972d-e5c7419e2bc3", "recroomoriginal,featured,hangout,chill", "image_DriveIn.png"), ("Drive-In", "Rec Drive-In", "Watch movies, hang out with friends, or chill at the bar.", "65ddbb48-5a01-4e3e-972d-e5c7419e2bc3", "recroomoriginal,featured,hangout,chill", "image_DriveIn.png"),
// Hub / hang rooms — no specific gameplay, just shared spaces. // Hub / hang rooms — no specific gameplay, just shared spaces.
@@ -368,7 +368,7 @@ public class RoomService(DorkNetDbContext db)
("PerformanceHall", "Performance Hall", "A big stage for live performances. Sing, perform, or just enjoy the show.", "9932f88f-3929-43a0-a012-a40b5128e346", "recroomoriginal", "image_PerformanceHall.png"), ("PerformanceHall", "Performance Hall", "A big stage for live performances. Sing, perform, or just enjoy the show.", "9932f88f-3929-43a0-a012-a40b5128e346", "recroomoriginal", "image_PerformanceHall.png"),
("EventRoom", "The Lounge", "A modular event hall with movable furniture for parties and meetings.", "a067557f-ca32-43e6-b6e5-daaec60b4f5a", "recroomoriginal", "image_EventRoom.png"), ("EventRoom", "The Lounge", "A modular event hall with movable furniture for parties and meetings.", "a067557f-ca32-43e6-b6e5-daaec60b4f5a", "recroomoriginal", "image_EventRoom.png"),
("BowlingAlley", "Bowling Alley", "Classic ten-pin bowling. Roll strikes, beat your friends.", "ae929543-9a07-41d5-8ee9-dbbee8c36800", "recroomoriginal,sport", "image_BowlingAlley.png"), ("BowlingAlley", "Bowling Alley", "Classic ten-pin bowling. Roll strikes, beat your friends.", "ae929543-9a07-41d5-8ee9-dbbee8c36800", "recroomoriginal,sport", "image_BowlingAlley.png"),
("Crescendo", "Crescendo of the Blood Moon", "Brave the haunted halls of Castle Dracula and survive the night.", "49cb8993-a956-43e2-86f4-1318f279b22a", "recroomoriginal,quest", "image_Crescendo.png"), ("Crescendo", "Crescendo of the Blood Moon", "Brave the haunted halls of Castle Dracula and survive the night.", "49cb8993-a956-43e2-86f4-1318f279b22a", "recroomoriginal,quest", "by3mjs9jbozpdvu6g9aje7jgz.png"),
("LaserTagHangar", "Laser Tag Hangar", "Teams battle in an industrial warehouse map.", "239e676c-f12f-489f-bf3a-d4c383d692c3", "recroomoriginal,sport", "image_LaserTag.png"), ("LaserTagHangar", "Laser Tag Hangar", "Teams battle in an industrial warehouse map.", "239e676c-f12f-489f-bf3a-d4c383d692c3", "recroomoriginal,sport", "image_LaserTag.png"),
("MakerRoom", "Maker Room", "A blank-walled canvas. The classic starting point for a Maker Pen build.", "a75f7547-79eb-47c6-8986-6767abcb4f92", "recroomoriginal,template,featured", "image_RecCenter.png"), ("MakerRoom", "Maker Room", "A blank-walled canvas. The classic starting point for a Maker Pen build.", "a75f7547-79eb-47c6-8986-6767abcb4f92", "recroomoriginal,template,featured", "image_RecCenter.png"),
}; };
@@ -939,7 +939,7 @@ public class RoomService(DorkNetDbContext db)
// (no UI for that yet) since the user didn't pass a custom URL. // (no UI for that yet) since the user didn't pass a custom URL.
Directory.CreateDirectory(imagesDir); Directory.CreateDirectory(imagesDir);
await TryDownloadAsync(http, "https://img.rec.net/by3mjs9jbozpdvu6g9aje7jgz.png", await TryDownloadAsync(http, "https://img.rec.net/by3mjs9jbozpdvu6g9aje7jgz.png",
Path.Combine(imagesDir, "image_Crescendo.png")); Path.Combine(imagesDir, "by3mjs9jbozpdvu6g9aje7jgz.png"));
await TryDownloadAsync(http, await TryDownloadAsync(http,
"https://static.wikia.nocookie.net/rec-room/images/4/48/Paintball_-_Key_Art.png/revision/latest/scale-to-width-down/1000?cb=20200513122350", "https://static.wikia.nocookie.net/rec-room/images/4/48/Paintball_-_Key_Art.png/revision/latest/scale-to-width-down/1000?cb=20200513122350",
Path.Combine(imagesDir, "image_PaintballKeyArt.png")); Path.Combine(imagesDir, "image_PaintballKeyArt.png"));