Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ If you have any questions or run into any problems, please do feel free to ask f

Rainbow is currently capable of the following:

- Generating Geyser item mappings complete with data components and proper bedrock options, by detecting items with a custom `minecraft:item_model` component and analysing their components.
- Generating Geyser item mappings complete with data components and proper bedrock options, by detecting items with custom `minecraft:item_model` or `minecraft:custom_model_data` components, and analysing their components.
- Also includes generating mappings with predicates for more complicated Java item model definitions, such as checks for if an item is broken. The following definition types are currently supported by Rainbow:
- Plain item model definitions.
- Conditional item models, supported properties are:
- `broken`,
- `damaged`,
- `cutom_model_data`,
- `custom_model_data`,
- `has_component`, and,
- `fishing_rod/cast`.
- Range dispatch item models, supported properties are:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,10 @@ private static <T> Identifier getId(ExtraCodecs.LateBoundIdMapper<Identifier, T>
return ((LateBoundIdMapperAccessor<Identifier, ?>) mapper).getIdToValue().inverse().get(type);
}

public static void tryMapStack(ItemStackTemplate stack, Identifier modelIdentifier, ProblemReporter reporter, PackContext context) {
public static void tryMapStack(ItemStackTemplate stack, Identifier modelIdentifier, ProblemReporter reporter, PackContext context, boolean ignoreTopPlainModel) {
context.assetResolver().getClientItem(modelIdentifier).map(ClientItem::model)
.ifPresentOrElse(model -> mapItem(model, stack, reporter.forChild(() -> "client item definition " + modelIdentifier + " "), base -> new GeyserSingleDefinition(base, Optional.of(modelIdentifier)), context),
.ifPresentOrElse(model -> mapItem(model, stack, reporter.forChild(() -> "client item definition " + modelIdentifier + " "),
base -> new GeyserSingleDefinition(base, Optional.of(modelIdentifier)), context, ignoreTopPlainModel),
() -> reporter.report(() -> "missing client item definition " + modelIdentifier));
}

Expand All @@ -87,14 +88,18 @@ public static void tryMapStack(ItemStackTemplate stack, int customModelData, Pro
// WHY, Mojang?
if (property instanceof net.minecraft.client.renderer.item.properties.numeric.CustomModelDataProperty(int index)) {
if (index == 0) {
List<RangeSelectItemModel.Entry> sortedEntries = entries.stream()
.sorted(RangeSelectItemModel.Entry.BY_THRESHOLD)
.toList();
float scaledCustomModelData = customModelData * scale;

float[] thresholds = ArrayUtils.toPrimitive(entries.stream()
float[] thresholds = ArrayUtils.toPrimitive(sortedEntries.stream()
.map(RangeSelectItemModel.Entry::threshold)
.toArray(Float[]::new));
int modelIndex = RangeSelectItemModelAccessor.invokeLastIndexLessOrEqual(thresholds, scaledCustomModelData);
Optional<ItemModel.Unbaked> model = modelIndex == -1 ? fallback : Optional.of(entries.get(modelIndex).model());
model.ifPresentOrElse(present -> mapItem(present, stack, childReporter, base -> new GeyserLegacyDefinition(base, customModelData), context),
Optional<ItemModel.Unbaked> model = modelIndex == -1 ? fallback : Optional.of(sortedEntries.get(modelIndex).model());
model.ifPresentOrElse(present -> mapItem(present, stack, childReporter,
base -> new GeyserLegacyDefinition(base, customModelData), context, false),
() -> childReporter.report(() -> "custom model data index lookup returned -1, and no fallback is present"));
} else {
childReporter.report(() -> "range_dispatch custom model data property index is not zero, unable to apply custom model data");
Expand All @@ -106,13 +111,20 @@ public static void tryMapStack(ItemStackTemplate stack, int customModelData, Pro
}

public static void mapItem(ItemModel.Unbaked model, ItemStackTemplate stack, ProblemReporter reporter,
Function<GeyserBaseDefinition, GeyserItemDefinition> definitionCreator, PackContext packContext) {
mapItem(model, new MappingContext(stack, reporter, definitionCreator, packContext));
Function<GeyserBaseDefinition, GeyserItemDefinition> definitionCreator, PackContext packContext,
boolean ignoreTopPlainModel) {
mapItem(model, new MappingContext(stack, reporter, definitionCreator, packContext, ignoreTopPlainModel));
}

private static void mapItem(ItemModel.Unbaked model, MappingContext context) {
switch (model) {
case CuboidItemModelWrapper.Unbaked modelWrapper -> mapBlockModelWrapper(modelWrapper, context.child("plain model " + modelWrapper.model()));
case CuboidItemModelWrapper.Unbaked modelWrapper -> {
if (context.ignorePlainModel) {
context.report("ignoring plain model as requested by context");
} else {
mapBlockModelWrapper(modelWrapper, context.child("plain model " + modelWrapper.model()));
}
}
case ConditionalItemModel.Unbaked conditional -> mapConditionalModel(conditional, context.child("condition model "));
case RangeSelectItemModel.Unbaked rangeSelect -> mapRangeSelectModel(rangeSelect, context.child("range select model "));
case SelectItemModel.Unbaked select -> mapSelectModel(select, context.child("select model "));
Expand Down Expand Up @@ -208,22 +220,28 @@ private static void mapSelectModel(SelectItemModel.Unbaked model, MappingContext

private record MappingContext(List<GeyserPredicate> predicateStack, Optional<Transformation> transformationStack,
ItemStackTemplate itemStack, ProblemReporter reporter,
Function<GeyserBaseDefinition, GeyserItemDefinition> definitionCreator, PackContext packContext) {
Function<GeyserBaseDefinition, GeyserItemDefinition> definitionCreator, PackContext packContext,
boolean ignorePlainModel) {

public MappingContext(ItemStackTemplate stack, ProblemReporter reporter, Function<GeyserBaseDefinition, GeyserItemDefinition> definitionCreator, PackContext packContext) {
this(List.of(), Optional.empty(), stack, reporter, definitionCreator, packContext);
public MappingContext(ItemStackTemplate stack, ProblemReporter reporter, Function<GeyserBaseDefinition, GeyserItemDefinition> definitionCreator, PackContext packContext,
boolean ignorePlainModel) {
this(List.of(), Optional.empty(), stack, reporter, definitionCreator, packContext, ignorePlainModel);
}

// Only copy ignorePlainModel when there is not a predicate
public MappingContext with(GeyserPredicate predicate, Optional<Transformation> transformation, String childName) {
return new MappingContext(Stream.concat(predicateStack.stream(), Stream.of(predicate)).toList(), addTransformation(transformation), itemStack, reporter.forChild(() -> childName), definitionCreator, packContext);
return new MappingContext(Stream.concat(predicateStack.stream(), Stream.of(predicate)).toList(), addTransformation(transformation), itemStack,
reporter.forChild(() -> childName), definitionCreator, packContext, false);
}

public MappingContext with(Optional<Transformation> transformation, String childName) {
return new MappingContext(predicateStack, addTransformation(transformation), itemStack, reporter.forChild(() -> childName), definitionCreator, packContext);
return new MappingContext(predicateStack, addTransformation(transformation), itemStack,
reporter.forChild(() -> childName), definitionCreator, packContext, ignorePlainModel);
}

public MappingContext child(String childName) {
return new MappingContext(predicateStack, transformationStack, itemStack, reporter.forChild(() -> childName), definitionCreator, packContext);
return new MappingContext(predicateStack, transformationStack, itemStack,
reporter.forChild(() -> childName), definitionCreator, packContext, ignorePlainModel);
}

public Transformation finaliseTransformation(Optional<Transformation> finalTransformation) {
Expand Down
36 changes: 25 additions & 11 deletions rainbow/src/main/java/org/geysermc/rainbow/pack/BedrockPack.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.nio.file.Path;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
Expand Down Expand Up @@ -83,23 +84,32 @@ public void report(Problem problem) {
}
};

if (!stack.components().split().added().has(DataComponents.ITEM_MODEL)) {
Identifier customModel = stack.components().split().added().get(DataComponents.ITEM_MODEL);
if (customModel == null) {
// If no custom item_model patch exists, try custom model data
CustomModelData customModelData = stack.components().split().added().get(DataComponents.CUSTOM_MODEL_DATA);
Float firstNumber;
if (customModelData == null || (firstNumber = customModelData.getFloat(0)) == null
|| !customModelDataMapped.add(Pair.of(stack.item(), firstNumber.intValue()))) {
if (customModelData == null) {
return MappingResult.NONE_MAPPED;
} else if (isLegacyCustomModelData(customModelData)) {
// Legacy custom model data - only one float, nothing else
int customModelInt = Objects.requireNonNull(customModelData.getFloat(0)).intValue();
if (!customModelDataMapped.add(Pair.of(stack.item(), customModelInt))) {
return MappingResult.NONE_MAPPED;
}
BedrockItemMapper.tryMapStack(stack, customModelInt, mapReporter, context);
} else {
// Try to map the vanilla model, but ignore the first direct plain model if present - this is the vanilla case
Identifier vanillaModel = Objects.requireNonNull(stack.get(DataComponents.ITEM_MODEL));
if (!modelsMapped.add(vanillaModel)) {
return MappingResult.NONE_MAPPED;
}
BedrockItemMapper.tryMapStack(stack, vanillaModel, mapReporter, context, true);
}

BedrockItemMapper.tryMapStack(stack, firstNumber.intValue(), mapReporter, context);
} else {
Identifier model = stack.components().split().added().get(DataComponents.ITEM_MODEL);
assert model != null;
if (!modelsMapped.add(model)) {
if (!modelsMapped.add(customModel)) {
return MappingResult.NONE_MAPPED;
}

BedrockItemMapper.tryMapStack(stack, model, mapReporter, context);
BedrockItemMapper.tryMapStack(stack, customModel, mapReporter, context, false);
}

return problems.get() ? MappingResult.PROBLEMS_OCCURRED : MappingResult.MAPPED_SUCCESSFULLY;
Expand Down Expand Up @@ -157,6 +167,10 @@ private PackSerializingContext createSerializingContext() {
return new PackSerializingContext(context.assetResolver(), serializer, paths, reporter);
}

private static boolean isLegacyCustomModelData(CustomModelData customModelData) {
return customModelData.floats().size() == 1 && customModelData.colors().isEmpty() && customModelData.flags().isEmpty() && customModelData.strings().isEmpty();
}

public static Builder builder(String name, Path mappingsPath, Path packRootPath, PackSerializer packSerializer, AssetResolver assetResolver) {
return new Builder(name, mappingsPath, packRootPath, packSerializer, assetResolver);
}
Expand Down
Loading