Files
SeamlessClient/SeamlessTransfer/SwitchServers.cs
zznty aa323f1ff6
Some checks failed
Build / Compute Version (push) Successful in 6s
Build / Build Nuget package (push) Failing after 6s
fixes for nexus v3
2025-07-28 03:58:25 +07:00

391 lines
15 KiB
C#

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 SeamlessClientPlugin.Utilities;
using SpaceEngineers.Game.GUI;
using VRage.Game;
using VRage.Game.ModAPI;
using VRage.GameServices;
using VRage.Network;
using VRage.Utils;
using VRageRender;
using VRageRender.Messages;
using Game = Sandbox.Engine.Platform.Game;
namespace SeamlessClientPlugin.SeamlessTransfer;
public class SwitchServers
{
public SwitchServers(MyGameServerItem targetServer, MyObjectBuilder_World targetWorld)
{
this.TargetServer = targetServer;
this.TargetWorld = targetWorld;
//ModLoader.DownloadNewMods(TargetWorld.Checkpoint.Mods);
}
public MyGameServerItem TargetServer { get; }
public MyObjectBuilder_World TargetWorld { get; }
private string OldArmorSkin { get; set; } = string.Empty;
public void BeginSwitch()
{
OldArmorSkin = MySession.Static.LocalHumanPlayer.BuildArmorSkin;
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
Patches.OnJoinEvent += OnJoinEvent;
MySandboxGame.Static.SessionCompatHelper.FixSessionComponentObjectBuilders(TargetWorld.Checkpoint,
TargetWorld.Sector);
// Create constructors
var layerInstance = new MyTransportLayer(MyMultiplayer.GAME_EVENT_CHANNEL);
var syncInstance = new MySyncLayer(layerInstance);
var instance = new MyMultiplayerClient(TargetServer, syncInstance);
MyMultiplayer.Static = instance;
MyMultiplayer.Static.ExperimentalMode = true;
// Set the new SyncLayer to the MySession.Static.SyncLayer
MySession.Static.SyncLayer = MyMultiplayer.Static.SyncLayer;
SeamlessClient.TryShow("Successfully set MyMultiplayer.Static");
Sync.Clients.SetLocalSteamId(Sync.MyId, false, MyGameService.UserName);
Sync.Players.RegisterEvents();
}
private void OnJoinEvent(object sender, JoinResultMsg e)
{
ForceClientConnection();
// Un-register the event
Patches.OnJoinEvent -= OnJoinEvent;
}
private void ForceClientConnection()
{
//Set World Settings
SetWorldSettings();
//Load force load any connected players
LoadConnectedClients();
MySector.InitEnvironmentSettings(TargetWorld.Sector.Environment);
var text = !string.IsNullOrEmpty(TargetWorld.Checkpoint.CustomSkybox)
? TargetWorld.Checkpoint.CustomSkybox
: MySector.EnvironmentDefinition.EnvironmentTexture;
MyRenderProxy.PreloadTextures([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);
MySession.Static.Gpss.RegisterChat(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);
//MySession.Static.LocalHumanPlayer.BuildArmorSkin = OldArmorSkin;
}
private void LoadOnlinePlayers()
{
//Get This players ID
MyPlayer.PlayerId? savingPlayerId = new MyPlayer.PlayerId(Sync.MyId);
if (!savingPlayerId.HasValue)
{
SeamlessClient.TryShow("SavingPlayerID is null! Creating Default!");
savingPlayerId = new MyPlayer.PlayerId(Sync.MyId);
}
SeamlessClient.TryShow($"Saving PlayerID: {savingPlayerId}");
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 void SetWorldSettings()
{
//MyEntities.MemoryLimitAddFailureReset();
//Clear old list
MySession.Static.PromotedUsers.Clear();
MySession.Static.CreativeTools.Clear();
MySession.Static.RemoteAdminSettings.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), false,
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)
{
SeamlessClient.TryShow(
$"An error occured while loading GPS points! You will have an empty gps list! \n {ex}");
}
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 (var item in TargetWorld.Checkpoint.AllPlayersData.Dictionary)
{
var clientId = item.Key.GetClientId();
var 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;
}
MySession.Static.RemoteAdminSettings[clientId] = adminSettingsEnum;
if (!Sync.IsDedicated && clientId == Sync.MyId)
MySession.Static.AdminSettings = adminSettingsEnum;
var value2 = MySession.Static.PromotedUsers.GetValueOrDefault(clientId, 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 void LoadConnectedClients()
{
MySession.Static.LoadMembersFromWorld(TargetWorld, MyMultiplayer.Static);
//Re-Initilize Virtual clients
MySession.Static.VirtualClients.Init();
//load online players
LoadOnlinePlayers();
}
private void StartEntitySync()
{
SeamlessClient.TryShow("Requesting Player From Server");
Sync.Players.RequestNewPlayer(Sync.MyId, 0, MyGameService.UserName, null, true, true);
if (MySession.Static.ControlledEntity == null && Sync.IsServer && !Game.IsDedicated)
{
MyLog.Default.WriteLine("ControlledObject was null, respawning character");
//m_cameraAwaitingEntity = true;
MyPlayerCollection.RequestLocalRespawn();
}
//Request client state batch
((MyMultiplayerClientBase)MyMultiplayer.Static).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();
SeamlessClient.TryShow($"OnlinePlayers: {MySession.Static.Players.GetOnlinePlayers().Count}");
SeamlessClient.TryShow("Loading Complete!");
}
private void MyMultiplayer_PendingReplicablesDone()
{
if (MySession.Static.VoxelMaps.Instances.Count > 0) MySandboxGame.AreClipmapsReady = false;
MyMultiplayer.Static.PendingReplicablesDone -= MyMultiplayer_PendingReplicablesDone;
}
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
Patches.UnloadProceduralWorldGenerator.Invoke(generator, null);
//Re-call the generator init
var generatorSettings = TargetWorld.Checkpoint.SessionComponents.OfType<MyObjectBuilder_WorldGenerator>().FirstOrDefault();
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.
foreach (var planet in MyEntities.GetEntities().OfType<MyPlanet>())
{
generator.MarkEmptyArea(planet.PositionComp.GetPosition(), planet.m_planetInitValues.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
var 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();
}
}
}