fixes for nexus v3
Some checks failed
Build / Compute Version (push) Successful in 6s
Build / Build Nuget package (push) Failing after 6s

This commit is contained in:
zznty
2025-07-28 03:58:25 +07:00
parent 32ae6569a8
commit aa323f1ff6
17 changed files with 1766 additions and 2392 deletions

View File

@@ -1,218 +1,203 @@
using Sandbox.Engine.Multiplayer;
using Sandbox.Engine.Networking;
using Sandbox.Game;
using Sandbox.Game.Entities;
using Sandbox.Game.Gui;
using Sandbox.Game.Multiplayer;
using System.Runtime.CompilerServices;
using CringePlugins.Ui;
using Sandbox.Game.World;
using Sandbox.Graphics.GUI;
using Sandbox.ModAPI;
using SeamlessClientPlugin.Messages;
using SeamlessClientPlugin.SeamlessTransfer;
using SeamlessClientPlugin.Utilities;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Windows.Forms;
using VRage.Game.ModAPI;
using VRage.Input;
using VRage.Plugins;
using VRage.Utils;
using VRageMath;
using VRageRender;
[assembly: IgnoresAccessChecksTo("Sandbox.Game")]
[assembly: IgnoresAccessChecksTo("Sandbox.Graphics")]
namespace SeamlessClientPlugin;
namespace SeamlessClientPlugin
//SendAllMembersDataToClient
public class SeamlessClient : IPlugin
{
public const ushort SeamlessClientNetId = 2936;
/* First Step. How does a player join a game?
* First JoinGameInternal is called with the ServersLobbyID.
* In this method, MySessionLoader.UnloadAndExitToMenu() is called. Ultimatley we want to prevent this as we dont want to unload the entire game. Just the basic neccessities.
* Then JoinLobby is called. This looks to be simply a check to see if the client gets a result from the server. If it does, it initilizes a new STATIC/Multiplayer base by: [Static = new MyMultiplayerLobbyClient(lobby, new MySyncLayer(new MyTransportLayer(2)))));]
* Once this above method is done and join is done, we begin the OnJoin()
*
* On join begins by checking join result. Success downloads world and failed sends you back to menu. (May need to use this to send players to menu)
* Download world Requires the multiplayerbase and MyGUIScreenProgress. Which is essentially checking that the user hasnt cliecked left or closed.
*
* StringBuilder text = MyTexts.Get(MyCommonTexts.DialogTextJoiningWorld);
* MyGuiScreenProgress progress = new MyGuiScreenProgress(text, MyCommonTexts.Cancel);
*
* This just looks to be like what happens still with the little GUI popupscreen before load starts
*
* DownloadWorld also contains a method to get the serversessionstate too. We will need to check this before load. Ultimatley once everything has passed join checks Downloadworld is called. (MyMultiplayerClientBase.DownloadWorld)
*
*
* MyMultiplayerClientBase.DownloadWorld simply rasies a static event to the server for world request. [return MyMultiplayerServerBase.WorldRequest;]
*
*
*
*
* MyMultiplayerServerBase.WorldRequest (WorldRequest) This is near the start of where the magic happens. THIS IS ALL RAN SERVERSIDE
* Checks to see if the client has been kicked or banned to prevent world requests. Not sure we really need to worry about this.
*
* Server gets world clears non important data such as player gps. Our player gps gets added on join so we can yeet this.
* Also theres a sendfluch via transport layer. Might need to keep this in mind and use this for our testings
* Theres a CleanUpData that gets called with world and playerid/identity ID. This is probably to limit things and only send whats neccessary
* CLEANUPDATA shows me how to send allplayers data synced on the server.
*
*
* Once we have everythings we use a MS to serialize everything to byte[] and via replication layer we send world to client.
*
*
* BACK TO CLIENT:
* RecieveWorld is called and packet is deserialized and then MyJoinGameHelper.WorldReceived(...,...) is called
*
* WorldReceived does some checks for extra version mismatches and to make sure the CheckPointObjectBuilder isnt null etc.
* Once it passes all these checks, CheckDx11AndJoin(world, MyMutliplayerBase) is called
*
* CheckDx11AndJoin just checks if its a scenario world or not. Forget about this. We can figure that out all later. Then it runs: MySessionLoader.LoadMultiplayerSession(world, multiplayer);
*
*
* MySessionLoader.LoadMultiplayerSession looks to be the start of the join code. It also checks for mod mismatches. (And Downloads them). However once it passes this, MySession.LoadMultiplayer is called.
*
*
*
*
*
*
*
* MySession.LoadMultiplayer (The most important step in world loading)
* Creates new MySession.
* Does settings stuff.
* LoadMembersFromWorld (Loads Clients)
* FixSessionComponentObjectBuilders
*
*
*
* PrepareBaseSession is something we need. Looks like it does some weird stuff to init fonts, Sector Enviroment settings, Loading datacomponents from world, and re-initilizes modAPI stuff
* DeserializeClusters
* Loads planets.
* RegistersChat
*
* LOADWORLD -----------
* Static.BeforeStartComponents
*
*
*
*
* -plugin "../Plugins/SeamlessClientPlugin.dll"
*/
//SendAllMembersDataToClient
public class SeamlessClient : IPlugin
// pretend to be the latest one
public static string Version = "3.0.0.10";
public static bool Debug = true;
private static bool _initilized;
public static bool IsSwitching = false;
public static bool RanJoin = false;
public void Init(object gameInstance)
{
/* First Step. How does a player join a game?
* First JoinGameInternal is called with the ServersLobbyID.
* In this method, MySessionLoader.UnloadAndExitToMenu() is called. Ultimatley we want to prevent this as we dont want to unload the entire game. Just the basic neccessities.
* Then JoinLobby is called. This looks to be simply a check to see if the client gets a result from the server. If it does, it initilizes a new STATIC/Multiplayer base by: [Static = new MyMultiplayerLobbyClient(lobby, new MySyncLayer(new MyTransportLayer(2)))));]
* Once this above method is done and join is done, we begin the OnJoin()
*
* On join begins by checking join result. Success downloads world and failed sends you back to menu. (May need to use this to send players to menu)
* Download world Requires the multiplayerbase and MyGUIScreenProgress. Which is essentially checking that the user hasnt cliecked left or closed.
*
* StringBuilder text = MyTexts.Get(MyCommonTexts.DialogTextJoiningWorld);
* MyGuiScreenProgress progress = new MyGuiScreenProgress(text, MyCommonTexts.Cancel);
*
* This just looks to be like what happens still with the little GUI popupscreen before load starts
*
* DownloadWorld also contains a method to get the serversessionstate too. We will need to check this before load. Ultimatley once everything has passed join checks Downloadworld is called. (MyMultiplayerClientBase.DownloadWorld)
*
*
* MyMultiplayerClientBase.DownloadWorld simply rasies a static event to the server for world request. [return MyMultiplayerServerBase.WorldRequest;]
*
*
*
*
* MyMultiplayerServerBase.WorldRequest (WorldRequest) This is near the start of where the magic happens. THIS IS ALL RAN SERVERSIDE
* Checks to see if the client has been kicked or banned to prevent world requests. Not sure we really need to worry about this.
*
* Server gets world clears non important data such as player gps. Our player gps gets added on join so we can yeet this.
* Also theres a sendfluch via transport layer. Might need to keep this in mind and use this for our testings
* Theres a CleanUpData that gets called with world and playerid/identity ID. This is probably to limit things and only send whats neccessary
* CLEANUPDATA shows me how to send allplayers data synced on the server.
*
*
* Once we have everythings we use a MS to serialize everything to byte[] and via replication layer we send world to client.
*
*
* BACK TO CLIENT:
* RecieveWorld is called and packet is deserialized and then MyJoinGameHelper.WorldReceived(...,...) is called
*
* WorldReceived does some checks for extra version mismatches and to make sure the CheckPointObjectBuilder isnt null etc.
* Once it passes all these checks, CheckDx11AndJoin(world, MyMutliplayerBase) is called
*
* CheckDx11AndJoin just checks if its a scenario world or not. Forget about this. We can figure that out all later. Then it runs: MySessionLoader.LoadMultiplayerSession(world, multiplayer);
*
*
* MySessionLoader.LoadMultiplayerSession looks to be the start of the join code. It also checks for mod mismatches. (And Downloads them). However once it passes this, MySession.LoadMultiplayer is called.
*
*
*
*
*
*
*
* MySession.LoadMultiplayer (The most important step in world loading)
* Creates new MySession.
* Does settings stuff.
* LoadMembersFromWorld (Loads Clients)
* FixSessionComponentObjectBuilders
*
*
*
* PrepareBaseSession is something we need. Looks like it does some weird stuff to init fonts, Sector Enviroment settings, Loading datacomponents from world, and re-initilizes modAPI stuff
* DeserializeClusters
* Loads planets.
* RegistersChat
*
* LOADWORLD -----------
* Static.BeforeStartComponents
*
*
*
*
* -plugin "../Plugins/SeamlessClientPlugin.dll"
*/
TryShow($"Running Seamless Client Plugin v[{Version}]");
MySession.LoadingStep += LoadingProgressStep;
}
private static void LoadingProgressStep(LoadingProgress step)
{
// i copied it from the new one no guarantee it works properly
if (step >= LoadingProgress.PROGRESS_STEP8)
SendFirstJoin();
}
public static string Version = "1.4.01";
public static bool Debug = true;
private static bool Initilized = false;
public void Update()
{
if (MyAPIGateway.Multiplayer == null)
return;
public const ushort SeamlessClientNetID = 2936;
public static bool IsSwitching = false;
public static bool RanJoin = false;
public void Init(object gameInstance)
if (!_initilized)
{
TryShow("Running Seamless Client Plugin v[" + Version + "]");
}
public void Update()
{
if (MyAPIGateway.Multiplayer == null)
return;
if (!Initilized)
{
Patches.GetPatches();
OnlinePlayers.Patch();
TryShow("Initilizing Communications!");
RunInitilizations();
}
}
public static void RunInitilizations()
{
MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(SeamlessClientNetID, MessageHandler);
Initilized = true;
}
public static void DisposeInitilizations()
{
MyAPIGateway.Multiplayer?.UnregisterSecureMessageHandler(SeamlessClientNetID, MessageHandler);
Initilized = false;
}
private static void MessageHandler(ushort obj1, byte[] obj2, ulong obj3, bool obj4)
{
try
{
ClientMessage Recieved = Utilities.Utility.Deserialize<ClientMessage>(obj2);
if(Recieved.MessageType == ClientMessageType.FirstJoin)
{
//Server sent a first join message! Send a reply back so the server knows what version we are on
ClientMessage PingServer = new ClientMessage(ClientMessageType.FirstJoin);
MyAPIGateway.Multiplayer?.SendMessageToServer(SeamlessClientNetID, Utilities.Utility.Serialize(PingServer));
}
else if (Recieved.MessageType == ClientMessageType.TransferServer)
{
//Server sent a transfer message! Begin transfer via seamless
Transfer TransferMessage = Recieved.GetTransferData();
ServerPing.StartServerPing(TransferMessage);
}else if(Recieved.MessageType == ClientMessageType.OnlinePlayers)
{
var p = Recieved.GetOnlinePlayers();
OnlinePlayers.AllServers = p.OnlineServers;
OnlinePlayers.currentServer = p.currentServerID;
//TryShow("Recieved Players! "+OnlinePlayers.AllServers.Count);
}
}
catch (Exception ex)
{
TryShow(ex.ToString());
}
}
public static void TryShow(string message)
{
if (MySession.Static?.LocalHumanPlayer != null && Debug)
MyAPIGateway.Utilities?.ShowMessage("NetworkClient", message);
MyLog.Default?.WriteLineAndConsole($"SeamlessClient: {message}");
}
public void Dispose()
{
DisposeInitilizations();
Patches.GetPatches();
OnlinePlayers.Patch();
TryShow("Initilizing Communications!");
RunInitilizations();
}
}
}
public void Dispose()
{
DisposeInitilizations();
}
public static void RunInitilizations()
{
MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(SeamlessClientNetId, MessageHandler);
_initilized = true;
}
public static void DisposeInitilizations()
{
MyAPIGateway.Multiplayer?.UnregisterSecureMessageHandler(SeamlessClientNetId, MessageHandler);
_initilized = false;
}
private static void MessageHandler(ushort obj1, byte[] obj2, ulong obj3, bool obj4)
{
try
{
var recieved = Utility.Deserialize<ClientMessage>(obj2);
switch (recieved.MessageType)
{
// was removed in newer versions, but we leave it here for backwards compatability
case ClientMessageType.FirstJoin:
{
SendFirstJoin();
break;
}
case ClientMessageType.TransferServer:
{
//Server sent a transfer message! Begin transfer via seamless
var transferMessage = recieved.GetTransferData();
ServerPing.StartServerPing(transferMessage);
break;
}
case ClientMessageType.OnlinePlayers:
{
var p = recieved.GetOnlinePlayers();
OnlinePlayers.AllServers = p.OnlineServers;
OnlinePlayers.CurrentServer = p.CurrentServerId;
//TryShow("Recieved Players! "+OnlinePlayers.AllServers.Count);
break;
}
}
}
catch (Exception ex)
{
TryShow(ex.ToString());
}
}
private static void SendFirstJoin()
{
var pingServer = new ClientMessage(ClientMessageType.FirstJoin);
MyAPIGateway.Multiplayer?.SendMessageToServer(SeamlessClientNetId, Utility.Serialize(pingServer));
}
public static void TryShow(string message)
{
NotificationsComponent.SpawnNotification(5f, message);
MyLog.Default?.WriteLineAndConsole($"SeamlessClient: {message}");
}
}