Skip to content

Commit

Permalink
Improve StructureTemplate
Browse files Browse the repository at this point in the history
- Implement Iterable<Pair<BlockPos, BlockStatePredicate>> for StructureTemplate
- Convert all fields to getters and introduce getters for more fields

Signed-off-by: solonovamax <[email protected]>
  • Loading branch information
solonovamax committed Jun 29, 2024
1 parent 2d66848 commit 8ee713a
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public void draw(OwoUIDrawContext context, int mouseX, int mouseY, float partial
var entityBuffers = client.getBufferBuilders().getEntityVertexConsumers();

float scale = Math.min(this.width, this.height);
scale /= Math.max(structure.xSize, Math.max(structure.ySize, structure.zSize));
scale /= Math.max(this.structure.xSize(), Math.max(this.structure.ySize(), this.structure.zSize()));
scale /= 1.625f;

var matrices = context.getMatrices();
Expand All @@ -69,7 +69,7 @@ public void draw(OwoUIDrawContext context, int mouseX, int mouseY, float partial

matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(this.displayAngle));
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(this.rotation));
matrices.translate(this.structure.xSize / -2f, this.structure.ySize / -2f, this.structure.zSize / -2f);
matrices.translate(this.structure.xSize() / -2.0f, this.structure.ySize() / -2.0f, this.structure.zSize() / -2.0f);

