Diplomacy is Not an Option

Diplomacy is Not an Option

Not enough ratings
Modding is an Option (with BepInEx) -- 模组教程(BepInEx版)
By 恨铁不成钢琴
This is no more than a simple attempt that works. Please do not rely on any theory mentioned in this guide.
这是一个简单示例,只保证在指南作者跑路之前程序能跑

As for BepInEx, although the Harmony part might not be used (unless you want to halve the frame rate), but its loader is still usable.
使用BepInEx的原因是馋它的加载逻辑。请不要使用宵夜97的BepInEx教程,除非你想让游戏帧率减半

The translations are not guarateened to be consist, be careful.
不保证中英文翻译一一对应……也不保证中文蕴含的信息比英文多
   
Award
Favorite
Favorited
Unfavorite
Use HarmonyPatch, which halves your frame rate -- 强行使用Harmony
Just delete (or rename) the following file is enough:
删掉这个文件之后,就可以参考宵夜97的BepInEx教程:
steamapps/common/Diplomacy is Not an Option/Diplomacy is Not an Option_Data/Plugins/x86_64/lib_burst_generated.dll

Please notice that, delete this dll at least halves your frame rate.
请注意,删掉这个dll会使帧率减半。
ECS based Mod -- 使用ECS系统写Mod
Since delete the dll file is not graceful, here, I recall the traditional way, writting ECS code directly.
由于上一种方法对性能的损耗非常严重,这里介绍一个非常传统的思路,使用ECS系统做Mod。

For example, the following code completes all the pending researches.
比如,下列代码可以瞬间完成当前选定的科技(甚至不用出科研界面)

[BurstCompile] // as @dou suggests in comments, these [BurstCompile] attributes are useless without some other modifications
// I cannot tell what should be done, since I'm new to Unity Burst.
[UpdateAfter(typeof(ResearchSystem))]
public class FastResearch : Systems.SystemBaseSimulation {
protected override void OnCreateSimulation() {
Enabled = Cheat.ConstEntry.research;
Cheat.logger($"{(Cheat.ConstEntry.research?"已":"未")}启用:才思敏捷(研究速度飞快)");
}
[BurstCompile]
protected override void OnUpdateSimulation() {
if (this.HasSingleton<Components.SharedContainerSingletons.ResearchContainerSingleton>()) {
var container = ComponentSystemBaseManagedComponentExtensions.GetSingleton<Components.SharedContainerSingletons.ResearchContainerSingleton>(this).Container;
if (container.CurrentResearchId.Value>0) {
container.ResearchProgress.Value = 1;
}
}
}
}

For a difficult job, we could write job struct directly:

[BurstCompile]
[UpdateInGroup(typeof(ResourceProducingGroup))]
[UpdateBefore(typeof(Systems.ResourceSystems.ResourceProduceSystem))]
public class ShinyTools : Systems.SystemBaseSimulation {
EntityQuery _query;
ComponentTypeHandle<WorkerProcess> _WorkerProcessRW;
protected override void OnCreateSimulation() {
if(Enabled = Cheat.ConstEntry.shiny_tools) {
_WorkerProcessRW = this.GetComponentTypeHandle<WorkerProcess>();
this._query = base.GetEntityQuery(new EntityQueryDesc {
All = new ComponentType[] {
ComponentType.ReadWrite<WorkerProcess>()
},
None = new ComponentType[]{}
});
}
Cheat.logger($"{(Enabled?"已":"未")}启用:欲善其事(先利其器,资源生产速度获得极大提升)");
}
[BurstCompile]
protected override void OnUpdateSimulation() {
var x = new Jobs(this);
var z = this._query.GetArchetypeChunkIterator();
x.RunWithoutJobs(ref z);
}
[BurstCompile]
public partial struct Jobs : IJobChunk {
ShinyTools system;
public Jobs(ShinyTools system) {
this.system = system;
}
[BurstCompile]
public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex) {
var count = chunk.Count;
IntPtr workerProcess = InternalCompilerInterface.UnsafeGetChunkNativeArrayIntPtr(chunk, this.system._WorkerProcessRW);
for (int i = 0; i != count; i++) {
this.Exec(ref InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement<WorkerProcess>(workerProcess, i));
}
}
[BurstCompile]
public void Exec(ref WorkerProcess w) {
w.workStartTime = 0;
}
}
}

I have no idea how to write jobs with ScheduleParallel, thus only `RunWithoutJobs` is used.
如果有人知道怎么写ScheduleParallel的话,望不吝告知:)

The only difficult is that, how to load the plugin, with BepInEx plugin system, the problem could be solved very easy.
之后,唯一剩下的问题是如何读取mod插件——这一点可以直接问BepInEx。

