From 56b8d4285dab433043540cc596ecd04fda879c80 Mon Sep 17 00:00:00 2001 From: Alexa <53471801+Alexa-RR@users.noreply.github.com> Date: Thu, 2 Jul 2026 10:11:05 +0300 Subject: [PATCH] feat: add new matchmake routes for player, event, club, and code --- .../MatchmakePullInviteTests.cs | 8 ++++ .../Controllers/Match/GoToController.cs | 45 +++++++++++++++++-- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/DorkNet.Server.Tests/MatchmakePullInviteTests.cs b/DorkNet.Server.Tests/MatchmakePullInviteTests.cs index 4a1767e..0950753 100644 --- a/DorkNet.Server.Tests/MatchmakePullInviteTests.cs +++ b/DorkNet.Server.Tests/MatchmakePullInviteTests.cs @@ -28,6 +28,10 @@ public sealed class MatchmakePullInviteTests : IClassFixture> GoToEvent(long eventId) { - var resp = await BuildResponseAsync("DormRoom"); + // Matchmake the caller into the event's host room. Falls back to + // the dorm if the event is unknown or has no room set. + var roomId = await db.PlayerEvents.AsNoTracking() + .Where(e => e.Id == eventId) + .Select(e => (long?)e.RoomId) + .FirstOrDefaultAsync(); + string? roomName = null; + if (roomId is long rid && rid > 0) + roomName = await db.Rooms.AsNoTracking() + .Where(r => r.Id == rid) + .Select(r => r.Name) + .FirstOrDefaultAsync(); + + var resp = await BuildResponseAsync( + string.IsNullOrWhiteSpace(roomName) ? "DormRoom" : roomName); await RecordResponseAsync(resp); return Ok(resp); } [HttpPost("/goto/club/{clubId:long}")] + // 2023 rename goto/club → matchmake/club (RecNet.Matchmaking KPAIGCCHIBN). + [HttpPost("/matchmake/club/{clubId:long}")] public async Task> GoToClub(long clubId) { var club = await db.Clubs.AsNoTracking().FirstOrDefaultAsync(c => c.Id == clubId); @@ -430,7 +448,20 @@ public class GoToController( }); } - var resp = await BuildResponseAsync("RecCenter"); + // Land in the club's clubhouse room when it has one set; otherwise + // fall back to the Rec Center. Either way the response carries the + // ClubId so the watch's club UI knows which club it matchmade for. + string roomName = "RecCenter"; + if (club.ClubhouseRoomId is long clubhouseId && clubhouseId > 0) + { + var name = await db.Rooms.AsNoTracking() + .Where(r => r.Id == clubhouseId) + .Select(r => r.Name) + .FirstOrDefaultAsync(); + if (!string.IsNullOrWhiteSpace(name)) roomName = name; + } + + var resp = await BuildResponseAsync(roomName); resp.RoomInstance!.ClubId = club.Id; await RecordResponseAsync(resp); return Ok(resp); @@ -438,7 +469,12 @@ public class GoToController( [HttpPost("/goto/code")] [HttpPost("/goto/code/{code}")] - public async Task> GoToCode(string? code = null) + // 2023 sends the room code as the second segment behind the room id + // (RecNet.Matchmaking CPINEGCODMD → "matchmake/code/{roomId}/{code}"). + // The code alone resolves the private instance, so the roomId is just + // routing context we don't need — resolve by code either way. + [HttpPost("/matchmake/code/{roomId:long}/{code}")] + public async Task> GoToCode(string? code = null, long roomId = 0) { code ??= Request.HasFormContentType ? (await Request.ReadFormAsync())["code"].FirstOrDefault() @@ -807,6 +843,9 @@ public class GoToController( /// or on the invite list per ; /// otherwise we refuse with errorCode=9 (RoomNotJoinable). [HttpPost("/goto/player/{playerId:long}")] + // 2023 rename goto/player → matchmake/player (RecNet.Matchmaking + // FJAFOJHHKPO). Follow a friend into their current room/instance. + [HttpPost("/matchmake/player/{playerId:long}")] public async Task> GoToPlayer(long playerId) { var theirRoom = presence.GetRoom(playerId);