1
0
mirror of https://github.com/GeyserMC/Rainbow.git synced 2025-12-19 14:59:16 +00:00

Use group definitions when generating mappings

This commit is contained in:
Eclipse
2025-07-03 08:32:59 +00:00
parent 2b2b32de16
commit e43e2250d5
5 changed files with 71 additions and 15 deletions

View File

@@ -6,6 +6,7 @@ import net.minecraft.resources.ResourceLocation;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Stream;
public record GeyserGroupDefinition(Optional<ResourceLocation> model, List<GeyserMapping> definitions) implements GeyserMapping { public record GeyserGroupDefinition(Optional<ResourceLocation> model, List<GeyserMapping> definitions) implements GeyserMapping {
@@ -16,6 +17,26 @@ public record GeyserGroupDefinition(Optional<ResourceLocation> model, List<Geyse
).apply(instance, GeyserGroupDefinition::new) ).apply(instance, GeyserGroupDefinition::new)
); );
public GeyserGroupDefinition with(GeyserMapping mapping) {
return new GeyserGroupDefinition(model, Stream.concat(definitions.stream(), Stream.of(mapping)).toList());
}
public boolean isFor(ResourceLocation model) {
return this.model.isPresent() && this.model.get().equals(model);
}
public boolean conflictsWith(Optional<ResourceLocation> parentModel, GeyserSingleDefinition other) {
Optional<ResourceLocation> thisModel = model.or(() -> parentModel);
for (GeyserMapping definition : definitions) {
if (definition instanceof GeyserGroupDefinition group && group.conflictsWith(thisModel, other)) {
return true;
} else if (definition instanceof GeyserSingleDefinition single && single.conflictsWith(thisModel, other)) {
return true;
}
}
return false;
}
@Override @Override
public Type type() { public Type type() {
return Type.GROUP; return Type.GROUP;

View File

@@ -128,7 +128,7 @@ public class GeyserItemMapper {
} }
public GeyserSingleDefinition create(ResourceLocation bedrockIdentifier) { public GeyserSingleDefinition create(ResourceLocation bedrockIdentifier) {
return new GeyserSingleDefinition(model, bedrockIdentifier, Optional.of(displayName), predicateStack, return new GeyserSingleDefinition(Optional.of(model), bedrockIdentifier, Optional.of(displayName), predicateStack,
new GeyserSingleDefinition.BedrockOptions(Optional.empty(), true, false, protectionValue), // TODO handheld prediction new GeyserSingleDefinition.BedrockOptions(Optional.empty(), true, false, protectionValue), // TODO handheld prediction
componentPatch); componentPatch);
} }

View File

@@ -1,13 +1,21 @@
package org.geysermc.packgenerator.mapping.geyser; package org.geysermc.packgenerator.mapping.geyser;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.MapCodec; import com.mojang.serialization.MapCodec;
import net.minecraft.util.StringRepresentable; import net.minecraft.util.StringRepresentable;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public interface GeyserMapping { public interface GeyserMapping {
Codec<GeyserMapping> CODEC = Type.CODEC.dispatch(GeyserMapping::type, Type::codec); Codec<GeyserMapping> CODEC = Codec.lazyInitialized(() -> Type.CODEC.dispatch(GeyserMapping::type, Type::codec));
// Not perfect since we're not checking single definitions in groups without a model... but good enough
Codec<GeyserMapping> MODEL_SAFE_CODEC = CODEC.validate(mapping -> {
if (mapping instanceof GeyserSingleDefinition single && single.model().isEmpty()) {
return DataResult.error(() -> "Top level single definition must have a model");
}
return DataResult.success(mapping);
});
Type type(); Type type();

View File

@@ -14,14 +14,14 @@ import org.geysermc.packgenerator.CodecUtil;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
// TODO group definitions
public class GeyserMappings { public class GeyserMappings {
private static final Codec<Map<Holder<Item>, Collection<GeyserSingleDefinition>>> MAPPINGS_CODEC = Codec.unboundedMap(Item.CODEC, GeyserSingleDefinition.CODEC.listOf().xmap(Function.identity(), ArrayList::new)); private static final Codec<Map<Holder<Item>, Collection<GeyserMapping>>> MAPPINGS_CODEC = Codec.unboundedMap(Item.CODEC, GeyserMapping.MODEL_SAFE_CODEC.listOf().xmap(Function.identity(), ArrayList::new));
public static final Codec<GeyserMappings> CODEC = RecordCodecBuilder.create(instance -> public static final Codec<GeyserMappings> CODEC = RecordCodecBuilder.create(instance ->
instance.group( instance.group(
@@ -30,24 +30,45 @@ public class GeyserMappings {
).apply(instance, (format, mappings) -> new GeyserMappings(mappings)) ).apply(instance, (format, mappings) -> new GeyserMappings(mappings))
); );
private final Multimap<Holder<Item>, GeyserSingleDefinition> mappings = MultimapBuilder.hashKeys().hashSetValues().build(); private final Multimap<Holder<Item>, GeyserMapping> mappings = MultimapBuilder.hashKeys().hashSetValues().build();
public GeyserMappings() {} public GeyserMappings() {}
private GeyserMappings(Map<Holder<Item>, Collection<GeyserSingleDefinition>> mappings) { private GeyserMappings(Map<Holder<Item>, Collection<GeyserMapping>> mappings) {
for (Holder<Item> item : mappings.keySet()) { for (Holder<Item> item : mappings.keySet()) {
this.mappings.putAll(item, mappings.get(item)); this.mappings.putAll(item, mappings.get(item));
} }
} }
public void map(Holder<Item> item, GeyserSingleDefinition mapping) { public void map(Holder<Item> item, GeyserSingleDefinition mapping) {
for (GeyserSingleDefinition existing : mappings.get(item)) { ResourceLocation model = mapping.model().orElseThrow();
if (existing.conflictsWith(mapping)) { Optional<GeyserGroupDefinition> modelGroup = Optional.empty();
throw new IllegalArgumentException("Mapping conflicts with existing mapping");
Collection<GeyserMapping> existingMappings = new ArrayList<>(mappings.get(item));
for (GeyserMapping existing : existingMappings) {
if (existing instanceof GeyserGroupDefinition existingGroup && existingGroup.isFor(model)) {
if (existingGroup.conflictsWith(Optional.empty(), mapping)) {
throw new IllegalArgumentException("Mapping conflicts with existing group mapping");
}
modelGroup = Optional.of(existingGroup);
break;
} else if (existing instanceof GeyserSingleDefinition single) {
if (single.conflictsWith(Optional.empty(), mapping)) {
throw new IllegalArgumentException("Mapping conflicts with existing single mapping");
} else if (model.equals(single.model().orElseThrow())) {
mappings.remove(item, single);
modelGroup = Optional.of(new GeyserGroupDefinition(Optional.of(model), List.of(single.withoutModel())));
} }
} }
}
if (modelGroup.isPresent()) {
mappings.remove(item, modelGroup.get());
mappings.put(item, modelGroup.get().with(mapping.withoutModel()));
} else {
mappings.put(item, mapping); mappings.put(item, mapping);
} }
}
public int size() { public int size() {
return mappings.size(); return mappings.size();
@@ -76,7 +97,7 @@ public class GeyserMappings {
}); });
} }
public Map<Holder<Item>, Collection<GeyserSingleDefinition>> mappings() { public Map<Holder<Item>, Collection<GeyserMapping>> mappings() {
return mappings.asMap(); return mappings.asMap();
} }
} }

View File

@@ -15,7 +15,7 @@ import java.util.function.Function;
// TODO other keys, etc. // TODO other keys, etc.
// TODO sometimes still includes components key when patch before filtering is not empty but after is // TODO sometimes still includes components key when patch before filtering is not empty but after is
public record GeyserSingleDefinition(ResourceLocation model, ResourceLocation bedrockIdentifier, Optional<String> displayName, public record GeyserSingleDefinition(Optional<ResourceLocation> model, ResourceLocation bedrockIdentifier, Optional<String> displayName,
List<GeyserPredicate> predicates, BedrockOptions bedrockOptions, DataComponentPatch components) implements GeyserMapping { List<GeyserPredicate> predicates, BedrockOptions bedrockOptions, DataComponentPatch components) implements GeyserMapping {
private static final List<DataComponentType<?>> SUPPORTED_COMPONENTS = List.of(DataComponents.CONSUMABLE, DataComponents.EQUIPPABLE, DataComponents.FOOD, 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); DataComponents.MAX_DAMAGE, DataComponents.MAX_STACK_SIZE, DataComponents.USE_COOLDOWN, DataComponents.ENCHANTABLE, DataComponents.ENCHANTMENT_GLINT_OVERRIDE);
@@ -36,7 +36,7 @@ public record GeyserSingleDefinition(ResourceLocation model, ResourceLocation be
public static final MapCodec<GeyserSingleDefinition> CODEC = RecordCodecBuilder.mapCodec(instance -> public static final MapCodec<GeyserSingleDefinition> CODEC = RecordCodecBuilder.mapCodec(instance ->
instance.group( instance.group(
ResourceLocation.CODEC.fieldOf("model").forGetter(GeyserSingleDefinition::model), ResourceLocation.CODEC.optionalFieldOf("model").forGetter(GeyserSingleDefinition::model),
ResourceLocation.CODEC.fieldOf("bedrock_identifier").forGetter(GeyserSingleDefinition::bedrockIdentifier), ResourceLocation.CODEC.fieldOf("bedrock_identifier").forGetter(GeyserSingleDefinition::bedrockIdentifier),
Codec.STRING.optionalFieldOf("display_name").forGetter(GeyserSingleDefinition::displayName), Codec.STRING.optionalFieldOf("display_name").forGetter(GeyserSingleDefinition::displayName),
GeyserPredicate.LIST_CODEC.optionalFieldOf("predicate", List.of()).forGetter(GeyserSingleDefinition::predicates), GeyserPredicate.LIST_CODEC.optionalFieldOf("predicate", List.of()).forGetter(GeyserSingleDefinition::predicates),
@@ -49,8 +49,10 @@ public record GeyserSingleDefinition(ResourceLocation model, ResourceLocation be
return bedrockOptions.icon.orElse(iconFromResourceLocation(bedrockIdentifier)); return bedrockOptions.icon.orElse(iconFromResourceLocation(bedrockIdentifier));
} }
public boolean conflictsWith(GeyserSingleDefinition other) { public boolean conflictsWith(Optional<ResourceLocation> parentModel, GeyserSingleDefinition other) {
if (!model.equals(other.model)) { ResourceLocation thisModel = model.or(() -> parentModel).orElseThrow();
ResourceLocation otherModel = other.model.or(() -> parentModel).orElseThrow();
if (!thisModel.equals(otherModel)) {
return false; return false;
} else if (predicates.size() == other.predicates.size()) { } else if (predicates.size() == other.predicates.size()) {
boolean predicatesAreEqual = true; boolean predicatesAreEqual = true;
@@ -65,6 +67,10 @@ public record GeyserSingleDefinition(ResourceLocation model, ResourceLocation be
return false; return false;
} }
public GeyserSingleDefinition withoutModel() {
return new GeyserSingleDefinition(Optional.empty(), bedrockIdentifier, displayName, predicates, bedrockOptions, components);
}
@Override @Override
public Type type() { public Type type() {
return Type.SINGLE; return Type.SINGLE;