Internals rewritten. PHoton config can now be fetched from the server, and if it fails, fallback to the local configuration.

This commit is contained in:
2025-08-16 21:04:26 -04:00
parent 8ca37170e4
commit 087d9a900e
20 changed files with 393 additions and 234 deletions

View File

@@ -35,10 +35,12 @@ namespace undead_universal_patch_il2cpp.Core.Config
public static class ServerPatchesConfig
{
public static ConfigEntry<bool> CustomEmotes;
public static ConfigEntry<bool> CustomPhoton;
}
public static class ServerPatchesConfigDefaults
{
public static bool CustomEmotes = false;
public static bool CustomPhoton = false;
}
public static class GameManagerConfig
{
@@ -69,6 +71,7 @@ namespace undead_universal_patch_il2cpp.Core.Config
public static ConfigEntry<bool> SelfHosted;
public static ConfigEntry<string> ServerAddress;
public static ConfigEntry<int> ServerPort;
public static ConfigEntry<byte> ConnectionProtocol;
}
public static class PhotonConfigDefaults
{
@@ -79,5 +82,6 @@ namespace undead_universal_patch_il2cpp.Core.Config
public static bool SelfHosted = false;
public static string ServerAddress = "127.0.0.1";
public static int ServerPort = 5055;
public static byte ConnectionProtocol = 0;
}
}

View File

@@ -4,9 +4,9 @@ using System.Collections.Concurrent;
using Mapster;
using System;
using System.Linq;
using undead_universal_patch_il2cpp.Core.Config;
using BestHTTP;
namespace undead_universal_patch_il2cpp.Core.CustomRecNet.CustomEmotes;
namespace undead_universal_patch_il2cpp.Core.Content.CustomRecNet.CustomEmotes;
public class EmoteConfigDTO
{
@@ -18,9 +18,15 @@ public class EmoteConfigDTO
public bool OnlyBroadcastToTeam { get; set; }
}
public class EmoteConfig
public class CustomEmotes : MonoBehaviour
{
public static void Patch(List<EmoteConfigDTO> emotes)
public void Start()
{
RecNetInteractions.postLocalAccountActions.Add(DownloadCustomEmotes);
}
void Patch(HTTPResponse _res, List<EmoteConfigDTO> emotes)
{
UniversalPatchPlugin.Log.LogInfo("Setting new emotes");
@@ -36,29 +42,10 @@ public class EmoteConfig
Util.ConditionalDebug($"{emotes.Count} new emote configurations");
}
}
public class CustomEmotes : MonoBehaviour
{
private static readonly ConcurrentQueue<Action> mainThreadQueue = new ConcurrentQueue<Action>();
public void Start()
{
if (ServerPatchesConfig.CustomEmotes.Value)
RecNetInteractions.postAuthenticationActions.Add(DownloadCustomEmotes);
}
void Update()
{
while (mainThreadQueue.TryDequeue(out var action))
{
try { action(); }
catch (Exception ex) { UniversalPatchPlugin.Log.LogError(ex); }
}
}
void DownloadCustomEmotes()
{
RecNetInteractions.SendRequest<List<EmoteConfigDTO>>(BestHTTP.HTTPMethods.Get, RecNet.Service.API, "/api/undead/v1/emotes", EmoteConfig.Patch);
RecNetInteractions.SendRequest<List<EmoteConfigDTO>>(HTTPMethods.Get, RecNet.Service.API, "/api/undead/v1/emotes", Patch);
}
}

View File

