Some changes to support mod reloading. (ModAPI messages are still not being recieved properly in nexus mod) this could be a serverside issue but unknown currently
This commit is contained in:
140
Components/ModReloader.cs
Normal file
140
Components/ModReloader.cs
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
using Sandbox.Game.World;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using VRage.Collections;
|
||||||
|
using VRage.Game.Components;
|
||||||
|
using VRage.Game;
|
||||||
|
using static Sandbox.Game.World.MySession;
|
||||||
|
using System.IO;
|
||||||
|
using VRage.Scripting;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using HarmonyLib;
|
||||||
|
using SeamlessClient.Models;
|
||||||
|
using VRage;
|
||||||
|
|
||||||
|
namespace SeamlessClient.Components
|
||||||
|
{
|
||||||
|
/* Keen compiles mods into assemblies. Unfortunetly, we cannot trust these assemblies after calling unload due to changed variables as the ASSEMBLY level. aka default types
|
||||||
|
* To fix this we need to go further and actually attempt to call the assembly to be unloaded, then reload the assembly and get the update type to be initiated.
|
||||||
|
*
|
||||||
|
* This might be a time consuming process? We will just have to see. Ideally would like to keep the whole mod script reloading down to like less than a second or two for N amount of mods. Might
|
||||||
|
* be able to speed up loading compared to keens way too
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ModReloader
|
||||||
|
{
|
||||||
|
Dictionary<Type, MyModContext> sessionsBeingRemoved = new Dictionary<Type, MyModContext>();
|
||||||
|
private static ModCache modCache = new ModCache();
|
||||||
|
|
||||||
|
|
||||||
|
public void Patch(Harmony patcher)
|
||||||
|
{
|
||||||
|
MethodInfo assemblyLoad = AccessTools.Method(typeof(Assembly), "Load", new Type[] { typeof(byte[]) });
|
||||||
|
|
||||||
|
var patchAsmLoad = PatchUtils.GetMethod(this.GetType(), "AssemblyLoad");
|
||||||
|
patcher.Patch(assemblyLoad, postfix: patchAsmLoad);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AssemblyLoad(Assembly __result, byte[] rawAssembly)
|
||||||
|
{
|
||||||
|
//This will get all of the mods being loading into the game. Worry about saving/loading later
|
||||||
|
modCache.AddModToCache(__result, rawAssembly);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void UnloadModSessionComponents()
|
||||||
|
{
|
||||||
|
CachingDictionary<Type, MySessionComponentBase> sessionComponents = (CachingDictionary<Type, MySessionComponentBase>)typeof(MySession).GetField("m_sessionComponents", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(MySession.Static);
|
||||||
|
List<MySessionComponentBase> m_loadOrder = (List<MySessionComponentBase>)typeof(MySession).GetField("m_loadOrder", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(MySession.Static);
|
||||||
|
List<MySessionComponentBase> m_sessionComponentForDraw = (List<MySessionComponentBase>)typeof(MySession).GetField("m_sessionComponentForDraw", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(MySession.Static);
|
||||||
|
Dictionary<int, SortedSet<MySessionComponentBase>> m_sessionComponentsForUpdate = (Dictionary<int, SortedSet<MySessionComponentBase>>)typeof(MySession).GetField("m_sessionComponentsForUpdate", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(MySession.Static);
|
||||||
|
|
||||||
|
foreach (var component in sessionComponents)
|
||||||
|
{
|
||||||
|
if (component.Value.ModContext != null && !component.Value.ModContext.IsBaseGame)
|
||||||
|
{
|
||||||
|
Seamless.TryShow($"{component.Key.FullName}");
|
||||||
|
sessionsBeingRemoved.Add(component.Key, component.Value.ModContext as MyModContext);
|
||||||
|
|
||||||
|
//Calls the component to be unloaded
|
||||||
|
component.Value.UnloadDataConditional();
|
||||||
|
m_loadOrder.Remove(component.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Remove all */
|
||||||
|
|
||||||
|
//Remove from session components
|
||||||
|
foreach (var item in sessionsBeingRemoved)
|
||||||
|
sessionComponents.Remove(item.Key, true);
|
||||||
|
|
||||||
|
//Remove from draw
|
||||||
|
m_sessionComponentForDraw.RemoveAll(x => x.ModContext != null && !x.ModContext.IsBaseGame);
|
||||||
|
|
||||||
|
|
||||||
|
//Remove from update
|
||||||
|
foreach (var kvp in m_sessionComponentsForUpdate)
|
||||||
|
{
|
||||||
|
kvp.Value.RemoveWhere(x => sessionsBeingRemoved.ContainsKey(x.ComponentType));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddModComponents()
|
||||||
|
{
|
||||||
|
List<MySessionComponentBase> m_sessionComponentForDraw = (List<MySessionComponentBase>)typeof(MySession).GetField("m_sessionComponentForDraw", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(MySession.Static);
|
||||||
|
|
||||||
|
|
||||||
|
List<MySessionComponentBase> newSessions = new List<MySessionComponentBase>();
|
||||||
|
int newLoadedmods = 0;
|
||||||
|
foreach (var item in sessionsBeingRemoved)
|
||||||
|
{
|
||||||
|
|
||||||
|
//If it fails, skip the mod? Or try to use old type? (May Fail to load)
|
||||||
|
if (!modCache.TryGetModAssembly(item.Value.ModItem.PublishedFileId, out Assembly newModAssembly))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
|
||||||
|
Type newType = newModAssembly.GetType(item.Key.FullName);
|
||||||
|
MySessionComponentBase mySessionComponentBase = (MySessionComponentBase)Activator.CreateInstance(newType);
|
||||||
|
mySessionComponentBase.ModContext = item.Value;
|
||||||
|
|
||||||
|
|
||||||
|
MySession.Static.RegisterComponent(mySessionComponentBase, mySessionComponentBase.UpdateOrder, mySessionComponentBase.Priority);
|
||||||
|
newSessions.Add(mySessionComponentBase);
|
||||||
|
|
||||||
|
m_sessionComponentForDraw.Add(mySessionComponentBase);
|
||||||
|
newLoadedmods++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Will check toi see if session is actually loaded before calling load
|
||||||
|
MySession.Static.LoadDataComponents();
|
||||||
|
sessionsBeingRemoved.Clear();
|
||||||
|
typeof(MySession).GetMethod("InitDataComponents", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(MySession.Static, null);
|
||||||
|
|
||||||
|
//Call before start
|
||||||
|
foreach (var session in newSessions)
|
||||||
|
session.BeforeStart();
|
||||||
|
|
||||||
|
|
||||||
|
Seamless.TryShow($"Loaded {newLoadedmods} mods");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@@ -38,6 +38,9 @@ using VRageRender.Effects;
|
|||||||
using VRage.Scripting;
|
using VRage.Scripting;
|
||||||
using VRage.Utils;
|
using VRage.Utils;
|
||||||
using SpaceEngineers.Game.World;
|
using SpaceEngineers.Game.World;
|
||||||
|
using VRage.Collections;
|
||||||
|
using VRage.Game.Components;
|
||||||
|
using System.CodeDom;
|
||||||
|
|
||||||
namespace SeamlessClient.Components
|
namespace SeamlessClient.Components
|
||||||
{
|
{
|
||||||
@@ -66,10 +69,15 @@ namespace SeamlessClient.Components
|
|||||||
private static bool StartPacketCheck = false;
|
private static bool StartPacketCheck = false;
|
||||||
|
|
||||||
private bool keepGrid = false;
|
private bool keepGrid = false;
|
||||||
|
private ModReloader modReload;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public SeamlessSwitcher()
|
public SeamlessSwitcher()
|
||||||
{
|
{
|
||||||
Instance = this;
|
Instance = this;
|
||||||
|
modReload = new ModReloader();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -106,7 +114,7 @@ namespace SeamlessClient.Components
|
|||||||
patcher.Patch(_SendRPC, prefix: preSendRPC);
|
patcher.Patch(_SendRPC, prefix: preSendRPC);
|
||||||
|
|
||||||
|
|
||||||
|
modReload.Patch(patcher);
|
||||||
base.Patch(patcher);
|
base.Patch(patcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,6 +151,8 @@ namespace SeamlessClient.Components
|
|||||||
/* Set New Multiplayer Stuff */
|
/* Set New Multiplayer Stuff */
|
||||||
_MyGameServerItemProperty.SetValue(MyMultiplayer.Static, TargetServer);
|
_MyGameServerItemProperty.SetValue(MyMultiplayer.Static, TargetServer);
|
||||||
_MultiplayerServerID.SetValue(MyMultiplayer.Static, TargetServer.SteamID);
|
_MultiplayerServerID.SetValue(MyMultiplayer.Static, TargetServer.SteamID);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Connect To Server */
|
/* Connect To Server */
|
||||||
MyGameService.ConnectToServer(TargetServer, delegate (JoinResult joinResult)
|
MyGameService.ConnectToServer(TargetServer, delegate (JoinResult joinResult)
|
||||||
@@ -184,7 +194,7 @@ namespace SeamlessClient.Components
|
|||||||
typeof(MyMultiplayerBase).GetMethod("SendControlMessage", BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(typeof(MyControlDisconnectedMsg)).Invoke(MyMultiplayer.Static, new object[] { Sync.ServerId, myControlDisconnectedMsg, true });
|
typeof(MyMultiplayerBase).GetMethod("SendControlMessage", BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(typeof(MyControlDisconnectedMsg)).Invoke(MyMultiplayer.Static, new object[] { Sync.ServerId, myControlDisconnectedMsg, true });
|
||||||
MyGameService.Peer2Peer.CloseSession(Sync.ServerId);
|
MyGameService.Peer2Peer.CloseSession(Sync.ServerId);
|
||||||
MyGameService.DisconnectFromServer();
|
MyGameService.DisconnectFromServer();
|
||||||
|
MyGameService.ClearCache();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -199,6 +209,8 @@ namespace SeamlessClient.Components
|
|||||||
|
|
||||||
UnloadOldEntities();
|
UnloadOldEntities();
|
||||||
ResetReplicationTime(false);
|
ResetReplicationTime(false);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//MyMultiplayer.Static.ReplicationLayer.Disconnect();
|
//MyMultiplayer.Static.ReplicationLayer.Disconnect();
|
||||||
//MyMultiplayer.Static.ReplicationLayer.Dispose();
|
//MyMultiplayer.Static.ReplicationLayer.Dispose();
|
||||||
@@ -284,6 +296,10 @@ namespace SeamlessClient.Components
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void ClearClientReplicables()
|
private void ClearClientReplicables()
|
||||||
{
|
{
|
||||||
MyReplicationClient replicationClient = (MyReplicationClient)MyMultiplayer.Static.ReplicationLayer;
|
MyReplicationClient replicationClient = (MyReplicationClient)MyMultiplayer.Static.ReplicationLayer;
|
||||||
@@ -310,6 +326,8 @@ namespace SeamlessClient.Components
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
Seamless.TryShow($"OnUserJoin! Result: {joinResult}");
|
Seamless.TryShow($"OnUserJoin! Result: {joinResult}");
|
||||||
|
modReload.UnloadModSessionComponents();
|
||||||
|
|
||||||
LoadDestinationServer();
|
LoadDestinationServer();
|
||||||
|
|
||||||
|
|
||||||
@@ -324,6 +342,8 @@ namespace SeamlessClient.Components
|
|||||||
Seamless.TryShow($"5 NexusMajor: {Seamless.NexusVersion.Major} - ConrolledEntity {MySession.Static.ControlledEntity == null} - HumanPlayer {MySession.Static.LocalHumanPlayer == null} - Character {MySession.Static.LocalCharacter == null}");
|
Seamless.TryShow($"5 NexusMajor: {Seamless.NexusVersion.Major} - ConrolledEntity {MySession.Static.ControlledEntity == null} - HumanPlayer {MySession.Static.LocalHumanPlayer == null} - Character {MySession.Static.LocalCharacter == null}");
|
||||||
Seamless.TryShow("Starting new MP Client!");
|
Seamless.TryShow("Starting new MP Client!");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* On Server Successfull Join
|
/* On Server Successfull Join
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
@@ -350,33 +370,28 @@ namespace SeamlessClient.Components
|
|||||||
|
|
||||||
typeof(MySandboxGame).GetField("m_pauseStackCount", BindingFlags.Static | BindingFlags.NonPublic).SetValue(null, 0);
|
typeof(MySandboxGame).GetField("m_pauseStackCount", BindingFlags.Static | BindingFlags.NonPublic).SetValue(null, 0);
|
||||||
|
|
||||||
|
|
||||||
Seamless.TryShow($"6 NexusMajor: {Seamless.NexusVersion.Major} - ConrolledEntity {MySession.Static.ControlledEntity == null} - HumanPlayer {MySession.Static.LocalHumanPlayer == null} - Character {MySession.Static.LocalCharacter == null}");
|
Seamless.TryShow($"6 NexusMajor: {Seamless.NexusVersion.Major} - ConrolledEntity {MySession.Static.ControlledEntity == null} - HumanPlayer {MySession.Static.LocalHumanPlayer == null} - Character {MySession.Static.LocalCharacter == null}");
|
||||||
Seamless.TryShow($"6 Streaming: {clienta.HasPendingStreamingReplicables} - LastMessage: {clienta.LastMessageFromServer}");
|
Seamless.TryShow($"6 Streaming: {clienta.HasPendingStreamingReplicables} - LastMessage: {clienta.LastMessageFromServer}");
|
||||||
|
|
||||||
|
modReload.AddModComponents();
|
||||||
|
SendClientReady();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ResetReplicationTime(true);
|
ResetReplicationTime(true);
|
||||||
//MyPlayerCollection.ChangePlayerCharacter(MySession.Static.LocalHumanPlayer, MySession.Static.LocalCharacter, MySession.Static.LocalCharacter);
|
//MyPlayerCollection.ChangePlayerCharacter(MySession.Static.LocalHumanPlayer, MySession.Static.LocalCharacter, MySession.Static.LocalCharacter);
|
||||||
// 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();
|
||||||
//Send Client Ready
|
//Send Client Ready
|
||||||
ClientReadyDataMsg clientReadyDataMsg = default(ClientReadyDataMsg);
|
|
||||||
clientReadyDataMsg.ForcePlayoutDelayBuffer = MyFakes.ForcePlayoutDelayBuffer;
|
|
||||||
clientReadyDataMsg.UsePlayoutDelayBufferForCharacter = true;
|
|
||||||
clientReadyDataMsg.UsePlayoutDelayBufferForJetpack = true;
|
|
||||||
clientReadyDataMsg.UsePlayoutDelayBufferForGrids = true;
|
|
||||||
ClientReadyDataMsg msg = clientReadyDataMsg;
|
|
||||||
clienta.SendClientReady(ref msg);
|
|
||||||
|
|
||||||
PreventRPC = false;
|
PreventRPC = false;
|
||||||
//_ClearTransportLayer.Invoke(_TransportLayer.GetValue(MyMultiplayer.Static.SyncLayer), null);
|
//_ClearTransportLayer.Invoke(_TransportLayer.GetValue(MyMultiplayer.Static.SyncLayer), null);
|
||||||
|
|
||||||
|
|
||||||
StartEntitySync();
|
StartEntitySync();
|
||||||
Seamless.SendSeamlessVersion();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -388,23 +403,25 @@ namespace SeamlessClient.Components
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SendClientReady()
|
||||||
|
{
|
||||||
|
MyReplicationClient clienta = (MyReplicationClient)MyMultiplayer.Static.ReplicationLayer;
|
||||||
|
ClientReadyDataMsg clientReadyDataMsg = default(ClientReadyDataMsg);
|
||||||
|
clientReadyDataMsg.ForcePlayoutDelayBuffer = MyFakes.ForcePlayoutDelayBuffer;
|
||||||
|
clientReadyDataMsg.UsePlayoutDelayBufferForCharacter = true;
|
||||||
|
clientReadyDataMsg.UsePlayoutDelayBufferForJetpack = true;
|
||||||
|
clientReadyDataMsg.UsePlayoutDelayBufferForGrids = true;
|
||||||
|
ClientReadyDataMsg msg = clientReadyDataMsg;
|
||||||
|
clienta.SendClientReady(ref msg);
|
||||||
|
|
||||||
|
Seamless.SendSeamlessVersion();
|
||||||
|
}
|
||||||
|
|
||||||
private void StartEntitySync()
|
private void StartEntitySync()
|
||||||
{
|
{
|
||||||
Seamless.TryShow("Requesting Player From Server");
|
Seamless.TryShow("Requesting Player From Server");
|
||||||
|
|
||||||
Sync.Players.RequestNewPlayer(Sync.MyId, 0, MyGameService.UserName, null, true, true);
|
|
||||||
if (!Sandbox.Engine.Platform.Game.IsDedicated && MySession.Static.LocalHumanPlayer == null)
|
|
||||||
{
|
|
||||||
Seamless.TryShow("RequestNewPlayer");
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (MySession.Static.ControlledEntity == null && Sync.IsServer && !Sandbox.Engine.Platform.Game.IsDedicated)
|
|
||||||
{
|
|
||||||
Seamless.TryShow("ControlledObject was null, respawning character");
|
|
||||||
//m_cameraAwaitingEntity = true;
|
|
||||||
MyPlayerCollection.RequestLocalRespawn();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -438,7 +455,19 @@ namespace SeamlessClient.Components
|
|||||||
MySandboxGame.AreClipmapsReady = false;
|
MySandboxGame.AreClipmapsReady = false;
|
||||||
}
|
}
|
||||||
MyMultiplayer.Static.PendingReplicablesDone -= Static_PendingReplicablesDone;
|
MyMultiplayer.Static.PendingReplicablesDone -= Static_PendingReplicablesDone;
|
||||||
|
|
||||||
|
|
||||||
|
Sync.Players.RequestNewPlayer(Sync.MyId, 0, MyGameService.UserName, null, true, true);
|
||||||
|
if (!Sandbox.Engine.Platform.Game.IsDedicated && MySession.Static.LocalHumanPlayer == null)
|
||||||
|
{
|
||||||
|
Seamless.TryShow("RequestNewPlayer");
|
||||||
|
}
|
||||||
|
else if (MySession.Static.ControlledEntity == null && Sync.IsServer && !Sandbox.Engine.Platform.Game.IsDedicated)
|
||||||
|
{
|
||||||
|
Seamless.TryShow("ControlledObject was null, respawning character");
|
||||||
|
//m_cameraAwaitingEntity = true;
|
||||||
|
MyPlayerCollection.RequestLocalRespawn();
|
||||||
|
}
|
||||||
|
|
||||||
//Try find existing seat:
|
//Try find existing seat:
|
||||||
//bool found = (bool)typeof(MySpaceRespawnComponent).GetMethod("TryFindExistingCharacter", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[] { MySession.Static.LocalHumanPlayer });
|
//bool found = (bool)typeof(MySpaceRespawnComponent).GetMethod("TryFindExistingCharacter", BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, new object[] { MySession.Static.LocalHumanPlayer });
|
||||||
|
28
Models/ModByte.cs
Normal file
28
Models/ModByte.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using ProtoBuf;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SeamlessClient.Models
|
||||||
|
{
|
||||||
|
[ProtoContract]
|
||||||
|
public class ModByte
|
||||||
|
{
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public ulong ModID { get; set; }
|
||||||
|
|
||||||
|
[ProtoMember(2)]
|
||||||
|
public byte[] AssemblyBytes { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Assembly GetNewAssembly()
|
||||||
|
{
|
||||||
|
return Assembly.Load(AssemblyBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
83
Models/ModCache.cs
Normal file
83
Models/ModCache.cs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
using ProtoBuf;
|
||||||
|
using SeamlessClient.Utilities;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using VRage.Serialization;
|
||||||
|
|
||||||
|
namespace SeamlessClient.Models
|
||||||
|
{
|
||||||
|
[ProtoContract]
|
||||||
|
public class ModCache
|
||||||
|
{
|
||||||
|
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public List<ModByte> CachedMods { get; set; } = new List<ModByte>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void AddModToCache(Assembly asm, byte[] raw)
|
||||||
|
{
|
||||||
|
//Get the modID from the loaded assembly name
|
||||||
|
ulong? modid = GetLeadingNumber(asm.FullName);
|
||||||
|
if (!modid.HasValue || modid.Value == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//Check to see if the loading mod is already in our cache
|
||||||
|
if(CachedMods.Any(x => x.ModID == modid.Value))
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
ModByte mod = new ModByte();
|
||||||
|
mod.ModID = modid.Value;
|
||||||
|
mod.AssemblyBytes = raw;
|
||||||
|
|
||||||
|
CachedMods.Add(mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SaveToFile(ModCache cache)
|
||||||
|
{
|
||||||
|
byte[] data = NetUtils.Serialize(cache);
|
||||||
|
File.WriteAllBytes("", data);
|
||||||
|
}
|
||||||
|
public ModCache LoadFromFile()
|
||||||
|
{
|
||||||
|
byte[] data = File.ReadAllBytes("");
|
||||||
|
return NetUtils.Deserialize<ModCache>(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetModAssembly(ulong modid, out Assembly asm)
|
||||||
|
{
|
||||||
|
asm = null;
|
||||||
|
if (modid == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ModByte mod = CachedMods.FirstOrDefault(x => x.ModID == modid);
|
||||||
|
if(mod == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//Compiles new assembly
|
||||||
|
try
|
||||||
|
{
|
||||||
|
asm = mod.GetNewAssembly();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static ulong? GetLeadingNumber(string assemblyName)
|
||||||
|
{
|
||||||
|
Match match = Regex.Match(assemblyName, @"^\d+");
|
||||||
|
return match.Success ? ulong.Parse(match.Value) : (ulong?)null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -113,6 +113,10 @@ namespace SeamlessClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void MessageHandler2(ushort packetID, byte[] data, ulong sender, bool fromServer)
|
||||||
|
{
|
||||||
|
Seamless.TryShow("Recieved visuals");
|
||||||
|
}
|
||||||
|
|
||||||
private static void MessageHandler(ushort packetID, byte[] data, ulong sender, bool fromServer)
|
private static void MessageHandler(ushort packetID, byte[] data, ulong sender, bool fromServer)
|
||||||
{
|
{
|
||||||
@@ -174,6 +178,7 @@ namespace SeamlessClient
|
|||||||
if (!Initilized)
|
if (!Initilized)
|
||||||
{
|
{
|
||||||
MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(SeamlessClientNetId, MessageHandler);
|
MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(SeamlessClientNetId, MessageHandler);
|
||||||
|
MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(2938, MessageHandler2);
|
||||||
InitilizeComponents();
|
InitilizeComponents();
|
||||||
|
|
||||||
Initilized = true;
|
Initilized = true;
|
||||||
|
@@ -34,6 +34,14 @@
|
|||||||
<Reference Include="0Harmony, Version=2.3.3.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="0Harmony, Version=2.3.3.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>packages\Lib.Harmony.2.3.3\lib\net48\0Harmony.dll</HintPath>
|
<HintPath>packages\Lib.Harmony.2.3.3\lib\net48\0Harmony.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.CodeAnalysis">
|
||||||
|
<HintPath>GameBinaries\Microsoft.CodeAnalysis.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Microsoft.CodeAnalysis.CSharp">
|
||||||
|
<HintPath>GameBinaries\Microsoft.CodeAnalysis.CSharp.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="NLog">
|
<Reference Include="NLog">
|
||||||
<HintPath>GameBinaries\NLog.dll</HintPath>
|
<HintPath>GameBinaries\NLog.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
@@ -68,6 +76,10 @@
|
|||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Collections.Immutable, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>GameBinaries\System.Collections.Immutable.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
@@ -99,17 +111,25 @@
|
|||||||
<HintPath>GameBinaries\VRage.Render.dll</HintPath>
|
<HintPath>GameBinaries\VRage.Render.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="VRage.Scripting">
|
||||||
|
<HintPath>GameBinaries\VRage.Scripting.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Components\EntityPerformanceImprovements.cs" />
|
<Compile Include="Components\EntityPerformanceImprovements.cs" />
|
||||||
<Compile Include="Components\LoadingScreenComponent.cs" />
|
<Compile Include="Components\LoadingScreenComponent.cs" />
|
||||||
|
<Compile Include="Components\ModReloader.cs" />
|
||||||
<Compile Include="Components\MyGUIScreenMedicalsPatch.cs" />
|
<Compile Include="Components\MyGUIScreenMedicalsPatch.cs" />
|
||||||
<Compile Include="Components\MyHudTimeTracker.cs" />
|
<Compile Include="Components\MyHudTimeTracker.cs" />
|
||||||
<Compile Include="Components\SeamlessScriptManager.cs" />
|
<Compile Include="Components\SeamlessScriptManager.cs" />
|
||||||
<Compile Include="Components\SeamlessSwitcher.cs" />
|
<Compile Include="Components\SeamlessSwitcher.cs" />
|
||||||
<Compile Include="GUI\Screens\GUILoadingScreen.cs" />
|
<Compile Include="GUI\Screens\GUILoadingScreen.cs" />
|
||||||
<Compile Include="Messages\OnlinePlayerData.cs" />
|
<Compile Include="Messages\OnlinePlayerData.cs" />
|
||||||
|
<Compile Include="Models\ModCache.cs" />
|
||||||
|
<Compile Include="Models\ModByte.cs" />
|
||||||
<Compile Include="Utilities\GameEvents.cs" />
|
<Compile Include="Utilities\GameEvents.cs" />
|
||||||
|
<Compile Include="Utilities\NetUtils.cs" />
|
||||||
<Compile Include="Utilities\UtilExtensions.cs" />
|
<Compile Include="Utilities\UtilExtensions.cs" />
|
||||||
<Compile Include="Utilities\PatchUtils.cs" />
|
<Compile Include="Utilities\PatchUtils.cs" />
|
||||||
<Compile Include="Messages\ClientMessage.cs" />
|
<Compile Include="Messages\ClientMessage.cs" />
|
||||||
|
36
Utilities/NetUtils.cs
Normal file
36
Utilities/NetUtils.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using ProtoBuf;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SeamlessClient.Utilities
|
||||||
|
{
|
||||||
|
public class NetUtils
|
||||||
|
{
|
||||||
|
public static byte[] Serialize<T>(T instance)
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
using (var m = new MemoryStream())
|
||||||
|
{
|
||||||
|
Serializer.Serialize(m, instance);
|
||||||
|
return m.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T Deserialize<T>(byte[] data)
|
||||||
|
{
|
||||||
|
if (data == null)
|
||||||
|
return default(T);
|
||||||
|
|
||||||
|
using (var m = new MemoryStream(data))
|
||||||
|
{
|
||||||
|
return Serializer.Deserialize<T>(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user