Add project files.
This commit is contained in:
68
Messages/ClientMessages.cs
Normal file
68
Messages/ClientMessages.cs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
using ProtoBuf;
|
||||||
|
using Sandbox.Game.World;
|
||||||
|
using Sandbox.ModAPI;
|
||||||
|
using SeamlessClientPlugin.Utilities;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SeamlessClientPlugin.ClientMessages
|
||||||
|
{
|
||||||
|
public enum ClientMessageType
|
||||||
|
{
|
||||||
|
FirstJoin,
|
||||||
|
TransferServer
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[ProtoContract]
|
||||||
|
public class ClientMessage
|
||||||
|
{
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public ClientMessageType MessageType;
|
||||||
|
[ProtoMember(2)]
|
||||||
|
public byte[] MessageData;
|
||||||
|
[ProtoMember(3)]
|
||||||
|
public long IdentityID;
|
||||||
|
[ProtoMember(4)]
|
||||||
|
public ulong SteamID;
|
||||||
|
[ProtoMember(5)]
|
||||||
|
public string PluginVersion = "0";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public ClientMessage(ClientMessageType Type)
|
||||||
|
{
|
||||||
|
MessageType = Type;
|
||||||
|
|
||||||
|
if (!MyAPIGateway.Multiplayer.IsServer)
|
||||||
|
{
|
||||||
|
if(MyAPIGateway.Session.LocalHumanPlayer == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentityID = MySession.Static?.LocalHumanPlayer?.Identity?.IdentityId ?? 0;
|
||||||
|
SteamID = MySession.Static?.LocalHumanPlayer?.Id.SteamId ?? 0;
|
||||||
|
PluginVersion = SeamlessClient.Version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientMessage() { }
|
||||||
|
|
||||||
|
public void SerializeData<T>(T Data)
|
||||||
|
{
|
||||||
|
MessageData = Utility.Serialize(Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T DeserializeData<T>()
|
||||||
|
{
|
||||||
|
if (MessageData == null)
|
||||||
|
return default(T);
|
||||||
|
|
||||||
|
return Utility.Deserialize<T>(MessageData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
Messages/WorldRequest.cs
Normal file
65
Messages/WorldRequest.cs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
using NLog;
|
||||||
|
using ProtoBuf;
|
||||||
|
using Sandbox.Engine.Multiplayer;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using VRage.Game;
|
||||||
|
using VRage.ObjectBuilders;
|
||||||
|
|
||||||
|
namespace SeamlessClientPlugin.ClientMessages
|
||||||
|
{
|
||||||
|
[ProtoContract]
|
||||||
|
public class WorldRequest
|
||||||
|
{
|
||||||
|
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public ulong PlayerID;
|
||||||
|
[ProtoMember(2)]
|
||||||
|
public long IdentityID;
|
||||||
|
[ProtoMember(3)]
|
||||||
|
public string PlayerName;
|
||||||
|
[ProtoMember(4)]
|
||||||
|
public byte[] WorldData;
|
||||||
|
|
||||||
|
public WorldRequest(ulong PlayerID,long PlayerIdentity, string Name)
|
||||||
|
{
|
||||||
|
this.PlayerID = PlayerID;
|
||||||
|
this.PlayerName = Name;
|
||||||
|
this.IdentityID = PlayerIdentity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WorldRequest() { }
|
||||||
|
|
||||||
|
public void SerializeWorldData(MyObjectBuilder_World WorldData)
|
||||||
|
{
|
||||||
|
MethodInfo CleanupData = typeof(MyMultiplayerServerBase).GetMethod("CleanUpData", BindingFlags.Static | BindingFlags.NonPublic, null, new Type[3]
|
||||||
|
{
|
||||||
|
typeof(MyObjectBuilder_World),
|
||||||
|
typeof(ulong),
|
||||||
|
typeof(long),
|
||||||
|
}, null);
|
||||||
|
object[] Data = new object[] { WorldData, PlayerID, IdentityID };
|
||||||
|
CleanupData.Invoke(null, Data);
|
||||||
|
WorldData = (MyObjectBuilder_World)Data[0];
|
||||||
|
using (MemoryStream memoryStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
MyObjectBuilderSerializer.SerializeXML(memoryStream, WorldData, MyObjectBuilderSerializer.XmlCompression.Gzip);
|
||||||
|
this.WorldData = memoryStream.ToArray();
|
||||||
|
Log.Warn("Successfully Converted World");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MyObjectBuilder_World DeserializeWorldData()
|
||||||
|
{
|
||||||
|
MyObjectBuilderSerializer.DeserializeGZippedXML<MyObjectBuilder_World>(new MemoryStream(WorldData), out var objectBuilder);
|
||||||
|
return objectBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
36
Properties/AssemblyInfo.cs
Normal file
36
Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
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("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[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.0.0.0")]
|
||||||
|
[assembly: AssemblyFileVersion("1.0.0.0")]
|
226
SeamlessClient.cs
Normal file
226
SeamlessClient.cs
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
using Sandbox.Engine.Multiplayer;
|
||||||
|
using Sandbox.Engine.Networking;
|
||||||
|
using Sandbox.Game;
|
||||||
|
using Sandbox.Game.Entities;
|
||||||
|
using Sandbox.Game.Gui;
|
||||||
|
using Sandbox.Game.Multiplayer;
|
||||||
|
using Sandbox.Game.World;
|
||||||
|
using Sandbox.Graphics.GUI;
|
||||||
|
using Sandbox.ModAPI;
|
||||||
|
using SeamlessClientPlugin.ClientMessages;
|
||||||
|
using SeamlessClientPlugin.SeamlessTransfer;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Timers;
|
||||||
|
using VRage.Input;
|
||||||
|
using VRage.Plugins;
|
||||||
|
using VRage.Utils;
|
||||||
|
using VRageRender;
|
||||||
|
|
||||||
|
namespace SeamlessClientPlugin
|
||||||
|
{
|
||||||
|
|
||||||
|
//SendAllMembersDataToClient
|
||||||
|
|
||||||
|
public class SeamlessClient : IPlugin
|
||||||
|
{
|
||||||
|
/* First Step. How does a player join a game?
|
||||||
|
* First JoinGameInternal is called with the ServersLobbyID.
|
||||||
|
* In this method, MySessionLoader.UnloadAndExitToMenu() is called. Ultimatley we want to prevent this as we dont want to unload the entire game. Just the basic neccessities.
|
||||||
|
* Then JoinLobby is called. This looks to be simply a check to see if the client gets a result from the server. If it does, it initilizes a new STATIC/Multiplayer base by: [Static = new MyMultiplayerLobbyClient(lobby, new MySyncLayer(new MyTransportLayer(2)))));]
|
||||||
|
* Once this above method is done and join is done, we begin the OnJoin()
|
||||||
|
*
|
||||||
|
* On join begins by checking join result. Success downloads world and failed sends you back to menu. (May need to use this to send players to menu)
|
||||||
|
* Download world Requires the multiplayerbase and MyGUIScreenProgress. Which is essentially checking that the user hasnt cliecked left or closed.
|
||||||
|
*
|
||||||
|
* StringBuilder text = MyTexts.Get(MyCommonTexts.DialogTextJoiningWorld);
|
||||||
|
* MyGuiScreenProgress progress = new MyGuiScreenProgress(text, MyCommonTexts.Cancel);
|
||||||
|
*
|
||||||
|
* This just looks to be like what happens still with the little GUI popupscreen before load starts
|
||||||
|
*
|
||||||
|
* DownloadWorld also contains a method to get the serversessionstate too. We will need to check this before load. Ultimatley once everything has passed join checks Downloadworld is called. (MyMultiplayerClientBase.DownloadWorld)
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* MyMultiplayerClientBase.DownloadWorld simply rasies a static event to the server for world request. [return MyMultiplayerServerBase.WorldRequest;]
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* MyMultiplayerServerBase.WorldRequest (WorldRequest) This is near the start of where the magic happens. THIS IS ALL RAN SERVERSIDE
|
||||||
|
* Checks to see if the client has been kicked or banned to prevent world requests. Not sure we really need to worry about this.
|
||||||
|
*
|
||||||
|
* Server gets world clears non important data such as player gps. Our player gps gets added on join so we can yeet this.
|
||||||
|
* Also theres a sendfluch via transport layer. Might need to keep this in mind and use this for our testings
|
||||||
|
* Theres a CleanUpData that gets called with world and playerid/identity ID. This is probably to limit things and only send whats neccessary
|
||||||
|
* CLEANUPDATA shows me how to send allplayers data synced on the server.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Once we have everythings we use a MS to serialize everything to byte[] and via replication layer we send world to client.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* BACK TO CLIENT:
|
||||||
|
* RecieveWorld is called and packet is deserialized and then MyJoinGameHelper.WorldReceived(...,...) is called
|
||||||
|
*
|
||||||
|
* WorldReceived does some checks for extra version mismatches and to make sure the CheckPointObjectBuilder isnt null etc.
|
||||||
|
* Once it passes all these checks, CheckDx11AndJoin(world, MyMutliplayerBase) is called
|
||||||
|
*
|
||||||
|
* CheckDx11AndJoin just checks if its a scenario world or not. Forget about this. We can figure that out all later. Then it runs: MySessionLoader.LoadMultiplayerSession(world, multiplayer);
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* MySessionLoader.LoadMultiplayerSession looks to be the start of the join code. It also checks for mod mismatches. (And Downloads them). However once it passes this, MySession.LoadMultiplayer is called.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* MySession.LoadMultiplayer (The most important step in world loading)
|
||||||
|
* Creates new MySession.
|
||||||
|
* Does settings stuff.
|
||||||
|
* LoadMembersFromWorld (Loads Clients)
|
||||||
|
* FixSessionComponentObjectBuilders
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* PrepareBaseSession is something we need. Looks like it does some weird stuff to init fonts, Sector Enviroment settings, Loading datacomponents from world, and re-initilizes modAPI stuff
|
||||||
|
* DeserializeClusters
|
||||||
|
* Loads planets.
|
||||||
|
* RegistersChat
|
||||||
|
*
|
||||||
|
* LOADWORLD -----------
|
||||||
|
* Static.BeforeStartComponents
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* -plugin "../Plugins/SeamlessClientPlugin.dll"
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static string Version = "1.0.0";
|
||||||
|
private bool Initilized = false;
|
||||||
|
private bool SentPingResponse = false;
|
||||||
|
public const ushort SeamlessClientNetID = 2936;
|
||||||
|
private Timer PingTimer = new Timer(2000);
|
||||||
|
|
||||||
|
public static LoadServer Server = new LoadServer();
|
||||||
|
|
||||||
|
|
||||||
|
public static bool IsSwitching = false;
|
||||||
|
public static bool RanJoin = false;
|
||||||
|
|
||||||
|
public static Action JoinAction = () => { };
|
||||||
|
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
#pragma warning disable CS0618 // Type or member is obsolete
|
||||||
|
MyAPIGateway.Multiplayer?.UnregisterMessageHandler(SeamlessClientNetID, MessageHandler);
|
||||||
|
#pragma warning restore CS0618 // Type or member is obsolete
|
||||||
|
Initilized = false;
|
||||||
|
SentPingResponse = false;
|
||||||
|
PingTimer.Stop();
|
||||||
|
//throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Init(object gameInstance)
|
||||||
|
{
|
||||||
|
TryShow("Running Seamless Client Plugin v[" + Version + "]");
|
||||||
|
// Reload = new ReloadPatch();
|
||||||
|
//Patching goes here
|
||||||
|
|
||||||
|
|
||||||
|
PingTimer.Elapsed += PingTimer_Elapsed;
|
||||||
|
PingTimer.Start();
|
||||||
|
//throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PingTimer_Elapsed(object sender, ElapsedEventArgs e)
|
||||||
|
{
|
||||||
|
//TryShow("Sending PluginVersion to Server!");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
ClientMessage PingServer = new ClientMessage(ClientMessageType.FirstJoin);
|
||||||
|
MyAPIGateway.Multiplayer.SendMessageToServer(SeamlessClientNetID, Utilities.Utility.Serialize(PingServer));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
TryShow(ex.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
if (MyAPIGateway.Multiplayer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!Initilized)
|
||||||
|
{
|
||||||
|
TryShow("Initilizing Communications!");
|
||||||
|
RunInitilizations();
|
||||||
|
Initilized = true;
|
||||||
|
}
|
||||||
|
//OnNewPlayerRequest
|
||||||
|
//throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static void RunInitilizations()
|
||||||
|
{
|
||||||
|
#pragma warning disable CS0618 // Type or member is obsolete
|
||||||
|
MyAPIGateway.Multiplayer.RegisterMessageHandler(SeamlessClientNetID, MessageHandler);
|
||||||
|
#pragma warning restore CS0618 // Type or member is obsolete
|
||||||
|
//We need to initiate ping request
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DisposeInitilizations()
|
||||||
|
{
|
||||||
|
MyAPIGateway.Multiplayer.UnregisterMessageHandler(SeamlessClientNetID, MessageHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static void MessageHandler(byte[] bytes)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ClientMessage Recieved = Utilities.Utility.Deserialize<ClientMessage>(bytes);
|
||||||
|
if (Recieved.MessageType == ClientMessageType.TransferServer)
|
||||||
|
{
|
||||||
|
Transfer TransferMessage = Recieved.DeserializeData<Transfer>();
|
||||||
|
IsSwitching = true;
|
||||||
|
TransferMessage.PingServerAndBeginRedirect();
|
||||||
|
RanJoin = false;
|
||||||
|
//DisposeInitilizations();
|
||||||
|
}
|
||||||
|
else if (Recieved.MessageType == ClientMessageType.FirstJoin)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
TryShow(ex.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static void TryShow(string message)
|
||||||
|
{
|
||||||
|
if (MySession.Static?.LocalHumanPlayer != null)
|
||||||
|
MyAPIGateway.Utilities?.ShowMessage("NetworkClient", message);
|
||||||
|
|
||||||
|
MyLog.Default?.WriteLineAndConsole($"SeamlessClient: {message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
158
SeamlessClientPlugin.csproj
Normal file
158
SeamlessClientPlugin.csproj
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{102A3D80-B588-43BA-B686-000FA8FF1A0C}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||||
|
<RootNamespace>SeamlessClientPlugin</RootNamespace>
|
||||||
|
<AssemblyName>SeamlessClientPlugin</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<Deterministic>true</Deterministic>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
|
<NuGetPackageImportStamp>
|
||||||
|
</NuGetPackageImportStamp>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>..\..\..\..\..\Program Files %28x86%29\Steam\steamapps\common\SpaceEngineers\Plugins\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="0Harmony, Version=2.0.4.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
|
<HintPath>..\Nexus\packages\Lib.Harmony.2.0.4\lib\net472\0Harmony.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="NLog">
|
||||||
|
<HintPath>..\..\..\Desktop\TorchServers\torch-server\NLog.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="ProtoBuf.Net.Core">
|
||||||
|
<HintPath>..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\SpaceEngineers\Bin64\ProtoBuf.Net.Core.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Sandbox.Common">
|
||||||
|
<HintPath>..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\SpaceEngineers\Bin64\Sandbox.Common.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Sandbox.Game">
|
||||||
|
<HintPath>..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\SpaceEngineers\Bin64\Sandbox.Game.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Sandbox.Game.XmlSerializers">
|
||||||
|
<HintPath>..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\SpaceEngineers\Bin64\Sandbox.Game.XmlSerializers.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Sandbox.Graphics, Version=0.1.1.0, Culture=neutral, processorArchitecture=AMD64">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\SpaceEngineers\Bin64\Sandbox.Graphics.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="SpaceEngineers.Game">
|
||||||
|
<HintPath>..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\SpaceEngineers\Bin64\SpaceEngineers.Game.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="SpaceEngineers.ObjectBuilders">
|
||||||
|
<HintPath>..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\SpaceEngineers\Bin64\SpaceEngineers.ObjectBuilders.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="SpaceEngineers.ObjectBuilders.XmlSerializers">
|
||||||
|
<HintPath>..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\SpaceEngineers\Bin64\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.Runtime.InteropServices.RuntimeInformation">
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<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>..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\SpaceEngineers\Bin64\VRage.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="VRage.Audio">
|
||||||
|
<HintPath>..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\SpaceEngineers\Bin64\VRage.Audio.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="VRage.Game">
|
||||||
|
<HintPath>..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\SpaceEngineers\Bin64\VRage.Game.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="VRage.Input">
|
||||||
|
<HintPath>..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\SpaceEngineers\Bin64\VRage.Input.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="VRage.Library">
|
||||||
|
<HintPath>..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\SpaceEngineers\Bin64\VRage.Library.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="VRage.Math">
|
||||||
|
<HintPath>..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\SpaceEngineers\Bin64\VRage.Math.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="VRage.Render">
|
||||||
|
<HintPath>..\..\..\Desktop\TorchServers\torch-server\DedicatedServer64\VRage.Render.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="VRage.Steam">
|
||||||
|
<HintPath>..\..\..\Desktop\TorchServers\torch-server\DedicatedServer64\VRage.Steam.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="VRage.UserInterface">
|
||||||
|
<HintPath>..\..\..\..\..\Program Files (x86)\Steam\steamapps\common\SpaceEngineers\Bin64\VRage.UserInterface.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Messages\ClientMessages.cs" />
|
||||||
|
<Compile Include="Messages\WorldRequest.cs" />
|
||||||
|
<Compile Include="SeamlessClient.cs" />
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="SeamlessTransfer\LoadServer.cs" />
|
||||||
|
<Compile Include="SeamlessTransfer\Transfer.cs" />
|
||||||
|
<Compile Include="Utilities\Utility.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
</Project>
|
25
SeamlessClientPlugin.sln
Normal file
25
SeamlessClientPlugin.sln
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 16
|
||||||
|
VisualStudioVersion = 16.0.31005.135
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SeamlessClientPlugin", "SeamlessClientPlugin.csproj", "{102A3D80-B588-43BA-B686-000FA8FF1A0C}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{102A3D80-B588-43BA-B686-000FA8FF1A0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{102A3D80-B588-43BA-B686-000FA8FF1A0C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{102A3D80-B588-43BA-B686-000FA8FF1A0C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{102A3D80-B588-43BA-B686-000FA8FF1A0C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {8B085CF6-FC14-48F2-B9F2-4D88971D74DD}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
551
SeamlessTransfer/LoadServer.cs
Normal file
551
SeamlessTransfer/LoadServer.cs
Normal file
@@ -0,0 +1,551 @@
|
|||||||
|
using HarmonyLib;
|
||||||
|
using Sandbox;
|
||||||
|
using Sandbox.Definitions;
|
||||||
|
using Sandbox.Engine;
|
||||||
|
using Sandbox.Engine.Multiplayer;
|
||||||
|
using Sandbox.Engine.Networking;
|
||||||
|
using Sandbox.Engine.Physics;
|
||||||
|
using Sandbox.Engine.Utils;
|
||||||
|
using Sandbox.Engine.Voxels;
|
||||||
|
using Sandbox.Game;
|
||||||
|
using Sandbox.Game.Entities;
|
||||||
|
using Sandbox.Game.Gui;
|
||||||
|
using Sandbox.Game.GUI;
|
||||||
|
using Sandbox.Game.Multiplayer;
|
||||||
|
using Sandbox.Game.Screens.Helpers;
|
||||||
|
using Sandbox.Game.SessionComponents;
|
||||||
|
using Sandbox.Game.World;
|
||||||
|
using Sandbox.Graphics.GUI;
|
||||||
|
using Sandbox.ModAPI;
|
||||||
|
using SeamlessClientPlugin.Utilities;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using VRage;
|
||||||
|
using VRage.Game;
|
||||||
|
using VRage.Game.ModAPI;
|
||||||
|
using VRage.Game.SessionComponents;
|
||||||
|
using VRage.Game.Voxels;
|
||||||
|
using VRage.GameServices;
|
||||||
|
using VRage.Network;
|
||||||
|
using VRage.Serialization;
|
||||||
|
using VRage.Utils;
|
||||||
|
using VRageMath;
|
||||||
|
using VRageRender;
|
||||||
|
using VRageRender.Messages;
|
||||||
|
|
||||||
|
namespace SeamlessClientPlugin.SeamlessTransfer
|
||||||
|
{
|
||||||
|
public class LoadServer
|
||||||
|
{
|
||||||
|
//Protected or internal class types
|
||||||
|
private static readonly Type ClientType = Type.GetType("Sandbox.Engine.Multiplayer.MyMultiplayerClient, Sandbox.Game");
|
||||||
|
private static readonly Type SyncLayerType = Type.GetType("Sandbox.Game.Multiplayer.MySyncLayer, Sandbox.Game");
|
||||||
|
private static readonly Type MyTransportLayerType = Type.GetType("Sandbox.Engine.Multiplayer.MyTransportLayer, Sandbox.Game");
|
||||||
|
private static readonly Type MySessionType = Type.GetType("Sandbox.Game.World.MySession, Sandbox.Game");
|
||||||
|
private static readonly Type VirtualClientsType = Type.GetType("Sandbox.Engine.Multiplayer.MyVirtualClients, Sandbox.Game");
|
||||||
|
private static readonly Type GUIScreenChat = Type.GetType("Sandbox.Game.Gui.MyGuiScreenChat, Sandbox.Game");
|
||||||
|
|
||||||
|
private static Harmony Patcher = new Harmony("SeamlessClientReUnload");
|
||||||
|
private static MyGameServerItem Server;
|
||||||
|
private static MyObjectBuilder_World World;
|
||||||
|
|
||||||
|
public static object MyMulitplayerClient;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static ConstructorInfo ClientConstructor;
|
||||||
|
public static ConstructorInfo SyncLayerConstructor;
|
||||||
|
public static ConstructorInfo TransportLayerConstructor;
|
||||||
|
public static ConstructorInfo MySessionConstructor;
|
||||||
|
|
||||||
|
//Reflected Methods
|
||||||
|
public static FieldInfo VirtualClients;
|
||||||
|
public static FieldInfo AdminSettings;
|
||||||
|
public static FieldInfo RemoteAdminSettings;
|
||||||
|
public static FieldInfo MPlayerGPSCollection;
|
||||||
|
public static MethodInfo RemovePlayerFromDictionary;
|
||||||
|
public static MethodInfo InitVirtualClients;
|
||||||
|
public static MethodInfo LoadPlayerInternal;
|
||||||
|
public static MethodInfo LoadMembersFromWorld;
|
||||||
|
|
||||||
|
|
||||||
|
public LoadServer()
|
||||||
|
{
|
||||||
|
InitiatePatches();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//TargetWorld = World;
|
||||||
|
//Server = e;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void InitiatePatches()
|
||||||
|
{
|
||||||
|
//Patch the on connection event
|
||||||
|
MethodInfo OnJoin = ClientType.GetMethod("OnUserJoined", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
Patcher.Patch(OnJoin, postfix: new HarmonyMethod(GetPatchMethod(nameof(OnUserJoined))));
|
||||||
|
|
||||||
|
|
||||||
|
ClientConstructor = ClientType?.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[2] { typeof(MyGameServerItem), SyncLayerType }, null);
|
||||||
|
SyncLayerConstructor = SyncLayerType?.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[1] { MyTransportLayerType }, null);
|
||||||
|
TransportLayerConstructor = MyTransportLayerType?.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(int) }, null);
|
||||||
|
MySessionConstructor = MySessionType?.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[2] { typeof(MySyncLayer), typeof(bool) }, null);
|
||||||
|
|
||||||
|
if (ClientConstructor == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Couldn't find ClientConstructor");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SyncLayerConstructor == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Couldn't find SyncLayerConstructor");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TransportLayerConstructor == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Couldn't find TransportLayerConstructor");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MySessionConstructor == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Couldn't find MySessionConstructor");
|
||||||
|
}
|
||||||
|
|
||||||
|
RemovePlayerFromDictionary = typeof(MyPlayerCollection).GetMethod("RemovePlayerFromDictionary", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||||
|
VirtualClients = typeof(MySession).GetField("VirtualClients", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
InitVirtualClients = VirtualClientsType.GetMethod("Init", BindingFlags.Instance | BindingFlags.Public);
|
||||||
|
LoadPlayerInternal = typeof(MyPlayerCollection).GetMethod("LoadPlayerInternal", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||||
|
LoadMembersFromWorld = typeof(MySession).GetMethod("LoadMembersFromWorld", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
AdminSettings = typeof(MySession).GetField("m_adminSettings", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||||
|
RemoteAdminSettings = typeof(MySession).GetField("m_remoteAdminSettings", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||||
|
MPlayerGPSCollection = typeof(MyPlayerCollection).GetField("m_players", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static MethodInfo GetPatchMethod(string v)
|
||||||
|
{
|
||||||
|
return typeof(LoadServer).GetMethod(v, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static void OnUserJoined(ref JoinResultMsg msg)
|
||||||
|
{
|
||||||
|
if (SeamlessClient.IsSwitching && msg.JoinResult == JoinResult.OK)
|
||||||
|
{
|
||||||
|
SeamlessClient.TryShow("User Joined! Result: "+msg.JoinResult.ToString());
|
||||||
|
ForceClientConnection();
|
||||||
|
}else if (SeamlessClient.IsSwitching && msg.JoinResult != JoinResult.OK)
|
||||||
|
{
|
||||||
|
SeamlessClient.TryShow("Failed to join server! Reason: " + msg.JoinResult.ToString());
|
||||||
|
MySession.Static.Unload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static void LoadWorldData(MyGameServerItem TargetServer, MyObjectBuilder_World TargetWorld)
|
||||||
|
{
|
||||||
|
Server = TargetServer;
|
||||||
|
World = TargetWorld;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void ResetMPClient()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
MySandboxGame.Static.SessionCompatHelper.FixSessionComponentObjectBuilders(World.Checkpoint, World.Sector);
|
||||||
|
|
||||||
|
|
||||||
|
var LayerInstance = TransportLayerConstructor.Invoke(new object[] { 2 });
|
||||||
|
var SyncInstance = SyncLayerConstructor.Invoke(new object[] { LayerInstance });
|
||||||
|
var instance = ClientConstructor.Invoke(new object[] { Server, SyncInstance });
|
||||||
|
MyMulitplayerClient = instance;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
MyMultiplayer.Static = (MyMultiplayerBase)instance;
|
||||||
|
MyMultiplayer.Static.ExperimentalMode = MySandboxGame.Config.ExperimentalMode;
|
||||||
|
SeamlessClient.TryShow("Successfully set MyMultiplayer.Static");
|
||||||
|
//var m = ClientType.GetMethod("SendPlayerData", BindingFlags.Public | BindingFlags.Instance);
|
||||||
|
//m.Invoke(MyMultiplayer.Static, new object[] { MyGameService.UserName });
|
||||||
|
Server.GetGameTagByPrefix("gamemode");
|
||||||
|
//typeof(MySession).GetMethod("LoadMembersFromWorld", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(MySession.Static, new object[] { LoadServer.TargetWorld, MyMultiplayer.Static });
|
||||||
|
|
||||||
|
|
||||||
|
//MyScreenManager.CloseScreen(GUIScreenChat);
|
||||||
|
MyHud.Chat.RegisterChat(MyMultiplayer.Static);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
SeamlessClient.TryShow("Error! " + ex.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LoadMP(MyObjectBuilder_World world, MyMultiplayerBase multiplayerSession)
|
||||||
|
{
|
||||||
|
SeamlessClient.TryShow("Starting LoadMP!");
|
||||||
|
|
||||||
|
|
||||||
|
//var MySessionConstructor = MySessionType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[2] { typeof(MySyncLayer), typeof(bool) }, null);
|
||||||
|
//MySession.Static = (MySession)MySessionConstructor.Invoke(new object[] { MyMultiplayer.Static.SyncLayer, true });
|
||||||
|
MySession.Static.Mods = World.Checkpoint.Mods;
|
||||||
|
MySession.Static.Settings = World.Checkpoint.Settings;
|
||||||
|
MySession.Static.CurrentPath = MyLocalCache.GetSessionSavesPath(MyUtils.StripInvalidChars(world.Checkpoint.SessionName), contentFolder: false, createIfNotExists: false);
|
||||||
|
MySession.Static.WorldBoundaries = world.Checkpoint.WorldBoundaries;
|
||||||
|
MySession.Static.InGameTime = MyObjectBuilder_Checkpoint.DEFAULT_DATE;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// MySession.Static.Players.LoadConnectedPlayers(world.Checkpoint);
|
||||||
|
|
||||||
|
//typeof(MySession).GetMethod("PrepareBaseSession", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(MyObjectBuilder_Checkpoint), typeof(MyObjectBuilder_Sector) }, null).Invoke(MySession.Static, new object[] { world.Checkpoint, world.Sector });
|
||||||
|
|
||||||
|
if (MyFakes.MP_SYNC_CLUSTERTREE)
|
||||||
|
{
|
||||||
|
SeamlessClient.TryShow("Deserializing Clusters!");
|
||||||
|
//MyPhysics.DeserializeClusters(world.Clusters);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//_ = world.Checkpoint.ControlledObject;
|
||||||
|
//world.Checkpoint.ControlledObject = -1L;
|
||||||
|
LoadOnlinePlayers(world.Checkpoint);
|
||||||
|
LoadWorld(world.Checkpoint, world.Sector);
|
||||||
|
SeamlessClient.TryShow("Loading World Complete!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static void LoadWorld(MyObjectBuilder_Checkpoint checkpoint, MyObjectBuilder_Sector sector)
|
||||||
|
{
|
||||||
|
|
||||||
|
Dictionary<ulong, AdminSettingsEnum> AdminSettingsList = (Dictionary<ulong, AdminSettingsEnum>)RemoteAdminSettings.GetValue(MySession.Static);
|
||||||
|
AdminSettingsList.Clear();
|
||||||
|
|
||||||
|
MySession.Static.PromotedUsers.Clear();
|
||||||
|
MySession.Static.CreativeTools.Clear();
|
||||||
|
|
||||||
|
MyEntities.MemoryLimitAddFailureReset();
|
||||||
|
MySession.Static.ElapsedGameTime = new TimeSpan(checkpoint.ElapsedGameTime);
|
||||||
|
MySession.Static.InGameTime = checkpoint.InGameTime;
|
||||||
|
MySession.Static.Name = MyStatControlText.SubstituteTexts(checkpoint.SessionName);
|
||||||
|
MySession.Static.Description = checkpoint.Description;
|
||||||
|
|
||||||
|
|
||||||
|
if (checkpoint.PromotedUsers != null)
|
||||||
|
{
|
||||||
|
MySession.Static.PromotedUsers = checkpoint.PromotedUsers.Dictionary;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MySession.Static.PromotedUsers = new Dictionary<ulong, MyPromoteLevel>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
foreach (KeyValuePair<MyObjectBuilder_Checkpoint.PlayerId, MyObjectBuilder_Player> item in checkpoint.AllPlayersData.Dictionary)
|
||||||
|
{
|
||||||
|
ulong clientId = item.Key.GetClientId();
|
||||||
|
AdminSettingsEnum adminSettingsEnum = (AdminSettingsEnum)item.Value.RemoteAdminSettings;
|
||||||
|
if (checkpoint.RemoteAdminSettings != null && checkpoint.RemoteAdminSettings.Dictionary.TryGetValue(clientId, out var value))
|
||||||
|
{
|
||||||
|
adminSettingsEnum = (AdminSettingsEnum)value;
|
||||||
|
}
|
||||||
|
if (!MyPlatformGameSettings.IsIgnorePcuAllowed)
|
||||||
|
{
|
||||||
|
adminSettingsEnum &= ~AdminSettingsEnum.IgnorePcu;
|
||||||
|
adminSettingsEnum &= ~AdminSettingsEnum.KeepOriginalOwnershipOnPaste;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AdminSettingsList[clientId] = adminSettingsEnum;
|
||||||
|
if (!Sync.IsDedicated && clientId == Sync.MyId)
|
||||||
|
{
|
||||||
|
AdminSettings.SetValue(MySession.Static, adminSettingsEnum);
|
||||||
|
|
||||||
|
//m_adminSettings = adminSettingsEnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (!MySession.Static.PromotedUsers.TryGetValue(clientId, out var value2))
|
||||||
|
{
|
||||||
|
value2 = MyPromoteLevel.None;
|
||||||
|
}
|
||||||
|
if (item.Value.PromoteLevel > value2)
|
||||||
|
{
|
||||||
|
MySession.Static.PromotedUsers[clientId] = item.Value.PromoteLevel;
|
||||||
|
}
|
||||||
|
if (!MySession.Static.CreativeTools.Contains(clientId) && item.Value.CreativeToolsEnabled)
|
||||||
|
{
|
||||||
|
MySession.Static.CreativeTools.Add(clientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//MySession.Static.WorkshopId = checkpoint.WorkshopId;
|
||||||
|
MySession.Static.Password = checkpoint.Password;
|
||||||
|
MySession.Static.PreviousEnvironmentHostility = checkpoint.PreviousEnvironmentHostility;
|
||||||
|
MySession.Static.RequiresDX = checkpoint.RequiresDX;
|
||||||
|
MySession.Static.CustomLoadingScreenImage = checkpoint.CustomLoadingScreenImage;
|
||||||
|
MySession.Static.CustomLoadingScreenText = checkpoint.CustomLoadingScreenText;
|
||||||
|
MySession.Static.CustomSkybox = checkpoint.CustomSkybox;
|
||||||
|
//FixIncorrectSettings(Settings);
|
||||||
|
// MySession.Static.AppVersionFromSave = checkpoint.AppVersion;
|
||||||
|
//MyToolbarComponent.InitCharacterToolbar(checkpoint.CharacterToolbar);
|
||||||
|
//LoadCameraControllerSettings(checkpoint);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
foreach(var GPS in checkpoint.Gps.Dictionary)
|
||||||
|
{
|
||||||
|
if (GPS.Key != MySession.Static.LocalPlayerId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
SeamlessClient.TryShow(GPS.Key + ":" + GPS.Value.Entries.Count);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SeamlessClient.TryShow("LocalPlayerID: " + MySession.Static.LocalPlayerId);
|
||||||
|
checkpoint.Gps.Dictionary.TryGetValue(MySession.Static.LocalPlayerId, out MyObjectBuilder_Gps GPSCollection);
|
||||||
|
SeamlessClient.TryShow("You have " + GPSCollection.Entries.Count + " gps points!");
|
||||||
|
|
||||||
|
|
||||||
|
Dictionary<long, Dictionary<int, MyGps>> m_playerGpss = (Dictionary<long, Dictionary<int, MyGps>>)typeof(MyGpsCollection).GetField("m_playerGpss", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(MySession.Static.Gpss);
|
||||||
|
m_playerGpss.Clear();
|
||||||
|
|
||||||
|
|
||||||
|
foreach (var GPS in GPSCollection.Entries)
|
||||||
|
{
|
||||||
|
MyGps myGps = new MyGps(GPS);
|
||||||
|
|
||||||
|
if(MySession.Static.Gpss.AddPlayerGps(MySession.Static.LocalPlayerId, ref myGps))
|
||||||
|
{
|
||||||
|
MyHud.GpsMarkers.RegisterMarker(myGps);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SeamlessClient.TryShow("Failed to registered Marker! It already exsists!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
MySession.Static.Gpss.LoadGpss(checkpoint);
|
||||||
|
MySession.Static.Toolbars.LoadToolbars(checkpoint);
|
||||||
|
|
||||||
|
Sync.Players.RespawnComponent.InitFromCheckpoint(checkpoint);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static void ForceClientConnection()
|
||||||
|
{
|
||||||
|
SeamlessClient.IsSwitching = false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (MyMultiplayer.Static == null)
|
||||||
|
SeamlessClient.TryShow("MyMultiplayer.Static is null");
|
||||||
|
|
||||||
|
if (World == null)
|
||||||
|
SeamlessClient.TryShow("TargetWorld is null");
|
||||||
|
|
||||||
|
LoadClients();
|
||||||
|
|
||||||
|
LoadMP(World, MyMultiplayer.Static);
|
||||||
|
|
||||||
|
|
||||||
|
}catch(Exception ex)
|
||||||
|
{
|
||||||
|
SeamlessClient.TryShow(ex.ToString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SeamlessClient.TryShow("Requesting Player From Server");
|
||||||
|
Sync.Players.RequestNewPlayer(Sync.MyId, 0, MyGameService.UserName, null, realPlayer: true, initialPlayer: true);
|
||||||
|
if (MySession.Static.ControlledEntity == null && Sync.IsServer && !Sandbox.Engine.Platform.Game.IsDedicated)
|
||||||
|
{
|
||||||
|
SeamlessClient.TryShow("C");
|
||||||
|
MyLog.Default.WriteLine("ControlledObject was null, respawning character");
|
||||||
|
//m_cameraAwaitingEntity = true;
|
||||||
|
MyPlayerCollection.RequestLocalRespawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//typeof(MyGuiScreenTerminal).GetMethod("CreateTabs")
|
||||||
|
MyMultiplayer.Static.OnSessionReady();
|
||||||
|
MySession.Static.LoadDataComponents();
|
||||||
|
//MyGuiSandbox.LoadData(false);
|
||||||
|
//MyGuiSandbox.AddScreen(MyGuiSandbox.CreateScreen(MyPerGameSettings.GUI.HUDScreen));
|
||||||
|
MyRenderProxy.RebuildCullingStructure();
|
||||||
|
MyRenderProxy.CollectGarbage();
|
||||||
|
|
||||||
|
SeamlessClient.TryShow("OnlinePlayers: " + MySession.Static.Players.GetOnlinePlayers().Count);
|
||||||
|
SeamlessClient.TryShow("Loading Complete!");
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception Ex)
|
||||||
|
{
|
||||||
|
SeamlessClient.TryShow(Ex.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static void LoadClients()
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//Remove all old players
|
||||||
|
foreach (var Client in MySession.Static.Players.GetOnlinePlayers())
|
||||||
|
{
|
||||||
|
if (Client.Id.SteamId == Sync.MyId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
SeamlessClient.TryShow("Disconnecting: " + Client.DisplayName);
|
||||||
|
RemovePlayerFromDictionary.Invoke(MySession.Static.Players, new object[] { Client.Id });
|
||||||
|
}
|
||||||
|
|
||||||
|
//Clear all exsisting clients
|
||||||
|
foreach (var Client in Sync.Clients.GetClients().ToList())
|
||||||
|
{
|
||||||
|
if (Client.SteamUserId == Sync.MyId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Sync.Clients.RemoveClient(Client.SteamUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
object VirtualClientsValue = VirtualClients.GetValue(MySession.Static);
|
||||||
|
|
||||||
|
//Re-Initilize Virtual clients
|
||||||
|
SeamlessClient.TryShow("Initilizing Virtual Clients!");
|
||||||
|
InitVirtualClients.Invoke(VirtualClientsValue, null);
|
||||||
|
|
||||||
|
|
||||||
|
//Load Members from world
|
||||||
|
SeamlessClient.TryShow("Loading Members From World!");
|
||||||
|
LoadMembersFromWorld.Invoke(MySession.Static, new object[] { World, MyMulitplayerClient });
|
||||||
|
foreach (var Client in World.Checkpoint.Clients)
|
||||||
|
{
|
||||||
|
SeamlessClient.TryShow("Adding New Client: " + Client.Name);
|
||||||
|
Sync.Clients.AddClient(Client.SteamId, Client.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
SeamlessClient.TryShow(ex.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LoadOnlinePlayers(MyObjectBuilder_Checkpoint checkpoint)
|
||||||
|
{
|
||||||
|
//Get This players ID
|
||||||
|
MyPlayer.PlayerId? savingPlayerId = new MyPlayer.PlayerId(Sync.MyId);
|
||||||
|
if (!savingPlayerId.HasValue)
|
||||||
|
{
|
||||||
|
SeamlessClient.TryShow("SavingPlayerID is null! Creating Default!");
|
||||||
|
savingPlayerId = new MyPlayer.PlayerId(Sync.MyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
SeamlessClient.TryShow("Saving PlayerID: "+savingPlayerId.ToString());
|
||||||
|
|
||||||
|
|
||||||
|
SeamlessClient.TryShow("Checkpoint.AllPlayers: " + checkpoint.AllPlayers.Count);
|
||||||
|
//These both are null/empty. Server doesnt need to send them to the client
|
||||||
|
//SeamlessClient.TryShow("Checkpoint.ConnectedPlayers: " + checkpoint.ConnectedPlayers.Dictionary.Count);
|
||||||
|
//SeamlessClient.TryShow("Checkpoint.DisconnectedPlayers: " + checkpoint.DisconnectedPlayers.Dictionary.Count);
|
||||||
|
SeamlessClient.TryShow("Checkpoint.AllPlayersData: " + checkpoint.AllPlayersData.Dictionary.Count);
|
||||||
|
|
||||||
|
|
||||||
|
foreach (KeyValuePair<MyObjectBuilder_Checkpoint.PlayerId, MyObjectBuilder_Player> item3 in checkpoint.AllPlayersData.Dictionary)
|
||||||
|
{
|
||||||
|
MyPlayer.PlayerId playerId5 = new MyPlayer.PlayerId(item3.Key.GetClientId(), item3.Key.SerialId);
|
||||||
|
if (savingPlayerId.HasValue && playerId5.SteamId == savingPlayerId.Value.SteamId)
|
||||||
|
{
|
||||||
|
playerId5 = new MyPlayer.PlayerId(Sync.MyId, playerId5.SerialId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadPlayerInternal.Invoke(MySession.Static.Players, new object[] { playerId5, item3.Value, false });
|
||||||
|
ConcurrentDictionary<MyPlayer.PlayerId, MyPlayer> Players = (ConcurrentDictionary<MyPlayer.PlayerId, MyPlayer>)MPlayerGPSCollection.GetValue(MySession.Static.Players);
|
||||||
|
//LoadPlayerInternal(ref playerId5, item3.Value);
|
||||||
|
if (Players.TryGetValue(playerId5, out MyPlayer myPlayer))
|
||||||
|
{
|
||||||
|
List<Vector3> value2 = null;
|
||||||
|
if (checkpoint.AllPlayersColors != null && checkpoint.AllPlayersColors.Dictionary.TryGetValue(item3.Key, out value2))
|
||||||
|
{
|
||||||
|
myPlayer.SetBuildColorSlots(value2);
|
||||||
|
}
|
||||||
|
else if (checkpoint.CharacterToolbar != null && checkpoint.CharacterToolbar.ColorMaskHSVList != null && checkpoint.CharacterToolbar.ColorMaskHSVList.Count > 0)
|
||||||
|
{
|
||||||
|
myPlayer.SetBuildColorSlots(checkpoint.CharacterToolbar.ColorMaskHSVList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static void UpdatePlayerData()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
145
SeamlessTransfer/Transfer.cs
Normal file
145
SeamlessTransfer/Transfer.cs
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
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 SeamlessClientPlugin.ClientMessages;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using VRage;
|
||||||
|
using VRage.Game;
|
||||||
|
using VRage.GameServices;
|
||||||
|
using VRage.Network;
|
||||||
|
using VRage.Steam;
|
||||||
|
using VRage.Utils;
|
||||||
|
using VRageMath;
|
||||||
|
|
||||||
|
namespace SeamlessClientPlugin.SeamlessTransfer
|
||||||
|
{
|
||||||
|
|
||||||
|
[ProtoContract]
|
||||||
|
public class Transfer
|
||||||
|
{
|
||||||
|
[ProtoMember(1)]
|
||||||
|
public ulong TargetServerID;
|
||||||
|
[ProtoMember(2)]
|
||||||
|
public string IPAdress;
|
||||||
|
[ProtoMember(6)]
|
||||||
|
public WorldRequest WorldRequest;
|
||||||
|
[ProtoMember(7)]
|
||||||
|
public string PlayerName;
|
||||||
|
|
||||||
|
[ProtoMember(8)]
|
||||||
|
public List<MyObjectBuilder_Gps.Entry> PlayerGPSCoords;
|
||||||
|
|
||||||
|
[ProtoMember(9)]
|
||||||
|
public MyObjectBuilder_Toolbar PlayerToolbar;
|
||||||
|
|
||||||
|
public List<Vector3> PlayerBuildSlots;
|
||||||
|
|
||||||
|
public Transfer(ulong ServerID, string IPAdress)
|
||||||
|
{
|
||||||
|
/* This is only called serverside
|
||||||
|
*/
|
||||||
|
|
||||||
|
this.IPAdress = IPAdress;
|
||||||
|
TargetServerID = ServerID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transfer() { }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void PingServerAndBeginRedirect()
|
||||||
|
{
|
||||||
|
if (TargetServerID == 0)
|
||||||
|
{
|
||||||
|
SeamlessClient.TryShow("This is not a valid server!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SeamlessClient.TryShow("SyncMyID: " + Sync.MyId.ToString());
|
||||||
|
SeamlessClient.TryShow("Beginning Redirect to server: " + TargetServerID);
|
||||||
|
MyGameService.OnPingServerResponded += MyGameService_OnPingServerResponded;
|
||||||
|
MyGameService.OnPingServerFailedToRespond += MyGameService_OnPingServerFailedToRespond;
|
||||||
|
|
||||||
|
MyGameService.PingServer(IPAdress);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MyGameService_OnPingServerFailedToRespond(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
MyGameService.OnPingServerResponded -= MyGameService_OnPingServerResponded;
|
||||||
|
MyGameService.OnPingServerFailedToRespond -= MyGameService_OnPingServerFailedToRespond;
|
||||||
|
SeamlessClient.TryShow("ServerPing failed!");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MyGameService_OnPingServerResponded(object sender, MyGameServerItem e)
|
||||||
|
{
|
||||||
|
MyGameService.OnPingServerResponded -= MyGameService_OnPingServerResponded;
|
||||||
|
MyGameService.OnPingServerFailedToRespond -= MyGameService_OnPingServerFailedToRespond;
|
||||||
|
SeamlessClient.TryShow("ServerPing Successful! Attempting to connect to lobby: " + e.GameID);
|
||||||
|
|
||||||
|
|
||||||
|
LoadServer.LoadWorldData(e, WorldRequest.DeserializeWorldData());
|
||||||
|
MySandboxGame.Static.Invoke(delegate
|
||||||
|
{
|
||||||
|
//MySessionLoader.UnloadAndExitToMenu();
|
||||||
|
UnloadCurrentServer();
|
||||||
|
//MyJoinGameHelper.JoinGame(e, true);
|
||||||
|
LoadServer.ResetMPClient();
|
||||||
|
ClearEntities();
|
||||||
|
//ReloadPatch.SeamlessSwitch = false;
|
||||||
|
}, "SeamlessClient");
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void UnloadCurrentServer()
|
||||||
|
{
|
||||||
|
if (MyMultiplayer.Static != null)
|
||||||
|
{
|
||||||
|
MyHud.Chat.UnregisterChat(MyMultiplayer.Static);
|
||||||
|
|
||||||
|
MyMultiplayer.Static.ReplicationLayer.Disconnect();
|
||||||
|
MyMultiplayer.Static.ReplicationLayer.Dispose();
|
||||||
|
|
||||||
|
MyMultiplayer.Static.Dispose();
|
||||||
|
MyMultiplayer.Static = null;
|
||||||
|
|
||||||
|
//Sync.Clients.Clear();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// MyGuiSandbox.UnloadContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void ClearEntities()
|
||||||
|
{
|
||||||
|
foreach (var ent in MyEntities.GetEntities())
|
||||||
|
{
|
||||||
|
if (ent is MyPlanet)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ent.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
38
Utilities/Utility.cs
Normal file
38
Utilities/Utility.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using ProtoBuf;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace SeamlessClientPlugin.Utilities
|
||||||
|
{
|
||||||
|
public static class Utility
|
||||||
|
{
|
||||||
|
public static byte[] Serialize<T>(T instance)
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
using (var m = new MemoryStream())
|
||||||
|
{
|
||||||
|
// m.Seek(0, SeekOrigin.Begin);
|
||||||
|
Serializer.Serialize(m, instance);
|
||||||
|
|
||||||
|
return m.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T Deserialize<T>(byte[] data)
|
||||||
|
{
|
||||||
|
if (data == null)
|
||||||
|
return default(T);
|
||||||
|
|
||||||
|
using (var m = new MemoryStream(data))
|
||||||
|
{
|
||||||
|
return Serializer.Deserialize<T>(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
packages.config
Normal file
4
packages.config
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="Lib.Harmony" version="2.0.4" targetFramework="net472" />
|
||||||
|
</packages>
|
Reference in New Issue
Block a user