using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using NebulaAPI;
using NebulaAPI.Networking;
using NebulaAPI.Packets;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace NebulaLocalResync;
[BepInPlugin("com.local.nebulalocalresync", "Nebula Local Resync", "0.1.4")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public sealed class LocalResyncPlugin : BaseUnityPlugin
{
public const string PluginGuid = "com.local.nebulalocalresync";
public const string PluginName = "Nebula Local Resync";
public const string PluginVersion = "0.1.4";
private static readonly object MainThreadLock = new object();
private static readonly Queue<Action> MainThreadActions = new Queue<Action>();
private static int NextRequestId;
internal static LocalResyncPlugin Instance;
internal static ManualLogSource Log;
private ConfigEntry<KeyboardShortcut> _hotkey;
private ConfigEntry<float> _defaultRadius;
private ConfigEntry<float> _maximumRadius;
private ConfigEntry<int> _maximumEntities;
private ConfigEntry<int> _maximumGroundEnemies;
private ConfigEntry<int> _maximumPayloadBytes;
private ConfigEntry<float> _serverCooldownSeconds;
private ConfigEntry<bool> _showRealtimeTip;
private readonly Dictionary<int, float> _serverNextAllowedByConnection = new Dictionary<int, float>();
private float _clientNextAllowedAt;
private int _pendingRequestId;
private void Awake()
{
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
//IL_016a: Unknown result type (might be due to invalid IL or missing references)
//IL_016f: Unknown result type (might be due to invalid IL or missing references)
Instance = this;
Log = ((BaseUnityPlugin)this).Logger;
_hotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Client", "ResyncHotkey", new KeyboardShortcut((KeyCode)114, (KeyCode[])(object)new KeyCode[2]
{
(KeyCode)306,
(KeyCode)304
}), "Request a host-authoritative resync around the local mech. Default: Ctrl+Shift+R.");
_defaultRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Client", "DefaultRadiusMeters", 150f, "Radius around the mech to audit/resync when the hotkey is pressed.");
_maximumRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Server", "MaximumRadiusMeters", 500f, "Hard server-side clamp for client resync requests.");
_maximumEntities = ((BaseUnityPlugin)this).Config.Bind<int>("Server", "MaximumEntitiesPerRequest", 5000, "Maximum number of host entities allowed in one resync area. Requests exceeding this are rejected rather than truncated.");
_maximumGroundEnemies = ((BaseUnityPlugin)this).Config.Bind<int>("Server", "MaximumGroundEnemiesPerPlanet", 100000, "Safety cap for the authoritative planet-wide active ground-enemy ID set included in a resync. The ID set is used to safely clear stale local Dark Fog enemies without deleting enemies that still exist elsewhere on the host planet.");
_maximumPayloadBytes = ((BaseUnityPlugin)this).Config.Bind<int>("Server", "MaximumPayloadBytes", 16777216, "Maximum uncompressed custom resync payload. Requests exceeding this are rejected.");
_serverCooldownSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Server", "MinimumSecondsBetweenRequestsPerConnection", 2f, "Minimum server-side interval between resync requests from the same client.");
_showRealtimeTip = ((BaseUnityPlugin)this).Config.Bind<bool>("Client", "ShowRealtimeTip", true, "Attempt to show a small in-game realtime-tip message in addition to BepInEx logging.");
NebulaModAPI.RegisterPackets(Assembly.GetExecutingAssembly());
((BaseUnityPlugin)this).Logger.LogInfo((object)("Nebula Local Resync 0.1.4 loaded. Client hotkey: " + ((object)_hotkey.Value/*cast due to .constrained prefix*/).ToString()));
}
private void Update()
{
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
DrainMainThreadQueue();
if (NebulaModAPI.IsMultiplayerActive && NebulaModAPI.MultiplayerSession != null && NebulaModAPI.MultiplayerSession.IsClient && NebulaModAPI.MultiplayerSession.IsGameLoaded)
{
KeyboardShortcut value = _hotkey.Value;
if (((KeyboardShortcut)(ref value)).IsDown())
{
RequestLocalResync(_defaultRadius.Value);
}
}
}
private void OnDestroy()
{
if (object.ReferenceEquals(Instance, this))
{
Instance = null;
}
}
internal static void EnqueueMainThread(Action action)
{
if (action == null)
{
return;
}
lock (MainThreadLock)
{
MainThreadActions.Enqueue(action);
}
}
private static void DrainMainThreadQueue()
{
while (true)
{
Action action = null;
lock (MainThreadLock)
{
if (MainThreadActions.Count == 0)
{
break;
}
action = MainThreadActions.Dequeue();
}
try
{
action();
}
catch (Exception ex)
{
if (Log != null)
{
Log.LogError((object)("Main-thread resync action failed: " + ex));
}
}
}
}
private void RequestLocalResync(float requestedRadius)
{
if (Time.realtimeSinceStartup < _clientNextAllowedAt)
{
ShowClientMessage("Nebula resync is cooling down.");
return;
}
if (!DspAccess.TryGetLocalFactoryContext(out var planetId, out var center, out var _, out var error))
{
ShowClientMessage("Resync unavailable: " + error);
return;
}
float val = Math.Max(25f, _maximumRadius.Value);
float radius = Math.Max(25f, Math.Min(requestedRadius, val));
int requestId = (_pendingRequestId = Interlocked.Increment(ref NextRequestId));
_clientNextAllowedAt = Time.realtimeSinceStartup + 1f;
LocalResyncRequest localResyncRequest = new LocalResyncRequest();
localResyncRequest.RequestId = requestId;
localResyncRequest.PlanetId = planetId;
localResyncRequest.CenterX = center.X;
localResyncRequest.CenterY = center.Y;
localResyncRequest.CenterZ = center.Z;
localResyncRequest.Radius = radius;
NebulaModAPI.MultiplayerSession.Network.SendPacket<LocalResyncRequest>(localResyncRequest);
ShowClientMessage("Requested host resync within " + radius.ToString("0") + " m.");
}
internal void HandleHostRequest(LocalResyncRequest packet, INebulaConnection connection)
{
if (packet == null || connection == null || !connection.IsAlive || !NebulaModAPI.IsMultiplayerActive || NebulaModAPI.MultiplayerSession == null || !NebulaModAPI.MultiplayerSession.IsServer)
{
return;
}
float realtimeSinceStartup = Time.realtimeSinceStartup;
if (_serverNextAllowedByConnection.TryGetValue(connection.Id, out var value) && realtimeSinceStartup < value)
{
SendError(connection, packet, "Server cooldown is active for this connection.");
return;
}
_serverNextAllowedByConnection[connection.Id] = realtimeSinceStartup + Math.Max(0.5f, _serverCooldownSeconds.Value);
float val = Math.Max(25f, _maximumRadius.Value);
float radius = Math.Max(25f, Math.Min(packet.Radius, val));
Vec3 center = new Vec3(packet.CenterX, packet.CenterY, packet.CenterZ);
if (!DspAccess.TryGetFactoryForPlanet(packet.PlanetId, out var factory, out var error))
{
SendError(connection, packet, error);
return;
}
try
{
SnapshotBuildResult snapshotBuildResult = SnapshotCodec.BuildHostSnapshot(factory, center, radius, Math.Max(100, _maximumEntities.Value), Math.Max(1000, _maximumGroundEnemies.Value), Math.Max(1048576, _maximumPayloadBytes.Value));
if (!string.IsNullOrEmpty(snapshotBuildResult.Error))
{
SendError(connection, packet, snapshotBuildResult.Error);
return;
}
LocalResyncResponse localResyncResponse = new LocalResyncResponse();
localResyncResponse.RequestId = packet.RequestId;
localResyncResponse.PlanetId = packet.PlanetId;
localResyncResponse.CenterX = center.X;
localResyncResponse.CenterY = center.Y;
localResyncResponse.CenterZ = center.Z;
localResyncResponse.Radius = radius;
localResyncResponse.Payload = snapshotBuildResult.Payload;
localResyncResponse.Error = string.Empty;
connection.SendPacket<LocalResyncResponse>(localResyncResponse);
((BaseUnityPlugin)this).Logger.LogInfo((object)("Resync request " + packet.RequestId + " for planet " + packet.PlanetId + ": entities=" + snapshotBuildResult.EntityCount + ", componentSnapshots=" + snapshotBuildResult.ComponentCount + ", beltsAudited=" + snapshotBuildResult.BeltCount + ", activeGroundEnemies=" + snapshotBuildResult.ActiveGroundEnemyCount + ", nearbyGroundEnemies=" + snapshotBuildResult.NearbyGroundEnemyCount + ", bytes=" + snapshotBuildResult.Payload.Length + "."));
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogError((object)("Failed to build host resync snapshot: " + ex));
SendError(connection, packet, "Host failed to build resync snapshot. Check the server BepInEx log.");
}
}
private void SendError(INebulaConnection connection, LocalResyncRequest request, string message)
{
LocalResyncResponse localResyncResponse = new LocalResyncResponse();
localResyncResponse.RequestId = request.RequestId;
localResyncResponse.PlanetId = request.PlanetId;
localResyncResponse.CenterX = request.CenterX;
localResyncResponse.CenterY = request.CenterY;
localResyncResponse.CenterZ = request.CenterZ;
localResyncResponse.Radius = request.Radius;
localResyncResponse.Payload = new byte[0];
localResyncResponse.Error = message ?? "Unknown resync error.";
connection.SendPacket<LocalResyncResponse>(localResyncResponse);
((BaseUnityPlugin)this).Logger.LogWarning((object)("Rejected resync request " + request.RequestId + ": " + localResyncResponse.Error));
}
internal void HandleClientResponse(LocalResyncResponse packet)
{
if (packet == null || !NebulaModAPI.IsMultiplayerActive || NebulaModAPI.MultiplayerSession == null || !NebulaModAPI.MultiplayerSession.IsClient)
{
return;
}
if (packet.RequestId != _pendingRequestId)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)("Ignoring stale resync response " + packet.RequestId + "; current request is " + _pendingRequestId + "."));
return;
}
if (!string.IsNullOrEmpty(packet.Error))
{
ShowClientMessage("Resync rejected: " + packet.Error);
return;
}
if (!DspAccess.TryGetLocalFactoryContext(out var planetId, out var _, out var factory, out var error))
{
ShowClientMessage("Resync response could not be applied: " + error);
return;
}
if (planetId != packet.PlanetId)
{
ShowClientMessage("Resync discarded because the local player changed planets.");
return;
}
try
{
SnapshotApplyResult snapshotApplyResult = SnapshotCodec.ApplyClientSnapshot(factory, packet.Payload, new Vec3(packet.CenterX, packet.CenterY, packet.CenterZ), packet.Radius);
string text = "Resync complete: " + snapshotApplyResult.ComponentsApplied + " component states repaired; " + snapshotApplyResult.GroundEnemyPhantomsCleared + " Dark Fog phantoms cleared; " + snapshotApplyResult.GroundEnemyPendingFlagsRepaired + " enemy pending flags repaired; " + snapshotApplyResult.GroundEnemiesMissingLocally + " host enemies missing locally; " + snapshotApplyResult.HostOnlyEntities + " factory entities missing local, " + snapshotApplyResult.ClientOnlyEntities + " factory entities phantom local, " + snapshotApplyResult.MismatchedEntities + " factory entities mismatched; " + snapshotApplyResult.BeltsAudited + " belts audited (cargo paths unchanged).";
((BaseUnityPlugin)this).Logger.LogInfo((object)text);
if (snapshotApplyResult.StructuralProblems > 0)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)"Structural mismatches were NOT created/deleted by Local Resync. Use Nebula's /reconnect if the mismatches remain visible.");
}
if (snapshotApplyResult.GroundEnemiesMissingLocally > 0)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)(snapshotApplyResult.GroundEnemiesMissingLocally + " authoritative host Dark Fog enemies are missing locally. Local Resync does not synthesize new enemy units; use Nebula's /reconnect if they remain missing."));
}
if (snapshotApplyResult.GroundEnemyRepairsSkipped > 0)
{
((BaseUnityPlugin)this).Logger.LogWarning((object)(snapshotApplyResult.GroundEnemyRepairsSkipped + " Dark Fog repairs were skipped because the local state could not be reconciled safely."));
}
string message = "Resync complete: " + snapshotApplyResult.GroundEnemyPhantomsCleared + " Dark Fog phantoms cleared, " + snapshotApplyResult.GroundEnemyPendingFlagsRepaired + " enemy flags repaired, " + snapshotApplyResult.ComponentsApplied + " factory states repaired.";
ShowClientMessage(message);
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogError((object)("Failed to apply resync response: " + ex));
ShowClientMessage("Resync failed while applying the host snapshot. Check BepInEx LogOutput.log.");
}
}
private void ShowClientMessage(string message)
{
((BaseUnityPlugin)this).Logger.LogMessage((object)message);
if (_showRealtimeTip.Value)
{
DspAccess.TryShowRealtimeTip("[Nebula Resync] " + message);
}
}
}
public sealed class LocalResyncRequest
{
public int RequestId { get; set; }
public int PlanetId { get; set; }
public float CenterX { get; set; }
public float CenterY { get; set; }
public float CenterZ { get; set; }
public float Radius { get; set; }
}
public sealed class LocalResyncResponse
{
public int RequestId { get; set; }
public int PlanetId { get; set; }
public float CenterX { get; set; }
public float CenterY { get; set; }
public float CenterZ { get; set; }
public float Radius { get; set; }
public byte[] Payload { get; set; }
public string Error { get; set; }
}
[RegisterPacketProcessor]
public sealed class LocalResyncRequestProcessor : BasePacketProcessor<LocalResyncRequest>
{
public override void ProcessPacket(LocalResyncRequest packet, INebulaConnection conn)
{
if (!base.IsHost)
{
return;
}
LocalResyncPlugin.EnqueueMainThread(delegate
{
if ((Object)(object)LocalResyncPlugin.Instance != (Object)null)
{
LocalResyncPlugin.Instance.HandleHostRequest(packet, conn);
}
});
}
}
[RegisterPacketProcessor]
public sealed class LocalResyncResponseProcessor : BasePacketProcessor<LocalResyncResponse>
{
public override void ProcessPacket(LocalResyncResponse packet, INebulaConnection conn)
{
if (!base.IsClient)
{
return;
}
LocalResyncPlugin.EnqueueMainThread(delegate
{
if ((Object)(object)LocalResyncPlugin.Instance != (Object)null)
{
LocalResyncPlugin.Instance.HandleClientResponse(packet);
}
});
}
}
internal struct Vec3
{
public readonly float X;
public readonly float Y;
public readonly float Z;
public Vec3(float x, float y, float z)
{
X = x;
Y = y;
Z = z;
}
public float DistanceSquared(Vec3 other)
{
float num = X - other.X;
float num2 = Y - other.Y;
float num3 = Z - other.Z;
return num * num + num2 * num2 + num3 * num3;
}
}
internal sealed class EntitySnapshot
{
public int EntityId;
public int ProtoId;
public int StorageId;
public int AssemblerId;
public int MinerId;
public int LabId;
public int BeltId;
public Vec3 Position;
}
internal sealed class GroundEnemySnapshot
{
public int EnemyId;
public int ProtoId;
public Vec3 Position;
}
internal sealed class LocalGroundEnemyState
{
public int EnemyId;
public int ProtoId;
public Vec3 Position;
public bool IsActive;
public bool IsInvinciblePending;
}
internal sealed class ComponentSnapshot
{
public byte Kind;
public int EntityId;
public int ComponentId;
public byte[] State;
}
internal sealed class SnapshotBuildResult
{
public byte[] Payload;
public string Error;
public int EntityCount;
public int ComponentCount;
public int BeltCount;
public int ActiveGroundEnemyCount;
public int NearbyGroundEnemyCount;
}
internal sealed class SnapshotApplyResult
{
public int ComponentsApplied;
public int ComponentsSkipped;
public int HostOnlyEntities;
public int ClientOnlyEntities;
public int MismatchedEntities;
public int BeltsAudited;
public int GroundEnemyPhantomsCleared;
public int GroundEnemyPendingFlagsRepaired;
public int GroundEnemiesMissingLocally;
public int GroundEnemyRepairsSkipped;
public int StructuralProblems => HostOnlyEntities + ClientOnlyEntities + MismatchedEntities;
}
internal static class SnapshotCodec
{
private const int Magic = 1313624625;
private const int Version = 2;
private const byte StorageKind = 1;
private const byte AssemblerKind = 2;
private const byte MinerKind = 3;
private const byte LabKind = 4;
internal static SnapshotBuildResult BuildHostSnapshot(object factory, Vec3 center, float radius, int maxEntities, int maxGroundEnemies, int maxPayloadBytes)
{
SnapshotBuildResult snapshotBuildResult = new SnapshotBuildResult();
List<EntitySnapshot> list = DspAccess.ScanEntities(factory, center, radius, maxEntities + 1);
if (list.Count > maxEntities)
{
snapshotBuildResult.Error = "Resync area contains more than " + maxEntities + " entities. Lower the radius or raise Server.MaximumEntitiesPerRequest.";
snapshotBuildResult.Payload = new byte[0];
return snapshotBuildResult;
}
List<ComponentSnapshot> list2 = new List<ComponentSnapshot>();
int num = 0;
if (!DspAccess.TryScanHostGroundEnemies(factory, center, radius, maxGroundEnemies, out var activeEnemyIds, out var nearbyEnemies, out var error))
{
snapshotBuildResult.Error = error;
snapshotBuildResult.Payload = new byte[0];
return snapshotBuildResult;
}
for (int i = 0; i < list.Count; i++)
{
EntitySnapshot entitySnapshot = list[i];
if (entitySnapshot.BeltId > 0)
{
num++;
}
AddComponentSnapshot(factory, list2, 1, entitySnapshot.EntityId, entitySnapshot.StorageId, "factoryStorage", "storagePool", useExportImport: true);
AddComponentSnapshot(factory, list2, 2, entitySnapshot.EntityId, entitySnapshot.AssemblerId, "factorySystem", "assemblerPool", useExportImport: false);
AddComponentSnapshot(factory, list2, 3, entitySnapshot.EntityId, entitySnapshot.MinerId, "factorySystem", "minerPool", useExportImport: false);
AddComponentSnapshot(factory, list2, 4, entitySnapshot.EntityId, entitySnapshot.LabId, "factorySystem", "labPool", useExportImport: false);
}
using (MemoryStream memoryStream = new MemoryStream())
{
using BinaryWriter binaryWriter = new BinaryWriter(memoryStream);
binaryWriter.Write(1313624625);
binaryWriter.Write(2);
binaryWriter.Write(list.Count);
for (int i = 0; i < list.Count; i++)
{
EntitySnapshot entitySnapshot2 = list[i];
binaryWriter.Write(entitySnapshot2.EntityId);
binaryWriter.Write(entitySnapshot2.ProtoId);
binaryWriter.Write(entitySnapshot2.StorageId);
binaryWriter.Write(entitySnapshot2.AssemblerId);
binaryWriter.Write(entitySnapshot2.MinerId);
binaryWriter.Write(entitySnapshot2.LabId);
binaryWriter.Write(entitySnapshot2.BeltId);
binaryWriter.Write(entitySnapshot2.Position.X);
binaryWriter.Write(entitySnapshot2.Position.Y);
binaryWriter.Write(entitySnapshot2.Position.Z);
}
binaryWriter.Write(list2.Count);
for (int i = 0; i < list2.Count; i++)
{
ComponentSnapshot componentSnapshot = list2[i];
binaryWriter.Write(componentSnapshot.Kind);
binaryWriter.Write(componentSnapshot.EntityId);
binaryWriter.Write(componentSnapshot.ComponentId);
binaryWriter.Write(componentSnapshot.State.Length);
binaryWriter.Write(componentSnapshot.State);
}
binaryWriter.Write(activeEnemyIds.Count);
for (int i = 0; i < activeEnemyIds.Count; i++)
{
binaryWriter.Write(activeEnemyIds[i]);
}
binaryWriter.Write(nearbyEnemies.Count);
for (int i = 0; i < nearbyEnemies.Count; i++)
{
GroundEnemySnapshot groundEnemySnapshot = nearbyEnemies[i];
binaryWriter.Write(groundEnemySnapshot.EnemyId);
binaryWriter.Write(groundEnemySnapshot.ProtoId);
binaryWriter.Write(groundEnemySnapshot.Position.X);
binaryWriter.Write(groundEnemySnapshot.Position.Y);
binaryWriter.Write(groundEnemySnapshot.Position.Z);
}
binaryWriter.Flush();
if (memoryStream.Length > maxPayloadBytes)
{
snapshotBuildResult.Error = "Resync payload would be " + memoryStream.Length + " bytes, above the server limit of " + maxPayloadBytes + ". Lower the radius.";
snapshotBuildResult.Payload = new byte[0];
return snapshotBuildResult;
}
snapshotBuildResult.Payload = memoryStream.ToArray();
}
snapshotBuildResult.EntityCount = list.Count;
snapshotBuildResult.ComponentCount = list2.Count;
snapshotBuildResult.BeltCount = num;
snapshotBuildResult.ActiveGroundEnemyCount = activeEnemyIds.Count;
snapshotBuildResult.NearbyGroundEnemyCount = nearbyEnemies.Count;
snapshotBuildResult.Error = string.Empty;
return snapshotBuildResult;
}
private static void AddComponentSnapshot(object factory, List<ComponentSnapshot> components, byte kind, int entityId, int componentId, string subsystemMember, string poolMember, bool useExportImport)
{
if (componentId <= 0 || !DspAccess.TryGetComponent(factory, subsystemMember, poolMember, componentId, out var component, out var _))
{
return;
}
byte[] bytes;
if (useExportImport)
{
if (!DspAccess.TryExportComponent(component, out bytes))
{
return;
}
}
else
{
bytes = SimpleFieldState.Serialize(component);
if (bytes == null || bytes.Length == 0)
{
return;
}
}
ComponentSnapshot componentSnapshot = new ComponentSnapshot();
componentSnapshot.Kind = kind;
componentSnapshot.EntityId = entityId;
componentSnapshot.ComponentId = componentId;
componentSnapshot.State = bytes;
components.Add(componentSnapshot);
}
internal static SnapshotApplyResult ApplyClientSnapshot(object factory, byte[] payload, Vec3 center, float radius)
{
if (payload == null || payload.Length == 0)
{
throw new InvalidDataException("Resync payload is empty.");
}
List<EntitySnapshot> list = new List<EntitySnapshot>();
List<ComponentSnapshot> list2 = new List<ComponentSnapshot>();
HashSet<int> hashSet = new HashSet<int>();
List<GroundEnemySnapshot> list3 = new List<GroundEnemySnapshot>();
using (MemoryStream input = new MemoryStream(payload, writable: false))
{
using BinaryReader binaryReader = new BinaryReader(input);
if (binaryReader.ReadInt32() != 1313624625)
{
throw new InvalidDataException("Invalid Nebula Local Resync payload magic.");
}
int num = binaryReader.ReadInt32();
if (num != 2)
{
throw new InvalidDataException("Unsupported Nebula Local Resync payload version " + num + ".");
}
int num2 = binaryReader.ReadInt32();
if (num2 < 0 || num2 > 100000)
{
throw new InvalidDataException("Invalid entity count " + num2 + ".");
}
for (int i = 0; i < num2; i++)
{
EntitySnapshot entitySnapshot = new EntitySnapshot();
entitySnapshot.EntityId = binaryReader.ReadInt32();
entitySnapshot.ProtoId = binaryReader.ReadInt32();
entitySnapshot.StorageId = binaryReader.ReadInt32();
entitySnapshot.AssemblerId = binaryReader.ReadInt32();
entitySnapshot.MinerId = binaryReader.ReadInt32();
entitySnapshot.LabId = binaryReader.ReadInt32();
entitySnapshot.BeltId = binaryReader.ReadInt32();
entitySnapshot.Position = new Vec3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
list.Add(entitySnapshot);
}
int num3 = binaryReader.ReadInt32();
if (num3 < 0 || num3 > 100000)
{
throw new InvalidDataException("Invalid component snapshot count " + num3 + ".");
}
for (int i = 0; i < num3; i++)
{
ComponentSnapshot componentSnapshot = new ComponentSnapshot();
componentSnapshot.Kind = binaryReader.ReadByte();
componentSnapshot.EntityId = binaryReader.ReadInt32();
componentSnapshot.ComponentId = binaryReader.ReadInt32();
int num4 = binaryReader.ReadInt32();
if (num4 < 0 || num4 > payload.Length)
{
throw new InvalidDataException("Invalid component payload length " + num4 + ".");
}
componentSnapshot.State = binaryReader.ReadBytes(num4);
if (componentSnapshot.State.Length != num4)
{
throw new EndOfStreamException("Truncated component payload.");
}
list2.Add(componentSnapshot);
}
int num5 = binaryReader.ReadInt32();
if (num5 < 0 || num5 > 500000)
{
throw new InvalidDataException("Invalid active ground enemy count " + num5 + ".");
}
for (int i = 0; i < num5; i++)
{
int num6 = binaryReader.ReadInt32();
if (num6 > 0)
{
hashSet.Add(num6);
}
}
int num7 = binaryReader.ReadInt32();
if (num7 < 0 || num7 > 500000)
{
throw new InvalidDataException("Invalid nearby ground enemy count " + num7 + ".");
}
for (int i = 0; i < num7; i++)
{
GroundEnemySnapshot groundEnemySnapshot = new GroundEnemySnapshot();
groundEnemySnapshot.EnemyId = binaryReader.ReadInt32();
groundEnemySnapshot.ProtoId = binaryReader.ReadInt32();
groundEnemySnapshot.Position = new Vec3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
list3.Add(groundEnemySnapshot);
}
}
SnapshotApplyResult snapshotApplyResult = new SnapshotApplyResult();
Dictionary<int, EntitySnapshot> dictionary = new Dictionary<int, EntitySnapshot>();
HashSet<int> hashSet2 = new HashSet<int>();
for (int j = 0; j < list.Count; j++)
{
EntitySnapshot entitySnapshot2 = list[j];
dictionary[entitySnapshot2.EntityId] = entitySnapshot2;
if (entitySnapshot2.BeltId > 0)
{
snapshotApplyResult.BeltsAudited++;
}
if (!DspAccess.TryReadEntity(factory, entitySnapshot2.EntityId, out var snapshot))
{
snapshotApplyResult.HostOnlyEntities++;
}
else if (snapshot.ProtoId != entitySnapshot2.ProtoId || snapshot.StorageId != entitySnapshot2.StorageId || snapshot.AssemblerId != entitySnapshot2.AssemblerId || snapshot.MinerId != entitySnapshot2.MinerId || snapshot.LabId != entitySnapshot2.LabId || snapshot.BeltId != entitySnapshot2.BeltId)
{
snapshotApplyResult.MismatchedEntities++;
}
else
{
hashSet2.Add(entitySnapshot2.EntityId);
}
}
List<EntitySnapshot> list4 = DspAccess.ScanEntities(factory, center, radius, 100001);
for (int k = 0; k < list4.Count; k++)
{
if (!dictionary.ContainsKey(list4[k].EntityId))
{
snapshotApplyResult.ClientOnlyEntities++;
}
}
DspAccess.ReconcileGroundEnemies(factory, center, radius, hashSet, list3, snapshotApplyResult);
for (int l = 0; l < list2.Count; l++)
{
ComponentSnapshot componentSnapshot2 = list2[l];
if (!hashSet2.Contains(componentSnapshot2.EntityId))
{
snapshotApplyResult.ComponentsSkipped++;
continue;
}
bool flag = false;
if (componentSnapshot2.Kind == 1)
{
flag = ApplyComponent(factory, componentSnapshot2, "factoryStorage", "storagePool", useExportImport: true);
}
else if (componentSnapshot2.Kind == 2)
{
flag = ApplyComponent(factory, componentSnapshot2, "factorySystem", "assemblerPool", useExportImport: false);
}
else if (componentSnapshot2.Kind == 3)
{
flag = ApplyComponent(factory, componentSnapshot2, "factorySystem", "minerPool", useExportImport: false);
}
else if (componentSnapshot2.Kind == 4)
{
flag = ApplyComponent(factory, componentSnapshot2, "factorySystem", "labPool", useExportImport: false);
}
if (flag)
{
snapshotApplyResult.ComponentsApplied++;
}
else
{
snapshotApplyResult.ComponentsSkipped++;
}
}
return snapshotApplyResult;
}
private static bool ApplyComponent(object factory, ComponentSnapshot snapshot, string subsystemMember, string poolMember, bool useExportImport)
{
if (!DspAccess.TryGetComponent(factory, subsystemMember, poolMember, snapshot.ComponentId, out var component, out var pool))
{
return false;
}
bool flag = ((!useExportImport) ? SimpleFieldState.Apply(component, snapshot.State) : DspAccess.TryImportComponent(component, snapshot.State));
if (flag && component.GetType().IsValueType)
{
pool.SetValue(component, snapshot.ComponentId);
}
return flag;
}
}
internal static class SimpleFieldState
{
private const byte BoolCode = 1;
private const byte ByteCode = 2;
private const byte SByteCode = 3;
private const byte Int16Code = 4;
private const byte UInt16Code = 5;
private const byte Int32Code = 6;
private const byte UInt32Code = 7;
private const byte Int64Code = 8;
private const byte UInt64Code = 9;
private const byte SingleCode = 10;
private const byte DoubleCode = 11;
private const byte CharCode = 12;
private const byte StringCode = 13;
private const byte EnumCode = 14;
private const byte ArrayOffset = 64;
internal static byte[] Serialize(object component)
{
if (component == null)
{
return new byte[0];
}
List<FieldInfo> serializableFields = GetSerializableFields(component.GetType());
if (serializableFields.Count == 0)
{
return new byte[0];
}
using MemoryStream memoryStream = new MemoryStream();
using BinaryWriter binaryWriter = new BinaryWriter(memoryStream);
binaryWriter.Write(serializableFields.Count);
for (int i = 0; i < serializableFields.Count; i++)
{
FieldInfo fieldInfo = serializableFields[i];
object value = fieldInfo.GetValue(component);
byte typeCode = GetTypeCode(fieldInfo.FieldType);
binaryWriter.Write(fieldInfo.Name);
binaryWriter.Write(typeCode);
WriteValue(binaryWriter, fieldInfo.FieldType, typeCode, value);
}
binaryWriter.Flush();
return memoryStream.ToArray();
}
internal static bool Apply(object component, byte[] state)
{
if (component == null || state == null || state.Length == 0)
{
return false;
}
Type type = component.GetType();
Dictionary<string, FieldInfo> dictionary = new Dictionary<string, FieldInfo>(StringComparer.Ordinal);
FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
for (int i = 0; i < fields.Length; i++)
{
if (!fields[i].IsStatic)
{
dictionary[fields[i].Name] = fields[i];
}
}
using (MemoryStream input = new MemoryStream(state, writable: false))
{
using BinaryReader binaryReader = new BinaryReader(input);
int num = binaryReader.ReadInt32();
if (num < 0 || num > 1024)
{
return false;
}
for (int i = 0; i < num; i++)
{
string key = binaryReader.ReadString();
byte b = binaryReader.ReadByte();
if (!dictionary.TryGetValue(key, out var value) || value.IsInitOnly || value.IsLiteral)
{
SkipValue(binaryReader, b);
continue;
}
byte typeCode = GetTypeCode(value.FieldType);
if (typeCode == 0 || typeCode != b)
{
SkipValue(binaryReader, b);
continue;
}
object value2 = ReadValue(binaryReader, value.FieldType, b);
value.SetValue(component, value2);
}
}
return true;
}
private static List<FieldInfo> GetSerializableFields(Type type)
{
List<FieldInfo> list = new List<FieldInfo>();
FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (FieldInfo fieldInfo in fields)
{
if (!fieldInfo.IsStatic && !fieldInfo.IsLiteral && !fieldInfo.IsInitOnly && GetTypeCode(fieldInfo.FieldType) != 0)
{
list.Add(fieldInfo);
}
}
list.Sort((FieldInfo a, FieldInfo b) => string.CompareOrdinal(a.Name, b.Name));
return list;
}
private static byte GetTypeCode(Type type)
{
if (type.IsArray)
{
Type elementType = type.GetElementType();
byte scalarTypeCode = GetScalarTypeCode(elementType);
if (scalarTypeCode == 0 || scalarTypeCode == 13)
{
return 0;
}
return (byte)(64 + scalarTypeCode);
}
return GetScalarTypeCode(type);
}
private static byte GetScalarTypeCode(Type type)
{
if (type == typeof(bool))
{
return 1;
}
if (type == typeof(byte))
{
return 2;
}
if (type == typeof(sbyte))
{
return 3;
}
if (type == typeof(short))
{
return 4;
}
if (type == typeof(ushort))
{
return 5;
}
if (type == typeof(int))
{
return 6;
}
if (type == typeof(uint))
{
return 7;
}
if (type == typeof(long))
{
return 8;
}
if (type == typeof(ulong))
{
return 9;
}
if (type == typeof(float))
{
return 10;
}
if (type == typeof(double))
{
return 11;
}
if (type == typeof(char))
{
return 12;
}
if (type == typeof(string))
{
return 13;
}
if (type != null && type.IsEnum)
{
return 14;
}
return 0;
}
private static void WriteValue(BinaryWriter writer, Type declaredType, byte code, object value)
{
if (code >= 64)
{
if (!(value is Array array))
{
writer.Write(-1);
return;
}
writer.Write(array.Length);
byte code2 = (byte)(code - 64);
Type elementType = declaredType.GetElementType();
for (int i = 0; i < array.Length; i++)
{
WriteScalar(writer, elementType, code2, array.GetValue(i));
}
}
else
{
WriteScalar(writer, declaredType, code, value);
}
}
private static object ReadValue(BinaryReader reader, Type declaredType, byte code)
{
if (code >= 64)
{
int num = reader.ReadInt32();
if (num < 0)
{
return null;
}
if (num > 1000000)
{
throw new InvalidDataException("Component array is unreasonably large: " + num + ".");
}
Type elementType = declaredType.GetElementType();
byte code2 = (byte)(code - 64);
Array array = Array.CreateInstance(elementType, num);
for (int i = 0; i < num; i++)
{
array.SetValue(ReadScalar(reader, elementType, code2), i);
}
return array;
}
return ReadScalar(reader, declaredType, code);
}
private static void SkipValue(BinaryReader reader, byte code)
{
if (code >= 64)
{
int num = reader.ReadInt32();
if (num >= 0)
{
byte code2 = (byte)(code - 64);
for (int i = 0; i < num; i++)
{
ReadScalar(reader, null, code2);
}
}
}
else
{
ReadScalar(reader, null, code);
}
}
private static void WriteScalar(BinaryWriter writer, Type declaredType, byte code, object value)
{
switch (code)
{
case 1:
writer.Write(value != null && (bool)value);
break;
case 2:
writer.Write((byte)((value != null) ? ((byte)value) : 0));
break;
case 3:
writer.Write((sbyte)((value != null) ? ((sbyte)value) : 0));
break;
case 4:
writer.Write((short)((value != null) ? ((short)value) : 0));
break;
case 5:
writer.Write((ushort)((value != null) ? ((ushort)value) : 0));
break;
case 6:
writer.Write((value != null) ? ((int)value) : 0);
break;
case 7:
writer.Write((value != null) ? ((uint)value) : 0u);
break;
case 8:
writer.Write((value == null) ? 0 : ((long)value));
break;
case 9:
writer.Write((value == null) ? 0 : ((ulong)value));
break;
case 10:
writer.Write((value == null) ? 0f : ((float)value));
break;
case 11:
writer.Write((value == null) ? 0.0 : ((double)value));
break;
case 12:
writer.Write((value != null) ? ((char)value) : '\0');
break;
case 13:
writer.Write(value != null);
if (value != null)
{
writer.Write((string)value);
}
break;
case 14:
writer.Write((value == null) ? 0 : Convert.ToInt64(value));
break;
default:
throw new InvalidDataException("Unsupported field type code " + code + ".");
}
}
private static object ReadScalar(BinaryReader reader, Type declaredType, byte code)
{
switch (code)
{
case 1:
return reader.ReadBoolean();
case 2:
return reader.ReadByte();
case 3:
return reader.ReadSByte();
case 4:
return reader.ReadInt16();
case 5:
return reader.ReadUInt16();
case 6:
return reader.ReadInt32();
case 7:
return reader.ReadUInt32();
case 8:
return reader.ReadInt64();
case 9:
return reader.ReadUInt64();
case 10:
return reader.ReadSingle();
case 11:
return reader.ReadDouble();
case 12:
return reader.ReadChar();
case 13:
if (!reader.ReadBoolean())
{
return null;
}
return reader.ReadString();
case 14:
{
long num = reader.ReadInt64();
if (!(declaredType != null) || !declaredType.IsEnum)
{
return num;
}
return Enum.ToObject(declaredType, num);
}
default:
throw new InvalidDataException("Unsupported field type code " + code + ".");
}
}
}
internal static class DspAccess
{
private const BindingFlags AnyInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
private const BindingFlags AnyStatic = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
private static Type _gameMainType;
internal static bool TryGetLocalFactoryContext(out int planetId, out Vec3 center, out object factory, out string error)
{
planetId = 0;
center = default(Vec3);
factory = null;
error = string.Empty;
try
{
Type gameMainType = GetGameMainType();
if (gameMainType == null)
{
error = "GameMain type is unavailable.";
return false;
}
object memberValue = GetMemberValue(gameMainType, null, "localPlanet", isStatic: true);
object memberValue2 = GetMemberValue(gameMainType, null, "mainPlayer", isStatic: true);
if (memberValue == null || memberValue2 == null)
{
error = "the local mech is not on a loaded planet.";
return false;
}
planetId = Convert.ToInt32(GetMemberValue(memberValue.GetType(), memberValue, "id", isStatic: false));
object memberValue3 = GetMemberValue(memberValue2.GetType(), memberValue2, "position", isStatic: false);
if (!TryReadVector(memberValue3, out center))
{
error = "the local mech position could not be read.";
return false;
}
factory = GetMemberValue(memberValue.GetType(), memberValue, "factory", isStatic: false);
if (factory == null)
{
error = "the local PlanetFactory is not loaded.";
return false;
}
return true;
}
catch (Exception ex)
{
error = "DSP state lookup failed: " + ex.Message;
return false;
}
}
internal static bool TryGetFactoryForPlanet(int planetId, out object factory, out string error)
{
factory = null;
error = string.Empty;
try
{
Type gameMainType = GetGameMainType();
if (gameMainType == null)
{
error = "Host GameMain type is unavailable.";
return false;
}
object memberValue = GetMemberValue(gameMainType, null, "galaxy", isStatic: true);
if (memberValue == null)
{
error = "Host galaxy is not loaded.";
return false;
}
MethodInfo method = memberValue.GetType().GetMethod("PlanetById", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(int) }, null);
if (method == null)
{
error = "Host GalaxyData.PlanetById(int) was not found.";
return false;
}
object obj = method.Invoke(memberValue, new object[1] { planetId });
if (obj == null)
{
error = "Host does not know planet " + planetId + ".";
return false;
}
factory = GetMemberValue(obj.GetType(), obj, "factory", isStatic: false);
if (factory == null)
{
error = "Host PlanetFactory for planet " + planetId + " is not loaded.";
return false;
}
return true;
}
catch (Exception ex)
{
error = "Host factory lookup failed: " + ex.Message;
return false;
}
}
internal static List<EntitySnapshot> ScanEntities(object factory, Vec3 center, float radius, int hardLimit)
{
List<EntitySnapshot> list = new List<EntitySnapshot>();
if (!(GetMemberValue(factory.GetType(), factory, "entityPool", isStatic: false) is Array array))
{
return list;
}
int val = Convert.ToInt32(GetMemberValue(factory.GetType(), factory, "entityCursor", isStatic: false));
int num = Math.Min(val, array.Length);
float num2 = radius * radius;
for (int i = 1; i < num; i++)
{
object value = array.GetValue(i);
if (!IsActiveIndexedObject(value, i))
{
continue;
}
object value2 = TryGetMemberValue(value.GetType(), value, "pos", isStatic: false);
if (TryReadVector(value2, out var vector) && !(vector.DistanceSquared(center) > num2) && TryCreateEntitySnapshot(value, i, vector, out var snapshot))
{
list.Add(snapshot);
if (list.Count >= hardLimit)
{
return list;
}
}
}
return list;
}
internal static bool TryReadEntity(object factory, int entityId, out EntitySnapshot snapshot)
{
snapshot = null;
if (!(TryGetMemberValue(factory.GetType(), factory, "entityPool", isStatic: false) is Array array) || entityId <= 0 || entityId >= array.Length)
{
return false;
}
object value = array.GetValue(entityId);
if (!IsActiveIndexedObject(value, entityId))
{
return false;
}
if (!TryReadVector(TryGetMemberValue(value.GetType(), value, "pos", isStatic: false), out var vector))
{
vector = default(Vec3);
}
return TryCreateEntitySnapshot(value, entityId, vector, out snapshot);
}
private static bool TryCreateEntitySnapshot(object entity, int fallbackId, Vec3 position, out EntitySnapshot snapshot)
{
snapshot = null;
if (entity == null)
{
return false;
}
EntitySnapshot entitySnapshot = new EntitySnapshot();
entitySnapshot.EntityId = ReadIntMember(entity, "id", fallbackId);
entitySnapshot.ProtoId = ReadIntMember(entity, "protoId", 0);
entitySnapshot.StorageId = ReadIntMember(entity, "storageId", 0);
entitySnapshot.AssemblerId = ReadIntMember(entity, "assemblerId", 0);
entitySnapshot.MinerId = ReadIntMember(entity, "minerId", 0);
entitySnapshot.LabId = ReadIntMember(entity, "labId", 0);
entitySnapshot.BeltId = ReadIntMember(entity, "beltId", 0);
entitySnapshot.Position = position;
snapshot = entitySnapshot;
if (entitySnapshot.EntityId > 0)
{
return entitySnapshot.ProtoId > 0;
}
return false;
}
internal static bool TryScanHostGroundEnemies(object factory, Vec3 center, float radius, int hardLimit, out List<int> activeEnemyIds, out List<GroundEnemySnapshot> nearbyEnemies, out string error)
{
activeEnemyIds = new List<int>();
nearbyEnemies = new List<GroundEnemySnapshot>();
error = string.Empty;
if (factory == null)
{
error = "Host PlanetFactory is unavailable while scanning Dark Fog enemies.";
return false;
}
if (!(TryGetMemberValue(factory.GetType(), factory, "enemyPool", isStatic: false) is Array array))
{
return true;
}
int val = ReadIntMember(factory, "enemyCursor", array.Length);
int num = Math.Min(Math.Max(val, 0), array.Length);
float num2 = radius * radius;
for (int i = 1; i < num; i++)
{
object value = array.GetValue(i);
if (value == null)
{
continue;
}
int num3 = ReadIntMember(value, "id", 0);
if (num3 == i)
{
activeEnemyIds.Add(i);
if (activeEnemyIds.Count > hardLimit)
{
error = "Planet contains more than " + hardLimit + " active ground enemies. Raise Server.MaximumGroundEnemiesPerPlanet before using Local Resync on this save.";
activeEnemyIds.Clear();
nearbyEnemies.Clear();
return false;
}
if (TryReadVector(TryGetMemberValue(value.GetType(), value, "pos", isStatic: false), out var vector) && vector.DistanceSquared(center) <= num2)
{
GroundEnemySnapshot groundEnemySnapshot = new GroundEnemySnapshot();
groundEnemySnapshot.EnemyId = i;
groundEnemySnapshot.ProtoId = ReadIntMember(value, "protoId", 0);
groundEnemySnapshot.Position = vector;
nearbyEnemies.Add(groundEnemySnapshot);
}
}
}
return true;
}
internal static void ReconcileGroundEnemies(object factory, Vec3 center, float radius, HashSet<int> hostActiveEnemyIds, List<GroundEnemySnapshot> hostNearbyEnemies, SnapshotApplyResult result)
{
if (factory == null || result == null || hostActiveEnemyIds == null || hostNearbyEnemies == null)
{
return;
}
List<LocalGroundEnemyState> list = ScanLocalGroundEnemyCandidates(factory, center, radius);
Dictionary<int, LocalGroundEnemyState> dictionary = new Dictionary<int, LocalGroundEnemyState>();
for (int i = 0; i < list.Count; i++)
{
dictionary[list[i].EnemyId] = list[i];
}
for (int i = 0; i < hostNearbyEnemies.Count; i++)
{
GroundEnemySnapshot groundEnemySnapshot = hostNearbyEnemies[i];
if (!dictionary.TryGetValue(groundEnemySnapshot.EnemyId, out var value))
{
result.GroundEnemiesMissingLocally++;
}
else if (groundEnemySnapshot.ProtoId > 0 && value.ProtoId > 0 && groundEnemySnapshot.ProtoId != value.ProtoId)
{
result.GroundEnemyRepairsSkipped++;
}
else if (value.IsInvinciblePending && hostActiveEnemyIds.Contains(value.EnemyId))
{
if (TryRestorePendingGroundEnemy(factory, value.EnemyId))
{
result.GroundEnemyPendingFlagsRepaired++;
}
else
{
result.GroundEnemyRepairsSkipped++;
}
}
}
using IDisposable disposable = TryEnterNebulaIncomingCombatScope();
for (int i = 0; i < list.Count; i++)
{
LocalGroundEnemyState localGroundEnemyState = list[i];
if (!hostActiveEnemyIds.Contains(localGroundEnemyState.EnemyId))
{
if (disposable == null)
{
result.GroundEnemyRepairsSkipped++;
}
else if (TryRemoveLocalGroundEnemyFinal(factory, localGroundEnemyState.EnemyId))
{
result.GroundEnemyPhantomsCleared++;
}
else
{
result.GroundEnemyRepairsSkipped++;
}
}
}
}
private static List<LocalGroundEnemyState> ScanLocalGroundEnemyCandidates(object factory, Vec3 center, float radius)
{
List<LocalGroundEnemyState> list = new List<LocalGroundEnemyState>();
if (!(TryGetMemberValue(factory.GetType(), factory, "enemyPool", isStatic: false) is Array array))
{
return list;
}
int val = ReadIntMember(factory, "enemyCursor", array.Length);
int num = Math.Min(Math.Max(val, 0), array.Length);
float num2 = radius * radius;
for (int i = 1; i < num; i++)
{
object value = array.GetValue(i);
if (value != null)
{
int num3 = ReadIntMember(value, "id", 0);
bool flag = num3 == i;
bool flag2 = ReadBoolMember(value, "isInvincible", fallback: false) && num3 != i;
if ((flag || flag2) && TryReadVector(TryGetMemberValue(value.GetType(), value, "pos", isStatic: false), out var vector) && !(vector.DistanceSquared(center) > num2))
{
LocalGroundEnemyState localGroundEnemyState = new LocalGroundEnemyState();
localGroundEnemyState.EnemyId = i;
localGroundEnemyState.ProtoId = ReadIntMember(value, "protoId", 0);
localGroundEnemyState.Position = vector;
localGroundEnemyState.IsActive = flag;
localGroundEnemyState.IsInvinciblePending = flag2;
list.Add(localGroundEnemyState);
}
}
}
return list;
}
private static bool TryRestorePendingGroundEnemy(object factory, int enemyId)
{
if (!(TryGetMemberValue(factory.GetType(), factory, "enemyPool", isStatic: false) is Array array) || enemyId <= 0 || enemyId >= array.Length)
{
return false;
}
object value = array.GetValue(enemyId);
if (value == null || !ReadBoolMember(value, "isInvincible", fallback: false))
{
return false;
}
if (!TrySetMemberValue(value.GetType(), value, "id", enemyId) || !TrySetMemberValue(value.GetType(), value, "isInvincible", false))
{
return false;
}
if (value.GetType().IsValueType)
{
array.SetValue(value, enemyId);
}
return true;
}
private static bool TryRemoveLocalGroundEnemyFinal(object factory, int enemyId)
{
if (factory == null || enemyId <= 0)
{
return false;
}
if (!(TryGetMemberValue(factory.GetType(), factory, "enemyPool", isStatic: false) is Array array) || enemyId >= array.Length)
{
return false;
}
MethodInfo method = factory.GetType().GetMethod("RemoveEnemyFinal", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(int) }, null);
if (method == null)
{
return false;
}
object value = array.GetValue(enemyId);
if (value == null)
{
return false;
}
int num = ReadIntMember(value, "id", 0);
bool flag = ReadBoolMember(value, "isInvincible", fallback: false);
if (num != enemyId && !TrySetMemberValue(value.GetType(), value, "id", enemyId))
{
return false;
}
if (flag && !TrySetMemberValue(value.GetType(), value, "isInvincible", false))
{
return false;
}
if (value.GetType().IsValueType)
{
array.SetValue(value, enemyId);
}
try
{
method.Invoke(factory, new object[1] { enemyId });
return true;
}
catch
{
object value2 = array.GetValue(enemyId);
if (value2 != null)
{
TrySetMemberValue(value2.GetType(), value2, "id", num);
TrySetMemberValue(value2.GetType(), value2, "isInvincible", flag);
if (value2.GetType().IsValueType)
{
array.SetValue(value2, enemyId);
}
}
return false;
}
}
private static IDisposable TryEnterNebulaIncomingCombatScope()
{
try
{
Type type = FindLoadedType("NebulaWorld.Multiplayer");
if (type == null)
{
return null;
}
object obj = TryGetMemberValue(type, null, "Session", isStatic: true);
if (obj == null)
{
return null;
}
object obj2 = TryGetMemberValue(obj.GetType(), obj, "Combat", isStatic: false);
if (obj2 == null)
{
return null;
}
object obj3 = TryGetMemberValue(obj2.GetType(), obj2, "IsIncomingRequest", isStatic: false);
if (obj3 == null)
{
return null;
}
MethodInfo method = obj3.GetType().GetMethod("On", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
if (method == null)
{
return null;
}
return method.Invoke(obj3, null) as IDisposable;
}
catch
{
return null;
}
}
internal static bool TryGetComponent(object factory, string subsystemMember, string poolMember, int componentId, out object component, out Array pool)
{
component = null;
pool = null;
if (factory == null || componentId <= 0)
{
return false;
}
object obj = TryGetMemberValue(factory.GetType(), factory, subsystemMember, isStatic: false);
if (obj == null)
{
return false;
}
pool = TryGetMemberValue(obj.GetType(), obj, poolMember, isStatic: false) as Array;
if (pool == null || componentId >= pool.Length)
{
return false;
}
component = pool.GetValue(componentId);
if (!IsActiveIndexedObject(component, componentId))
{
component = null;
return false;
}
return true;
}
internal static bool TryExportComponent(object component, out byte[] bytes)
{
bytes = null;
if (component == null)
{
return false;
}
MethodInfo method = component.GetType().GetMethod("Export", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(BinaryWriter) }, null);
if (method == null)
{
return false;
}
using MemoryStream memoryStream = new MemoryStream();
using BinaryWriter binaryWriter = new BinaryWriter(memoryStream);
method.Invoke(component, new object[1] { binaryWriter });
binaryWriter.Flush();
bytes = memoryStream.ToArray();
return true;
}
internal static bool TryImportComponent(object component, byte[] bytes)
{
if (component == null || bytes == null)
{
return false;
}
MethodInfo method = component.GetType().GetMethod("Import", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(BinaryReader) }, null);
if (method == null)
{
return false;
}
using MemoryStream input = new MemoryStream(bytes, writable: false);
using BinaryReader binaryReader = new BinaryReader(input);
method.Invoke(component, new object[1] { binaryReader });
return true;
}
internal static void TryShowRealtimeTip(string message)
{
try
{
Type type = FindLoadedType("UIRealtimeTip");
if (type == null)
{
return;
}
MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
foreach (MethodInfo methodInfo in methods)
{
if (methodInfo.Name != "Popup")
{
continue;
}
ParameterInfo[] parameters = methodInfo.GetParameters();
if (parameters.Length == 0 || parameters[0].ParameterType != typeof(string))
{
continue;
}
object[] array = new object[parameters.Length];
bool flag = true;
for (int j = 0; j < parameters.Length; j++)
{
Type parameterType = parameters[j].ParameterType;
if (j == 0 && parameterType == typeof(string))
{
array[j] = message;
continue;
}
if (parameters[j].HasDefaultValue)
{
array[j] = parameters[j].DefaultValue;
continue;
}
if (parameterType == typeof(bool))
{
array[j] = false;
continue;
}
if (parameterType == typeof(int))
{
array[j] = 0;
continue;
}
if (parameterType == typeof(float))
{
array[j] = 0f;
continue;
}
flag = false;
break;
}
if (flag)
{
methodInfo.Invoke(null, array);
break;
}
}
}
catch
{
}
}
private static Type GetGameMainType()
{
if (_gameMainType == null)
{
_gameMainType = FindLoadedType("GameMain");
}
return _gameMainType;
}
private static Type FindLoadedType(string fullName)
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
for (int i = 0; i < assemblies.Length; i++)
{
Type type = assemblies[i].GetType(fullName, throwOnError: false);
if (type != null)
{
return type;
}
}
return null;
}
private static bool IsActiveIndexedObject(object value, int expectedId)
{
if (value == null)
{
return false;
}
object obj = TryGetMemberValue(value.GetType(), value, "id", isStatic: false);
if (obj == null)
{
return true;
}
try
{
int num = Convert.ToInt32(obj);
return num == expectedId;
}
catch
{
return true;
}
}
private static int ReadIntMember(object value, string memberName, int fallback)
{
object obj = TryGetMemberValue(value.GetType(), value, memberName, isStatic: false);
if (obj == null)
{
return fallback;
}
try
{
return Convert.ToInt32(obj);
}
catch
{
return fallback;
}
}
private static bool ReadBoolMember(object value, string memberName, bool fallback)
{
if (value == null)
{
return fallback;
}
object obj = TryGetMemberValue(value.GetType(), value, memberName, isStatic: false);
if (obj == null)
{
return fallback;
}
try
{
return Convert.ToBoolean(obj);
}
catch
{
return fallback;
}
}
private static bool TrySetMemberValue(Type type, object instance, string name, object value)
{
try
{
FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field != null && !field.IsInitOnly && !field.IsLiteral)
{
field.SetValue(instance, value);
return true;
}
PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (property != null && property.CanWrite)
{
property.SetValue(instance, value, null);
return true;
}
}
catch
{
}
return false;
}
private static bool TryReadVector(object value, out Vec3 vector)
{
vector = default(Vec3);
if (value == null)
{
return false;
}
object obj = TryGetMemberValue(value.GetType(), value, "x", isStatic: false);
object obj2 = TryGetMemberValue(value.GetType(), value, "y", isStatic: false);
object obj3 = TryGetMemberValue(value.GetType(), value, "z", isStatic: false);
if (obj == null || obj2 == null || obj3 == null)
{
return false;
}
vector = new Vec3(Convert.ToSingle(obj), Convert.ToSingle(obj2), Convert.ToSingle(obj3));
return true;
}
private static object TryGetMemberValue(Type type, object instance, string name, bool isStatic)
{
try
{
return GetMemberValue(type, instance, name, isStatic);
}
catch
{
return null;
}
}
private static object GetMemberValue(Type type, object instance, string name, bool isStatic)
{
BindingFlags bindingAttr = (isStatic ? (BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) : (BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
FieldInfo field = type.GetField(name, bindingAttr);
if (field != null)
{
return field.GetValue(instance);
}
PropertyInfo property = type.GetProperty(name, bindingAttr);
if (property != null)
{
return property.GetValue(instance, null);
}
throw new MissingMemberException(type.FullName, name);
}
}