Skip to content

Commit

Permalink
Merge pull request NebulaModTeam#666 from starfi5h/pr-feature
Browse files Browse the repository at this point in the history
Construction drones range limit & command /playerdata
  • Loading branch information
starfi5h authored Mar 23, 2024
2 parents f3e346e + dc77884 commit 49b45a6
Show file tree
Hide file tree
Showing 7 changed files with 315 additions and 39 deletions.
78 changes: 43 additions & 35 deletions NebulaNetwork/SaveManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,25 +91,31 @@ private static void HandleAutoSave()
File.Move(str1, str2);
}

public static void LoadServerData()
public static void LoadServerData(bool loadSaveFile)
{
var path = GameConfig.gameSaveFolder + DSPGame.LoadFile + FILE_EXTENSION;
playerSaves.Clear();

// var playerManager = Multiplayer.Session.Network.PlayerManager;
if (!loadSaveFile)
{
return;
}
var path = GameConfig.gameSaveFolder + DSPGame.LoadFile + FILE_EXTENSION;
if (!File.Exists(path))
{
Log.Info($"No server file");
return;
}

var source = File.ReadAllBytes(path);
var netDataReader = new NetDataReader(source);
ushort revision;
try
{
var source = File.ReadAllBytes(path);
var netDataReader = new NetDataReader(source);
ushort revision;

var revString = netDataReader.GetString();
if (revString != "REV")
{
throw new Exception();
throw new Exception("Incorrect header");
}

revision = netDataReader.GetUShort();
Expand All @@ -119,43 +125,45 @@ public static void LoadServerData()
// Supported revision: 5~8
if (revision is < 5 or > REVISION)
{
throw new Exception();
throw new Exception($"Unsupported version {revision}");
}
}
}
catch (Exception)
{
Log.Warn("Skipping server data from unsupported Nebula version...");
return;
}

var playerNum = netDataReader.GetInt();
var playerNum = netDataReader.GetInt();


for (var i = 0; i < playerNum; i++)
{
var hash = netDataReader.GetString();
PlayerData playerData = null;
switch (revision)
for (var i = 0; i < playerNum; i++)
{
case REVISION:
playerData = netDataReader.Get(() => new PlayerData());
break;
case >= 5:
playerData = new PlayerData();
playerData.Import(netDataReader, revision);
break;
}
var hash = netDataReader.GetString();
PlayerData playerData = null;
switch (revision)
{
case REVISION:
playerData = netDataReader.Get(() => new PlayerData());
break;
case >= 5:
playerData = new PlayerData();
playerData.Import(netDataReader, revision);
break;
}

if (!playerSaves.ContainsKey(hash) && playerData != null)
{
playerSaves.Add(hash, playerData);
}
else if (playerData == null)
{
Log.Warn($"could not load player data from unsupported save file revision {revision}");
if (!playerSaves.ContainsKey(hash) && playerData != null)
{
playerSaves.Add(hash, playerData);
}
else if (playerData == null)
{
Log.Warn($"Could not load player data from unsupported save file revision {revision}");
}
}
}
catch (Exception e)
{
playerSaves.Clear();
Log.WarnInform("Skipping server data due to exception:\n" + e.Message);
Log.Warn(e);
return;
}
}

public static bool TryAdd(string clientCertHash, IPlayerData playerData)
Expand Down
5 changes: 1 addition & 4 deletions NebulaNetwork/Server.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,7 @@ internal void OnSocketDisconnection(INebulaConnection conn)

