Seamless rework
This commit is contained in:
107
Components/EntityPerformanceImprovements.cs
Normal file
107
Components/EntityPerformanceImprovements.cs
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
using HarmonyLib;
|
||||||
|
using Sandbox.Game.Entities;
|
||||||
|
using SeamlessClient.Utilities;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using VRage.Game.Entity;
|
||||||
|
|
||||||
|
namespace SeamlessClient.Components
|
||||||
|
{
|
||||||
|
public class EntityPerformanceImprovements : ComponentBase
|
||||||
|
{
|
||||||
|
private static bool isEnabled = false;
|
||||||
|
private static ConcurrentDictionary<long, string> EntityNameReverseLookup = new ConcurrentDictionary<long, string>();
|
||||||
|
|
||||||
|
|
||||||
|
public override void Patch(Harmony patcher)
|
||||||
|
{
|
||||||
|
if (!isEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var setEntityName = PatchUtils.GetMethod(PatchUtils.MyEntitiesType, "SetEntityName");
|
||||||
|
var removeName = PatchUtils.GetMethod(PatchUtils.MyEntitiesType, "RemoveName");
|
||||||
|
var isNameExists = PatchUtils.GetMethod(PatchUtils.MyEntitiesType, "IsNameExists");
|
||||||
|
var unloadData = PatchUtils.GetMethod(PatchUtils.MyEntitiesType, "UnloadData");
|
||||||
|
|
||||||
|
patcher.Patch(setEntityName, prefix: new HarmonyMethod(Get(typeof(EntityPerformanceImprovements), nameof(SetEntityName))));
|
||||||
|
patcher.Patch(removeName, prefix: new HarmonyMethod(Get(typeof(EntityPerformanceImprovements), nameof(RemoveName))));
|
||||||
|
patcher.Patch(isNameExists, prefix: new HarmonyMethod(Get(typeof(EntityPerformanceImprovements), nameof(IsNameExists))));
|
||||||
|
patcher.Patch(unloadData, postfix: new HarmonyMethod(Get(typeof(EntityPerformanceImprovements), nameof(UnloadData))));
|
||||||
|
|
||||||
|
base.Patch(patcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// reverse dictionary
|
||||||
|
|
||||||
|
private static bool SetEntityName(MyEntity myEntity, bool possibleRename)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(myEntity.Name))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
if (possibleRename && EntityNameReverseLookup.ContainsKey(myEntity.EntityId))
|
||||||
|
{
|
||||||
|
var previousName = EntityNameReverseLookup[myEntity.EntityId];
|
||||||
|
if (previousName != myEntity.Name) MyEntities.m_entityNameDictionary.Remove(previousName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (MyEntities.m_entityNameDictionary.TryGetValue(myEntity.Name, out var myEntity1))
|
||||||
|
{
|
||||||
|
if (myEntity1 == myEntity)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MyEntities.m_entityNameDictionary[myEntity.Name] = myEntity;
|
||||||
|
EntityNameReverseLookup[myEntity.EntityId] = myEntity.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool RemoveName(MyEntity entity)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(entity.Name))
|
||||||
|
return false;
|
||||||
|
MyEntities.m_entityNameDictionary.Remove(entity.Name);
|
||||||
|
EntityNameReverseLookup.Remove(entity.EntityId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsNameExists(ref bool __result, MyEntity entity, string name)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(entity.Name))
|
||||||
|
{
|
||||||
|
__result = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MyEntities.m_entityNameDictionary.ContainsKey(name))
|
||||||
|
{
|
||||||
|
var ent = MyEntities.m_entityNameDictionary[entity.Name];
|
||||||
|
__result = ent != entity;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
__result = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UnloadData()
|
||||||
|
{
|
||||||
|
EntityNameReverseLookup.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
177
Components/LoadingScreenComponent.cs
Normal file
177
Components/LoadingScreenComponent.cs
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
using HarmonyLib;
|
||||||
|
using Sandbox.Game.Gui;
|
||||||
|
using Sandbox.Graphics.GUI;
|
||||||
|
using Sandbox.Graphics;
|
||||||
|
using SeamlessClient.Utilities;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using VRage.Utils;
|
||||||
|
using VRageMath;
|
||||||
|
using Sandbox.Game.World;
|
||||||
|
using Sandbox.Engine.Multiplayer;
|
||||||
|
using VRage.Game;
|
||||||
|
using Sandbox.Engine.Networking;
|
||||||
|
using VRage;
|
||||||
|
using VRage.GameServices;
|
||||||
|
|
||||||
|
namespace SeamlessClient.Components
|
||||||
|
{
|
||||||
|
public class LoadingScreenComponent : ComponentBase
|
||||||
|
{
|
||||||
|
private static MethodInfo LoadMultiplayer;
|
||||||
|
|
||||||
|
private static string _loadingScreenTexture = null;
|
||||||
|
private static string _serverName;
|
||||||
|
|
||||||
|
|
||||||
|
public override void Patch(Harmony patcher)
|
||||||
|
{
|
||||||
|
var loadingAction = PatchUtils.GetMethod(typeof(MySessionLoader), "LoadMultiplayerSession");
|
||||||
|
var loadingScreenDraw = PatchUtils.GetMethod(typeof(MyGuiScreenLoading), "DrawInternal");
|
||||||
|
LoadMultiplayer = PatchUtils.GetMethod(typeof(MySession), "LoadMultiplayer");
|
||||||
|
|
||||||
|
|
||||||
|
patcher.Patch(loadingAction, prefix: new HarmonyMethod(Get(typeof(LoadingScreenComponent), nameof(LoadMultiplayerSession))));
|
||||||
|
patcher.Patch(loadingScreenDraw, prefix: new HarmonyMethod(Get(typeof(LoadingScreenComponent), nameof(DrawInternal))));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
base.Patch(patcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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(MyMessageBoxStyleEnum.Error, MyMessageBoxButtonsType.OK, messageCaption: MyTexts.Get(MyCommonTexts.MessageBoxCaptionError), messageText: MyTexts.Get(MyCommonTexts.DialogTextLocalModsDisabledInMultiplayer)));
|
||||||
|
MyLog.Default.WriteLine("LoadSession() - End");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MyWorkshop.DownloadModsAsync(world.Checkpoint.Mods, delegate (MyGameServiceCallResult result)
|
||||||
|
{
|
||||||
|
switch (result)
|
||||||
|
{
|
||||||
|
case MyGameServiceCallResult.NotEnoughSpace:
|
||||||
|
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateMessageBox(MyMessageBoxStyleEnum.Error, MyMessageBoxButtonsType.OK, messageCaption: MyTexts.Get(MyCommonTexts.MessageBoxCaptionError), messageText: MyTexts.Get(MyCommonTexts.DialogTextDownloadModsFailed_NotEnoughSpace), okButtonText: null, cancelButtonText: null, yesButtonText: null, noButtonText: null, callback: delegate
|
||||||
|
{
|
||||||
|
MySessionLoader.UnloadAndExitToMenu();
|
||||||
|
}));
|
||||||
|
break;
|
||||||
|
case MyGameServiceCallResult.OK:
|
||||||
|
MyScreenManager.CloseAllScreensNowExcept(null);
|
||||||
|
MyGuiSandbox.Update(16);
|
||||||
|
|
||||||
|
if (MySession.Static != null)
|
||||||
|
{
|
||||||
|
MySession.Static.Unload();
|
||||||
|
MySession.Static = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
MySessionLoader.StartLoading(delegate
|
||||||
|
{
|
||||||
|
LoadMultiplayer.Invoke(null, new object[] { world, multiplayerSession });
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
multiplayerSession.Dispose();
|
||||||
|
MySessionLoader.UnloadAndExitToMenu();
|
||||||
|
if (MyGameService.IsOnline)
|
||||||
|
{
|
||||||
|
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateMessageBox(MyMessageBoxStyleEnum.Error, MyMessageBoxButtonsType.OK, messageCaption: MyTexts.Get(MyCommonTexts.MessageBoxCaptionError), messageText: MyTexts.Get(MyCommonTexts.DialogTextDownloadModsFailed)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateMessageBox(MyMessageBoxStyleEnum.Error, MyMessageBoxButtonsType.OK, messageCaption: MyTexts.Get(MyCommonTexts.MessageBoxCaptionError), messageText: new StringBuilder(string.Format(MyTexts.GetString(MyCommonTexts.DialogTextDownloadModsFailedSteamOffline), MySession.GameServiceDisplayName))));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
MyLog.Default.WriteLine("LoadSession() - End");
|
||||||
|
}, delegate
|
||||||
|
{
|
||||||
|
multiplayerSession.Dispose();
|
||||||
|
MySessionLoader.UnloadAndExitToMenu();
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnFinished(MyGameServiceCallResult result)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static bool DrawInternal(MyGuiScreenLoading __instance)
|
||||||
|
{
|
||||||
|
//If we dont have a custom loading screen texture, do not do the special crap below
|
||||||
|
|
||||||
|
const string mFont = "LoadingScreen";
|
||||||
|
var mTransitionAlpha = (float)typeof(MyGuiScreenBase).GetField("m_transitionAlpha", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(__instance);
|
||||||
|
MyGuiManager.DrawString(mFont, "Nexus & SeamlessClient Made by: Casimir", new Vector2(0.95f, 0.95f),
|
||||||
|
MyGuiSandbox.GetDefaultTextScaleWithLanguage() * 1.1f,
|
||||||
|
new Color(MyGuiConstants.LOADING_PLEASE_WAIT_COLOR * mTransitionAlpha),
|
||||||
|
MyGuiDrawAlignEnum.HORISONTAL_CENTER_AND_VERTICAL_BOTTOM);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(_loadingScreenTexture))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var color = new Color(255, 255, 255, 250);
|
||||||
|
color.A = (byte)(color.A * mTransitionAlpha);
|
||||||
|
var fullscreenRectangle = MyGuiManager.GetFullscreenRectangle();
|
||||||
|
MyGuiManager.DrawSpriteBatch("Textures\\GUI\\Blank.dds", fullscreenRectangle, Color.Black, false, true);
|
||||||
|
Rectangle outRect;
|
||||||
|
MyGuiManager.GetSafeHeightFullScreenPictureSize(MyGuiConstants.LOADING_BACKGROUND_TEXTURE_REAL_SIZE,
|
||||||
|
out outRect);
|
||||||
|
MyGuiManager.DrawSpriteBatch(_loadingScreenTexture, outRect,
|
||||||
|
new Color(new Vector4(1f, 1f, 1f, mTransitionAlpha)), true, true);
|
||||||
|
MyGuiManager.DrawSpriteBatch("Textures\\Gui\\Screens\\screen_background_fade.dds", outRect,
|
||||||
|
new Color(new Vector4(1f, 1f, 1f, mTransitionAlpha)), 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(mFont, loadScreen, new Vector2(0.5f, 0.95f),
|
||||||
|
MyGuiSandbox.GetDefaultTextScaleWithLanguage() * 1.1f,
|
||||||
|
new Color(MyGuiConstants.LOADING_PLEASE_WAIT_COLOR * mTransitionAlpha),
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -2,6 +2,7 @@
|
|||||||
using Sandbox.Game;
|
using Sandbox.Game;
|
||||||
using Sandbox.Game.Gui;
|
using Sandbox.Game.Gui;
|
||||||
using Sandbox.Game.World;
|
using Sandbox.Game.World;
|
||||||
|
using SeamlessClient.Messages;
|
||||||
using SeamlessClient.Utilities;
|
using SeamlessClient.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -12,7 +13,7 @@ using System.Threading.Tasks;
|
|||||||
namespace SeamlessClient.OnlinePlayersWindow
|
namespace SeamlessClient.OnlinePlayersWindow
|
||||||
{
|
{
|
||||||
|
|
||||||
public class PlayersWindow : ComponentBase
|
public class PlayersWindowComponent : ComponentBase
|
||||||
{
|
{
|
||||||
|
|
||||||
public override void Patch(Harmony patcher)
|
public override void Patch(Harmony patcher)
|
||||||
@@ -26,6 +27,11 @@ namespace SeamlessClient.OnlinePlayersWindow
|
|||||||
MyPerGameSettings.GUI.PlayersScreen = typeof(OnlineNexusPlayersWindow);
|
MyPerGameSettings.GUI.PlayersScreen = typeof(OnlineNexusPlayersWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ApplyRecievedPlayers(List<OnlineServer> servers, int CurrentServer)
|
||||||
|
{
|
||||||
|
Seamless.TryShow($"Recieved {CurrentServer} - {servers.Count}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
433
Components/ServerSwitcherComponent.cs
Normal file
433
Components/ServerSwitcherComponent.cs
Normal file
@@ -0,0 +1,433 @@
|
|||||||
|
using HarmonyLib;
|
||||||
|
using Sandbox;
|
||||||
|
using Sandbox.Engine.Multiplayer;
|
||||||
|
using Sandbox.Engine.Networking;
|
||||||
|
using Sandbox.Game;
|
||||||
|
using Sandbox.Game.Entities;
|
||||||
|
using Sandbox.Game.Gui;
|
||||||
|
using Sandbox.Game.GUI;
|
||||||
|
using Sandbox.Game.Multiplayer;
|
||||||
|
using Sandbox.Game.SessionComponents;
|
||||||
|
using Sandbox.Game.World;
|
||||||
|
using Sandbox.Game.World.Generator;
|
||||||
|
using Sandbox.ModAPI;
|
||||||
|
using SeamlessClient.Components;
|
||||||
|
using SeamlessClient.OnlinePlayersWindow;
|
||||||
|
using SeamlessClient.Utilities;
|
||||||
|
using SpaceEngineers.Game.GUI;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using VRage.Game;
|
||||||
|
using VRage.Game.ModAPI;
|
||||||
|
using VRage.GameServices;
|
||||||
|
using VRage.Network;
|
||||||
|
using VRage.Utils;
|
||||||
|
using VRageRender;
|
||||||
|
using VRageRender.Messages;
|
||||||
|
|
||||||
|
namespace SeamlessClient.ServerSwitching
|
||||||
|
{
|
||||||
|
public class ServerSwitcherComponent : ComponentBase
|
||||||
|
{
|
||||||
|
private static FieldInfo RemoteAdminSettings;
|
||||||
|
private static FieldInfo AdminSettings;
|
||||||
|
private static MethodInfo UnloadProceduralWorldGenerator;
|
||||||
|
private static MethodInfo GpsRegisterChat;
|
||||||
|
private static MethodInfo LoadMembersFromWorld;
|
||||||
|
private static MethodInfo InitVirtualClients;
|
||||||
|
|
||||||
|
private static FieldInfo VirtualClients;
|
||||||
|
|
||||||
|
|
||||||
|
public static ServerSwitcherComponent Instance { get; private set; }
|
||||||
|
|
||||||
|
private MyGameServerItem TargetServer { get; set; }
|
||||||
|
private MyObjectBuilder_World TargetWorld { get; set; }
|
||||||
|
private string OldArmorSkin { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
private bool needsEntityUnload = true;
|
||||||
|
|
||||||
|
public static event EventHandler<JoinResultMsg> OnJoinEvent;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public ServerSwitcherComponent()
|
||||||
|
{
|
||||||
|
Instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override void Patch(Harmony patcher)
|
||||||
|
{
|
||||||
|
RemoteAdminSettings = PatchUtils.GetField(typeof(MySession), "m_remoteAdminSettings");
|
||||||
|
AdminSettings = PatchUtils.GetField(typeof(MySession), "m_adminSettings");
|
||||||
|
VirtualClients = PatchUtils.GetField(typeof(MySession), "VirtualClients");
|
||||||
|
|
||||||
|
UnloadProceduralWorldGenerator = PatchUtils.GetMethod(typeof(MyProceduralWorldGenerator), "UnloadData");
|
||||||
|
GpsRegisterChat = PatchUtils.GetMethod(typeof(MyGpsCollection), "RegisterChat");
|
||||||
|
LoadMembersFromWorld = PatchUtils.GetMethod(typeof(MySession), "LoadMembersFromWorld");
|
||||||
|
InitVirtualClients = PatchUtils.GetMethod(PatchUtils.VirtualClientsType, "Init");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var onJoin = PatchUtils.GetMethod(PatchUtils.ClientType, "OnUserJoined");
|
||||||
|
patcher.Patch(onJoin, postfix: new HarmonyMethod(Get(typeof(ServerSwitcherComponent), nameof(OnUserJoined))));
|
||||||
|
|
||||||
|
base.Patch(patcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static void OnUserJoined(ref JoinResultMsg msg)
|
||||||
|
{
|
||||||
|
if (msg.JoinResult == JoinResult.OK)
|
||||||
|
{
|
||||||
|
Seamless.TryShow("User Joined! Result: " + msg.JoinResult.ToString());
|
||||||
|
|
||||||
|
//Invoke the switch event
|
||||||
|
OnJoinEvent?.Invoke(null, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public override void Initilized()
|
||||||
|
{
|
||||||
|
base.Initilized();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void StartBackendSwitch(MyGameServerItem TargetServer, MyObjectBuilder_World TargetWorld)
|
||||||
|
{
|
||||||
|
this.TargetServer = TargetServer;
|
||||||
|
this.TargetWorld = TargetWorld;
|
||||||
|
OldArmorSkin = MySession.Static.LocalHumanPlayer.BuildArmorSkin;
|
||||||
|
|
||||||
|
if (Seamless.NexusVersion.Major >= 2)
|
||||||
|
needsEntityUnload = false;
|
||||||
|
|
||||||
|
|
||||||
|
AsyncInvoke.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
if(needsEntityUnload)
|
||||||
|
MySession.Static.SetCameraController(MyCameraControllerEnum.SpectatorFixed);
|
||||||
|
|
||||||
|
|
||||||
|
UnloadServer();
|
||||||
|
SetNewMultiplayerClient();
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnloadServer()
|
||||||
|
{
|
||||||
|
if (MyMultiplayer.Static == null)
|
||||||
|
throw new Exception("MyMultiplayer.Static is null on unloading? dafuq?");
|
||||||
|
|
||||||
|
UnloadOldEntities();
|
||||||
|
|
||||||
|
//Close and Cancel any screens (Medical or QuestLog)
|
||||||
|
MySessionComponentIngameHelp component = MySession.Static.GetComponent<MySessionComponentIngameHelp>();
|
||||||
|
component?.TryCancelObjective();
|
||||||
|
MyGuiScreenMedicals.Close();
|
||||||
|
|
||||||
|
|
||||||
|
//Clear all old players and clients.
|
||||||
|
Sync.Clients.Clear();
|
||||||
|
Sync.Players.ClearPlayers();
|
||||||
|
|
||||||
|
//Unregister Chat
|
||||||
|
MyHud.Chat.UnregisterChat(MyMultiplayer.Static);
|
||||||
|
|
||||||
|
//Clear all local GPS Points
|
||||||
|
MySession.Static.Gpss.RemovePlayerGpss(MySession.Static.LocalPlayerId);
|
||||||
|
MyHud.GpsMarkers.Clear();
|
||||||
|
|
||||||
|
//DisconnectReplication
|
||||||
|
MyMultiplayer.Static.ReplicationLayer.Disconnect();
|
||||||
|
MyMultiplayer.Static.ReplicationLayer.Dispose();
|
||||||
|
MyMultiplayer.Static.Dispose();
|
||||||
|
MyMultiplayer.Static = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnloadOldEntities()
|
||||||
|
{
|
||||||
|
foreach (var ent in MyEntities.GetEntities())
|
||||||
|
{
|
||||||
|
if (ent is MyPlanet)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
//ent.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetNewMultiplayerClient()
|
||||||
|
{
|
||||||
|
OnJoinEvent += ServerSwitcherComponent_OnJoinEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ServerSwitcherComponent_OnJoinEvent(object sender, JoinResultMsg e)
|
||||||
|
{
|
||||||
|
OnJoinEvent -= ServerSwitcherComponent_OnJoinEvent;
|
||||||
|
if (e.JoinResult != JoinResult.OK)
|
||||||
|
{
|
||||||
|
Seamless.TryShow("Failed to join the target server!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* On Server Successfull Join
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
SetWorldSettings();
|
||||||
|
InitComponents();
|
||||||
|
LoadConnectedClients();
|
||||||
|
StartEntitySync();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
MyMultiplayer.Static.OnSessionReady();
|
||||||
|
|
||||||
|
MyHud.Chat.RegisterChat(MyMultiplayer.Static);
|
||||||
|
GpsRegisterChat.Invoke(MySession.Static.Gpss, new object[] { MyMultiplayer.Static });
|
||||||
|
|
||||||
|
|
||||||
|
// Allow the game to start proccessing incoming messages in the buffer
|
||||||
|
MyMultiplayer.Static.StartProcessingClientMessages();
|
||||||
|
|
||||||
|
//Recreate all controls... Will fix weird gui/paint/crap
|
||||||
|
MyGuiScreenHudSpace.Static.RecreateControls(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void SetWorldSettings()
|
||||||
|
{
|
||||||
|
//Clear old list
|
||||||
|
MySession.Static.PromotedUsers.Clear();
|
||||||
|
MySession.Static.CreativeTools.Clear();
|
||||||
|
Dictionary<ulong, AdminSettingsEnum> AdminSettingsList = (Dictionary<ulong, AdminSettingsEnum>)RemoteAdminSettings.GetValue(MySession.Static);
|
||||||
|
AdminSettingsList.Clear();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Set new world settings
|
||||||
|
MySession.Static.Name = MyStatControlText.SubstituteTexts(TargetWorld.Checkpoint.SessionName);
|
||||||
|
MySession.Static.Description = TargetWorld.Checkpoint.Description;
|
||||||
|
|
||||||
|
MySession.Static.Mods = TargetWorld.Checkpoint.Mods;
|
||||||
|
MySession.Static.Settings = TargetWorld.Checkpoint.Settings;
|
||||||
|
MySession.Static.CurrentPath = MyLocalCache.GetSessionSavesPath(MyUtils.StripInvalidChars(TargetWorld.Checkpoint.SessionName), contentFolder: false, createIfNotExists: false);
|
||||||
|
MySession.Static.WorldBoundaries = TargetWorld.Checkpoint.WorldBoundaries;
|
||||||
|
MySession.Static.InGameTime = MyObjectBuilder_Checkpoint.DEFAULT_DATE;
|
||||||
|
MySession.Static.ElapsedGameTime = new TimeSpan(TargetWorld.Checkpoint.ElapsedGameTime);
|
||||||
|
MySession.Static.Settings.EnableSpectator = false;
|
||||||
|
|
||||||
|
MySession.Static.Password = TargetWorld.Checkpoint.Password;
|
||||||
|
MySession.Static.PreviousEnvironmentHostility = TargetWorld.Checkpoint.PreviousEnvironmentHostility;
|
||||||
|
MySession.Static.RequiresDX = TargetWorld.Checkpoint.RequiresDX;
|
||||||
|
MySession.Static.CustomLoadingScreenImage = TargetWorld.Checkpoint.CustomLoadingScreenImage;
|
||||||
|
MySession.Static.CustomLoadingScreenText = TargetWorld.Checkpoint.CustomLoadingScreenText;
|
||||||
|
MySession.Static.CustomSkybox = TargetWorld.Checkpoint.CustomSkybox;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
MySession.Static.Gpss = new MyGpsCollection();
|
||||||
|
MySession.Static.Gpss.LoadGpss(TargetWorld.Checkpoint);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Seamless.TryShow($"An error occured while loading GPS points! You will have an empty gps list! \n {ex.ToString()}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MyRenderProxy.RebuildCullingStructure();
|
||||||
|
//MySession.Static.Toolbars.LoadToolbars(checkpoint);
|
||||||
|
|
||||||
|
Sync.Players.RespawnComponent.InitFromCheckpoint(TargetWorld.Checkpoint);
|
||||||
|
|
||||||
|
|
||||||
|
// Set new admin settings
|
||||||
|
if (TargetWorld.Checkpoint.PromotedUsers != null)
|
||||||
|
{
|
||||||
|
MySession.Static.PromotedUsers = TargetWorld.Checkpoint.PromotedUsers.Dictionary;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MySession.Static.PromotedUsers = new Dictionary<ulong, MyPromoteLevel>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
foreach (KeyValuePair<MyObjectBuilder_Checkpoint.PlayerId, MyObjectBuilder_Player> item in TargetWorld.Checkpoint.AllPlayersData.Dictionary)
|
||||||
|
{
|
||||||
|
ulong clientId = item.Key.GetClientId();
|
||||||
|
AdminSettingsEnum adminSettingsEnum = (AdminSettingsEnum)item.Value.RemoteAdminSettings;
|
||||||
|
if (TargetWorld.Checkpoint.RemoteAdminSettings != null && TargetWorld.Checkpoint.RemoteAdminSettings.Dictionary.TryGetValue(clientId, out var value))
|
||||||
|
{
|
||||||
|
adminSettingsEnum = (AdminSettingsEnum)value;
|
||||||
|
}
|
||||||
|
if (!MyPlatformGameSettings.IsIgnorePcuAllowed)
|
||||||
|
{
|
||||||
|
adminSettingsEnum &= ~AdminSettingsEnum.IgnorePcu;
|
||||||
|
adminSettingsEnum &= ~AdminSettingsEnum.KeepOriginalOwnershipOnPaste;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AdminSettingsList[clientId] = adminSettingsEnum;
|
||||||
|
if (!Sync.IsDedicated && clientId == Sync.MyId)
|
||||||
|
{
|
||||||
|
AdminSettings.SetValue(MySession.Static, adminSettingsEnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (!MySession.Static.PromotedUsers.TryGetValue(clientId, out var value2))
|
||||||
|
{
|
||||||
|
value2 = MyPromoteLevel.None;
|
||||||
|
}
|
||||||
|
if (item.Value.PromoteLevel > value2)
|
||||||
|
{
|
||||||
|
MySession.Static.PromotedUsers[clientId] = item.Value.PromoteLevel;
|
||||||
|
}
|
||||||
|
if (!MySession.Static.CreativeTools.Contains(clientId) && item.Value.CreativeToolsEnabled)
|
||||||
|
{
|
||||||
|
MySession.Static.CreativeTools.Add(clientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MySector.InitEnvironmentSettings(TargetWorld.Sector.Environment);
|
||||||
|
|
||||||
|
string text = ((!string.IsNullOrEmpty(TargetWorld.Checkpoint.CustomSkybox)) ? TargetWorld.Checkpoint.CustomSkybox : MySector.EnvironmentDefinition.EnvironmentTexture);
|
||||||
|
MyRenderProxy.PreloadTextures(new string[1] { text }, TextureType.CubeMap);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitComponents()
|
||||||
|
{
|
||||||
|
MyModAPIHelper.Initialize();
|
||||||
|
MySession.Static.LoadDataComponents();
|
||||||
|
|
||||||
|
//MySession.Static.LoadObjectBuildersComponents(TargetWorld.Checkpoint.SessionComponents);
|
||||||
|
MyModAPIHelper.Initialize();
|
||||||
|
// MySession.Static.LoadObjectBuildersComponents(TargetWorld.Checkpoint.SessionComponents);
|
||||||
|
|
||||||
|
UpdateWorldGenerator();
|
||||||
|
//MethodInfo A = typeof(MySession).GetMethod("LoadGameDefinition", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||||
|
// A.Invoke(MySession.Static, new object[] { TargetWorld.Checkpoint });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void UpdateWorldGenerator()
|
||||||
|
{
|
||||||
|
//This will re-init the MyProceduralWorldGenerator. (Not doing this will result in asteroids not rendering in properly)
|
||||||
|
|
||||||
|
|
||||||
|
//This shoud never be null
|
||||||
|
var Generator = MySession.Static.GetComponent<MyProceduralWorldGenerator>();
|
||||||
|
|
||||||
|
//Force component to unload
|
||||||
|
UnloadProceduralWorldGenerator.Invoke(Generator, null);
|
||||||
|
|
||||||
|
//Re-call the generator init
|
||||||
|
MyObjectBuilder_WorldGenerator GeneratorSettings = (MyObjectBuilder_WorldGenerator)TargetWorld.Checkpoint.SessionComponents.FirstOrDefault(x => x.GetType() == typeof(MyObjectBuilder_WorldGenerator));
|
||||||
|
if (GeneratorSettings != null)
|
||||||
|
{
|
||||||
|
//Re-initilized this component (forces to update asteroid areas like not in planets etc)
|
||||||
|
Generator.Init(GeneratorSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Force component to reload, re-syncing settings and seeds to the destination server
|
||||||
|
Generator.LoadData();
|
||||||
|
|
||||||
|
//We need to go in and force planets to be empty areas in the generator. This is originially done on planet init.
|
||||||
|
FieldInfo PlanetInitArgs = typeof(MyPlanet).GetField("m_planetInitValues", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||||
|
foreach (var Planet in MyEntities.GetEntities().OfType<MyPlanet>())
|
||||||
|
{
|
||||||
|
MyPlanetInitArguments args = (MyPlanetInitArguments)PlanetInitArgs.GetValue(Planet);
|
||||||
|
|
||||||
|
float MaxRadius = args.MaxRadius;
|
||||||
|
|
||||||
|
Generator.MarkEmptyArea(Planet.PositionComp.GetPosition(), MaxRadius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartEntitySync()
|
||||||
|
{
|
||||||
|
Seamless.TryShow("Requesting Player From Server");
|
||||||
|
Sync.Players.RequestNewPlayer(Sync.MyId, 0, MyGameService.UserName, null, realPlayer: true, initialPlayer: true);
|
||||||
|
if (MySession.Static.ControlledEntity == null && Sync.IsServer && !Sandbox.Engine.Platform.Game.IsDedicated)
|
||||||
|
{
|
||||||
|
MyLog.Default.WriteLine("ControlledObject was null, respawning character");
|
||||||
|
//m_cameraAwaitingEntity = true;
|
||||||
|
MyPlayerCollection.RequestLocalRespawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Request client state batch
|
||||||
|
(MyMultiplayer.Static as MyMultiplayerClientBase).RequestBatchConfirmation();
|
||||||
|
MyMultiplayer.Static.PendingReplicablesDone += Static_PendingReplicablesDone;
|
||||||
|
//typeof(MyGuiScreenTerminal).GetMethod("CreateTabs")
|
||||||
|
|
||||||
|
MySession.Static.LoadDataComponents();
|
||||||
|
//MyGuiSandbox.LoadData(false);
|
||||||
|
//MyGuiSandbox.AddScreen(MyGuiSandbox.CreateScreen(MyPerGameSettings.GUI.HUDScreen));
|
||||||
|
MyRenderProxy.RebuildCullingStructure();
|
||||||
|
MyRenderProxy.CollectGarbage();
|
||||||
|
|
||||||
|
Seamless.TryShow("OnlinePlayers: " + MySession.Static.Players.GetOnlinePlayers().Count);
|
||||||
|
Seamless.TryShow("Loading Complete!");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Static_PendingReplicablesDone()
|
||||||
|
{
|
||||||
|
if (MySession.Static.VoxelMaps.Instances.Count > 0)
|
||||||
|
{
|
||||||
|
MySandboxGame.AreClipmapsReady = false;
|
||||||
|
}
|
||||||
|
MyMultiplayer.Static.PendingReplicablesDone -= Static_PendingReplicablesDone;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadConnectedClients()
|
||||||
|
{
|
||||||
|
LoadMembersFromWorld.Invoke(MySession.Static, new object[] { TargetWorld, MyMultiplayer.Static });
|
||||||
|
|
||||||
|
|
||||||
|
//Re-Initilize Virtual clients
|
||||||
|
object VirtualClientsValue = VirtualClients.GetValue(MySession.Static);
|
||||||
|
InitVirtualClients.Invoke(VirtualClientsValue, null);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
MyPlayer.PlayerId? savingPlayerId = new MyPlayer.PlayerId(Sync.MyId);
|
||||||
|
if (!savingPlayerId.HasValue)
|
||||||
|
{
|
||||||
|
Seamless.TryShow("SavingPlayerID is null! Creating Default!");
|
||||||
|
savingPlayerId = new MyPlayer.PlayerId(Sync.MyId);
|
||||||
|
}
|
||||||
|
Seamless.TryShow("Saving PlayerID: " + savingPlayerId.ToString());
|
||||||
|
|
||||||
|
Sync.Players.LoadConnectedPlayers(TargetWorld.Checkpoint, savingPlayerId);
|
||||||
|
Sync.Players.LoadControlledEntities(TargetWorld.Checkpoint.ControlledEntities, TargetWorld.Checkpoint.ControlledObject, savingPlayerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -21,29 +21,35 @@ namespace SeamlessClient.Messages
|
|||||||
public class ClientMessage
|
public class ClientMessage
|
||||||
{
|
{
|
||||||
[ProtoMember(1)] public ClientMessageType MessageType;
|
[ProtoMember(1)] public ClientMessageType MessageType;
|
||||||
[ProtoMember(2)] public TransferData data;
|
[ProtoMember(2)] public byte[] MessageData;
|
||||||
[ProtoMember(3)] public long IdentityID;
|
[ProtoMember(3)] public long IdentityID;
|
||||||
[ProtoMember(4)] public ulong SteamID;
|
[ProtoMember(4)] public ulong SteamID;
|
||||||
[ProtoMember(5)] public string PluginVersion = "0";
|
[ProtoMember(5)] public string PluginVersion = "0";
|
||||||
|
[ProtoMember(6)] public string NexusVersion = "0";
|
||||||
|
|
||||||
public ClientMessage(ClientMessageType Type)
|
public ClientMessage(string PluginVersion)
|
||||||
{
|
{
|
||||||
MessageType = Type;
|
MessageType = ClientMessageType.FirstJoin;
|
||||||
|
|
||||||
if (MyAPIGateway.Multiplayer == null || MyAPIGateway.Multiplayer.IsServer) return;
|
|
||||||
if (MyAPIGateway.Session.LocalHumanPlayer == null) return;
|
|
||||||
|
|
||||||
IdentityID = MySession.Static?.LocalHumanPlayer?.Identity?.IdentityId ?? 0;
|
IdentityID = MySession.Static?.LocalHumanPlayer?.Identity?.IdentityId ?? 0;
|
||||||
SteamID = MySession.Static?.LocalHumanPlayer?.Id.SteamId ?? 0;
|
SteamID = MySession.Static?.LocalHumanPlayer?.Id.SteamId ?? 0;
|
||||||
//PluginVersion = SeamlessClient.Version;
|
this.PluginVersion = PluginVersion;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TransferData GetTransferData()
|
||||||
public ClientMessage()
|
|
||||||
{
|
{
|
||||||
|
return MessageData == null ? default : MessageUtils.Deserialize<TransferData>(MessageData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OnlinePlayerData GetOnlinePlayers()
|
||||||
|
{
|
||||||
|
if (MessageData == null)
|
||||||
|
return default;
|
||||||
|
|
||||||
|
var msg = MessageUtils.Deserialize<OnlinePlayerData>(MessageData);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
57
Messages/OnlinePlayerData.cs
Normal file
57
Messages/OnlinePlayerData.cs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
using ProtoBuf;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SeamlessClient.Messages
|
||||||
|
{
|
||||||
|
[ProtoContract]
|
||||||
|
public class OnlinePlayerData
|
||||||
|
{
|
||||||
|
[ProtoMember(10)] public List<OnlineServer> OnlineServers = new List<OnlineServer>();
|
||||||
|
|
||||||
|
[ProtoMember(12)] public int currentServerID;
|
||||||
|
}
|
||||||
|
|
||||||
|
[ProtoContract]
|
||||||
|
public class OnlineServer
|
||||||
|
{
|
||||||
|
[ProtoMember(2)] public List<OnlinePlayer> Players = new List<OnlinePlayer>();
|
||||||
|
|
||||||
|
[ProtoMember(3)] public bool ServerRunning = false;
|
||||||
|
|
||||||
|
[ProtoMember(10)] public int ServerID;
|
||||||
|
|
||||||
|
[ProtoMember(11)] public string ServerName;
|
||||||
|
|
||||||
|
public OnlineServer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ProtoContract]
|
||||||
|
public class OnlinePlayer
|
||||||
|
{
|
||||||
|
[ProtoMember(1)] public string PlayerName;
|
||||||
|
|
||||||
|
[ProtoMember(2)] public ulong SteamID;
|
||||||
|
|
||||||
|
[ProtoMember(3)] public long IdentityID;
|
||||||
|
|
||||||
|
[ProtoMember(4)] public int OnServer;
|
||||||
|
|
||||||
|
public OnlinePlayer(string PlayerName, ulong SteamID, long IdentityID, int OnServer)
|
||||||
|
{
|
||||||
|
this.PlayerName = PlayerName;
|
||||||
|
this.SteamID = SteamID;
|
||||||
|
this.IdentityID = IdentityID;
|
||||||
|
this.OnServer = OnServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OnlinePlayer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -25,6 +25,10 @@ namespace SeamlessClient.Messages
|
|||||||
|
|
||||||
[ProtoMember(5)] public MyObjectBuilder_Gps GpsCollection;
|
[ProtoMember(5)] public MyObjectBuilder_Gps GpsCollection;
|
||||||
|
|
||||||
|
public WorldRequest()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public WorldRequest(ulong playerId, long playerIdentity, string name)
|
public WorldRequest(ulong playerId, long playerIdentity, string name)
|
||||||
{
|
{
|
||||||
this.PlayerID = playerId;
|
this.PlayerID = playerId;
|
||||||
@@ -32,9 +36,7 @@ namespace SeamlessClient.Messages
|
|||||||
this.IdentityID = playerIdentity;
|
this.IdentityID = playerIdentity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WorldRequest()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SerializeWorldData(MyObjectBuilder_World WorldData)
|
public void SerializeWorldData(MyObjectBuilder_World WorldData)
|
||||||
{
|
{
|
||||||
|
@@ -33,5 +33,5 @@ using System.Runtime.InteropServices;
|
|||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
|
|
||||||
[assembly: AssemblyVersion("1.0.0.0")] //Set these both to the same
|
[assembly: AssemblyVersion("2.0.0.0")] //Set these both to the same
|
||||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
[assembly: AssemblyFileVersion("2.0.0.0")]
|
||||||
|
50
Seamless.cs
50
Seamless.cs
@@ -3,6 +3,7 @@ using NLog.Fluent;
|
|||||||
using Sandbox.Game.World;
|
using Sandbox.Game.World;
|
||||||
using Sandbox.ModAPI;
|
using Sandbox.ModAPI;
|
||||||
using SeamlessClient.Messages;
|
using SeamlessClient.Messages;
|
||||||
|
using SeamlessClient.OnlinePlayersWindow;
|
||||||
using SeamlessClient.ServerSwitching;
|
using SeamlessClient.ServerSwitching;
|
||||||
using SeamlessClient.Utilities;
|
using SeamlessClient.Utilities;
|
||||||
using System;
|
using System;
|
||||||
@@ -13,6 +14,7 @@ using System.Net.NetworkInformation;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using VRage.GameServices;
|
||||||
using VRage.Plugins;
|
using VRage.Plugins;
|
||||||
using VRage.Sync;
|
using VRage.Sync;
|
||||||
using VRage.Utils;
|
using VRage.Utils;
|
||||||
@@ -22,12 +24,17 @@ namespace SeamlessClient
|
|||||||
public class Seamless : IPlugin
|
public class Seamless : IPlugin
|
||||||
{
|
{
|
||||||
public static Version SeamlessVersion;
|
public static Version SeamlessVersion;
|
||||||
|
public static Version NexusVersion;
|
||||||
private static Harmony SeamlessPatcher;
|
private static Harmony SeamlessPatcher;
|
||||||
public const ushort SeamlessClientNetId = 2936;
|
public const ushort SeamlessClientNetId = 2936;
|
||||||
|
|
||||||
private List<ComponentBase> allComps = new List<ComponentBase>();
|
private List<ComponentBase> allComps = new List<ComponentBase>();
|
||||||
private Assembly thisAssembly;
|
private Assembly thisAssembly;
|
||||||
private bool Initilized = false;
|
private bool Initilized = false;
|
||||||
|
public static bool isSeamlessServer = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
public static bool isDebug = true;
|
public static bool isDebug = true;
|
||||||
@@ -72,6 +79,7 @@ namespace SeamlessClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PatchComponents(Harmony patcher)
|
private void PatchComponents(Harmony patcher)
|
||||||
{
|
{
|
||||||
foreach (ComponentBase component in allComps)
|
foreach (ComponentBase component in allComps)
|
||||||
@@ -89,6 +97,7 @@ namespace SeamlessClient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitilizeComponents()
|
private void InitilizeComponents()
|
||||||
{
|
{
|
||||||
foreach(ComponentBase component in allComps)
|
foreach(ComponentBase component in allComps)
|
||||||
@@ -112,27 +121,35 @@ namespace SeamlessClient
|
|||||||
if (!fromServer || sender != 0)
|
if (!fromServer || sender != 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (MyAPIGateway.Multiplayer == null || MyAPIGateway.Multiplayer.IsServer) return;
|
||||||
|
if (MyAPIGateway.Session.LocalHumanPlayer == null) return;
|
||||||
|
|
||||||
|
|
||||||
ClientMessage msg = MessageUtils.Deserialize<ClientMessage>(data);
|
ClientMessage msg = MessageUtils.Deserialize<ClientMessage>(data);
|
||||||
if (msg == null)
|
if (msg == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
//Get Nexus Version
|
||||||
|
NexusVersion = Version.Parse(msg.NexusVersion);
|
||||||
|
|
||||||
switch (msg.MessageType)
|
switch (msg.MessageType)
|
||||||
{
|
{
|
||||||
case ClientMessageType.FirstJoin:
|
case ClientMessageType.FirstJoin:
|
||||||
MyAPIGateway.Multiplayer?.SendMessageToServer(SeamlessClientNetId, MessageUtils.Serialize(new ClientMessage(ClientMessageType.FirstJoin)));
|
|
||||||
|
ClientMessage response = new ClientMessage(SeamlessVersion.ToString());
|
||||||
|
MyAPIGateway.Multiplayer?.SendMessageToServer(SeamlessClientNetId, MessageUtils.Serialize(response));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ClientMessageType.TransferServer:
|
case ClientMessageType.TransferServer:
|
||||||
ServerSwitcher.StartSwitching(msg.data);
|
StartSwitch(msg.GetTransferData());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ClientMessageType.OnlinePlayers:
|
case ClientMessageType.OnlinePlayers:
|
||||||
//Not implemented yet
|
//Not implemented yet
|
||||||
|
var playerData = msg.GetOnlinePlayers();
|
||||||
|
PlayersWindowComponent.ApplyRecievedPlayers(playerData.OnlineServers, playerData.currentServerID);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -160,6 +177,31 @@ namespace SeamlessClient
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static void StartSwitch(TransferData targetServer)
|
||||||
|
{
|
||||||
|
if (targetServer.TargetServerId == 0)
|
||||||
|
{
|
||||||
|
Seamless.TryShow("This is not a valid server!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var server = new MyGameServerItem
|
||||||
|
{
|
||||||
|
ConnectionString = targetServer.IpAddress,
|
||||||
|
SteamID = targetServer.TargetServerId,
|
||||||
|
Name = targetServer.ServerName
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Seamless.TryShow($"Beginning Redirect to server: {targetServer.TargetServerId}");
|
||||||
|
var world = targetServer.WorldRequest.DeserializeWorldData();
|
||||||
|
ServerSwitcherComponent.Instance.StartBackendSwitch(server, world);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -61,6 +61,10 @@
|
|||||||
<HintPath>GameBinaries\Sandbox.Graphics.dll</HintPath>
|
<HintPath>GameBinaries\Sandbox.Graphics.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="SpaceEngineers.Game">
|
||||||
|
<HintPath>GameBinaries\SpaceEngineers.Game.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
@@ -86,16 +90,24 @@
|
|||||||
<HintPath>GameBinaries\VRage.Math.dll</HintPath>
|
<HintPath>GameBinaries\VRage.Math.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="VRage.Render">
|
||||||
|
<HintPath>GameBinaries\VRage.Render.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="Components\EntityPerformanceImprovements.cs" />
|
||||||
|
<Compile Include="Components\LoadingScreenComponent.cs" />
|
||||||
|
<Compile Include="Messages\OnlinePlayerData.cs" />
|
||||||
|
<Compile Include="Utilities\PatchUtils.cs" />
|
||||||
<Compile Include="Messages\ClientMessage.cs" />
|
<Compile Include="Messages\ClientMessage.cs" />
|
||||||
<Compile Include="Messages\TransferData.cs" />
|
<Compile Include="Messages\TransferData.cs" />
|
||||||
<Compile Include="Messages\WorldRequestData.cs" />
|
<Compile Include="Messages\WorldRequestData.cs" />
|
||||||
<Compile Include="OnlinePlayersWindow\OnlineNexusPlayersWindow.cs" />
|
<Compile Include="OnlinePlayersWindow\OnlineNexusPlayersWindow.cs" />
|
||||||
<Compile Include="OnlinePlayersWindow\PlayersWindow.cs" />
|
<Compile Include="Components\PlayersWindowComponent.cs" />
|
||||||
<Compile Include="Seamless.cs" />
|
<Compile Include="Seamless.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="ServerSwitching\ServerSwitcher.cs" />
|
<Compile Include="Components\ServerSwitcherComponent.cs" />
|
||||||
|
<Compile Include="Utilities\AsyncInvoke.cs" />
|
||||||
<Compile Include="Utilities\MessageUtils.cs" />
|
<Compile Include="Utilities\MessageUtils.cs" />
|
||||||
<Compile Include="Utilities\ComponentBase.cs" />
|
<Compile Include="Utilities\ComponentBase.cs" />
|
||||||
<Compile Include="Utilities\Types.cs" />
|
<Compile Include="Utilities\Types.cs" />
|
||||||
@@ -105,7 +117,9 @@
|
|||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup />
|
<ItemGroup>
|
||||||
|
<Folder Include="ServerSwitching\" />
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="SeamlessClient.xml">
|
<Content Include="SeamlessClient.xml">
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
@@ -1,27 +0,0 @@
|
|||||||
using SeamlessClient.Messages;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http.Headers;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using VRageMath;
|
|
||||||
|
|
||||||
namespace SeamlessClient.ServerSwitching
|
|
||||||
{
|
|
||||||
public class ServerSwitcher
|
|
||||||
{
|
|
||||||
|
|
||||||
public static void StartSwitching(TransferData targetServer)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
118
Utilities/AsyncInvoke.cs
Normal file
118
Utilities/AsyncInvoke.cs
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
using Sandbox;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SeamlessClient.Utilities
|
||||||
|
{
|
||||||
|
public static class AsyncInvoke
|
||||||
|
{
|
||||||
|
public static Task InvokeAsync(Action action, [CallerMemberName] string caller = "SeamlessClient")
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
//Jimm thank you. This is the best
|
||||||
|
var ctx = new TaskCompletionSource<object>();
|
||||||
|
MySandboxGame.Static.Invoke(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
action.Invoke();
|
||||||
|
ctx.SetResult(null);
|
||||||
|
ctx.Task.ContinueWith(task => task.Dispose());
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
ctx.SetException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}, caller);
|
||||||
|
return ctx.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task<T> InvokeAsync<T>(Func<T> action, [CallerMemberName] string caller = "SeamlessClient")
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
//Jimm thank you. This is the best
|
||||||
|
var ctx = new TaskCompletionSource<T>();
|
||||||
|
MySandboxGame.Static.Invoke(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ctx.SetResult(action.Invoke());
|
||||||
|
ctx.Task.ContinueWith(task => task.Dispose());
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
ctx.SetException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}, caller);
|
||||||
|
return ctx.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task<T2> InvokeAsync<T1, T2>(Func<T1, T2> action, T1 arg, [CallerMemberName] string caller = "SeamlessClient")
|
||||||
|
{
|
||||||
|
//Jimm thank you. This is the best
|
||||||
|
var ctx = new TaskCompletionSource<T2>();
|
||||||
|
MySandboxGame.Static.Invoke(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ctx.SetResult(action.Invoke(arg));
|
||||||
|
ctx.Task.ContinueWith(task => task.Dispose());
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
ctx.SetException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}, caller);
|
||||||
|
return ctx.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task<T3> InvokeAsync<T1, T2, T3>(Func<T1, T2, T3> action, T1 arg, T2 arg2, [CallerMemberName] string caller = "SeamlessClient")
|
||||||
|
{
|
||||||
|
//Jimm thank you. This is the best
|
||||||
|
var ctx = new TaskCompletionSource<T3>();
|
||||||
|
MySandboxGame.Static.Invoke(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ctx.SetResult(action.Invoke(arg, arg2));
|
||||||
|
ctx.Task.ContinueWith(task => task.Dispose());
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
ctx.SetException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}, caller);
|
||||||
|
return ctx.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Task<T4> InvokeAsync<T1, T2, T3, T4>(Func<T1, T2, T3, T4> action, T1 arg, T2 arg2, T3 arg3, [CallerMemberName] string caller = "SeamlessClient")
|
||||||
|
{
|
||||||
|
//Jimm thank you. This is the best
|
||||||
|
var ctx = new TaskCompletionSource<T4>();
|
||||||
|
MySandboxGame.Static.Invoke(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ctx.SetResult(action.Invoke(arg, arg2, arg3));
|
||||||
|
ctx.Task.ContinueWith(task => task.Dispose());
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
ctx.SetException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}, caller);
|
||||||
|
return ctx.Task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -2,6 +2,7 @@
|
|||||||
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.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@@ -23,5 +24,10 @@ namespace SeamlessClient.Utilities
|
|||||||
public virtual void Destroy() { }
|
public virtual void Destroy() { }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public MethodInfo Get(Type type, string v)
|
||||||
|
{
|
||||||
|
return type.GetMethod(v, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
152
Utilities/PatchUtils.cs
Normal file
152
Utilities/PatchUtils.cs
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
using HarmonyLib;
|
||||||
|
using Sandbox.Game.Gui;
|
||||||
|
using Sandbox.Game.Multiplayer;
|
||||||
|
using Sandbox.Game.World.Generator;
|
||||||
|
using Sandbox.Game.World;
|
||||||
|
using SeamlessClient.Utilities;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using VRage.GameServices;
|
||||||
|
using VRage.Network;
|
||||||
|
|
||||||
|
namespace SeamlessClient.Components
|
||||||
|
{
|
||||||
|
public class PatchUtils : ComponentBase
|
||||||
|
{
|
||||||
|
/* All internal classes Types */
|
||||||
|
public static readonly Type ClientType =
|
||||||
|
Type.GetType("Sandbox.Engine.Multiplayer.MyMultiplayerClient, Sandbox.Game");
|
||||||
|
|
||||||
|
public static readonly Type SyncLayerType = Type.GetType("Sandbox.Game.Multiplayer.MySyncLayer, Sandbox.Game");
|
||||||
|
|
||||||
|
public static readonly Type MyTransportLayerType =
|
||||||
|
Type.GetType("Sandbox.Engine.Multiplayer.MyTransportLayer, Sandbox.Game");
|
||||||
|
|
||||||
|
public static readonly Type MySessionType = Type.GetType("Sandbox.Game.World.MySession, Sandbox.Game");
|
||||||
|
|
||||||
|
public static readonly Type VirtualClientsType =
|
||||||
|
Type.GetType("Sandbox.Engine.Multiplayer.MyVirtualClients, Sandbox.Game");
|
||||||
|
|
||||||
|
public static readonly Type GUIScreenChat = Type.GetType("Sandbox.Game.Gui.MyGuiScreenChat, Sandbox.Game");
|
||||||
|
|
||||||
|
public static readonly Type MyMultiplayerClientBase =
|
||||||
|
Type.GetType("Sandbox.Engine.Multiplayer.MyMultiplayerClientBase, Sandbox.Game");
|
||||||
|
|
||||||
|
public static readonly Type MySteamServerDiscovery =
|
||||||
|
Type.GetType("VRage.Steam.MySteamServerDiscovery, Vrage.Steam");
|
||||||
|
|
||||||
|
public static readonly Type MyEntitiesType =
|
||||||
|
Type.GetType("Sandbox.Game.Entities.MyEntities, Sandbox.Game");
|
||||||
|
|
||||||
|
public static readonly Type MySlimBlockType =
|
||||||
|
Type.GetType("Sandbox.Game.Entities.Cube.MySlimBlock, Sandbox.Game");
|
||||||
|
|
||||||
|
/* Harmony Patcher */
|
||||||
|
private static readonly Harmony Patcher = new Harmony("SeamlessClientPatcher");
|
||||||
|
|
||||||
|
|
||||||
|
/* Static Contructors */
|
||||||
|
public static ConstructorInfo ClientConstructor { get; private set; }
|
||||||
|
public static ConstructorInfo SyncLayerConstructor { get; private set; }
|
||||||
|
public static ConstructorInfo TransportLayerConstructor { get; private set; }
|
||||||
|
public static ConstructorInfo MySessionConstructor { get; private set; }
|
||||||
|
public static ConstructorInfo MyMultiplayerClientBaseConstructor { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
|
/* Static FieldInfos and PropertyInfos */
|
||||||
|
public static PropertyInfo MySessionLayer { get; private set; }
|
||||||
|
|
||||||
|
public static FieldInfo AdminSettings { get; private set; }
|
||||||
|
public static FieldInfo MPlayerGpsCollection { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
|
/* Static MethodInfos */
|
||||||
|
public static MethodInfo LoadPlayerInternal { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
|
public static MethodInfo SendPlayerData;
|
||||||
|
|
||||||
|
|
||||||
|
public static event EventHandler<JoinResultMsg> OnJoinEvent;
|
||||||
|
|
||||||
|
|
||||||
|
/* WorldGenerator */
|
||||||
|
public static MethodInfo UnloadProceduralWorldGenerator;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public override void Patch(Harmony patcher)
|
||||||
|
{
|
||||||
|
ClientConstructor = GetConstructor(ClientType, new[] { typeof(MyGameServerItem), SyncLayerType });
|
||||||
|
SyncLayerConstructor = GetConstructor(SyncLayerType, new[] { MyTransportLayerType });
|
||||||
|
TransportLayerConstructor = GetConstructor(MyTransportLayerType, new[] { typeof(int) });
|
||||||
|
MySessionConstructor = GetConstructor(MySessionType, new[] { typeof(MySyncLayer), typeof(bool) });
|
||||||
|
MyMultiplayerClientBaseConstructor = GetConstructor(MyMultiplayerClientBase, new[] { typeof(MySyncLayer) });
|
||||||
|
|
||||||
|
|
||||||
|
/* Get Fields and Properties */
|
||||||
|
MySessionLayer = GetProperty(typeof(MySession), "SyncLayer");
|
||||||
|
AdminSettings = GetField(typeof(MySession), "m_adminSettings");
|
||||||
|
|
||||||
|
MPlayerGpsCollection = GetField(typeof(MyPlayerCollection), "m_players");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Get Methods */
|
||||||
|
|
||||||
|
LoadPlayerInternal = GetMethod(typeof(MyPlayerCollection), "LoadPlayerInternal");
|
||||||
|
SendPlayerData = GetMethod(ClientType, "SendPlayerData");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//MethodInfo ConnectToServer = GetMethod(typeof(MyGameService), "ConnectToServer", BindingFlags.Static | BindingFlags.Public);
|
||||||
|
base.Patch(patcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region PatchMethods
|
||||||
|
|
||||||
|
public static MethodInfo GetMethod(Type type, string methodName)
|
||||||
|
{
|
||||||
|
|
||||||
|
var foundMethod = AccessTools.Method(type, methodName);
|
||||||
|
if (foundMethod == null)
|
||||||
|
throw new NullReferenceException($"Method for {methodName} is null!");
|
||||||
|
return foundMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FieldInfo GetField(Type type, string fieldName)
|
||||||
|
{
|
||||||
|
var foundField = AccessTools.Field(type, fieldName);
|
||||||
|
if (foundField == null)
|
||||||
|
throw new NullReferenceException($"Field for {fieldName} is null!");
|
||||||
|
return foundField;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PropertyInfo GetProperty(Type type, string propertyName)
|
||||||
|
{
|
||||||
|
var foundProperty = AccessTools.Property(type, propertyName);
|
||||||
|
if (foundProperty == null)
|
||||||
|
throw new NullReferenceException($"Property for {propertyName} is null!");
|
||||||
|
return foundProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ConstructorInfo GetConstructor(Type type, Type[] types)
|
||||||
|
{
|
||||||
|
var foundConstructor = AccessTools.Constructor(type, types);
|
||||||
|
if (foundConstructor == null)
|
||||||
|
throw new NullReferenceException($"Contructor for {type.Name} is null!");
|
||||||
|
return foundConstructor;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user