RenderSystem.runAsFancy(() -> {
structure.forEachPredicate((blockPos, predicate) -> {
Expand All @@ -95,7 +95,7 @@ public void draw(OwoUIDrawContext context, int mouseX, int mouseY, float partial
});

if (this.placeable) {
if (StructureOverlayRenderer.isShowingOverlay(this.structure.id)) {
if (StructureOverlayRenderer.isShowingOverlay(this.structure.id())) {
context.drawText(client.textRenderer, Text.translatable("text.lavender.structure_component.active_overlay_hint"), this.x + this.width - 5 - client.textRenderer.getWidth("⚓"), this.y + this.height - 9 - 5, 0, false);
this.tooltip(Text.translatable("text.lavender.structure_component.hide_hint"));
} else {
Expand All @@ -109,11 +109,11 @@ public boolean onMouseDown(double mouseX, double mouseY, int button) {
var result = super.onMouseDown(mouseX, mouseY, button);
if (!this.placeable || button != GLFW.GLFW_MOUSE_BUTTON_LEFT || !Screen.hasShiftDown()) return result;

if (StructureOverlayRenderer.isShowingOverlay(this.structure.id)) {
StructureOverlayRenderer.removeAllOverlays(this.structure.id);
if (StructureOverlayRenderer.isShowingOverlay(this.structure.id())) {
StructureOverlayRenderer.removeAllOverlays(this.structure.id());
} else {
StructureOverlayRenderer.addPendingOverlay(this.structure.id);
StructureOverlayRenderer.restrictVisibleLayer(this.structure.id, this.visibleLayer);
StructureOverlayRenderer.addPendingOverlay(this.structure.id());
StructureOverlayRenderer.restrictVisibleLayer(this.structure.id(), this.visibleLayer);

MinecraftClient.getInstance().setScreen(null);
}
Expand All @@ -138,7 +138,7 @@ public boolean canFocus(FocusSource source) {
}

public StructureComponent visibleLayer(int visibleLayer) {
StructureOverlayRenderer.restrictVisibleLayer(this.structure.id, visibleLayer);
StructureOverlayRenderer.restrictVisibleLayer(this.structure.id(), visibleLayer);

this.visibleLayer = visibleLayer;
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,8 +283,8 @@ private static Vec3i getPendingOffset(StructureTemplate structure) {
return switch (PENDING_OVERLAY.rotation) {
case NONE -> new Vec3i(-structure.anchor().getX(), -structure.anchor().getY(), -structure.anchor().getZ());
case CLOCKWISE_90 -> new Vec3i(-structure.anchor().getZ(), -structure.anchor().getY(), -structure.anchor().getX());
case CLOCKWISE_180 -> new Vec3i(-structure.xSize + structure.anchor.getX() + 1, -structure.anchor().getY(), -structure.zSize + structure.anchor.getZ() + 1);
case COUNTERCLOCKWISE_90 -> new Vec3i(-structure.zSize + structure.anchor.getZ() + 1, -structure.anchor().getY(), -structure.xSize + structure.anchor.getX() + 1);
case CLOCKWISE_180 -> new Vec3i(-structure.xSize() + structure.anchor().getX() + 1, -structure.anchor().getY(), -structure.zSize() + structure.anchor().getZ() + 1);
case COUNTERCLOCKWISE_90 -> new Vec3i(-structure.zSize() + structure.anchor().getZ() + 1, -structure.anchor().getY(), -structure.xSize() + structure.anchor().getX() + 1);
};
// @formatter:on
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,23 +108,23 @@ public StructureNode(StructureTemplate structure, int angle, boolean placeable)
protected void visitStart(MarkdownCompiler<?> compiler) {
var structureComponent = StructureFeature.this.bookComponentSource.builtinTemplate(
ParentComponent.class,
this.structure.ySize > 1 ? "structure-preview-with-layers" : "structure-preview",
Map.of("structure", this.structure.id.toString(), "angle", String.valueOf(this.angle))
this.structure.ySize() > 1 ? "structure-preview-with-layers" : "structure-preview",
Map.of("structure", this.structure.id().toString(), "angle", String.valueOf(this.angle))
);

var structurePreview = structureComponent.childById(StructureComponent.class, "structure").placeable(this.placeable);
var layerSlider = structureComponent.childById(SlimSliderComponent.class, "layer-slider");

if (layerSlider != null) {
layerSlider.max(0).min(this.structure.ySize).tooltipSupplier(layer -> {
layerSlider.max(0).min(this.structure.ySize()).tooltipSupplier(layer -> {
return layer > 0
? Text.translatable("text.lavender.structure_component.layer_tooltip", layer.intValue())
: Text.translatable("text.lavender.structure_component.all_layers_tooltip");
}).onChanged().subscribe(layer -> {
structurePreview.visibleLayer((int) layer - 1);
});

layerSlider.value(StructureOverlayRenderer.getLayerRestriction(this.structure.id) + 1);
layerSlider.value(StructureOverlayRenderer.getLayerRestriction(this.structure.id()) + 1);
}

((OwoUICompiler) compiler).visitComponent(structureComponent);
Expand Down
144 changes: 125 additions & 19 deletions src/main/java/io/wispforest/lavender/structure/StructureTemplate.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.wispforest.lavender.structure;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
Expand Down Expand Up @@ -28,17 +29,22 @@
import net.minecraft.world.biome.ColorResolver;
import net.minecraft.world.chunk.light.LightingProvider;
import org.apache.commons.lang3.mutable.MutableInt;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.Nullable;

import java.util.Arrays;
import java.util.EnumMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.StreamSupport;

public class StructureTemplate {
public class StructureTemplate implements Iterable<Pair<BlockPos, BlockStatePredicate>> {

private static final char AIR_BLOCKSTATE_KEY = '_';
private static final char NULL_BLOCKSTATE_KEY = ' ';
Expand Down Expand Up @@ -78,6 +84,30 @@ public int predicatesOfType(BlockStatePredicate.MatchCategory type) {
return this.predicateCountByType.get(type).intValue();
}

public Identifier id() {
return this.id;
}

public BlockStatePredicate[][][] predicates() {
return this.predicates;
}

public EnumMap<BlockStatePredicate.MatchCategory, MutableInt> predicateCountByType() {
return this.predicateCountByType;
}

public int xSize() {
return this.xSize;
}

public int ySize() {
return this.ySize;
}

public int zSize() {
return this.zSize;
}

/**
* @return The anchor position of this template,
* to be used when placing in the world
Expand Down Expand Up @@ -116,6 +146,25 @@ public void forEachPredicate(BiConsumer<BlockPos, BlockStatePredicate> action, B
}
}

@Override
public Iterator<Pair<BlockPos, BlockStatePredicate>> iterator() {
return iterator(BlockRotation.NONE);
}

@Override
public void forEach(Consumer<? super Pair<BlockPos, BlockStatePredicate>> action) {
var mutablePair = new MutablePair<BlockPos, BlockStatePredicate>();
forEachPredicate((pos, predicate) -> {
mutablePair.setLeft(pos);
mutablePair.setRight(predicate);
action.accept(mutablePair);
});
}

public Iterator<Pair<BlockPos, BlockStatePredicate>> iterator(BlockRotation rotation) {
return new StructureTemplateIterator(this, rotation);
}

// --- validation ---

/**
Expand Down Expand Up @@ -263,27 +312,12 @@ public static StructureTemplate parse(Identifier resourceId, JsonObject json) {
private static Char2ObjectOpenHashMap<BlockStatePredicate> buildStructureKeysMap(JsonObject keyObject) {
var keys = new Char2ObjectOpenHashMap<BlockStatePredicate>();
for (var entry : keyObject.entrySet()) {
char key;
if (entry.getKey().length() == 1) {
key = entry.getKey().charAt(0);
if (key == ANCHOR_BLOCKSTATE_KEY) {
throw new JsonParseException("Key '#' is reserved for 'anchor' declarations. Rename the key to 'anchor' and use '#' in the structure definition.");
} else if (key == AIR_BLOCKSTATE_KEY) {
throw new JsonParseException("Key '_' is a reserved key for marking a block that must be AIR.");
} else if (key == NULL_BLOCKSTATE_KEY) {
throw new JsonParseException("Key ' ' is a reserved key for marking a block that can be anything.");
}
} else if ("anchor".equals(entry.getKey())) {
key = ANCHOR_BLOCKSTATE_KEY;
} else {
throw new JsonParseException("Keys should only be a single character or should be 'anchor'.");
}
char key = blockstateKeyForEntry(entry);

if (keys.containsKey(key)) {
throw new JsonParseException("Keys can only appear once. Key '" + key + "' appears twice.");
throw new JsonParseException("Keys can only appear once. Key '%s' appears twice.".formatted(key));
}


if (entry.getValue().isJsonArray()) {
JsonArray blockStringsArray = entry.getValue().getAsJsonArray();
var blockStatePredicates = StreamSupport.stream(blockStringsArray.spliterator(), false)
Expand All @@ -296,9 +330,29 @@ private static Char2ObjectOpenHashMap<BlockStatePredicate> buildStructureKeysMap
throw new JsonParseException("The values for the map of key-to-blocks must either be a string or an array of strings.");
}
}

return keys;
}

private static char blockstateKeyForEntry(final Map.Entry<String, JsonElement> entry) {
char key;
if (entry.getKey().length() == 1) {
key = entry.getKey().charAt(0);
if (key == ANCHOR_BLOCKSTATE_KEY) {
throw new JsonParseException("Key '#' is reserved for 'anchor' declarations. Rename the key to 'anchor' and use '#' in the structure definition.");
} else if (key == AIR_BLOCKSTATE_KEY) {
throw new JsonParseException("Key '_' is a reserved key for marking a block that must be AIR.");
} else if (key == NULL_BLOCKSTATE_KEY) {
throw new JsonParseException("Key ' ' is a reserved key for marking a block that can be anything.");
}
} else if ("anchor".equals(entry.getKey())) {
key = ANCHOR_BLOCKSTATE_KEY;
} else {
throw new JsonParseException("Keys should only be a single character or should be 'anchor'.");
}
return key;
}

private static BlockStatePredicate parseStringToBlockStatePredicate(String blockOrTag) {
try {
var result = BlockArgumentParser.blockOrTag(Registries.BLOCK.getReadOnlyWrapper(), blockOrTag, false);
Expand Down Expand Up @@ -453,7 +507,7 @@ public BlockState getBlockState(BlockPos pos) {
pos.getY() < 0 || pos.getY() >= this.template.ySize ||
pos.getZ() < 0 || pos.getZ() >= this.template.zSize)
return Blocks.AIR.getDefaultState();
return this.template.predicates[pos.getX()][pos.getY()][pos.getZ()].preview();
return this.template.predicates()[pos.getX()][pos.getY()][pos.getZ()].preview();
}

@Override
Expand All @@ -471,4 +525,56 @@ public int getBottomY() {
return this.world.getBottomY();
}
}

private static final class StructureTemplateIterator implements Iterator<Pair<BlockPos, BlockStatePredicate>> {

private final StructureTemplate template;

private final BlockPos.Mutable currentPos = new BlockPos.Mutable();

private final MutablePair<BlockPos, BlockStatePredicate> currentElement = new MutablePair<>();

private final BlockRotation rotation;

private int posX = 0, posY = 0, posZ = 0;

private StructureTemplateIterator(StructureTemplate template, BlockRotation rotation) {
this.template = template;
this.rotation = rotation;
}

@Override
public boolean hasNext() {
return this.posX < this.template.xSize() - 1 && this.posY < this.template.ySize() - 1 && this.posZ < this.template.zSize() - 1;
}

@Override
public Pair<BlockPos, BlockStatePredicate> next() {
if (!hasNext()) {
throw new NoSuchElementException();
}

switch (this.rotation) {
case CLOCKWISE_90 -> this.currentPos.set(this.template.zSize() - this.posZ - 1, this.posY, this.posX);
case COUNTERCLOCKWISE_90 -> this.currentPos.set(this.posZ, this.posY, this.template.xSize() - this.posX - 1);
case CLOCKWISE_180 ->
this.currentPos.set(this.template.xSize() - this.posX - 1, this.posY, this.template.zSize() - this.posZ - 1);
default -> this.currentPos.set(this.posX, this.posY, this.posZ);
}

this.currentElement.setRight(this.template.predicates()[this.posX][this.posY][this.posZ]);
this.currentElement.setLeft(this.currentPos);

// Advance to next position
if (++this.posZ >= this.template.zSize()) {
this.posZ = 0;
if (++this.posY >= this.template.ySize()) {
this.posY = 0;
++this.posX;
}
}

return this.currentElement;
}
}
}

0 comments on commit 8ee713a

Please sign in to comment.