using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using ABN;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("DSPEasy.Framework")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.34.0")]
[assembly: AssemblyInformationalVersion("0.2.34+8d07755fdac77ea113d82f799a4e08686c89e363")]
[assembly: AssemblyProduct("DSPEasy.Framework")]
[assembly: AssemblyTitle("DSPEasy.Framework")]
[assembly: AssemblyVersion("0.2.34.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace DSPEasy.Framework.UI
{
public class UiDrag : MonoBehaviour
{
public RectTransform target;
public RectTransform grabArea;
public bool keepInsideParent = true;
private bool dragging;
private Camera cam;
private RectTransform parentRt;
private Vector2 beginLocal;
private Vector2 beginAnchored;
private void OnDisable()
{
dragging = false;
}
private void Update()
{
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
//IL_006c: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
//IL_0078: Unknown result type (might be due to invalid IL or missing references)
Vector2 val = default(Vector2);
if ((Object)(object)target == (Object)null)
{
dragging = false;
}
else if (!Input.GetMouseButton(0))
{
dragging = false;
}
else if ((dragging || (Input.GetMouseButtonDown(0) && TryBegin())) && RectTransformUtility.ScreenPointToLocalPointInRectangle(parentRt, Vector2.op_Implicit(Input.mousePosition), cam, ref val))
{
target.anchoredPosition = beginAnchored + (val - beginLocal);
if (keepInsideParent)
{
ClampInsideParent();
}
}
}
private bool TryBegin()
{
//IL_003a: Unknown result type (might be due to invalid IL or missing references)
//IL_0054: Unknown result type (might be due to invalid IL or missing references)
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
//IL_0060: Unknown result type (might be due to invalid IL or missing references)
//IL_008a: Unknown result type (might be due to invalid IL or missing references)
//IL_009d: Unknown result type (might be due to invalid IL or missing references)
//IL_009e: Unknown result type (might be due to invalid IL or missing references)
//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
//IL_00af: Unknown result type (might be due to invalid IL or missing references)
ref RectTransform reference = ref parentRt;
Transform parent = ((Transform)target).parent;
reference = (RectTransform)(object)((parent is RectTransform) ? parent : null);
if ((Object)(object)parentRt == (Object)null)
{
return false;
}
RectTransform val = (RectTransform)(((Object)(object)grabArea != (Object)null) ? ((object)grabArea) : ((object)(RectTransform)((Component)this).transform));
cam = UiFactory.CanvasCamera((Component)(object)val);
Vector2 val2 = Vector2.op_Implicit(Input.mousePosition);
if (!RectTransformUtility.RectangleContainsScreenPoint(val, val2, cam))
{
return false;
}
if (!UiFocus.TopmostIs(((Component)target).gameObject))
{
return false;
}
Vector2 val3 = default(Vector2);
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(parentRt, val2, cam, ref val3))
{
return false;
}
beginLocal = val3;
beginAnchored = target.anchoredPosition;
dragging = true;
return true;
}
private void ClampInsideParent()
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
//IL_00da: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_015b: Unknown result type (might be due to invalid IL or missing references)
//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
//IL_007c: Unknown result type (might be due to invalid IL or missing references)
//IL_008a: Unknown result type (might be due to invalid IL or missing references)
//IL_0092: Unknown result type (might be due to invalid IL or missing references)
//IL_0098: Unknown result type (might be due to invalid IL or missing references)
//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
//IL_010b: Unknown result type (might be due to invalid IL or missing references)
//IL_0113: Unknown result type (might be due to invalid IL or missing references)
//IL_0119: Unknown result type (might be due to invalid IL or missing references)
//IL_0131: Unknown result type (might be due to invalid IL or missing references)
Rect rect = parentRt.rect;
Rect rect2 = target.rect;
Vector2 size = ((Rect)(ref rect2)).size;
Vector2 pivot = target.pivot;
Vector2 anchorMin = target.anchorMin;
Vector2 anchorMax = target.anchorMax;
Vector2 anchoredPosition = target.anchoredPosition;
if (Mathf.Approximately(anchorMin.x, anchorMax.x) && ((Rect)(ref rect)).width > size.x + 8f)
{
float num = anchorMin.x * ((Rect)(ref rect)).width + anchoredPosition.x - pivot.x * size.x;
float num2 = Mathf.Clamp(num, 2f, ((Rect)(ref rect)).width - size.x - 2f);
anchoredPosition.x += num2 - num;
}
if (Mathf.Approximately(anchorMin.y, anchorMax.y) && ((Rect)(ref rect)).height > size.y + 8f)
{
float num3 = anchorMin.y * ((Rect)(ref rect)).height + anchoredPosition.y - pivot.y * size.y;
float num4 = Mathf.Clamp(num3, 2f, ((Rect)(ref rect)).height - size.y - 2f);
anchoredPosition.y += num4 - num3;
}
target.anchoredPosition = anchoredPosition;
}
}
public static class UiFactory
{
public static readonly Color PanelColor = new Color(0.1f, 0.12f, 0.15f, 0.97f);
public static readonly Color TextColor = new Color(0.92f, 0.95f, 0.97f, 1f);
public static readonly Color ButtonColor = new Color(0.17f, 0.24f, 0.3f, 0.98f);
public static readonly Color SliderBackColor = new Color(0.12f, 0.16f, 0.19f, 1f);
public static readonly Color SliderFillColor = new Color(0.35f, 0.75f, 0.95f, 1f);
private static Font cachedFont;
public static Font DefaultFont
{
get
{
if ((Object)(object)cachedFont == (Object)null)
{
cachedFont = FindFont();
}
return cachedFont;
}
set
{
cachedFont = value;
}
}
public static void EnsureFont()
{
cachedFont = FindFont();
}
public static Font FindFont()
{
Text[] array = Resources.FindObjectsOfTypeAll<Text>();
for (int i = 0; i < array.Length; i++)
{
if (!((Object)(object)array[i] == (Object)null) && !((Object)(object)array[i].font == (Object)null))
{
return array[i].font;
}
}
try
{
return Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
}
catch (Exception)
{
return null;
}
}
public static Camera CanvasCamera(Component ui)
{
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)ui == (Object)null)
{
return null;
}
Canvas componentInParent = ui.GetComponentInParent<Canvas>();
if ((Object)(object)componentInParent == (Object)null)
{
return null;
}
if ((int)componentInParent.renderMode != 0)
{
return componentInParent.worldCamera;
}
return null;
}
public static RectTransform NewRect(string name, Transform parent, float w, float h)
{
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_004a: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
//IL_005f: Unknown result type (might be due to invalid IL or missing references)
//IL_0060: Unknown result type (might be due to invalid IL or missing references)
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
//IL_006d: Unknown result type (might be due to invalid IL or missing references)
//IL_0078: Expected O, but got Unknown
RectTransform val = (RectTransform)new GameObject(name, new Type[1] { typeof(RectTransform) }).transform;
((Transform)val).SetParent(parent, false);
Vector2 val2 = default(Vector2);
((Vector2)(ref val2))..ctor(0f, 1f);
val.anchorMax = val2;
val.anchorMin = val2;
val.pivot = new Vector2(0f, 1f);
val.anchoredPosition = Vector2.zero;
val.sizeDelta = new Vector2(w, h);
return val;
}
public static RectTransform Place(RectTransform rt, float x, float y, float w, float h)
{
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_0045: Unknown result type (might be due to invalid IL or missing references)
Vector2 val = default(Vector2);
((Vector2)(ref val))..ctor(0f, 1f);
rt.anchorMax = val;
rt.anchorMin = val;
rt.pivot = new Vector2(0f, 1f);
rt.anchoredPosition = new Vector2(x, y);
rt.sizeDelta = new Vector2(w, h);
return rt;
}
public static void Pos(RectTransform rt, float x, float y, float w, float h)
{
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_0045: Unknown result type (might be due to invalid IL or missing references)
Vector2 val = default(Vector2);
((Vector2)(ref val))..ctor(0f, 1f);
rt.anchorMax = val;
rt.anchorMin = val;
rt.pivot = new Vector2(0f, 1f);
rt.anchoredPosition = new Vector2(x, y);
rt.sizeDelta = new Vector2(w, h);
}
public static Text MakeText(string name, Transform parent, float x, float y, float w, float h, int size, TextAnchor align)
{
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
Text obj = ((Component)Place(NewRect(name, parent, w, h), x, y, w, h)).gameObject.AddComponent<Text>();
obj.font = DefaultFont;
obj.fontSize = size;
((Graphic)obj).color = TextColor;
obj.alignment = align;
((Graphic)obj).raycastTarget = false;
obj.supportRichText = false;
obj.horizontalOverflow = (HorizontalWrapMode)0;
obj.verticalOverflow = (VerticalWrapMode)1;
return obj;
}
public static Text MakeButton(string name, Transform parent, float x, float y, float w, float h, string label, Action onClick)
{
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_009e: Unknown result type (might be due to invalid IL or missing references)
//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
//IL_00be: Unknown result type (might be due to invalid IL or missing references)
//IL_006b: Unknown result type (might be due to invalid IL or missing references)
//IL_0075: Expected O, but got Unknown
RectTransform val = Place(NewRect(name, parent, w, h), x, y, w, h);
Image val2 = ((Component)val).gameObject.AddComponent<Image>();
((Graphic)val2).color = ButtonColor;
((Graphic)val2).raycastTarget = true;
Button val3 = ((Component)val).gameObject.AddComponent<Button>();
((Selectable)val3).targetGraphic = (Graphic)(object)val2;
if (onClick != null)
{
((UnityEvent)val3.onClick).AddListener((UnityAction)delegate
{
onClick();
});
}
Text obj = MakeText(name + "Label", (Transform)(object)val, 0f, 0f, w, h, 14, (TextAnchor)4);
RectTransform rectTransform = ((Graphic)obj).rectTransform;
rectTransform.anchorMin = Vector2.zero;
rectTransform.anchorMax = Vector2.one;
rectTransform.offsetMin = Vector2.zero;
rectTransform.offsetMax = Vector2.zero;
obj.text = label;
return obj;
}
public static Image MakeImage(string name, Transform parent, float x, float y, float w, float h, Color color, Sprite sprite = null)
{
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
Image val = ((Component)Place(NewRect(name, parent, w, h), x, y, w, h)).gameObject.AddComponent<Image>();
((Graphic)val).color = color;
val.sprite = sprite;
((Graphic)val).raycastTarget = false;
if ((Object)(object)sprite != (Object)null)
{
val.preserveAspect = true;
}
return val;
}
public static InputField MakeInputField(string name, Transform parent, float x, float y, float w, float h, string placeholder, int fontSize)
{
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
//IL_010f: Unknown result type (might be due to invalid IL or missing references)
//IL_0116: Unknown result type (might be due to invalid IL or missing references)
RectTransform val = Place(NewRect(name, parent, w, h), x, y, w, h);
Image val2 = ((Component)val).gameObject.AddComponent<Image>();
((Graphic)val2).color = SliderBackColor;
((Graphic)val2).raycastTarget = true;
InputField obj = ((Component)val).gameObject.AddComponent<InputField>();
Text val3 = MakeText(name + "Text", (Transform)(object)val, 6f, 0f, w - 12f, h, fontSize, (TextAnchor)3);
((Graphic)val3).raycastTarget = false;
val3.horizontalOverflow = (HorizontalWrapMode)1;
Text val4 = MakeText(name + "Placeholder", (Transform)(object)val, 6f, 0f, w - 12f, h, fontSize, (TextAnchor)3);
((Graphic)val4).raycastTarget = false;
((Graphic)val4).color = new Color(1f, 1f, 1f, 0.35f);
val4.text = placeholder;
obj.textComponent = val3;
obj.placeholder = (Graphic)(object)val4;
((Selectable)obj).targetGraphic = (Graphic)(object)val2;
obj.contentType = (ContentType)2;
obj.characterLimit = 7;
obj.text = "";
Vector2 val5 = default(Vector2);
((Vector2)(ref val5))..ctor(0f, 1f);
val.anchorMax = val5;
val.anchorMin = val5;
return obj;
}
public static Slider MakeSlider(Transform parent, string name, float x, float y, float w, float h, float min, float max, bool wholeNumbers, float value, Action<float> onChange)
{
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_0062: Unknown result type (might be due to invalid IL or missing references)
//IL_0077: Unknown result type (might be due to invalid IL or missing references)
//IL_0082: Unknown result type (might be due to invalid IL or missing references)
//IL_0097: Unknown result type (might be due to invalid IL or missing references)
//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
//IL_00db: Unknown result type (might be due to invalid IL or missing references)
//IL_0121: Unknown result type (might be due to invalid IL or missing references)
RectTransform val = Place(NewRect(name, parent, w, h), x, y, w, h);
Slider obj = ((Component)val).gameObject.AddComponent<Slider>();
RectTransform obj2 = NewRect("Background", (Transform)(object)val, w, 6f);
obj2.anchorMin = new Vector2(0f, 0.5f);
obj2.anchorMax = new Vector2(1f, 0.5f);
obj2.pivot = new Vector2(0.5f, 0.5f);
obj2.anchoredPosition = Vector2.zero;
obj2.sizeDelta = new Vector2(0f, 6f);
Image obj3 = ((Component)obj2).gameObject.AddComponent<Image>();
((Graphic)obj3).color = SliderBackColor;
((Graphic)obj3).raycastTarget = true;
RectTransform val2 = NewRect("Fill", (Transform)(object)val, 0f, h);
Image obj4 = ((Component)val2).gameObject.AddComponent<Image>();
((Graphic)obj4).color = SliderFillColor;
((Graphic)obj4).raycastTarget = false;
RectTransform val3 = NewRect("Handle", (Transform)(object)val, 14f, h);
Image val4 = ((Component)val3).gameObject.AddComponent<Image>();
((Graphic)val4).color = new Color(0.85f, 0.93f, 1f, 1f);
((Graphic)val4).raycastTarget = true;
obj.fillRect = val2;
obj.handleRect = val3;
((Selectable)obj).targetGraphic = (Graphic)(object)val4;
obj.direction = (Direction)0;
obj.minValue = min;
obj.maxValue = max;
obj.wholeNumbers = wholeNumbers;
obj.value = value;
((UnityEvent<float>)(object)obj.onValueChanged).AddListener((UnityAction<float>)delegate(float v)
{
if (onChange != null)
{
onChange(v);
}
});
return obj;
}
}
public static class UiFocus
{
private static readonly List<RaycastResult> Hits = new List<RaycastResult>(16);
public static void BringToFront(GameObject root)
{
if (!((Object)(object)root == (Object)null))
{
root.transform.SetAsLastSibling();
}
}
public static void Poll(GameObject root)
{
if (!((Object)(object)root == (Object)null) && root.activeInHierarchy && Input.GetMouseButtonDown(0) && TopmostIs(root))
{
root.transform.SetAsLastSibling();
}
}
public static bool TopmostIs(GameObject root)
{
if ((Object)(object)root == (Object)null)
{
return false;
}
Transform val = Topmost();
if ((Object)(object)val != (Object)null)
{
if (!((Object)(object)val == (Object)(object)root.transform))
{
return val.IsChildOf(root.transform);
}
return true;
}
return false;
}
private static Transform Topmost()
{
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Expected O, but got Unknown
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
EventSystem current = EventSystem.current;
if ((Object)(object)current == (Object)null)
{
return null;
}
PointerEventData val = new PointerEventData(current)
{
position = Vector2.op_Implicit(Input.mousePosition)
};
Hits.Clear();
current.RaycastAll(val, Hits);
if (Hits.Count == 0)
{
return null;
}
RaycastResult val2 = Hits[0];
GameObject gameObject = ((RaycastResult)(ref val2)).gameObject;
if (!((Object)(object)gameObject != (Object)null))
{
return null;
}
return gameObject.transform;
}
}
}
namespace DSPEasy.Framework.Save
{
public static class ModSave
{
private sealed class Entry
{
public string Namespace;
public IModSaveChunk Chunk;
}
public const uint Magic = 827346244u;
public const byte Format = 2;
public static Action<string> LogInfo = delegate
{
};
public static Action<string> LogWarn = delegate
{
};
private static readonly object sync = new object();
private static readonly List<Entry> Entries = new List<Entry>();
private static readonly Dictionary<string, Entry> ByKey = new Dictionary<string, Entry>(StringComparer.Ordinal);
public static object Sync => sync;
public static void Register(string ns, IModSaveChunk chunk)
{
if (chunk == null)
{
throw new ArgumentNullException("chunk");
}
if (string.IsNullOrEmpty(ns))
{
throw new ArgumentException("命名空间不能为空,用插件名(例如 DSPEasy.Buildings)", "ns");
}
if (ns.Length > 64 || ns.IndexOf('/') >= 0)
{
throw new ArgumentException("命名空间不合法(不超过 64 字符、不含 '/'):" + ns, "ns");
}
if (chunk.Id == null || chunk.Id.Length != 4)
{
throw new ArgumentException("状态块 ID 必须是 4 个字符:" + chunk.Id, "chunk");
}
for (int i = 0; i < chunk.Id.Length; i++)
{
char c = chunk.Id[i];
if (c < ' ' || c > '~')
{
throw new ArgumentException("状态块 ID 必须是可打印 ASCII:" + chunk.Id, "chunk");
}
}
lock (sync)
{
string key = Key(ns, chunk.Id);
if (ByKey.ContainsKey(key))
{
throw new ArgumentException("状态块重复注册:" + ns + "/" + chunk.Id, "chunk");
}
Entry entry = new Entry
{
Namespace = ns,
Chunk = chunk
};
Entries.Add(entry);
ByKey[key] = entry;
}
}
public static string[] Namespaces()
{
lock (sync)
{
List<string> list = new List<string>();
for (int i = 0; i < Entries.Count; i++)
{
if (!list.Contains(Entries[i].Namespace))
{
list.Add(Entries[i].Namespace);
}
}
return list.ToArray();
}
}
public static bool HasSidecar(string saveName)
{
string text = SidecarPath(SavePathOf(saveName));
if (text != null)
{
return File.Exists(text);
}
return false;
}
public static bool WriteForSaveName(string saveName)
{
return WriteForSaveName(saveName, null);
}
public static bool WriteForSaveName(string saveName, string ns)
{
lock (sync)
{
string text = SavePathOf(saveName);
return text != null && WriteSidecar(text, ns);
}
}
public static bool ReadForSaveName(string saveName)
{
return ReadForSaveName(saveName, null);
}
public static bool ReadForSaveName(string saveName, string ns)
{
lock (sync)
{
ClearAll(ns);
string text = SavePathOf(saveName);
return text != null && ReadSidecar(text, ns);
}
}
public static void ClearAll()
{
ClearAll(null);
}
public static void ClearAll(string ns)
{
lock (sync)
{
for (int i = 0; i < Entries.Count; i++)
{
Entry entry = Entries[i];
if (ns == null || !(entry.Namespace != ns))
{
entry.Chunk.Clear();
}
}
}
}
public static string DescribeCounts()
{
return DescribeCounts(null);
}
public static string DescribeCounts(string ns)
{
lock (sync)
{
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < Entries.Count; i++)
{
Entry entry = Entries[i];
if (ns == null || !(entry.Namespace != ns))
{
if (stringBuilder.Length > 0)
{
stringBuilder.Append(' ');
}
stringBuilder.Append(entry.Namespace).Append('/').Append(entry.Chunk.Id)
.Append('=')
.Append(entry.Chunk.Count);
}
}
return stringBuilder.ToString();
}
}
public static string SidecarPath(string dsvPath)
{
if (string.IsNullOrEmpty(dsvPath))
{
return null;
}
string text = Path.GetDirectoryName(dsvPath);
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(dsvPath);
if (text == null)
{
text = string.Empty;
}
return Path.Combine(text, fileNameWithoutExtension + ".mod.dat");
}
public static void DeleteSidecar(string dsvPath)
{
lock (sync)
{
string text = SidecarPath(dsvPath);
if (text != null)
{
TryDelete(text);
}
}
}
public static string SavePathOf(string saveName)
{
if (string.IsNullOrEmpty(saveName))
{
return null;
}
try
{
return GameSave.SavePath(saveName);
}
catch (Exception ex)
{
LogWarn("[ModSave] 解析存档路径失败:" + saveName + " -> " + ex.Message);
return null;
}
}
private static string Key(string ns, string id)
{
return ns + "/" + id;
}
private static bool WriteSidecar(string dsvPath, string ns)
{
FileInfo fileInfo = new FileInfo(dsvPath);
if (!fileInfo.Exists)
{
LogWarn("[ModSave] 主存档不存在,跳过旁车写入:" + dsvPath);
return false;
}
string text = SidecarPath(dsvPath);
List<Entry> list = new List<Entry>();
for (int i = 0; i < Entries.Count; i++)
{
Entry entry = Entries[i];
if (ns == null || entry.Namespace == ns)
{
list.Add(entry);
}
}
List<SidecarChunk> list2 = new List<SidecarChunk>();
int num = 0;
if (SidecarFile.TryRead(text, out var header, out var chunks, out var error))
{
bool flag = header.Format < 2;
for (int j = 0; j < chunks.Count; j++)
{
SidecarChunk sidecarChunk = chunks[j];
Entry entry2 = (flag ? FindUnique(sidecarChunk.IdText) : Lookup(sidecarChunk.Namespace, sidecarChunk.IdText));
if (entry2 == null || (ns != null && !(entry2.Namespace == ns)) || sidecarChunk.Version > entry2.Chunk.Version)
{
list2.Add(sidecarChunk);
num++;
}
}
if (flag)
{
LogInfo("[ModSave] 旁车是格式 " + header.Format + " 的旧文件,本次写出会升级为格式 " + (byte)2 + "(块头带命名空间)");
}
}
else if (File.Exists(text))
{
LogWarn("[ModSave] 旧旁车无法解析(" + error + "),本次按当前内存状态整体重写:" + text);
}
for (int k = 0; k < list.Count; k++)
{
list2.Add(SerializeEntry(list[k]));
}
SidecarHeader header2 = new SidecarHeader
{
SaveLength = fileInfo.Length,
SaveTicks = fileInfo.LastWriteTimeUtc.Ticks,
GameTick = GameMain.gameTick,
Format = 2
};
if (!SidecarFile.TryWrite(text, header2, list2, out var error2))
{
LogWarn("[ModSave] 写入旁车失败:" + error2);
return false;
}
if (num > 0)
{
LogInfo("[ModSave] 旁车已保留 " + num + " 个不属于本次命名空间的数据块");
}
return true;
}
private static bool ReadSidecar(string dsvPath, string ns)
{
string text = SidecarPath(dsvPath);
if (!SidecarFile.TryRead(text, out var header, out var chunks, out var error))
{
if (File.Exists(text))
{
LogWarn("[ModSave] 读取旁车失败:" + error + "(" + text + ")");
}
return false;
}
long length = new FileInfo(dsvPath).Length;
if (length != header.SaveLength)
{
LogWarn("[ModSave] 旁车与存档不配对(存档 " + length + " 字节,旁车记录 " + header.SaveLength + " 字节),本次按无模组数据加载:" + text);
return false;
}
int num = 0;
for (int i = 0; i < chunks.Count; i++)
{
SidecarChunk sidecarChunk = chunks[i];
Entry entry = Resolve(sidecarChunk, header.Format);
if (entry == null || (ns != null && entry.Namespace != ns))
{
continue;
}
string text2 = entry.Namespace + "/" + sidecarChunk.IdText;
if (sidecarChunk.Version > entry.Chunk.Version)
{
LogWarn("[ModSave] 跳过版本过新的数据块 " + text2 + " v" + sidecarChunk.Version + "(支持到 v" + entry.Chunk.Version + ")");
continue;
}
if (SidecarFile.Fnv1a(sidecarChunk.Payload) != sidecarChunk.Hash)
{
LogWarn("[ModSave] 数据块校验失败,已跳过:" + text2);
continue;
}
try
{
using MemoryStream input = new MemoryStream(sidecarChunk.Payload, writable: false);
using BinaryReader reader = new BinaryReader(input, Encoding.UTF8);
entry.Chunk.Read(reader, sidecarChunk.Version);
num++;
}
catch (Exception ex)
{
LogWarn("[ModSave] 读取数据块失败,已跳过:" + text2 + " -> " + ex.Message);
entry.Chunk.Clear();
}
}
LogInfo("[ModSave] 已读取旁车 " + Path.GetFileName(text) + ":块 " + num + "/" + chunks.Count + ((ns == null) ? "" : ("(命名空间 " + ns + ")")) + ",存档 tick " + header.GameTick + ",写出时间 " + new DateTime(header.SaveTicks, DateTimeKind.Utc));
return num > 0;
}
private static Entry Resolve(SidecarChunk chunk, byte format)
{
if (format >= 2)
{
return Lookup(chunk.Namespace, chunk.IdText);
}
if (chunk.Namespace.Length == 0)
{
return FindUnique(chunk.IdText);
}
return Lookup(chunk.Namespace, chunk.IdText);
}
private static Entry Lookup(string ns, string id)
{
if (!ByKey.TryGetValue(Key(ns, id), out var value))
{
return null;
}
return value;
}
private static Entry FindUnique(string id)
{
Entry result = null;
int num = 0;
for (int i = 0; i < Entries.Count; i++)
{
if (!(Entries[i].Chunk.Id != id))
{
result = Entries[i];
num++;
}
}
if (num == 1)
{
return result;
}
if (num > 1)
{
LogWarn("[ModSave] 旧格式旁车里的块 " + id + " 同时被 " + num + " 个命名空间注册,无法判断归属,已跳过");
}
return null;
}
private static SidecarChunk SerializeEntry(Entry entry)
{
byte[] array = Serialize(entry.Chunk);
return new SidecarChunk
{
Namespace = entry.Namespace,
Id = SidecarFile.PackId(entry.Chunk.Id),
Version = entry.Chunk.Version,
Payload = array,
Hash = SidecarFile.Fnv1a(array)
};
}
private static byte[] Serialize(IModSaveChunk chunk)
{
using MemoryStream memoryStream = new MemoryStream();
using (BinaryWriter writer = new BinaryWriter(memoryStream, Encoding.UTF8, leaveOpen: true))
{
chunk.Write(writer);
}
return memoryStream.ToArray();
}
private static void TryDelete(string path)
{
try
{
if (File.Exists(path))
{
File.Delete(path);
}
}
catch (Exception)
{
}
}
}
public interface IModSaveChunk
{
string Id { get; }
int Version { get; }
int Count { get; }
void Write(BinaryWriter writer);
void Read(BinaryReader reader, int version);
void Clear();
}
public abstract class EntityStateChunk<T> : IModSaveChunk
{
public readonly Dictionary<long, T> Map;
private const int MaxRecords = 1000000;
public abstract string Id { get; }
public abstract int Version { get; }
public int Count => Map.Count;
protected EntityStateChunk(Dictionary<long, T> map)
{
if (map == null)
{
throw new ArgumentNullException("map");
}
Map = map;
}
protected abstract void WriteValue(BinaryWriter writer, T value);
protected abstract T ReadValue(BinaryReader reader, int version);
public void Write(BinaryWriter writer)
{
writer.Write(Map.Count);
foreach (KeyValuePair<long, T> item in Map)
{
writer.Write(item.Key);
WriteValue(writer, item.Value);
}
}
public void Read(BinaryReader reader, int version)
{
Map.Clear();
int num = reader.ReadInt32();
if (num < 0 || num > 1000000)
{
throw new InvalidDataException("状态块 " + Id + " 记录数非法:" + num);
}
for (int i = 0; i < num; i++)
{
long key = reader.ReadInt64();
Map[key] = ReadValue(reader, version);
}
}
public void Clear()
{
Map.Clear();
}
}
public struct SidecarHeader
{
public long SaveLength;
public long SaveTicks;
public long GameTick;
public byte Format;
}
public sealed class SidecarChunk
{
public string Namespace = "";
public uint Id;
public int Version;
public byte[] Payload;
public uint Hash;
public string IdText => SidecarFile.UnpackId(Id);
}
public static class SidecarFile
{
public const uint Magic = 827346244u;
public const byte Format = 2;
public const byte MinFormat = 1;
public const int MaxChunkBytes = 8388608;
public const int MaxChunks = 4096;
private const int HeaderOverhead = 32;
private static readonly object FileLock = new object();
public static bool TryRead(string path, out SidecarHeader header, out List<SidecarChunk> chunks, out string error)
{
bool num = ReadCore(path, out header, out chunks, out error);
if (!num)
{
chunks = null;
}
return num;
}
private static bool ReadCore(string path, out SidecarHeader header, out List<SidecarChunk> chunks, out string error)
{
header = default(SidecarHeader);
chunks = new List<SidecarChunk>();
error = null;
if (string.IsNullOrEmpty(path))
{
error = "路径为空";
return false;
}
lock (FileLock)
{
if (!File.Exists(path))
{
error = "文件不存在";
return false;
}
try
{
using FileStream input = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
using BinaryReader binaryReader = new BinaryReader(input, Encoding.UTF8);
if (binaryReader.ReadUInt32() != 827346244)
{
error = "magic 不匹配";
return false;
}
byte b = binaryReader.ReadByte();
if (b < 1 || b > 2)
{
error = "格式版本不支持:" + b;
return false;
}
binaryReader.ReadByte();
int num = binaryReader.ReadUInt16();
header.Format = b;
header.SaveLength = binaryReader.ReadInt64();
header.SaveTicks = binaryReader.ReadInt64();
header.GameTick = binaryReader.ReadInt64();
if (num > 4096)
{
error = "块数量非法:" + num;
return false;
}
for (int i = 0; i < num; i++)
{
string text = ((b >= 2) ? binaryReader.ReadString() : "");
uint id = binaryReader.ReadUInt32();
int version = binaryReader.ReadInt32();
int num2 = binaryReader.ReadInt32();
if (num2 < 0 || num2 > 8388608)
{
error = "数据块长度非法:" + num2;
return false;
}
byte[] array = binaryReader.ReadBytes(num2);
if (array.Length != num2)
{
error = "数据块被截断";
return false;
}
uint hash = binaryReader.ReadUInt32();
chunks.Add(new SidecarChunk
{
Namespace = (text ?? ""),
Id = id,
Version = version,
Payload = array,
Hash = hash
});
}
}
catch (Exception ex)
{
error = ex.Message;
return false;
}
}
return true;
}
public static bool TryWrite(string path, SidecarHeader header, IList<SidecarChunk> chunks, out string error)
{
error = null;
if (string.IsNullOrEmpty(path))
{
error = "路径为空";
return false;
}
if (chunks == null)
{
error = "块列表为空";
return false;
}
if (chunks.Count > 4096)
{
error = "块数量超过上限:" + chunks.Count;
return false;
}
string text = path + ".tmp";
lock (FileLock)
{
try
{
using (FileStream fileStream = new FileStream(text, FileMode.Create, FileAccess.Write, FileShare.None))
{
using BinaryWriter binaryWriter = new BinaryWriter(fileStream, Encoding.UTF8);
binaryWriter.Write(827346244u);
binaryWriter.Write((byte)2);
binaryWriter.Write((byte)0);
binaryWriter.Write((ushort)chunks.Count);
binaryWriter.Write(header.SaveLength);
binaryWriter.Write(header.SaveTicks);
binaryWriter.Write(header.GameTick);
for (int i = 0; i < chunks.Count; i++)
{
SidecarChunk sidecarChunk = chunks[i];
binaryWriter.Write(sidecarChunk.Namespace ?? "");
binaryWriter.Write(sidecarChunk.Id);
binaryWriter.Write(sidecarChunk.Version);
binaryWriter.Write(sidecarChunk.Payload.Length);
binaryWriter.Write(sidecarChunk.Payload);
binaryWriter.Write(sidecarChunk.Hash);
}
binaryWriter.Flush();
fileStream.Flush(flushToDisk: true);
}
Replace(text, path);
return true;
}
catch (Exception ex)
{
error = ex.Message;
TryDelete(text);
return false;
}
}
}
public static int EstimateSize(int chunkCount, int payloadBytes)
{
return 32 + chunkCount * 16 + payloadBytes;
}
public static uint Fnv1a(byte[] data)
{
uint num = 2166136261u;
for (int i = 0; i < data.Length; i++)
{
num ^= data[i];
num *= 16777619;
}
return num;
}
public static uint PackId(string id)
{
return id[0] | ((uint)id[1] << 8) | ((uint)id[2] << 16) | ((uint)id[3] << 24);
}
public static string UnpackId(uint id)
{
return new string(new char[4]
{
(char)(id & 0xFF),
(char)((id >> 8) & 0xFF),
(char)((id >> 16) & 0xFF),
(char)((id >> 24) & 0xFF)
});
}
private static void Replace(string tmp, string path)
{
if (File.Exists(path))
{
try
{
File.Replace(tmp, path, null);
return;
}
catch (Exception)
{
File.Delete(path);
}
}
File.Move(tmp, path);
}
private static void TryDelete(string path)
{
try
{
if (File.Exists(path))
{
File.Delete(path);
}
}
catch (Exception)
{
}
}
}
}
namespace DSPEasy.Framework.Recipes
{
public interface IRecipePolicy
{
int Signature { get; }
bool IsRaw(int itemId);
int Choose(int itemId, List<RecipeProto> producers);
}
public sealed class RecipePlan
{
public int Target;
public int OutPer = 1;
public readonly Dictionary<int, int> Crafts = new Dictionary<int, int>();
public readonly Dictionary<int, int> External = new Dictionary<int, int>();
public readonly Dictionary<int, int> Byprod = new Dictionary<int, int>();
public readonly Dictionary<int, int> Consumed = new Dictionary<int, int>();
public readonly Dictionary<int, int> Produced = new Dictionary<int, int>();
public readonly HashSet<int> Crafted = new HashSet<int>();
public int Ticks;
public int[] Overflow = new int[0];
public int Kinds => External.Count + Byprod.Count + 1;
}
public sealed class RecipeSolver
{
public int MaxKinds = 12;
public int SpeedScale = 1;
public int MaxRounds = 24;
public int DepthLimit = 64;
private readonly Dictionary<int, List<RecipeProto>> producerCache = new Dictionary<int, List<RecipeProto>>();
private readonly Dictionary<long, RecipePlan> planCache = new Dictionary<long, RecipePlan>();
private readonly List<RecipeProto> empty = new List<RecipeProto>();
public List<RecipeProto> Producers(int itemId)
{
if (itemId <= 0)
{
return empty;
}
if (producerCache.TryGetValue(itemId, out var value))
{
return value;
}
List<RecipeProto> list = new List<RecipeProto>(4);
RecipeProto[] dataArray = ((ProtoSet<RecipeProto>)(object)LDB.recipes).dataArray;
for (int i = 0; i < 2; i++)
{
foreach (RecipeProto val in dataArray)
{
if (val == null || ((Proto)val).ID <= 0 || val.Results == null || val.ResultCounts == null)
{
continue;
}
bool flag = false;
bool flag2 = false;
for (int k = 0; k < val.Results.Length; k++)
{
if (val.Results[k] == itemId && val.ResultCounts[k] > 0)
{
flag2 = true;
if (k == 0)
{
flag = true;
}
}
}
if (flag2 && flag == (i == 0) && !list.Contains(val))
{
list.Add(val);
}
}
}
producerCache[itemId] = list;
return list;
}
public RecipeProto Choose(int itemId, IRecipePolicy policy, out int outIdx, out int per)
{
outIdx = -1;
per = 0;
List<RecipeProto> list = Producers(itemId);
if (list.Count == 0)
{
return null;
}
int num = policy?.Choose(itemId, list) ?? 0;
if (num < 0 || num >= list.Count)
{
num = 0;
}
RecipeProto val = list[num];
for (int i = 0; i < val.Results.Length; i++)
{
if (val.Results[i] == itemId && val.ResultCounts[i] > 0)
{
outIdx = i;
per = val.ResultCounts[i];
break;
}
}
if (outIdx >= 0)
{
return val;
}
return null;
}
public RecipePlan Plan(int target, IRecipePolicy policy)
{
if (target <= 0)
{
return null;
}
int num = policy?.Signature ?? 0;
long key = ((long)target << 40) ^ ((long)MaxKinds << 32) ^ (uint)num;
if (planCache.TryGetValue(key, out var value))
{
return value;
}
RecipePlan recipePlan = Build(target, policy);
if (recipePlan == null)
{
return null;
}
if (planCache.Count > 128)
{
planCache.Clear();
}
planCache[key] = recipePlan;
return recipePlan;
}
public void ClearCache()
{
planCache.Clear();
}
private RecipePlan Build(int target, IRecipePolicy policy)
{
if (Choose(target, policy, out var _, out var per) == null)
{
return null;
}
per = Math.Max(1, per);
HashSet<int> hashSet = new HashSet<int>();
RecipePlan recipePlan = new RecipePlan
{
Target = target,
OutPer = per
};
Expand(target, policy, hashSet, recipePlan);
for (int i = 0; i < MaxRounds; i++)
{
if (recipePlan.Kinds <= MaxKinds)
{
break;
}
int kinds = recipePlan.Kinds;
int num = 0;
int num2 = int.MaxValue;
foreach (int item in recipePlan.Crafted)
{
if (item == target)
{
continue;
}
hashSet.Add(item);
RecipePlan recipePlan2 = NewPlan(target, policy, hashSet);
hashSet.Remove(item);
if (recipePlan2 != null)
{
int kinds2 = recipePlan2.Kinds;
if (kinds2 < num2)
{
num2 = kinds2;
num = item;
}
}
}
if (num == 0 || num2 >= kinds)
{
break;
}
hashSet.Add(num);
recipePlan = NewPlan(target, policy, hashSet);
if (recipePlan == null)
{
return null;
}
}
if (recipePlan.Kinds > MaxKinds)
{
return null;
}
recipePlan.OutPer = ((recipePlan.Produced.TryGetValue(target, out var value) && value > 0) ? value : per);
List<int> list = new List<int>();
foreach (int item2 in hashSet)
{
if (recipePlan.External.ContainsKey(item2))
{
list.Add(item2);
}
}
recipePlan.Overflow = list.ToArray();
return recipePlan;
}
private RecipePlan NewPlan(int target, IRecipePolicy policy, HashSet<int> forced)
{
RecipePlan recipePlan = new RecipePlan
{
Target = target
};
Expand(target, policy, forced, recipePlan);
recipePlan.Target = target;
return recipePlan;
}
private void Expand(int target, IRecipePolicy policy, HashSet<int> forced, RecipePlan plan)
{
Choose(target, policy, out var _, out var per);
plan.OutPer = Math.Max(1, (per <= 0) ? 1 : per);
HashSet<int> stack = new HashSet<int>();
Demand(target, plan.OutPer, policy, forced, stack, plan, 0);
HashSet<int> hashSet = new HashSet<int>();
foreach (int key in plan.External.Keys)
{
hashSet.Add(key);
}
foreach (int key2 in plan.Byprod.Keys)
{
hashSet.Add(key2);
}
foreach (int item in hashSet)
{
plan.External.TryGetValue(item, out var value);
plan.Byprod.TryGetValue(item, out var value2);
int num = value - value2;
if (num > 0)
{
plan.External[item] = num;
plan.Byprod.Remove(item);
}
else if (num < 0)
{
plan.Byprod[item] = -num;
plan.External.Remove(item);
}
else
{
plan.External.Remove(item);
plan.Byprod.Remove(item);
}
}
plan.External.Remove(target);
plan.Byprod.Remove(target);
}
private void Demand(int item, int amount, IRecipePolicy policy, HashSet<int> forced, HashSet<int> stack, RecipePlan plan, int depth)
{
if (item <= 0 || amount <= 0)
{
return;
}
if (depth > DepthLimit)
{
AddTo(plan.External, item, amount);
return;
}
bool num = !forced.Contains(item) && (policy == null || !policy.IsRaw(item));
RecipeProto val = null;
int outIdx = -1;
int per = 1;
if (num)
{
val = Choose(item, policy, out outIdx, out per);
}
if (val == null || stack.Contains(item))
{
AddTo(plan.External, item, amount);
return;
}
per = Math.Max(1, per);
int num2 = (amount + per - 1) / per;
plan.Crafts.TryGetValue(((Proto)val).ID, out var value);
plan.Crafts[((Proto)val).ID] = value + num2;
plan.Crafted.Add(item);
plan.Ticks += Math.Max(1, val.TimeSpend) * num2;
stack.Add(item);
for (int i = 0; i < val.Items.Length; i++)
{
int num3 = ((i < val.ItemCounts.Length) ? val.ItemCounts[i] : 0) * num2;
if (num3 > 0)
{
Demand(val.Items[i], num3, policy, forced, stack, plan, depth + 1);
}
}
stack.Remove(item);
for (int j = 0; j < val.Results.Length; j++)
{
if (j != outIdx)
{
int num4 = Math.Max(0, (j < val.ResultCounts.Length) ? val.ResultCounts[j] : 0) * num2;
if (num4 > 0)
{
AddTo(plan.Byprod, val.Results[j], num4);
}
}
}
AddTo(plan.Consumed, item, amount);
AddTo(plan.Produced, item, num2 * per);
}
private static void AddTo(Dictionary<int, int> dict, int key, int value)
{
if (key > 0 && value > 0)
{
dict.TryGetValue(key, out var value2);
dict[key] = value2 + value;
}
}
}
}
namespace DSPEasy.Framework.Items
{
public static class ItemStack
{
public static int SplitInc(ref int n, ref int m, int p)
{
if (n == 0)
{
return 0;
}
int num = m / n;
int num2 = m - num * n;
n -= p;
num2 -= n;
int num3 = ((num2 > 0) ? (num * p + num2) : (num * p));
m -= num3;
return num3;
}
public static void TakeFromHand(Player player, int count)
{
if (player != null && count > 0 && player.inhandItemCount > 0)
{
int n = player.inhandItemCount;
int m = player.inhandItemInc;
int num = SplitInc(ref n, ref m, count);
player.AddHandItemCount_Unsafe(-count);
player.SetHandItemInc_Unsafe(player.inhandItemInc - num);
if (player.inhandItemCount <= 0)
{
player.SetHandItemId_Unsafe(0);
player.SetHandItemCount_Unsafe(0);
player.SetHandItemInc_Unsafe(0);
}
}
}
}
}
namespace DSPEasy.Framework.Diagnostics
{
public static class ModProfiler
{
private const string HarmonyId = "DSPEasy.Framework.ModProfiler";
private static readonly List<ProfilerNode> Modules;
private static readonly Dictionary<string, ProfilerNode> ModuleIndex;
private static readonly bool useGameCounter;
private static readonly long frequency;
private static bool installed;
private static int lastFrame;
public static bool Installed => installed;
public static bool Active => PerformanceMonitor.CpuProfilerOn;
static ModProfiler()
{
Modules = new List<ProfilerNode>();
ModuleIndex = new Dictionary<string, ProfilerNode>(StringComparer.Ordinal);
try
{
long num = PerformanceCounter.Frequency;
if (num > 0)
{
frequency = num;
useGameCounter = true;
}
}
catch (Exception)
{
}
if (frequency <= 0)
{
frequency = Stopwatch.Frequency;
useGameCounter = false;
}
}
internal static long NowTicks()
{
if (!useGameCounter)
{
return Stopwatch.GetTimestamp();
}
return PerformanceCounter.Counter;
}
public static ProfilerNode RegisterModule(string name)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException("模组统计名不能为空", "name");
}
if (ModuleIndex.TryGetValue(name, out var value))
{
return value;
}
ProfilerNode profilerNode = new ProfilerNode(name, 0, null);
Modules.Add(profilerNode);
ModuleIndex[name] = profilerNode;
return profilerNode;
}
public static bool Install(Action<string> info = null, Action<string> warn = null)
{
//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
if (installed)
{
return true;
}
try
{
MethodInfo methodInfo = AccessTools.Method(typeof(UIPerformancePanel), "RefreshCpuStatTexts", (Type[])null, (Type[])null);
if (methodInfo == null)
{
warn?.Invoke("[EasyFramework] 性能面板统计:找不到 UIPerformancePanel.RefreshCpuStatTexts,已跳过");
return false;
}
Patches patchInfo = Harmony.GetPatchInfo((MethodBase)methodInfo);
if (patchInfo != null && patchInfo.Postfixes != null)
{
for (int i = 0; i < patchInfo.Postfixes.Count; i++)
{
if (patchInfo.Postfixes[i].owner == "DSPEasy.Framework.ModProfiler")
{
installed = true;
info?.Invoke("[EasyFramework] 性能面板统计补丁已存在(DSPEasy.Framework.ModProfiler)");
return true;
}
}
}
new Harmony("DSPEasy.Framework.ModProfiler").PatchAll(typeof(ModProfilerPatches));
installed = true;
info?.Invoke("[EasyFramework] 性能面板统计补丁已安装(DSPEasy.Framework.ModProfiler)");
return true;
}
catch (Exception ex)
{
warn?.Invoke("[EasyFramework] 性能面板统计补丁安装失败:" + ex);
return false;
}
}
internal static void OnRefresh(UIPerformancePanel panel)
{
if (!installed)
{
return;
}
if (!Active || PerformanceMonitor.aveFrame <= -7)
{
ResetAll();
return;
}
int frameCount = Time.frameCount;
int num = frameCount - lastFrame;
if (num <= 0)
{
return;
}
lastFrame = frameCount;
if (frequency > 0)
{
double num2 = (double)num / 30.0;
if (num2 > 1.0)
{
num2 = 1.0;
}
for (int i = 0; i < Modules.Count; i++)
{
Modules[i].ApplyFrame(num2, num, frequency);
}
AppendToPanel(panel);
}
}
internal static void ResetAll()
{
for (int i = 0; i < Modules.Count; i++)
{
Modules[i].Reset();
}
lastFrame = Time.frameCount;
}
private static void AppendToPanel(UIPerformancePanel panel)
{
//IL_0145: Unknown result type (might be due to invalid IL or missing references)
//IL_015b: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)panel == (Object)null)
{
return;
}
Text cpuLabelText = panel.cpuLabelText;
Text cpuValueText = panel.cpuValueText1;
Text cpuValueText2 = panel.cpuValueText2;
RectTransform cpuContentTrans = panel.cpuContentTrans;
if ((Object)(object)cpuLabelText == (Object)null || (Object)(object)cpuValueText == (Object)null || (Object)(object)cpuValueText2 == (Object)null || (Object)(object)cpuContentTrans == (Object)null)
{
return;
}
double total = (PerformanceMonitor.showCompleteEngineCost ? PerformanceMonitor.totalTimeCostsShowingComplete : PerformanceMonitor.totalTimeCostsShowing);
StringBuilder stringBuilder = new StringBuilder(512);
StringBuilder stringBuilder2 = new StringBuilder(256);
StringBuilder stringBuilder3 = new StringBuilder(256);
bool flag = false;
for (int i = 0; i < Modules.Count; i++)
{
ProfilerNode profilerNode = Modules[i];
if (profilerNode.IsVisible)
{
flag = true;
stringBuilder.AppendLine();
stringBuilder2.AppendLine();
stringBuilder3.AppendLine();
AppendRow(profilerNode, stringBuilder, stringBuilder2, stringBuilder3, total);
}
}
if (flag)
{
cpuLabelText.text += stringBuilder;
cpuValueText.text += stringBuilder2;
cpuValueText2.text += stringBuilder3;
cpuContentTrans.sizeDelta = new Vector2(cpuContentTrans.sizeDelta.x, cpuLabelText.preferredHeight - 10f);
}
}
private static void AppendRow(ProfilerNode node, StringBuilder b0, StringBuilder b1, StringBuilder b2, double total)
{
string value = Localization.Translate(node.Name);
if (node.Level == 1)
{
b0.Append(" · ").Append(value).AppendLine();
}
else if (node.Level >= 2)
{
b0.Append(" - ").Append(value).AppendLine();
}
else
{
b0.AppendLine(value);
}
double num = node.DisplaySeconds();
if (num >= 2.5E-07)
{
b1.AppendFormat("{0:0.000} ms\r\n", num * 1000.0);
}
else
{
b1.AppendLine("-");
}
if (total < 1E-08)
{
b2.AppendLine();
}
else
{
b2.AppendFormat("{0:0.00} %\r\n", num / total * 100.0);
}
IReadOnlyList<ProfilerNode> children = node.Children;
for (int i = 0; i < children.Count; i++)
{
if (children[i].IsVisible)
{
AppendRow(children[i], b0, b1, b2, total);
}
}
}
}
[HarmonyPatch(typeof(UIPerformancePanel), "RefreshCpuStatTexts")]
internal static class ModProfilerPatches
{
private static void Postfix(UIPerformancePanel __instance)
{
ModProfiler.OnRefresh(__instance);
}
}
public sealed class ProfilerNode
{
private readonly Dictionary<string, ProfilerNode> childIndex = new Dictionary<string, ProfilerNode>(StringComparer.Ordinal);
private readonly List<ProfilerNode> childList = new List<ProfilerNode>();
internal long PendingTicks;
private bool hasSample;
public string Name { get; }
public int Level { get; }
public ProfilerNode Parent { get; }
public IReadOnlyList<ProfilerNode> Children => childList;
public bool HasSelfTiming { get; private set; }
public double ShowingSeconds { get; private set; }
internal bool IsVisible
{
get
{
if (HasSelfTiming)
{
return true;
}
for (int i = 0; i < childList.Count; i++)
{
if (childList[i].IsVisible)
{
return true;
}
}
return false;
}
}
internal ProfilerNode(string name, int level, ProfilerNode parent)
{
Name = name;
Level = level;
Parent = parent;
}
public ProfilerNode GetOrAddChild(string name)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException("统计节点名不能为空", "name");
}
if (childIndex.TryGetValue(name, out var value))
{
return value;
}
int num = Level + 1;
if (num > 2)
{
num = 2;
}
ProfilerNode profilerNode = new ProfilerNode(name, num, this);
childList.Add(profilerNode);
childIndex[name] = profilerNode;
return profilerNode;
}
public TimingScope Measure()
{
if (!ModProfiler.Active)
{
return default(TimingScope);
}
return new TimingScope(this, ModProfiler.NowTicks());
}
internal void AddTicks(long ticks)
{
HasSelfTiming = true;
if (ticks > 0)
{
PendingTicks += ticks;
}
}
internal void ApplyFrame(double alpha, int frames, long frequency)
{
if (HasSelfTiming)
{
double num = (double)PendingTicks / (double)frames / (double)frequency;
if (!hasSample)
{
ShowingSeconds = num;
hasSample = true;
}
else
{
ShowingSeconds += (num - ShowingSeconds) * alpha;
}
PendingTicks = 0L;
}
for (int i = 0; i < childList.Count; i++)
{
childList[i].ApplyFrame(alpha, frames, frequency);
}
}
internal void Reset()
{
PendingTicks = 0L;
ShowingSeconds = 0.0;
HasSelfTiming = false;
hasSample = false;
for (int i = 0; i < childList.Count; i++)
{
childList[i].Reset();
}
}
internal double DisplaySeconds()
{
if (HasSelfTiming)
{
return ShowingSeconds;
}
double num = 0.0;
for (int i = 0; i < childList.Count; i++)
{
ProfilerNode profilerNode = childList[i];
if (profilerNode.IsVisible)
{
num += profilerNode.DisplaySeconds();
}
}
return num;
}
}
public readonly struct TimingScope : IDisposable
{
private readonly ProfilerNode node;
private readonly long startTicks;
internal TimingScope(ProfilerNode node, long startTicks)
{
this.node = node;
this.startTicks = startTicks;
}
public void Dispose()
{
if (node != null)
{
node.AddTicks(ModProfiler.NowTicks() - startTicks);
}
}
}
}
namespace DSPEasy.Framework.Blueprint
{
public interface IBlueprintProvider
{
int ProtoId { get; }
int Version { get; }
void Write(PlanetFactory factory, int entityId, BinaryWriter writer);
void Read(PlanetFactory factory, int entityId, BinaryReader reader, int version);
}
public static class BlueprintChannel
{
public const string ContentPrefix = "DSPBP1:";
private const uint PayloadMagic = 827343428u;
private static readonly Dictionary<int, IBlueprintProvider> Providers = new Dictionary<int, IBlueprintProvider>();
public static Action<string> LogWarn = delegate
{
};
public static void Register(IBlueprintProvider provider)
{
if (provider == null)
{
throw new ArgumentNullException("provider");
}
if (Providers.ContainsKey(provider.ProtoId))
{
throw new ArgumentException("蓝图读写器重复注册:" + provider.ProtoId);
}
Providers.Add(provider.ProtoId, provider);
}
public static IBlueprintProvider Find(int protoId)
{
if (!Providers.TryGetValue(protoId, out var value))
{
return null;
}
return value;
}
public static string Capture(PlanetFactory factory, int entityId)
{
if (factory == null || factory.entityPool == null)
{
return null;
}
if (entityId <= 0 || entityId >= factory.entityPool.Length)
{
return null;
}
int protoId = factory.entityPool[entityId].protoId;
IBlueprintProvider blueprintProvider = Find(protoId);
if (blueprintProvider == null)
{
return null;
}
using MemoryStream memoryStream = new MemoryStream();
using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8, leaveOpen: true))
{
binaryWriter.Write(827343428u);
binaryWriter.Write(protoId);
binaryWriter.Write(blueprintProvider.Version);
blueprintProvider.Write(factory, entityId, binaryWriter);
}
return "DSPBP1:" + Convert.ToBase64String(memoryStream.ToArray());
}
public static bool Apply(PlanetFactory factory, int entityId, string content)
{
if (string.IsNullOrEmpty(content) || !content.StartsWith("DSPBP1:", StringComparison.Ordinal))
{
return false;
}
if (factory == null || factory.entityPool == null)
{
return false;
}
if (entityId <= 0 || entityId >= factory.entityPool.Length)
{
return false;
}
int protoId = factory.entityPool[entityId].protoId;
byte[] buffer;
try
{
buffer = Convert.FromBase64String(content.Substring("DSPBP1:".Length));
}
catch (FormatException)
{
LogWarn("[Blueprint] content 不是合法的 Base64,已忽略:" + content);
return false;
}
try
{
using MemoryStream input = new MemoryStream(buffer, writable: false);
using BinaryReader binaryReader = new BinaryReader(input, Encoding.UTF8);
if (binaryReader.ReadUInt32() != 827343428)
{
return false;
}
int num = binaryReader.ReadInt32();
int num2 = binaryReader.ReadInt32();
if (num != protoId)
{
return false;
}
IBlueprintProvider blueprintProvider = Find(num);
if (blueprintProvider == null)
{
return false;
}
if (num2 > blueprintProvider.Version)
{
LogWarn("[Blueprint] 蓝图数据版本过新,已忽略:proto=" + num + " v" + num2 + "(支持到 v" + blueprintProvider.Version + ")");
return false;
}
blueprintProvider.Read(factory, entityId, binaryReader, num2);
return true;
}
catch (Exception ex2)
{
LogWarn("[Blueprint] 读取蓝图数据失败:proto=" + protoId + " -> " + ex2.Message);
return false;
}
}
}
internal static class BlueprintPatches
{
[HarmonyPatch(typeof(BlueprintUtils), "GenerateBlueprintData")]
private static class Patch_CaptureBlueprintContent
{
private static void Postfix(BlueprintData _blueprintData, PlanetData _planet, int[] _objIds, int _objCount)
{
if (_blueprintData == null || _planet == null || _objIds == null)
{
return;
}
PlanetFactory factory = _planet.factory;
if (factory == null)
{
return;
}
int num = Math.Min(_objCount, Math.Min(_objIds.Length, _blueprintData.buildings.Length));
for (int i = 0; i < num; i++)
{
int num2 = _objIds[i];
if (num2 <= 0)
{
continue;
}
try
{
string text = BlueprintChannel.Capture(factory, num2);
if (text != null)
{
_blueprintData.buildings[i].content = text;
}
}
catch (Exception ex)
{
BlueprintChannel.LogWarn("[Blueprint] 抓取建筑设置失败:entity=" + num2 + " -> " + ex.Message);
}
}
}
}
[HarmonyPatch(typeof(BuildingParameters), "ApplyPrebuildParametersToEntity")]
private static class Patch_ApplyBlueprintContent
{
private static void Postfix(int entityId, string content, PlanetFactory factory)
{
BlueprintChannel.Apply(factory, entityId, content);
}
}
}
}
namespace DSPEasy.Framework.Abnormal
{
public static class AbnormalityGuard
{
public static Action<string> LogInfo = delegate
{
};
public static Action<string> LogWarn = delegate
{
};
internal static void Info(string msg)
{
try
{
LogInfo?.Invoke(msg);
}
catch
{
}
}
internal static void Warn(string msg)
{
try
{
LogWarn?.Invoke(msg);
}
catch
{
}
}
public static void Bind(Action<string> logInfo, Action<string> logWarn)
{
if (logInfo != null)
{
LogInfo = logInfo;
}
if (logWarn != null)
{
LogWarn = logWarn;
}
}
}
[HarmonyPatch(typeof(GameAbnormalityData_0925), "TriggerAbnormality")]
internal static class Patch_BlockAbnormality
{
private static readonly HashSet<int> Logged = new HashSet<int>();
private static bool Prefix(int protoId, long[] vals)
{
if (!Logged.Add(protoId))
{
return false;
}
string arg = "?";
try
{
AbnormalityProto val = (((Object)(object)LDB.abnormalities != (Object)null) ? ((ProtoSet<AbnormalityProto>)(object)LDB.abnormalities).Select(protoId) : null);
if (val != null)
{
arg = ((Proto)val).Name;
}
}
catch
{
}
string arg2 = "";
try
{
if (vals != null)
{
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < vals.Length; i++)
{
if (i > 0)
{
stringBuilder.Append(',');
}
stringBuilder.Append(vals[i]);
}
arg2 = stringBuilder.ToString();
}
}
catch
{
}
AbnormalityGuard.Info($"[Framework] 已拦截数据异常 protoId={protoId}({arg}) evidences=[{arg2}]");
return false;
}
}
[HarmonyPatch(typeof(GameAbnormalityData_0925), "Import")]
internal static class Patch_ClearAbnormalityOnImport
{
private static void Postfix(GameAbnormalityData_0925 __instance)
{
try
{
if (__instance != null && __instance.runtimeDatas != null)
{
__instance.ClearAbnormality(0);
}
}
catch (Exception ex)
{
AbnormalityGuard.Warn("[Framework] 清理存档异常记录失败:" + ex.Message);
}
}
}
[HarmonyPatch(typeof(GameAbnormalityData_0925), "Export")]
internal static class Patch_ClearAbnormalityOnExport
{
private static void Prefix(GameAbnormalityData_0925 __instance)
{
try
{
if (__instance != null && __instance.runtimeDatas != null)
{
__instance.ClearAbnormality(0);
}
}
catch (Exception ex)
{
AbnormalityGuard.Warn("[Framework] 写存档前清理异常记录失败:" + ex.Message);
}
}
}
}