From 61857bd4b6f7402d82d71944739940296993b0d8 Mon Sep 17 00:00:00 2001 From: Felix Cravic Date: Wed, 5 Aug 2020 10:56:16 +0200 Subject: [PATCH] Advancement API --- src/main/java/fr/themode/demo/PlayerInit.java | 66 +----- .../net/minestom/server/MinecraftServer.java | 7 + .../server/advancements/Advancement.java | 209 ++++++++++++++++++ .../advancements/AdvancementManager.java | 20 ++ .../server/advancements/AdvancementRoot.java | 25 +++ .../server/advancements/AdvancementTab.java | 128 +++++++++++ .../notifications/NotificationCenter.java | 2 +- 7 files changed, 402 insertions(+), 55 deletions(-) create mode 100644 src/main/java/net/minestom/server/advancements/Advancement.java create mode 100644 src/main/java/net/minestom/server/advancements/AdvancementManager.java create mode 100644 src/main/java/net/minestom/server/advancements/AdvancementRoot.java create mode 100644 src/main/java/net/minestom/server/advancements/AdvancementTab.java diff --git a/src/main/java/fr/themode/demo/PlayerInit.java b/src/main/java/fr/themode/demo/PlayerInit.java index 583b94199..b686062fc 100644 --- a/src/main/java/fr/themode/demo/PlayerInit.java +++ b/src/main/java/fr/themode/demo/PlayerInit.java @@ -3,7 +3,7 @@ package fr.themode.demo; import fr.themode.demo.generator.ChunkGeneratorDemo; import fr.themode.demo.generator.NoiseTestGenerator; import net.minestom.server.MinecraftServer; -import net.minestom.server.advancements.FrameType; +import net.minestom.server.advancements.*; import net.minestom.server.benchmark.BenchmarkManager; import net.minestom.server.benchmark.ThreadResult; import net.minestom.server.chat.ChatColor; @@ -25,7 +25,6 @@ import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; import net.minestom.server.item.metadata.MapMeta; import net.minestom.server.network.ConnectionManager; -import net.minestom.server.network.packet.server.play.AdvancementsPacket; import net.minestom.server.ping.ResponseDataConsumer; import net.minestom.server.utils.MathUtils; import net.minestom.server.utils.Position; @@ -226,62 +225,21 @@ public class PlayerInit { scoreboard.setTitle("test");*/ - // Testing advancements - AdvancementsPacket advancementsPacket = new AdvancementsPacket(); - advancementsPacket.resetAdvancements = true; - - AdvancementsPacket.AdvancementMapping firstMapping = new AdvancementsPacket.AdvancementMapping(); { - AdvancementsPacket.Advancement firstAdvancement = new AdvancementsPacket.Advancement(); - firstMapping.key = "minestom:advancement"; - firstMapping.value = firstAdvancement; + AdvancementManager advancementManager = MinecraftServer.getAdvancementManager(); + AdvancementRoot root = new AdvancementRoot(ColoredText.of("title"), ColoredText.of(ChatColor.BLUE + "description"), + Material.APPLE, FrameType.TASK, 0, 0, "minecraft:textures/block/red_wool.png"); + AdvancementTab tab = advancementManager.createTab("root", root); + Advancement advancement = new Advancement(ColoredText.of("adv"), ColoredText.of("desc"), + Material.WOODEN_AXE, FrameType.CHALLENGE, 1, 0) + .showToast(true).setHidden(false); + tab.createAdvancement("second", advancement, root); - AdvancementsPacket.DisplayData displayData = new AdvancementsPacket.DisplayData(); - displayData.x = 0.0F; - displayData.y = 0.0F; - displayData.title = ColoredText.of("Hello"); - displayData.description = ColoredText.of("Hello"); - displayData.icon = new ItemStack(Material.DIRT, (byte) 1); - displayData.frameType = FrameType.TASK; - displayData.flags = 0x1; - displayData.backgroundTexture = "minecraft:textures/block/red_wool.png"; - - firstAdvancement.displayData = displayData; - firstAdvancement.criterions = new String[]{}; - firstAdvancement.requirements = new AdvancementsPacket.Requirement[]{}; + tab.addViewer(player); + advancement.setTitle(ColoredText.of("test ttlechange")); + //player.getPlayerConnection().sendPacket(tab.removePacket()); } - // This advancement will be to the bottom right of the firstAdvancement - // The background of this advancement is apparentely ignored! - AdvancementsPacket.AdvancementMapping secondMapping = new AdvancementsPacket.AdvancementMapping(); - { - AdvancementsPacket.Advancement secondAdvancement = new AdvancementsPacket.Advancement(); - secondMapping.key = "minestom:advance"; - secondMapping.value = secondAdvancement; - - secondAdvancement.parentIdentifier = "minestom:advancement"; - - AdvancementsPacket.DisplayData displayData = new AdvancementsPacket.DisplayData(); - displayData.x = 2.0F; - displayData.y = 2.0F; - displayData.title = ColoredText.of("Hello World"); - displayData.description = ColoredText.of("Hello World"); - displayData.icon = new ItemStack(Material.DIAMOND, (byte) 1); - displayData.frameType = FrameType.GOAL; - displayData.flags = 0x2; - - secondAdvancement.displayData = displayData; - secondAdvancement.criterions = new String[]{}; - secondAdvancement.requirements = new AdvancementsPacket.Requirement[]{}; - } - - - advancementsPacket.identifiersToRemove = new String[]{}; - advancementsPacket.advancementMappings = new AdvancementsPacket.AdvancementMapping[]{firstMapping, secondMapping}; - advancementsPacket.progressMappings = new AdvancementsPacket.ProgressMapping[]{}; - - - player.getPlayerConnection().sendPacket(advancementsPacket); }); player.addEventCallback(PlayerSpawnEvent.class, event -> { diff --git a/src/main/java/net/minestom/server/MinecraftServer.java b/src/main/java/net/minestom/server/MinecraftServer.java index a4fc1140a..0ba49ee98 100644 --- a/src/main/java/net/minestom/server/MinecraftServer.java +++ b/src/main/java/net/minestom/server/MinecraftServer.java @@ -5,6 +5,7 @@ import com.mojang.authlib.minecraft.MinecraftSessionService; import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; import lombok.Getter; import lombok.Setter; +import net.minestom.server.advancements.AdvancementManager; import net.minestom.server.benchmark.BenchmarkManager; import net.minestom.server.command.CommandManager; import net.minestom.server.data.DataManager; @@ -107,6 +108,7 @@ public class MinecraftServer { private static SchedulerManager schedulerManager; private static BenchmarkManager benchmarkManager; private static DimensionTypeManager dimensionTypeManager; + private static AdvancementManager advancementManager; private static UpdateManager updateManager; private static MinecraftServer minecraftServer; @@ -157,6 +159,7 @@ public class MinecraftServer { schedulerManager = new SchedulerManager(); benchmarkManager = new BenchmarkManager(); dimensionTypeManager = new DimensionTypeManager(); + advancementManager = new AdvancementManager(); updateManager = new UpdateManager(); @@ -277,6 +280,10 @@ public class MinecraftServer { return dimensionTypeManager; } + public static AdvancementManager getAdvancementManager() { + return advancementManager; + } + public static TagManager getTagManager() { return tagManager; } diff --git a/src/main/java/net/minestom/server/advancements/Advancement.java b/src/main/java/net/minestom/server/advancements/Advancement.java new file mode 100644 index 000000000..3d24d885a --- /dev/null +++ b/src/main/java/net/minestom/server/advancements/Advancement.java @@ -0,0 +1,209 @@ +package net.minestom.server.advancements; + +import net.minestom.server.chat.ColoredText; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import net.minestom.server.network.packet.server.play.AdvancementsPacket; + +public class Advancement { + + protected AdvancementTab tab; + + private ColoredText title; + private ColoredText description; + + private ItemStack icon; + + private FrameType frameType; + + private String background; // Only on root + private boolean toast; + private boolean hidden; + + private float x, y; + + private String identifier; + private Advancement parent; + + public Advancement(ColoredText title, ColoredText description, + ItemStack icon, FrameType frameType, + float x, float y) { + this.title = title; + this.description = description; + this.icon = icon; + this.frameType = frameType; + this.x = x; + this.y = y; + } + + public Advancement(ColoredText title, ColoredText description, + Material icon, FrameType frameType, + float x, float y) { + this(title, description, new ItemStack(icon, (byte) 1), frameType, x, y); + } + + public AdvancementTab getTab() { + return tab; + } + + public void setTab(AdvancementTab tab) { + this.tab = tab; + } + + public ColoredText getTitle() { + return title; + } + + public void setTitle(ColoredText title) { + this.title = title; + update(); + } + + public ColoredText getDescription() { + return description; + } + + public void setDescription(ColoredText description) { + this.description = description; + update(); + } + + public boolean hasToast() { + return toast; + } + + public Advancement showToast(boolean toast) { + this.toast = toast; + return this; + } + + public boolean isHidden() { + return hidden; + } + + public Advancement setHidden(boolean hidden) { + this.hidden = hidden; + update(); + return this; + } + + public FrameType getFrameType() { + return frameType; + } + + public void setFrameType(FrameType frameType) { + this.frameType = frameType; + update(); + } + + public float getX() { + return x; + } + + public void setX(float x) { + this.x = x; + update(); + } + + public float getY() { + return y; + } + + public void setY(float y) { + this.y = y; + update(); + } + + protected void setBackground(String background) { + this.background = background; + } + + protected String getIdentifier() { + return identifier; + } + + protected void setIdentifier(String identifier) { + this.identifier = identifier; + } + + protected Advancement getParent() { + return parent; + } + + protected void setParent(Advancement parent) { + this.parent = parent; + } + + protected AdvancementsPacket.DisplayData toDisplayData() { + AdvancementsPacket.DisplayData displayData = new AdvancementsPacket.DisplayData(); + displayData.x = x; + displayData.y = y; + displayData.title = title; + displayData.description = description; + displayData.icon = icon; + displayData.frameType = frameType; + displayData.flags = getFlags(); + if (background != null) { + displayData.backgroundTexture = background; + } + return displayData; + } + + protected AdvancementsPacket getUpdatePacket() { + AdvancementsPacket advancementsPacket = new AdvancementsPacket(); + advancementsPacket.resetAdvancements = false; + + AdvancementsPacket.AdvancementMapping mapping = new AdvancementsPacket.AdvancementMapping(); + { + AdvancementsPacket.Advancement adv = new AdvancementsPacket.Advancement(); + mapping.key = getIdentifier(); + mapping.value = adv; + + final Advancement parent = getParent(); + if (parent != null) { + final String parentIdentifier = parent.getIdentifier(); + adv.parentIdentifier = parentIdentifier; + } + + adv.displayData = toDisplayData(); + adv.criterions = new String[]{}; + adv.requirements = new AdvancementsPacket.Requirement[]{}; + } + + advancementsPacket.identifiersToRemove = new String[]{}; + advancementsPacket.advancementMappings = new AdvancementsPacket.AdvancementMapping[]{mapping}; + advancementsPacket.progressMappings = new AdvancementsPacket.ProgressMapping[]{}; + + return advancementsPacket; + } + + /** + * Update this advancement value when a field is modified + */ + protected void update() { + if (tab != null) { + // TODO: how to update an advancement without clearing everything + //final AdvancementsPacket packet = getUpdatePacket(); + //tab.sendPacketToViewers(packet); + } + } + + private int getFlags() { + byte result = 0; + + if (background != null) { + result |= 0x1; + } + + if (hasToast()) { + result |= 0x2; + } + + if (isHidden()) { + result |= 0x4; + } + + return result; + } + +} diff --git a/src/main/java/net/minestom/server/advancements/AdvancementManager.java b/src/main/java/net/minestom/server/advancements/AdvancementManager.java new file mode 100644 index 000000000..4131d11d6 --- /dev/null +++ b/src/main/java/net/minestom/server/advancements/AdvancementManager.java @@ -0,0 +1,20 @@ +package net.minestom.server.advancements; + +import java.util.HashMap; +import java.util.Map; + +public class AdvancementManager { + + private Map advancementTabMap = new HashMap<>(); + + public AdvancementTab createTab(String rootIdentifier, AdvancementRoot root) { + final AdvancementTab advancementTab = new AdvancementTab(rootIdentifier, root); + this.advancementTabMap.put(rootIdentifier, advancementTab); + return advancementTab; + } + + public AdvancementTab getTab(String rootIdentifier) { + return advancementTabMap.get(rootIdentifier); + } + +} diff --git a/src/main/java/net/minestom/server/advancements/AdvancementRoot.java b/src/main/java/net/minestom/server/advancements/AdvancementRoot.java new file mode 100644 index 000000000..4d559e878 --- /dev/null +++ b/src/main/java/net/minestom/server/advancements/AdvancementRoot.java @@ -0,0 +1,25 @@ +package net.minestom.server.advancements; + +import net.minestom.server.chat.ColoredText; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; + +public class AdvancementRoot extends Advancement { + + public AdvancementRoot(ColoredText title, ColoredText description, + ItemStack icon, FrameType frameType, + float x, float y, + String background) { + super(title, description, icon, frameType, x, y); + setBackground(background); + } + + public AdvancementRoot(ColoredText title, ColoredText description, + Material icon, FrameType frameType, + float x, float y, + String background) { + super(title, description, icon, frameType, x, y); + setBackground(background); + } + +} diff --git a/src/main/java/net/minestom/server/advancements/AdvancementTab.java b/src/main/java/net/minestom/server/advancements/AdvancementTab.java new file mode 100644 index 000000000..23d7df1c6 --- /dev/null +++ b/src/main/java/net/minestom/server/advancements/AdvancementTab.java @@ -0,0 +1,128 @@ +package net.minestom.server.advancements; + +import net.minestom.server.Viewable; +import net.minestom.server.entity.Player; +import net.minestom.server.network.packet.server.play.AdvancementsPacket; +import net.minestom.server.network.player.PlayerConnection; +import net.minestom.server.utils.validate.Check; + +import java.util.*; + +public class AdvancementTab implements Viewable { + + private Set viewers = new HashSet<>(); + + private Advancement root; + + // Advancement -> its parent + private Map advancementMap = new HashMap<>(); + + protected AdvancementTab(String rootIdentifier, Advancement root) { + this.root = root; + cacheAdvancement(rootIdentifier, root, null); + } + + public void createAdvancement(String identifier, Advancement advancement, Advancement parent) { + Check.stateCondition(!advancementMap.containsKey(parent), + "You tried to set a parent which doesn't exist or isn't registered"); + cacheAdvancement(identifier, advancement, parent); + } + + /** + * Build the packet which build the whole advancement tab + * + * @return the packet adding this advancement tab and all its advancements + */ + public AdvancementsPacket createPacket() { + AdvancementsPacket advancementsPacket = new AdvancementsPacket(); + advancementsPacket.resetAdvancements = false; + + List mappings = new ArrayList<>(); + + for (Advancement advancement : advancementMap.keySet()) { + AdvancementsPacket.AdvancementMapping mapping = new AdvancementsPacket.AdvancementMapping(); + { + AdvancementsPacket.Advancement adv = new AdvancementsPacket.Advancement(); + mapping.key = advancement.getIdentifier(); + mapping.value = adv; + + final Advancement parent = advancement.getParent(); + if (parent != null) { + final String parentIdentifier = parent.getIdentifier(); + adv.parentIdentifier = parentIdentifier; + } + + adv.displayData = advancement.toDisplayData(); + adv.criterions = new String[]{}; + adv.requirements = new AdvancementsPacket.Requirement[]{}; + } + mappings.add(mapping); + } + + advancementsPacket.identifiersToRemove = new String[]{}; + advancementsPacket.advancementMappings = mappings.toArray(new AdvancementsPacket.AdvancementMapping[0]); + advancementsPacket.progressMappings = new AdvancementsPacket.ProgressMapping[]{}; + + return advancementsPacket; + } + + /** + * Create a packet which remove the root advancement + *

+ * This does in fact remove the whole advancement tab + * + * @return the packet which remove the root advancement + */ + public AdvancementsPacket removePacket() { + AdvancementsPacket advancementsPacket = new AdvancementsPacket(); + advancementsPacket.resetAdvancements = false; + advancementsPacket.identifiersToRemove = new String[]{root.getIdentifier()}; + advancementsPacket.advancementMappings = new AdvancementsPacket.AdvancementMapping[]{}; + advancementsPacket.progressMappings = new AdvancementsPacket.ProgressMapping[]{}; + + return advancementsPacket; + } + + private void cacheAdvancement(String identifier, Advancement advancement, Advancement parent) { + Check.stateCondition(advancement.getTab() != null, + "You tried to add an advancement already linked to a tab"); + advancement.setTab(this); + advancement.setIdentifier(identifier); + advancement.setParent(parent); + this.advancementMap.put(advancement, parent); + } + + @Override + public boolean addViewer(Player player) { + final boolean result = viewers.add(player); + if (!result) { + return false; + } + + final PlayerConnection playerConnection = player.getPlayerConnection(); + + // Send the tab to the player + playerConnection.sendPacket(createPacket()); + + return true; + } + + @Override + public boolean removeViewer(Player player) { + if (!isViewer(player)) { + return false; + } + + final PlayerConnection playerConnection = player.getPlayerConnection(); + + // Remove the tab + playerConnection.sendPacket(removePacket()); + + return viewers.remove(player); + } + + @Override + public Set getViewers() { + return viewers; + } +} diff --git a/src/main/java/net/minestom/server/advancements/notifications/NotificationCenter.java b/src/main/java/net/minestom/server/advancements/notifications/NotificationCenter.java index a83a9bfe7..8082c778d 100644 --- a/src/main/java/net/minestom/server/advancements/notifications/NotificationCenter.java +++ b/src/main/java/net/minestom/server/advancements/notifications/NotificationCenter.java @@ -40,7 +40,7 @@ public class NotificationCenter { * Create the packet responsive for showing the Toast to players * * @param notification the notification - * @return the packet to show the Toast + * @return the packet used to show the Toast */ private static AdvancementsPacket getCreatePacket(Notification notification) { // For An advancement to be shown, it must have all of it's criteria achieved (progress 100%)