Import march-2020-03-10 from private repo (scrubbed 2026-05-27)

Targets Rec Room 2020.03.10 client (also serves 2020.03.06).

Re-import after adding:
- DorkNet.ClientMod/dorknet-clientmod.json.template — config schema
  the launcher's manifest-driven patcher renders into UserData/.
- tools/build-patcher-zip.ps1 — produces dorknet-clientpatch-{key}.zip
  for v1-march-* Release uploads.
- .github/workflows/release.yml — on tag push publishes both
  dorknet-server-march_2020_03_10-win-x64.zip and
  dorknet-clientpatch-march_2020_03_10.zip as Release assets.

Same scrub rules as december; tools/build-patcher-zip.ps1 added to
the maintainer-side allow-list so it survives.
This commit is contained in:
Alexa
2026-05-27 01:06:12 +03:00
commit d471b418b9
389 changed files with 104767 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
using System.Text.Json.Serialization;
namespace DorkNet.Models.Auth;
/// <summary>
/// Login.CachedLogin — dump.cs:578388, Deserialize at RVA 0x1447BD0.
///
/// Verified by disassembly: reads platform, platformId, accountId,
/// lastLoginTime (DateTime), requirePassword (default false). The Account
/// field on the C# class is NOT read from the JSON dict — it is set via
/// SetAccount(Account) after the fact, so the JSON shape doesn't need it.
///
/// Returned by:
/// GET auth.rec.net/cachedlogin/forplatformid/{platform}/{platformId}
/// GET api.rec.net/api/platformlogin/cached
/// GET accounts.rec.net/account/v1/savedlogins
/// as a List&lt;CachedLogin&gt;. Empty list = no remembered accounts.
/// </summary>
public class CachedLogin
{
// PlatformManager.PlatformType (int)
[JsonPropertyName("platform")]
public int Platform { get; set; }
[JsonPropertyName("platformId")]
public string PlatformId { get; set; } = string.Empty;
[JsonPropertyName("accountId")]
public int AccountId { get; set; }
[JsonPropertyName("lastLoginTime")]
public DateTime LastLoginTime { get; set; } = DateTime.UtcNow;
[JsonPropertyName("requirePassword")]
public bool RequirePassword { get; set; } = false;
}
+9
View File
@@ -0,0 +1,9 @@
namespace DorkNet.Models.Auth;
public class LoginRequest
{
public string? Name { get; set; }
public long BuildTimestamp { get; set; }
public int Platform { get; set; }
public string? DeviceId { get; set; }
}
+39
View File
@@ -0,0 +1,39 @@
using System.Text.Json.Serialization;
namespace DorkNet.Models.Auth;
/// <summary>
/// Login.LoginResponse — dump.cs:578473 (private nested class),
/// Deserialize at RVA 0x1447D10.
///
/// Verified by disassembly: this is a standard OAuth 2.0 RFC 6749
/// password-grant token response. The Deserialize body has two branches:
///
/// error = dict["error"];
/// if (string.IsNullOrEmpty(error)) {
/// this.AccessToken = dict["access_token"];
/// this.RefreshToken = dict["refresh_token"];
/// } else {
/// this.ErrorDescription = dict["error_description"];
/// }
///
/// Returned by api/platformlogin/v5 (the 2020 build's login endpoint)
/// and api/platformlogin/refresh.
///
/// All four keys are snake_case, NOT PascalCase. This is the standard
/// OAuth response format — Rec Room conforms to RFC 6749 here.
/// </summary>
public class LoginResponse
{
[JsonPropertyName("error")]
public string? Error { get; set; }
[JsonPropertyName("error_description")]
public string? ErrorDescription { get; set; }
[JsonPropertyName("access_token")]
public string AccessToken { get; set; } = string.Empty;
[JsonPropertyName("refresh_token")]
public string RefreshToken { get; set; } = string.Empty;
}
+122
View File
@@ -0,0 +1,122 @@
using System.Text.Json.Serialization;
namespace DorkNet.Models.Auth;
// =====================================================================
// JSON keys verified by disassembling each .Deserialize method body
// directly from GameAssembly.dll. Each key here is the literal string
// passed to Util.GetKey / Util.GetKeyOrDefault inside the client. Util.GetKey
// uses Dictionary<string,object>.TryGetValue, which is case-sensitive — so
// the keys MUST match exactly.
//
// Convention summary (it's mixed):
// RecNetResult lowercase "success", "error"
// Account camelCase "accountId", "displayName", "isJunior"…
// SelfAccount camelCase "email", "birthday", "juniorState"…
// LoginResponse snake_case (OAuth) "access_token", "error_description"…
// RefreshLoginResp. lowercase "token"
// CachedLogin camelCase "platformId", "lastLoginTime"…
// CreateAccountResp uses "value" as the wrapper key for the Account dict
//
// NOTE: TreatAsJunior on the C# side maps to JSON key "isJunior" — they
// don't match. Account.Deserialize reads dict["isJunior"], not dict["treatAsJunior"].
// =====================================================================
/// <summary>
/// RecNet.RecNetResult — dump.cs:584325, Deserialize at RVA 0x1146340.
/// Reads "success" (required) and "error" (optional).
/// </summary>
public class RecNetResult
{
[JsonPropertyName("success")]
public bool Success { get; set; } = true;
[JsonPropertyName("error")]
public string? Error { get; set; }
}
/// <summary>
/// RecNet.Account — dump.cs:567684, Deserialize at RVA 0xF9DE50.
/// JSON keys verified in the current 2020 IL2CPP build. Account.Deserialize
/// reads profileImage.
/// Other keys: accountId, username, displayName, isJunior, platforms. The C# Account class also has
/// RawUsername and HasBirthday properties but Deserialize does not read
/// them from JSON (they're set via MakeNameAdhereToPlatformRequirements
/// or other flows). We expose them with reasonable camelCase keys for
/// other endpoints that may consume them.
/// </summary>
public class RecNetAccount
{
private string profileImage = string.Empty;
[JsonPropertyName("accountId")]
public int AccountId { get; set; }
[JsonPropertyName("username")]
public string Username { get; set; } = string.Empty;
[JsonPropertyName("displayName")]
public string DisplayName { get; set; } = string.Empty;
[JsonPropertyName("profileImage")]
public string ProfileImage
{
get => profileImage;
set => profileImage = value ?? string.Empty;
}
// Account.Deserialize reads "isJunior" — NOT "treatAsJunior".
// The C# property name and the JSON key name differ.
[JsonPropertyName("isJunior")]
public bool TreatAsJunior { get; set; } = false;
// PlatformManager.PlatformMask: Steam=1, Oculus=2, etc.
[JsonPropertyName("platforms")]
public int Platforms { get; set; } = 1;
// Not in Account.Deserialize body but kept for forward-compat.
[JsonPropertyName("rawUsername")]
public string RawUsername { get; set; } = string.Empty;
// Likewise.
[JsonPropertyName("hasBirthday")]
public bool HasBirthday { get; set; } = true;
}
/// <summary>
/// RecNet.SelfAccount : Account — dump.cs:567797, Deserialize at RVA 0xAF4FB0.
/// Calls base Account.Deserialize, then reads:
/// email, phone, birthday, juniorState, parentAccountId.
/// </summary>
public class RecNetSelfAccount : RecNetAccount
{
[JsonPropertyName("email")]
public string Email { get; set; } = string.Empty;
[JsonPropertyName("phone")]
public string Phone { get; set; } = string.Empty;
[JsonPropertyName("birthday")]
public DateTime? Birthday { get; set; } = new DateTime(2000, 1, 1);
// RecNet.JuniorState enum (int)
[JsonPropertyName("juniorState")]
public int JuniorState { get; set; } = 0;
[JsonPropertyName("parentAccountId")]
public int? ParentAccountId { get; set; } = null;
}
/// <summary>
/// Accounts.CreateAccountResponse : RecNetResult — dump.cs:568167,
/// Deserialize at RVA 0xFAA200.
/// Calls base RecNetResult.Deserialize (reads "success", "error"),
/// then reads the Account from dict["value"] — NOT dict["account"].
/// Verified by disassembly: the only string literal after the base
/// call is "value", passed to Util.GetObjectKey&lt;object&gt;.
/// </summary>
public class CreateAccountResponse : RecNetResult
{
[JsonPropertyName("value")]
public RecNetAccount Account { get; set; } = new();
}
@@ -0,0 +1,17 @@
using System.Text.Json.Serialization;
namespace DorkNet.Models.Auth;
/// <summary>
/// Login.RefreshLoginResponse — dump.cs:578533 (private nested class),
/// Deserialize at RVA 0x145B1E0.
///
/// Verified by disassembly: reads exactly one key — lowercase "token" —
/// via Util.GetKey&lt;string&gt;. Single-field response from
/// POST api/platformlogin/refresh: the new (rotated) bearer access token.
/// </summary>
public class RefreshLoginResponse
{
[JsonPropertyName("token")]
public string Token { get; set; } = string.Empty;
}