mirror of
https://github.com/GeyserMC/Rainbow.git
synced 2025-12-19 14:59:16 +00:00
Support writing legacy definitions
This commit is contained in:
@@ -34,4 +34,8 @@ public class Rainbow implements ClientModInitializer {
|
|||||||
public static ResourceLocation getModdedLocation(String path) {
|
public static ResourceLocation getModdedLocation(String path) {
|
||||||
return ResourceLocation.fromNamespaceAndPath(MOD_ID, path);
|
return ResourceLocation.fromNamespaceAndPath(MOD_ID, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String fileSafeResourceLocation(ResourceLocation location) {
|
||||||
|
return location.toString().replace(':', '.').replace('/', '_');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import org.geysermc.rainbow.mapping.attachable.AttachableMapper;
|
|||||||
import org.geysermc.rainbow.mapping.geometry.BedrockGeometryContext;
|
import org.geysermc.rainbow.mapping.geometry.BedrockGeometryContext;
|
||||||
import org.geysermc.rainbow.mapping.geometry.GeometryMapper;
|
import org.geysermc.rainbow.mapping.geometry.GeometryMapper;
|
||||||
import org.geysermc.rainbow.mapping.geometry.GeometryRenderer;
|
import org.geysermc.rainbow.mapping.geometry.GeometryRenderer;
|
||||||
|
import org.geysermc.rainbow.mapping.geyser.GeyserBaseDefinition;
|
||||||
import org.geysermc.rainbow.mapping.geyser.GeyserMappings;
|
import org.geysermc.rainbow.mapping.geyser.GeyserMappings;
|
||||||
import org.geysermc.rainbow.mapping.geyser.GeyserSingleDefinition;
|
import org.geysermc.rainbow.mapping.geyser.GeyserSingleDefinition;
|
||||||
import org.geysermc.rainbow.mapping.geyser.predicate.GeyserConditionPredicate;
|
import org.geysermc.rainbow.mapping.geyser.predicate.GeyserConditionPredicate;
|
||||||
@@ -78,14 +79,17 @@ public class BedrockItemMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mapItem(model, displayName, protectionValue, stack.getComponentsPatch(), reporter,
|
mapItem(model, displayName, protectionValue, stack.getComponentsPatch(), reporter,
|
||||||
mapping -> mappings.map(stack.getItemHolder(), mapping), packPath, itemConsumer, additionalTextureConsumer);
|
packPath, mapping -> mappings.map(stack.getItemHolder(), new GeyserSingleDefinition(mapping, Optional.of(model))),
|
||||||
|
itemConsumer, additionalTextureConsumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void mapItem(ResourceLocation modelLocation, String displayName, int protectionValue, DataComponentPatch componentPatch, ProblemReporter reporter,
|
public static void mapItem(ResourceLocation modelLocation, String displayName, int protectionValue, DataComponentPatch componentPatch, ProblemReporter reporter,
|
||||||
Consumer<GeyserSingleDefinition> mappingConsumer, Path packPath, BedrockItemConsumer itemConsumer, Consumer<ResourceLocation> additionalTextureConsumer) {
|
Path packPath, Consumer<GeyserBaseDefinition> mappingConsumer, BedrockItemConsumer itemConsumer,
|
||||||
|
Consumer<ResourceLocation> additionalTextureConsumer) {
|
||||||
ItemModel model = Minecraft.getInstance().getModelManager().getItemModel(modelLocation);
|
ItemModel model = Minecraft.getInstance().getModelManager().getItemModel(modelLocation);
|
||||||
MappingContext context = new MappingContext(List.of(), modelLocation, displayName, protectionValue, componentPatch,
|
MappingContext context = new MappingContext(List.of(), displayName, protectionValue, componentPatch,
|
||||||
reporter.forChild(() -> "client item definition " + modelLocation + " "), mappingConsumer, itemConsumer, packPath, additionalTextureConsumer);
|
reporter.forChild(() -> "client item definition " + modelLocation + " "),
|
||||||
|
packPath, mappingConsumer, itemConsumer, additionalTextureConsumer);
|
||||||
mapItem(model, context);
|
mapItem(model, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,8 +157,8 @@ public class BedrockItemMapper {
|
|||||||
mapItem(onFalse, context.with(new GeyserConditionPredicate(predicateProperty, false), "condition on false "));
|
mapItem(onFalse, context.with(new GeyserConditionPredicate(predicateProperty, false), "condition on false "));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
private static <T> void mapSelectModel(SelectItemModel<T> model, MappingContext context) {
|
private static <T> void mapSelectModel(SelectItemModel<T> model, MappingContext context) {
|
||||||
//noinspection unchecked
|
|
||||||
SelectItemModelProperty<T> property = ((SelectItemModelAccessor<T>) model).getProperty();
|
SelectItemModelProperty<T> property = ((SelectItemModelAccessor<T>) model).getProperty();
|
||||||
Function<T, GeyserMatchPredicate.MatchPredicateData> dataConstructor = switch (property) {
|
Function<T, GeyserMatchPredicate.MatchPredicateData> dataConstructor = switch (property) {
|
||||||
case Charge ignored -> chargeType -> new GeyserMatchPredicate.ChargeType((CrossbowItem.ChargeType) chargeType);
|
case Charge ignored -> chargeType -> new GeyserMatchPredicate.ChargeType((CrossbowItem.ChargeType) chargeType);
|
||||||
@@ -165,7 +169,6 @@ public class BedrockItemMapper {
|
|||||||
default -> null;
|
default -> null;
|
||||||
};
|
};
|
||||||
|
|
||||||
//noinspection unchecked
|
|
||||||
Object2ObjectMap<T, ItemModel> cases = ((SelectItemModelCasesAccessor<T>) model).rainbow$getCases();
|
Object2ObjectMap<T, ItemModel> cases = ((SelectItemModelCasesAccessor<T>) model).rainbow$getCases();
|
||||||
|
|
||||||
if (dataConstructor == null) {
|
if (dataConstructor == null) {
|
||||||
@@ -186,24 +189,24 @@ public class BedrockItemMapper {
|
|||||||
mapItem(cases.defaultReturnValue(), context.child("select fallback case "));
|
mapItem(cases.defaultReturnValue(), context.child("select fallback case "));
|
||||||
}
|
}
|
||||||
|
|
||||||
private record MappingContext(List<GeyserPredicate> predicateStack, ResourceLocation model, String displayName, int protectionValue, DataComponentPatch componentPatch, ProblemReporter reporter,
|
private record MappingContext(List<GeyserPredicate> predicateStack, String displayName, int protectionValue, DataComponentPatch componentPatch, ProblemReporter reporter,
|
||||||
Consumer<GeyserSingleDefinition> mappingConsumer, BedrockItemConsumer itemConsumer, Path packPath,
|
Path packPath, Consumer<GeyserBaseDefinition> mappingConsumer, BedrockItemConsumer itemConsumer,
|
||||||
Consumer<ResourceLocation> additionalTextureConsumer) {
|
Consumer<ResourceLocation> additionalTextureConsumer) {
|
||||||
|
|
||||||
public MappingContext with(GeyserPredicate predicate, String childName) {
|
public MappingContext with(GeyserPredicate predicate, String childName) {
|
||||||
return new MappingContext(Stream.concat(predicateStack.stream(), Stream.of(predicate)).toList(), model, displayName, protectionValue, componentPatch,
|
return new MappingContext(Stream.concat(predicateStack.stream(), Stream.of(predicate)).toList(), displayName, protectionValue, componentPatch,
|
||||||
reporter.forChild(() -> childName), mappingConsumer, itemConsumer, packPath, additionalTextureConsumer);
|
reporter.forChild(() -> childName), packPath, mappingConsumer, itemConsumer, additionalTextureConsumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MappingContext child(String childName) {
|
public MappingContext child(String childName) {
|
||||||
return new MappingContext(predicateStack, model, displayName, protectionValue, componentPatch, reporter.forChild(() -> childName),
|
return new MappingContext(predicateStack, displayName, protectionValue, componentPatch, reporter.forChild(() -> childName),
|
||||||
mappingConsumer, itemConsumer, packPath, additionalTextureConsumer);
|
packPath, mappingConsumer, itemConsumer, additionalTextureConsumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void create(ResourceLocation bedrockIdentifier, ResourceLocation texture, boolean displayHandheld,
|
public void create(ResourceLocation bedrockIdentifier, ResourceLocation texture, boolean displayHandheld,
|
||||||
Optional<ResolvedModel> customModel) {
|
Optional<ResolvedModel> customModel) {
|
||||||
GeyserSingleDefinition definition = new GeyserSingleDefinition(Optional.of(model), bedrockIdentifier, Optional.of(displayName), predicateStack,
|
GeyserBaseDefinition definition = new GeyserBaseDefinition(bedrockIdentifier, Optional.of(displayName), predicateStack,
|
||||||
new GeyserSingleDefinition.BedrockOptions(Optional.empty(), true, displayHandheld, protectionValue), componentPatch);
|
new GeyserBaseDefinition.BedrockOptions(Optional.empty(), true, displayHandheld, protectionValue), componentPatch);
|
||||||
try {
|
try {
|
||||||
mappingConsumer.accept(definition);
|
mappingConsumer.accept(definition);
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
@@ -221,7 +224,7 @@ public class BedrockItemMapper {
|
|||||||
boolean exportTexture = true;
|
boolean exportTexture = true;
|
||||||
if (customModel.isPresent()) {
|
if (customModel.isPresent()) {
|
||||||
ItemStack fakeItem = new ItemStack(Items.FLINT);
|
ItemStack fakeItem = new ItemStack(Items.FLINT);
|
||||||
fakeItem.set(DataComponents.ITEM_MODEL, model);
|
//fakeItem.set(DataComponents.ITEM_MODEL, model); TODO
|
||||||
|
|
||||||
texture = texture.withPath(path -> path + "_icon");
|
texture = texture.withPath(path -> path + "_icon");
|
||||||
GeometryRenderer.render(fakeItem, packPath.resolve(BedrockTextures.TEXTURES_FOLDER + texture.getPath() + ".png"));
|
GeometryRenderer.render(fakeItem, packPath.resolve(BedrockTextures.TEXTURES_FOLDER + texture.getPath() + ".png"));
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package org.geysermc.rainbow.mapping.geyser;
|
||||||
|
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
import net.minecraft.core.component.DataComponentPatch;
|
||||||
|
import net.minecraft.core.component.DataComponentType;
|
||||||
|
import net.minecraft.core.component.DataComponents;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.geysermc.rainbow.Rainbow;
|
||||||
|
import org.geysermc.rainbow.mapping.geyser.predicate.GeyserPredicate;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
// TODO other keys, etc.
|
||||||
|
// TODO sometimes still includes components key when patch before filtering is not empty but after is
|
||||||
|
// TODO display name can be a component
|
||||||
|
public record GeyserBaseDefinition(ResourceLocation bedrockIdentifier, Optional<String> displayName,
|
||||||
|
List<GeyserPredicate> predicates, BedrockOptions bedrockOptions, DataComponentPatch components) {
|
||||||
|
private static final List<DataComponentType<?>> SUPPORTED_COMPONENTS = List.of(DataComponents.CONSUMABLE, DataComponents.EQUIPPABLE, DataComponents.FOOD,
|
||||||
|
DataComponents.MAX_DAMAGE, DataComponents.MAX_STACK_SIZE, DataComponents.USE_COOLDOWN, DataComponents.ENCHANTABLE, DataComponents.ENCHANTMENT_GLINT_OVERRIDE);
|
||||||
|
|
||||||
|
private static final Codec<DataComponentPatch> FILTERED_COMPONENT_MAP_CODEC = DataComponentPatch.CODEC.xmap(Function.identity(), patch -> {
|
||||||
|
DataComponentPatch.Builder filtered = DataComponentPatch.builder();
|
||||||
|
patch.entrySet().stream()
|
||||||
|
.filter(entry -> entry.getValue().isEmpty() || SUPPORTED_COMPONENTS.contains(entry.getKey()))
|
||||||
|
.forEach(entry -> {
|
||||||
|
if (entry.getValue().isPresent()) {
|
||||||
|
filtered.set((DataComponentType) entry.getKey(), entry.getValue().orElseThrow());
|
||||||
|
} else {
|
||||||
|
filtered.remove(entry.getKey());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return filtered.build();
|
||||||
|
});
|
||||||
|
|
||||||
|
public static final MapCodec<GeyserBaseDefinition> MAP_CODEC = RecordCodecBuilder.mapCodec(instance ->
|
||||||
|
instance.group(
|
||||||
|
ResourceLocation.CODEC.fieldOf("bedrock_identifier").forGetter(GeyserBaseDefinition::bedrockIdentifier),
|
||||||
|
Codec.STRING.optionalFieldOf("display_name").forGetter(GeyserBaseDefinition::displayName),
|
||||||
|
GeyserPredicate.LIST_CODEC.optionalFieldOf("predicate", List.of()).forGetter(GeyserBaseDefinition::predicates),
|
||||||
|
BedrockOptions.CODEC.optionalFieldOf("bedrock_options", BedrockOptions.DEFAULT).forGetter(GeyserBaseDefinition::bedrockOptions),
|
||||||
|
FILTERED_COMPONENT_MAP_CODEC.optionalFieldOf("components", DataComponentPatch.EMPTY).forGetter(GeyserBaseDefinition::components)
|
||||||
|
).apply(instance, GeyserBaseDefinition::new)
|
||||||
|
);
|
||||||
|
|
||||||
|
public boolean conflictsWith(GeyserBaseDefinition other) {
|
||||||
|
if (predicates.size() == other.predicates.size()) {
|
||||||
|
boolean predicatesAreEqual = true;
|
||||||
|
for (GeyserPredicate predicate : predicates) {
|
||||||
|
if (!other.predicates.contains(predicate)) {
|
||||||
|
predicatesAreEqual = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return predicatesAreEqual;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String textureName() {
|
||||||
|
return bedrockOptions.icon.orElse(Rainbow.fileSafeResourceLocation(bedrockIdentifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
public record BedrockOptions(Optional<String> icon, boolean allowOffhand, boolean displayHandheld, int protectionValue) {
|
||||||
|
public static final Codec<BedrockOptions> CODEC = RecordCodecBuilder.create(instance ->
|
||||||
|
instance.group(
|
||||||
|
Codec.STRING.optionalFieldOf("icon").forGetter(BedrockOptions::icon),
|
||||||
|
Codec.BOOL.optionalFieldOf("allow_offhand", true).forGetter(BedrockOptions::allowOffhand),
|
||||||
|
Codec.BOOL.optionalFieldOf("display_handheld", false).forGetter(BedrockOptions::displayHandheld),
|
||||||
|
Codec.INT.optionalFieldOf("protection_value", 0).forGetter(BedrockOptions::protectionValue)
|
||||||
|
).apply(instance, BedrockOptions::new)
|
||||||
|
);
|
||||||
|
public static final BedrockOptions DEFAULT = new BedrockOptions(Optional.empty(), true, false, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,16 +21,16 @@ public record GeyserGroupDefinition(Optional<ResourceLocation> model, List<Geyse
|
|||||||
return new GeyserGroupDefinition(model, Stream.concat(definitions.stream(), Stream.of(mapping)).toList());
|
return new GeyserGroupDefinition(model, Stream.concat(definitions.stream(), Stream.of(mapping)).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFor(ResourceLocation model) {
|
public boolean isFor(Optional<ResourceLocation> model) {
|
||||||
return this.model.isPresent() && this.model.get().equals(model);
|
return this.model.isPresent() && model.isPresent() && this.model.get().equals(model.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean conflictsWith(Optional<ResourceLocation> parentModel, GeyserSingleDefinition other) {
|
public boolean conflictsWith(Optional<ResourceLocation> parentModel, GeyserItemDefinition other) {
|
||||||
Optional<ResourceLocation> thisModel = model.or(() -> parentModel);
|
Optional<ResourceLocation> thisModel = model.or(() -> parentModel);
|
||||||
for (GeyserMapping definition : definitions) {
|
for (GeyserMapping definition : definitions) {
|
||||||
if (definition instanceof GeyserGroupDefinition group && group.conflictsWith(thisModel, other)) {
|
if (definition instanceof GeyserGroupDefinition group && group.conflictsWith(thisModel, other)) {
|
||||||
return true;
|
return true;
|
||||||
} else if (definition instanceof GeyserSingleDefinition single && single.conflictsWith(thisModel, other)) {
|
} else if (definition instanceof GeyserItemDefinition item && item.conflictsWith(thisModel, other)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package org.geysermc.rainbow.mapping.geyser;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public interface GeyserItemDefinition extends GeyserMapping {
|
||||||
|
|
||||||
|
GeyserBaseDefinition base();
|
||||||
|
|
||||||
|
boolean conflictsWith(Optional<ResourceLocation> parentModel, GeyserItemDefinition other);
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package org.geysermc.rainbow.mapping.geyser;
|
||||||
|
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public record GeyserLegacyDefinition(GeyserBaseDefinition base, int customModelData) implements GeyserItemDefinition {
|
||||||
|
|
||||||
|
public static final MapCodec<GeyserLegacyDefinition> CODEC = RecordCodecBuilder.mapCodec(instance ->
|
||||||
|
instance.group(
|
||||||
|
GeyserBaseDefinition.MAP_CODEC.forGetter(GeyserLegacyDefinition::base),
|
||||||
|
Codec.INT.fieldOf("custom_model_data").forGetter(GeyserLegacyDefinition::customModelData)
|
||||||
|
).apply(instance, GeyserLegacyDefinition::new)
|
||||||
|
);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean conflictsWith(Optional<ResourceLocation> parentModel, GeyserItemDefinition other) {
|
||||||
|
if (other instanceof GeyserLegacyDefinition otherLegacy) {
|
||||||
|
return customModelData == otherLegacy.customModelData && base.conflictsWith(otherLegacy.base);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type type() {
|
||||||
|
return Type.LEGACY;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ public interface GeyserMapping {
|
|||||||
|
|
||||||
enum Type implements StringRepresentable {
|
enum Type implements StringRepresentable {
|
||||||
SINGLE("definition", GeyserSingleDefinition.CODEC),
|
SINGLE("definition", GeyserSingleDefinition.CODEC),
|
||||||
|
LEGACY("legacy", GeyserLegacyDefinition.CODEC),
|
||||||
GROUP("group", GeyserGroupDefinition.CODEC);
|
GROUP("group", GeyserGroupDefinition.CODEC);
|
||||||
|
|
||||||
public static final Codec<Type> CODEC = StringRepresentable.fromEnum(Type::values);
|
public static final Codec<Type> CODEC = StringRepresentable.fromEnum(Type::values);
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ public class GeyserMappings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void map(Holder<Item> item, GeyserSingleDefinition mapping) {
|
public void map(Holder<Item> item, GeyserItemDefinition mapping) {
|
||||||
ResourceLocation model = mapping.model().orElseThrow();
|
Optional<ResourceLocation> model = mapping instanceof GeyserSingleDefinition single ? Optional.of(single.model().orElseThrow()) : Optional.empty();
|
||||||
Optional<GeyserGroupDefinition> modelGroup = Optional.empty();
|
Optional<GeyserGroupDefinition> modelGroup = Optional.empty();
|
||||||
|
|
||||||
Collection<GeyserMapping> existingMappings = new ArrayList<>(mappings.get(item));
|
Collection<GeyserMapping> existingMappings = new ArrayList<>(mappings.get(item));
|
||||||
@@ -48,19 +48,22 @@ public class GeyserMappings {
|
|||||||
}
|
}
|
||||||
modelGroup = Optional.of(existingGroup);
|
modelGroup = Optional.of(existingGroup);
|
||||||
break;
|
break;
|
||||||
} else if (existing instanceof GeyserSingleDefinition single) {
|
} else if (existing instanceof GeyserItemDefinition itemDefinition) {
|
||||||
if (single.conflictsWith(Optional.empty(), mapping)) {
|
if (itemDefinition.conflictsWith(Optional.empty(), mapping)) {
|
||||||
throw new IllegalArgumentException("Mapping conflicts with existing single mapping");
|
throw new IllegalArgumentException("Mapping conflicts with existing item mapping");
|
||||||
} else if (model.equals(single.model().orElseThrow())) {
|
} else if (model.isPresent() && itemDefinition instanceof GeyserSingleDefinition single && model.get().equals(single.model().orElseThrow())) {
|
||||||
mappings.remove(item, single);
|
mappings.remove(item, itemDefinition);
|
||||||
modelGroup = Optional.of(new GeyserGroupDefinition(Optional.of(model), List.of(single.withoutModel())));
|
modelGroup = Optional.of(new GeyserGroupDefinition(model, List.of(single.withoutModel())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modelGroup.isPresent()) {
|
if (modelGroup.isPresent()) {
|
||||||
mappings.remove(item, modelGroup.get());
|
mappings.remove(item, modelGroup.get());
|
||||||
mappings.put(item, modelGroup.get().with(mapping.withoutModel()));
|
|
||||||
|
// We're only putting mappings in groups when they're single definitions - legacy mappings always go ungrouped
|
||||||
|
assert mapping instanceof GeyserSingleDefinition;
|
||||||
|
mappings.put(item, modelGroup.get().with(((GeyserSingleDefinition) mapping).withoutModel()));
|
||||||
} else {
|
} else {
|
||||||
mappings.put(item, mapping);
|
mappings.put(item, mapping);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,96 +1,35 @@
|
|||||||
package org.geysermc.rainbow.mapping.geyser;
|
package org.geysermc.rainbow.mapping.geyser;
|
||||||
|
|
||||||
import com.mojang.serialization.Codec;
|
|
||||||
import com.mojang.serialization.MapCodec;
|
import com.mojang.serialization.MapCodec;
|
||||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
import net.minecraft.core.component.DataComponentPatch;
|
|
||||||
import net.minecraft.core.component.DataComponentType;
|
|
||||||
import net.minecraft.core.component.DataComponents;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import org.geysermc.rainbow.mapping.geyser.predicate.GeyserPredicate;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
// TODO other keys, etc.
|
|
||||||
// TODO sometimes still includes components key when patch before filtering is not empty but after is
|
|
||||||
// TODO display name can be a component
|
|
||||||
public record GeyserSingleDefinition(Optional<ResourceLocation> model, ResourceLocation bedrockIdentifier, Optional<String> displayName,
|
|
||||||
List<GeyserPredicate> predicates, BedrockOptions bedrockOptions, DataComponentPatch components) implements GeyserMapping {
|
|
||||||
private static final List<DataComponentType<?>> SUPPORTED_COMPONENTS = List.of(DataComponents.CONSUMABLE, DataComponents.EQUIPPABLE, DataComponents.FOOD,
|
|
||||||
DataComponents.MAX_DAMAGE, DataComponents.MAX_STACK_SIZE, DataComponents.USE_COOLDOWN, DataComponents.ENCHANTABLE, DataComponents.ENCHANTMENT_GLINT_OVERRIDE);
|
|
||||||
|
|
||||||
private static final Codec<DataComponentPatch> FILTERED_COMPONENT_MAP_CODEC = DataComponentPatch.CODEC.xmap(Function.identity(), patch -> {
|
|
||||||
DataComponentPatch.Builder filtered = DataComponentPatch.builder();
|
|
||||||
patch.entrySet().stream()
|
|
||||||
.filter(entry -> entry.getValue().isEmpty() || SUPPORTED_COMPONENTS.contains(entry.getKey()))
|
|
||||||
.forEach(entry -> {
|
|
||||||
if (entry.getValue().isPresent()) {
|
|
||||||
filtered.set((DataComponentType) entry.getKey(), entry.getValue().orElseThrow());
|
|
||||||
} else {
|
|
||||||
filtered.remove(entry.getKey());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return filtered.build();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
public record GeyserSingleDefinition(GeyserBaseDefinition base, Optional<ResourceLocation> model) implements GeyserItemDefinition {
|
||||||
public static final MapCodec<GeyserSingleDefinition> CODEC = RecordCodecBuilder.mapCodec(instance ->
|
public static final MapCodec<GeyserSingleDefinition> CODEC = RecordCodecBuilder.mapCodec(instance ->
|
||||||
instance.group(
|
instance.group(
|
||||||
ResourceLocation.CODEC.optionalFieldOf("model").forGetter(GeyserSingleDefinition::model),
|
GeyserBaseDefinition.MAP_CODEC.forGetter(GeyserSingleDefinition::base),
|
||||||
ResourceLocation.CODEC.fieldOf("bedrock_identifier").forGetter(GeyserSingleDefinition::bedrockIdentifier),
|
ResourceLocation.CODEC.optionalFieldOf("model").forGetter(GeyserSingleDefinition::model)
|
||||||
Codec.STRING.optionalFieldOf("display_name").forGetter(GeyserSingleDefinition::displayName),
|
|
||||||
GeyserPredicate.LIST_CODEC.optionalFieldOf("predicate", List.of()).forGetter(GeyserSingleDefinition::predicates),
|
|
||||||
BedrockOptions.CODEC.optionalFieldOf("bedrock_options", BedrockOptions.DEFAULT).forGetter(GeyserSingleDefinition::bedrockOptions),
|
|
||||||
FILTERED_COMPONENT_MAP_CODEC.optionalFieldOf("components", DataComponentPatch.EMPTY).forGetter(GeyserSingleDefinition::components)
|
|
||||||
).apply(instance, GeyserSingleDefinition::new)
|
).apply(instance, GeyserSingleDefinition::new)
|
||||||
);
|
);
|
||||||
|
|
||||||
public String textureName() {
|
@Override
|
||||||
return bedrockOptions.icon.orElse(iconFromResourceLocation(bedrockIdentifier));
|
public boolean conflictsWith(Optional<ResourceLocation> parentModel, GeyserItemDefinition other) {
|
||||||
}
|
if (other instanceof GeyserSingleDefinition otherSingle) {
|
||||||
|
ResourceLocation thisModel = model.or(() -> parentModel).orElseThrow();
|
||||||
public boolean conflictsWith(Optional<ResourceLocation> parentModel, GeyserSingleDefinition other) {
|
ResourceLocation otherModel = otherSingle.model.or(() -> parentModel).orElseThrow();
|
||||||
ResourceLocation thisModel = model.or(() -> parentModel).orElseThrow();
|
return thisModel.equals(otherModel) && base.conflictsWith(other.base());
|
||||||
ResourceLocation otherModel = other.model.or(() -> parentModel).orElseThrow();
|
|
||||||
if (!thisModel.equals(otherModel)) {
|
|
||||||
return false;
|
|
||||||
} else if (predicates.size() == other.predicates.size()) {
|
|
||||||
boolean predicatesAreEqual = true;
|
|
||||||
for (GeyserPredicate predicate : predicates) {
|
|
||||||
if (!other.predicates.contains(predicate)) {
|
|
||||||
predicatesAreEqual = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return predicatesAreEqual;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GeyserSingleDefinition withoutModel() {
|
public GeyserSingleDefinition withoutModel() {
|
||||||
return new GeyserSingleDefinition(Optional.empty(), bedrockIdentifier, displayName, predicates, bedrockOptions, components);
|
return new GeyserSingleDefinition(base, Optional.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type type() {
|
public Type type() {
|
||||||
return Type.SINGLE;
|
return Type.SINGLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public record BedrockOptions(Optional<String> icon, boolean allowOffhand, boolean displayHandheld, int protectionValue) {
|
|
||||||
public static final Codec<BedrockOptions> CODEC = RecordCodecBuilder.create(instance ->
|
|
||||||
instance.group(
|
|
||||||
Codec.STRING.optionalFieldOf("icon").forGetter(BedrockOptions::icon),
|
|
||||||
Codec.BOOL.optionalFieldOf("allow_offhand", true).forGetter(BedrockOptions::allowOffhand),
|
|
||||||
Codec.BOOL.optionalFieldOf("display_handheld", false).forGetter(BedrockOptions::displayHandheld),
|
|
||||||
Codec.INT.optionalFieldOf("protection_value", 0).forGetter(BedrockOptions::protectionValue)
|
|
||||||
).apply(instance, BedrockOptions::new)
|
|
||||||
);
|
|
||||||
public static final BedrockOptions DEFAULT = new BedrockOptions(Optional.empty(), true, false, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO this method is used in other places too now, maybe move it
|
|
||||||
public static String iconFromResourceLocation(ResourceLocation location) {
|
|
||||||
return location.toString().replace(':', '.').replace('/', '_');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package org.geysermc.rainbow.pack;
|
package org.geysermc.rainbow.pack;
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import org.geysermc.rainbow.mapping.geyser.GeyserSingleDefinition;
|
import org.geysermc.rainbow.Rainbow;
|
||||||
import org.geysermc.rainbow.pack.animation.BedrockAnimation;
|
import org.geysermc.rainbow.pack.animation.BedrockAnimation;
|
||||||
import org.geysermc.rainbow.pack.attachable.BedrockAttachable;
|
import org.geysermc.rainbow.pack.attachable.BedrockAttachable;
|
||||||
import org.geysermc.rainbow.pack.geometry.BedrockGeometry;
|
import org.geysermc.rainbow.pack.geometry.BedrockGeometry;
|
||||||
@@ -21,7 +21,7 @@ public record BedrockItem(ResourceLocation identifier, String textureName, Resou
|
|||||||
geometry.get().save(geometryDirectory);
|
geometry.get().save(geometryDirectory);
|
||||||
}
|
}
|
||||||
if (animation.isPresent()) {
|
if (animation.isPresent()) {
|
||||||
animation.get().save(animationDirectory, GeyserSingleDefinition.iconFromResourceLocation(identifier));
|
animation.get().save(animationDirectory, Rainbow.fileSafeResourceLocation(identifier));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import net.minecraft.util.StringRepresentable;
|
|||||||
import net.minecraft.world.entity.EquipmentSlot;
|
import net.minecraft.world.entity.EquipmentSlot;
|
||||||
import org.geysermc.rainbow.CodecUtil;
|
import org.geysermc.rainbow.CodecUtil;
|
||||||
import org.geysermc.rainbow.PackConstants;
|
import org.geysermc.rainbow.PackConstants;
|
||||||
import org.geysermc.rainbow.mapping.geyser.GeyserSingleDefinition;
|
import org.geysermc.rainbow.Rainbow;
|
||||||
import org.geysermc.rainbow.pack.BedrockTextures;
|
import org.geysermc.rainbow.pack.BedrockTextures;
|
||||||
import org.geysermc.rainbow.pack.BedrockVersion;
|
import org.geysermc.rainbow.pack.BedrockVersion;
|
||||||
import org.geysermc.rainbow.pack.geometry.BedrockGeometry;
|
import org.geysermc.rainbow.pack.geometry.BedrockGeometry;
|
||||||
@@ -37,7 +37,7 @@ public record BedrockAttachable(BedrockVersion formatVersion, AttachableInfo inf
|
|||||||
|
|
||||||
public void save(Path attachablesDirectory) throws IOException {
|
public void save(Path attachablesDirectory) throws IOException {
|
||||||
// Get a safe attachable path by using Geyser's way of getting icons
|
// Get a safe attachable path by using Geyser's way of getting icons
|
||||||
CodecUtil.trySaveJson(CODEC, this, attachablesDirectory.resolve(GeyserSingleDefinition.iconFromResourceLocation(info.identifier) + ".json"));
|
CodecUtil.trySaveJson(CODEC, this, attachablesDirectory.resolve(Rainbow.fileSafeResourceLocation(info.identifier) + ".json"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Builder builder(ResourceLocation identifier) {
|
public static Builder builder(ResourceLocation identifier) {
|
||||||
|
|||||||
Reference in New Issue
Block a user