Works with just character
This commit is contained in:
@@ -16,8 +16,8 @@ namespace SeamlessClient.OnlinePlayersWindow
|
||||
|
||||
public class PlayersWindowComponent : ComponentBase
|
||||
{
|
||||
public static List<OnlineClientServer> allServers = new List<OnlineClientServer>();
|
||||
public static OnlineClientServer onlineServer;
|
||||
//public static List<OnlineClientServer> allServers = new List<OnlineClientServer>();
|
||||
//public static OnlineClientServer onlineServer;
|
||||
|
||||
|
||||
public override void Patch(Harmony patcher)
|
||||
@@ -38,6 +38,7 @@ namespace SeamlessClient.OnlinePlayersWindow
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public static void ApplyRecievedPlayers(List<OnlineClientServer> servers, int CurrentServer)
|
||||
{
|
||||
//Seamless.TryShow($"Recieved {CurrentServer} - {servers.Count}");
|
||||
@@ -56,6 +57,7 @@ namespace SeamlessClient.OnlinePlayersWindow
|
||||
allServers.Add(server);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
65
Components/SeamlessScriptManager.cs
Normal file
65
Components/SeamlessScriptManager.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VRage.Game;
|
||||
using VRage.Utils;
|
||||
|
||||
namespace SeamlessClient.Components
|
||||
{
|
||||
internal class SeamlessScriptManager
|
||||
{
|
||||
/* Sandbox.Game.World.MyScriptManager
|
||||
*
|
||||
* ScriptManager handles all mod script compiling and loading. Stores all compilations as various dictionaries like below:
|
||||
* Looks like TryAddEntityScripts(,) can be called whenever to pass in the mod context and the compiled assembly on the fly
|
||||
*
|
||||
* Possibly to dynamically load/unload mods?
|
||||
*/
|
||||
|
||||
|
||||
/* Dictionary<MyModContext, HashSet<MyStringId>> ScriptsPerMod ->
|
||||
*
|
||||
* Use in Sandbox.Game.World.MySession.RegisterComponentsFromAssembly
|
||||
* Primarily MySessionComponentDescriptor/Session Component
|
||||
*/
|
||||
|
||||
/* Dictionary<MyStringId, Assembly> Scripts ->
|
||||
*
|
||||
* Use in Sandbox.Game.World.MySession.RegisterComponentsFromAssembly
|
||||
* Stores actual assemblies for all modded scripts
|
||||
*/
|
||||
|
||||
|
||||
/* Dictionary<Type, HashSet<Type>> EntityScripts ->
|
||||
*
|
||||
* Use in Sandbox.Game.Entities.MyEntityFactory.AddScriptGameLogic
|
||||
* List of Type entity Scripts. Activator.CreatInstance called on entity creation
|
||||
*/
|
||||
|
||||
/* Dictionary<Tuple<Type, string>, HashSet<Type>> SubEntityScripts ->
|
||||
*
|
||||
* Use in Sandbox.Game.Entities.MyEntityFactory.AddScriptGameLogic
|
||||
* List of Type && SubType entity Scripts. Activator.CreatInstance called on entity creation
|
||||
*/
|
||||
|
||||
/* Dictionary<string, Type> StatScripts ->
|
||||
*
|
||||
* Use in Sandbox.Game.Components.MyEntityStatComponent
|
||||
* No idea what this is but prob need to deal with it
|
||||
*/
|
||||
|
||||
|
||||
/* Dictionary<MyStringId, Type> InGameScripts -> */
|
||||
|
||||
/* Dictionary<MyStringId, StringBuilder> InGameScriptsCode -> */
|
||||
|
||||
/* Dictionary<Type, MyModContext> TypeToModMap ->
|
||||
*
|
||||
* Use in Sandbox.Game.Entities.MyEntityFactory.AddScriptGameLogic
|
||||
* Some sort of type to mod logic component?
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
446
Components/SeamlessSwitcher.cs
Normal file
446
Components/SeamlessSwitcher.cs
Normal file
@@ -0,0 +1,446 @@
|
||||
using HarmonyLib;
|
||||
using Sandbox;
|
||||
using Sandbox.Engine.Multiplayer;
|
||||
using Sandbox.Engine.Networking;
|
||||
using Sandbox.Engine.Utils;
|
||||
using Sandbox.Game.Entities.Character;
|
||||
using Sandbox.Game.Entities;
|
||||
using Sandbox.Game.Gui;
|
||||
using Sandbox.Game.Multiplayer;
|
||||
using Sandbox.Game.SessionComponents;
|
||||
using Sandbox.Game.World;
|
||||
using Sandbox.Graphics.GUI;
|
||||
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.GameServices;
|
||||
using VRage.Library.Utils;
|
||||
using VRage.Network;
|
||||
using VRageRender;
|
||||
using EmptyKeys.UserInterface.Generated.StoreBlockView_Bindings;
|
||||
using System.Security.Cryptography;
|
||||
using VRage.Groups;
|
||||
using VRage.Game.ModAPI;
|
||||
using VRage;
|
||||
using static Sandbox.ModAPI.MyModAPIHelper;
|
||||
using NLog.Targets;
|
||||
using MyMultiplayer = Sandbox.Engine.Multiplayer.MyMultiplayer;
|
||||
using static Sandbox.Engine.Networking.MyNetworkWriter;
|
||||
using System.Collections.Concurrent;
|
||||
using VRage.Game.Entity;
|
||||
using VRageRender.Effects;
|
||||
using VRage.Scripting;
|
||||
using VRage.Utils;
|
||||
|
||||
namespace SeamlessClient.Components
|
||||
{
|
||||
public class SeamlessSwitcher : ComponentBase
|
||||
{
|
||||
private bool isSwitchingServer = false;
|
||||
|
||||
private PropertyInfo _MyGameServerItemProperty;
|
||||
private PropertyInfo _MultiplayerServerID;
|
||||
private static MethodInfo PauseClient;
|
||||
private MethodInfo _SendPlayerData;
|
||||
private MethodInfo _SendFlush;
|
||||
private MethodInfo _ClearTransportLayer;
|
||||
private FieldInfo _NetworkWriteQueue;
|
||||
private FieldInfo _TransportLayer;
|
||||
|
||||
private MethodInfo _OnConnectToServer;
|
||||
public static SeamlessSwitcher Instance;
|
||||
|
||||
private static List<MyCubeGrid> allGrids = new List<MyCubeGrid>();
|
||||
|
||||
private long _OriginalCharacterEntity = -1;
|
||||
private long _OriginalGridEntity = -1;
|
||||
private ulong TargetServerID;
|
||||
private static bool PreventRPC = false;
|
||||
private static bool StartPacketCheck = false;
|
||||
|
||||
public SeamlessSwitcher()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
|
||||
public override void Patch(Harmony patcher)
|
||||
{
|
||||
_MyGameServerItemProperty = PatchUtils.GetProperty(PatchUtils.ClientType, "Server");
|
||||
_MultiplayerServerID = PatchUtils.GetProperty(typeof(MyMultiplayerBase), "ServerId");
|
||||
|
||||
_OnConnectToServer = PatchUtils.GetMethod(PatchUtils.ClientType, "OnConnectToServer");
|
||||
PauseClient = PatchUtils.GetMethod(PatchUtils.MyMultiplayerClientBase, "PauseClient");
|
||||
|
||||
_SendPlayerData = PatchUtils.GetMethod(PatchUtils.ClientType, "SendPlayerData");
|
||||
_ClearTransportLayer = PatchUtils.GetMethod(PatchUtils.MyTransportLayerType, "Clear");
|
||||
|
||||
|
||||
List<string> st = AccessTools.GetMethodNames(PatchUtils.MyMultiplayerClientBase);
|
||||
|
||||
var _SendRPC = PatchUtils.GetMethod(PatchUtils.MyMultiplayerClientBase, "VRage.Replication.IReplicationClientCallback.SendEvent");
|
||||
|
||||
var preSendRPC = PatchUtils.GetMethod(this.GetType(), "PreventEvents");
|
||||
_SendFlush = PatchUtils.GetMethod(PatchUtils.MyTransportLayerType, "SendFlush");
|
||||
_NetworkWriteQueue = PatchUtils.GetField(typeof(MyNetworkWriter), "m_packetsToSend");
|
||||
_TransportLayer = PatchUtils.GetField(typeof(MySyncLayer), "TransportLayer");
|
||||
|
||||
|
||||
var method = AccessTools.Method(PatchUtils.MyTransportLayerType, "SendMessage", new Type[] { typeof(MyMessageId), typeof(IPacketData), typeof(bool), typeof(EndpointId), typeof(byte) });
|
||||
var patchSend = PatchUtils.GetMethod(this.GetType(), "SendMessage_Patch");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
patcher.Patch(method, prefix: patchSend);
|
||||
patcher.Patch(_SendRPC, prefix: preSendRPC);
|
||||
|
||||
|
||||
|
||||
base.Patch(patcher);
|
||||
}
|
||||
|
||||
public static void SendMessage_Patch(MyMessageId id, IPacketData data, bool reliable, EndpointId endpoint, byte index = 0)
|
||||
{
|
||||
if (!StartPacketCheck)
|
||||
return;
|
||||
|
||||
MyLog.Default?.WriteLineAndConsole($"{System.Environment.StackTrace}");
|
||||
Seamless.TryShow($"Id:{id} -> {endpoint}");
|
||||
}
|
||||
|
||||
|
||||
public void StartSwitch(MyGameServerItem TargetServer, MyObjectBuilder_World TargetWorld)
|
||||
{
|
||||
isSwitchingServer = true;
|
||||
TargetServerID = TargetServer.GameID;
|
||||
MyReplicationClient localMPClient = (MyReplicationClient)MyMultiplayer.Static.ReplicationLayer;
|
||||
_OriginalCharacterEntity = MySession.Static.LocalCharacter?.EntityId ?? 0;
|
||||
|
||||
if (MySession.Static.LocalCharacter?.Parent != null && MySession.Static.LocalCharacter.Parent is MyCockpit cockpit)
|
||||
{
|
||||
_OriginalGridEntity = cockpit.CubeGrid.EntityId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
UnloadCurrentServer(localMPClient);
|
||||
|
||||
|
||||
/* Fix any weird compatibilities */
|
||||
MySandboxGame.Static.SessionCompatHelper.FixSessionComponentObjectBuilders(TargetWorld.Checkpoint, TargetWorld.Sector);
|
||||
|
||||
/* Set New Multiplayer Stuff */
|
||||
_MyGameServerItemProperty.SetValue(MyMultiplayer.Static, TargetServer);
|
||||
_MultiplayerServerID.SetValue(MyMultiplayer.Static, TargetServer.SteamID);
|
||||
|
||||
/* Connect To Server */
|
||||
MyGameService.ConnectToServer(TargetServer, delegate (JoinResult joinResult)
|
||||
{
|
||||
MySandboxGame.Static.Invoke(delegate
|
||||
{
|
||||
Seamless.TryShow("Connected to server!");
|
||||
_OnConnectToServer.Invoke(MyMultiplayer.Static, new object[] { joinResult });
|
||||
OnUserJoined(joinResult);
|
||||
|
||||
}, "OnConnectToServer");
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void UnloadCurrentServer(MyReplicationClient localMPClient)
|
||||
{
|
||||
|
||||
//Close and Cancel any screens (Medical or QuestLog)
|
||||
MySessionComponentIngameHelp component = MySession.Static.GetComponent<MySessionComponentIngameHelp>();
|
||||
component?.TryCancelObjective();
|
||||
MyGuiScreenMedicals.Close();
|
||||
|
||||
MethodInfo removeClient = PatchUtils.GetMethod(PatchUtils.ClientType, "MyMultiplayerClient_ClientLeft");
|
||||
foreach (var connectedClient in Sync.Clients.GetClients())
|
||||
{
|
||||
if (connectedClient.SteamUserId == Sync.MyId || connectedClient.SteamUserId == Sync.ServerId)
|
||||
continue;
|
||||
|
||||
removeClient.Invoke(MyMultiplayer.Static, new object[] { connectedClient.SteamUserId, MyChatMemberStateChangeEnum.Left });
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Sends disconnect message to outbound server */
|
||||
MyControlDisconnectedMsg myControlDisconnectedMsg = default(MyControlDisconnectedMsg);
|
||||
myControlDisconnectedMsg.Client = Sync.MyId;
|
||||
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();
|
||||
|
||||
|
||||
|
||||
|
||||
//Remove old signals
|
||||
MyHud.GpsMarkers.Clear();
|
||||
MyHud.LocationMarkers.Clear();
|
||||
MyHud.HackingMarkers.Clear();
|
||||
|
||||
|
||||
Seamless.TryShow($"2 Streaming: {localMPClient.HasPendingStreamingReplicables} - LastMessage: {localMPClient.LastMessageFromServer}");
|
||||
Seamless.TryShow($"2 NexusMajor: {Seamless.NexusVersion.Major} - ConrolledEntity {MySession.Static.ControlledEntity == null} - HumanPlayer {MySession.Static.LocalHumanPlayer == null} - Character {MySession.Static.LocalCharacter == null}");
|
||||
|
||||
UnloadOldEntities();
|
||||
ResetReplicationTime(false);
|
||||
|
||||
//MyMultiplayer.Static.ReplicationLayer.Disconnect();
|
||||
//MyMultiplayer.Static.ReplicationLayer.Dispose();
|
||||
//MyMultiplayer.Static.Dispose();
|
||||
}
|
||||
|
||||
private static bool PreventEvents(IPacketData data, bool reliable)
|
||||
{
|
||||
return !PreventRPC;
|
||||
}
|
||||
|
||||
private void UnloadOldEntities()
|
||||
{
|
||||
|
||||
|
||||
foreach (var ent in MyEntities.GetEntities().ToList())
|
||||
{
|
||||
if (ent is MyPlanet || ent is MyCubeGrid)
|
||||
continue;
|
||||
|
||||
if(ent.EntityId == _OriginalCharacterEntity && ent is MyCharacter character)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ent.Close();
|
||||
}
|
||||
|
||||
List<IMyGridGroupData> grids = new List<IMyGridGroupData>();
|
||||
|
||||
foreach (var gridgroup in MyCubeGridGroups.GetGridGroups(VRage.Game.ModAPI.GridLinkTypeEnum.Physical, grids))
|
||||
{
|
||||
|
||||
List<IMyCubeGrid> localGrids = new List<IMyCubeGrid>();
|
||||
localGrids = gridgroup.GetGrids(localGrids);
|
||||
|
||||
if (localGrids.Count == 0)
|
||||
continue;
|
||||
|
||||
if (localGrids.Any(x => x.EntityId == _OriginalGridEntity))
|
||||
{
|
||||
|
||||
foreach(var grid in localGrids)
|
||||
{
|
||||
MyEntity ent = grid as MyEntity;
|
||||
allGrids.Add((MyCubeGrid)grid);
|
||||
grid.Synchronized = false;
|
||||
|
||||
ent.SyncFlag = false;
|
||||
ent.Save = false;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
//Delete
|
||||
foreach (var grid in localGrids)
|
||||
{
|
||||
grid.Close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MyMultiplayer.Static.ReplicationLayer.Dispose();
|
||||
PreventRPC = true;
|
||||
StartPacketCheck = true;
|
||||
|
||||
//_ClearTransportLayer.Invoke(_TransportLayer.GetValue(MyMultiplayer.Static.SyncLayer), null);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void OnUserJoined(JoinResult joinResult)
|
||||
{
|
||||
isSwitchingServer = false;
|
||||
|
||||
if (joinResult != JoinResult.OK)
|
||||
return;
|
||||
|
||||
Seamless.TryShow($"OnUserJoin! Result: {joinResult}");
|
||||
LoadDestinationServer();
|
||||
|
||||
|
||||
|
||||
_SendPlayerData.Invoke(MyMultiplayer.Static, new object[] { MyGameService.OnlineName });
|
||||
}
|
||||
|
||||
private void LoadDestinationServer()
|
||||
{
|
||||
MyReplicationClient clienta = (MyReplicationClient)MyMultiplayer.Static.ReplicationLayer;
|
||||
Seamless.TryShow($"5 Streaming: {clienta.HasPendingStreamingReplicables} - LastMessage: {clienta.LastMessageFromServer}");
|
||||
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
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
List<ulong> clients = new List<ulong>();
|
||||
foreach (var client in Sync.Clients.GetClients())
|
||||
{
|
||||
clients.Add(client.SteamUserId);
|
||||
Seamless.TryShow($"ADDING {client.SteamUserId} - {Sync.MyId}");
|
||||
}
|
||||
|
||||
foreach (var client in clients)
|
||||
{
|
||||
if (client == TargetServerID || client == Sync.MyId)
|
||||
continue;
|
||||
|
||||
Seamless.TryShow($"REMOVING {client}");
|
||||
Sync.Clients.RemoveClient(client);
|
||||
}
|
||||
|
||||
|
||||
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}");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
PauseClient.Invoke(MyMultiplayer.Static, new object[] { false });
|
||||
MySandboxGame.PausePop();
|
||||
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Request client state batch
|
||||
(MyMultiplayer.Static as MyMultiplayerClientBase).RequestBatchConfirmation();
|
||||
MyMultiplayer.Static.PendingReplicablesDone += Static_PendingReplicablesDone;
|
||||
//typeof(MyGuiScreenTerminal).GetMethod("CreateTabs")
|
||||
|
||||
//MySession.Static.LocalHumanPlayer.Controller.TakeControl(originalControlledEntity);
|
||||
|
||||
MyGuiSandbox.UnloadContent();
|
||||
MyGuiSandbox.LoadContent();
|
||||
MyGuiScreenHudSpace.Static?.RecreateControls(true);
|
||||
|
||||
|
||||
|
||||
//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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void ResetReplicationTime(bool ClientReady)
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
if (!ClientReady)
|
||||
{
|
||||
PatchUtils.GetField(typeof(MyReplicationClient), "m_lastServerTimestamp").SetValue(MyMultiplayer.Static.ReplicationLayer, MyTimeSpan.Zero);
|
||||
PatchUtils.GetField(typeof(MyReplicationClient), "m_lastServerTimeStampReceivedTime").SetValue(MyMultiplayer.Static.ReplicationLayer, MyTimeSpan.Zero);
|
||||
PatchUtils.GetField(typeof(MyReplicationClient), "m_clientStartTimeStamp").SetValue(MyMultiplayer.Static.ReplicationLayer, MyTimeSpan.Zero);
|
||||
PatchUtils.GetField(typeof(MyReplicationClient), "m_lastTime").SetValue(MyMultiplayer.Static.ReplicationLayer, MyTimeSpan.Zero);
|
||||
PatchUtils.GetField(typeof(MyReplicationClient), "m_lastClientTime").SetValue(MyMultiplayer.Static.ReplicationLayer, MyTimeSpan.Zero);
|
||||
PatchUtils.GetField(typeof(MyReplicationClient), "m_lastServerTime").SetValue(MyMultiplayer.Static.ReplicationLayer, MyTimeSpan.Zero);
|
||||
PatchUtils.GetField(typeof(MyReplicationClient), "m_lastClientTimestamp").SetValue(MyMultiplayer.Static.ReplicationLayer, MyTimeSpan.Zero);
|
||||
PatchUtils.GetField(typeof(MyReplicationClient), "m_lastStateSyncPacketId").SetValue(MyMultiplayer.Static.ReplicationLayer,(byte)0);
|
||||
PatchUtils.GetField(typeof(MyReplicationClient), "m_lastStreamingPacketId").SetValue(MyMultiplayer.Static.ReplicationLayer, (byte)0);
|
||||
PatchUtils.GetField(typeof(MyReplicationClient), "m_lastStreamingPacketId").SetValue(MyMultiplayer.Static.ReplicationLayer, (byte)0);
|
||||
//PatchUtils.GetField(typeof(MyReplicationClient), "m_acks").SetValue(MyMultiplayer.Static.ReplicationLayer, new List<byte>());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
PatchUtils.GetField(typeof(MyReplicationClient), "m_clientReady").SetValue(MyMultiplayer.Static.ReplicationLayer, ClientReady);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
@@ -1,845 +0,0 @@
|
||||
using HarmonyLib;
|
||||
using Sandbox;
|
||||
using Sandbox.Engine.Multiplayer;
|
||||
using Sandbox.Engine.Networking;
|
||||
using Sandbox.Engine.Utils;
|
||||
using Sandbox.Game;
|
||||
using Sandbox.Game.Entities;
|
||||
using Sandbox.Game.Entities.Character;
|
||||
using Sandbox.Game.Gui;
|
||||
using Sandbox.Game.GUI;
|
||||
using Sandbox.Game.GUI.HudViewers;
|
||||
using Sandbox.Game.Multiplayer;
|
||||
using Sandbox.Game.Replication;
|
||||
using Sandbox.Game.SessionComponents;
|
||||
using Sandbox.Game.World;
|
||||
using Sandbox.Game.World.Generator;
|
||||
using Sandbox.Graphics.GUI;
|
||||
using Sandbox.ModAPI;
|
||||
using SeamlessClient.Components;
|
||||
using SeamlessClient.OnlinePlayersWindow;
|
||||
using SeamlessClient.Utilities;
|
||||
using SpaceEngineers.Game.GUI;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using VRage;
|
||||
using VRage.Game;
|
||||
using VRage.Game.Entity;
|
||||
using VRage.Game.ModAPI;
|
||||
using VRage.GameServices;
|
||||
using VRage.Library.Utils;
|
||||
using VRage.Network;
|
||||
using VRage.Utils;
|
||||
using VRageRender;
|
||||
using VRageRender.Messages;
|
||||
|
||||
namespace SeamlessClient.ServerSwitching
|
||||
{
|
||||
public class ServerSwitcherComponent : ComponentBase
|
||||
{
|
||||
public static ConstructorInfo ClientConstructor;
|
||||
public static ConstructorInfo SyncLayerConstructor;
|
||||
public static ConstructorInfo TransportLayerConstructor;
|
||||
|
||||
public static PropertyInfo MySessionLayer;
|
||||
|
||||
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 MethodInfo CreateNewPlayerInternal;
|
||||
private static MethodInfo PauseClient;
|
||||
public static MethodInfo SendPlayerData;
|
||||
|
||||
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;
|
||||
|
||||
private MyCharacter originalLocalCharacter;
|
||||
private IMyControllableEntity originalControlledEntity;
|
||||
|
||||
private static bool isSwitch = false;
|
||||
private MyObjectBuilder_Player player { get; set; }
|
||||
private static Timer pauseResetTimer = new Timer(1000);
|
||||
|
||||
|
||||
|
||||
public ServerSwitcherComponent()
|
||||
{
|
||||
pauseResetTimer.Elapsed += PauseResetTimer_Elapsed;
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
private void PauseResetTimer_Elapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
if(MySandboxGame.IsPaused)
|
||||
{
|
||||
Seamless.TryShow("Game is still paused... Attempting to unpause!");
|
||||
MySandboxGame.PausePop();
|
||||
}
|
||||
else
|
||||
{
|
||||
pauseResetTimer.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Patch(Harmony patcher)
|
||||
{
|
||||
MySessionLayer = PatchUtils.GetProperty(typeof(MySession), "SyncLayer");
|
||||
|
||||
PauseClient = PatchUtils.GetMethod(PatchUtils.MyMultiplayerClientBase, "PauseClient");
|
||||
ClientConstructor = PatchUtils.GetConstructor(PatchUtils.ClientType, new[] { typeof(MyGameServerItem), PatchUtils.SyncLayerType });
|
||||
SyncLayerConstructor = PatchUtils.GetConstructor(PatchUtils.SyncLayerType, new[] { PatchUtils.MyTransportLayerType });
|
||||
TransportLayerConstructor = PatchUtils.GetConstructor(PatchUtils.MyTransportLayerType, new[] { typeof(int) });
|
||||
|
||||
|
||||
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");
|
||||
SendPlayerData = PatchUtils.GetMethod(PatchUtils.ClientType, "SendPlayerData");
|
||||
|
||||
CreateNewPlayerInternal = PatchUtils.GetMethod(typeof(MyPlayerCollection), "CreateNewPlayerInternal");
|
||||
|
||||
|
||||
MethodInfo onAllmembersData = PatchUtils.ClientType.GetMethod("OnAllMembersData", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
|
||||
var onClientRemoved = PatchUtils.GetMethod(typeof(MyClientCollection), "RemoveClient");
|
||||
var onDisconnectedClient = PatchUtils.GetMethod(typeof(MyMultiplayerBase), "OnDisconnectedClient");
|
||||
var onClientConnected = PatchUtils.GetMethod(PatchUtils.ClientType, "OnClientConnected");
|
||||
var processAllMembersData = PatchUtils.GetMethod(typeof(MyMultiplayerBase), "ProcessAllMembersData");
|
||||
var RemovePlayer = PatchUtils.GetMethod(typeof(MyPlayerCollection), "RemovePlayerFromDictionary");
|
||||
var LoadClient = PatchUtils.GetMethod(PatchUtils.ClientType, "LoadMembersFromWorld");
|
||||
|
||||
|
||||
Seamless.TryShow("Patched!");
|
||||
|
||||
|
||||
patcher.Patch(LoadClient, prefix: new HarmonyMethod(Get(typeof(ServerSwitcherComponent), nameof(LoadClientsFromWorld))));
|
||||
patcher.Patch(RemovePlayer, prefix: new HarmonyMethod(Get(typeof(ServerSwitcherComponent), nameof(RemovePlayerFromDict))));
|
||||
patcher.Patch(processAllMembersData, prefix: new HarmonyMethod(Get(typeof(ServerSwitcherComponent), nameof(ProcessAllMembersData))));
|
||||
patcher.Patch(onDisconnectedClient, prefix: new HarmonyMethod(Get(typeof(ServerSwitcherComponent), nameof(OnDisconnectedClient))));
|
||||
patcher.Patch(onClientRemoved, prefix: new HarmonyMethod(Get(typeof(ServerSwitcherComponent), nameof(RemoveClient))));
|
||||
patcher.Patch(onAllmembersData, prefix: new HarmonyMethod(Get(typeof(ServerSwitcherComponent), nameof(OnAllMembersData))));
|
||||
|
||||
|
||||
|
||||
patcher.Patch(onClientConnected, prefix: new HarmonyMethod(Get(typeof(ServerSwitcherComponent), nameof(OnClientConnected))));
|
||||
|
||||
|
||||
base.Patch(patcher);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void OnUserJoined(JoinResult msg)
|
||||
{
|
||||
Seamless.TryShow($"OnUserJoin! Result: {msg}");
|
||||
if (msg == JoinResult.OK)
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
//SendPlayerData
|
||||
|
||||
|
||||
//Invoke the switch event
|
||||
if (isSwitch)
|
||||
Instance.StartSwitch();
|
||||
|
||||
if (MySandboxGame.IsPaused)
|
||||
{
|
||||
pauseResetTimer.Start();
|
||||
MyHud.Notifications.Remove(MyNotificationSingletons.ConnectionProblem);
|
||||
MySandboxGame.PausePop();
|
||||
}
|
||||
|
||||
SendPlayerData.Invoke(MyMultiplayer.Static, new object[] { MyGameService.OnlineName });
|
||||
isSwitch = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool LoadClientsFromWorld(ref List<MyObjectBuilder_Client> clients)
|
||||
{
|
||||
if(!isSwitch || clients == null || clients.Count == 0)
|
||||
return true;
|
||||
|
||||
|
||||
//Dictionary<ulong, MyConnectedClientData>
|
||||
|
||||
IDictionary m_memberData = (IDictionary)PatchUtils.GetField(PatchUtils.ClientType, "m_memberData").GetValue(MyMultiplayer.Static);
|
||||
|
||||
Seamless.TryShow($"{m_memberData.Count} members from clients");
|
||||
|
||||
var keys = m_memberData.Keys.Cast<ulong>();
|
||||
|
||||
for(int i = clients.Count - 1; i >= 0; i-- )
|
||||
{
|
||||
Seamless.TryShow($"Client {clients[i].SteamId}");
|
||||
if (keys.Contains(clients[i].SteamId))
|
||||
{
|
||||
Seamless.TryShow($"Remove {clients[i].SteamId}");
|
||||
clients.RemoveAt(i);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static bool ProcessAllMembersData(ref AllMembersDataMsg msg)
|
||||
{
|
||||
if(!isSwitch)
|
||||
return true;
|
||||
|
||||
|
||||
Sync.Players.ClearIdentities();
|
||||
if (msg.Identities != null)
|
||||
{
|
||||
Sync.Players.LoadIdentities(msg.Identities);
|
||||
}
|
||||
|
||||
Seamless.TryShow($"Clearing Players! \n {Environment.StackTrace.ToString()} ");
|
||||
|
||||
//Sync.Players.ClearPlayers();
|
||||
if (msg.Players != null)
|
||||
{
|
||||
Sync.Players.LoadPlayers(msg.Players);
|
||||
}
|
||||
|
||||
//MySession.Static.Factions.LoadFactions(msg.Factions);
|
||||
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool RemovePlayerFromDict(MyPlayer.PlayerId playerId)
|
||||
{
|
||||
//Seamless.TryShow($"Removing player {playerId.SteamId} from dictionariy! \n {Environment.StackTrace.ToString()} - Sender: {MyEventContext.Current.Sender}");
|
||||
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public override void Initilized()
|
||||
{
|
||||
base.Initilized();
|
||||
}
|
||||
|
||||
|
||||
public static bool OnClientConnected(MyPacket packet)
|
||||
{
|
||||
Seamless.TryShow("OnClientConnected");
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void RemoveClient(ulong steamId)
|
||||
{
|
||||
if (steamId != Sync.MyId)
|
||||
return;
|
||||
|
||||
//Seamless.TryShow(Environment.StackTrace.ToString());
|
||||
}
|
||||
|
||||
|
||||
public static void OnAllMembersData(ref AllMembersDataMsg msg)
|
||||
{
|
||||
Seamless.TryShow("Recieved all members data!");
|
||||
}
|
||||
|
||||
public static bool OnDisconnectedClient(ref MyControlDisconnectedMsg data, ulong sender)
|
||||
{
|
||||
Seamless.TryShow($"OnDisconnectedClient {data.Client} - Sender {sender}");
|
||||
if (data.Client == Sync.MyId)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public void StartBackendSwitch(MyGameServerItem TargetServer, MyObjectBuilder_World TargetWorld)
|
||||
{
|
||||
this.TargetServer = TargetServer;
|
||||
this.TargetWorld = TargetWorld;
|
||||
OldArmorSkin = MySession.Static.LocalHumanPlayer.BuildArmorSkin;
|
||||
|
||||
Seamless.TryShow($"1 NexusMajor: {Seamless.NexusVersion.Major} - ConrolledEntity {MySession.Static.ControlledEntity == null} - HumanPlayer {MySession.Static.LocalHumanPlayer == null} - Character {MySession.Static.LocalCharacter == null}");
|
||||
if (Seamless.NexusVersion.Major >= 2)
|
||||
needsEntityUnload = false;
|
||||
|
||||
isSwitch = true;
|
||||
|
||||
originalLocalCharacter = MySession.Static.LocalCharacter;
|
||||
//originalControlledEntity = MySession.Static.ControlledEntity;
|
||||
|
||||
|
||||
|
||||
|
||||
player = MySession.Static.LocalHumanPlayer.GetObjectBuilder();
|
||||
player.Connected = false;
|
||||
|
||||
AsyncInvoke.InvokeAsync(() =>
|
||||
{
|
||||
Seamless.TryShow($"Needs entity Unload: {needsEntityUnload}");
|
||||
|
||||
if (needsEntityUnload)
|
||||
MySession.Static.SetCameraController(MyCameraControllerEnum.SpectatorFixed);
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
UnloadServer();
|
||||
SetNewMultiplayerClient();
|
||||
|
||||
}catch(Exception ex)
|
||||
{
|
||||
Seamless.TryShow(ex.ToString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void ResetReplicationTime(bool ClientReady)
|
||||
{
|
||||
PatchUtils.GetField(typeof(MyReplicationClient), "m_lastServerTimestamp").SetValue(MyMultiplayer.Static.ReplicationLayer, MyTimeSpan.Zero);
|
||||
PatchUtils.GetField(typeof(MyReplicationClient), "m_lastServerTimeStampReceivedTime").SetValue(MyMultiplayer.Static.ReplicationLayer, MyTimeSpan.Zero);
|
||||
PatchUtils.GetField(typeof(MyReplicationClient), "m_clientStartTimeStamp").SetValue(MyMultiplayer.Static.ReplicationLayer, MyTimeSpan.Zero);
|
||||
PatchUtils.GetField(typeof(MyReplicationClient), "m_lastTime").SetValue(MyMultiplayer.Static.ReplicationLayer, MyTimeSpan.Zero);
|
||||
PatchUtils.GetField(typeof(MyReplicationClient), "m_lastClientTime").SetValue(MyMultiplayer.Static.ReplicationLayer, MyTimeSpan.Zero);
|
||||
PatchUtils.GetField(typeof(MyReplicationClient), "m_lastServerTime").SetValue(MyMultiplayer.Static.ReplicationLayer, MyTimeSpan.Zero);
|
||||
PatchUtils.GetField(typeof(MyReplicationClient), "m_lastClientTimestamp").SetValue(MyMultiplayer.Static.ReplicationLayer, MyTimeSpan.Zero);
|
||||
|
||||
|
||||
PatchUtils.GetField(typeof(MyReplicationClient), "m_clientReady").SetValue(MyMultiplayer.Static.ReplicationLayer, ClientReady);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
MethodInfo removeClient = PatchUtils.GetMethod(PatchUtils.ClientType, "MyMultiplayerClient_ClientLeft");
|
||||
foreach (var connectedClient in Sync.Clients.GetClients())
|
||||
{
|
||||
if (connectedClient.SteamUserId == Sync.MyId || connectedClient.SteamUserId == Sync.ServerId)
|
||||
continue;
|
||||
|
||||
removeClient.Invoke(MyMultiplayer.Static, new object[] { connectedClient.SteamUserId, MyChatMemberStateChangeEnum.Left });
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//Clear all local GPS Points
|
||||
MyReplicationClient client = (MyReplicationClient)MyMultiplayer.Static.ReplicationLayer;
|
||||
client.Dispose();
|
||||
//client.Disconnect();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
MyGameService.Peer2Peer.CloseSession(Sync.ServerId);
|
||||
MyGameService.DisconnectFromServer();
|
||||
|
||||
|
||||
MyControlDisconnectedMsg myControlDisconnectedMsg = default(MyControlDisconnectedMsg);
|
||||
myControlDisconnectedMsg.Client = Sync.MyId;
|
||||
|
||||
typeof(MyMultiplayerBase).GetMethod("SendControlMessage", BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(typeof(MyControlDisconnectedMsg)).Invoke(MyMultiplayer.Static, new object[] { Sync.ServerId, myControlDisconnectedMsg, true });
|
||||
|
||||
//DisconnectReplication
|
||||
//MyMultiplayer.Static.ReplicationLayer.Disconnect();
|
||||
//MyMultiplayer.Static.ReplicationLayer.Dispose();
|
||||
//MyMultiplayer.Static.Dispose();
|
||||
//MyMultiplayer.Static = null;
|
||||
|
||||
MyReplicationClient clienta = (MyReplicationClient)MyMultiplayer.Static.ReplicationLayer;
|
||||
|
||||
ResetReplicationTime(false);
|
||||
|
||||
//Remove old signals
|
||||
MyHud.GpsMarkers.Clear();
|
||||
MyHud.LocationMarkers.Clear();
|
||||
MyHud.HackingMarkers.Clear();
|
||||
|
||||
|
||||
Seamless.TryShow($"2 Streaming: {clienta.HasPendingStreamingReplicables} - LastMessage: {clienta.LastMessageFromServer}");
|
||||
Seamless.TryShow($"2 NexusMajor: {Seamless.NexusVersion.Major} - ConrolledEntity {MySession.Static.ControlledEntity == null} - HumanPlayer {MySession.Static.LocalHumanPlayer == null} - Character {MySession.Static.LocalCharacter == null}");
|
||||
|
||||
|
||||
|
||||
|
||||
//MyMultiplayer.Static.ReplicationLayer.Disconnect();
|
||||
//MyMultiplayer.Static.ReplicationLayer.Dispose();
|
||||
//MyMultiplayer.Static.Dispose();
|
||||
}
|
||||
|
||||
private void UnloadOldEntities()
|
||||
{
|
||||
|
||||
|
||||
foreach (var ent in MyEntities.GetEntities())
|
||||
{
|
||||
if (ent is MyPlanet)
|
||||
continue;
|
||||
|
||||
if (ent is MyCharacter)
|
||||
continue;
|
||||
|
||||
|
||||
ent.Close();
|
||||
|
||||
if (needsEntityUnload)
|
||||
{
|
||||
ent.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void SetNewMultiplayerClient()
|
||||
{
|
||||
|
||||
MyReplicationClient clienta = (MyReplicationClient)MyMultiplayer.Static.ReplicationLayer;
|
||||
Seamless.TryShow($"3 Streaming: {clienta.HasPendingStreamingReplicables} - LastMessage: {clienta.LastMessageFromServer}");
|
||||
Seamless.TryShow($"3 NexusMajor: {Seamless.NexusVersion.Major} - ConrolledEntity {MySession.Static.ControlledEntity == null} - HumanPlayer {MySession.Static.LocalHumanPlayer == null} - Character {MySession.Static.LocalCharacter == null}");
|
||||
|
||||
|
||||
|
||||
MySandboxGame.Static.SessionCompatHelper.FixSessionComponentObjectBuilders(TargetWorld.Checkpoint, TargetWorld.Sector);
|
||||
|
||||
|
||||
|
||||
|
||||
PatchUtils.ClientType.GetProperty("Server", BindingFlags.Public | BindingFlags.Instance).SetValue(MyMultiplayer.Static, TargetServer);
|
||||
typeof(MyMultiplayerBase).GetProperty("ServerId", BindingFlags.Public | BindingFlags.Instance).SetValue(MyMultiplayer.Static, TargetServer.SteamID);
|
||||
|
||||
|
||||
MyGameService.ConnectToServer(TargetServer, delegate (JoinResult joinResult)
|
||||
{
|
||||
MySandboxGame.Static.Invoke(delegate
|
||||
{
|
||||
Seamless.TryShow("Connected to server!");
|
||||
PatchUtils.ClientType.GetMethod("OnConnectToServer", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(MyMultiplayer.Static, new object[] { joinResult });
|
||||
OnUserJoined(joinResult);
|
||||
|
||||
|
||||
}, "OnConnectToServer");
|
||||
});
|
||||
|
||||
|
||||
|
||||
Seamless.TryShow($"4 Streaming: {clienta.HasPendingStreamingReplicables} - LastMessage: {clienta.LastMessageFromServer}");
|
||||
Seamless.TryShow($"4 NexusMajor: {Seamless.NexusVersion.Major} - ConrolledEntity {MySession.Static.ControlledEntity == null} - HumanPlayer {MySession.Static.LocalHumanPlayer == null} - Character {MySession.Static.LocalCharacter == null}");
|
||||
|
||||
|
||||
return;
|
||||
// Create constructors
|
||||
var LayerInstance = TransportLayerConstructor.Invoke(new object[] { 2 });
|
||||
var SyncInstance = SyncLayerConstructor.Invoke(new object[] { LayerInstance });
|
||||
var instance = ClientConstructor.Invoke(new object[] { TargetServer, SyncInstance });
|
||||
|
||||
|
||||
MyMultiplayer.Static = UtilExtensions.CastToReflected(instance, PatchUtils.ClientType);
|
||||
MyMultiplayer.Static.ExperimentalMode = true;
|
||||
|
||||
|
||||
|
||||
// Set the new SyncLayer to the MySession.Static.SyncLayer
|
||||
MySessionLayer.SetValue(MySession.Static, MyMultiplayer.Static.SyncLayer);
|
||||
Sync.Clients.SetLocalSteamId(Sync.MyId, false, MyGameService.UserName);
|
||||
return;
|
||||
Seamless.TryShow("Successfully set MyMultiplayer.Static");
|
||||
|
||||
|
||||
|
||||
Sync.Clients.SetLocalSteamId(Sync.MyId, false, MyGameService.UserName);
|
||||
Sync.Players.RegisterEvents();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private void StartSwitch()
|
||||
{
|
||||
|
||||
MyReplicationClient clienta = (MyReplicationClient)MyMultiplayer.Static.ReplicationLayer;
|
||||
Seamless.TryShow($"5 Streaming: {clienta.HasPendingStreamingReplicables} - LastMessage: {clienta.LastMessageFromServer}");
|
||||
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
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
List<ulong> clients = new List<ulong>();
|
||||
foreach(var client in Sync.Clients.GetClients())
|
||||
{
|
||||
clients.Add(client.SteamUserId);
|
||||
Seamless.TryShow($"ADDING {client.SteamUserId} - {Sync.MyId}");
|
||||
}
|
||||
|
||||
foreach(var client in clients)
|
||||
{
|
||||
if (client == TargetServer.SteamID || client == Sync.MyId)
|
||||
continue;
|
||||
|
||||
Seamless.TryShow($"REMOVING {client}");
|
||||
Sync.Clients.RemoveClient(client);
|
||||
}
|
||||
|
||||
LoadConnectedClients();
|
||||
|
||||
//Sync.Clients.SetLocalSteamId(Sync.MyId, !Sync.Clients.HasClient(Sync.MyId), MyGameService.UserName);
|
||||
|
||||
|
||||
Seamless.TryShow("Applying World Settings...");
|
||||
SetWorldSettings();
|
||||
|
||||
Seamless.TryShow("Starting Components...");
|
||||
InitComponents();
|
||||
|
||||
Seamless.TryShow("Starting Entity Sync...");
|
||||
StartEntitySync();
|
||||
|
||||
//MyGuiSandbox.RemoveScreen(MyGuiScreenHudSpace.Static);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
typeof(MySandboxGame).GetField("m_pauseStackCount", BindingFlags.Static | BindingFlags.NonPublic).SetValue(null, 0);
|
||||
|
||||
|
||||
|
||||
MyHud.Chat.RegisterChat(MyMultiplayer.Static);
|
||||
//GpsRegisterChat.Invoke(MySession.Static.Gpss, new object[] { MyMultiplayer.Static });
|
||||
|
||||
|
||||
|
||||
//Recreate all controls... Will fix weird gui/paint/crap
|
||||
//MyGuiScreenHudSpace.Static?.RecreateControls(true);
|
||||
|
||||
|
||||
|
||||
|
||||
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}");
|
||||
|
||||
originalLocalCharacter?.Close();
|
||||
ResetReplicationTime(true);
|
||||
|
||||
// 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);
|
||||
|
||||
Seamless.SendSeamlessVersion();
|
||||
|
||||
FieldInfo hudPoints = typeof(MyHudMarkerRender).GetField("m_pointsOfInterest", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
IList points = (IList)hudPoints.GetValue(MyGuiScreenHudSpace.Static.MarkerRender);
|
||||
|
||||
MySandboxGame.PausePop();
|
||||
//Sync.Players.RequestNewPlayer(Sync.MyId, 0, MyGameService.UserName, null, true, true);
|
||||
PauseClient.Invoke(MyMultiplayer.Static, new object[] { false });
|
||||
MySandboxGame.PausePop();
|
||||
MyHud.Notifications.Remove(MyNotificationSingletons.ConnectionProblem);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
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()
|
||||
{
|
||||
UpdateWorldGenerator();
|
||||
}
|
||||
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, 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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Request client state batch
|
||||
(MyMultiplayer.Static as MyMultiplayerClientBase).RequestBatchConfirmation();
|
||||
MyMultiplayer.Static.PendingReplicablesDone += Static_PendingReplicablesDone;
|
||||
//typeof(MyGuiScreenTerminal).GetMethod("CreateTabs")
|
||||
|
||||
//MySession.Static.LocalHumanPlayer.Controller.TakeControl(originalControlledEntity);
|
||||
|
||||
MyGuiSandbox.UnloadContent();
|
||||
MyGuiSandbox.LoadContent();
|
||||
|
||||
//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()
|
||||
{
|
||||
Seamless.TryShow($"BEFORE {MySession.Static.LocalHumanPlayer == null} - {MySession.Static.LocalCharacter == 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);
|
||||
}
|
||||
|
||||
|
||||
LoadMembersFromWorld.Invoke(MySession.Static, new object[] { TargetWorld, MyMultiplayer.Static });
|
||||
|
||||
|
||||
|
||||
|
||||
player.IsWildlifeAgent = true;
|
||||
CreateNewPlayerInternal.Invoke(MySession.Static.Players, new object[] { Sync.Clients.LocalClient, savingPlayerId.Value, player });
|
||||
typeof(MyPlayerCollection).GetMethod("LoadPlayerInternal", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(MySession.Static.Players, new object[] { savingPlayerId.Value, player, false });
|
||||
|
||||
Seamless.TryShow("Saving PlayerID: " + savingPlayerId.ToString());
|
||||
|
||||
Sync.Players.LoadConnectedPlayers(TargetWorld.Checkpoint, savingPlayerId);
|
||||
Sync.Players.LoadControlledEntities(TargetWorld.Checkpoint.ControlledEntities, TargetWorld.Checkpoint.ControlledObject, savingPlayerId);
|
||||
|
||||
Seamless.TryShow($"AFTER {MySession.Static.LocalHumanPlayer == null} - {MySession.Static.LocalCharacter == null}");
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,494 +0,0 @@
|
||||
using HarmonyLib;
|
||||
using Sandbox.Game.World;
|
||||
using Sandbox;
|
||||
using SeamlessClient.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VRage.Game;
|
||||
using VRage.GameServices;
|
||||
using Sandbox.Game.Gui;
|
||||
using Sandbox.Game.SessionComponents;
|
||||
using SpaceEngineers.Game.GUI;
|
||||
using Sandbox.Engine.Multiplayer;
|
||||
using Sandbox.Game.Multiplayer;
|
||||
using Sandbox.Game.Entities;
|
||||
using Sandbox.Engine.Networking;
|
||||
using System.Reflection;
|
||||
using VRage.Network;
|
||||
using Sandbox.ModAPI;
|
||||
using VRageRender.Messages;
|
||||
using VRageRender;
|
||||
using Sandbox.Game.GUI;
|
||||
using Sandbox.Game.World.Generator;
|
||||
using Sandbox.Game;
|
||||
using VRage.Game.ModAPI;
|
||||
using VRage.Utils;
|
||||
using SeamlessClient.ServerSwitching;
|
||||
|
||||
namespace SeamlessClient.Components
|
||||
{
|
||||
public class ServerSwitcherComponentOLD : ComponentBase
|
||||
{
|
||||
private static bool isSeamlessSwitching = false;
|
||||
private static ConstructorInfo TransportLayerConstructor;
|
||||
private static ConstructorInfo SyncLayerConstructor;
|
||||
private static ConstructorInfo ClientConstructor;
|
||||
|
||||
private static MethodInfo UnloadProceduralWorldGenerator;
|
||||
private static MethodInfo GpsRegisterChat;
|
||||
private static MethodInfo LoadMembersFromWorld;
|
||||
private static MethodInfo InitVirtualClients;
|
||||
private static FieldInfo AdminSettings;
|
||||
private static FieldInfo RemoteAdminSettings;
|
||||
private static FieldInfo VirtualClients;
|
||||
private static PropertyInfo MySessionLayer;
|
||||
|
||||
public static MyGameServerItem TargetServer { get; private set; }
|
||||
public static MyObjectBuilder_World TargetWorld { get; private set; }
|
||||
|
||||
public static ServerSwitcherComponentOLD Instance { get; private set; }
|
||||
private string OldArmorSkin { get; set; } = string.Empty;
|
||||
|
||||
public ServerSwitcherComponentOLD() { Instance = this; }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public override void Patch(Harmony patcher)
|
||||
{
|
||||
TransportLayerConstructor = PatchUtils.GetConstructor(PatchUtils.MyTransportLayerType, new[] { typeof(int) });
|
||||
SyncLayerConstructor = PatchUtils.GetConstructor(PatchUtils.SyncLayerType, new[] { PatchUtils.MyTransportLayerType });
|
||||
ClientConstructor = PatchUtils.GetConstructor(PatchUtils.ClientType, new[] { typeof(MyGameServerItem), PatchUtils.SyncLayerType });
|
||||
MySessionLayer = PatchUtils.GetProperty(typeof(MySession), "SyncLayer");
|
||||
|
||||
var onJoin = PatchUtils.GetMethod(PatchUtils.ClientType, "OnUserJoined");
|
||||
UnloadProceduralWorldGenerator = PatchUtils.GetMethod(typeof(MyProceduralWorldGenerator), "UnloadData");
|
||||
GpsRegisterChat = PatchUtils.GetMethod(typeof(MyGpsCollection), "RegisterChat");
|
||||
AdminSettings = PatchUtils.GetField(typeof(MySession), "m_adminSettings");
|
||||
RemoteAdminSettings = PatchUtils.GetField(typeof(MySession), "m_remoteAdminSettings");
|
||||
LoadMembersFromWorld = PatchUtils.GetMethod(typeof(MySession), "LoadMembersFromWorld");
|
||||
InitVirtualClients = PatchUtils.GetMethod(PatchUtils.VirtualClientsType, "Init");
|
||||
VirtualClients = PatchUtils.GetField(typeof(MySession), "VirtualClients");
|
||||
|
||||
patcher.Patch(onJoin, postfix: new HarmonyMethod(Get(typeof(ServerSwitcherComponentOLD), nameof(OnUserJoined))));
|
||||
base.Patch(patcher);
|
||||
}
|
||||
|
||||
private static void OnUserJoined(ref JoinResultMsg msg)
|
||||
{
|
||||
if (msg.JoinResult == JoinResult.OK && isSeamlessSwitching)
|
||||
{
|
||||
//SeamlessClient.TryShow("User Joined! Result: " + msg.JoinResult.ToString());
|
||||
|
||||
//Invoke the switch event
|
||||
ForceClientConnection();
|
||||
isSeamlessSwitching = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void StartBackendSwitch(MyGameServerItem _TargetServer, MyObjectBuilder_World _TargetWorld)
|
||||
{
|
||||
isSeamlessSwitching = true;
|
||||
OldArmorSkin = MySession.Static.LocalHumanPlayer.BuildArmorSkin;
|
||||
TargetServer = _TargetServer;
|
||||
TargetWorld = _TargetWorld;
|
||||
|
||||
MySandboxGame.Static.Invoke(delegate
|
||||
{
|
||||
//Set camera controller to fixed spectator
|
||||
MySession.Static.SetCameraController(MyCameraControllerEnum.SpectatorFixed);
|
||||
UnloadCurrentServer();
|
||||
SetNewMultiplayerClient();
|
||||
//SeamlessClient.IsSwitching = false;
|
||||
|
||||
|
||||
}, "SeamlessClient");
|
||||
|
||||
}
|
||||
|
||||
private void SetNewMultiplayerClient()
|
||||
{
|
||||
// Following is called when the multiplayer is set successfully to target server
|
||||
MySandboxGame.Static.SessionCompatHelper.FixSessionComponentObjectBuilders(TargetWorld.Checkpoint, TargetWorld.Sector);
|
||||
|
||||
// Create constructors
|
||||
var LayerInstance = TransportLayerConstructor.Invoke(new object[] { 2 });
|
||||
var SyncInstance = SyncLayerConstructor.Invoke(new object[] { LayerInstance });
|
||||
var instance = ClientConstructor.Invoke(new object[] { TargetServer, SyncInstance });
|
||||
|
||||
|
||||
MyMultiplayer.Static = UtilExtensions.CastToReflected(instance, PatchUtils.ClientType);
|
||||
MyMultiplayer.Static.ExperimentalMode = true;
|
||||
|
||||
|
||||
|
||||
// Set the new SyncLayer to the MySession.Static.SyncLayer
|
||||
MySessionLayer.SetValue(MySession.Static, MyMultiplayer.Static.SyncLayer);
|
||||
|
||||
Seamless.TryShow("Successfully set MyMultiplayer.Static");
|
||||
|
||||
|
||||
Sync.Clients.SetLocalSteamId(Sync.MyId, false, MyGameService.UserName);
|
||||
Sync.Players.RegisterEvents();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static void ForceClientConnection()
|
||||
{
|
||||
|
||||
//Set World Settings
|
||||
SetWorldSettings();
|
||||
|
||||
//Load force load any connected players
|
||||
LoadConnectedClients();
|
||||
|
||||
|
||||
|
||||
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);
|
||||
|
||||
MyModAPIHelper.Initialize();
|
||||
MySession.Static.LoadDataComponents();
|
||||
|
||||
//MySession.Static.LoadObjectBuildersComponents(TargetWorld.Checkpoint.SessionComponents);
|
||||
MyModAPIHelper.Initialize();
|
||||
// MySession.Static.LoadObjectBuildersComponents(TargetWorld.Checkpoint.SessionComponents);
|
||||
|
||||
|
||||
//MethodInfo A = typeof(MySession).GetMethod("LoadGameDefinition", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
// A.Invoke(MySession.Static, new object[] { TargetWorld.Checkpoint });
|
||||
|
||||
|
||||
|
||||
MyMultiplayer.Static.OnSessionReady();
|
||||
|
||||
|
||||
UpdateWorldGenerator();
|
||||
|
||||
StartEntitySync();
|
||||
|
||||
|
||||
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
|
||||
if(MyGuiScreenHudSpace.Static != null)
|
||||
MyGuiScreenHudSpace.Static.RecreateControls(true);
|
||||
//MySession.Static.LocalHumanPlayer.BuildArmorSkin = OldArmorSkin;
|
||||
}
|
||||
|
||||
private static void LoadOnlinePlayers()
|
||||
{
|
||||
//Get This players ID
|
||||
|
||||
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);
|
||||
/*
|
||||
|
||||
|
||||
SeamlessClient.TryShow("Saving PlayerID: " + savingPlayerId.ToString());
|
||||
|
||||
|
||||
|
||||
foreach (KeyValuePair<MyObjectBuilder_Checkpoint.PlayerId, MyObjectBuilder_Player> item3 in TargetWorld.Checkpoint.AllPlayersData.Dictionary)
|
||||
{
|
||||
MyPlayer.PlayerId playerId5 = new MyPlayer.PlayerId(item3.Key.GetClientId(), item3.Key.SerialId);
|
||||
|
||||
SeamlessClient.TryShow($"ConnectedPlayer: {playerId5.ToString()}");
|
||||
if (savingPlayerId.HasValue && playerId5.SteamId == savingPlayerId.Value.SteamId)
|
||||
{
|
||||
playerId5 = new MyPlayer.PlayerId(Sync.MyId, playerId5.SerialId);
|
||||
}
|
||||
|
||||
Patches.LoadPlayerInternal.Invoke(MySession.Static.Players, new object[] { playerId5, item3.Value, false });
|
||||
ConcurrentDictionary<MyPlayer.PlayerId, MyPlayer> Players = (ConcurrentDictionary<MyPlayer.PlayerId, MyPlayer>)Patches.MPlayerGPSCollection.GetValue(MySession.Static.Players);
|
||||
//LoadPlayerInternal(ref playerId5, item3.Value);
|
||||
if (Players.TryGetValue(playerId5, out MyPlayer myPlayer))
|
||||
{
|
||||
List<Vector3> value2 = null;
|
||||
if (TargetWorld.Checkpoint.AllPlayersColors != null && TargetWorld.Checkpoint.AllPlayersColors.Dictionary.TryGetValue(item3.Key, out value2))
|
||||
{
|
||||
myPlayer.SetBuildColorSlots(value2);
|
||||
}
|
||||
else if (TargetWorld.Checkpoint.CharacterToolbar != null && TargetWorld.Checkpoint.CharacterToolbar.ColorMaskHSVList != null && TargetWorld.Checkpoint.CharacterToolbar.ColorMaskHSVList.Count > 0)
|
||||
{
|
||||
myPlayer.SetBuildColorSlots(TargetWorld.Checkpoint.CharacterToolbar.ColorMaskHSVList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
private static void SetWorldSettings()
|
||||
{
|
||||
//MyEntities.MemoryLimitAddFailureReset();
|
||||
|
||||
//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);
|
||||
|
||||
//m_adminSettings = 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static 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);
|
||||
|
||||
|
||||
//load online players
|
||||
LoadOnlinePlayers();
|
||||
|
||||
}
|
||||
|
||||
private static 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 += MyMultiplayer_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 static void MyMultiplayer_PendingReplicablesDone()
|
||||
{
|
||||
if (MySession.Static.VoxelMaps.Instances.Count > 0)
|
||||
{
|
||||
MySandboxGame.AreClipmapsReady = false;
|
||||
}
|
||||
MyMultiplayer.Static.PendingReplicablesDone -= MyMultiplayer_PendingReplicablesDone;
|
||||
}
|
||||
|
||||
|
||||
private static 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 UnloadCurrentServer()
|
||||
{
|
||||
//Unload current session on game thread
|
||||
if (MyMultiplayer.Static == null)
|
||||
throw new Exception("MyMultiplayer.Static is null on unloading? dafuq?");
|
||||
|
||||
|
||||
RemoveOldEntities();
|
||||
|
||||
//Try and close the quest log
|
||||
MySessionComponentIngameHelp component = MySession.Static.GetComponent<MySessionComponentIngameHelp>();
|
||||
component?.TryCancelObjective();
|
||||
|
||||
//Clear all old players and clients.
|
||||
Sync.Clients.Clear();
|
||||
Sync.Players.ClearPlayers();
|
||||
|
||||
|
||||
MyHud.Chat.UnregisterChat(MyMultiplayer.Static);
|
||||
|
||||
|
||||
|
||||
|
||||
MySession.Static.Gpss.RemovePlayerGpss(MySession.Static.LocalPlayerId);
|
||||
MyHud.GpsMarkers.Clear();
|
||||
MyMultiplayer.Static.ReplicationLayer.Disconnect();
|
||||
MyMultiplayer.Static.ReplicationLayer.Dispose();
|
||||
MyMultiplayer.Static.Dispose();
|
||||
MyMultiplayer.Static = null;
|
||||
|
||||
//Close any respawn screens that are open
|
||||
MyGuiScreenMedicals.Close();
|
||||
|
||||
//MySession.Static.UnloadDataComponents();
|
||||
|
||||
}
|
||||
|
||||
private void RemoveOldEntities()
|
||||
{
|
||||
foreach (var ent in MyEntities.GetEntities())
|
||||
{
|
||||
if (ent is MyPlanet)
|
||||
continue;
|
||||
|
||||
ent.Close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
@@ -10,26 +10,11 @@ namespace SeamlessClient.Messages
|
||||
[ProtoContract]
|
||||
public class OnlinePlayerData
|
||||
{
|
||||
[ProtoMember(10)] public List<OnlineClientServer> OnlineServers = new List<OnlineClientServer>();
|
||||
[ProtoMember(10)] public List<OnlinePlayer> OnlinePlayers = new List<OnlinePlayer>();
|
||||
|
||||
[ProtoMember(12)] public int currentServerID;
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class OnlineClientServer
|
||||
{
|
||||
[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 OnlineClientServer()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class OnlinePlayer
|
||||
@@ -42,9 +27,6 @@ namespace SeamlessClient.Messages
|
||||
|
||||
[ProtoMember(4)] public int OnServer;
|
||||
|
||||
|
||||
public string ServerName;
|
||||
|
||||
public OnlinePlayer(string PlayerName, ulong SteamID, long IdentityID, int OnServer)
|
||||
{
|
||||
this.PlayerName = PlayerName;
|
||||
|
@@ -385,7 +385,8 @@ namespace SeamlessClient.OnlinePlayersWindow
|
||||
Controls.Add(m_playersTable);
|
||||
|
||||
|
||||
string servername = PlayersWindowComponent.onlineServer?.ServerName ?? "thisServer";
|
||||
//string servername = PlayersWindowComponent.onlineServer?.ServerName ?? "thisServer";
|
||||
string servername = "thisserver";
|
||||
|
||||
foreach (MyPlayer onlinePlayer in Sync.Players.GetOnlinePlayers())
|
||||
{
|
||||
@@ -408,13 +409,14 @@ namespace SeamlessClient.OnlinePlayersWindow
|
||||
AddPlayer(onlinePlayer.Id.SteamId, servername);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
foreach(var server in PlayersWindowComponent.allServers)
|
||||
{
|
||||
|
||||
foreach(var player in server.Players)
|
||||
AddPlayerFromOtherServer(player, server.ServerName);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
m_lobbyTypeCombo.ItemSelected += lobbyTypeCombo_OnSelect;
|
||||
@@ -796,7 +798,7 @@ namespace SeamlessClient.OnlinePlayersWindow
|
||||
|
||||
protected void Multiplayer_PlayerJoined(ulong userId, string userName)
|
||||
{
|
||||
AddPlayer(userId, PlayersWindowComponent.onlineServer.ServerName);
|
||||
//AddPlayer(userId, PlayersWindowComponent.onlineServer.ServerName);
|
||||
}
|
||||
|
||||
protected void Multiplayer_PlayerLeft(ulong userId, MyChatMemberStateChangeEnum arg2)
|
||||
|
13
Seamless.cs
13
Seamless.cs
@@ -1,13 +1,13 @@
|
||||
using HarmonyLib;
|
||||
using NLog.Fluent;
|
||||
using Sandbox;
|
||||
using Sandbox.Game.Entities;
|
||||
using Sandbox.Game.Localization;
|
||||
using Sandbox.Game.World;
|
||||
using Sandbox.ModAPI;
|
||||
using SeamlessClient.Components;
|
||||
using SeamlessClient.Messages;
|
||||
using SeamlessClient.OnlinePlayersWindow;
|
||||
using SeamlessClient.ServerSwitching;
|
||||
using SeamlessClient.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -33,6 +33,7 @@ namespace SeamlessClient
|
||||
public static ushort SeamlessClientNetId = 2936;
|
||||
|
||||
private List<ComponentBase> allComps = new List<ComponentBase>();
|
||||
|
||||
private Assembly thisAssembly => typeof(Seamless).Assembly;
|
||||
private bool Initilized = false;
|
||||
public static bool isSeamlessServer { get; private set; } = false;
|
||||
@@ -141,7 +142,7 @@ namespace SeamlessClient
|
||||
case ClientMessageType.OnlinePlayers:
|
||||
//Not implemented yet
|
||||
var playerData = msg.GetOnlinePlayers();
|
||||
PlayersWindowComponent.ApplyRecievedPlayers(playerData.OnlineServers, playerData.currentServerID);
|
||||
//PlayersWindowComponent.ApplyRecievedPlayers(playerData.OnlineServers, playerData.currentServerID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -204,11 +205,11 @@ namespace SeamlessClient
|
||||
Seamless.TryShow($"Beginning Redirect to server: {targetServer.TargetServerId}");
|
||||
var world = targetServer.WorldRequest.DeserializeWorldData();
|
||||
|
||||
|
||||
SeamlessSwitcher.Instance.StartSwitch(server, world);
|
||||
//Temp fix till im not lazy enough to fix new version
|
||||
if (UseNewVersion)
|
||||
ServerSwitcherComponent.Instance.StartBackendSwitch(server, world);
|
||||
else
|
||||
ServerSwitcherComponentOLD.Instance.StartBackendSwitch(server, world);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@@ -105,9 +105,11 @@
|
||||
<Compile Include="Components\LoadingScreenComponent.cs" />
|
||||
<Compile Include="Components\MyGUIScreenMedicalsPatch.cs" />
|
||||
<Compile Include="Components\MyHudTimeTracker.cs" />
|
||||
<Compile Include="Components\ServerSwitcherComponentOLD.cs" />
|
||||
<Compile Include="Components\SeamlessScriptManager.cs" />
|
||||
<Compile Include="Components\SeamlessSwitcher.cs" />
|
||||
<Compile Include="GUI\Screens\GUILoadingScreen.cs" />
|
||||
<Compile Include="Messages\OnlinePlayerData.cs" />
|
||||
<Compile Include="Utilities\GameEvents.cs" />
|
||||
<Compile Include="Utilities\UtilExtensions.cs" />
|
||||
<Compile Include="Utilities\PatchUtils.cs" />
|
||||
<Compile Include="Messages\ClientMessage.cs" />
|
||||
@@ -117,7 +119,6 @@
|
||||
<Compile Include="Components\PlayersWindowComponent.cs" />
|
||||
<Compile Include="Seamless.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Components\ServerSwitcherComponent.cs" />
|
||||
<Compile Include="Utilities\AsyncInvoke.cs" />
|
||||
<Compile Include="Utilities\MessageUtils.cs" />
|
||||
<Compile Include="Utilities\ComponentBase.cs" />
|
||||
|
34
Utilities/GameEvents.cs
Normal file
34
Utilities/GameEvents.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using Sandbox.Engine.Multiplayer;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VRage.Network;
|
||||
using VRageMath;
|
||||
|
||||
namespace SeamlessClient.Utilities
|
||||
{
|
||||
public class GameEvents
|
||||
{
|
||||
private static Dictionary<MethodInfo, Delegate> _delegateCache = new Dictionary<MethodInfo, Delegate>();
|
||||
|
||||
private static Func<T, TA> GetDelegate<T, TA>(MethodInfo method) where TA : class
|
||||
{
|
||||
if (!_delegateCache.TryGetValue(method, out var del))
|
||||
{
|
||||
del = (Func<T, TA>)(x => Delegate.CreateDelegate(typeof(TA), x, method) as TA);
|
||||
_delegateCache[method] = del;
|
||||
}
|
||||
|
||||
return (Func<T, TA>)del;
|
||||
}
|
||||
|
||||
public static void RaiseStaticEvent<T1>(MethodInfo method, T1 arg1, EndpointId target = default, Vector3D? position = null)
|
||||
{
|
||||
var del = GetDelegate<IMyEventOwner, Action<T1>>(method);
|
||||
MyMultiplayer.RaiseStaticEvent(del, arg1, target, position);
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user