Skip to content

Commit

Permalink
fix(sample-site): fixed sample site desync on block break
Browse files Browse the repository at this point in the history
- Fixed desync on multiple players waiting for sample site placement
- Improved Payload Codec to include chunk position
- Added BlockEvents handler
- Added helper classes for complex data management and mutations over final records
  • Loading branch information
D4RKAR117 committed Jun 8, 2024
1 parent b4a23d8 commit 60ccadd
Show file tree
Hide file tree
Showing 8 changed files with 321 additions and 122 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ mod_name=Cog Works Engineering
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
mod_license=PolyFormPerimeter-1.0
# The mod version. See https://semver.org/
mod_version=0.4.2
mod_version=0.4.3
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
# This should match the base package used for the mod sources.
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
Expand Down
73 changes: 73 additions & 0 deletions src/main/java/org/darkar/cog_works/block/events/BlockEvents.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package org.darkar.cog_works.block.events;

import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.event.level.BlockEvent;
import net.neoforged.neoforge.network.PacketDistributor;
import org.darkar.cog_works.level.chunk.attachment.ChunkSampleSiteMap;
import org.darkar.cog_works.net.payload.client.ClientSampleSiteMapUpdatePayload;
import org.darkar.cog_works.utils.Data.AttachmentUtils.ChunkSampleSiteMapUtil;

import java.util.Optional;

import static org.darkar.cog_works.CogWorks.MOD_ID;
import static org.darkar.cog_works.Registry.DataAttachments.CHUNK_SAMPLE_SITE_MAP;

@EventBusSubscriber(modid = MOD_ID)
public class BlockEvents {

/**
* Handles the logic to update the {@link ChunkSampleSiteMap} when a block is broken and is a sample site.
*
* @param levelAccessor level where the block is broken
* @param pos position of the block that was broken
* @param player player that broke the block
*/
private static void handleChunkSampleSiteMap(LevelAccessor levelAccessor, BlockPos pos, Player player) {
ChunkAccess chunk = levelAccessor.getChunk(pos);
Level level = player.level();
if (level.isClientSide()) return;
Optional<ClientSampleSiteMapUpdatePayload> payload = Optional.empty();
if (!chunk.hasAttachments() || !chunk.hasData(CHUNK_SAMPLE_SITE_MAP)) return;
ChunkSampleSiteMap chunkSampleSiteMap = chunk.getData(CHUNK_SAMPLE_SITE_MAP);
if (!ChunkSampleSiteMapUtil.isDirty(chunkSampleSiteMap)) return;

if (ChunkSampleSiteMapUtil.isSurface(chunkSampleSiteMap, pos)) {
ChunkSampleSiteMap newChunkSampleSiteMap = ChunkSampleSiteMapUtil.resetDefaultSurface(chunkSampleSiteMap);
chunk.setData(CHUNK_SAMPLE_SITE_MAP, newChunkSampleSiteMap);
payload = Optional.of(new ClientSampleSiteMapUpdatePayload(chunk.getPos(),
newChunkSampleSiteMap));

}

if (ChunkSampleSiteMapUtil.isDeep(chunkSampleSiteMap, pos)) {
ChunkSampleSiteMap newChunkSampleSiteMap = ChunkSampleSiteMapUtil.resetDefaultDeep(chunkSampleSiteMap);
chunk.setData(CHUNK_SAMPLE_SITE_MAP, newChunkSampleSiteMap);
payload = Optional.of(new ClientSampleSiteMapUpdatePayload(chunk.getPos(),
newChunkSampleSiteMap));
}


payload.ifPresent(updatePayload -> {
PacketDistributor.sendToPlayersTrackingChunk((ServerLevel) level, chunk.getPos(), updatePayload);
});

}

@SubscribeEvent
public static void onBlockBreak(final BlockEvent.BreakEvent event) {
Player player = event.getPlayer();
LevelAccessor level = event.getLevel();
BlockPos breakPos = event.getPos();

if (!level.isClientSide()) {
handleChunkSampleSiteMap(level, breakPos, player);
}
}
}
36 changes: 15 additions & 21 deletions src/main/java/org/darkar/cog_works/item/ProspectingPickItem.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import net.minecraft.core.Direction;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.tags.BlockTags;
import net.minecraft.tags.TagKey;
import net.minecraft.world.InteractionHand;
Expand Down Expand Up @@ -144,10 +143,9 @@ public void handleLeftClickBlock(Player player, Level level, BlockPos pos, Direc
player.getCooldowns().addCooldown(this, COOLDOWN_TICKS);
if (needsCollection) {
String message = String.format(
"This chunk already has a surface sampling site. Please collect the surface sample at" +
" %s" +
" before placing a new one",
chunkSampleSiteMap.surfacePos());
"This chunk already has a surface sampling site. Please collect the surface sample " +
"at" +
" %s" + " before placing a new one", chunkSampleSiteMap.surfacePos());
player.sendSystemMessage(Component.literal(message));
return;
}
Expand All @@ -159,28 +157,26 @@ public void handleLeftClickBlock(Player player, Level level, BlockPos pos, Direc

chunkSampleSiteMap = new ChunkSampleSiteMap(pos, chunkSampleSiteMap.deepPos(),
surfaceSiteState,
chunkSampleSiteMap.deepState(),
face, chunkSampleSiteMap.deepFace());
chunkSampleSiteMap.deepState(), face,
chunkSampleSiteMap.deepFace());
chunk.setData(CHUNK_SAMPLE_SITE_MAP, chunkSampleSiteMap);
PacketDistributor.sendToPlayer((ServerPlayer) player, new ClientSampleSiteMapUpdatePayload(
chunkSampleSiteMap.surfacePos(), chunkSampleSiteMap.deepPos(),
chunkSampleSiteMap.surfaceState(), chunkSampleSiteMap.deepState(),
chunkSampleSiteMap.surfaceFace(), chunkSampleSiteMap.deepFace()));
ClientSampleSiteMapUpdatePayload payload = new ClientSampleSiteMapUpdatePayload(chunk.getPos(),
chunkSampleSiteMap);
PacketDistributor.sendToPlayersTrackingChunk(serverLevel, chunk.getPos(), payload);
}

