Implement all remaining block components

This commit is contained in:
Joshua Castle 2023-01-15 04:24:52 -08:00
parent 3d1d51d198
commit bfdebadacc
No known key found for this signature in database
GPG key ID: F674F38216C35D5D
6 changed files with 249 additions and 1 deletions

View file

@ -26,6 +26,7 @@
package org.geysermc.geyser.api.block.custom.component;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.api.block.custom.component.placementfilter.PlacementFilter;
import java.util.Map;
import java.util.Set;
@ -74,6 +75,15 @@ public interface CustomBlockComponents {
*/
@NonNull Map<String, MaterialInstance> materialInstances();
/**
* Gets the placement filter component
* Equivalent to "minecraft:material_instances"
*
* @return The placement filter.
*/
PlacementFilter placementFilter();
/**
* Gets the destructible by mining component
* Equivalent to "minecraft:destructible_by_mining"
@ -114,6 +124,14 @@ public interface CustomBlockComponents {
*/
RotationComponent rotation();
/**
* Gets the unit cube component
* Equivalent to "minecraft:unit_cube"
*
* @return The rotation.
*/
boolean unitCube();
/**
* Gets if the block should place only air
* Equivalent to setting a dummy event to run on "minecraft:on_player_placing"
@ -141,6 +159,8 @@ public interface CustomBlockComponents {
Builder materialInstance(@NonNull String name, @NonNull MaterialInstance materialInstance);
Builder placementFilter(PlacementFilter placementFilter);
Builder destructibleByMining(Float destructibleByMining);
Builder friction(Float friction);
@ -151,6 +171,8 @@ public interface CustomBlockComponents {
Builder rotation(RotationComponent rotation);
Builder unitCube(boolean unitCube);
Builder placeAir(boolean placeAir);
Builder tags(Set<String> tags);

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.block.custom.component.placementfilter;
import java.util.LinkedHashMap;
import java.util.Set;
import org.checkerframework.checker.nullness.qual.NonNull;
/*
* This class is used to store conditions for a placement filter for a custom block.
*/
public record Conditions(@NonNull Set<Face> allowedFaces, @NonNull LinkedHashMap<String, BlockFilterType> blockFilters) {
public enum Face {
DOWN(1),
UP(2),
NORTH(4),
SOUTH(8),
WEST(16),
EAST(32);
private final byte value;
Face(int value) {
this.value = (byte) value;
}
public byte getValue() {
return value;
}
}
public enum BlockFilterType {
BLOCK,
TAG
}
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.api.block.custom.component.placementfilter;
import java.util.List;
import org.checkerframework.checker.nullness.qual.NonNull;
/*
* This class is used to store a placement filter for a custom block.
*/
public record PlacementFilter(@NonNull List<Conditions> conditions) {
}

View file

@ -34,6 +34,7 @@ import org.geysermc.geyser.api.block.custom.component.BoxComponent;
import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents;
import org.geysermc.geyser.api.block.custom.component.MaterialInstance;
import org.geysermc.geyser.api.block.custom.component.RotationComponent;
import org.geysermc.geyser.api.block.custom.component.placementfilter.PlacementFilter;
import org.jetbrains.annotations.NotNull;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
@ -49,11 +50,13 @@ public class GeyserCustomBlockComponents implements CustomBlockComponents {
String displayName;
String geometry;
Map<String, MaterialInstance> materialInstances;
PlacementFilter placementFilter;
Float destructibleByMining;
Float friction;
Integer lightEmission;
Integer lightDampening;
RotationComponent rotation;
boolean unitCube;
boolean placeAir;
Set<String> tags;
@ -67,11 +70,13 @@ public class GeyserCustomBlockComponents implements CustomBlockComponents {
} else {
this.materialInstances = Object2ObjectMaps.unmodifiable(new Object2ObjectArrayMap<>(builder.materialInstances));
}
this.placementFilter = builder.placementFilter;
this.destructibleByMining = builder.destructibleByMining;
this.friction = builder.friction;
this.lightEmission = builder.lightEmission;
this.lightDampening = builder.lightDampening;
this.rotation = builder.rotation;
this.unitCube = builder.unitCube;
this.placeAir = builder.placeAir;
if (builder.tags.isEmpty()) {
this.tags = Set.of();
@ -105,6 +110,11 @@ public class GeyserCustomBlockComponents implements CustomBlockComponents {
return materialInstances;
}
@Override
public PlacementFilter placementFilter() {
return placementFilter;
}
@Override
public Float destructibleByMining() {
return destructibleByMining;
@ -130,6 +140,11 @@ public class GeyserCustomBlockComponents implements CustomBlockComponents {
return rotation;
}
@Override
public boolean unitCube() {
return unitCube;
}
@Override
public boolean placeAir() {
return placeAir;
@ -146,11 +161,13 @@ public class GeyserCustomBlockComponents implements CustomBlockComponents {
protected String displayName;
protected String geometry;
protected final Object2ObjectMap<String, MaterialInstance> materialInstances = new Object2ObjectOpenHashMap<>();
protected PlacementFilter placementFilter;
protected Float destructibleByMining;
protected Float friction;
protected Integer lightEmission;
protected Integer lightDampening;
protected RotationComponent rotation;
protected boolean unitCube = false;
protected boolean placeAir = false;
protected final Set<String> tags = new HashSet<>();
@ -204,6 +221,12 @@ public class GeyserCustomBlockComponents implements CustomBlockComponents {
return this;
}
@Override
public Builder placementFilter(PlacementFilter placementFilter) {
this.placementFilter = placementFilter;
return this;
}
@Override
public Builder destructibleByMining(Float destructibleByMining) {
if (destructibleByMining != null && destructibleByMining < 0) {
@ -255,6 +278,12 @@ public class GeyserCustomBlockComponents implements CustomBlockComponents {
return this;
}
@Override
public Builder unitCube(boolean unitCube) {
this.unitCube = unitCube;
return this;
}
@Override
public Builder placeAir(boolean placeAir) {
this.placeAir = placeAir;

View file

@ -37,6 +37,10 @@ import org.geysermc.geyser.api.block.custom.component.BoxComponent;
import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents;
import org.geysermc.geyser.api.block.custom.component.MaterialInstance;
import org.geysermc.geyser.api.block.custom.component.RotationComponent;
import org.geysermc.geyser.api.block.custom.component.placementfilter.Conditions;
import org.geysermc.geyser.api.block.custom.component.placementfilter.PlacementFilter;
import org.geysermc.geyser.api.block.custom.component.placementfilter.Conditions.BlockFilterType;
import org.geysermc.geyser.api.block.custom.component.placementfilter.Conditions.Face;
import org.geysermc.geyser.api.item.custom.CustomItemData;
import org.geysermc.geyser.api.item.custom.CustomItemOptions;
import org.geysermc.geyser.item.exception.InvalidCustomMappingsFileException;
@ -56,6 +60,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@ -347,9 +352,11 @@ public class MappingsReader_v1 extends MappingsReader {
builder.geometry(node.get("geometry").asText());
}
String displayName = name;
if (node.has("display_name")) {
builder.displayName(node.get("display_name").asText());
displayName = node.get("display_name").asText();
}
builder.displayName(displayName);
if (node.has("light_emission")) {
builder.lightEmission(node.get("light_emission").asInt());
@ -373,6 +380,10 @@ public class MappingsReader_v1 extends MappingsReader {
builder.rotation(new RotationComponent(rotationX, rotationY, rotationZ));
}
if (node.has("unit_cube")) {
builder.unitCube(node.get("unit_cube").asBoolean());
}
if (node.has("material_instances")) {
JsonNode materialInstances = node.get("material_instances");
if (materialInstances.isObject()) {
@ -387,6 +398,19 @@ public class MappingsReader_v1 extends MappingsReader {
}
}
if (node.has("placement_filter")) {
JsonNode placementFilter = node.get("placement_filter");
if (placementFilter.isObject()) {
if (placementFilter.has("conditions")) {
JsonNode conditions = placementFilter.get("conditions");
if (conditions.isArray()) {
PlacementFilter filter = createPlacementFilterComponent(conditions);
builder.placementFilter(filter);
}
}
}
}
if (node.has("tags")) {
JsonNode tags = node.get("tags");
if (tags.isArray()) {
@ -446,6 +470,43 @@ public class MappingsReader_v1 extends MappingsReader {
return new MaterialInstance(texture, renderMethod, faceDimming, ambientOcclusion);
}
private PlacementFilter createPlacementFilterComponent(JsonNode node) {
List<Conditions> conditions = new ArrayList<>();
node.forEach(condition -> {
Set<Face> faces = new HashSet<>();
if (condition.has("allowed_faces")) {
JsonNode allowedFaces = condition.get("allowed_faces");
if (allowedFaces.isArray()) {
allowedFaces.forEach(face -> {
faces.add(Face.valueOf(face.asText().toUpperCase()));
});
}
}
LinkedHashMap<String, BlockFilterType> blockFilters = new LinkedHashMap<>();
if (condition.has("block_filter")) {
JsonNode blockFilter = condition.get("block_filter");
if (blockFilter.isArray()) {
blockFilter.forEach(filter -> {
if (filter.isObject()) {
if (filter.has("tags")) {
JsonNode tags = filter.get("tags");
blockFilters.put(tags.asText(), BlockFilterType.TAG);
}
} else if (filter.isTextual()) {
blockFilters.put(filter.asText(), BlockFilterType.BLOCK);
}
});
}
}
conditions.add(new Conditions(faces, blockFilters));
});
return new PlacementFilter(conditions);
}
private String createCustomBlockPropertyQuery(String state) {
String[] conditions = splitStateString(state);
String[] queries = new String[conditions.length];

View file

@ -15,6 +15,8 @@ import org.geysermc.geyser.api.block.custom.CustomBlockState;
import org.geysermc.geyser.api.block.custom.component.BoxComponent;
import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents;
import org.geysermc.geyser.api.block.custom.component.MaterialInstance;
import org.geysermc.geyser.api.block.custom.component.placementfilter.PlacementFilter;
import org.geysermc.geyser.api.block.custom.component.placementfilter.Conditions.Face;
import org.geysermc.geyser.api.block.custom.property.CustomBlockProperty;
import org.geysermc.geyser.api.block.custom.property.PropertyType;
import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCustomBlocksEvent;
@ -176,6 +178,11 @@ public class CustomBlockRegistryPopulator {
return NbtMap.EMPTY;
}
NbtMapBuilder builder = NbtMap.builder();
if (components.displayName() != null) {
builder.putCompound("minecraft:display_name", NbtMap.builder()
.putString("value", components.displayName())
.build());
}
if (components.selectionBox() != null) {
builder.putCompound("minecraft:selection_box", convertBox(components.selectionBox()));
}
@ -203,6 +210,11 @@ public class CustomBlockRegistryPopulator {
.putCompound("materials", materialsBuilder.build())
.build());
}
if (components.placementFilter() != null) {
builder.putCompound("minecraft:placement_filter", NbtMap.builder()
.putList("conditions", NbtType.COMPOUND, convertPlacementFilter(components.placementFilter()))
.build());
}
if (components.destructibleByMining() != null) {
builder.putCompound("minecraft:destructible_by_mining", NbtMap.builder()
.putFloat("value", components.destructibleByMining())
@ -230,6 +242,9 @@ public class CustomBlockRegistryPopulator {
.putFloat("z", components.rotation().z())
.build());
}
if (components.unitCube()) {
builder.putCompound("minecraft:unit_cube", NbtMap.EMPTY);
}
if (components.placeAir()) {
builder.putCompound("minecraft:on_player_placing", NbtMap.builder()
.putString("triggerType", "geyser:place_event")
@ -248,4 +263,29 @@ public class CustomBlockRegistryPopulator {
.putList("size", NbtType.FLOAT, boxComponent.sizeX(), boxComponent.sizeY(), boxComponent.sizeZ())
.build();
}
private static List<NbtMap> convertPlacementFilter(PlacementFilter placementFilter) {
List<NbtMap> conditions = new ArrayList<>();
placementFilter.conditions().forEach((condition) -> {
NbtMapBuilder conditionBuilder = NbtMap.builder();
byte allowedFaces = 0;
for (Face face : condition.allowedFaces()) { allowedFaces |= face.getValue(); }
conditionBuilder.putByte("allowed_faces", allowedFaces);
List <NbtMap> blockFilters = new ArrayList<>();
condition.blockFilters().forEach((value, type) -> {
NbtMapBuilder blockFilterBuilder = NbtMap.builder();
switch (type) {
case BLOCK -> blockFilterBuilder.putString("name", value);
case TAG -> blockFilterBuilder.putString("tags", value).putInt("tags_version", 6);
}
blockFilters.add(blockFilterBuilder.build());
});
conditionBuilder.putList("block_filters", NbtType.COMPOUND, blockFilters);
conditions.add(conditionBuilder.build());
});
return conditions;
}
}