@@ -0,0 +1,67 @@
using System;
using BestHTTP;
using Il2CppInterop.Runtime;
using undead_universal_patch_il2cpp.Core.Config;
using undead_universal_patch_il2cpp.Patches.Photon;
using UnityEngine;
namespace undead_universal_patch_il2cpp.Core.Content.CustomRecNet.CustomPhoton;
public class PhotonConfigDTO
{
public bool Logging { get; set; }
public string AppID { get; set; }
public string VoiceAppID { get; set; }
public bool SelfHosted { get; set; }
public string ServerAddress { get; set; }
public int ServerPort { get; set; }
public byte ConnectionProtocol { get; set; }
}
public class CustomPhoton : MonoBehaviour
{
public static bool ServerConfigFailed { get; set; } = false;
public void Start()
{
RecNetInteractions.postLocalAccountActions.Add(DownloadServerConfig);
}
void OnServerConfigFailed(HTTPResponse res)
{
ServerConfigFailed = true;
if (PhotonConfig.PatchPhotonIds.Value)
{
UniversalPatchPlugin.Log.LogInfo("Attempting Photon patch from local configuration");
PhotonPatch.Patch(new PhotonConfigDTO
{
Logging = PhotonConfig.PunLogging.Value,
AppID = PhotonConfig.AppID.Value,
VoiceAppID = PhotonConfig.VoiceAppID.Value,
SelfHosted = PhotonConfig.SelfHosted.Value,
ServerAddress = PhotonConfig.ServerAddress.Value,
ServerPort = PhotonConfig.ServerPort.Value,
ConnectionProtocol = PhotonConfig.ConnectionProtocol.Value
});
}
}
void ApplyPhotonConfig(HTTPResponse res, PhotonConfigDTO photonConfig)
{
if (!ServerPatchesConfig.CustomPhoton.Value) return;
try
{
UniversalPatchPlugin.Log.LogInfo("Attempting Photon patch from server configuration");
PhotonPatch.Patch(photonConfig);
}
catch (Exception err)
{
UniversalPatchPlugin.Log.LogError($"Failed to apply Photon configuration from server: {err}");
OnServerConfigFailed(res);
}
}
void DownloadServerConfig()
{
RecNetInteractions.SendRequest<PhotonConfigDTO>(HTTPMethods.Get, RecNet.Service.API, "/api/undead/v1/photon", ApplyPhotonConfig, OnServerConfigFailed);
}
}

View File

@@ -4,16 +4,17 @@ using System.Text.Json;
using BestHTTP;
using Il2CppInterop.Runtime;
using RecNet;
using RecRoom.Async;
namespace undead_universal_patch_il2cpp.Core.CustomRecNet;
namespace undead_universal_patch_il2cpp.Core.Content.CustomRecNet;
public class RecNetInteractions
{
public static int AccountId { get; set; }
public static string AccessToken { get; set; }
public static List<Action> postNameServerActions = [];
public static List<Action> postAuthenticationActions = [];
public static List<Action> postLocalAccountActions = [];
public static Il2CppSystem.Uri CreateServiceUri(Service service, string pathAndQuery)
{
@@ -28,7 +29,7 @@ public class RecNetInteractions
return RecNet.Core.ServiceUris.Count > 1;
}
public static void SendRequest<T>(HTTPMethods method, Service service, string requestUri, Action<T> reqFinished)
public static void SendRequest<T>(HTTPMethods method, Service service, string requestUri, Action<HTTPResponse, T> reqFinished, Action<HTTPResponse>? reqFailed = null)
{
var res = RecNet.Core.SendRequest(method, service, requestUri);
@@ -37,11 +38,12 @@ public class RecNetInteractions
try
{
var data = JsonSerializer.Deserialize<T>(res.DataAsText);
reqFinished(data);
reqFinished(res, data);
}
catch (Exception ex)
{
UniversalPatchPlugin.Log.LogError($"'{requestUri}' failed\n{ex}");
if (reqFailed != null) reqFailed(res);
}
}));
}

View File

