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 # 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) * 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` * 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 * 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; package io.github.cottonmc.templates;
import io.github.cottonmc.templates.block.SlopeBlock; 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.api.ModInitializer;
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder; import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntityType; import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.item.BlockItem; import net.minecraft.item.BlockItem;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.registry.Registries; import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry; import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier; 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 class Templates implements ModInitializer {
public static final String MODID = "templates"; public static final String MODID = "templates";
public static final Block SLOPE = Registry.register(Registries.BLOCK, id("slope"), new SlopeBlock()); public static final Block SLOPE = Registry.register(Registries.BLOCK, id("slope"), new SlopeBlock());
public static final BlockEntityType<SlopeEntity> SLOPE_ENTITY = Registry.register( public static final BlockEntityType<TemplateEntity> SLOPE_ENTITY = Registry.register(
Registries.BLOCK_ENTITY_TYPE, Registries.BLOCK_ENTITY_TYPE, id("slope"),
id("slope"), FabricBlockEntityTypeBuilder.create(Templates::makeSlopeEntity, SLOPE).build(null)
FabricBlockEntityTypeBuilder.create(SlopeEntity::new, SLOPE).build(null)
); );
//Overridden in TemplatesClient
public static BiConsumer<World, BlockPos> chunkRerenderProxy = (world, pos) -> {};
@Override @Override
public void onInitialize() { public void onInitialize() {
Registry.register(Registries.ITEM, id("slope"), (Item) new BlockItem(SLOPE, new Item.Settings())); 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) { public static Identifier id(String path) {
return new Identifier(MODID, 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.api.ClientModInitializer;
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap; import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry; import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.RenderLayer;
import net.minecraft.util.math.ChunkSectionPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
public class TemplatesClient implements ClientModInitializer { public class TemplatesClient implements ClientModInitializer {
@ -14,6 +16,16 @@ public class TemplatesClient implements ClientModInitializer {
@Override @Override
public void onInitializeClient() { 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); ModelLoadingRegistry.INSTANCE.registerVariantProvider(rm -> provider);
provider.registerTemplateModels2(Templates.SLOPE, Templates.SLOPE.getDefaultState().with(SlopeBlock.FACING, Direction.SOUTH), SlopeUnbakedModel::new); 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; 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.fabricmc.fabric.api.rendering.data.v1.RenderAttachmentBlockEntity;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityType; import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtHelper; import net.minecraft.nbt.NbtHelper;
import net.minecraft.network.listener.ClientPlayPacketListener; import net.minecraft.network.listener.ClientPlayPacketListener;
import net.minecraft.network.packet.Packet; import net.minecraft.network.packet.Packet;
import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket; import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket;
import net.minecraft.registry.Registries; import net.minecraft.registry.Registries;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.Objects; 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 BlockState renderedState = Blocks.AIR.getDefaultState();
protected boolean glowstone = false; protected boolean glowstone = false;
protected boolean redstone = 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); super(type, pos, state);
this.baseBlock = baseBlock;
} }
@Override @Override
public void readNbt(NbtCompound tag) { public void readNbt(NbtCompound tag) {
super.readNbt(tag); super.readNbt(tag);
BlockState lastRenderedState = renderedState;
renderedState = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), tag.getCompound("BlockState")); renderedState = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), tag.getCompound("BlockState"));
glowstone = tag.getBoolean("Glowstone"); glowstone = tag.getBoolean("Glowstone");
redstone = tag.getBoolean("Redstone"); 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 //Force a chunk remesh on the client, if the displayed blockstate has changed
// i cant find any relevant method that takes only 1 blockpos argument if(world != null && world.isClient && !Objects.equals(lastRenderedState, renderedState)) {
((ClientWorld) world).scheduleBlockRenders(pos.getX(), pos.getY(), pos.getZ()); Templates.chunkRerenderProxy.accept(world, pos);
//world.scheduleBlockRender(pos);
} }
} }
@ -70,13 +69,7 @@ public abstract class TemplateEntity extends BlockEntity implements RenderAttach
public void change() { public void change() {
markDirty(); markDirty();
if(world != null && !world.isClient) { if(world instanceof ServerWorld sworld) sworld.getChunkManager().markForUpdate(pos); //dispatch to clients
//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);
}
} }
public BlockState getRenderedState() { public BlockState getRenderedState() {
@ -106,6 +99,9 @@ public abstract class TemplateEntity extends BlockEntity implements RenderAttach
public void setRedstone(boolean newRedstone) { public void setRedstone(boolean newRedstone) {
boolean lastRedstone = redstone; boolean lastRedstone = redstone;
redstone = newRedstone; redstone = newRedstone;
if(lastRedstone != newRedstone) change(); if(lastRedstone != newRedstone) {
world.updateNeighbors(pos, getCachedState().getBlock());
change();
}
} }
} }