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.Utils;
|
||||
using SpaceEngineers.Game.World;
|
||||
using VRage.Collections;
|
||||
using VRage.Game.Components;
|
||||
using System.CodeDom;
|
||||
|
||||
namespace SeamlessClient.Components
|
||||
{
|
||||
@@ -66,10 +69,15 @@ namespace SeamlessClient.Components
|
||||
private static bool StartPacketCheck = false;
|
||||
|
||||
private bool keepGrid = false;
|
||||
private ModReloader modReload;
|
||||
|
||||
|
||||
|
||||
public SeamlessSwitcher()
|
||||
{
|
||||
Instance = this;
|
||||
Instance = this;
|
||||
modReload = new ModReloader();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +114,7 @@ namespace SeamlessClient.Components
|
||||
patcher.Patch(_SendRPC, prefix: preSendRPC);
|
||||
|
||||
|
||||
|
||||
modReload.Patch(patcher);
|
||||
base.Patch(patcher);
|
||||
}
|
||||
|
||||
@@ -143,6 +151,8 @@ namespace SeamlessClient.Components
|
||||
/* Set New Multiplayer Stuff */
|
||||
_MyGameServerItemProperty.SetValue(MyMultiplayer.Static, TargetServer);
|
||||
_MultiplayerServerID.SetValue(MyMultiplayer.Static, TargetServer.SteamID);
|
||||
|
||||
|
||||
|
||||
/* Connect To Server */
|
||||
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 });
|
||||
MyGameService.Peer2Peer.CloseSession(Sync.ServerId);
|
||||
MyGameService.DisconnectFromServer();
|
||||
|
||||
MyGameService.ClearCache();
|
||||
|
||||
|
||||
|
||||
@@ -199,6 +209,8 @@ namespace SeamlessClient.Components
|
||||
|
||||
UnloadOldEntities();
|
||||
ResetReplicationTime(false);
|
||||
|
||||
|
||||
|
||||
//MyMultiplayer.Static.ReplicationLayer.Disconnect();
|
||||
//MyMultiplayer.Static.ReplicationLayer.Dispose();
|
||||
@@ -284,6 +296,10 @@ namespace SeamlessClient.Components
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private void ClearClientReplicables()
|
||||
{
|
||||
MyReplicationClient replicationClient = (MyReplicationClient)MyMultiplayer.Static.ReplicationLayer;
|
||||
@@ -310,6 +326,8 @@ namespace SeamlessClient.Components
|
||||
return;
|
||||
|
||||
Seamless.TryShow($"OnUserJoin! Result: {joinResult}");
|
||||
modReload.UnloadModSessionComponents();
|
||||
|
||||
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("Starting new MP Client!");
|
||||
|
||||
|
||||
|
||||
/* On Server Successfull Join
|
||||
*
|
||||
*
|
||||
@@ -350,33 +370,28 @@ namespace SeamlessClient.Components
|
||||
|
||||
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 Streaming: {clienta.HasPendingStreamingReplicables} - LastMessage: {clienta.LastMessageFromServer}");
|
||||
|
||||
|
||||
|
||||
|
||||
modReload.AddModComponents();
|
||||
SendClientReady();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ResetReplicationTime(true);
|
||||
//MyPlayerCollection.ChangePlayerCharacter(MySession.Static.LocalHumanPlayer, MySession.Static.LocalCharacter, MySession.Static.LocalCharacter);
|
||||
// Allow the game to start proccessing incoming messages in the buffer
|
||||
//MyMultiplayer.Static.StartProcessingClientMessages();
|
||||
//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;
|
||||
//_ClearTransportLayer.Invoke(_TransportLayer.GetValue(MyMultiplayer.Static.SyncLayer), null);
|
||||
|
||||
|
||||
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()
|
||||
{
|
||||
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;
|
||||
}
|
||||
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:
|
||||
//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)
|
||||
{
|
||||
@@ -174,6 +178,7 @@ namespace SeamlessClient
|
||||
if (!Initilized)
|
||||
{
|
||||
MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(SeamlessClientNetId, MessageHandler);
|
||||
MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(2938, MessageHandler2);
|
||||
InitilizeComponents();
|
||||
|
||||
Initilized = true;
|
||||
|
@@ -34,6 +34,14 @@
|
||||
<Reference Include="0Harmony, Version=2.3.3.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>packages\Lib.Harmony.2.3.3\lib\net48\0Harmony.dll</HintPath>
|
||||
</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">
|
||||
<HintPath>GameBinaries\NLog.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
@@ -68,6 +76,10 @@
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<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.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
@@ -99,17 +111,25 @@
|
||||
<HintPath>GameBinaries\VRage.Render.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="VRage.Scripting">
|
||||
<HintPath>GameBinaries\VRage.Scripting.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Components\EntityPerformanceImprovements.cs" />
|
||||
<Compile Include="Components\LoadingScreenComponent.cs" />
|
||||
<Compile Include="Components\ModReloader.cs" />
|
||||
<Compile Include="Components\MyGUIScreenMedicalsPatch.cs" />
|
||||
<Compile Include="Components\MyHudTimeTracker.cs" />
|
||||
<Compile Include="Components\SeamlessScriptManager.cs" />
|
||||
<Compile Include="Components\SeamlessSwitcher.cs" />
|
||||
<Compile Include="GUI\Screens\GUILoadingScreen.cs" />
|
||||
<Compile Include="Messages\OnlinePlayerData.cs" />
|
||||
<Compile Include="Models\ModCache.cs" />
|
||||
<Compile Include="Models\ModByte.cs" />
|
||||
<Compile Include="Utilities\GameEvents.cs" />
|
||||
<Compile Include="Utilities\NetUtils.cs" />
|
||||
<Compile Include="Utilities\UtilExtensions.cs" />
|
||||
<Compile Include="Utilities\PatchUtils.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