feat: 落沙替换改用记录+回放(装置驱动)
26.1.2单线程下1.12的替换窗口物理不存在,改用LegacyBlockChangeRecorder记录最近方块变更,fall时若命中窗口则用记录的新方块,保留装置驱动玩法。新增LevelChunkMixin记录setBlockState。
This commit is contained in:
1 parent
c42fffad3c
commit
0649034969
4 files changed
+118
-18
No files matched your search
+12
-18
@@ -1,25 +1,23 @@
|
|||||||
package com.github.zly2006.carpetslsaddition.mixin.legacy;
|
package com.github.zly2006.carpetslsaddition.mixin.legacy;
|
||||||
|
|
||||||
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
|
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
|
||||||
|
import com.github.zly2006.carpetslsaddition.util.LegacyBlockChangeRecorder;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.registries.BuiltInRegistries;
|
|
||||||
import net.minecraft.resources.Identifier;
|
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [旧版] 落沙替换的核心注入点。
|
* [旧版] 落沙替换的核心注入点——重建 1.12 的"替换窗口"(记录+回放版)。
|
||||||
*
|
*
|
||||||
* <p>26.1.2 的 {@code FallingBlockEntity.fall(Level, BlockPos, BlockState)} 直接使用传入的 state,
|
* <p>1.12:{@code BlockFalling} 创建实体时【重读世界】,异步线程在"检查→读取"间改块即可替换。
|
||||||
* 不像 1.12 那样在构造实体时重新读取世界。本 mixin 用 {@link ModifyVariable} 拦截该 state 参数:
|
* 26.1.2 单线程下该窗口物理不存在(tick 内同步 fall,无让出)。
|
||||||
* 当 {@code legacyFallingBlockReplace} 开启且配置了目标方块时,把 state 替换为目标方块的默认状态。
|
|
||||||
*
|
*
|
||||||
* <p>等价于 1.12 在"检查可下落"与"读取方块状态"之间的窗口替换效果——最终得到一个
|
* <p>本 mixin 改用 {@link LegacyBlockChangeRecorder}:
|
||||||
* "装着目标方块"的下落实体,落地后即还原为该目标方块。
|
* 当 {@code legacyFallingBlockReplace} 开启时,若该位置在最近 {@code WINDOW_TICKS} 内被改动过,
|
||||||
|
* 就用记录的新方块作为下落实体的内容。这样玩家搭建的装置(改变世界方块)会决定下落实体的内容,
|
||||||
|
* 与 1.12 的"装置驱动"玩法一致。
|
||||||
*/
|
*/
|
||||||
@Mixin(net.minecraft.world.entity.item.FallingBlockEntity.class)
|
@Mixin(net.minecraft.world.entity.item.FallingBlockEntity.class)
|
||||||
public class FallingBlockEntityMixin {
|
public class FallingBlockEntityMixin {
|
||||||
@@ -30,18 +28,14 @@ public class FallingBlockEntityMixin {
|
|||||||
argsOnly = true,
|
argsOnly = true,
|
||||||
index = 2
|
index = 2
|
||||||
)
|
)
|
||||||
private static BlockState legacyReplaceFallingState(BlockState original) {
|
private static BlockState legacyReplaceFallingState(BlockState original, net.minecraft.world.level.Level level, BlockPos pos, BlockState state) {
|
||||||
if (!SLSCarpetSettings.legacyExploitMode || !SLSCarpetSettings.legacyFallingBlockReplace) {
|
if (!SLSCarpetSettings.legacyExploitMode || !SLSCarpetSettings.legacyFallingBlockReplace) {
|
||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
String target = SLSCarpetSettings.legacyFallingBlockTarget;
|
BlockState recent = LegacyBlockChangeRecorder.getRecentChange(pos);
|
||||||
if (target == null || target.isEmpty() || "#none".equals(target)) {
|
if (recent != null && !recent.isAir()) {
|
||||||
return original;
|
return recent;
|
||||||
}
|
}
|
||||||
Block block = BuiltInRegistries.BLOCK.getOptional(Identifier.withDefaultNamespace(target)).orElse(null);
|
|
||||||
if (block == null) {
|
|
||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
return block.defaultBlockState();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.github.zly2006.carpetslsaddition.mixin.legacy;
|
||||||
|
|
||||||
|
import com.github.zly2006.carpetslsaddition.SLSCarpetSettings;
|
||||||
|
import com.github.zly2006.carpetslsaddition.util.LegacyBlockChangeRecorder;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [旧版] 方块变更记录——为"替换窗口"提供数据源。
|
||||||
|
*
|
||||||
|
* <p>hook {@link LevelChunk#setBlockState}:当规则开启时,记录每次方块变更(位置 + 新状态 + 当前 tick)。
|
||||||
|
* 重力方块下落时({@code FallingBlockEntityMixin})会查询此记录,
|
||||||
|
* 若该位置"刚被改过",就用记录的新方块作为下落实体内容——重建 1.12 的窗口替换效果。
|
||||||
|
*
|
||||||
|
* <p>仅在 {@code legacyExploitMode} + {@code legacyFallingBlockReplace} 同时开启时记录,避免性能损耗。
|
||||||
|
*/
|
||||||
|
@Mixin(LevelChunk.class)
|
||||||
|
public abstract class LevelChunkMixin {
|
||||||
|
|
||||||
|
@Inject(method = "setBlockState", at = @At("RETURN"))
|
||||||
|
private void legacyRecordBlockChange(BlockPos pos, BlockState newState, int flags, CallbackInfoReturnable<BlockState> cir) {
|
||||||
|
if (!SLSCarpetSettings.legacyExploitMode || !SLSCarpetSettings.legacyFallingBlockReplace) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (newState == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LegacyBlockChangeRecorder.record(pos, newState);
|
||||||
|
}
|
||||||
|
}
|
||||||
+70
@@ -0,0 +1,70 @@
|
|||||||
|
package com.github.zly2006.carpetslsaddition.util;
|
||||||
|
|
||||||
|
import com.github.zly2006.carpetslsaddition.ServerMain;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [旧版] 方块变更记录器——用于重建 1.12 的"替换窗口"。
|
||||||
|
*
|
||||||
|
* <p>1.12 的窗口依赖"异步线程在主线程 tick 中途改块",26.1.2 单线程模型下物理不存在。
|
||||||
|
* 本类改用"记录+回放":把最近发生的方块变更记录在此,
|
||||||
|
* 当重力方块触发下落时,若该位置"刚被改过",就用记录的新方块作为下落实体的内容——
|
||||||
|
* 等价于 1.12 的窗口替换效果(只是窗口由 mod 主动开启)。
|
||||||
|
*
|
||||||
|
* <p>记录以"服务器 tick 数"为界:仅保留最近 {@link #WINDOW_TICKS} 个 tick 内的变更,
|
||||||
|
* 超过即失效,避免记录无限增长或误伤无关方块。
|
||||||
|
*/
|
||||||
|
public final class LegacyBlockChangeRecorder {
|
||||||
|
|
||||||
|
private LegacyBlockChangeRecorder() {}
|
||||||
|
|
||||||
|
/** 窗口大小:变更与下落之间允许的最大 tick 间隔。 */
|
||||||
|
public static final long WINDOW_TICKS = 2L;
|
||||||
|
|
||||||
|
private record Change(BlockState state, long tick) {}
|
||||||
|
|
||||||
|
private static final Map<Long, Change> RECENT = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/** 获取当前服务器 tick(拿不到时回退为 0)。 */
|
||||||
|
private static long now() {
|
||||||
|
MinecraftServer server = ServerMain.server;
|
||||||
|
return server != null ? server.getTickCount() : 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 记录一次方块变更。 */
|
||||||
|
public static void record(BlockPos pos, BlockState newState) {
|
||||||
|
RECENT.put(pos.asLong(), new Change(newState, now()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询某位置是否在窗口期内有变更记录。
|
||||||
|
*
|
||||||
|
* @return 若命中窗口,返回记录的新状态;否则返回 null。
|
||||||
|
*/
|
||||||
|
public static BlockState getRecentChange(BlockPos pos) {
|
||||||
|
Change c = RECENT.get(pos.asLong());
|
||||||
|
if (c == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (now() - c.tick() > WINDOW_TICKS) {
|
||||||
|
RECENT.remove(pos.asLong());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return c.state();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 清理过期记录。 */
|
||||||
|
public static void cleanup() {
|
||||||
|
long t = now();
|
||||||
|
RECENT.entrySet().removeIf(e -> t - e.getValue().tick() > WINDOW_TICKS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clear() {
|
||||||
|
RECENT.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
"legacy.FallingBlockEntityMixin",
|
"legacy.FallingBlockEntityMixin",
|
||||||
"legacy.ScheduledTickMixin",
|
"legacy.ScheduledTickMixin",
|
||||||
"legacy.ChunkMapMixin",
|
"legacy.ChunkMapMixin",
|
||||||
|
"legacy.LevelChunkMixin",
|
||||||
"entity.MixinArmadilloEntity",
|
"entity.MixinArmadilloEntity",
|
||||||
"entity.MixinArmorStandEntity",
|
"entity.MixinArmorStandEntity",
|
||||||
"entity.MixinBoatEntity",
|
"entity.MixinBoatEntity",
|
||||||
|
|||||||
Reference in new issue
Block a user