From ba8e341b9402b86d44ebe428cad70499f133b34b Mon Sep 17 00:00:00 2001 From: mallusrgreat Date: Mon, 3 Aug 2026 15:22:33 +0530 Subject: [PATCH] Update to Minecraft version 26.2 --- .github/workflows/maven.yml | 4 +- API/pom.xml | 8 +- Plugin/pom.xml | 26 +- .../main/java/dev/lrxh/neptune/Neptune.java | 4 +- .../neptune/game/arena/ArenaDuplicator.java | 4 - .../game/arena/menu/ArenaDuplicatesMenu.java | 5 +- .../fr/mrmicky/fastboard/FastBoardBase.java | 330 ++++++++++-------- Plugin/src/main/resources/paper-plugin.yml | 2 +- README.md | 2 +- jitpack.yml | 2 +- 10 files changed, 216 insertions(+), 171 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 244133116..d46f4e32e 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -21,10 +21,10 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up JDK 21 + - name: Set up JDK 25 uses: actions/setup-java@v4 with: - java-version: '21' + java-version: '25' distribution: 'temurin' cache: maven - name: Build with Maven diff --git a/API/pom.xml b/API/pom.xml index 8a4baa4a8..f25660b17 100644 --- a/API/pom.xml +++ b/API/pom.xml @@ -12,8 +12,8 @@ API - 21 - 21 + 25 + 25 UTF-8 @@ -36,13 +36,13 @@ org.projectlombok lombok - 1.18.42 + 1.18.46 provided io.papermc.paper paper-api - 1.21.11-R0.1-SNAPSHOT + [26.2.build,) provided diff --git a/Plugin/pom.xml b/Plugin/pom.xml index 6f1a85a38..7b4fe5414 100644 --- a/Plugin/pom.xml +++ b/Plugin/pom.xml @@ -12,10 +12,9 @@ Plugin - 21 + 25 UTF-8 - - 1.21.11 + 26.2 @@ -74,28 +73,35 @@ io.papermc.paper paper-api - ${mc.version}-R0.1-SNAPSHOT + [${mc.version}.build,) + provided + + + + net.kyori + adventure-api + 5.2.0 provided org.projectlombok lombok - 1.18.42 + 1.18.46 provided me.clip placeholderapi - 2.12.1 + 2.12.3 provided com.github.retrooper packetevents-spigot - 2.12.1 + 2.13.0 provided @@ -160,13 +166,13 @@ maven-compiler-plugin 3.8.1 - 21 - 21 + 25 + 25 org.projectlombok lombok - 1.18.42 + 1.18.46 diff --git a/Plugin/src/main/java/dev/lrxh/neptune/Neptune.java b/Plugin/src/main/java/dev/lrxh/neptune/Neptune.java index 183dcf728..179783f0f 100644 --- a/Plugin/src/main/java/dev/lrxh/neptune/Neptune.java +++ b/Plugin/src/main/java/dev/lrxh/neptune/Neptune.java @@ -75,6 +75,7 @@ import fr.mrmicky.fastboard.FastManager; import lombok.Getter; import lombok.Setter; +import org.bukkit.Bukkit; import org.bukkit.Difficulty; import org.bukkit.GameRules; import org.bukkit.World; @@ -137,8 +138,7 @@ private void loadManager() { return; ArenaService.get().load(); - duplicatesEnabled = ArenaDuplicator.isAvailable(); - if (duplicatesEnabled) { + if (Bukkit.getPluginManager().getPlugin("FastAsyncWorldEdit") != null) { Settings.settings().CLIPBOARD.USE_DISK = false; ArenaService.get().setupDuplicatesWorld(); ArenaService.get().loadDuplicates(); diff --git a/Plugin/src/main/java/dev/lrxh/neptune/game/arena/ArenaDuplicator.java b/Plugin/src/main/java/dev/lrxh/neptune/game/arena/ArenaDuplicator.java index 0df61dbef..e0b1ed160 100644 --- a/Plugin/src/main/java/dev/lrxh/neptune/game/arena/ArenaDuplicator.java +++ b/Plugin/src/main/java/dev/lrxh/neptune/game/arena/ArenaDuplicator.java @@ -20,10 +20,6 @@ public final class ArenaDuplicator { // since each duplicate operates on a non-overlapping region with fastMode + changeSetNull. private static final Object COLD_LOCK = new Object(); - public static boolean isAvailable() { - return Bukkit.getPluginManager().getPlugin("FastAsyncWorldEdit") != null; - } - public static void copyPaste(World sourceWorld, Location min, Location max, World targetWorld, int tx, int ty, int tz) { CuboidRegion region = new CuboidRegion(BukkitAdapter.adapt(sourceWorld), BlockVector3.at(min.getBlockX(), min.getBlockY(), min.getBlockZ()), diff --git a/Plugin/src/main/java/dev/lrxh/neptune/game/arena/menu/ArenaDuplicatesMenu.java b/Plugin/src/main/java/dev/lrxh/neptune/game/arena/menu/ArenaDuplicatesMenu.java index 1c0ebdc05..bfabd5324 100644 --- a/Plugin/src/main/java/dev/lrxh/neptune/game/arena/menu/ArenaDuplicatesMenu.java +++ b/Plugin/src/main/java/dev/lrxh/neptune/game/arena/menu/ArenaDuplicatesMenu.java @@ -1,7 +1,6 @@ package dev.lrxh.neptune.game.arena.menu; import dev.lrxh.neptune.game.arena.Arena; -import dev.lrxh.neptune.game.arena.ArenaDuplicator; import dev.lrxh.neptune.game.arena.ArenaService; import dev.lrxh.neptune.utils.CC; import dev.lrxh.neptune.utils.ItemBuilder; @@ -11,13 +10,13 @@ import dev.lrxh.neptune.utils.menu.impl.DisplayButton; import dev.lrxh.neptune.utils.menu.impl.ReturnButton; import dev.lrxh.neptune.utils.sign.SignInputMenu; +import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.inventory.ClickType; import org.bukkit.inventory.ItemStack; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -97,7 +96,7 @@ public void onClick(ClickType type, Player player) { })); buttons.add(new DisplayButton(getSize() - 6, Material.EMERALD, "&aRecopy Duplicates", List.of("&7Click to repaste all duplicate copies of this arena"), p -> { - if (!ArenaDuplicator.isAvailable()) { + if (Bukkit.getPluginManager().getPlugin("FastAsyncWorldEdit") == null) { p.sendMessage(CC.error("FastAsyncWorldEdit is not installed.")); return; } diff --git a/Plugin/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java b/Plugin/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java index 231a8e677..d08dd5b0d 100644 --- a/Plugin/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java +++ b/Plugin/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java @@ -1,7 +1,7 @@ /* * This file is part of FastBoard, licensed under the MIT License. * - * Copyright (c) 2019-2023 MrMicky + * Copyright (c) 2019-2026 MrMicky * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,6 +29,7 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -38,33 +39,34 @@ /** * Lightweight packet-based scoreboard API for Bukkit plugins. - * It can be safely used asynchronously as everything is at packet level. + * It can be safely used asynchronously because everything is handled at the packet level. *

* The project is on GitHub. * * @author MrMicky - * @version 2.1.5 + * @version 2.2.0 */ public abstract class FastBoardBase { + private static final Map, Field[]> PACKETS = new HashMap<>(8); protected static final String[] COLOR_CODES = Arrays.stream(ChatColor.values()) .map(Object::toString) .toArray(String[]::new); - private static final Map, Field[]> PACKETS = new HashMap<>(8); private static final VersionType VERSION_TYPE; // Packets and components private static final Class CHAT_COMPONENT_CLASS; - private static final Class CHAT_FORMAT_ENUM; - private static final Object RESET_FORMATTING; private static final MethodHandle PLAYER_CONNECTION; private static final MethodHandle SEND_PACKET; private static final MethodHandle PLAYER_GET_HANDLE; private static final MethodHandle FIXED_NUMBER_FORMAT; + // Scoreboard teams + private static final MethodHandle OBJECTIVE; + private static final MethodHandle PLAYER_TEAM; // Scoreboard packets - private static final FastReflection.PacketConstructor PACKET_SB_OBJ; - private static final FastReflection.PacketConstructor PACKET_SB_DISPLAY_OBJ; - private static final FastReflection.PacketConstructor PACKET_SB_TEAM; - private static final FastReflection.PacketConstructor PACKET_SB_SERIALIZABLE_TEAM; + private static final MethodHandle PACKET_SB_OBJ; + private static final MethodHandle PACKET_SB_DISPLAY_OBJ; + private static final MethodHandle PACKET_SB_TEAM; + private static final MethodHandle PACKET_SB_SERIALIZABLE_TEAM; private static final MethodHandle PACKET_SB_SET_SCORE; private static final MethodHandle PACKET_SB_RESET_SCORE; private static final boolean SCORE_OPTIONAL_COMPONENTS; @@ -72,15 +74,12 @@ public abstract class FastBoardBase { private static final Class DISPLAY_SLOT_TYPE; private static final Class ENUM_SB_HEALTH_DISPLAY; private static final Class ENUM_SB_ACTION; - private static final Class ENUM_VISIBILITY; - private static final Class ENUM_COLLISION_RULE; private static final Object BLANK_NUMBER_FORMAT; private static final Object SIDEBAR_DISPLAY_SLOT; private static final Object ENUM_SB_HEALTH_DISPLAY_INTEGER; private static final Object ENUM_SB_ACTION_CHANGE; private static final Object ENUM_SB_ACTION_REMOVE; - private static final Object ENUM_VISIBILITY_ALWAYS; - private static final Object ENUM_COLLISION_RULE_ALWAYS; + private static final Object DUMMY_SCOREBOARD_CRITERIA; static { try { @@ -120,15 +119,19 @@ public abstract class FastBoardBase { .findFirst().orElseThrow(NoSuchMethodException::new); Optional> displaySlotEnum = FastReflection.nmsOptionalClass("world.scores", "DisplaySlot"); CHAT_COMPONENT_CLASS = FastReflection.nmsClass("network.chat", "IChatBaseComponent", "Component"); - CHAT_FORMAT_ENUM = FastReflection.nmsClass(null, "EnumChatFormat", "ChatFormatting"); DISPLAY_SLOT_TYPE = displaySlotEnum.orElse(int.class); - RESET_FORMATTING = FastReflection.enumValueOf(CHAT_FORMAT_ENUM, "RESET", 21); SIDEBAR_DISPLAY_SLOT = displaySlotEnum.isPresent() ? FastReflection.enumValueOf(DISPLAY_SLOT_TYPE, "SIDEBAR", 1) : 1; PLAYER_GET_HANDLE = lookup.findVirtual(craftPlayerClass, "getHandle", MethodType.methodType(entityPlayerClass)); PLAYER_CONNECTION = lookup.unreflectGetter(playerConnectionField); SEND_PACKET = lookup.unreflect(sendPacketMethod); - PACKET_SB_OBJ = FastReflection.findPacketConstructor(packetSbObjClass, lookup); - PACKET_SB_DISPLAY_OBJ = FastReflection.findPacketConstructor(packetSbDisplayObjClass, lookup); + + Class scoreboardClass = FastReflection.nmsClass("world.scores", "Scoreboard"); + Class playerTeamClass = FastReflection.nmsClass("world.scores", "ScoreboardTeam", "PlayerTeam"); + Class objectiveClass = FastReflection.nmsClass("world.scores", "ScoreboardObjective", "Objective"); + Class objectiveCriteriaClass = FastReflection.nmsClass("world.scores.criteria", "IScoreboardCriteria", "ObjectiveCriteria"); + PLAYER_TEAM = lookup.unreflectConstructor(playerTeamClass.getConstructor(scoreboardClass, String.class)); + + Class objectiveRenderTypeClass = FastReflection.nmsOptionalClass("world.scores.criteria", "IScoreboardCriteria$EnumScoreboardHealthDisplay", "ObjectiveCriteria$RenderType").orElse(null); Optional> numberFormat = FastReflection.nmsOptionalClass("network.chat.numbers", "NumberFormat"); MethodHandle packetSbSetScore; @@ -138,6 +141,10 @@ public abstract class FastBoardBase { boolean scoreOptionalComponents = false; if (numberFormat.isPresent()) { // 1.20.3 + OBJECTIVE = lookup.unreflectConstructor(objectiveClass.getConstructor(scoreboardClass, String.class, objectiveCriteriaClass, CHAT_COMPONENT_CLASS, objectiveRenderTypeClass, boolean.class, numberFormat.get())); + PACKET_SB_OBJ = lookup.unreflectConstructor(packetSbObjClass.getConstructor(objectiveClass, int.class)); + PACKET_SB_DISPLAY_OBJ = lookup.unreflectConstructor(packetSbDisplayObjClass.getConstructor(DISPLAY_SLOT_TYPE, objectiveClass)); + Class blankFormatClass = FastReflection.nmsClass("network.chat.numbers", "BlankFormat"); Class fixedFormatClass = FastReflection.nmsClass("network.chat.numbers", "FixedFormat"); Class resetScoreClass = FastReflection.nmsClass(gameProtocolPackage, "ClientboundResetScorePacket"); @@ -158,31 +165,31 @@ public abstract class FastBoardBase { Class enumSbAction = FastReflection.nmsClass("server", "ScoreboardServer$Action", "ServerScoreboard$Method"); MethodType scoreType = MethodType.methodType(void.class, enumSbAction, String.class, String.class, int.class); packetSbSetScore = lookup.findConstructor(packetSbScoreClass, scoreType); + OBJECTIVE = lookup.unreflectConstructor(objectiveClass.getConstructor(scoreboardClass, String.class, objectiveCriteriaClass, CHAT_COMPONENT_CLASS, objectiveRenderTypeClass)); + PACKET_SB_OBJ = lookup.unreflectConstructor(packetSbObjClass.getConstructor(objectiveClass, int.class)); + PACKET_SB_DISPLAY_OBJ = lookup.unreflectConstructor(packetSbDisplayObjClass.getConstructor(int.class, objectiveClass)); } else { packetSbSetScore = lookup.findConstructor(packetSbScoreClass, MethodType.methodType(void.class)); + if (VersionType.V1_13.isHigherOrEqual()) { + OBJECTIVE = lookup.unreflectConstructor(objectiveClass.getConstructor(scoreboardClass, String.class, objectiveCriteriaClass, CHAT_COMPONENT_CLASS, objectiveRenderTypeClass)); + } else { + OBJECTIVE = lookup.unreflectConstructor(objectiveClass.getConstructor(scoreboardClass, String.class, objectiveCriteriaClass)); + } + PACKET_SB_OBJ = lookup.unreflectConstructor(packetSbObjClass.getConstructor(objectiveClass, int.class)); + PACKET_SB_DISPLAY_OBJ = lookup.unreflectConstructor(packetSbDisplayObjClass.getConstructor(int.class, objectiveClass)); } PACKET_SB_SET_SCORE = packetSbSetScore; PACKET_SB_RESET_SCORE = packetSbResetScore; - PACKET_SB_TEAM = FastReflection.findPacketConstructor(packetSbTeamClass, lookup); - PACKET_SB_SERIALIZABLE_TEAM = sbTeamClass != null ? FastReflection.findPacketConstructor(sbTeamClass, lookup) : null; + Constructor packetSbTeamConstructor = sbTeamClass != null ? packetSbTeamClass.getDeclaredConstructor(String.class, int.class, Optional.class, Collection.class) : packetSbTeamClass.getDeclaredConstructor(); + packetSbTeamConstructor.setAccessible(true); + PACKET_SB_TEAM = lookup.unreflectConstructor(packetSbTeamConstructor); + PACKET_SB_SERIALIZABLE_TEAM = sbTeamClass != null ? lookup.unreflectConstructor(sbTeamClass.getConstructor(playerTeamClass)) : null; FIXED_NUMBER_FORMAT = fixedFormatConstructor; BLANK_NUMBER_FORMAT = blankNumberFormat; SCORE_OPTIONAL_COMPONENTS = scoreOptionalComponents; - if (VersionType.V1_17.isHigherOrEqual()) { - ENUM_VISIBILITY = FastReflection.nmsClass("world.scores", "ScoreboardTeamBase$EnumNameTagVisibility", "Team$Visibility"); - ENUM_COLLISION_RULE = FastReflection.nmsClass("world.scores", "ScoreboardTeamBase$EnumTeamPush", "Team$CollisionRule"); - ENUM_VISIBILITY_ALWAYS = FastReflection.enumValueOf(ENUM_VISIBILITY, "ALWAYS", 0); - ENUM_COLLISION_RULE_ALWAYS = FastReflection.enumValueOf(ENUM_COLLISION_RULE, "ALWAYS", 0); - } else { - ENUM_VISIBILITY = null; - ENUM_COLLISION_RULE = null; - ENUM_VISIBILITY_ALWAYS = null; - ENUM_COLLISION_RULE_ALWAYS = null; - } - - for (Class clazz : Arrays.asList(packetSbObjClass, packetSbDisplayObjClass, packetSbScoreClass, packetSbTeamClass, sbTeamClass)) { + for (Class clazz : Arrays.asList(packetSbScoreClass, packetSbTeamClass, sbTeamClass, playerTeamClass, objectiveClass)) { if (clazz == null) { continue; } @@ -211,6 +218,11 @@ public abstract class FastBoardBase { ENUM_SB_ACTION_CHANGE = null; ENUM_SB_ACTION_REMOVE = null; } + if (VersionType.V1_13.isHigherOrEqual()) { + DUMMY_SCOREBOARD_CRITERIA = null; + } else { + DUMMY_SCOREBOARD_CRITERIA = FastReflection.nmsClass("world.scores.criteria", "ScoreboardBaseCriteria").getConstructor(String.class).newInstance("dummy"); + } } catch (Throwable t) { throw new ExceptionInInitializerError(t); } @@ -223,7 +235,7 @@ public abstract class FastBoardBase { private final List scores = new ArrayList<>(); private T title = emptyLine(); - private boolean deleted = false; + private volatile boolean deleted = false; /** * Creates a new FastBoard. @@ -235,30 +247,29 @@ protected FastBoardBase(Player player) { this.id = "fb-" + Integer.toHexString(ThreadLocalRandom.current().nextInt()); try { - sendObjectivePacket(ObjectiveMode.CREATE); - sendDisplayObjectivePacket(); + Object objective = sendObjectivePacket(ObjectiveMode.CREATE); + sendDisplayObjectivePacket(objective); } catch (Throwable t) { throw new RuntimeException("Unable to create scoreboard", t); } } /** - * Get the scoreboard title. + * Returns the scoreboard title. * * @return the scoreboard title */ - public T getTitle() { + public synchronized T getTitle() { return this.title; } /** - * Update the scoreboard title. + * Updates the scoreboard title. * * @param title the new scoreboard title * @throws IllegalArgumentException if the title is longer than 32 chars on 1.12 or lower - * @throws IllegalStateException if {@link #delete()} was call before */ - public void updateTitle(T title) { + public synchronized void updateTitle(T title) { if (this.title.equals(Objects.requireNonNull(title, "title"))) { return; } @@ -273,66 +284,66 @@ public void updateTitle(T title) { } /** - * Get the scoreboard lines. + * Returns the scoreboard lines. * * @return the scoreboard lines */ - public List getLines() { + public synchronized List getLines() { return new ArrayList<>(this.lines); } /** - * Get the specified scoreboard line. + * Returns the specified scoreboard line. * * @param line the line number * @return the line - * @throws IndexOutOfBoundsException if the line is higher than {@code size} + * @throws IllegalArgumentException if the line number is out of range */ - public T getLine(int line) { + public synchronized T getLine(int line) { checkLineNumber(line, true, false); return this.lines.get(line); } /** - * Get how a specific line's score is displayed. On 1.20.2 or below, the value returned isn't used. + * Returns how a specific line's score is displayed. On 1.20.2 or below, the value returned isn't used. * * @param line the line number * @return the text of how the line is displayed - * @throws IndexOutOfBoundsException if the line is higher than {@code size} + * @throws IllegalArgumentException if the line number is out of range */ - public Optional getScore(int line) { + public synchronized Optional getScore(int line) { checkLineNumber(line, true, false); return Optional.ofNullable(this.scores.get(line)); } /** - * Update a single scoreboard line. + * Updates a single scoreboard line. * - * @param line the line number - * @param text the new line text - * @throws IndexOutOfBoundsException if the line is higher than {@link #size() size() + 1} + * @param line the line number + * @param score the new line text + * @throws IllegalArgumentException if the line number is out of range */ - public synchronized void updateLine(int line, T text) { - updateLine(line, text, null); + public synchronized void updateLine(int line, T score) { + updateLine(line, score, null); } /** - * Update a single scoreboard line including how its score is displayed. + * Updates a single scoreboard line including how its score is displayed. * The score will only be displayed on 1.20.3 and higher. * * @param line the line number - * @param text the new line text - * @param scoreText the new line's score, if null will not change current value - * @throws IndexOutOfBoundsException if the line is higher than {@link #size() size() + 1} + * @param score the new line text + * @param scoreText the new line score, or null to use the default blank score + * @throws IllegalArgumentException if the line number is out of range */ - public synchronized void updateLine(int line, T text, T scoreText) { + public synchronized void updateLine(int line, T score, T scoreText) { checkLineNumber(line, false, false); try { if (line < size()) { - this.lines.set(line, text); + this.lines.set(line, score); this.scores.set(line, scoreText); sendLineChange(getScoreByLine(line)); @@ -354,7 +365,7 @@ public synchronized void updateLine(int line, T text, T scoreText) { } } - newLines.add(text); + newLines.add(score); newScores.add(scoreText); updateLines(newLines, newScores); @@ -364,7 +375,7 @@ public synchronized void updateLine(int line, T text, T scoreText) { } /** - * Remove a scoreboard line. + * Removes a scoreboard line. * * @param line the line number */ @@ -383,36 +394,36 @@ public synchronized void removeLine(int line) { } /** - * Update all the scoreboard lines. + * Updates all the scoreboard lines. * * @param lines the new lines * @throws IllegalArgumentException if one line is longer than 30 chars on 1.12 or lower - * @throws IllegalStateException if {@link #delete()} was call before + * @throws IllegalStateException if this FastBoard has already been deleted */ public void updateLines(T... lines) { updateLines(Arrays.asList(lines)); } /** - * Update the lines of the scoreboard + * Updates the lines of the scoreboard. * * @param lines the new scoreboard lines * @throws IllegalArgumentException if one line is longer than 30 chars on 1.12 or lower - * @throws IllegalStateException if {@link #delete()} was call before + * @throws IllegalStateException if this FastBoard has already been deleted */ public synchronized void updateLines(Collection lines) { updateLines(lines, null); } /** - * Update the lines and how their score is displayed on the scoreboard. + * Updates the lines and how their score is displayed on the scoreboard. * The scores will only be displayed for servers on 1.20.3 and higher. * * @param lines the new scoreboard lines - * @param scores the set for how each line's score should be, if null will fall back to default (blank) + * @param scores the custom score text for each line, or null to use the default blank scores * @throws IllegalArgumentException if one line is longer than 30 chars on 1.12 or lower * @throws IllegalArgumentException if lines and scores are not the same size - * @throws IllegalStateException if {@link #delete()} was call before + * @throws IllegalStateException if this FastBoard has already been deleted */ public synchronized void updateLines(Collection lines, Collection scores) { Objects.requireNonNull(lines, "lines"); @@ -464,18 +475,18 @@ public synchronized void updateLines(Collection lines, Collection scores) } /** - * Update how a specified line's score is displayed on the scoreboard. A null value will reset the displayed + * Updates how a specified line's score is displayed on the scoreboard. A null value will reset the displayed * text back to default. The scores will only be displayed for servers on 1.20.3 and higher. * - * @param line the line number - * @param text the text to be displayed as the score. if null, no score will be displayed + * @param line the line number + * @param score the new line score, or null to use the default blank score * @throws IllegalArgumentException if the line number is not in range - * @throws IllegalStateException if {@link #delete()} was call before + * @throws IllegalStateException if this FastBoard has already been deleted */ - public synchronized void updateScore(int line, T text) { + public synchronized void updateScore(int line, T score) { checkLineNumber(line, true, false); - this.scores.set(line, text); + this.scores.set(line, score); try { if (customScoresSupported()) { @@ -487,44 +498,44 @@ public synchronized void updateScore(int line, T text) { } /** - * Reset a line's score back to default (blank). The score will only be displayed for servers on 1.20.3 and higher. + * Resets a line's score back to default (blank). The score will only be displayed for servers on 1.20.3 and higher. * * @param line the line number * @throws IllegalArgumentException if the line number is not in range - * @throws IllegalStateException if {@link #delete()} was call before + * @throws IllegalStateException if this FastBoard has already been deleted */ public synchronized void removeScore(int line) { updateScore(line, null); } /** - * Update how all lines' scores are displayed. A value of null will reset the displayed text back to default. + * Updates how all lines' scores are displayed. A value of null will reset the displayed text back to default. * The scores will only be displayed for servers on 1.20.3 and higher. * - * @param texts the set of texts to be displayed as the scores + * @param scores the custom score texts for the lines, or null to use the default blank scores * @throws IllegalArgumentException if the size of the texts does not match the current size of the board - * @throws IllegalStateException if {@link #delete()} was call before + * @throws IllegalStateException if this FastBoard has already been deleted */ - public synchronized void updateScores(T... texts) { - updateScores(Arrays.asList(texts)); + public synchronized void updateScores(T... scores) { + updateScores(Arrays.asList(scores)); } /** - * Update how all lines' scores are displayed. A null value will reset the displayed + * Updates how all lines' scores are displayed. A null value will reset the displayed * text back to default (blank). Only available on 1.20.3+ servers. * - * @param texts the set of texts to be displayed as the scores + * @param scores the set of texts to be displayed as the scores * @throws IllegalArgumentException if the size of the texts does not match the current size of the board - * @throws IllegalStateException if {@link #delete()} was call before + * @throws IllegalStateException if this FastBoard has already been deleted */ - public synchronized void updateScores(Collection texts) { - Objects.requireNonNull(texts, "texts"); + public synchronized void updateScores(Collection scores) { + Objects.requireNonNull(scores, "scores"); - if (this.scores.size() != this.lines.size()) { + if (scores.size() != this.lines.size()) { throw new IllegalArgumentException("The size of the scores must match the size of the board"); } - List newScores = new ArrayList<>(texts); + List newScores = new ArrayList<>(scores); for (int i = 0; i < this.scores.size(); i++) { if (Objects.equals(this.scores.get(i), newScores.get(i))) { continue; @@ -543,7 +554,7 @@ public synchronized void updateScores(Collection texts) { } /** - * Get the player who has the scoreboard. + * Returns the player who has the scoreboard. * * @return current player for this FastBoard */ @@ -552,7 +563,7 @@ public Player getPlayer() { } /** - * Get the scoreboard id. + * Returns the scoreboard ID. * * @return the id */ @@ -561,7 +572,7 @@ public String getId() { } /** - * Get if the scoreboard is deleted. + * Returns whether this FastBoard has been deleted. * * @return true if the scoreboard is deleted */ @@ -570,7 +581,7 @@ public boolean isDeleted() { } /** - * Get if the server supports custom scoreboard scores (1.20.3+ servers only). + * Returns whether the server supports custom scoreboard scores (1.20.3+ servers only). * * @return true if the server supports custom scores */ @@ -579,21 +590,23 @@ public boolean customScoresSupported() { } /** - * Get the scoreboard size (the number of lines). + * Returns the scoreboard size (the number of lines). * * @return the size */ - public int size() { + public synchronized int size() { return this.lines.size(); } /** - * Delete this FastBoard, and will remove the scoreboard for the associated player if he is online. - * After this, all uses of {@link #updateLines} and {@link #updateTitle} will throw an {@link IllegalStateException} - * - * @throws IllegalStateException if this was already call before + * Deletes this FastBoard and removes the scoreboard from the associated player if they are online. + * After deletion, all scoreboard update methods will throw an {@link IllegalStateException}. */ - public void delete() { + public synchronized void delete() { + if (this.deleted) { + return; + } + try { for (int i = 0; i < this.lines.size(); i++) { sendTeamPacket(i, TeamMode.REMOVE); @@ -624,7 +637,7 @@ private void checkLineNumber(int line, boolean checkInRange, boolean checkMax) { throw new IllegalArgumentException("Line number must be under " + this.lines.size()); } - if (checkMax && line >= COLOR_CODES.length - 1) { + if (checkMax && line >= COLOR_CODES.length) { throw new IllegalArgumentException("Line number is too high: " + line); } } @@ -641,32 +654,53 @@ protected T getLineByScore(List lines, int score) { return score < lines.size() ? lines.get(lines.size() - score - 1) : null; } - protected void sendObjectivePacket(ObjectiveMode mode) throws Throwable { - Object packet = PACKET_SB_OBJ.invoke(); - - setField(packet, String.class, this.id); - setField(packet, int.class, mode.ordinal()); - - if (mode != ObjectiveMode.REMOVE) { - setComponentField(packet, this.title, 1); - setField(packet, Optional.class, Optional.empty()); // Number format for 1.20.5+, previously nullable - - if (VersionType.V1_8.isHigherOrEqual()) { - setField(packet, ENUM_SB_HEALTH_DISPLAY, ENUM_SB_HEALTH_DISPLAY_INTEGER); - } - } else if (VERSION_TYPE == VersionType.V1_7) { - setField(packet, String.class, "", 1); + protected Object sendObjectivePacket(ObjectiveMode mode) throws Throwable { + Object objective; + if (BLANK_NUMBER_FORMAT != null) { + objective = OBJECTIVE.invoke( + null, // Scoreboard, unused + this.id, // Objective name + null, // Criteria, unused + toMinecraftComponent(this.title), // Display name + ENUM_SB_HEALTH_DISPLAY_INTEGER, // Render type + false, // Auto-update, unused + null // Number format + ); + } else if (VersionType.V1_17.isHigherOrEqual()) { + objective = OBJECTIVE.invoke( + null, // Scoreboard, unused + this.id, // Objective name + null, // Criteria, unused + toMinecraftComponent(this.title), // Display name + ENUM_SB_HEALTH_DISPLAY_INTEGER // Render type + ); + } else if (VersionType.V1_13.isHigherOrEqual()) { + objective = OBJECTIVE.invoke( + null, // Scoreboard, unused + this.id, // Objective name + null, // Criteria, unused + toMinecraftComponent(this.title), // Display name + ENUM_SB_HEALTH_DISPLAY_INTEGER // Render type + ); + } else { + objective = OBJECTIVE.invoke( + null, // Scoreboard, unused + this.id, // Objective name + DUMMY_SCOREBOARD_CRITERIA // Criteria + ); + setComponentField(objective, this.title, 1); } + Object packet = PACKET_SB_OBJ.invoke(objective, mode.ordinal()); sendPacket(packet); + return objective; } - protected void sendDisplayObjectivePacket() throws Throwable { - Object packet = PACKET_SB_DISPLAY_OBJ.invoke(); - - setField(packet, DISPLAY_SLOT_TYPE, SIDEBAR_DISPLAY_SLOT); // Position - setField(packet, String.class, this.id); // Score Name - + protected void sendDisplayObjectivePacket(Object objective) throws Throwable { + Object packet = PACKET_SB_DISPLAY_OBJ.invoke( + SIDEBAR_DISPLAY_SLOT, // Position + objective // Score Name + ); sendPacket(packet); } @@ -732,37 +766,47 @@ protected void sendTeamPacket(int score, TeamMode mode, T prefix, T suffix) throw new UnsupportedOperationException(); } - Object packet = PACKET_SB_TEAM.invoke(); - - setField(packet, String.class, this.id + ':' + score); // Team name - setField(packet, int.class, mode.ordinal(), VERSION_TYPE == VersionType.V1_8 ? 1 : 0); // Update mode - + Object packet; if (mode == TeamMode.REMOVE) { + if (VersionType.V1_17.isHigherOrEqual()) { + packet = PACKET_SB_TEAM.invoke( + this.id + ':' + score, // Team name + mode.ordinal(), // Update mode + Optional.empty(), // Serializable team, unused + Collections.emptyList() // Players + ); + } else { + packet = PACKET_SB_TEAM.invoke(); + setField(packet, String.class, this.id + ':' + score); // Team name + setField(packet, int.class, mode.ordinal(), VERSION_TYPE == VersionType.V1_8 ? 1 : 0); // Update mode + } sendPacket(packet); return; } if (VersionType.V1_17.isHigherOrEqual()) { - Object team = PACKET_SB_SERIALIZABLE_TEAM.invoke(); - // Since the packet is initialized with null values, we need to change more things. - setComponentField(team, null, 0); // Display name - setField(team, CHAT_FORMAT_ENUM, RESET_FORMATTING); // Color - setComponentField(team, prefix, 1); // Prefix - setComponentField(team, suffix, 2); // Suffix - setField(team, String.class, "always", 0); // Visibility before 1.21.5 - setField(team, String.class, "always", 1); // Collisions before 1.21.5 - setField(team, ENUM_VISIBILITY, ENUM_VISIBILITY_ALWAYS, 0); // 1.21.5+ - setField(team, ENUM_COLLISION_RULE, ENUM_COLLISION_RULE_ALWAYS, 0); // 1.21.5+ - setField(packet, Optional.class, Optional.of(team)); + Object team = PLAYER_TEAM.invoke(null, this.id + ':' + score); + setComponentField(team, null, 1); // Display name + setComponentField(team, prefix, 2); // Prefix + setComponentField(team, suffix, 3); // Suffix + Object serializableTeam = PACKET_SB_SERIALIZABLE_TEAM.invoke(team); + packet = PACKET_SB_TEAM.invoke( + this.id + ':' + score, // Team name + mode.ordinal(), // Update mode + Optional.of(serializableTeam), // Serializable team + mode == TeamMode.CREATE ? Collections.singletonList(COLOR_CODES[score]) : Collections.emptyList() // Players + ); } else { + packet = PACKET_SB_TEAM.invoke(); + setField(packet, String.class, this.id + ':' + score); // Team name + setField(packet, int.class, mode.ordinal(), VERSION_TYPE == VersionType.V1_8 ? 1 : 0); // Update mode setComponentField(packet, prefix, 2); // Prefix setComponentField(packet, suffix, 3); // Suffix setField(packet, String.class, "always", 4); // Visibility for 1.8+ setField(packet, String.class, "always", 5); // Collisions for 1.9+ - } - - if (mode == TeamMode.CREATE) { - setField(packet, Collection.class, Collections.singletonList(COLOR_CODES[score])); // Players in the team + if (mode == TeamMode.CREATE) { + setField(packet, Collection.class, Collections.singletonList(COLOR_CODES[score])); // Players in the team + } } sendPacket(packet); diff --git a/Plugin/src/main/resources/paper-plugin.yml b/Plugin/src/main/resources/paper-plugin.yml index 5d52b6e00..df72ef6da 100644 --- a/Plugin/src/main/resources/paper-plugin.yml +++ b/Plugin/src/main/resources/paper-plugin.yml @@ -1,7 +1,7 @@ name: Neptune version: ${project.version} main: dev.lrxh.neptune.Neptune -api-version: ${mc.version} +api-version: 26.2 description: Neptune - Practice Core diff --git a/README.md b/README.md index c0a21011a..22c8c7e4d 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ ## 💿 Installation -0. You need a PaperMC server (or forks of PaperMC) with version **1.21.11** or above. +0. You need a PaperMC server (or forks of PaperMC) with version **26.2** or above. 1. **Download** the plugin. diff --git a/jitpack.yml b/jitpack.yml index 727c9abd3..aaa589060 100644 --- a/jitpack.yml +++ b/jitpack.yml @@ -1,2 +1,2 @@ jdk: - - openjdk21 + - openjdk25