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 ProtoBuf;
|
||||||
|
using Sandbox.Engine.Networking;
|
||||||
using Sandbox.Game.World;
|
using Sandbox.Game.World;
|
||||||
using Sandbox.ModAPI;
|
using Sandbox.ModAPI;
|
||||||
using SeamlessClientPlugin.SeamlessTransfer;
|
|
||||||
using SeamlessClientPlugin.Utilities;
|
using SeamlessClientPlugin.Utilities;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace SeamlessClientPlugin.Messages
|
namespace SeamlessClientPlugin.Messages;
|
||||||
|
|
||||||
|
public enum ClientMessageType
|
||||||
{
|
{
|
||||||
public enum ClientMessageType
|
|
||||||
{
|
|
||||||
FirstJoin,
|
FirstJoin,
|
||||||
TransferServer,
|
TransferServer,
|
||||||
OnlinePlayers,
|
OnlinePlayers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[ProtoContract]
|
||||||
|
public class ClientMessage
|
||||||
|
{
|
||||||
|
[ProtoMember(1)] public ClientMessageType MessageType;
|
||||||
|
|
||||||
[ProtoContract]
|
[ProtoMember(2)] public byte[] MessageData;
|
||||||
public class ClientMessage
|
|
||||||
|
[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)
|
||||||
{
|
{
|
||||||
[ProtoMember(1)]
|
MessageType = type;
|
||||||
public ClientMessageType MessageType;
|
|
||||||
[ProtoMember(2)]
|
|
||||||
public byte[] MessageData;
|
|
||||||
[ProtoMember(3)]
|
|
||||||
public long IdentityID;
|
|
||||||
[ProtoMember(4)]
|
|
||||||
public ulong SteamID;
|
|
||||||
[ProtoMember(5)]
|
|
||||||
public string PluginVersion = "0";
|
|
||||||
|
|
||||||
public ClientMessage(ClientMessageType Type)
|
IdentityId = MySession.Static?.LocalHumanPlayer?.Identity?.IdentityId ?? 0;
|
||||||
{
|
SteamId = MyGameService.UserId;
|
||||||
MessageType = Type;
|
|
||||||
|
|
||||||
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;
|
PluginVersion = SeamlessClient.Version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ClientMessage()
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClientMessage() { }
|
public void SerializeData<T>(T data)
|
||||||
|
|
||||||
public void SerializeData<T>(T Data)
|
|
||||||
{
|
{
|
||||||
MessageData = Utility.Serialize(Data);
|
MessageData = Utility.Serialize(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Transfer GetTransferData()
|
public Transfer GetTransferData()
|
||||||
{
|
{
|
||||||
if (MessageData == null)
|
if (MessageData == null)
|
||||||
return default(Transfer);
|
return null;
|
||||||
|
|
||||||
return Utility.Deserialize<Transfer>(MessageData);
|
return Utility.Deserialize<Transfer>(MessageData);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public OnlinePlayersMessage GetOnlinePlayers()
|
public OnlinePlayersMessage GetOnlinePlayers()
|
||||||
{
|
{
|
||||||
if (MessageData == null)
|
if (MessageData == null)
|
||||||
return default(OnlinePlayersMessage);
|
return null;
|
||||||
|
|
||||||
|
|
||||||
OnlinePlayersMessage msg = Utility.Deserialize<OnlinePlayersMessage>(MessageData);
|
var msg = Utility.Deserialize<OnlinePlayersMessage>(MessageData);
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -1,73 +1,48 @@
|
|||||||
using ProtoBuf;
|
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]
|
[ProtoMember(10)] public List<OnlineServer> OnlineServers = [];
|
||||||
public class OnlinePlayersMessage
|
}
|
||||||
{
|
|
||||||
[ProtoMember(10)]
|
[ProtoContract]
|
||||||
public List<OnlineServer> OnlineServers = new List<OnlineServer>();
|
public class OnlineServer
|
||||||
|
{
|
||||||
[ProtoMember(12)]
|
[ProtoMember(2)] public List<OnlinePlayer> Players = [];
|
||||||
public int currentServerID;
|
|
||||||
}
|
[ProtoMember(10)] public int ServerId;
|
||||||
|
|
||||||
[ProtoContract]
|
[ProtoMember(11)] public string ServerName;
|
||||||
public class OnlineServer
|
|
||||||
{
|
[ProtoMember(3)] public bool ServerRunning = false;
|
||||||
|
}
|
||||||
[ProtoMember(2)]
|
|
||||||
public List<OnlinePlayer> Players = new List<OnlinePlayer>();
|
[ProtoContract]
|
||||||
|
public class OnlinePlayer
|
||||||
[ProtoMember(3)]
|
{
|
||||||
public bool ServerRunning = false;
|
[ProtoMember(3)] public long IdentityId;
|
||||||
|
|
||||||
[ProtoMember(10)]
|
[ProtoMember(4)] public int OnServer;
|
||||||
public int ServerID;
|
|
||||||
|
[ProtoMember(1)] public string PlayerName;
|
||||||
[ProtoMember(11)]
|
|
||||||
public string ServerName;
|
[ProtoMember(2)] public ulong SteamId;
|
||||||
|
|
||||||
public OnlineServer() { }
|
public OnlinePlayer(string playerName, ulong steamId, long identityId, int onServer)
|
||||||
|
{
|
||||||
}
|
this.PlayerName = playerName;
|
||||||
|
this.SteamId = steamId;
|
||||||
[ProtoContract]
|
this.IdentityId = identityId;
|
||||||
public class OnlinePlayer
|
this.OnServer = onServer;
|
||||||
{
|
}
|
||||||
[ProtoMember(1)]
|
|
||||||
public string PlayerName;
|
|
||||||
|
public OnlinePlayer()
|
||||||
[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() { }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@@ -1,63 +1,33 @@
|
|||||||
using ProtoBuf;
|
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.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]
|
[ProtoMember(7)] public string PlayerName;
|
||||||
public class Transfer
|
|
||||||
{
|
|
||||||
[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)]
|
[ProtoMember(9)] public MyObjectBuilder_Toolbar PlayerToolbar;
|
||||||
public string ServerName;
|
|
||||||
|
|
||||||
public Transfer(ulong ServerID, string IPAdress)
|
[ProtoMember(10)] public string ServerName;
|
||||||
|
|
||||||
|
[ProtoMember(1)] public ulong TargetServerId;
|
||||||
|
|
||||||
|
[ProtoMember(6)] public WorldRequest WorldRequest;
|
||||||
|
|
||||||
|
public Transfer(ulong serverId, string ipAdress)
|
||||||
{
|
{
|
||||||
/* This is only called serverside
|
/* This is only called serverside
|
||||||
*/
|
*/
|
||||||
|
|
||||||
this.IPAdress = IPAdress;
|
this.IpAdress = ipAdress;
|
||||||
TargetServerID = ServerID;
|
TargetServerId = serverId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Transfer() { }
|
public Transfer()
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,72 +1,55 @@
|
|||||||
using NLog;
|
using System.Reflection;
|
||||||
|
using NLog;
|
||||||
using ProtoBuf;
|
using ProtoBuf;
|
||||||
using Sandbox.Engine.Multiplayer;
|
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.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();
|
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
[ProtoMember(1)]
|
[ProtoMember(5)] public MyObjectBuilder_Gps GpsCollection;
|
||||||
public ulong PlayerID;
|
|
||||||
[ProtoMember(2)]
|
|
||||||
public long IdentityID;
|
|
||||||
[ProtoMember(3)]
|
|
||||||
public string PlayerName;
|
|
||||||
[ProtoMember(4)]
|
|
||||||
public byte[] WorldData;
|
|
||||||
|
|
||||||
[ProtoMember(5)]
|
[ProtoMember(2)] public long IdentityId;
|
||||||
public MyObjectBuilder_Gps gpsCollection;
|
|
||||||
|
|
||||||
public WorldRequest(ulong PlayerID,long PlayerIdentity, string Name)
|
[ProtoMember(1)] public ulong PlayerId;
|
||||||
|
|
||||||
|
[ProtoMember(3)] public string PlayerName;
|
||||||
|
|
||||||
|
[ProtoMember(4)] public byte[] WorldData;
|
||||||
|
|
||||||
|
public WorldRequest(ulong playerId, long playerIdentity, string name)
|
||||||
{
|
{
|
||||||
this.PlayerID = PlayerID;
|
this.PlayerId = playerId;
|
||||||
this.PlayerName = Name;
|
PlayerName = name;
|
||||||
this.IdentityID = PlayerIdentity;
|
IdentityId = playerIdentity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WorldRequest() { }
|
public WorldRequest()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public void SerializeWorldData(MyObjectBuilder_World WorldData)
|
public void SerializeWorldData(MyObjectBuilder_World worldData)
|
||||||
{
|
{
|
||||||
MethodInfo CleanupData = typeof(MyMultiplayerServerBase).GetMethod("CleanUpData", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[3]
|
MyMultiplayerServerBase.CleanUpData(worldData, PlayerId, IdentityId);
|
||||||
{
|
using var memoryStream = new MemoryStream();
|
||||||
typeof(MyObjectBuilder_World),
|
MyObjectBuilderSerializerKeen.SerializeXML(memoryStream, worldData,
|
||||||
typeof(ulong),
|
MyObjectBuilderSerializerKeen.XmlCompression.Gzip);
|
||||||
typeof(long),
|
WorldData = memoryStream.ToArray();
|
||||||
}, 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");
|
Log.Warn("Successfully Converted World");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public MyObjectBuilder_World DeserializeWorldData()
|
public MyObjectBuilder_World DeserializeWorldData()
|
||||||
{
|
{
|
||||||
MyObjectBuilderSerializer.DeserializeGZippedXML<MyObjectBuilder_World>(new MemoryStream(WorldData), out var objectBuilder);
|
MyObjectBuilderSerializerKeen.DeserializeGZippedXML<MyObjectBuilder_World>(new MemoryStream(WorldData),
|
||||||
objectBuilder.Checkpoint.Gps.Dictionary.Add(IdentityID, gpsCollection);
|
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
|
# 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.
|
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.
|
||||||
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 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
|
## 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
|
## 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
|
## 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,40 +1,23 @@
|
|||||||
using Sandbox.Engine.Multiplayer;
|
using System.Runtime.CompilerServices;
|
||||||
using Sandbox.Engine.Networking;
|
using CringePlugins.Ui;
|
||||||
using Sandbox.Game;
|
|
||||||
using Sandbox.Game.Entities;
|
|
||||||
using Sandbox.Game.Gui;
|
|
||||||
using Sandbox.Game.Multiplayer;
|
|
||||||
using Sandbox.Game.World;
|
using Sandbox.Game.World;
|
||||||
using Sandbox.Graphics.GUI;
|
|
||||||
using Sandbox.ModAPI;
|
using Sandbox.ModAPI;
|
||||||
using SeamlessClientPlugin.Messages;
|
using SeamlessClientPlugin.Messages;
|
||||||
using SeamlessClientPlugin.SeamlessTransfer;
|
using SeamlessClientPlugin.SeamlessTransfer;
|
||||||
using SeamlessClientPlugin.Utilities;
|
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.Plugins;
|
||||||
using VRage.Utils;
|
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;
|
||||||
//SendAllMembersDataToClient
|
|
||||||
|
|
||||||
public class SeamlessClient : IPlugin
|
|
||||||
{
|
|
||||||
/* First Step. How does a player join a game?
|
/* First Step. How does a player join a game?
|
||||||
* First JoinGameInternal is called with the ServersLobbyID.
|
* 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.
|
* 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.
|
||||||
@@ -109,29 +92,26 @@ namespace SeamlessClientPlugin
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// pretend to be the latest one
|
||||||
public static string Version = "1.4.01";
|
public static string Version = "3.0.0.10";
|
||||||
public static bool Debug = true;
|
public static bool Debug = true;
|
||||||
private static bool Initilized = false;
|
private static bool _initilized;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public const ushort SeamlessClientNetID = 2936;
|
|
||||||
|
|
||||||
public static bool IsSwitching = false;
|
public static bool IsSwitching = false;
|
||||||
public static bool RanJoin = false;
|
public static bool RanJoin = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void Init(object gameInstance)
|
public void Init(object gameInstance)
|
||||||
{
|
{
|
||||||
|
TryShow($"Running Seamless Client Plugin v[{Version}]");
|
||||||
|
MySession.LoadingStep += LoadingProgressStep;
|
||||||
|
}
|
||||||
|
|
||||||
TryShow("Running Seamless Client Plugin v[" + Version + "]");
|
private static void LoadingProgressStep(LoadingProgress step)
|
||||||
|
{
|
||||||
|
// i copied it from the new one no guarantee it works properly
|
||||||
|
if (step >= LoadingProgress.PROGRESS_STEP8)
|
||||||
|
SendFirstJoin();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -141,7 +121,7 @@ namespace SeamlessClientPlugin
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
if (!Initilized)
|
if (!_initilized)
|
||||||
{
|
{
|
||||||
Patches.GetPatches();
|
Patches.GetPatches();
|
||||||
OnlinePlayers.Patch();
|
OnlinePlayers.Patch();
|
||||||
@@ -151,19 +131,22 @@ namespace SeamlessClientPlugin
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
DisposeInitilizations();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void RunInitilizations()
|
public static void RunInitilizations()
|
||||||
{
|
{
|
||||||
|
MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(SeamlessClientNetId, MessageHandler);
|
||||||
|
_initilized = true;
|
||||||
MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(SeamlessClientNetID, MessageHandler);
|
|
||||||
Initilized = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DisposeInitilizations()
|
public static void DisposeInitilizations()
|
||||||
{
|
{
|
||||||
MyAPIGateway.Multiplayer?.UnregisterSecureMessageHandler(SeamlessClientNetID, MessageHandler);
|
MyAPIGateway.Multiplayer?.UnregisterSecureMessageHandler(SeamlessClientNetId, MessageHandler);
|
||||||
Initilized = false;
|
_initilized = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -171,26 +154,32 @@ namespace SeamlessClientPlugin
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ClientMessage Recieved = Utilities.Utility.Deserialize<ClientMessage>(obj2);
|
var recieved = Utility.Deserialize<ClientMessage>(obj2);
|
||||||
|
|
||||||
if(Recieved.MessageType == ClientMessageType.FirstJoin)
|
switch (recieved.MessageType)
|
||||||
{
|
{
|
||||||
//Server sent a first join message! Send a reply back so the server knows what version we are on
|
// was removed in newer versions, but we leave it here for backwards compatability
|
||||||
ClientMessage PingServer = new ClientMessage(ClientMessageType.FirstJoin);
|
case ClientMessageType.FirstJoin:
|
||||||
MyAPIGateway.Multiplayer?.SendMessageToServer(SeamlessClientNetID, Utilities.Utility.Serialize(PingServer));
|
{
|
||||||
|
SendFirstJoin();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else if (Recieved.MessageType == ClientMessageType.TransferServer)
|
case ClientMessageType.TransferServer:
|
||||||
{
|
{
|
||||||
//Server sent a transfer message! Begin transfer via seamless
|
//Server sent a transfer message! Begin transfer via seamless
|
||||||
Transfer TransferMessage = Recieved.GetTransferData();
|
var transferMessage = recieved.GetTransferData();
|
||||||
ServerPing.StartServerPing(TransferMessage);
|
ServerPing.StartServerPing(transferMessage);
|
||||||
}else if(Recieved.MessageType == ClientMessageType.OnlinePlayers)
|
break;
|
||||||
|
}
|
||||||
|
case ClientMessageType.OnlinePlayers:
|
||||||
{
|
{
|
||||||
var p = Recieved.GetOnlinePlayers();
|
var p = recieved.GetOnlinePlayers();
|
||||||
OnlinePlayers.AllServers = p.OnlineServers;
|
OnlinePlayers.AllServers = p.OnlineServers;
|
||||||
OnlinePlayers.currentServer = p.currentServerID;
|
OnlinePlayers.CurrentServer = p.CurrentServerId;
|
||||||
|
|
||||||
//TryShow("Recieved Players! "+OnlinePlayers.AllServers.Count);
|
//TryShow("Recieved Players! "+OnlinePlayers.AllServers.Count);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -199,20 +188,16 @@ namespace SeamlessClientPlugin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void SendFirstJoin()
|
||||||
|
{
|
||||||
|
var pingServer = new ClientMessage(ClientMessageType.FirstJoin);
|
||||||
|
MyAPIGateway.Multiplayer?.SendMessageToServer(SeamlessClientNetId, Utility.Serialize(pingServer));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void TryShow(string message)
|
public static void TryShow(string message)
|
||||||
{
|
{
|
||||||
if (MySession.Static?.LocalHumanPlayer != null && Debug)
|
NotificationsComponent.SpawnNotification(5f, message);
|
||||||
MyAPIGateway.Utilities?.ShowMessage("NetworkClient", message);
|
|
||||||
|
|
||||||
MyLog.Default?.WriteLineAndConsole($"SeamlessClient: {message}");
|
MyLog.Default?.WriteLineAndConsole($"SeamlessClient: {message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
DisposeInitilizations();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -1,162 +1,49 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<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>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
<TargetFramework>net9.0-windows</TargetFramework>
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
<ProjectGuid>{102A3D80-B588-43BA-B686-000FA8FF1A0C}</ProjectGuid>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<OutputType>Library</OutputType>
|
<Nullable>disable</Nullable>
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||||
<RootNamespace>SeamlessClientPlugin</RootNamespace>
|
<PackageType>CringePlugin</PackageType>
|
||||||
<AssemblyName>SeamlessClientPlugin</AssemblyName>
|
<RestoreAdditionalProjectSources>https://ng.zznty.ru/v3/index.json</RestoreAdditionalProjectSources>
|
||||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<FileAlignment>512</FileAlignment>
|
<Authors>Casimir</Authors>
|
||||||
<Deterministic>true</Deterministic>
|
<PackageId>Plugin.Casimir255.SeamlessClient</PackageId>
|
||||||
<TargetFrameworkProfile />
|
<AssemblyName>Plugin.Casimir255.SeamlessClient</AssemblyName>
|
||||||
<NuGetPackageImportStamp>
|
<Title>Nexus Seamless Switcher</Title>
|
||||||
</NuGetPackageImportStamp>
|
<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>
|
||||||
</PropertyGroup>
|
<PackageProjectUrl>https://git.zznty.ru/PvE/SeamlessClient</PackageProjectUrl>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<RepositoryUrl>https://git.zznty.ru/PvE/SeamlessClient</RepositoryUrl>
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<RepositoryType>git</RepositoryType>
|
||||||
<DebugType>full</DebugType>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<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>
|
||||||
|
|
||||||
|
<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>
|
<PropertyGroup>
|
||||||
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
|
<AppdataPath>$([System.Environment]::GetFolderPath(SpecialFolder.ApplicationData))</AppdataPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="0Harmony, Version=2.2.1.0, Culture=neutral, processorArchitecture=MSIL">
|
<OutputFiles Include="$(OutputPath)\*"/>
|
||||||
<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>
|
||||||
<ItemGroup>
|
<Copy SourceFiles="@(OutputFiles)" DestinationFolder="$(AppdataPath)\CringeLauncher\plugins\$(ProjectName)" OverwriteReadOnlyFiles="true"/>
|
||||||
<Compile Include="Messages\ClientMessages.cs" />
|
</Target>
|
||||||
<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>
|
</Project>
|
@@ -1,21 +1,13 @@
|
|||||||
using Sandbox.Definitions;
|
using Sandbox.Engine.Networking;
|
||||||
using Sandbox.Engine.Networking;
|
|
||||||
using Sandbox.Game.World;
|
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;
|
||||||
using VRage.Game.GUI;
|
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
|
/* 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.MyScriptManager.LoadData() is where modded scripts get loaded and added
|
||||||
@@ -32,107 +24,88 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
|
|
||||||
|
|
||||||
//Mods that are currently loaded in this instance.
|
//Mods that are currently loaded in this instance.
|
||||||
private static List<MyObjectBuilder_Checkpoint.ModItem> CurrentLoadedMods = new List<MyObjectBuilder_Checkpoint.ModItem>();
|
private static List<MyObjectBuilder_Checkpoint.ModItem> _currentLoadedMods = [];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Mods that we need to Load
|
//Mods that we need to Load
|
||||||
private static List<MyObjectBuilder_Checkpoint.ModItem> TargetLoadMods = new List<MyObjectBuilder_Checkpoint.ModItem>();
|
private static readonly List<MyObjectBuilder_Checkpoint.ModItem> TargetLoadMods = [];
|
||||||
|
|
||||||
//Mods that we need to UnLoad
|
//Mods that we need to UnLoad
|
||||||
private static List<MyObjectBuilder_Checkpoint.ModItem> TargetUnLoadMods = new List<MyObjectBuilder_Checkpoint.ModItem>();
|
private static readonly List<MyObjectBuilder_Checkpoint.ModItem> TargetUnLoadMods = [];
|
||||||
|
|
||||||
|
|
||||||
private static bool FinishedDownloadingMods = false;
|
private static bool _finishedDownloadingMods;
|
||||||
private static bool DownloadSuccess = false;
|
private static bool _downloadSuccess;
|
||||||
|
|
||||||
private static DateTime DownloadTimeout;
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static void DownloadNewMods(List<MyObjectBuilder_Checkpoint.ModItem> Target)
|
|
||||||
{
|
{
|
||||||
CurrentLoadedMods = MySession.Static.Mods;
|
_currentLoadedMods = MySession.Static.Mods;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Loop through our current mods
|
//Loop through our current mods
|
||||||
foreach(var mod in CurrentLoadedMods)
|
foreach (var mod in _currentLoadedMods)
|
||||||
{
|
if (!target.Contains(mod))
|
||||||
if (!Target.Contains(mod))
|
|
||||||
TargetUnLoadMods.Add(mod);
|
TargetUnLoadMods.Add(mod);
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//Loop through our TargetMods
|
//Loop through our TargetMods
|
||||||
foreach(var mod in Target)
|
foreach (var mod in target)
|
||||||
{
|
if (!_currentLoadedMods.Contains(mod))
|
||||||
if (!CurrentLoadedMods.Contains(mod))
|
|
||||||
TargetLoadMods.Add(mod);
|
TargetLoadMods.Add(mod);
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
DownloadTimeout = DateTime.Now + TimeSpan.FromMinutes(5);
|
_downloadTimeout = DateTime.Now + TimeSpan.FromMinutes(5);
|
||||||
SeamlessClient.TryShow("Downloading New Mods");
|
SeamlessClient.TryShow("Downloading New Mods");
|
||||||
MyWorkshop.DownloadModsAsync(TargetLoadMods, ModDownloadingFinished);
|
MyWorkshop.DownloadModsAsync(TargetLoadMods, ModDownloadingFinished);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ModDownloadingFinished(bool Success)
|
private static void ModDownloadingFinished(MyGameServiceCallResult result)
|
||||||
{
|
{
|
||||||
if (Success)
|
if (result == MyGameServiceCallResult.OK)
|
||||||
{
|
{
|
||||||
SeamlessClient.TryShow("Mod Downloading Finished!");
|
SeamlessClient.TryShow("Mod Downloading Finished!");
|
||||||
FinishedDownloadingMods = true;
|
_finishedDownloadingMods = true;
|
||||||
DownloadSuccess = true;
|
_downloadSuccess = true;
|
||||||
//May need to wait seamless loading if mods have yet to finish downloading
|
//May need to wait seamless loading if mods have yet to finish downloading
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DownloadSuccess = false;
|
_downloadSuccess = false;
|
||||||
FinishedDownloadingMods = true;
|
_finishedDownloadingMods = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void ReadyModSwitch(MyObjectBuilder_Checkpoint checkpoint, MyObjectBuilder_Sector sector)
|
public static void ReadyModSwitch(MyObjectBuilder_Checkpoint checkpoint, MyObjectBuilder_Sector sector)
|
||||||
{
|
{
|
||||||
|
while (!_finishedDownloadingMods)
|
||||||
while (!FinishedDownloadingMods)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
//Break out of loop
|
//Break out of loop
|
||||||
if (DownloadTimeout < DateTime.Now)
|
if (_downloadTimeout < DateTime.Now)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
Thread.Sleep(20);
|
Thread.Sleep(20);
|
||||||
}
|
}
|
||||||
|
|
||||||
FinishedDownloadingMods = false;
|
_finishedDownloadingMods = false;
|
||||||
|
|
||||||
//Skip mod switch
|
//Skip mod switch
|
||||||
if (!DownloadSuccess)
|
if (!_downloadSuccess)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Create new script manager?
|
//Create new script manager?
|
||||||
ScriptManager.SetValue(MySession.Static, new MyScriptManager());
|
MySession.Static.ScriptManager = new();
|
||||||
|
|
||||||
|
|
||||||
MyGuiTextures.Static.Unload();
|
MyGuiTextures.Static.Unload();
|
||||||
MySession.Static.ScriptManager.Init(checkpoint.ScriptManagerData);
|
MySession.Static.ScriptManager.Init(checkpoint.ScriptManagerData);
|
||||||
//MyDefinitionManager.Static.LoadData(TargetServerMods);
|
//MyDefinitionManager.Static.LoadData(TargetServerMods);
|
||||||
PrepareBaseSession.Invoke(null, new object[] { sector });
|
MySession.PreloadModels(sector);
|
||||||
|
|
||||||
|
|
||||||
MyLocalCache.PreloadLocalInventoryConfig();
|
MyLocalCache.PreloadLocalInventoryConfig();
|
||||||
@@ -142,32 +115,22 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
|
|
||||||
|
|
||||||
// PrepareBaseSession.Invoke(MySession.Static, new object[] { TargetServerMods, null });
|
// PrepareBaseSession.Invoke(MySession.Static, new object[] { TargetServerMods, null });
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void UnloadOldScripts()
|
private static void UnloadOldScripts()
|
||||||
{
|
{
|
||||||
// MySandboxGame.Log.WriteLine(string.Format("Script loaded: {0}", value.FullName));
|
// MySandboxGame.Log.WriteLine(string.Format("Script loaded: {0}", value.FullName));
|
||||||
int amount = 0;
|
var amount = 0;
|
||||||
foreach (var mod in TargetUnLoadMods)
|
foreach (var mod in TargetUnLoadMods)
|
||||||
{
|
{
|
||||||
var val = MySession.Static.ScriptManager.Scripts.FirstOrDefault(x => x.Value.FullName.Contains(mod.PublishedFileId.ToString()));
|
var val = MySession.Static.ScriptManager.Scripts.FirstOrDefault(x =>
|
||||||
|
x.Value.FullName.Contains(mod.PublishedFileId.ToString()));
|
||||||
MySession.Static.ScriptManager.Scripts.Remove(val.Key);
|
MySession.Static.ScriptManager.Scripts.Remove(val.Key);
|
||||||
|
|
||||||
amount++;
|
amount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
SeamlessClient.TryShow($"Removed {amount} old scripts!");
|
SeamlessClient.TryShow($"Removed {amount} old scripts!");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,15 +1,7 @@
|
|||||||
using Sandbox;
|
namespace SeamlessClientPlugin.SeamlessTransfer;
|
||||||
using Sandbox.Game.World;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace SeamlessClientPlugin.SeamlessTransfer
|
public class MyScriptManagerLoader
|
||||||
{
|
{
|
||||||
public class MyScriptManagerLoader
|
|
||||||
{
|
|
||||||
/*
|
/*
|
||||||
public void LoadData(MyScriptManager __instance)
|
public void LoadData(MyScriptManager __instance)
|
||||||
{
|
{
|
||||||
@@ -78,6 +70,4 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
MySandboxGame.Log.WriteLine("MyScriptManager.LoadData() - END");
|
MySandboxGame.Log.WriteLine("MyScriptManager.LoadData() - END");
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -1,51 +1,41 @@
|
|||||||
using Sandbox.Engine.Networking;
|
using SeamlessClientPlugin.Messages;
|
||||||
using Sandbox.Game.Multiplayer;
|
|
||||||
using SeamlessClientPlugin.Messages;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using VRage.GameServices;
|
using VRage.GameServices;
|
||||||
|
|
||||||
namespace SeamlessClientPlugin.SeamlessTransfer
|
namespace SeamlessClientPlugin.SeamlessTransfer;
|
||||||
|
|
||||||
|
public class ServerPing
|
||||||
{
|
{
|
||||||
public class ServerPing
|
private static Transfer _transfer;
|
||||||
{
|
|
||||||
|
|
||||||
private static WorldRequest Request { get { return Transfer.WorldRequest; } }
|
private static WorldRequest Request => _transfer.WorldRequest;
|
||||||
private static Transfer Transfer;
|
|
||||||
|
|
||||||
|
|
||||||
public static void StartServerPing(Transfer ClientTransfer)
|
public static void StartServerPing(Transfer clientTransfer)
|
||||||
{
|
{
|
||||||
// We need to first ping the server to make sure its running and so we can get a connection
|
// We need to first ping the server to make sure its running and so we can get a connection
|
||||||
Transfer = ClientTransfer;
|
_transfer = clientTransfer;
|
||||||
|
|
||||||
|
|
||||||
if (Transfer.TargetServerID == 0)
|
if (_transfer.TargetServerId == 0)
|
||||||
{
|
{
|
||||||
SeamlessClient.TryShow("This is not a valid server!");
|
SeamlessClient.TryShow("This is not a valid server!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var e = new MyGameServerItem();
|
||||||
MyGameServerItem E = new MyGameServerItem();
|
e.ConnectionString = _transfer.IpAdress;
|
||||||
E.ConnectionString = Transfer.IPAdress;
|
e.SteamID = _transfer.TargetServerId;
|
||||||
E.SteamID = Transfer.TargetServerID;
|
e.Name = _transfer.ServerName;
|
||||||
E.Name = Transfer.ServerName;
|
|
||||||
|
|
||||||
|
|
||||||
|
SeamlessClient.TryShow($"Beginning Redirect to server: {_transfer.TargetServerId}");
|
||||||
SeamlessClient.TryShow("Beginning Redirect to server: " + Transfer.TargetServerID);
|
|
||||||
|
|
||||||
|
|
||||||
var world = Request.DeserializeWorldData();
|
var world = Request.DeserializeWorldData();
|
||||||
|
|
||||||
|
|
||||||
SwitchServers Switcher = new SwitchServers(E, world);
|
var switcher = new SwitchServers(e, world);
|
||||||
Switcher.BeginSwitch();
|
switcher.BeginSwitch();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,5 +1,4 @@
|
|||||||
using Sandbox;
|
using Sandbox;
|
||||||
using Sandbox.Definitions;
|
|
||||||
using Sandbox.Engine.Multiplayer;
|
using Sandbox.Engine.Multiplayer;
|
||||||
using Sandbox.Engine.Networking;
|
using Sandbox.Engine.Networking;
|
||||||
using Sandbox.Game;
|
using Sandbox.Game;
|
||||||
@@ -10,51 +9,36 @@ using Sandbox.Game.Multiplayer;
|
|||||||
using Sandbox.Game.SessionComponents;
|
using Sandbox.Game.SessionComponents;
|
||||||
using Sandbox.Game.World;
|
using Sandbox.Game.World;
|
||||||
using Sandbox.Game.World.Generator;
|
using Sandbox.Game.World.Generator;
|
||||||
using Sandbox.Graphics.GUI;
|
|
||||||
using Sandbox.ModAPI;
|
using Sandbox.ModAPI;
|
||||||
using SeamlessClientPlugin.Utilities;
|
using SeamlessClientPlugin.Utilities;
|
||||||
using SpaceEngineers.Game.GUI;
|
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;
|
||||||
using VRage.Game.Components;
|
|
||||||
using VRage.Game.ModAPI;
|
using VRage.Game.ModAPI;
|
||||||
using VRage.GameServices;
|
using VRage.GameServices;
|
||||||
using VRage.Steam;
|
using VRage.Network;
|
||||||
using VRage.Utils;
|
using VRage.Utils;
|
||||||
using VRageMath;
|
|
||||||
using VRageRender;
|
using VRageRender;
|
||||||
using VRageRender.Messages;
|
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)
|
||||||
{
|
{
|
||||||
|
this.TargetServer = targetServer;
|
||||||
|
this.TargetWorld = targetWorld;
|
||||||
|
|
||||||
|
//ModLoader.DownloadNewMods(TargetWorld.Checkpoint.Mods);
|
||||||
|
}
|
||||||
|
|
||||||
public MyGameServerItem TargetServer { get; }
|
public MyGameServerItem TargetServer { get; }
|
||||||
public MyObjectBuilder_World TargetWorld { get; }
|
public MyObjectBuilder_World TargetWorld { get; }
|
||||||
|
|
||||||
private string OldArmorSkin { get; set; } = string.Empty;
|
private string OldArmorSkin { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public SwitchServers(MyGameServerItem TargetServer, MyObjectBuilder_World TargetWorld)
|
|
||||||
{
|
|
||||||
this.TargetServer = TargetServer;
|
|
||||||
this.TargetWorld = TargetWorld;
|
|
||||||
|
|
||||||
//ModLoader.DownloadNewMods(TargetWorld.Checkpoint.Mods);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void BeginSwitch()
|
public void BeginSwitch()
|
||||||
{
|
{
|
||||||
OldArmorSkin = MySession.Static.LocalHumanPlayer.BuildArmorSkin;
|
OldArmorSkin = MySession.Static.LocalHumanPlayer.BuildArmorSkin;
|
||||||
@@ -66,8 +50,6 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
UnloadCurrentServer();
|
UnloadCurrentServer();
|
||||||
SetNewMultiplayerClient();
|
SetNewMultiplayerClient();
|
||||||
SeamlessClient.IsSwitching = false;
|
SeamlessClient.IsSwitching = false;
|
||||||
|
|
||||||
|
|
||||||
}, "SeamlessClient");
|
}, "SeamlessClient");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,35 +59,28 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
// Following is called when the multiplayer is set successfully to target server
|
// Following is called when the multiplayer is set successfully to target server
|
||||||
Patches.OnJoinEvent += OnJoinEvent;
|
Patches.OnJoinEvent += OnJoinEvent;
|
||||||
|
|
||||||
|
MySandboxGame.Static.SessionCompatHelper.FixSessionComponentObjectBuilders(TargetWorld.Checkpoint,
|
||||||
MySandboxGame.Static.SessionCompatHelper.FixSessionComponentObjectBuilders(TargetWorld.Checkpoint, TargetWorld.Sector);
|
TargetWorld.Sector);
|
||||||
|
|
||||||
// Create constructors
|
// Create constructors
|
||||||
var LayerInstance = Patches.TransportLayerConstructor.Invoke(new object[] { 2 });
|
var layerInstance = new MyTransportLayer(MyMultiplayer.GAME_EVENT_CHANNEL);
|
||||||
var SyncInstance = Patches.SyncLayerConstructor.Invoke(new object[] { LayerInstance });
|
var syncInstance = new MySyncLayer(layerInstance);
|
||||||
var instance = Patches.ClientConstructor.Invoke(new object[] { TargetServer, SyncInstance });
|
var instance = new MyMultiplayerClient(TargetServer, syncInstance);
|
||||||
|
|
||||||
|
MyMultiplayer.Static = instance;
|
||||||
MyMultiplayer.Static = Utility.CastToReflected(instance, Patches.ClientType);
|
|
||||||
MyMultiplayer.Static.ExperimentalMode = true;
|
MyMultiplayer.Static.ExperimentalMode = true;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Set the new SyncLayer to the MySession.Static.SyncLayer
|
// Set the new SyncLayer to the MySession.Static.SyncLayer
|
||||||
Patches.MySessionLayer.SetValue(MySession.Static, MyMultiplayer.Static.SyncLayer);
|
MySession.Static.SyncLayer = MyMultiplayer.Static.SyncLayer;
|
||||||
|
|
||||||
SeamlessClient.TryShow("Successfully set MyMultiplayer.Static");
|
SeamlessClient.TryShow("Successfully set MyMultiplayer.Static");
|
||||||
|
|
||||||
|
|
||||||
Sync.Clients.SetLocalSteamId(Sync.MyId, false, MyGameService.UserName);
|
Sync.Clients.SetLocalSteamId(Sync.MyId, false, MyGameService.UserName);
|
||||||
Sync.Players.RegisterEvents();
|
Sync.Players.RegisterEvents();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void OnJoinEvent(object sender, JoinResultMsg e)
|
||||||
|
|
||||||
private void OnJoinEvent(object sender, VRage.Network.JoinResultMsg e)
|
|
||||||
{
|
{
|
||||||
ForceClientConnection();
|
ForceClientConnection();
|
||||||
|
|
||||||
@@ -114,10 +89,8 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void ForceClientConnection()
|
private void ForceClientConnection()
|
||||||
{
|
{
|
||||||
|
|
||||||
//Set World Settings
|
//Set World Settings
|
||||||
SetWorldSettings();
|
SetWorldSettings();
|
||||||
|
|
||||||
@@ -125,13 +98,13 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
LoadConnectedClients();
|
LoadConnectedClients();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
MySector.InitEnvironmentSettings(TargetWorld.Sector.Environment);
|
MySector.InitEnvironmentSettings(TargetWorld.Sector.Environment);
|
||||||
|
|
||||||
|
|
||||||
|
var text = !string.IsNullOrEmpty(TargetWorld.Checkpoint.CustomSkybox)
|
||||||
string text = ((!string.IsNullOrEmpty(TargetWorld.Checkpoint.CustomSkybox)) ? TargetWorld.Checkpoint.CustomSkybox : MySector.EnvironmentDefinition.EnvironmentTexture);
|
? TargetWorld.Checkpoint.CustomSkybox
|
||||||
MyRenderProxy.PreloadTextures(new string[1] { text }, TextureType.CubeMap);
|
: MySector.EnvironmentDefinition.EnvironmentTexture;
|
||||||
|
MyRenderProxy.PreloadTextures([text], TextureType.CubeMap);
|
||||||
|
|
||||||
MyModAPIHelper.Initialize();
|
MyModAPIHelper.Initialize();
|
||||||
MySession.Static.LoadDataComponents();
|
MySession.Static.LoadDataComponents();
|
||||||
@@ -145,7 +118,6 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
// A.Invoke(MySession.Static, new object[] { TargetWorld.Checkpoint });
|
// A.Invoke(MySession.Static, new object[] { TargetWorld.Checkpoint });
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
MyMultiplayer.Static.OnSessionReady();
|
MyMultiplayer.Static.OnSessionReady();
|
||||||
|
|
||||||
|
|
||||||
@@ -155,7 +127,7 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
|
|
||||||
|
|
||||||
MyHud.Chat.RegisterChat(MyMultiplayer.Static);
|
MyHud.Chat.RegisterChat(MyMultiplayer.Static);
|
||||||
Patches.GPSRegisterChat.Invoke(MySession.Static.Gpss, new object[] { MyMultiplayer.Static });
|
MySession.Static.Gpss.RegisterChat(MyMultiplayer.Static);
|
||||||
|
|
||||||
|
|
||||||
// Allow the game to start proccessing incoming messages in the buffer
|
// Allow the game to start proccessing incoming messages in the buffer
|
||||||
@@ -164,14 +136,9 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
//Recreate all controls... Will fix weird gui/paint/crap
|
//Recreate all controls... Will fix weird gui/paint/crap
|
||||||
MyGuiScreenHudSpace.Static.RecreateControls(true);
|
MyGuiScreenHudSpace.Static.RecreateControls(true);
|
||||||
//MySession.Static.LocalHumanPlayer.BuildArmorSkin = OldArmorSkin;
|
//MySession.Static.LocalHumanPlayer.BuildArmorSkin = OldArmorSkin;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void LoadOnlinePlayers()
|
private void LoadOnlinePlayers()
|
||||||
{
|
{
|
||||||
//Get This players ID
|
//Get This players ID
|
||||||
@@ -182,10 +149,12 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
SeamlessClient.TryShow("SavingPlayerID is null! Creating Default!");
|
SeamlessClient.TryShow("SavingPlayerID is null! Creating Default!");
|
||||||
savingPlayerId = new MyPlayer.PlayerId(Sync.MyId);
|
savingPlayerId = new MyPlayer.PlayerId(Sync.MyId);
|
||||||
}
|
}
|
||||||
SeamlessClient.TryShow("Saving PlayerID: " + savingPlayerId.ToString());
|
|
||||||
|
SeamlessClient.TryShow($"Saving PlayerID: {savingPlayerId}");
|
||||||
|
|
||||||
Sync.Players.LoadConnectedPlayers(TargetWorld.Checkpoint, savingPlayerId);
|
Sync.Players.LoadConnectedPlayers(TargetWorld.Checkpoint, savingPlayerId);
|
||||||
Sync.Players.LoadControlledEntities(TargetWorld.Checkpoint.ControlledEntities, TargetWorld.Checkpoint.ControlledObject, savingPlayerId);
|
Sync.Players.LoadControlledEntities(TargetWorld.Checkpoint.ControlledEntities,
|
||||||
|
TargetWorld.Checkpoint.ControlledObject, savingPlayerId);
|
||||||
/*
|
/*
|
||||||
|
|
||||||
|
|
||||||
@@ -221,7 +190,6 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
}
|
}
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetWorldSettings()
|
private void SetWorldSettings()
|
||||||
@@ -231,9 +199,7 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
//Clear old list
|
//Clear old list
|
||||||
MySession.Static.PromotedUsers.Clear();
|
MySession.Static.PromotedUsers.Clear();
|
||||||
MySession.Static.CreativeTools.Clear();
|
MySession.Static.CreativeTools.Clear();
|
||||||
Dictionary<ulong, AdminSettingsEnum> AdminSettingsList = (Dictionary<ulong, AdminSettingsEnum>)Patches.RemoteAdminSettings.GetValue(MySession.Static);
|
MySession.Static.RemoteAdminSettings.Clear();
|
||||||
AdminSettingsList.Clear();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Set new world settings
|
// Set new world settings
|
||||||
@@ -242,7 +208,9 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
|
|
||||||
MySession.Static.Mods = TargetWorld.Checkpoint.Mods;
|
MySession.Static.Mods = TargetWorld.Checkpoint.Mods;
|
||||||
MySession.Static.Settings = TargetWorld.Checkpoint.Settings;
|
MySession.Static.Settings = TargetWorld.Checkpoint.Settings;
|
||||||
MySession.Static.CurrentPath = MyLocalCache.GetSessionSavesPath(MyUtils.StripInvalidChars(TargetWorld.Checkpoint.SessionName), contentFolder: false, createIfNotExists: false);
|
MySession.Static.CurrentPath =
|
||||||
|
MyLocalCache.GetSessionSavesPath(MyUtils.StripInvalidChars(TargetWorld.Checkpoint.SessionName), false,
|
||||||
|
false);
|
||||||
MySession.Static.WorldBoundaries = TargetWorld.Checkpoint.WorldBoundaries;
|
MySession.Static.WorldBoundaries = TargetWorld.Checkpoint.WorldBoundaries;
|
||||||
MySession.Static.InGameTime = MyObjectBuilder_Checkpoint.DEFAULT_DATE;
|
MySession.Static.InGameTime = MyObjectBuilder_Checkpoint.DEFAULT_DATE;
|
||||||
MySession.Static.ElapsedGameTime = new TimeSpan(TargetWorld.Checkpoint.ElapsedGameTime);
|
MySession.Static.ElapsedGameTime = new TimeSpan(TargetWorld.Checkpoint.ElapsedGameTime);
|
||||||
@@ -259,11 +227,11 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
{
|
{
|
||||||
MySession.Static.Gpss = new MyGpsCollection();
|
MySession.Static.Gpss = new MyGpsCollection();
|
||||||
MySession.Static.Gpss.LoadGpss(TargetWorld.Checkpoint);
|
MySession.Static.Gpss.LoadGpss(TargetWorld.Checkpoint);
|
||||||
|
|
||||||
}
|
}
|
||||||
catch(Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
SeamlessClient.TryShow($"An error occured while loading GPS points! You will have an empty gps list! \n {ex.ToString()}");
|
SeamlessClient.TryShow(
|
||||||
|
$"An error occured while loading GPS points! You will have an empty gps list! \n {ex}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -275,25 +243,18 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
|
|
||||||
// Set new admin settings
|
// Set new admin settings
|
||||||
if (TargetWorld.Checkpoint.PromotedUsers != null)
|
if (TargetWorld.Checkpoint.PromotedUsers != null)
|
||||||
{
|
|
||||||
MySession.Static.PromotedUsers = TargetWorld.Checkpoint.PromotedUsers.Dictionary;
|
MySession.Static.PromotedUsers = TargetWorld.Checkpoint.PromotedUsers.Dictionary;
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
MySession.Static.PromotedUsers = new Dictionary<ulong, MyPromoteLevel>();
|
MySession.Static.PromotedUsers = new Dictionary<ulong, MyPromoteLevel>();
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
foreach (var item in TargetWorld.Checkpoint.AllPlayersData.Dictionary)
|
||||||
|
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
|
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;
|
adminSettingsEnum = (AdminSettingsEnum)value;
|
||||||
}
|
|
||||||
if (!MyPlatformGameSettings.IsIgnorePcuAllowed)
|
if (!MyPlatformGameSettings.IsIgnorePcuAllowed)
|
||||||
{
|
{
|
||||||
adminSettingsEnum &= ~AdminSettingsEnum.IgnorePcu;
|
adminSettingsEnum &= ~AdminSettingsEnum.IgnorePcu;
|
||||||
@@ -301,56 +262,33 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
AdminSettingsList[clientId] = adminSettingsEnum;
|
MySession.Static.RemoteAdminSettings[clientId] = adminSettingsEnum;
|
||||||
if (!Sync.IsDedicated && clientId == Sync.MyId)
|
if (!Sync.IsDedicated && clientId == Sync.MyId)
|
||||||
{
|
MySession.Static.AdminSettings = adminSettingsEnum;
|
||||||
Patches.AdminSettings.SetValue(MySession.Static, adminSettingsEnum);
|
|
||||||
|
|
||||||
//m_adminSettings = adminSettingsEnum;
|
var value2 = MySession.Static.PromotedUsers.GetValueOrDefault(clientId, MyPromoteLevel.None);
|
||||||
}
|
if (item.Value.PromoteLevel > value2) MySession.Static.PromotedUsers[clientId] = item.Value.PromoteLevel;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (!MySession.Static.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)
|
if (!MySession.Static.CreativeTools.Contains(clientId) && item.Value.CreativeToolsEnabled)
|
||||||
{
|
|
||||||
MySession.Static.CreativeTools.Add(clientId);
|
MySession.Static.CreativeTools.Add(clientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadConnectedClients()
|
private void LoadConnectedClients()
|
||||||
{
|
{
|
||||||
|
MySession.Static.LoadMembersFromWorld(TargetWorld, MyMultiplayer.Static);
|
||||||
Patches.LoadMembersFromWorld.Invoke(MySession.Static, new object[] { TargetWorld, MyMultiplayer.Static });
|
|
||||||
|
|
||||||
|
|
||||||
//Re-Initilize Virtual clients
|
//Re-Initilize Virtual clients
|
||||||
object VirtualClientsValue = Patches.VirtualClients.GetValue(MySession.Static);
|
MySession.Static.VirtualClients.Init();
|
||||||
Patches.InitVirtualClients.Invoke(VirtualClientsValue, null);
|
|
||||||
|
|
||||||
|
|
||||||
//load online players
|
//load online players
|
||||||
LoadOnlinePlayers();
|
LoadOnlinePlayers();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartEntitySync()
|
private void StartEntitySync()
|
||||||
{
|
{
|
||||||
SeamlessClient.TryShow("Requesting Player From Server");
|
SeamlessClient.TryShow("Requesting Player From Server");
|
||||||
Sync.Players.RequestNewPlayer(Sync.MyId, 0, MyGameService.UserName, null, realPlayer: true, initialPlayer: true);
|
Sync.Players.RequestNewPlayer(Sync.MyId, 0, MyGameService.UserName, null, true, true);
|
||||||
if (MySession.Static.ControlledEntity == null && Sync.IsServer && !Sandbox.Engine.Platform.Game.IsDedicated)
|
if (MySession.Static.ControlledEntity == null && Sync.IsServer && !Game.IsDedicated)
|
||||||
{
|
{
|
||||||
MyLog.Default.WriteLine("ControlledObject was null, respawning character");
|
MyLog.Default.WriteLine("ControlledObject was null, respawning character");
|
||||||
//m_cameraAwaitingEntity = true;
|
//m_cameraAwaitingEntity = true;
|
||||||
@@ -358,7 +296,7 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Request client state batch
|
//Request client state batch
|
||||||
(MyMultiplayer.Static as MyMultiplayerClientBase).RequestBatchConfirmation();
|
((MyMultiplayerClientBase)MyMultiplayer.Static).RequestBatchConfirmation();
|
||||||
MyMultiplayer.Static.PendingReplicablesDone += MyMultiplayer_PendingReplicablesDone;
|
MyMultiplayer.Static.PendingReplicablesDone += MyMultiplayer_PendingReplicablesDone;
|
||||||
//typeof(MyGuiScreenTerminal).GetMethod("CreateTabs")
|
//typeof(MyGuiScreenTerminal).GetMethod("CreateTabs")
|
||||||
|
|
||||||
@@ -368,16 +306,13 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
MyRenderProxy.RebuildCullingStructure();
|
MyRenderProxy.RebuildCullingStructure();
|
||||||
MyRenderProxy.CollectGarbage();
|
MyRenderProxy.CollectGarbage();
|
||||||
|
|
||||||
SeamlessClient.TryShow("OnlinePlayers: " + MySession.Static.Players.GetOnlinePlayers().Count);
|
SeamlessClient.TryShow($"OnlinePlayers: {MySession.Static.Players.GetOnlinePlayers().Count}");
|
||||||
SeamlessClient.TryShow("Loading Complete!");
|
SeamlessClient.TryShow("Loading Complete!");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MyMultiplayer_PendingReplicablesDone()
|
private void MyMultiplayer_PendingReplicablesDone()
|
||||||
{
|
{
|
||||||
if (MySession.Static.VoxelMaps.Instances.Count > 0)
|
if (MySession.Static.VoxelMaps.Instances.Count > 0) MySandboxGame.AreClipmapsReady = false;
|
||||||
{
|
|
||||||
MySandboxGame.AreClipmapsReady = false;
|
|
||||||
}
|
|
||||||
MyMultiplayer.Static.PendingReplicablesDone -= MyMultiplayer_PendingReplicablesDone;
|
MyMultiplayer.Static.PendingReplicablesDone -= MyMultiplayer_PendingReplicablesDone;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,31 +323,24 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
|
|
||||||
|
|
||||||
//This shoud never be null
|
//This shoud never be null
|
||||||
var Generator = MySession.Static.GetComponent<MyProceduralWorldGenerator>();
|
var generator = MySession.Static.GetComponent<MyProceduralWorldGenerator>();
|
||||||
|
|
||||||
//Force component to unload
|
//Force component to unload
|
||||||
Patches.UnloadProceduralWorldGenerator.Invoke(Generator, null);
|
Patches.UnloadProceduralWorldGenerator.Invoke(generator, null);
|
||||||
|
|
||||||
//Re-call the generator init
|
//Re-call the generator init
|
||||||
MyObjectBuilder_WorldGenerator GeneratorSettings = (MyObjectBuilder_WorldGenerator)TargetWorld.Checkpoint.SessionComponents.FirstOrDefault(x => x.GetType() == typeof(MyObjectBuilder_WorldGenerator));
|
var generatorSettings = TargetWorld.Checkpoint.SessionComponents.OfType<MyObjectBuilder_WorldGenerator>().FirstOrDefault();
|
||||||
if (GeneratorSettings != null)
|
if (generatorSettings != null)
|
||||||
{
|
|
||||||
//Re-initilized this component (forces to update asteroid areas like not in planets etc)
|
//Re-initilized this component (forces to update asteroid areas like not in planets etc)
|
||||||
Generator.Init(GeneratorSettings);
|
generator.Init(generatorSettings);
|
||||||
}
|
|
||||||
|
|
||||||
//Force component to reload, re-syncing settings and seeds to the destination server
|
//Force component to reload, re-syncing settings and seeds to the destination server
|
||||||
Generator.LoadData();
|
generator.LoadData();
|
||||||
|
|
||||||
//We need to go in and force planets to be empty areas in the generator. This is originially done on planet init.
|
//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>())
|
||||||
foreach (var Planet in MyEntities.GetEntities().OfType<MyPlanet>())
|
|
||||||
{
|
{
|
||||||
MyPlanetInitArguments args = (MyPlanetInitArguments)PlanetInitArgs.GetValue(Planet);
|
generator.MarkEmptyArea(planet.PositionComp.GetPosition(), planet.m_planetInitValues.MaxRadius);
|
||||||
|
|
||||||
float MaxRadius = args.MaxRadius;
|
|
||||||
|
|
||||||
Generator.MarkEmptyArea(Planet.PositionComp.GetPosition(), MaxRadius);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,7 +354,7 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
RemoveOldEntities();
|
RemoveOldEntities();
|
||||||
|
|
||||||
//Try and close the quest log
|
//Try and close the quest log
|
||||||
MySessionComponentIngameHelp component = MySession.Static.GetComponent<MySessionComponentIngameHelp>();
|
var component = MySession.Static.GetComponent<MySessionComponentIngameHelp>();
|
||||||
component?.TryCancelObjective();
|
component?.TryCancelObjective();
|
||||||
|
|
||||||
//Clear all old players and clients.
|
//Clear all old players and clients.
|
||||||
@@ -437,8 +365,6 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
MyHud.Chat.UnregisterChat(MyMultiplayer.Static);
|
MyHud.Chat.UnregisterChat(MyMultiplayer.Static);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
MySession.Static.Gpss.RemovePlayerGpss(MySession.Static.LocalPlayerId);
|
MySession.Static.Gpss.RemovePlayerGpss(MySession.Static.LocalPlayerId);
|
||||||
MyHud.GpsMarkers.Clear();
|
MyHud.GpsMarkers.Clear();
|
||||||
MyMultiplayer.Static.ReplicationLayer.Disconnect();
|
MyMultiplayer.Static.ReplicationLayer.Disconnect();
|
||||||
@@ -450,7 +376,6 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
MyGuiScreenMedicals.Close();
|
MyGuiScreenMedicals.Close();
|
||||||
|
|
||||||
//MySession.Static.UnloadDataComponents();
|
//MySession.Static.UnloadDataComponents();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RemoveOldEntities()
|
private void RemoveOldEntities()
|
||||||
@@ -463,6 +388,4 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
ent.Close();
|
ent.Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -1,5 +1,6 @@
|
|||||||
using HarmonyLib;
|
using System.Reflection;
|
||||||
using ProtoBuf;
|
using System.Text;
|
||||||
|
using HarmonyLib;
|
||||||
using Sandbox.Engine.Multiplayer;
|
using Sandbox.Engine.Multiplayer;
|
||||||
using Sandbox.Game;
|
using Sandbox.Game;
|
||||||
using Sandbox.Game.Gui;
|
using Sandbox.Game.Gui;
|
||||||
@@ -7,154 +8,47 @@ using Sandbox.Game.GUI;
|
|||||||
using Sandbox.Game.Localization;
|
using Sandbox.Game.Localization;
|
||||||
using Sandbox.Game.Multiplayer;
|
using Sandbox.Game.Multiplayer;
|
||||||
using Sandbox.Game.SessionComponents;
|
using Sandbox.Game.SessionComponents;
|
||||||
using Sandbox.Game.VoiceChat;
|
|
||||||
using Sandbox.Game.World;
|
using Sandbox.Game.World;
|
||||||
using Sandbox.Graphics.GUI;
|
using Sandbox.Graphics.GUI;
|
||||||
using SeamlessClientPlugin.Messages;
|
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;
|
||||||
using VRage.Audio;
|
using VRage.Audio;
|
||||||
using VRage.Game;
|
using VRage.Game;
|
||||||
using VRage.Game.ModAPI;
|
|
||||||
using VRage.Utils;
|
using VRage.Utils;
|
||||||
using VRageMath;
|
using VRageMath;
|
||||||
|
|
||||||
namespace SeamlessClientPlugin.Utilities
|
namespace SeamlessClientPlugin.Utilities;
|
||||||
{
|
|
||||||
|
|
||||||
public class OnlinePlayers
|
public class OnlinePlayers
|
||||||
{
|
{
|
||||||
private static Harmony Patcher = new Harmony("OnlinePlayersPatcher");
|
private static readonly Harmony Patcher = new("OnlinePlayersPatcher");
|
||||||
public static List<OnlineServer> AllServers = new List<OnlineServer>();
|
public static List<OnlineServer> AllServers = [];
|
||||||
public static int currentServer;
|
public static int CurrentServer;
|
||||||
private static string _currentServerName;
|
private static string _currentServerName;
|
||||||
|
|
||||||
public static int totalPlayerCount = 0;
|
public static int TotalPlayerCount;
|
||||||
public static int currentPlayerCount = 0;
|
public static int CurrentPlayerCount;
|
||||||
|
|
||||||
|
|
||||||
private static MethodInfo m_UpdateCaption;
|
|
||||||
private static MethodInfo m_RefreshMuteIcons;
|
|
||||||
private static MethodInfo m_OnToggleMutePressed;
|
|
||||||
private static MethodInfo m_AddCaption;
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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()
|
public static void Patch()
|
||||||
{
|
{
|
||||||
m_playersTable = typeof(MyGuiScreenPlayers).GetField("m_playersTable", BindingFlags.Instance | BindingFlags.NonPublic);
|
var recreateControls =
|
||||||
m_pings = typeof(MyGuiScreenPlayers).GetField("pings", BindingFlags.Instance | BindingFlags.NonPublic);
|
typeof(MyGuiScreenPlayers).GetMethod("RecreateControls", BindingFlags.Instance | BindingFlags.Public);
|
||||||
m_UpdateCaption = typeof(MyGuiScreenPlayers).GetMethod("UpdateCaption", BindingFlags.Instance | BindingFlags.NonPublic);
|
var updateCaption =
|
||||||
m_RefreshMuteIcons = typeof(MyGuiScreenPlayers).GetMethod("RefreshMuteIcons", BindingFlags.Instance | BindingFlags.NonPublic);
|
typeof(MyGuiScreenPlayers).GetMethod("UpdateCaption", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||||
m_OnToggleMutePressed = typeof(MyGuiScreenPlayers).GetMethod("OnToggleMutePressed", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
||||||
|
|
||||||
|
|
||||||
m_profileButton_ButtonClicked = typeof(MyGuiScreenPlayers).GetMethod("profileButton_ButtonClicked", BindingFlags.Instance | BindingFlags.NonPublic);
|
Patcher.Patch(recreateControls, new HarmonyMethod(GetPatchMethod(nameof(RecreateControlsPrefix))));
|
||||||
m_promoteButton_ButtonClicked = typeof(MyGuiScreenPlayers).GetMethod("promoteButton_ButtonClicked", BindingFlags.Instance | BindingFlags.NonPublic);
|
Patcher.Patch(updateCaption, new HarmonyMethod(GetPatchMethod(nameof(UpdateCaption))));
|
||||||
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);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
MethodInfo recreateControls = typeof(MyGuiScreenPlayers).GetMethod("RecreateControls", BindingFlags.Instance | BindingFlags.Public);
|
|
||||||
MethodInfo updateCaption = typeof(MyGuiScreenPlayers).GetMethod("UpdateCaption", BindingFlags.Instance | BindingFlags.NonPublic);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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))));
|
//Patcher.Patch(recreateControls, postfix: new HarmonyMethod(GetPatchMethod(nameof(RecreateControlsSuffix))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static bool RecreateControlsPrefix(MyGuiScreenPlayers __instance, bool constructor)
|
public static bool RecreateControlsPrefix(MyGuiScreenPlayers __instance, bool constructor)
|
||||||
{
|
{
|
||||||
|
if (MyMultiplayer.Static != null && MyMultiplayer.Static.IsLobby) return true;
|
||||||
if (MyMultiplayer.Static != null && MyMultiplayer.Static.IsLobby)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
__instance.Controls.Clear();
|
__instance.Controls.Clear();
|
||||||
__instance.Elements.Clear();
|
__instance.Elements.Clear();
|
||||||
//__instance.Elements.Add(m_cl);
|
//__instance.Elements.Add(m_cl);
|
||||||
@@ -177,195 +71,213 @@ namespace SeamlessClientPlugin.Utilities
|
|||||||
//MyCommonTexts.ScreenCaptionPlayers
|
//MyCommonTexts.ScreenCaptionPlayers
|
||||||
|
|
||||||
//MyStringId ID = MyStringId.GetOrCompute("Test Caption");
|
//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 }));
|
__instance.m_caption =
|
||||||
|
__instance.AddCaption(MyCommonTexts.ScreenCaptionPlayers, null, new Vector2(0f, 0.003f));
|
||||||
|
|
||||||
|
|
||||||
float StartX = -0.435f;
|
const float startX = -0.435f;
|
||||||
float StartY = -0.36f;
|
const float startY = -0.36f;
|
||||||
|
|
||||||
MyGuiControlSeparatorList myGuiControlSeparatorList = new MyGuiControlSeparatorList();
|
var myGuiControlSeparatorList = new MyGuiControlSeparatorList();
|
||||||
myGuiControlSeparatorList.AddHorizontal(new Vector2(StartX, StartY), .83f);
|
myGuiControlSeparatorList.AddHorizontal(new Vector2(startX, startY), .83f);
|
||||||
|
|
||||||
|
|
||||||
|
var start = new Vector2(startX, 0.358f);
|
||||||
|
|
||||||
Vector2 start = new Vector2(StartX, 0.358f);
|
|
||||||
myGuiControlSeparatorList.AddHorizontal(start, 0.728f);
|
myGuiControlSeparatorList.AddHorizontal(start, 0.728f);
|
||||||
myGuiControlSeparatorList.AddHorizontal(new Vector2(StartX, 0.05f), 0.17f);
|
myGuiControlSeparatorList.AddHorizontal(new Vector2(startX, 0.05f), 0.17f);
|
||||||
__instance.Controls.Add(myGuiControlSeparatorList);
|
__instance.Controls.Add(myGuiControlSeparatorList);
|
||||||
|
|
||||||
|
|
||||||
Vector2 Spacing = new Vector2(0f, 0.057f);
|
var spacing = new Vector2(0f, 0.057f);
|
||||||
Vector2 vector3 = new Vector2(StartX, StartY + 0.035f);
|
var vector3 = new Vector2(startX, startY + 0.035f);
|
||||||
|
|
||||||
//SeamlessClient.TryShow("B");
|
//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));
|
var mProfileButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null,
|
||||||
m_profileButton.ButtonClicked += delegate (MyGuiControlButton obj) { m_profileButton_ButtonClicked.Invoke(__instance, new object[] { obj }); };
|
MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null,
|
||||||
__instance.Controls.Add(m_profileButton);
|
MyTexts.Get(MyCommonTexts.ScreenPlayers_Profile));
|
||||||
vector3 += Spacing;
|
mProfileButton.ButtonClicked += __instance.profileButton_ButtonClicked;
|
||||||
m_ProfileButton.SetValue(__instance, m_profileButton);
|
__instance.Controls.Add(mProfileButton);
|
||||||
|
vector3 += spacing;
|
||||||
|
__instance.m_profileButton = mProfileButton;
|
||||||
|
|
||||||
|
|
||||||
MyGuiControlButton m_promoteButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null, MyTexts.Get(MyCommonTexts.ScreenPlayers_Promote));
|
var mPromoteButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null,
|
||||||
m_promoteButton.ButtonClicked += delegate (MyGuiControlButton obj) { m_promoteButton_ButtonClicked.Invoke(__instance, new object[] { obj }); };
|
MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null,
|
||||||
__instance.Controls.Add(m_promoteButton);
|
MyTexts.Get(MyCommonTexts.ScreenPlayers_Promote));
|
||||||
vector3 += Spacing;
|
mPromoteButton.ButtonClicked += __instance.promoteButton_ButtonClicked;
|
||||||
m_PromoteButton.SetValue(__instance, m_promoteButton);
|
__instance.Controls.Add(mPromoteButton);
|
||||||
|
vector3 += spacing;
|
||||||
|
__instance.m_promoteButton = mPromoteButton;
|
||||||
|
|
||||||
MyGuiControlButton m_demoteButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null, MyTexts.Get(MyCommonTexts.ScreenPlayers_Demote));
|
var mDemoteButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null,
|
||||||
m_demoteButton.ButtonClicked += delegate (MyGuiControlButton obj) { m_demoteButton_ButtonClicked.Invoke(__instance, new object[] { obj }); };
|
MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null,
|
||||||
__instance.Controls.Add(m_demoteButton);
|
MyTexts.Get(MyCommonTexts.ScreenPlayers_Demote));
|
||||||
vector3 += Spacing;
|
mDemoteButton.ButtonClicked += __instance.demoteButton_ButtonClicked;
|
||||||
m_DemoteButton.SetValue(__instance, m_demoteButton);
|
__instance.Controls.Add(mDemoteButton);
|
||||||
|
vector3 += spacing;
|
||||||
|
__instance.m_demoteButton = mDemoteButton;
|
||||||
|
|
||||||
|
|
||||||
MyGuiControlButton m_kickButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null, MyTexts.Get(MyCommonTexts.ScreenPlayers_Kick));
|
var mKickButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null,
|
||||||
m_kickButton.ButtonClicked += delegate (MyGuiControlButton obj) { m_kickButton_ButtonClicked.Invoke(__instance, new object[] { obj }); };
|
MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null,
|
||||||
__instance.Controls.Add(m_kickButton);
|
MyTexts.Get(MyCommonTexts.ScreenPlayers_Kick));
|
||||||
vector3 += Spacing;
|
mKickButton.ButtonClicked += __instance.kickButton_ButtonClicked;
|
||||||
m_KickButton.SetValue(__instance, m_kickButton);
|
__instance.Controls.Add(mKickButton);
|
||||||
|
vector3 += spacing;
|
||||||
|
__instance.m_kickButton = mKickButton;
|
||||||
|
|
||||||
MyGuiControlButton m_banButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null, MyTexts.Get(MyCommonTexts.ScreenPlayers_Ban));
|
var mBanButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null,
|
||||||
m_banButton.ButtonClicked += delegate (MyGuiControlButton obj) { m_banButton_ButtonClicked.Invoke(__instance, new object[] { obj }); };
|
MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null,
|
||||||
__instance.Controls.Add(m_banButton);
|
MyTexts.Get(MyCommonTexts.ScreenPlayers_Ban));
|
||||||
vector3 += Spacing;
|
mBanButton.ButtonClicked += __instance.banButton_ButtonClicked;
|
||||||
m_BanButton.SetValue(__instance, m_banButton);
|
__instance.Controls.Add(mBanButton);
|
||||||
|
vector3 += spacing;
|
||||||
|
__instance.m_banButton = mBanButton;
|
||||||
|
|
||||||
|
|
||||||
MyGuiControlButton m_tradeButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null, MyTexts.Get(MySpaceTexts.PlayersScreen_TradeBtn));
|
var mTradeButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null,
|
||||||
m_tradeButton.SetTooltip(MyTexts.GetString(MySpaceTexts.PlayersScreen_TradeBtn_TTP));
|
MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null,
|
||||||
m_tradeButton.ButtonClicked += delegate (MyGuiControlButton obj) { m_tradeButton_ButtonClicked.Invoke(__instance, new object[] { obj }); };
|
MyTexts.Get(MySpaceTexts.PlayersScreen_TradeBtn));
|
||||||
__instance.Controls.Add(m_tradeButton);
|
mTradeButton.SetTooltip(MyTexts.GetString(MySpaceTexts.PlayersScreen_TradeBtn_TTP));
|
||||||
m_TradeButton.SetValue(__instance, m_tradeButton);
|
mTradeButton.ButtonClicked += __instance.tradeButton_ButtonClicked;
|
||||||
|
__instance.Controls.Add(mTradeButton);
|
||||||
|
__instance.m_tradeButton = mTradeButton;
|
||||||
|
|
||||||
|
|
||||||
//SeamlessClient.TryShow("C");
|
//SeamlessClient.TryShow("C");
|
||||||
|
|
||||||
Vector2 vector4 = vector3 + new Vector2(-0.002f, m_tradeButton.Size.Y + 0.03f);
|
var vector4 = vector3 + new Vector2(-0.002f, mTradeButton.Size.Y + 0.03f);
|
||||||
MyGuiControlCombobox m_lobbyTypeCombo = new MyGuiControlCombobox(vector4, null, null, null, 3);
|
__instance.m_lobbyTypeCombo = new MyGuiControlCombobox(vector4, null, null, null, 3);
|
||||||
m_LobbyTypeCombo.SetValue(__instance, m_lobbyTypeCombo);
|
|
||||||
|
|
||||||
Vector2 vector5 = vector4 + new Vector2(0f, 0.05f);
|
var vector5 = vector4 + new Vector2(0f, 0.05f);
|
||||||
vector5 += new Vector2(0f, 0.03f);
|
vector5 += new Vector2(0f, 0.03f);
|
||||||
int m_maxPlayers = (Sync.IsServer ? MyMultiplayerLobby.MAX_PLAYERS : 16);
|
__instance.m_maxPlayers = Sync.IsServer ? MyMultiplayerLobby.MAX_PLAYERS : 16;
|
||||||
m_MaxPlayers.SetValue(__instance, m_maxPlayers);
|
__instance.m_maxPlayersSlider = new MyGuiControlSlider(vector5, 2f, Math.Max(__instance.m_maxPlayers, 3), 0.177f,
|
||||||
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);
|
Sync.IsServer ? MySession.Static.MaxPlayers : MyMultiplayer.Static.MemberLimit, null, null, 1, 0.8f, 0f,
|
||||||
m_MaxPlayersSlider.SetValue(__instance, m_maxPlayersSlider);
|
"White", null, MyGuiControlSliderStyleEnum.Default, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP,
|
||||||
|
true);
|
||||||
|
|
||||||
|
|
||||||
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));
|
var mInviteButton = new MyGuiControlButton(new Vector2(startX, 0.25000026f),
|
||||||
m_inviteButton.ButtonClicked += delegate (MyGuiControlButton obj) { m_inviteButton_ButtonClicked.Invoke(__instance, new object[] { obj }); };
|
MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP,
|
||||||
__instance.Controls.Add(m_inviteButton);
|
null, MyTexts.Get(MyCommonTexts.ScreenPlayers_Invite));
|
||||||
m_InviteButton.SetValue(__instance, m_inviteButton);
|
mInviteButton.ButtonClicked += __instance.inviteButton_ButtonClicked;
|
||||||
|
__instance.Controls.Add(mInviteButton);
|
||||||
|
__instance.m_inviteButton = mInviteButton;
|
||||||
|
|
||||||
Vector2 vector6 = new Vector2(-StartX - 0.034f, StartY + 0.033f);
|
var vector6 = new Vector2(-startX - 0.034f, startY + 0.033f);
|
||||||
Vector2 size = new Vector2(0.66f, 1.2f);
|
var size = new Vector2(0.66f, 1.2f);
|
||||||
int num2 = 18;
|
var num2 = 18;
|
||||||
float num3 = 0f;
|
var num3 = 0f;
|
||||||
|
|
||||||
|
|
||||||
//SeamlessClient.TryShow("D");
|
//SeamlessClient.TryShow("D");
|
||||||
MySessionComponentMatch component = MySession.Static.GetComponent<MySessionComponentMatch>();
|
var component = MySession.Static.GetComponent<MySessionComponentMatch>();
|
||||||
if (component.IsEnabled)
|
if (component.IsEnabled)
|
||||||
{
|
{
|
||||||
Vector2 vector7 = __instance.GetPositionAbsolute() + vector6 + new Vector2(0f - size.X, 0f);
|
var vector7 = __instance.GetPositionAbsolute() + vector6 + new Vector2(0f - size.X, 0f);
|
||||||
MyGuiControlLabel m_warfare_timeRemainting_label = new MyGuiControlLabel(vector6 - new Vector2(size.X, 0f));
|
var mWarfareTimeRemaintingLabel = 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)
|
|
||||||
{
|
{
|
||||||
if ((myFaction.Name.StartsWith("Red") || myFaction.Name.StartsWith("Green") || myFaction.Name.StartsWith("Blue")) && myFaction.Name.EndsWith("Faction"))
|
Text = $"{MyTexts.GetString(MySpaceTexts.WarfareCounter_TimeRemaining)}: "
|
||||||
|
};
|
||||||
|
__instance.Controls.Add(mWarfareTimeRemaintingLabel);
|
||||||
|
__instance.m_warfare_timeRemainting_label = mWarfareTimeRemaintingLabel;
|
||||||
|
|
||||||
|
|
||||||
|
var timeSpan = TimeSpan.FromMinutes(component.RemainingMinutes);
|
||||||
|
var mWarfareTimeRemaintingTime = new MyGuiControlLabel(vector6 - new Vector2(size.X, 0f) +
|
||||||
|
new Vector2(
|
||||||
|
mWarfareTimeRemaintingLabel.Size.X,
|
||||||
|
0f))
|
||||||
{
|
{
|
||||||
__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)));
|
Text = timeSpan.ToString(timeSpan.TotalHours >= 1.0 ? @"hh\:mm\:ss" : "mm\\:ss")
|
||||||
|
};
|
||||||
|
__instance.Controls.Add(mWarfareTimeRemaintingTime);
|
||||||
|
__instance.m_warfare_timeRemainting_time = mWarfareTimeRemaintingTime;
|
||||||
|
|
||||||
|
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++;
|
num6++;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
vector6.Y += m_warfare_timeRemainting_label.Size.Y + num4 + num3 * 2f;
|
vector6.Y += mWarfareTimeRemaintingLabel.Size.Y + num4 + num3 * 2f;
|
||||||
num2 -= 3;
|
num2 -= 3;
|
||||||
}
|
}
|
||||||
//SeamlessClient.TryShow("E");
|
//SeamlessClient.TryShow("E");
|
||||||
|
|
||||||
MyGuiControlTable m_playersTable = new MyGuiControlTable
|
__instance.m_playersTable = new MyGuiControlTable
|
||||||
{
|
{
|
||||||
Position = vector6,
|
Position = vector6,
|
||||||
Size = size,
|
Size = size,
|
||||||
OriginAlign = MyGuiDrawAlignEnum.HORISONTAL_RIGHT_AND_VERTICAL_TOP,
|
OriginAlign = MyGuiDrawAlignEnum.HORISONTAL_RIGHT_AND_VERTICAL_TOP,
|
||||||
ColumnsCount = 7
|
ColumnsCount = 7,
|
||||||
|
//SeamlessClient.TryShow("F");
|
||||||
|
GamepadHelpTextId = MySpaceTexts.PlayersScreen_Help_PlayersList,
|
||||||
|
VisibleRowsCount = num2
|
||||||
};
|
};
|
||||||
|
|
||||||
m_PlayersTable.SetValue(__instance, m_playersTable);
|
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;
|
||||||
|
|
||||||
//SeamlessClient.TryShow("F");
|
__instance.m_playersTable.SetCustomColumnWidths([
|
||||||
|
steamIconWidth,
|
||||||
m_playersTable.GamepadHelpTextId = MySpaceTexts.PlayersScreen_Help_PlayersList;
|
playerNameWidth,
|
||||||
m_playersTable.VisibleRowsCount = num2;
|
factionNameWidth,
|
||||||
float PlayerName = 0.2f;
|
rankWidth,
|
||||||
float Rank = 0.1f;
|
pingWidth,
|
||||||
float Ping = 0.08f;
|
mutedWidth,
|
||||||
float Muted = 0.1f;
|
serverWidth
|
||||||
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");
|
//SeamlessClient.TryShow("G");
|
||||||
|
|
||||||
m_playersTable.SetColumnComparison(1, (MyGuiControlTable.Cell a, MyGuiControlTable.Cell b) => a.Text.CompareToIgnoreCase(b.Text));
|
__instance.m_playersTable.SetColumnComparison(1, (a, b) => a.Text.CompareToIgnoreCase(b.Text));
|
||||||
m_playersTable.SetColumnName(1, MyTexts.Get(MyCommonTexts.ScreenPlayers_PlayerName));
|
__instance.m_playersTable.SetColumnName(1, MyTexts.Get(MyCommonTexts.ScreenPlayers_PlayerName));
|
||||||
m_playersTable.SetColumnComparison(2, (MyGuiControlTable.Cell a, MyGuiControlTable.Cell b) => a.Text.CompareToIgnoreCase(b.Text));
|
__instance.m_playersTable.SetColumnComparison(2, (a, b) => a.Text.CompareToIgnoreCase(b.Text));
|
||||||
m_playersTable.SetColumnName(2, MyTexts.Get(MyCommonTexts.ScreenPlayers_FactionName));
|
__instance.m_playersTable.SetColumnName(2, MyTexts.Get(MyCommonTexts.ScreenPlayers_FactionName));
|
||||||
m_playersTable.SetColumnName(5, new StringBuilder(MyTexts.GetString(MyCommonTexts.ScreenPlayers_Muted)));
|
__instance.m_playersTable.SetColumnName(5, new StringBuilder(MyTexts.GetString(MyCommonTexts.ScreenPlayers_Muted)));
|
||||||
m_playersTable.SetColumnComparison(3, GameAdminCompare);
|
__instance.m_playersTable.SetColumnComparison(3, GameAdminCompare);
|
||||||
m_playersTable.SetColumnName(3, MyTexts.Get(MyCommonTexts.ScreenPlayers_Rank));
|
__instance.m_playersTable.SetColumnName(3, MyTexts.Get(MyCommonTexts.ScreenPlayers_Rank));
|
||||||
m_playersTable.SetColumnComparison(4, GamePingCompare);
|
__instance.m_playersTable.SetColumnComparison(4, GamePingCompare);
|
||||||
m_playersTable.SetColumnName(4, MyTexts.Get(MyCommonTexts.ScreenPlayers_Ping));
|
__instance.m_playersTable.SetColumnName(4, MyTexts.Get(MyCommonTexts.ScreenPlayers_Ping));
|
||||||
|
|
||||||
|
|
||||||
StringBuilder colName = new StringBuilder("Server");
|
var colName = new StringBuilder("Server");
|
||||||
m_playersTable.SetColumnName(6, colName);
|
__instance.m_playersTable.SetColumnName(6, colName);
|
||||||
m_playersTable.SetColumnComparison(6, (MyGuiControlTable.Cell a, MyGuiControlTable.Cell b) => a.Text.CompareToIgnoreCase(b.Text));
|
__instance.m_playersTable.SetColumnComparison(6, (a, b) => a.Text.CompareToIgnoreCase(b.Text));
|
||||||
|
|
||||||
//SeamlessClient.TryShow("H");
|
//SeamlessClient.TryShow("H");
|
||||||
|
|
||||||
|
|
||||||
//m_PlayersTable_ItemSelected
|
//m_PlayersTable_ItemSelected
|
||||||
m_playersTable.ItemSelected += delegate (MyGuiControlTable i, MyGuiControlTable.EventArgs x) { m_PlayersTable_ItemSelected.Invoke(__instance, new object[] { i, x }); };
|
__instance.m_playersTable.ItemSelected += __instance.playersTable_ItemSelected;
|
||||||
m_playersTable.UpdateTableSortHelpText();
|
__instance.m_playersTable.UpdateTableSortHelpText();
|
||||||
__instance.Controls.Add(m_playersTable);
|
__instance.Controls.Add(__instance.m_playersTable);
|
||||||
|
|
||||||
|
|
||||||
string thisServerName = "thisServer";
|
var thisServerName = "<this>";
|
||||||
totalPlayerCount = 0;
|
TotalPlayerCount = 0;
|
||||||
foreach(var server in AllServers)
|
foreach (var server in AllServers)
|
||||||
{
|
{
|
||||||
|
var servername = server.ServerName;
|
||||||
string servername = server.ServerName;
|
if (server.ServerId == CurrentServer)
|
||||||
if (server.ServerID == currentServer)
|
|
||||||
{
|
{
|
||||||
thisServerName = servername;
|
thisServerName = servername;
|
||||||
_currentServerName = servername;
|
_currentServerName = servername;
|
||||||
@@ -374,168 +286,141 @@ namespace SeamlessClientPlugin.Utilities
|
|||||||
|
|
||||||
foreach (var player in server.Players)
|
foreach (var player in server.Players)
|
||||||
{
|
{
|
||||||
|
AddPlayer(__instance, player.SteamId, servername, player.PlayerName, player.IdentityId);
|
||||||
AddPlayer(__instance, player.SteamID, servername, player.PlayerName, player.IdentityID);
|
TotalPlayerCount++;
|
||||||
totalPlayerCount++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CurrentPlayerCount = 0;
|
||||||
currentPlayerCount = 0;
|
foreach (var onlinePlayer in Sync.Players.GetOnlinePlayers())
|
||||||
foreach (MyPlayer onlinePlayer in Sync.Players.GetOnlinePlayers())
|
|
||||||
{
|
{
|
||||||
if (onlinePlayer.Id.SerialId != 0)
|
if (onlinePlayer.Id.SerialId != 0) continue;
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentPlayerCount++;
|
CurrentPlayerCount++;
|
||||||
totalPlayerCount++;
|
TotalPlayerCount++;
|
||||||
AddPlayer(__instance, onlinePlayer.Id.SteamId, thisServerName);
|
AddPlayer(__instance, onlinePlayer.Id.SteamId, thisServerName);
|
||||||
}
|
}
|
||||||
|
|
||||||
//SeamlessClient.TryShow("I");
|
//SeamlessClient.TryShow("I");
|
||||||
|
|
||||||
ulong m_lastSelected = (ulong)m_LastSelected.GetValue(__instance);
|
if (__instance.m_lastSelected != 0L)
|
||||||
if (m_lastSelected != 0L)
|
|
||||||
{
|
{
|
||||||
MyGuiControlTable.Row row2 = m_playersTable.Find((MyGuiControlTable.Row r) => (ulong)r.UserData == m_lastSelected);
|
var row2 = __instance.m_playersTable.Find(r => (ulong)r.UserData == __instance.m_lastSelected);
|
||||||
if (row2 != null)
|
if (row2 != null) __instance.m_playersTable.SelectedRow = row2;
|
||||||
{
|
|
||||||
m_playersTable.SelectedRow = row2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_UpdateButtonsEnabledState.Invoke(__instance, null);
|
__instance.UpdateButtonsEnabledState();
|
||||||
//UpdateButtonsEnabledState();
|
//UpdateButtonsEnabledState();
|
||||||
|
|
||||||
m_UpdateCaption.Invoke(__instance, null);
|
__instance.UpdateCaption();
|
||||||
|
|
||||||
|
|
||||||
|
var minSizeGui = MyGuiControlButton.GetVisualStyle(MyGuiControlButtonStyleEnum.Default).NormalTexture
|
||||||
|
.MinSizeGui;
|
||||||
Vector2 minSizeGui = MyGuiControlButton.GetVisualStyle(MyGuiControlButtonStyleEnum.Default).NormalTexture.MinSizeGui;
|
var myGuiControlLabel = new MyGuiControlLabel(new Vector2(start.X, start.Y + minSizeGui.Y / 2f))
|
||||||
MyGuiControlLabel myGuiControlLabel = new MyGuiControlLabel(new Vector2(start.X, start.Y + minSizeGui.Y / 2f));
|
{
|
||||||
myGuiControlLabel.Name = MyGuiScreenBase.GAMEPAD_HELP_LABEL_NAME;
|
Name = MyGuiScreenBase.GAMEPAD_HELP_LABEL_NAME
|
||||||
|
};
|
||||||
__instance.Controls.Add(myGuiControlLabel);
|
__instance.Controls.Add(myGuiControlLabel);
|
||||||
__instance.GamepadHelpTextId = MySpaceTexts.PlayersScreen_Help_Screen;
|
__instance.GamepadHelpTextId = MySpaceTexts.PlayersScreen_Help_Screen;
|
||||||
__instance.FocusedControl = m_playersTable;
|
__instance.FocusedControl = __instance.m_playersTable;
|
||||||
|
|
||||||
//SeamlessClient.TryShow("J");
|
//SeamlessClient.TryShow("J");
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
SeamlessClient.TryShow(ex.ToString());
|
SeamlessClient.TryShow(ex.ToString());
|
||||||
|
__instance.Controls.Clear();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool AddPlayer(MyGuiScreenPlayers __instance, ulong userId, string server, string playername = null, long playerId = 0)
|
private static bool AddPlayer(MyGuiScreenPlayers instance, ulong userId, string server, string playerName = null,
|
||||||
|
long playerId = 0)
|
||||||
{
|
{
|
||||||
MyGuiControlTable table = (MyGuiControlTable)m_playersTable.GetValue(__instance);
|
var table = instance.m_playersTable;
|
||||||
Dictionary<ulong, short> pings = (Dictionary<ulong, short>)m_pings.GetValue(__instance);
|
var pings = instance.pings;
|
||||||
|
|
||||||
if(playername == null)
|
playerName ??= MyMultiplayer.Static.GetMemberName(userId);
|
||||||
playername = MyMultiplayer.Static.GetMemberName(userId);
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(playername))
|
if (string.IsNullOrEmpty(playerName)) return false;
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
MyGuiControlTable.Row row = new MyGuiControlTable.Row(userId);
|
var row = new MyGuiControlTable.Row(userId);
|
||||||
string memberServiceName = MyMultiplayer.Static.GetMemberServiceName(userId);
|
var memberServiceName = MyMultiplayer.Static.GetMemberServiceName(userId);
|
||||||
StringBuilder text = new StringBuilder();
|
var text = new StringBuilder();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
MyGuiHighlightTexture? icon = new MyGuiHighlightTexture
|
MyGuiHighlightTexture? icon = new MyGuiHighlightTexture
|
||||||
{
|
{
|
||||||
Normal = "Textures\\GUI\\Icons\\Services\\" + memberServiceName + ".png",
|
Normal = $@"Textures\GUI\Icons\Services\{memberServiceName}.png",
|
||||||
Highlight = "Textures\\GUI\\Icons\\Services\\" + memberServiceName + ".png",
|
Highlight = $@"Textures\GUI\Icons\Services\{memberServiceName}.png",
|
||||||
SizePx = new Vector2(24f, 24f)
|
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(text, null, memberServiceName, Color.White, icon,
|
||||||
row.AddCell(new MyGuiControlTable.Cell(new StringBuilder(playername), playername));
|
MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_CENTER));
|
||||||
|
row.AddCell(new MyGuiControlTable.Cell(new StringBuilder(playerName), playerName));
|
||||||
|
|
||||||
|
|
||||||
if(playerId == 0)
|
if (playerId == 0)
|
||||||
playerId = Sync.Players.TryGetIdentityId(userId);
|
playerId = Sync.Players.TryGetIdentityId(userId);
|
||||||
|
|
||||||
|
|
||||||
|
var playerFaction = MySession.Static.Factions.GetPlayerFaction(playerId);
|
||||||
MyFaction playerFaction = MySession.Static.Factions.GetPlayerFaction(playerId);
|
var text2 = "";
|
||||||
string text2 = "";
|
var stringBuilder = new StringBuilder();
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
|
||||||
if (playerFaction != null)
|
if (playerFaction != null)
|
||||||
{
|
{
|
||||||
text2 += playerFaction.Name;
|
text2 += playerFaction.Name;
|
||||||
text2 = text2 + " | " + playername;
|
text2 = $"{text2} | {playerName}";
|
||||||
foreach (KeyValuePair<long, MyFactionMember> member in playerFaction.Members)
|
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))
|
||||||
{
|
{
|
||||||
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}";
|
||||||
{
|
|
||||||
text2 = text2 + " | " + player.DisplayName;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
stringBuilder.Append(MyStatControlText.SubstituteTexts(playerFaction.Name));
|
stringBuilder.Append(MyStatControlText.SubstituteTexts(playerFaction.Name));
|
||||||
if (playerFaction.IsLeader(playerId))
|
if (playerFaction.IsLeader(playerId))
|
||||||
{
|
|
||||||
stringBuilder.Append(" (").Append((object)MyTexts.Get(MyCommonTexts.Leader)).Append(")");
|
stringBuilder.Append(" (").Append((object)MyTexts.Get(MyCommonTexts.Leader)).Append(")");
|
||||||
|
if (!string.IsNullOrEmpty(playerFaction.Tag)) stringBuilder.Insert(0, $"[{playerFaction.Tag}] ");
|
||||||
}
|
}
|
||||||
if (!string.IsNullOrEmpty(playerFaction.Tag))
|
|
||||||
{
|
|
||||||
stringBuilder.Insert(0, "[" + playerFaction.Tag + "] ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
row.AddCell(new MyGuiControlTable.Cell(stringBuilder, null, text2));
|
row.AddCell(new MyGuiControlTable.Cell(stringBuilder, null, text2));
|
||||||
StringBuilder stringBuilder2 = new StringBuilder();
|
var stringBuilder2 = new StringBuilder();
|
||||||
MyPromoteLevel userPromoteLevel = MySession.Static.GetUserPromoteLevel(userId);
|
var userPromoteLevel = MySession.Static.GetUserPromoteLevel(userId);
|
||||||
for (int i = 0; i < (int)userPromoteLevel; i++)
|
for (var i = 0; i < (int)userPromoteLevel; i++) stringBuilder2.Append('*');
|
||||||
{
|
|
||||||
stringBuilder2.Append("*");
|
|
||||||
}
|
|
||||||
row.AddCell(new MyGuiControlTable.Cell(stringBuilder2));
|
row.AddCell(new MyGuiControlTable.Cell(stringBuilder2));
|
||||||
if (pings.ContainsKey(userId))
|
if (pings.TryGetValue(userId, out var ping))
|
||||||
{
|
row.AddCell(new MyGuiControlTable.Cell(new StringBuilder(ping.ToString())));
|
||||||
row.AddCell(new MyGuiControlTable.Cell(new StringBuilder(pings[userId].ToString())));
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
row.AddCell(new MyGuiControlTable.Cell(new StringBuilder("----")));
|
row.AddCell(new MyGuiControlTable.Cell(new StringBuilder("----")));
|
||||||
}
|
var cell = new MyGuiControlTable.Cell(new StringBuilder(""));
|
||||||
MyGuiControlTable.Cell cell = new MyGuiControlTable.Cell(new StringBuilder(""));
|
|
||||||
row.AddCell(cell);
|
row.AddCell(cell);
|
||||||
if (userId != Sync.MyId)
|
if (userId != Sync.MyId)
|
||||||
{
|
{
|
||||||
MyGuiControlButton myGuiControlButton = new MyGuiControlButton();
|
var myGuiControlButton = new MyGuiControlButton
|
||||||
myGuiControlButton.CustomStyle = m_buttonSizeStyleUnMuted;
|
|
||||||
myGuiControlButton.Size = new Vector2(0.03f, 0.04f);
|
|
||||||
myGuiControlButton.CueEnum = GuiSounds.None;
|
|
||||||
|
|
||||||
|
|
||||||
Action<MyGuiControlButton> btnClicked = delegate (MyGuiControlButton b)
|
|
||||||
{
|
{
|
||||||
m_OnToggleMutePressed.Invoke(__instance, new object[] { b });
|
CustomStyle = instance.m_buttonSizeStyleSilent,
|
||||||
|
Size = new Vector2(0.03f, 0.04f),
|
||||||
|
CueEnum = GuiSounds.None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
myGuiControlButton.ButtonClicked += instance.OnToggleMutePressed;
|
||||||
|
|
||||||
myGuiControlButton.ButtonClicked += btnClicked;
|
|
||||||
myGuiControlButton.UserData = userId;
|
myGuiControlButton.UserData = userId;
|
||||||
cell.Control = myGuiControlButton;
|
cell.Control = myGuiControlButton;
|
||||||
table.Controls.Add(myGuiControlButton);
|
table.Controls.Add(myGuiControlButton);
|
||||||
m_RefreshMuteIcons.Invoke(__instance, null);
|
instance.RefreshMuteIcons();
|
||||||
//RefreshMuteIcons();
|
//RefreshMuteIcons();
|
||||||
}
|
}
|
||||||
|
|
||||||
table.Add(row);
|
table.Add(row);
|
||||||
m_UpdateCaption.Invoke(__instance, null);
|
instance.UpdateCaption();
|
||||||
|
|
||||||
row.AddCell(new MyGuiControlTable.Cell(new StringBuilder(server), "Server Name"));
|
row.AddCell(new MyGuiControlTable.Cell(new StringBuilder(server), "Server Name"));
|
||||||
|
|
||||||
@@ -544,19 +429,12 @@ namespace SeamlessClientPlugin.Utilities
|
|||||||
|
|
||||||
private static bool UpdateCaption(MyGuiScreenPlayers __instance)
|
private static bool UpdateCaption(MyGuiScreenPlayers __instance)
|
||||||
{
|
{
|
||||||
if (MyMultiplayer.Static != null && MyMultiplayer.Static.IsLobby)
|
if (MyMultiplayer.Static != null && MyMultiplayer.Static.IsLobby) return true;
|
||||||
{
|
|
||||||
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 )";
|
//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, " )");
|
__instance.m_caption.Text =
|
||||||
|
$"Server: {_currentServerName} - Innstance Players ({CurrentPlayerCount} / {MySession.Static.MaxPlayers}) TotalPlayers: ({TotalPlayerCount})";
|
||||||
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -564,38 +442,22 @@ namespace SeamlessClientPlugin.Utilities
|
|||||||
|
|
||||||
private static int GamePingCompare(MyGuiControlTable.Cell a, MyGuiControlTable.Cell b)
|
private static int GamePingCompare(MyGuiControlTable.Cell a, MyGuiControlTable.Cell b)
|
||||||
{
|
{
|
||||||
if (!int.TryParse(a.Text.ToString(), out var result))
|
if (!int.TryParse(a.Text.ToString(), out var result)) result = -1;
|
||||||
{
|
if (!int.TryParse(b.Text.ToString(), out var result2)) result2 = -1;
|
||||||
result = -1;
|
|
||||||
}
|
|
||||||
if (!int.TryParse(b.Text.ToString(), out var result2))
|
|
||||||
{
|
|
||||||
result2 = -1;
|
|
||||||
}
|
|
||||||
return result.CompareTo(result2);
|
return result.CompareTo(result2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GameAdminCompare(MyGuiControlTable.Cell a, MyGuiControlTable.Cell b)
|
private static int GameAdminCompare(MyGuiControlTable.Cell a, MyGuiControlTable.Cell b)
|
||||||
{
|
{
|
||||||
ulong steamId = (ulong)a.Row.UserData;
|
var steamId = (ulong)a.Row.UserData;
|
||||||
ulong steamId2 = (ulong)b.Row.UserData;
|
var steamId2 = (ulong)b.Row.UserData;
|
||||||
int userPromoteLevel = (int)MySession.Static.GetUserPromoteLevel(steamId);
|
var userPromoteLevel = (int)MySession.Static.GetUserPromoteLevel(steamId);
|
||||||
int userPromoteLevel2 = (int)MySession.Static.GetUserPromoteLevel(steamId2);
|
var userPromoteLevel2 = (int)MySession.Static.GetUserPromoteLevel(steamId2);
|
||||||
return userPromoteLevel.CompareTo(userPromoteLevel2);
|
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
|
|
||||||
};
|
|
||||||
|
|
||||||
private static MethodInfo GetPatchMethod(string v)
|
private static MethodInfo GetPatchMethod(string v)
|
||||||
{
|
{
|
||||||
return typeof(OnlinePlayers).GetMethod(v, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
return typeof(OnlinePlayers).GetMethod(v, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -1,181 +1,152 @@
|
|||||||
using HarmonyLib;
|
using System.Reflection;
|
||||||
using Sandbox.Engine.Analytics;
|
using System.Text;
|
||||||
|
using HarmonyLib;
|
||||||
using Sandbox.Engine.Multiplayer;
|
using Sandbox.Engine.Multiplayer;
|
||||||
using Sandbox.Engine.Networking;
|
using Sandbox.Engine.Networking;
|
||||||
using Sandbox.Game;
|
|
||||||
using Sandbox.Game.Entities;
|
|
||||||
using Sandbox.Game.Gui;
|
using Sandbox.Game.Gui;
|
||||||
using Sandbox.Game.Multiplayer;
|
|
||||||
using Sandbox.Game.World;
|
using Sandbox.Game.World;
|
||||||
using Sandbox.Game.World.Generator;
|
using Sandbox.Game.World.Generator;
|
||||||
using Sandbox.Graphics;
|
using Sandbox.Graphics;
|
||||||
using Sandbox.Graphics.GUI;
|
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;
|
||||||
using VRage.Collections;
|
|
||||||
using VRage.FileSystem;
|
using VRage.FileSystem;
|
||||||
using VRage.Game;
|
using VRage.Game;
|
||||||
using VRage.Game.Entity;
|
|
||||||
using VRage.GameServices;
|
using VRage.GameServices;
|
||||||
using VRage.Network;
|
using VRage.Network;
|
||||||
using VRage.Utils;
|
using VRage.Utils;
|
||||||
using VRageMath;
|
using VRageMath;
|
||||||
|
|
||||||
namespace SeamlessClientPlugin.SeamlessTransfer
|
namespace SeamlessClientPlugin.Utilities;
|
||||||
|
|
||||||
|
public static class Patches
|
||||||
{
|
{
|
||||||
public static class Patches
|
|
||||||
{
|
|
||||||
/* 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");
|
|
||||||
|
|
||||||
/* Harmony Patcher */
|
/* Harmony Patcher */
|
||||||
private static Harmony Patcher = new Harmony("SeamlessClientPatcher");
|
private static readonly Harmony Patcher = new("SeamlessClientPatcher");
|
||||||
|
|
||||||
|
/* WorldGenerator */
|
||||||
/* Static Contructors */
|
public static MethodInfo UnloadProceduralWorldGenerator { get; private set; }
|
||||||
public static ConstructorInfo ClientConstructor { get; private set; }
|
|
||||||
public static ConstructorInfo SyncLayerConstructor { get; private set; }
|
|
||||||
public static ConstructorInfo TransportLayerConstructor { get; private set; }
|
|
||||||
public static ConstructorInfo MySessionConstructor { get; private set; }
|
|
||||||
public static ConstructorInfo MyMultiplayerClientBaseConstructor { get; private set; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Static FieldInfos and PropertyInfos */
|
|
||||||
public static PropertyInfo MySessionLayer { get; private set; }
|
|
||||||
public static FieldInfo 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; }
|
|
||||||
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
|
|
||||||
|
|
||||||
public static event EventHandler<JoinResultMsg> OnJoinEvent;
|
public static event EventHandler<JoinResultMsg> OnJoinEvent;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* WorldGenerator */
|
|
||||||
public static MethodInfo UnloadProceduralWorldGenerator;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static FieldInfo MBuffer;
|
|
||||||
|
|
||||||
public static void GetPatches()
|
public static void GetPatches()
|
||||||
{
|
{
|
||||||
//Get reflected values and store them
|
//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 */
|
/* Get Methods */
|
||||||
MethodInfo OnJoin = GetMethod(ClientType, "OnUserJoined", BindingFlags.NonPublic | BindingFlags.Instance);
|
var onJoin = GetMethod(typeof(MyMultiplayerClient), "OnUserJoined", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
MethodInfo LoadingAction = GetMethod(typeof(MySessionLoader), "LoadMultiplayerSession", BindingFlags.Public | BindingFlags.Static);
|
var loadingAction = GetMethod(typeof(MySessionLoader), "LoadMultiplayerSession",
|
||||||
InitVirtualClients = GetMethod(VirtualClientsType, "Init", BindingFlags.Instance | BindingFlags.Public);
|
BindingFlags.Public | BindingFlags.Static);
|
||||||
LoadPlayerInternal = GetMethod(typeof(MyPlayerCollection), "LoadPlayerInternal", BindingFlags.Instance | BindingFlags.NonPublic);
|
UnloadProceduralWorldGenerator = GetMethod(typeof(MyProceduralWorldGenerator), "UnloadData",
|
||||||
LoadMembersFromWorld = GetMethod(typeof(MySession), "LoadMembersFromWorld", BindingFlags.NonPublic | BindingFlags.Instance);
|
BindingFlags.Instance | BindingFlags.NonPublic);
|
||||||
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 ConnectToServer = GetMethod(typeof(MyGameService), "ConnectToServer", BindingFlags.Static | BindingFlags.Public);
|
||||||
MethodInfo LoadingScreenDraw = GetMethod(typeof(MyGuiScreenLoading), "DrawInternal", BindingFlags.Instance | BindingFlags.NonPublic);
|
var loadingScreenDraw = GetMethod(typeof(MyGuiScreenLoading), "DrawInternal",
|
||||||
|
BindingFlags.Instance | BindingFlags.NonPublic);
|
||||||
|
|
||||||
|
|
||||||
//Test patches
|
//Test patches
|
||||||
//MethodInfo SetPlayerDed = GetMethod(typeof(MyPlayerCollection), "SetPlayerDeadInternal", BindingFlags.Instance | BindingFlags.NonPublic);
|
//MethodInfo SetPlayerDed = GetMethod(typeof(MyPlayerCollection), "SetPlayerDeadInternal", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||||
|
|
||||||
|
|
||||||
|
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(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))));
|
//Patcher.Patch(SetPlayerDed, prefix: new HarmonyMethod(GetPatchMethod(nameof(SetPlayerDeadInternal))));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static MethodInfo GetPatchMethod(string v)
|
private static MethodInfo GetPatchMethod(string v)
|
||||||
{
|
{
|
||||||
return typeof(Patches).GetMethod(v, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
return typeof(Patches).GetMethod(v, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
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
|
#region LoadingScreen
|
||||||
|
|
||||||
/* Loading Screen Stuff */
|
/* Loading Screen Stuff */
|
||||||
|
|
||||||
private static string LoadingScreenTexture = null;
|
private static string _loadingScreenTexture;
|
||||||
private static string ServerName;
|
private static string _serverName;
|
||||||
|
|
||||||
private static bool LoadMultiplayerSession(MyObjectBuilder_World world, MyMultiplayerBase multiplayerSession)
|
private static bool LoadMultiplayerSession(MyObjectBuilder_World world, MyMultiplayerBase multiplayerSession)
|
||||||
{
|
{
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
MyLog.Default.WriteLine("LoadSession() - Start");
|
MyLog.Default.WriteLine("LoadSession() - Start");
|
||||||
if (!MyWorkshop.CheckLocalModsAllowed(world.Checkpoint.Mods, allowLocalMods: false))
|
if (!MyWorkshop.CheckLocalModsAllowed(world.Checkpoint.Mods, false))
|
||||||
{
|
{
|
||||||
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateMessageBox(MyMessageBoxStyleEnum.Error, MyMessageBoxButtonsType.OK, messageCaption: MyTexts.Get(MyCommonTexts.MessageBoxCaptionError), messageText: MyTexts.Get(MyCommonTexts.DialogTextLocalModsDisabledInMultiplayer)));
|
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateMessageBox(
|
||||||
|
messageCaption: MyTexts.Get(MyCommonTexts.MessageBoxCaptionError),
|
||||||
|
messageText: MyTexts.Get(MyCommonTexts.DialogTextLocalModsDisabledInMultiplayer)));
|
||||||
MyLog.Default.WriteLine("LoadSession() - End");
|
MyLog.Default.WriteLine("LoadSession() - End");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -183,10 +154,9 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
MyLog.Default.WriteLine("Seamless Downloading mods!");
|
MyLog.Default.WriteLine("Seamless Downloading mods!");
|
||||||
|
|
||||||
|
|
||||||
|
MyWorkshop.DownloadModsAsync(world.Checkpoint.Mods, result =>
|
||||||
MyWorkshop.DownloadModsAsync(world.Checkpoint.Mods, delegate (bool success)
|
|
||||||
{
|
{
|
||||||
if (success)
|
if (result == MyGameServiceCallResult.OK)
|
||||||
{
|
{
|
||||||
MyScreenManager.CloseAllScreensNowExcept(null);
|
MyScreenManager.CloseAllScreensNowExcept(null);
|
||||||
MyGuiSandbox.Update(16);
|
MyGuiSandbox.Update(16);
|
||||||
@@ -196,32 +166,33 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
MySession.Static = null;
|
MySession.Static = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerName = multiplayerSession.HostName;
|
_serverName = multiplayerSession.HostName;
|
||||||
GetCustomLoadingScreenPath(world.Checkpoint.Mods, out LoadingScreenTexture);
|
GetCustomLoadingScreenPath(world.Checkpoint.Mods, out _loadingScreenTexture);
|
||||||
|
|
||||||
|
|
||||||
MySessionLoader.StartLoading(delegate
|
MySessionLoader.StartLoading(delegate
|
||||||
{
|
{
|
||||||
|
MySession.LoadMultiplayer(world, multiplayerSession);
|
||||||
LoadMultiplayer.Invoke(null, new object[] { world, multiplayerSession });
|
});
|
||||||
//MySession.LoadMultiplayer(world, multiplayerSession);
|
|
||||||
}, null, null, null);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
multiplayerSession.Dispose();
|
multiplayerSession.Dispose();
|
||||||
MySessionLoader.UnloadAndExitToMenu();
|
MySessionLoader.UnloadAndExitToMenu();
|
||||||
if (MyGameService.IsOnline)
|
if (MyGameService.IsOnline)
|
||||||
{
|
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateMessageBox(
|
||||||
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateMessageBox(MyMessageBoxStyleEnum.Error, MyMessageBoxButtonsType.OK, messageCaption: MyTexts.Get(MyCommonTexts.MessageBoxCaptionError), messageText: MyTexts.Get(MyCommonTexts.DialogTextDownloadModsFailed)));
|
messageCaption: MyTexts.Get(MyCommonTexts.MessageBoxCaptionError),
|
||||||
}
|
messageText: MyTexts.Get(MyCommonTexts.DialogTextDownloadModsFailed)));
|
||||||
else
|
else
|
||||||
{
|
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateMessageBox(
|
||||||
MyGuiSandbox.AddScreen(MyGuiSandbox.CreateMessageBox(MyMessageBoxStyleEnum.Error, MyMessageBoxButtonsType.OK, messageCaption: MyTexts.Get(MyCommonTexts.MessageBoxCaptionError), messageText: new StringBuilder(string.Format(MyTexts.GetString(MyCommonTexts.DialogTextDownloadModsFailedSteamOffline), MySession.GameServiceName))));
|
messageCaption: MyTexts.Get(MyCommonTexts.MessageBoxCaptionError),
|
||||||
}
|
messageText: new StringBuilder(string.Format(
|
||||||
|
MyTexts.GetString(MyCommonTexts.DialogTextDownloadModsFailedSteamOffline),
|
||||||
|
MySession.GameServiceName))));
|
||||||
}
|
}
|
||||||
|
|
||||||
MyLog.Default.WriteLine("LoadSession() - End");
|
MyLog.Default.WriteLine("LoadSession() - End");
|
||||||
}, delegate
|
}, () =>
|
||||||
{
|
{
|
||||||
multiplayerSession.Dispose();
|
multiplayerSession.Dispose();
|
||||||
MySessionLoader.UnloadAndExitToMenu();
|
MySessionLoader.UnloadAndExitToMenu();
|
||||||
@@ -232,34 +203,37 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
|
|
||||||
private static bool DrawInternal(MyGuiScreenLoading __instance)
|
private static bool DrawInternal(MyGuiScreenLoading __instance)
|
||||||
{
|
{
|
||||||
|
|
||||||
//If we dont have a custom loading screen texture, do not do the special crap below
|
//If we dont have a custom loading screen texture, do not do the special crap below
|
||||||
if (string.IsNullOrEmpty(LoadingScreenTexture))
|
if (string.IsNullOrEmpty(_loadingScreenTexture))
|
||||||
return true;
|
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);
|
//MyGuiControlMultilineText m_multiTextControl = (MyGuiControlMultilineText)typeof(MyGuiScreenLoading).GetField("m_multiTextControl", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(__instance);
|
||||||
|
|
||||||
|
|
||||||
Color color = new Color(255, 255, 255, 250);
|
var color = new Color(255, 255, 255, 250);
|
||||||
color.A = (byte)((float)(int)color.A * m_transitionAlpha);
|
color.A = (byte)(color.A * __instance.m_transitionAlpha);
|
||||||
Rectangle fullscreenRectangle = MyGuiManager.GetFullscreenRectangle();
|
var fullscreenRectangle = MyGuiManager.GetFullscreenRectangle();
|
||||||
MyGuiManager.DrawSpriteBatch("Textures\\GUI\\Blank.dds", fullscreenRectangle, Color.Black, false, true);
|
MyGuiManager.DrawSpriteBatch(@"Textures\GUI\Blank.dds", fullscreenRectangle, Color.Black, false, true);
|
||||||
Rectangle outRect;
|
MyGuiManager.GetSafeHeightFullScreenPictureSize(MyGuiConstants.LOADING_BACKGROUND_TEXTURE_REAL_SIZE,
|
||||||
MyGuiManager.GetSafeHeightFullScreenPictureSize(MyGuiConstants.LOADING_BACKGROUND_TEXTURE_REAL_SIZE, out outRect);
|
out var outRect);
|
||||||
MyGuiManager.DrawSpriteBatch(LoadingScreenTexture, outRect, new Color(new Vector4(1f, 1f, 1f, m_transitionAlpha)), true, true);
|
MyGuiManager.DrawSpriteBatch(_loadingScreenTexture, outRect,
|
||||||
MyGuiManager.DrawSpriteBatch("Textures\\Gui\\Screens\\screen_background_fade.dds", outRect, new Color(new Vector4(1f, 1f, 1f, m_transitionAlpha)), true, true);
|
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));
|
//MyGuiSandbox.DrawGameLogoHandler(m_transitionAlpha, MyGuiManager.ComputeFullscreenGuiCoordinate(MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, 44, 68));
|
||||||
|
|
||||||
string LoadScreen = $"Loading into {ServerName}! Please wait!";
|
var 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(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(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);
|
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))
|
if (string.IsNullOrEmpty(m_customTextFromConstructor))
|
||||||
@@ -279,38 +253,35 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static bool GetCustomLoadingScreenPath(List<MyObjectBuilder_Checkpoint.ModItem> mods, out string file)
|
||||||
private static bool GetCustomLoadingScreenPath(List<MyObjectBuilder_Checkpoint.ModItem> Mods, out string File)
|
|
||||||
{
|
{
|
||||||
File = null;
|
file = null;
|
||||||
string WorkshopDir = MyFileSystem.ModsPath;
|
var workshopDir = MyFileSystem.ModsPath;
|
||||||
List<string> backgrounds = new List<string>();
|
var backgrounds = new List<string>();
|
||||||
Random r = new Random(DateTime.Now.Millisecond);
|
SeamlessClient.TryShow(workshopDir);
|
||||||
SeamlessClient.TryShow(WorkshopDir);
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
SeamlessClient.TryShow("Installed Mods: " + Mods);
|
SeamlessClient.TryShow($"Installed Mods: ({mods.Count}) [{string.Join(", ", mods.Select(x => x.FriendlyName))}]");
|
||||||
foreach (var Mod in Mods)
|
foreach (var mod in mods)
|
||||||
{
|
{
|
||||||
string SearchDir = Mod.GetPath();
|
var searchDir = mod.GetPath();
|
||||||
|
|
||||||
if (!Directory.Exists(SearchDir))
|
if (!Directory.Exists(searchDir))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
||||||
var files = Directory.GetFiles(SearchDir, "CustomLoadingBackground*.dds", SearchOption.TopDirectoryOnly);
|
var files = Directory.GetFiles(searchDir, "CustomLoadingBackground*.dds",
|
||||||
foreach (var file in files)
|
SearchOption.TopDirectoryOnly);
|
||||||
|
foreach (var filePath in files)
|
||||||
{
|
{
|
||||||
// Adds all files containing CustomLoadingBackground to a list for later randomisation
|
// Adds all files containing CustomLoadingBackground to a list for later randomisation
|
||||||
SeamlessClient.TryShow(Mod.FriendlyName + " contains a custom loading background!");
|
SeamlessClient.TryShow($"{mod.FriendlyName} contains a custom loading background!");
|
||||||
backgrounds.Add(file);
|
backgrounds.Add(filePath);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Randomly pick a loading screen from the available backgrounds
|
// Randomly pick a loading screen from the available backgrounds
|
||||||
var rInt = r.Next(0, backgrounds.Count() - 1);
|
file = backgrounds[Random.Shared.Next(0, backgrounds.Count - 1)];
|
||||||
File = backgrounds[rInt];
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -323,117 +294,4 @@ namespace SeamlessClientPlugin.SeamlessTransfer
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#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;
|
|
||||||
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Patch Utils */
|
|
||||||
|
|
||||||
private static MethodInfo GetMethod(Type type, string MethodName, BindingFlags Flags)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -1,16 +1,9 @@
|
|||||||
using HarmonyLib;
|
using ProtoBuf;
|
||||||
using ProtoBuf;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
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)
|
if (instance == null)
|
||||||
@@ -29,18 +22,11 @@ namespace SeamlessClientPlugin.Utilities
|
|||||||
public static T Deserialize<T>(byte[] data)
|
public static T Deserialize<T>(byte[] data)
|
||||||
{
|
{
|
||||||
if (data == null)
|
if (data == null)
|
||||||
return default(T);
|
return default;
|
||||||
|
|
||||||
using (var m = new MemoryStream(data))
|
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