Files
SeamlessClient/Utilities/Patches.cs
zznty aa323f1ff6
Some checks failed
Build / Compute Version (push) Successful in 6s
Build / Build Nuget package (push) Failing after 6s
fixes for nexus v3
2025-07-28 03:58:25 +07:00

297 lines
11 KiB
C#

using System.Reflection;
using System.Text;
using HarmonyLib;
using Sandbox.Engine.Multiplayer;
using Sandbox.Engine.Networking;
using Sandbox.Game.Gui;
using Sandbox.Game.World;
using Sandbox.Game.World.Generator;
using Sandbox.Graphics;
using Sandbox.Graphics.GUI;
using VRage;
using VRage.FileSystem;
using VRage.Game;
using VRage.GameServices;
using VRage.Network;
using VRage.Utils;
using VRageMath;
namespace SeamlessClientPlugin.Utilities;
public static class Patches
{
/* Harmony Patcher */
private static readonly Harmony Patcher = new("SeamlessClientPatcher");
/* WorldGenerator */
public static MethodInfo UnloadProceduralWorldGenerator { get; private set; }
public static event EventHandler<JoinResultMsg> OnJoinEvent;
public static void GetPatches()
{
//Get reflected values and store them
/* Get Methods */
var onJoin = GetMethod(typeof(MyMultiplayerClient), "OnUserJoined", BindingFlags.NonPublic | BindingFlags.Instance);
var loadingAction = GetMethod(typeof(MySessionLoader), "LoadMultiplayerSession",
BindingFlags.Public | BindingFlags.Static);
UnloadProceduralWorldGenerator = GetMethod(typeof(MyProceduralWorldGenerator), "UnloadData",
BindingFlags.Instance | BindingFlags.NonPublic);
//MethodInfo ConnectToServer = GetMethod(typeof(MyGameService), "ConnectToServer", BindingFlags.Static | BindingFlags.Public);
var loadingScreenDraw = GetMethod(typeof(MyGuiScreenLoading), "DrawInternal",
BindingFlags.Instance | BindingFlags.NonPublic);
//Test patches
//MethodInfo SetPlayerDed = GetMethod(typeof(MyPlayerCollection), "SetPlayerDeadInternal", BindingFlags.Instance | BindingFlags.NonPublic);
Patcher.Patch(loadingScreenDraw, new HarmonyMethod(GetPatchMethod(nameof(DrawInternal))));
Patcher.Patch(onJoin, postfix: new HarmonyMethod(GetPatchMethod(nameof(OnUserJoined))));
Patcher.Patch(loadingAction, new HarmonyMethod(GetPatchMethod(nameof(LoadMultiplayerSession))));
//Patcher.Patch(SetPlayerDed, prefix: new HarmonyMethod(GetPatchMethod(nameof(SetPlayerDeadInternal))));
}
private static MethodInfo GetPatchMethod(string v)
{
return typeof(Patches).GetMethod(v, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
}
private static void OnUserJoined(ref JoinResultMsg msg)
{
if (msg.JoinResult == JoinResult.OK)
//SeamlessClient.TryShow("User Joined! Result: " + msg.JoinResult.ToString());
//Invoke the switch event
OnJoinEvent?.Invoke(null, msg);
}
private static bool OnConnectToServer(MyGameServerItem server, Action<JoinResult> onDone)
{
if (SeamlessClient.IsSwitching)
return false;
return true;
}
/* Patch Utils */
private static MethodInfo GetMethod(Type type, string methodName, BindingFlags flags)
{
var foundMethod = type.GetMethod(methodName, flags);
if (foundMethod == null)
throw new NullReferenceException($"Method for {methodName} is null!");
return foundMethod;
}
private static FieldInfo GetField(Type type, string fieldName, BindingFlags flags)
{
var foundField = type.GetField(fieldName, flags);
if (foundField == null)
throw new NullReferenceException($"Field for {fieldName} is null!");
return foundField;
}
private static PropertyInfo GetProperty(Type type, string propertyName, BindingFlags flags)
{
var foundProperty = type.GetProperty(propertyName, flags);
if (foundProperty == null)
throw new NullReferenceException($"Property for {propertyName} is null!");
return foundProperty;
}
private static ConstructorInfo GetConstructor(Type type, BindingFlags flags, Type[] types)
{
var foundConstructor = type.GetConstructor(flags, null, types, null);
if (foundConstructor == null)
throw new NullReferenceException($"Contructor for {type.Name} is null!");
return foundConstructor;
}
#region LoadingScreen
/* Loading Screen Stuff */
private static string _loadingScreenTexture;
private static string _serverName;
private static bool LoadMultiplayerSession(MyObjectBuilder_World world, MyMultiplayerBase multiplayerSession)
{
//
MyLog.Default.WriteLine("LoadSession() - Start");
if (!MyWorkshop.CheckLocalModsAllowed(world.Checkpoint.Mods, false))
{
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateMessageBox(
messageCaption: MyTexts.Get(MyCommonTexts.MessageBoxCaptionError),
messageText: MyTexts.Get(MyCommonTexts.DialogTextLocalModsDisabledInMultiplayer)));
MyLog.Default.WriteLine("LoadSession() - End");
return false;
}
MyLog.Default.WriteLine("Seamless Downloading mods!");
MyWorkshop.DownloadModsAsync(world.Checkpoint.Mods, result =>
{
if (result == MyGameServiceCallResult.OK)
{
MyScreenManager.CloseAllScreensNowExcept(null);
MyGuiSandbox.Update(16);
if (MySession.Static != null)
{
MySession.Static.Unload();
MySession.Static = null;
}
_serverName = multiplayerSession.HostName;
GetCustomLoadingScreenPath(world.Checkpoint.Mods, out _loadingScreenTexture);
MySessionLoader.StartLoading(delegate
{
MySession.LoadMultiplayer(world, multiplayerSession);
});
}
else
{
multiplayerSession.Dispose();
MySessionLoader.UnloadAndExitToMenu();
if (MyGameService.IsOnline)
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateMessageBox(
messageCaption: MyTexts.Get(MyCommonTexts.MessageBoxCaptionError),
messageText: MyTexts.Get(MyCommonTexts.DialogTextDownloadModsFailed)));
else
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateMessageBox(
messageCaption: MyTexts.Get(MyCommonTexts.MessageBoxCaptionError),
messageText: new StringBuilder(string.Format(
MyTexts.GetString(MyCommonTexts.DialogTextDownloadModsFailedSteamOffline),
MySession.GameServiceName))));
}
MyLog.Default.WriteLine("LoadSession() - End");
}, () =>
{
multiplayerSession.Dispose();
MySessionLoader.UnloadAndExitToMenu();
});
return false;
}
private static bool DrawInternal(MyGuiScreenLoading __instance)
{
//If we dont have a custom loading screen texture, do not do the special crap below
if (string.IsNullOrEmpty(_loadingScreenTexture))
return true;
//MyGuiControlMultilineText m_multiTextControl = (MyGuiControlMultilineText)typeof(MyGuiScreenLoading).GetField("m_multiTextControl", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(__instance);
var color = new Color(255, 255, 255, 250);
color.A = (byte)(color.A * __instance.m_transitionAlpha);
var fullscreenRectangle = MyGuiManager.GetFullscreenRectangle();
MyGuiManager.DrawSpriteBatch(@"Textures\GUI\Blank.dds", fullscreenRectangle, Color.Black, false, true);
MyGuiManager.GetSafeHeightFullScreenPictureSize(MyGuiConstants.LOADING_BACKGROUND_TEXTURE_REAL_SIZE,
out var outRect);
MyGuiManager.DrawSpriteBatch(_loadingScreenTexture, outRect,
new Color(new Vector4(1f, 1f, 1f, __instance.m_transitionAlpha)), true, true);
MyGuiManager.DrawSpriteBatch(@"Textures\Gui\Screens\screen_background_fade.dds", outRect,
new Color(new Vector4(1f, 1f, 1f, __instance.m_transitionAlpha)), true, true);
//MyGuiSandbox.DrawGameLogoHandler(m_transitionAlpha, MyGuiManager.ComputeFullscreenGuiCoordinate(MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, 44, 68));
var loadScreen = $"Loading into {_serverName}! Please wait!";
MyGuiManager.DrawString(MyFontEnum.LoadingScreen, loadScreen, new Vector2(0.5f, 0.95f),
MyGuiSandbox.GetDefaultTextScaleWithLanguage() * 1.1f,
new Color(MyGuiConstants.LOADING_PLEASE_WAIT_COLOR * __instance.m_transitionAlpha),
MyGuiDrawAlignEnum.HORISONTAL_CENTER_AND_VERTICAL_BOTTOM);
MyGuiManager.DrawString(MyFontEnum.LoadingScreen, "Nexus & SeamlessClient Made by: Casimir", new Vector2(0.95f, 0.95f),
MyGuiSandbox.GetDefaultTextScaleWithLanguage() * 1.1f,
new Color(MyGuiConstants.LOADING_PLEASE_WAIT_COLOR * __instance.m_transitionAlpha),
MyGuiDrawAlignEnum.HORISONTAL_CENTER_AND_VERTICAL_BOTTOM);
/*
if (string.IsNullOrEmpty(m_customTextFromConstructor))
{
string font = m_font;
Vector2 positionAbsoluteBottomLeft = m_multiTextControl.GetPositionAbsoluteBottomLeft();
Vector2 textSize = m_multiTextControl.TextSize;
Vector2 normalizedCoord = positionAbsoluteBottomLeft + new Vector2((m_multiTextControl.Size.X - textSize.X) * 0.5f + 0.025f, 0.025f);
MyGuiManager.DrawString(font, m_authorWithDash.ToString(), normalizedCoord, MyGuiSandbox.GetDefaultTextScaleWithLanguage());
}
*/
//m_multiTextControl.Draw(1f, 1f);
return false;
}
private static bool GetCustomLoadingScreenPath(List<MyObjectBuilder_Checkpoint.ModItem> mods, out string file)
{
file = null;
var workshopDir = MyFileSystem.ModsPath;
var backgrounds = new List<string>();
SeamlessClient.TryShow(workshopDir);
try
{
SeamlessClient.TryShow($"Installed Mods: ({mods.Count}) [{string.Join(", ", mods.Select(x => x.FriendlyName))}]");
foreach (var mod in mods)
{
var searchDir = mod.GetPath();
if (!Directory.Exists(searchDir))
continue;
var files = Directory.GetFiles(searchDir, "CustomLoadingBackground*.dds",
SearchOption.TopDirectoryOnly);
foreach (var filePath in files)
{
// Adds all files containing CustomLoadingBackground to a list for later randomisation
SeamlessClient.TryShow($"{mod.FriendlyName} contains a custom loading background!");
backgrounds.Add(filePath);
}
}
// Randomly pick a loading screen from the available backgrounds
file = backgrounds[Random.Shared.Next(0, backgrounds.Count - 1)];
return true;
}
catch (Exception ex)
{
SeamlessClient.TryShow(ex.ToString());
}
SeamlessClient.TryShow("No installed custom loading screen!");
return false;
}
#endregion
}