feat: Add PlayerReputation handling and WebSocket handshake for 2018 client

This commit is contained in:
Alexa
2026-06-08 01:27:08 +03:00
parent 2eba82b342
commit da596a2692
3 changed files with 36 additions and 0 deletions
+24
View File
@@ -102,6 +102,30 @@ public class Player2018
[JsonPropertyName("AvoidJuniors")] [JsonPropertyName("AvoidJuniors")]
public bool AvoidJuniors { get; set; } public bool AvoidJuniors { get; set; }
// REQUIRED to be non-null: the post-login analytics Identify dereferences
// localPlayer.PlayerReputation.<field> (RecNet.cs ~NECCOFKHCAA) — a null
// here NREs the boot coroutine and the client hangs on splash_screen.
[JsonPropertyName("PlayerReputation")]
public PlayerReputation2018 PlayerReputation { get; set; } = new();
}
/// <summary>
/// 2018 PlayerReputation (RecNet.HOLNFPNKNGB.Deserialize) — all int. Keys exact
/// incl. the "Noteriety" misspelling. Must be present (non-null) on the player.
/// </summary>
public class PlayerReputation2018
{
[JsonPropertyName("Noteriety")] public int Noteriety { get; set; }
[JsonPropertyName("CheerGeneral")] public int CheerGeneral { get; set; }
[JsonPropertyName("CheerHelpful")] public int CheerHelpful { get; set; }
[JsonPropertyName("CheerGreatHost")] public int CheerGreatHost { get; set; }
[JsonPropertyName("CheerSportsman")] public int CheerSportsman { get; set; }
[JsonPropertyName("CheerCreative")] public int CheerCreative { get; set; }
[JsonPropertyName("CheerCredit")] public int CheerCredit { get; set; }
[JsonPropertyName("SubscriberCount")] public int SubscriberCount { get; set; }
[JsonPropertyName("SubscribedCount")] public int SubscribedCount { get; set; }
[JsonPropertyName("SelectedCheer")] public int SelectedCheer { get; set; }
} }
/// <summary> /// <summary>
@@ -26,5 +26,6 @@ public static class Player2018Mapper
ProfileImageName = p.ProfileImageName ?? string.Empty, ProfileImageName = p.ProfileImageName ?? string.Empty,
JuniorProfile = p.IsJunior, JuniorProfile = p.IsJunior,
HasBirthday = p.Birthday.HasValue, HasBirthday = p.Birthday.HasValue,
PlayerReputation = new PlayerReputation2018 { Noteriety = p.Reputation },
}; };
} }
@@ -37,6 +37,17 @@ public class NotificationController(
using var ws = await HttpContext.WebSockets.AcceptWebSocketAsync(); using var ws = await HttpContext.WebSockets.AcceptWebSocketAsync();
log.LogInformation("[notify] 2018 client opened notification WebSocket"); log.LogInformation("[notify] 2018 client opened notification WebSocket");
// HANDSHAKE: on connect the 2018 client enters PerformingHandshake and
// blocks ("Connecting to RecNet") until the SERVER sends a handshake
// message — any valid JSON object marks the channel Connected
// (RecNet.cs:49616-49620, JFBBMEIDBNA just needs dict != null). Without
// it both sides wait for each other ~35s, then the client aborts the
// channel and shows "Failed to connect to RecNet". Send {} immediately.
var handshake = System.Text.Encoding.UTF8.GetBytes("{}");
await ws.SendAsync(handshake, WebSocketMessageType.Text, endOfMessage: true,
HttpContext.RequestAborted);
var buf = new byte[4096]; var buf = new byte[4096];
try try
{ {