diff --git a/README.md b/README.md index ba00abd..19d97ed 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/src/main/java/io/github/cottonmc/templates/Templates.java b/src/main/java/io/github/cottonmc/templates/Templates.java index 2ab341d..51f51ea 100644 --- a/src/main/java/io/github/cottonmc/templates/Templates.java +++ b/src/main/java/io/github/cottonmc/templates/Templates.java @@ -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 SLOPE_ENTITY = Registry.register( - Registries.BLOCK_ENTITY_TYPE, - id("slope"), - FabricBlockEntityTypeBuilder.create(SlopeEntity::new, SLOPE).build(null) + public static final BlockEntityType SLOPE_ENTITY = Registry.register( + Registries.BLOCK_ENTITY_TYPE, id("slope"), + FabricBlockEntityTypeBuilder.create(Templates::makeSlopeEntity, SLOPE).build(null) ); + //Overridden in TemplatesClient + public static BiConsumer 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); + } } diff --git a/src/main/java/io/github/cottonmc/templates/TemplatesClient.java b/src/main/java/io/github/cottonmc/templates/TemplatesClient.java index e105852..e6a14d9 100644 --- a/src/main/java/io/github/cottonmc/templates/TemplatesClient.java +++ b/src/main/java/io/github/cottonmc/templates/TemplatesClient.java @@ -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); diff --git a/src/main/java/io/github/cottonmc/templates/block/entity/SlopeEntity.java b/src/main/java/io/github/cottonmc/templates/block/entity/SlopeEntity.java deleted file mode 100644 index e4cbd7c..0000000 --- a/src/main/java/io/github/cottonmc/templates/block/entity/SlopeEntity.java +++ /dev/null @@ -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); - } -} diff --git a/src/main/java/io/github/cottonmc/templates/block/entity/TemplateEntity.java b/src/main/java/io/github/cottonmc/templates/block/entity/TemplateEntity.java index a572f60..abdf717 100644 --- a/src/main/java/io/github/cottonmc/templates/block/entity/TemplateEntity.java +++ b/src/main/java/io/github/cottonmc/templates/block/entity/TemplateEntity.java @@ -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(); + } } }