From 872da1c0fd4eed05bac688618a66cfaa86b533aa Mon Sep 17 00:00:00 2001 From: Kli Kli Date: Mon, 17 Jun 2024 13:43:16 +0200 Subject: [PATCH] feat: Bookmarks (#219) * feat: add bookmark storage and bookmark access helpers * feat: add bookmark screen * feat: add buttons for bookmark system * feat: add show category button to index screens * feat: add add/remove bookmark buttons Also update some map init code and use fastutil maps more extensively * fix: neo network packet registration (play direction) * fix: crash * fix: locked entries are clickable --- .../modonomicon/api/ModonomiconConstants.java | 7 + .../AbstractModonomiconLanguageProvider.java | 3 +- .../api/datagen/ModonomiconProviderBase.java | 2 +- .../com/klikli_dev/modonomicon/book/Book.java | 2 +- .../bookstate/BookUnlockStates.java | 7 +- .../bookstate/BookVisualStateManager.java | 23 ++ .../bookstate/BookVisualStates.java | 21 +- .../client/gui/BookGuiManager.java | 4 - .../client/gui/book/BookAddress.java | 9 +- .../client/gui/book/BookParentScreen.java | 2 +- .../book/bookmarks/BookBookmarksScreen.java | 269 ++++++++++++++++++ .../gui/book/button/AddBookmarkButton.java | 66 +++++ .../gui/book/button/EntryListButton.java | 14 +- .../gui/book/button/RemoveBookmarkButton.java | 65 +++++ .../gui/book/button/ShowBookmarksButton.java | 64 +++++ .../book/entry/BookEntryDoublePageScreen.java | 5 + .../gui/book/entry/BookEntryScreen.java | 73 +++++ .../book/entry/BookEntrySinglePageScreen.java | 8 +- .../gui/book/index/BookParentIndexScreen.java | 18 +- .../gui/book/node/BookParentNodeScreen.java | 18 +- .../render/page/PageRendererRegistry.java | 11 +- .../modonomicon/data/LoaderRegistry.java | 25 +- .../modonomicon/datagen/EnUsProvider.java | 17 ++ .../networking/AddBookmarkMessage.java | 52 ++++ .../networking/RemoveBookmarkMessage.java | 48 ++++ .../SyncBookUnlockStatesMessage.java | 8 +- .../SyncBookVisualStatesMessage.java | 12 +- .../textures/gui/book_overview.png | Bin 5829 -> 1578 bytes .../modonomicon/network/Networking.java | 4 + .../modonomicon/network/Networking.java | 12 + .../c622617f6fabf890a00b9275cd5f643584a8a2c8 | 4 +- .../assets/modonomicon/lang/en_us.json | 7 + .../modonomicon/network/Networking.java | 6 +- 33 files changed, 840 insertions(+), 46 deletions(-) create mode 100644 common/src/main/java/com/klikli_dev/modonomicon/client/gui/book/bookmarks/BookBookmarksScreen.java create mode 100644 common/src/main/java/com/klikli_dev/modonomicon/client/gui/book/button/AddBookmarkButton.java create mode 100644 common/src/main/java/com/klikli_dev/modonomicon/client/gui/book/button/RemoveBookmarkButton.java create mode 100644 common/src/main/java/com/klikli_dev/modonomicon/client/gui/book/button/ShowBookmarksButton.java create mode 100644 common/src/main/java/com/klikli_dev/modonomicon/networking/AddBookmarkMessage.java create mode 100644 common/src/main/java/com/klikli_dev/modonomicon/networking/RemoveBookmarkMessage.java diff --git a/common/src/main/java/com/klikli_dev/modonomicon/api/ModonomiconConstants.java b/common/src/main/java/com/klikli_dev/modonomicon/api/ModonomiconConstants.java index 75c1dea75..19adaf67c 100644 --- a/common/src/main/java/com/klikli_dev/modonomicon/api/ModonomiconConstants.java +++ b/common/src/main/java/com/klikli_dev/modonomicon/api/ModonomiconConstants.java @@ -153,8 +153,15 @@ public static class Gui { public static final String BOOK_INDEX_LIST_TITLE = PREFIX + "book.index_list_title"; public static final String CATEGORY_INDEX_LIST_TITLE = PREFIX + "category.index_list_title"; + public static final String BOOKMARKS_SCREEN_TITLE = PREFIX + "bookmarks.screen.title"; + public static final String BOOKMARKS_INFO_TEXT = PREFIX + "bookmarks.info"; + public static final String BOOKMARKS_ENTRY_LIST_TITLE = PREFIX + "bookmarks.entry_list_title"; + public static final String BOOKMARKS_NO_RESULTS = PREFIX + "bookmarks.no_results"; public static final String OPEN_SEARCH = PREFIX + "open_search"; + public static final String OPEN_BOOKMARKS = PREFIX + "open_bookmarks"; + public static final String ADD_BOOKMARK = PREFIX + "add_bookmark"; + public static final String REMOVE_BOOKMARK = PREFIX + "remove_bookmark"; public static final String RECIPE_PAGE_RECIPE_MISSING = PREFIX + "recipe_page.recipe_missing"; } diff --git a/common/src/main/java/com/klikli_dev/modonomicon/api/datagen/AbstractModonomiconLanguageProvider.java b/common/src/main/java/com/klikli_dev/modonomicon/api/datagen/AbstractModonomiconLanguageProvider.java index 92512f669..c3c386125 100644 --- a/common/src/main/java/com/klikli_dev/modonomicon/api/datagen/AbstractModonomiconLanguageProvider.java +++ b/common/src/main/java/com/klikli_dev/modonomicon/api/datagen/AbstractModonomiconLanguageProvider.java @@ -5,6 +5,7 @@ package com.klikli_dev.modonomicon.api.datagen; import com.google.gson.JsonObject; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.data.CachedOutput; import net.minecraft.data.DataProvider; import net.minecraft.data.PackOutput; @@ -23,7 +24,7 @@ import java.util.function.Supplier; public abstract class AbstractModonomiconLanguageProvider implements ModonomiconLanguageProvider, DataProvider { - private final Map data = new TreeMap<>(); + private final Map data = new Object2ObjectOpenHashMap<>(); private final PackOutput output; private final String modid; private final String locale; diff --git a/common/src/main/java/com/klikli_dev/modonomicon/api/datagen/ModonomiconProviderBase.java b/common/src/main/java/com/klikli_dev/modonomicon/api/datagen/ModonomiconProviderBase.java index e15fe5863..fdc743ba2 100644 --- a/common/src/main/java/com/klikli_dev/modonomicon/api/datagen/ModonomiconProviderBase.java +++ b/common/src/main/java/com/klikli_dev/modonomicon/api/datagen/ModonomiconProviderBase.java @@ -43,7 +43,7 @@ public abstract class ModonomiconProviderBase { protected final Map translations; protected final BookContextHelper context; protected final ConditionHelper conditionHelper; - private Map macros = new Object2ObjectOpenHashMap<>(); + private final Map macros = new Object2ObjectOpenHashMap<>(); protected ModonomiconProviderBase(String modId, ModonomiconLanguageProvider lang, Map translations, BookContextHelper context, ConditionHelper conditionHelper) { this.modId = modId; diff --git a/common/src/main/java/com/klikli_dev/modonomicon/book/Book.java b/common/src/main/java/com/klikli_dev/modonomicon/book/Book.java index 507303802..7b8a176b1 100644 --- a/common/src/main/java/com/klikli_dev/modonomicon/book/Book.java +++ b/common/src/main/java/com/klikli_dev/modonomicon/book/Book.java @@ -516,7 +516,7 @@ public boolean isLeaflet() { public BookAddress getLeafletAddress() { var leafletEntry = this.getEntry(this.leafletEntry); - return BookAddress.ignoreSavedAndOpen(leafletEntry); + return BookAddress.ignoreSaved(leafletEntry); } public PageDisplayMode getPageDisplayMode() { diff --git a/common/src/main/java/com/klikli_dev/modonomicon/bookstate/BookUnlockStates.java b/common/src/main/java/com/klikli_dev/modonomicon/bookstate/BookUnlockStates.java index f255cac54..28c128216 100644 --- a/common/src/main/java/com/klikli_dev/modonomicon/bookstate/BookUnlockStates.java +++ b/common/src/main/java/com/klikli_dev/modonomicon/bookstate/BookUnlockStates.java @@ -90,8 +90,11 @@ public BookUnlockStates(Map> readEntries entryPagesMap.forEach((entryId, pages) -> innerMap.put(entryId, new ObjectOpenHashSet<>(pages))); }); - this.unlockedEntries = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>(unlockedEntries)); - this.unlockedCategories = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>(unlockedCategories)); + this.unlockedEntries = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>()); + unlockedEntries.forEach((bookId, entries) -> this.unlockedEntries.put(bookId, new ObjectOpenHashSet<>(entries))); + + this.unlockedCategories = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>()); + unlockedCategories.forEach((bookId, categories) -> this.unlockedCategories.put(bookId, new ObjectOpenHashSet<>(categories))); this.usedCommands = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>()); usedCommands.forEach((bookId, commandUsesMap) -> { diff --git a/common/src/main/java/com/klikli_dev/modonomicon/bookstate/BookVisualStateManager.java b/common/src/main/java/com/klikli_dev/modonomicon/bookstate/BookVisualStateManager.java index af97ea135..4d67a7ec6 100644 --- a/common/src/main/java/com/klikli_dev/modonomicon/bookstate/BookVisualStateManager.java +++ b/common/src/main/java/com/klikli_dev/modonomicon/bookstate/BookVisualStateManager.java @@ -13,6 +13,7 @@ import com.klikli_dev.modonomicon.bookstate.visual.BookVisualState; import com.klikli_dev.modonomicon.bookstate.visual.CategoryVisualState; import com.klikli_dev.modonomicon.bookstate.visual.EntryVisualState; +import com.klikli_dev.modonomicon.client.gui.book.BookAddress; import com.klikli_dev.modonomicon.networking.RequestSyncBookStatesMessage; import com.klikli_dev.modonomicon.networking.SyncBookVisualStatesMessage; import com.klikli_dev.modonomicon.platform.Services; @@ -21,6 +22,8 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.saveddata.SavedData; +import java.util.List; + public class BookVisualStateManager { private static final BookVisualStateManager instance = new BookVisualStateManager(); @@ -47,6 +50,10 @@ public EntryVisualState getEntryStateFor(Player player, BookEntry entry) { return this.getStateFor(player).getEntryState(entry); } + public List getBookmarksFor(Player player, Book book) { + return this.getStateFor(player).getBookmarks(book); + } + public void setEntryStateFor(ServerPlayer player, BookEntry entry, EntryVisualState state) { this.getStateFor(player).setEntryState(entry, state); this.saveData.setDirty(); @@ -62,6 +69,22 @@ public void setBookStateFor(ServerPlayer player, Book book, BookVisualState stat this.saveData.setDirty(); } + public void setBookmarksFor(ServerPlayer player, Book book, List bookmarks) { + this.getStateFor(player).setBookmarks(book, bookmarks); + this.saveData.setDirty(); + } + + public void addBookmarkFor(Player player, Book book, BookAddress bookmark) { + this.getStateFor(player).addBookmark(book, bookmark); + this.saveData.setDirty(); + } + + public boolean removeBookmarkFor(Player player, Book book, BookAddress bookmark) { + var result = this.getStateFor(player).removeBookmark(book, bookmark); + this.saveData.setDirty(); + return result; + } + public void syncFor(ServerPlayer player) { Services.NETWORK.sendTo(player, new SyncBookVisualStatesMessage(this.getStateFor(player))); } diff --git a/common/src/main/java/com/klikli_dev/modonomicon/bookstate/BookVisualStates.java b/common/src/main/java/com/klikli_dev/modonomicon/bookstate/BookVisualStates.java index 552df8a4c..d4115c756 100644 --- a/common/src/main/java/com/klikli_dev/modonomicon/bookstate/BookVisualStates.java +++ b/common/src/main/java/com/klikli_dev/modonomicon/bookstate/BookVisualStates.java @@ -18,10 +18,12 @@ import io.netty.buffer.ByteBuf; import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; import net.minecraft.resources.ResourceLocation; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -43,7 +45,8 @@ public BookVisualStates() { public BookVisualStates(Map bookStates, Map> bookBookmarks) { this.bookStates = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>(bookStates)); - this.bookBookmarks = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>(bookBookmarks)); + this.bookBookmarks = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>()); + bookBookmarks.forEach((bookId, entries) -> this.bookBookmarks.put(bookId, new ArrayList<>(entries))); } public BookVisualState getBookState(Book book) { @@ -58,6 +61,10 @@ public EntryVisualState getEntryState(BookEntry entry) { return this.getCategoryState(entry.getCategory()).entryStates.computeIfAbsent(entry.getId(), (id) -> new EntryVisualState()); } + public List getBookmarks(Book book) { + return this.bookBookmarks.computeIfAbsent(book.getId(), (id) -> new ArrayList<>()); + } + public void setBookState(Book book, BookVisualState state) { this.bookStates.put(book.getId(), state); } @@ -69,4 +76,16 @@ public void setEntryState(BookEntry entry, EntryVisualState state) { public void setCategoryState(BookCategory category, CategoryVisualState state) { this.getBookState(category.getBook()).categoryStates.put(category.getId(), state); } + + public void setBookmarks(Book book, List bookmarks) { + this.bookBookmarks.put(book.getId(), bookmarks); + } + + public void addBookmark(Book book, BookAddress bookmark) { + this.getBookmarks(book).add(bookmark); + } + + public boolean removeBookmark(Book book, BookAddress bookmark) { + return this.getBookmarks(book).remove(bookmark); + } } diff --git a/common/src/main/java/com/klikli_dev/modonomicon/client/gui/BookGuiManager.java b/common/src/main/java/com/klikli_dev/modonomicon/client/gui/BookGuiManager.java index 8da53f429..b13cd4cb2 100644 --- a/common/src/main/java/com/klikli_dev/modonomicon/client/gui/BookGuiManager.java +++ b/common/src/main/java/com/klikli_dev/modonomicon/client/gui/BookGuiManager.java @@ -212,10 +212,6 @@ protected void openBookInIndexMode(Book book, BookAddress address) { @ApiStatus.Internal public void openCategory(BookCategory category, BookAddress address) { if (this.openBookCategoryScreen != null) { - //skip if the category is already open - if (this.openBookCategoryScreen.getCategory() == category) - return; - BookGuiManager.get().closeCategoryScreen(this.openBookCategoryScreen); } diff --git a/common/src/main/java/com/klikli_dev/modonomicon/client/gui/book/BookAddress.java b/common/src/main/java/com/klikli_dev/modonomicon/client/gui/book/BookAddress.java index 176d37f20..11b2978ed 100644 --- a/common/src/main/java/com/klikli_dev/modonomicon/client/gui/book/BookAddress.java +++ b/common/src/main/java/com/klikli_dev/modonomicon/client/gui/book/BookAddress.java @@ -66,8 +66,13 @@ private BookAddress(@NotNull ResourceLocation bookId, this(bookId, categoryId.orElse(null), ignoreSavedCategory, entryId.orElse(null), ignoreSavedEntry, page, ignoreSavedPage); } - public static BookAddress ignoreSavedAndOpen(@NotNull BookEntry entry) { - return ignoreSaved(entry.getBook().getId(), entry.getCategory().getId(), entry.getId(), -1); + public static BookAddress ignoreSaved(@NotNull BookEntry entry, int page) { + return ignoreSaved(entry.getBook().getId(), entry.getCategory().getId(), entry.getId(), page); + } + + + public static BookAddress ignoreSaved(@NotNull BookEntry entry) { + return ignoreSaved(entry, -1); } public static BookAddress defaultFor(@NotNull BookCategory category) { diff --git a/common/src/main/java/com/klikli_dev/modonomicon/client/gui/book/BookParentScreen.java b/common/src/main/java/com/klikli_dev/modonomicon/client/gui/book/BookParentScreen.java index dbe68f5d2..23fbf3343 100644 --- a/common/src/main/java/com/klikli_dev/modonomicon/client/gui/book/BookParentScreen.java +++ b/common/src/main/java/com/klikli_dev/modonomicon/client/gui/book/BookParentScreen.java @@ -27,5 +27,5 @@ public interface BookParentScreen { void saveState(BookVisualState state); - void onSyncBookUnlockCapabilityMessage(SyncBookUnlockStatesMessage message); + void onSyncBookUnlockStatesMessage(SyncBookUnlockStatesMessage message); } diff --git a/common/src/main/java/com/klikli_dev/modonomicon/client/gui/book/bookmarks/BookBookmarksScreen.java b/common/src/main/java/com/klikli_dev/modonomicon/client/gui/book/bookmarks/BookBookmarksScreen.java new file mode 100644 index 000000000..bc69aaca5 --- /dev/null +++ b/common/src/main/java/com/klikli_dev/modonomicon/client/gui/book/bookmarks/BookBookmarksScreen.java @@ -0,0 +1,269 @@ +/* + * SPDX-FileCopyrightText: 2022 klikli-dev + * SPDX-FileCopyrightText: 2021 Authors of Patchouli + * + * SPDX-License-Identifier: MIT + */ + +package com.klikli_dev.modonomicon.client.gui.book.bookmarks; + +import com.klikli_dev.modonomicon.api.ModonomiconConstants.I18n.Gui; +import com.klikli_dev.modonomicon.book.Book; +import com.klikli_dev.modonomicon.book.BookTextHolder; +import com.klikli_dev.modonomicon.book.RenderedBookTextHolder; +import com.klikli_dev.modonomicon.bookstate.BookUnlockStateManager; +import com.klikli_dev.modonomicon.bookstate.BookVisualStateManager; +import com.klikli_dev.modonomicon.client.gui.BookGuiManager; +import com.klikli_dev.modonomicon.client.gui.book.BookAddress; +import com.klikli_dev.modonomicon.client.gui.book.BookPaginatedScreen; +import com.klikli_dev.modonomicon.client.gui.book.BookParentScreen; +import com.klikli_dev.modonomicon.client.gui.book.button.EntryListButton; +import com.klikli_dev.modonomicon.client.gui.book.entry.BookEntryScreen; +import com.klikli_dev.modonomicon.client.gui.book.markdown.BookTextRenderer; +import com.klikli_dev.modonomicon.client.render.page.BookPageRenderer; +import com.klikli_dev.modonomicon.platform.ClientServices; +import com.klikli_dev.modonomicon.util.GuiGraphicsExt; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.components.Button; +import net.minecraft.network.chat.Component; +import org.lwjgl.glfw.GLFW; + +import java.util.ArrayList; +import java.util.List; + +public class BookBookmarksScreen extends BookPaginatedScreen { + public static final int ENTRIES_PER_PAGE = 13; + public static final int ENTRIES_IN_FIRST_PAGE = 11; + protected final List