case DEEP -> {
boolean needsCollection = !chunkSampleSiteMap.deepPos().equals(BlockPos.ZERO) &&
(!chunkSampleSiteMap.deepState().isAir() &&
serverLevel.getBlockState(chunkSampleSiteMap.deepPos()).is(
tagsPerRegion.get(SampleSiteRegion.DEEP)));
(!chunkSampleSiteMap.deepState().isAir() && serverLevel.getBlockState(
chunkSampleSiteMap.deepPos()).is(
tagsPerRegion.get(SampleSiteRegion.DEEP)));

itemStack.set(IS_DIGGING_SAMPLE, isDiggingSample);
player.getCooldowns().addCooldown(this, COOLDOWN_TICKS);
if (needsCollection) {
String message = String.format(
"This chunk already has a deep sampling site. Please collect the deep sample at %s " +
"before placing a new one",
chunkSampleSiteMap.deepPos());
"before placing a new one", chunkSampleSiteMap.deepPos());
player.sendSystemMessage(Component.literal(message));
return;
}
Expand All @@ -192,11 +188,9 @@ public void handleLeftClickBlock(Player player, Level level, BlockPos pos, Direc
chunkSampleSiteMap.surfaceState(), deepSiteState,
chunkSampleSiteMap.surfaceFace(), face);
chunk.setData(CHUNK_SAMPLE_SITE_MAP, chunkSampleSiteMap);
PacketDistributor.sendToPlayer((ServerPlayer) player, new ClientSampleSiteMapUpdatePayload(
chunkSampleSiteMap.surfacePos(), chunkSampleSiteMap.deepPos(),
chunkSampleSiteMap.surfaceState(), chunkSampleSiteMap.deepState(),
chunkSampleSiteMap.surfaceFace(), chunkSampleSiteMap.deepFace()));