@@ -1,7 +1,7 @@
using System.Collections.Generic;
using RecRoom.Tools;
namespace undead_universal_patch_il2cpp.Core.UndeadGameManager;
namespace undead_universal_patch_il2cpp.Core.Content.UndeadGameManager;
public class AutoHealSettingsDTO
{

View File

@@ -1,6 +1,6 @@
using System.Collections.Generic;
namespace undead_universal_patch_il2cpp.Core.UndeadGameManager;
namespace undead_universal_patch_il2cpp.Core.Content.UndeadGameManager;
public static class GameConfigurator
{

View File

@@ -1,6 +1,6 @@
using System.Collections.Generic;
namespace undead_universal_patch_il2cpp.Core.UndeadGameManager;
namespace undead_universal_patch_il2cpp.Core.Content.UndeadGameManager;
public static class GameFreeSpawns
{

View File

@@ -4,8 +4,10 @@ using System.Linq;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Mapster;
using undead_universal_patch_il2cpp.Core.Config;
using undead_universal_patch_il2cpp.Core.CustomRecNet.CustomEmotes;
using undead_universal_patch_il2cpp.Core.UndeadGameManager;
using undead_universal_patch_il2cpp.Core.Content.CustomRecNet.CustomEmotes;
using undead_universal_patch_il2cpp.Core.Content.CustomRecNet.CustomPhoton;
using undead_universal_patch_il2cpp.Core.Content.UndeadGameManager;
using UnityEngine;
namespace undead_universal_patch_il2cpp.Core;
@@ -13,8 +15,6 @@ public class Initialization
{
public static void Initialize()
{
Util.LocalInstanceGuid = Guid.NewGuid().ToString();
AddMapsterGlobalTypeConfigs();
FetchConfigurations();
AttachGameObjects();
@@ -55,7 +55,9 @@ public class Initialization
private static void AttachGameObjects()
{
UniversalPatchPlugin.Log.LogInfo("Attaching game objects");
UniversalPatchPlugin.Instance.AddComponent<CustomEmotes>();
if (ServerPatchesConfig.CustomEmotes.Value) UniversalPatchPlugin.Instance.AddComponent<CustomEmotes>();
UniversalPatchPlugin.Instance.AddComponent<CustomPhoton>();
}
private static void FetchConfigurations()
@@ -70,7 +72,7 @@ public class Initialization
"The game expects a certain certificate from rec.net when making HTTPS requests. Enable this to allow any valid certificate.");
PatchConfig.HilePatch = UniversalPatchPlugin.Instance.Config.Bind("Generic", "HilePatch", PatchConfigDefaults.HilePatch,
"The game will close after a short period of time if BepInEx is found. Enable to stop this from happening." +
"This will also enable the AGRoomRuntimeConfig patch. See the README for more info.");
"\nThis will also enable the AGRoomRuntimeConfig patch. See the README for more info.");
PatchConfig.SignalRHandshakeFix = UniversalPatchPlugin.Instance.Config.Bind("Generic", "SignalRHandshakeFix", PatchConfigDefaults.SignalRHandshakeFix,
"Replace apostrophes with quotes in the initial SignalR handshake.");
PatchConfig.ImageSignaturePatch = UniversalPatchPlugin.Instance.Config.Bind("Generic", "ImageSignaturePatch", PatchConfigDefaults.ImageSignaturePatch,
@@ -83,6 +85,10 @@ public class Initialization
ServerPatchesConfig.CustomEmotes = UniversalPatchPlugin.Instance.Config.Bind("ServerPatches", "CustomEmotes", ServerPatchesConfigDefaults.CustomEmotes,
"Modify the game's emote text with a configuration from the server. Requires a custom server.");
ServerPatchesConfig.CustomPhoton = UniversalPatchPlugin.Instance.Config.Bind("ServerPatches", "CustomPhoton", ServerPatchesConfigDefaults.CustomPhoton,
"Patch ServerSettings values with a configuration from the server." +
"\nWhen the server fails to provide a valid configuration, values from the local config will be used." +
"\nPhoton.PatchPhotonIds must be enabled for this fallback to work.");
GameManagerConfig.AnyGameFreeSpawn = UniversalPatchPlugin.Instance.Config.Bind("GameManagerConfig", "AnyGameFreeSpawn", GameManagerConfigDefaults.AnyGameFreeSpawn,
$"Use patches from '{GameFreeSpawns.config.path}' to spawn in any valid player spawnpoint in specified games. See README.md");
@@ -103,9 +109,14 @@ public class Initialization
"The new target Voice App ID from the Photon dashboard." +
"\nWhen self-hosting, this value is ignored, since Photon voice in Rec Room is based on PUN.");
PhotonConfig.ServerAddress = UniversalPatchPlugin.Instance.Config.Bind("Photon", "ServerAddress", PhotonConfigDefaults.ServerAddress,
"Address of the Photon master server (ignored if not using self-hosted)");
"Address of the Photon target server (ignored if not using self-hosted)");
PhotonConfig.ServerPort = UniversalPatchPlugin.Instance.Config.Bind("Photon", "ServerPort", PhotonConfigDefaults.ServerPort,
"Photon master server UDP port (ignored if not using self-hosted)");
"Photon target server port (ignored if not using self-hosted)." +
"\nYou can set this port to the matching protocol port from the server, e.g. 5055 for UDP, 9091 for WebSockets");
PhotonConfig.ConnectionProtocol = UniversalPatchPlugin.Instance.Config.Bind("Photon", "ConnectionProtocol", PhotonConfigDefaults.ConnectionProtocol,
"Connection protocol to use when connecting to the target port (ignored if not using self-hosted)." +
"\n0: UDP, 1: TCP;" +
"\nWebSockets are not supported by Photon in this build.");
NameserverConfig.Rewrite = UniversalPatchPlugin.Instance.Config.Bind("Nameserver", "Rewrite", NameserverConfigDefaults.Rewrite,
"Enable/disable rewriting the URL for nameserver requests.");
@@ -118,9 +129,11 @@ public class Initialization
TypeAdapterConfig.GlobalSettings
.ForType<List<TeamConfigurationDTO>, Il2CppStructArray<TeamConfiguration>>()
.MapWith(src => new Il2CppStructArray<TeamConfiguration>(src.Select(x => x.Adapt<TeamConfiguration>()).ToArray()));
TypeAdapterConfig.GlobalSettings
.ForType<List<StatConfigurationDTO>, Il2CppReferenceArray<StatConfiguration>>()
.MapWith(src => new Il2CppReferenceArray<StatConfiguration>(src.Select(x => x.Adapt<StatConfiguration>()).ToArray()));
TypeAdapterConfig.GlobalSettings
.ForType<EmoteConfigDTO, RecRoom.AGUI.Expresso.ContextualEmotesConfig.ExpressoEmote>()
.Map(dest => dest.emoteUniqueName, source => source.UniqueName)

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using BepInEx.Configuration;
using CodeStage.AntiCheat.ObscuredTypes;
using HarmonyLib;
using undead_universal_patch_il2cpp.Core.Config;
@@ -16,6 +17,22 @@ namespace undead_universal_patch_il2cpp.Core
public bool Success;
}
public class RecNetUtil
{
public static ObscuredInt GetAccountId()
{
PropertyInfo selfAccountProp = AccessTools.Property("RecNet.Accounts:LocalAccount");
object acc = selfAccountProp.GetValue(null);
Type selfAccountType = acc.GetType();
PropertyInfo accountIdProp = AccessTools.Property(selfAccountType, "AccountId");
ObscuredInt val = (ObscuredInt)accountIdProp.GetValue(acc);
return val;
}
}
public class Util
{
private static readonly PatchTypesResult UnsuccessfulPatchResult = new()
@@ -25,8 +42,6 @@ namespace undead_universal_patch_il2cpp.Core
Success = false
};
public static string LocalInstanceGuid { set; get; }
public static void ConditionalDebug(string msg)
{
if (GenericConfig.PatchDebug.Value) UniversalPatchPlugin.Log.LogDebug(msg);