diff --git a/Source/Client/Patches/Determinism.cs b/Source/Client/Patches/Determinism.cs index a56bf353..9bdf674e 100644 --- a/Source/Client/Patches/Determinism.cs +++ b/Source/Client/Patches/Determinism.cs @@ -725,4 +725,30 @@ static IEnumerable Transpiler(IEnumerable inst } } + // FastTileFinder.ComputeQueryJob uses Interlocked.Increment to race-fill a 50-slot result array + // across parallel Unity Job batches. Thread scheduling differs between machines, so clients get + // different candidate tile sets. Force single-batch execution in MP so tiles are processed in + // tileId order, making the first 50 valid tiles consistent across all clients. + [HarmonyPatch(typeof(FastTileFinder), nameof(FastTileFinder.Query))] + static class FastTileFinderQueryDeterminismPatch + { + static IEnumerable Transpiler(IEnumerable instructions) + { + var getIdealBatchCount = AccessTools.Method(typeof(UnityData), nameof(UnityData.GetIdealBatchCount)); + var getBatchCount = AccessTools.Method(typeof(FastTileFinderQueryDeterminismPatch), nameof(GetBatchCount)); + + foreach (var instr in instructions) + { + if (instr.Calls(getIdealBatchCount)) + yield return new CodeInstruction(OpCodes.Call, getBatchCount); + else + yield return instr; + } + } + + static int GetBatchCount(int length) => + Multiplayer.Client != null ? length : UnityData.GetIdealBatchCount(length); + } + + }