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.Optional;
import java.util.stream.Stream;
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)
);
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
public Type type() {
return Type.GROUP;

View File

@@ -128,7 +128,7 @@ public class GeyserItemMapper {
}
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
componentPatch);
}

View File

@@ -1,13 +1,21 @@
package org.geysermc.packgenerator.mapping.geyser;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.MapCodec;
import net.minecraft.util.StringRepresentable;
import org.jetbrains.annotations.NotNull;
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();

View File

@@ -14,14 +14,14 @@ import org.geysermc.packgenerator.CodecUtil;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
// TODO group definitions
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 ->
instance.group(
@@ -30,24 +30,45 @@ public class GeyserMappings {
).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() {}
private GeyserMappings(Map<Holder<Item>, Collection<GeyserSingleDefinition>> mappings) {
private GeyserMappings(Map<Holder<Item>, Collection<GeyserMapping>> mappings) {
for (Holder<Item> item : mappings.keySet()) {
this.mappings.putAll(item, mappings.get(item));
}
}
public void map(Holder<Item> item, GeyserSingleDefinition mapping) {
for (GeyserSingleDefinition existing : mappings.get(item)) {
if (existing.conflictsWith(mapping)) {
throw new IllegalArgumentException("Mapping conflicts with existing mapping");
ResourceLocation model = mapping.model().orElseThrow();
Optional<GeyserGroupDefinition> modelGroup = Optional.empty();
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);
}
}
public int 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();
}
}

View File

@@ -15,7 +15,7 @@ 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
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 {
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);
@@ -36,7 +36,7 @@ public record GeyserSingleDefinition(ResourceLocation model, ResourceLocation be
public static final MapCodec<GeyserSingleDefinition> CODEC = RecordCodecBuilder.mapCodec(instance ->
instance.group(
ResourceLocation.CODEC.fieldOf("model").forGetter(GeyserSingleDefinition::model),
ResourceLocation.CODEC.optionalFieldOf("model").forGetter(GeyserSingleDefinition::model),
ResourceLocation.CODEC.fieldOf("bedrock_identifier").forGetter(GeyserSingleDefinition::bedrockIdentifier),
Codec.STRING.optionalFieldOf("display_name").forGetter(GeyserSingleDefinition::displayName),
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));
}
public boolean conflictsWith(GeyserSingleDefinition other) {
if (!model.equals(other.model)) {
public boolean conflictsWith(Optional<ResourceLocation> parentModel, GeyserSingleDefinition other) {
ResourceLocation thisModel = model.or(() -> parentModel).orElseThrow();
ResourceLocation otherModel = other.model.or(() -> parentModel).orElseThrow();
if (!thisModel.equals(otherModel)) {
return false;
} else if (predicates.size() == other.predicates.size()) {
boolean predicatesAreEqual = true;
@@ -65,6 +67,10 @@ public record GeyserSingleDefinition(ResourceLocation model, ResourceLocation be
return false;
}
public GeyserSingleDefinition withoutModel() {
return new GeyserSingleDefinition(Optional.empty(), bedrockIdentifier, displayName, predicates, bedrockOptions, components);
}
@Override
public Type type() {
return Type.SINGLE;