diff --git a/src/main/java/net/smoofyuniverse/superpiston/impl/internal/InternalStructureResolver.java b/src/main/java/net/smoofyuniverse/superpiston/impl/internal/InternalStructureResolver.java new file mode 100644 index 0000000..1d5c380 --- /dev/null +++ b/src/main/java/net/smoofyuniverse/superpiston/impl/internal/InternalStructureResolver.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018-2022 Hugo Dupanloup (Yeregorix) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.smoofyuniverse.superpiston.impl.internal; + +import net.minecraft.core.BlockPos; + +import java.util.Set; + +public interface InternalStructureResolver { + + void resolveBlocksToRefresh(); + + Set getBlocksToRefresh(); +} diff --git a/src/main/java/net/smoofyuniverse/superpiston/mixin/block/PistonBaseBlockMixin.java b/src/main/java/net/smoofyuniverse/superpiston/mixin/block/PistonBaseBlockMixin.java index d70ef44..8d655d8 100644 --- a/src/main/java/net/smoofyuniverse/superpiston/mixin/block/PistonBaseBlockMixin.java +++ b/src/main/java/net/smoofyuniverse/superpiston/mixin/block/PistonBaseBlockMixin.java @@ -24,13 +24,20 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.piston.PistonBaseBlock; +import net.minecraft.world.level.block.piston.PistonStructureResolver; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.PushReaction; +import net.smoofyuniverse.superpiston.impl.internal.InternalStructureResolver; 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.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; @Mixin(PistonBaseBlock.class) public abstract class PistonBaseBlockMixin { @@ -44,4 +51,23 @@ public PushReaction alwaysPushNormal(BlockState stateIn, BlockState state, Level public boolean alwaysPushable(BlockState state, Level level, BlockPos pos, Direction facing, boolean destroy, Direction facing2) { return true; } + + @Redirect(method = "moveBlocks", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/piston/PistonStructureResolver;resolve()Z")) + public boolean onMoveResolve(PistonStructureResolver resolver) { + if (resolver.resolve()) { + ((InternalStructureResolver) resolver).resolveBlocksToRefresh(); + return true; + } + return false; + } + + @Inject(method = "moveBlocks", at = @At(value = "RETURN"), locals = LocalCapture.CAPTURE_FAILHARD) + public void onMoveEnd(Level level, BlockPos piston, Direction facing, boolean extending, + CallbackInfoReturnable cir, BlockPos head, PistonStructureResolver resolver) { + if (level instanceof ServerLevel) { + ServerChunkCache cache = ((ServerLevel) level).getChunkSource(); + for (BlockPos pos : ((InternalStructureResolver) resolver).getBlocksToRefresh()) + cache.blockChanged(pos); + } + } } diff --git a/src/main/java/net/smoofyuniverse/superpiston/mixin/block/PistonStructureResolverMixin.java b/src/main/java/net/smoofyuniverse/superpiston/mixin/block/PistonStructureResolverMixin.java index c3a040d..7768ca7 100644 --- a/src/main/java/net/smoofyuniverse/superpiston/mixin/block/PistonStructureResolverMixin.java +++ b/src/main/java/net/smoofyuniverse/superpiston/mixin/block/PistonStructureResolverMixin.java @@ -32,6 +32,7 @@ import net.smoofyuniverse.superpiston.api.structure.calculator.PistonStructureCalculator; import net.smoofyuniverse.superpiston.impl.event.PostStructureCalculationEvent; import net.smoofyuniverse.superpiston.impl.event.PreStructureCalculationEvent; +import net.smoofyuniverse.superpiston.impl.internal.InternalStructureResolver; import org.spongepowered.api.Sponge; import org.spongepowered.api.block.BlockSnapshot; import org.spongepowered.api.event.Cause; @@ -39,31 +40,51 @@ import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.common.util.Constants; import org.spongepowered.common.util.VecHelper; import org.spongepowered.math.vector.Vector3i; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; @Mixin(PistonStructureResolver.class) -public class PistonStructureResolverMixin { - @Shadow +public abstract class PistonStructureResolverMixin implements InternalStructureResolver { + private final Set toRefresh = new HashSet<>(); @Final - private Level level; @Shadow + private Level level; + @Mutable @Final - private List toPush; @Shadow + private List toPush; + @Mutable @Final + @Shadow private List toDestroy; + @Final + @Shadow + private net.minecraft.core.Direction pushDirection; private Direction direction, movement; private BlockSnapshot piston; + private boolean resolveCustom = true; + + @Inject(method = "resolve", at = @At("HEAD"), cancellable = true) + public void onResolve(CallbackInfoReturnable cir) { + if (this.resolveCustom) { + SuperPistonTimings.CALCULATION.startTiming(); + cir.setReturnValue(resolveCustom()); + SuperPistonTimings.CALCULATION.stopTiming(); + } + } @Inject(method = "", at = @At("RETURN")) public void onInit(Level level, BlockPos pos, net.minecraft.core.Direction pistonDirection, boolean extending, CallbackInfo ci) { @@ -72,21 +93,10 @@ public void onInit(Level level, BlockPos pos, net.minecraft.core.Direction pisto this.movement = extending ? this.direction : this.direction.opposite(); } - /** - * @author Yeregorix - */ - @Overwrite - public boolean resolve() { - SuperPistonTimings.CALCULATION.startTiming(); - boolean r = calculate(); - SuperPistonTimings.CALCULATION.stopTiming(); - - return r; - } - - public boolean calculate() { + private boolean resolveCustom() { this.toPush.clear(); this.toDestroy.clear(); + this.toRefresh.clear(); Cause cause = Sponge.server().causeStackManager().currentCause(); @@ -127,4 +137,52 @@ public boolean calculate() { return structure.isMoveable(); } + + private void resolveVanilla() { + this.resolveCustom = false; + resolve(); + this.resolveCustom = true; + } + + @Shadow + public abstract boolean resolve(); + + @Override + public void resolveBlocksToRefresh() { + this.toRefresh.clear(); + + // Backup custom blocks + List toPushCustom = this.toPush; + List toDestroyCustom = this.toDestroy; + this.toPush = new ArrayList<>(); + this.toDestroy = new ArrayList<>(); + + // Resolve vanilla blocks + this.resolveCustom = false; + resolve(); + this.resolveCustom = true; + + // Difference between blocks updated by the client and blocks updated by the server + for (BlockPos pos : this.toPush) { + this.toRefresh.add(pos); + this.toRefresh.add(pos.relative(this.pushDirection)); + } + this.toRefresh.addAll(this.toDestroy); + for (BlockPos pos : toPushCustom) { + this.toRefresh.remove(pos); + this.toRefresh.remove(pos.relative(this.pushDirection)); + } + for (BlockPos pos : toDestroyCustom) { + this.toRefresh.remove(pos); + } + + // Restore custom blocks + this.toPush = toPushCustom; + this.toDestroy = toDestroyCustom; + } + + @Override + public Set getBlocksToRefresh() { + return this.toRefresh; + } }