ClientSampleSiteMapUpdatePayload payload = new ClientSampleSiteMapUpdatePayload(chunk.getPos(),
chunkSampleSiteMap);
PacketDistributor.sendToPlayersTrackingChunk(serverLevel, chunk.getPos(), payload);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@ public static void onChunkSent(final ChunkWatchEvent.Sent event) {
ChunkPos pos = chunk.getPos();
if (chunk.hasAttachments() && chunk.hasData(CHUNK_SAMPLE_SITE_MAP)) {
ChunkSampleSiteMap chunkSampleSiteMap = chunk.getData(CHUNK_SAMPLE_SITE_MAP);
ClientSampleSiteMapUpdatePayload payload = new ClientSampleSiteMapUpdatePayload(
chunkSampleSiteMap.surfacePos(), chunkSampleSiteMap.deepPos(), chunkSampleSiteMap.surfaceState(),
chunkSampleSiteMap.deepState(), chunkSampleSiteMap.surfaceFace(), chunkSampleSiteMap.deepFace()
);
ClientSampleSiteMapUpdatePayload payload = new ClientSampleSiteMapUpdatePayload(pos, chunkSampleSiteMap);
PacketDistributor.sendToPlayersTrackingChunk(serverLevel, pos, payload);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,18 @@
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.inventory.InventoryMenu;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.neoforged.neoforge.client.NeoForgeRenderTypes;
import org.darkar.cog_works.level.chunk.attachment.ChunkSampleSiteMap;
import org.darkar.cog_works.utils.Data;
import org.darkar.cog_works.utils.World;
import org.joml.Matrix4f;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static org.darkar.cog_works.CogWorks.MOD_ID;
import static org.darkar.cog_works.Registry.DataAttachments.CHUNK_SAMPLE_SITE_MAP;
Expand All @@ -42,62 +45,65 @@ public static void render(Frustum frustum, PoseStack poseStack, Camera cam) {
LocalPlayer player = minecraft.player;

BlockPos currentPos = player.getOnPos();
LevelChunk chunk = level.getChunkAt(currentPos);
ChunkAccess chunk = level.getChunk(currentPos);

List<LevelChunk> nearbyChunks = World.getNearbyChunks(chunk, 2);
RenderBuffers buffers = minecraft.renderBuffers();
MultiBufferSource.BufferSource source = buffers.bufferSource();
Vec3 camPos = cam.getPosition();
RenderType bufferType = NeoForgeRenderTypes.TRANSLUCENT_ON_PARTICLES_TARGET.get();
Map<ChunkPos, ChunkSampleSiteMap> mapsToRender = World.getNearbyChunks(chunk, level, 4).filter(
nearbyChunk -> nearbyChunk.hasAttachments() && nearbyChunk.hasData(CHUNK_SAMPLE_SITE_MAP) &&
Data.AttachmentUtils.ChunkSampleSiteMapUtil.isDirty(
nearbyChunk.getData(CHUNK_SAMPLE_SITE_MAP))).collect(
Collectors.toMap(ChunkAccess::getPos, mapChunk -> mapChunk.getData(CHUNK_SAMPLE_SITE_MAP)));

nearbyChunks.stream().filter(
nearbyChunk -> nearbyChunk.hasAttachments() && nearbyChunk.hasData(CHUNK_SAMPLE_SITE_MAP)).forEach(
nearbyChunk -> {

ChunkSampleSiteMap chunkSampleSiteMap = nearbyChunk.getData(CHUNK_SAMPLE_SITE_MAP);
boolean isSurfaceVisible = frustum.isVisible(new AABB(chunkSampleSiteMap.surfacePos()));
boolean isDeepVisible = frustum.isVisible(new AABB(chunkSampleSiteMap.deepPos()));
if (!isSurfaceVisible && !isDeepVisible) {
return;
}

BlockPos pos = isSurfaceVisible ? chunkSampleSiteMap.surfacePos() : chunkSampleSiteMap.deepPos();
Direction face = isSurfaceVisible ? chunkSampleSiteMap.surfaceFace() : chunkSampleSiteMap.deepFace();
Vec3 offset = Vec3.atLowerCornerOf(pos).subtract(camPos);

poseStack.pushPose();
poseStack.translate(offset.x, offset.y, offset.z);
poseStack.translate(.5F, .5F, .5F);
switch (face) {
case UP -> poseStack.mulPose(Axis.XP.rotationDegrees(90F));
case DOWN -> poseStack.mulPose(Axis.XN.rotationDegrees(90F));
default -> poseStack.mulPose(Axis.YN.rotationDegrees((face.toYRot() + 180F) % 360F));
}
poseStack.translate(-.5F, -.5F, -.5F);
VertexConsumer textureConsumer = source.getBuffer(bufferType);
TextureAtlasSprite texture = minecraft.getTextureAtlas(InventoryMenu.BLOCK_ATLAS).apply(
SAMPLE_SITE_TEXTURE_KEY);

Matrix4f quadMatrix = poseStack.last().pose();
textureConsumer.vertex(quadMatrix, 0, 1, -0.01F).color(255, 255, 255, 255).uv(texture.getU0(),
texture.getV0())
.overlayCoords(OverlayTexture.NO_OVERLAY).uv2(LightTexture.FULL_BRIGHT).normal(
poseStack.last(), 0, 0, -1).endVertex();
textureConsumer.vertex(quadMatrix, 1, 1, -0.01F).color(255, 255, 255, 255).uv(texture.getU0(),
texture.getV1())
.overlayCoords(OverlayTexture.NO_OVERLAY).uv2(LightTexture.FULL_BRIGHT).normal(
poseStack.last(), 0, 0, -1).endVertex();
textureConsumer.vertex(quadMatrix, 1, 0, -0.01F).color(255, 255, 255, 255).uv(texture.getU1(),
texture.getV1())
.overlayCoords(OverlayTexture.NO_OVERLAY).uv2(LightTexture.FULL_BRIGHT).normal(
poseStack.last(), 0, 0, -1).endVertex();
textureConsumer.vertex(quadMatrix, 0, 0, -0.01F).color(255, 255, 255, 255).uv(texture.getU1(),
texture.getV0())
.overlayCoords(OverlayTexture.NO_OVERLAY).uv2(LightTexture.FULL_BRIGHT).normal(
poseStack.last(), 0, 0, -1).endVertex();
source.endBatch(bufferType);
poseStack.popPose();
});
for (ChunkSampleSiteMap chunkSampleSiteMap : mapsToRender.values()) {
boolean isSurfaceVisible = frustum.isVisible(new AABB(chunkSampleSiteMap.surfacePos())) &&
!chunkSampleSiteMap.surfacePos().equals(BlockPos.ZERO);
boolean isDeepVisible = frustum.isVisible(new AABB(chunkSampleSiteMap.deepPos())) &&
!chunkSampleSiteMap.deepPos().equals(BlockPos.ZERO);

if (!isSurfaceVisible && !isDeepVisible) {
return;
}

BlockPos pos = isSurfaceVisible ? chunkSampleSiteMap.surfacePos() : chunkSampleSiteMap.deepPos();
Direction face = isSurfaceVisible ? chunkSampleSiteMap.surfaceFace() : chunkSampleSiteMap.deepFace();
Vec3 offset = Vec3.atLowerCornerOf(pos).subtract(camPos);

poseStack.pushPose();
poseStack.translate(offset.x, offset.y, offset.z);
poseStack.translate(.5F, .5F, .5F);
switch (face) {
case UP -> poseStack.mulPose(Axis.XP.rotationDegrees(90F));
case DOWN -> poseStack.mulPose(Axis.XN.rotationDegrees(90F));
default -> poseStack.mulPose(Axis.YN.rotationDegrees((face.toYRot() + 180F) % 360F));
}
poseStack.translate(-.5F, -.5F, -.5F);
VertexConsumer textureConsumer = source.getBuffer(bufferType);
TextureAtlasSprite texture = minecraft.getTextureAtlas(InventoryMenu.BLOCK_ATLAS).apply(
SAMPLE_SITE_TEXTURE_KEY);

Matrix4f quadMatrix = poseStack.last().pose();
textureConsumer.vertex(quadMatrix, 0, 1, -0.01F).color(255, 255, 255, 255).uv(texture.getU0(),
texture.getV0())
.overlayCoords(OverlayTexture.NO_OVERLAY).uv2(LightTexture.FULL_BRIGHT).normal(
poseStack.last(), 0, 0, -1).endVertex();
textureConsumer.vertex(quadMatrix, 1, 1, -0.01F).color(255, 255, 255, 255).uv(texture.getU0(),
texture.getV1())
.overlayCoords(OverlayTexture.NO_OVERLAY).uv2(LightTexture.FULL_BRIGHT).normal(
poseStack.last(), 0, 0, -1).endVertex();
textureConsumer.vertex(quadMatrix, 1, 0, -0.01F).color(255, 255, 255, 255).uv(texture.getU1(),
texture.getV1())
.overlayCoords(OverlayTexture.NO_OVERLAY).uv2(LightTexture.FULL_BRIGHT).normal(
poseStack.last(), 0, 0, -1).endVertex();
textureConsumer.vertex(quadMatrix, 0, 0, -0.01F).color(255, 255, 255, 255).uv(texture.getU1(),
texture.getV0())
.overlayCoords(OverlayTexture.NO_OVERLAY).uv2(LightTexture.FULL_BRIGHT).normal(
poseStack.last(), 0, 0, -1).endVertex();
source.endBatch(bufferType);
poseStack.popPose();
}


}
Expand Down
Loading

0 comments on commit 60ccadd

Please sign in to comment.