feat: Upgrade server with HyNB v2.4 modular architecture, minigames, admin GM web panel, companion summons, and car radio
This commit is contained in:
1 parent
74e2963a12
commit
3e5adf1dc5
1571 files changed
+336468
-6348
No files matched your search
@@ -0,0 +1,170 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using HarmonyLib;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AnantaSpoonProbe;
|
||||
|
||||
// Hotkeys (main thread):
|
||||
// F8 = SAFE probe: SpoonTaskMgr.Instance != null? path constants, workAction count for 60004938.
|
||||
// Ctrl+F8 = LoadSingleTask(60004938) with empty md5. Empty = safe-ignore per R211b.
|
||||
public class SpoonProbeBehaviour : MonoBehaviour
|
||||
{
|
||||
public SpoonProbeBehaviour(IntPtr ptr) : base(ptr) { }
|
||||
public SpoonProbeBehaviour() { }
|
||||
|
||||
private void Update()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.F8))
|
||||
{
|
||||
if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl))
|
||||
TryLoadSingleTask();
|
||||
else
|
||||
SafeProbe();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SpoonProbePlugin.PLog($"hotkey error: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void SafeProbe()
|
||||
{
|
||||
try
|
||||
{
|
||||
var mgrType = AccessTools.TypeByName("L50.Spoon.SpoonRunTime.SpoonTaskMgr");
|
||||
if (mgrType is null) { SpoonProbePlugin.PLog("SAFE: SpoonTaskMgr type NOT FOUND"); return; }
|
||||
var instProp = AccessTools.Property(mgrType, "Instance");
|
||||
var inst = instProp?.GetValue(null);
|
||||
SpoonProbePlugin.PLog($"SAFE: SpoonTaskMgr.Instance {(inst is null ? "NULL" : "OK")}");
|
||||
|
||||
foreach (var n in new[] { "TASK_SPOON_FILE_PATH", "TASK_RAID_FILE_PATH", "EVENT_FILE_PATH",
|
||||
"TASK_SPOON_FILE_NAME", "TASK_RAID_FILE_NAME", "EVENT_FILE_NAME" })
|
||||
{
|
||||
try
|
||||
{
|
||||
var f = AccessTools.Field(mgrType, n);
|
||||
var v = f?.GetValue(null);
|
||||
SpoonProbePlugin.PLog($"SAFE: const {n} = {SpoonProbePlugin.Summarize(v)} (field {(f is null ? "MISSING" : f.FieldType.Name)})");
|
||||
}
|
||||
catch (Exception ex) { SpoonProbePlugin.PLog($"SAFE: const {n} read FAIL: {ex.Message}"); }
|
||||
}
|
||||
|
||||
// v2: enumerate ALL static string fields (interop may rename/hide privates).
|
||||
try
|
||||
{
|
||||
int n = 0;
|
||||
foreach (var f in mgrType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static))
|
||||
{
|
||||
if (f.FieldType != typeof(string)) continue;
|
||||
if (n >= 20) break;
|
||||
n++;
|
||||
object? v = null;
|
||||
try { v = f.GetValue(null); } catch (Exception ex) { v = $"<readfail {ex.Message}>"; }
|
||||
SpoonProbePlugin.PLog($"SAFE: staticstr {f.Name} = {SpoonProbePlugin.Summarize(v)}");
|
||||
}
|
||||
if (n == 0) SpoonProbePlugin.PLog("SAFE: no static string fields visible via reflection");
|
||||
}
|
||||
catch (Exception ex) { SpoonProbePlugin.PLog($"SAFE: staticstr enum FAIL: {ex.Message}"); }
|
||||
|
||||
// Read-only: workAction count for the Prologue start task.
|
||||
try
|
||||
{
|
||||
var m = AccessTools.Method(mgrType, "GetTaskWorkActionByTask");
|
||||
if (m is not null && inst is not null)
|
||||
{
|
||||
var r = m.Invoke(inst, new object[] { 60004938u });
|
||||
SpoonProbePlugin.PLog($"SAFE: GetTaskWorkActionByTask(60004938) -> {SpoonProbePlugin.Summarize(r)}");
|
||||
}
|
||||
else SpoonProbePlugin.PLog("SAFE: GetTaskWorkActionByTask method/instance missing");
|
||||
}
|
||||
catch (Exception ex) { SpoonProbePlugin.PLog($"SAFE: workAction read FAIL: {ex.Message}"); }
|
||||
|
||||
SpoonProbePlugin.PLog("SAFE probe done. (Ctrl+F8 = LoadSingleTask attempt)");
|
||||
}
|
||||
catch (Exception ex) { SpoonProbePlugin.PLog($"SAFE probe FAIL: {ex}"); }
|
||||
}
|
||||
|
||||
private static void TryLoadSingleTask()
|
||||
{
|
||||
try
|
||||
{
|
||||
const uint taskId = 60004938;
|
||||
var mgrType = AccessTools.TypeByName("L50.Spoon.SpoonRunTime.SpoonTaskMgr");
|
||||
var spoonType = AccessTools.TypeByName("UX.Game.TaskSpoonViewInfo");
|
||||
var stateType = AccessTools.TypeByName("UX.Game.TaskState");
|
||||
if (mgrType is null || spoonType is null)
|
||||
{
|
||||
SpoonProbePlugin.PLog($"LOAD: type missing mgr={mgrType is not null} spoon={spoonType is not null}");
|
||||
return;
|
||||
}
|
||||
var inst = AccessTools.Property(mgrType, "Instance")?.GetValue(null);
|
||||
if (inst is null) { SpoonProbePlugin.PLog("LOAD: Instance NULL"); return; }
|
||||
|
||||
var spoon = Activator.CreateInstance(spoonType);
|
||||
Set(spoon!, "SpoonMd5", ""); // MUST stay empty (fake md5 hard-crashes, R211b)
|
||||
Set(spoon!, "SpRaidId", 23301180u);
|
||||
Set(spoon!, "StartTaskId", taskId);
|
||||
Set(spoon!, "EndTaskId", 60004955u);
|
||||
Set(spoon!, "Alias", "");
|
||||
Set(spoon!, "EventId", 1441u);
|
||||
Set(spoon!, "EventStartTaskId", taskId);
|
||||
SpoonProbePlugin.PLog($"LOAD: calling LoadSingleTask task={taskId} {SpoonProbePlugin.Summarize(spoon)}");
|
||||
|
||||
object stateVal = 1; // TaskState.Accepted = 1
|
||||
try
|
||||
{
|
||||
if (stateType is not null && stateType.IsEnum)
|
||||
stateVal = Enum.ToObject(stateType, 1);
|
||||
}
|
||||
catch { }
|
||||
|
||||
// counterValues: match the declared parameter type (Il2Cpp List<int> or managed List<int>).
|
||||
object? counters = null;
|
||||
try
|
||||
{
|
||||
var loadMi = FindLoadSingleTask(mgrType);
|
||||
var ps = loadMi?.GetParameters();
|
||||
if (ps is not null && ps.Length >= 5)
|
||||
{
|
||||
var listType = ps[2].ParameterType;
|
||||
counters = Activator.CreateInstance(listType);
|
||||
var add = listType.GetMethod("Add");
|
||||
add?.Invoke(counters, new object[] { 0 });
|
||||
}
|
||||
}
|
||||
catch (Exception ex) { SpoonProbePlugin.PLog($"LOAD: counter list build FAIL: {ex.Message}"); }
|
||||
|
||||
var mi2 = FindLoadSingleTask(mgrType);
|
||||
if (mi2 is null) { SpoonProbePlugin.PLog("LOAD: LoadSingleTask method NOT FOUND"); return; }
|
||||
mi2.Invoke(inst, new object?[] { taskId, stateVal, counters, spoon, false });
|
||||
SpoonProbePlugin.PLog("LOAD: LoadSingleTask invoked (watch ENTER/LEAVE lines + dumps/spoon/)");
|
||||
}
|
||||
catch (Exception ex) { SpoonProbePlugin.PLog($"LOAD FAIL: {ex}"); }
|
||||
}
|
||||
|
||||
private static MethodInfo? FindLoadSingleTask(Type mgrType)
|
||||
{
|
||||
foreach (var mi in mgrType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
|
||||
if (mi.Name == "LoadSingleTask" && mi.GetParameters().Length == 5)
|
||||
return mi;
|
||||
return AccessTools.Method(mgrType, "LoadSingleTask");
|
||||
}
|
||||
|
||||
private static void Set(object o, string name, object? v)
|
||||
{
|
||||
var t = o.GetType();
|
||||
var f = t.GetField(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
if (f is not null)
|
||||
{
|
||||
if (v is uint u && f.FieldType.IsEnum) f.SetValue(o, Enum.ToObject(f.FieldType, u));
|
||||
else f.SetValue(o, v);
|
||||
return;
|
||||
}
|
||||
var p = t.GetProperty(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
p?.SetValue(o, v);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user