// ───────────────────────────────────────────────────────────────────── // AuthValuesInjector — PARKED 2026-05-28 (december-2020-12-18) // ───────────────────────────────────────────────────────────────────── // // Filename suffix is .cs.attic on purpose so the .NET SDK's globber // doesn't pick it up. Don't rename to .cs unless you're putting the // feature back. // // What this used to do // -------------------- // Built a Photon AuthenticationValues with `userid` + `LoginLock` // custom-auth parameters from the in-game RecNet account state. The // server's /photon/customauth controller (PhotonCustomAuthController.cs) // validated those parameters against the player's /player/login lock. // // Status in this branch // --------------------- // The method existed but was **never actually wired to a harmony // hook** in this branch's OnLateInitializeMelon. December's Photon // AuthValues path goes through PhotonPatches.LogAuthValues_Postfix // (postfix on PUNNetworkManager.EMGGKBALALH), which only logs — it // doesn't inject. So this code was effectively dead in december. // // Removed because: // 1. Photon Cloud is currently configured with Custom Authentication // disabled at the AppId level (anonymous connects allowed), so // no server-side auth probe. // 2. Even when the watch did send AuthValues, the upstream Cloudflare // / Localtunnel layer 403'd Photon's auth probe before our // controller could decide anything. // // How to put it back // ------------------ // 1. Rename this file to AuthValuesInjector.cs (drop the .attic suffix). // 2. Reinstate the `InjectAuthValues` field on Mod.Cfg + the LoadConfig // line. // 3. Wire the harmony hook in OnLateInitializeMelon. The march branch's // pick of NetworkingPeer.CallAuthenticate doesn't fire in IL2CPP, so // consider hooking PUNNetworkManager.EMGGKBALALH postfix instead and // mutating its returned AuthValues in place — that's the mangled // method december verifies fires (LogAuthValues_Postfix already // proves this entry point works). // 4. Put `"InjectAuthValues": true` back in dorknet-clientmod.json.template. // 5. If you also want the server to enforce the lock, set // Photon__RequireCustomAuth=true on the server. using System.Reflection; namespace DorkNet.ClientMod.Attic; internal static class AuthValuesInjector { public static void AuthValuesInjector_Prefix(object __instance) { if (!Mod.Cfg.InjectAuthValues) return; try { // Photon's AuthValues live on PhotonNetwork.AuthValues. The 2020 // watch never sets them itself, so we set them right before // NetworkingPeer.CallAuthenticate forwards to the wire op. The // userid is RecNet.Core.LocalAccountId, the LoginLock comes // from PlayerPrefs key LoginLockTokenV2. // RecNet.Core.LocalAccountId is declared `int` in the game // build (dump.cs:573622 → 0x30 backing field). Convert via // Convert.ToInt64 so boxing as int still gives us the long we // need below; a direct (long) cast on boxed int would throw. var coreType = FindGameType("RecNet.Core"); var accountIdObj = coreType?.GetProperty("LocalAccountId", BindingFlags.Public | BindingFlags.Static)?.GetValue(null); var accountId = accountIdObj is null ? 0L : Convert.ToInt64(accountIdObj); if (accountId <= 0) { Mod.Log.Msg("[auth-injector] skip — no LocalAccountId yet (pre-login Photon ping)"); return; } var photonNetwork = FindGameType("PhotonNetwork"); var avProp = photonNetwork?.GetProperty("AuthValues", BindingFlags.Public | BindingFlags.Static); var existing = avProp?.GetValue(null); if (existing is not null) { var userIdProp = existing.GetType().GetProperty("UserId"); var existingUserId = userIdProp?.GetValue(existing) as string; if (!string.IsNullOrEmpty(existingUserId)) return; } var token = GetCurrentLockToken(); var authValuesType = FindGameType("ExitGames.Client.Photon.LoadBalancing.AuthenticationValues"); if (authValuesType is null) { Mod.Log.Warning("[auth-injector] AuthenticationValues type not found"); return; } var av = Activator.CreateInstance(authValuesType); var authTypeProp = authValuesType.GetProperty("AuthType"); if (authTypeProp is not null) { var customAuthValue = authTypeProp.PropertyType.IsEnum ? Enum.ToObject(authTypeProp.PropertyType, 0) : Convert.ChangeType(0, authTypeProp.PropertyType); authTypeProp.SetValue(av, customAuthValue); } authValuesType.GetProperty("UserId")?.SetValue(av, accountId.ToString()); var addParam = authValuesType.GetMethod("AddAuthParameter", new[] { typeof(string), typeof(string) }); addParam?.Invoke(av, new object[] { "userid", accountId.ToString() }); addParam?.Invoke(av, new object[] { "LoginLock", token ?? string.Empty }); avProp?.SetValue(null, av); Mod.Log.Msg($"[auth-injector] set AuthValues userid={accountId} LoginLock={(string.IsNullOrEmpty(token) ? "" : "")}"); } catch (Exception ex) { Mod.Log.Warning($"[auth-injector] failed: {ex.Message}"); } } private static string? GetCurrentLockToken() { try { var playerPrefs = FindGameType("UnityEngine.PlayerPrefs"); var getString = playerPrefs?.GetMethod("GetString", new[] { typeof(string), typeof(string) }); var raw = (string?)getString?.Invoke(null, new object[] { "LoginLockTokenV2", string.Empty }); if (string.IsNullOrEmpty(raw)) return null; const string key = "\"LockToken\":\""; var ix = raw.IndexOf(key, StringComparison.Ordinal); if (ix < 0) return null; ix += key.Length; var end = raw.IndexOf('"', ix); return end < 0 ? null : raw[ix..end]; } catch { return null; } } // FindGameType in the live Mod.cs is a delegate to Mod.ResolveType. // Use that on restore; this is just a placeholder to keep the file // compilable if someone untreats it without re-checking the wiring. private static Type? FindGameType(string name) => null; }