mirror of
https://github.com/Maxlego08/zKoth.git
synced 2024-11-22 11:55:12 +01:00
📝 Fix fast board
This commit is contained in:
parent
b65b59b3b7
commit
f260e51552
120
dependency-reduced-pom.xml
Normal file
120
dependency-reduced-pom.xml
Normal file
@ -0,0 +1,120 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>fr.maxlego08.koth</groupId>
|
||||
<artifactId>zKoth</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<build>
|
||||
<sourceDirectory>src</sourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>com/cryptomorin/xseries/messages/*</exclude>
|
||||
<exclude>com/cryptomorin/xseries/particles/*</exclude>
|
||||
<exclude>com/cryptomorin/xseries/XBiome*</exclude>
|
||||
<exclude>com/cryptomorin/xseries/NMSExtras*</exclude>
|
||||
<exclude>com/cryptomorin/xseries/NoteBlockMusic*</exclude>
|
||||
<exclude>com/cryptomorin/xseries/SkullCacheListener*</exclude>
|
||||
<exclude>com/cryptomorin/xseries/NoteBlockMusic*</exclude>
|
||||
<exclude>com/cryptomorin/xseries/XTag*</exclude>
|
||||
<exclude>com/cryptomorin/xseries/XPotion*</exclude>
|
||||
<exclude>com/cryptomorin/xseries/XMaterial*</exclude>
|
||||
<exclude>com/cryptomorin/xseries/XItemStack*</exclude>
|
||||
<exclude>com/cryptomorin/xseries/XBlock*</exclude>
|
||||
<exclude>com/cryptomorin/xseries/XEntity*</exclude>
|
||||
<exclude>com/cryptomorin/xseries/XEnchantment*</exclude>
|
||||
<exclude>com/cryptomorin/xseries/SkullUtils*</exclude>
|
||||
<exclude>com/cryptomorin/xseries/ReflectionUtils*</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>fr.mrmicky.fastboard</pattern>
|
||||
<shadedPattern>fr.maxlego08.koth.fastboard</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.cryptomorin.xseries</pattern>
|
||||
<shadedPattern>fr.maxlego08.koth.xseries</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>placeholderapi</id>
|
||||
<url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>minecraft-repo</id>
|
||||
<url>https://libraries.minecraft.net/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>papermc</id>
|
||||
<url>https://repo.papermc.io/repository/maven-public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.Maxlego08</groupId>
|
||||
<artifactId>zTranslator</artifactId>
|
||||
<version>1.0.0.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.folia</groupId>
|
||||
<artifactId>folia-api</artifactId>
|
||||
<version>1.19.4-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>me.clip</groupId>
|
||||
<artifactId>placeholderapi</artifactId>
|
||||
<version>2.11.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mojang</groupId>
|
||||
<artifactId>authlib</artifactId>
|
||||
<version>3.11.50</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<properties>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
</project>
|
23
pom.xml
23
pom.xml
@ -24,6 +24,14 @@
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<filters>
|
||||
<filter>
|
||||
@ -48,6 +56,16 @@
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>fr.mrmicky.fastboard</pattern>
|
||||
<shadedPattern>fr.maxlego08.koth.fastboard</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.cryptomorin.xseries</pattern>
|
||||
<shadedPattern>fr.maxlego08.koth.xseries</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
@ -104,5 +122,10 @@
|
||||
<artifactId>XSeries</artifactId>
|
||||
<version>9.4.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>fr.mrmicky</groupId>
|
||||
<artifactId>fastboard</artifactId>
|
||||
<version>2.0.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@ -1,6 +1,7 @@
|
||||
package fr.maxlego08.koth;
|
||||
|
||||
import fr.maxlego08.koth.api.Koth;
|
||||
import fr.maxlego08.koth.api.KothType;
|
||||
import fr.maxlego08.koth.api.events.KothCreateEvent;
|
||||
import fr.maxlego08.koth.loader.KothLoader;
|
||||
import fr.maxlego08.koth.zcore.enums.Message;
|
||||
@ -93,7 +94,7 @@ public class KothManager extends ZUtils implements Savable {
|
||||
}
|
||||
|
||||
|
||||
public void createKoth(Player player, String name, Location minLocation, Location maxLocation, int captureSeconds) {
|
||||
public void createKoth(Player player, String name, Location minLocation, Location maxLocation, int capture, KothType kothType) {
|
||||
|
||||
Optional<Koth> optional = getKoth(name);
|
||||
if (optional.isPresent()) {
|
||||
@ -108,7 +109,7 @@ public class KothManager extends ZUtils implements Savable {
|
||||
}
|
||||
|
||||
String fileName = name.replace(" ", "_");
|
||||
Koth koth = new ZKoth(fileName, name, captureSeconds, minLocation, maxLocation, new ArrayList<>(), new ArrayList<>());
|
||||
Koth koth = new ZKoth(fileName, kothType, name, capture, minLocation, maxLocation, new ArrayList<>(), new ArrayList<>());
|
||||
|
||||
KothCreateEvent event = new KothCreateEvent(koth);
|
||||
event.call();
|
||||
|
@ -1,6 +1,7 @@
|
||||
package fr.maxlego08.koth;
|
||||
|
||||
import fr.maxlego08.koth.api.Koth;
|
||||
import fr.maxlego08.koth.api.KothType;
|
||||
import fr.maxlego08.koth.zcore.utils.Cuboid;
|
||||
import org.bukkit.Location;
|
||||
|
||||
@ -10,6 +11,7 @@ import java.util.List;
|
||||
public class ZKoth implements Koth {
|
||||
|
||||
private final String fileName;
|
||||
private final KothType kothType;
|
||||
private String name;
|
||||
private int captureSeconds;
|
||||
private Location minLocation;
|
||||
@ -17,8 +19,9 @@ public class ZKoth implements Koth {
|
||||
private List<String> startCommands = new ArrayList<>();
|
||||
private List<String> endCommands = new ArrayList<>();
|
||||
|
||||
public ZKoth(String fileName, String name, int captureSeconds, Location minLocation, Location maxLocation, List<String> startCommands, List<String> endCommands) {
|
||||
public ZKoth(String fileName, KothType kothType, String name, int captureSeconds, Location minLocation, Location maxLocation, List<String> startCommands, List<String> endCommands) {
|
||||
this.fileName = fileName;
|
||||
this.kothType = kothType;
|
||||
this.name = name;
|
||||
this.captureSeconds = captureSeconds;
|
||||
this.minLocation = minLocation;
|
||||
@ -32,11 +35,21 @@ public class ZKoth implements Koth {
|
||||
return this.fileName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KothType getKothType() {
|
||||
return this.kothType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getMinLocation() {
|
||||
return this.minLocation;
|
||||
@ -75,17 +88,12 @@ public class ZKoth implements Koth {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
public int getCaptureSeconds() {
|
||||
return captureSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCaptureSeconds(int captureSeconds) {
|
||||
this.captureSeconds = captureSeconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCaptureSeconds() {
|
||||
return captureSeconds;
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ public interface Koth {
|
||||
|
||||
String getFileName();
|
||||
|
||||
KothType getKothType();
|
||||
|
||||
String getName();
|
||||
|
||||
void setName(String name);
|
||||
|
14
src/fr/maxlego08/koth/api/KothScoreboard.java
Normal file
14
src/fr/maxlego08/koth/api/KothScoreboard.java
Normal file
@ -0,0 +1,14 @@
|
||||
package fr.maxlego08.koth.api;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
|
||||
public interface KothScoreboard {
|
||||
|
||||
void toggle(Player player, Consumer<Player> after);
|
||||
|
||||
void hide(Player player, Consumer<Player> after);
|
||||
|
||||
}
|
8
src/fr/maxlego08/koth/api/KothType.java
Normal file
8
src/fr/maxlego08/koth/api/KothType.java
Normal file
@ -0,0 +1,8 @@
|
||||
package fr.maxlego08.koth.api;
|
||||
|
||||
public enum KothType {
|
||||
|
||||
CAPTURE,
|
||||
SCORE,
|
||||
|
||||
}
|
@ -2,13 +2,16 @@ package fr.maxlego08.koth.command.commands;
|
||||
|
||||
import fr.maxlego08.koth.KothPlugin;
|
||||
import fr.maxlego08.koth.Selection;
|
||||
import fr.maxlego08.koth.api.KothType;
|
||||
import fr.maxlego08.koth.command.VCommand;
|
||||
import fr.maxlego08.koth.zcore.enums.Message;
|
||||
import fr.maxlego08.koth.zcore.enums.Permission;
|
||||
import fr.maxlego08.koth.zcore.utils.commands.CommandType;
|
||||
import org.bukkit.Location;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CommandKothCreate extends VCommand {
|
||||
|
||||
@ -19,6 +22,7 @@ public class CommandKothCreate extends VCommand {
|
||||
this.setDescription(Message.DESCRIPTION_CREATE);
|
||||
this.setConsoleCanUse(false);
|
||||
this.addRequireArg("name");
|
||||
this.addOptionalArg("type", (a, b) -> Arrays.stream(KothType.values()).map(e -> e.name().toLowerCase()).collect(Collectors.toList()));
|
||||
this.addOptionalArg("capture/score");
|
||||
}
|
||||
|
||||
@ -26,7 +30,8 @@ public class CommandKothCreate extends VCommand {
|
||||
protected CommandType perform(KothPlugin plugin) {
|
||||
|
||||
String name = argAsString(0);
|
||||
int captureSeconds = argAsInteger(1, 30);
|
||||
KothType kothType = KothType.valueOf(argAsString(1, KothType.SCORE.name()).toUpperCase());
|
||||
int capture = argAsInteger(2, 30);
|
||||
|
||||
Optional<Selection> optional = this.manager.getSelection(this.player.getUniqueId());
|
||||
|
||||
@ -53,7 +58,7 @@ public class CommandKothCreate extends VCommand {
|
||||
|
||||
Location minLocation = selection.getRightLocation();
|
||||
Location maxLocation = selection.getLeftLocation();
|
||||
this.manager.createKoth(this.player, name, minLocation, maxLocation, captureSeconds);
|
||||
this.manager.createKoth(this.player, name, minLocation, maxLocation, capture, kothType);
|
||||
|
||||
return CommandType.SUCCESS;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package fr.maxlego08.koth.loader;
|
||||
|
||||
import fr.maxlego08.koth.ZKoth;
|
||||
import fr.maxlego08.koth.api.Koth;
|
||||
import fr.maxlego08.koth.api.KothType;
|
||||
import fr.maxlego08.koth.zcore.utils.ZUtils;
|
||||
import fr.maxlego08.koth.zcore.utils.loader.Loader;
|
||||
import org.bukkit.Location;
|
||||
@ -18,6 +19,7 @@ public class KothLoader extends ZUtils implements Loader<Koth> {
|
||||
public Koth load(YamlConfiguration configuration, String path, File file) {
|
||||
|
||||
String fileName = getFileNameWithoutExtension(file);
|
||||
KothType kothType = KothType.valueOf(configuration.getString("type", KothType.CAPTURE.name()).toUpperCase());
|
||||
String name = configuration.getString("name");
|
||||
int captureSeconds = configuration.getInt("capture");
|
||||
List<String> startCommands = configuration.getStringList("startCommands");
|
||||
@ -25,12 +27,13 @@ public class KothLoader extends ZUtils implements Loader<Koth> {
|
||||
Location minLocation = locationLoader.load(configuration, "minLocation.", file);
|
||||
Location manLocation = locationLoader.load(configuration, "maxLocation.", file);
|
||||
|
||||
return new ZKoth(fileName, name, captureSeconds, minLocation, manLocation, startCommands, endCommands);
|
||||
return new ZKoth(fileName, kothType, name, captureSeconds, minLocation, manLocation, startCommands, endCommands);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Koth object, YamlConfiguration configuration, String path) {
|
||||
|
||||
configuration.set("type", object.getKothType().name());
|
||||
configuration.set("name", object.getName());
|
||||
configuration.set("capture", object.getCaptureSeconds());
|
||||
configuration.set("startCommands", object.getStartCommands());
|
||||
|
@ -1,629 +0,0 @@
|
||||
/*
|
||||
* This file is part of FastBoard, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) 2019-2021 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
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fr.maxlego08.koth.scoreboard;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* Lightweight packet-based scoreboard API for Bukkit plugins.
|
||||
* It can be safely used asynchronously as everything is at packet level.
|
||||
* <p>
|
||||
* The project is on <a href="https://github.com/MrMicky-FR/FastBoard">GitHub</a>.
|
||||
*
|
||||
* @author MrMicky
|
||||
* @version 1.2.0
|
||||
*/
|
||||
public class FastBoard {
|
||||
|
||||
private static final Map<Class<?>, Field[]> PACKETS = new HashMap<>(8);
|
||||
private static final String[] COLOR_CODES = Arrays.stream(ChatColor.values())
|
||||
.map(Object::toString)
|
||||
.toArray(String[]::new);
|
||||
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 EMPTY_MESSAGE;
|
||||
private static final Object RESET_FORMATTING;
|
||||
private static final MethodHandle MESSAGE_FROM_STRING;
|
||||
private static final MethodHandle PLAYER_CONNECTION;
|
||||
private static final MethodHandle SEND_PACKET;
|
||||
private static final MethodHandle PLAYER_GET_HANDLE;
|
||||
// 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_SCORE;
|
||||
private static final FastReflection.PacketConstructor PACKET_SB_TEAM;
|
||||
private static final FastReflection.PacketConstructor PACKET_SB_SERIALIZABLE_TEAM;
|
||||
// Scoreboard enums
|
||||
private static final Class<?> ENUM_SB_HEALTH_DISPLAY;
|
||||
private static final Class<?> ENUM_SB_ACTION;
|
||||
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;
|
||||
|
||||
static {
|
||||
try {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
|
||||
if (FastReflection.isRepackaged()) {
|
||||
VERSION_TYPE = VersionType.V1_17;
|
||||
} else if (FastReflection.nmsOptionalClass(null, "ScoreboardServer$Action").isPresent()) {
|
||||
VERSION_TYPE = VersionType.V1_13;
|
||||
} else if (FastReflection.nmsOptionalClass(null, "IScoreboardCriteria$EnumScoreboardHealthDisplay").isPresent()) {
|
||||
VERSION_TYPE = VersionType.V1_8;
|
||||
} else {
|
||||
VERSION_TYPE = VersionType.V1_7;
|
||||
}
|
||||
|
||||
String gameProtocolPackage = "network.protocol.game";
|
||||
Class<?> craftPlayerClass = FastReflection.obcClass("entity.CraftPlayer");
|
||||
Class<?> craftChatMessageClass = FastReflection.obcClass("util.CraftChatMessage");
|
||||
Class<?> entityPlayerClass = FastReflection.nmsClass("server.level", "EntityPlayer");
|
||||
Class<?> playerConnectionClass = FastReflection.nmsClass("server.network", "PlayerConnection");
|
||||
Class<?> packetClass = FastReflection.nmsClass("network.protocol", "Packet");
|
||||
Class<?> packetSbObjClass = FastReflection.nmsClass(gameProtocolPackage, "PacketPlayOutScoreboardObjective");
|
||||
Class<?> packetSbDisplayObjClass = FastReflection.nmsClass(gameProtocolPackage, "PacketPlayOutScoreboardDisplayObjective");
|
||||
Class<?> packetSbScoreClass = FastReflection.nmsClass(gameProtocolPackage, "PacketPlayOutScoreboardScore");
|
||||
Class<?> packetSbTeamClass = FastReflection.nmsClass(gameProtocolPackage, "PacketPlayOutScoreboardTeam");
|
||||
Class<?> sbTeamClass = VersionType.V1_17.isHigherOrEqual()
|
||||
? FastReflection.innerClass(packetSbTeamClass, innerClass -> !innerClass.isEnum()) : null;
|
||||
Field playerConnectionField = Arrays.stream(entityPlayerClass.getFields())
|
||||
.filter(field -> field.getType().isAssignableFrom(playerConnectionClass))
|
||||
.findFirst().orElseThrow(NoSuchFieldException::new);
|
||||
|
||||
MESSAGE_FROM_STRING = lookup.unreflect(craftChatMessageClass.getMethod("fromString", String.class));
|
||||
CHAT_COMPONENT_CLASS = FastReflection.nmsClass("network.chat", "IChatBaseComponent");
|
||||
CHAT_FORMAT_ENUM = FastReflection.nmsClass(null, "EnumChatFormat");
|
||||
EMPTY_MESSAGE = Array.get(MESSAGE_FROM_STRING.invoke(""), 0);
|
||||
RESET_FORMATTING = FastReflection.enumValueOf(CHAT_FORMAT_ENUM, "RESET", 21);
|
||||
PLAYER_GET_HANDLE = lookup.findVirtual(craftPlayerClass, "getHandle", MethodType.methodType(entityPlayerClass));
|
||||
PLAYER_CONNECTION = lookup.unreflectGetter(playerConnectionField);
|
||||
SEND_PACKET = lookup.findVirtual(playerConnectionClass, "sendPacket", MethodType.methodType(void.class, packetClass));
|
||||
PACKET_SB_OBJ = FastReflection.findPacketConstructor(packetSbObjClass, lookup);
|
||||
PACKET_SB_DISPLAY_OBJ = FastReflection.findPacketConstructor(packetSbDisplayObjClass, lookup);
|
||||
PACKET_SB_SCORE = FastReflection.findPacketConstructor(packetSbScoreClass, lookup);
|
||||
PACKET_SB_TEAM = FastReflection.findPacketConstructor(packetSbTeamClass, lookup);
|
||||
PACKET_SB_SERIALIZABLE_TEAM = sbTeamClass == null ? null : FastReflection.findPacketConstructor(sbTeamClass, lookup);
|
||||
|
||||
for (Class<?> clazz : Arrays.asList(packetSbObjClass, packetSbDisplayObjClass, packetSbScoreClass, packetSbTeamClass, sbTeamClass)) {
|
||||
if (clazz == null) {
|
||||
continue;
|
||||
}
|
||||
Field[] fields = Arrays.stream(clazz.getDeclaredFields())
|
||||
.filter(field -> !Modifier.isStatic(field.getModifiers()))
|
||||
.toArray(Field[]::new);
|
||||
for (Field field : fields) {
|
||||
field.setAccessible(true);
|
||||
}
|
||||
PACKETS.put(clazz, fields);
|
||||
}
|
||||
|
||||
if (VersionType.V1_8.isHigherOrEqual()) {
|
||||
String enumSbActionClass = VersionType.V1_13.isHigherOrEqual()
|
||||
? "ScoreboardServer$Action"
|
||||
: "PacketPlayOutScoreboardScore$EnumScoreboardAction";
|
||||
ENUM_SB_HEALTH_DISPLAY = FastReflection.nmsClass("world.scores.criteria", "IScoreboardCriteria$EnumScoreboardHealthDisplay");
|
||||
ENUM_SB_ACTION = FastReflection.nmsClass("server", enumSbActionClass);
|
||||
ENUM_SB_HEALTH_DISPLAY_INTEGER = FastReflection.enumValueOf(ENUM_SB_HEALTH_DISPLAY, "INTEGER", 0);
|
||||
ENUM_SB_ACTION_CHANGE = FastReflection.enumValueOf(ENUM_SB_ACTION, "CHANGE", 0);
|
||||
ENUM_SB_ACTION_REMOVE = FastReflection.enumValueOf(ENUM_SB_ACTION, "REMOVE", 1);
|
||||
} else {
|
||||
ENUM_SB_HEALTH_DISPLAY = null;
|
||||
ENUM_SB_ACTION = null;
|
||||
ENUM_SB_HEALTH_DISPLAY_INTEGER = null;
|
||||
ENUM_SB_ACTION_CHANGE = null;
|
||||
ENUM_SB_ACTION_REMOVE = null;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
throw new ExceptionInInitializerError(t);
|
||||
}
|
||||
}
|
||||
|
||||
private final Player player;
|
||||
private final String id;
|
||||
|
||||
private final List<String> lines = new ArrayList<>();
|
||||
private String title = ChatColor.RESET.toString();
|
||||
|
||||
private boolean deleted = false;
|
||||
|
||||
/**
|
||||
* Creates a new FastBoard.
|
||||
*
|
||||
* @param player the owner of the scoreboard
|
||||
*/
|
||||
public FastBoard(Player player) {
|
||||
this.player = Objects.requireNonNull(player, "player");
|
||||
this.id = "fb-" + Integer.toHexString(ThreadLocalRandom.current().nextInt());
|
||||
|
||||
try {
|
||||
sendObjectivePacket(ObjectiveMode.CREATE);
|
||||
sendDisplayObjectivePacket();
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException("Unable to create scoreboard", t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the scoreboard title.
|
||||
*
|
||||
* @return the scoreboard title
|
||||
*/
|
||||
public String getTitle() {
|
||||
return this.title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update 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(String title) {
|
||||
if (this.title.equals(Objects.requireNonNull(title, "title"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!VersionType.V1_13.isHigherOrEqual() && title.length() > 32) {
|
||||
throw new IllegalArgumentException("Title is longer than 32 chars");
|
||||
}
|
||||
|
||||
this.title = title;
|
||||
|
||||
try {
|
||||
sendObjectivePacket(ObjectiveMode.UPDATE);
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException("Unable to update scoreboard title", t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the scoreboard lines.
|
||||
*
|
||||
* @return the scoreboard lines
|
||||
*/
|
||||
public List<String> getLines() {
|
||||
return new ArrayList<>(this.lines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the specified scoreboard line.
|
||||
*
|
||||
* @param line the line number
|
||||
* @return the line
|
||||
* @throws IndexOutOfBoundsException if the line is higher than {@code size}
|
||||
*/
|
||||
public String getLine(int line) {
|
||||
checkLineNumber(line, true, false);
|
||||
|
||||
return this.lines.get(line);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update 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}
|
||||
*/
|
||||
public synchronized void updateLine(int line, String text) {
|
||||
checkLineNumber(line, false, true);
|
||||
|
||||
try {
|
||||
if (line < size()) {
|
||||
this.lines.set(line, text);
|
||||
|
||||
sendTeamPacket(getScoreByLine(line), TeamMode.UPDATE);
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> newLines = new ArrayList<>(this.lines);
|
||||
|
||||
if (line > size()) {
|
||||
for (int i = size(); i < line; i++) {
|
||||
newLines.add("");
|
||||
}
|
||||
}
|
||||
|
||||
newLines.add(text);
|
||||
|
||||
updateLines(newLines);
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException("Unable to update scoreboard lines", t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a scoreboard line.
|
||||
*
|
||||
* @param line the line number
|
||||
*/
|
||||
public synchronized void removeLine(int line) {
|
||||
checkLineNumber(line, false, false);
|
||||
|
||||
if (line >= size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> newLines = new ArrayList<>(this.lines);
|
||||
newLines.remove(line);
|
||||
updateLines(newLines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update 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
|
||||
*/
|
||||
public void updateLines(String... lines) {
|
||||
updateLines(Arrays.asList(lines));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update 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
|
||||
*/
|
||||
public synchronized void updateLines(Collection<String> lines) {
|
||||
Objects.requireNonNull(lines, "lines");
|
||||
checkLineNumber(lines.size(), false, true);
|
||||
|
||||
if (!VersionType.V1_13.isHigherOrEqual()) {
|
||||
int lineCount = 0;
|
||||
for (String s : lines) {
|
||||
if (s != null && s.length() > 30) {
|
||||
throw new IllegalArgumentException("Line " + lineCount + " is longer than 30 chars");
|
||||
}
|
||||
lineCount++;
|
||||
}
|
||||
}
|
||||
|
||||
List<String> oldLines = new ArrayList<>(this.lines);
|
||||
this.lines.clear();
|
||||
this.lines.addAll(lines);
|
||||
|
||||
int linesSize = this.lines.size();
|
||||
|
||||
try {
|
||||
if (oldLines.size() != linesSize) {
|
||||
List<String> oldLinesCopy = new ArrayList<>(oldLines);
|
||||
|
||||
if (oldLines.size() > linesSize) {
|
||||
for (int i = oldLinesCopy.size(); i > linesSize; i--) {
|
||||
sendTeamPacket(i - 1, TeamMode.REMOVE);
|
||||
sendScorePacket(i - 1, ScoreboardAction.REMOVE);
|
||||
|
||||
oldLines.remove(0);
|
||||
}
|
||||
} else {
|
||||
for (int i = oldLinesCopy.size(); i < linesSize; i++) {
|
||||
sendScorePacket(i, ScoreboardAction.CHANGE);
|
||||
sendTeamPacket(i, TeamMode.CREATE);
|
||||
|
||||
oldLines.add(oldLines.size() - i, getLineByScore(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < linesSize; i++) {
|
||||
if (!Objects.equals(getLineByScore(oldLines, i), getLineByScore(i))) {
|
||||
sendTeamPacket(i, TeamMode.UPDATE);
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException("Unable to update scoreboard lines", t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the player who has the scoreboard.
|
||||
*
|
||||
* @return current player for this FastBoard
|
||||
*/
|
||||
public Player getPlayer() {
|
||||
return this.player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the scoreboard id.
|
||||
*
|
||||
* @return the id
|
||||
*/
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if the scoreboard is deleted.
|
||||
*
|
||||
* @return true if the scoreboard is deleted
|
||||
*/
|
||||
public boolean isDeleted() {
|
||||
return this.deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the scoreboard size (the number of lines).
|
||||
*
|
||||
* @return the size
|
||||
*/
|
||||
public 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 throws an {@link IllegalStateException}
|
||||
*
|
||||
* @throws IllegalStateException if this was already call before
|
||||
*/
|
||||
public void delete() {
|
||||
try {
|
||||
for (int i = 0; i < this.lines.size(); i++) {
|
||||
sendTeamPacket(i, TeamMode.REMOVE);
|
||||
}
|
||||
|
||||
sendObjectivePacket(ObjectiveMode.REMOVE);
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException("Unable to delete scoreboard", t);
|
||||
}
|
||||
|
||||
this.deleted = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the player has a prefix/suffix characters limit.
|
||||
* By default, it returns true only in 1.12 or lower.
|
||||
* This method can be overridden to fix compatibility with some versions support plugin.
|
||||
*
|
||||
* @return max length
|
||||
*/
|
||||
protected boolean hasLinesMaxLength() {
|
||||
return !VersionType.V1_13.isHigherOrEqual();
|
||||
}
|
||||
|
||||
private void checkLineNumber(int line, boolean checkInRange, boolean checkMax) {
|
||||
if (line < 0) {
|
||||
throw new IllegalArgumentException("Line number must be positive");
|
||||
}
|
||||
|
||||
if (checkInRange && line >= this.lines.size()) {
|
||||
throw new IllegalArgumentException("Line number must be under " + this.lines.size());
|
||||
}
|
||||
|
||||
if (checkMax && line >= COLOR_CODES.length - 1) {
|
||||
throw new IllegalArgumentException("Line number is too high: " + line);
|
||||
}
|
||||
}
|
||||
|
||||
private int getScoreByLine(int line) {
|
||||
return this.lines.size() - line - 1;
|
||||
}
|
||||
|
||||
private String getLineByScore(int score) {
|
||||
return getLineByScore(this.lines, score);
|
||||
}
|
||||
|
||||
private String getLineByScore(List<String> lines, int score) {
|
||||
return lines.get(lines.size() - score - 1);
|
||||
}
|
||||
|
||||
private 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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
private void sendDisplayObjectivePacket() throws Throwable {
|
||||
Object packet = PACKET_SB_DISPLAY_OBJ.invoke();
|
||||
|
||||
setField(packet, int.class, 1); // Position (1: sidebar)
|
||||
setField(packet, String.class, this.id); // Score Name
|
||||
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
private void sendScorePacket(int score, ScoreboardAction action) throws Throwable {
|
||||
Object packet = PACKET_SB_SCORE.invoke();
|
||||
|
||||
setField(packet, String.class, COLOR_CODES[score], 0); // Player Name
|
||||
|
||||
if (VersionType.V1_8.isHigherOrEqual()) {
|
||||
setField(packet, ENUM_SB_ACTION, action == ScoreboardAction.REMOVE ? ENUM_SB_ACTION_REMOVE : ENUM_SB_ACTION_CHANGE);
|
||||
} else {
|
||||
setField(packet, int.class, action.ordinal(), 1); // Action
|
||||
}
|
||||
|
||||
if (action == ScoreboardAction.CHANGE) {
|
||||
setField(packet, String.class, this.id, 1); // Objective Name
|
||||
setField(packet, int.class, score); // Score
|
||||
}
|
||||
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
private void sendTeamPacket(int score, TeamMode mode) throws Throwable {
|
||||
if (mode == TeamMode.ADD_PLAYERS || mode == TeamMode.REMOVE_PLAYERS) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
int maxLength = hasLinesMaxLength() ? 16 : 1024;
|
||||
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
|
||||
|
||||
if (mode == TeamMode.CREATE || mode == TeamMode.UPDATE) {
|
||||
String line = getLineByScore(score);
|
||||
String prefix;
|
||||
String suffix = null;
|
||||
|
||||
if (line == null || line.isEmpty()) {
|
||||
prefix = COLOR_CODES[score] + ChatColor.RESET;
|
||||
} else if (line.length() <= maxLength) {
|
||||
prefix = line;
|
||||
} else {
|
||||
// Prevent splitting color codes
|
||||
int index = line.charAt(maxLength - 1) == ChatColor.COLOR_CHAR ? (maxLength - 1) : maxLength;
|
||||
prefix = line.substring(0, index);
|
||||
String suffixTmp = line.substring(index);
|
||||
ChatColor chatColor = null;
|
||||
|
||||
if (suffixTmp.length() >= 2 && suffixTmp.charAt(0) == ChatColor.COLOR_CHAR) {
|
||||
chatColor = ChatColor.getByChar(suffixTmp.charAt(1));
|
||||
}
|
||||
|
||||
String color = ChatColor.getLastColors(prefix);
|
||||
boolean addColor = chatColor == null || chatColor.isFormat();
|
||||
|
||||
suffix = (addColor ? (color.isEmpty() ? ChatColor.RESET.toString() : color) : "") + suffixTmp;
|
||||
}
|
||||
|
||||
if (prefix.length() > maxLength || (suffix != null && suffix.length() > maxLength)) {
|
||||
// Something went wrong, just cut to prevent client crash/kick
|
||||
prefix = prefix.substring(0, maxLength);
|
||||
suffix = (suffix != null) ? suffix.substring(0, maxLength) : null;
|
||||
}
|
||||
|
||||
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, "", 0); // Display name
|
||||
setField(team, CHAT_FORMAT_ENUM, RESET_FORMATTING); // Color
|
||||
setComponentField(team, prefix, 1); // Prefix
|
||||
setComponentField(team, suffix == null ? "" : suffix, 2); // Suffix
|
||||
setField(team, String.class, "always", 0); // Visibility
|
||||
setField(team, String.class, "always", 1); // Collisions
|
||||
setField(packet, Optional.class, Optional.of(team));
|
||||
} else {
|
||||
setComponentField(packet, prefix, 2); // Prefix
|
||||
setComponentField(packet, suffix == null ? "" : 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
|
||||
}
|
||||
}
|
||||
|
||||
sendPacket(packet);
|
||||
}
|
||||
|
||||
private void sendPacket(Object packet) throws Throwable {
|
||||
if (this.deleted) {
|
||||
throw new IllegalStateException("This FastBoard is deleted");
|
||||
}
|
||||
|
||||
if (this.player.isOnline()) {
|
||||
Object entityPlayer = PLAYER_GET_HANDLE.invoke(this.player);
|
||||
Object playerConnection = PLAYER_CONNECTION.invoke(entityPlayer);
|
||||
SEND_PACKET.invoke(playerConnection, packet);
|
||||
}
|
||||
}
|
||||
|
||||
private void setField(Object object, Class<?> fieldType, Object value) throws ReflectiveOperationException {
|
||||
setField(object, fieldType, value, 0);
|
||||
}
|
||||
|
||||
private void setField(Object packet, Class<?> fieldType, Object value, int count) throws ReflectiveOperationException {
|
||||
int i = 0;
|
||||
for (Field field : PACKETS.get(packet.getClass())) {
|
||||
if (field.getType() == fieldType && count == i++) {
|
||||
field.set(packet, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setComponentField(Object packet, String value, int count) throws Throwable {
|
||||
if (!VersionType.V1_13.isHigherOrEqual()) {
|
||||
setField(packet, String.class, value, count);
|
||||
return;
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (Field field : PACKETS.get(packet.getClass())) {
|
||||
if ((field.getType() == String.class || field.getType() == CHAT_COMPONENT_CLASS) && count == i++) {
|
||||
field.set(packet, value.isEmpty() ? EMPTY_MESSAGE : Array.get(MESSAGE_FROM_STRING.invoke(value), 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ObjectiveMode {
|
||||
CREATE, REMOVE, UPDATE
|
||||
}
|
||||
|
||||
enum TeamMode {
|
||||
CREATE, REMOVE, UPDATE, ADD_PLAYERS, REMOVE_PLAYERS
|
||||
}
|
||||
|
||||
enum ScoreboardAction {
|
||||
CHANGE, REMOVE
|
||||
}
|
||||
|
||||
enum VersionType {
|
||||
V1_7, V1_8, V1_13, V1_17;
|
||||
|
||||
public boolean isHigherOrEqual() {
|
||||
return VERSION_TYPE.ordinal() >= ordinal();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
/*
|
||||
* This file is part of FastBoard, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) 2019-2021 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
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package fr.maxlego08.koth.scoreboard;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Small reflection utility class to use CraftBukkit and NMS.
|
||||
*
|
||||
* @author MrMicky
|
||||
*/
|
||||
public final class FastReflection {
|
||||
|
||||
private static final String NM_PACKAGE = "net.minecraft";
|
||||
public static final String OBC_PACKAGE = "org.bukkit.craftbukkit";
|
||||
public static final String NMS_PACKAGE = NM_PACKAGE + ".server";
|
||||
|
||||
public static final String VERSION = Bukkit.getServer().getClass().getPackage().getName().substring(OBC_PACKAGE.length() + 1);
|
||||
|
||||
private static final MethodType VOID_METHOD_TYPE = MethodType.methodType(void.class);
|
||||
private static final boolean NMS_REPACKAGED = optionalClass(NM_PACKAGE + ".network.protocol.Packet").isPresent();
|
||||
|
||||
private static volatile Object theUnsafe;
|
||||
|
||||
private FastReflection() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public static boolean isRepackaged() {
|
||||
return NMS_REPACKAGED;
|
||||
}
|
||||
|
||||
public static String nmsClassName(String post1_17package, String className) {
|
||||
if (NMS_REPACKAGED) {
|
||||
String classPackage = post1_17package == null ? NM_PACKAGE : NM_PACKAGE + '.' + post1_17package;
|
||||
return classPackage + '.' + className;
|
||||
}
|
||||
return NMS_PACKAGE + '.' + VERSION + '.' + className;
|
||||
}
|
||||
|
||||
public static Class<?> nmsClass(String post1_17package, String className) throws ClassNotFoundException {
|
||||
return Class.forName(nmsClassName(post1_17package, className));
|
||||
}
|
||||
|
||||
public static Optional<Class<?>> nmsOptionalClass(String post1_17package, String className) {
|
||||
return optionalClass(nmsClassName(post1_17package, className));
|
||||
}
|
||||
|
||||
public static String obcClassName(String className) {
|
||||
return OBC_PACKAGE + '.' + VERSION + '.' + className;
|
||||
}
|
||||
|
||||
public static Class<?> obcClass(String className) throws ClassNotFoundException {
|
||||
return Class.forName(obcClassName(className));
|
||||
}
|
||||
|
||||
public static Optional<Class<?>> obcOptionalClass(String className) {
|
||||
return optionalClass(obcClassName(className));
|
||||
}
|
||||
|
||||
public static Optional<Class<?>> optionalClass(String className) {
|
||||
try {
|
||||
return Optional.of(Class.forName(className));
|
||||
} catch (ClassNotFoundException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public static Object enumValueOf(Class<?> enumClass, String enumName) {
|
||||
return Enum.valueOf(enumClass.asSubclass(Enum.class), enumName);
|
||||
}
|
||||
|
||||
public static Object enumValueOf(Class<?> enumClass, String enumName, int fallbackOrdinal) {
|
||||
try {
|
||||
return enumValueOf(enumClass, enumName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Object[] constants = enumClass.getEnumConstants();
|
||||
if (constants.length > fallbackOrdinal) {
|
||||
return constants[fallbackOrdinal];
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
static Class<?> innerClass(Class<?> parentClass, Predicate<Class<?>> classPredicate) throws ClassNotFoundException {
|
||||
for (Class<?> innerClass : parentClass.getDeclaredClasses()) {
|
||||
if (classPredicate.test(innerClass)) {
|
||||
return innerClass;
|
||||
}
|
||||
}
|
||||
throw new ClassNotFoundException("No class in " + parentClass.getCanonicalName() + " matches the predicate.");
|
||||
}
|
||||
|
||||
public static PacketConstructor findPacketConstructor(Class<?> packetClass, MethodHandles.Lookup lookup) throws Exception {
|
||||
try {
|
||||
MethodHandle constructor = lookup.findConstructor(packetClass, VOID_METHOD_TYPE);
|
||||
return constructor::invoke;
|
||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||
// try below with Unsafe
|
||||
}
|
||||
|
||||
if (theUnsafe == null) {
|
||||
synchronized (FastReflection.class) {
|
||||
if (theUnsafe == null) {
|
||||
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
|
||||
Field theUnsafeField = unsafeClass.getDeclaredField("theUnsafe");
|
||||
theUnsafeField.setAccessible(true);
|
||||
theUnsafe = theUnsafeField.get(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MethodType allocateMethodType = MethodType.methodType(Object.class, Class.class);
|
||||
MethodHandle allocateMethod = lookup.findVirtual(theUnsafe.getClass(), "allocateInstance", allocateMethodType);
|
||||
return () -> allocateMethod.invoke(theUnsafe, packetClass);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface PacketConstructor {
|
||||
Object invoke() throws Throwable;
|
||||
}
|
||||
}
|
@ -1,230 +1,220 @@
|
||||
package fr.maxlego08.koth.scoreboard;
|
||||
|
||||
import fr.maxlego08.koth.zcore.utils.ZUtils;
|
||||
import fr.maxlego08.koth.zcore.utils.interfaces.CollectionConsumer;
|
||||
import fr.mrmicky.fastboard.FastBoard;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import fr.maxlego08.koth.zcore.utils.ZUtils;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import fr.maxlego08.koth.zcore.utils.interfaces.CollectionConsumer;
|
||||
|
||||
public class ScoreBoardManager extends ZUtils {
|
||||
|
||||
private final Plugin plugin;
|
||||
private final Map<Player, FastBoard> boards = new HashMap<Player, FastBoard>();
|
||||
private final long schedulerMillisecond;
|
||||
private boolean isRunning = false;
|
||||
private CollectionConsumer<Player> lines;
|
||||
private final Plugin plugin;
|
||||
private final Map<Player, FastBoard> boards = new HashMap<Player, FastBoard>();
|
||||
private final long schedulerMillisecond;
|
||||
private boolean isRunning = false;
|
||||
private CollectionConsumer<Player> lines;
|
||||
|
||||
public ScoreBoardManager(Plugin plugin, long schedulerMillisecond) {
|
||||
super();
|
||||
this.schedulerMillisecond = schedulerMillisecond;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
public ScoreBoardManager(Plugin plugin, long schedulerMillisecond) {
|
||||
super();
|
||||
this.schedulerMillisecond = schedulerMillisecond;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start scheduler
|
||||
*/
|
||||
public void schedule() {
|
||||
/**
|
||||
* Start scheduler
|
||||
*/
|
||||
public void schedule() {
|
||||
|
||||
if (this.isRunning) {
|
||||
return;
|
||||
}
|
||||
if (this.isRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isRunning = true;
|
||||
this.isRunning = true;
|
||||
|
||||
scheduleFix(this.plugin, this.schedulerMillisecond, (task, canRun) -> {
|
||||
scheduleFix(this.plugin, this.schedulerMillisecond, (task, canRun) -> {
|
||||
|
||||
// If the task cannot continue then we do not update the scoreboard
|
||||
if (!canRun)
|
||||
return;
|
||||
// If the task cannot continue then we do not update the scoreboard
|
||||
if (!canRun)
|
||||
return;
|
||||
|
||||
if (!this.isRunning) {
|
||||
task.cancel();
|
||||
return;
|
||||
}
|
||||
if (!this.isRunning) {
|
||||
task.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
// if the addition of the lines is null then we stop the task
|
||||
if (this.lines == null) {
|
||||
task.cancel();
|
||||
return;
|
||||
}
|
||||
// if the addition of the lines is null then we stop the task
|
||||
if (this.lines == null) {
|
||||
task.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
Iterator<FastBoard> iterator = this.boards.values().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
FastBoard b = iterator.next();
|
||||
if (b.isDeleted() || !b.getPlayer().isOnline()) {
|
||||
this.boards.remove(b.getPlayer());
|
||||
}
|
||||
}
|
||||
Iterator<FastBoard> iterator = this.boards.values().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
FastBoard b = iterator.next();
|
||||
if (b.isDeleted() || !b.getPlayer().isOnline()) {
|
||||
this.boards.remove(b.getPlayer());
|
||||
}
|
||||
}
|
||||
|
||||
this.boards.forEach((player, board) -> board.updateLines(this.lines.accept(player)));
|
||||
this.boards.forEach((player, board) -> board.updateLines(this.lines.accept(player)));
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a scoreboard for a player
|
||||
*
|
||||
* @param player
|
||||
* @param title
|
||||
* @return {@link FastBoard}
|
||||
*/
|
||||
public FastBoard createBoard(Player player, String title) {
|
||||
/**
|
||||
* Create a scoreboard for a player
|
||||
*
|
||||
* @param player
|
||||
* @param title
|
||||
* @return {@link FastBoard}
|
||||
*/
|
||||
public FastBoard createBoard(Player player, String title) {
|
||||
|
||||
if (this.hasBoard(player)) {
|
||||
return this.getBoard(player);
|
||||
}
|
||||
if (this.hasBoard(player)) {
|
||||
return this.getBoard(player);
|
||||
}
|
||||
|
||||
FastBoard board = new FastBoard(player);
|
||||
board.updateTitle(title);
|
||||
FastBoard board = new FastBoard(player);
|
||||
board.updateTitle(title);
|
||||
|
||||
if (this.lines != null) {
|
||||
board.updateLines(this.lines.accept(player));
|
||||
}
|
||||
if (this.lines != null) {
|
||||
board.updateLines(this.lines.accept(player));
|
||||
}
|
||||
|
||||
this.boards.put(player, board);
|
||||
this.boards.put(player, board);
|
||||
|
||||
return board;
|
||||
return board;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete player board
|
||||
*
|
||||
* @param player
|
||||
* @return
|
||||
*/
|
||||
public boolean delete(Player player) {
|
||||
/**
|
||||
* Delete player board
|
||||
*
|
||||
* @param player
|
||||
* @return
|
||||
*/
|
||||
public boolean delete(Player player) {
|
||||
|
||||
if (!this.hasBoard(player)) {
|
||||
return false;
|
||||
}
|
||||
if (!this.hasBoard(player)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FastBoard board = getBoard(player);
|
||||
if (!board.isDeleted()) {
|
||||
board.delete();
|
||||
return true;
|
||||
}
|
||||
FastBoard board = getBoard(player);
|
||||
if (!board.isDeleted()) {
|
||||
board.delete();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update board title
|
||||
*
|
||||
* @param player
|
||||
* @param title
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean updateTitle(Player player, String title) {
|
||||
/**
|
||||
* Update board title
|
||||
*
|
||||
* @param player
|
||||
* @param title
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean updateTitle(Player player, String title) {
|
||||
|
||||
if (!hasBoard(player)) {
|
||||
return false;
|
||||
}
|
||||
if (!hasBoard(player)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FastBoard board = getBoard(player);
|
||||
if (!board.isDeleted()) {
|
||||
board.updateTitle(title);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
FastBoard board = getBoard(player);
|
||||
if (!board.isDeleted()) {
|
||||
board.updateTitle(title);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update board line
|
||||
*
|
||||
* @param player
|
||||
* @param title
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean updateLine(Player player, int line, String string) {
|
||||
public boolean updateLine(Player player, int line, String string) {
|
||||
|
||||
if (!hasBoard(player)) {
|
||||
return false;
|
||||
}
|
||||
if (!hasBoard(player)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FastBoard board = getBoard(player);
|
||||
if (!board.isDeleted()) {
|
||||
board.updateLine(line, string);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
FastBoard board = getBoard(player);
|
||||
if (!board.isDeleted()) {
|
||||
board.updateLine(line, string);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if player has board
|
||||
*
|
||||
* @param player
|
||||
* @return {@link Boolean}
|
||||
*/
|
||||
public boolean hasBoard(Player player) {
|
||||
return this.boards.containsKey(player);
|
||||
}
|
||||
/**
|
||||
* Check if player has board
|
||||
*
|
||||
* @param player
|
||||
* @return {@link Boolean}
|
||||
*/
|
||||
public boolean hasBoard(Player player) {
|
||||
return this.boards.containsKey(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return player's board
|
||||
*
|
||||
* @param player
|
||||
* @return {@link FastBoard}
|
||||
*/
|
||||
public FastBoard getBoard(Player player) {
|
||||
return this.boards.getOrDefault(player, null);
|
||||
}
|
||||
/**
|
||||
* Return player's board
|
||||
*
|
||||
* @param player
|
||||
* @return {@link FastBoard}
|
||||
*/
|
||||
public FastBoard getBoard(Player player) {
|
||||
return this.boards.getOrDefault(player, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the boards
|
||||
*/
|
||||
public Map<Player, FastBoard> getBoards() {
|
||||
return this.boards;
|
||||
}
|
||||
/**
|
||||
* @return the boards
|
||||
*/
|
||||
public Map<Player, FastBoard> getBoards() {
|
||||
return this.boards;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the schedulerMillisecond
|
||||
*/
|
||||
public long getSchedulerMillisecond() {
|
||||
return this.schedulerMillisecond;
|
||||
}
|
||||
/**
|
||||
* @return the schedulerMillisecond
|
||||
*/
|
||||
public long getSchedulerMillisecond() {
|
||||
return this.schedulerMillisecond;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the isRunning
|
||||
*/
|
||||
public boolean isRunning() {
|
||||
return this.isRunning;
|
||||
}
|
||||
/**
|
||||
* @return the isRunning
|
||||
*/
|
||||
public boolean isRunning() {
|
||||
return this.isRunning;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the lines
|
||||
*/
|
||||
public CollectionConsumer<Player> getLines() {
|
||||
return this.lines;
|
||||
}
|
||||
/**
|
||||
* @param isRunning the isRunning to set
|
||||
*/
|
||||
public void setRunning(boolean isRunning) {
|
||||
this.isRunning = isRunning;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isRunning
|
||||
* the isRunning to set
|
||||
*/
|
||||
public void setRunning(boolean isRunning) {
|
||||
this.isRunning = isRunning;
|
||||
}
|
||||
/**
|
||||
* @return the lines
|
||||
*/
|
||||
public CollectionConsumer<Player> getLines() {
|
||||
return this.lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lines
|
||||
* the lines to set
|
||||
*/
|
||||
public void setLines(CollectionConsumer<Player> lines) {
|
||||
this.lines = lines;
|
||||
}
|
||||
/**
|
||||
* @param lines the lines to set
|
||||
*/
|
||||
public void setLines(CollectionConsumer<Player> lines) {
|
||||
this.lines = lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lines
|
||||
* the lines to set
|
||||
*/
|
||||
public void setLinesAndSchedule(CollectionConsumer<Player> lines) {
|
||||
this.lines = lines;
|
||||
this.schedule();
|
||||
}
|
||||
/**
|
||||
* @param lines the lines to set
|
||||
*/
|
||||
public void setLinesAndSchedule(CollectionConsumer<Player> lines) {
|
||||
this.lines = lines;
|
||||
this.schedule();
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user