From 23211ba7b1079d3e305de54716dd712cc18cdd50 Mon Sep 17 00:00:00 2001 From: Sollace Date: Tue, 11 Jan 2022 12:22:24 +0200 Subject: [PATCH] Change how item traits are defined to make my life easier --- .../magic/spell/trait/SpellTraits.java | 33 ++++- .../magic/spell/trait/TraitLoader.java | 129 +++++++++++++----- 2 files changed, 123 insertions(+), 39 deletions(-) diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/SpellTraits.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/SpellTraits.java index 77ac56d8..2f66b215 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/SpellTraits.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/SpellTraits.java @@ -1,6 +1,7 @@ package com.minelittlepony.unicopia.ability.magic.spell.trait; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.EnumMap; import java.util.HashMap; @@ -17,6 +18,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import com.google.gson.JsonObject; +import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.client.gui.ItemTraitsTooltipRenderer; import com.minelittlepony.unicopia.util.InventoryUtil; @@ -32,7 +34,6 @@ import net.minecraft.network.PacketByteBuf; import net.minecraft.text.Text; import net.minecraft.util.Identifier; import net.minecraft.util.math.MathHelper; -import net.minecraft.util.registry.Registry; public final class SpellTraits implements Iterable> { public static final SpellTraits EMPTY = new SpellTraits(Map.of()); @@ -73,6 +74,10 @@ public final class SpellTraits implements Iterable> { return traits.isEmpty(); } + public boolean isPresent() { + return !isEmpty(); + } + public boolean includes(SpellTraits other) { return other.stream().allMatch(pair -> { return get(pair.getKey()) >= pair.getValue(); @@ -142,6 +147,19 @@ public final class SpellTraits implements Iterable> { return this == other || other instanceof SpellTraits && Objects.equals(traits, ((SpellTraits) other).traits); } + public static SpellTraits union(SpellTraits a, SpellTraits b) { + if (a.isEmpty()) { + return b; + } + if (b.isEmpty()) { + return a; + } + Map traits = new HashMap<>(); + combine(traits, a.traits); + combine(traits, b.traits); + return traits.isEmpty() ? EMPTY : new SpellTraits(traits); + } + public static SpellTraits union(SpellTraits...many) { Map traits = new HashMap<>(); for (SpellTraits i : many) { @@ -165,7 +183,7 @@ public final class SpellTraits implements Iterable> { } public static SpellTraits of(Item item) { - return TraitLoader.INSTANCE.values.getOrDefault(Registry.ITEM.getId(item), EMPTY); + return TraitLoader.INSTANCE.getTraits(item); } public static SpellTraits of(Block block) { @@ -221,6 +239,17 @@ public final class SpellTraits implements Iterable> { return Optional.of(new SpellTraits(entries)); } + public static Optional fromString(String traits) { + return fromEntries(Arrays.stream(traits.split(" ")).map(a -> a.split(":")).map(pair -> { + Trait key = Trait.fromName(pair[0]).orElse(null); + if (key == null) { + Unicopia.LOGGER.warn("Skipping unknown trait {}", pair[0]); + return null; + } + return Map.entry(key, Float.parseFloat(pair[1])); + })); + } + public static Stream> streamFromNbt(NbtCompound traits) { return traits.getKeys().stream().map(key -> { Trait trait = Trait.fromId(key).orElse(null); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/TraitLoader.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/TraitLoader.java index 64a83e14..dcedae05 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/TraitLoader.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/TraitLoader.java @@ -3,44 +3,55 @@ package com.minelittlepony.unicopia.ability.magic.spell.trait; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.reflect.TypeToken; import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.util.Resources; import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener; +import net.minecraft.item.Item; import net.minecraft.resource.Resource; import net.minecraft.resource.ResourceManager; import net.minecraft.resource.SinglePreparationResourceReloader; import net.minecraft.util.Identifier; -import net.minecraft.util.InvalidIdentifierException; import net.minecraft.util.JsonHelper; import net.minecraft.util.profiler.Profiler; +import net.minecraft.util.registry.Registry; -public class TraitLoader extends SinglePreparationResourceReloader> implements IdentifiableResourceReloadListener { +public class TraitLoader extends SinglePreparationResourceReloader> implements IdentifiableResourceReloadListener { private static final Identifier ID = new Identifier("unicopia", "data/traits"); - private static final TypeToken> TYPE = new TypeToken<>() {}; - public static final TraitLoader INSTANCE = new TraitLoader(); - Map values = new HashMap<>(); + private Map values = new HashMap<>(); @Override public Identifier getFabricId() { return ID; } + public SpellTraits getTraits(Item item) { + return values.getOrDefault(Registry.ITEM.getId(item), SpellTraits.EMPTY); + } + @Override - protected Map prepare(ResourceManager manager, Profiler profiler) { + protected Multimap prepare(ResourceManager manager, Profiler profiler) { profiler.startTick(); - Map prepared = new HashMap<>(); + Multimap prepared = HashMultimap.create(); for (Identifier path : new HashSet<>(manager.findResources("traits", p -> p.endsWith(".json")))) { profiler.push(path.toString()); @@ -49,49 +60,93 @@ public class TraitLoader extends SinglePreparationResourceReloader data = JsonHelper.deserialize(Resources.GSON, reader, TYPE); + JsonObject data = JsonHelper.deserialize(Resources.GSON, reader, JsonObject.class); - data.forEach((name, set) -> { - if (set.isEmpty()) { - return; - } + TraitStream set = TraitStream.of(path, resource.getResourcePackName(), data); - try { - Identifier id = new Identifier(name); - SpellTraits.fromEntries(Arrays.stream(set.split(" ")).map(a -> a.split(":")).map(pair -> { - Trait key = Trait.fromName(pair[0]).orElse(null); - if (key == null) { - Unicopia.LOGGER.warn("Failed to load trait entry for item {} in {}. {} is not a valid trait", id, resource.getResourcePackName(), pair[0]); - return null; - } - try { - return Map.entry(key, Float.parseFloat(pair[1])); - } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) { - Unicopia.LOGGER.warn("Failed to load trait entry for item {} in {}. {} is not a valid weighting", id, resource.getResourcePackName(), Arrays.toString(pair)); - return null; - } - })).ifPresent(value -> prepared.put(id, value)); - } catch (InvalidIdentifierException e) { - Unicopia.LOGGER.warn("Failed to load traits for item {} in {}.", name, resource.getResourcePackName(), e); - } - }); + if (set.replace()) { + prepared.removeAll(path); + } + prepared.put(path, set); + } catch (JsonParseException e) { + Unicopia.LOGGER.error("Error reading traits file " + resource.getResourcePackName() + ":" + path, e); } finally { profiler.pop(); } - } - } catch (IOException | JsonParseException e) { + } catch (IOException e) { + Unicopia.LOGGER.error("Error reading traits file " + path, e); } finally { profiler.pop(); } } - + profiler.endTick(); return prepared; } @Override - protected void apply(Map prepared, ResourceManager manager, Profiler profiler) { - values = prepared; + protected void apply(Multimap prepared, ResourceManager manager, Profiler profiler) { + profiler.startTick(); + values = prepared.values().stream() + .flatMap(TraitStream::entries) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, SpellTraits::union)); + profiler.endTick(); } + + interface TraitStream { + TypeToken> TYPE = new TypeToken<>() {}; + + boolean replace(); + + Stream> entries(); + + static TraitStream of(Identifier id, String pack, JsonObject json) { + + if (json.has("items") && json.get("items").isJsonObject()) { + return new TraitMap(JsonHelper.getBoolean(json, "replace", false), + Resources.GSON.getAdapter(TYPE).fromJsonTree(json.get("items")).entrySet().stream().collect(Collectors.toMap( + a -> Identifier.tryParse(a.getKey()), + a -> SpellTraits.fromString(a.getValue()).orElse(SpellTraits.EMPTY) + )) + ); + } + + return new TraitSet( + JsonHelper.getBoolean(json, "replace", false), + SpellTraits.fromString(JsonHelper.getString(json, "traits")).orElse(SpellTraits.EMPTY), + StreamSupport.stream(JsonHelper.getArray(json, "items").spliterator(), false) + .map(JsonElement::getAsString) + .map(Identifier::tryParse) + .filter(item -> { + if (item == null || !Registry.ITEM.containsId(item)) { + Unicopia.LOGGER.warn("Skipping unknown item {} in {}:{}", item, pack, id); + return false; + } + return true; + }) + .collect(Collectors.toSet()) + ); + } + + record TraitMap ( + boolean replace, + Map items) implements TraitStream { + @Override + public Stream> entries() { + return items.entrySet().stream(); + } + } + + record TraitSet ( + boolean replace, + SpellTraits traits, + Set items) implements TraitStream { + @Override + public Stream> entries() { + return items().stream().map(item -> Map.entry(item, traits())); + } + } + } + }