Skip to content

Commit

Permalink
Fire an advancement trigger when reading a book
Browse files Browse the repository at this point in the history
(implements #714)
  • Loading branch information
TheRealWormbo committed Dec 31, 2023
1 parent b87e91a commit f45a1c2
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.CreativeModeTabs;

import vazkii.patchouli.common.advancement.PatchouliCriteriaTriggers;
import vazkii.patchouli.common.base.PatchouliSounds;
import vazkii.patchouli.common.book.BookRegistry;
import vazkii.patchouli.common.command.OpenBookCommand;
Expand All @@ -30,6 +31,7 @@ public void onInitialize() {
CommandRegistrationCallback.EVENT.register((disp, buildCtx, selection) -> OpenBookCommand.register(disp));
UseBlockCallback.EVENT.register(LecternEventHandler::rightClick);

PatchouliCriteriaTriggers.init();
BookRegistry.INSTANCE.init();

ServerLifecycleEvents.END_DATA_PACK_RELOAD.register((server, _r, success) -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import net.minecraftforge.registries.RegisterEvent;

import vazkii.patchouli.api.PatchouliAPI;
import vazkii.patchouli.common.advancement.PatchouliCriteriaTriggers;
import vazkii.patchouli.common.base.PatchouliSounds;
import vazkii.patchouli.common.book.BookRegistry;
import vazkii.patchouli.common.command.OpenBookCommand;
Expand Down Expand Up @@ -73,6 +74,7 @@ public static void onInitialize(FMLCommonSetupEvent evt) {

ForgeNetworkHandler.registerMessages();

PatchouliCriteriaTriggers.init();
BookRegistry.INSTANCE.init();

MinecraftForge.EVENT_BUS.addListener((ServerStartedEvent e) -> ReloadContentsHandler.dataReloaded(e.getServer()));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package vazkii.patchouli.common.advancement;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;

import net.minecraft.advancements.critereon.*;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;

import vazkii.patchouli.api.PatchouliAPI;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* An advancement trigger for opening Patchouli books.
*/
public class PatchouliBookOpenTrigger extends SimpleCriterionTrigger<PatchouliBookOpenTrigger.Instance> {
public static final ResourceLocation ID = new ResourceLocation(PatchouliAPI.MOD_ID, "open_book");
public static final PatchouliBookOpenTrigger INSTANCE = new PatchouliBookOpenTrigger();
private static final String ELEMENT_BOOK = "book";
private static final String ELEMENT_ENTRY = "entry";
private static final String ELEMENT_PAGE = "page";

private PatchouliBookOpenTrigger() {}

@NotNull
@Override
public ResourceLocation getId() {
return ID;
}

@NotNull
@Override
protected PatchouliBookOpenTrigger.Instance createInstance(@NotNull JsonObject json,
@NotNull ContextAwarePredicate playerPred, @NotNull DeserializationContext conditions) {
JsonElement bookElement = json.get(ELEMENT_BOOK);
ResourceLocation book = bookElement instanceof JsonPrimitive bookPrimitive && bookPrimitive.isString()
? new ResourceLocation(bookElement.getAsString()) : null;
JsonElement entryElement = json.get(ELEMENT_ENTRY);
ResourceLocation entry = entryElement instanceof JsonPrimitive entryPrimitive && entryPrimitive.isString()
? new ResourceLocation(entryElement.getAsString()) : null;
JsonElement pageElement = json.get(ELEMENT_PAGE);
int page = pageElement instanceof JsonPrimitive pagePrimitive && pagePrimitive.isNumber() ? pageElement.getAsInt() : 0;
return new PatchouliBookOpenTrigger.Instance(playerPred, book, entry, page);
}

public void trigger(@NotNull ServerPlayer player, @NotNull ResourceLocation book) {
trigger(player, instance -> instance.test(book, null, 0));
}

public void trigger(@NotNull ServerPlayer player, @NotNull ResourceLocation book, @Nullable ResourceLocation entry, int page) {
trigger(player, instance -> instance.test(book, entry, page));
}

public static class Instance extends AbstractCriterionTriggerInstance {
private final ResourceLocation book;
private final ResourceLocation entry;
private final int page;

/**
* Creates a trigger instance that tests against a book ID, and optional entry ID and an optional page number.
* Note that the entry and page are only tested when the server opens the book for a player, not when the player
* navigates to that entry and page while the book is open.
*
* @param playerPred The player predicate.
* @param book The book's {@link ResourceLocation}.
* @param entry The entry's {@link ResourceLocation}. May be {@code null} to skip the check.
* @param page The page number. Specify zero to skip the check.
*/
public Instance(@NotNull ContextAwarePredicate playerPred, @Nullable ResourceLocation book, @Nullable ResourceLocation entry, int page) {
super(ID, playerPred);
if (book == null) {
PatchouliAPI.LOGGER.error("Missing book ID in advancement criterion {} - condition can never be satisfied!", getCriterion());
}
this.book = book;
this.entry = entry;
this.page = page;
}

/**
* Creates a trigger instance that tests only against a book ID.
* This type of instance will match regardless whether the book is opened on a particular entry or page.
*
* @param playerPred The player predicate.
* @param book The book's {@link ResourceLocation}.
*/
@SuppressWarnings("unused")
public Instance(@NotNull ContextAwarePredicate playerPred, @NotNull ResourceLocation book) {
this(playerPred, book, null, 0);
}

@NotNull
@Override
public ResourceLocation getCriterion() {
return ID;
}

@Nullable
public ResourceLocation getBook() {
return this.book;
}

@Nullable
public ResourceLocation getEntry() {
return this.entry;
}

public int getPage() {
return this.page;
}

boolean test(ResourceLocation book, ResourceLocation entry, int page) {
return this.book != null && this.book.equals(book)
&& (this.entry == null || this.entry.equals(entry))
&& (this.page <= 0 || this.page == page);
}

@NotNull
@Override
public JsonObject serializeToJson(@NotNull SerializationContext serializationContext) {
JsonObject json = super.serializeToJson(serializationContext);
if (book != null) {
json.addProperty(ELEMENT_BOOK, book.toString());
}
if (entry != null) {
json.addProperty(ELEMENT_ENTRY, entry.toString());
}
if (page > 0) {
json.addProperty(ELEMENT_PAGE, page);
}
return json;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package vazkii.patchouli.common.advancement;

import vazkii.patchouli.mixin.AccessorCriteriaTriggers;

public class PatchouliCriteriaTriggers {
public static void init() {
AccessorCriteriaTriggers.patchouli$register(PatchouliBookOpenTrigger.INSTANCE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import vazkii.patchouli.client.book.template.BookTemplate;
import vazkii.patchouli.client.book.text.BookTextParser;
import vazkii.patchouli.client.handler.MultiblockVisualizationHandler;
import vazkii.patchouli.common.advancement.PatchouliBookOpenTrigger;
import vazkii.patchouli.common.book.Book;
import vazkii.patchouli.common.book.BookRegistry;
import vazkii.patchouli.common.item.ItemModBook;
Expand Down Expand Up @@ -80,11 +81,13 @@ public boolean getConfigFlag(String flag) {

@Override
public void openBookGUI(ServerPlayer player, ResourceLocation book) {
PatchouliBookOpenTrigger.INSTANCE.trigger(player, book);
IXplatAbstractions.INSTANCE.sendOpenBookGui(player, book, null, 0);
}

@Override
public void openBookEntry(ServerPlayer player, ResourceLocation book, ResourceLocation entry, int page) {
PatchouliBookOpenTrigger.INSTANCE.trigger(player, book, entry, page);
IXplatAbstractions.INSTANCE.sendOpenBookGui(player, book, entry, page);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package vazkii.patchouli.mixin;

import net.minecraft.advancements.CriteriaTriggers;
import net.minecraft.advancements.CriterionTrigger;

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;

@Mixin(CriteriaTriggers.class)
public interface AccessorCriteriaTriggers {
@Invoker("register")
static <T extends CriterionTrigger<?>> T patchouli$register(T thing) {
throw new IllegalStateException();
}
}
1 change: 1 addition & 0 deletions Xplat/src/main/resources/patchouli_xplat.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"package": "vazkii.patchouli.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [
"AccessorCriteriaTriggers",
"AccessorSmithingTransformRecipe",
"AccessorSmithingTrimRecipe"
],
Expand Down

0 comments on commit f45a1c2

Please sign in to comment.