mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-25 23:18:44 -07:00
Fixed dorm room button
This commit is contained in:
@@ -81,3 +81,6 @@ ProficioVII-recovered/
|
|||||||
Recovered.zip
|
Recovered.zip
|
||||||
Recovered/
|
Recovered/
|
||||||
Recovered(*)/
|
Recovered(*)/
|
||||||
|
|
||||||
|
|
||||||
|
local-releases/**
|
||||||
+241
-14
@@ -43,9 +43,11 @@ public class Mod : MelonMod
|
|||||||
private static readonly Dictionary<string, Type> ResolvedTypeCache = new();
|
private static readonly Dictionary<string, Type> ResolvedTypeCache = new();
|
||||||
private readonly HashSet<string> _diagnosticPatchLabels = new();
|
private readonly HashSet<string> _diagnosticPatchLabels = new();
|
||||||
private bool _networkPatchesRegistered;
|
private bool _networkPatchesRegistered;
|
||||||
|
private bool _joinTracePatchesRegistered;
|
||||||
private bool _diagnosticCoreRegistered;
|
private bool _diagnosticCoreRegistered;
|
||||||
private bool _diagnosticGameComplete;
|
private bool _diagnosticGameComplete;
|
||||||
private int _diagnosticRetryFrame;
|
private int _diagnosticRetryFrame;
|
||||||
|
private bool _firstUpdateLogged;
|
||||||
|
|
||||||
// Loader-agnostic config — same .Value-shaped accessor the patches
|
// Loader-agnostic config — same .Value-shaped accessor the patches
|
||||||
// would see in the BepInEx port, just backed by a plain JSON file
|
// would see in the BepInEx port, just backed by a plain JSON file
|
||||||
@@ -217,10 +219,146 @@ public class Mod : MelonMod
|
|||||||
args: new[] { typeof(string) },
|
args: new[] { typeof(string) },
|
||||||
prefix: nameof(ChatPatches.RpcChatEmote_Prefix));
|
prefix: nameof(ChatPatches.RpcChatEmote_Prefix));
|
||||||
|
|
||||||
|
RegisterJoinTracePatches();
|
||||||
|
|
||||||
RegisterDiagnostics();
|
RegisterDiagnostics();
|
||||||
Log.Msg("=== Client patches registered ===");
|
Log.Msg("=== Client patches registered ===");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Room-join + watch-button trace ────────────────────────────────
|
||||||
|
// Traces the full "press the Dorm Room button → join the room" chain
|
||||||
|
// so a stuck join (e.g. the watch's Dorm Room button doing nothing)
|
||||||
|
// is debuggable from the log alone. The chain, verified against the
|
||||||
|
// 2020.12.18 IL2CPP dump:
|
||||||
|
//
|
||||||
|
// AGUI.StackedUI.HomeScreenFlow.Button_DormRoom() (button)
|
||||||
|
// → SessionManager.RunJoinRoom("DormRoom", …) (join entry)
|
||||||
|
// → OJMCBOKJFOF.NHBPIIGDAJP(name) → GET /goto/room/{name}
|
||||||
|
// rejects with "No such room" (OJMCBOKJFOF.txt:958-1019)
|
||||||
|
// → .Then(SessionManager.RunJoinRoom(RoomInstance, …))
|
||||||
|
// → on reject: SessionManager+<>c.<RunJoinRoom>b__165_1(error)
|
||||||
|
// → NotificationManager.Play("An error occured", "…happyfox…")
|
||||||
|
// (the "contact recroom.happyfox.com" toast)
|
||||||
|
//
|
||||||
|
// We can't express the obfuscated RunJoinRoom param types as a Type[]
|
||||||
|
// for AccessTools, so every RunJoinRoom overload + every /goto overload
|
||||||
|
// is patched by iterating declared methods by name, and the prefixes
|
||||||
|
// read arguments generically via Harmony's `object[] __args`.
|
||||||
|
private void RegisterJoinTracePatches()
|
||||||
|
{
|
||||||
|
if (_joinTracePatchesRegistered) return;
|
||||||
|
Log.Msg("=== Registering room-join trace ===");
|
||||||
|
|
||||||
|
// Watch "Dorm Room" button. Void, no params, unique name —
|
||||||
|
// HomeScreenFlow.txt:4791, calls RunJoinRoom("DormRoom",…) at :4848.
|
||||||
|
TryPatchByName("AGUI.StackedUI.HomeScreenFlow", "Button_DormRoom",
|
||||||
|
args: Type.EmptyTypes,
|
||||||
|
prefix: nameof(JoinPatches.ButtonDormRoom_Prefix));
|
||||||
|
|
||||||
|
// Every SessionManager.RunJoinRoom overload (string-name entry +
|
||||||
|
// the RoomInstance inner legs). Logs the method + its arguments.
|
||||||
|
PatchAllOverloads("SessionManager", "RunJoinRoom",
|
||||||
|
nameof(JoinPatches.RunJoinRoom_Prefix));
|
||||||
|
|
||||||
|
// The /goto/room/{name|id} promise builders — both the string
|
||||||
|
// (room-name) and Int64 (room-id) overloads of NHBPIIGDAJP.
|
||||||
|
PatchAllOverloads("OJMCBOKJFOF", "NHBPIIGDAJP",
|
||||||
|
nameof(JoinPatches.Goto_Prefix));
|
||||||
|
|
||||||
|
// The RunJoinRoom promise-chain callbacks compiled onto the
|
||||||
|
// SessionManager <>c display class — most importantly the reject
|
||||||
|
// handler b__165_1(string error) that raises the happyfox toast.
|
||||||
|
// Match by the (de-mangled) substring "RunJoinRoom" + the lambda
|
||||||
|
// marker "b__" so we hit the callbacks but not the RunJoinRoom
|
||||||
|
// state-machine MoveNext.
|
||||||
|
PatchNestedLambdas("SessionManager", "RunJoinRoom",
|
||||||
|
nameof(JoinPatches.JoinCallback_Prefix));
|
||||||
|
|
||||||
|
// Additionally capture the REJECT REASON string on b__165_1 (the
|
||||||
|
// happyfox-toast handler). Matches only that one lambda; it ends
|
||||||
|
// up with two prefixes (flow + error string), which is harmless.
|
||||||
|
PatchNestedLambdas("SessionManager", "RunJoinRoom_b__165_1",
|
||||||
|
nameof(JoinPatches.JoinError_Prefix));
|
||||||
|
|
||||||
|
_joinTracePatchesRegistered = true;
|
||||||
|
Log.Msg("=== Room-join trace registered ===");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch every declared method on `typeName` named `methodName`,
|
||||||
|
// regardless of signature, with a single prefix. Used for obfuscated
|
||||||
|
// overload sets whose param types we can't name in a Type[].
|
||||||
|
private bool PatchAllOverloads(string typeName, string methodName, string prefix, bool logMiss = true)
|
||||||
|
{
|
||||||
|
var type = ResolveType(typeName);
|
||||||
|
if (type is null)
|
||||||
|
{
|
||||||
|
if (logMiss) Log.Warning($"[patch-miss] {typeName}.{methodName}(*): type not found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var prefixMethod = GetPatchMethod(prefix);
|
||||||
|
var patched = 0;
|
||||||
|
foreach (var method in AccessTools.GetDeclaredMethods(type))
|
||||||
|
{
|
||||||
|
if (method.Name != methodName) continue;
|
||||||
|
if (method.IsAbstract || method.ContainsGenericParameters) continue;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
HarmonyInstance.Patch(method, prefix: new HarmonyMethod(prefixMethod));
|
||||||
|
patched++;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Warning($"[patch-fail] {typeName}.{methodName} overload: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (patched == 0)
|
||||||
|
{
|
||||||
|
if (logMiss) Log.Warning($"[patch-miss] {typeName}.{methodName}(*): no overloads matched");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Log.Msg($"[patch-ok] {typeName}.{methodName}(*) x{patched}");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch compiler-generated lambda methods (names containing "b__")
|
||||||
|
// on the nested display classes of `typeName` whose (de-mangled) name
|
||||||
|
// also contains `nameContains`. Il2CppInterop mangles the angle
|
||||||
|
// brackets of `<RunJoinRoom>b__165_1` to `_003C…_003E` but leaves the
|
||||||
|
// method name itself intact, so a plain substring match still works.
|
||||||
|
private bool PatchNestedLambdas(string typeName, string nameContains, string prefix, bool logMiss = true)
|
||||||
|
{
|
||||||
|
var type = ResolveType(typeName);
|
||||||
|
if (type is null)
|
||||||
|
{
|
||||||
|
if (logMiss) Log.Warning($"[patch-miss] {typeName}+<lambdas>({nameContains}): type not found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var prefixMethod = GetPatchMethod(prefix);
|
||||||
|
var patched = 0;
|
||||||
|
foreach (var nested in type.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic))
|
||||||
|
{
|
||||||
|
foreach (var method in AccessTools.GetDeclaredMethods(nested))
|
||||||
|
{
|
||||||
|
if (method.IsAbstract || method.ContainsGenericParameters) continue;
|
||||||
|
if (method.Name.IndexOf("b__", StringComparison.Ordinal) < 0) continue;
|
||||||
|
if (method.Name.IndexOf(nameContains, StringComparison.Ordinal) < 0) continue;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
HarmonyInstance.Patch(method, prefix: new HarmonyMethod(prefixMethod));
|
||||||
|
patched++;
|
||||||
|
Log.Msg($"[patch-ok] {typeName}+{nested.Name}.{method.Name}");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Warning($"[patch-fail] {typeName}+{nested.Name}.{method.Name}: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (patched == 0 && logMiss)
|
||||||
|
Log.Warning($"[patch-miss] {typeName}+<lambdas>({nameContains}): none matched");
|
||||||
|
return patched > 0;
|
||||||
|
}
|
||||||
|
|
||||||
private void RegisterNetworkPatches()
|
private void RegisterNetworkPatches()
|
||||||
{
|
{
|
||||||
if (_networkPatchesRegistered) return;
|
if (_networkPatchesRegistered) return;
|
||||||
@@ -274,6 +412,11 @@ public class Mod : MelonMod
|
|||||||
|
|
||||||
public override void OnUpdate()
|
public override void OnUpdate()
|
||||||
{
|
{
|
||||||
|
if (!_firstUpdateLogged)
|
||||||
|
{
|
||||||
|
_firstUpdateLogged = true;
|
||||||
|
Log.Msg("[lifecycle] first OnUpdate tick (Unity frame loop is live)");
|
||||||
|
}
|
||||||
if (_diagnosticGameComplete) return;
|
if (_diagnosticGameComplete) return;
|
||||||
if (_diagnosticRetryFrame++ > 3600) return;
|
if (_diagnosticRetryFrame++ > 3600) return;
|
||||||
if ((_diagnosticRetryFrame % 60) != 0) return;
|
if ((_diagnosticRetryFrame % 60) != 0) return;
|
||||||
@@ -470,7 +613,7 @@ public class Mod : MelonMod
|
|||||||
{
|
{
|
||||||
// Look in all three patch holder classes — small enough that a
|
// Look in all three patch holder classes — small enough that a
|
||||||
// linear scan is cheaper than per-class lookups.
|
// linear scan is cheaper than per-class lookups.
|
||||||
foreach (var holder in new[] { typeof(UriPatches), typeof(PhotonPatches), typeof(TlsPatches), typeof(DiagnosticPatches), typeof(SavePatches), typeof(ChatPatches) })
|
foreach (var holder in new[] { typeof(UriPatches), typeof(PhotonPatches), typeof(TlsPatches), typeof(DiagnosticPatches), typeof(SavePatches), typeof(ChatPatches), typeof(JoinPatches) })
|
||||||
{
|
{
|
||||||
var m = holder.GetMethod(name, BindingFlags.Public | BindingFlags.Static);
|
var m = holder.GetMethod(name, BindingFlags.Public | BindingFlags.Static);
|
||||||
if (m is not null) return m;
|
if (m is not null) return m;
|
||||||
@@ -519,15 +662,14 @@ public class Mod : MelonMod
|
|||||||
complete &= TryPatchDiagnosticByName("SessionManager", "TryApplicationQuit",
|
complete &= TryPatchDiagnosticByName("SessionManager", "TryApplicationQuit",
|
||||||
prefix: nameof(DiagnosticPatches.SessionManagerTryApplicationQuit_Prefix),
|
prefix: nameof(DiagnosticPatches.SessionManagerTryApplicationQuit_Prefix),
|
||||||
logMiss: logMisses);
|
logMiss: logMisses);
|
||||||
complete &= TryPatchDiagnosticByName("SteamManager", "Awake",
|
// SteamManager.Awake + SteamPlatformManager.*LoginInitialize were
|
||||||
prefix: nameof(DiagnosticPatches.NamedMethod_Prefix),
|
// pure-logging diagnostics that fired during Unity's very first
|
||||||
logMiss: logMisses);
|
// wake-up tick. On december (EAC build, IL2CPP wrapped Steam
|
||||||
complete &= TryPatchDiagnosticByName("SteamPlatformManager", "PreLoginInitialize",
|
// methods) MonoMod's lazy CompileMethodHook on the trampoline
|
||||||
prefix: nameof(DiagnosticPatches.NamedMethod_Prefix),
|
// crashes the CLR with 0x80131506 the instant Awake JITs. The
|
||||||
logMiss: logMisses);
|
// hooks gave us no behavioral leverage — only a `[call] …` log
|
||||||
complete &= TryPatchDiagnosticByName("SteamPlatformManager", "PostLoginInitialize",
|
// line — so they're disabled. If anyone needs Steam-side visibility
|
||||||
prefix: nameof(DiagnosticPatches.NamedMethod_Prefix),
|
// later, route it through the game's own log lines instead.
|
||||||
logMiss: logMisses);
|
|
||||||
return complete;
|
return complete;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -891,17 +1033,30 @@ internal static class PhotonPatches
|
|||||||
/// PUN 2.x, etc.).</summary>
|
/// PUN 2.x, etc.).</summary>
|
||||||
private static void DumpSettings(object obj, Type t, string indent)
|
private static void DumpSettings(object obj, Type t, string indent)
|
||||||
{
|
{
|
||||||
|
// DeclaredOnly so we don't walk Il2CppObjectBase's properties
|
||||||
|
// (ObjectClass, Pointer, WasCollected) — and, importantly, so
|
||||||
|
// we don't reflect over any property whose getter internally
|
||||||
|
// constructs a System.Uri. The Uri ctor is harmony-patched, so
|
||||||
|
// the FIRST call to a Uri-returning getter triggers MonoMod's
|
||||||
|
// lazy CompileMethodHook for the ctor, which fatal-CLRs
|
||||||
|
// (0x80131506) on this build. The override has already run by
|
||||||
|
// the time DumpSettings is called — the dump is diagnostic
|
||||||
|
// only, so it's safer to stay scoped to user-land properties.
|
||||||
|
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
foreach (var p in t.GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
foreach (var p in t.GetProperties(flags))
|
||||||
{
|
{
|
||||||
if (p.GetIndexParameters().Length > 0) continue;
|
if (p.GetIndexParameters().Length > 0) continue;
|
||||||
if (!p.CanRead) continue;
|
if (!p.CanRead) continue;
|
||||||
|
// Defensively skip any property whose declared return
|
||||||
|
// type is Uri (would still trigger ctor on .ToString()
|
||||||
|
// path even if we tried to read it).
|
||||||
|
if (p.PropertyType == typeof(Uri)) continue;
|
||||||
object? v;
|
object? v;
|
||||||
try { v = p.GetValue(obj); } catch { continue; }
|
try { v = p.GetValue(obj); } catch { continue; }
|
||||||
|
if (v is Uri) continue;
|
||||||
var str = v is null ? "<null>" : v.ToString();
|
var str = v is null ? "<null>" : v.ToString();
|
||||||
// Nested AppSettings — recurse one level so we capture
|
|
||||||
// PhotonServerSettings.AppSettings.{AppIdRealtime,Server,Port,NameServer,FixedRegion,AuthMode,Protocol,...}
|
|
||||||
if (v is not null && v.GetType().Name.IndexOf("AppSettings", StringComparison.Ordinal) >= 0 && indent.Length < 6)
|
if (v is not null && v.GetType().Name.IndexOf("AppSettings", StringComparison.Ordinal) >= 0 && indent.Length < 6)
|
||||||
{
|
{
|
||||||
Mod.Log.Msg($"{indent}{p.Name} (nested):");
|
Mod.Log.Msg($"{indent}{p.Name} (nested):");
|
||||||
@@ -910,10 +1065,12 @@ internal static class PhotonPatches
|
|||||||
}
|
}
|
||||||
Mod.Log.Msg($"{indent}{p.Name} = {str}");
|
Mod.Log.Msg($"{indent}{p.Name} = {str}");
|
||||||
}
|
}
|
||||||
foreach (var f in t.GetFields(BindingFlags.Public | BindingFlags.Instance))
|
foreach (var f in t.GetFields(flags))
|
||||||
{
|
{
|
||||||
|
if (f.FieldType == typeof(Uri)) continue;
|
||||||
object? v;
|
object? v;
|
||||||
try { v = f.GetValue(obj); } catch { continue; }
|
try { v = f.GetValue(obj); } catch { continue; }
|
||||||
|
if (v is Uri) continue;
|
||||||
var str = v is null ? "<null>" : v.ToString();
|
var str = v is null ? "<null>" : v.ToString();
|
||||||
if (v is not null && v.GetType().Name.IndexOf("AppSettings", StringComparison.Ordinal) >= 0 && indent.Length < 6)
|
if (v is not null && v.GetType().Name.IndexOf("AppSettings", StringComparison.Ordinal) >= 0 && indent.Length < 6)
|
||||||
{
|
{
|
||||||
@@ -1301,3 +1458,73 @@ internal static class ChatPatches
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Trace hooks for the watch's room-join flow. Registered by
|
||||||
|
// Mod.RegisterJoinTracePatches — see that method for the verified call
|
||||||
|
// chain. Everything here is log-only; no prefix returns false, so the
|
||||||
|
// game's own join logic runs unchanged.
|
||||||
|
internal static class JoinPatches
|
||||||
|
{
|
||||||
|
// AGUI.StackedUI.HomeScreenFlow.Button_DormRoom() — the watch's
|
||||||
|
// "Dorm Room" button. If this never fires, the button's UnityEvent
|
||||||
|
// binding is the problem, not the join chain.
|
||||||
|
public static void ButtonDormRoom_Prefix()
|
||||||
|
{
|
||||||
|
DiagnosticPatches.Write("[join-trace] >>> watch DORM ROOM button pressed (HomeScreenFlow.Button_DormRoom)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any SessionManager.RunJoinRoom overload. We DON'T read argument
|
||||||
|
// values here: Harmony's `object[] __args` forces a trampoline that
|
||||||
|
// boxes the obfuscated IL2CPP reference-type params (KMKPEOGJDFK,
|
||||||
|
// RoomInstance, …), and MonoMod fatal-CLRs (0x80131506) compiling it
|
||||||
|
// when the boot-time dorm auto-join first calls this method. Logging
|
||||||
|
// the overload's parameter *types* (pure reflection metadata, no value
|
||||||
|
// boxing) is enough to tell which overload fired — the entry overload
|
||||||
|
// (String,…) is the "DormRoom" press; the others are inner legs.
|
||||||
|
public static void RunJoinRoom_Prefix(MethodBase __originalMethod)
|
||||||
|
{
|
||||||
|
DiagnosticPatches.Write($"[join-trace] SessionManager.RunJoinRoom [{Sig(__originalMethod)}]");
|
||||||
|
}
|
||||||
|
|
||||||
|
// OJMCBOKJFOF.NHBPIIGDAJP(name|id) — the get-room-by-name / by-id
|
||||||
|
// resolver that feeds the join. __0 is the single first param: a room
|
||||||
|
// NAME string (String overload) or room ID (Int64 overload). Reading
|
||||||
|
// it via `object __0` is safe — it's a string/long, not one of the
|
||||||
|
// obfuscated ref-types that crashed `object[] __args`. This is the
|
||||||
|
// value we need to compare boot (works) vs button (fails).
|
||||||
|
public static void Goto_Prefix(MethodBase __originalMethod, object __0)
|
||||||
|
{
|
||||||
|
var arg = __0?.ToString() ?? "<null>";
|
||||||
|
DiagnosticPatches.Write($"[join-trace] /goto resolve {__originalMethod.DeclaringType?.Name}.{__originalMethod.Name}(\"{arg}\") [{Sig(__originalMethod)}]");
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunJoinRoom promise-chain callbacks (SessionManager <>c lambdas).
|
||||||
|
// b__165_1 is the reject handler that raises the "An error occured /
|
||||||
|
// contact recroom.happyfox.com" toast — so if THIS name appears, the
|
||||||
|
// join failed; the success legs (other b__*) firing means it advanced.
|
||||||
|
// (Same __args/0x80131506 constraint as above — name only.)
|
||||||
|
public static void JoinCallback_Prefix(MethodBase __originalMethod)
|
||||||
|
{
|
||||||
|
DiagnosticPatches.Write($"[join-trace] RunJoinRoom callback {__originalMethod.DeclaringType?.Name}.{__originalMethod.Name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dedicated capture for the reject handler b__165_1(string error) —
|
||||||
|
// the answer to "why did the join fail". Single string param, so
|
||||||
|
// `object __0` is safe (same as OnCustomAuthenticationFailed). This
|
||||||
|
// is the string that becomes the happyfox toast.
|
||||||
|
public static void JoinError_Prefix(object __0)
|
||||||
|
{
|
||||||
|
var err = __0?.ToString() ?? "<null>";
|
||||||
|
DiagnosticPatches.Write($"[join-trace] !!! RunJoinRoom REJECTED — error=\"{err}\" (raises the happyfox toast)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parameter-type list from reflection metadata only — never reads
|
||||||
|
// instance values, so it can't trigger the MonoMod trampoline crash.
|
||||||
|
private static string Sig(MethodBase m)
|
||||||
|
{
|
||||||
|
var ps = m.GetParameters();
|
||||||
|
var names = new List<string>(ps.Length);
|
||||||
|
foreach (var p in ps) names.Add(p.ParameterType.Name);
|
||||||
|
return $"{ps.Length} args: {string.Join(",", names)}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,5 +3,5 @@
|
|||||||
"PhotonAppId": "{PHOTON_APPID}",
|
"PhotonAppId": "{PHOTON_APPID}",
|
||||||
"PhotonVoiceAppId": "{PHOTON_VOICE_APPID}",
|
"PhotonVoiceAppId": "{PHOTON_VOICE_APPID}",
|
||||||
"PhotonCloudRegion": "{PHOTON_REGION}",
|
"PhotonCloudRegion": "{PHOTON_REGION}",
|
||||||
"EnableTlsTrustBypass": true
|
"EnableTlsTrustBypass": {ENABLE_TLS_TRUST_BYPASS}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -877,7 +877,10 @@ public class RoomsController(
|
|||||||
.Select(d => d.CurrentDataBlobName)
|
.Select(d => d.CurrentDataBlobName)
|
||||||
.FirstOrDefaultAsync()
|
.FirstOrDefaultAsync()
|
||||||
?? string.Empty;
|
?? string.Empty;
|
||||||
room = CloneWithCreator(room, room.CreatorPlayerId, dormBlobName);
|
// Stamp the wire Name with the requested name so the watch's
|
||||||
|
// name-keyed room cache (OJMCBOKJFOF.NHBPIIGDAJP) can find it.
|
||||||
|
room = CloneWithCreator(room, room.CreatorPlayerId, dormBlobName,
|
||||||
|
overrideName: DormNameOverride(roomName));
|
||||||
}
|
}
|
||||||
|
|
||||||
var scenes = await db.RoomScenes
|
var scenes = await db.RoomScenes
|
||||||
@@ -924,7 +927,10 @@ public class RoomsController(
|
|||||||
.Select(d => d.CurrentDataBlobName)
|
.Select(d => d.CurrentDataBlobName)
|
||||||
.FirstOrDefaultAsync()
|
.FirstOrDefaultAsync()
|
||||||
?? string.Empty;
|
?? string.Empty;
|
||||||
room = CloneWithCreator(room, room.CreatorPlayerId, dormBlobName);
|
// Stamp the wire Name with the requested name so the watch's
|
||||||
|
// name-keyed room cache (OJMCBOKJFOF.NHBPIIGDAJP) finds it.
|
||||||
|
room = CloneWithCreator(room, room.CreatorPlayerId, dormBlobName,
|
||||||
|
overrideName: DormNameOverride(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
var scenes = await db.RoomScenes
|
var scenes = await db.RoomScenes
|
||||||
@@ -946,11 +952,33 @@ public class RoomsController(
|
|||||||
/// caller and pointing at the caller's own most recent save.
|
/// caller and pointing at the caller's own most recent save.
|
||||||
/// Doesn't touch the DB — purely a response-shaping utility.
|
/// Doesn't touch the DB — purely a response-shaping utility.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <summary>When a dorm was resolved via the magic name "DormRoom",
|
||||||
|
/// returns that requested name (verbatim, so casing matches what the
|
||||||
|
/// watch will look the cache up by) to stamp onto the wire room.
|
||||||
|
/// Returns null for any other request name so the dorm keeps its real
|
||||||
|
/// "Dorm_{playerId}" name in by-id / direct-name responses.</summary>
|
||||||
|
private static string? DormNameOverride(string requestedName) =>
|
||||||
|
requestedName.Equals("DormRoom", StringComparison.OrdinalIgnoreCase)
|
||||||
|
? requestedName
|
||||||
|
: null;
|
||||||
|
|
||||||
public static RoomEntity CloneWithCreator(
|
public static RoomEntity CloneWithCreator(
|
||||||
RoomEntity src, long creatorId, string? overrideBlobName = null) => new()
|
RoomEntity src, long creatorId, string? overrideBlobName = null,
|
||||||
|
string? overrideName = null) => new()
|
||||||
{
|
{
|
||||||
Id = src.Id,
|
Id = src.Id,
|
||||||
Name = src.Name,
|
// overrideName lets the by-name resolvers stamp the wire Name with
|
||||||
|
// the name the client actually requested. The personal-dorm entity
|
||||||
|
// is named "Dorm_{playerId}", but the watch resolves it via the
|
||||||
|
// magic name "DormRoom" (HomeScreenFlow.Button_DormRoom →
|
||||||
|
// RunJoinRoom("DormRoom") → OJMCBOKJFOF.NHBPIIGDAJP("DormRoom")).
|
||||||
|
// That call caches the response in a Dictionary<string,Room> and
|
||||||
|
// looks it back up by the REQUESTED string ("DormRoom"); if the
|
||||||
|
// wire Name is "Dorm_{id}" the lookup misses → the promise rejects
|
||||||
|
// with "No such room" → the "contact recroom.happyfox.com" toast.
|
||||||
|
// Stamping Name="DormRoom" here makes the lookup hit, the same way
|
||||||
|
// RecCenter (whose entity Name IS "RecCenter") already works.
|
||||||
|
Name = overrideName ?? src.Name,
|
||||||
Description = src.Description,
|
Description = src.Description,
|
||||||
CreatorPlayerId = creatorId,
|
CreatorPlayerId = creatorId,
|
||||||
ImageName = src.ImageName,
|
ImageName = src.ImageName,
|
||||||
|
|||||||
@@ -122,8 +122,17 @@ try {
|
|||||||
'BepInEx/plugins/DorkNet.ClientPatch.dll'
|
'BepInEx/plugins/DorkNet.ClientPatch.dll'
|
||||||
)
|
)
|
||||||
'steam_stub' = @{
|
'steam_stub' = @{
|
||||||
|
# 2020.12+ is an EAC build: the real Unity binary is
|
||||||
|
# RecRoom.exe (loads RecRoom_Data/Plugins/x86_64/steam_api64.dll),
|
||||||
|
# NOT Recroom_Release.exe (which is the EAC launcher wrapper
|
||||||
|
# that loads Recroom_Release_Data/Plugins/steam_api64.dll).
|
||||||
|
# Patching the wrapper's DLL did nothing because the
|
||||||
|
# launcher's data dir is never opened by the real game
|
||||||
|
# process — symptom was "Failed to initialize Steam
|
||||||
|
# platform" because the unpatched real Steam DLL in
|
||||||
|
# RecRoom_Data tried to handshake.
|
||||||
'api_dll' = 'steam_api64.dll'
|
'api_dll' = 'steam_api64.dll'
|
||||||
'api_dest' = 'Recroom_Release_Data/Plugins/steam_api64.dll'
|
'api_dest' = 'RecRoom_Data/Plugins/x86_64/steam_api64.dll'
|
||||||
'api_backup_suffix' = '.steam-original'
|
'api_backup_suffix' = '.steam-original'
|
||||||
'appid_file' = 'steam_appid.txt'
|
'appid_file' = 'steam_appid.txt'
|
||||||
'appid_dest' = 'steam_appid.txt'
|
'appid_dest' = 'steam_appid.txt'
|
||||||
|
|||||||
Reference in New Issue
Block a user