mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-03-13 11:18:19 +01:00
More work on predicates, I'll probably simplify it later
This commit is contained in:
parent
8ea3c973f3
commit
7888956a36
21 changed files with 609 additions and 90 deletions
|
@ -28,8 +28,11 @@ package org.geysermc.geyser.api.item.custom.v2;
|
|||
import net.kyori.adventure.key.Key;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.GeyserApi;
|
||||
import org.geysermc.geyser.api.item.custom.v2.predicate.CustomItemPredicate;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is used to define a custom item and its properties.
|
||||
*/
|
||||
|
@ -66,12 +69,12 @@ public interface CustomItemDefinition {
|
|||
}
|
||||
|
||||
/**
|
||||
* The predicate that has to match for this item to be used. These predicates are similar to the Java item model predicates.
|
||||
* The predicates that have to match for this item to be used. These predicates are similar to the Java item model predicates.
|
||||
*
|
||||
* <p>If multiple predicates match, then the first registered item with a matching predicate is used. If no predicates match, then the item definition without a predicate
|
||||
* <p>If all predicates match for multiple definitions, then the first registered item with all matching predicates is used. If no predicates match, then the item definition without any predicates
|
||||
* is used, if any.</p>
|
||||
*/
|
||||
void predicate();
|
||||
@NonNull List<CustomItemPredicate<?>> predicates();
|
||||
|
||||
/**
|
||||
* The item's Bedrock options. These describe item properties that can't be described in item components, e.g. item texture size and if the item is allowed in the off-hand.
|
||||
|
@ -105,6 +108,8 @@ public interface CustomItemDefinition {
|
|||
|
||||
Builder displayName(String displayName);
|
||||
|
||||
Builder predicate(@NonNull CustomItemPredicate<?> predicate);
|
||||
|
||||
Builder bedrockOptions(CustomItemBedrockOptions.@NonNull Builder options);
|
||||
|
||||
// TODO do we want another format for this?
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.item.custom.v2.predicate;
|
||||
|
||||
public record CustomItemPredicate<T>(ItemPredicateType<T> type, T data) {
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.item.custom.v2.predicate;
|
||||
|
||||
import org.geysermc.geyser.api.item.custom.v2.predicate.data.ConditionPredicateData;
|
||||
import org.geysermc.geyser.api.item.custom.v2.predicate.data.match.MatchPredicateData;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ItemPredicateType<T> {
|
||||
private static final Map<String, ItemPredicateType<?>> TYPES = new HashMap<>();
|
||||
|
||||
public static final ItemPredicateType<ConditionPredicateData> CONDITION = create("condition");
|
||||
public static final ItemPredicateType<MatchPredicateData<?>> MATCH = create("match");
|
||||
|
||||
public static ItemPredicateType<?> getType(String name) {
|
||||
return TYPES.get(name);
|
||||
}
|
||||
|
||||
private static <T> ItemPredicateType<T> create(String name) {
|
||||
ItemPredicateType<T> type = new ItemPredicateType<>();
|
||||
TYPES.put(name, type);
|
||||
return type;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.item.custom.v2.predicate.data;
|
||||
|
||||
// TODO maybe type should be a generic class with data, but this works for now
|
||||
public record ConditionPredicateData(ConditionProperty property, boolean expected, int index) {
|
||||
|
||||
public ConditionPredicateData(ConditionProperty property, boolean expected) {
|
||||
this(property, expected, 0);
|
||||
}
|
||||
|
||||
// TODO maybe we can extend this
|
||||
public enum ConditionProperty {
|
||||
BROKEN,
|
||||
DAMAGED,
|
||||
CUSTOM_MODEL_DATA
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.item.custom.v2.predicate.data;
|
||||
|
||||
public record CustomModelDataPredicate<T>(T data, int index) {
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.item.custom.v2.predicate.data.match;
|
||||
|
||||
public enum ChargeType {
|
||||
NONE,
|
||||
ARROW,
|
||||
ROCKET
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.item.custom.v2.predicate.data.match;
|
||||
|
||||
public record MatchPredicateData<T>(MatchPredicateProperty<T> property, T data) {
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.item.custom.v2.predicate.data.match;
|
||||
|
||||
import net.kyori.adventure.key.Key;
|
||||
import org.geysermc.geyser.api.item.custom.v2.predicate.data.CustomModelDataPredicate;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
// TODO can we do more?
|
||||
public class MatchPredicateProperty<T> {
|
||||
private static final Map<String, MatchPredicateProperty<?>> PROPERTIES = new HashMap<>();
|
||||
|
||||
public static final MatchPredicateProperty<ChargeType> CHARGE_TYPE = create("charge_type");
|
||||
public static final MatchPredicateProperty<Key> TRIM_MATERIAL = create("trim_material");
|
||||
public static final MatchPredicateProperty<Key> CONTEXT_DIMENSION = create("context_dimension");
|
||||
public static final MatchPredicateProperty<CustomModelDataPredicate<String>> CUSTOM_MODEL_DATA = create("custom_model_data");
|
||||
|
||||
public static MatchPredicateProperty<?> getProperty(String name) {
|
||||
return PROPERTIES.get(name);
|
||||
}
|
||||
|
||||
private static <T> MatchPredicateProperty<T> create(String name) {
|
||||
MatchPredicateProperty<T> property = new MatchPredicateProperty<>();
|
||||
PROPERTIES.put(name, property);
|
||||
return property;
|
||||
}
|
||||
}
|
|
@ -29,16 +29,20 @@ import net.kyori.adventure.key.Key;
|
|||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.item.custom.v2.CustomItemBedrockOptions;
|
||||
import org.geysermc.geyser.api.item.custom.v2.CustomItemDefinition;
|
||||
import org.geysermc.geyser.api.item.custom.v2.predicate.CustomItemPredicate;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public record GeyserCustomItemDefinition(@NonNull Key bedrockIdentifier, String displayName, @NonNull Key model,
|
||||
public record GeyserCustomItemDefinition(@NonNull Key bedrockIdentifier, String displayName, @NonNull Key model, @NonNull List<CustomItemPredicate<?>> predicates,
|
||||
@NonNull CustomItemBedrockOptions bedrockOptions, @NonNull DataComponents components) implements CustomItemDefinition {
|
||||
|
||||
public static class Builder implements CustomItemDefinition.Builder {
|
||||
private final Key bedrockIdentifier;
|
||||
private final Key model;
|
||||
private final List<CustomItemPredicate<?>> predicates = new ArrayList<>();
|
||||
private String displayName;
|
||||
private CustomItemBedrockOptions bedrockOptions = CustomItemBedrockOptions.builder().build();
|
||||
private DataComponents components = new DataComponents(new HashMap<>());
|
||||
|
@ -55,6 +59,12 @@ public record GeyserCustomItemDefinition(@NonNull Key bedrockIdentifier, String
|
|||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomItemDefinition.Builder predicate(@NonNull CustomItemPredicate<?> predicate) {
|
||||
predicates.add(predicate);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomItemDefinition.Builder bedrockOptions(CustomItemBedrockOptions.@NonNull Builder options) {
|
||||
this.bedrockOptions = options.build();
|
||||
|
@ -69,7 +79,7 @@ public record GeyserCustomItemDefinition(@NonNull Key bedrockIdentifier, String
|
|||
|
||||
@Override
|
||||
public CustomItemDefinition build() {
|
||||
return new GeyserCustomItemDefinition(bedrockIdentifier, displayName, model, bedrockOptions, components);
|
||||
return new GeyserCustomItemDefinition(bedrockIdentifier, displayName, model, List.copyOf(predicates), bedrockOptions, components);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@ public class Item {
|
|||
.damage(mapping.getBedrockData())
|
||||
.count(count);
|
||||
|
||||
ItemTranslator.translateCustomItem(components, builder, mapping);
|
||||
ItemTranslator.translateCustomItem(session, components, builder, mapping);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ public class PotionItem extends Item {
|
|||
if (components == null) return super.translateToBedrock(session, count, components, mapping, mappings);
|
||||
PotionContents potionContents = components.get(DataComponentType.POTION_CONTENTS);
|
||||
if (potionContents != null) {
|
||||
ItemDefinition customItemDefinition = CustomItemTranslator.getCustomItem(components, mapping);
|
||||
ItemDefinition customItemDefinition = CustomItemTranslator.getCustomItem(session, components, mapping);
|
||||
if (customItemDefinition == null) {
|
||||
Potion potion = Potion.getByJavaId(potionContents.getPotionId());
|
||||
if (potion != null) {
|
||||
|
|
|
@ -74,7 +74,7 @@ public class ShulkerBoxItem extends BlockItem {
|
|||
|
||||
if (boxComponents != null) {
|
||||
// Check for custom items
|
||||
ItemDefinition customItemDefinition = CustomItemTranslator.getCustomItem(boxComponents, boxMapping);
|
||||
ItemDefinition customItemDefinition = CustomItemTranslator.getCustomItem(session, boxComponents, boxMapping);
|
||||
if (customItemDefinition != null) {
|
||||
bedrockIdentifier = customItemDefinition.getIdentifier();
|
||||
bedrockData = 0;
|
||||
|
|
|
@ -29,11 +29,19 @@ import com.fasterxml.jackson.databind.JsonNode;
|
|||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.Constants;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.item.custom.v2.BedrockCreativeTab;
|
||||
import org.geysermc.geyser.api.item.custom.v2.CustomItemBedrockOptions;
|
||||
import org.geysermc.geyser.api.item.custom.v2.CustomItemDefinition;
|
||||
import org.geysermc.geyser.api.item.custom.v2.predicate.CustomItemPredicate;
|
||||
import org.geysermc.geyser.api.item.custom.v2.predicate.ItemPredicateType;
|
||||
import org.geysermc.geyser.api.item.custom.v2.predicate.data.ConditionPredicateData;
|
||||
import org.geysermc.geyser.api.item.custom.v2.predicate.data.CustomModelDataPredicate;
|
||||
import org.geysermc.geyser.api.item.custom.v2.predicate.data.match.ChargeType;
|
||||
import org.geysermc.geyser.api.item.custom.v2.predicate.data.match.MatchPredicateData;
|
||||
import org.geysermc.geyser.api.item.custom.v2.predicate.data.match.MatchPredicateProperty;
|
||||
import org.geysermc.geyser.item.exception.InvalidCustomMappingsFileException;
|
||||
import org.geysermc.geyser.registry.mappings.components.DataComponentReaders;
|
||||
import org.geysermc.geyser.registry.mappings.util.CustomBlockMapping;
|
||||
|
@ -101,7 +109,7 @@ public class MappingsReader_v2 extends MappingsReader {
|
|||
builder.displayName(node.get("display_name").asText());
|
||||
}
|
||||
|
||||
// TODO predicate
|
||||
readPredicates(builder, node.get("predicate"));
|
||||
|
||||
builder.bedrockOptions(readBedrockOptions(node.get("bedrock_options")));
|
||||
|
||||
|
@ -166,6 +174,86 @@ public class MappingsReader_v2 extends MappingsReader {
|
|||
return builder;
|
||||
}
|
||||
|
||||
private void readPredicates(CustomItemDefinition.Builder builder, JsonNode node) throws InvalidCustomMappingsFileException {
|
||||
if (node == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.isObject()) {
|
||||
readPredicate(builder, node);
|
||||
} else if (node.isArray()) {
|
||||
node.forEach(predicate -> {
|
||||
try {
|
||||
readPredicate(builder, predicate);
|
||||
} catch (InvalidCustomMappingsFileException e) {
|
||||
GeyserImpl.getInstance().getLogger().error("Error in reading predicate", e); // TODO log this better
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw new InvalidCustomMappingsFileException("Expected predicate key to be a list of predicates or a predicate");
|
||||
}
|
||||
}
|
||||
|
||||
private void readPredicate(CustomItemDefinition.Builder builder, @NonNull JsonNode node) throws InvalidCustomMappingsFileException {
|
||||
if (!node.isObject()) {
|
||||
throw new InvalidCustomMappingsFileException("Expected predicate to be an object");
|
||||
}
|
||||
|
||||
JsonNode typeNode = node.get("type");
|
||||
if (typeNode == null || !typeNode.isTextual()) {
|
||||
throw new InvalidCustomMappingsFileException("Predicate missing type key");
|
||||
}
|
||||
|
||||
ItemPredicateType<?> type = ItemPredicateType.getType(typeNode.asText());
|
||||
JsonNode propertyNode = node.get("property");
|
||||
if (propertyNode == null || !propertyNode.isTextual()) {
|
||||
throw new InvalidCustomMappingsFileException("Predicate missing property key");
|
||||
}
|
||||
|
||||
if (type == ItemPredicateType.CONDITION) {
|
||||
try {
|
||||
ConditionPredicateData.ConditionProperty property = ConditionPredicateData.ConditionProperty.valueOf(propertyNode.asText().toUpperCase());
|
||||
JsonNode expected = node.get("expected");
|
||||
JsonNode index = node.get("index");
|
||||
|
||||
builder.predicate(new CustomItemPredicate<>(ItemPredicateType.CONDITION, new ConditionPredicateData(property,
|
||||
expected == null || expected.asBoolean(), index == null || !index.isIntegralNumber() ? 0 : index.asInt())));
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw new InvalidCustomMappingsFileException("Unknown property " + propertyNode.asText());
|
||||
}
|
||||
} else if (type == ItemPredicateType.MATCH) {
|
||||
MatchPredicateProperty<?> property = MatchPredicateProperty.getProperty(propertyNode.asText());
|
||||
if (property == null) {
|
||||
throw new InvalidCustomMappingsFileException("Unknown property " + propertyNode.asText());
|
||||
}
|
||||
|
||||
JsonNode value = node.get("value");
|
||||
if (value == null || !value.isTextual()) {
|
||||
throw new InvalidCustomMappingsFileException("Predicate missing value key");
|
||||
}
|
||||
|
||||
if (property == MatchPredicateProperty.CHARGE_TYPE) {
|
||||
try {
|
||||
ChargeType chargeType = ChargeType.valueOf(value.asText().toUpperCase());
|
||||
builder.predicate(new CustomItemPredicate<>(ItemPredicateType.MATCH,
|
||||
new MatchPredicateData<>(MatchPredicateProperty.CHARGE_TYPE, chargeType)));
|
||||
} catch (IllegalArgumentException exception) {
|
||||
throw new InvalidCustomMappingsFileException("Unknown charge type " + value.asText());
|
||||
}
|
||||
} else if (property == MatchPredicateProperty.TRIM_MATERIAL || property == MatchPredicateProperty.CONTEXT_DIMENSION) {
|
||||
builder.predicate(new CustomItemPredicate<>(ItemPredicateType.MATCH,
|
||||
new MatchPredicateData<>((MatchPredicateProperty<Key>) property, Key.key(value.asText())))); // TODO
|
||||
} else if (property == MatchPredicateProperty.CUSTOM_MODEL_DATA) {
|
||||
JsonNode index = node.get("index");
|
||||
if (index == null || !index.isIntegralNumber()) {
|
||||
throw new InvalidCustomMappingsFileException("Predicate missing index key");
|
||||
}
|
||||
builder.predicate(new CustomItemPredicate<>(ItemPredicateType.MATCH,
|
||||
new MatchPredicateData<>(MatchPredicateProperty.CUSTOM_MODEL_DATA, new CustomModelDataPredicate<>(value.asText(), index.asInt()))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomBlockMapping readBlockMappingEntry(String identifier, JsonNode node) throws InvalidCustomMappingsFileException {
|
||||
return null; // TODO
|
||||
|
|
|
@ -40,6 +40,7 @@ import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData;
|
|||
import org.geysermc.geyser.api.item.custom.v2.BedrockCreativeTab;
|
||||
import org.geysermc.geyser.api.item.custom.v2.CustomItemBedrockOptions;
|
||||
import org.geysermc.geyser.api.item.custom.v2.CustomItemDefinition;
|
||||
import org.geysermc.geyser.api.item.custom.v2.predicate.CustomItemPredicate;
|
||||
import org.geysermc.geyser.item.GeyserCustomMappingData;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.item.components.WearableSlot;
|
||||
|
@ -61,6 +62,7 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
public class CustomItemRegistryPopulator_v2 {
|
||||
|
@ -82,7 +84,7 @@ public class CustomItemRegistryPopulator_v2 {
|
|||
MappingsConfigReader mappingsConfigReader = new MappingsConfigReader();
|
||||
// Load custom items from mappings files
|
||||
mappingsConfigReader.loadItemMappingsFromJson((id, item) -> {
|
||||
if (initialCheck(item, items)) {
|
||||
if (initialCheck(id, item, customItems, items)) {
|
||||
customItems.get(id).add(item);
|
||||
}
|
||||
});
|
||||
|
@ -103,15 +105,84 @@ public class CustomItemRegistryPopulator_v2 {
|
|||
return new GeyserCustomMappingData(componentItemData, itemDefinition, customItemName, bedrockId);
|
||||
}
|
||||
|
||||
static boolean initialCheck(CustomItemDefinition item, Map<String, GeyserMappingItem> mappings) {
|
||||
private static boolean initialCheck(String identifier, CustomItemDefinition item, Multimap<String, CustomItemDefinition> registered, Map<String, GeyserMappingItem> mappings) {
|
||||
// TODO check if there's already a same model without predicate and this hasn't a predicate either
|
||||
Key name = item.bedrockIdentifier();
|
||||
if (name.namespace().equals(Key.MINECRAFT_NAMESPACE)) {
|
||||
GeyserImpl.getInstance().getLogger().warning("Custom item namespace can't be minecraft");
|
||||
if (!mappings.containsKey(identifier)) {
|
||||
GeyserImpl.getInstance().getLogger().error("Could not find the Java item to add custom item properties to for " + item.bedrockIdentifier());
|
||||
return false;
|
||||
}
|
||||
Key bedrockIdentifier = item.bedrockIdentifier();
|
||||
if (bedrockIdentifier.namespace().equals(Key.MINECRAFT_NAMESPACE)) {
|
||||
GeyserImpl.getInstance().getLogger().error("Custom item bedrock identifier namespace can't be minecraft");
|
||||
return false;
|
||||
} else if (item.model().namespace().equals(Key.MINECRAFT_NAMESPACE) && item.predicates().isEmpty()) {
|
||||
GeyserImpl.getInstance().getLogger().error("Custom item definition model can't be minecraft without a predicate");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Map.Entry<String, CustomItemDefinition> entry : registered.entries()) {
|
||||
if (entry.getValue().bedrockIdentifier().equals(item.bedrockIdentifier())) {
|
||||
GeyserImpl.getInstance().getLogger().error("Duplicate custom item definition for Bedrock ID " + item.bedrockIdentifier());
|
||||
return false;
|
||||
}
|
||||
Optional<String> error = checkPredicate(entry, identifier, item);
|
||||
if (error.isPresent()) {
|
||||
GeyserImpl.getInstance().getLogger().error("An existing item definition for the Java item " + identifier + " was already registered that conflicts with this one!");
|
||||
GeyserImpl.getInstance().getLogger().error("First entry: " + entry.getValue().bedrockIdentifier());
|
||||
GeyserImpl.getInstance().getLogger().error("Second entry: " + item.bedrockIdentifier());
|
||||
GeyserImpl.getInstance().getLogger().error(error.orElseThrow());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an error message if there was a conflict, or an empty optional otherwise
|
||||
*/
|
||||
private static Optional<String> checkPredicate(Map.Entry<String, CustomItemDefinition> existing, String identifier, CustomItemDefinition item) {
|
||||
// TODO this is probably wrong
|
||||
// If the definitions are for different Java items or models then it doesn't matter
|
||||
if (!identifier.equals(existing.getKey()) || !item.model().equals(existing.getValue().model())) {
|
||||
return Optional.empty();
|
||||
}
|
||||
// If they both don't have predicates they conflict
|
||||
if (existing.getValue().predicates().isEmpty() && item.predicates().isEmpty()) {
|
||||
return Optional.of("Both entries don't have predicates, the first must have a predicate");
|
||||
}
|
||||
|
||||
// If a previously registered entry does have predicates, and this entry doesn't, then they also conflict
|
||||
// Entries with predicates must always be first
|
||||
if (existing.getValue().predicates().isEmpty() && !item.predicates().isEmpty()) {
|
||||
return Optional.of("The first entry has no predicates, meaning that one will always be used");
|
||||
} else if (item.predicates().isEmpty()) {
|
||||
return Optional.empty(); // Item definitions are correctly ordered
|
||||
}
|
||||
|
||||
// If all predicates of an existing entry also exist in a new entry, then the new entry is invalid
|
||||
// This makes it required to order definitions correctly, so that "fallback predicates" are added last:
|
||||
//
|
||||
// A && B -> item1
|
||||
// A -> item2
|
||||
//
|
||||
// Is the correct order, not
|
||||
//
|
||||
// A -> item2
|
||||
// A && B -> item1
|
||||
boolean existingHasAllPredicates = true;
|
||||
for (CustomItemPredicate<?> predicate : existing.getValue().predicates()) {
|
||||
if (!item.predicates().contains(predicate)) {
|
||||
existingHasAllPredicates = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (existingHasAllPredicates) {
|
||||
return Optional.of("Reorder your entries so that the one with the least amount of predicates is last");
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static NbtMapBuilder createComponentNbt(CustomItemDefinition customItemDefinition, Item vanillaJavaItem, GeyserMappingItem vanillaMapping,
|
||||
String customItemName, int customItemId) {
|
||||
NbtMapBuilder builder = NbtMap.builder()
|
||||
|
@ -325,6 +396,9 @@ public class CustomItemRegistryPopulator_v2 {
|
|||
itemProperties.putInt("use_duration", (int) (consumable.consumeSeconds() * 20));
|
||||
|
||||
itemProperties.putInt("use_animation", BEDROCK_ANIMATIONS.get(consumable.animation()));
|
||||
componentBuilder.putCompound("minecraft:use_animation", NbtMap.builder()
|
||||
.putString("value", consumable.animation().toString().toLowerCase())
|
||||
.build()); // TODO check
|
||||
|
||||
// this component is required to allow the eat animation to play
|
||||
componentBuilder.putCompound("minecraft:food", NbtMap.builder().putBoolean("can_always_eat", canAlwaysEat).build());
|
||||
|
|
|
@ -51,6 +51,7 @@ import org.geysermc.geyser.session.cache.registry.JavaRegistries;
|
|||
import org.geysermc.geyser.session.cache.registry.JavaRegistry;
|
||||
import org.geysermc.geyser.session.cache.registry.JavaRegistryKey;
|
||||
import org.geysermc.geyser.session.cache.registry.RegistryEntryContext;
|
||||
import org.geysermc.geyser.session.cache.registry.RegistryEntryData;
|
||||
import org.geysermc.geyser.session.cache.registry.SimpleJavaRegistry;
|
||||
import org.geysermc.geyser.text.ChatDecoration;
|
||||
import org.geysermc.geyser.translator.level.BiomeTranslator;
|
||||
|
@ -189,7 +190,7 @@ public final class RegistryCache {
|
|||
entryIdMap.put(entries.get(i).getId(), i);
|
||||
}
|
||||
|
||||
List<T> builder = new ArrayList<>(entries.size());
|
||||
List<RegistryEntryData<T>> builder = new ArrayList<>(entries.size());
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
RegistryEntry entry = entries.get(i);
|
||||
// If the data is null, that's the server telling us we need to use our default values.
|
||||
|
@ -203,7 +204,7 @@ public final class RegistryCache {
|
|||
RegistryEntryContext context = new RegistryEntryContext(entry, entryIdMap, registryCache.session);
|
||||
// This is what Geyser wants to keep as a value for this registry.
|
||||
T cacheEntry = reader.apply(context);
|
||||
builder.add(i, cacheEntry);
|
||||
builder.add(i, new RegistryEntryData<>(entry.getId(), cacheEntry));
|
||||
}
|
||||
localCache.reset(builder);
|
||||
});
|
||||
|
|
|
@ -39,15 +39,25 @@ public interface JavaRegistry<T> {
|
|||
*/
|
||||
T byId(@NonNegative int id);
|
||||
|
||||
/**
|
||||
* Looks up a registry entry by its ID, and returns it wrapped in {@link RegistryEntryData} so that its registered key is also known. The object can be null, or not present.
|
||||
*/
|
||||
RegistryEntryData<T> entryById(@NonNegative int id);
|
||||
|
||||
/**
|
||||
* Reverse looks-up an object to return its network ID, or -1.
|
||||
*/
|
||||
int byValue(T value);
|
||||
|
||||
/**
|
||||
* Reverse looks-up an object to return it wrapped in {@link RegistryEntryData}, or null.
|
||||
*/
|
||||
RegistryEntryData<T> entryByValue(T value);
|
||||
|
||||
/**
|
||||
* Resets the objects by these IDs.
|
||||
*/
|
||||
void reset(List<T> values);
|
||||
void reset(List<RegistryEntryData<T>> values);
|
||||
|
||||
/**
|
||||
* All values of this registry, as a list.
|
||||
|
|
31
core/src/main/java/org/geysermc/geyser/session/cache/registry/RegistryEntryData.java
vendored
Normal file
31
core/src/main/java/org/geysermc/geyser/session/cache/registry/RegistryEntryData.java
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.session.cache.registry;
|
||||
|
||||
import net.kyori.adventure.key.Key;
|
||||
|
||||
public record RegistryEntryData<T>(Key key, T data) {
|
||||
}
|
|
@ -31,10 +31,18 @@ import org.checkerframework.checker.index.qual.NonNegative;
|
|||
import java.util.List;
|
||||
|
||||
public class SimpleJavaRegistry<T> implements JavaRegistry<T> {
|
||||
protected final ObjectArrayList<T> values = new ObjectArrayList<>();
|
||||
protected final ObjectArrayList<RegistryEntryData<T>> values = new ObjectArrayList<>();
|
||||
|
||||
@Override
|
||||
public T byId(@NonNegative int id) {
|
||||
if (id < 0 || id >= this.values.size()) {
|
||||
return null;
|
||||
}
|
||||
return this.values.get(id).data();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegistryEntryData<T> entryById(@NonNegative int id) {
|
||||
if (id < 0 || id >= this.values.size()) {
|
||||
return null;
|
||||
}
|
||||
|
@ -43,11 +51,26 @@ public class SimpleJavaRegistry<T> implements JavaRegistry<T> {
|
|||
|
||||
@Override
|
||||
public int byValue(T value) {
|
||||
return this.values.indexOf(value);
|
||||
for (int i = 0; i < this.values.size(); i++) {
|
||||
if (values.get(i).data().equals(value)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(List<T> values) {
|
||||
public RegistryEntryData<T> entryByValue(T value) {
|
||||
for (RegistryEntryData<T> entry : this.values) {
|
||||
if (entry.data().equals(value)) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset(List<RegistryEntryData<T>> values) {
|
||||
this.values.clear();
|
||||
this.values.addAll(values);
|
||||
this.values.trim();
|
||||
|
@ -55,7 +78,7 @@ public class SimpleJavaRegistry<T> implements JavaRegistry<T> {
|
|||
|
||||
@Override
|
||||
public List<T> values() {
|
||||
return this.values;
|
||||
return this.values.stream().map(RegistryEntryData::data).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -26,8 +26,21 @@
|
|||
package org.geysermc.geyser.translator.item;
|
||||
|
||||
import net.kyori.adventure.key.Key;
|
||||
import org.cloudburstmc.protocol.bedrock.data.TrimMaterial;
|
||||
import org.geysermc.geyser.api.item.custom.v2.CustomItemDefinition;
|
||||
import org.geysermc.geyser.api.item.custom.v2.predicate.CustomItemPredicate;
|
||||
import org.geysermc.geyser.api.item.custom.v2.predicate.ItemPredicateType;
|
||||
import org.geysermc.geyser.api.item.custom.v2.predicate.data.ConditionPredicateData;
|
||||
import org.geysermc.geyser.api.item.custom.v2.predicate.data.match.ChargeType;
|
||||
import org.geysermc.geyser.api.item.custom.v2.predicate.data.match.MatchPredicateData;
|
||||
import org.geysermc.geyser.api.item.custom.v2.predicate.data.match.MatchPredicateProperty;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.level.JavaDimension;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.registry.RegistryEntryData;
|
||||
import org.geysermc.geyser.util.MinecraftKey;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.ArmorTrim;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||
import it.unimi.dsi.fastutil.Pair;
|
||||
|
@ -43,7 +56,7 @@ import java.util.List;
|
|||
public final class CustomItemTranslator {
|
||||
|
||||
@Nullable
|
||||
public static ItemDefinition getCustomItem(DataComponents components, ItemMapping mapping) {
|
||||
public static ItemDefinition getCustomItem(GeyserSession session, DataComponents components, ItemMapping mapping) {
|
||||
if (components == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -55,79 +68,82 @@ public final class CustomItemTranslator {
|
|||
|
||||
Key itemModel = components.getOrDefault(DataComponentType.ITEM_MODEL, MinecraftKey.key("air")); // TODO fallback onto default item model (when thats done by chris)
|
||||
|
||||
// TODO check if definitions/predicates are in the correct order
|
||||
for (Pair<CustomItemDefinition, ItemDefinition> customModel : customItems) { // TODO Predicates
|
||||
if (customModel.first().model().equals(itemModel)) {
|
||||
return customModel.second();
|
||||
boolean allMatch = true;
|
||||
for (CustomItemPredicate<?> predicate : customModel.first().predicates()) {
|
||||
if (!predicateMatches(session, predicate, components)) {
|
||||
allMatch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allMatch) {
|
||||
return customModel.second();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
/*
|
||||
List<Pair<CustomItemOptions, ItemDefinition>> customMappings = mapping.getCustomItemOptions();
|
||||
if (customMappings.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int customModelData = components.getOrDefault(DataComponentType.CUSTOM_MODEL_DATA, 0);
|
||||
boolean checkDamage = mapping.getJavaItem().maxDamage() > 0;
|
||||
int damage = !checkDamage ? 0 : components.getOrDefault(DataComponentType.DAMAGE, 0);
|
||||
boolean unbreakable = checkDamage && !isDamaged(components, damage);
|
||||
|
||||
for (Pair<CustomItemOptions, ItemDefinition> mappingTypes : customMappings) {
|
||||
CustomItemOptions options = mappingTypes.key();
|
||||
|
||||
// Code note: there may be two or more conditions that a custom item must follow, hence the "continues"
|
||||
// here with the return at the end.
|
||||
|
||||
// Implementation details: Java's predicate system works exclusively on comparing float numbers.
|
||||
// A value doesn't necessarily have to match 100%; it just has to be the first to meet all predicate conditions.
|
||||
// This is also why the order of iteration is important as the first to match will be the chosen display item.
|
||||
// For example, if CustomModelData is set to 2f as the requirement, then the NBT can be any number greater or equal (2, 3, 4...)
|
||||
// The same behavior exists for Damage (in fraction form instead of whole numbers),
|
||||
// and Damaged/Unbreakable handles no damage as 0f and damaged as 1f.
|
||||
|
||||
if (checkDamage) {
|
||||
if (unbreakable && options.unbreakable() == TriState.FALSE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
OptionalInt damagePredicate = options.damagePredicate();
|
||||
if (damagePredicate.isPresent() && damage < damagePredicate.getAsInt()) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (options.unbreakable() != TriState.NOT_SET || options.damagePredicate().isPresent()) {
|
||||
// These will never match on this item. 1.19.2 behavior
|
||||
// Maybe move this to CustomItemRegistryPopulator since it'll be the same for every item? If so, add a test.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
OptionalInt customModelDataOption = options.customModelData();
|
||||
if (customModelDataOption.isPresent() && customModelData < customModelDataOption.getAsInt()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (options.defaultItem()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return mappingTypes.value();
|
||||
}
|
||||
|
||||
return null;*/
|
||||
}
|
||||
|
||||
/* These two functions are based off their Mojmap equivalents from 1.19.2 */
|
||||
private static boolean predicateMatches(GeyserSession session, CustomItemPredicate<?> predicate, DataComponents components) {
|
||||
if (predicate.type() == ItemPredicateType.CONDITION) {
|
||||
ConditionPredicateData data = (ConditionPredicateData) predicate.data();
|
||||
return switch (data.property()) {
|
||||
case BROKEN -> nextDamageWillBreak(components);
|
||||
case DAMAGED -> isDamaged(components);
|
||||
case CUSTOM_MODEL_DATA -> false; // TODO 1.21.4
|
||||
};
|
||||
} else if (predicate.type() == ItemPredicateType.MATCH) {
|
||||
MatchPredicateData<?> data = (MatchPredicateData<?>) predicate.data();
|
||||
|
||||
private static boolean isDamaged(DataComponents components, int damage) {
|
||||
return isDamagableItem(components) && damage > 0;
|
||||
if (data.property() == MatchPredicateProperty.CHARGE_TYPE) {
|
||||
ChargeType expected = (ChargeType) data.data();
|
||||
List<ItemStack> charged = components.get(DataComponentType.CHARGED_PROJECTILES);
|
||||
if (charged == null) {
|
||||
return expected == ChargeType.NONE;
|
||||
} else if (expected == ChargeType.ROCKET) {
|
||||
for (ItemStack projectile : charged) {
|
||||
if (projectile.getId() == Items.FIREWORK_ROCKET.javaId()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else if (data.property() == MatchPredicateProperty.TRIM_MATERIAL) {
|
||||
Key material = (Key) data.data();
|
||||
ArmorTrim trim = components.get(DataComponentType.TRIM);
|
||||
if (trim == null || trim.material().isCustom()) {
|
||||
return false;
|
||||
}
|
||||
RegistryEntryData<TrimMaterial> registered = session.getRegistryCache().trimMaterials().entryById(trim.material().id());
|
||||
return registered != null && registered.key().equals(material);
|
||||
} else if (data.property() == MatchPredicateProperty.CONTEXT_DIMENSION) {
|
||||
Key dimension = (Key) data.data();
|
||||
RegistryEntryData<JavaDimension> registered = session.getRegistryCache().dimensions().entryByValue(session.getDimensionType());
|
||||
return registered != null && dimension.equals(registered.key()); // TODO check if this works
|
||||
} else if (data.property() == MatchPredicateProperty.CUSTOM_MODEL_DATA) {
|
||||
// TODO 1.21.4
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Unimplemented predicate type");
|
||||
}
|
||||
|
||||
private static boolean isDamagableItem(DataComponents components) {
|
||||
// mapping.getMaxDamage > 0 should also be checked (return false if not true) but we already check prior to this function
|
||||
Boolean unbreakable = components.get(DataComponentType.UNBREAKABLE);
|
||||
// Tag must either not be present or be set to false
|
||||
return unbreakable == null || !unbreakable;
|
||||
/* These three functions are based off their Mojmap equivalents from 1.21.3 */
|
||||
|
||||
private static boolean nextDamageWillBreak(DataComponents components) {
|
||||
return isDamageableItem(components) && components.getOrDefault(DataComponentType.DAMAGE, 0) >= components.getOrDefault(DataComponentType.MAX_DAMAGE, 0) - 1;
|
||||
}
|
||||
|
||||
private static boolean isDamaged(DataComponents components) {
|
||||
return isDamageableItem(components) && components.getOrDefault(DataComponentType.DAMAGE, 0) > 0;
|
||||
}
|
||||
|
||||
private static boolean isDamageableItem(DataComponents components) {
|
||||
return components.getOrDefault(DataComponentType.UNBREAKABLE, false) && components.getOrDefault(DataComponentType.MAX_DAMAGE, 0) > 0;
|
||||
}
|
||||
|
||||
private CustomItemTranslator() {
|
||||
|
|
|
@ -215,7 +215,7 @@ public final class ItemTranslator {
|
|||
translatePlayerHead(session, components, builder);
|
||||
}
|
||||
|
||||
translateCustomItem(components, builder, bedrockItem);
|
||||
translateCustomItem(session, components, builder, bedrockItem);
|
||||
|
||||
if (components != null) {
|
||||
// Translate the canDestroy and canPlaceOn Java components
|
||||
|
@ -428,7 +428,7 @@ public final class ItemTranslator {
|
|||
}
|
||||
}
|
||||
|
||||
ItemDefinition definition = CustomItemTranslator.getCustomItem(itemStack.getComponents(), mapping);
|
||||
ItemDefinition definition = CustomItemTranslator.getCustomItem(session, itemStack.getComponents(), mapping);
|
||||
if (definition == null) {
|
||||
// No custom item
|
||||
return itemDefinition;
|
||||
|
@ -469,8 +469,8 @@ public final class ItemTranslator {
|
|||
/**
|
||||
* Translates the custom model data of an item
|
||||
*/
|
||||
public static void translateCustomItem(DataComponents components, ItemData.Builder builder, ItemMapping mapping) {
|
||||
ItemDefinition definition = CustomItemTranslator.getCustomItem(components, mapping);
|
||||
public static void translateCustomItem(GeyserSession session, DataComponents components, ItemData.Builder builder, ItemMapping mapping) {
|
||||
ItemDefinition definition = CustomItemTranslator.getCustomItem(session, components, mapping);
|
||||
if (definition != null) {
|
||||
builder.definition(definition);
|
||||
builder.blockDefinition(null);
|
||||
|
|
|
@ -93,7 +93,7 @@ final class BedrockBlockActions {
|
|||
// If the block is custom or the breaking item is custom, we must keep track of break time ourselves
|
||||
GeyserItemStack item = session.getPlayerInventory().getItemInHand();
|
||||
ItemMapping mapping = item.getMapping(session);
|
||||
ItemDefinition customItem = mapping.isTool() ? CustomItemTranslator.getCustomItem(item.getComponents(), mapping) : null;
|
||||
ItemDefinition customItem = mapping.isTool() ? CustomItemTranslator.getCustomItem(session, item.getComponents(), mapping) : null;
|
||||
CustomBlockState blockStateOverride = BlockRegistries.CUSTOM_BLOCK_STATE_OVERRIDES.get(blockState);
|
||||
SkullCache.Skull skull = session.getSkullCache().getSkulls().get(vector);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue