Mod switching

This commit is contained in:
Bob Da Ross
2022-02-14 20:19:30 -06:00
parent cf51b5f0e7
commit 4bd456a05b
6 changed files with 330 additions and 29 deletions

View File

@@ -114,7 +114,7 @@ namespace SeamlessClientPlugin
public const ushort SeamlessClientNetID = 2936; public const ushort SeamlessClientNetID = 2936;
private static System.Timers.Timer PingTimer = new System.Timers.Timer(3000); private static System.Timers.Timer PingTimer = new System.Timers.Timer(500);
public static bool IsSwitching = false; public static bool IsSwitching = false;
public static bool RanJoin = false; public static bool RanJoin = false;
@@ -123,10 +123,9 @@ namespace SeamlessClientPlugin
public void Init(object gameInstance) public void Init(object gameInstance)
{ {
Patches.GetPatches();
TryShow("Running Seamless Client Plugin v[" + Version + "]"); TryShow("Running Seamless Client Plugin v[" + Version + "]");
// PingTimer.Elapsed += PingTimer_Elapsed;
// PingTimer.Start();
@@ -134,7 +133,6 @@ namespace SeamlessClientPlugin
} }
public void Update() public void Update()
{ {
if (MyAPIGateway.Multiplayer == null) if (MyAPIGateway.Multiplayer == null)
@@ -143,6 +141,7 @@ namespace SeamlessClientPlugin
if (!Initilized) if (!Initilized)
{ {
Patches.GetPatches();
TryShow("Initilizing Communications!"); TryShow("Initilizing Communications!");
RunInitilizations(); RunInitilizations();
} }
@@ -150,6 +149,8 @@ namespace SeamlessClientPlugin
public static void RunInitilizations() public static void RunInitilizations()
{ {
MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(SeamlessClientNetID, MessageHandler); MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(SeamlessClientNetID, MessageHandler);
@@ -158,7 +159,6 @@ namespace SeamlessClientPlugin
public static void DisposeInitilizations() public static void DisposeInitilizations()
{ {
PingTimer.Stop();
MyAPIGateway.Multiplayer?.UnregisterSecureMessageHandler(SeamlessClientNetID, MessageHandler); MyAPIGateway.Multiplayer?.UnregisterSecureMessageHandler(SeamlessClientNetID, MessageHandler);
Initilized = false; Initilized = false;

View File

@@ -163,6 +163,7 @@
<Compile Include="SeamlessClient.cs" /> <Compile Include="SeamlessClient.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SeamlessTransfer\ModLoader.cs" /> <Compile Include="SeamlessTransfer\ModLoader.cs" />
<Compile Include="SeamlessTransfer\MyScriptManagerLoader.cs" />
<Compile Include="SeamlessTransfer\PingServer.cs" /> <Compile Include="SeamlessTransfer\PingServer.cs" />
<Compile Include="Messages\Transfer.cs" /> <Compile Include="Messages\Transfer.cs" />
<Compile Include="Utilities\Patches.cs" /> <Compile Include="Utilities\Patches.cs" />

View File

@@ -1,13 +1,16 @@
using Sandbox.Definitions; using Sandbox.Definitions;
using Sandbox.Engine.Networking; using Sandbox.Engine.Networking;
using Sandbox.Game.World; using Sandbox.Game.World;
using Sandbox.ModAPI;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using VRage.Game; using VRage.Game;
using VRage.Game.GUI;
namespace SeamlessClientPlugin.SeamlessTransfer namespace SeamlessClientPlugin.SeamlessTransfer
{ {
@@ -31,8 +34,14 @@ namespace SeamlessClientPlugin.SeamlessTransfer
//Mods that are currently loaded in this instance. //Mods that are currently loaded in this instance.
private static List<MyObjectBuilder_Checkpoint.ModItem> CurrentLoadedMods = new List<MyObjectBuilder_Checkpoint.ModItem>(); private static List<MyObjectBuilder_Checkpoint.ModItem> CurrentLoadedMods = new List<MyObjectBuilder_Checkpoint.ModItem>();
//Mods that we are switching to.
private static List<MyObjectBuilder_Checkpoint.ModItem> TargetServerMods = new List<MyObjectBuilder_Checkpoint.ModItem>();
//Mods that we need to Load
private static List<MyObjectBuilder_Checkpoint.ModItem> TargetLoadMods = new List<MyObjectBuilder_Checkpoint.ModItem>();
//Mods that we need to UnLoad
private static List<MyObjectBuilder_Checkpoint.ModItem> TargetUnLoadMods = new List<MyObjectBuilder_Checkpoint.ModItem>();
private static bool FinishedDownloadingMods = false; private static bool FinishedDownloadingMods = false;
@@ -40,18 +49,40 @@ namespace SeamlessClientPlugin.SeamlessTransfer
private static DateTime DownloadTimeout; private static DateTime DownloadTimeout;
private static MethodInfo PrepareBaseSession = typeof(MySession).GetMethod("PreloadModels", BindingFlags.Static | BindingFlags.NonPublic);
private static FieldInfo ScriptManager = typeof(MySession).GetField("ScriptManager", BindingFlags.Instance | BindingFlags.Public);
public static void DownloadNewMods(List<MyObjectBuilder_Checkpoint.ModItem> Target) public static void DownloadNewMods(List<MyObjectBuilder_Checkpoint.ModItem> Target)
{ {
CurrentLoadedMods = MySession.Static.Mods; CurrentLoadedMods = MySession.Static.Mods;
TargetServerMods = Target;
//Loop through our current mods
foreach(var mod in CurrentLoadedMods)
{
if (!Target.Contains(mod))
TargetUnLoadMods.Add(mod);
}
//Loop through our TargetMods
foreach(var mod in Target)
{
if (!CurrentLoadedMods.Contains(mod))
TargetLoadMods.Add(mod);
}
DownloadTimeout = DateTime.Now + TimeSpan.FromMinutes(5); DownloadTimeout = DateTime.Now + TimeSpan.FromMinutes(5);
SeamlessClient.TryShow("Downloading New Mods"); SeamlessClient.TryShow("Downloading New Mods");
MyWorkshop.DownloadModsAsync(Target, ModDownloadingFinished); MyWorkshop.DownloadModsAsync(TargetLoadMods, ModDownloadingFinished);
} }
@@ -71,7 +102,8 @@ namespace SeamlessClientPlugin.SeamlessTransfer
} }
} }
public static void ReadyModSwitch()
public static void ReadyModSwitch(MyObjectBuilder_Checkpoint checkpoint, MyObjectBuilder_Sector sector)
{ {
while (!FinishedDownloadingMods) while (!FinishedDownloadingMods)
@@ -91,12 +123,46 @@ namespace SeamlessClientPlugin.SeamlessTransfer
if (!DownloadSuccess) if (!DownloadSuccess)
return; return;
/*
MySession.Static.ScriptManager.LoadData();
//Create new script manager?
ScriptManager.SetValue(MySession.Static, new MyScriptManager());
MyGuiTextures.Static.Unload();
MySession.Static.ScriptManager.Init(checkpoint.ScriptManagerData);
MyDefinitionManager.Static.LoadData(TargetServerMods); MyDefinitionManager.Static.LoadData(TargetServerMods);
PrepareBaseSession.Invoke(null, new object[] { sector });
MyLocalCache.PreloadLocalInventoryConfig(); MyLocalCache.PreloadLocalInventoryConfig();
SeamlessClient.TryShow("Finished transfering!");
*/
//SeamlessClient.TryShow("Finished transfering!");
// PrepareBaseSession.Invoke(MySession.Static, new object[] { TargetServerMods, null });
}
private static void UnloadOldScripts()
{
// MySandboxGame.Log.WriteLine(string.Format("Script loaded: {0}", value.FullName));
int amount = 0;
foreach (var mod in TargetUnLoadMods)
{
var val = MySession.Static.ScriptManager.Scripts.FirstOrDefault(x => x.Value.FullName.Contains(mod.PublishedFileId.ToString()));
MySession.Static.ScriptManager.Scripts.Remove(val.Key);
amount++;
}
SeamlessClient.TryShow($"Removed {amount} old scripts!");
} }

View File

@@ -0,0 +1,83 @@
using Sandbox;
using Sandbox.Game.World;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SeamlessClientPlugin.SeamlessTransfer
{
public class MyScriptManagerLoader
{
/*
public void LoadData(MyScriptManager __instance)
{
MySandboxGame.Log.WriteLine("MyScriptManager.LoadData() - START");
MySandboxGame.Log.IncreaseIndent();
MyScriptManager.Static = __instance;
__instance.Scripts.Clear();
__instance.EntityScripts.Clear();
__instance.SubEntityScripts.Clear();
TryAddEntityScripts(MyModContext.BaseGame, MyPlugins.SandboxAssembly);
TryAddEntityScripts(MyModContext.BaseGame, MyPlugins.SandboxGameAssembly);
if (MySession.Static.CurrentPath != null)
{
LoadScripts(MySession.Static.CurrentPath, MyModContext.BaseGame);
}
if (MySession.Static.Mods != null)
{
bool isServer = Sync.IsServer;
foreach (MyObjectBuilder_Checkpoint.ModItem mod in MySession.Static.Mods)
{
bool flag = false;
if (mod.IsModData())
{
ListReader<string> tags = mod.GetModData().Tags;
if (tags.Contains(MySteamConstants.TAG_SERVER_SCRIPTS) && !isServer)
{
continue;
}
flag = tags.Contains(MySteamConstants.TAG_NO_SCRIPTS);
}
MyModContext myModContext = (MyModContext)mod.GetModContext();
try
{
LoadScripts(mod.GetPath(), myModContext);
}
catch (MyLoadingRuntimeCompilationNotSupportedException)
{
if (flag)
{
MyVRage.Platform.Scripting.ReportIncorrectBehaviour(MyCommonTexts.ModRuleViolation_RuntimeScripts);
continue;
}
throw;
}
catch (Exception ex2)
{
MyLog.Default.WriteLine(string.Format("Fatal error compiling {0}:{1} - {2}. This item is likely not a mod and should be removed from the mod list.", myModContext.ModServiceName, myModContext.ModId, myModContext.ModName));
MyLog.Default.WriteLine(ex2);
throw;
}
}
}
foreach (Assembly value in Scripts.Values)
{
if (MyFakes.ENABLE_TYPES_FROM_MODS)
{
MyGlobalTypeMetadata.Static.RegisterAssembly(value);
}
MySandboxGame.Log.WriteLine(string.Format("Script loaded: {0}", value.FullName));
}
MyTextSurfaceScriptFactory.LoadScripts();
MyUseObjectFactory.RegisterAssemblyTypes(Scripts.Values.ToArray());
MySandboxGame.Log.DecreaseIndent();
MySandboxGame.Log.WriteLine("MyScriptManager.LoadData() - END");
}
*/
}
}

View File

@@ -22,6 +22,7 @@ using System.Reflection;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using VRage; using VRage;
using VRage.Collections;
using VRage.Game; using VRage.Game;
using VRage.Game.Components; using VRage.Game.Components;
using VRage.Game.ModAPI; using VRage.Game.ModAPI;
@@ -42,12 +43,15 @@ namespace SeamlessClientPlugin.SeamlessTransfer
private string OldArmorSkin { get; set; } = string.Empty; private string OldArmorSkin { get; set; } = string.Empty;
public SwitchServers(MyGameServerItem TargetServer, MyObjectBuilder_World TargetWorld) public SwitchServers(MyGameServerItem TargetServer, MyObjectBuilder_World TargetWorld)
{ {
this.TargetServer = TargetServer; this.TargetServer = TargetServer;
this.TargetWorld = TargetWorld; this.TargetWorld = TargetWorld;
ModLoader.DownloadNewMods(TargetWorld.Checkpoint.Mods); //ModLoader.DownloadNewMods(TargetWorld.Checkpoint.Mods);
} }
@@ -61,7 +65,7 @@ namespace SeamlessClientPlugin.SeamlessTransfer
MySession.Static.SetCameraController(MyCameraControllerEnum.SpectatorFixed); MySession.Static.SetCameraController(MyCameraControllerEnum.SpectatorFixed);
UnloadCurrentServer(); UnloadCurrentServer();
SetNewMultiplayerClient(); SetNewMultiplayerClient();
SeamlessClient.IsSwitching = false;
}, "SeamlessClient"); }, "SeamlessClient");
@@ -119,23 +123,75 @@ namespace SeamlessClientPlugin.SeamlessTransfer
{ {
MyHud.Chat.RegisterChat(MyMultiplayer.Static);
SetWorldSettings();
LoadConnectedClients(); LoadConnectedClients();
LoadOnlinePlayers(); LoadOnlinePlayers();
SetWorldSettings();
ModLoader.ReadyModSwitch();
//ModLoader.ReadyModSwitch(TargetWorld.Checkpoint, TargetWorld.Sector);
MySector.InitEnvironmentSettings(TargetWorld.Sector.Environment); MySector.InitEnvironmentSettings(TargetWorld.Sector.Environment);
MyModAPIHelper.Initialize();
//MethodInfo inf = typeof(MySession).GetMethod("LoadGameDefinition", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[1] { typeof(MyObjectBuilder_Checkpoint) }, null);
//inf.Invoke(MySession.Static, new object[] { TargetWorld.Checkpoint });
/*
CachingDictionary<Type, MySessionComponentBase> dic = (CachingDictionary<Type, MySessionComponentBase>)typeof(MySession).GetField("m_sessionComponents", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(MySession.Static);
foreach (var item in dic.ToList())
{
if (item.Value.ModContext != null)
dic.Remove(item.Key, true);
}
foreach (KeyValuePair<MyModContext, HashSet<MyStringId>> item in MyScriptManager.Static.ScriptsPerMod)
{
MyStringId key = item.Value.First();
MySession.Static.RegisterComponentsFromAssembly(MyScriptManager.Static.Scripts[key], true, item.Key);
}
List<MySessionComponentBase> dic1 = (List<MySessionComponentBase>)typeof(MySession).GetField("m_sessionComponentForDrawAsync", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(MySession.Static);
List<MySessionComponentBase> dic2 = (List<MySessionComponentBase>)typeof(MySession).GetField("m_sessionComponentForDraw", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(MySession.Static);
dic1.Clear();
dic2.Clear();
foreach (MySessionComponentBase value in dic.Values)
{
if (value.ModContext == null || value.ModContext.IsBaseGame)
{
dic1.Add(value);
}
else
{
dic2.Add(value);
}
}
*/
string text = ((!string.IsNullOrEmpty(TargetWorld.Checkpoint.CustomSkybox)) ? TargetWorld.Checkpoint.CustomSkybox : MySector.EnvironmentDefinition.EnvironmentTexture); string text = ((!string.IsNullOrEmpty(TargetWorld.Checkpoint.CustomSkybox)) ? TargetWorld.Checkpoint.CustomSkybox : MySector.EnvironmentDefinition.EnvironmentTexture);
MyRenderProxy.PreloadTextures(new string[1] { text }, TextureType.CubeMap); MyRenderProxy.PreloadTextures(new string[1] { text }, TextureType.CubeMap);
MyModAPIHelper.Initialize();
MySession.Static.LoadDataComponents(); MySession.Static.LoadDataComponents();
//MySession.Static.LoadObjectBuildersComponents(TargetWorld.Checkpoint.SessionComponents);
MyModAPIHelper.Initialize();
// MySession.Static.LoadObjectBuildersComponents(TargetWorld.Checkpoint.SessionComponents); // MySession.Static.LoadObjectBuildersComponents(TargetWorld.Checkpoint.SessionComponents);
@@ -152,7 +208,21 @@ namespace SeamlessClientPlugin.SeamlessTransfer
StartEntitySync(); StartEntitySync();
MyHud.Chat.RegisterChat(MyMultiplayer.Static);
/*
foreach (MySessionComponentBase value in dic.Values)
{
if(value.ModContext != null)
{
SeamlessClient.TryShow($"{value.Definition?.ToString()} - {value.ModContext.ModName}");
value.BeforeStart();
}
}
*/
// Allow the game to start proccessing incoming messages in the buffer // Allow the game to start proccessing incoming messages in the buffer
MyMultiplayer.Static.StartProcessingClientMessages(); MyMultiplayer.Static.StartProcessingClientMessages();
@@ -163,6 +233,9 @@ namespace SeamlessClientPlugin.SeamlessTransfer
} }
private void LoadOnlinePlayers() private void LoadOnlinePlayers()
{ {
//Get This players ID //Get This players ID
@@ -432,6 +505,8 @@ namespace SeamlessClientPlugin.SeamlessTransfer
//Close any respawn screens that are open //Close any respawn screens that are open
MyGuiScreenMedicals.Close(); MyGuiScreenMedicals.Close();
//MySession.Static.UnloadDataComponents();
} }
private void RemoveOldEntities() private void RemoveOldEntities()

View File

@@ -3,6 +3,7 @@ using Sandbox.Engine.Analytics;
using Sandbox.Engine.Multiplayer; using Sandbox.Engine.Multiplayer;
using Sandbox.Engine.Networking; using Sandbox.Engine.Networking;
using Sandbox.Game; using Sandbox.Game;
using Sandbox.Game.Entities;
using Sandbox.Game.Gui; using Sandbox.Game.Gui;
using Sandbox.Game.Multiplayer; using Sandbox.Game.Multiplayer;
using Sandbox.Game.World; using Sandbox.Game.World;
@@ -19,8 +20,10 @@ using System.Reflection;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using VRage; using VRage;
using VRage.Collections;
using VRage.FileSystem; using VRage.FileSystem;
using VRage.Game; using VRage.Game;
using VRage.Game.Entity;
using VRage.GameServices; using VRage.GameServices;
using VRage.Network; using VRage.Network;
using VRage.Utils; using VRage.Utils;
@@ -79,6 +82,8 @@ namespace SeamlessClientPlugin.SeamlessTransfer
private static FieldInfo MBuffer;
public static void GetPatches() public static void GetPatches()
{ {
//Get reflected values and store them //Get reflected values and store them
@@ -101,7 +106,7 @@ namespace SeamlessClientPlugin.SeamlessTransfer
RemoteAdminSettings = GetField(typeof(MySession), "m_remoteAdminSettings", BindingFlags.Instance | BindingFlags.NonPublic); RemoteAdminSettings = GetField(typeof(MySession), "m_remoteAdminSettings", BindingFlags.Instance | BindingFlags.NonPublic);
MPlayerGPSCollection = GetField(typeof(MyPlayerCollection), "m_players", BindingFlags.Instance | BindingFlags.NonPublic); MPlayerGPSCollection = GetField(typeof(MyPlayerCollection), "m_players", BindingFlags.Instance | BindingFlags.NonPublic);
MBuffer = GetField(MyTransportLayerType, "m_buffer", BindingFlags.Instance | BindingFlags.NonPublic);
/* Get Methods */ /* Get Methods */
@@ -122,12 +127,20 @@ namespace SeamlessClientPlugin.SeamlessTransfer
//Test patches
MethodInfo SetPlayerDed = GetMethod(typeof(MyPlayerCollection), "SetPlayerDeadInternal", BindingFlags.Instance | BindingFlags.NonPublic);
Patcher.Patch(LoadingScreenDraw, prefix: new HarmonyMethod(GetPatchMethod(nameof(DrawInternal)))); Patcher.Patch(LoadingScreenDraw, prefix: new HarmonyMethod(GetPatchMethod(nameof(DrawInternal))));
Patcher.Patch(OnJoin, postfix: new HarmonyMethod(GetPatchMethod(nameof(OnUserJoined)))); Patcher.Patch(OnJoin, postfix: new HarmonyMethod(GetPatchMethod(nameof(OnUserJoined))));
Patcher.Patch(LoadingAction, prefix: new HarmonyMethod(GetPatchMethod(nameof(LoadMultiplayerSession)))); Patcher.Patch(LoadingAction, prefix: new HarmonyMethod(GetPatchMethod(nameof(LoadMultiplayerSession))));
Patcher.Patch(SetPlayerDed, prefix: new HarmonyMethod(GetPatchMethod(nameof(SetPlayerDeadInternal))));
@@ -426,5 +439,68 @@ namespace SeamlessClientPlugin.SeamlessTransfer
} }
private static bool SetPlayerDeadInternal(MyPlayerCollection __instance, bool __result, ulong playerSteamId, int playerSerialId, bool deadState, bool resetIdentity)
{
FieldInfo ControlledEntities = typeof(MyPlayerCollection).GetField("m_controlledEntities", BindingFlags.Instance | BindingFlags.NonPublic);
MethodInfo RemoveControlledEntity = typeof(MyPlayerCollection).GetMethod("RemoveControlledEntityInternal", BindingFlags.Instance | BindingFlags.NonPublic);
CachingDictionary<long, MyPlayer.PlayerId> m_controlledEntities = (CachingDictionary<long, MyPlayer.PlayerId>)ControlledEntities.GetValue(__instance);
MyPlayer.PlayerId id = new MyPlayer.PlayerId(playerSteamId, playerSerialId);
MyPlayer playerById = Sync.Players.GetPlayerById(id);
if (playerById == null)
{
__result = false;
return false;
}
if (playerById.Identity == null)
{
__result = false;
return false;
}
playerById.Identity.SetDead(resetIdentity);
if (deadState)
{
SeamlessClient.TryShow($"Player: {playerSteamId}, SerialID: {playerSerialId} deadstate: {deadState}, resetIdentity: {resetIdentity}");
playerById.Controller.TakeControl(null);
foreach (KeyValuePair<long, MyPlayer.PlayerId> controlledEntity in m_controlledEntities)
{
if (controlledEntity.Value == playerById.Id)
{
MyEntity entity = null;
MyEntities.TryGetEntityById(controlledEntity.Key, out entity);
if (entity != null)
{
//RemoveControlledEntityInternal(entity, false);
RemoveControlledEntity.Invoke(__instance, new object[] { entity, false });
}
}
}
m_controlledEntities.ApplyRemovals();
if (Sync.Clients.LocalClient != null && playerById == Sync.Clients.LocalClient.FirstPlayer)
{
MyPlayer P = Sync.Clients.LocalClient.FirstPlayer;
SeamlessClient.TryShow($"FirstPlayerName: {P.DisplayName}, ID: {P.Id.SteamId}, Serial: {P.Id.SerialId}");
SeamlessClient.TryShow(Environment.StackTrace);
MyPlayerCollection.RequestLocalRespawn();
}
}
__result = true;
return false;
}
} }
} }