Remesh chunks on the client when changing the blockstate

And slim down the code a bit
This commit is contained in:
quat1024 2023-06-15 22:50:41 -04:00
parent 871c3ac54e
commit f2a60f4e8e
5 changed files with 45 additions and 37 deletions

View File

@ -14,7 +14,6 @@ Template blocks can be placed in the world, then right-clicked with a full-size
# quat was here - TODO
* Schedule chunk rerenders when a player edits a template block :sweat_smile:
* Re-generalize the model system (I removed a layer of indirection while rewriting it, so it's just slopes now)
* See what I can do about using the vanilla rotation system (`ModelBakeSettings.getRotation`) instead of manually rotating the `Mesh`
* A simplification of the mesh system would *definitely* reduce the friction of adding new meshes

View File

@ -1,27 +1,34 @@
package io.github.cottonmc.templates;
import io.github.cottonmc.templates.block.SlopeBlock;
import io.github.cottonmc.templates.block.entity.SlopeEntity;
import io.github.cottonmc.templates.block.entity.TemplateEntity;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import java.util.function.BiConsumer;
public class Templates implements ModInitializer {
public static final String MODID = "templates";
public static final Block SLOPE = Registry.register(Registries.BLOCK, id("slope"), new SlopeBlock());
public static final BlockEntityType<SlopeEntity> SLOPE_ENTITY = Registry.register(
Registries.BLOCK_ENTITY_TYPE,
id("slope"),
FabricBlockEntityTypeBuilder.create(SlopeEntity::new, SLOPE).build(null)
public static final BlockEntityType<TemplateEntity> SLOPE_ENTITY = Registry.register(
Registries.BLOCK_ENTITY_TYPE, id("slope"),
FabricBlockEntityTypeBuilder.create(Templates::makeSlopeEntity, SLOPE).build(null)
);
//Overridden in TemplatesClient
public static BiConsumer<World, BlockPos> chunkRerenderProxy = (world, pos) -> {};
@Override
public void onInitialize() {
Registry.register(Registries.ITEM, id("slope"), (Item) new BlockItem(SLOPE, new Item.Settings()));
@ -30,4 +37,9 @@ public class Templates implements ModInitializer {
public static Identifier id(String path) {
return new Identifier(MODID, path);
}
//simply for breaking the circular reference in the SLOPE_ENTITY constructor call
private static TemplateEntity makeSlopeEntity(BlockPos pos, BlockState state) {
return new TemplateEntity(SLOPE_ENTITY, pos, state);
}
}

View File

@ -6,7 +6,9 @@ import io.github.cottonmc.templates.model.TemplateModelVariantProvider;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.util.math.ChunkSectionPos;
import net.minecraft.util.math.Direction;
public class TemplatesClient implements ClientModInitializer {
@ -14,6 +16,16 @@ public class TemplatesClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
Templates.chunkRerenderProxy = (world, pos) -> {
if(world == MinecraftClient.getInstance().world) {
MinecraftClient.getInstance().worldRenderer.scheduleBlockRender(
ChunkSectionPos.getSectionCoord(pos.getX()),
ChunkSectionPos.getSectionCoord(pos.getY()),
ChunkSectionPos.getSectionCoord(pos.getZ())
);
}
};
ModelLoadingRegistry.INSTANCE.registerVariantProvider(rm -> provider);
provider.registerTemplateModels2(Templates.SLOPE, Templates.SLOPE.getDefaultState().with(SlopeBlock.FACING, Direction.SOUTH), SlopeUnbakedModel::new);

View File

@ -1,11 +0,0 @@
package io.github.cottonmc.templates.block.entity;
import io.github.cottonmc.templates.Templates;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
public class SlopeEntity extends TemplateEntity {
public SlopeEntity(BlockPos pos, BlockState state) {
super(Templates.SLOPE_ENTITY, pos, state, Templates.SLOPE);
}
}

View File

@ -1,46 +1,45 @@
package io.github.cottonmc.templates.block.entity;
import net.fabricmc.fabric.api.networking.v1.PlayerLookup;
import io.github.cottonmc.templates.Templates;
import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachmentBlockEntity;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtHelper;
import net.minecraft.network.listener.ClientPlayPacketListener;
import net.minecraft.network.packet.Packet;
import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket;
import net.minecraft.registry.Registries;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public abstract class TemplateEntity extends BlockEntity implements RenderAttachmentBlockEntity {
public class TemplateEntity extends BlockEntity implements RenderAttachmentBlockEntity {
protected BlockState renderedState = Blocks.AIR.getDefaultState();
protected boolean glowstone = false;
protected boolean redstone = false;
private final Block baseBlock;
public TemplateEntity(BlockEntityType<?> type, BlockPos pos, BlockState state, Block baseBlock) {
public TemplateEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state);
this.baseBlock = baseBlock;
}
@Override
public void readNbt(NbtCompound tag) {
super.readNbt(tag);
BlockState lastRenderedState = renderedState;
renderedState = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), tag.getCompound("BlockState"));
glowstone = tag.getBoolean("Glowstone");
redstone = tag.getBoolean("Redstone");
if(world != null && world.isClient) {
//TODO probably unsafe, i think the method was removed in 1.14.4 or something though
// i cant find any relevant method that takes only 1 blockpos argument
((ClientWorld) world).scheduleBlockRenders(pos.getX(), pos.getY(), pos.getZ());
//world.scheduleBlockRender(pos);
//Force a chunk remesh on the client, if the displayed blockstate has changed
if(world != null && world.isClient && !Objects.equals(lastRenderedState, renderedState)) {
Templates.chunkRerenderProxy.accept(world, pos);
}
}
@ -70,13 +69,7 @@ public abstract class TemplateEntity extends BlockEntity implements RenderAttach
public void change() {
markDirty();
if(world != null && !world.isClient) {
//for(ServerPlayerEntity player : PlayerLookup.tracking(this)) player.networkHandler.sendPacket(this.toUpdatePacket());
//TODO is this needed
//world.updateNeighborsAlways(pos.offset(Direction.UP), baseBlock);
world.updateListeners(pos, getCachedState(), getCachedState(), 3);
}
if(world instanceof ServerWorld sworld) sworld.getChunkManager().markForUpdate(pos); //dispatch to clients
}
public BlockState getRenderedState() {
@ -106,6 +99,9 @@ public abstract class TemplateEntity extends BlockEntity implements RenderAttach
public void setRedstone(boolean newRedstone) {
boolean lastRedstone = redstone;
redstone = newRedstone;
if(lastRedstone != newRedstone) change();
if(lastRedstone != newRedstone) {
world.updateNeighbors(pos, getCachedState().getBlock());
change();
}
}
}