Skip to content

Commit

Permalink
Compute difference between server and client to fix ghost blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
Yeregorix committed Jan 29, 2022
1 parent 9667c2f commit 4ad10e8
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -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<BlockPos> getBlocksToRefresh();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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<Boolean> cir, BlockPos head, PistonStructureResolver resolver) {
if (level instanceof ServerLevel) {
ServerChunkCache cache = ((ServerLevel) level).getChunkSource();
for (BlockPos pos : ((InternalStructureResolver) resolver).getBlocksToRefresh())
cache.blockChanged(pos);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,38 +32,59 @@
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;
import org.spongepowered.api.util.Direction;
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<BlockPos> toRefresh = new HashSet<>();
@Final
private Level level;
@Shadow
private Level level;
@Mutable
@Final
private List<BlockPos> toPush;
@Shadow
private List<BlockPos> toPush;
@Mutable
@Final
@Shadow
private List<BlockPos> 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<Boolean> cir) {
if (this.resolveCustom) {
SuperPistonTimings.CALCULATION.startTiming();
cir.setReturnValue(resolveCustom());
SuperPistonTimings.CALCULATION.stopTiming();
}
}

@Inject(method = "<init>", at = @At("RETURN"))
public void onInit(Level level, BlockPos pos, net.minecraft.core.Direction pistonDirection, boolean extending, CallbackInfo ci) {
Expand All @@ -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();

Expand Down Expand Up @@ -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<BlockPos> toPushCustom = this.toPush;
List<BlockPos> 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<BlockPos> getBlocksToRefresh() {
return this.toRefresh;
}
}

0 comments on commit 4ad10e8

Please sign in to comment.