diff --git a/src/main/java/com/minelittlepony/unicopia/InteractionManager.java b/src/main/java/com/minelittlepony/unicopia/InteractionManager.java index 9595321f..d1910296 100644 --- a/src/main/java/com/minelittlepony/unicopia/InteractionManager.java +++ b/src/main/java/com/minelittlepony/unicopia/InteractionManager.java @@ -7,7 +7,7 @@ import java.util.UUID; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import com.minelittlepony.unicopia.container.SpellbookChapter; +import com.minelittlepony.unicopia.container.spellbook.SpellbookChapter; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.dummy.DummyPlayerEntity; import com.minelittlepony.unicopia.particle.ParticleSpawner; diff --git a/src/main/java/com/minelittlepony/unicopia/Unicopia.java b/src/main/java/com/minelittlepony/unicopia/Unicopia.java index 6c195056..46e1c4a6 100644 --- a/src/main/java/com/minelittlepony/unicopia/Unicopia.java +++ b/src/main/java/com/minelittlepony/unicopia/Unicopia.java @@ -18,8 +18,8 @@ import com.minelittlepony.unicopia.block.UBlocks; import com.minelittlepony.unicopia.block.state.StateMapLoader; import com.minelittlepony.unicopia.command.Commands; import com.minelittlepony.unicopia.compat.trinkets.TrinketsDelegate; -import com.minelittlepony.unicopia.container.SpellbookChapterLoader; import com.minelittlepony.unicopia.container.UScreenHandlers; +import com.minelittlepony.unicopia.container.spellbook.SpellbookChapterLoader; import com.minelittlepony.unicopia.diet.DietsLoader; import com.minelittlepony.unicopia.diet.affliction.AfflictionType; import com.minelittlepony.unicopia.entity.damage.UDamageTypes; diff --git a/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java b/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java index 45ba0074..f2b5073a 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java +++ b/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java @@ -19,7 +19,7 @@ import com.minelittlepony.unicopia.client.gui.DismissSpellScreen; import com.minelittlepony.unicopia.client.gui.spellbook.ClientChapters; import com.minelittlepony.unicopia.client.particle.ClientBoundParticleSpawner; import com.minelittlepony.unicopia.client.sound.*; -import com.minelittlepony.unicopia.container.SpellbookChapter; +import com.minelittlepony.unicopia.container.spellbook.SpellbookChapter; import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.entity.player.PlayerPhysics; import com.minelittlepony.unicopia.entity.player.Pony; diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/ClientChapters.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/ClientChapters.java index 3f606311..59e818e3 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/ClientChapters.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/ClientChapters.java @@ -3,7 +3,8 @@ package com.minelittlepony.unicopia.client.gui.spellbook; import java.util.*; import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.*; import com.minelittlepony.unicopia.client.gui.spellbook.element.DynamicContent; -import com.minelittlepony.unicopia.container.SpellbookChapter; +import com.minelittlepony.unicopia.container.spellbook.SpellbookChapter; +import com.minelittlepony.unicopia.container.spellbook.TabSide; import net.minecraft.network.PacketByteBuf; import net.minecraft.util.*; @@ -26,7 +27,7 @@ public class ClientChapters { buffer.readEnumConstant(TabSide.class), buffer.readInt(), buffer.readInt(), - buffer.readOptional(DynamicContent::new) + Optional.of(new DynamicContent(buffer)) ); } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookChapterList.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookChapterList.java index 2cd22233..c1938e48 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookChapterList.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookChapterList.java @@ -6,7 +6,8 @@ import java.util.stream.Stream; import com.minelittlepony.common.client.gui.IViewRoot; import com.minelittlepony.unicopia.Debug; -import com.minelittlepony.unicopia.container.SpellbookChapter; +import com.minelittlepony.unicopia.container.spellbook.SpellbookChapter; +import com.minelittlepony.unicopia.container.spellbook.TabSide; import net.minecraft.client.gui.DrawContext; import net.minecraft.util.Identifier; @@ -59,11 +60,6 @@ public class SpellbookChapterList { } } - public enum TabSide { - LEFT, - RIGHT - } - public interface Content extends Drawable { void init(SpellbookScreen screen, Identifier pageId); diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookScreen.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookScreen.java index 2a7337bc..52442026 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookScreen.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookScreen.java @@ -19,6 +19,7 @@ import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.*; import com.minelittlepony.unicopia.compat.trinkets.TrinketSlotBackSprites; import com.minelittlepony.unicopia.container.*; import com.minelittlepony.unicopia.container.inventory.*; +import com.minelittlepony.unicopia.container.spellbook.TabSide; import com.minelittlepony.unicopia.network.Channel; import com.minelittlepony.unicopia.network.MsgSpellbookStateChanged; import com.mojang.blaze3d.platform.GlStateManager; diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookTabBar.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookTabBar.java index 8888e305..bdaec232 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookTabBar.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/SpellbookTabBar.java @@ -6,7 +6,7 @@ import java.util.stream.Stream; import com.minelittlepony.common.client.gui.dimension.Bounds; import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.Chapter; -import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.TabSide; +import com.minelittlepony.unicopia.container.spellbook.TabSide; import net.minecraft.util.Identifier; diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java index fad7e65d..a8e4c684 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/DynamicContent.java @@ -9,8 +9,8 @@ import com.minelittlepony.unicopia.client.gui.MagicText; import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookScreen; import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.Content; import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.Drawable; -import com.minelittlepony.unicopia.container.SpellbookChapterLoader.Flow; import com.minelittlepony.unicopia.container.SpellbookState; +import com.minelittlepony.unicopia.container.spellbook.Flow; import com.minelittlepony.unicopia.entity.player.Pony; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawContext; diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Image.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Image.java index 090de1f7..da1f537a 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Image.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/Image.java @@ -2,7 +2,7 @@ package com.minelittlepony.unicopia.client.gui.spellbook.element; import com.minelittlepony.common.client.gui.IViewRoot; import com.minelittlepony.common.client.gui.dimension.Bounds; -import com.minelittlepony.unicopia.container.SpellbookChapterLoader.Flow; +import com.minelittlepony.unicopia.container.spellbook.Flow; import net.minecraft.client.gui.DrawContext; import net.minecraft.util.Identifier; diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/PageElement.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/PageElement.java index e8f9708f..48383c4b 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/PageElement.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/PageElement.java @@ -12,7 +12,9 @@ import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; import com.minelittlepony.unicopia.block.state.Schematic; import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.Drawable; -import com.minelittlepony.unicopia.container.SpellbookChapterLoader.Flow; +import com.minelittlepony.unicopia.container.spellbook.ChapterPageElement; +import com.minelittlepony.unicopia.container.spellbook.Flow; + import net.minecraft.client.gui.DrawContext; import net.minecraft.network.PacketByteBuf; import net.minecraft.network.RegistryByteBuf; @@ -45,24 +47,22 @@ public interface PageElement extends Drawable { static PageElement read(DynamicContent.Page page, PacketByteBuf buffer) { byte type = buffer.readByte(); return switch (type) { - case 0 -> new Image(buffer.readIdentifier(), boundsFromBuffer(buffer), buffer.readEnumConstant(Flow.class)); - case 1 -> new Recipe(page, buffer.readIdentifier(), Bounds.empty()); - case 2 -> new Stack(page, IngredientWithSpell.PACKET_CODEC.decode((RegistryByteBuf)buffer), boundsFromBuffer(buffer)); - case 3 -> new TextBlock(page, List.of(Suppliers.ofInstance(TextCodecs.PACKET_CODEC.decode(buffer)))); - case 4 -> new TextBlock(page, buffer.readList(b -> { + case ChapterPageElement.IMAGE -> new Image(buffer.readIdentifier(), boundsFromBuffer(buffer), buffer.readEnumConstant(Flow.class)); + case ChapterPageElement.RECIPE -> new Recipe(page, buffer.readIdentifier(), Bounds.empty()); + case ChapterPageElement.STACK -> new Stack(page, IngredientWithSpell.PACKET_CODEC.decode((RegistryByteBuf)buffer), boundsFromBuffer(buffer)); + case ChapterPageElement.TEXT_BLOCK -> new TextBlock(page, List.of(Suppliers.ofInstance(TextCodecs.PACKET_CODEC.decode(buffer)))); + case ChapterPageElement.INGREDIENTS -> new TextBlock(page, buffer.readList(b -> { int count = b.readVarInt(); byte t = b.readByte(); return switch (t) { - case 1 -> formatLine(capture(b.readIdentifier(), id -> { - return Registries.ITEM.get(id).getDefaultStack().getName(); - }), "item", count); + case 1 -> formatLine(capture(b.readIdentifier(), id -> Registries.ITEM.get(id).getDefaultStack().getName()), "item", count); case 2 -> formatLine(Trait.PACKET_CODEC.decode(b)::getShortName, "trait", count); case 3 -> Suppliers.ofInstance(TextCodecs.PACKET_CODEC.decode(b)); case 4 -> formatLine(SpellType.getKey(b.readIdentifier())::getName, "spell", count); default -> throw new IllegalArgumentException("Unexpected value: " + t); }; })); - case 5 -> new Structure(Bounds.empty(), Schematic.fromPacket(buffer)); + case ChapterPageElement.STRUCTURE -> new Structure(Bounds.empty(), Schematic.fromPacket(buffer)); default -> throw new IllegalArgumentException("Unexpected value: " + type); }; } diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/TextBlock.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/TextBlock.java index 165b75cf..0acb1374 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/TextBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/element/TextBlock.java @@ -7,7 +7,7 @@ import java.util.function.Supplier; import com.minelittlepony.common.client.gui.IViewRoot; import com.minelittlepony.common.client.gui.dimension.Bounds; import com.minelittlepony.unicopia.client.gui.ParagraphWrappingVisitor; -import com.minelittlepony.unicopia.container.SpellbookChapterLoader.Flow; +import com.minelittlepony.unicopia.container.spellbook.Flow; import com.minelittlepony.unicopia.entity.player.Pony; import net.minecraft.client.MinecraftClient; diff --git a/src/main/java/com/minelittlepony/unicopia/container/SpellbookChapterLoader.java b/src/main/java/com/minelittlepony/unicopia/container/SpellbookChapterLoader.java deleted file mode 100644 index ba0cdeac..00000000 --- a/src/main/java/com/minelittlepony/unicopia/container/SpellbookChapterLoader.java +++ /dev/null @@ -1,362 +0,0 @@ -package com.minelittlepony.unicopia.container; - -import java.util.*; -import java.util.concurrent.*; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; - -import org.slf4j.Logger; - -import com.google.gson.*; -import com.minelittlepony.common.client.gui.dimension.Bounds; -import com.minelittlepony.unicopia.Debug; -import com.minelittlepony.unicopia.Unicopia; -import com.minelittlepony.unicopia.ability.magic.spell.crafting.IngredientWithSpell; -import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; -import com.minelittlepony.unicopia.block.state.StateUtil; -import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.*; -import com.minelittlepony.unicopia.network.Channel; -import com.minelittlepony.unicopia.network.MsgServerResources; -import com.minelittlepony.unicopia.util.Resources; -import com.mojang.logging.LogUtils; -import com.mojang.serialization.JsonOps; - -import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener; -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; -import net.minecraft.network.PacketByteBuf; -import net.minecraft.network.RegistryByteBuf; -import net.minecraft.registry.DynamicRegistryManager; -import net.minecraft.resource.JsonDataLoader; -import net.minecraft.resource.ResourceManager; -import net.minecraft.server.MinecraftServer; -import net.minecraft.text.Text; -import net.minecraft.text.TextCodecs; -import net.minecraft.text.TextColor; -import net.minecraft.util.*; -import net.minecraft.util.profiler.Profiler; - -public class SpellbookChapterLoader extends JsonDataLoader implements IdentifiableResourceReloadListener { - private static final Logger LOGGER = LogUtils.getLogger(); - private static final Identifier ID = Unicopia.id("spellbook/chapters"); - private static final Executor EXECUTOR = CompletableFuture.delayedExecutor(5, TimeUnit.SECONDS); - - public static final SpellbookChapterLoader INSTANCE = new SpellbookChapterLoader(); - - private boolean dirty; - private Map chapters = new HashMap<>(); - - public SpellbookChapterLoader() { - super(Resources.GSON, ID.getPath()); - } - - @Override - public Identifier getFabricId() { - return ID; - } - - public Map getChapters() { - return chapters; - } - - public void sendUpdate(MinecraftServer server) { - if (dirty) { - dirty = false; - Channel.SERVER_RESOURCES.sendToAllPlayers(new MsgServerResources(), server); - } - } - - @Override - protected void apply(Map data, ResourceManager manager, Profiler profiler) { - try { - chapters = data.entrySet().stream().collect(Collectors.toMap( - Map.Entry::getKey, - entry -> new Chapter(entry.getKey(), JsonHelper.asObject(entry.getValue(), "root")) - )); - } catch (IllegalStateException | JsonParseException e) { - LOGGER.error("Could not load spellbook chapters due to exception", e); - } - - if (Debug.SPELLBOOK_CHAPTERS) { - CompletableFuture.runAsync(() -> { - try { - Util.waitAndApply(executor -> reload(CompletableFuture::completedFuture, manager, profiler, profiler, Util.getMainWorkerExecutor(), executor)).get(); - } catch (InterruptedException | ExecutionException e) { - } - dirty = true; - }, EXECUTOR); - } - } - - private static Text readText(JsonElement json) { - return json.isJsonPrimitive() ? Text.translatable(json.getAsString()) : Text.Serialization.fromJsonTree(json, DynamicRegistryManager.EMPTY); - } - - public enum Flow { - NONE, LEFT, RIGHT - } - - public record Chapter ( - Identifier id, - TabSide side, - int tabY, - int color, - List pages) implements SpellbookChapter { - @Deprecated - public Chapter(Identifier id, JsonObject json) { - this(id, - TabSide.valueOf(JsonHelper.getString(json, "side")), - JsonHelper.getInt(json, "y_position"), - JsonHelper.getInt(json, "color", 0), - loadContent(JsonHelper.getObject(json, "content", new JsonObject())) - ); - } - - @Deprecated - private static List loadContent(JsonObject json) { - return Optional.of(JsonHelper.getArray(json, "pages", new JsonArray())) - .filter(pages -> pages.size() > 0) - .stream() - .flatMap(pages -> StreamSupport.stream(pages.spliterator(), false)) - .map(Page::of) - .toList(); - } - - public void write(RegistryByteBuf buffer) { - buffer.writeIdentifier(id); - buffer.writeEnumConstant(side); - buffer.writeInt(tabY); - buffer.writeInt(color); - buffer.writeBoolean(true); - buffer.writeCollection(pages, Page::write); - } - } - - private record Page ( - Text title, - int level, - int color, - List elements - ) { - private static final Page EMPTY = new Page(Text.empty(), 0, 0, List.of()); - - public static Page of(JsonElement json) { - return json.isJsonObject() && json.getAsJsonObject().keySet().isEmpty() ? EMPTY : new Page(json); - } - - @Deprecated - Page(JsonElement json) { - this(json.getAsJsonObject()); - } - - @Deprecated - Page(JsonObject json) { - this( - readText(json.get("title")), - JsonHelper.getInt(json, "level", 0), - TextColor.parse(JsonHelper.getString(json, "color", "")).result().map(TextColor::getRgb).orElse(0), - new ArrayList<>() - ); - JsonHelper.getArray(json, "elements", new JsonArray()).forEach(element -> { - elements.add(Element.read(element)); - }); - } - - public void toBuffer(RegistryByteBuf buffer) { - TextCodecs.PACKET_CODEC.encode(buffer, title); - buffer.writeInt(level); - buffer.writeInt(color); - buffer.writeCollection(elements, Element::write); - } - - public static void write(PacketByteBuf buffer, Page page) { - page.toBuffer((RegistryByteBuf)buffer); - } - } - - private interface Element { - void toBuffer(RegistryByteBuf buffer); - - record Image (Identifier texture, Bounds bounds, Flow flow) implements Element { - @Override - public void toBuffer(RegistryByteBuf buffer) { - buffer.writeByte(0); - buffer.writeIdentifier(texture); - boundsToBuffer(bounds, buffer); - buffer.writeEnumConstant(flow); - } - } - - record Multi(int count, Element element) implements Element { - @Override - public void toBuffer(RegistryByteBuf buffer) { - buffer.writeVarInt(count); - element.toBuffer(buffer); - } - } - - record Id(byte id, Identifier value) implements Element { - @Override - public void toBuffer(RegistryByteBuf buffer) { - buffer.writeByte(id); - buffer.writeIdentifier(value); - } - } - - record Stack (IngredientWithSpell ingredient, Bounds bounds) implements Element { - @Override - public void toBuffer(RegistryByteBuf buffer) { - buffer.writeByte(2); - IngredientWithSpell.PACKET_CODEC.encode(buffer, ingredient); - boundsToBuffer(bounds, buffer); - } - } - - record TextBlock (Text text) implements Element { - @Override - public void toBuffer(RegistryByteBuf buffer) { - buffer.writeByte(3); - TextCodecs.PACKET_CODEC.encode(buffer, text); - } - } - - record Structure(List commands) implements Element { - static Element loadCommand(JsonObject json) { - - if (json.has("pos")) { - var pos = JsonHelper.getArray(json, "pos"); - return new Set( - pos.get(0).getAsInt(), pos.get(1).getAsInt(), pos.get(2).getAsInt(), - StateUtil.stateFromString(json.get("state").getAsString()) - ); - } - - var min = JsonHelper.getArray(json, "min"); - var max = JsonHelper.getArray(json, "max"); - return new Fill( - min.get(0).getAsInt(), min.get(1).getAsInt(), min.get(2).getAsInt(), - max.get(0).getAsInt(), max.get(1).getAsInt(), max.get(2).getAsInt(), - StateUtil.stateFromString(json.get("state").getAsString()) - ); - } - - @Override - public void toBuffer(RegistryByteBuf buffer) { - buffer.writeByte(5); - buffer.writeCollection(commands, (b, c) -> c.toBuffer(buffer)); - } - - record Set(int x, int y, int z, BlockState state) implements Element { - @Override - public void toBuffer(RegistryByteBuf buffer) { - buffer.writeByte(1); - buffer.writeInt(x); - buffer.writeInt(y); - buffer.writeInt(z); - buffer.writeInt(Block.getRawIdFromState(state)); - } - - } - record Fill(int x1, int y1, int z1, int x2, int y2, int z2, BlockState state) implements Element { - @Override - public void toBuffer(RegistryByteBuf buffer) { - buffer.writeByte(2); - buffer.writeInt(x1); - buffer.writeInt(y1); - buffer.writeInt(z1); - buffer.writeInt(x2); - buffer.writeInt(y2); - buffer.writeInt(z2); - buffer.writeInt(Block.getRawIdFromState(state)); - } - - } - } - - record Ingredients(List entries) implements Element { - @Deprecated - static Element loadIngredient(JsonObject json) { - int count = JsonHelper.getInt(json, "count", 1); - if (json.has("item")) { - return new Multi(count, new Id((byte)1, Identifier.tryParse(json.get("item").getAsString()))); - } - - if (json.has("trait")) { - return new Multi(count, new Id((byte)2, Trait.fromId(json.get("trait").getAsString()).orElseThrow().getId())); - } - - if (json.has("spell")) { - return new Multi(count, new Id((byte)4, Identifier.tryParse(json.get("spell").getAsString()))); - } - - return new Multi(count, new TextBlock(readText(json.get("text")))); - } - - @Override - public void toBuffer(RegistryByteBuf buffer) { - buffer.writeByte(4); - buffer.writeCollection(entries, (b, c) -> c.toBuffer(buffer)); - } - } - - static void write(PacketByteBuf buffer, Element element) { - element.toBuffer((RegistryByteBuf)buffer); - } - - @Deprecated - static Element read(JsonElement json) { - if (!json.isJsonPrimitive()) { - JsonObject el = JsonHelper.asObject(json, "element"); - if (el.has("texture")) { - return new Image( - Identifier.of(JsonHelper.getString(el, "texture")), - boundsFromJson(el), - Flow.valueOf(JsonHelper.getString(el, "flow", "RIGHT")) - ); - } - - if (el.has("recipe")) { - return new Id((byte)1, Identifier.of(JsonHelper.getString(el, "recipe"))); - } - - if (el.has("item")) { - return new Stack(IngredientWithSpell.CODEC.decode(JsonOps.INSTANCE, el.get("item")).result().get().getFirst(), boundsFromJson(el)); - } - - if (el.has("ingredients")) { - return new Ingredients(JsonHelper.getArray(el, "ingredients").asList().stream() - .map(JsonElement::getAsJsonObject) - .map(Ingredients::loadIngredient) - .toList() - ); - } - - if (el.has("structure")) { - return new Structure(JsonHelper.getArray(el, "structure").asList().stream() - .map(JsonElement::getAsJsonObject) - .map(Structure::loadCommand) - .toList() - ); - } - } - - return new TextBlock(readText(json)); - } - - private static Bounds boundsFromJson(JsonObject el) { - return new Bounds( - JsonHelper.getInt(el, "y", 0), - JsonHelper.getInt(el, "x", 0), - JsonHelper.getInt(el, "width", 0), - JsonHelper.getInt(el, "height", 0) - ); - } - - private static void boundsToBuffer(Bounds bounds, PacketByteBuf buffer) { - buffer.writeInt(bounds.top); - buffer.writeInt(bounds.left); - buffer.writeInt(bounds.width); - buffer.writeInt(bounds.height); - } - } -} diff --git a/src/main/java/com/minelittlepony/unicopia/container/spellbook/Chapter.java b/src/main/java/com/minelittlepony/unicopia/container/spellbook/Chapter.java new file mode 100644 index 00000000..e0e9030e --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/container/spellbook/Chapter.java @@ -0,0 +1,63 @@ +package com.minelittlepony.unicopia.container.spellbook; + +import java.util.List; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import net.minecraft.network.PacketByteBuf; +import net.minecraft.network.RegistryByteBuf; +import net.minecraft.text.Text; +import net.minecraft.text.TextCodecs; +import net.minecraft.text.TextColor; + +public record Chapter ( + TabSide side, + int tabY, + int color, + Chapter.Contents contents) { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + TabSide.CODEC.fieldOf("side").forGetter(Chapter::side), + Codec.INT.fieldOf("y_position").forGetter(Chapter::tabY), + Codec.INT.optionalFieldOf("color", 0).forGetter(Chapter::color), + Contents.CODEC.fieldOf("contents").forGetter(Chapter::contents) + ).apply(instance, Chapter::new)); + + record Contents(List pages) { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + Page.CODEC.listOf().fieldOf("pages").forGetter(Contents::pages) + ).apply(instance, Chapter.Contents::new)); + + public record Page ( + Text title, + int level, + int color, + List elements + ) { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + TextCodecs.CODEC.fieldOf("title").forGetter(Page::title), + Codec.INT.fieldOf("level").forGetter(Page::level), + TextColor.CODEC.fieldOf("color").xmap(TextColor::getRgb, TextColor::fromRgb).forGetter(Page::level), + ChapterPageElement.CODEC.listOf().fieldOf("elements").forGetter(Page::elements) + ).apply(instance, Page::new)); + + public void toBuffer(RegistryByteBuf buffer) { + TextCodecs.PACKET_CODEC.encode(buffer, title); + buffer.writeInt(level); + buffer.writeInt(color); + buffer.writeCollection(elements, ChapterPageElement::write); + } + + public static void write(PacketByteBuf buffer, Page page) { + page.toBuffer((RegistryByteBuf)buffer); + } + } + } + + public void write(RegistryByteBuf buffer) { + buffer.writeEnumConstant(side); + buffer.writeInt(tabY); + buffer.writeInt(color); + buffer.writeCollection(contents.pages(), Contents.Page::write); + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/container/spellbook/ChapterPageElement.java b/src/main/java/com/minelittlepony/unicopia/container/spellbook/ChapterPageElement.java new file mode 100644 index 00000000..ce34abf3 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/container/spellbook/ChapterPageElement.java @@ -0,0 +1,219 @@ +package com.minelittlepony.unicopia.container.spellbook; + +import java.util.List; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.minelittlepony.common.client.gui.dimension.Bounds; +import com.minelittlepony.unicopia.ability.magic.spell.crafting.IngredientWithSpell; +import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait; +import com.minelittlepony.unicopia.block.state.StateUtil; +import com.mojang.serialization.Codec; +import com.mojang.serialization.JsonOps; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.network.RegistryByteBuf; +import net.minecraft.registry.DynamicRegistryManager; +import net.minecraft.text.Text; +import net.minecraft.text.TextCodecs; +import net.minecraft.util.Identifier; +import net.minecraft.util.JsonHelper; +import net.minecraft.util.dynamic.Codecs; + +public interface ChapterPageElement { + byte IMAGE = 0; + byte RECIPE = 1; + byte STACK = 2; + byte TEXT_BLOCK = 3; + byte INGREDIENTS = 4; + byte STRUCTURE = 5; + + Codec CODEC = Codecs.JSON_ELEMENT.xmap(json -> { + if (!json.isJsonPrimitive()) { + JsonObject el = JsonHelper.asObject(json, "element"); + if (el.has("texture")) return new Image(el); + if (el.has("recipe")) return new Recipe(el); + if (el.has("item")) return new Stack(el); + if (el.has("ingredients")) return new Ingredients(el); + if (el.has("structure")) return new Structure(el); + } + return new TextBlock(json); + }, page -> { + throw new RuntimeException(); + }); + + static void write(PacketByteBuf buffer, ChapterPageElement element) { + element.toBuffer((RegistryByteBuf)buffer); + } + + private static Bounds boundsFromJson(JsonObject el) { + return new Bounds( + JsonHelper.getInt(el, "y", 0), + JsonHelper.getInt(el, "x", 0), + JsonHelper.getInt(el, "width", 0), + JsonHelper.getInt(el, "height", 0) + ); + } + + private static void boundsToBuffer(Bounds bounds, PacketByteBuf buffer) { + buffer.writeInt(bounds.top); + buffer.writeInt(bounds.left); + buffer.writeInt(bounds.width); + buffer.writeInt(bounds.height); + } + + void toBuffer(RegistryByteBuf buffer); + + record Image (Identifier texture, Bounds bounds, Flow flow) implements ChapterPageElement { + public Image(JsonObject json) { + this( + Identifier.of(JsonHelper.getString(json, "texture")), + boundsFromJson(json), + Flow.valueOf(JsonHelper.getString(json, "flow", "RIGHT")) + ); + } + @Override + public void toBuffer(RegistryByteBuf buffer) { + buffer.writeByte(IMAGE); + buffer.writeIdentifier(texture); + boundsToBuffer(bounds, buffer); + buffer.writeEnumConstant(flow); + } + } + + record Recipe(Identifier value) implements ChapterPageElement { + public Recipe(JsonObject json) { + this(Identifier.of(JsonHelper.getString(json, "recipe"))); + } + @Override + public void toBuffer(RegistryByteBuf buffer) { + buffer.writeByte(RECIPE); + buffer.writeIdentifier(value); + } + } + + record Stack (IngredientWithSpell ingredient, Bounds bounds) implements ChapterPageElement { + public Stack(JsonObject json) { + this(IngredientWithSpell.CODEC.decode(JsonOps.INSTANCE, json.get("item")).result().get().getFirst(), boundsFromJson(json)); + } + @Override + public void toBuffer(RegistryByteBuf buffer) { + buffer.writeByte(STACK); + IngredientWithSpell.PACKET_CODEC.encode(buffer, ingredient); + boundsToBuffer(bounds, buffer); + } + } + + record TextBlock (Text text) implements ChapterPageElement { + public TextBlock(JsonElement json) { + this(json.isJsonPrimitive() ? Text.translatable(json.getAsString()) : Text.Serialization.fromJsonTree(json, DynamicRegistryManager.EMPTY)); + } + + @Override + public void toBuffer(RegistryByteBuf buffer) { + buffer.writeByte(TEXT_BLOCK); + TextCodecs.PACKET_CODEC.encode(buffer, text); + } + } + + record Ingredients(List entries) implements ChapterPageElement { + public Ingredients(JsonObject json) { + this(JsonHelper.getArray(json, "ingredients").asList().stream() + .map(JsonElement::getAsJsonObject) + .map(Ingredients::loadIngredient) + .toList()); + } + + @Deprecated + static ChapterPageElement loadIngredient(JsonObject json) { + int count = JsonHelper.getInt(json, "count", 1); + if (json.has("item")) return new Multi(count, new Id((byte)1, Identifier.tryParse(json.get("item").getAsString()))); + if (json.has("trait")) return new Multi(count, new Id((byte)2, Trait.fromId(json.get("trait").getAsString()).orElseThrow().getId())); + if (json.has("spell")) return new Multi(count, new Id((byte)4, Identifier.tryParse(json.get("spell").getAsString()))); + return new Multi(count, new TextBlock(json.get("text"))); + } + + record Id(byte id, Identifier value) implements ChapterPageElement { + @Override + public void toBuffer(RegistryByteBuf buffer) { + buffer.writeByte(id); + buffer.writeIdentifier(value); + } + } + + record Multi(int count, ChapterPageElement element) implements ChapterPageElement { + @Override + public void toBuffer(RegistryByteBuf buffer) { + buffer.writeVarInt(count); + element.toBuffer(buffer); + } + } + + @Override + public void toBuffer(RegistryByteBuf buffer) { + buffer.writeByte(INGREDIENTS); + buffer.writeCollection(entries, (b, c) -> c.toBuffer(buffer)); + } + } + + record Structure(List commands) implements ChapterPageElement { + public Structure(JsonObject json) { + this(JsonHelper.getArray(json, "structure").asList().stream() + .map(JsonElement::getAsJsonObject) + .map(Structure::loadCommand) + .toList()); + } + + static ChapterPageElement loadCommand(JsonObject json) { + if (json.has("pos")) { + var pos = JsonHelper.getArray(json, "pos"); + return new Set( + pos.get(0).getAsInt(), pos.get(1).getAsInt(), pos.get(2).getAsInt(), + StateUtil.stateFromString(json.get("state").getAsString()) + ); + } + + var min = JsonHelper.getArray(json, "min"); + var max = JsonHelper.getArray(json, "max"); + return new Fill( + min.get(0).getAsInt(), min.get(1).getAsInt(), min.get(2).getAsInt(), + max.get(0).getAsInt(), max.get(1).getAsInt(), max.get(2).getAsInt(), + StateUtil.stateFromString(json.get("state").getAsString()) + ); + } + + @Override + public void toBuffer(RegistryByteBuf buffer) { + buffer.writeByte(STRUCTURE); + buffer.writeCollection(commands, (b, c) -> c.toBuffer(buffer)); + } + + record Set(int x, int y, int z, BlockState state) implements ChapterPageElement { + @Override + public void toBuffer(RegistryByteBuf buffer) { + buffer.writeByte(1); + buffer.writeInt(x); + buffer.writeInt(y); + buffer.writeInt(z); + buffer.writeInt(Block.getRawIdFromState(state)); + } + + } + record Fill(int x1, int y1, int z1, int x2, int y2, int z2, BlockState state) implements ChapterPageElement { + @Override + public void toBuffer(RegistryByteBuf buffer) { + buffer.writeByte(2); + buffer.writeInt(x1); + buffer.writeInt(y1); + buffer.writeInt(z1); + buffer.writeInt(x2); + buffer.writeInt(y2); + buffer.writeInt(z2); + buffer.writeInt(Block.getRawIdFromState(state)); + } + + } + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/container/spellbook/Flow.java b/src/main/java/com/minelittlepony/unicopia/container/spellbook/Flow.java new file mode 100644 index 00000000..8b67d7ea --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/container/spellbook/Flow.java @@ -0,0 +1,24 @@ +package com.minelittlepony.unicopia.container.spellbook; + +import java.util.Locale; + +import com.minelittlepony.unicopia.util.serialization.PacketCodecUtils; + +import io.netty.buffer.ByteBuf; +import net.minecraft.network.codec.PacketCodec; +import net.minecraft.util.StringIdentifiable; + +public enum Flow implements StringIdentifiable { + NONE, LEFT, RIGHT; + + @SuppressWarnings("deprecation") + public static final EnumCodec CODEC = StringIdentifiable.createCodec(Flow::values); + public static final PacketCodec PACKET_CODEC = PacketCodecUtils.ofEnum(Flow.class); + + private final String name = name().toLowerCase(Locale.ROOT); + + @Override + public String asString() { + return name; + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/container/SpellbookChapter.java b/src/main/java/com/minelittlepony/unicopia/container/spellbook/SpellbookChapter.java similarity index 69% rename from src/main/java/com/minelittlepony/unicopia/container/SpellbookChapter.java rename to src/main/java/com/minelittlepony/unicopia/container/spellbook/SpellbookChapter.java index 80391ac6..3cf83efb 100644 --- a/src/main/java/com/minelittlepony/unicopia/container/SpellbookChapter.java +++ b/src/main/java/com/minelittlepony/unicopia/container/spellbook/SpellbookChapter.java @@ -1,4 +1,4 @@ -package com.minelittlepony.unicopia.container; +package com.minelittlepony.unicopia.container.spellbook; import com.minelittlepony.unicopia.InteractionManager; @@ -7,7 +7,7 @@ import net.minecraft.network.codec.PacketCodec; public interface SpellbookChapter { PacketCodec PACKET_CODEC = PacketCodec.ofStatic( - (buffer, chapter) -> ((SpellbookChapterLoader.Chapter)chapter).write(buffer), + (buffer, chapter) -> ((SpellbookChapterLoader.IdentifiableChapter)chapter).write(buffer), buffer -> InteractionManager.getInstance().readChapter(buffer) ); } diff --git a/src/main/java/com/minelittlepony/unicopia/container/spellbook/SpellbookChapterLoader.java b/src/main/java/com/minelittlepony/unicopia/container/spellbook/SpellbookChapterLoader.java new file mode 100644 index 00000000..c763433e --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/container/spellbook/SpellbookChapterLoader.java @@ -0,0 +1,90 @@ +package com.minelittlepony.unicopia.container.spellbook; + +import java.util.*; +import java.util.concurrent.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +import org.slf4j.Logger; + +import com.google.gson.*; +import com.minelittlepony.unicopia.Debug; +import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.network.Channel; +import com.minelittlepony.unicopia.network.MsgServerResources; +import com.minelittlepony.unicopia.util.Resources; +import com.mojang.datafixers.util.Pair; +import com.mojang.logging.LogUtils; +import com.mojang.serialization.JsonOps; + +import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener; +import net.minecraft.network.RegistryByteBuf; +import net.minecraft.resource.JsonDataLoader; +import net.minecraft.resource.ResourceManager; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.Identifier; +import net.minecraft.util.Util; +import net.minecraft.util.profiler.Profiler; + +public class SpellbookChapterLoader extends JsonDataLoader implements IdentifiableResourceReloadListener { + private static final Logger LOGGER = LogUtils.getLogger(); + private static final Identifier ID = Unicopia.id("spellbook/chapters"); + private static final Executor EXECUTOR = CompletableFuture.delayedExecutor(5, TimeUnit.SECONDS); + + public static final SpellbookChapterLoader INSTANCE = new SpellbookChapterLoader(); + + private boolean dirty; + private Map chapters = new HashMap<>(); + + public SpellbookChapterLoader() { + super(Resources.GSON, ID.getPath()); + } + + @Override + public Identifier getFabricId() { + return ID; + } + + public Map getChapters() { + return chapters; + } + + public void sendUpdate(MinecraftServer server) { + if (dirty) { + dirty = false; + Channel.SERVER_RESOURCES.sendToAllPlayers(new MsgServerResources(), server); + } + } + + @Override + protected void apply(Map data, ResourceManager manager, Profiler profiler) { + try { + chapters = data.entrySet().stream().map(entry -> { + return Chapter.CODEC.decode(JsonOps.INSTANCE, entry.getValue()) + .result() + .map(Pair::getFirst) + .map(chapter -> new IdentifiableChapter(entry.getKey(), chapter)) + .orElse(null); + }).filter(Objects::nonNull).collect(Collectors.toMap(IdentifiableChapter::id, Function.identity())); + } catch (IllegalStateException | JsonParseException e) { + LOGGER.error("Could not load spellbook chapters due to exception", e); + } + + if (Debug.SPELLBOOK_CHAPTERS) { + CompletableFuture.runAsync(() -> { + try { + Util.waitAndApply(executor -> reload(CompletableFuture::completedFuture, manager, profiler, profiler, Util.getMainWorkerExecutor(), executor)).get(); + } catch (InterruptedException | ExecutionException e) { + } + dirty = true; + }, EXECUTOR); + } + } + + record IdentifiableChapter(Identifier id, Chapter chapter) implements SpellbookChapter { + public void write(RegistryByteBuf buffer) { + buffer.writeIdentifier(id); + chapter.write(buffer); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/container/spellbook/TabSide.java b/src/main/java/com/minelittlepony/unicopia/container/spellbook/TabSide.java new file mode 100644 index 00000000..c3ab493b --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/container/spellbook/TabSide.java @@ -0,0 +1,25 @@ +package com.minelittlepony.unicopia.container.spellbook; + +import java.util.Locale; + +import com.minelittlepony.unicopia.util.serialization.PacketCodecUtils; + +import io.netty.buffer.ByteBuf; +import net.minecraft.network.codec.PacketCodec; +import net.minecraft.util.StringIdentifiable; + +public enum TabSide implements StringIdentifiable { + LEFT, + RIGHT; + + @SuppressWarnings("deprecation") + public static final EnumCodec CODEC = StringIdentifiable.createCodec(TabSide::values); + public static final PacketCodec PACKET_CODEC = PacketCodecUtils.ofEnum(TabSide.class); + + private final String name = name().toLowerCase(Locale.ROOT); + + @Override + public String asString() { + return name; + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/network/MsgServerResources.java b/src/main/java/com/minelittlepony/unicopia/network/MsgServerResources.java index 004c7e06..e6ae6bcf 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/MsgServerResources.java +++ b/src/main/java/com/minelittlepony/unicopia/network/MsgServerResources.java @@ -4,8 +4,8 @@ import java.util.*; import com.minelittlepony.unicopia.ability.data.tree.TreeTypeLoader; import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits; -import com.minelittlepony.unicopia.container.SpellbookChapter; -import com.minelittlepony.unicopia.container.SpellbookChapterLoader; +import com.minelittlepony.unicopia.container.spellbook.SpellbookChapter; +import com.minelittlepony.unicopia.container.spellbook.SpellbookChapterLoader; import com.minelittlepony.unicopia.diet.PonyDiets; import net.minecraft.network.RegistryByteBuf; import net.minecraft.network.codec.PacketCodec;