fixes for nexus v3
This commit is contained in:
44
.github/workflows/build.yml
vendored
Normal file
44
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
compute-version:
|
||||
name: Compute Version
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
fetch-depth: 0
|
||||
|
||||
- id: version
|
||||
uses: paulhatch/semantic-version@v5.3.0
|
||||
with:
|
||||
tag_prefix: ''
|
||||
major_pattern: 'Add project files'
|
||||
minor_pattern: 'feature:'
|
||||
bump_each_commit: true
|
||||
enable_prerelease_mode: false
|
||||
|
||||
build-nuget:
|
||||
name: Build Nuget package
|
||||
runs-on: ubuntu-latest
|
||||
needs: [compute-version]
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
|
||||
- uses: actions/setup-dotnet@v4
|
||||
|
||||
- run: dotnet restore ./SeamlessClient.csproj --locked-mode
|
||||
name: Restore Project
|
||||
|
||||
- run: dotnet pack -c Release -o ./pub ./SeamlessClient.csproj --no-restore -p:Version="${{ needs.compute-version.outputs.version }}"
|
||||
name: Pack Project
|
||||
|
||||
- name: Push Project
|
||||
run: dotnet nuget push -s https://ng.zznty.ru/v3/index.json -k ${{ secrets.NUGET_API_KEY }} ./pub/*.nupkg
|
@@ -1,77 +1,67 @@
|
||||
using ProtoBuf;
|
||||
using Sandbox.Engine.Networking;
|
||||
using Sandbox.Game.World;
|
||||
using Sandbox.ModAPI;
|
||||
using SeamlessClientPlugin.SeamlessTransfer;
|
||||
using SeamlessClientPlugin.Utilities;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SeamlessClientPlugin.Messages
|
||||
namespace SeamlessClientPlugin.Messages;
|
||||
|
||||
public enum ClientMessageType
|
||||
{
|
||||
public enum ClientMessageType
|
||||
FirstJoin,
|
||||
TransferServer,
|
||||
OnlinePlayers
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class ClientMessage
|
||||
{
|
||||
[ProtoMember(1)] public ClientMessageType MessageType;
|
||||
|
||||
[ProtoMember(2)] public byte[] MessageData;
|
||||
|
||||
[ProtoMember(3)] public long IdentityId;
|
||||
|
||||
[ProtoMember(4)] public ulong SteamId;
|
||||
|
||||
[ProtoMember(5)] public string PluginVersion = "0";
|
||||
|
||||
[ProtoMember(6)] public string NexusVersion;
|
||||
|
||||
public ClientMessage(ClientMessageType type)
|
||||
{
|
||||
FirstJoin,
|
||||
TransferServer,
|
||||
OnlinePlayers,
|
||||
MessageType = type;
|
||||
|
||||
IdentityId = MySession.Static?.LocalHumanPlayer?.Identity?.IdentityId ?? 0;
|
||||
SteamId = MyGameService.UserId;
|
||||
PluginVersion = SeamlessClient.Version;
|
||||
}
|
||||
|
||||
public ClientMessage()
|
||||
{
|
||||
}
|
||||
|
||||
public void SerializeData<T>(T data)
|
||||
{
|
||||
MessageData = Utility.Serialize(data);
|
||||
}
|
||||
|
||||
|
||||
[ProtoContract]
|
||||
public class ClientMessage
|
||||
public Transfer GetTransferData()
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public ClientMessageType MessageType;
|
||||
[ProtoMember(2)]
|
||||
public byte[] MessageData;
|
||||
[ProtoMember(3)]
|
||||
public long IdentityID;
|
||||
[ProtoMember(4)]
|
||||
public ulong SteamID;
|
||||
[ProtoMember(5)]
|
||||
public string PluginVersion = "0";
|
||||
if (MessageData == null)
|
||||
return null;
|
||||
|
||||
public ClientMessage(ClientMessageType Type)
|
||||
{
|
||||
MessageType = Type;
|
||||
return Utility.Deserialize<Transfer>(MessageData);
|
||||
}
|
||||
|
||||
if (MyAPIGateway.Multiplayer != null && !MyAPIGateway.Multiplayer.IsServer)
|
||||
{
|
||||
if (MyAPIGateway.Session.LocalHumanPlayer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IdentityID = MySession.Static?.LocalHumanPlayer?.Identity?.IdentityId ?? 0;
|
||||
SteamID = MySession.Static?.LocalHumanPlayer?.Id.SteamId ?? 0;
|
||||
PluginVersion = SeamlessClient.Version;
|
||||
}
|
||||
}
|
||||
|
||||
public ClientMessage() { }
|
||||
|
||||
public void SerializeData<T>(T Data)
|
||||
{
|
||||
MessageData = Utility.Serialize(Data);
|
||||
}
|
||||
public OnlinePlayersMessage GetOnlinePlayers()
|
||||
{
|
||||
if (MessageData == null)
|
||||
return null;
|
||||
|
||||
|
||||
public Transfer GetTransferData()
|
||||
{
|
||||
if (MessageData == null)
|
||||
return default(Transfer);
|
||||
|
||||
return Utility.Deserialize<Transfer>(MessageData);
|
||||
|
||||
}
|
||||
|
||||
public OnlinePlayersMessage GetOnlinePlayers()
|
||||
{
|
||||
if (MessageData == null)
|
||||
return default(OnlinePlayersMessage);
|
||||
|
||||
|
||||
OnlinePlayersMessage msg = Utility.Deserialize<OnlinePlayersMessage>(MessageData);
|
||||
return msg;
|
||||
}
|
||||
|
||||
var msg = Utility.Deserialize<OnlinePlayersMessage>(MessageData);
|
||||
return msg;
|
||||
}
|
||||
}
|
@@ -1,73 +1,48 @@
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SeamlessClientPlugin.Messages
|
||||
namespace SeamlessClientPlugin.Messages;
|
||||
|
||||
[ProtoContract]
|
||||
public class OnlinePlayersMessage
|
||||
{
|
||||
[ProtoMember(12)] public int CurrentServerId;
|
||||
|
||||
[ProtoContract]
|
||||
public class OnlinePlayersMessage
|
||||
{
|
||||
[ProtoMember(10)]
|
||||
public List<OnlineServer> OnlineServers = new List<OnlineServer>();
|
||||
|
||||
[ProtoMember(12)]
|
||||
public int currentServerID;
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class OnlineServer
|
||||
{
|
||||
|
||||
[ProtoMember(2)]
|
||||
public List<OnlinePlayer> Players = new List<OnlinePlayer>();
|
||||
|
||||
[ProtoMember(3)]
|
||||
public bool ServerRunning = false;
|
||||
|
||||
[ProtoMember(10)]
|
||||
public int ServerID;
|
||||
|
||||
[ProtoMember(11)]
|
||||
public string ServerName;
|
||||
|
||||
public OnlineServer() { }
|
||||
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class OnlinePlayer
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public string PlayerName;
|
||||
|
||||
[ProtoMember(2)]
|
||||
public ulong SteamID;
|
||||
|
||||
[ProtoMember(3)]
|
||||
public long IdentityID;
|
||||
|
||||
[ProtoMember(4)]
|
||||
public int OnServer;
|
||||
|
||||
public OnlinePlayer(string PlayerName, ulong SteamID, long IdentityID, int OnServer)
|
||||
{
|
||||
this.PlayerName = PlayerName;
|
||||
this.SteamID = SteamID;
|
||||
this.IdentityID = IdentityID;
|
||||
this.OnServer = OnServer;
|
||||
}
|
||||
|
||||
|
||||
public OnlinePlayer() { }
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
[ProtoMember(10)] public List<OnlineServer> OnlineServers = [];
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class OnlineServer
|
||||
{
|
||||
[ProtoMember(2)] public List<OnlinePlayer> Players = [];
|
||||
|
||||
[ProtoMember(10)] public int ServerId;
|
||||
|
||||
[ProtoMember(11)] public string ServerName;
|
||||
|
||||
[ProtoMember(3)] public bool ServerRunning = false;
|
||||
}
|
||||
|
||||
[ProtoContract]
|
||||
public class OnlinePlayer
|
||||
{
|
||||
[ProtoMember(3)] public long IdentityId;
|
||||
|
||||
[ProtoMember(4)] public int OnServer;
|
||||
|
||||
[ProtoMember(1)] public string PlayerName;
|
||||
|
||||
[ProtoMember(2)] public ulong SteamId;
|
||||
|
||||
public OnlinePlayer(string playerName, ulong steamId, long identityId, int onServer)
|
||||
{
|
||||
this.PlayerName = playerName;
|
||||
this.SteamId = steamId;
|
||||
this.IdentityId = identityId;
|
||||
this.OnServer = onServer;
|
||||
}
|
||||
|
||||
|
||||
public OnlinePlayer()
|
||||
{
|
||||
}
|
||||
}
|
@@ -1,63 +1,33 @@
|
||||
using ProtoBuf;
|
||||
using Sandbox;
|
||||
using Sandbox.Engine.Multiplayer;
|
||||
using Sandbox.Engine.Networking;
|
||||
using Sandbox.Game.Entities;
|
||||
using Sandbox.Game.Gui;
|
||||
using Sandbox.Game.Multiplayer;
|
||||
using Sandbox.Game.World;
|
||||
using Sandbox.Graphics.GUI;
|
||||
using Sandbox.ModAPI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VRage;
|
||||
using VRage.Game;
|
||||
using VRage.GameServices;
|
||||
using VRage.Network;
|
||||
using VRage.Steam;
|
||||
using VRage.Utils;
|
||||
using VRageMath;
|
||||
|
||||
namespace SeamlessClientPlugin.Messages
|
||||
namespace SeamlessClientPlugin.Messages;
|
||||
|
||||
[ProtoContract]
|
||||
public class Transfer
|
||||
{
|
||||
[ProtoMember(2)] public string IpAdress;
|
||||
|
||||
[ProtoContract]
|
||||
public class Transfer
|
||||
[ProtoMember(7)] public string PlayerName;
|
||||
|
||||
[ProtoMember(9)] public MyObjectBuilder_Toolbar PlayerToolbar;
|
||||
|
||||
[ProtoMember(10)] public string ServerName;
|
||||
|
||||
[ProtoMember(1)] public ulong TargetServerId;
|
||||
|
||||
[ProtoMember(6)] public WorldRequest WorldRequest;
|
||||
|
||||
public Transfer(ulong serverId, string ipAdress)
|
||||
{
|
||||
[ProtoMember(1)]
|
||||
public ulong TargetServerID;
|
||||
[ProtoMember(2)]
|
||||
public string IPAdress;
|
||||
[ProtoMember(6)]
|
||||
public WorldRequest WorldRequest;
|
||||
[ProtoMember(7)]
|
||||
public string PlayerName;
|
||||
[ProtoMember(9)]
|
||||
public MyObjectBuilder_Toolbar PlayerToolbar;
|
||||
|
||||
[ProtoMember(10)]
|
||||
public string ServerName;
|
||||
|
||||
public Transfer(ulong ServerID, string IPAdress)
|
||||
{
|
||||
/* This is only called serverside
|
||||
*/
|
||||
|
||||
this.IPAdress = IPAdress;
|
||||
TargetServerID = ServerID;
|
||||
}
|
||||
|
||||
public Transfer() { }
|
||||
|
||||
|
||||
|
||||
/* This is only called serverside
|
||||
*/
|
||||
|
||||
this.IpAdress = ipAdress;
|
||||
TargetServerId = serverId;
|
||||
}
|
||||
|
||||
public Transfer()
|
||||
{
|
||||
}
|
||||
}
|
@@ -1,72 +1,55 @@
|
||||
using NLog;
|
||||
using System.Reflection;
|
||||
using NLog;
|
||||
using ProtoBuf;
|
||||
using Sandbox.Engine.Multiplayer;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VRage.Game;
|
||||
using VRage.ObjectBuilders;
|
||||
using VRage.ObjectBuilders.Private;
|
||||
|
||||
namespace SeamlessClientPlugin.Messages
|
||||
namespace SeamlessClientPlugin.Messages;
|
||||
|
||||
[ProtoContract]
|
||||
public class WorldRequest
|
||||
{
|
||||
[ProtoContract]
|
||||
public class WorldRequest
|
||||
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
[ProtoMember(5)] public MyObjectBuilder_Gps GpsCollection;
|
||||
|
||||
[ProtoMember(2)] public long IdentityId;
|
||||
|
||||
[ProtoMember(1)] public ulong PlayerId;
|
||||
|
||||
[ProtoMember(3)] public string PlayerName;
|
||||
|
||||
[ProtoMember(4)] public byte[] WorldData;
|
||||
|
||||
public WorldRequest(ulong playerId, long playerIdentity, string name)
|
||||
{
|
||||
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||
this.PlayerId = playerId;
|
||||
PlayerName = name;
|
||||
IdentityId = playerIdentity;
|
||||
}
|
||||
|
||||
[ProtoMember(1)]
|
||||
public ulong PlayerID;
|
||||
[ProtoMember(2)]
|
||||
public long IdentityID;
|
||||
[ProtoMember(3)]
|
||||
public string PlayerName;
|
||||
[ProtoMember(4)]
|
||||
public byte[] WorldData;
|
||||
public WorldRequest()
|
||||
{
|
||||
}
|
||||
|
||||
[ProtoMember(5)]
|
||||
public MyObjectBuilder_Gps gpsCollection;
|
||||
public void SerializeWorldData(MyObjectBuilder_World worldData)
|
||||
{
|
||||
MyMultiplayerServerBase.CleanUpData(worldData, PlayerId, IdentityId);
|
||||
using var memoryStream = new MemoryStream();
|
||||
MyObjectBuilderSerializerKeen.SerializeXML(memoryStream, worldData,
|
||||
MyObjectBuilderSerializerKeen.XmlCompression.Gzip);
|
||||
WorldData = memoryStream.ToArray();
|
||||
Log.Warn("Successfully Converted World");
|
||||
}
|
||||
|
||||
public WorldRequest(ulong PlayerID,long PlayerIdentity, string Name)
|
||||
{
|
||||
this.PlayerID = PlayerID;
|
||||
this.PlayerName = Name;
|
||||
this.IdentityID = PlayerIdentity;
|
||||
}
|
||||
|
||||
public WorldRequest() { }
|
||||
|
||||
public void SerializeWorldData(MyObjectBuilder_World WorldData)
|
||||
{
|
||||
MethodInfo CleanupData = typeof(MyMultiplayerServerBase).GetMethod("CleanUpData", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[3]
|
||||
{
|
||||
typeof(MyObjectBuilder_World),
|
||||
typeof(ulong),
|
||||
typeof(long),
|
||||
}, null);
|
||||
object[] Data = new object[] { WorldData, PlayerID, IdentityID };
|
||||
CleanupData.Invoke(null, Data);
|
||||
WorldData = (MyObjectBuilder_World)Data[0];
|
||||
using (MemoryStream memoryStream = new MemoryStream())
|
||||
{
|
||||
MyObjectBuilderSerializer.SerializeXML(memoryStream, WorldData, MyObjectBuilderSerializer.XmlCompression.Gzip);
|
||||
this.WorldData = memoryStream.ToArray();
|
||||
Log.Warn("Successfully Converted World");
|
||||
}
|
||||
}
|
||||
|
||||
public MyObjectBuilder_World DeserializeWorldData()
|
||||
{
|
||||
MyObjectBuilderSerializer.DeserializeGZippedXML<MyObjectBuilder_World>(new MemoryStream(WorldData), out var objectBuilder);
|
||||
objectBuilder.Checkpoint.Gps.Dictionary.Add(IdentityID, gpsCollection);
|
||||
public MyObjectBuilder_World DeserializeWorldData()
|
||||
{
|
||||
MyObjectBuilderSerializerKeen.DeserializeGZippedXML<MyObjectBuilder_World>(new MemoryStream(WorldData),
|
||||
out var objectBuilder);
|
||||
objectBuilder.Checkpoint.Gps.Dictionary.Add(IdentityId, GpsCollection);
|
||||
|
||||
|
||||
|
||||
return objectBuilder;
|
||||
}
|
||||
|
||||
return objectBuilder;
|
||||
}
|
||||
}
|
@@ -1,38 +0,0 @@
|
||||
using System.Resources;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("SeamlessClientPlugin")]
|
||||
[assembly: AssemblyDescription("A seamless client plugin for Nexus compatible servers")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Casimir")]
|
||||
[assembly: AssemblyProduct("SeamlessClientPlugin")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2020")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("102a3d80-b588-43ba-b686-000fa8ff1a0c")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.4.02.0")]
|
||||
[assembly: AssemblyFileVersion("1.4.02.0")]
|
||||
[assembly: NeutralResourcesLanguage("en")]
|
30
README.md
30
README.md
@@ -1,21 +1,33 @@
|
||||
# SeamlessClientPlugin
|
||||
The seamless client plugin lets you switch between SE servers without a loading screen. This currently *only* works with Nexus compatible servers as all data is shared and synced between them.
|
||||
|
||||
The main load time is the time it takes for your client to ping the destination server, and the client to recieve the go-ahead. Any extra time is contributed to entities syncing to the client similar to if you were respawning at the grid. (sometimes it takes forever). I will be looking into pre-loading synced entities to the client in the near future, but atm this was the easier solution.
|
||||
|
||||
This has taken countless hours of testing and debugging to get right. Not to mention the countless hours implementing the server plugin Nexus. If you enjoy this kind of work, please donate [here](https://se-nexus.net/en/Contribute) to help keep this project alive.
|
||||
The seamless client plugin lets you switch between SE servers without a loading screen. This currently *only* works with
|
||||
Nexus compatible servers as all data is shared and synced between them.
|
||||
|
||||
The main load time is the time it takes for your client to ping the destination server, and the client to recieve the
|
||||
go-ahead. Any extra time is contributed to entities syncing to the client similar to if you were respawning at the
|
||||
grid. (sometimes it takes forever). I will be looking into pre-loading synced entities to the client in the near future,
|
||||
but atm this was the easier solution.
|
||||
|
||||
This has taken countless hours of testing and debugging to get right. Not to mention the countless hours implementing
|
||||
the server plugin Nexus. If you enjoy this kind of work, please donate [here](https://se-nexus.net/en/Contribute) to
|
||||
help keep this project alive.
|
||||
|
||||
## How it works
|
||||
With Nexus servers, all data is shared between servers. (Factions, Identities, Players, Econ etc) This is a huge benefit as we dont have to go in and reload all identities and factions etc. The next thing that happens is that the server tells the client to switch to the proper server. It then goes in and just re-applies the MyMultiplayerClient to the target server. Of course there is a few other things that must happen to fix any errors or bugs, but that is the main rundown.
|
||||
|
||||
|
||||
With Nexus servers, all data is shared between servers. (Factions, Identities, Players, Econ etc) This is a huge benefit
|
||||
as we dont have to go in and reload all identities and factions etc. The next thing that happens is that the server
|
||||
tells the client to switch to the proper server. It then goes in and just re-applies the MyMultiplayerClient to the
|
||||
target server. Of course there is a few other things that must happen to fix any errors or bugs, but that is the main
|
||||
rundown.
|
||||
|
||||
## How to install
|
||||
Simply install the plguin loader, and check this plugins box to be added to the plugin loaders' active plugin list. (SE will need to be restarted afterwards)
|
||||
|
||||
|
||||
Simply install the plguin loader, and check this plugins box to be added to the plugin loaders' active plugin list. (SE
|
||||
will need to be restarted afterwards)
|
||||
|
||||
## Known issues
|
||||
Obviously this is not an issue free-system. Currently since im doing no mod unloading or loading there could be issues if your servers dont have the exact same mods, or the mods dont properly work right. Please do not swarm mod authors with faults if seamless doesnt play nice with it. ***Its not their fault*** its ***mine***. I will be trying to implement mod unloading and loading switching between servers, just no ETA.
|
||||
|
||||
Obviously this is not an issue free-system. Currently since im doing no mod unloading or loading there could be issues
|
||||
if your servers dont have the exact same mods, or the mods dont properly work right. Please do not swarm mod authors
|
||||
with faults if seamless doesnt play nice with it. ***Its not their fault*** its ***mine***. I will be trying to
|
||||
implement mod unloading and loading switching between servers, just no ETA.
|
||||
|
@@ -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}");
|
||||
}
|
||||
}
|
@@ -1,162 +1,49 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{102A3D80-B588-43BA-B686-000FA8FF1A0C}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>SeamlessClientPlugin</RootNamespace>
|
||||
<AssemblyName>SeamlessClientPlugin</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
<TargetFrameworkProfile />
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>F:\SteamLibrary\steamapps\common\SpaceEngineers\Plugins\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="0Harmony, Version=2.2.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\Nexus\packages\Lib.Harmony.2.2.1\lib\net48\0Harmony.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NLog">
|
||||
<HintPath>..\..\..\Desktop\TorchServers\torch-server1\DedicatedServer64\NLog.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="ProtoBuf.Net">
|
||||
<HintPath>..\..\..\Desktop\TorchServers\torch-server1\DedicatedServer64\ProtoBuf.Net.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="ProtoBuf.Net.Core">
|
||||
<HintPath>..\..\..\Desktop\TorchServers\torch-server1\DedicatedServer64\ProtoBuf.Net.Core.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Sandbox.Common">
|
||||
<HintPath>..\..\..\Desktop\TorchServers\torch-server1\DedicatedServer64\Sandbox.Common.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Sandbox.Game">
|
||||
<HintPath>..\..\..\Desktop\TorchServers\torch-server1\DedicatedServer64\Sandbox.Game.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Sandbox.Graphics">
|
||||
<HintPath>..\..\..\Desktop\TorchServers\torch-server1\DedicatedServer64\Sandbox.Graphics.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="SpaceEngineers.Game">
|
||||
<HintPath>..\..\..\Desktop\TorchServers\torch-server1\DedicatedServer64\SpaceEngineers.Game.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="SpaceEngineers.ObjectBuilders.XmlSerializers">
|
||||
<HintPath>..\..\..\Desktop\TorchServers\torch-server1\DedicatedServer64\SpaceEngineers.ObjectBuilders.XmlSerializers.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Core">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.IO.Compression, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||
<HintPath>..\Nexus\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.IO.Compression.FileSystem" />
|
||||
<Reference Include="System.IO.Compression.ZipFile, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||
<HintPath>..\Nexus\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.InteropServices.RuntimeInformation">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.Runtime.Serialization.Json" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xaml" />
|
||||
<Reference Include="System.Xml.Linq">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Data.DataSetExtensions">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CSharp">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Data">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="VRage">
|
||||
<HintPath>..\..\..\Desktop\TorchServers\torch-server1\DedicatedServer64\VRage.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="VRage.Game">
|
||||
<HintPath>..\..\..\Desktop\TorchServers\torch-server1\DedicatedServer64\VRage.Game.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="VRage.Library">
|
||||
<HintPath>..\..\..\Desktop\TorchServers\torch-server1\DedicatedServer64\VRage.Library.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="VRage.Math">
|
||||
<HintPath>..\..\..\Desktop\TorchServers\torch-server1\DedicatedServer64\VRage.Math.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="VRage.Render">
|
||||
<HintPath>..\..\..\Desktop\TorchServers\torch-server1\DedicatedServer64\VRage.Render.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="VRage.Steam">
|
||||
<HintPath>..\..\..\Desktop\TorchServers\torch-server1\DedicatedServer64\VRage.Steam.dll</HintPath>
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Messages\ClientMessages.cs" />
|
||||
<Compile Include="Messages\OnlinePlayersMessage.cs" />
|
||||
<Compile Include="Messages\WorldRequest.cs" />
|
||||
<Compile Include="SeamlessClient.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SeamlessTransfer\ModLoader.cs" />
|
||||
<Compile Include="SeamlessTransfer\MyScriptManagerLoader.cs" />
|
||||
<Compile Include="SeamlessTransfer\PingServer.cs" />
|
||||
<Compile Include="Messages\Transfer.cs" />
|
||||
<Compile Include="Utilities\OnlinePlayers.cs" />
|
||||
<Compile Include="Utilities\Patches.cs" />
|
||||
<Compile Include="SeamlessTransfer\SwitchServers.cs" />
|
||||
<Compile Include="Utilities\Utility.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0-windows</TargetFramework>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>disable</Nullable>
|
||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||
<PackageType>CringePlugin</PackageType>
|
||||
<RestoreAdditionalProjectSources>https://ng.zznty.ru/v3/index.json</RestoreAdditionalProjectSources>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Authors>Casimir</Authors>
|
||||
<PackageId>Plugin.Casimir255.SeamlessClient</PackageId>
|
||||
<AssemblyName>Plugin.Casimir255.SeamlessClient</AssemblyName>
|
||||
<Title>Nexus Seamless Switcher</Title>
|
||||
<Description>This plugin allows seamless transfers between Nexus enabled servers. Some mods or plugins may not play nice with switching between servers. Be cautious when using!</Description>
|
||||
<PackageProjectUrl>https://git.zznty.ru/PvE/SeamlessClient</PackageProjectUrl>
|
||||
<RepositoryUrl>https://git.zznty.ru/PvE/SeamlessClient</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Publicize Include="Sandbox.Game;Sandbox.Graphics" IncludeVirtualMembers="false" IncludeCompilerGeneratedMembers="false" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CringePlugins" Version="*" ExcludeAssets="runtime; native"/>
|
||||
<PackageReference Include="Krafs.Publicizer" Version="2.3.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.11.0" ExcludeAssets="runtime; native" PrivateAssets="all"/>
|
||||
<PackageReference Include="Steamworks.NET" Version="20.1.0" ExcludeAssets="runtime; native" PrivateAssets="all"/>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<Target Name="CopyFiles" AfterTargets="PostBuildEvent">
|
||||
<PropertyGroup>
|
||||
<AppdataPath>$([System.Environment]::GetFolderPath(SpecialFolder.ApplicationData))</AppdataPath>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<OutputFiles Include="$(OutputPath)\*"/>
|
||||
</ItemGroup>
|
||||
<Copy SourceFiles="@(OutputFiles)" DestinationFolder="$(AppdataPath)\CringeLauncher\plugins\$(ProjectName)" OverwriteReadOnlyFiles="true"/>
|
||||
</Target>
|
||||
|
||||
|
||||
</Project>
|
@@ -1,173 +1,136 @@
|
||||
using Sandbox.Definitions;
|
||||
using Sandbox.Engine.Networking;
|
||||
using Sandbox.Engine.Networking;
|
||||
using Sandbox.Game.World;
|
||||
using Sandbox.ModAPI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VRage.Game;
|
||||
using VRage.Game.GUI;
|
||||
using VRage.GameServices;
|
||||
|
||||
namespace SeamlessClientPlugin.SeamlessTransfer
|
||||
namespace SeamlessClientPlugin.SeamlessTransfer;
|
||||
|
||||
public static class ModLoader
|
||||
{
|
||||
public static class ModLoader
|
||||
/* Mod loader should download and load missing mods for target server, and unload ones that arent needed
|
||||
*
|
||||
* Sandbox.Game.World.MyScriptManager.LoadData() is where modded scripts get loaded and added
|
||||
* Sandbox.Game.World.MySession() calls MyDefinitionManager.Static.LoadData(mods); which loads mod data files
|
||||
*
|
||||
*
|
||||
* Need to be called in the following order:
|
||||
* ScriptManager.Init(checkpoint.ScriptManagerData);
|
||||
* MyDefinitionManager.Static.LoadData(checkpoint.Mods);
|
||||
* PreloadModels(sector);
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
//Mods that are currently loaded in this instance.
|
||||
private static List<MyObjectBuilder_Checkpoint.ModItem> _currentLoadedMods = [];
|
||||
|
||||
|
||||
//Mods that we need to Load
|
||||
private static readonly List<MyObjectBuilder_Checkpoint.ModItem> TargetLoadMods = [];
|
||||
|
||||
//Mods that we need to UnLoad
|
||||
private static readonly List<MyObjectBuilder_Checkpoint.ModItem> TargetUnLoadMods = [];
|
||||
|
||||
|
||||
private static bool _finishedDownloadingMods;
|
||||
private static bool _downloadSuccess;
|
||||
|
||||
private static DateTime _downloadTimeout;
|
||||
|
||||
|
||||
public static void DownloadNewMods(List<MyObjectBuilder_Checkpoint.ModItem> target)
|
||||
{
|
||||
/* Mod loader should download and load missing mods for target server, and unload ones that arent needed
|
||||
*
|
||||
* Sandbox.Game.World.MyScriptManager.LoadData() is where modded scripts get loaded and added
|
||||
* Sandbox.Game.World.MySession() calls MyDefinitionManager.Static.LoadData(mods); which loads mod data files
|
||||
*
|
||||
*
|
||||
* Need to be called in the following order:
|
||||
* ScriptManager.Init(checkpoint.ScriptManagerData);
|
||||
* MyDefinitionManager.Static.LoadData(checkpoint.Mods);
|
||||
* PreloadModels(sector);
|
||||
*
|
||||
*
|
||||
*/
|
||||
_currentLoadedMods = MySession.Static.Mods;
|
||||
|
||||
|
||||
//Mods that are currently loaded in this instance.
|
||||
private static List<MyObjectBuilder_Checkpoint.ModItem> CurrentLoadedMods = new List<MyObjectBuilder_Checkpoint.ModItem>();
|
||||
//Loop through our current mods
|
||||
foreach (var mod in _currentLoadedMods)
|
||||
if (!target.Contains(mod))
|
||||
TargetUnLoadMods.Add(mod);
|
||||
|
||||
|
||||
//Loop through our TargetMods
|
||||
foreach (var mod in target)
|
||||
if (!_currentLoadedMods.Contains(mod))
|
||||
TargetLoadMods.Add(mod);
|
||||
|
||||
|
||||
//Mods that we need to Load
|
||||
private static List<MyObjectBuilder_Checkpoint.ModItem> TargetLoadMods = new List<MyObjectBuilder_Checkpoint.ModItem>();
|
||||
_downloadTimeout = DateTime.Now + TimeSpan.FromMinutes(5);
|
||||
SeamlessClient.TryShow("Downloading New Mods");
|
||||
MyWorkshop.DownloadModsAsync(TargetLoadMods, ModDownloadingFinished);
|
||||
}
|
||||
|
||||
//Mods that we need to UnLoad
|
||||
private static List<MyObjectBuilder_Checkpoint.ModItem> TargetUnLoadMods = new List<MyObjectBuilder_Checkpoint.ModItem>();
|
||||
|
||||
|
||||
private static bool FinishedDownloadingMods = false;
|
||||
private static bool DownloadSuccess = false;
|
||||
|
||||
private static DateTime DownloadTimeout;
|
||||
|
||||
private static MethodInfo PrepareBaseSession = typeof(MySession).GetMethod("PreloadModels", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
private static FieldInfo ScriptManager = typeof(MySession).GetField("ScriptManager", BindingFlags.Instance | BindingFlags.Public);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static void DownloadNewMods(List<MyObjectBuilder_Checkpoint.ModItem> Target)
|
||||
private static void ModDownloadingFinished(MyGameServiceCallResult result)
|
||||
{
|
||||
if (result == MyGameServiceCallResult.OK)
|
||||
{
|
||||
CurrentLoadedMods = MySession.Static.Mods;
|
||||
SeamlessClient.TryShow("Mod Downloading Finished!");
|
||||
_finishedDownloadingMods = true;
|
||||
_downloadSuccess = true;
|
||||
//May need to wait seamless loading if mods have yet to finish downloading
|
||||
}
|
||||
else
|
||||
{
|
||||
_downloadSuccess = false;
|
||||
_finishedDownloadingMods = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Loop through our current mods
|
||||
foreach(var mod in CurrentLoadedMods)
|
||||
{
|
||||
if (!Target.Contains(mod))
|
||||
TargetUnLoadMods.Add(mod);
|
||||
}
|
||||
public static void ReadyModSwitch(MyObjectBuilder_Checkpoint checkpoint, MyObjectBuilder_Sector sector)
|
||||
{
|
||||
while (!_finishedDownloadingMods)
|
||||
{
|
||||
//Break out of loop
|
||||
if (_downloadTimeout < DateTime.Now)
|
||||
break;
|
||||
|
||||
|
||||
//Loop through our TargetMods
|
||||
foreach(var mod in Target)
|
||||
{
|
||||
if (!CurrentLoadedMods.Contains(mod))
|
||||
TargetLoadMods.Add(mod);
|
||||
}
|
||||
|
||||
|
||||
DownloadTimeout = DateTime.Now + TimeSpan.FromMinutes(5);
|
||||
SeamlessClient.TryShow("Downloading New Mods");
|
||||
MyWorkshop.DownloadModsAsync(TargetLoadMods, ModDownloadingFinished);
|
||||
|
||||
Thread.Sleep(20);
|
||||
}
|
||||
|
||||
private static void ModDownloadingFinished(bool Success)
|
||||
_finishedDownloadingMods = false;
|
||||
|
||||
//Skip mod switch
|
||||
if (!_downloadSuccess)
|
||||
return;
|
||||
|
||||
|
||||
//Create new script manager?
|
||||
MySession.Static.ScriptManager = new();
|
||||
|
||||
|
||||
MyGuiTextures.Static.Unload();
|
||||
MySession.Static.ScriptManager.Init(checkpoint.ScriptManagerData);
|
||||
//MyDefinitionManager.Static.LoadData(TargetServerMods);
|
||||
MySession.PreloadModels(sector);
|
||||
|
||||
|
||||
MyLocalCache.PreloadLocalInventoryConfig();
|
||||
|
||||
|
||||
//SeamlessClient.TryShow("Finished transfering!");
|
||||
|
||||
|
||||
// PrepareBaseSession.Invoke(MySession.Static, new object[] { TargetServerMods, null });
|
||||
}
|
||||
|
||||
|
||||
private static void UnloadOldScripts()
|
||||
{
|
||||
// MySandboxGame.Log.WriteLine(string.Format("Script loaded: {0}", value.FullName));
|
||||
var amount = 0;
|
||||
foreach (var mod in TargetUnLoadMods)
|
||||
{
|
||||
if (Success)
|
||||
{
|
||||
SeamlessClient.TryShow("Mod Downloading Finished!");
|
||||
FinishedDownloadingMods = true;
|
||||
DownloadSuccess = true;
|
||||
//May need to wait seamless loading if mods have yet to finish downloading
|
||||
}
|
||||
else
|
||||
{
|
||||
DownloadSuccess = false;
|
||||
FinishedDownloadingMods = true;
|
||||
}
|
||||
var val = MySession.Static.ScriptManager.Scripts.FirstOrDefault(x =>
|
||||
x.Value.FullName.Contains(mod.PublishedFileId.ToString()));
|
||||
MySession.Static.ScriptManager.Scripts.Remove(val.Key);
|
||||
|
||||
amount++;
|
||||
}
|
||||
|
||||
|
||||
public static void ReadyModSwitch(MyObjectBuilder_Checkpoint checkpoint, MyObjectBuilder_Sector sector)
|
||||
{
|
||||
|
||||
while (!FinishedDownloadingMods)
|
||||
{
|
||||
|
||||
//Break out of loop
|
||||
if (DownloadTimeout < DateTime.Now)
|
||||
break;
|
||||
|
||||
|
||||
Thread.Sleep(20);
|
||||
}
|
||||
|
||||
FinishedDownloadingMods = false;
|
||||
|
||||
//Skip mod switch
|
||||
if (!DownloadSuccess)
|
||||
return;
|
||||
|
||||
|
||||
|
||||
//Create new script manager?
|
||||
ScriptManager.SetValue(MySession.Static, new MyScriptManager());
|
||||
|
||||
|
||||
MyGuiTextures.Static.Unload();
|
||||
MySession.Static.ScriptManager.Init(checkpoint.ScriptManagerData);
|
||||
//MyDefinitionManager.Static.LoadData(TargetServerMods);
|
||||
PrepareBaseSession.Invoke(null, new object[] { sector });
|
||||
|
||||
|
||||
MyLocalCache.PreloadLocalInventoryConfig();
|
||||
|
||||
|
||||
//SeamlessClient.TryShow("Finished transfering!");
|
||||
|
||||
|
||||
// PrepareBaseSession.Invoke(MySession.Static, new object[] { TargetServerMods, null });
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static void UnloadOldScripts()
|
||||
{
|
||||
// MySandboxGame.Log.WriteLine(string.Format("Script loaded: {0}", value.FullName));
|
||||
int amount = 0;
|
||||
foreach (var mod in TargetUnLoadMods)
|
||||
{
|
||||
var val = MySession.Static.ScriptManager.Scripts.FirstOrDefault(x => x.Value.FullName.Contains(mod.PublishedFileId.ToString()));
|
||||
MySession.Static.ScriptManager.Scripts.Remove(val.Key);
|
||||
|
||||
amount++;
|
||||
}
|
||||
|
||||
SeamlessClient.TryShow($"Removed {amount} old scripts!");
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SeamlessClient.TryShow($"Removed {amount} old scripts!");
|
||||
}
|
||||
}
|
@@ -1,83 +1,73 @@
|
||||
using Sandbox;
|
||||
using Sandbox.Game.World;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
namespace SeamlessClientPlugin.SeamlessTransfer;
|
||||
|
||||
namespace SeamlessClientPlugin.SeamlessTransfer
|
||||
public class MyScriptManagerLoader
|
||||
{
|
||||
public class MyScriptManagerLoader
|
||||
/*
|
||||
public void LoadData(MyScriptManager __instance)
|
||||
{
|
||||
/*
|
||||
public void LoadData(MyScriptManager __instance)
|
||||
{
|
||||
MySandboxGame.Log.WriteLine("MyScriptManager.LoadData() - START");
|
||||
MySandboxGame.Log.IncreaseIndent();
|
||||
MyScriptManager.Static = __instance;
|
||||
__instance.Scripts.Clear();
|
||||
__instance.EntityScripts.Clear();
|
||||
__instance.SubEntityScripts.Clear();
|
||||
MySandboxGame.Log.WriteLine("MyScriptManager.LoadData() - START");
|
||||
MySandboxGame.Log.IncreaseIndent();
|
||||
MyScriptManager.Static = __instance;
|
||||
__instance.Scripts.Clear();
|
||||
__instance.EntityScripts.Clear();
|
||||
__instance.SubEntityScripts.Clear();
|
||||
|
||||
TryAddEntityScripts(MyModContext.BaseGame, MyPlugins.SandboxAssembly);
|
||||
TryAddEntityScripts(MyModContext.BaseGame, MyPlugins.SandboxGameAssembly);
|
||||
TryAddEntityScripts(MyModContext.BaseGame, MyPlugins.SandboxAssembly);
|
||||
TryAddEntityScripts(MyModContext.BaseGame, MyPlugins.SandboxGameAssembly);
|
||||
|
||||
if (MySession.Static.CurrentPath != null)
|
||||
{
|
||||
LoadScripts(MySession.Static.CurrentPath, MyModContext.BaseGame);
|
||||
}
|
||||
if (MySession.Static.Mods != null)
|
||||
{
|
||||
bool isServer = Sync.IsServer;
|
||||
foreach (MyObjectBuilder_Checkpoint.ModItem mod in MySession.Static.Mods)
|
||||
{
|
||||
bool flag = false;
|
||||
if (mod.IsModData())
|
||||
{
|
||||
ListReader<string> tags = mod.GetModData().Tags;
|
||||
if (tags.Contains(MySteamConstants.TAG_SERVER_SCRIPTS) && !isServer)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
flag = tags.Contains(MySteamConstants.TAG_NO_SCRIPTS);
|
||||
}
|
||||
MyModContext myModContext = (MyModContext)mod.GetModContext();
|
||||
try
|
||||
{
|
||||
LoadScripts(mod.GetPath(), myModContext);
|
||||
}
|
||||
catch (MyLoadingRuntimeCompilationNotSupportedException)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
MyVRage.Platform.Scripting.ReportIncorrectBehaviour(MyCommonTexts.ModRuleViolation_RuntimeScripts);
|
||||
continue;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
MyLog.Default.WriteLine(string.Format("Fatal error compiling {0}:{1} - {2}. This item is likely not a mod and should be removed from the mod list.", myModContext.ModServiceName, myModContext.ModId, myModContext.ModName));
|
||||
MyLog.Default.WriteLine(ex2);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (Assembly value in Scripts.Values)
|
||||
{
|
||||
if (MyFakes.ENABLE_TYPES_FROM_MODS)
|
||||
{
|
||||
MyGlobalTypeMetadata.Static.RegisterAssembly(value);
|
||||
}
|
||||
MySandboxGame.Log.WriteLine(string.Format("Script loaded: {0}", value.FullName));
|
||||
}
|
||||
MyTextSurfaceScriptFactory.LoadScripts();
|
||||
MyUseObjectFactory.RegisterAssemblyTypes(Scripts.Values.ToArray());
|
||||
MySandboxGame.Log.DecreaseIndent();
|
||||
MySandboxGame.Log.WriteLine("MyScriptManager.LoadData() - END");
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
if (MySession.Static.CurrentPath != null)
|
||||
{
|
||||
LoadScripts(MySession.Static.CurrentPath, MyModContext.BaseGame);
|
||||
}
|
||||
if (MySession.Static.Mods != null)
|
||||
{
|
||||
bool isServer = Sync.IsServer;
|
||||
foreach (MyObjectBuilder_Checkpoint.ModItem mod in MySession.Static.Mods)
|
||||
{
|
||||
bool flag = false;
|
||||
if (mod.IsModData())
|
||||
{
|
||||
ListReader<string> tags = mod.GetModData().Tags;
|
||||
if (tags.Contains(MySteamConstants.TAG_SERVER_SCRIPTS) && !isServer)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
flag = tags.Contains(MySteamConstants.TAG_NO_SCRIPTS);
|
||||
}
|
||||
MyModContext myModContext = (MyModContext)mod.GetModContext();
|
||||
try
|
||||
{
|
||||
LoadScripts(mod.GetPath(), myModContext);
|
||||
}
|
||||
catch (MyLoadingRuntimeCompilationNotSupportedException)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
MyVRage.Platform.Scripting.ReportIncorrectBehaviour(MyCommonTexts.ModRuleViolation_RuntimeScripts);
|
||||
continue;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
MyLog.Default.WriteLine(string.Format("Fatal error compiling {0}:{1} - {2}. This item is likely not a mod and should be removed from the mod list.", myModContext.ModServiceName, myModContext.ModId, myModContext.ModName));
|
||||
MyLog.Default.WriteLine(ex2);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (Assembly value in Scripts.Values)
|
||||
{
|
||||
if (MyFakes.ENABLE_TYPES_FROM_MODS)
|
||||
{
|
||||
MyGlobalTypeMetadata.Static.RegisterAssembly(value);
|
||||
}
|
||||
MySandboxGame.Log.WriteLine(string.Format("Script loaded: {0}", value.FullName));
|
||||
}
|
||||
MyTextSurfaceScriptFactory.LoadScripts();
|
||||
MyUseObjectFactory.RegisterAssemblyTypes(Scripts.Values.ToArray());
|
||||
MySandboxGame.Log.DecreaseIndent();
|
||||
MySandboxGame.Log.WriteLine("MyScriptManager.LoadData() - END");
|
||||
}
|
||||
*/
|
||||
}
|
@@ -1,51 +1,41 @@
|
||||
using Sandbox.Engine.Networking;
|
||||
using Sandbox.Game.Multiplayer;
|
||||
using SeamlessClientPlugin.Messages;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SeamlessClientPlugin.Messages;
|
||||
using VRage.GameServices;
|
||||
|
||||
namespace SeamlessClientPlugin.SeamlessTransfer
|
||||
namespace SeamlessClientPlugin.SeamlessTransfer;
|
||||
|
||||
public class ServerPing
|
||||
{
|
||||
public class ServerPing
|
||||
private static Transfer _transfer;
|
||||
|
||||
private static WorldRequest Request => _transfer.WorldRequest;
|
||||
|
||||
|
||||
public static void StartServerPing(Transfer clientTransfer)
|
||||
{
|
||||
|
||||
private static WorldRequest Request { get { return Transfer.WorldRequest; } }
|
||||
private static Transfer Transfer;
|
||||
// We need to first ping the server to make sure its running and so we can get a connection
|
||||
_transfer = clientTransfer;
|
||||
|
||||
|
||||
public static void StartServerPing(Transfer ClientTransfer)
|
||||
if (_transfer.TargetServerId == 0)
|
||||
{
|
||||
// We need to first ping the server to make sure its running and so we can get a connection
|
||||
Transfer = ClientTransfer;
|
||||
|
||||
|
||||
if (Transfer.TargetServerID == 0)
|
||||
{
|
||||
SeamlessClient.TryShow("This is not a valid server!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
MyGameServerItem E = new MyGameServerItem();
|
||||
E.ConnectionString = Transfer.IPAdress;
|
||||
E.SteamID = Transfer.TargetServerID;
|
||||
E.Name = Transfer.ServerName;
|
||||
|
||||
|
||||
|
||||
SeamlessClient.TryShow("Beginning Redirect to server: " + Transfer.TargetServerID);
|
||||
|
||||
|
||||
var world = Request.DeserializeWorldData();
|
||||
|
||||
|
||||
SwitchServers Switcher = new SwitchServers(E, world);
|
||||
Switcher.BeginSwitch();
|
||||
SeamlessClient.TryShow("This is not a valid server!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var e = new MyGameServerItem();
|
||||
e.ConnectionString = _transfer.IpAdress;
|
||||
e.SteamID = _transfer.TargetServerId;
|
||||
e.Name = _transfer.ServerName;
|
||||
|
||||
|
||||
SeamlessClient.TryShow($"Beginning Redirect to server: {_transfer.TargetServerId}");
|
||||
|
||||
|
||||
var world = Request.DeserializeWorldData();
|
||||
|
||||
|
||||
var switcher = new SwitchServers(e, world);
|
||||
switcher.BeginSwitch();
|
||||
}
|
||||
}
|
@@ -1,5 +1,4 @@
|
||||
using Sandbox;
|
||||
using Sandbox.Definitions;
|
||||
using Sandbox.Engine.Multiplayer;
|
||||
using Sandbox.Engine.Networking;
|
||||
using Sandbox.Game;
|
||||
@@ -10,459 +9,383 @@ using Sandbox.Game.Multiplayer;
|
||||
using Sandbox.Game.SessionComponents;
|
||||
using Sandbox.Game.World;
|
||||
using Sandbox.Game.World.Generator;
|
||||
using Sandbox.Graphics.GUI;
|
||||
using Sandbox.ModAPI;
|
||||
using SeamlessClientPlugin.Utilities;
|
||||
using SpaceEngineers.Game.GUI;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VRage;
|
||||
using VRage.Collections;
|
||||
using VRage.Game;
|
||||
using VRage.Game.Components;
|
||||
using VRage.Game.ModAPI;
|
||||
using VRage.GameServices;
|
||||
using VRage.Steam;
|
||||
using VRage.Network;
|
||||
using VRage.Utils;
|
||||
using VRageMath;
|
||||
using VRageRender;
|
||||
using VRageRender.Messages;
|
||||
using Game = Sandbox.Engine.Platform.Game;
|
||||
|
||||
namespace SeamlessClientPlugin.SeamlessTransfer
|
||||
namespace SeamlessClientPlugin.SeamlessTransfer;
|
||||
|
||||
public class SwitchServers
|
||||
{
|
||||
public class SwitchServers
|
||||
public SwitchServers(MyGameServerItem targetServer, MyObjectBuilder_World targetWorld)
|
||||
{
|
||||
public MyGameServerItem TargetServer { get; }
|
||||
public MyObjectBuilder_World TargetWorld { get; }
|
||||
this.TargetServer = targetServer;
|
||||
this.TargetWorld = targetWorld;
|
||||
|
||||
private string OldArmorSkin { get; set; } = string.Empty;
|
||||
//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;
|
||||
|
||||
|
||||
|
||||
public SwitchServers(MyGameServerItem TargetServer, MyObjectBuilder_World TargetWorld)
|
||||
MySandboxGame.Static.Invoke(delegate
|
||||
{
|
||||
this.TargetServer = TargetServer;
|
||||
this.TargetWorld = TargetWorld;
|
||||
//Set camera controller to fixed spectator
|
||||
MySession.Static.SetCameraController(MyCameraControllerEnum.SpectatorFixed);
|
||||
UnloadCurrentServer();
|
||||
SetNewMultiplayerClient();
|
||||
SeamlessClient.IsSwitching = false;
|
||||
}, "SeamlessClient");
|
||||
}
|
||||
|
||||
//ModLoader.DownloadNewMods(TargetWorld.Checkpoint.Mods);
|
||||
|
||||
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}");
|
||||
|
||||
public void BeginSwitch()
|
||||
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)
|
||||
{
|
||||
OldArmorSkin = MySession.Static.LocalHumanPlayer.BuildArmorSkin;
|
||||
MyPlayer.PlayerId playerId5 = new MyPlayer.PlayerId(item3.Key.GetClientId(), item3.Key.SerialId);
|
||||
|
||||
MySandboxGame.Static.Invoke(delegate
|
||||
SeamlessClient.TryShow($"ConnectedPlayer: {playerId5.ToString()}");
|
||||
if (savingPlayerId.HasValue && playerId5.SteamId == savingPlayerId.Value.SteamId)
|
||||
{
|
||||
//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 = Patches.TransportLayerConstructor.Invoke(new object[] { 2 });
|
||||
var SyncInstance = Patches.SyncLayerConstructor.Invoke(new object[] { LayerInstance });
|
||||
var instance = Patches.ClientConstructor.Invoke(new object[] { TargetServer, SyncInstance });
|
||||
|
||||
|
||||
MyMultiplayer.Static = Utility.CastToReflected(instance, Patches.ClientType);
|
||||
MyMultiplayer.Static.ExperimentalMode = true;
|
||||
|
||||
|
||||
|
||||
// Set the new SyncLayer to the MySession.Static.SyncLayer
|
||||
Patches.MySessionLayer.SetValue(MySession.Static, 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, VRage.Network.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);
|
||||
|
||||
|
||||
|
||||
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);
|
||||
Patches.GPSRegisterChat.Invoke(MySession.Static.Gpss, new object[] { MyMultiplayer.Static });
|
||||
|
||||
|
||||
// Allow the game to start proccessing incoming messages in the buffer
|
||||
MyMultiplayer.Static.StartProcessingClientMessages();
|
||||
|
||||
//Recreate all controls... Will fix weird gui/paint/crap
|
||||
MyGuiScreenHudSpace.Static.RecreateControls(true);
|
||||
//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);
|
||||
playerId5 = new MyPlayer.PlayerId(Sync.MyId, playerId5.SerialId);
|
||||
}
|
||||
SeamlessClient.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)
|
||||
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))
|
||||
{
|
||||
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)
|
||||
List<Vector3> value2 = null;
|
||||
if (TargetWorld.Checkpoint.AllPlayersColors != null && TargetWorld.Checkpoint.AllPlayersColors.Dictionary.TryGetValue(item3.Key, out value2))
|
||||
{
|
||||
playerId5 = new MyPlayer.PlayerId(Sync.MyId, playerId5.SerialId);
|
||||
myPlayer.SetBuildColorSlots(value2);
|
||||
}
|
||||
|
||||
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))
|
||||
else if (TargetWorld.Checkpoint.CharacterToolbar != null && TargetWorld.Checkpoint.CharacterToolbar.ColorMaskHSVList != null && TargetWorld.Checkpoint.CharacterToolbar.ColorMaskHSVList.Count > 0)
|
||||
{
|
||||
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);
|
||||
}
|
||||
myPlayer.SetBuildColorSlots(TargetWorld.Checkpoint.CharacterToolbar.ColorMaskHSVList);
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
private void SetWorldSettings()
|
||||
*/
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
//MyEntities.MemoryLimitAddFailureReset();
|
||||
|
||||
//Clear old list
|
||||
MySession.Static.PromotedUsers.Clear();
|
||||
MySession.Static.CreativeTools.Clear();
|
||||
Dictionary<ulong, AdminSettingsEnum> AdminSettingsList = (Dictionary<ulong, AdminSettingsEnum>)Patches.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)
|
||||
{
|
||||
SeamlessClient.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)
|
||||
{
|
||||
Patches.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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
MySession.Static.Gpss = new MyGpsCollection();
|
||||
MySession.Static.Gpss.LoadGpss(TargetWorld.Checkpoint);
|
||||
}
|
||||
|
||||
private void LoadConnectedClients()
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
Patches.LoadMembersFromWorld.Invoke(MySession.Static, new object[] { TargetWorld, MyMultiplayer.Static });
|
||||
|
||||
|
||||
//Re-Initilize Virtual clients
|
||||
object VirtualClientsValue = Patches.VirtualClients.GetValue(MySession.Static);
|
||||
Patches.InitVirtualClients.Invoke(VirtualClientsValue, null);
|
||||
|
||||
|
||||
//load online players
|
||||
LoadOnlinePlayers();
|
||||
|
||||
SeamlessClient.TryShow(
|
||||
$"An error occured while loading GPS points! You will have an empty gps list! \n {ex}");
|
||||
}
|
||||
|
||||
private void StartEntitySync()
|
||||
|
||||
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)
|
||||
{
|
||||
SeamlessClient.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)
|
||||
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)
|
||||
{
|
||||
MyLog.Default.WriteLine("ControlledObject was null, respawning character");
|
||||
//m_cameraAwaitingEntity = true;
|
||||
MyPlayerCollection.RequestLocalRespawn();
|
||||
adminSettingsEnum &= ~AdminSettingsEnum.IgnorePcu;
|
||||
adminSettingsEnum &= ~AdminSettingsEnum.KeepOriginalOwnershipOnPaste;
|
||||
}
|
||||
|
||||
//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();
|
||||
MySession.Static.RemoteAdminSettings[clientId] = adminSettingsEnum;
|
||||
if (!Sync.IsDedicated && clientId == Sync.MyId)
|
||||
MySession.Static.AdminSettings = adminSettingsEnum;
|
||||
|
||||
SeamlessClient.TryShow("OnlinePlayers: " + MySession.Static.Players.GetOnlinePlayers().Count);
|
||||
SeamlessClient.TryShow("Loading Complete!");
|
||||
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 MyMultiplayer_PendingReplicablesDone()
|
||||
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)
|
||||
{
|
||||
if (MySession.Static.VoxelMaps.Instances.Count > 0)
|
||||
{
|
||||
MySandboxGame.AreClipmapsReady = false;
|
||||
}
|
||||
MyMultiplayer.Static.PendingReplicablesDone -= MyMultiplayer_PendingReplicablesDone;
|
||||
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")
|
||||
|
||||
private void UpdateWorldGenerator()
|
||||
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>())
|
||||
{
|
||||
//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
|
||||
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);
|
||||
}
|
||||
generator.MarkEmptyArea(planet.PositionComp.GetPosition(), planet.m_planetInitValues.MaxRadius);
|
||||
}
|
||||
}
|
||||
|
||||
private void UnloadCurrentServer()
|
||||
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())
|
||||
{
|
||||
//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();
|
||||
if (ent is MyPlanet)
|
||||
continue;
|
||||
|
||||
ent.Close();
|
||||
}
|
||||
|
||||
private void RemoveOldEntities()
|
||||
{
|
||||
foreach (var ent in MyEntities.GetEntities())
|
||||
{
|
||||
if (ent is MyPlanet)
|
||||
continue;
|
||||
|
||||
ent.Close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
using HarmonyLib;
|
||||
using ProtoBuf;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using HarmonyLib;
|
||||
using Sandbox.Engine.Multiplayer;
|
||||
using Sandbox.Game;
|
||||
using Sandbox.Game.Gui;
|
||||
@@ -7,595 +8,456 @@ using Sandbox.Game.GUI;
|
||||
using Sandbox.Game.Localization;
|
||||
using Sandbox.Game.Multiplayer;
|
||||
using Sandbox.Game.SessionComponents;
|
||||
using Sandbox.Game.VoiceChat;
|
||||
using Sandbox.Game.World;
|
||||
using Sandbox.Graphics.GUI;
|
||||
using SeamlessClientPlugin.Messages;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VRage;
|
||||
using VRage.Audio;
|
||||
using VRage.Game;
|
||||
using VRage.Game.ModAPI;
|
||||
using VRage.Utils;
|
||||
using VRageMath;
|
||||
|
||||
namespace SeamlessClientPlugin.Utilities
|
||||
namespace SeamlessClientPlugin.Utilities;
|
||||
|
||||
public class OnlinePlayers
|
||||
{
|
||||
private static readonly Harmony Patcher = new("OnlinePlayersPatcher");
|
||||
public static List<OnlineServer> AllServers = [];
|
||||
public static int CurrentServer;
|
||||
private static string _currentServerName;
|
||||
|
||||
public class OnlinePlayers
|
||||
public static int TotalPlayerCount;
|
||||
public static int CurrentPlayerCount;
|
||||
|
||||
public static void Patch()
|
||||
{
|
||||
private static Harmony Patcher = new Harmony("OnlinePlayersPatcher");
|
||||
public static List<OnlineServer> AllServers = new List<OnlineServer>();
|
||||
public static int currentServer;
|
||||
private static string _currentServerName;
|
||||
|
||||
public static int totalPlayerCount = 0;
|
||||
public static int currentPlayerCount = 0;
|
||||
var recreateControls =
|
||||
typeof(MyGuiScreenPlayers).GetMethod("RecreateControls", BindingFlags.Instance | BindingFlags.Public);
|
||||
var updateCaption =
|
||||
typeof(MyGuiScreenPlayers).GetMethod("UpdateCaption", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
|
||||
private static MethodInfo m_UpdateCaption;
|
||||
private static MethodInfo m_RefreshMuteIcons;
|
||||
private static MethodInfo m_OnToggleMutePressed;
|
||||
private static MethodInfo m_AddCaption;
|
||||
Patcher.Patch(recreateControls, new HarmonyMethod(GetPatchMethod(nameof(RecreateControlsPrefix))));
|
||||
Patcher.Patch(updateCaption, new HarmonyMethod(GetPatchMethod(nameof(UpdateCaption))));
|
||||
//Patcher.Patch(recreateControls, postfix: new HarmonyMethod(GetPatchMethod(nameof(RecreateControlsSuffix))));
|
||||
}
|
||||
|
||||
|
||||
private static MethodInfo m_profileButton_ButtonClicked;
|
||||
private static MethodInfo m_promoteButton_ButtonClicked;
|
||||
private static MethodInfo m_demoteButton_ButtonClicked;
|
||||
private static MethodInfo m_kickButton_ButtonClicked;
|
||||
private static MethodInfo m_banButton_ButtonClicked;
|
||||
private static MethodInfo m_tradeButton_ButtonClicked;
|
||||
private static MethodInfo m_inviteButton_ButtonClicked;
|
||||
private static MethodInfo m_PlayersTable_ItemSelected;
|
||||
public static bool RecreateControlsPrefix(MyGuiScreenPlayers __instance, bool constructor)
|
||||
{
|
||||
if (MyMultiplayer.Static != null && MyMultiplayer.Static.IsLobby) return true;
|
||||
|
||||
private static MethodInfo m_UpdateButtonsEnabledState;
|
||||
|
||||
|
||||
private static FieldInfo m_playersTable;
|
||||
private static FieldInfo m_pings;
|
||||
private static FieldInfo m_PlayersTable;
|
||||
private static FieldInfo m_MaxPlayers;
|
||||
private static FieldInfo m_Warfare_timeRemainting_label;
|
||||
private static FieldInfo m_Warfare_timeRemainting_time;
|
||||
private static FieldInfo m_LastSelected;
|
||||
|
||||
/* Buttons */
|
||||
private static FieldInfo m_ProfileButton;
|
||||
private static FieldInfo m_PromoteButton;
|
||||
private static FieldInfo m_DemoteButton;
|
||||
private static FieldInfo m_KickButton;
|
||||
private static FieldInfo m_BanButton;
|
||||
private static FieldInfo m_TradeButton;
|
||||
private static FieldInfo m_InviteButton;
|
||||
|
||||
private static FieldInfo m_caption;
|
||||
private static FieldInfo m_LobbyTypeCombo;
|
||||
private static FieldInfo m_MaxPlayersSlider;
|
||||
|
||||
|
||||
|
||||
public static void Patch()
|
||||
try
|
||||
{
|
||||
m_playersTable = typeof(MyGuiScreenPlayers).GetField("m_playersTable", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_pings = typeof(MyGuiScreenPlayers).GetField("pings", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_UpdateCaption = typeof(MyGuiScreenPlayers).GetMethod("UpdateCaption", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_RefreshMuteIcons = typeof(MyGuiScreenPlayers).GetMethod("RefreshMuteIcons", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_OnToggleMutePressed = typeof(MyGuiScreenPlayers).GetMethod("OnToggleMutePressed", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
__instance.Controls.Clear();
|
||||
__instance.Elements.Clear();
|
||||
//__instance.Elements.Add(m_cl);
|
||||
__instance.FocusedControl = null;
|
||||
//__instance.m_firstUpdateServed = false;
|
||||
//__instance.m_screenCreation = DateTime.UtcNow;
|
||||
//__instance.m_gamepadHelpInitialized = false;
|
||||
//__instance.m_gamepadHelpLabel = null;
|
||||
|
||||
|
||||
m_profileButton_ButtonClicked = typeof(MyGuiScreenPlayers).GetMethod("profileButton_ButtonClicked", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_promoteButton_ButtonClicked = typeof(MyGuiScreenPlayers).GetMethod("promoteButton_ButtonClicked", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_demoteButton_ButtonClicked = typeof(MyGuiScreenPlayers).GetMethod("demoteButton_ButtonClicked", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_kickButton_ButtonClicked = typeof(MyGuiScreenPlayers).GetMethod("kickButton_ButtonClicked", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_banButton_ButtonClicked = typeof(MyGuiScreenPlayers).GetMethod("banButton_ButtonClicked", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_tradeButton_ButtonClicked = typeof(MyGuiScreenPlayers).GetMethod("tradeButton_ButtonClicked", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_inviteButton_ButtonClicked = typeof(MyGuiScreenPlayers).GetMethod("inviteButton_ButtonClicked", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_UpdateButtonsEnabledState = typeof(MyGuiScreenPlayers).GetMethod("UpdateButtonsEnabledState", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_PlayersTable_ItemSelected = typeof(MyGuiScreenPlayers).GetMethod("playersTable_ItemSelected", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
//m_SetColumnName = typeof(MyGuiScreenPlayers).GetMethod("SetColumnName", BindingFlags.Instance | BindingFlags.se);
|
||||
//SeamlessClient.TryShow("A");
|
||||
|
||||
|
||||
//__instance.RecreateControls(constructor);
|
||||
__instance.Size = new Vector2(0.937f, 0.913f);
|
||||
__instance.CloseButtonEnabled = true;
|
||||
|
||||
|
||||
//SeamlessClient.TryShow("A2");
|
||||
//MyCommonTexts.ScreenCaptionPlayers
|
||||
|
||||
m_caption = typeof(MyGuiScreenPlayers).GetField("m_caption", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_PlayersTable = typeof(MyGuiScreenPlayers).GetField("m_playersTable", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_MaxPlayers = typeof(MyGuiScreenPlayers).GetField("m_maxPlayers", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_Warfare_timeRemainting_label = typeof(MyGuiScreenPlayers).GetField("m_warfare_timeRemainting_label", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_Warfare_timeRemainting_time = typeof(MyGuiScreenPlayers).GetField("m_warfare_timeRemainting_time", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_LastSelected = typeof(MyGuiScreenPlayers).GetField("m_lastSelected", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_MaxPlayersSlider = typeof(MyGuiScreenPlayers).GetField("m_maxPlayersSlider", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
//MyStringId ID = MyStringId.GetOrCompute("Test Caption");
|
||||
__instance.m_caption =
|
||||
__instance.AddCaption(MyCommonTexts.ScreenCaptionPlayers, null, new Vector2(0f, 0.003f));
|
||||
|
||||
|
||||
/* Buttons */
|
||||
m_ProfileButton = typeof(MyGuiScreenPlayers).GetField("m_profileButton", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_PromoteButton = typeof(MyGuiScreenPlayers).GetField("m_promoteButton", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_DemoteButton = typeof(MyGuiScreenPlayers).GetField("m_demoteButton", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_KickButton = typeof(MyGuiScreenPlayers).GetField("m_kickButton", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_BanButton = typeof(MyGuiScreenPlayers).GetField("m_banButton", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_TradeButton = typeof(MyGuiScreenPlayers).GetField("m_tradeButton", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_InviteButton = typeof(MyGuiScreenPlayers).GetField("m_inviteButton", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_LobbyTypeCombo = typeof(MyGuiScreenPlayers).GetField("m_lobbyTypeCombo", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
m_AddCaption = typeof(MyGuiScreenPlayers).GetMethod("AddCaption", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(MyStringId), typeof(Vector4?), typeof(Vector2?), typeof(float) }, null);
|
||||
const float startX = -0.435f;
|
||||
const float startY = -0.36f;
|
||||
|
||||
var myGuiControlSeparatorList = new MyGuiControlSeparatorList();
|
||||
myGuiControlSeparatorList.AddHorizontal(new Vector2(startX, startY), .83f);
|
||||
|
||||
|
||||
var start = new Vector2(startX, 0.358f);
|
||||
myGuiControlSeparatorList.AddHorizontal(start, 0.728f);
|
||||
myGuiControlSeparatorList.AddHorizontal(new Vector2(startX, 0.05f), 0.17f);
|
||||
__instance.Controls.Add(myGuiControlSeparatorList);
|
||||
|
||||
|
||||
var spacing = new Vector2(0f, 0.057f);
|
||||
var vector3 = new Vector2(startX, startY + 0.035f);
|
||||
|
||||
//SeamlessClient.TryShow("B");
|
||||
|
||||
var mProfileButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null,
|
||||
MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null,
|
||||
MyTexts.Get(MyCommonTexts.ScreenPlayers_Profile));
|
||||
mProfileButton.ButtonClicked += __instance.profileButton_ButtonClicked;
|
||||
__instance.Controls.Add(mProfileButton);
|
||||
vector3 += spacing;
|
||||
__instance.m_profileButton = mProfileButton;
|
||||
|
||||
|
||||
var mPromoteButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null,
|
||||
MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null,
|
||||
MyTexts.Get(MyCommonTexts.ScreenPlayers_Promote));
|
||||
mPromoteButton.ButtonClicked += __instance.promoteButton_ButtonClicked;
|
||||
__instance.Controls.Add(mPromoteButton);
|
||||
vector3 += spacing;
|
||||
__instance.m_promoteButton = mPromoteButton;
|
||||
|
||||
var mDemoteButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null,
|
||||
MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null,
|
||||
MyTexts.Get(MyCommonTexts.ScreenPlayers_Demote));
|
||||
mDemoteButton.ButtonClicked += __instance.demoteButton_ButtonClicked;
|
||||
__instance.Controls.Add(mDemoteButton);
|
||||
vector3 += spacing;
|
||||
__instance.m_demoteButton = mDemoteButton;
|
||||
|
||||
|
||||
MethodInfo recreateControls = typeof(MyGuiScreenPlayers).GetMethod("RecreateControls", BindingFlags.Instance | BindingFlags.Public);
|
||||
MethodInfo updateCaption = typeof(MyGuiScreenPlayers).GetMethod("UpdateCaption", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
var mKickButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null,
|
||||
MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null,
|
||||
MyTexts.Get(MyCommonTexts.ScreenPlayers_Kick));
|
||||
mKickButton.ButtonClicked += __instance.kickButton_ButtonClicked;
|
||||
__instance.Controls.Add(mKickButton);
|
||||
vector3 += spacing;
|
||||
__instance.m_kickButton = mKickButton;
|
||||
|
||||
var mBanButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null,
|
||||
MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null,
|
||||
MyTexts.Get(MyCommonTexts.ScreenPlayers_Ban));
|
||||
mBanButton.ButtonClicked += __instance.banButton_ButtonClicked;
|
||||
__instance.Controls.Add(mBanButton);
|
||||
vector3 += spacing;
|
||||
__instance.m_banButton = mBanButton;
|
||||
|
||||
|
||||
var mTradeButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null,
|
||||
MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null,
|
||||
MyTexts.Get(MySpaceTexts.PlayersScreen_TradeBtn));
|
||||
mTradeButton.SetTooltip(MyTexts.GetString(MySpaceTexts.PlayersScreen_TradeBtn_TTP));
|
||||
mTradeButton.ButtonClicked += __instance.tradeButton_ButtonClicked;
|
||||
__instance.Controls.Add(mTradeButton);
|
||||
__instance.m_tradeButton = mTradeButton;
|
||||
|
||||
|
||||
Patcher.Patch(recreateControls, prefix: new HarmonyMethod(GetPatchMethod(nameof(RecreateControlsPrefix))));
|
||||
Patcher.Patch(updateCaption, prefix: new HarmonyMethod(GetPatchMethod(nameof(UpdateCaption))));
|
||||
//Patcher.Patch(recreateControls, postfix: new HarmonyMethod(GetPatchMethod(nameof(RecreateControlsSuffix))));
|
||||
}
|
||||
//SeamlessClient.TryShow("C");
|
||||
|
||||
var vector4 = vector3 + new Vector2(-0.002f, mTradeButton.Size.Y + 0.03f);
|
||||
__instance.m_lobbyTypeCombo = new MyGuiControlCombobox(vector4, null, null, null, 3);
|
||||
|
||||
var vector5 = vector4 + new Vector2(0f, 0.05f);
|
||||
vector5 += new Vector2(0f, 0.03f);
|
||||
__instance.m_maxPlayers = Sync.IsServer ? MyMultiplayerLobby.MAX_PLAYERS : 16;
|
||||
__instance.m_maxPlayersSlider = new MyGuiControlSlider(vector5, 2f, Math.Max(__instance.m_maxPlayers, 3), 0.177f,
|
||||
Sync.IsServer ? MySession.Static.MaxPlayers : MyMultiplayer.Static.MemberLimit, null, null, 1, 0.8f, 0f,
|
||||
"White", null, MyGuiControlSliderStyleEnum.Default, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP,
|
||||
true);
|
||||
|
||||
|
||||
public static bool RecreateControlsPrefix(MyGuiScreenPlayers __instance, bool constructor)
|
||||
{
|
||||
var mInviteButton = new MyGuiControlButton(new Vector2(startX, 0.25000026f),
|
||||
MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP,
|
||||
null, MyTexts.Get(MyCommonTexts.ScreenPlayers_Invite));
|
||||
mInviteButton.ButtonClicked += __instance.inviteButton_ButtonClicked;
|
||||
__instance.Controls.Add(mInviteButton);
|
||||
__instance.m_inviteButton = mInviteButton;
|
||||
|
||||
if (MyMultiplayer.Static != null && MyMultiplayer.Static.IsLobby)
|
||||
var vector6 = new Vector2(-startX - 0.034f, startY + 0.033f);
|
||||
var size = new Vector2(0.66f, 1.2f);
|
||||
var num2 = 18;
|
||||
var num3 = 0f;
|
||||
|
||||
|
||||
//SeamlessClient.TryShow("D");
|
||||
var component = MySession.Static.GetComponent<MySessionComponentMatch>();
|
||||
if (component.IsEnabled)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
__instance.Controls.Clear();
|
||||
__instance.Elements.Clear();
|
||||
//__instance.Elements.Add(m_cl);
|
||||
__instance.FocusedControl = null;
|
||||
//__instance.m_firstUpdateServed = false;
|
||||
//__instance.m_screenCreation = DateTime.UtcNow;
|
||||
//__instance.m_gamepadHelpInitialized = false;
|
||||
//__instance.m_gamepadHelpLabel = null;
|
||||
|
||||
|
||||
//SeamlessClient.TryShow("A");
|
||||
|
||||
|
||||
//__instance.RecreateControls(constructor);
|
||||
__instance.Size = new Vector2(0.937f, 0.913f);
|
||||
__instance.CloseButtonEnabled = true;
|
||||
|
||||
|
||||
//SeamlessClient.TryShow("A2");
|
||||
//MyCommonTexts.ScreenCaptionPlayers
|
||||
|
||||
//MyStringId ID = MyStringId.GetOrCompute("Test Caption");
|
||||
m_caption.SetValue(__instance, m_AddCaption.Invoke(__instance, new object[4] { MyCommonTexts.ScreenCaptionPlayers, null, new Vector2(0f, 0.003f), 0.8f }));
|
||||
|
||||
|
||||
float StartX = -0.435f;
|
||||
float StartY = -0.36f;
|
||||
|
||||
MyGuiControlSeparatorList myGuiControlSeparatorList = new MyGuiControlSeparatorList();
|
||||
myGuiControlSeparatorList.AddHorizontal(new Vector2(StartX, StartY), .83f);
|
||||
|
||||
|
||||
|
||||
|
||||
Vector2 start = new Vector2(StartX, 0.358f);
|
||||
myGuiControlSeparatorList.AddHorizontal(start, 0.728f);
|
||||
myGuiControlSeparatorList.AddHorizontal(new Vector2(StartX, 0.05f), 0.17f);
|
||||
__instance.Controls.Add(myGuiControlSeparatorList);
|
||||
|
||||
|
||||
Vector2 Spacing = new Vector2(0f, 0.057f);
|
||||
Vector2 vector3 = new Vector2(StartX, StartY + 0.035f);
|
||||
|
||||
//SeamlessClient.TryShow("B");
|
||||
|
||||
MyGuiControlButton m_profileButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null, MyTexts.Get(MyCommonTexts.ScreenPlayers_Profile));
|
||||
m_profileButton.ButtonClicked += delegate (MyGuiControlButton obj) { m_profileButton_ButtonClicked.Invoke(__instance, new object[] { obj }); };
|
||||
__instance.Controls.Add(m_profileButton);
|
||||
vector3 += Spacing;
|
||||
m_ProfileButton.SetValue(__instance, m_profileButton);
|
||||
|
||||
|
||||
MyGuiControlButton m_promoteButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null, MyTexts.Get(MyCommonTexts.ScreenPlayers_Promote));
|
||||
m_promoteButton.ButtonClicked += delegate (MyGuiControlButton obj) { m_promoteButton_ButtonClicked.Invoke(__instance, new object[] { obj }); };
|
||||
__instance.Controls.Add(m_promoteButton);
|
||||
vector3 += Spacing;
|
||||
m_PromoteButton.SetValue(__instance, m_promoteButton);
|
||||
|
||||
MyGuiControlButton m_demoteButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null, MyTexts.Get(MyCommonTexts.ScreenPlayers_Demote));
|
||||
m_demoteButton.ButtonClicked += delegate (MyGuiControlButton obj) { m_demoteButton_ButtonClicked.Invoke(__instance, new object[] { obj }); };
|
||||
__instance.Controls.Add(m_demoteButton);
|
||||
vector3 += Spacing;
|
||||
m_DemoteButton.SetValue(__instance, m_demoteButton);
|
||||
|
||||
|
||||
MyGuiControlButton m_kickButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null, MyTexts.Get(MyCommonTexts.ScreenPlayers_Kick));
|
||||
m_kickButton.ButtonClicked += delegate (MyGuiControlButton obj) { m_kickButton_ButtonClicked.Invoke(__instance, new object[] { obj }); };
|
||||
__instance.Controls.Add(m_kickButton);
|
||||
vector3 += Spacing;
|
||||
m_KickButton.SetValue(__instance, m_kickButton);
|
||||
|
||||
MyGuiControlButton m_banButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null, MyTexts.Get(MyCommonTexts.ScreenPlayers_Ban));
|
||||
m_banButton.ButtonClicked += delegate (MyGuiControlButton obj) { m_banButton_ButtonClicked.Invoke(__instance, new object[] { obj }); };
|
||||
__instance.Controls.Add(m_banButton);
|
||||
vector3 += Spacing;
|
||||
m_BanButton.SetValue(__instance, m_banButton);
|
||||
|
||||
|
||||
MyGuiControlButton m_tradeButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null, MyTexts.Get(MySpaceTexts.PlayersScreen_TradeBtn));
|
||||
m_tradeButton.SetTooltip(MyTexts.GetString(MySpaceTexts.PlayersScreen_TradeBtn_TTP));
|
||||
m_tradeButton.ButtonClicked += delegate (MyGuiControlButton obj) { m_tradeButton_ButtonClicked.Invoke(__instance, new object[] { obj }); };
|
||||
__instance.Controls.Add(m_tradeButton);
|
||||
m_TradeButton.SetValue(__instance, m_tradeButton);
|
||||
|
||||
|
||||
|
||||
//SeamlessClient.TryShow("C");
|
||||
|
||||
Vector2 vector4 = vector3 + new Vector2(-0.002f, m_tradeButton.Size.Y + 0.03f);
|
||||
MyGuiControlCombobox m_lobbyTypeCombo = new MyGuiControlCombobox(vector4, null, null, null, 3);
|
||||
m_LobbyTypeCombo.SetValue(__instance, m_lobbyTypeCombo);
|
||||
|
||||
Vector2 vector5 = vector4 + new Vector2(0f, 0.05f);
|
||||
vector5 += new Vector2(0f, 0.03f);
|
||||
int m_maxPlayers = (Sync.IsServer ? MyMultiplayerLobby.MAX_PLAYERS : 16);
|
||||
m_MaxPlayers.SetValue(__instance, m_maxPlayers);
|
||||
MyGuiControlSlider m_maxPlayersSlider = new MyGuiControlSlider(vector5, 2f, Math.Max(m_maxPlayers, 3), 0.177f, Sync.IsServer ? MySession.Static.MaxPlayers : MyMultiplayer.Static.MemberLimit, null, null, 1, 0.8f, 0f, "White", null, MyGuiControlSliderStyleEnum.Default, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, intValue: true);
|
||||
m_MaxPlayersSlider.SetValue(__instance, m_maxPlayersSlider);
|
||||
|
||||
|
||||
MyGuiControlButton m_inviteButton = new MyGuiControlButton(new Vector2(StartX, 0.25000026f), MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null, MyTexts.Get(MyCommonTexts.ScreenPlayers_Invite));
|
||||
m_inviteButton.ButtonClicked += delegate (MyGuiControlButton obj) { m_inviteButton_ButtonClicked.Invoke(__instance, new object[] { obj }); };
|
||||
__instance.Controls.Add(m_inviteButton);
|
||||
m_InviteButton.SetValue(__instance, m_inviteButton);
|
||||
|
||||
Vector2 vector6 = new Vector2(-StartX - 0.034f, StartY + 0.033f);
|
||||
Vector2 size = new Vector2(0.66f, 1.2f);
|
||||
int num2 = 18;
|
||||
float num3 = 0f;
|
||||
|
||||
|
||||
//SeamlessClient.TryShow("D");
|
||||
MySessionComponentMatch component = MySession.Static.GetComponent<MySessionComponentMatch>();
|
||||
if (component.IsEnabled)
|
||||
{
|
||||
Vector2 vector7 = __instance.GetPositionAbsolute() + vector6 + new Vector2(0f - size.X, 0f);
|
||||
MyGuiControlLabel m_warfare_timeRemainting_label = new MyGuiControlLabel(vector6 - new Vector2(size.X, 0f));
|
||||
m_warfare_timeRemainting_label.Text = MyTexts.GetString(MySpaceTexts.WarfareCounter_TimeRemaining).ToString() + ": ";
|
||||
__instance.Controls.Add(m_warfare_timeRemainting_label);
|
||||
m_Warfare_timeRemainting_label.SetValue(__instance, m_warfare_timeRemainting_label);
|
||||
|
||||
|
||||
TimeSpan timeSpan = TimeSpan.FromMinutes(component.RemainingMinutes);
|
||||
MyGuiControlLabel m_warfare_timeRemainting_time = new MyGuiControlLabel(vector6 - new Vector2(size.X, 0f) + new Vector2(m_warfare_timeRemainting_label.Size.X, 0f));
|
||||
m_warfare_timeRemainting_time.Text = timeSpan.ToString((timeSpan.TotalHours >= 1.0) ? "hh\\:mm\\:ss" : "mm\\:ss");
|
||||
__instance.Controls.Add(m_warfare_timeRemainting_time);
|
||||
m_Warfare_timeRemainting_time.SetValue(__instance, m_warfare_timeRemainting_label);
|
||||
|
||||
float num4 = 0.09f;
|
||||
float num5 = size.X / 3f - 2f * num3;
|
||||
int num6 = 0;
|
||||
MyFaction[] allFactions = MySession.Static.Factions.GetAllFactions();
|
||||
foreach (MyFaction myFaction in allFactions)
|
||||
var vector7 = __instance.GetPositionAbsolute() + vector6 + new Vector2(0f - size.X, 0f);
|
||||
var mWarfareTimeRemaintingLabel = new MyGuiControlLabel(vector6 - new Vector2(size.X, 0f))
|
||||
{
|
||||
if ((myFaction.Name.StartsWith("Red") || myFaction.Name.StartsWith("Green") || myFaction.Name.StartsWith("Blue")) && myFaction.Name.EndsWith("Faction"))
|
||||
{
|
||||
__instance.Controls.Add(new MyGuiScreenPlayersWarfareTeamScoreTable(vector7 + new Vector2((float)num6 * (num5 + num3), m_warfare_timeRemainting_label.Size.Y + num3), num5, num4, myFaction.Name, myFaction.FactionIcon.Value.String, MyTexts.GetString(MySpaceTexts.WarfareCounter_EscapePod), myFaction.FactionId, drawOwnBackground: false, drawBorders: true, myFaction.IsMember(MySession.Static.LocalHumanPlayer.Identity.IdentityId)));
|
||||
num6++;
|
||||
}
|
||||
}
|
||||
vector6.Y += m_warfare_timeRemainting_label.Size.Y + num4 + num3 * 2f;
|
||||
num2 -= 3;
|
||||
}
|
||||
//SeamlessClient.TryShow("E");
|
||||
Text = $"{MyTexts.GetString(MySpaceTexts.WarfareCounter_TimeRemaining)}: "
|
||||
};
|
||||
__instance.Controls.Add(mWarfareTimeRemaintingLabel);
|
||||
__instance.m_warfare_timeRemainting_label = mWarfareTimeRemaintingLabel;
|
||||
|
||||
MyGuiControlTable m_playersTable = new MyGuiControlTable
|
||||
|
||||
var timeSpan = TimeSpan.FromMinutes(component.RemainingMinutes);
|
||||
var mWarfareTimeRemaintingTime = new MyGuiControlLabel(vector6 - new Vector2(size.X, 0f) +
|
||||
new Vector2(
|
||||
mWarfareTimeRemaintingLabel.Size.X,
|
||||
0f))
|
||||
{
|
||||
Position = vector6,
|
||||
Size = size,
|
||||
OriginAlign = MyGuiDrawAlignEnum.HORISONTAL_RIGHT_AND_VERTICAL_TOP,
|
||||
ColumnsCount = 7
|
||||
Text = timeSpan.ToString(timeSpan.TotalHours >= 1.0 ? @"hh\:mm\:ss" : "mm\\:ss")
|
||||
};
|
||||
__instance.Controls.Add(mWarfareTimeRemaintingTime);
|
||||
__instance.m_warfare_timeRemainting_time = mWarfareTimeRemaintingTime;
|
||||
|
||||
m_PlayersTable.SetValue(__instance, m_playersTable);
|
||||
var num4 = 0.09f;
|
||||
var num5 = size.X / 3f - 2f * num3;
|
||||
var num6 = 0;
|
||||
var allFactions = MySession.Static.Factions.GetAllFactions();
|
||||
foreach (var myFaction in allFactions)
|
||||
if ((myFaction.Name.StartsWith("Red") || myFaction.Name.StartsWith("Green") ||
|
||||
myFaction.Name.StartsWith("Blue")) && myFaction.Name.EndsWith("Faction"))
|
||||
{
|
||||
__instance.Controls.Add(new MyGuiScreenPlayersWarfareTeamScoreTable(
|
||||
vector7 + new Vector2(num6 * (num5 + num3), mWarfareTimeRemaintingLabel.Size.Y + num3),
|
||||
num5, num4, myFaction.Name, myFaction.FactionIcon.Value.String,
|
||||
MyTexts.GetString(MySpaceTexts.WarfareCounter_EscapePod), myFaction.FactionId, false, true,
|
||||
myFaction.IsMember(MySession.Static.LocalHumanPlayer.Identity.IdentityId)));
|
||||
num6++;
|
||||
}
|
||||
|
||||
vector6.Y += mWarfareTimeRemaintingLabel.Size.Y + num4 + num3 * 2f;
|
||||
num2 -= 3;
|
||||
}
|
||||
//SeamlessClient.TryShow("E");
|
||||
|
||||
__instance.m_playersTable = new MyGuiControlTable
|
||||
{
|
||||
Position = vector6,
|
||||
Size = size,
|
||||
OriginAlign = MyGuiDrawAlignEnum.HORISONTAL_RIGHT_AND_VERTICAL_TOP,
|
||||
ColumnsCount = 7,
|
||||
//SeamlessClient.TryShow("F");
|
||||
|
||||
m_playersTable.GamepadHelpTextId = MySpaceTexts.PlayersScreen_Help_PlayersList;
|
||||
m_playersTable.VisibleRowsCount = num2;
|
||||
float PlayerName = 0.2f;
|
||||
float Rank = 0.1f;
|
||||
float Ping = 0.08f;
|
||||
float Muted = 0.1f;
|
||||
float SteamIcon = 0.04f;
|
||||
float Server = 0.20f;
|
||||
float FactionName = 1f - PlayerName - Rank - Muted - Ping - SteamIcon - Server;
|
||||
|
||||
m_playersTable.SetCustomColumnWidths(new float[7]
|
||||
{
|
||||
SteamIcon,
|
||||
PlayerName,
|
||||
FactionName,
|
||||
Rank,
|
||||
Ping,
|
||||
Muted,
|
||||
Server,
|
||||
});
|
||||
|
||||
//SeamlessClient.TryShow("G");
|
||||
|
||||
m_playersTable.SetColumnComparison(1, (MyGuiControlTable.Cell a, MyGuiControlTable.Cell b) => a.Text.CompareToIgnoreCase(b.Text));
|
||||
m_playersTable.SetColumnName(1, MyTexts.Get(MyCommonTexts.ScreenPlayers_PlayerName));
|
||||
m_playersTable.SetColumnComparison(2, (MyGuiControlTable.Cell a, MyGuiControlTable.Cell b) => a.Text.CompareToIgnoreCase(b.Text));
|
||||
m_playersTable.SetColumnName(2, MyTexts.Get(MyCommonTexts.ScreenPlayers_FactionName));
|
||||
m_playersTable.SetColumnName(5, new StringBuilder(MyTexts.GetString(MyCommonTexts.ScreenPlayers_Muted)));
|
||||
m_playersTable.SetColumnComparison(3, GameAdminCompare);
|
||||
m_playersTable.SetColumnName(3, MyTexts.Get(MyCommonTexts.ScreenPlayers_Rank));
|
||||
m_playersTable.SetColumnComparison(4, GamePingCompare);
|
||||
m_playersTable.SetColumnName(4, MyTexts.Get(MyCommonTexts.ScreenPlayers_Ping));
|
||||
|
||||
|
||||
StringBuilder colName = new StringBuilder("Server");
|
||||
m_playersTable.SetColumnName(6, colName);
|
||||
m_playersTable.SetColumnComparison(6, (MyGuiControlTable.Cell a, MyGuiControlTable.Cell b) => a.Text.CompareToIgnoreCase(b.Text));
|
||||
|
||||
//SeamlessClient.TryShow("H");
|
||||
|
||||
|
||||
//m_PlayersTable_ItemSelected
|
||||
m_playersTable.ItemSelected += delegate (MyGuiControlTable i, MyGuiControlTable.EventArgs x) { m_PlayersTable_ItemSelected.Invoke(__instance, new object[] { i, x }); };
|
||||
m_playersTable.UpdateTableSortHelpText();
|
||||
__instance.Controls.Add(m_playersTable);
|
||||
|
||||
|
||||
string thisServerName = "thisServer";
|
||||
totalPlayerCount = 0;
|
||||
foreach(var server in AllServers)
|
||||
{
|
||||
|
||||
string servername = server.ServerName;
|
||||
if (server.ServerID == currentServer)
|
||||
{
|
||||
thisServerName = servername;
|
||||
_currentServerName = servername;
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var player in server.Players)
|
||||
{
|
||||
|
||||
AddPlayer(__instance, player.SteamID, servername, player.PlayerName, player.IdentityID);
|
||||
totalPlayerCount++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
currentPlayerCount = 0;
|
||||
foreach (MyPlayer onlinePlayer in Sync.Players.GetOnlinePlayers())
|
||||
{
|
||||
if (onlinePlayer.Id.SerialId != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
currentPlayerCount++;
|
||||
totalPlayerCount++;
|
||||
AddPlayer(__instance, onlinePlayer.Id.SteamId, thisServerName);
|
||||
}
|
||||
|
||||
//SeamlessClient.TryShow("I");
|
||||
|
||||
ulong m_lastSelected = (ulong)m_LastSelected.GetValue(__instance);
|
||||
if (m_lastSelected != 0L)
|
||||
{
|
||||
MyGuiControlTable.Row row2 = m_playersTable.Find((MyGuiControlTable.Row r) => (ulong)r.UserData == m_lastSelected);
|
||||
if (row2 != null)
|
||||
{
|
||||
m_playersTable.SelectedRow = row2;
|
||||
}
|
||||
}
|
||||
|
||||
m_UpdateButtonsEnabledState.Invoke(__instance, null);
|
||||
//UpdateButtonsEnabledState();
|
||||
|
||||
m_UpdateCaption.Invoke(__instance, null);
|
||||
|
||||
|
||||
|
||||
|
||||
Vector2 minSizeGui = MyGuiControlButton.GetVisualStyle(MyGuiControlButtonStyleEnum.Default).NormalTexture.MinSizeGui;
|
||||
MyGuiControlLabel myGuiControlLabel = new MyGuiControlLabel(new Vector2(start.X, start.Y + minSizeGui.Y / 2f));
|
||||
myGuiControlLabel.Name = MyGuiScreenBase.GAMEPAD_HELP_LABEL_NAME;
|
||||
__instance.Controls.Add(myGuiControlLabel);
|
||||
__instance.GamepadHelpTextId = MySpaceTexts.PlayersScreen_Help_Screen;
|
||||
__instance.FocusedControl = m_playersTable;
|
||||
|
||||
//SeamlessClient.TryShow("J");
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SeamlessClient.TryShow(ex.ToString());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool AddPlayer(MyGuiScreenPlayers __instance, ulong userId, string server, string playername = null, long playerId = 0)
|
||||
{
|
||||
MyGuiControlTable table = (MyGuiControlTable)m_playersTable.GetValue(__instance);
|
||||
Dictionary<ulong, short> pings = (Dictionary<ulong, short>)m_pings.GetValue(__instance);
|
||||
|
||||
if(playername == null)
|
||||
playername = MyMultiplayer.Static.GetMemberName(userId);
|
||||
|
||||
if (string.IsNullOrEmpty(playername))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
MyGuiControlTable.Row row = new MyGuiControlTable.Row(userId);
|
||||
string memberServiceName = MyMultiplayer.Static.GetMemberServiceName(userId);
|
||||
StringBuilder text = new StringBuilder();
|
||||
|
||||
|
||||
|
||||
MyGuiHighlightTexture? icon = new MyGuiHighlightTexture
|
||||
{
|
||||
Normal = "Textures\\GUI\\Icons\\Services\\" + memberServiceName + ".png",
|
||||
Highlight = "Textures\\GUI\\Icons\\Services\\" + memberServiceName + ".png",
|
||||
SizePx = new Vector2(24f, 24f)
|
||||
GamepadHelpTextId = MySpaceTexts.PlayersScreen_Help_PlayersList,
|
||||
VisibleRowsCount = num2
|
||||
};
|
||||
row.AddCell(new MyGuiControlTable.Cell(text, null, memberServiceName, Color.White, icon, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_CENTER));
|
||||
row.AddCell(new MyGuiControlTable.Cell(new StringBuilder(playername), playername));
|
||||
|
||||
const float playerNameWidth = 0.2f;
|
||||
const float rankWidth = 0.1f;
|
||||
const float pingWidth = 0.08f;
|
||||
const float mutedWidth = 0.1f;
|
||||
const float steamIconWidth = 0.04f;
|
||||
const float serverWidth = 0.20f;
|
||||
const float factionNameWidth = 1f - playerNameWidth - rankWidth - mutedWidth - pingWidth - steamIconWidth - serverWidth;
|
||||
|
||||
__instance.m_playersTable.SetCustomColumnWidths([
|
||||
steamIconWidth,
|
||||
playerNameWidth,
|
||||
factionNameWidth,
|
||||
rankWidth,
|
||||
pingWidth,
|
||||
mutedWidth,
|
||||
serverWidth
|
||||
]);
|
||||
|
||||
//SeamlessClient.TryShow("G");
|
||||
|
||||
__instance.m_playersTable.SetColumnComparison(1, (a, b) => a.Text.CompareToIgnoreCase(b.Text));
|
||||
__instance.m_playersTable.SetColumnName(1, MyTexts.Get(MyCommonTexts.ScreenPlayers_PlayerName));
|
||||
__instance.m_playersTable.SetColumnComparison(2, (a, b) => a.Text.CompareToIgnoreCase(b.Text));
|
||||
__instance.m_playersTable.SetColumnName(2, MyTexts.Get(MyCommonTexts.ScreenPlayers_FactionName));
|
||||
__instance.m_playersTable.SetColumnName(5, new StringBuilder(MyTexts.GetString(MyCommonTexts.ScreenPlayers_Muted)));
|
||||
__instance.m_playersTable.SetColumnComparison(3, GameAdminCompare);
|
||||
__instance.m_playersTable.SetColumnName(3, MyTexts.Get(MyCommonTexts.ScreenPlayers_Rank));
|
||||
__instance.m_playersTable.SetColumnComparison(4, GamePingCompare);
|
||||
__instance.m_playersTable.SetColumnName(4, MyTexts.Get(MyCommonTexts.ScreenPlayers_Ping));
|
||||
|
||||
|
||||
if(playerId == 0)
|
||||
playerId = Sync.Players.TryGetIdentityId(userId);
|
||||
var colName = new StringBuilder("Server");
|
||||
__instance.m_playersTable.SetColumnName(6, colName);
|
||||
__instance.m_playersTable.SetColumnComparison(6, (a, b) => a.Text.CompareToIgnoreCase(b.Text));
|
||||
|
||||
//SeamlessClient.TryShow("H");
|
||||
|
||||
|
||||
//m_PlayersTable_ItemSelected
|
||||
__instance.m_playersTable.ItemSelected += __instance.playersTable_ItemSelected;
|
||||
__instance.m_playersTable.UpdateTableSortHelpText();
|
||||
__instance.Controls.Add(__instance.m_playersTable);
|
||||
|
||||
MyFaction playerFaction = MySession.Static.Factions.GetPlayerFaction(playerId);
|
||||
string text2 = "";
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
if (playerFaction != null)
|
||||
|
||||
var thisServerName = "<this>";
|
||||
TotalPlayerCount = 0;
|
||||
foreach (var server in AllServers)
|
||||
{
|
||||
text2 += playerFaction.Name;
|
||||
text2 = text2 + " | " + playername;
|
||||
foreach (KeyValuePair<long, MyFactionMember> member in playerFaction.Members)
|
||||
var servername = server.ServerName;
|
||||
if (server.ServerId == CurrentServer)
|
||||
{
|
||||
if ((member.Value.IsLeader || member.Value.IsFounder) && MySession.Static.Players.TryGetPlayerId(member.Value.PlayerId, out var result) && MySession.Static.Players.TryGetPlayerById(result, out var player))
|
||||
{
|
||||
text2 = text2 + " | " + player.DisplayName;
|
||||
break;
|
||||
}
|
||||
thisServerName = servername;
|
||||
_currentServerName = servername;
|
||||
continue;
|
||||
}
|
||||
stringBuilder.Append(MyStatControlText.SubstituteTexts(playerFaction.Name));
|
||||
if (playerFaction.IsLeader(playerId))
|
||||
|
||||
foreach (var player in server.Players)
|
||||
{
|
||||
stringBuilder.Append(" (").Append((object)MyTexts.Get(MyCommonTexts.Leader)).Append(")");
|
||||
}
|
||||
if (!string.IsNullOrEmpty(playerFaction.Tag))
|
||||
{
|
||||
stringBuilder.Insert(0, "[" + playerFaction.Tag + "] ");
|
||||
AddPlayer(__instance, player.SteamId, servername, player.PlayerName, player.IdentityId);
|
||||
TotalPlayerCount++;
|
||||
}
|
||||
}
|
||||
row.AddCell(new MyGuiControlTable.Cell(stringBuilder, null, text2));
|
||||
StringBuilder stringBuilder2 = new StringBuilder();
|
||||
MyPromoteLevel userPromoteLevel = MySession.Static.GetUserPromoteLevel(userId);
|
||||
for (int i = 0; i < (int)userPromoteLevel; i++)
|
||||
|
||||
|
||||
CurrentPlayerCount = 0;
|
||||
foreach (var onlinePlayer in Sync.Players.GetOnlinePlayers())
|
||||
{
|
||||
stringBuilder2.Append("*");
|
||||
if (onlinePlayer.Id.SerialId != 0) continue;
|
||||
|
||||
CurrentPlayerCount++;
|
||||
TotalPlayerCount++;
|
||||
AddPlayer(__instance, onlinePlayer.Id.SteamId, thisServerName);
|
||||
}
|
||||
row.AddCell(new MyGuiControlTable.Cell(stringBuilder2));
|
||||
if (pings.ContainsKey(userId))
|
||||
|
||||
//SeamlessClient.TryShow("I");
|
||||
|
||||
if (__instance.m_lastSelected != 0L)
|
||||
{
|
||||
row.AddCell(new MyGuiControlTable.Cell(new StringBuilder(pings[userId].ToString())));
|
||||
var row2 = __instance.m_playersTable.Find(r => (ulong)r.UserData == __instance.m_lastSelected);
|
||||
if (row2 != null) __instance.m_playersTable.SelectedRow = row2;
|
||||
}
|
||||
else
|
||||
{
|
||||
row.AddCell(new MyGuiControlTable.Cell(new StringBuilder("----")));
|
||||
}
|
||||
MyGuiControlTable.Cell cell = new MyGuiControlTable.Cell(new StringBuilder(""));
|
||||
row.AddCell(cell);
|
||||
if (userId != Sync.MyId)
|
||||
{
|
||||
MyGuiControlButton myGuiControlButton = new MyGuiControlButton();
|
||||
myGuiControlButton.CustomStyle = m_buttonSizeStyleUnMuted;
|
||||
myGuiControlButton.Size = new Vector2(0.03f, 0.04f);
|
||||
myGuiControlButton.CueEnum = GuiSounds.None;
|
||||
|
||||
__instance.UpdateButtonsEnabledState();
|
||||
//UpdateButtonsEnabledState();
|
||||
|
||||
__instance.UpdateCaption();
|
||||
|
||||
|
||||
Action<MyGuiControlButton> btnClicked = delegate (MyGuiControlButton b)
|
||||
{
|
||||
m_OnToggleMutePressed.Invoke(__instance, new object[] { b });
|
||||
};
|
||||
var minSizeGui = MyGuiControlButton.GetVisualStyle(MyGuiControlButtonStyleEnum.Default).NormalTexture
|
||||
.MinSizeGui;
|
||||
var myGuiControlLabel = new MyGuiControlLabel(new Vector2(start.X, start.Y + minSizeGui.Y / 2f))
|
||||
{
|
||||
Name = MyGuiScreenBase.GAMEPAD_HELP_LABEL_NAME
|
||||
};
|
||||
__instance.Controls.Add(myGuiControlLabel);
|
||||
__instance.GamepadHelpTextId = MySpaceTexts.PlayersScreen_Help_Screen;
|
||||
__instance.FocusedControl = __instance.m_playersTable;
|
||||
|
||||
|
||||
|
||||
myGuiControlButton.ButtonClicked += btnClicked;
|
||||
myGuiControlButton.UserData = userId;
|
||||
cell.Control = myGuiControlButton;
|
||||
table.Controls.Add(myGuiControlButton);
|
||||
m_RefreshMuteIcons.Invoke(__instance, null);
|
||||
//RefreshMuteIcons();
|
||||
}
|
||||
table.Add(row);
|
||||
m_UpdateCaption.Invoke(__instance, null);
|
||||
|
||||
row.AddCell(new MyGuiControlTable.Cell(new StringBuilder(server), "Server Name"));
|
||||
|
||||
return false;
|
||||
//SeamlessClient.TryShow("J");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SeamlessClient.TryShow(ex.ToString());
|
||||
__instance.Controls.Clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool UpdateCaption(MyGuiScreenPlayers __instance)
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool AddPlayer(MyGuiScreenPlayers instance, ulong userId, string server, string playerName = null,
|
||||
long playerId = 0)
|
||||
{
|
||||
var table = instance.m_playersTable;
|
||||
var pings = instance.pings;
|
||||
|
||||
playerName ??= MyMultiplayer.Static.GetMemberName(userId);
|
||||
|
||||
if (string.IsNullOrEmpty(playerName)) return false;
|
||||
|
||||
|
||||
var row = new MyGuiControlTable.Row(userId);
|
||||
var memberServiceName = MyMultiplayer.Static.GetMemberServiceName(userId);
|
||||
var text = new StringBuilder();
|
||||
|
||||
|
||||
MyGuiHighlightTexture? icon = new MyGuiHighlightTexture
|
||||
{
|
||||
if (MyMultiplayer.Static != null && MyMultiplayer.Static.IsLobby)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
MyGuiControlLabel mM_caption = (MyGuiControlLabel)m_caption.GetValue(__instance);
|
||||
MyGuiControlTable mm_playersTable = (MyGuiControlTable)m_playersTable.GetValue(__instance);
|
||||
|
||||
|
||||
|
||||
//string s = $"{MyTexts.Get(MyCommonTexts.ScreenCaptionServerName).ToString()} - SectorPlayers: ({ mm_playersTable.RowsCount} / {MySession.Static.MaxPlayers}) TotalPlayers: ( {5} / 200 )";
|
||||
|
||||
mM_caption.Text = string.Concat("Server: ", _currentServerName, " - ", "Innstance Players", " (", currentPlayerCount, " / ", MySession.Static.MaxPlayers, ") TotalPlayers: ( ", totalPlayerCount, " )");
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int GamePingCompare(MyGuiControlTable.Cell a, MyGuiControlTable.Cell b)
|
||||
{
|
||||
if (!int.TryParse(a.Text.ToString(), out var result))
|
||||
{
|
||||
result = -1;
|
||||
}
|
||||
if (!int.TryParse(b.Text.ToString(), out var result2))
|
||||
{
|
||||
result2 = -1;
|
||||
}
|
||||
return result.CompareTo(result2);
|
||||
}
|
||||
private static int GameAdminCompare(MyGuiControlTable.Cell a, MyGuiControlTable.Cell b)
|
||||
{
|
||||
ulong steamId = (ulong)a.Row.UserData;
|
||||
ulong steamId2 = (ulong)b.Row.UserData;
|
||||
int userPromoteLevel = (int)MySession.Static.GetUserPromoteLevel(steamId);
|
||||
int userPromoteLevel2 = (int)MySession.Static.GetUserPromoteLevel(steamId2);
|
||||
return userPromoteLevel.CompareTo(userPromoteLevel2);
|
||||
}
|
||||
|
||||
|
||||
private static readonly MyGuiControlButton.StyleDefinition m_buttonSizeStyleUnMuted = new MyGuiControlButton.StyleDefinition
|
||||
{
|
||||
NormalFont = "White",
|
||||
HighlightFont = "White",
|
||||
NormalTexture = MyGuiConstants.TEXTURE_HUD_VOICE_CHAT,
|
||||
HighlightTexture = MyGuiConstants.TEXTURE_HUD_VOICE_CHAT
|
||||
Normal = $@"Textures\GUI\Icons\Services\{memberServiceName}.png",
|
||||
Highlight = $@"Textures\GUI\Icons\Services\{memberServiceName}.png",
|
||||
SizePx = new Vector2(24f, 24f)
|
||||
};
|
||||
row.AddCell(new MyGuiControlTable.Cell(text, null, memberServiceName, Color.White, icon,
|
||||
MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_CENTER));
|
||||
row.AddCell(new MyGuiControlTable.Cell(new StringBuilder(playerName), playerName));
|
||||
|
||||
private static MethodInfo GetPatchMethod(string v)
|
||||
|
||||
if (playerId == 0)
|
||||
playerId = Sync.Players.TryGetIdentityId(userId);
|
||||
|
||||
|
||||
var playerFaction = MySession.Static.Factions.GetPlayerFaction(playerId);
|
||||
var text2 = "";
|
||||
var stringBuilder = new StringBuilder();
|
||||
if (playerFaction != null)
|
||||
{
|
||||
return typeof(OnlinePlayers).GetMethod(v, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
text2 += playerFaction.Name;
|
||||
text2 = $"{text2} | {playerName}";
|
||||
foreach (var member in playerFaction.Members)
|
||||
if ((member.Value.IsLeader || member.Value.IsFounder) &&
|
||||
MySession.Static.Players.TryGetPlayerId(member.Value.PlayerId, out var result) &&
|
||||
MySession.Static.Players.TryGetPlayerById(result, out var player))
|
||||
{
|
||||
text2 = $"{text2} | {player.DisplayName}";
|
||||
break;
|
||||
}
|
||||
|
||||
stringBuilder.Append(MyStatControlText.SubstituteTexts(playerFaction.Name));
|
||||
if (playerFaction.IsLeader(playerId))
|
||||
stringBuilder.Append(" (").Append((object)MyTexts.Get(MyCommonTexts.Leader)).Append(")");
|
||||
if (!string.IsNullOrEmpty(playerFaction.Tag)) stringBuilder.Insert(0, $"[{playerFaction.Tag}] ");
|
||||
}
|
||||
|
||||
row.AddCell(new MyGuiControlTable.Cell(stringBuilder, null, text2));
|
||||
var stringBuilder2 = new StringBuilder();
|
||||
var userPromoteLevel = MySession.Static.GetUserPromoteLevel(userId);
|
||||
for (var i = 0; i < (int)userPromoteLevel; i++) stringBuilder2.Append('*');
|
||||
row.AddCell(new MyGuiControlTable.Cell(stringBuilder2));
|
||||
if (pings.TryGetValue(userId, out var ping))
|
||||
row.AddCell(new MyGuiControlTable.Cell(new StringBuilder(ping.ToString())));
|
||||
else
|
||||
row.AddCell(new MyGuiControlTable.Cell(new StringBuilder("----")));
|
||||
var cell = new MyGuiControlTable.Cell(new StringBuilder(""));
|
||||
row.AddCell(cell);
|
||||
if (userId != Sync.MyId)
|
||||
{
|
||||
var myGuiControlButton = new MyGuiControlButton
|
||||
{
|
||||
CustomStyle = instance.m_buttonSizeStyleSilent,
|
||||
Size = new Vector2(0.03f, 0.04f),
|
||||
CueEnum = GuiSounds.None
|
||||
};
|
||||
|
||||
myGuiControlButton.ButtonClicked += instance.OnToggleMutePressed;
|
||||
myGuiControlButton.UserData = userId;
|
||||
cell.Control = myGuiControlButton;
|
||||
table.Controls.Add(myGuiControlButton);
|
||||
instance.RefreshMuteIcons();
|
||||
//RefreshMuteIcons();
|
||||
}
|
||||
|
||||
table.Add(row);
|
||||
instance.UpdateCaption();
|
||||
|
||||
row.AddCell(new MyGuiControlTable.Cell(new StringBuilder(server), "Server Name"));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool UpdateCaption(MyGuiScreenPlayers __instance)
|
||||
{
|
||||
if (MyMultiplayer.Static != null && MyMultiplayer.Static.IsLobby) return true;
|
||||
|
||||
//string s = $"{MyTexts.Get(MyCommonTexts.ScreenCaptionServerName).ToString()} - SectorPlayers: ({ mm_playersTable.RowsCount} / {MySession.Static.MaxPlayers}) TotalPlayers: ( {5} / 200 )";
|
||||
|
||||
__instance.m_caption.Text =
|
||||
$"Server: {_currentServerName} - Innstance Players ({CurrentPlayerCount} / {MySession.Static.MaxPlayers}) TotalPlayers: ({TotalPlayerCount})";
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int GamePingCompare(MyGuiControlTable.Cell a, MyGuiControlTable.Cell b)
|
||||
{
|
||||
if (!int.TryParse(a.Text.ToString(), out var result)) result = -1;
|
||||
if (!int.TryParse(b.Text.ToString(), out var result2)) result2 = -1;
|
||||
return result.CompareTo(result2);
|
||||
}
|
||||
|
||||
private static int GameAdminCompare(MyGuiControlTable.Cell a, MyGuiControlTable.Cell b)
|
||||
{
|
||||
var steamId = (ulong)a.Row.UserData;
|
||||
var steamId2 = (ulong)b.Row.UserData;
|
||||
var userPromoteLevel = (int)MySession.Static.GetUserPromoteLevel(steamId);
|
||||
var userPromoteLevel2 = (int)MySession.Static.GetUserPromoteLevel(steamId2);
|
||||
return userPromoteLevel.CompareTo(userPromoteLevel2);
|
||||
}
|
||||
|
||||
private static MethodInfo GetPatchMethod(string v)
|
||||
{
|
||||
return typeof(OnlinePlayers).GetMethod(v, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
}
|
||||
}
|
@@ -1,439 +1,297 @@
|
||||
using HarmonyLib;
|
||||
using Sandbox.Engine.Analytics;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using HarmonyLib;
|
||||
using Sandbox.Engine.Multiplayer;
|
||||
using Sandbox.Engine.Networking;
|
||||
using Sandbox.Game;
|
||||
using Sandbox.Game.Entities;
|
||||
using Sandbox.Game.Gui;
|
||||
using Sandbox.Game.Multiplayer;
|
||||
using Sandbox.Game.World;
|
||||
using Sandbox.Game.World.Generator;
|
||||
using Sandbox.Graphics;
|
||||
using Sandbox.Graphics.GUI;
|
||||
using Sandbox.ModAPI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VRage;
|
||||
using VRage.Collections;
|
||||
using VRage.FileSystem;
|
||||
using VRage.Game;
|
||||
using VRage.Game.Entity;
|
||||
using VRage.GameServices;
|
||||
using VRage.Network;
|
||||
using VRage.Utils;
|
||||
using VRageMath;
|
||||
|
||||
namespace SeamlessClientPlugin.SeamlessTransfer
|
||||
namespace SeamlessClientPlugin.Utilities;
|
||||
|
||||
public static class Patches
|
||||
{
|
||||
public static class Patches
|
||||
/* Harmony Patcher */
|
||||
private static readonly Harmony Patcher = new("SeamlessClientPatcher");
|
||||
|
||||
/* WorldGenerator */
|
||||
public static MethodInfo UnloadProceduralWorldGenerator { get; private set; }
|
||||
|
||||
|
||||
public static event EventHandler<JoinResultMsg> OnJoinEvent;
|
||||
|
||||
public static void GetPatches()
|
||||
{
|
||||
/* All internal classes Types */
|
||||
public static readonly Type ClientType = Type.GetType("Sandbox.Engine.Multiplayer.MyMultiplayerClient, Sandbox.Game");
|
||||
public static readonly Type SyncLayerType = Type.GetType("Sandbox.Game.Multiplayer.MySyncLayer, Sandbox.Game");
|
||||
public static readonly Type MyTransportLayerType = Type.GetType("Sandbox.Engine.Multiplayer.MyTransportLayer, Sandbox.Game");
|
||||
public static readonly Type MySessionType = Type.GetType("Sandbox.Game.World.MySession, Sandbox.Game");
|
||||
public static readonly Type VirtualClientsType = Type.GetType("Sandbox.Engine.Multiplayer.MyVirtualClients, Sandbox.Game");
|
||||
public static readonly Type GUIScreenChat = Type.GetType("Sandbox.Game.Gui.MyGuiScreenChat, Sandbox.Game");
|
||||
public static readonly Type MyMultiplayerClientBase = Type.GetType("Sandbox.Engine.Multiplayer.MyMultiplayerClientBase, Sandbox.Game");
|
||||
public static readonly Type MySteamServerDiscovery = Type.GetType("VRage.Steam.MySteamServerDiscovery, Vrage.Steam");
|
||||
//Get reflected values and store them
|
||||
|
||||
/* Harmony Patcher */
|
||||
private static Harmony Patcher = new Harmony("SeamlessClientPatcher");
|
||||
/* Get Methods */
|
||||
var onJoin = GetMethod(typeof(MyMultiplayerClient), "OnUserJoined", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
var loadingAction = GetMethod(typeof(MySessionLoader), "LoadMultiplayerSession",
|
||||
BindingFlags.Public | BindingFlags.Static);
|
||||
UnloadProceduralWorldGenerator = GetMethod(typeof(MyProceduralWorldGenerator), "UnloadData",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
|
||||
/* Static Contructors */
|
||||
public static ConstructorInfo ClientConstructor { get; private set; }
|
||||
public static ConstructorInfo SyncLayerConstructor { get; private set; }
|
||||
public static ConstructorInfo TransportLayerConstructor { get; private set; }
|
||||
public static ConstructorInfo MySessionConstructor { get; private set; }
|
||||
public static ConstructorInfo MyMultiplayerClientBaseConstructor { get; private set; }
|
||||
//MethodInfo ConnectToServer = GetMethod(typeof(MyGameService), "ConnectToServer", BindingFlags.Static | BindingFlags.Public);
|
||||
var loadingScreenDraw = GetMethod(typeof(MyGuiScreenLoading), "DrawInternal",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
|
||||
|
||||
/* Static FieldInfos and PropertyInfos */
|
||||
public static PropertyInfo MySessionLayer { get; private set; }
|
||||
public static FieldInfo VirtualClients { get; private set; }
|
||||
public static FieldInfo AdminSettings { get; private set; }
|
||||
public static FieldInfo RemoteAdminSettings { get; private set; }
|
||||
public static FieldInfo MPlayerGPSCollection { get; private set; }
|
||||
//Test patches
|
||||
//MethodInfo SetPlayerDed = GetMethod(typeof(MyPlayerCollection), "SetPlayerDeadInternal", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
|
||||
/* Static MethodInfos */
|
||||
public static MethodInfo InitVirtualClients { get; private set; }
|
||||
public static MethodInfo LoadPlayerInternal { get; private set; }
|
||||
public static MethodInfo LoadMembersFromWorld { get; private set; }
|
||||
public static MethodInfo LoadMultiplayer { get; private set; }
|
||||
public static MethodInfo GPSRegisterChat { get; private set; }
|
||||
|
||||
public static MethodInfo SendPlayerData;
|
||||
Patcher.Patch(loadingScreenDraw, new HarmonyMethod(GetPatchMethod(nameof(DrawInternal))));
|
||||
Patcher.Patch(onJoin, postfix: new HarmonyMethod(GetPatchMethod(nameof(OnUserJoined))));
|
||||
Patcher.Patch(loadingAction, new HarmonyMethod(GetPatchMethod(nameof(LoadMultiplayerSession))));
|
||||
//Patcher.Patch(SetPlayerDed, prefix: new HarmonyMethod(GetPatchMethod(nameof(SetPlayerDeadInternal))));
|
||||
}
|
||||
|
||||
|
||||
public static event EventHandler<JoinResultMsg> OnJoinEvent;
|
||||
private static MethodInfo GetPatchMethod(string v)
|
||||
{
|
||||
return typeof(Patches).GetMethod(v, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* WorldGenerator */
|
||||
public static MethodInfo UnloadProceduralWorldGenerator;
|
||||
private static void OnUserJoined(ref JoinResultMsg msg)
|
||||
{
|
||||
if (msg.JoinResult == JoinResult.OK)
|
||||
//SeamlessClient.TryShow("User Joined! Result: " + msg.JoinResult.ToString());
|
||||
//Invoke the switch event
|
||||
OnJoinEvent?.Invoke(null, msg);
|
||||
}
|
||||
|
||||
|
||||
private static bool OnConnectToServer(MyGameServerItem server, Action<JoinResult> onDone)
|
||||
{
|
||||
if (SeamlessClient.IsSwitching)
|
||||
return false;
|
||||
|
||||
private static FieldInfo MBuffer;
|
||||
|
||||
public static void GetPatches()
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Patch Utils */
|
||||
|
||||
private static MethodInfo GetMethod(Type type, string methodName, BindingFlags flags)
|
||||
{
|
||||
var foundMethod = type.GetMethod(methodName, flags);
|
||||
|
||||
if (foundMethod == null)
|
||||
throw new NullReferenceException($"Method for {methodName} is null!");
|
||||
|
||||
|
||||
return foundMethod;
|
||||
}
|
||||
|
||||
private static FieldInfo GetField(Type type, string fieldName, BindingFlags flags)
|
||||
{
|
||||
var foundField = type.GetField(fieldName, flags);
|
||||
|
||||
if (foundField == null)
|
||||
throw new NullReferenceException($"Field for {fieldName} is null!");
|
||||
|
||||
|
||||
return foundField;
|
||||
}
|
||||
|
||||
private static PropertyInfo GetProperty(Type type, string propertyName, BindingFlags flags)
|
||||
{
|
||||
var foundProperty = type.GetProperty(propertyName, flags);
|
||||
|
||||
if (foundProperty == null)
|
||||
throw new NullReferenceException($"Property for {propertyName} is null!");
|
||||
|
||||
|
||||
return foundProperty;
|
||||
}
|
||||
|
||||
private static ConstructorInfo GetConstructor(Type type, BindingFlags flags, Type[] types)
|
||||
{
|
||||
var foundConstructor = type.GetConstructor(flags, null, types, null);
|
||||
|
||||
if (foundConstructor == null)
|
||||
throw new NullReferenceException($"Contructor for {type.Name} is null!");
|
||||
|
||||
|
||||
return foundConstructor;
|
||||
}
|
||||
|
||||
|
||||
#region LoadingScreen
|
||||
|
||||
/* Loading Screen Stuff */
|
||||
|
||||
private static string _loadingScreenTexture;
|
||||
private static string _serverName;
|
||||
|
||||
private static bool LoadMultiplayerSession(MyObjectBuilder_World world, MyMultiplayerBase multiplayerSession)
|
||||
{
|
||||
//
|
||||
|
||||
|
||||
MyLog.Default.WriteLine("LoadSession() - Start");
|
||||
if (!MyWorkshop.CheckLocalModsAllowed(world.Checkpoint.Mods, false))
|
||||
{
|
||||
//Get reflected values and store them
|
||||
|
||||
|
||||
|
||||
|
||||
/* Get Constructors */
|
||||
ClientConstructor = GetConstructor(ClientType, BindingFlags.Instance | BindingFlags.NonPublic, new Type[2] { typeof(MyGameServerItem), SyncLayerType });
|
||||
SyncLayerConstructor = GetConstructor(SyncLayerType, BindingFlags.Instance | BindingFlags.NonPublic, new Type[1] { MyTransportLayerType });
|
||||
TransportLayerConstructor = GetConstructor(MyTransportLayerType, BindingFlags.Instance | BindingFlags.Public, new Type[1] { typeof(int) });
|
||||
MySessionConstructor = GetConstructor(MySessionType, BindingFlags.Instance | BindingFlags.NonPublic, new Type[2] { typeof(MySyncLayer), typeof(bool) });
|
||||
MyMultiplayerClientBaseConstructor = GetConstructor(MyMultiplayerClientBase, BindingFlags.Instance | BindingFlags.NonPublic, new Type[] { typeof(MySyncLayer) });
|
||||
|
||||
|
||||
/* Get Fields and Properties */
|
||||
MySessionLayer = GetProperty(typeof(MySession), "SyncLayer", BindingFlags.Instance | BindingFlags.Public);
|
||||
VirtualClients = GetField(typeof(MySession), "VirtualClients", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
AdminSettings = GetField(typeof(MySession), "m_adminSettings", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
RemoteAdminSettings = GetField(typeof(MySession), "m_remoteAdminSettings", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
MPlayerGPSCollection = GetField(typeof(MyPlayerCollection), "m_players", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
MBuffer = GetField(MyTransportLayerType, "m_buffer", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
|
||||
/* Get Methods */
|
||||
MethodInfo OnJoin = GetMethod(ClientType, "OnUserJoined", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
MethodInfo LoadingAction = GetMethod(typeof(MySessionLoader), "LoadMultiplayerSession", BindingFlags.Public | BindingFlags.Static);
|
||||
InitVirtualClients = GetMethod(VirtualClientsType, "Init", BindingFlags.Instance | BindingFlags.Public);
|
||||
LoadPlayerInternal = GetMethod(typeof(MyPlayerCollection), "LoadPlayerInternal", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
LoadMembersFromWorld = GetMethod(typeof(MySession), "LoadMembersFromWorld", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
LoadMultiplayer = GetMethod(typeof(MySession), "LoadMultiplayer", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
SendPlayerData = GetMethod(ClientType, "SendPlayerData", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
UnloadProceduralWorldGenerator = GetMethod(typeof(MyProceduralWorldGenerator), "UnloadData", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
GPSRegisterChat = GetMethod(typeof(MyGpsCollection), "RegisterChat", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
|
||||
|
||||
//MethodInfo ConnectToServer = GetMethod(typeof(MyGameService), "ConnectToServer", BindingFlags.Static | BindingFlags.Public);
|
||||
MethodInfo LoadingScreenDraw = GetMethod(typeof(MyGuiScreenLoading), "DrawInternal", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
|
||||
|
||||
//Test patches
|
||||
//MethodInfo SetPlayerDed = GetMethod(typeof(MyPlayerCollection), "SetPlayerDeadInternal", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Patcher.Patch(LoadingScreenDraw, prefix: new HarmonyMethod(GetPatchMethod(nameof(DrawInternal))));
|
||||
Patcher.Patch(OnJoin, postfix: new HarmonyMethod(GetPatchMethod(nameof(OnUserJoined))));
|
||||
Patcher.Patch(LoadingAction, prefix: new HarmonyMethod(GetPatchMethod(nameof(LoadMultiplayerSession))));
|
||||
//Patcher.Patch(SetPlayerDed, prefix: new HarmonyMethod(GetPatchMethod(nameof(SetPlayerDeadInternal))));
|
||||
|
||||
|
||||
|
||||
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateMessageBox(
|
||||
messageCaption: MyTexts.Get(MyCommonTexts.MessageBoxCaptionError),
|
||||
messageText: MyTexts.Get(MyCommonTexts.DialogTextLocalModsDisabledInMultiplayer)));
|
||||
MyLog.Default.WriteLine("LoadSession() - End");
|
||||
return false;
|
||||
}
|
||||
|
||||
MyLog.Default.WriteLine("Seamless Downloading mods!");
|
||||
|
||||
|
||||
|
||||
private static MethodInfo GetPatchMethod(string v)
|
||||
MyWorkshop.DownloadModsAsync(world.Checkpoint.Mods, result =>
|
||||
{
|
||||
return typeof(Patches).GetMethod(v, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#region LoadingScreen
|
||||
|
||||
/* Loading Screen Stuff */
|
||||
|
||||
private static string LoadingScreenTexture = null;
|
||||
private static string ServerName;
|
||||
|
||||
private static bool LoadMultiplayerSession(MyObjectBuilder_World world, MyMultiplayerBase multiplayerSession)
|
||||
{
|
||||
|
||||
//
|
||||
|
||||
|
||||
|
||||
MyLog.Default.WriteLine("LoadSession() - Start");
|
||||
if (!MyWorkshop.CheckLocalModsAllowed(world.Checkpoint.Mods, allowLocalMods: false))
|
||||
if (result == MyGameServiceCallResult.OK)
|
||||
{
|
||||
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateMessageBox(MyMessageBoxStyleEnum.Error, MyMessageBoxButtonsType.OK, messageCaption: MyTexts.Get(MyCommonTexts.MessageBoxCaptionError), messageText: MyTexts.Get(MyCommonTexts.DialogTextLocalModsDisabledInMultiplayer)));
|
||||
MyLog.Default.WriteLine("LoadSession() - End");
|
||||
return false;
|
||||
MyScreenManager.CloseAllScreensNowExcept(null);
|
||||
MyGuiSandbox.Update(16);
|
||||
if (MySession.Static != null)
|
||||
{
|
||||
MySession.Static.Unload();
|
||||
MySession.Static = null;
|
||||
}
|
||||
|
||||
_serverName = multiplayerSession.HostName;
|
||||
GetCustomLoadingScreenPath(world.Checkpoint.Mods, out _loadingScreenTexture);
|
||||
|
||||
|
||||
MySessionLoader.StartLoading(delegate
|
||||
{
|
||||
MySession.LoadMultiplayer(world, multiplayerSession);
|
||||
});
|
||||
}
|
||||
|
||||
MyLog.Default.WriteLine("Seamless Downloading mods!");
|
||||
|
||||
|
||||
|
||||
MyWorkshop.DownloadModsAsync(world.Checkpoint.Mods, delegate (bool success)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
MyScreenManager.CloseAllScreensNowExcept(null);
|
||||
MyGuiSandbox.Update(16);
|
||||
if (MySession.Static != null)
|
||||
{
|
||||
MySession.Static.Unload();
|
||||
MySession.Static = null;
|
||||
}
|
||||
|
||||
ServerName = multiplayerSession.HostName;
|
||||
GetCustomLoadingScreenPath(world.Checkpoint.Mods, out LoadingScreenTexture);
|
||||
|
||||
|
||||
MySessionLoader.StartLoading(delegate
|
||||
{
|
||||
|
||||
LoadMultiplayer.Invoke(null, new object[] { world, multiplayerSession });
|
||||
//MySession.LoadMultiplayer(world, multiplayerSession);
|
||||
}, null, null, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
multiplayerSession.Dispose();
|
||||
MySessionLoader.UnloadAndExitToMenu();
|
||||
if (MyGameService.IsOnline)
|
||||
{
|
||||
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateMessageBox(MyMessageBoxStyleEnum.Error, MyMessageBoxButtonsType.OK, messageCaption: MyTexts.Get(MyCommonTexts.MessageBoxCaptionError), messageText: MyTexts.Get(MyCommonTexts.DialogTextDownloadModsFailed)));
|
||||
}
|
||||
else
|
||||
{
|
||||
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateMessageBox(MyMessageBoxStyleEnum.Error, MyMessageBoxButtonsType.OK, messageCaption: MyTexts.Get(MyCommonTexts.MessageBoxCaptionError), messageText: new StringBuilder(string.Format(MyTexts.GetString(MyCommonTexts.DialogTextDownloadModsFailedSteamOffline), MySession.GameServiceName))));
|
||||
}
|
||||
}
|
||||
MyLog.Default.WriteLine("LoadSession() - End");
|
||||
}, delegate
|
||||
else
|
||||
{
|
||||
multiplayerSession.Dispose();
|
||||
MySessionLoader.UnloadAndExitToMenu();
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool DrawInternal(MyGuiScreenLoading __instance)
|
||||
{
|
||||
|
||||
//If we dont have a custom loading screen texture, do not do the special crap below
|
||||
if (string.IsNullOrEmpty(LoadingScreenTexture))
|
||||
return true;
|
||||
|
||||
|
||||
float m_transitionAlpha = (float)typeof(MyGuiScreenBase).GetField("m_transitionAlpha", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(__instance);
|
||||
string m_font = "LoadingScreen";
|
||||
//MyGuiControlMultilineText m_multiTextControl = (MyGuiControlMultilineText)typeof(MyGuiScreenLoading).GetField("m_multiTextControl", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(__instance);
|
||||
|
||||
|
||||
Color color = new Color(255, 255, 255, 250);
|
||||
color.A = (byte)((float)(int)color.A * m_transitionAlpha);
|
||||
Rectangle fullscreenRectangle = MyGuiManager.GetFullscreenRectangle();
|
||||
MyGuiManager.DrawSpriteBatch("Textures\\GUI\\Blank.dds", fullscreenRectangle, Color.Black, false, true);
|
||||
Rectangle outRect;
|
||||
MyGuiManager.GetSafeHeightFullScreenPictureSize(MyGuiConstants.LOADING_BACKGROUND_TEXTURE_REAL_SIZE, out outRect);
|
||||
MyGuiManager.DrawSpriteBatch(LoadingScreenTexture, outRect, new Color(new Vector4(1f, 1f, 1f, m_transitionAlpha)), true, true);
|
||||
MyGuiManager.DrawSpriteBatch("Textures\\Gui\\Screens\\screen_background_fade.dds", outRect, new Color(new Vector4(1f, 1f, 1f, m_transitionAlpha)), true, true);
|
||||
|
||||
//MyGuiSandbox.DrawGameLogoHandler(m_transitionAlpha, MyGuiManager.ComputeFullscreenGuiCoordinate(MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, 44, 68));
|
||||
|
||||
string LoadScreen = $"Loading into {ServerName}! Please wait!";
|
||||
|
||||
|
||||
MyGuiManager.DrawString(m_font, LoadScreen, new Vector2(0.5f, 0.95f), MyGuiSandbox.GetDefaultTextScaleWithLanguage() * 1.1f, new Color(MyGuiConstants.LOADING_PLEASE_WAIT_COLOR * m_transitionAlpha), MyGuiDrawAlignEnum.HORISONTAL_CENTER_AND_VERTICAL_BOTTOM);
|
||||
|
||||
MyGuiManager.DrawString(m_font, "Nexus & SeamlessClient Made by: Casimir", new Vector2(0.95f, 0.95f), MyGuiSandbox.GetDefaultTextScaleWithLanguage() * 1.1f, new Color(MyGuiConstants.LOADING_PLEASE_WAIT_COLOR * m_transitionAlpha), MyGuiDrawAlignEnum.HORISONTAL_CENTER_AND_VERTICAL_BOTTOM);
|
||||
|
||||
/*
|
||||
if (string.IsNullOrEmpty(m_customTextFromConstructor))
|
||||
{
|
||||
string font = m_font;
|
||||
Vector2 positionAbsoluteBottomLeft = m_multiTextControl.GetPositionAbsoluteBottomLeft();
|
||||
Vector2 textSize = m_multiTextControl.TextSize;
|
||||
Vector2 normalizedCoord = positionAbsoluteBottomLeft + new Vector2((m_multiTextControl.Size.X - textSize.X) * 0.5f + 0.025f, 0.025f);
|
||||
MyGuiManager.DrawString(font, m_authorWithDash.ToString(), normalizedCoord, MyGuiSandbox.GetDefaultTextScaleWithLanguage());
|
||||
if (MyGameService.IsOnline)
|
||||
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateMessageBox(
|
||||
messageCaption: MyTexts.Get(MyCommonTexts.MessageBoxCaptionError),
|
||||
messageText: MyTexts.Get(MyCommonTexts.DialogTextDownloadModsFailed)));
|
||||
else
|
||||
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateMessageBox(
|
||||
messageCaption: MyTexts.Get(MyCommonTexts.MessageBoxCaptionError),
|
||||
messageText: new StringBuilder(string.Format(
|
||||
MyTexts.GetString(MyCommonTexts.DialogTextDownloadModsFailedSteamOffline),
|
||||
MySession.GameServiceName))));
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
//m_multiTextControl.Draw(1f, 1f);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static bool GetCustomLoadingScreenPath(List<MyObjectBuilder_Checkpoint.ModItem> Mods, out string File)
|
||||
MyLog.Default.WriteLine("LoadSession() - End");
|
||||
}, () =>
|
||||
{
|
||||
File = null;
|
||||
string WorkshopDir = MyFileSystem.ModsPath;
|
||||
List<string> backgrounds = new List<string>();
|
||||
Random r = new Random(DateTime.Now.Millisecond);
|
||||
SeamlessClient.TryShow(WorkshopDir);
|
||||
try
|
||||
multiplayerSession.Dispose();
|
||||
MySessionLoader.UnloadAndExitToMenu();
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool DrawInternal(MyGuiScreenLoading __instance)
|
||||
{
|
||||
//If we dont have a custom loading screen texture, do not do the special crap below
|
||||
if (string.IsNullOrEmpty(_loadingScreenTexture))
|
||||
return true;
|
||||
//MyGuiControlMultilineText m_multiTextControl = (MyGuiControlMultilineText)typeof(MyGuiScreenLoading).GetField("m_multiTextControl", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(__instance);
|
||||
|
||||
|
||||
var color = new Color(255, 255, 255, 250);
|
||||
color.A = (byte)(color.A * __instance.m_transitionAlpha);
|
||||
var fullscreenRectangle = MyGuiManager.GetFullscreenRectangle();
|
||||
MyGuiManager.DrawSpriteBatch(@"Textures\GUI\Blank.dds", fullscreenRectangle, Color.Black, false, true);
|
||||
MyGuiManager.GetSafeHeightFullScreenPictureSize(MyGuiConstants.LOADING_BACKGROUND_TEXTURE_REAL_SIZE,
|
||||
out var outRect);
|
||||
MyGuiManager.DrawSpriteBatch(_loadingScreenTexture, outRect,
|
||||
new Color(new Vector4(1f, 1f, 1f, __instance.m_transitionAlpha)), true, true);
|
||||
MyGuiManager.DrawSpriteBatch(@"Textures\Gui\Screens\screen_background_fade.dds", outRect,
|
||||
new Color(new Vector4(1f, 1f, 1f, __instance.m_transitionAlpha)), true, true);
|
||||
|
||||
//MyGuiSandbox.DrawGameLogoHandler(m_transitionAlpha, MyGuiManager.ComputeFullscreenGuiCoordinate(MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, 44, 68));
|
||||
|
||||
var loadScreen = $"Loading into {_serverName}! Please wait!";
|
||||
|
||||
|
||||
MyGuiManager.DrawString(MyFontEnum.LoadingScreen, loadScreen, new Vector2(0.5f, 0.95f),
|
||||
MyGuiSandbox.GetDefaultTextScaleWithLanguage() * 1.1f,
|
||||
new Color(MyGuiConstants.LOADING_PLEASE_WAIT_COLOR * __instance.m_transitionAlpha),
|
||||
MyGuiDrawAlignEnum.HORISONTAL_CENTER_AND_VERTICAL_BOTTOM);
|
||||
|
||||
MyGuiManager.DrawString(MyFontEnum.LoadingScreen, "Nexus & SeamlessClient Made by: Casimir", new Vector2(0.95f, 0.95f),
|
||||
MyGuiSandbox.GetDefaultTextScaleWithLanguage() * 1.1f,
|
||||
new Color(MyGuiConstants.LOADING_PLEASE_WAIT_COLOR * __instance.m_transitionAlpha),
|
||||
MyGuiDrawAlignEnum.HORISONTAL_CENTER_AND_VERTICAL_BOTTOM);
|
||||
|
||||
/*
|
||||
if (string.IsNullOrEmpty(m_customTextFromConstructor))
|
||||
{
|
||||
string font = m_font;
|
||||
Vector2 positionAbsoluteBottomLeft = m_multiTextControl.GetPositionAbsoluteBottomLeft();
|
||||
Vector2 textSize = m_multiTextControl.TextSize;
|
||||
Vector2 normalizedCoord = positionAbsoluteBottomLeft + new Vector2((m_multiTextControl.Size.X - textSize.X) * 0.5f + 0.025f, 0.025f);
|
||||
MyGuiManager.DrawString(font, m_authorWithDash.ToString(), normalizedCoord, MyGuiSandbox.GetDefaultTextScaleWithLanguage());
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
//m_multiTextControl.Draw(1f, 1f);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private static bool GetCustomLoadingScreenPath(List<MyObjectBuilder_Checkpoint.ModItem> mods, out string file)
|
||||
{
|
||||
file = null;
|
||||
var workshopDir = MyFileSystem.ModsPath;
|
||||
var backgrounds = new List<string>();
|
||||
SeamlessClient.TryShow(workshopDir);
|
||||
try
|
||||
{
|
||||
SeamlessClient.TryShow($"Installed Mods: ({mods.Count}) [{string.Join(", ", mods.Select(x => x.FriendlyName))}]");
|
||||
foreach (var mod in mods)
|
||||
{
|
||||
SeamlessClient.TryShow("Installed Mods: " + Mods);
|
||||
foreach (var Mod in Mods)
|
||||
var searchDir = mod.GetPath();
|
||||
|
||||
if (!Directory.Exists(searchDir))
|
||||
continue;
|
||||
|
||||
|
||||
var files = Directory.GetFiles(searchDir, "CustomLoadingBackground*.dds",
|
||||
SearchOption.TopDirectoryOnly);
|
||||
foreach (var filePath in files)
|
||||
{
|
||||
string SearchDir = Mod.GetPath();
|
||||
|
||||
if (!Directory.Exists(SearchDir))
|
||||
continue;
|
||||
|
||||
|
||||
var files = Directory.GetFiles(SearchDir, "CustomLoadingBackground*.dds", SearchOption.TopDirectoryOnly);
|
||||
foreach (var file in files)
|
||||
{
|
||||
// Adds all files containing CustomLoadingBackground to a list for later randomisation
|
||||
SeamlessClient.TryShow(Mod.FriendlyName + " contains a custom loading background!");
|
||||
backgrounds.Add(file);
|
||||
|
||||
}
|
||||
// Adds all files containing CustomLoadingBackground to a list for later randomisation
|
||||
SeamlessClient.TryShow($"{mod.FriendlyName} contains a custom loading background!");
|
||||
backgrounds.Add(filePath);
|
||||
}
|
||||
|
||||
// Randomly pick a loading screen from the available backgrounds
|
||||
var rInt = r.Next(0, backgrounds.Count() - 1);
|
||||
File = backgrounds[rInt];
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SeamlessClient.TryShow(ex.ToString());
|
||||
}
|
||||
|
||||
SeamlessClient.TryShow("No installed custom loading screen!");
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
private static void OnUserJoined(ref JoinResultMsg msg)
|
||||
{
|
||||
if (msg.JoinResult == JoinResult.OK)
|
||||
{
|
||||
//SeamlessClient.TryShow("User Joined! Result: " + msg.JoinResult.ToString());
|
||||
|
||||
//Invoke the switch event
|
||||
OnJoinEvent?.Invoke(null, msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private static bool OnConnectToServer(MyGameServerItem server, Action<JoinResult> onDone)
|
||||
{
|
||||
if (SeamlessClient.IsSwitching)
|
||||
return false;
|
||||
|
||||
|
||||
// Randomly pick a loading screen from the available backgrounds
|
||||
file = backgrounds[Random.Shared.Next(0, backgrounds.Count - 1)];
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* Patch Utils */
|
||||
|
||||
private static MethodInfo GetMethod(Type type, string MethodName, BindingFlags Flags)
|
||||
catch (Exception ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
MethodInfo FoundMethod = type.GetMethod(MethodName, Flags);
|
||||
|
||||
if (FoundMethod == null)
|
||||
throw new NullReferenceException($"Method for {MethodName} is null!");
|
||||
|
||||
|
||||
return FoundMethod;
|
||||
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
throw Ex;
|
||||
}
|
||||
|
||||
SeamlessClient.TryShow(ex.ToString());
|
||||
}
|
||||
|
||||
private static FieldInfo GetField(Type type, string FieldName, BindingFlags Flags)
|
||||
{
|
||||
try
|
||||
{
|
||||
FieldInfo FoundField = type.GetField(FieldName, Flags);
|
||||
|
||||
if (FoundField == null)
|
||||
throw new NullReferenceException($"Field for {FieldName} is null!");
|
||||
|
||||
|
||||
return FoundField;
|
||||
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
throw Ex;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static PropertyInfo GetProperty(Type type, string PropertyName, BindingFlags Flags)
|
||||
{
|
||||
try
|
||||
{
|
||||
PropertyInfo FoundProperty = type.GetProperty(PropertyName, Flags);
|
||||
|
||||
if (FoundProperty == null)
|
||||
throw new NullReferenceException($"Property for {PropertyName} is null!");
|
||||
|
||||
|
||||
return FoundProperty;
|
||||
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
throw Ex;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static ConstructorInfo GetConstructor(Type type, BindingFlags Flags, Type[] Types)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
ConstructorInfo FoundConstructor = type.GetConstructor(Flags, null, Types, null);
|
||||
|
||||
if (FoundConstructor == null)
|
||||
throw new NullReferenceException($"Contructor for {type.Name} is null!");
|
||||
|
||||
|
||||
return FoundConstructor;
|
||||
|
||||
}
|
||||
catch (Exception Ex)
|
||||
{
|
||||
throw Ex;
|
||||
}
|
||||
}
|
||||
SeamlessClient.TryShow("No installed custom loading screen!");
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
@@ -1,46 +1,32 @@
|
||||
using HarmonyLib;
|
||||
using ProtoBuf;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ProtoBuf;
|
||||
|
||||
namespace SeamlessClientPlugin.Utilities
|
||||
namespace SeamlessClientPlugin.Utilities;
|
||||
|
||||
public static class Utility
|
||||
{
|
||||
public static class Utility
|
||||
public static byte[] Serialize<T>(T instance)
|
||||
{
|
||||
public static byte[] Serialize<T>(T instance)
|
||||
if (instance == null)
|
||||
return null;
|
||||
|
||||
using (var m = new MemoryStream())
|
||||
{
|
||||
if (instance == null)
|
||||
return null;
|
||||
// m.Seek(0, SeekOrigin.Begin);
|
||||
Serializer.Serialize(m, instance);
|
||||
|
||||
using (var m = new MemoryStream())
|
||||
{
|
||||
// m.Seek(0, SeekOrigin.Begin);
|
||||
Serializer.Serialize(m, instance);
|
||||
|
||||
return m.ToArray();
|
||||
}
|
||||
return m.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static T Deserialize<T>(byte[] data)
|
||||
public static T Deserialize<T>(byte[] data)
|
||||
{
|
||||
if (data == null)
|
||||
return default;
|
||||
|
||||
using (var m = new MemoryStream(data))
|
||||
{
|
||||
if (data == null)
|
||||
return default(T);
|
||||
|
||||
using (var m = new MemoryStream(data))
|
||||
{
|
||||
return Serializer.Deserialize<T>(m);
|
||||
}
|
||||
return Serializer.Deserialize<T>(m);
|
||||
}
|
||||
|
||||
public static dynamic CastToReflected(this object o, Type type)
|
||||
{
|
||||
return Convert.ChangeType(o, type);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Lib.Harmony" version="2.2.1" targetFramework="net48" />
|
||||
<package id="System.IO.Compression" version="4.3.0" targetFramework="net472" />
|
||||
<package id="System.IO.Compression.ZipFile" version="4.3.0" targetFramework="net472" />
|
||||
</packages>
|
Reference in New Issue
Block a user