The full source code is uploaded into gitee (Chinese Github), the url is provided in comments (in order to make steam's external link check happy)
更多代码上传到了gitee,有兴趣的自己去看就好了,链接放在评论区,防止steam的钓鱼网站审查系统报警
11 Comments
dou Oct 18, 2024 @ 3:07am 
[HarmonyPatch] can also be used without deleting the Burst dll. In fact, a lot can be done without looping through components data every frame, including the effects in your example.
dou Oct 17, 2024 @ 10:45pm 
You didn't need to test that. I didn't claim there is negative performance impact from the attribute. It is probably never read by anything.
I don't know what you mean by enable JIT with other dlls. All c# code is jited by mono.
Burst uses LLVM. If you look at LLVM release page on github [github.com], the binary release is 800mb. I don't know where you are expecting this compiler to come from and compile your code.
The main reason I don't like the [BurstCompile] attribute is it makes your guide misleading. By adding it you are leading people to think this code will be compiled by Burst and is performant. But it's not, it is still run by the CLR. Even though you can loop through component data and modify them there is still a negative performance impact.
恨铁不成钢琴  [author] Oct 17, 2024 @ 7:56am 
@dou tested.
There is no difference with/without the [BurstCompile] attribute.
(code: adding a simple line `for (var i=100000000;i-->0;){}`, the frame rate is ~10 FPS no matter whether the attribute is set.)

But I cannot ensure whether we could enable JIT with some other dlls.
Thus I'll keep them.
dou Oct 14, 2024 @ 5:23am 
I don't believe. I find evidence. Unity docs tells you everything you need to know [docs.unity3d.com]
"I choose to believe my code works." No coder should ever say that.
恨铁不成钢琴  [author] Oct 14, 2024 @ 5:10am 
@dou
I cannot tell whether you're correct, since I have no knowledge about unity.burst until I started writting mods for this game.

The performance issue might be, JIT always slower than AOT. Even with some JIT support, the generated code might be slower.

Anyway, maybe you're correct, but I just cannot ensure that.

Currently, the [BurstCompile] seems like another God. You could believe it works, or not. In all cases, adding the [BurstCompile] won't be slower than without it.

I choose to believe its existance. And maybe someone could find out how to enable JIT(even AOT) for mods. In that case, keeping such attribute should be a better choice.
dou Oct 14, 2024 @ 4:15am 
[code]
0x00007FF838628AE8 (UnityPlayer) (function-name not available)
ERROR: SymGetSymFromAddr64, GetLastError: 'Attempt to access invalid address.' (Address: 00007FF8382A6F6F)
0x00007FF8382A6F6F (UnityPlayer) (function-name not available)
0x000001E9F779AF83 (Mono JIT Code) (wrapper managed-to-native) Unity.Jobs.LowLevel.Unsafe.JobsUtility:ScheduleParallelFor_Injected (Unity.Jobs.LowLevel.Unsafe.JobsUtility/JobScheduleParameters&,int,int,Unity.Jobs.JobHandle&)
[/code]
It happens in the schedule call because of some memory access violation. I don't know what's causing the memory violation. However I can schedule empty jobs with IJobParallelFor which also uses ScheduleParallelFor_Injected.

For the jobs themselves I use IJobEntityBatch example from unity docs [docs.unity3d.com]
dou Oct 14, 2024 @ 4:15am 
Burst JIT mode only works in the unity editor. Otherwise you could just delete the lib_burst_generated.dll, let Burst compile at runtime and there would be no performance issues.

The crash that happens when you call ScheduleParallel is not from Burst code. It's from unity player. The crash log is at C:\Users\username\AppData\Local\Temp\Door 407\Diplomacy is Not an Option\Crashes.
恨铁不成钢琴  [author] Oct 14, 2024 @ 12:05am 
@大刘志 代码不需要放在游戏里面,你需要的只是dll
游戏没有可用的API文档

如果你熟悉linux的话你可以参照gitee源码的前100行(编译用的sh脚本)自己编译
如果你不熟linux,可以打开windows的wsl,然后在wsl里面装dotnet sdk,之后用命令
./"Diplomacy is Not an Option.cs" d
编译dll(这需要把"Diplomacy is Not an Option.cs"和"utils.cs"下载到steamapps文件夹里,然后cd 到steamapps目录)

至于Mod成品dll在哪儿以及该怎么使用,我不想教
毕竟这里是写mod的指南,不是给mod打广告的指南:steamhappy:
恨铁不成钢琴  [author] Oct 13, 2024 @ 8:09pm 
@dou Burst has both AOT and JIT mode. Although AOT mode does not work, JIT mode might works. If the mod crashes (for example, with some failed attempt that using `ScheduleParallel`), you'll find the error messages from JIT code, rather than your original mod.
Burst同时具有AOT和JIT两种模式,虽然mod并不能以AOT模式运行,但Mod的确可以跑在JIT模式下。
dou Oct 13, 2024 @ 6:12pm 
I don't think your [BurstCompile] does anything. Burst code is AOT compiled and your mod runs in the CLR.