public void Start()
{
if (loadSaveFile)
{
SaveManager.LoadServerData();
}
SaveManager.LoadServerData(loadSaveFile);

foreach (var assembly in AssembliesUtils.GetNebulaAssemblies())
{
Expand Down
14 changes: 14 additions & 0 deletions NebulaPatcher/Patches/Dynamic/ConstructionModuleComponent_Patch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using HarmonyLib;
using NebulaModel.Packets.Players;
using NebulaWorld;
using NebulaWorld.Player;

#endregion

Expand All @@ -25,4 +26,17 @@ public static void EjectMechaDrone_Prefix(PlanetFactory factory, Player player,
var packet = new PlayerEjectMechaDronePacket(playerId, planetId, targetObjectId, next1ObjectId, next2ObjectId, next3ObjectId, priority);
Multiplayer.Session.Network.SendPacketToLocalStar(packet);
}

[HarmonyPrefix]
[HarmonyPatch(nameof(ConstructionModuleComponent.InsertTmpBuildTarget))]
public static bool InsertTmpBuildTarget_Prefix(ConstructionModuleComponent __instance, int objectId, float value)
{
if (__instance.entityId != 0 || value < DroneManager.MinSqrDistance) return true; // BAB, or distance is very close
if (!Multiplayer.IsActive) return true;

// Only send out mecha drones if local player is the closest player to the target prebuild
if (GameMain.localPlanet == null) return true;
var sqrDistToOtherPlayer = Multiplayer.Session.Drones.GetClosestRemotePlayerSqrDistance(GameMain.localPlanet.factory.prebuildPool[objectId].pos);
return value <= sqrDistToOtherPlayer;
}
}
63 changes: 63 additions & 0 deletions NebulaPatcher/Patches/Transpilers/ConstructionSystem_Transpiler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#region

using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using HarmonyLib;
using NebulaModel.Logger;
using NebulaModel.Packets.Factory;
using NebulaWorld;
using NebulaWorld.Player;
using UnityEngine;

#endregion

namespace NebulaPatcher.Patches.Transpilers;

[HarmonyPatch(typeof(ConstructionSystem))]
internal class ConstructionSystem_Transpiler
{
[HarmonyTranspiler]
[HarmonyPatch(nameof(ConstructionSystem.AddBuildTargetToModules))]
public static IEnumerable<CodeInstruction> AddBuildTargetToModules_Transpiler(IEnumerable<CodeInstruction> instructions)
{
try
{
/* Sync Prebuild.itemRequired changes by player, insert local method call after player.package.TakeTailItems
Replace: if (num8 <= num) { this.player.mecha.constructionModule.InsertBuildTarget ... }
With: if (num8 <= num && IsClosestPlayer(ref pos)) { this.player.mecha.constructionModule.InsertBuildTarget ... }
*/

var codeMatcher = new CodeMatcher(instructions)
.MatchForward(true,
new CodeMatch(OpCodes.Ldloc_S),
new CodeMatch(OpCodes.Ldloc_0),
new CodeMatch(OpCodes.Bgt_Un)
);
Log.Info(codeMatcher.IsValid);
var sqrDist = codeMatcher.InstructionAt(-2).operand;
var skipLabel = codeMatcher.Operand;
codeMatcher.Advance(1)
.Insert(
new CodeInstruction(OpCodes.Ldloc_S, sqrDist),
new CodeInstruction(OpCodes.Ldarg_2), //ref Vector3 pos
new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(ConstructionSystem_Transpiler), nameof(IsClosestPlayer))),
new CodeInstruction(OpCodes.Brfalse_S, skipLabel)
);

return codeMatcher.InstructionEnumeration();
}
catch (System.Exception e)
{
Log.Error("Transpiler ConstructionSystem.AddBuildTargetToModules failed.");
Log.Error(e);
return instructions;
}
}

static bool IsClosestPlayer(float sqrDist, ref Vector3 pos)
{
if (!Multiplayer.IsActive || sqrDist < DroneManager.MinSqrDistance) return true;
return sqrDist <= Multiplayer.Session.Drones.GetClosestRemotePlayerSqrDistance(pos);
}
}
1 change: 1 addition & 0 deletions NebulaWorld/Chat/ChatCommandRegistry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ static ChatCommandRegistry()
RegisterCommand("system", new SystemCommandHandler(), "s");
RegisterCommand("reconnect", new ReconnectCommandHandler(), "r");
RegisterCommand("server", new ServerCommandHandler());
RegisterCommand("playerdata", new PlayerDataCommandHandler());
}

private static void RegisterCommand(string commandName, IChatCommandHandler commandHandlerHandler, params string[] aliases)
Expand Down
167 changes: 167 additions & 0 deletions NebulaWorld/Chat/Commands/PlayerDataCommandHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
#region

using NebulaWorld.MonoBehaviours.Local.Chat;
using System.Collections.Generic;
using NebulaAPI.GameState;
using HarmonyLib;
using NebulaModel.DataStructures.Chat;
using System.IO;
using NebulaModel;
using NebulaModel.Logger;
using NebulaAPI.DataStructures;
using NebulaModel.Networking;
using NebulaModel.Packets.Players;

#endregion

namespace NebulaWorld.Chat.Commands;

