diff --git a/common/src/main/java/com/klikli_dev/modonomicon/api/events/EntryFirstReadEvent.java b/common/src/main/java/com/klikli_dev/modonomicon/api/events/EntryFirstReadEvent.java index aa8cfb20..e869fcd7 100644 --- a/common/src/main/java/com/klikli_dev/modonomicon/api/events/EntryFirstReadEvent.java +++ b/common/src/main/java/com/klikli_dev/modonomicon/api/events/EntryFirstReadEvent.java @@ -12,8 +12,11 @@ /** * An event that is fired on both the client and server side, when an entry is read for the first time. * Note that resetting the book and then reading the entry again, will trigger this event again. + * * If you are e.g. awarding rewards based on this event you should save somewhere that the player already received the entry. * See e.g. {@link BookCommand#execute(ServerPlayer)} which stores how many times a command has been executed. + * + * Further, any rewards or persistent game logic should only be done on the server side call of this event. */ public class EntryFirstReadEvent extends ModonomiconEvent { protected ResourceLocation bookId; 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 e279412f..412116e3 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 @@ -6,6 +6,7 @@ package com.klikli_dev.modonomicon.client.gui; +import com.klikli_dev.modonomicon.api.events.EntryFirstReadEvent; import com.klikli_dev.modonomicon.book.Book; import com.klikli_dev.modonomicon.book.BookCategory; import com.klikli_dev.modonomicon.book.BookDisplayMode; @@ -30,6 +31,7 @@ import com.klikli_dev.modonomicon.client.gui.book.node.BookParentNodeScreen; import com.klikli_dev.modonomicon.client.gui.book.node.DummyBookCategoryNodeScreen; import com.klikli_dev.modonomicon.data.BookDataManager; +import com.klikli_dev.modonomicon.events.ModonomiconEvents; import com.klikli_dev.modonomicon.networking.BookEntryReadMessage; import com.klikli_dev.modonomicon.networking.SaveBookStateMessage; import com.klikli_dev.modonomicon.networking.SaveCategoryStateMessage; @@ -334,6 +336,7 @@ public void openCategoryLinkEntry(CategoryLinkBookEntry entry) { public void openEntry(BookEntry entry, BookAddress address) { if (!BookUnlockStateManager.get().isReadFor(this.player(), entry)) { Services.NETWORK.sendToServer(new BookEntryReadMessage(entry.getBook().getId(), entry.getId())); + ModonomiconEvents.client().entryFirstRead(new EntryFirstReadEvent(entry.getBook().getId(), entry.getId())); } entry.openEntry(address); //visitor pattern that will call openContentEntry or openCategoryLinkEntry diff --git a/common/src/main/java/com/klikli_dev/modonomicon/events/ModonomiconEvents.java b/common/src/main/java/com/klikli_dev/modonomicon/events/ModonomiconEvents.java index ec196efa..7e985faa 100644 --- a/common/src/main/java/com/klikli_dev/modonomicon/events/ModonomiconEvents.java +++ b/common/src/main/java/com/klikli_dev/modonomicon/events/ModonomiconEvents.java @@ -5,6 +5,7 @@ package com.klikli_dev.modonomicon.events; import com.klikli_dev.modonomicon.api.events.EntryClickedEvent; +import com.klikli_dev.modonomicon.api.events.EntryFirstReadEvent; import com.klikli_dev.modonomicon.api.events.EventPriority; import com.mojang.datafixers.util.Pair; @@ -27,7 +28,36 @@ public static Server server() { } public static class Server { + private final List, EventPriority>> entryFirstReadCallbacks = new ArrayList<>(); + /** + * Register a callback for the EntryFirstReadEvent with Normal priority. + * Forge: Should be called in FMLClientSetupEvent + * Fabric: Should be called from ClientModInitializer + */ + public void onEntryFirstRead(Consumer callback) { + this.onEntryFirstRead(callback, EventPriority.NORMAL); + + } + + /** + * Register a callback for the EntryFirstReadEvent with the given priority. + * Forge: Should be called in FMLClientSetupEvent + * Fabric: Should be called from ClientModInitializer + */ + public void onEntryFirstRead(Consumer callback, EventPriority priority) { + this.entryFirstReadCallbacks.add(Pair.of(callback, priority)); + this.entryFirstReadCallbacks.sort((a, b) -> b.getSecond().compareTo(a.getSecond())); + } + + /** + * Fires the entry clicked event. + */ + public void entryFirstRead(EntryFirstReadEvent event) { + for (var callback : this.entryFirstReadCallbacks) { + callback.getFirst().accept(event); + } + } } public static class Client { @@ -67,6 +97,37 @@ public boolean entryClicked(EntryClickedEvent event) { return event.isCanceled(); } + + private final List, EventPriority>> entryFirstReadCallbacks = new ArrayList<>(); + + /** + * Register a callback for the EntryFirstReadEvent with Normal priority. + * Forge: Should be called in FMLClientSetupEvent + * Fabric: Should be called from ClientModInitializer + */ + public void onEntryFirstRead(Consumer callback) { + this.onEntryFirstRead(callback, EventPriority.NORMAL); + + } + + /** + * Register a callback for the EntryFirstReadEvent with the given priority. + * Forge: Should be called in FMLClientSetupEvent + * Fabric: Should be called from ClientModInitializer + */ + public void onEntryFirstRead(Consumer callback, EventPriority priority) { + this.entryFirstReadCallbacks.add(Pair.of(callback, priority)); + this.entryFirstReadCallbacks.sort((a, b) -> b.getSecond().compareTo(a.getSecond())); + } + + /** + * Fires the entry clicked event. + */ + public void entryFirstRead(EntryFirstReadEvent event) { + for (var callback : this.entryFirstReadCallbacks) { + callback.getFirst().accept(event); + } + } } } diff --git a/common/src/main/java/com/klikli_dev/modonomicon/networking/BookEntryReadMessage.java b/common/src/main/java/com/klikli_dev/modonomicon/networking/BookEntryReadMessage.java index 805c2522..30683bfb 100644 --- a/common/src/main/java/com/klikli_dev/modonomicon/networking/BookEntryReadMessage.java +++ b/common/src/main/java/com/klikli_dev/modonomicon/networking/BookEntryReadMessage.java @@ -7,8 +7,10 @@ package com.klikli_dev.modonomicon.networking; import com.klikli_dev.modonomicon.Modonomicon; +import com.klikli_dev.modonomicon.api.events.EntryFirstReadEvent; import com.klikli_dev.modonomicon.bookstate.BookUnlockStateManager; import com.klikli_dev.modonomicon.data.BookDataManager; +import com.klikli_dev.modonomicon.events.ModonomiconEvents; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; @@ -47,6 +49,7 @@ public void onServerReceived(MinecraftServer minecraftServer, ServerPlayer playe //unlock page, then update the unlock capability, finally sync. if (BookUnlockStateManager.get().readFor(player, entry)) { BookUnlockStateManager.get().updateAndSyncFor(player); + ModonomiconEvents.server().entryFirstRead(new EntryFirstReadEvent(entry.getBook().getId(), entry.getId())); } } }