mirror of
https://github.com/GeyserMC/Rainbow.git
synced 2025-12-19 14:59:16 +00:00
Work on actually mapping predicates, conditional predicates work
This commit is contained in:
@@ -15,6 +15,7 @@ public class GeyserMappingsGenerator implements ClientModInitializer {
|
||||
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
// TODO do the exceptions properly
|
||||
ClientCommandRegistrationCallback.EVENT.register((dispatcher, buildContext) -> {
|
||||
dispatcher.register(ClientCommandManager.literal("packgenerator")
|
||||
.then(ClientCommandManager.literal("create")
|
||||
|
||||
@@ -7,18 +7,13 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.geysermc.packgenerator.mappings.GeyserMapping;
|
||||
import org.geysermc.packgenerator.mappings.GeyserMappings;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public final class PackManager {
|
||||
public static final Path EXPORT_DIRECTORY = FabricLoader.getInstance().getGameDir()
|
||||
@@ -44,24 +39,16 @@ public final class PackManager {
|
||||
public boolean map(ItemStack stack, boolean throwOnModelMissing) throws CommandSyntaxException {
|
||||
ensurePackIsCreated();
|
||||
|
||||
Optional<? extends ResourceLocation> patchedModel = stack.getComponentsPatch().get(DataComponents.ITEM_MODEL);
|
||||
//noinspection OptionalAssignedToNull - annoying Mojang
|
||||
if (patchedModel == null || patchedModel.isEmpty()) {
|
||||
try {
|
||||
mappings.map(stack);
|
||||
return true;
|
||||
} catch (IllegalArgumentException exception) {
|
||||
if (throwOnModelMissing) {
|
||||
throw new SimpleCommandExceptionType(Component.literal("Item stack does not have a custom model")).create();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ResourceLocation model = patchedModel.get();
|
||||
String displayName = stack.getHoverName().getString();
|
||||
GeyserMapping mapping = new GeyserMapping(model, model, Optional.of(displayName),
|
||||
List.of(),
|
||||
new GeyserMapping.BedrockOptions(Optional.empty(), true, false, 0),
|
||||
stack.getComponentsPatch());
|
||||
mappings.map(stack.getItemHolder(), mapping);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void finish() throws CommandSyntaxException {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.geysermc.packgenerator.accessor;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
// Implemented on BlockModelWrapper, since this class doesn't store its model, we have to store it manually
|
||||
public interface BlockModelWrapperLocationAccessor {
|
||||
|
||||
ResourceLocation geyser_mappings_generator$getModelOrigin();
|
||||
|
||||
void geyser_mappings_generator$setModelOrigin(ResourceLocation model);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.geysermc.packgenerator.accessor;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import net.minecraft.client.renderer.item.ItemModel;
|
||||
|
||||
// Implemented on BlockModelWrapper, since this class doesn't store the cases it has in a nice format, we have to store it manually
|
||||
public interface SelectItemModelCaseAccessor<T> {
|
||||
|
||||
Object2ObjectMap<T, ItemModel> geyser_mappings_generator$getCases();
|
||||
|
||||
void geyser_mappings_generator$setCases(Object2ObjectMap<T, ItemModel> cases);
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package org.geysermc.packgenerator.mappings;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.item.BlockModelWrapper;
|
||||
import net.minecraft.client.renderer.item.ConditionalItemModel;
|
||||
import net.minecraft.client.renderer.item.ItemModel;
|
||||
import net.minecraft.client.renderer.item.SelectItemModel;
|
||||
import net.minecraft.client.renderer.item.properties.conditional.Broken;
|
||||
import net.minecraft.client.renderer.item.properties.conditional.CustomModelDataProperty;
|
||||
import net.minecraft.client.renderer.item.properties.conditional.Damaged;
|
||||
import net.minecraft.client.renderer.item.properties.conditional.FishingRodCast;
|
||||
import net.minecraft.client.renderer.item.properties.conditional.HasComponent;
|
||||
import net.minecraft.client.renderer.item.properties.conditional.ItemModelPropertyTest;
|
||||
import net.minecraft.client.renderer.item.properties.select.Charge;
|
||||
import net.minecraft.client.renderer.item.properties.select.ContextDimension;
|
||||
import net.minecraft.client.renderer.item.properties.select.SelectItemModelProperty;
|
||||
import net.minecraft.client.renderer.item.properties.select.TrimMaterialProperty;
|
||||
import net.minecraft.core.component.DataComponentPatch;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.geysermc.packgenerator.accessor.BlockModelWrapperLocationAccessor;
|
||||
import org.geysermc.packgenerator.mappings.predicate.GeyserConditionPredicate;
|
||||
import org.geysermc.packgenerator.mappings.predicate.GeyserMatchPredicate;
|
||||
import org.geysermc.packgenerator.mappings.predicate.GeyserPredicate;
|
||||
import org.geysermc.packgenerator.mixin.ConditionalItemModelAccessor;
|
||||
import org.geysermc.packgenerator.mixin.SelectItemModelAccessor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class GeyserItemMapper {
|
||||
|
||||
public static Stream<GeyserMapping> mapItem(ResourceLocation modelLocation, String displayName, int protectionValue, DataComponentPatch componentPatch) {
|
||||
MappingContext context = new MappingContext(List.of(), modelLocation, displayName, protectionValue, componentPatch);
|
||||
ItemModel model = Minecraft.getInstance().getModelManager().getItemModel(modelLocation);
|
||||
return mapItem(model, context);
|
||||
}
|
||||
|
||||
private static Stream<GeyserMapping> mapItem(ItemModel model, MappingContext context) {
|
||||
switch (model) {
|
||||
case BlockModelWrapper modelWrapper -> {
|
||||
ResourceLocation itemModel = ((BlockModelWrapperLocationAccessor) modelWrapper).geyser_mappings_generator$getModelOrigin();
|
||||
return Stream.of(context.create(itemModel));
|
||||
}
|
||||
case ConditionalItemModel conditional -> {
|
||||
return mapConditionalModel(conditional, context);
|
||||
}
|
||||
case SelectItemModel<?> select -> {
|
||||
return Stream.of();
|
||||
}
|
||||
default -> {}
|
||||
}
|
||||
throw new UnsupportedOperationException("Unable to map item model " + model.getClass());
|
||||
}
|
||||
|
||||
private static Stream<GeyserMapping> mapConditionalModel(ConditionalItemModel model, MappingContext context) {
|
||||
ItemModelPropertyTest property = ((ConditionalItemModelAccessor) model).getProperty();
|
||||
GeyserConditionPredicate.Property predicateProperty = switch (property) {
|
||||
case Broken ignored -> GeyserConditionPredicate.BROKEN;
|
||||
case Damaged ignored -> GeyserConditionPredicate.DAMAGED;
|
||||
case CustomModelDataProperty customModelData -> new GeyserConditionPredicate.CustomModelData(customModelData.index());
|
||||
case HasComponent hasComponent -> new GeyserConditionPredicate.HasComponent(hasComponent.componentType()); // ignoreDefault property not a thing, we should look into that in Geyser! TODO
|
||||
case FishingRodCast ignored -> GeyserConditionPredicate.FISHING_ROD_CAST;
|
||||
default -> throw new UnsupportedOperationException("Unsupported conditional model property " + property.getClass());
|
||||
};
|
||||
|
||||
ItemModel onTrue = ((ConditionalItemModelAccessor) model).getOnTrue();
|
||||
ItemModel onFalse = ((ConditionalItemModelAccessor) model).getOnFalse();
|
||||
|
||||
return Stream.concat(
|
||||
mapItem(onTrue, context.with(new GeyserConditionPredicate(predicateProperty, true))),
|
||||
mapItem(onFalse, context.with(new GeyserConditionPredicate(predicateProperty, false)))
|
||||
);
|
||||
}
|
||||
|
||||
private static <T> Stream<GeyserMapping> mapSelectModel(SelectItemModel<T> model, MappingContext context) {
|
||||
//noinspection unchecked
|
||||
SelectItemModelProperty<T> property = ((SelectItemModelAccessor<T>) model).getProperty();
|
||||
Function<T, GeyserMatchPredicate.MatchPredicateData> dataConstructor = switch (property) {
|
||||
case Charge ignored -> t -> new GeyserMatchPredicate.ChargeType(t);
|
||||
case TrimMaterialProperty trimMaterial -> ;
|
||||
case ContextDimension contextDimension -> ;
|
||||
case net.minecraft.client.renderer.item.properties.select.CustomModelDataProperty customModelData -> ; // Why, Mojang?
|
||||
}
|
||||
}
|
||||
|
||||
private record MappingContext(List<GeyserPredicate> predicateStack, ResourceLocation model, String displayName, int protectionValue, DataComponentPatch componentPatch) {
|
||||
|
||||
public MappingContext with(GeyserPredicate predicate) {
|
||||
return new MappingContext(Stream.concat(predicateStack.stream(), Stream.of(predicate)).toList(), model, displayName, protectionValue, componentPatch);
|
||||
}
|
||||
|
||||
public GeyserMapping create(ResourceLocation bedrockIdentifier) {
|
||||
return new GeyserMapping(model, bedrockIdentifier, Optional.of(displayName), predicateStack,
|
||||
new GeyserMapping.BedrockOptions(Optional.empty(), true, false, protectionValue), // TODO handheld prediction
|
||||
componentPatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
// TODO predicates, etc.
|
||||
// TODO other keys, etc.
|
||||
public record GeyserMapping(ResourceLocation model, 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,
|
||||
|
||||
@@ -5,13 +5,18 @@ import com.google.common.collect.MultimapBuilder;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
// TODO group definitions
|
||||
public class GeyserMappings {
|
||||
private static final Codec<Map<Holder<Item>, Collection<GeyserMapping>>> MAPPINGS_CODEC = Codec.unboundedMap(Item.CODEC, GeyserMapping.CODEC.listOf().xmap(Function.identity(), ArrayList::new));
|
||||
|
||||
@@ -37,6 +42,21 @@ public class GeyserMappings {
|
||||
mappings.put(item, mapping);
|
||||
}
|
||||
|
||||
public void map(ItemStack stack) {
|
||||
Optional<? extends ResourceLocation> patchedModel = stack.getComponentsPatch().get(DataComponents.ITEM_MODEL);
|
||||
//noinspection OptionalAssignedToNull - annoying Mojang
|
||||
if (patchedModel == null || patchedModel.isEmpty()) {
|
||||
throw new IllegalArgumentException("Item stack does not have a custom model");
|
||||
}
|
||||
|
||||
ResourceLocation model = patchedModel.get();
|
||||
String displayName = stack.getHoverName().getString();
|
||||
int protectionValue = 0; // TODO check the attributes
|
||||
|
||||
GeyserItemMapper.mapItem(model, displayName, protectionValue, stack.getComponentsPatch())
|
||||
.forEach(mapping -> map(stack.getItemHolder(), mapping));
|
||||
}
|
||||
|
||||
public Map<Holder<Item>, Collection<GeyserMapping>> mappings() {
|
||||
return mappings.asMap();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.geysermc.packgenerator.mixin;
|
||||
|
||||
import net.minecraft.client.renderer.item.BlockModelWrapper;
|
||||
import net.minecraft.client.renderer.item.ItemModel;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.geysermc.packgenerator.accessor.BlockModelWrapperLocationAccessor;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(BlockModelWrapper.class)
|
||||
public abstract class BlockModelWrapperMixin implements ItemModel, BlockModelWrapperLocationAccessor {
|
||||
|
||||
@Unique
|
||||
private ResourceLocation modelOrigin;
|
||||
|
||||
@Override
|
||||
public ResourceLocation geyser_mappings_generator$getModelOrigin() {
|
||||
return modelOrigin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void geyser_mappings_generator$setModelOrigin(ResourceLocation model) {
|
||||
modelOrigin = model;
|
||||
}
|
||||
|
||||
@Mixin(BlockModelWrapper.Unbaked.class)
|
||||
public abstract static class UnbakedMixin implements Unbaked {
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private ResourceLocation model;
|
||||
|
||||
@Inject(method = "bake", at = @At("TAIL"))
|
||||
public void setModelOrigin(BakingContext context, CallbackInfoReturnable<ItemModel> callbackInfoReturnable) {
|
||||
((BlockModelWrapperLocationAccessor) callbackInfoReturnable.getReturnValue()).geyser_mappings_generator$setModelOrigin(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.geysermc.packgenerator.mixin;
|
||||
|
||||
import net.minecraft.client.renderer.item.ConditionalItemModel;
|
||||
import net.minecraft.client.renderer.item.ItemModel;
|
||||
import net.minecraft.client.renderer.item.properties.conditional.ItemModelPropertyTest;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(ConditionalItemModel.class)
|
||||
public interface ConditionalItemModelAccessor {
|
||||
|
||||
@Accessor
|
||||
ItemModelPropertyTest getProperty();
|
||||
|
||||
@Accessor
|
||||
ItemModel getOnTrue();
|
||||
|
||||
@Accessor
|
||||
ItemModel getOnFalse();
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.geysermc.packgenerator.mixin;
|
||||
|
||||
import net.minecraft.client.renderer.item.SelectItemModel;
|
||||
import net.minecraft.client.renderer.item.properties.select.SelectItemModelProperty;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(SelectItemModel.class)
|
||||
public interface SelectItemModelAccessor<T> {
|
||||
|
||||
@Accessor
|
||||
SelectItemModelProperty<T> getProperty();
|
||||
|
||||
@Accessor
|
||||
SelectItemModel.ModelSelector<T> getModels();
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package org.geysermc.packgenerator.mixin;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import net.minecraft.client.renderer.item.ItemModel;
|
||||
import net.minecraft.client.renderer.item.SelectItemModel;
|
||||
import net.minecraft.client.renderer.item.properties.select.SelectItemModelProperty;
|
||||
import org.geysermc.packgenerator.accessor.SelectItemModelCaseAccessor;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(SelectItemModel.class)
|
||||
public abstract class SelectItemModelMixin<T> implements ItemModel, SelectItemModelCaseAccessor<T> {
|
||||
|
||||
@Unique
|
||||
private Object2ObjectMap<T, ItemModel> cases;
|
||||
|
||||
@Override
|
||||
public Object2ObjectMap<T, ItemModel> geyser_mappings_generator$getCases() {
|
||||
return cases;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void geyser_mappings_generator$setCases(Object2ObjectMap<T, ItemModel> cases) {
|
||||
this.cases = cases;
|
||||
}
|
||||
|
||||
@Mixin(SelectItemModel.UnbakedSwitch.class)
|
||||
public abstract static class UnbakedSwitchMixin<P extends SelectItemModelProperty<T>, T> {
|
||||
|
||||
@Inject(method = "bake", at = @At("TAIL"))
|
||||
public void setCases(BakingContext bakingContext, ItemModel model, CallbackInfoReturnable<ItemModel> callbackInfoReturnable,
|
||||
@Local Object2ObjectMap<T, ItemModel> cases) {
|
||||
//noinspection unchecked
|
||||
((SelectItemModelCaseAccessor<T>) callbackInfoReturnable.getReturnValue()).geyser_mappings_generator$setCases(cases);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,14 @@
|
||||
"package": "org.geysermc.packgenerator.mixin",
|
||||
"compatibilityLevel": "JAVA_21",
|
||||
"mixins": [],
|
||||
"client": [],
|
||||
"client": [
|
||||
"BlockModelWrapperMixin",
|
||||
"BlockModelWrapperMixin$UnbakedMixin",
|
||||
"ConditionalItemModelAccessor",
|
||||
"SelectItemModelAccessor",
|
||||
"SelectItemModelMixin",
|
||||
"SelectItemModelMixin$UnbakedSwitchMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user