public class PlayerDataCommandHandler : IChatCommandHandler
{
public void Execute(ChatWindow window, string[] parameters)
{
if (parameters.Length < 1)
{
throw new ChatCommandUsageException("Not enough arguments!".Translate());
}

// Due to dependency order, get SaveManager.playerSaves by reflection
var saveManagerType = AccessTools.TypeByName("NebulaNetwork.SaveManager");
var playerSaves = (Dictionary<string, IPlayerData>)AccessTools.Field(saveManagerType, "playerSaves").GetValue(null);

switch (parameters[0])
{
case "list":
{
var resp = $"Player count in .server file: {playerSaves.Count}\n";
foreach (var pair in playerSaves)
{
resp += $"[{pair.Key.Substring(0, 5)}] {pair.Value.Username}";
}
window.SendLocalChatMessage(resp, ChatMessageType.CommandOutputMessage);
break;
}
case "load" when parameters.Length < 2:
throw new ChatCommandUsageException("Need to specifiy hash string or name of a player!");
case "load":
{
var input = parameters[1];
foreach (var pair in playerSaves)
{
if (input == pair.Key.Substring(0, input.Length) || input == pair.Value.Username)
{
window.SendLocalChatMessage($"Load [{pair.Key.Substring(0, 5)}] {pair.Value.Username}", ChatMessageType.CommandOutputMessage);
LoadPlayerData(pair.Value);
return;
}
}
break;
}
case "remove" when parameters.Length < 2:
throw new ChatCommandUsageException("Need to specifiy hash string or name of a player!");
case "remove":
{
var input = parameters[1];
var removeHash = "";
foreach (var pair in playerSaves)
{
if (input == pair.Key.Substring(0, input.Length) || input == pair.Value.Username)
{
window.SendLocalChatMessage($"Remove [{pair.Key.Substring(0, 5)}] {pair.Value.Username}", ChatMessageType.CommandOutputMessage);
removeHash = pair.Key;
break;
}
}
playerSaves.Remove(removeHash);
break;
}
}
}

public string GetDescription()
{
return "Manage the stored multiplayer player data".Translate();
}

public string[] GetUsage()
{
return ["list", "load <hashString>", "remove <hashString>"];
}

static void LoadPlayerData(IPlayerData playerData)
{
Log.Info($"Teleporting to target planet {GameMain.localPlanet?.id ?? 0} => {playerData.LocalPlanetId}");
var actionSail = GameMain.mainPlayer.controller.actionSail;
if (playerData.LocalPlanetId > 0)
{
if (playerData.LocalPlanetId == GameMain.localPlanet?.id)
{
UIRoot.instance.uiGame.globemap.LocalPlanetTeleport(playerData.LocalPlanetPosition.ToVector3());
}
else
{
var destPlanet = GameMain.galaxy.PlanetById(playerData.LocalPlanetId);
actionSail.fastTravelTargetPlanet = destPlanet;
actionSail.fastTravelTargetLPos = playerData.LocalPlanetPosition.ToVector3();
actionSail.fastTravelTargetUPos = destPlanet.uPosition + (VectorLF3)(destPlanet.runtimeRotation * actionSail.fastTravelTargetLPos);
actionSail.StartFastTravelToUPosition(actionSail.fastTravelTargetUPos);
}
}
else
{
GameMain.mainPlayer.controller.actionSail.StartFastTravelToUPosition(playerData.UPosition.ToVectorLF3());
}

var mechaData = playerData.Mecha;
Log.Info("Loading player inventory...");
using (var ms = new MemoryStream())
{
var bw = new BinaryWriter(ms);
mechaData.Inventory.Export(bw);
mechaData.DeliveryPackage.Export(bw);
mechaData.WarpStorage.Export(bw);

ms.Seek(0, SeekOrigin.Begin);
var br = new BinaryReader(ms);
GameMain.mainPlayer.package.Import(br);
GameMain.mainPlayer.deliveryPackage.Import(br);
GameMain.mainPlayer.mecha.warpStorage.Import(br);
}
if (!Config.Options.SyncSoil)
{
GameMain.mainPlayer.sandCount = mechaData.SandCount;
}
GameMain.mainPlayer.mecha.coreEnergy = mechaData.CoreEnergy;
GameMain.mainPlayer.mecha.reactorEnergy = mechaData.ReactorEnergy;

if (playerData.Appearance != null)
{
Log.Info("Loading custom appearance...");
playerData.Appearance.CopyTo(GameMain.mainPlayer.mecha.appearance);
GameMain.mainPlayer.mechaArmorModel.RefreshAllPartObjects();
GameMain.mainPlayer.mechaArmorModel.RefreshAllBoneObjects();
GameMain.mainPlayer.mecha.appearance.NotifyAllEvents();

var editor = UIRoot.instance.uiMechaEditor;
editor.selection.ClearSelection();
editor.saveGroup._Close();
if (editor.mecha.diyAppearance == null)
{
editor.mecha.diyAppearance = new MechaAppearance();
editor.mecha.diyAppearance.Init();
}
GameMain.mainPlayer.mecha.appearance.CopyTo(editor.mecha.diyAppearance);
editor.mechaArmorModel.RefreshAllPartObjects();
editor.mechaArmorModel.RefreshAllBoneObjects();
editor.mecha.diyAppearance.NotifyAllEvents();
editor._left_content_height_max = 0f;
editor.SetLeftScrollTop();
editor.saveGroup._Open();

using var writer = new BinaryUtils.Writer();
GameMain.mainPlayer.mecha.appearance.Export(writer.BinaryWriter);
Multiplayer.Session.Network.SendPacket(new PlayerMechaArmor(Multiplayer.Session.LocalPlayer.Id,
writer.CloseAndGetBytes()));
}
}
}
Loading

0 comments on commit 49b45a6

Please sign in to comment.