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 MainThreadActions = new Queue(); private static int NextRequestId; internal static LocalResyncPlugin Instance; internal static ManualLogSource Log; private ConfigEntry _hotkey; private ConfigEntry _defaultRadius; private ConfigEntry _maximumRadius; private ConfigEntry _maximumEntities; private ConfigEntry _maximumGroundEnemies; private ConfigEntry _maximumPayloadBytes; private ConfigEntry _serverCooldownSeconds; private ConfigEntry _showRealtimeTip; private readonly Dictionary _serverNextAllowedByConnection = new Dictionary(); 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("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("Client", "DefaultRadiusMeters", 150f, "Radius around the mech to audit/resync when the hotkey is pressed."); _maximumRadius = ((BaseUnityPlugin)this).Config.Bind("Server", "MaximumRadiusMeters", 500f, "Hard server-side clamp for client resync requests."); _maximumEntities = ((BaseUnityPlugin)this).Config.Bind("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("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("Server", "MaximumPayloadBytes", 16777216, "Maximum uncompressed custom resync payload. Requests exceeding this are rejected."); _serverCooldownSeconds = ((BaseUnityPlugin)this).Config.Bind("Server", "MinimumSecondsBetweenRequestsPerConnection", 2f, "Minimum server-side interval between resync requests from the same client."); _showRealtimeTip = ((BaseUnityPlugin)this).Config.Bind("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); 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); ((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); ((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 { 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 { 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 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 list2 = new List(); 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 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 list = new List(); List list2 = new List(); HashSet hashSet = new HashSet(); List list3 = new List(); 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 dictionary = new Dictionary(); HashSet hashSet2 = new HashSet(); 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 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 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 dictionary = new Dictionary(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 GetSerializableFields(Type type) { List list = new List(); 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 ScanEntities(object factory, Vec3 center, float radius, int hardLimit) { List list = new List(); 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 activeEnemyIds, out List nearbyEnemies, out string error) { activeEnemyIds = new List(); nearbyEnemies = new List(); 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 hostActiveEnemyIds, List hostNearbyEnemies, SnapshotApplyResult result) { if (factory == null || result == null || hostActiveEnemyIds == null || hostNearbyEnemies == null) { return; } List list = ScanLocalGroundEnemyCandidates(factory, center, radius); Dictionary dictionary = new Dictionary(); 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 ScanLocalGroundEnemyCandidates(object factory, Vec3 center, float radius) { List list = new List(); 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); } }