Mind the gap

- Fixed trapdoors and doors causing a contraption to flicker when activated
- Hastily added a few ticks of extra time before a contraption is discarded on the client to close the time gap between entity and chunk render
This commit is contained in:
simibubi 2022-06-20 00:50:05 +02:00
parent d1570736c5
commit 56ec2f127c
7 changed files with 44 additions and 25 deletions

View file

@ -90,6 +90,15 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
protected boolean prevPosInvalid;
private boolean skipActorStop;
/*
* staleTicks are a band-aid to prevent a frame or two of missing blocks between
* contraption discard and off-thread block placement on disassembly
*
* FIXME this timeout should be longer but then also cancelled early based on a
* chunk rebuild listener
*/
public int staleTicks = 3;
public AbstractContraptionEntity(EntityType<?> entityTypeIn, Level worldIn) {
super(entityTypeIn, worldIn);
prevPosInvalid = true;
@ -309,6 +318,14 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
tickContraption();
super.tick();
if (level.isClientSide())
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
if (!contraption.deferInvalidate)
return;
contraption.deferInvalidate = false;
ContraptionRenderDispatcher.invalidate(contraption);
});
if (!(level instanceof ServerLevelAccessor sl))
return;
@ -695,7 +712,7 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
StructureBlockInfo info = contraption.blocks.get(localPos);
contraption.blocks.put(localPos, new StructureBlockInfo(info.pos, newState, info.nbt));
if (info.state != newState && !(newState.getBlock() instanceof SlidingDoorBlock))
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> ContraptionRenderDispatcher.invalidate(contraption));
contraption.deferInvalidate = true;
contraption.invalidateColliders();
}
@ -851,4 +868,8 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
return initialized;
}
public boolean isAliveOrStale() {
return isAlive() || level.isClientSide() ? staleTicks > 0 : false;
}
}

View file

@ -151,6 +151,7 @@ public abstract class Contraption {
public List<BlockEntity> specialRenderedTileEntities;
protected ContraptionWorld world;
public boolean deferInvalidate;
public Contraption() {
blocks = new HashMap<>();

View file

@ -45,10 +45,15 @@ public class ContraptionHandler {
for (Iterator<WeakReference<AbstractContraptionEntity>> iterator = values.iterator(); iterator.hasNext();) {
WeakReference<AbstractContraptionEntity> weakReference = iterator.next();
AbstractContraptionEntity contraptionEntity = weakReference.get();
if (contraptionEntity == null || !contraptionEntity.isAlive()) {
if (contraptionEntity == null || !contraptionEntity.isAliveOrStale()) {
iterator.remove();
continue;
}
if (!contraptionEntity.isAlive()) {
contraptionEntity.staleTicks--;
continue;
}
ContraptionCollider.collideEntities(contraptionEntity);
}
}

View file

@ -2,16 +2,11 @@ package com.simibubi.create.content.contraptions.components.structureMovement;
import org.apache.commons.lang3.tuple.MutablePair;
import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionRenderDispatcher;
import net.minecraft.core.BlockPos;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.DistExecutor;
public abstract class MovingInteractionBehaviour {
@ -22,7 +17,7 @@ public abstract class MovingInteractionBehaviour {
contraptionEntity.contraption.actors.remove(index);
contraptionEntity.contraption.actors.add(index, MutablePair.of(info, ctx));
if (contraptionEntity.level.isClientSide)
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> invalidate(contraptionEntity.contraption));
contraptionEntity.contraption.deferInvalidate = true;
}
protected void setContraptionBlockData(AbstractContraptionEntity contraptionEntity, BlockPos pos,
@ -32,11 +27,6 @@ public abstract class MovingInteractionBehaviour {
contraptionEntity.setBlock(pos, info);
}
@OnlyIn(Dist.CLIENT)
protected void invalidate(Contraption contraption) {
ContraptionRenderDispatcher.invalidate(contraption);
}
public boolean handlePlayerInteraction(Player player, InteractionHand activeHand, BlockPos localPos,
AbstractContraptionEntity contraptionEntity) {
return true;

View file

@ -26,7 +26,7 @@ public class ContraptionEntityRenderer<C extends AbstractContraptionEntity> exte
double cameraZ) {
if (entity.getContraption() == null)
return false;
if (!entity.isAlive())
if (!entity.isAliveOrStale())
return false;
if (!entity.isReadyForRender())
return false;

View file

@ -26,7 +26,7 @@ public class ContraptionRenderInfo {
}
public boolean isDead() {
return !contraption.entity.isAlive();
return !contraption.entity.isAliveOrStale();
}
public void beginFrame(BeginFrameEvent event) {
@ -34,11 +34,13 @@ public class ContraptionRenderInfo {
AbstractContraptionEntity entity = contraption.entity;
visible = event.getFrustum().isVisible(entity.getBoundingBoxForCulling().inflate(2));
visible = event.getFrustum()
.isVisible(entity.getBoundingBoxForCulling()
.inflate(2));
}
public boolean isVisible() {
return visible && contraption.entity.isAlive() && contraption.entity.isReadyForRender();
return visible && contraption.entity.isAliveOrStale() && contraption.entity.isReadyForRender();
}
/**

View file

@ -762,7 +762,7 @@ public class Carriage {
@OnlyIn(Dist.CLIENT)
private void invalidate(CarriageContraptionEntity entity) {
ContraptionRenderDispatcher.invalidate(entity.getContraption());
entity.getContraption().deferInvalidate = true;
entity.updateRenderedPortalCutoff();
}