mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-26 07:28:45 -07:00
Enhance GoToRoom method: add JoinMode handling for instance creation and improve player movement tracking
This commit is contained in:
@@ -42,7 +42,22 @@ public class GoToController(
|
|||||||
[HttpPost("/goto/room/{roomName}")]
|
[HttpPost("/goto/room/{roomName}")]
|
||||||
public async Task<ActionResult<MatchmakingResponseDto>> GoToRoom(
|
public async Task<ActionResult<MatchmakingResponseDto>> GoToRoom(
|
||||||
string roomName,
|
string roomName,
|
||||||
[FromForm(Name = "CreatePrivateInstance")] bool createPrivateInstance = false)
|
[FromForm(Name = "CreatePrivateInstance")] bool createPrivateInstance = false,
|
||||||
|
// The December watch signals "make a new instance" via the
|
||||||
|
// JoinMode form field, NOT CreatePrivateInstance (which it never
|
||||||
|
// sends — verified on the wire: a "New Private Instance" request
|
||||||
|
// carries `JoinMode=2` and no CreatePrivateInstance key). Maps to
|
||||||
|
// RecNet.Matchmaking's GIJOGKJCEKG enum:
|
||||||
|
// 0 = PublicMatchmaking (join the existing shared instance)
|
||||||
|
// 1 = PublicNewInstance (fork a fresh PUBLIC instance)
|
||||||
|
// 2 = PrivateNewInstance (fork a fresh, invite-gated instance)
|
||||||
|
// Without honouring this, JoinMode 1/2 fell through to the
|
||||||
|
// default branch and returned the SAME roomInstanceId+photonRoomId
|
||||||
|
// the player was already standing in. Per the sub-room note below,
|
||||||
|
// identical instance id + photon room => the watch's transition
|
||||||
|
// state machine never fires and "Going to <room>" hangs forever
|
||||||
|
// (the player softlocks; the join-watchdog then times out at 30s).
|
||||||
|
[FromForm(Name = "JoinMode")] int joinMode = 0)
|
||||||
{
|
{
|
||||||
// Unknown room name shouldn't silently drop the player into a
|
// Unknown room name shouldn't silently drop the player into a
|
||||||
// synth'd dorm scene any more — that masked typo bug reports
|
// synth'd dorm scene any more — that masked typo bug reports
|
||||||
@@ -688,23 +703,28 @@ public class GoToController(
|
|||||||
private async Task RecordResponseAsync(MatchmakingResponseDto response)
|
private async Task RecordResponseAsync(MatchmakingResponseDto response)
|
||||||
{
|
{
|
||||||
if (this.CurrentPlayerId() is not long playerId) return;
|
if (this.CurrentPlayerId() is not long playerId) return;
|
||||||
if (response.RoomInstance is null) return;
|
var room = response.RoomInstance;
|
||||||
|
if (room is null) return;
|
||||||
|
|
||||||
presence.SetRoom(playerId, response.RoomInstance);
|
var previousRoom = presence.GetRoom(playerId);
|
||||||
joinTimeouts.MarkPending(playerId, response.RoomInstance);
|
var additionalPlayerIds = await GetEligibleAdditionalPlayerIdsAsync(playerId, previousRoom);
|
||||||
|
await RecordPlayerMoveAsync(playerId, room);
|
||||||
|
|
||||||
// Real visit tracking: upsert the per-(room, player) row so
|
if (additionalPlayerIds.Count == 0)
|
||||||
// we get distinct VisitorCount (unique players ever) AND the
|
return;
|
||||||
// gross VisitCount (every join, including rejoins). Skip the
|
|
||||||
// canonical dorm (RoomId=1) so per-day boots don't skew it.
|
|
||||||
if (response.RoomInstance.RoomId != 1)
|
|
||||||
await RecordVisitAsync(playerId, response.RoomInstance.RoomId);
|
|
||||||
|
|
||||||
// Push a presence-update notification to every accepted-friend
|
logger.LogInformation(
|
||||||
// so their watch's "friends" list shows the new room. Cheap
|
"[goto-party] player={PlayerId} moving additional players [{AdditionalPlayerIds}] to instance={InstanceId}",
|
||||||
// query — Relationships is small, and we only fan out to the
|
playerId, string.Join(",", additionalPlayerIds), room.RoomInstanceId);
|
||||||
// actually-connected friends.
|
|
||||||
await NotifyFriendsOfMoveAsync(playerId, response.RoomInstance);
|
foreach (var additionalPlayerId in additionalPlayerIds)
|
||||||
|
{
|
||||||
|
var roomForPartyMember = CloneRoomInstance(room);
|
||||||
|
if (roomForPartyMember.IsPrivate)
|
||||||
|
await privateInstances.InviteAsync(roomForPartyMember.RoomInstanceId, additionalPlayerId);
|
||||||
|
|
||||||
|
await RecordPlayerMoveAsync(additionalPlayerId, roomForPartyMember);
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: we INTENTIONALLY do NOT self-push SubscriptionUpdateRoom
|
// NOTE: we INTENTIONALLY do NOT self-push SubscriptionUpdateRoom
|
||||||
// from /goto. The watch's Rooms.OnSubscriptionUpdateRoom
|
// from /goto. The watch's Rooms.OnSubscriptionUpdateRoom
|
||||||
@@ -724,6 +744,77 @@ public class GoToController(
|
|||||||
// first one for any (room, subRoom).
|
// first one for any (room, subRoom).
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task RecordPlayerMoveAsync(long playerId, RoomInstanceDto room)
|
||||||
|
{
|
||||||
|
presence.SetRoom(playerId, room);
|
||||||
|
joinTimeouts.MarkPending(playerId, room);
|
||||||
|
|
||||||
|
// Real visit tracking: upsert the per-(room, player) row so
|
||||||
|
// we get distinct VisitorCount (unique players ever) AND the
|
||||||
|
// gross VisitCount (every join, including rejoins). Skip the
|
||||||
|
// canonical dorm (RoomId=1) so per-day boots don't skew it.
|
||||||
|
if (room.RoomId != 1)
|
||||||
|
await RecordVisitAsync(playerId, room.RoomId);
|
||||||
|
|
||||||
|
// Push a presence-update notification to every accepted-friend
|
||||||
|
// so their watch's "friends" list shows the new room. Cheap
|
||||||
|
// query — Relationships is small, and we only fan out to the
|
||||||
|
// actually-connected friends.
|
||||||
|
await NotifyFriendsOfMoveAsync(playerId, room);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IReadOnlyList<long>> GetEligibleAdditionalPlayerIdsAsync(
|
||||||
|
long callerPlayerId,
|
||||||
|
RoomInstanceDto? callerPreviousRoom)
|
||||||
|
{
|
||||||
|
if (!Request.HasFormContentType || callerPreviousRoom is null)
|
||||||
|
return Array.Empty<long>();
|
||||||
|
|
||||||
|
var form = await Request.ReadFormAsync();
|
||||||
|
var rawValues = form["AdditionalPlayerIds"]
|
||||||
|
.Concat(form["additionalPlayerIds"])
|
||||||
|
.Concat(form["additionalPlayerIds[]"])
|
||||||
|
.ToArray();
|
||||||
|
if (rawValues.Length == 0)
|
||||||
|
return Array.Empty<long>();
|
||||||
|
|
||||||
|
var ids = rawValues
|
||||||
|
.SelectMany(v => (v ?? string.Empty).Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
|
||||||
|
.Select(v => long.TryParse(v, out var id) ? id : 0)
|
||||||
|
.Where(id => id > 0 && id != callerPlayerId)
|
||||||
|
.Distinct()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (ids.Count == 0)
|
||||||
|
return Array.Empty<long>();
|
||||||
|
|
||||||
|
var eligible = new List<long>(ids.Count);
|
||||||
|
foreach (var id in ids)
|
||||||
|
{
|
||||||
|
var current = presence.GetRoom(id);
|
||||||
|
if (current is not null && IsSameLiveInstance(current, callerPreviousRoom))
|
||||||
|
{
|
||||||
|
eligible.Add(id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.LogWarning(
|
||||||
|
"[goto-party] ignoring AdditionalPlayerIds entry caller={CallerId} additional={AdditionalId} callerPrevious={CallerInstance} additionalCurrent={AdditionalInstance}",
|
||||||
|
callerPlayerId, id, callerPreviousRoom.RoomInstanceId, current?.RoomInstanceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return eligible;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsSameLiveInstance(RoomInstanceDto a, RoomInstanceDto b)
|
||||||
|
{
|
||||||
|
if (a.RoomInstanceId == b.RoomInstanceId)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return !string.IsNullOrWhiteSpace(a.PhotonRoomId)
|
||||||
|
&& string.Equals(a.PhotonRoomId, b.PhotonRoomId, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Mutate the response so it represents a fresh invite-only
|
/// <summary>Mutate the response so it represents a fresh invite-only
|
||||||
/// match: forks the PhotonRoomId with a per-(player, nonce) suffix
|
/// match: forks the PhotonRoomId with a per-(player, nonce) suffix
|
||||||
/// so randos can't matchmake into it, sets isPrivate=true, and
|
/// so randos can't matchmake into it, sets isPrivate=true, and
|
||||||
|
|||||||
Reference in New Issue
Block a user