diff --git a/.github/workflows/compile_snapshot.yml b/.github/workflows/compile_snapshot.yml index 0135b4f..fa66df2 100644 --- a/.github/workflows/compile_snapshot.yml +++ b/.github/workflows/compile_snapshot.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest environment: name: development - url: https://repo.andrei1058.dev + url: https://repo.andrei1058.com steps: - uses: actions/checkout@v4 - name: Set up JDK 21 @@ -22,8 +22,7 @@ jobs: distribution: 'temurin' - name: Deploy snapshot with Maven env: - MVN_REPO_USER: ${{ secrets.MVN_REPO_USER }} - MVN_REPO_PASS: ${{ secrets.MVN_REPO_PASS }} + MVN_REPO_TOKEN: ${{ secrets.MVN_REPO_TOKEN }} ANDEV_RES_ID: 4 run: | sudo apt install jq -y diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b8d4dfd..ca82217 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest environment: name: production - url: https://repo.andrei1058.dev + url: https://repo.andrei1058.com steps: - uses: actions/checkout@v4 - name: Set up JDK 21 @@ -19,8 +19,7 @@ jobs: distribution: 'temurin' - name: Deploy with Maven env: - MVN_REPO_USER: ${{ secrets.MVN_REPO_USER }} - MVN_REPO_PASS: ${{ secrets.MVN_REPO_PASS }} + MVN_REPO_TOKEN: ${{ secrets.MVN_REPO_TOKEN }} ANDEV_API_TOKEN: ${{ secrets.ANDEV_API_TOKEN }} J_DOCS_USER: ${{ secrets.J_DOCS_USER }} J_DOCS_PASS: ${{ secrets.J_DOCS_PASS }} diff --git a/.github/workflows/deploy_snapshot.yml b/.github/workflows/deploy_snapshot.yml index 2e05684..e90c5ae 100644 --- a/.github/workflows/deploy_snapshot.yml +++ b/.github/workflows/deploy_snapshot.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest environment: name: development - url: https://repo.andrei1058.dev/snapshots + url: https://repo.andrei1058.com/snapshots steps: - uses: actions/checkout@v4 - name: Set up JDK 21 @@ -20,8 +20,7 @@ jobs: distribution: 'temurin' - name: Deploy snapshot with Maven env: - MVN_REPO_USER: ${{ secrets.MVN_REPO_USER }} - MVN_REPO_PASS: ${{ secrets.MVN_REPO_PASS }} + MVN_REPO_TOKEN: ${{ secrets.MVN_REPO_TOKEN }} ANDEV_RES_ID: 4 run: | sudo apt install jq -y diff --git a/README.md b/README.md index 871bbfc..8414ab9 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ This project is divided in several modules: - `sidebar-v_1_21_R1` provides support for 1.21 and 1.21.1 (R1). Requires `sidebar-cmn1`. - `sidebar-v_1_21_R2` provides support for 1.21.3 (R2). Requires `sidebar-cmn1`. - `sidebar-v_1_21_R3` provides support for 1.21.4 (R3). Requires `sidebar-cmn1`. +- `sidebar-v26_1_2` provides support for 26.1.2. Requires `sidebar-cmn1`. ### IMPORTANT It is really important to call Sidebar#remove(player) when a player leaves the server to avoid memory leaks. @@ -86,9 +87,21 @@ public class MySidebar { ChatColor.GREEN + "Hello {player}", ChatColor.LIGHT_PURPLE + "Hello {player}" })); + // a static line that rotates through multiple placeholder values + lines.add(new SidebarLine() { + @Override + public String getLine() { + return ChatColor.YELLOW + "Tip: {tip}"; + } + }); List placeholders = new ArrayList<>(); placeholders.add(new PlaceholderProvider("{player}", () -> receiver.getDisplayName())); + placeholders.add(new AlternatedPlaceholderProvider("{tip}", + () -> ChatColor.GOLD + "Mine blocks", + () -> ChatColor.GREEN + "Open crates", + () -> ChatColor.AQUA + "Visit spawn" + )); handle = MyPlugin.getSidebarManager().createSidebar(title, lines, placeholders); @@ -139,6 +152,26 @@ public class MySidebar { } } ``` + +## Alternated placeholders +`AlternatedPlaceholderProvider` works like `PlaceholderProvider`, but rotates through multiple replacements. +It accepts both static `String[]` values and lazy `Callable` replacements, so you can compute each value right before it is rendered. + +The next value is selected every time the placeholder is parsed, which means it can be used anywhere internal placeholders are supported: sidebar titles, regular lines, animated lines, player tab formatting, and tab header/footer content. + +```java +var tips = new AlternatedPlaceholderProvider("{tip}", + () -> "Mine blocks", + () -> "Open crates", + () -> "Visit spawn" +); +``` + +Refresh guidance: +- `Sidebar#refreshPlaceholders()` updates alternated placeholders in non-animated sidebar lines. +- `Sidebar#refreshAnimatedLines()` also advances alternated placeholders used inside `SidebarLineAnimated`. +- Re-sending tab header/footer or refreshing titles/tab animations re-parses alternated placeholders there as well. + An example of how you can refresh and manage your sidebar. ```java @@ -261,11 +294,11 @@ Since 1.20.3 we can replace sidebar score numbers with string placeholders. You andrei1058-snapshots - https://repo.andrei1058.dev/snapshots/ + https://repo.andrei1058.com/snapshots/ andrei1058-releases - https://repo.andrei1058.dev/releases/ + https://repo.andrei1058.com/releases/ ``` @@ -275,7 +308,7 @@ Since 1.20.3 we can replace sidebar score numbers with string placeholders. You com.andrei1058.spigot.sidebar sidebar-base - 24.8 // make sure this is the latest + 26.5-SNAPSHOT // make sure this is the latest compile diff --git a/ci_settings.xml b/ci_settings.xml index ad2d649..96914db 100644 --- a/ci_settings.xml +++ b/ci_settings.xml @@ -12,9 +12,9 @@ - ftp-repo - ${env.MVN_REPO_USER} - ${env.MVN_REPO_PASS} + andrei1058-repo + token + ${env.MVN_REPO_TOKEN} diff --git a/pom.xml b/pom.xml index 6ed6368..f7b2928 100644 --- a/pom.xml +++ b/pom.xml @@ -7,9 +7,10 @@ com.andrei1058.spigot.sidebar sidebar-pom pom - 25.2.2-SNAPSHOT + 26.6-SNAPSHOT sidebar-base + sidebar-v26_1_2 sidebar-v1_21_R3 sidebar-v1_21_R2 sidebar-v1_21_R1 @@ -36,6 +37,10 @@ placeholderapi https://repo.extendedclip.com/content/repositories/placeholderapi/ + + nms + https://repo.codemc.io/repository/nms/ + @@ -54,24 +59,15 @@ - - - - org.apache.maven.wagon - wagon-ftp - 3.5.3 - - - - ftp-repo - ftp://andrei1058.dev/releases + andrei1058-repo + https://repo.andrei1058.com/releases - ftp-repo - ftp://andrei1058.dev/snapshots + andrei1058-repo + https://repo.andrei1058.com/snapshots - \ No newline at end of file + diff --git a/sidebar-base/pom.xml b/sidebar-base/pom.xml index 2a73b91..a351fda 100644 --- a/sidebar-base/pom.xml +++ b/sidebar-base/pom.xml @@ -5,7 +5,7 @@ sidebar-pom com.andrei1058.spigot.sidebar - 25.2.2-SNAPSHOT + 26.6-SNAPSHOT 4.0.0 @@ -30,6 +30,12 @@ 2.11.6 provided + + junit + junit + 4.10 + test + @@ -55,6 +61,11 @@ 20 + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.5 + - \ No newline at end of file + diff --git a/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/AlternatedPlaceholderProvider.java b/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/AlternatedPlaceholderProvider.java new file mode 100644 index 0000000..d52b1b1 --- /dev/null +++ b/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/AlternatedPlaceholderProvider.java @@ -0,0 +1,74 @@ +package com.andrei1058.spigot.sidebar; + +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Callable; + +public class AlternatedPlaceholderProvider extends PlaceholderProvider { + + private final List> replacements; + private int pos = -1; + + /** + * Create an alternated placeholder provider. + * Every replacement will get the next value and then will repeat. + * + * @param placeholder placeholder with brackets. + * @param replacements replacement values. + */ + @SafeVarargs + public AlternatedPlaceholderProvider(String placeholder, Callable... replacements) { + this(placeholder, null == replacements ? Collections.emptyList() : Arrays.asList(replacements.clone())); + } + + /** + * Create an alternated placeholder provider. + * Every replacement will get the next value and then will repeat. + * + * @param placeholder placeholder with brackets. + * @param replacements replacement values. + */ + public AlternatedPlaceholderProvider(String placeholder, String[] replacements) { + this(placeholder, toCallables(replacements)); + } + + private AlternatedPlaceholderProvider(String placeholder, Collection> replacements) { + super(placeholder, () -> null); + this.replacements = Collections.unmodifiableList(new ArrayList<>(replacements)); + } + + private static List> toCallables(String[] replacements) { + if (null == replacements || replacements.length == 0) { + return Collections.emptyList(); + } + + List> callables = new ArrayList<>(replacements.length); + for (String replacement : replacements) { + callables.add(() -> replacement); + } + return callables; + } + + /** + * @return replacement. + */ + @NotNull + @Override + public String getReplacement() { + if (replacements.isEmpty()) { + return ""; + } + Callable replacement = replacements.get(++pos == replacements.size() ? pos = 0 : pos); + try { + String rep = null == replacement ? null : replacement.call(); + return null == rep ? "null" : rep; + } catch (Exception e) { + return "-"; + } + } +} \ No newline at end of file diff --git a/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/CompiledPlaceholders.java b/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/CompiledPlaceholders.java new file mode 100644 index 0000000..f300469 --- /dev/null +++ b/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/CompiledPlaceholders.java @@ -0,0 +1,45 @@ +package com.andrei1058.spigot.sidebar; + +import java.util.*; + +/** + * Pre-compiled placeholders for faster replacement. + */ +public class CompiledPlaceholders { + private final Map> map; + private final char[] startChars; + + public CompiledPlaceholders(Collection replacements) { + if (replacements == null || replacements.isEmpty()) { + this.map = Collections.emptyMap(); + this.startChars = new char[0]; + return; + } + this.map = new HashMap<>(); + for (PlaceholderProvider provider : replacements) { + String placeholder = provider.getPlaceholder(); + if (placeholder != null && !placeholder.isEmpty()) { + map.computeIfAbsent(placeholder.charAt(0), k -> new ArrayList<>()).add(provider); + } + } + + Set startCharsSet = map.keySet(); + this.startChars = new char[startCharsSet.size()]; + int idx = 0; + for (char c : startCharsSet) { + startChars[idx++] = c; + } + } + + public Map> getMap() { + return map; + } + + public char[] getStartChars() { + return startChars; + } + + public boolean isEmpty() { + return startChars.length == 0; + } +} diff --git a/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/PlayerTab.java b/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/PlayerTab.java index 85adee3..464bb5a 100644 --- a/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/PlayerTab.java +++ b/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/PlayerTab.java @@ -1,5 +1,6 @@ package com.andrei1058.spigot.sidebar; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.jetbrains.annotations.Nullable; @@ -12,12 +13,26 @@ public interface PlayerTab { @SuppressWarnings("unused") void add(Player player); + /** + * Add an entity to the tab-list formatting. + * + * @param entity the entity to be formatted. + */ + void add(Entity entity); + /** * Remove the given player from tab-list formatting. * @param player to be removed. */ void remove(Player player); + /** + * Removes the given entity from the tab-list formatting. + * + * @param entity the entity to be removed. + */ + void remove(Entity entity); + /** * PAPI subject. * @param player papi target for placeholders. diff --git a/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/SidebarLine.java b/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/SidebarLine.java index ce8f8a3..a3d5cd0 100644 --- a/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/SidebarLine.java +++ b/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/SidebarLine.java @@ -47,27 +47,34 @@ public static void markHasPlaceholders(@NotNull SidebarLine text, Collection placeholders) { if (this instanceof ScoredLine) { @@ -90,15 +108,34 @@ public String getTrimReplacePlaceholdersScore(@Nullable Player papiSubject, @Nul return ""; } + @ApiStatus.Experimental + public String getTrimReplacePlaceholdersScore(@Nullable Player papiSubject, @Nullable Integer limit, CompiledPlaceholders placeholders) { + if (this instanceof ScoredLine) { + return getTrimReplacePlaceholders(((ScoredLine) this).getScore(), papiSubject, limit, placeholders); + } + return ""; + } + public static @NotNull String getTrimReplacePlaceholders(String scope, @Nullable Player papiSubject, @Nullable Integer limit, Collection placeholders) { String t = scope; if (null != placeholders) { - for (PlaceholderProvider placeholderProvider : placeholders) { - if (t.contains(placeholderProvider.getPlaceholder())) { - t = t.replace(placeholderProvider.getPlaceholder(), placeholderProvider.getReplacement()); - } - } + t = SidebarManager.replacePlaceholders(t, placeholders); } + return getTrimReplacePlaceholdersAfterInternal(t, papiSubject, limit); + } + + /** + * Use this for internal placeholders replacement with pre-compiled placeholders. + */ + public static @NotNull String getTrimReplacePlaceholders(String scope, @Nullable Player papiSubject, @Nullable Integer limit, CompiledPlaceholders placeholders) { + String t = scope; + if (null != placeholders) { + t = SidebarManager.replacePlaceholders(t, placeholders); + } + return getTrimReplacePlaceholdersAfterInternal(t, papiSubject, limit); + } + + private static String getTrimReplacePlaceholdersAfterInternal(String t, @Nullable Player papiSubject, @Nullable Integer limit) { if (null != papiSubject) { t = ChatColor.translateAlternateColorCodes('&', SidebarManager.getInstance().getPapiSupport().replacePlaceholders(papiSubject, t) diff --git a/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/SidebarManager.java b/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/SidebarManager.java index 91f2bfc..777c619 100644 --- a/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/SidebarManager.java +++ b/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/SidebarManager.java @@ -9,8 +9,7 @@ import org.jetbrains.annotations.Nullable; import java.lang.reflect.InvocationTargetException; -import java.util.Collection; -import java.util.List; +import java.util.*; public class SidebarManager { @@ -48,6 +47,7 @@ public SidebarManager() throws InstantiationException { } // load server version support + /* String serverVersion = Bukkit.getServer().getClass().getName().split("\\.")[3]; String className = "com.andrei1058.spigot.sidebar." + serverVersion + ".ProviderImpl"; @@ -58,6 +58,7 @@ public SidebarManager() throws InstantiationException { InstantiationException | IllegalAccessException ignored) { throw new InstantiationException(); } + */ } /** @@ -128,14 +129,10 @@ private String buildTabContent(Player player, @NotNull List lines, SidebarLine line = lines.get(i); String currentLine = line.getLine(); if (line.isInternalPlaceholders()) { - for (PlaceholderProvider placeholderProvider : headerFooter.getPlaceholders()) { - currentLine = currentLine.replace(placeholderProvider.getPlaceholder(), placeholderProvider.getReplacement()); - } + currentLine = replacePlaceholders(currentLine, headerFooter.getCompiledPlaceholders()); } if (line.isPapiPlaceholders()) { - currentLine = ChatColor.translateAlternateColorCodes( - '&', SidebarManager.getInstance().getPapiSupport().replacePlaceholders(player, currentLine) - ); + currentLine = SidebarManager.getInstance().getPapiSupport().replacePlaceholders(player, currentLine); } data[i] = currentLine; } @@ -143,6 +140,179 @@ private String buildTabContent(Player player, @NotNull List lines, return StringUtils.join(data, "\n"); } + /** + * Replace placeholders in a message. + * Use this for internal placeholders replacement. + * + * @param message message to replace in. + * @param replacements replacements. + * @return replaced message. + */ + public static String replacePlaceholders(String message, Collection replacements) { + if (message == null || replacements == null || replacements.isEmpty()) return message; + return replacePlaceholders(message, new CompiledPlaceholders(replacements)); + } + + /** + * Replace placeholders in a message using pre-compiled placeholders. + * + * @param message message to replace in. + * @param replacements pre-compiled replacements. + * @return replaced message. + */ + public static String replacePlaceholders(String message, CompiledPlaceholders replacements) { + if (message == null || replacements == null || replacements.isEmpty()) return message; + + Map> map = replacements.getMap(); + char[] startChars = replacements.getStartChars(); + + StringBuilder sb = new StringBuilder(message.length() + 16); + int lastIndex = 0; + int len = message.length(); + + while (lastIndex < len) { + int start = -1; + char foundChar = 0; + + // Găsim cel mai apropiat simbol de start + if (startChars.length == 1) { + foundChar = startChars[0]; + start = message.indexOf(foundChar, lastIndex); + } else { + for (char c : startChars) { + int pos = message.indexOf(c, lastIndex); + if (pos != -1 && (start == -1 || pos < start)) { + start = pos; + foundChar = c; + } + } + } + + if (start == -1) { + sb.append(message, lastIndex, len); + break; + } + + // Adăugăm textul de dinainte de placeholder + sb.append(message, lastIndex, start); + + String value = null; + int keyLen = 0; + + List candidates = map.get(foundChar); + if (candidates != null) { + for (PlaceholderProvider provider : candidates) { + String key = provider.getPlaceholder(); + int kLen = key.length(); + // Folosim atât primul cât și ultimul caracter ca "chei" pentru filtrare rapidă + if (start + kLen <= len && message.charAt(start + kLen - 1) == key.charAt(kLen - 1)) { + if (message.regionMatches(start, key, 0, kLen)) { + value = provider.getReplacement(); + keyLen = kLen; + break; + } + } + } + } + + if (value != null) { + sb.append(value); + lastIndex = start + keyLen; + } else { + // Nu este un placeholder cunoscut, păstrăm simbolul și continuăm + sb.append(foundChar); + lastIndex = start + 1; + } + } + return sb.toString(); + } + + /** + * Replace placeholders in a message. + * Use this for internal placeholders replacement. + * + * @param message message to replace in. + * @param replacements replacements. + * @return replaced message. + */ + @SuppressWarnings("unused") + public static String replacePlaceholders(String message, String... replacements) { + if (message == null || replacements.length < 2) return message; + + // Grupăm index-urile din array după primul caracter al cheii + Map> map = new HashMap<>(); + for (int i = 0; i < replacements.length; i += 2) { + String key = replacements[i]; + if (key != null && !key.isEmpty()) { + map.computeIfAbsent(key.charAt(0), k -> new ArrayList<>()).add(i); + } + } + + Set startCharsSet = map.keySet(); + char[] startChars = new char[startCharsSet.size()]; + int idx = 0; + for (char c : startCharsSet) { + startChars[idx++] = c; + } + + StringBuilder sb = new StringBuilder(message.length() + 16); + int lastIndex = 0; + int len = message.length(); + + while (lastIndex < len) { + int start = -1; + char foundChar = 0; + + if (startChars.length == 1) { + foundChar = startChars[0]; + start = message.indexOf(foundChar, lastIndex); + } else { + for (char c : startChars) { + int pos = message.indexOf(c, lastIndex); + if (pos != -1 && (start == -1 || pos < start)) { + start = pos; + foundChar = c; + } + } + } + + if (start == -1) { + sb.append(message, lastIndex, len); + break; + } + + sb.append(message, lastIndex, start); + + String value = null; + int keyLen = 0; + + List candidates = map.get(foundChar); + if (candidates != null) { + for (int i : candidates) { + String key = replacements[i]; + int kLen = key.length(); + // Folosim primul și ultimul caracter ca markeri de identificare + if (start + kLen <= len && message.charAt(start + kLen - 1) == key.charAt(kLen - 1)) { + if (message.regionMatches(start, key, 0, kLen)) { + value = replacements[i + 1]; + keyLen = kLen; + break; + } + } + } + } + + if (value != null) { + sb.append(value); + lastIndex = start + keyLen; + } else { + sb.append(foundChar); + lastIndex = start + 1; + } + } + return sb.toString(); + } + public PAPISupport getPapiSupport() { return papiSupport; } diff --git a/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/TabHeaderFooter.java b/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/TabHeaderFooter.java index 9984076..dc83757 100644 --- a/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/TabHeaderFooter.java +++ b/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/TabHeaderFooter.java @@ -14,6 +14,7 @@ public class TabHeaderFooter { private LinkedList header; private LinkedList footer; private Collection placeholders; + private volatile CompiledPlaceholders compiledPlaceholders; /** * Create a new tab context. @@ -37,6 +38,10 @@ public Collection getPlaceholders() { return placeholders; } + public CompiledPlaceholders getCompiledPlaceholders() { + return compiledPlaceholders; + } + public LinkedList getHeader() { return header; } @@ -48,10 +53,12 @@ public LinkedList getFooter() { public void setPlaceholders(@Nullable Collection placeholders) { if (null == placeholders) { this.placeholders = new ConcurrentLinkedQueue<>(); + this.compiledPlaceholders = new CompiledPlaceholders(this.placeholders); return; } this.placeholders = placeholders; + this.compiledPlaceholders = new CompiledPlaceholders(this.placeholders); for (SidebarLine line : footer) { markHasPlaceholders(line, placeholders); } diff --git a/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/WrappedSidebar.java b/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/WrappedSidebar.java index 50ffaec..ade6528 100644 --- a/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/WrappedSidebar.java +++ b/sidebar-base/src/main/java/com/andrei1058/spigot/sidebar/WrappedSidebar.java @@ -16,6 +16,7 @@ public abstract class WrappedSidebar implements Sidebar { private final LinkedList receivers = new LinkedList<>(); // placeholders for sidebar lines private final Collection placeholderProviders = new ConcurrentLinkedQueue<>(); + private volatile CompiledPlaceholders compiledPlaceholders; // chat colors used for line indexing - private final LinkedList availableColors = new LinkedList<>(); // sidebar lines objective @@ -38,6 +39,7 @@ public WrappedSidebar(@NotNull SidebarLine title, @NotNull Collection placeholder = new ConcurrentLinkedQueue<>(); placeholder.add(placeholderProvider); @@ -145,11 +148,7 @@ public void refreshPlaceholders() { // refresh placeholders for the given line before sending it // private String applyLinePlaceholders(@NotNull SidebarLine line) { -// String content = line.getLine(); -// for (PlaceholderProvider pp : this.placeholderProviders) { -// content = content.replace(pp.getPlaceholder(), pp.getReplacement()); -// } -// return content; +// return SidebarManager.replacePlaceholders(line.getLine(), this.placeholderProviders); // } // public ScoreLine applyPlaceholders(@NotNull ScoreLine line) { @@ -240,6 +239,11 @@ public int lineCount() { @Override public void removePlaceholder(String placeholder) { placeholderProviders.removeIf(p -> p.getPlaceholder().equalsIgnoreCase(placeholder)); + this.compiledPlaceholders = new CompiledPlaceholders(this.placeholderProviders); + } + + public CompiledPlaceholders getCompiledPlaceholders() { + return compiledPlaceholders; } @Override diff --git a/sidebar-base/src/test/java/com/andrei1058/spigot/sidebar/AlternatedPlaceholderProviderTest.java b/sidebar-base/src/test/java/com/andrei1058/spigot/sidebar/AlternatedPlaceholderProviderTest.java new file mode 100644 index 0000000..8ebd74d --- /dev/null +++ b/sidebar-base/src/test/java/com/andrei1058/spigot/sidebar/AlternatedPlaceholderProviderTest.java @@ -0,0 +1,58 @@ +package com.andrei1058.spigot.sidebar; + +import org.junit.Test; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.assertEquals; + +public class AlternatedPlaceholderProviderTest { + + @Test + public void alternatesReplacementAndRepeatsFromStart() { + AlternatedPlaceholderProvider provider = new AlternatedPlaceholderProvider("{anim}", + () -> "one", + () -> "two", + () -> "three" + ); + + assertEquals("one", provider.getReplacement()); + assertEquals("two", provider.getReplacement()); + assertEquals("three", provider.getReplacement()); + assertEquals("one", provider.getReplacement()); + } + + @Test + public void replacesWithNextValueOnEachRender() { + CompiledPlaceholders placeholders = new CompiledPlaceholders(List.of( + new AlternatedPlaceholderProvider("{anim}", () -> "A", () -> "B") + )); + + assertEquals("Value A", SidebarManager.replacePlaceholders("Value {anim}", placeholders)); + assertEquals("Value B", SidebarManager.replacePlaceholders("Value {anim}", placeholders)); + assertEquals("Value A", SidebarManager.replacePlaceholders("Value {anim}", placeholders)); + } + + @Test + public void callsEachReplacementWhenItIsRendered() { + AtomicInteger counter = new AtomicInteger(); + AlternatedPlaceholderProvider provider = new AlternatedPlaceholderProvider("{anim}", + () -> "one-" + counter.incrementAndGet(), + () -> "two-" + counter.incrementAndGet() + ); + + assertEquals("one-1", provider.getReplacement()); + assertEquals("two-2", provider.getReplacement()); + assertEquals("one-3", provider.getReplacement()); + } + + @Test + public void returnsSafeValuesForNullAndEmptyReplacementLists() { + AlternatedPlaceholderProvider nullProvider = new AlternatedPlaceholderProvider("{anim}", () -> null); + AlternatedPlaceholderProvider emptyProvider = new AlternatedPlaceholderProvider("{anim}"); + + assertEquals("null", nullProvider.getReplacement()); + assertEquals("", emptyProvider.getReplacement()); + } +} \ No newline at end of file diff --git a/sidebar-cmn1/pom.xml b/sidebar-cmn1/pom.xml index 547f0f9..e73dcc7 100644 --- a/sidebar-cmn1/pom.xml +++ b/sidebar-cmn1/pom.xml @@ -6,7 +6,7 @@ com.andrei1058.spigot.sidebar sidebar-pom - 25.2.2-SNAPSHOT + 26.6-SNAPSHOT sidebar-cmn1 @@ -51,4 +51,4 @@ - \ No newline at end of file + diff --git a/sidebar-cmn1/src/main/java/dev/andrei1058/spigot/sidebar/cmn1/PlayerListImplCmn1.java b/sidebar-cmn1/src/main/java/dev/andrei1058/spigot/sidebar/cmn1/PlayerListImplCmn1.java index 9424dd9..4dc9909 100644 --- a/sidebar-cmn1/src/main/java/dev/andrei1058/spigot/sidebar/cmn1/PlayerListImplCmn1.java +++ b/sidebar-cmn1/src/main/java/dev/andrei1058/spigot/sidebar/cmn1/PlayerListImplCmn1.java @@ -21,6 +21,7 @@ public class PlayerListImplCmn1 { private ScoreboardTeamBase.EnumNameTagVisibility nameTagVisibility = ScoreboardTeamBase.EnumNameTagVisibility.a; private Player papiSubject = null; private final Collection placeholders; + private final CompiledPlaceholders compiledPlaceholders; public PlayerListImplCmn1( @NotNull WrappedSidebar sidebar, @@ -38,6 +39,7 @@ public PlayerListImplCmn1( setNameTagVisibility(toNmsTagVisibility(nameTagVisibility)); this.id = identifier; this.placeholders = placeholders; + this.compiledPlaceholders = new CompiledPlaceholders(placeholders); } public ScoreboardTeamBase.EnumTeamPush toNmsPushing(PlayerTab.@NotNull PushingRule rule) { @@ -120,8 +122,8 @@ public Collection getPlaceholders() { } public boolean refreshContent() { - var newPrefix = prefix.getTrimReplacePlaceholders(papiSubject, 256, this.placeholders); - var newSuffix = suffix.getTrimReplacePlaceholders(papiSubject, 256, this.placeholders); + var newPrefix = prefix.getTrimReplacePlaceholders(papiSubject, 256, this.compiledPlaceholders); + var newSuffix = suffix.getTrimReplacePlaceholders(papiSubject, 256, this.compiledPlaceholders); if (newPrefix.equals(prefixComp.getString()) && newSuffix.equals(suffixComp.getString())) { return false; diff --git a/sidebar-v1_12_R1/pom.xml b/sidebar-v1_12_R1/pom.xml index 66f4bf4..9048fba 100644 --- a/sidebar-v1_12_R1/pom.xml +++ b/sidebar-v1_12_R1/pom.xml @@ -5,7 +5,7 @@ sidebar-pom com.andrei1058.spigot.sidebar - 25.2.2-SNAPSHOT + 26.6-SNAPSHOT 4.0.0 @@ -59,4 +59,4 @@ - \ No newline at end of file + diff --git a/sidebar-v1_12_R1/src/main/java/com/andrei1058/spigot/sidebar/v1_12_R1/PlayerListImpl.java b/sidebar-v1_12_R1/src/main/java/com/andrei1058/spigot/sidebar/v1_12_R1/PlayerListImpl.java index fa49f9c..863ebb6 100644 --- a/sidebar-v1_12_R1/src/main/java/com/andrei1058/spigot/sidebar/v1_12_R1/PlayerListImpl.java +++ b/sidebar-v1_12_R1/src/main/java/com/andrei1058/spigot/sidebar/v1_12_R1/PlayerListImpl.java @@ -4,6 +4,7 @@ import net.minecraft.server.v1_12_R1.PacketPlayOutScoreboardTeam; import net.minecraft.server.v1_12_R1.ScoreboardTeam; import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -23,6 +24,7 @@ public class PlayerListImpl extends ScoreboardTeam implements VersionedTabGroup private EnumNameTagVisibility nameTagVisibility; private Player papiSubject = null; private final Collection placeholders; + private final CompiledPlaceholders compiledPlaceholders; public PlayerListImpl(@NotNull WrappedSidebar sidebar, String identifier, SidebarLine prefix, SidebarLine suffix, PushingRule pushingRule, NameTagVisibility nameTagVisibility, @@ -35,6 +37,7 @@ public PlayerListImpl(@NotNull WrappedSidebar sidebar, String identifier, Sideba this.setNameTagVisibility(nameTagVisibility); this.id = identifier; this.placeholders = placeholders; + this.compiledPlaceholders = new CompiledPlaceholders(placeholders); } @Override @@ -83,6 +86,11 @@ public void add(@NotNull Player player) { sidebar.getReceivers().forEach(r -> ((CraftPlayer) r).getHandle().playerConnection.sendPacket(packetPlayOutScoreboardTeam)); } + @Override + public void add(Entity entity) { + + } + @Override public void sendCreateToPlayer(Player player) { PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam(this, 0); @@ -97,6 +105,11 @@ public void remove(@NotNull Player player) { sidebar.getReceivers().forEach(r -> ((CraftPlayer) r).getHandle().playerConnection.sendPacket(packetPlayOutScoreboardTeam)); } + @Override + public void remove(Entity entity) { + + } + @Override public void sendUserCreateToReceivers(@NotNull Player player) { // send 3: add entities to team @@ -119,8 +132,8 @@ public void sendRemoveToReceivers() { @Override public boolean refreshContent() { - String newPrefix = this.prefix.getTrimReplacePlaceholders(getSubject(), 16, this.placeholders); - String newSuffix = this.suffix.getTrimReplacePlaceholders(getSubject(), 16, this.placeholders); + String newPrefix = this.prefix.getTrimReplacePlaceholders(getSubject(), 16, this.compiledPlaceholders); + String newSuffix = this.suffix.getTrimReplacePlaceholders(getSubject(), 16, this.compiledPlaceholders); if (newPrefix.equals(prefixString) && newSuffix.equals(suffixString)) { return false; diff --git a/sidebar-v1_16_R3/pom.xml b/sidebar-v1_16_R3/pom.xml index 00ddbad..35f2159 100644 --- a/sidebar-v1_16_R3/pom.xml +++ b/sidebar-v1_16_R3/pom.xml @@ -5,7 +5,7 @@ sidebar-pom com.andrei1058.spigot.sidebar - 25.2.2-SNAPSHOT + 26.6-SNAPSHOT 4.0.0 @@ -58,4 +58,4 @@ - \ No newline at end of file + diff --git a/sidebar-v1_16_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_16_R3/PlayerListImpl.java b/sidebar-v1_16_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_16_R3/PlayerListImpl.java index 12da7e2..5e4e9f7 100644 --- a/sidebar-v1_16_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_16_R3/PlayerListImpl.java +++ b/sidebar-v1_16_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_16_R3/PlayerListImpl.java @@ -3,6 +3,7 @@ import com.andrei1058.spigot.sidebar.*; import net.minecraft.server.v1_16_R3.*; import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -82,6 +83,11 @@ public void add(@NotNull Player player) { sidebar.getReceivers().forEach(r -> ((CraftPlayer) r).getHandle().playerConnection.sendPacket(packetPlayOutScoreboardTeam)); } + @Override + public void add(Entity entity) { + + } + @Override public void sendCreateToPlayer(Player player) { PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam(this, 0); @@ -96,6 +102,11 @@ public void remove(@NotNull Player player) { sidebar.getReceivers().forEach(r -> ((CraftPlayer) r).getHandle().playerConnection.sendPacket(packetPlayOutScoreboardTeam)); } + @Override + public void remove(Entity entity) { + + } + @Override public void sendUserCreateToReceivers(@NotNull Player player) { // send 3: add entities to team diff --git a/sidebar-v1_17_R1/pom.xml b/sidebar-v1_17_R1/pom.xml index 1f89053..23f2e9d 100644 --- a/sidebar-v1_17_R1/pom.xml +++ b/sidebar-v1_17_R1/pom.xml @@ -5,7 +5,7 @@ sidebar-pom com.andrei1058.spigot.sidebar - 25.2.2-SNAPSHOT + 26.6-SNAPSHOT 4.0.0 @@ -58,4 +58,4 @@ - \ No newline at end of file + diff --git a/sidebar-v1_17_R1/src/main/java/com/andrei1058/spigot/sidebar/v1_17_R1/PlayerListImpl.java b/sidebar-v1_17_R1/src/main/java/com/andrei1058/spigot/sidebar/v1_17_R1/PlayerListImpl.java index 68f3ea4..2728c0b 100644 --- a/sidebar-v1_17_R1/src/main/java/com/andrei1058/spigot/sidebar/v1_17_R1/PlayerListImpl.java +++ b/sidebar-v1_17_R1/src/main/java/com/andrei1058/spigot/sidebar/v1_17_R1/PlayerListImpl.java @@ -8,6 +8,7 @@ import net.minecraft.world.scores.ScoreboardTeam; import net.minecraft.world.scores.ScoreboardTeamBase; import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; @@ -86,6 +87,11 @@ public void add(@NotNull Player player) { sidebar.getReceivers().forEach(r -> ((CraftPlayer) r).getHandle().b.sendPacket(packetPlayOutScoreboardTeam)); } + @Override + public void add(Entity entity) { + + } + @Override public void sendCreateToPlayer(Player player) { PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = PacketPlayOutScoreboardTeam.a(this, true); @@ -100,6 +106,11 @@ public void remove(@NotNull Player player) { sidebar.getReceivers().forEach(r -> ((CraftPlayer) r).getHandle().b.sendPacket(packetPlayOutScoreboardTeam)); } + @Override + public void remove(Entity entity) { + + } + @Override public void sendUserCreateToReceivers(@NotNull Player player) { // send 3: add entities to team diff --git a/sidebar-v1_18_R2/pom.xml b/sidebar-v1_18_R2/pom.xml index da71b32..ed020bd 100644 --- a/sidebar-v1_18_R2/pom.xml +++ b/sidebar-v1_18_R2/pom.xml @@ -5,7 +5,7 @@ sidebar-pom com.andrei1058.spigot.sidebar - 25.2.2-SNAPSHOT + 26.6-SNAPSHOT 4.0.0 @@ -52,4 +52,4 @@ provided - \ No newline at end of file + diff --git a/sidebar-v1_18_R2/src/main/java/com/andrei1058/spigot/sidebar/v1_18_R2/PlayerListImpl.java b/sidebar-v1_18_R2/src/main/java/com/andrei1058/spigot/sidebar/v1_18_R2/PlayerListImpl.java index ec26ad9..dd3fc2d 100644 --- a/sidebar-v1_18_R2/src/main/java/com/andrei1058/spigot/sidebar/v1_18_R2/PlayerListImpl.java +++ b/sidebar-v1_18_R2/src/main/java/com/andrei1058/spigot/sidebar/v1_18_R2/PlayerListImpl.java @@ -7,6 +7,7 @@ import net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam; import net.minecraft.world.scores.ScoreboardTeam; import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; @@ -102,6 +103,11 @@ public void add(@NotNull Player player) { sidebar.getReceivers().forEach(r -> ((CraftPlayer) r).getHandle().b.a(packetPlayOutScoreboardTeam)); } + @Override + public void add(Entity entity) { + + } + @Override public void sendCreateToPlayer(Player player) { PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = PacketPlayOutScoreboardTeam.a(this, true); @@ -116,6 +122,11 @@ public void remove(@NotNull Player player) { sidebar.getReceivers().forEach(r -> ((CraftPlayer) r).getHandle().b.a(packetPlayOutScoreboardTeam)); } + @Override + public void remove(Entity entity) { + + } + @Override public void sendUserCreateToReceivers(@NotNull Player player) { // send 3: add entities to team diff --git a/sidebar-v1_19_R2/pom.xml b/sidebar-v1_19_R2/pom.xml index 9f5d9cb..4ebdad7 100644 --- a/sidebar-v1_19_R2/pom.xml +++ b/sidebar-v1_19_R2/pom.xml @@ -5,7 +5,7 @@ sidebar-pom com.andrei1058.spigot.sidebar - 25.2.2-SNAPSHOT + 26.6-SNAPSHOT 4.0.0 @@ -52,4 +52,4 @@ provided - \ No newline at end of file + diff --git a/sidebar-v1_19_R2/src/main/java/com/andrei1058/spigot/sidebar/v1_19_R2/PlayerListImpl.java b/sidebar-v1_19_R2/src/main/java/com/andrei1058/spigot/sidebar/v1_19_R2/PlayerListImpl.java index eb8a9c3..243e19f 100644 --- a/sidebar-v1_19_R2/src/main/java/com/andrei1058/spigot/sidebar/v1_19_R2/PlayerListImpl.java +++ b/sidebar-v1_19_R2/src/main/java/com/andrei1058/spigot/sidebar/v1_19_R2/PlayerListImpl.java @@ -6,6 +6,7 @@ import net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam; import net.minecraft.world.scores.ScoreboardTeam; import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; @@ -101,6 +102,11 @@ public void add(@NotNull Player player) { sidebar.getReceivers().forEach(r -> ((CraftPlayer) r).getHandle().b.a(packetPlayOutScoreboardTeam)); } + @Override + public void add(Entity entity) { + + } + @Override public void sendCreateToPlayer(Player player) { PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = PacketPlayOutScoreboardTeam.a(this, true); @@ -115,6 +121,11 @@ public void remove(@NotNull Player player) { sidebar.getReceivers().forEach(r -> ((CraftPlayer) r).getHandle().b.a(packetPlayOutScoreboardTeam)); } + @Override + public void remove(Entity entity) { + + } + @Override public void sendUserCreateToReceivers(@NotNull Player player) { // send 3: add entities to team diff --git a/sidebar-v1_19_R3/pom.xml b/sidebar-v1_19_R3/pom.xml index 373e756..c34ed9e 100644 --- a/sidebar-v1_19_R3/pom.xml +++ b/sidebar-v1_19_R3/pom.xml @@ -5,7 +5,7 @@ sidebar-pom com.andrei1058.spigot.sidebar - 25.2.2-SNAPSHOT + 26.6-SNAPSHOT 4.0.0 @@ -52,4 +52,4 @@ provided - \ No newline at end of file + diff --git a/sidebar-v1_19_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_19_R3/PlayerListImpl.java b/sidebar-v1_19_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_19_R3/PlayerListImpl.java index 5357ae7..2208653 100644 --- a/sidebar-v1_19_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_19_R3/PlayerListImpl.java +++ b/sidebar-v1_19_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_19_R3/PlayerListImpl.java @@ -6,6 +6,7 @@ import net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam; import net.minecraft.world.scores.ScoreboardTeam; import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; @@ -101,6 +102,11 @@ public void add(@NotNull Player player) { sidebar.getReceivers().forEach(r -> ((CraftPlayer) r).getHandle().b.a(packetPlayOutScoreboardTeam)); } + @Override + public void add(Entity entity) { + + } + @Override public void sendCreateToPlayer(Player player) { PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = PacketPlayOutScoreboardTeam.a(this, true); @@ -115,6 +121,11 @@ public void remove(@NotNull Player player) { sidebar.getReceivers().forEach(r -> ((CraftPlayer) r).getHandle().b.a(packetPlayOutScoreboardTeam)); } + @Override + public void remove(Entity entity) { + + } + @Override public void sendUserCreateToReceivers(@NotNull Player player) { // send 3: add entities to team diff --git a/sidebar-v1_20_R1/pom.xml b/sidebar-v1_20_R1/pom.xml index 846b61b..0621c5a 100644 --- a/sidebar-v1_20_R1/pom.xml +++ b/sidebar-v1_20_R1/pom.xml @@ -5,7 +5,7 @@ sidebar-pom com.andrei1058.spigot.sidebar - 25.2.2-SNAPSHOT + 26.6-SNAPSHOT 4.0.0 @@ -52,4 +52,4 @@ provided - \ No newline at end of file + diff --git a/sidebar-v1_20_R1/src/main/java/com/andrei1058/spigot/sidebar/v1_20_R1/PlayerListImpl.java b/sidebar-v1_20_R1/src/main/java/com/andrei1058/spigot/sidebar/v1_20_R1/PlayerListImpl.java index edce4aa..3ac9d86 100644 --- a/sidebar-v1_20_R1/src/main/java/com/andrei1058/spigot/sidebar/v1_20_R1/PlayerListImpl.java +++ b/sidebar-v1_20_R1/src/main/java/com/andrei1058/spigot/sidebar/v1_20_R1/PlayerListImpl.java @@ -6,6 +6,7 @@ import net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam; import net.minecraft.world.scores.ScoreboardTeam; import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; @@ -107,6 +108,11 @@ public void add(@NotNull Player player) { sidebar.getReceivers().forEach(r -> ((CraftPlayer) r).getHandle().c.a(packetPlayOutScoreboardTeam)); } + @Override + public void add(Entity entity) { + + } + @Override public void sendCreateToPlayer(Player player) { PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = PacketPlayOutScoreboardTeam.a(this, true); @@ -121,6 +127,11 @@ public void remove(@NotNull Player player) { sidebar.getReceivers().forEach(r -> ((CraftPlayer) r).getHandle().c.a(packetPlayOutScoreboardTeam)); } + @Override + public void remove(Entity entity) { + + } + @Override public void sendUserCreateToReceivers(@NotNull Player player) { // send 3: add entities to team diff --git a/sidebar-v1_20_R2/pom.xml b/sidebar-v1_20_R2/pom.xml index 4677e62..53808c9 100644 --- a/sidebar-v1_20_R2/pom.xml +++ b/sidebar-v1_20_R2/pom.xml @@ -5,7 +5,7 @@ sidebar-pom com.andrei1058.spigot.sidebar - 25.2.2-SNAPSHOT + 26.6-SNAPSHOT 4.0.0 @@ -52,4 +52,4 @@ provided - \ No newline at end of file + diff --git a/sidebar-v1_20_R2/src/main/java/com/andrei1058/spigot/sidebar/v1_20_R2/PlayerListImpl.java b/sidebar-v1_20_R2/src/main/java/com/andrei1058/spigot/sidebar/v1_20_R2/PlayerListImpl.java index be07a5a..9f13e77 100644 --- a/sidebar-v1_20_R2/src/main/java/com/andrei1058/spigot/sidebar/v1_20_R2/PlayerListImpl.java +++ b/sidebar-v1_20_R2/src/main/java/com/andrei1058/spigot/sidebar/v1_20_R2/PlayerListImpl.java @@ -6,6 +6,7 @@ import net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam; import net.minecraft.world.scores.ScoreboardTeam; import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; @@ -107,6 +108,11 @@ public void add(@NotNull Player player) { sidebar.getReceivers().forEach(r -> ((CraftPlayer) r).getHandle().c.b(packetPlayOutScoreboardTeam)); } + @Override + public void add(Entity entity) { + + } + @Override public void sendCreateToPlayer(Player player) { PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = PacketPlayOutScoreboardTeam.a(this, true); @@ -121,6 +127,11 @@ public void remove(@NotNull Player player) { sidebar.getReceivers().forEach(r -> ((CraftPlayer) r).getHandle().c.b(packetPlayOutScoreboardTeam)); } + @Override + public void remove(Entity entity) { + + } + @Override public void sendUserCreateToReceivers(@NotNull Player player) { // send 3: add entities to team diff --git a/sidebar-v1_20_R3/pom.xml b/sidebar-v1_20_R3/pom.xml index d18573f..ec88374 100644 --- a/sidebar-v1_20_R3/pom.xml +++ b/sidebar-v1_20_R3/pom.xml @@ -5,7 +5,7 @@ sidebar-pom com.andrei1058.spigot.sidebar - 25.2.2-SNAPSHOT + 26.6-SNAPSHOT 4.0.0 @@ -52,4 +52,4 @@ provided - \ No newline at end of file + diff --git a/sidebar-v1_20_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_20_R3/PlayerListImpl.java b/sidebar-v1_20_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_20_R3/PlayerListImpl.java index 25ae622..b142b6e 100644 --- a/sidebar-v1_20_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_20_R3/PlayerListImpl.java +++ b/sidebar-v1_20_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_20_R3/PlayerListImpl.java @@ -6,6 +6,7 @@ import net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam; import net.minecraft.world.scores.ScoreboardTeam; import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; @@ -107,6 +108,11 @@ public void add(@NotNull Player player) { sidebar.getReceivers().forEach(r -> ((CraftPlayer) r).getHandle().c.b(packetPlayOutScoreboardTeam)); } + @Override + public void add(Entity entity) { + + } + @Override public void sendCreateToPlayer(Player player) { PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = PacketPlayOutScoreboardTeam.a(this, true); @@ -121,6 +127,11 @@ public void remove(@NotNull Player player) { sidebar.getReceivers().forEach(r -> ((CraftPlayer) r).getHandle().c.b(packetPlayOutScoreboardTeam)); } + @Override + public void remove(Entity entity) { + + } + @Override public void sendUserCreateToReceivers(@NotNull Player player) { // send 3: add entities to team diff --git a/sidebar-v1_20_R4/pom.xml b/sidebar-v1_20_R4/pom.xml index cde30c7..92075f6 100644 --- a/sidebar-v1_20_R4/pom.xml +++ b/sidebar-v1_20_R4/pom.xml @@ -5,7 +5,7 @@ sidebar-pom com.andrei1058.spigot.sidebar - 25.2.2-SNAPSHOT + 26.6-SNAPSHOT 4.0.0 @@ -58,4 +58,4 @@ provided - \ No newline at end of file + diff --git a/sidebar-v1_21_R1/pom.xml b/sidebar-v1_21_R1/pom.xml index dd1ddfb..05da639 100644 --- a/sidebar-v1_21_R1/pom.xml +++ b/sidebar-v1_21_R1/pom.xml @@ -5,7 +5,7 @@ sidebar-pom com.andrei1058.spigot.sidebar - 25.2.2-SNAPSHOT + 26.6-SNAPSHOT 4.0.0 @@ -58,4 +58,4 @@ compile - \ No newline at end of file + diff --git a/sidebar-v1_21_R1/src/main/java/com/andrei1058/spigot/sidebar/v1_21_R1/PlayerListImpl.java b/sidebar-v1_21_R1/src/main/java/com/andrei1058/spigot/sidebar/v1_21_R1/PlayerListImpl.java index 711e5c9..9f7d808 100644 --- a/sidebar-v1_21_R1/src/main/java/com/andrei1058/spigot/sidebar/v1_21_R1/PlayerListImpl.java +++ b/sidebar-v1_21_R1/src/main/java/com/andrei1058/spigot/sidebar/v1_21_R1/PlayerListImpl.java @@ -10,6 +10,7 @@ import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -105,6 +106,11 @@ public void add(@NotNull Player player) { handle.getSidebar().getReceivers().forEach(r -> sendPacket(r, packetPlayOutScoreboardTeam)); } + @Override + public void add(Entity entity) { + + } + @Override public void remove(@NotNull Player player) { // send 4: remove entities from team @@ -114,6 +120,11 @@ public void remove(@NotNull Player player) { handle.getSidebar().getReceivers().forEach(r -> sendPacket(r, packetPlayOutScoreboardTeam)); } + @Override + public void remove(Entity entity) { + + } + @Override public void setSubject(@Nullable Player player) { this.handle.setPapiSubject(player); diff --git a/sidebar-v1_21_R2/pom.xml b/sidebar-v1_21_R2/pom.xml index c86cc31..6fec529 100644 --- a/sidebar-v1_21_R2/pom.xml +++ b/sidebar-v1_21_R2/pom.xml @@ -5,7 +5,7 @@ sidebar-pom com.andrei1058.spigot.sidebar - 25.2.2-SNAPSHOT + 26.6-SNAPSHOT 4.0.0 @@ -58,4 +58,4 @@ compile - \ No newline at end of file + diff --git a/sidebar-v1_21_R2/src/main/java/com/andrei1058/spigot/sidebar/v1_21_R2/PlayerListImpl.java b/sidebar-v1_21_R2/src/main/java/com/andrei1058/spigot/sidebar/v1_21_R2/PlayerListImpl.java index 889bbd9..6b5c0f0 100644 --- a/sidebar-v1_21_R2/src/main/java/com/andrei1058/spigot/sidebar/v1_21_R2/PlayerListImpl.java +++ b/sidebar-v1_21_R2/src/main/java/com/andrei1058/spigot/sidebar/v1_21_R2/PlayerListImpl.java @@ -8,6 +8,7 @@ import net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam; import net.minecraft.world.scores.ScoreboardTeam; import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -103,6 +104,11 @@ public void add(@NotNull Player player) { handle.getSidebar().getReceivers().forEach(r -> sendPacket(r, packetPlayOutScoreboardTeam)); } + @Override + public void add(Entity entity) { + + } + @Override public void remove(@NotNull Player player) { // send 4: remove entities from team @@ -112,6 +118,11 @@ public void remove(@NotNull Player player) { handle.getSidebar().getReceivers().forEach(r -> sendPacket(r, packetPlayOutScoreboardTeam)); } + @Override + public void remove(Entity entity) { + + } + @Override public void setSubject(@Nullable Player player) { this.handle.setPapiSubject(player); diff --git a/sidebar-v1_21_R3/pom.xml b/sidebar-v1_21_R3/pom.xml index 31e6fdb..0f3d335 100644 --- a/sidebar-v1_21_R3/pom.xml +++ b/sidebar-v1_21_R3/pom.xml @@ -5,7 +5,7 @@ sidebar-pom com.andrei1058.spigot.sidebar - 25.2.2-SNAPSHOT + 26.6-SNAPSHOT 4.0.0 @@ -58,4 +58,4 @@ compile - \ No newline at end of file + diff --git a/sidebar-v1_21_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_21_R3/PlayerListImpl.java b/sidebar-v1_21_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_21_R3/PlayerListImpl.java index 96b7e3f..a696f35 100644 --- a/sidebar-v1_21_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_21_R3/PlayerListImpl.java +++ b/sidebar-v1_21_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_21_R3/PlayerListImpl.java @@ -8,6 +8,7 @@ import net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam; import net.minecraft.world.scores.ScoreboardTeam; import org.bukkit.craftbukkit.v1_21_R3.entity.CraftPlayer; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -97,17 +98,27 @@ private void sendPacket(Player player, Packet packet) { @Override public void add(@NotNull Player player) { + add((Entity) player); + } + + @Override + public void add(@NotNull Entity entity) { PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = PacketPlayOutScoreboardTeam.a( - this, player.getName(), cachedScoreboardActionA + this, entity.getUniqueId().toString(), cachedScoreboardActionA ); handle.getSidebar().getReceivers().forEach(r -> sendPacket(r, packetPlayOutScoreboardTeam)); } @Override public void remove(@NotNull Player player) { + remove((Entity) player); + } + + @Override + public void remove(@NotNull Entity entity) { // send 4: remove entities from team PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = PacketPlayOutScoreboardTeam.a( - this, player.getName(), cachedScoreboardActionB + this, entity.getUniqueId().toString(), cachedScoreboardActionB ); handle.getSidebar().getReceivers().forEach(r -> sendPacket(r, packetPlayOutScoreboardTeam)); } diff --git a/sidebar-v1_21_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_21_R3/SidebarImpl.java b/sidebar-v1_21_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_21_R3/SidebarImpl.java index cd88882..07e0223 100644 --- a/sidebar-v1_21_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_21_R3/SidebarImpl.java +++ b/sidebar-v1_21_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_21_R3/SidebarImpl.java @@ -79,7 +79,7 @@ public boolean refreshTitle() { var newTitle = displayName.getTrimReplacePlaceholders( getReceivers().isEmpty() ? null : getReceivers().getFirst(), 256, - getPlaceholders() + getCompiledPlaceholders() ); if (newTitle.equals(displayNameComp.getString())) { @@ -254,7 +254,7 @@ public boolean setContent(@NotNull SidebarLine line) { String content = line.getTrimReplacePlaceholders( getReceivers().isEmpty() ? null : getReceivers().getFirst(), null, - getPlaceholders() + getCompiledPlaceholders() ); if (content.length() > 256) { diff --git a/sidebar-v1_8_R3/pom.xml b/sidebar-v1_8_R3/pom.xml index 5a74fbe..c04e44f 100644 --- a/sidebar-v1_8_R3/pom.xml +++ b/sidebar-v1_8_R3/pom.xml @@ -5,7 +5,7 @@ sidebar-pom com.andrei1058.spigot.sidebar - 25.2.2-SNAPSHOT + 26.6-SNAPSHOT 4.0.0 @@ -59,4 +59,4 @@ - \ No newline at end of file + diff --git a/sidebar-v1_8_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_8_R3/PlayerListImpl.java b/sidebar-v1_8_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_8_R3/PlayerListImpl.java index 13d0bbc..990dc02 100644 --- a/sidebar-v1_8_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_8_R3/PlayerListImpl.java +++ b/sidebar-v1_8_R3/src/main/java/com/andrei1058/spigot/sidebar/v1_8_R3/PlayerListImpl.java @@ -4,6 +4,7 @@ import net.minecraft.server.v1_8_R3.PacketPlayOutScoreboardTeam; import net.minecraft.server.v1_8_R3.ScoreboardTeam; import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -73,6 +74,11 @@ public void add(@NotNull Player player) { sidebar.getReceivers().forEach(r -> ((CraftPlayer) r).getHandle().playerConnection.sendPacket(packetPlayOutScoreboardTeam)); } + @Override + public void add(Entity entity) { + + } + @Override public void sendCreateToPlayer(Player player) { PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam(this, 0); @@ -87,6 +93,11 @@ public void remove(@NotNull Player player) { sidebar.getReceivers().forEach(r -> ((CraftPlayer) r).getHandle().playerConnection.sendPacket(packetPlayOutScoreboardTeam)); } + @Override + public void remove(Entity entity) { + + } + @Override public void sendUserCreateToReceivers(@NotNull Player player) { // send 3: add entities to team diff --git a/sidebar-v26_1_2/pom.xml b/sidebar-v26_1_2/pom.xml new file mode 100644 index 0000000..06ad3a1 --- /dev/null +++ b/sidebar-v26_1_2/pom.xml @@ -0,0 +1,61 @@ + + + + sidebar-pom + com.andrei1058.spigot.sidebar + 26.6-SNAPSHOT + + 4.0.0 + + sidebar-v26_1_2 + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 25 + 25 + + + + org.apache.maven.plugins + maven-javadoc-plugin + + true + + + + + + + + nms + https://repo.codemc.io/repository/nms/ + + + + + + com.andrei1058.spigot.sidebar + sidebar-base + ${project.version} + provided + + + org.spigotmc + spigot + 26.1.2-R0.1-SNAPSHOT + provided + + + com.andrei1058.spigot.sidebar + sidebar-cmn1 + ${project.version} + compile + + + diff --git a/sidebar-v26_1_2/src/main/java/com/andrei1058/spigot/sidebar/v26_1_2/ComponentUtil.java b/sidebar-v26_1_2/src/main/java/com/andrei1058/spigot/sidebar/v26_1_2/ComponentUtil.java new file mode 100644 index 0000000..596c0cb --- /dev/null +++ b/sidebar-v26_1_2/src/main/java/com/andrei1058/spigot/sidebar/v26_1_2/ComponentUtil.java @@ -0,0 +1,168 @@ +package com.andrei1058.spigot.sidebar.v26_1_2; + +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.Style; +import org.jetbrains.annotations.NotNull; + +/** + * Utility for converting Minecraft color codes (§c, §x§r§r...) to styled Components. + */ +public class ComponentUtil { + + private static final int[] LEGACY_COLORS = { + 0x000000, // §0 Black + 0x0000AA, // §1 Dark Blue + 0x00AA00, // §2 Dark Green + 0x00AAAA, // §3 Dark Aqua + 0xAA0000, // §4 Dark Red + 0xAA00AA, // §5 Dark Purple + 0xFFAA00, // §6 Gold + 0xAAAAAA, // §7 Gray + 0x555555, // §8 Dark Gray + 0x5555FF, // §9 Blue + 0x55FF55, // §a Green + 0x55FFFF, // §b Aqua + 0xFF5555, // §c Red + 0xFF55FF, // §d Light Purple + 0xFFFF55, // §e Yellow + 0xFFFFFF // §f White + }; + + /** + * Gets RGB color from a legacy color code character (0-9, a-f). + * + * @param code the color code character + * @return RGB color integer, or -1 if invalid + */ + private static int getLegacyColor(char code) { + if (code >= '0' && code <= '9') { + return LEGACY_COLORS[code - '0']; + } else if (code >= 'a' && code <= 'f') { + return LEGACY_COLORS[10 + (code - 'a')]; + } else if (code >= 'A' && code <= 'F') { + return LEGACY_COLORS[10 + (code - 'A')]; + } + return -1; + } + + /** + * Extracts the last active color code from a string up to a given position. + * Returns the color code (e.g., "§x§5§4§D§A§F§4") that should be prepended to continue coloring. + * + * @param text the text to search + * @param upToPosition search up to this position + * @return the last color code, or empty string if none found + */ + @NotNull + public static String getLastColorCodeUpTo(@NotNull String text, int upToPosition) { + String searchText = text.substring(0, Math.min(upToPosition, text.length())); + String lastColor = ""; + int i = 0; + + while (i < searchText.length()) { + if (i < searchText.length() - 1 && searchText.charAt(i) == '\u00A7') { + char nextChar = searchText.charAt(i + 1); + + // Handle hex color: §x§r§r§g§g§b§b + if (nextChar == 'x' && i < searchText.length() - 13) { + try { + String hex = "" + searchText.charAt(i + 3) + searchText.charAt(i + 5) + + searchText.charAt(i + 7) + searchText.charAt(i + 9) + + searchText.charAt(i + 11) + searchText.charAt(i + 13); + Long.parseLong(hex, 16); // Validate hex + lastColor = searchText.substring(i, i + 14); + i += 14; + continue; + } catch (Exception e) { + // Invalid hex, skip + i += 2; + continue; + } + } + // Legacy code (§c, §a, etc) + if (getLegacyColor(nextChar) != -1) { + lastColor = searchText.substring(i, i + 2); + i += 2; + continue; + } + i++; + } else { + i++; + } + } + + return lastColor; + } + + /** + * Converts a string with Minecraft color codes (§c, §x§r§r§g§g§b§b) to styled Component. + * Properly handles both legacy color codes and hex color codes by maintaining active style. + * + * @param text the text with color codes + * @return styled Component + */ + @NotNull + public static Component fromColoredString(@NotNull String text) { + if (text.isEmpty()) { + return Component.literal(""); + } + + MutableComponent result = null; + StringBuilder current = new StringBuilder(); + Style currentStyle = Style.EMPTY; + int i = 0; + + while (i < text.length()) { + if (i < text.length() - 1 && text.charAt(i) == '\u00A7') { + // Flush current text with accumulated style + if (current.length() > 0) { + MutableComponent part = Component.literal(current.toString()); + part.setStyle(currentStyle); + result = result == null ? part : result.append(part); + current = new StringBuilder(); + } + + char nextChar = text.charAt(i + 1); + + // Handle hex color: §x§r§r§g§g§b§b + if (nextChar == 'x' && i < text.length() - 13) { + try { + String hex = "" + text.charAt(i + 3) + text.charAt(i + 5) + + text.charAt(i + 7) + text.charAt(i + 9) + + text.charAt(i + 11) + text.charAt(i + 13); + int color = (int) Long.parseLong(hex, 16); + currentStyle = Style.EMPTY.withColor(color); + i += 14; + continue; + } catch (Exception e) { + // Invalid hex, skip + i += 2; + continue; + } + } + + // Handle legacy codes (§c, §a, etc) + int legacyColor = getLegacyColor(nextChar); + if (legacyColor != -1) { + currentStyle = Style.EMPTY.withColor(legacyColor); + i += 2; + continue; + } + + i++; + } else { + current.append(text.charAt(i)); + i++; + } + } + + if (current.length() > 0) { + MutableComponent part = Component.literal(current.toString()); + part.setStyle(currentStyle); + result = result == null ? part : result.append(part); + } + + return result == null ? Component.literal("") : result; + } +} diff --git a/sidebar-v26_1_2/src/main/java/com/andrei1058/spigot/sidebar/v26_1_2/PlayerListImpl.java b/sidebar-v26_1_2/src/main/java/com/andrei1058/spigot/sidebar/v26_1_2/PlayerListImpl.java new file mode 100644 index 0000000..ffa8102 --- /dev/null +++ b/sidebar-v26_1_2/src/main/java/com/andrei1058/spigot/sidebar/v26_1_2/PlayerListImpl.java @@ -0,0 +1,176 @@ +package com.andrei1058.spigot.sidebar.v26_1_2; + +import com.andrei1058.spigot.sidebar.*; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket; +import net.minecraft.world.scores.PlayerTeam; +import net.minecraft.world.scores.Team; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +@SuppressWarnings("unused") +public class PlayerListImpl extends PlayerTeam implements VersionedTabGroup { + + private final WrappedSidebar sidebar; + private final String id; + private final SidebarLine prefix; + private final SidebarLine suffix; + private final Collection placeholders; + private final CompiledPlaceholders compiledPlaceholders; + private Player papiSubject = null; + private Component prefixComp = Component.literal(" "); + private Component suffixComp = Component.literal(" "); + + public PlayerListImpl( + @NotNull WrappedSidebar sidebar, + String identifier, + SidebarLine prefix, + SidebarLine suffix, + PlayerTab.PushingRule pushingRule, + PlayerTab.NameTagVisibility nameTagVisibility, + @Nullable Collection placeholders + ) { + super(null, identifier); + this.sidebar = sidebar; + this.id = identifier; + this.prefix = prefix; + this.suffix = suffix; + this.placeholders = placeholders; + this.compiledPlaceholders = new CompiledPlaceholders(placeholders); + } + + private Team.CollisionRule toNmsPushing(PlayerTab.@NotNull PushingRule rule) { + return switch (rule) { + case NEVER -> Team.CollisionRule.NEVER; + case ALWAYS -> Team.CollisionRule.ALWAYS; + case PUSH_OTHER_TEAMS -> Team.CollisionRule.PUSH_OTHER_TEAMS; + case PUSH_OWN_TEAM -> Team.CollisionRule.PUSH_OWN_TEAM; + }; + } + + private Team.Visibility toNmsTagVisibility(PlayerTab.@NotNull NameTagVisibility nameTagVisibility) { + return switch (nameTagVisibility) { + case NEVER -> Team.Visibility.NEVER; + case ALWAYS -> Team.Visibility.ALWAYS; + case HIDE_FOR_OTHER_TEAMS -> Team.Visibility.HIDE_FOR_OTHER_TEAMS; + case HIDE_FOR_OWN_TEAM -> Team.Visibility.HIDE_FOR_OWN_TEAM; + }; + } + + @Override + public void sendCreateToPlayer(Player player) { + sendPacket(player, ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(this, true)); + } + + @Override + public void sendUserCreateToReceivers(@NotNull Player player) { + ClientboundSetPlayerTeamPacket packet = ClientboundSetPlayerTeamPacket.createPlayerPacket( + this, player.getName(), ClientboundSetPlayerTeamPacket.Action.ADD); + sidebar.getReceivers().forEach(r -> sendPacket(r, packet)); + } + + @Override + public void sendUpdateToReceivers() { + ClientboundSetPlayerTeamPacket packet = ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(this, false); + sidebar.getReceivers().forEach(r -> sendPacket(r, packet)); + } + + @Override + public void sendRemoveToReceivers() { + ClientboundSetPlayerTeamPacket packet = ClientboundSetPlayerTeamPacket.createRemovePacket(this); + sidebar.getReceivers().forEach(r -> sendPacket(r, packet)); + } + + @Override + public boolean refreshContent() { + var newPrefix = prefix.getTrimReplacePlaceholders(papiSubject, 256, this.compiledPlaceholders); + var newSuffix = suffix.getTrimReplacePlaceholders(papiSubject, 256, this.compiledPlaceholders); + + if (newPrefix.equals(prefixComp.getString()) && newSuffix.equals(suffixComp.getString())) { + return false; + } + + this.prefixComp = ComponentUtil.fromColoredString(newPrefix); + this.suffixComp = ComponentUtil.fromColoredString(newSuffix); + return true; + } + + private void sendPacket(Player player, Packet packet) { + ((CraftPlayer) player).getHandle().connection.send(packet); + } + + @Override + public void add(@NotNull Player player) { + add((Entity) player); + } + + @Override + public void add(@NotNull Entity entity) { + ClientboundSetPlayerTeamPacket packet = ClientboundSetPlayerTeamPacket.createPlayerPacket( + this, entity.getUniqueId().toString(), ClientboundSetPlayerTeamPacket.Action.ADD + ); + sidebar.getReceivers().forEach(r -> sendPacket(r, packet)); + } + + @Override + public void remove(@NotNull Player player) { + remove((Entity) player); + } + + @Override + public void remove(@NotNull Entity entity) { + ClientboundSetPlayerTeamPacket packet = ClientboundSetPlayerTeamPacket.createPlayerPacket( + this, entity.getUniqueId().toString(), ClientboundSetPlayerTeamPacket.Action.REMOVE + ); + sidebar.getReceivers().forEach(r -> sendPacket(r, packet)); + } + + @Override + public void setSubject(@Nullable Player player) { + this.papiSubject = player; + } + + @Override + public @Nullable Player getSubject() { + return this.papiSubject; + } + + @Override + public void setPushingRule(@NotNull PushingRule rule) { + // todo + if (null != this.id) { + sendUpdateToReceivers(); + } + } + + @Override + public void setNameTagVisibility(@NotNull NameTagVisibility nameTagVisibility) { + // todo + if (null != this.id){ + sendUpdateToReceivers(); + } + } + + @Override + public String getIdentifier() { + return id; + } + + @Override + public @NotNull Component getPlayerPrefix() { + return prefixComp; + } + + @Override + public @NotNull Component getPlayerSuffix() { + return suffixComp; + } +} diff --git a/sidebar-v26_1_2/src/main/java/com/andrei1058/spigot/sidebar/v26_1_2/ProviderImpl.java b/sidebar-v26_1_2/src/main/java/com/andrei1058/spigot/sidebar/v26_1_2/ProviderImpl.java new file mode 100644 index 0000000..cefd1f7 --- /dev/null +++ b/sidebar-v26_1_2/src/main/java/com/andrei1058/spigot/sidebar/v26_1_2/ProviderImpl.java @@ -0,0 +1,66 @@ +package com.andrei1058.spigot.sidebar.v26_1_2; + +import com.andrei1058.spigot.sidebar.*; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ClientboundTabListPacket; +import net.minecraft.network.protocol.game.ClientboundSetScorePacket; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.scores.criteria.ObjectiveCriteria; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Optional; + +@SuppressWarnings("unused") +public class ProviderImpl extends SidebarProvider { + private static SidebarProvider instance; + + @Override + public Sidebar createSidebar(SidebarLine title, Collection lines, Collection placeholderProviders) { + return new SidebarImpl(title, lines, placeholderProviders); + } + + @Override + public SidebarObjective createObjective(@NotNull WrappedSidebar sidebar, String name, boolean health, SidebarLine title, int type) { + return ((SidebarImpl)sidebar).createObjective(name, health ? ObjectiveCriteria.HEALTH : ObjectiveCriteria.DUMMY, title, type); + } + + @Override + public ScoreLine createScoreLine(WrappedSidebar sidebar, SidebarLine line, int score, String color) { + return ((SidebarImpl)sidebar).createScore(line, score, color); + } + + @Override + public void sendScore(@NotNull WrappedSidebar sidebar, String playerName, int score) { + if (sidebar.getHealthObjective() == null) return; + ClientboundSetScorePacket packet = new ClientboundSetScorePacket( + playerName, + sidebar.getHealthObjective().getName(), + score, + Optional.empty(), + Optional.empty() + ); + for (Player player : sidebar.getReceivers()) { + ServerGamePacketListenerImpl playerConnection = ((CraftPlayer) player).getHandle().connection; + playerConnection.send(packet); + } + } + + @Override + public VersionedTabGroup createPlayerTab(WrappedSidebar sidebar, String identifier, SidebarLine prefix, SidebarLine suffix, PlayerTab.PushingRule pushingRule, PlayerTab.NameTagVisibility nameTagVisibility, @Nullable Collection placeholders) { + return new PlayerListImpl(sidebar, identifier, prefix, suffix, pushingRule, nameTagVisibility, placeholders); + } + + @Override + public void sendHeaderFooter(Player player, String header, String footer) { + ClientboundTabListPacket packet = new ClientboundTabListPacket(Component.literal(header), Component.literal(footer)); + ((CraftPlayer)player).getHandle().connection.send(packet); + } + + public static SidebarProvider getInstance() { + return null == instance ? instance = new ProviderImpl() : instance; + } +} diff --git a/sidebar-v26_1_2/src/main/java/com/andrei1058/spigot/sidebar/v26_1_2/SidebarImpl.java b/sidebar-v26_1_2/src/main/java/com/andrei1058/spigot/sidebar/v26_1_2/SidebarImpl.java new file mode 100644 index 0000000..a7c2b12 --- /dev/null +++ b/sidebar-v26_1_2/src/main/java/com/andrei1058/spigot/sidebar/v26_1_2/SidebarImpl.java @@ -0,0 +1,303 @@ +package com.andrei1058.spigot.sidebar.v26_1_2; + +import com.andrei1058.spigot.sidebar.*; +import net.md_5.bungee.api.ChatColor; +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.numbers.FixedFormat; +import net.minecraft.network.chat.numbers.NumberFormat; +import net.minecraft.network.protocol.game.*; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.scores.DisplaySlot; +import net.minecraft.world.scores.Objective; +import net.minecraft.world.scores.PlayerTeam; +import net.minecraft.world.scores.criteria.ObjectiveCriteria; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Optional; + +@SuppressWarnings("unused") +public class SidebarImpl extends WrappedSidebar { + + public SidebarImpl(@NotNull SidebarLine title, @NotNull Collection lines, Collection placeholderProvider) { + super(title, lines, placeholderProvider); + } + + public ScoreLine createScore(SidebarLine line, int score, String color) { + return new SidebarImpl.ScoreLineImpl(line, score, color); + } + + public SidebarObjective createObjective(String name, ObjectiveCriteria criteria, SidebarLine title, int type) { + return new SidebarObjectiveImpl(name, criteria, title, type); + } + + protected class SidebarObjectiveImpl extends Objective implements SidebarObjective { + + private SidebarLine displayName; + private Component displayNameComp = Component.literal(""); + private final DisplaySlot type; + + public SidebarObjectiveImpl(String name, ObjectiveCriteria criteria, SidebarLine displayName, int type) { + super(null, name, criteria, Component.literal(name), ObjectiveCriteria.RenderType.INTEGER, false, null); + this.displayName = displayName; + this.type = DisplaySlot.values()[type]; + } + + @Override + public void setTitle(SidebarLine title) { + this.displayName = title; + } + + @Override + public SidebarLine getTitle() { + return displayName; + } + + @Override + public void sendCreate(Player player) { + this.sendCreate(((CraftPlayer) player).getHandle().connection); + } + + @Override + public void sendRemove(Player player) { + this.sendRemove(((CraftPlayer) player).getHandle().connection); + } + + @Override + public String getName() { + return super.getName(); + } + + @Override + public boolean refreshTitle() { + var newTitle = displayName.getTrimReplacePlaceholders( + getReceivers().isEmpty() ? null : getReceivers().getFirst(), + 256, + getCompiledPlaceholders() + ); + + if (newTitle.equals(displayNameComp.getString())) { + return false; + } + this.displayNameComp = ComponentUtil.fromColoredString(newTitle); + return true; + } + + @Override + public Component getDisplayName() { + return displayNameComp; + } + + private void sendCreate(@NotNull ServerGamePacketListenerImpl playerConnection) { + var packetPlayOutScoreboardObjective = new ClientboundSetObjectivePacket(this, 0); + playerConnection.send(packetPlayOutScoreboardObjective); + var packetPlayOutScoreboardDisplayObjective = new ClientboundSetDisplayObjectivePacket(type, this); + playerConnection.send(packetPlayOutScoreboardDisplayObjective); + + if (getName().equalsIgnoreCase("health")) { + var packetPlayOutScoreboardDisplayObjective2 = new ClientboundSetDisplayObjectivePacket(DisplaySlot.LIST, this); + playerConnection.send(packetPlayOutScoreboardDisplayObjective2); + } + } + + // must be called when updating the name + public void sendUpdate() { + ClientboundSetObjectivePacket packetPlayOutScoreboardObjective = new ClientboundSetObjectivePacket(this, 2); + getReceivers().forEach(player -> ((CraftPlayer) player).getHandle().connection.send(packetPlayOutScoreboardObjective)); + } + + public void sendRemove(@NotNull ServerGamePacketListenerImpl playerConnection) { + ClientboundSetObjectivePacket packetPlayOutScoreboardObjective = new ClientboundSetObjectivePacket(this, 1); + playerConnection.send(packetPlayOutScoreboardObjective); + } + } + + + public class ScoreLineImpl implements ScoreLine, Comparable { + + private int score; + private Component prefix = Component.literal(" "), suffix = Component.literal(" "); + private final TeamLine team; + private SidebarLine text; + private final String color; + + public ScoreLineImpl(@NotNull SidebarLine text, int score, @NotNull String color) { + this.score = score; + this.text = text; + this.team = new TeamLine(color); + this.color = color; + } + + @Override + public void setScoreAmount(int score) { + this.score = score; + ClientboundSetScorePacket packetPlayOutScoreboardScore = new ClientboundSetScorePacket( + getColor(), + getSidebarObjective().getName(), + score, + Optional.empty(), + Optional.of(new FixedFormat(ComponentUtil.fromColoredString(text.getTrimReplacePlaceholdersScore( + getReceivers().isEmpty() ? null : getReceivers().getFirst(), + null, + getPlaceholders() + )))) + ); + getReceivers().forEach(r -> ((CraftPlayer) r).getHandle().connection.send(packetPlayOutScoreboardScore)); + } + + @Override + public SidebarLine getLine() { + return text; + } + + @Override + public void setLine(SidebarLine line) { + this.text = line; + } + + @Override + public int getScoreAmount() { + return score; + } + + @Override + public void sendCreateToAllReceivers() { + ClientboundSetPlayerTeamPacket packetPlayOutScoreboardTeam = ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, true); + getReceivers().forEach(p -> ((CraftPlayer) p).getHandle().connection.send(packetPlayOutScoreboardTeam)); + ClientboundSetScorePacket packetPlayOutScoreboardScore = new ClientboundSetScorePacket( + this.getColor(), + getSidebarObjective().getName(), + this.getScoreAmount(), + Optional.empty(), + Optional.of(new FixedFormat(ComponentUtil.fromColoredString(text.getTrimReplacePlaceholdersScore( + getReceivers().isEmpty() ? null : getReceivers().getFirst(), + null, + getPlaceholders() + )))) + ); + getReceivers().forEach(p -> ((CraftPlayer) p).getHandle().connection.send(packetPlayOutScoreboardScore)); + } + + @Override + public void sendCreate(Player player) { + ServerGamePacketListenerImpl conn = ((CraftPlayer) player).getHandle().connection; + ClientboundSetPlayerTeamPacket packetPlayOutScoreboardTeam = ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, true); + conn.send(packetPlayOutScoreboardTeam); + + ClientboundSetScorePacket packetPlayOutScoreboardScore = new ClientboundSetScorePacket( + this.getColor(), + getSidebarObjective().getName(), + this.getScoreAmount(), + Optional.empty(), + Optional.of(new FixedFormat(ComponentUtil.fromColoredString(text.getTrimReplacePlaceholdersScore( + getReceivers().isEmpty() ? null : getReceivers().getFirst(), + null, + getPlaceholders() + )))) + ); + conn.send(packetPlayOutScoreboardScore); + } + + @Override + public void sendRemove(Player player) { + ServerGamePacketListenerImpl conn = ((CraftPlayer) player).getHandle().connection; + ClientboundSetPlayerTeamPacket packetPlayOutScoreboardTeam = ClientboundSetPlayerTeamPacket.createRemovePacket(team); + var resetScore = new ClientboundResetScorePacket(getColor(), getSidebarObjective().getName()); + conn.send(resetScore); + conn.send(packetPlayOutScoreboardTeam); + } + + public void sendRemoveToAllReceivers() { + ClientboundSetPlayerTeamPacket packetPlayOutScoreboardTeam = ClientboundSetPlayerTeamPacket.createRemovePacket(team); + var resetScore = new ClientboundResetScorePacket(getColor(), getSidebarObjective().getName()); + getReceivers().forEach(p -> ((CraftPlayer) p).getHandle().connection.send(resetScore)); + getReceivers().forEach(p -> ((CraftPlayer) p).getHandle().connection.send(packetPlayOutScoreboardTeam)); + } + + public void sendUpdate(Player player) { + ClientboundSetPlayerTeamPacket packetTeamUpdate = ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, false); + ((CraftPlayer) player).getHandle().connection.send(packetTeamUpdate); + } + + @Contract(pure = true) + public boolean setContent(@NotNull SidebarLine line) { + var oldPrefix = this.prefix; + var oldSuffix = this.suffix; + String content = line.getTrimReplacePlaceholders( + getReceivers().isEmpty() ? null : getReceivers().getFirst(), + null, + getCompiledPlaceholders() + ); + + if (content.length() > 256) { + this.prefix = ComponentUtil.fromColoredString(content.substring(0, 256)); + int splitPoint = 256; + if (this.prefix.getString().charAt(255) == ChatColor.COLOR_CHAR) { + this.prefix = ComponentUtil.fromColoredString(content.substring(0, 255)); + splitPoint = 255; + } + String lastColor = ComponentUtil.getLastColorCodeUpTo(content, splitPoint); + setSuffix(lastColor + content.substring(splitPoint)); + } else { + this.prefix = ComponentUtil.fromColoredString(content); + this.suffix = Component.literal(""); + } + return !oldPrefix.equals(this.prefix) || !oldSuffix.equals(this.suffix); + } + + public void setSuffix(@NotNull String secondPart) { + if (secondPart.isEmpty()) { + this.suffix = Component.literal(""); + return; + } + this.suffix = ComponentUtil.fromColoredString(secondPart.length() > 256 ? secondPart.substring(0, 256) : secondPart); + } + + public void sendUpdateToAllReceivers() { + ClientboundSetPlayerTeamPacket packetTeamUpdate = ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, false); + getReceivers().forEach(r -> ((CraftPlayer) r).getHandle().connection.send(packetTeamUpdate)); + } + + public int compareTo(@NotNull ScoreLine o) { + return Integer.compare(score, o.getScoreAmount()); + } + + public String getColor() { + return color.charAt(0) == ChatColor.COLOR_CHAR ? color : ChatColor.COLOR_CHAR + color; + } + + @Override + public boolean refreshContent() { + return setContent(getLine()); + } + + private class TeamLine extends PlayerTeam { + + public TeamLine(String color) { + super(null, color); + getPlayers().add(color); + } + + @Override + public @NotNull Component getPlayerPrefix() { + return prefix; + } + + @Override + public @NotNull Component getPlayerSuffix() { + return suffix; + } + + @Override + public @NotNull ChatFormatting getColor() { + return ChatFormatting.WHITE; + } + } + } +}