mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-23 21:38:00 +01:00
Store the state in the spellbook and synchronize it between players when multiple are viewing the same book
This commit is contained in:
parent
0f90593a9a
commit
6e04c6ab6d
16 changed files with 356 additions and 67 deletions
|
@ -8,18 +8,18 @@ import com.minelittlepony.common.client.gui.dimension.Bounds;
|
|||
import com.minelittlepony.unicopia.client.gui.DrawableUtil;
|
||||
import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.Content;
|
||||
import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.Drawable;
|
||||
import com.minelittlepony.unicopia.container.SpellbookState;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.*;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
public class DynamicContent implements Content {
|
||||
private static final Text UNKNOWN = Text.of("???");
|
||||
private static final Text UNKNOWN_LEVEL = Text.literal("Level: ???").formatted(Formatting.DARK_GREEN);
|
||||
|
||||
private int offset = 0;
|
||||
private SpellbookState.PageState state = new SpellbookState.PageState();
|
||||
private final List<Page> pages = new ArrayList<>();
|
||||
|
||||
private Bounds bounds = Bounds.empty();
|
||||
|
@ -30,7 +30,7 @@ public class DynamicContent implements Content {
|
|||
|
||||
@Override
|
||||
public void draw(MatrixStack matrices, int mouseX, int mouseY, IViewRoot container) {
|
||||
int pageIndex = offset * 2;
|
||||
int pageIndex = state.getOffset() * 2;
|
||||
|
||||
getPage(pageIndex).ifPresent(page -> page.draw(matrices, mouseX, mouseY, container));
|
||||
|
||||
|
@ -45,7 +45,7 @@ public class DynamicContent implements Content {
|
|||
@Override
|
||||
public void copyStateFrom(Content old) {
|
||||
if (old instanceof DynamicContent o) {
|
||||
offset = o.offset;
|
||||
state = o.state;
|
||||
setBounds(o.bounds);
|
||||
}
|
||||
}
|
||||
|
@ -67,10 +67,11 @@ public class DynamicContent implements Content {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void init(SpellbookScreen screen) {
|
||||
public void init(SpellbookScreen screen, Identifier pageId) {
|
||||
state = screen.getState().getState(pageId);
|
||||
setBounds(screen.getFrameBounds());
|
||||
screen.addPageButtons(187, 30, 350, incr -> {
|
||||
offset = MathHelper.clamp(offset + incr, 0, (int)Math.ceil(pages.size() / 2F) - 1);
|
||||
state.swap(incr, (int)Math.ceil(pages.size() / 2F));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.minelittlepony.unicopia.client.gui.spellbook;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.minelittlepony.common.client.gui.IViewRoot;
|
||||
|
@ -15,13 +15,14 @@ public class SpellbookChapterList {
|
|||
public static final Identifier PROFILE_ID = Unicopia.id("profile");
|
||||
public static final Identifier TRAIT_DEX_ID = Unicopia.id("traits");
|
||||
|
||||
private final Chapter craftingChapter;
|
||||
private final SpellbookScreen screen;
|
||||
|
||||
private Optional<Identifier> currentChapter = Optional.empty();
|
||||
private final Chapter craftingChapter;
|
||||
|
||||
private final Map<Identifier, Chapter> chapters = new HashMap<>();
|
||||
|
||||
public SpellbookChapterList(Chapter craftingChapter, Chapter... builtIn) {
|
||||
public SpellbookChapterList(SpellbookScreen screen, Chapter craftingChapter, Chapter... builtIn) {
|
||||
this.screen = screen;
|
||||
this.craftingChapter = craftingChapter;
|
||||
SpellbookChapterLoader.INSTANCE.getChapters().forEach(chapter -> {
|
||||
chapters.put(chapter.id(), chapter);
|
||||
|
@ -45,11 +46,8 @@ public class SpellbookChapterList {
|
|||
chapters.put(chapter.id(), chapter);
|
||||
});
|
||||
}
|
||||
return currentChapter.map(chapters::get).orElse(craftingChapter);
|
||||
}
|
||||
|
||||
public void setCurrentChapter(Chapter chapter) {
|
||||
currentChapter = Optional.of(chapter.id());
|
||||
return screen.getState().getCurrentPageId().map(chapters::get).orElse(craftingChapter);
|
||||
}
|
||||
|
||||
public record Chapter (
|
||||
|
@ -70,15 +68,19 @@ public class SpellbookChapterList {
|
|||
}
|
||||
|
||||
public interface Content extends Drawable {
|
||||
void init(SpellbookScreen screen);
|
||||
void init(SpellbookScreen screen, Identifier pageId);
|
||||
|
||||
default void copyStateFrom(Content old) {}
|
||||
|
||||
static Optional<Content> of(Consumer<SpellbookScreen> init, Drawable obj) {
|
||||
default boolean showInventory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
static Optional<Content> of(BiConsumer<SpellbookScreen, Identifier> init, Drawable obj) {
|
||||
return Optional.of(new Content() {
|
||||
@Override
|
||||
public void init(SpellbookScreen screen) {
|
||||
init.accept(screen);
|
||||
public void init(SpellbookScreen screen, Identifier pageId) {
|
||||
init.accept(screen, pageId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -6,15 +6,19 @@ import com.minelittlepony.common.client.gui.element.Label;
|
|||
import com.minelittlepony.unicopia.ability.magic.spell.crafting.SpellbookRecipe;
|
||||
import com.minelittlepony.unicopia.client.gui.DrawableUtil;
|
||||
import com.minelittlepony.unicopia.container.SpellbookPage;
|
||||
import com.minelittlepony.unicopia.container.SpellbookState;
|
||||
import com.minelittlepony.unicopia.item.URecipes;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public class SpellbookCraftingPageContent extends ScrollContainer implements SpellbookChapterList.Content, SpellbookScreen.RecipesChangedListener {
|
||||
private final SpellbookScreen screen;
|
||||
|
||||
private SpellbookState.PageState state = new SpellbookState.PageState();
|
||||
|
||||
public SpellbookCraftingPageContent(SpellbookScreen screen) {
|
||||
this.screen = screen;
|
||||
backgroundColor = 0xFFf9efd3;
|
||||
|
@ -22,8 +26,11 @@ public class SpellbookCraftingPageContent extends ScrollContainer implements Spe
|
|||
}
|
||||
|
||||
@Override
|
||||
public void init(SpellbookScreen screen) {
|
||||
screen.addPageButtons(187, 300, 350, SpellbookPage::swap);
|
||||
public void init(SpellbookScreen screen, Identifier pageId) {
|
||||
state = screen.getState().getState(pageId);
|
||||
screen.addPageButtons(187, 300, 350, incr -> {
|
||||
state.swap(incr, SpellbookPage.VALUES.length);
|
||||
});
|
||||
initContents();
|
||||
screen.addDrawable(this);
|
||||
((IViewRoot)screen).getChildElements().add(this);
|
||||
|
@ -34,9 +41,9 @@ public class SpellbookCraftingPageContent extends ScrollContainer implements Spe
|
|||
|
||||
int headerColor = mouseY % 255;
|
||||
|
||||
DrawableUtil.drawScaledText(matrices, SpellbookPage.getCurrent().getLabel(), screen.getFrameBounds().left + screen.getFrameBounds().width / 2 + 20, SpellbookScreen.TITLE_Y, 1.3F, headerColor);
|
||||
DrawableUtil.drawScaledText(matrices, SpellbookPage.VALUES[state.getOffset()].getLabel(), screen.getFrameBounds().left + screen.getFrameBounds().width / 2 + 20, SpellbookScreen.TITLE_Y, 1.3F, headerColor);
|
||||
|
||||
Text pageText = Text.translatable("%s/%s", SpellbookPage.getCurrent().ordinal() + 1, SpellbookPage.VALUES.length);
|
||||
Text pageText = Text.translatable("%s/%s", state.getOffset() + 1, SpellbookPage.VALUES.length);
|
||||
textRenderer.draw(matrices, pageText, 337 - textRenderer.getWidth(pageText) / 2F, 190, headerColor);
|
||||
}
|
||||
|
||||
|
@ -49,11 +56,16 @@ public class SpellbookCraftingPageContent extends ScrollContainer implements Spe
|
|||
init(this::initPageContent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showInventory() {
|
||||
return SpellbookPage.VALUES[state.getOffset()] == SpellbookPage.INVENTORY;
|
||||
}
|
||||
|
||||
private void initPageContent() {
|
||||
getContentPadding().setVertical(10);
|
||||
getContentPadding().bottom = 30;
|
||||
|
||||
switch (SpellbookPage.getCurrent()) {
|
||||
switch (SpellbookPage.VALUES[state.getOffset()]) {
|
||||
case INVENTORY:
|
||||
// handled elsewhere
|
||||
break;
|
||||
|
|
|
@ -14,6 +14,7 @@ import net.minecraft.client.gui.DrawableHelper;
|
|||
import net.minecraft.client.resource.language.I18n;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
public class SpellbookProfilePageContent extends DrawableHelper implements SpellbookChapterList.Content {
|
||||
|
@ -28,10 +29,15 @@ public class SpellbookProfilePageContent extends DrawableHelper implements Spell
|
|||
}
|
||||
|
||||
@Override
|
||||
public void init(SpellbookScreen screen) {
|
||||
public void init(SpellbookScreen screen, Identifier pageId) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showInventory() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(MatrixStack matrices, int mouseX, int mouseY, IViewRoot container) {
|
||||
|
||||
|
|
|
@ -10,9 +10,10 @@ import com.minelittlepony.common.client.gui.element.Button;
|
|||
import com.minelittlepony.common.client.gui.sprite.TextureSprite;
|
||||
import com.minelittlepony.unicopia.Unicopia;
|
||||
import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookChapterList.*;
|
||||
import com.minelittlepony.unicopia.container.SpellbookPage;
|
||||
import com.minelittlepony.unicopia.container.SpellbookScreenHandler;
|
||||
import com.minelittlepony.unicopia.container.*;
|
||||
import com.minelittlepony.unicopia.container.SpellbookScreenHandler.*;
|
||||
import com.minelittlepony.unicopia.network.Channel;
|
||||
import com.minelittlepony.unicopia.network.MsgSpellbookStateChanged;
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
|
||||
|
@ -25,6 +26,7 @@ import net.minecraft.client.texture.NativeImage;
|
|||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.screen.slot.Slot;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
@ -42,11 +44,12 @@ public class SpellbookScreen extends HandledScreen<SpellbookScreenHandler> imple
|
|||
|
||||
private final RecipeBookWidget recipeBook = new RecipeBookWidget();
|
||||
|
||||
private final Chapter craftingChapter = new Chapter(SpellbookChapterList.CRAFTING_ID, TabSide.LEFT, 0, 0, Optional.of(new SpellbookCraftingPageContent(this)));
|
||||
private final Chapter profileChapter = new Chapter(SpellbookChapterList.PROFILE_ID, TabSide.LEFT, 1, 0, Optional.of(new SpellbookProfilePageContent(this)));
|
||||
private final Chapter traitdexChapter = new Chapter(SpellbookChapterList.TRAIT_DEX_ID, TabSide.LEFT, 3, 0, Optional.of(new SpellbookTraitDexPageContent(this)));
|
||||
|
||||
private final SpellbookChapterList chapters = new SpellbookChapterList(craftingChapter, profileChapter, traitdexChapter);
|
||||
private final Chapter craftingChapter;
|
||||
private final SpellbookChapterList chapters = new SpellbookChapterList(this,
|
||||
craftingChapter = new Chapter(SpellbookChapterList.CRAFTING_ID, TabSide.LEFT, 0, 0, Optional.of(new SpellbookCraftingPageContent(this))),
|
||||
new Chapter(SpellbookChapterList.PROFILE_ID, TabSide.LEFT, 1, 0, Optional.of(new SpellbookProfilePageContent(this))),
|
||||
new Chapter(SpellbookChapterList.TRAIT_DEX_ID, TabSide.LEFT, 3, 0, Optional.of(new SpellbookTraitDexPageContent(this)))
|
||||
);
|
||||
private final SpellbookTabBar tabs = new SpellbookTabBar(this, chapters);
|
||||
|
||||
private Bounds contentBounds = Bounds.empty();
|
||||
|
@ -59,11 +62,17 @@ public class SpellbookScreen extends HandledScreen<SpellbookScreenHandler> imple
|
|||
|
||||
handler.addSlotShowingCondition(slotType -> {
|
||||
if (slotType == SlotType.INVENTORY) {
|
||||
return chapters.getCurrentChapter() == profileChapter
|
||||
|| (chapters.getCurrentChapter() == craftingChapter && SpellbookPage.getCurrent() == SpellbookPage.INVENTORY);
|
||||
return chapters.getCurrentChapter().content().filter(Content::showInventory).isPresent();
|
||||
}
|
||||
return chapters.getCurrentChapter() == craftingChapter;
|
||||
});
|
||||
handler.getSpellbookState().setSynchronizer(state -> {
|
||||
Channel.CLIENT_SPELLBOOK_UPDATE.send(new MsgSpellbookStateChanged<ServerPlayerEntity>(handler.syncId, state));
|
||||
});
|
||||
}
|
||||
|
||||
public SpellbookState getState() {
|
||||
return handler.getSpellbookState();
|
||||
}
|
||||
|
||||
public void addPageButtons(int buttonY, int prevX, int nextX, IntConsumer pageAction) {
|
||||
|
@ -100,7 +109,7 @@ public class SpellbookScreen extends HandledScreen<SpellbookScreenHandler> imple
|
|||
public void init() {
|
||||
super.init();
|
||||
tabs.init();
|
||||
chapters.getCurrentChapter().content().ifPresent(content -> content.init(this));
|
||||
chapters.getCurrentChapter().content().ifPresent(content -> content.init(this, chapters.getCurrentChapter().id()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -196,9 +205,8 @@ public class SpellbookScreen extends HandledScreen<SpellbookScreenHandler> imple
|
|||
@Override
|
||||
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
||||
return tabs.getAllTabs().anyMatch(tab -> {
|
||||
|
||||
if (tab.bounds().contains(mouseX, mouseY) && chapters.getCurrentChapter() != tab.chapter()) {
|
||||
chapters.setCurrentChapter(tab.chapter());
|
||||
getState().setCurrentPageId(tab.chapter().id());
|
||||
GameGui.playSound(SoundEvents.ITEM_BOOK_PAGE_TURN);
|
||||
clearAndInit();
|
||||
return true;
|
||||
|
|
|
@ -10,6 +10,7 @@ import com.minelittlepony.common.client.gui.element.Label;
|
|||
import com.minelittlepony.common.client.gui.sprite.TextureSprite;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.*;
|
||||
import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookScreen.ImageButton;
|
||||
import com.minelittlepony.unicopia.container.SpellbookState;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
|
||||
|
@ -18,13 +19,13 @@ import net.minecraft.client.gui.DrawableHelper;
|
|||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.item.*;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.collection.DefaultedList;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
public class SpellbookTraitDexPageContent extends DrawableHelper implements SpellbookChapterList.Content, SpellbookScreen.RecipesChangedListener {
|
||||
|
||||
private final Trait[] traits = Trait.values();
|
||||
private int offset;
|
||||
private SpellbookState.PageState state = new SpellbookState.PageState();
|
||||
|
||||
private final DexPage leftPage = new DexPage();
|
||||
private final DexPage rightPage = new DexPage();
|
||||
|
@ -41,12 +42,14 @@ public class SpellbookTraitDexPageContent extends DrawableHelper implements Spel
|
|||
}
|
||||
|
||||
@Override
|
||||
public void init(SpellbookScreen screen) {
|
||||
int page = offset * 2;
|
||||
public void init(SpellbookScreen screen, Identifier pageId) {
|
||||
state = screen.getState().getState(pageId);
|
||||
|
||||
int page = state.getOffset() * 2;
|
||||
leftPage.init(screen, page);
|
||||
rightPage.init(screen, page + 1);
|
||||
screen.addPageButtons(187, 30, 350, incr -> {
|
||||
offset = MathHelper.clamp(offset + incr, 0, (int)Math.ceil(traits.length / 2F) - 1);
|
||||
state.swap(incr, (int)Math.ceil(traits.length / 2F));
|
||||
leftPage.scrollbar.scrollBy(leftPage.scrollbar.getVerticalScrollAmount());
|
||||
rightPage.scrollbar.scrollBy(rightPage.scrollbar.getVerticalScrollAmount());
|
||||
});
|
||||
|
@ -54,7 +57,7 @@ public class SpellbookTraitDexPageContent extends DrawableHelper implements Spel
|
|||
|
||||
@Override
|
||||
public void onRecipesChanged() {
|
||||
init(screen);
|
||||
init(screen, SpellbookChapterList.TRAIT_DEX_ID);
|
||||
}
|
||||
|
||||
private final class DexPage extends ScrollContainer {
|
||||
|
|
|
@ -1,26 +1,16 @@
|
|||
package com.minelittlepony.unicopia.container;
|
||||
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
public enum SpellbookPage {
|
||||
INVENTORY,
|
||||
RECIPES;
|
||||
|
||||
public static final SpellbookPage[] VALUES = values();
|
||||
private static int current;
|
||||
|
||||
private final Text label = Text.translatable("gui.unicopia.spellbook.page." + name().toLowerCase());
|
||||
|
||||
public Text getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public static SpellbookPage getCurrent() {
|
||||
return VALUES[current];
|
||||
}
|
||||
|
||||
public static void swap(int increment) {
|
||||
current = MathHelper.clamp(current + increment, 0, VALUES.length - 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package com.minelittlepony.unicopia.container;
|
|||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.EquinePredicates;
|
||||
import com.minelittlepony.unicopia.USounds;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.crafting.SpellbookRecipe;
|
||||
|
@ -23,6 +25,7 @@ import net.minecraft.inventory.CraftingInventory;
|
|||
import net.minecraft.inventory.CraftingResultInventory;
|
||||
import net.minecraft.inventory.Inventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket;
|
||||
import net.minecraft.screen.PlayerScreenHandler;
|
||||
import net.minecraft.screen.ScreenHandler;
|
||||
|
@ -59,12 +62,19 @@ public class SpellbookScreenHandler extends ScreenHandler {
|
|||
|
||||
private Predicate<SlotType> canShowSlots;
|
||||
|
||||
protected SpellbookScreenHandler(int syncId, PlayerInventory inv) {
|
||||
this(syncId, inv, ScreenHandlerContext.EMPTY);
|
||||
private final SpellbookState state;
|
||||
|
||||
@Nullable
|
||||
public UUID entityId;
|
||||
|
||||
protected SpellbookScreenHandler(int syncId, PlayerInventory inv, PacketByteBuf buf) {
|
||||
this(syncId, inv, ScreenHandlerContext.EMPTY, new SpellbookState().fromPacket(buf), null);
|
||||
}
|
||||
|
||||
public SpellbookScreenHandler(int syncId, PlayerInventory inv, ScreenHandlerContext context) {
|
||||
public SpellbookScreenHandler(int syncId, PlayerInventory inv, ScreenHandlerContext context, SpellbookState state, UUID entityId) {
|
||||
super(UScreenHandlers.SPELL_BOOK, syncId);
|
||||
this.entityId = entityId;
|
||||
this.state = state;
|
||||
inventory = inv;
|
||||
this.context = context;
|
||||
|
||||
|
@ -136,6 +146,10 @@ public class SpellbookScreenHandler extends ScreenHandler {
|
|||
onContentChanged(input);
|
||||
}
|
||||
|
||||
public SpellbookState getSpellbookState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void addSlotShowingCondition(Predicate<SlotType> canShowSlots) {
|
||||
this.canShowSlots = canShowSlots;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
package com.minelittlepony.unicopia.container;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.minelittlepony.unicopia.network.datasync.Synchronizable;
|
||||
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
||||
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.nbt.NbtElement;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
public class SpellbookState extends Synchronizable<SpellbookState> implements NbtSerialisable {
|
||||
public static final SpellbookState INSTANCE = new SpellbookState();
|
||||
|
||||
private Optional<Identifier> currentPageId = Optional.empty();
|
||||
|
||||
private final Map<Identifier, PageState> states = new HashMap<>();
|
||||
|
||||
public Optional<Identifier> getCurrentPageId() {
|
||||
return currentPageId;
|
||||
}
|
||||
|
||||
public void setCurrentPageId(Identifier pageId) {
|
||||
currentPageId = Optional.ofNullable(pageId);
|
||||
synchronize();
|
||||
}
|
||||
|
||||
public PageState getState(Identifier pageId) {
|
||||
return states.computeIfAbsent(pageId, i -> new PageState(page -> synchronize()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyFrom(SpellbookState state) {
|
||||
currentPageId = state.currentPageId;
|
||||
state.states.forEach((id, page) -> getState(id).copyFrom(page));
|
||||
}
|
||||
|
||||
public void toPacket(PacketByteBuf buf) {
|
||||
buf.writeOptional(currentPageId, PacketByteBuf::writeIdentifier);
|
||||
buf.writeMap(states, PacketByteBuf::writeIdentifier, (b, v) -> v.toPacket(b));
|
||||
}
|
||||
|
||||
public SpellbookState fromPacket(PacketByteBuf buf) {
|
||||
currentPageId = buf.readOptional(PacketByteBuf::readIdentifier);
|
||||
buf.readMap(PacketByteBuf::readIdentifier, b -> new PageState(page -> synchronize()).fromPacket(b)).forEach((id, state) -> {
|
||||
getState(id).copyFrom(state);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toNBT(NbtCompound compound) {
|
||||
currentPageId.ifPresent(id -> compound.putString("current_page", id.toString()));
|
||||
NbtCompound states = new NbtCompound();
|
||||
compound.put("states", states);
|
||||
this.states.forEach((id, page) -> {
|
||||
states.put(id.toString(), page.toNBT());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromNBT(NbtCompound compound) {
|
||||
currentPageId = compound.contains("current_page", NbtElement.STRING_TYPE) ? Optional.ofNullable(Identifier.tryParse(compound.getString("current_page"))) : Optional.empty();
|
||||
NbtCompound states = compound.getCompound("states");
|
||||
states.getKeys().stream().forEach(key -> {
|
||||
Identifier id = Identifier.tryParse(key);
|
||||
if (id != null) {
|
||||
getState(id).fromNBT(states.getCompound(key));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static class PageState extends Synchronizable<PageState> implements NbtSerialisable {
|
||||
private int offset;
|
||||
|
||||
public PageState() {}
|
||||
|
||||
PageState(Consumer<PageState> synchronizer) {
|
||||
setSynchronizer(synchronizer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyFrom(PageState other) {
|
||||
offset = other.offset;
|
||||
}
|
||||
|
||||
public void setOffset(int offset) {
|
||||
this.offset = offset;
|
||||
synchronize();
|
||||
}
|
||||
|
||||
public int getOffset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
public void swap(int incr, int max) {
|
||||
setOffset(MathHelper.clamp(getOffset() + incr, 0, max - 1));
|
||||
}
|
||||
|
||||
public void toPacket(PacketByteBuf buf) {
|
||||
buf.writeInt(offset);
|
||||
}
|
||||
|
||||
public PageState fromPacket(PacketByteBuf buf) {
|
||||
offset = buf.readInt();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toNBT(NbtCompound compound) {
|
||||
compound.putInt("offset", offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromNBT(NbtCompound compound) {
|
||||
offset = compound.getInt("offset");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,15 +2,16 @@ package com.minelittlepony.unicopia.container;
|
|||
|
||||
import com.minelittlepony.unicopia.Unicopia;
|
||||
|
||||
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerType;
|
||||
import net.minecraft.screen.ScreenHandler;
|
||||
import net.minecraft.screen.ScreenHandlerType;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
|
||||
public interface UScreenHandlers {
|
||||
ScreenHandlerType<SpellbookScreenHandler> SPELL_BOOK = register("spell_book", SpellbookScreenHandler::new);
|
||||
ScreenHandlerType<SpellbookScreenHandler> SPELL_BOOK = register("spell_book", new ExtendedScreenHandlerType<>(SpellbookScreenHandler::new));
|
||||
|
||||
static <T extends ScreenHandler> ScreenHandlerType<T> register(String name, ScreenHandlerType.Factory<T> factory) {
|
||||
return Registry.register(Registry.SCREEN_HANDLER, Unicopia.id(name), new ScreenHandlerType<>(factory));
|
||||
static <T extends ScreenHandler> ScreenHandlerType<T> register(String name, ScreenHandlerType<T> type) {
|
||||
return Registry.register(Registry.SCREEN_HANDLER, Unicopia.id(name), type);
|
||||
}
|
||||
|
||||
static void bootstrap() { }
|
||||
|
|
|
@ -4,8 +4,12 @@ import org.jetbrains.annotations.Nullable;
|
|||
|
||||
import com.minelittlepony.unicopia.EquinePredicates;
|
||||
import com.minelittlepony.unicopia.container.SpellbookScreenHandler;
|
||||
import com.minelittlepony.unicopia.container.SpellbookState;
|
||||
import com.minelittlepony.unicopia.item.UItems;
|
||||
import com.minelittlepony.unicopia.network.Channel;
|
||||
import com.minelittlepony.unicopia.network.MsgSpellbookStateChanged;
|
||||
|
||||
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory;
|
||||
import net.fabricmc.fabric.api.util.TriState;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityType;
|
||||
|
@ -15,14 +19,17 @@ import net.minecraft.entity.data.TrackedData;
|
|||
import net.minecraft.entity.data.TrackedDataHandlerRegistry;
|
||||
import net.minecraft.entity.mob.MobEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.particle.ParticleTypes;
|
||||
import net.minecraft.screen.ScreenHandlerContext;
|
||||
import net.minecraft.screen.SimpleNamedScreenHandlerFactory;
|
||||
import net.minecraft.screen.*;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.sound.BlockSoundGroup;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
@ -39,10 +46,23 @@ public class SpellbookEntity extends MobEntity {
|
|||
|
||||
private int activeTicks = TICKS_TO_SLEEP;
|
||||
|
||||
private final SpellbookState state = new SpellbookState();
|
||||
|
||||
public SpellbookEntity(EntityType<SpellbookEntity> type, World world) {
|
||||
super(type, world);
|
||||
setPersistent();
|
||||
setAltered(world.random.nextInt(3) == 0);
|
||||
if (!world.isClient) {
|
||||
state.setSynchronizer(state -> {
|
||||
getWorld().getPlayers().forEach(player -> {
|
||||
if (player instanceof ServerPlayerEntity recipient
|
||||
&& player.currentScreenHandler instanceof SpellbookScreenHandler book
|
||||
&& getUuid().equals(book.entityId)) {
|
||||
Channel.SERVER_SPELLBOOK_UPDATE.send(recipient, new MsgSpellbookStateChanged<>(player.currentScreenHandler.syncId, state));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -54,9 +74,15 @@ public class SpellbookEntity extends MobEntity {
|
|||
dataTracker.startTracking(ALTERED, false);
|
||||
}
|
||||
|
||||
public SpellbookState getSpellbookState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getPickBlockStack() {
|
||||
return new ItemStack(UItems.SPELLBOOK);
|
||||
ItemStack stack = UItems.SPELLBOOK.getDefaultStack();
|
||||
stack.getOrCreateNbt().put("spellbookState", state.toNBT());
|
||||
return stack;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -180,7 +206,7 @@ public class SpellbookEntity extends MobEntity {
|
|||
world.playSound(getX(), getY(), getZ(), sound.getBreakSound(), SoundCategory.BLOCKS, sound.getVolume(), sound.getPitch(), true);
|
||||
|
||||
if (world.getGameRules().getBoolean(GameRules.DO_TILE_DROPS)) {
|
||||
dropItem(UItems.SPELLBOOK, 1);
|
||||
dropStack(getPickBlockStack(), 1);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -198,7 +224,22 @@ public class SpellbookEntity extends MobEntity {
|
|||
|
||||
if (isOpen() && EquinePredicates.PLAYER_UNICORN.test(player)) {
|
||||
setBored(false);
|
||||
player.openHandledScreen(new SimpleNamedScreenHandlerFactory((syncId, inv, ply) -> new SpellbookScreenHandler(syncId, inv, ScreenHandlerContext.create(world, getBlockPos())), getDisplayName()));
|
||||
player.openHandledScreen(new ExtendedScreenHandlerFactory() {
|
||||
@Override
|
||||
public Text getDisplayName() {
|
||||
return SpellbookEntity.this.getDisplayName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScreenHandler createMenu(int syncId, PlayerInventory inv, PlayerEntity player) {
|
||||
return new SpellbookScreenHandler(syncId, inv, ScreenHandlerContext.create(world, getBlockPos()), state, getUuid());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeScreenOpeningData(ServerPlayerEntity player, PacketByteBuf buf) {
|
||||
state.toPacket(buf);
|
||||
}
|
||||
});
|
||||
player.playSound(SoundEvents.ITEM_BOOK_PAGE_TURN, 2, 1);
|
||||
return ActionResult.SUCCESS;
|
||||
}
|
||||
|
@ -213,6 +254,8 @@ public class SpellbookEntity extends MobEntity {
|
|||
setBored(compound.getBoolean("bored"));
|
||||
setAltered(compound.getBoolean("altered"));
|
||||
setLocked(compound.contains("locked") ? TriState.of(compound.getBoolean("locked")) : TriState.DEFAULT);
|
||||
|
||||
state.fromNBT(compound.getCompound("spellbookState"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -226,5 +269,7 @@ public class SpellbookEntity extends MobEntity {
|
|||
if (locked != TriState.DEFAULT) {
|
||||
compound.putBoolean("locked", locked.get());
|
||||
}
|
||||
|
||||
compound.put("spellbookState", state.toNBT());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import net.minecraft.entity.player.PlayerEntity;
|
|||
import net.minecraft.item.BookItem;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.ItemUsageContext;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.TypedActionResult;
|
||||
import net.minecraft.util.math.BlockPointer;
|
||||
|
@ -31,7 +32,7 @@ public class SpellbookItem extends BookItem implements Dispensable {
|
|||
BlockPos pos = source.getPos().offset(facing);
|
||||
|
||||
float yaw = facing.getOpposite().asRotation();
|
||||
placeBook(source.getWorld(), pos.getX(), pos.getY(), pos.getZ(), yaw);
|
||||
placeBook(stack, source.getWorld(), pos.getX(), pos.getY(), pos.getZ(), yaw);
|
||||
stack.decrement(1);
|
||||
|
||||
return new TypedActionResult<>(ActionResult.SUCCESS, stack);
|
||||
|
@ -46,7 +47,7 @@ public class SpellbookItem extends BookItem implements Dispensable {
|
|||
if (!context.getWorld().isClient && EquinePredicates.PLAYER_UNICORN.test(player)) {
|
||||
BlockPos pos = context.getBlockPos().offset(context.getSide());
|
||||
|
||||
placeBook(context.getWorld(), pos.getX(), pos.getY(), pos.getZ(), context.getPlayerYaw() + 180);
|
||||
placeBook(context.getStack(), context.getWorld(), pos.getX(), pos.getY(), pos.getZ(), context.getPlayerYaw() + 180);
|
||||
|
||||
if (!player.getAbilities().creativeMode) {
|
||||
player.getStackInHand(context.getHand()).decrement(1);
|
||||
|
@ -57,12 +58,19 @@ public class SpellbookItem extends BookItem implements Dispensable {
|
|||
return ActionResult.PASS;
|
||||
}
|
||||
|
||||
private static void placeBook(World world, int x, int y, int z, float yaw) {
|
||||
private static void placeBook(ItemStack stack, World world, int x, int y, int z, float yaw) {
|
||||
SpellbookEntity book = UEntities.SPELLBOOK.create(world);
|
||||
|
||||
book.refreshPositionAndAngles(x + 0.5, y, z + 0.5, 0, 0);
|
||||
book.setHeadYaw(yaw);
|
||||
book.setYaw(yaw);
|
||||
|
||||
@Nullable
|
||||
NbtCompound tag = stack.getSubNbt("spellbookState");
|
||||
if (tag != null) {
|
||||
book.getSpellbookState().fromNBT(tag);
|
||||
}
|
||||
|
||||
world.spawnEntity(book);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ import com.minelittlepony.unicopia.util.network.C2SPacketType;
|
|||
import com.minelittlepony.unicopia.util.network.SimpleNetworking;
|
||||
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public interface Channel {
|
||||
|
@ -25,6 +27,9 @@ public interface Channel {
|
|||
Identifier SERVER_SELECT_TRIBE_ID = Unicopia.id("select_tribe");
|
||||
S2CPacketType<MsgTribeSelect> SERVER_SELECT_TRIBE = SimpleNetworking.serverToClient(SERVER_SELECT_TRIBE_ID, MsgTribeSelect::new);
|
||||
|
||||
S2CPacketType<MsgSpellbookStateChanged<PlayerEntity>> SERVER_SPELLBOOK_UPDATE = SimpleNetworking.serverToClient(Unicopia.id("server_spellbook_update"), MsgSpellbookStateChanged::new);
|
||||
C2SPacketType<MsgSpellbookStateChanged<ServerPlayerEntity>> CLIENT_SPELLBOOK_UPDATE = SimpleNetworking.clientToServer(Unicopia.id("client_spellbook_update"), MsgSpellbookStateChanged::new);
|
||||
|
||||
Identifier SERVER_RESOURCES_SEND_ID = Unicopia.id("resources_send");
|
||||
S2CPacketType<MsgServerResources> SERVER_RESOURCES_SEND = SimpleNetworking.serverToClient(SERVER_RESOURCES_SEND_ID, MsgServerResources::new);
|
||||
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
package com.minelittlepony.unicopia.network;
|
||||
|
||||
import com.minelittlepony.unicopia.container.SpellbookScreenHandler;
|
||||
import com.minelittlepony.unicopia.container.SpellbookState;
|
||||
import com.minelittlepony.unicopia.util.network.Packet;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
/**
|
||||
* Received by the server when a player changes their opened spellbook's state
|
||||
* Received by the client when another player changes the shared spellbook's state
|
||||
*/
|
||||
public class MsgSpellbookStateChanged<T extends PlayerEntity> implements Packet<T> {
|
||||
|
||||
private final int syncId;
|
||||
private final SpellbookState state;
|
||||
|
||||
public MsgSpellbookStateChanged(int syncId, SpellbookState state) {
|
||||
this.syncId = syncId;
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public MsgSpellbookStateChanged(PacketByteBuf buffer) {
|
||||
syncId = buffer.readInt();
|
||||
state = new SpellbookState().fromPacket(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBuffer(PacketByteBuf buffer) {
|
||||
buffer.writeInt(syncId);
|
||||
state.toPacket(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(T sender) {
|
||||
|
||||
if (sender.currentScreenHandler.syncId != syncId) {
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println("Spellbook update page=" + state.getCurrentPageId().get() + " offset=" + state.getState(state.getCurrentPageId().get()).getOffset() + " to " + sender);
|
||||
|
||||
if (sender.currentScreenHandler instanceof SpellbookScreenHandler spellbook) {
|
||||
spellbook.getSpellbookState().copyFrom(state);
|
||||
if (sender instanceof ServerPlayerEntity) {
|
||||
spellbook.getSpellbookState().synchronize();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package com.minelittlepony.unicopia.network.datasync;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public abstract class Synchronizable<T extends Synchronizable<T>> {
|
||||
|
||||
private Optional<Consumer<T>> synchronizer = Optional.empty();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void synchronize() {
|
||||
synchronizer.ifPresent(s -> s.accept((T)this));
|
||||
}
|
||||
|
||||
public void setSynchronizer(Consumer<T> synchronizer) {
|
||||
this.synchronizer = Optional.of(synchronizer);
|
||||
}
|
||||
|
||||
public abstract void copyFrom(T state);
|
||||
}
|
|
@ -8,7 +8,7 @@ import net.minecraft.util.Identifier;
|
|||
/**
|
||||
* A client packet type. Sent by the server to a specific player.
|
||||
*/
|
||||
public interface S2CPacketType<T extends Packet<PlayerEntity>> {
|
||||
public interface S2CPacketType<T extends Packet<? extends PlayerEntity>> {
|
||||
Identifier getId();
|
||||
|
||||
default void send(ServerPlayerEntity recipient, T packet) {
|
||||
|
|
Loading…
Reference in a new issue