From f9d9134121e13f8097d6a94a9f7386e52364dc3d Mon Sep 17 00:00:00 2001 From: Kli Kli Date: Mon, 17 Jun 2024 08:19:59 +0200 Subject: [PATCH] feat: switch to Fastutil synchronized maps Closes #211 --- .../com/klikli_dev/modonomicon/book/Book.java | 22 +++---- .../modonomicon/book/BookCategory.java | 8 +-- .../book/error/BookErrorManager.java | 7 ++- .../bookstate/BookStatesSaveData.java | 24 ++++---- .../bookstate/BookUnlockStates.java | 58 +++++++++++-------- .../bookstate/BookVisualStates.java | 22 ++++--- .../bookstate/visual/BookVisualState.java | 13 +++-- .../bookstate/visual/CategoryVisualState.java | 8 ++- .../client/gui/book/BookAddress.java | 45 ++++++++++++++ .../modonomicon/data/BookDataManager.java | 16 ++--- .../data/MultiblockDataManager.java | 10 ++-- .../networking/SyncBookDataMessage.java | 10 ++-- .../networking/SyncMultiblockDataMessage.java | 10 ++-- .../klikli_dev/modonomicon/util/Codecs.java | 24 ++------ .../modonomicon/util/StreamCodecs.java | 51 ++++++++++++++++ 15 files changed, 217 insertions(+), 111 deletions(-) create mode 100644 common/src/main/java/com/klikli_dev/modonomicon/util/StreamCodecs.java 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 46b85c5a4..507303802 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 @@ -13,6 +13,8 @@ import com.klikli_dev.modonomicon.client.gui.book.BookAddress; import com.klikli_dev.modonomicon.client.gui.book.markdown.BookTextRenderer; import com.klikli_dev.modonomicon.util.BookGsonHelper; +import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.core.HolderLookup; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.RegistryFriendlyByteBuf; @@ -24,8 +26,6 @@ import java.util.Comparator; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; public class Book { protected ResourceLocation id; @@ -46,9 +46,9 @@ public class Book { protected ResourceLocation craftingTexture; protected ResourceLocation turnPageSound; - protected ConcurrentMap categories; - protected ConcurrentMap entries; - protected ConcurrentMap commands; + protected Map categories; + protected Map entries; + protected Map commands; protected int defaultTitleColor; @@ -97,7 +97,7 @@ public class Book { public Book(ResourceLocation id, String name, BookTextHolder description, String tooltip, ResourceLocation model, BookDisplayMode displayMode, boolean generateBookItem, - ResourceLocation customBookItem, String creativeTab, ResourceLocation font, ResourceLocation bookOverviewTexture, ResourceLocation frameTexture, + @Nullable ResourceLocation customBookItem, String creativeTab, ResourceLocation font, ResourceLocation bookOverviewTexture, ResourceLocation frameTexture, BookFrameOverlay topFrameOverlay, BookFrameOverlay bottomFrameOverlay, BookFrameOverlay leftFrameOverlay, BookFrameOverlay rightFrameOverlay, ResourceLocation bookContentTexture, ResourceLocation craftingTexture, ResourceLocation turnPageSound, int defaultTitleColor, float categoryButtonIconScale, boolean autoAddReadConditions, int bookTextOffsetX, int bookTextOffsetY, int bookTextOffsetWidth, @@ -125,9 +125,9 @@ public Book(ResourceLocation id, String name, BookTextHolder description, String this.defaultTitleColor = defaultTitleColor; this.categoryButtonIconScale = categoryButtonIconScale; this.autoAddReadConditions = autoAddReadConditions; - this.categories = new ConcurrentHashMap<>(); - this.entries = new ConcurrentHashMap<>(); - this.commands = new ConcurrentHashMap<>(); + this.categories = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>()); + this.entries = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>()); + this.commands = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>()); this.bookTextOffsetX = bookTextOffsetX; this.bookTextOffsetY = bookTextOffsetY; this.bookTextOffsetWidth = bookTextOffsetWidth; @@ -394,7 +394,7 @@ public void addCommand(BookCommand command) { this.commands.putIfAbsent(command.id, command); } - public ConcurrentMap getCommands() { + public Map getCommands() { return this.commands; } @@ -464,7 +464,7 @@ public ResourceLocation getModel() { } public BookDisplayMode getDisplayMode() { - if(this.isLeaflet()) { + if (this.isLeaflet()) { return BookDisplayMode.INDEX; } return this.displayMode; diff --git a/common/src/main/java/com/klikli_dev/modonomicon/book/BookCategory.java b/common/src/main/java/com/klikli_dev/modonomicon/book/BookCategory.java index 39cf4d096..07d71342e 100644 --- a/common/src/main/java/com/klikli_dev/modonomicon/book/BookCategory.java +++ b/common/src/main/java/com/klikli_dev/modonomicon/book/BookCategory.java @@ -14,6 +14,8 @@ import com.klikli_dev.modonomicon.book.error.BookErrorManager; import com.klikli_dev.modonomicon.client.gui.book.markdown.BookTextRenderer; import com.klikli_dev.modonomicon.util.BookGsonHelper; +import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.core.HolderLookup; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.RegistryFriendlyByteBuf; @@ -24,8 +26,6 @@ import java.text.MessageFormat; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; public class BookCategory { @@ -49,7 +49,7 @@ public class BookCategory { protected float backgroundTextureZoomMultiplier; protected List backgroundParallaxLayers; protected ResourceLocation entryTextures; - protected ConcurrentMap entries; + protected Map entries; protected BookCondition condition; protected boolean showCategoryButton; /** @@ -78,7 +78,7 @@ public BookCategory(ResourceLocation id, String name, BookTextHolder description this.backgroundTextureZoomMultiplier = backgroundTextureZoomMultiplier; this.backgroundParallaxLayers = backgroundParallaxLayers; this.entryTextures = entryTextures; - this.entries = new ConcurrentHashMap<>(); + this.entries = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>()); this.entryToOpen = entryToOpen; this.openEntryToOpenOnlyOnce = openEntryOnlyOnce; } diff --git a/common/src/main/java/com/klikli_dev/modonomicon/book/error/BookErrorManager.java b/common/src/main/java/com/klikli_dev/modonomicon/book/error/BookErrorManager.java index e5ecfcd81..73aae8762 100644 --- a/common/src/main/java/com/klikli_dev/modonomicon/book/error/BookErrorManager.java +++ b/common/src/main/java/com/klikli_dev/modonomicon/book/error/BookErrorManager.java @@ -9,16 +9,17 @@ import com.klikli_dev.modonomicon.Modonomicon; import com.klikli_dev.modonomicon.book.entries.BookContentEntry; import com.klikli_dev.modonomicon.book.page.BookPage; +import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.resources.ResourceLocation; import org.slf4j.helpers.MessageFormatter; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; +import java.util.Map; public class BookErrorManager { private static final BookErrorManager instance = new BookErrorManager(); - private final ConcurrentMap booksErrors = new ConcurrentHashMap<>(); + private final Map booksErrors = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>()); private final BookErrorContextHelper contextHelper = new BookErrorContextHelper(); private ResourceLocation currentBookId; private String currentContext; diff --git a/common/src/main/java/com/klikli_dev/modonomicon/bookstate/BookStatesSaveData.java b/common/src/main/java/com/klikli_dev/modonomicon/bookstate/BookStatesSaveData.java index f7bc60fd5..3723829d8 100644 --- a/common/src/main/java/com/klikli_dev/modonomicon/bookstate/BookStatesSaveData.java +++ b/common/src/main/java/com/klikli_dev/modonomicon/bookstate/BookStatesSaveData.java @@ -9,33 +9,35 @@ import com.klikli_dev.modonomicon.util.Codecs; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; +import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtOps; import net.minecraft.world.level.saveddata.SavedData; +import org.jetbrains.annotations.NotNull; +import java.util.Map; import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; public class BookStatesSaveData extends SavedData { public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( - Codecs.concurrentMap(Codecs.UUID, BookUnlockStates.CODEC).fieldOf("unlockStates").forGetter((state) -> state.unlockStates), - Codecs.concurrentMap(Codecs.UUID, BookVisualStates.CODEC).fieldOf("visualStates").forGetter((state) -> state.visualStates) + Codec.unboundedMap(Codecs.UUID, BookUnlockStates.CODEC).fieldOf("unlockStates").forGetter((state) -> state.unlockStates), + Codec.unboundedMap(Codecs.UUID, BookVisualStates.CODEC).fieldOf("visualStates").forGetter((state) -> state.visualStates) ).apply(instance, BookStatesSaveData::new)); public static final String ID = "modonomicon_book_states"; - public ConcurrentMap unlockStates; - public ConcurrentMap visualStates; + public Map unlockStates; + public Map visualStates; public BookStatesSaveData() { - this(new ConcurrentHashMap<>(), new ConcurrentHashMap<>()); + this(Object2ObjectMaps.emptyMap(), Object2ObjectMaps.emptyMap()); } - public BookStatesSaveData(ConcurrentMap unlockStates, ConcurrentMap visualStates) { - this.unlockStates = unlockStates; - this.visualStates = visualStates; + public BookStatesSaveData(Map unlockStates, Map visualStates) { + this.unlockStates = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>(unlockStates)); + this.visualStates = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>(visualStates)); this.setDirty(); } @@ -59,7 +61,7 @@ public BookVisualStates getVisualStates(UUID playerUUID) { } @Override - public CompoundTag save(CompoundTag compoundTag, HolderLookup.Provider pHolderProvider) { + public @NotNull CompoundTag save(CompoundTag compoundTag, HolderLookup.@NotNull Provider pHolderProvider) { compoundTag.put("bookStates", CODEC.encodeStart(NbtOps.INSTANCE, this).result().orElseThrow()); return compoundTag; } 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 dd74222a6..f255cac54 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 @@ -22,6 +22,8 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import io.netty.buffer.Unpooled; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +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.FriendlyByteBuf; @@ -32,16 +34,15 @@ import net.minecraft.server.level.ServerPlayer; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; +import java.util.stream.Collectors; public class BookUnlockStates { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - Codecs.concurrentMap(ResourceLocation.CODEC, Codecs.set(ResourceLocation.CODEC)).fieldOf("readEntries").forGetter((s) -> s.readEntries), - Codecs.concurrentMap(ResourceLocation.CODEC, Codecs.mutableMap(ResourceLocation.CODEC, Codecs.set(Codec.INT))).fieldOf("unlockedPages").forGetter((s) -> s.unlockedPages), - Codecs.concurrentMap(ResourceLocation.CODEC, Codecs.set(ResourceLocation.CODEC)).fieldOf("unlockedEntries").forGetter((s) -> s.unlockedEntries), - Codecs.concurrentMap(ResourceLocation.CODEC, Codecs.set(ResourceLocation.CODEC)).fieldOf("unlockedCategories").forGetter((s) -> s.unlockedCategories), - Codecs.concurrentMap(ResourceLocation.CODEC, Codecs.mutableMap(ResourceLocation.CODEC, Codec.INT)).fieldOf("usedCommands").forGetter((s) -> s.usedCommands) + Codec.unboundedMap(ResourceLocation.CODEC, Codecs.set(ResourceLocation.CODEC)).fieldOf("readEntries").forGetter((s) -> s.readEntries), + Codec.unboundedMap(ResourceLocation.CODEC, Codec.unboundedMap(ResourceLocation.CODEC, Codecs.set(Codec.INT))).fieldOf("unlockedPages").forGetter((s) -> s.unlockedPages), + Codec.unboundedMap(ResourceLocation.CODEC, Codecs.set(ResourceLocation.CODEC)).fieldOf("unlockedEntries").forGetter((s) -> s.unlockedEntries), + Codec.unboundedMap(ResourceLocation.CODEC, Codecs.set(ResourceLocation.CODEC)).fieldOf("unlockedCategories").forGetter((s) -> s.unlockedCategories), + Codec.unboundedMap(ResourceLocation.CODEC, Codec.unboundedMap(ResourceLocation.CODEC, Codec.INT)).fieldOf("usedCommands").forGetter((s) -> s.usedCommands) ).apply(instance, BookUnlockStates::new)); public static final StreamCodec STREAM_CODEC = ByteBufCodecs.fromCodecWithRegistries(CODEC); @@ -50,42 +51,53 @@ public class BookUnlockStates { /** * Map Book ID to read entry IDs */ - public ConcurrentMap> readEntries; + public Map> readEntries; /** * Map Book ID to entry IDs to lists of unlocked pages */ - public ConcurrentMap>> unlockedPages; + public Map>> unlockedPages; /** * Map Book ID to unlocked entry IDs */ - public ConcurrentMap> unlockedEntries; + public Map> unlockedEntries; /** * Map Book ID to unlocked categories IDs */ - public ConcurrentMap> unlockedCategories; + public Map> unlockedCategories; /** * Map Book ID to commands used. This is never wiped to avoid reusing reward commands. */ - public ConcurrentMap> usedCommands; + public Map> usedCommands; public BookUnlockStates() { - this(new ConcurrentHashMap<>(), new ConcurrentHashMap<>(), new ConcurrentHashMap<>(), new ConcurrentHashMap<>(), new ConcurrentHashMap<>()); + this(Object2ObjectMaps.emptyMap(), Object2ObjectMaps.emptyMap(), Object2ObjectMaps.emptyMap(), Object2ObjectMaps.emptyMap(), Object2ObjectMaps.emptyMap()); } - public BookUnlockStates(ConcurrentMap> readEntries, - ConcurrentMap>> unlockedPages, - ConcurrentMap> unlockedEntries, - ConcurrentMap> unlockedCategories, - ConcurrentMap> usedCommands) { - this.readEntries = readEntries; - this.unlockedPages = unlockedPages; - this.unlockedEntries = unlockedEntries; - this.unlockedCategories = unlockedCategories; - this.usedCommands = usedCommands; + public BookUnlockStates(Map> readEntries, + Map>> unlockedPages, + Map> unlockedEntries, + Map> unlockedCategories, + Map> usedCommands) { + this.readEntries = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>(readEntries)); + + this.unlockedPages = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>()); + unlockedPages.forEach((bookId, entryPagesMap) -> { + var innerMap = this.unlockedPages.computeIfAbsent(bookId, k -> Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>())); + 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.usedCommands = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>()); + usedCommands.forEach((bookId, commandUsesMap) -> { + var innerMap = this.usedCommands.computeIfAbsent(bookId, k -> Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>())); + innerMap.putAll(commandUsesMap); + }); } public void update(ServerPlayer owner) { 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 1bc94b9ba..552df8a4c 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 @@ -12,34 +12,38 @@ 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.util.Codecs; +import com.klikli_dev.modonomicon.client.gui.book.BookAddress; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import io.netty.buffer.ByteBuf; +import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; import net.minecraft.resources.ResourceLocation; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; +import java.util.List; +import java.util.Map; public class BookVisualStates { public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - Codecs.concurrentMap(ResourceLocation.CODEC, BookVisualState.CODEC).fieldOf("bookStates").forGetter((s) -> s.bookStates) + Codec.unboundedMap(ResourceLocation.CODEC, BookVisualState.CODEC).fieldOf("bookStates").forGetter((s) -> s.bookStates), + Codec.unboundedMap(ResourceLocation.CODEC, BookAddress.CODEC.listOf()).fieldOf("bookBookmarks").forGetter((s) -> s.bookBookmarks) ).apply(instance, BookVisualStates::new)); - //TODO: make proper stream codec public static final StreamCodec STREAM_CODEC = ByteBufCodecs.fromCodec(CODEC); + public Map bookStates; - public ConcurrentMap bookStates; + public Map> bookBookmarks; public BookVisualStates() { - this(new ConcurrentHashMap<>()); + this(Object2ObjectMaps.emptyMap(), Object2ObjectMaps.emptyMap()); } - public BookVisualStates(ConcurrentMap bookStates) { - this.bookStates = bookStates; + public BookVisualStates(Map bookStates, Map> bookBookmarks) { + this.bookStates = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>(bookStates)); + this.bookBookmarks = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>(bookBookmarks)); } public BookVisualState getBookState(Book book) { diff --git a/common/src/main/java/com/klikli_dev/modonomicon/bookstate/visual/BookVisualState.java b/common/src/main/java/com/klikli_dev/modonomicon/bookstate/visual/BookVisualState.java index 35fa60362..1907705c7 100644 --- a/common/src/main/java/com/klikli_dev/modonomicon/bookstate/visual/BookVisualState.java +++ b/common/src/main/java/com/klikli_dev/modonomicon/bookstate/visual/BookVisualState.java @@ -6,25 +6,26 @@ package com.klikli_dev.modonomicon.bookstate.visual; -import com.klikli_dev.modonomicon.util.Codecs; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.Nullable; import java.util.Map; import java.util.Optional; public class BookVisualState { public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( - Codecs.mutableMap(ResourceLocation.CODEC, CategoryVisualState.CODEC).fieldOf("categoryStates").forGetter((state) -> state.categoryStates), + Codec.unboundedMap(ResourceLocation.CODEC, CategoryVisualState.CODEC).fieldOf("categoryStates").forGetter((state) -> state.categoryStates), ResourceLocation.CODEC.optionalFieldOf("openCategory").forGetter((state) -> Optional.ofNullable(state.openCategory)), Codec.INT.fieldOf("openPagesIndex").forGetter((state) -> state.openPagesIndex) ).apply(instance, BookVisualState::new)); public Map categoryStates; + @Nullable public ResourceLocation openCategory; /** @@ -33,13 +34,17 @@ public class BookVisualState { public int openPagesIndex; public BookVisualState() { - this(Object2ObjectMaps.emptyMap(), Optional.empty(), 0); + this(Object2ObjectMaps.emptyMap(), (ResourceLocation) null, 0); } @SuppressWarnings("OptionalUsedAsFieldOrParameterType") public BookVisualState(Map categoryStates, Optional openCategory, int openPagesIndex) { + this(categoryStates, openCategory.orElse(null), openPagesIndex); + } + + public BookVisualState(Map categoryStates, @Nullable ResourceLocation openCategory, int openPagesIndex) { this.categoryStates = new Object2ObjectOpenHashMap<>(categoryStates); - this.openCategory = openCategory.orElse(null); + this.openCategory = openCategory; this.openPagesIndex = openPagesIndex; } } diff --git a/common/src/main/java/com/klikli_dev/modonomicon/bookstate/visual/CategoryVisualState.java b/common/src/main/java/com/klikli_dev/modonomicon/bookstate/visual/CategoryVisualState.java index d390fe51a..e262d9f19 100644 --- a/common/src/main/java/com/klikli_dev/modonomicon/bookstate/visual/CategoryVisualState.java +++ b/common/src/main/java/com/klikli_dev/modonomicon/bookstate/visual/CategoryVisualState.java @@ -9,6 +9,8 @@ import com.klikli_dev.modonomicon.util.Codecs; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.resources.ResourceLocation; @@ -18,7 +20,7 @@ public class CategoryVisualState { public static final Codec CODEC = RecordCodecBuilder.create((instance) -> instance.group( - Codecs.mutableMap(ResourceLocation.CODEC, EntryVisualState.CODEC).fieldOf("entryStates").forGetter((state) -> state.entryStates), + Codec.unboundedMap(ResourceLocation.CODEC, EntryVisualState.CODEC).fieldOf("entryStates").forGetter((state) -> state.entryStates), Codec.FLOAT.fieldOf("scrollX").forGetter((state) -> state.scrollX), Codec.FLOAT.fieldOf("scrollY").forGetter((state) -> state.scrollY), Codec.FLOAT.fieldOf("targetZoom").forGetter((state) -> state.targetZoom), @@ -40,12 +42,12 @@ public class CategoryVisualState { public int openPagesIndex; public CategoryVisualState() { - this(new Object2ObjectOpenHashMap<>(), 0, 0, 0.7f, Optional.empty(), 0); + this(Object2ObjectMaps.emptyMap(), 0, 0, 0.7f, Optional.empty(), 0); } @SuppressWarnings("OptionalUsedAsFieldOrParameterType") public CategoryVisualState(Map entryStates, float scrollX, float scrollY, float targetZoom, Optional openEntry, int openPagesIndex) { - this.entryStates = entryStates; + this.entryStates = new Object2ObjectOpenHashMap<>(entryStates); this.scrollX = scrollX; this.scrollY = scrollY; this.targetZoom = targetZoom; 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 f5ddb58d9..176d37f20 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 @@ -9,18 +9,63 @@ import com.klikli_dev.modonomicon.book.Book; import com.klikli_dev.modonomicon.book.BookCategory; import com.klikli_dev.modonomicon.book.entries.BookEntry; +import com.klikli_dev.modonomicon.util.StreamCodecs; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; import net.minecraft.resources.ResourceLocation; import org.jetbrains.annotations.NotNull; +import java.util.Optional; + /** * Represents an address in a book, consisting of the book, category, entry and page. * Used to navigate to a specific page in a book and to store such a state */ +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") public record BookAddress(@NotNull ResourceLocation bookId, ResourceLocation categoryId, boolean ignoreSavedCategory, ResourceLocation entryId, boolean ignoreSavedEntry, int page, boolean ignoreSavedPage ) { + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + ResourceLocation.CODEC.fieldOf("bookId").forGetter(BookAddress::bookId), + ResourceLocation.CODEC.optionalFieldOf("categoryId").forGetter((address) -> Optional.ofNullable(address.categoryId)), + Codec.BOOL.fieldOf("ignoreSavedCategory").forGetter(BookAddress::ignoreSavedCategory), + ResourceLocation.CODEC.optionalFieldOf("entryId").forGetter((address) -> Optional.ofNullable(address.entryId)), + Codec.BOOL.fieldOf("ignoreSavedEntry").forGetter(BookAddress::ignoreSavedEntry), + Codec.INT.fieldOf("page").forGetter(BookAddress::page), + Codec.BOOL.fieldOf("ignoreSavedPage").forGetter(BookAddress::ignoreSavedPage) + ).apply(instance, BookAddress::new)); + + public static final StreamCodec STREAM_CODEC = StreamCodecs.composite( + ResourceLocation.STREAM_CODEC, + BookAddress::bookId, + ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC), + (address) -> Optional.ofNullable(address.categoryId), + ByteBufCodecs.BOOL, + BookAddress::ignoreSavedCategory, + ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC), + (address) -> Optional.ofNullable(address.entryId), + ByteBufCodecs.BOOL, + BookAddress::ignoreSavedEntry, + ByteBufCodecs.INT, + BookAddress::page, + ByteBufCodecs.BOOL, + BookAddress::ignoreSavedPage, + BookAddress::new + ); + + private BookAddress(@NotNull ResourceLocation bookId, + Optional categoryId, boolean ignoreSavedCategory, + Optional entryId, boolean ignoreSavedEntry, + int page, boolean ignoreSavedPage + ) { + 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); } diff --git a/common/src/main/java/com/klikli_dev/modonomicon/data/BookDataManager.java b/common/src/main/java/com/klikli_dev/modonomicon/data/BookDataManager.java index 12d8d16e7..f7ff63882 100644 --- a/common/src/main/java/com/klikli_dev/modonomicon/data/BookDataManager.java +++ b/common/src/main/java/com/klikli_dev/modonomicon/data/BookDataManager.java @@ -25,6 +25,8 @@ import com.klikli_dev.modonomicon.networking.SyncBookDataMessage; import com.klikli_dev.modonomicon.platform.ClientServices; import com.klikli_dev.modonomicon.platform.Services; +import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.advancements.AdvancementHolder; import net.minecraft.client.Minecraft; import net.minecraft.core.HolderLookup; @@ -38,8 +40,6 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import java.util.stream.Collectors; @@ -49,7 +49,7 @@ public class BookDataManager extends SimpleJsonResourceReloadListener { private static final BookDataManager instance = new BookDataManager(); - private ConcurrentMap books = new ConcurrentHashMap<>(); + private Map books = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>()); private boolean loaded; private boolean booksBuilt; private HolderLookup.Provider registries; @@ -70,7 +70,7 @@ public boolean isLoaded() { return this.loaded; } - public ConcurrentMap getBooks() { + public Map getBooks() { return this.books; } @@ -80,7 +80,7 @@ public Book getBook(ResourceLocation id) { public Message getSyncMessage() { //we hand over a copy of the map, because otherwise in SP scenarios if we clear this.books to prepare for receiving the message, we also clear the books in the message - return new SyncBookDataMessage(new ConcurrentHashMap<>(this.books)); + return new SyncBookDataMessage(this.books); } public boolean areBooksBuilt() { @@ -89,7 +89,7 @@ public boolean areBooksBuilt() { public void onDatapackSyncPacket(SyncBookDataMessage message) { this.preLoad(); - this.books = message.books; + this.books = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>(message.books)); this.onLoadingComplete(); } @@ -324,7 +324,7 @@ protected void apply(Map content, ResourceManager continue; } - var bookEntry = this.loadEntry(entryId, entry.getValue(), books.get(bookId).autoAddReadConditions(), this.registries); + var bookEntry = this.loadEntry(entryId, entry.getValue(), this.books.get(bookId).autoAddReadConditions(), this.registries); //link entry and category var book = this.books.get(bookId); @@ -377,7 +377,7 @@ public static class Client extends SimpleJsonResourceReloadListener { /** * Our local advancement cache, because we cannot just store random advancement in ClientAdvancements -> they get rejected */ - private final ConcurrentMap advancements = new ConcurrentHashMap<>(); + private final Map advancements = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>()); private boolean isFallbackLocale; private boolean isFontInitialized; diff --git a/common/src/main/java/com/klikli_dev/modonomicon/data/MultiblockDataManager.java b/common/src/main/java/com/klikli_dev/modonomicon/data/MultiblockDataManager.java index b42f0e281..bcbfffb2d 100644 --- a/common/src/main/java/com/klikli_dev/modonomicon/data/MultiblockDataManager.java +++ b/common/src/main/java/com/klikli_dev/modonomicon/data/MultiblockDataManager.java @@ -14,6 +14,8 @@ import com.klikli_dev.modonomicon.networking.Message; import com.klikli_dev.modonomicon.networking.SyncMultiblockDataMessage; import com.klikli_dev.modonomicon.platform.Services; +import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.core.HolderLookup; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; @@ -23,8 +25,6 @@ import net.minecraft.util.profiling.ProfilerFiller; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; public class MultiblockDataManager extends SimpleJsonResourceReloadListener { @@ -33,7 +33,7 @@ public class MultiblockDataManager extends SimpleJsonResourceReloadListener { private static final MultiblockDataManager instance = new MultiblockDataManager(); - private ConcurrentMap multiblocks = new ConcurrentHashMap<>(); + private Map multiblocks = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>()); private boolean loaded; private HolderLookup.Provider registries; @@ -63,12 +63,12 @@ public Map getMultiblocks() { public Message getSyncMessage() { //we hand over a copy of the map, because otherwise in SP scenarios if we clear this.multiblocks to prepare for receiving the message, we also clear the books in the message - return new SyncMultiblockDataMessage(new ConcurrentHashMap<>(this.multiblocks)); + return new SyncMultiblockDataMessage(this.multiblocks); } public void onDatapackSyncPacket(SyncMultiblockDataMessage message) { this.preLoad(); - this.multiblocks = message.multiblocks; + this.multiblocks = Object2ObjectMaps.synchronize(new Object2ObjectOpenHashMap<>(message.multiblocks)); this.onLoadingComplete(); } diff --git a/common/src/main/java/com/klikli_dev/modonomicon/networking/SyncBookDataMessage.java b/common/src/main/java/com/klikli_dev/modonomicon/networking/SyncBookDataMessage.java index 15e147417..d790d832c 100644 --- a/common/src/main/java/com/klikli_dev/modonomicon/networking/SyncBookDataMessage.java +++ b/common/src/main/java/com/klikli_dev/modonomicon/networking/SyncBookDataMessage.java @@ -13,6 +13,7 @@ import com.klikli_dev.modonomicon.book.entries.BookEntry; import com.klikli_dev.modonomicon.data.BookDataManager; import com.klikli_dev.modonomicon.data.LoaderRegistry; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.client.Minecraft; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; @@ -20,8 +21,7 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Player; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; +import java.util.Map; public class SyncBookDataMessage implements Message { @@ -29,10 +29,10 @@ public class SyncBookDataMessage implements Message { public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Modonomicon.MOD_ID, "sync_book_data")); public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(SyncBookDataMessage::encode, SyncBookDataMessage::new); - public ConcurrentMap books = new ConcurrentHashMap<>(); + public Map books = new Object2ObjectOpenHashMap<>(); - public SyncBookDataMessage(ConcurrentMap books) { - this.books = books; + public SyncBookDataMessage(Map books) { + this.books = new Object2ObjectOpenHashMap<>(books); } public SyncBookDataMessage(RegistryFriendlyByteBuf buf) { diff --git a/common/src/main/java/com/klikli_dev/modonomicon/networking/SyncMultiblockDataMessage.java b/common/src/main/java/com/klikli_dev/modonomicon/networking/SyncMultiblockDataMessage.java index 4ef464af4..8c7395287 100644 --- a/common/src/main/java/com/klikli_dev/modonomicon/networking/SyncMultiblockDataMessage.java +++ b/common/src/main/java/com/klikli_dev/modonomicon/networking/SyncMultiblockDataMessage.java @@ -10,6 +10,7 @@ import com.klikli_dev.modonomicon.api.multiblock.Multiblock; import com.klikli_dev.modonomicon.data.LoaderRegistry; import com.klikli_dev.modonomicon.data.MultiblockDataManager; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.client.Minecraft; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; @@ -17,8 +18,7 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Player; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; +import java.util.Map; public class SyncMultiblockDataMessage implements Message { @@ -26,10 +26,10 @@ public class SyncMultiblockDataMessage implements Message { public static final Type TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath(Modonomicon.MOD_ID, "sync_multiblock_data")); public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(SyncMultiblockDataMessage::encode, SyncMultiblockDataMessage::new); - public ConcurrentMap multiblocks = new ConcurrentHashMap<>(); + public Map multiblocks = new Object2ObjectOpenHashMap<>(); - public SyncMultiblockDataMessage(ConcurrentMap multiblocks) { - this.multiblocks = multiblocks; + public SyncMultiblockDataMessage(Map multiblocks) { + this.multiblocks = new Object2ObjectOpenHashMap<>(multiblocks); } public SyncMultiblockDataMessage(RegistryFriendlyByteBuf buf) { diff --git a/common/src/main/java/com/klikli_dev/modonomicon/util/Codecs.java b/common/src/main/java/com/klikli_dev/modonomicon/util/Codecs.java index d31bf083c..2cc9e2f0c 100644 --- a/common/src/main/java/com/klikli_dev/modonomicon/util/Codecs.java +++ b/common/src/main/java/com/klikli_dev/modonomicon/util/Codecs.java @@ -7,32 +7,16 @@ package com.klikli_dev.modonomicon.util; import com.mojang.serialization.Codec; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.UUID; public class Codecs { public static final Codec UUID = Codec.STRING.xmap(java.util.UUID::fromString, java.util.UUID::toString); - public static Codec> mutableMap(final Codec keyCodec, final Codec elementCodec) { - return mutableMapFromMap(Codec.unboundedMap(keyCodec, elementCodec)); - } - - public static Codec> mutableMapFromMap(Codec> mapCodec) { - return mapCodec.xmap(Object2ObjectOpenHashMap::new, Object2ObjectOpenHashMap::new); - } - - public static Codec> concurrentMap(final Codec keyCodec, final Codec elementCodec) { - return concurrentMapFromMap(Codec.unboundedMap(keyCodec, elementCodec)); - } - - public static Codec> concurrentMapFromMap(Codec> mapCodec) { - return mapCodec.xmap(ConcurrentHashMap::new, HashMap::new); - } - public static Codec> set(Codec elementCodec) { return setFromList(elementCodec.listOf()); } diff --git a/common/src/main/java/com/klikli_dev/modonomicon/util/StreamCodecs.java b/common/src/main/java/com/klikli_dev/modonomicon/util/StreamCodecs.java new file mode 100644 index 000000000..f513a762a --- /dev/null +++ b/common/src/main/java/com/klikli_dev/modonomicon/util/StreamCodecs.java @@ -0,0 +1,51 @@ +package com.klikli_dev.modonomicon.util; + +import com.mojang.datafixers.util.Function7; +import net.minecraft.network.codec.StreamCodec; + +import java.util.function.Function; + +public class StreamCodecs { + public static StreamCodec composite( + final StreamCodec pCodec1, + final Function pGetter1, + final StreamCodec pCodec2, + final Function pGetter2, + final StreamCodec pCodec3, + final Function pGetter3, + final StreamCodec pCodec4, + final Function pGetter4, + final StreamCodec pCodec5, + final Function pGetter5, + final StreamCodec pCodec6, + final Function pGetter6, + final StreamCodec pCodec7, + final Function pGetter7, + final Function7 pFactory + ) { + return new StreamCodec() { + @Override + public C decode(B p_330310_) { + T1 t1 = pCodec1.decode(p_330310_); + T2 t2 = pCodec2.decode(p_330310_); + T3 t3 = pCodec3.decode(p_330310_); + T4 t4 = pCodec4.decode(p_330310_); + T5 t5 = pCodec5.decode(p_330310_); + T6 t6 = pCodec6.decode(p_330310_); + T7 t7 = pCodec7.decode(p_330310_); + return pFactory.apply(t1, t2, t3, t4, t5, t6, t7); + } + + @Override + public void encode(B p_332052_, C p_331912_) { + pCodec1.encode(p_332052_, pGetter1.apply(p_331912_)); + pCodec2.encode(p_332052_, pGetter2.apply(p_331912_)); + pCodec3.encode(p_332052_, pGetter3.apply(p_331912_)); + pCodec4.encode(p_332052_, pGetter4.apply(p_331912_)); + pCodec5.encode(p_332052_, pGetter5.apply(p_331912_)); + pCodec6.encode(p_332052_, pGetter6.apply(p_331912_)); + pCodec7.encode(p_332052_, pGetter7.apply(p_331912_)); + } + }; + } +}