Skip to content

Commit

Permalink
Allow block entity data to be updated with block placement
Browse files Browse the repository at this point in the history
  • Loading branch information
Niels-NTG committed Apr 26, 2023
1 parent b078f6d commit 8589b19
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 34 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# GDMC-HTTP 1.2.3 (Minecraft 1.19.2)

- FIX: With `PUT /blocks`, allow for changing the block entity (NBT) data of a block even if the target block matches the block state and block ID of the placement instruction. This makes it possible to do things such as changing the text on an existing sign or changing the items of an already placed chest.
- FIX: Reworked the algorithm for changing the shape of a block to fit with directly adjacent blocks (eg. fences) to be more efficient.

# GDMC-HTTP 1.2.2 (Minecraft 1.19.2)

- FIX: Ensure blocks placed via `POST /structure` always update on the client side to reflect its block entity data (eg. text on signs, pieces of armor on armor stands, etc.). Prior to this fix the data was correctly parsed, but only became visible in-game if the relevant chunks were reloaded.
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ plugins {
id 'net.minecraftforge.gradle' version '5.1.+'
}

version = '1.2.2'
version = '1.2.3'
group = 'com.nilsgawlik.gdmchttp' // http://maven.apache.org/guides/mini/guide-naming-conventions.html
archivesBaseName = 'gdmchttp'

Expand Down
73 changes: 40 additions & 33 deletions src/main/java/com/gdmc/httpinterfacemod/handlers/BlocksHandler.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.gdmc.httpinterfacemod.handlers;

import com.gdmc.httpinterfacemod.utils.TagComparator;
import com.google.gson.*;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
Expand All @@ -11,14 +12,11 @@
import net.minecraft.commands.arguments.coordinates.BlockPosArgument;
import net.minecraft.commands.arguments.coordinates.Coordinates;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.Registry;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.TagParser;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.Clearable;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.Block;
Expand Down Expand Up @@ -179,23 +177,24 @@ private JsonArray putBlocksHandler(InputStream requestBody) {
}
}

// If data field is present in JsonObject serialize to to a string so it can be parsed to a CompoundTag to set as NBT block entity data
// for this block placement.
String blockNBTString = "";
if (blockPlacementItem.has("data") && blockPlacementItem.get("data").isJsonPrimitive()) {
blockNBTString = blockPlacementItem.get("data").getAsString();
}

// Pass block Id and block state string into a Stringreader with the the block state parser.
BlockStateParser.BlockResult parsedBlockState = BlockStateParser.parseForBlock(
getBlockRegisteryLookup(commandSourceStack),
new StringReader(blockId + blockStateString),
new StringReader(blockId + blockStateString + blockNBTString),
true
);
BlockState blockState = parsedBlockState.blockState();

// If data field is present in JsonObject serialize to to a string so it can be parsed to a CompoundTag to set as NBT block entity data
// for this block placement.
CompoundTag compoundTag = null;
if (blockPlacementItem.has("data") && blockPlacementItem.get("data").isJsonPrimitive()) {
compoundTag = TagParser.parseTag(blockPlacementItem.get("data").getAsString());
}
CompoundTag compoundTag = parsedBlockState.nbt();

// Attempt to place block in the world.
boolean isSuccess = setBlock(blockPos, blockState, compoundTag, blockFlags) == 1;
boolean isSuccess = setBlock(blockPos, blockState, compoundTag, blockFlags);
returnValues.add(instructionStatus(isSuccess));

} catch (CommandSyntaxException e) {
Expand Down Expand Up @@ -302,44 +301,52 @@ private static String getBlockStateStringFromJSONObject(JsonObject json) {

/**
* @param pos Position in the world the block should be placed.
* @param blockState Contains both the state as well as the material of the block.
* @param inputBlockState Contains both the state as well as the material of the block.
* @param blockEntityData Optional tag of NBT data to be associated with the block (eg. contents of a chest).
* @param flags Block placement flags (see {@link #getBlockFlags(boolean, boolean)} and {@link Block} for more information).
* @return return 1 if block has been placed or 0 if it couldn't be placed at the given location.
*/
private int setBlock(BlockPos pos, BlockState blockState, CompoundTag blockEntityData, int flags) {
ServerLevel serverLevel = getServerLevel(dimension);
private boolean setBlock(BlockPos pos, BlockState inputBlockState, CompoundTag blockEntityData, int flags) {
boolean result = false;

BlockEntity blockEntitytoClear = serverLevel.getBlockEntity(pos);
Clearable.tryClear(blockEntitytoClear);
ServerLevel serverLevel = getServerLevel(dimension);

if (serverLevel.setBlock(pos, blockState, flags)) {
if (blockEntityData != null) {
BlockEntity existingBlockEntity = serverLevel.getExistingBlockEntity(pos);
if (existingBlockEntity != null) {
existingBlockEntity.deserializeNBT(blockEntityData);
}
// If block placement flags allow for updating neighbouring blocks, update the shape neighbouring blocks
// in the north, west, south, east, up, down directions.
BlockState blockState = inputBlockState;
if ((flags & Block.UPDATE_NEIGHBORS) != 0) {
blockState = Block.updateFromNeighbourShapes(inputBlockState, serverLevel, pos);
if (blockState.isAir()) {
blockState = inputBlockState;
}
}

if (serverLevel.setBlock(pos, blockState, flags)) {
// If block placement flags allow for updating the shape of placed blocks, resolve placing the block as if it was placed by a player.
// This is applicable to certain blocks that form an object larger than just a single block, such as doors and beds.
if ((flags & Block.UPDATE_KNOWN_SHAPE) == 0) {
blockState.getBlock().setPlacedBy(serverLevel, pos, blockState, blockPlaceEntity, new ItemStack(blockState.getBlock().asItem()));
}
result = true;
}

// If block placement flags allow for updating neighbouring blocks, update the shape neighbouring blocks
// in the north, west, south, east, up, down directions.
if ((flags & Block.UPDATE_NEIGHBORS) != 0) {
for (Direction direction : Direction.values()) {
BlockPos neighbourPosition = pos.relative(direction);
BlockState neighbourBlockState = serverLevel.getBlockState(neighbourPosition);
neighbourBlockState.updateNeighbourShapes(serverLevel, neighbourPosition, flags);
if (blockEntityData != null) {
BlockEntity existingBlockEntity = serverLevel.getExistingBlockEntity(pos);
if (existingBlockEntity != null) {
if (TagComparator.contains(existingBlockEntity.serializeNBT(), blockEntityData)) {
return result;
}
existingBlockEntity.deserializeNBT(blockEntityData);
serverLevel.markAndNotifyBlock(
pos, serverLevel.getChunkAt(pos),
serverLevel.getBlockState(pos), existingBlockEntity.getBlockState(),
flags, Block.UPDATE_LIMIT
);
return true;
}
return 1;
} else {
return 0;
}

return result;
}

/**
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/com/gdmc/httpinterfacemod/utils/TagComparator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.gdmc.httpinterfacemod.utils;

import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;

public class TagComparator {

public static boolean contains(CompoundTag existingCompound, CompoundTag newCompound) {
if (existingCompound == newCompound) {
return true;
}

if (existingCompound.isEmpty() != newCompound.isEmpty()) {
return false;
}

for (String newCompoundKey : newCompound.getAllKeys()) {
Tag existingTag = existingCompound.get(newCompoundKey);
if (existingTag == null) {
return false;
}
if (!existingTag.equals(newCompound.get(newCompoundKey))) {
return false;
}
}

return true;
}

}

0 comments on commit 8589b19

Please sign in to comment.