Слияние кода завершено, страница обновится автоматически
/*
* Neutron3529's Unity Game Plugin
* Copyright (C) 2022-2023 Neutron3529
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* This program is a part of other mod, which could not be used directly
* all of the bash script would append this file after the main .cs file
* if you want to compile program manually, you should append it by yourself.
*/
#if NO_LINQ_COLLECTIONS || NO_COLLECTIONS_LINQ
#define NO_LINQ
#define NO_COLLECTIONS
#endif
using System;
#if !NO_LINQ
using System.Linq;
#endif
using System.Reflection;
using System.Collections.Generic;
using HarmonyLib;
#if DOORSTOP
using utils;
using BasePlugin = UnityEngine.MonoBehaviour;
#elif IL2CPP
using BepInEx.Unity.IL2CPP;
#else
using BasePlugin = BepInEx.BaseUnityPlugin;
#endif
#if !DOORSTOP
using BepInEx;
using BepInEx.Configuration;
#endif
namespace Neutron3529.Utils {
#if NO_LINQ
public static class Linq {
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> item, Func<TSource,bool> fn) { foreach(var it in item) { if(fn(it)){yield return it;} } }
public static IEnumerable<Target> Select<TSource,Target>(this IEnumerable<TSource> item, Func<TSource,Target> fn) { foreach(var it in item) { yield return fn(it); } }
public static IEnumerable<TSource> Concat<TSource>(this IEnumerable<TSource> item, IEnumerable<TSource> item2) { foreach(var it in item) { yield return it; } foreach(var it in item2) { yield return it; } }
public static IEnumerable<TSource> Prepend<TSource>(this IEnumerable<TSource> item, TSource item2) { yield return item2; foreach(var it in item) { yield return it; } }
public static TSource[] ToArray<TSource>(this IEnumerable<TSource> item) => new List<TSource>(item).ToArray();
#if NO_COLLECTIONS
public static IList<TSource> ToHashSet<TSource>(this IEnumerable<TSource> item) => new List<TSource>(item).ToArray();
#else
public static HashSet<TSource> ToHashSet<TSource>(this IEnumerable<TSource> item) => new HashSet<TSource>(item);
#endif
}
#endif
#if DOORSTOP
public class ConfigEntry<T> {
public T Value {get;set;}
}
#endif
public enum Enable {
lt,
le,
ne,
ge,
gt,
always,
never
}
#if finished
// TODO: using Patch to replace HarmonyPatch
[System.AttributeUsage(System.AttributeTargets.Method, AllowMultiple = true)]
public class Patch : System.Attribute {
public Patch(Type ty, string name, Type[] types=null, MethodType mty=null, )
public MethodBase met;
}
#endif
[System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Struct | System.AttributeTargets.Field, AllowMultiple = false)]
public class Desc : System.Attribute {
public string desc;
public string str;
public double val;
public Enable enable=Enable.gt;
public Type ty=typeof(void);
public Desc(string desc, string str, Enable enable = Enable.gt, double val=0) {
this.desc = desc;
this.str = str;
this.enable = enable;
this.val = val;
}
public Desc(string desc, double val) {
this.desc = desc;
this.str = "";
this.val = val;
this.enable = Enable.gt;
}
public Desc(string desc=null, Enable enable = Enable.gt, double val=0) {
this.desc = desc;
this.str = "";
this.val = val;
this.enable = enable;
}
public Desc(string desc,string enable,double val) {
this.desc = desc;
this.str = "";
this.val = val;
this.enable=enable.ToLower() switch {
"lt"=>Enable.lt,
"le"=>Enable.le,
"ne" =>Enable.ne,
"ge"=>Enable.ge,
"gt"=>Enable.gt,
"always"=>Enable.always,
"never"=>Enable.never,
_=>throw new Exception($"Desc项目禁用选项{enable}无效")
};
}
public string desc_with_enable(string now)=>Type.GetTypeCode(this.ty) switch{
TypeCode.Object=>$"{this.desc}{now}",
TypeCode.Boolean=>$"{this.desc}{now}({this.enable switch {Enable.gt=>"为true时启用此修改",Enable.always=>"总是启用",Enable.never=>"不影响此项启用状态",_=>throw new Exception("bool的Enable情况只能为默认(其实是gt),always与never")}})",
TypeCode.String=>$"{this.desc}{now}(字符串长度在除去多余空白字符后{this.enable_op})",
_=>$"{this.desc}{now}({this.enable_op})"
};
public string desc_with_enable()=>desc_with_enable(string.Empty);
public string enable_op=>$"{(string.IsNullOrEmpty(this.str)?"":"字符串长度")}{(this.enable switch{
Enable.lt=>$"小于{this.val}时启用此修改",
Enable.le=>$"小于等于{this.val}时启用此修改",
Enable.ne=>$"不等于{this.val}时启用此修改",
Enable.ge=>$"大于等于{this.val}时启用此修改",
Enable.gt=>$"大于{this.val}时启用此修改",
Enable.always=>"总是启用此修改",
Enable.never=>"不影响当前项目的启用状态",
_=>throw new Exception($"枚举错误,this.disable的值{this.enable}不正确")
})}";
public string desc_with_disable(string now)=>Type.GetTypeCode(this.ty) switch{
TypeCode.Object=>$"{this.desc}{now}",
TypeCode.Boolean=>$"{this.desc}{now}({this.enable switch {Enable.gt=>"为false时禁用此修改",Enable.always=>"总是启用",Enable.never=>"不影响此项启用状态",_=>throw new Exception("bool的Enable情况只能为默认(其实是gt),always与never")}})",
TypeCode.String=>$"{this.desc}{now}(字符串长度在除去多余空白字符后{this.disable_op})",
_=>$"{this.desc}{now}({this.disable_op})"
};
public string desc_with_disable()=>desc_with_disable(string.Empty);
public string disable_op=>$"{(string.IsNullOrEmpty(this.str)?"":"字符串长度")}{(this.enable switch{
Enable.lt=>$"大于等于{this.val}时不影响当前项目的启用状态",
Enable.le=>$"大于{this.val}时不影响当前项目的启用状态",
Enable.ne=>$"等于{this.val}时不影响当前项目的启用状态",
Enable.ge=>$"小于{this.val}时不影响当前项目的启用状态",
Enable.gt=>$"小于等于{this.val}时不影响当前项目的启用状态",
Enable.always=>"总是启用此修改",
Enable.never=>"不影响当前项目的启用状态",
_=>throw new Exception($"枚举错误,this.enable的值{this.enable}不正确")
})}";
// public bool Enable()=>this.val>=0; // for class desc only.
public bool match<T>(T v)=>v is string s?this.match_str(s):v is bool b?this.match_bool(b):this.enable switch {
Enable.lt=>System.Convert.ToDouble(v) < this.val,
Enable.le=>System.Convert.ToDouble(v) <=this.val,
Enable.ne=>System.Convert.ToDouble(v) !=this.val,
Enable.ge=>System.Convert.ToDouble(v) >=this.val,
Enable.gt=>System.Convert.ToDouble(v) > this.val,
Enable.always=>true,
Enable.never=>false,
_=>false
};
public bool match_str(string v)=>this.match(v.Length);
public bool match_bool(bool v)=>this.enable switch {
Enable.gt=>v,
Enable.always=>true,
Enable.never=>false,
_=>throw new Exception("bool的Enable情况只能为默认(其实是gt),always与never")
};
public const MethodType get=MethodType.Getter;
public const MethodType set=MethodType.Setter;
public const MethodType ctor=MethodType.Constructor;
public const MethodType Get=MethodType.Getter;
public const MethodType Set=MethodType.Setter;
public const MethodType Ctor=MethodType.Constructor;
public const MethodType GET=MethodType.Getter;
public const MethodType SET=MethodType.Setter;
public const MethodType CTOR=MethodType.Constructor;
public const BindingFlags all=(BindingFlags)(-1);
public const BindingFlags All=(BindingFlags)(-1);
public const BindingFlags ALL=(BindingFlags)(-1);
}
public abstract class ModEntry : BasePlugin {
public static HarmonyLib.Harmony harmony;
public static DescConfig config;
#if DEBUG
public static Action<string> logger;
#else
public static void logger(string s){}
#endif
#if VERBOSE
public static Action<string> vlogger;
#else
public static void vlogger(string s){}
#endif
static Action<string> _logwarn;
public static void logwarn(string s, Exception ex=null){
_logwarn(ex is null?s:(s+str_exception(ex)));
}
public static void logexception(Exception ex){
_logwarn(str_exception(ex));
}
public static string str_exception(Exception ex){
var sb=new System.Text.StringBuilder();
_strexception(sb, ex, 0);
return sb.ToString();
}
static void _strexception(System.Text.StringBuilder sb, Exception ex, int depth){
var padding=depth==0?"\n":"\n"+new string(' ',depth*4);
sb.Append("\n").Append(ex.GetType().Name).Append(": ").Append(ex.Message).Append("\n").Append(ex.StackTrace).Replace("\n",padding);
if(ex.InnerException !=null){
_strexception(sb,ex.InnerException,depth+1);
}
}
#region static patch attibute
#if finished
// TODO: static 还需斟酌
public class Entry<T> : Base<T> {}
public static class BaseT {
public static Dictionary<Type, BaseTy> entries=new();
}
public abstract class BaseTy {
public bool enable=false;
public string full_desc="";
protected Dictionary<FieldInfo, object> dict=new Dictionary<FieldInfo, object>();
public static readonly MethodInfo CONFIG_BIND=typeof(DescConfig).GetMethod("Bind",new Type[]{typeof(string),typeof(string),Type.MakeGenericMethodParameter(0),typeof(string)});
public static readonly MethodInfo DESC_MATCH=typeof(Desc).GetMethod("match", new Type[]{Type.MakeGenericMethodParameter(0)});
public abstract bool assign(FieldInfo f, Desc desc);
public virtual void Init()=>Init(true);
public abstract void Init(bool check_enable);
}
public abstract class Base<T> : BaseTy {
public override bool assign(FieldInfo f, Desc desc){ // return whether the type is enabled.
var met=typeof(T).GetMethod("assign", (BindingFlags)(-1), new Type[]{typeof(Base<T>),typeof(FieldInfo),typeof(Desc)});
// static bool assign(this Base<T>, FieldInfo f, Desc desc)
if(met is not null){
if(met.ReturnType==typeof(bool)){
return (bool)(met.Invoke(null, new object[]{this, f, desc}));
}
}
met=typeof(T).GetMethod("assign", (BindingFlags)(-1), new Type[]{typeof(FieldInfo),typeof(Desc)});
// static bool assign(FieldInfo f, Desc desc)
if(met is not null){
if(met.ReturnType==typeof(bool)){
return (bool)(met.Invoke(null, new object[]{f, desc}));
}
}
// TODO: 增加[assign]属性
desc.ty=f.FieldType;
string full_entry = typeof(T).Name+"."+f.Name;
try {
var method=CONFIG_BIND.MakeGenericMethod(f.FieldType);
var prop=method.ReturnType.GetProperty("Value").GetGetMethod();
if(prop.ReturnType!=f.FieldType){
throw new Exception($"获取属性方法出错, prop:{prop.ReturnType} != field:{f.FieldType}");
}
dict[f]=method.Invoke(config,new object[]{"config",full_entry,f.GetValue(null),desc.desc_with_enable()});
var val=prop.Invoke(dict[f],null);
f.SetValue(null,val);
if(prop.ReturnType==typeof(string)){
return desc.match((string)val);
} else if (prop.ReturnType==typeof(bool)){
return desc.match((bool)val);
}else {
return (bool)(DESC_MATCH.MakeGenericMethod(prop.ReturnType).Invoke(desc, new object[]{val}));
}
} catch (Exception ex) {
logwarn($"使用了不支持的ConfigEntry: {f.Name} : {f.FieldType} ,错误如下",ex);
return true;
}
}
public override void Init(bool check_enable){
#if VERBOSE
vlogger("正在修改:"+typeof(T).Name);
#endif
var type = typeof(T);
this.full_desc="";
Desc dsc = (Desc) Attribute.GetCustomAttribute(type, typeof(Desc)); // 判断类是否有Desc
if(dsc!=null){
if(dsc.desc is null){
dsc.desc = typeof(T).Name;
}
this.enable=dsc.val>=0; // class desc only.
this.assign(type.GetField("enable"),dsc);
full_desc=dsc.desc;
}
foreach(var f in type.GetFields((BindingFlags)(-1))){
Desc desc = (Desc) Attribute.GetCustomAttribute(f, typeof(Desc));
#if VERBOSE
vlogger("正在检查:"+type.Name+"的"+f.Name+"字段");
#endif
if(desc!=null){
#if VERBOSE
vlogger("正在检查:"+type.Name+"的"+f.Name+"字段--包含desc信息");
#endif
var flag=this.assign(f,desc);
enable|=flag;
var now=$"={f.GetValue(null)}";
if(full_desc.Length>0){
full_desc=full_desc+(dsc==null?" & ":" : ")+(flag?desc.desc_with_disable(now):desc.desc_with_enable(now));
dsc=null;
}else{
full_desc=flag?desc.desc_with_disable(now):desc.desc_with_enable(now);
}
}
}
var met=type.GetMethod("Init", new Type[]{typeof(bool)});
if(met is not null){
if(met.ReturnType==typeof(bool)){
enable=(bool)(met.Invoke(null, new object[]{check_enable}));
} else {
met.Invoke(null, new object[]{check_enable});
}
}
}
}
public abstract class Custom<T> : Base<T> {
public abstract void Enable();
public override void Init(bool check_enable){
base.Init();
if(check_enable){
if(enable){
try {
this.Enable();
logger($"已启用 {full_desc}");
} catch (Exception e) {
logwarn($"({this.GetType()})在执行Enable时出错,这导致Mod的「{this.full_desc}」功能失效。具体错误如下:");
logexception(e);
}
} else {
logger($"未启用 {full_desc}");
}
} else {
logger($"开始手工处理 {full_desc} 的数据");
}
}
}
public abstract class Harmony<T> : Custom<T> {
public override void Enable(){
if(this.enable){
harmony.PatchAll(this.GetType());
}
}
}
public abstract class CustomHarmony<T> : Custom<T> {
static MethodInfo met=typeof(T).GetMethod("Enable", new Type[0]);
static bool flag=met is null || met.ReturnType!=typeof(bool);
public override void Enable(){
if(flag){
logwarn($"由于{(met is null?"enable is null":"met.ReturnType!=typeof(bool)")},Harmony Enable执行失败");
} else if((bool)(met.Invoke(null, null))){
harmony.PatchAll(this.GetType());
}
}
}
public abstract class CustomNoHarmony<T> : Custom<T> {
static MethodInfo met=typeof(T).GetMethod("Enable", new Type[0]);
static bool flag=met is null || met.ReturnType!=typeof(void);
public override void Enable(){
met.Invoke(null, null);
}
}
#endif
#endregion
public abstract class Entry : Base {
public override void Enable(){
harmony.PatchAll(this.GetType());
}
}
public class DescConfig {
#if !DOORSTOP
public static BepInEx.Configuration.ConfigFile config;
#else
// TODO: adding config file.
#endif
public static void Init(ModEntry entry){
#if !DOORSTOP
config = entry.Config;
#else
// TODO: adding config file.
#endif
}
public static ConfigEntry<T> ConfigEntry<T>(string arg1, string arg2, T arg3, string arg4){
#if !DOORSTOP
return config.Bind(arg1,arg2,arg3,arg4);
#else
return default(ConfigEntry<T>);
// TODO: adding config file.
#endif
}
public static bool match<T>(Desc desc, T t)=>desc.match<T>(t);
}
// Base类不会被Awake自动初始化,而Entry会。
// Base应该配合new Base().Init()使用。
public abstract class Base {
public bool enable=false;
public string full_desc="";
static Dictionary<FieldInfo, object> dict=new Dictionary<FieldInfo, object>();
public static void SetConfigValue<T>(FieldInfo f, T val) {
if (dict.TryGetValue(f, out var v) && v is ConfigEntry<T> entry) {
entry.Value = val;
} else {
logwarn($"either dict contains no key of {f}, or {f} does not has the type {typeof(T)}");
}
}
public static readonly MethodInfo CONFIG_BIND=typeof(DescConfig).GetMethod("ConfigEntry");
public static readonly MethodInfo DESC_MATCH=typeof(DescConfig).GetMethod("match");
public virtual bool assign(FieldInfo f, Desc desc){ // return whether the type is enabled.
desc.ty=f.FieldType;
string full_entry = this.GetType().Name+"."+f.Name;
try {
var method=CONFIG_BIND.MakeGenericMethod(f.FieldType);
var prop=method.ReturnType.GetProperty("Value").GetGetMethod();
if(prop.ReturnType!=f.FieldType){
throw new Exception($"获取属性方法出错, prop:{prop.ReturnType} != field:{f.FieldType}");
}
dict[f]=method.Invoke(config,new object[]{"config",full_entry,f.GetValue(this),desc.desc_with_enable()});
var val=prop.Invoke(dict[f],null);
f.SetValue(this,val);
if(prop.ReturnType==typeof(string)){
return desc.match((string)val);
} else if (prop.ReturnType==typeof(bool)){
return desc.match((bool)val);
}else {
return (bool)(DESC_MATCH.MakeGenericMethod(prop.ReturnType).Invoke(null, new object[]{desc, val}));
}
} catch (Exception ex) {
logwarn($"使用了不支持的ConfigEntry: {f.Name} : {f.FieldType} ,错误如下",ex);
return true;
}
}
public virtual void InitDelayed(){}
// 应当修改Enable的相关逻辑,最好不要修改Init的相关逻辑(除非决定使用InitDelayed)
public virtual void Init()=>Init(true);
public virtual void Init(bool check_enable){
#if VERBOSE
vlogger("正在修改:"+this.GetType().Name);
#endif
var type = this.GetType();
this.full_desc="";
Desc dsc = (Desc) Attribute.GetCustomAttribute(type, typeof(Desc)); // 判断类是否有Desc
if(dsc!=null){
if(dsc.desc is null){
dsc.desc = type.Name;
}
this.enable=dsc.val>=0;
this.assign(type.GetField("enable"),dsc);
full_desc=dsc.desc;
}
foreach(var f in type.GetFields((BindingFlags)(-1))){
Desc desc = (Desc) Attribute.GetCustomAttribute(f, typeof(Desc));
#if VERBOSE
vlogger("正在检查:"+this.GetType().Name+"的"+f.Name+"字段");
#endif
if(desc!=null){
#if VERBOSE
vlogger("正在检查:"+this.GetType().Name+"的"+f.Name+"字段--包含desc信息");
#endif
if(desc.desc is null){
desc.desc = full_desc.Length>0?f.Name:(type.Name+"."+f.Name);
}
var flag=this.assign(f,desc);
enable|=flag;
var now=$"={f.GetValue(null)}";
if(full_desc.Length>0){
full_desc=full_desc+(dsc==null?" & ":" : ")+(flag?desc.desc_with_disable(now):desc.desc_with_enable(now));
dsc=null;
}else{
full_desc=flag?desc.desc_with_disable(now):desc.desc_with_enable(now);
}
}
}
if(check_enable){
if(enable){
try {
this.Enable();
logger($"已启用 {full_desc}");
} catch (Exception e) {
logwarn($"({this.GetType()})在执行Enable时出错,这导致Mod的「{this.full_desc}」功能失效。具体错误如下:");
logexception(e);
}
} else {
logger($"未启用 {full_desc}");
}
} else {
logger($"开始手工处理 {full_desc} 的数据");
}
}
// 应当修改Enable的相关逻辑,需要注意,必须调用base.Enable();
public abstract void Enable();
}
public abstract class Const : Base {
public override bool assign(FieldInfo f, Desc desc){ // return whether the type is enabled.
base.assign(f,desc);
return true;
}
public override void Init(){
#if VERBOSE
vlogger("正在修改:"+this.GetType().Name);
#endif
var type = this.GetType();
this.full_desc="";
Desc dsc = (Desc) Attribute.GetCustomAttribute(type, typeof(Desc));
if(dsc!=null){
full_desc=dsc.desc;
}
foreach(var f in type.GetFields((BindingFlags)(-1))){
Desc desc = (Desc) Attribute.GetCustomAttribute(f, typeof(Desc));
#if VERBOSE
vlogger("正在检查:"+this.GetType().Name+"的"+f.Name+"字段");
#endif
if(desc!=null){
#if VERBOSE
vlogger("正在检查:"+this.GetType().Name+"的"+f.Name+"字段--包含desc信息");
#endif
this.assign(f,desc);
if(full_desc.Length>0){
full_desc=full_desc+(dsc==null?" & ":" : ")+desc.desc;
dsc=null;
}else{
full_desc=desc.desc;
}
}
}
logger($"读取常数 {full_desc}");
}
public override void Enable(){}
}
#if IL2CPP
public override void Load()=>InitDelayed();
#else
public virtual void Awake()=>Init();
public virtual void Start()=>InitDelayed();
#endif
static bool need_delayed_init=true;
static Base[] classes = new Base[0];
public virtual void InitDelayed() {
if(need_init){
Init();
}
if(need_delayed_init){
foreach(var entry in classes.Where(x=>x.GetType().GetMethod("InitDelayed",BindingFlags.Public | BindingFlags.Instance).DeclaringType != typeof(Base))) {
if(entry.enable) {
if(need_delayed_init){
logger("开始二段注入");
need_delayed_init=false;
}
try {
entry.InitDelayed();
logger($"{entry} InitDelayed 完成");
} catch (Exception ex) {
logwarn($"{entry} InitDelayed 出错:", ex);
}
}
}
}
}
static bool need_init=true;
public virtual void Init() {
if(need_init){
need_init=false;
logger("开始注入");
Type[] t=new Type[0]; // const
var whitelist = DescConfig.ConfigEntry("config", "..Whitelist.Enable..", false,"会启用自动白名单算法,将会自动把当前启用的mod记录在下面的..Whitelist..中。默认false。如果此项不为true,将会尝试清空..Whitelist..\n如果希望锁定Mod中生效的功能,请在完成设置之后,删除或清空..Whitelist..(否则..Whitelist..的取值会影响新功能的启用)并将..Whitelist.Enable..设置为true\n 值得注意的是,如果Mod因为长时间不更新而失效,请打开..Whitelist.Enable..功能,这个功能或许可以在一定程度上自动屏蔽失效项带来的影响").Value;
var enabled_entries = DescConfig.ConfigEntry("config", "..Whitelist..", "","当白名单不为空且启用时,只有下列修改会被启用,以英文逗号分割。大多数情况下你不需要修改这里的内容,因为程序会自动帮你完成修改。\n这个选项是为了防止Mod更新后默认启用各种新功能而设计的\n在Mod更新后,如果你的确想使用新功能,可整行删除此配置项,或者清空配置项的值(不必亲自动手把新功能填进来)\n需注意:\n 1. 这里留空的意思是「不禁用」后续选项(后续选项仍然需要根据选项设置决定是否生效),而非禁用全部选项。\n 2. 如果你的确需要不受更新影响地禁用全部选项,可以在..Whitelist.Enable..为true的情况下,在此填入一个英文句点。");
bool empty_enabled = string.IsNullOrWhiteSpace(enabled_entries.Value) || !whitelist;
var enabled_set = enabled_entries.Value.Split(',').Select(x=>x.Trim()).Where(x=>!string.IsNullOrWhiteSpace(x)).ToHashSet();
var tmp_classes = new List<Base>();
foreach(var type in this.GetType().Module.GetTypes()){
if(!type.IsAbstract){
try{
#if VERBOSE
vlogger("搜索到:"+type.ToString());
#endif
if(type.IsSubclassOf(typeof(Base))){
Base entry=(Base)(type.GetConstructor(t).Invoke(t));
if (entry!=null){
if(empty_enabled || enabled_set.Contains(entry.GetType().Name)) {
try {
entry.Init();
if(entry.enable) {
tmp_classes.Add(entry);
}
} catch(Exception ex) {
logwarn($"{entry} Init出错", ex);
}
// type.GetMethod("Init",t).Invoke(,t);
} else {
logger($"功能{entry.GetType().Name}不在启用列表..Whitelist..中,因而不启用这一功能");
}
}else{logwarn($"{(type.IsSubclassOf(typeof(Entry))?"Entry":"Base")}:{type}的type.GetConstructor().Invoke()是null,这多半是mod出了问题,如果你看到这个,请联系mod作者。");}
}
#if finished
else if(type.IsAbstract && type.IsSealed){ // find static classes
BaseTy entry=(BaseTy)(type.GetConstructor(t).Invoke(t));
if (entry!=null){
entry.Init();
}else{logwarn($"{(type.IsSubclassOf(typeof(Entry))?"Entry":"Base")}:{type}的type.GetConstructor().Invoke()是null,这多半是mod出了问题,如果你看到这个,请联系mod作者。");}
}
#endif
} catch (Exception e) {
logwarn($"{this.plugin_id}({type})出现了意料之外的错误,或许你可以联系Mod作者修复。");
logexception(e);
}
}
}
classes = tmp_classes.ToArray();
if (empty_enabled) {
var target = whitelist ? string.Join(",", classes.Select(x=>x.GetType().Name)): "";
if(enabled_entries.Value!=target) {enabled_entries.Value = target;}
}
}
}
string plugin_id;
public ModEntry(string plugin_id){
this.plugin_id=plugin_id;
#if IL2CPP
#if DEBUG
logger=Log.LogInfo;
#if VERBOSE
vlogger=Log.LogWarning; // `vlogger` is defined in utils.cs
#endif
#endif
#else
#if DEBUG
logger=Logger.LogInfo;
#if VERBOSE
vlogger=Logger.LogWarning; // `vlogger` is defined in utils.cs
#endif
#endif
#endif
#if IL2CPP
_logwarn=Log.LogWarning;
Log.LogInfo("此Mod使用AGPL-v3许可发布,如果你使用了这里的代码,请按照相同许可发布你修改后的mod。");
#else
_logwarn=Logger.LogWarning;
Logger.LogInfo("此Mod使用AGPL-v3许可发布,如果你使用了这里的代码,请按照相同许可发布你修改后的mod。");
#endif
harmony=new Harmony(plugin_id); // `harmony` is defined in utils.cs
DescConfig.Init(this); // `utils` is defined in utils.cs
}
#region dump
public class dump {
public static dump instance = new();
public string left="";
public string right="";
public Func<object, string> to_string=x=>x.ToString();
public string sep=", ";
public (char, string) cell_sep = ('\t', "\\t");
public (string, string) nl = ("\n", "\\n");
static readonly Type self_ty = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType;
static readonly MethodInfo join_list_gen = self_ty.GetMethod("join_list");
static readonly MethodInfo process_dict_gen = self_ty.GetMethod("process_dict");
public string join_list<T>(IEnumerable<T> content) {
var arr = content.Select(x=>x.ToString()).ToArray();
if (arr.Length>0) {
return $"{this.left}{string.Join(this.sep, content.Select(x=>this.detailed(x)))}{this.right}";
} else {
return "";
}
}
public string process_dict<K,V>(KeyValuePair<K,V> content) => $"{this.detailed(content.Key)} => {this.detailed(content.Value)}";
public string detailed(object content) {
if(content is null) {
return "";
} else if (content is string s) {
return s;
}
try { // iter check
var iter = content.GetType().FindInterfaces(Module.FilterTypeName, "IEnumerable`1*");
if(iter.Length>0 && iter[0].GenericTypeArguments.Length>0) {
return (string)(join_list_gen.MakeGenericMethod(iter[0].GenericTypeArguments).Invoke(this, new object[]{content}));
}
} catch (Exception ex) {
logwarn($"在处理{content} as IEnumerable时出现异常", ex);
}
try { // iter check
var iter = content.GetType().FindInterfaces(Module.FilterTypeName, "KeyValuePair`2*");
if(iter.Length>0 && iter[0].GenericTypeArguments.Length>0) {
return (string)(process_dict_gen.MakeGenericMethod(iter[0].GenericTypeArguments).Invoke(this, new object[]{content}));
}
} catch (Exception ex) {
logwarn($"在处理{content} as KeyValuePair时出现异常", ex);
}
return to_string is null ? content.ToString() : to_string(content);
}
public void dumping<T>(IEnumerable<T> items, Type[] as_type, string[] delayed, (string, Func<T, object>)[] special) {
var bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance/* | BindingFlags.Static*/;
if(as_type is null) {
bf |= BindingFlags.FlattenHierarchy;
as_type = new Type[0];
} else {
bf |= BindingFlags.DeclaredOnly;
}
if(delayed is null){
delayed = new string[0];
}
if(special is null){
special = new (string, Func<T, object>)[0];
}
var hs = ((delayed ?? new string[0]).Concat(special?.Select(x=>x.Item1) ?? new string[0])).ToHashSet();
bool first = true;
var sb = new System.Text.StringBuilder();
var maps = new Action<T>[0];
var sb_append=(object x)=>sb.Append($"{this.detailed(x).Replace(this.nl.Item1,this.nl.Item2).Replace(this.cell_sep.Item1.ToString(), this.cell_sep.Item2)}{this.cell_sep.Item1}");
var sb_nl=()=>{
while(sb.Length>0 && sb[sb.Length-1]==this.cell_sep.Item1) {
sb.Remove(sb.Length-1,1);
}
sb.Append(nl.Item1);
};
sb_nl();
foreach(var item in items) {
if(first) {
List<Action<T>> tmp_map = new();
// special
foreach(var sp in special) {
sb_append(sp.Item1);
tmp_map.Add((T x)=>sb_append(this.detailed(sp.Item2(x))));
}
// normal
foreach(var type in as_type.Prepend(item.GetType())) {
foreach(var field in type.GetFields().Where(field=>!hs.Contains(field.Name))) {
sb_append(field.Name);
tmp_map.Add((T x)=>sb_append(field.GetValue(x)));
}
}
// delayed
foreach(var delayed_field in delayed) {
if(item.GetType().GetField(delayed_field, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance/* | BindingFlags.Static */| BindingFlags.FlattenHierarchy ) is var field and not null) {
sb_append(delayed_field);
tmp_map.Add((T x)=>sb_append(field.GetValue(x)));
} else {
sb_append("");
logwarn($"找不到field {delayed_field}");
}
}
maps = tmp_map.ToArray();
sb_nl();
first = false;
}
foreach(var fun in maps) {
fun(item);
}
sb_nl();
}
logger(sb.ToString());
}
}
#endregion
}
// BLACK MAGIC: save one more #if ...#endif by breaking the `{}` matching rule.
#if DOORSTOP
// TODO: must write a sepearted Unity loader, otherwise the dll could not be loaded?
public static class Logger {
static Logger() {
System.IO.File.WriteAllText("./Neutron/Log.log", "");
}
public static void LogInfo(string s){
System.IO.File.AppendAllText("./Neutron/Log.log", s+"\n");
}
public static void LogWarning(string s){
System.IO.File.AppendAllText("./Neutron/Log.log", "Warning:"+s+"\n");
}
}
}
// harmony v2.3.3.0
namespace utils {
public static class ImplHarmonyXPatchAll {
public static void PatchAll(this Harmony harmony, Type type) {
(new HarmonyLib.PatchClassProcessor(harmony, type)).Patch();
}
}
}
// doorstop v0.4.3
namespace Doorstop {
class Entrypoint {
public static void Start() {
System.IO.File.WriteAllText("doorstop_hello.log", "Hello from Unity!");
(new Neutron3529.Cheat.Cheat()).Awake();
}
}
#endif
}
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )