Initial commit 🚀

This commit is contained in:
2020-12-27 15:39:07 +01:00
commit a83f6e42c9
33 changed files with 3307 additions and 0 deletions

View File

@@ -0,0 +1,80 @@
package fr.arthurdanjou.template;
import fr.arthurdanjou.template.game.Game;
import fr.arthurdanjou.template.team.TeamManager;
import fr.arthurdanjou.template.config.settings.Settings;
import fr.arthurdanjou.template.listeners.PlayerListeners;
import fr.arthurdanjou.template.listeners.ServerListeners;
import fr.arthurdanjou.template.listeners.WorldListeners;
import fr.arthurdanjou.template.config.FileManager;
import fr.arthurdanjou.template.config.ConfigFiles;
import fr.arthurdanjou.template.game.GameState;
import fr.arthurdanjou.template.users.UserManager;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.Difficulty;
import org.bukkit.GameRule;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
@Getter
public class Main extends JavaPlugin {
@Getter
private static Main instance;
private Game game;
private FileManager fileManager;
private TeamManager teamManager;
private UserManager userManager;
private Settings settings;
@Override
public void onEnable() {
instance = this;
this.getLogger().info("+------[ PROJET ]------+");
this.getLogger().info("Initialisation en cours...");
this.fileManager = new FileManager(this, getClassLoader());
this.teamManager = new TeamManager();
this.userManager = new UserManager();
this.fileManager.saveResourceAs(ConfigFiles.CONFIG.getFileName(), ConfigFiles.CONFIG.getFileName());
this.settings = this.fileManager.getSettings();
this.game = new Game(this);
this.registerCommands();
this.registerListeners();
GameState.setState(GameState.LOBBY);
Bukkit.getWorlds().forEach(world -> {
world.setDifficulty(Difficulty.PEACEFUL);
world.setPVP(false);
world.setTime(6000L);
world.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false);
world.setGameRule(GameRule.MOB_GRIEFING, false);
world.setGameRule(GameRule.DO_WEATHER_CYCLE, false);
});
this.getLogger().info("+----------------------+");
}
@Override
public void onDisable() {
super.onDisable();
}
private void registerListeners() {
PluginManager pluginManager = Bukkit.getPluginManager();
pluginManager.registerEvents(new PlayerListeners(this), this);
pluginManager.registerEvents(new WorldListeners(this), this);
pluginManager.registerEvents(new ServerListeners(this), this);
}
private void registerCommands() {
}
}

View File

@@ -0,0 +1,42 @@
package fr.arthurdanjou.template.config;
import fr.arthurdanjou.template.Main;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.io.IOException;
@AllArgsConstructor @Getter
public enum ConfigFiles {
CONFIG("config.yml"),
LANG("lang.yml"),
;
private final String fileName;
public File getDataFolder() {
return Main.getInstance().getDataFolder();
}
public File getFile() {
return new File(getDataFolder(), getFileName());
}
public FileConfiguration getConfiguration() {
return YamlConfiguration.loadConfiguration(getFile());
}
public void save(FileConfiguration configuration) {
try {
configuration.save(getFile());
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,77 @@
package fr.arthurdanjou.template.config;
import fr.arthurdanjou.template.Main;
import fr.arthurdanjou.template.config.settings.Settings;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.CustomClassLoaderConstructor;
import org.yaml.snakeyaml.introspector.BeanAccess;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@AllArgsConstructor
@Getter
public class FileManager {
private final Main main;
private final ClassLoader classLoader;
public void saveResourceAs(String resource, String output) {
if (resource == null || resource.isEmpty()) {
throw new IllegalArgumentException("La ressource ne peut pas etre nulle !");
}
InputStream inputStream = main.getResource(resource);
if (inputStream == null) {
throw new IllegalArgumentException("La ressource " + resource + " n'est pas presente dans le dossier !");
}
if (!main.getDataFolder().exists() && !main.getDataFolder().mkdir()) {
main.getLogger().severe("Permission insuffisante pour creer le dossier");
}
File outputFile = new File(main.getDataFolder(), output);
try {
if (!outputFile.exists()) {
main.getLogger().info("La ressource est introuvable, creation en cours !");
OutputStream outputStream = new FileOutputStream(outputFile);
byte[] buf = new byte[1024];
int n;
while ((n = inputStream.read(buf)) >= 0) {
outputStream.write(buf, 0, n);
}
outputStream.close();
inputStream.close();
if (!outputFile.exists()) {
main.getLogger().severe("Impossible de creer le fichier !");
}
}
} catch (IOException exception) {
exception.printStackTrace();
}
}
public Settings getSettings() {
Settings settings = null;
try (Reader reader = Files.newBufferedReader(ConfigFiles.CONFIG.getFile().toPath(), StandardCharsets.UTF_8)) {
Yaml yaml = new Yaml(new CustomClassLoaderConstructor(this.getClassLoader()));
yaml.setBeanAccess(BeanAccess.FIELD);
settings = yaml.loadAs(reader, Settings.class);
main.getLogger().info("Le fichier de configuration a ete lu correctement !");
} catch (IOException e) {
e.printStackTrace();
}
return settings;
}
}

View File

@@ -0,0 +1,53 @@
package fr.arthurdanjou.template.config.lang;
import fr.arthurdanjou.template.Main;
import fr.arthurdanjou.template.config.ConfigFiles;
import lombok.Getter;
import org.bukkit.ChatColor;
import org.bukkit.configuration.file.FileConfiguration;
import java.util.HashMap;
import java.util.Map;
public enum Lang {
PREFIX,
MOTD_LOBBY,
MOTD_GAME,
GAME_START_COUNTDOWN,
GAME_STOP_COUNTDOWN,
;
@Getter
private static final Map<Lang, String> VALUES = new HashMap<>();
static {
for (Lang lang : values()) {
VALUES.put(lang, lang.getFromFile());
}
Main.getInstance().getLogger().info("Lecture du fichier de langues !");
}
public String get() {
return VALUES.get(this);
}
public static String getPrefix() {
return PREFIX.get() + ChatColor.RESET + " ";
}
private String getFromFile() {
FileConfiguration configuration = ConfigFiles.LANG.getConfiguration();
String key = name().toLowerCase().replace('_', '-');
String value = configuration.getString(key);
if (value == null) {
value = "";
}
return ChatColor.translateAlternateColorCodes('&', value);
}
}

View File

@@ -0,0 +1,19 @@
package fr.arthurdanjou.template.config.lang;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public enum PlaceHolders {
PLAYER("player"),
SLOTS("slots"),
COUNTDOWN("countdown"),
;
private final String placeholder;
public String getPlaceholder() {
return "{" + placeholder + "}";
}
}

View File

@@ -0,0 +1,20 @@
package fr.arthurdanjou.template.config.settings;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Bukkit;
import org.bukkit.Location;
@AllArgsConstructor
@Getter @Setter
public class LocationConfig {
public final String world;
public final double x, y, z;
public final float yaw, pitch;
public Location toBukkitLocation() {
return new Location(Bukkit.getWorld(world), x, y, z, yaw, pitch);
}
}

View File

@@ -0,0 +1,19 @@
package fr.arthurdanjou.template.config.settings;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Location;
@Getter @Setter
public class Settings {
public int slots;
public boolean spectator;
public LocationConfig spawnLocation;
public int minimumPlayers;
public Location getSpawnLocation() {
return spawnLocation.toBukkitLocation();
}
}

View File

@@ -0,0 +1,51 @@
package fr.arthurdanjou.template.game;
import fr.arthurdanjou.template.Main;
import lombok.Getter;
import org.bukkit.Bukkit;
@Getter
public class Game {
private final Main main;
private StartRunnable startRunnable;
private GameRunnable gameRunnable;
public Game(Main main) {
this.main = main;
this.startRunnable = new StartRunnable(this);
this.gameRunnable = new GameRunnable();
}
public void startCountDown() {
this.startRunnable.runTaskTimer(main, 0, 20);
this.startRunnable.setStarting(true);
}
public void stopCountDown() {
if (this.startRunnable.getCountdown() > 0) {
this.startRunnable.cancel();
this.startRunnable = new StartRunnable(this);
}
}
public void startGame() {
}
public void checkWin() {
}
public void end() {
}
public void checkStart() {
if (main.getTeamManager().areAllInTeam() && main.getSettings().getMinimumPlayers() <= Bukkit.getOnlinePlayers().size()) {
this.startCountDown();
}
}
}

View File

@@ -0,0 +1,11 @@
package fr.arthurdanjou.template.game;
import org.bukkit.scheduler.BukkitRunnable;
public class GameRunnable extends BukkitRunnable {
@Override
public void run() {
}
}

View File

@@ -0,0 +1,29 @@
package fr.arthurdanjou.template.game;
import lombok.Getter;
import lombok.Setter;
import java.util.Arrays;
public enum GameState {
LOBBY,
GAME,
END,
;
public boolean isGame() {
return this == GAME;
}
public boolean isNotGame() {
return this == LOBBY || this == END;
}
@Getter @Setter
private static GameState state;
public static boolean isState(GameState... states) {
return Arrays.stream(states).anyMatch(gameState -> gameState == state);
}
}

View File

@@ -0,0 +1,45 @@
package fr.arthurdanjou.template.game;
import fr.arthurdanjou.template.config.lang.Lang;
import fr.arthurdanjou.template.config.lang.PlaceHolders;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Bukkit;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
public class StartRunnable extends BukkitRunnable {
private final Game game;
@Getter @Setter
private boolean starting;
@Getter @Setter
private int countdown;
public StartRunnable(Game game) {
this.game = game;
this.starting = false;
this.countdown = 30;
}
@Override
public void run() {
if (this.countdown == 30 || this.countdown == 20 || (this.countdown <= 5 && this.countdown >= 1)) {
Bukkit.broadcastMessage(
Lang.getPrefix() +
Lang.GAME_START_COUNTDOWN.get()
.replace(PlaceHolders.COUNTDOWN.getPlaceholder(), Integer.toString(this.countdown))
);
for (Player player : Bukkit.getOnlinePlayers()) {
player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_HARP, 1f, 1f);
}
}
if (this.countdown == 0) {
cancel();
}
this.countdown--;
}
}

View File

@@ -0,0 +1,79 @@
package fr.arthurdanjou.template.listeners;
import fr.arthurdanjou.template.Main;
import fr.arthurdanjou.template.game.GameState;
import fr.arthurdanjou.template.team.TeamUnit;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.bukkit.GameMode;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityPickupItemEvent;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerPickupArrowEvent;
import org.bukkit.event.player.PlayerQuitEvent;
@AllArgsConstructor @Getter
public class PlayerListeners implements Listener {
private final Main main;
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
if (GameState.isState(GameState.LOBBY)) {
preparePlayer(player, false);
main.getTeamManager().joinTeam(player, TeamUnit.NONE);
main.getUserManager().onLogin(player);
main.getGame().checkStart();
} else {
preparePlayer(player, true);
player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BELL, 1f, 1f);
main.getTeamManager().joinTeam(player, TeamUnit.NONE);
}
}
private void preparePlayer(Player player, boolean started) {
player.setFoodLevel(20);
player.setHealth(20);
player.setGameMode(started ? GameMode.SPECTATOR : GameMode.ADVENTURE);
player.getInventory().clear();
player.setExp(0);
player.setLevel(0);
player.getActivePotionEffects().forEach(potionEffect -> player.removePotionEffect(potionEffect.getType()));
player.teleport(main.getSettings().getSpawnLocation());
player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BELL, 1f, 1f);
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
main.getTeamManager().onLogout(player);
main.getUserManager().delete(player);
}
@EventHandler
public void onPlayerPickupArrow(PlayerPickupArrowEvent event) {
event.setCancelled(!GameState.isState(GameState.GAME));
}
@EventHandler
public void onEntityPickupItem(EntityPickupItemEvent event) {
event.setCancelled(!GameState.isState(GameState.GAME));
}
@EventHandler
public void onPlayerDropItem(PlayerDropItemEvent event) {
event.setCancelled(!GameState.isState(GameState.GAME));
}
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
event.setCancelled(GameState.isState(GameState.GAME));
}
}

View File

@@ -0,0 +1,27 @@
package fr.arthurdanjou.template.listeners;
import fr.arthurdanjou.template.Main;
import fr.arthurdanjou.template.game.GameState;
import fr.arthurdanjou.template.config.lang.Lang;
import lombok.AllArgsConstructor;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.server.ServerListPingEvent;
@AllArgsConstructor
public class ServerListeners implements Listener {
private final Main main;
@EventHandler
public void onServerListPing(ServerListPingEvent event) {
event.setMaxPlayers(main.getSettings().getSlots());
if (GameState.isState(GameState.GAME)) {
event.setMotd(Lang.MOTD_GAME.get());
} else {
event.setMotd(Lang.MOTD_LOBBY.get());
}
}
}

View File

@@ -0,0 +1,54 @@
package fr.arthurdanjou.template.listeners;
import fr.arthurdanjou.template.Main;
import fr.arthurdanjou.template.game.GameState;
import lombok.AllArgsConstructor;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockExplodeEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.entity.FoodLevelChangeEvent;
import org.bukkit.event.weather.WeatherChangeEvent;
@AllArgsConstructor
public class WorldListeners implements Listener {
private final Main main;
@EventHandler
public void onWeatherChange(WeatherChangeEvent event) {
if (event.toWeatherState()) {
event.getWorld().setStorm(false);
event.getWorld().setThundering(false);
event.getWorld().setWeatherDuration(0);
}
}
@EventHandler
public void onFoodLevelChange(FoodLevelChangeEvent event) {
event.setCancelled(!GameState.isState(GameState.GAME));
}
@EventHandler
public void onBlockPlace(BlockPlaceEvent event) {
event.setCancelled(!GameState.isState(GameState.GAME));
}
@EventHandler
public void onBlockBreak(BlockBreakEvent event) {
event.setCancelled(!GameState.isState(GameState.GAME));
}
@EventHandler
public void onEntityExplode(EntityExplodeEvent event) {
event.setCancelled(!GameState.isState(GameState.GAME));
}
@EventHandler
public void onBlockExplode(BlockExplodeEvent event) {
event.setCancelled(!GameState.isState(GameState.GAME));
}
}

View File

@@ -0,0 +1,77 @@
package fr.arthurdanjou.template.team;
import fr.arthurdanjou.template.utils.team.Team;
import fr.arthurdanjou.template.utils.team.TeamHandler;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.UUID;
public class TeamManager {
private final TeamHandler teamHandler;
//todo set teams here
public TeamManager() {
this.teamHandler = new TeamHandler();
for (TeamUnit team : TeamUnit.values()) {
if (this.teamHandler.getTeam(team) != null) {
continue;
}
TeamHandler.VTeam vTeam = this.teamHandler.createNewTeam(team.getName(), team.getColoredName());
vTeam.setRealName(team.getName());
vTeam.setPrefix(team.getColoredName());
this.teamHandler.addTeam(vTeam);
}
}
public void onLogout(Player player) {
this.teamHandler.removeReceiver(player);
}
public void joinTeam(Player player, TeamUnit teamUnit) {
this.leaveTeam(player);
Team team = getTeam(teamUnit);
if (team != null) {
team.getPlayers().add(player.getUniqueId());
}
this.teamHandler.addReceiver(player);
this.teamHandler.addPlayerToTeam(player, this.teamHandler.getTeam(teamUnit));
}
public void leaveTeam(Player player) {
this.leaveTeam(player.getUniqueId());
}
public void leaveTeam(UUID uuid) {
}
public Team getTeam(TeamUnit teamUnit) {
//todo if teamUnit == TEAM alors on retourne
return null;
}
public Team getPlayerTeam(Player player) {
return this.getPlayerTeam(player.getUniqueId());
}
public Team getPlayerTeam(UUID uuid) {
//todo if teams.getPlayers.contains(uuid) alors on retourne
return null;
}
public boolean areAllInTeam() {
int count = 0;
for (TeamHandler.VTeam team : this.teamHandler.getTeams()) {
count += team.getPlayers().size();
}
return count == Bukkit.getOnlinePlayers().size();
}
}

View File

@@ -0,0 +1,29 @@
package fr.arthurdanjou.template.team;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.bukkit.ChatColor;
@AllArgsConstructor
@Getter
public enum TeamUnit {
//todo set teams info here
NONE("none", "&f", "&f"),
;
private final String name, prefix, color;
public String getColor() {
return ChatColor.translateAlternateColorCodes('&', color);
}
public String getPrefix() {
return ChatColor.translateAlternateColorCodes('&', prefix);
}
public String getColoredName() {
return getColor() + getName();
}
}

View File

@@ -0,0 +1,34 @@
package fr.arthurdanjou.template.users;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.entity.Player;
import java.util.UUID;
@Getter @Setter
public class User {
private final UUID uuid;
private final String name;
private final boolean alive;
private int kills;
private int deaths;
public User(Player player) {
this.uuid = player.getUniqueId();
this.name = player.getName();
this.alive = true;
this.kills = 0;
this.deaths = 0;
}
public void addKill() {
this.kills++;
}
public void addDeath() {
this.deaths++;
}
}

View File

@@ -0,0 +1,45 @@
package fr.arthurdanjou.template.users;
import lombok.Getter;
import org.bukkit.entity.Player;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
@Getter
public class UserManager {
private final Set<User> users = new HashSet<>();
public void onLogin(Player player) {
users.add(new User(player));
}
public Optional<User> getUser(Player player) {
return getUser(player.getUniqueId());
}
public Optional<User> getUser(UUID uuid) {
return this.users.stream().filter(user -> user.getUuid().equals(uuid)).findFirst();
}
public Set<User> getAliveUsers() {
return this.users.stream().filter(User::isAlive).collect(Collectors.toSet());
}
public Set<User> getSpectators() {
return this.users.stream().filter(user -> !user.isAlive()).collect(Collectors.toSet());
}
public void delete(Player player) {
this.delete(player.getUniqueId());
}
public void delete(UUID uuid) {
this.getUser(uuid).ifPresent(users::remove);
}
}

View File

@@ -0,0 +1,188 @@
package fr.arthurdanjou.template.utils;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.SkullMeta;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
public class ItemBuilder {
private ItemStack is;
/**
* Create a new ItemBuilder from scratch.
* @param m The material to create the ItemBuilder with.
*/
public ItemBuilder(Material m) {
this(m, 1);
}
/**
* Create a new ItemBuilder over an existing itemstack.
* @param is The itemstack to create the ItemBuilder over.
*/
public ItemBuilder(ItemStack is) {
this.is = is;
}
/**
* Create a new ItemBuilder from scratch.
* @param m The material of the item.
* @param amount The amount of the item.
*/
public ItemBuilder(Material m, int amount) {
is = new ItemStack(m, amount);
}
/**
* Clone the ItemBuilder into a new one.
* @return The cloned instance.
*/
@Override
public ItemBuilder clone() {
return new ItemBuilder(is);
}
/**
* Set the displayname of the item.
* @param name The name to change it to.
*/
public ItemBuilder setName(String name) {
ItemMeta im = is.getItemMeta();
assert im != null;
im.setDisplayName(name);
is.setItemMeta(im);
return this;
}
/**
* Set the skull owner for the item. Works on skulls only.
* @param owner The name of the skull's owner.
*/
public ItemBuilder setSkullOwner(final OfflinePlayer owner) {
try {
SkullMeta im = (SkullMeta) is.getItemMeta();
assert im != null;
im.setOwningPlayer(owner);
is.setItemMeta(im);
} catch (ClassCastException ignored) {}
return this;
}
/**
* Sets infinity durability on the item by setting the durability to
* Short.MAX_VALUE.
*/
public ItemBuilder setInfinityDurability() {
ItemMeta im = is.getItemMeta();
assert im != null;
im.setUnbreakable(true);
is.setItemMeta(im);
return this;
}
/**
* Re-sets the lore.
* @param lore The lore to set it to.
*/
public ItemBuilder setLore(String... lore) {
ItemMeta im = is.getItemMeta();
assert im != null;
im.setLore(Arrays.asList(lore));
is.setItemMeta(im);
return this;
}
public ItemBuilder addNBTTag(String id, String value) {
ItemStack nmsIs = CraftItemStack.asNMSCopy(is);
NBTTagCompound nmsCompound = (nmsIs.hasTag()) ? nmsIs.getTag() : new NBTTagCompound();
assert nmsCompound != null;
nmsCompound.set(id, NBTTagString.a(value));
nmsIs.setTag(nmsCompound);
is = CraftItemStack.asBukkitCopy(nmsIs);
return this;
}
/**
* Remove a lore line.
* @param line The lore to remove.
*/
public ItemBuilder removeLoreLine(String line) {
ItemMeta im = is.getItemMeta();
assert im != null;
List<String> lore = new ArrayList<>(Objects.requireNonNull(im.getLore()));
if(!lore.contains(line))
return this;
lore.remove(line);
im.setLore(lore);
is.setItemMeta(im);
return this;
}
/**
* Remove a lore line.
* @param index The index of the lore line to remove.
*/
public ItemBuilder removeLoreLine(int index) {
ItemMeta im = is.getItemMeta();
assert im != null;
List<String> lore = new ArrayList<>(Objects.requireNonNull(im.getLore()));
if(index < 0 || index > lore.size())
return this;
lore.remove(index);
im.setLore(lore);
is.setItemMeta(im);
return this;
}
/**
* Add a lore line.
* @param line The lore line to add.
*/
public ItemBuilder addLoreLine(String line) {
ItemMeta im = is.getItemMeta();
List<String> lore = new ArrayList<>();
assert im != null;
if(im.hasLore())
lore = new ArrayList<>(Objects.requireNonNull(im.getLore()));
lore.add(line);
im.setLore(lore);
is.setItemMeta(im);
return this;
}
/**
* Add a lore line.
* @param line The lore line to add.
* @param pos The index of where to put it.
*/
public ItemBuilder addLoreLine(String line, int pos) {
ItemMeta im = is.getItemMeta();
assert im != null;
List<String> lore = new ArrayList<>(Objects.requireNonNull(im.getLore()));
lore.set(pos, line);
im.setLore(lore);
is.setItemMeta(im);
return this;
}
public void clearLoreLines() {
ItemMeta im = is.getItemMeta();
assert im != null;
im.setLore(new ArrayList<>());
is.setItemMeta(im);
}
/**
* Retrieves the itemstack from the ItemBuilder.
* @return The itemstack created/modified by the ItemBuilder instance.
*/
public ItemStack toItemStack() { return is; }
}

View File

@@ -0,0 +1,549 @@
package fr.arthurdanjou.template.utils;
import fr.arthurdanjou.template.utils.reflection.ScoreBoardReflection;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
public class ScoreBoard {
private static final VersionType VERSION_TYPE;
// Packets sending
private static final Field PLAYER_CONNECTION;
private static final Method SEND_PACKET;
private static final Method PLAYER_GET_HANDLE;
// Chat components
private static final Class<?> CHAT_COMPONENT_CLASS;
private static final Method MESSAGE_FROM_STRING;
// Scoreboard packets
private static final Constructor<?> PACKET_SB_OBJ;
private static final Constructor<?> PACKET_SB_DISPLAY_OBJ;
private static final Constructor<?> PACKET_SB_SCORE;
private static final Constructor<?> PACKET_SB_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 {
if (ScoreBoardReflection.nmsOptionalClass("ScoreboardServer$Action").isPresent()) {
VERSION_TYPE = VersionType.V1_13;
} else if (ScoreBoardReflection.nmsOptionalClass("IScoreboardCriteria$EnumScoreboardHealthDisplay").isPresent()) {
VERSION_TYPE = VersionType.V1_8;
} else {
VERSION_TYPE = VersionType.V1_7;
}
Class<?> craftChatMessageClass = ScoreBoardReflection.obcClass("util.CraftChatMessage");
Class<?> entityPlayerClass = ScoreBoardReflection.nmsClass("EntityPlayer");
Class<?> playerConnectionClass = ScoreBoardReflection.nmsClass("PlayerConnection");
Class<?> craftPlayerClass = ScoreBoardReflection.obcClass("entity.CraftPlayer");
MESSAGE_FROM_STRING = craftChatMessageClass.getDeclaredMethod("fromString", String.class);
CHAT_COMPONENT_CLASS = ScoreBoardReflection.nmsClass("IChatBaseComponent");
PLAYER_GET_HANDLE = craftPlayerClass.getDeclaredMethod("getHandle");
PLAYER_CONNECTION = entityPlayerClass.getDeclaredField("playerConnection");
SEND_PACKET = playerConnectionClass.getDeclaredMethod("sendPacket", ScoreBoardReflection.nmsClass("Packet"));
PACKET_SB_OBJ = ScoreBoardReflection.nmsClass("PacketPlayOutScoreboardObjective").getConstructor();
PACKET_SB_DISPLAY_OBJ = ScoreBoardReflection.nmsClass("PacketPlayOutScoreboardDisplayObjective").getConstructor();
PACKET_SB_SCORE = ScoreBoardReflection.nmsClass("PacketPlayOutScoreboardScore").getConstructor();
PACKET_SB_TEAM = ScoreBoardReflection.nmsClass("PacketPlayOutScoreboardTeam").getConstructor();
if (VersionType.V1_8.isHigherOrEqual()) {
ENUM_SB_HEALTH_DISPLAY = ScoreBoardReflection.nmsClass("IScoreboardCriteria$EnumScoreboardHealthDisplay");
if (VersionType.V1_13.isHigherOrEqual()) {
ENUM_SB_ACTION = ScoreBoardReflection.nmsClass("ScoreboardServer$Action");
} else {
ENUM_SB_ACTION = ScoreBoardReflection.nmsClass("PacketPlayOutScoreboardScore$EnumScoreboardAction");
}
ENUM_SB_HEALTH_DISPLAY_INTEGER = ScoreBoardReflection.enumValueOf(ENUM_SB_HEALTH_DISPLAY, "INTEGER");
ENUM_SB_ACTION_CHANGE = ScoreBoardReflection.enumValueOf(ENUM_SB_ACTION, "CHANGE");
ENUM_SB_ACTION_REMOVE = ScoreBoardReflection.enumValueOf(ENUM_SB_ACTION, "REMOVE");
} 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 (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}
private final Player player;
private final String id;
private String title = ChatColor.RESET.toString();
private final List<String> lines = new ArrayList<>();
private boolean deleted = false;
/**
* Creates a new FastBoard.
*
* @param player the player the scoreboard is for
*/
public ScoreBoard(Player player) {
this.player = Objects.requireNonNull(player, "player");
id = "fb-" + Double.toString(Math.random()).substring(2, 10);
try {
sendObjectivePacket(ObjectiveMode.CREATE);
sendDisplayObjectivePacket();
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
/**
* Get the scoreboard title.
*
* @return the scoreboard title
*/
public String getTitle() {
return 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 (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
/**
* Get the scoreboard lines.
*
* @return the scoreboard lines
*/
public List<String> getLines() {
return new ArrayList<>(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);
return 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 {@code size} + 1
*/
public void updateLine(int line, String text) {
checkLineNumber(line, false);
try {
if (line < size()) {
lines.set(line, text);
sendTeamPacket(getScoreByLine(line), TeamMode.UPDATE);
return;
}
List<String> newLines = new ArrayList<>(lines);
if (line > size()) {
for (int i = size(); i < line; i++) {
newLines.add("");
}
}
newLines.add(text);
updateLines(newLines);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
/**
* Remove a scoreboard line.
*
* @param line the line number
*/
public void removeLine(int line) {
checkLineNumber(line, false);
if (line >= size()) {
return; // The line don't exists
}
List<String> lines = new ArrayList<>(this.lines);
lines.remove(line);
updateLines(lines);
}
/**
* 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 void updateLines(Collection<String> lines) {
Objects.requireNonNull(lines, "lines");
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 (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
/**
* Get the player who has the scoreboard.
*
* @return current player for this FastBoard
*/
public Player getPlayer() {
return player;
}
/**
* Get the scoreboard id.
*
* @return the id
*/
public String getId() {
return id;
}
/**
* Get if the scoreboard is deleted.
*
* @return true if the scoreboard is deleted
*/
public boolean isDeleted() {
return deleted;
}
/**
* Get the scoreboard size (the number of lines).
*
* @return the size
*/
public int size() {
return 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 < lines.size(); i++) {
sendTeamPacket(i, TeamMode.REMOVE);
}
sendObjectivePacket(ObjectiveMode.REMOVE);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
deleted = true;
}
private void checkLineNumber(int line, boolean checkMax) {
if (line < 0) {
throw new IllegalArgumentException("Line number must be positive");
}
if (checkMax && line >= lines.size()) {
throw new IllegalArgumentException("Line number must be under " + lines.size());
}
}
private int getScoreByLine(int line) {
return lines.size() - line - 1;
}
private String getLineByScore(int score) {
return getLineByScore(lines, score);
}
private String getLineByScore(List<String> lines, int score) {
return lines.get(lines.size() - score - 1);
}
private void sendObjectivePacket(ObjectiveMode mode) throws ReflectiveOperationException {
Object packet = PACKET_SB_OBJ.newInstance();
setField(packet, String.class, id);
setField(packet, int.class, mode.ordinal());
if (mode != ObjectiveMode.REMOVE) {
setComponentField(packet, 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 ReflectiveOperationException {
Object packet = PACKET_SB_DISPLAY_OBJ.newInstance();
setField(packet, int.class, 1);
setField(packet, String.class, id);
sendPacket(packet);
}
private void sendScorePacket(int score, ScoreboardAction action) throws ReflectiveOperationException {
Object packet = PACKET_SB_SCORE.newInstance();
setField(packet, String.class, getColorCode(score), 0);
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);
}
if (action == ScoreboardAction.CHANGE) {
setField(packet, String.class, id, 1);
setField(packet, int.class, score);
}
sendPacket(packet);
}
private void sendTeamPacket(int score, TeamMode mode) throws ReflectiveOperationException {
if (mode == TeamMode.ADD_PLAYERS || mode == TeamMode.REMOVE_PLAYERS) {
throw new UnsupportedOperationException();
}
Object packet = PACKET_SB_TEAM.newInstance();
setField(packet, String.class, 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 = getColorCode(score) + ChatColor.RESET;
} else if (line.length() <= 16 || VersionType.V1_13.isHigherOrEqual()) {
prefix = line;
} else {
// Prevent splitting color codes
int index = line.charAt(15) == ChatColor.COLOR_CHAR ? 15 : 16;
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 (VERSION_TYPE != VersionType.V1_13) {
if (prefix.length() > 16 || (suffix != null && suffix.length() > 16)) {
// Something went wrong, just cut to prevent client crash/kick
prefix = prefix.substring(0, 16);
suffix = (suffix != null) ? suffix.substring(0, 16) : null;
}
}
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(getColorCode(score))); // Players in the team
}
}
sendPacket(packet);
}
private String getColorCode(int score) {
return ChatColor.values()[score].toString();
}
private void sendPacket(Object packet) throws ReflectiveOperationException {
if (deleted) {
throw new IllegalStateException("This FastBoard is deleted");
}
if (player.isOnline()) {
Object entityPlayer = PLAYER_GET_HANDLE.invoke(player);
Object playerConnection = PLAYER_CONNECTION.get(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 object, Class<?> fieldType, Object value, int count) throws ReflectiveOperationException {
int i = 0;
for (Field f : object.getClass().getDeclaredFields()) {
if (f.getType() == fieldType && i++ == count) {
f.setAccessible(true);
f.set(object, value);
}
}
}
private void setComponentField(Object object, String value, int count) throws ReflectiveOperationException {
if (VERSION_TYPE != VersionType.V1_13) {
setField(object, String.class, value, count);
return;
}
int i = 0;
for (Field f : object.getClass().getDeclaredFields()) {
if ((f.getType() == String.class || f.getType() == CHAT_COMPONENT_CLASS) && i++ == count) {
f.setAccessible(true);
f.set(object, Array.get(MESSAGE_FROM_STRING.invoke(null, 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;
public boolean isHigherOrEqual() {
return VERSION_TYPE.ordinal() >= ordinal();
}
}
}

View File

@@ -0,0 +1,965 @@
package fr.arthurdanjou.template.utils.reflection;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class Reflection {
public static void playSound(Player player, Location location, String soundName, float volume, float pitch) {
try {
Class<?> soundClass = getClass("org.bukkit.Sound");
Sound sound = (Sound) soundClass.getField(soundName).get(null);
Method playSoundMethod = getMethod(player.getClass(), "playSound", Location.class, Sound.class, float.class, float.class);
playSoundMethod.invoke(player, location, sound, volume, pitch);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | NoSuchFieldException e) {
e.printStackTrace();
}
}
public static Class<?> getNMSClass(String className) {
try {
return PackageType.MINECRAFT_SERVER.getClass(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
public static Class<?> getOBCClass(String className) {
try {
return PackageType.CRAFTBUKKIT.getClass(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
public static void sendPacket(Player player, Object packet) {
try {
Class<?> packetClass = getNMSClass("Packet");
Class<?> entityPlayerClass = getNMSClass("EntityPlayer");
Field playerConnectionField = getField(entityPlayerClass, "playerConnection");
Method sendPacketMethod = getMethod(playerConnectionField.getType(), "sendPacket", packetClass);
Object entityPlayer = getHandle(player);
Object playerConnection = playerConnectionField.get(entityPlayer);
sendPacketMethod.invoke(playerConnection, packet);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
public static Method makeMethod(Class<?> clazz, String methodName, Class<?>... paramaters) {
try {
return clazz.getDeclaredMethod(methodName, paramaters);
} catch (NoSuchMethodException ex) {
return null;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
@SuppressWarnings("unchecked")
public static <T> T callMethod(Method method, Object instance, Object... paramaters) {
if (method == null) throw new RuntimeException("No such method");
method.setAccessible(true);
try {
return (T) method.invoke(instance, paramaters);
} catch (InvocationTargetException ex) {
throw new RuntimeException(ex.getCause());
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
@SuppressWarnings("unchecked")
public static <T> Constructor<T> makeConstructor(Class<?> clazz, Class<?>... paramaterTypes) {
try {
return (Constructor<T>) clazz.getConstructor(paramaterTypes);
} catch (NoSuchMethodException ex) {
return null;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public static <T> T callConstructor(Constructor<T> constructor, Object... paramaters) {
if (constructor == null) throw new RuntimeException("No such constructor");
constructor.setAccessible(true);
try {
return (T) constructor.newInstance(paramaters);
} catch (InvocationTargetException ex) {
throw new RuntimeException(ex.getCause());
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public static Field makeField(Class<?> clazz, String name) {
try {
return clazz.getDeclaredField(name);
} catch (NoSuchFieldException ex) {
return null;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
@SuppressWarnings("unchecked")
public static <T> T getField(Field field, Object instance) {
if (field == null) throw new RuntimeException("No such field");
field.setAccessible(true);
try {
return (T) field.get(instance);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public static void setField(Field field, Object instance, Object value) {
if (field == null) throw new RuntimeException("No such field");
field.setAccessible(true);
try {
field.set(instance, value);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public static Class<?> getClass(String name) {
try {
return Class.forName(name);
} catch (ClassNotFoundException ex) {
return null;
}
}
public static <T> Class<? extends T> getClass(String name, Class<T> superClass) {
try {
return Class.forName(name).asSubclass(superClass);
} catch (ClassCastException | ClassNotFoundException ex) {
return null;
}
}
public static Object getHandle(Object obj) {
try {
return getMethod(obj.getClass(), "getHandle").invoke(obj);
} catch (Exception e) {
System.out.println("Reflection failed for getHandle Entity.");
e.printStackTrace();
return null;
}
}
public static Field getField(Class<?> clazz, String name) {
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static boolean compareClassList(Class<?>[] l1, Class<?>[] l2) {
boolean equal = true;
if (l1.length != l2.length)
return false;
for (int i = 0; i < l1.length; i++)
if (!Objects.equals(l1[i], l2[i])) {
equal = false;
break;
}
return equal;
}
/**
* Returns the constructor of a given class with the given parameter types
*
* @param clazz Target class
* @param parameterTypes Parameter types of the desired constructor
* @return The constructor of the target class with the specified parameter types
* @throws NoSuchMethodException If the desired constructor with the specified parameter types cannot be found
* @see DataType
* @see DataType#getPrimitive(Class[])
* @see DataType#compare(Class[], Class[])
*/
public static Constructor<?> getConstructor(Class<?> clazz, Class<?>... parameterTypes) throws NoSuchMethodException {
Class<?>[] primitiveTypes = DataType.getPrimitive(parameterTypes);
for (Constructor<?> constructor : clazz.getConstructors()) {
if (!DataType.compare(DataType.getPrimitive(constructor.getParameterTypes()), primitiveTypes)) {
continue;
}
return constructor;
}
throw new NoSuchMethodException("There is no such constructor in this class with the specified parameter types");
}
/**
* Returns the constructor of a desired class with the given parameter types
*
* @param className Name of the desired target class
* @param packageType Package where the desired target class is located
* @param parameterTypes Parameter types of the desired constructor
* @return The constructor of the desired target class with the specified parameter types
* @throws NoSuchMethodException If the desired constructor with the specified parameter types cannot be found
* @throws ClassNotFoundException ClassNotFoundException If the desired target class with the specified name and package cannot be found
* @see #getConstructor(Class, Class...)
*/
public static Constructor<?> getConstructor(String className, PackageType packageType, Class<?>... parameterTypes) throws NoSuchMethodException, ClassNotFoundException {
return getConstructor(packageType.getClass(className), parameterTypes);
}
/**
* Returns an instance of a class with the given arguments
*
* @param clazz Target class
* @param arguments Arguments which are used to construct an object of the target class
* @return The instance of the target class with the specified arguments
* @throws InstantiationException If you cannot create an instance of the target class due to certain circumstances
* @throws IllegalAccessException If the desired constructor cannot be accessed due to certain circumstances
* @throws IllegalArgumentException If the types of the arguments do not match the parameter types of the constructor (this should not occur since it searches for a constructor with the types of the arguments)
* @throws InvocationTargetException If the desired constructor cannot be invoked
* @throws NoSuchMethodException If the desired constructor with the specified arguments cannot be found
*/
public static Object instantiateObject(Class<?> clazz, Object... arguments) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException {
return getConstructor(clazz, DataType.getPrimitive(arguments)).newInstance(arguments);
}
/**
* Returns an instance of a desired class with the given arguments
*
* @param className Name of the desired target class
* @param packageType Package where the desired target class is located
* @param arguments Arguments which are used to construct an object of the desired target class
* @return The instance of the desired target class with the specified arguments
* @throws InstantiationException If you cannot create an instance of the desired target class due to certain circumstances
* @throws IllegalAccessException If the desired constructor cannot be accessed due to certain circumstances
* @throws IllegalArgumentException If the types of the arguments do not match the parameter types of the constructor (this should not occur since it searches for a constructor with the types of the arguments)
* @throws InvocationTargetException If the desired constructor cannot be invoked
* @throws NoSuchMethodException If the desired constructor with the specified arguments cannot be found
* @throws ClassNotFoundException If the desired target class with the specified name and package cannot be found
* @see #instantiateObject(Class, Object...)
*/
public static Object instantiateObject(String className, PackageType packageType, Object... arguments) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException {
return instantiateObject(packageType.getClass(className), arguments);
}
/**
* Returns a method of a class with the given parameter types
*
* @param clazz Target class
* @param methodName Name of the desired method
* @param parameterTypes Parameter types of the desired method
* @return The method of the target class with the specified name and parameter types
* @throws NoSuchMethodException If the desired method of the target class with the specified name and parameter types cannot be found
* @see DataType#getPrimitive(Class[])
* @see DataType#compare(Class[], Class[])
*/
public static Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) throws NoSuchMethodException {
Class<?>[] primitiveTypes = DataType.getPrimitive(parameterTypes);
for (Method method : clazz.getMethods()) {
if (!method.getName().equals(methodName) || !DataType.compare(DataType.getPrimitive(method.getParameterTypes()), primitiveTypes)) {
continue;
}
return method;
}
throw new NoSuchMethodException("There is no such method in this class with the specified name and parameter types");
}
/**
* Returns a method of a desired class with the given parameter types
*
* @param className Name of the desired target class
* @param packageType Package where the desired target class is located
* @param methodName Name of the desired method
* @param parameterTypes Parameter types of the desired method
* @return The method of the desired target class with the specified name and parameter types
* @throws NoSuchMethodException If the desired method of the desired target class with the specified name and parameter types cannot be found
* @throws ClassNotFoundException If the desired target class with the specified name and package cannot be found
* @see #getMethod(Class, String, Class...)
*/
public static Method getMethod(String className, PackageType packageType, String methodName, Class<?>... parameterTypes) throws NoSuchMethodException, ClassNotFoundException {
return getMethod(packageType.getClass(className), methodName, parameterTypes);
}
/**
* Invokes a method on an object with the given arguments
*
* @param instance Target object
* @param methodName Name of the desired method
* @param arguments Arguments which are used to invoke the desired method
* @return The result of invoking the desired method on the target object
* @throws IllegalAccessException If the desired method cannot be accessed due to certain circumstances
* @throws IllegalArgumentException If the types of the arguments do not match the parameter types of the method (this should not occur since it searches for a method with the types of the arguments)
* @throws InvocationTargetException If the desired method cannot be invoked on the target object
* @throws NoSuchMethodException If the desired method of the class of the target object with the specified name and arguments cannot be found
* @see #getMethod(Class, String, Class...)
* @see DataType#getPrimitive(Object[])
*/
public static Object invokeMethod(Object instance, String methodName, Object... arguments) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException {
return getMethod(instance.getClass(), methodName, DataType.getPrimitive(arguments)).invoke(instance, arguments);
}
/**
* Invokes a method of the target class on an object with the given arguments
*
* @param instance Target object
* @param clazz Target class
* @param methodName Name of the desired method
* @param arguments Arguments which are used to invoke the desired method
* @return The result of invoking the desired method on the target object
* @throws IllegalAccessException If the desired method cannot be accessed due to certain circumstances
* @throws IllegalArgumentException If the types of the arguments do not match the parameter types of the method (this should not occur since it searches for a method with the types of the arguments)
* @throws InvocationTargetException If the desired method cannot be invoked on the target object
* @throws NoSuchMethodException If the desired method of the target class with the specified name and arguments cannot be found
* @see #getMethod(Class, String, Class...)
* @see DataType#getPrimitive(Object[])
*/
public static Object invokeMethod(Object instance, Class<?> clazz, String methodName, Object... arguments) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException {
return getMethod(clazz, methodName, DataType.getPrimitive(arguments)).invoke(instance, arguments);
}
/**
* Invokes a method of a desired class on an object with the given arguments
*
* @param instance Target object
* @param className Name of the desired target class
* @param packageType Package where the desired target class is located
* @param methodName Name of the desired method
* @param arguments Arguments which are used to invoke the desired method
* @return The result of invoking the desired method on the target object
* @throws IllegalAccessException If the desired method cannot be accessed due to certain circumstances
* @throws IllegalArgumentException If the types of the arguments do not match the parameter types of the method (this should not occur since it searches for a method with the types of the arguments)
* @throws InvocationTargetException If the desired method cannot be invoked on the target object
* @throws NoSuchMethodException If the desired method of the desired target class with the specified name and arguments cannot be found
* @throws ClassNotFoundException If the desired target class with the specified name and package cannot be found
* @see #invokeMethod(Object, Class, String, Object...)
*/
public static Object invokeMethod(Object instance, String className, PackageType packageType, String methodName, Object... arguments) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException {
return invokeMethod(instance, packageType.getClass(className), methodName, arguments);
}
/**
* Returns a field of the target class with the given name
*
* @param clazz Target class
* @param declared Whether the desired field is declared or not
* @param fieldName Name of the desired field
* @return The field of the target class with the specified name
* @throws NoSuchFieldException If the desired field of the given class cannot be found
* @throws SecurityException If the desired field cannot be made accessible
*/
public static Field getField(Class<?> clazz, boolean declared, String fieldName) throws NoSuchFieldException, SecurityException {
Field field = declared ? clazz.getDeclaredField(fieldName) : clazz.getField(fieldName);
field.setAccessible(true);
return field;
}
/**
* Returns a field of a desired class with the given name
*
* @param className Name of the desired target class
* @param packageType Package where the desired target class is located
* @param declared Whether the desired field is declared or not
* @param fieldName Name of the desired field
* @return The field of the desired target class with the specified name
* @throws NoSuchFieldException If the desired field of the desired class cannot be found
* @throws SecurityException If the desired field cannot be made accessible
* @throws ClassNotFoundException If the desired target class with the specified name and package cannot be found
* @see #getField(Class, boolean, String)
*/
public static Field getField(String className, PackageType packageType, boolean declared, String fieldName) throws NoSuchFieldException, SecurityException, ClassNotFoundException {
return getField(packageType.getClass(className), declared, fieldName);
}
/**
* Returns the value of a field of the given class of an object
*
* @param instance Target object
* @param clazz Target class
* @param declared Whether the desired field is declared or not
* @param fieldName Name of the desired field
* @return The value of field of the target object
* @throws IllegalArgumentException If the target object does not feature the desired field
* @throws IllegalAccessException If the desired field cannot be accessed
* @throws NoSuchFieldException If the desired field of the target class cannot be found
* @throws SecurityException If the desired field cannot be made accessible
* @see #getField(Class, boolean, String)
*/
public static Object getValue(Object instance, Class<?> clazz, boolean declared, String fieldName) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
return getField(clazz, declared, fieldName).get(instance);
}
/**
* Returns the value of a field of a desired class of an object
*
* @param instance Target object
* @param className Name of the desired target class
* @param packageType Package where the desired target class is located
* @param declared Whether the desired field is declared or not
* @param fieldName Name of the desired field
* @return The value of field of the target object
* @throws IllegalArgumentException If the target object does not feature the desired field
* @throws IllegalAccessException If the desired field cannot be accessed
* @throws NoSuchFieldException If the desired field of the desired class cannot be found
* @throws SecurityException If the desired field cannot be made accessible
* @throws ClassNotFoundException If the desired target class with the specified name and package cannot be found
* @see #getValue(Object, Class, boolean, String)
*/
public static Object getValue(Object instance, String className, PackageType packageType, boolean declared, String fieldName) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException, ClassNotFoundException {
return getValue(instance, packageType.getClass(className), declared, fieldName);
}
/**
* Returns the value of a field with the given name of an object
*
* @param instance Target object
* @param declared Whether the desired field is declared or not
* @param fieldName Name of the desired field
* @return The value of field of the target object
* @throws IllegalArgumentException If the target object does not feature the desired field (should not occur since it searches for a field with the given name in the class of the object)
* @throws IllegalAccessException If the desired field cannot be accessed
* @throws NoSuchFieldException If the desired field of the target object cannot be found
* @throws SecurityException If the desired field cannot be made accessible
* @see #getValue(Object, Class, boolean, String)
*/
public static Object getValue(Object instance, boolean declared, String fieldName) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
return getValue(instance, instance.getClass(), declared, fieldName);
}
/**
* Sets the value of a field of the given class of an object
*
* @param instance Target object
* @param clazz Target class
* @param declared Whether the desired field is declared or not
* @param fieldName Name of the desired field
* @param value New value
* @throws IllegalArgumentException If the type of the value does not match the type of the desired field
* @throws IllegalAccessException If the desired field cannot be accessed
* @throws NoSuchFieldException If the desired field of the target class cannot be found
* @throws SecurityException If the desired field cannot be made accessible
* @see #getField(Class, boolean, String)
*/
public static void setValue(Object instance, Class<?> clazz, boolean declared, String fieldName, Object value) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
getField(clazz, declared, fieldName).set(instance, value);
}
/**
* Sets the value of a field of a desired class of an object
*
* @param instance Target object
* @param className Name of the desired target class
* @param packageType Package where the desired target class is located
* @param declared Whether the desired field is declared or not
* @param fieldName Name of the desired field
* @param value New value
* @throws IllegalArgumentException If the type of the value does not match the type of the desired field
* @throws IllegalAccessException If the desired field cannot be accessed
* @throws NoSuchFieldException If the desired field of the desired class cannot be found
* @throws SecurityException If the desired field cannot be made accessible
* @throws ClassNotFoundException If the desired target class with the specified name and package cannot be found
* @see #setValue(Object, Class, boolean, String, Object)
*/
public static void setValue(Object instance, String className, PackageType packageType, boolean declared, String fieldName, Object value) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException, ClassNotFoundException {
setValue(instance, packageType.getClass(className), declared, fieldName, value);
}
/**
* Sets the value of a field with the given name of an object
*
* @param instance Target object
* @param declared Whether the desired field is declared or not
* @param fieldName Name of the desired field
* @param value New value
* @throws IllegalArgumentException If the type of the value does not match the type of the desired field
* @throws IllegalAccessException If the desired field cannot be accessed
* @throws NoSuchFieldException If the desired field of the target object cannot be found
* @throws SecurityException If the desired field cannot be made accessible
* @see #setValue(Object, Class, boolean, String, Object)
*/
public static void setValue(Object instance, boolean declared, String fieldName, Object value) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
setValue(instance, instance.getClass(), declared, fieldName, value);
}
/**
* Sets a value of an {@link Object} via reflection
*
* @param instance instance the class to use
* @param fieldName the name of the {@link Field} to modify
* @param value the value to set
* @throws NoSuchFieldException, IllegalAccessException
*/
public static void setValue(Object instance, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
setValue(instance, true, fieldName, value);
}
/**
* Get a value of an {@link Object}'s {@link Field}
*
* @param instance the target {@link Object}
* @param fieldName name of the {@link Field}
* @return the value of {@link Object} instance's {@link Field} with the
* name of fieldName
* @throws NoSuchFieldException, IllegalAccessException
*/
public static Object getValue(Object instance, String fieldName) throws NoSuchFieldException, IllegalAccessException {
return getValue(instance, true, fieldName);
}
/**
* Set a final static var
* @param field The field object
* @param value The new value
* @throws ReflectiveOperationException
*/
public static void setFinalStatic(Field field, Object value) throws ReflectiveOperationException {
field.setAccessible(true);
Class.class.getModifiers();
Field mf = Field.class.getDeclaredField("modifiers");
mf.setAccessible(true);
mf.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, value);
}
/**
* Set a final static var
* @param field The field object
* @param value The new value
* @throws ReflectiveOperationException
*/
public static void setFinal(Object object, Field field, Object value) throws ReflectiveOperationException {
field.setAccessible(true);
Field mf = Field.class.getDeclaredField("modifiers");
mf.setAccessible(true);
mf.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(object, value);
}
/**
* Represents an enumeration of dynamic packages of NMS and CraftBukkit
* <p/>
* This class is part of the <b>ReflectionUtils</b> and follows the same usage conditions
*
* @author DarkBlade12
* @since 1.0
*/
public enum PackageType {
MINECRAFT_SERVER("net.minecraft.server." + getServerVersion()),
CRAFTBUKKIT("org.bukkit.craftbukkit." + getServerVersion()),
CRAFTBUKKIT_BLOCK(CRAFTBUKKIT, "block"),
CRAFTBUKKIT_CHUNKIO(CRAFTBUKKIT, "chunkio"),
CRAFTBUKKIT_COMMAND(CRAFTBUKKIT, "command"),
CRAFTBUKKIT_CONVERSATIONS(CRAFTBUKKIT, "conversations"),
CRAFTBUKKIT_ENCHANTMENS(CRAFTBUKKIT, "enchantments"),
CRAFTBUKKIT_ENTITY(CRAFTBUKKIT, "entity"),
CRAFTBUKKIT_EVENT(CRAFTBUKKIT, "event"),
CRAFTBUKKIT_GENERATOR(CRAFTBUKKIT, "generator"),
CRAFTBUKKIT_HELP(CRAFTBUKKIT, "help"),
CRAFTBUKKIT_INVENTORY(CRAFTBUKKIT, "inventory"),
CRAFTBUKKIT_MAP(CRAFTBUKKIT, "map"),
CRAFTBUKKIT_METADATA(CRAFTBUKKIT, "metadata"),
CRAFTBUKKIT_POTION(CRAFTBUKKIT, "potion"),
CRAFTBUKKIT_PROJECTILES(CRAFTBUKKIT, "projectiles"),
CRAFTBUKKIT_SCHEDULER(CRAFTBUKKIT, "scheduler"),
CRAFTBUKKIT_SCOREBOARD(CRAFTBUKKIT, "scoreboard"),
CRAFTBUKKIT_UPDATER(CRAFTBUKKIT, "updater"),
CRAFTBUKKIT_UTIL(CRAFTBUKKIT, "util"),
BUKKIT("org.bukkit");
private final String path;
/**
* Construct a new package type
*
* @param path Path of the package
*/
private PackageType(String path) {
this.path = path;
}
/**
* Construct a new package type
*
* @param parent Parent package of the package
* @param path Path of the package
*/
private PackageType(PackageType parent, String path) {
this(parent + "." + path);
}
/**
* Returns the version of your server
*
* @return The server version
*/
public static String getServerVersion() {
String name = Bukkit.getServer().getClass().getPackage().getName();
return name.substring(name.lastIndexOf('.') + 1);
}
/**
* Returns the path of this package type
*
* @return The path
*/
public String getPath() {
return path;
}
/**
* Returns the class with the given name
*
* @param className Name of the desired class
* @return The class with the specified name
* @throws ClassNotFoundException If the desired class with the specified name and package cannot be found
*/
public Class<?> getClass(String className) throws ClassNotFoundException {
return Class.forName(this + "." + className);
}
// Override for convenience
@Override
public String toString() {
return path;
}
}
/**
* Represents an enumeration of Java data types with corresponding classes
* <p/>
* This class is part of the <b>ReflectionUtils</b> and follows the same usage conditions
*
* @author DarkBlade12
* @since 1.0
*/
public enum DataType {
BYTE(byte.class, Byte.class),
SHORT(short.class, Short.class),
INTEGER(int.class, Integer.class),
LONG(long.class, Long.class),
CHARACTER(char.class, Character.class),
FLOAT(float.class, Float.class),
DOUBLE(double.class, Double.class),
BOOLEAN(boolean.class, Boolean.class);
private static final Map<Class<?>, DataType> CLASS_MAP = new HashMap<>();
private final Class<?> primitive;
private final Class<?> reference;
// Initialize map for quick class lookup
static {
for (DataType type : values()) {
CLASS_MAP.put(type.primitive, type);
CLASS_MAP.put(type.reference, type);
}
}
/**
* Construct a new data type
*
* @param primitive Primitive class of this data type
* @param reference Reference class of this data type
*/
private DataType(Class<?> primitive, Class<?> reference) {
this.primitive = primitive;
this.reference = reference;
}
/**
* Returns the data type with the given primitive/reference class
*
* @param clazz Primitive/Reference class of the data type
* @return The data type
*/
public static DataType fromClass(Class<?> clazz) {
return CLASS_MAP.get(clazz);
}
/**
* Returns the primitive class of the data type with the given reference class
*
* @param clazz Reference class of the data type
* @return The primitive class
*/
public static Class<?> getPrimitive(Class<?> clazz) {
DataType type = fromClass(clazz);
return type == null ? clazz : type.getPrimitive();
}
/**
* Returns the reference class of the data type with the given primitive class
*
* @param clazz Primitive class of the data type
* @return The reference class
*/
public static Class<?> getReference(Class<?> clazz) {
DataType type = fromClass(clazz);
return type == null ? clazz : type.getReference();
}
/**
* Returns the primitive class array of the given class array
*
* @param classes Given class array
* @return The primitive class array
*/
public static Class<?>[] getPrimitive(Class<?>[] classes) {
int length = classes == null ? 0 : classes.length;
Class<?>[] types = new Class<?>[length];
for (int index = 0; index < length; index++) {
types[index] = getPrimitive(classes[index]);
}
return types;
}
/**
* Returns the reference class array of the given class array
*
* @param classes Given class array
* @return The reference class array
*/
public static Class<?>[] getReference(Class<?>[] classes) {
int length = classes == null ? 0 : classes.length;
Class<?>[] types = new Class<?>[length];
for (int index = 0; index < length; index++) {
types[index] = getReference(classes[index]);
}
return types;
}
/**
* Returns the primitive class array of the given object array
*
* @param objects Given object array
* @return The primitive class array
*/
public static Class<?>[] getPrimitive(Object[] objects) {
int length = objects == null ? 0 : objects.length;
Class<?>[] types = new Class<?>[length];
for (int index = 0; index < length; index++) {
types[index] = getPrimitive(objects[index].getClass());
}
return types;
}
/**
* Returns the reference class array of the given object array
*
* @param objects Given object array
* @return The reference class array
*/
public static Class<?>[] getReference(Object[] objects) {
int length = objects == null ? 0 : objects.length;
Class<?>[] types = new Class<?>[length];
for (int index = 0; index < length; index++) {
types[index] = getReference(objects[index].getClass());
}
return types;
}
/**
* Compares two class arrays on equivalence
*
* @param primary Primary class array
* @param secondary Class array which is compared to the primary array
* @return Whether these arrays are equal or not
*/
public static boolean compare(Class<?>[] primary, Class<?>[] secondary) {
if (primary == null || secondary == null || primary.length != secondary.length) {
return false;
}
for (int index = 0; index < primary.length; index++) {
Class<?> primaryClass = primary[index];
Class<?> secondaryClass = secondary[index];
if (primaryClass.equals(secondaryClass) || primaryClass.isAssignableFrom(secondaryClass)) {
continue;
}
return false;
}
return true;
}
/**
* Returns the primitive class of this data type
*
* @return The primitive class
*/
public Class<?> getPrimitive() {
return primitive;
}
/**
* Returns the reference class of this data type
*
* @return The reference class
*/
public Class<?> getReference() {
return reference;
}
}
/**
* Represents an enumeration of all packet types that are featured in <b>Minecraft 1.7.10</b>
* <p/>
* If this enumeration is no longer up-to-date, please let me know in my <a href="http://forums.bukkit.org/threads/lib-1-7-particleeffect-v1-4.154406">forum post</a>
* <p/>
* This class is part of the <b>ReflectionUtils</b> and follows the same usage conditions
*
* @author DarkBlade12
* @since 1.0
*/
public enum PacketType {
HANDSHAKING_IN_SET_PROTOCOL("PacketHandshakingInSetProtocol"),
LOGIN_IN_ENCRYPTION_BEGIN("PacketLoginInEncryptionBegin"),
LOGIN_IN_START("PacketLoginInStart"),
LOGIN_OUT_DISCONNECT("PacketLoginOutDisconnect"),
LOGIN_OUT_ENCRYPTION_BEGIN("PacketLoginOutEncryptionBegin"),
LOGIN_OUT_SUCCESS("PacketLoginOutSuccess"),
PLAY_IN_ABILITIES("PacketPlayInAbilities"),
PLAY_IN_ARM_ANIMATION("PacketPlayInArmAnimation"),
PLAY_IN_BLOCK_DIG("PacketPlayInBlockDig"),
PLAY_IN_BLOCK_PLACE("PacketPlayInBlockPlace"),
PLAY_IN_CHAT("PacketPlayInChat"),
PLAY_IN_CLIENT_COMMAND("PacketPlayInClientCommand"),
PLAY_IN_CLOSE_WINDOW("PacketPlayInCloseWindow"),
PLAY_IN_CUSTOM_PAYLOAD("PacketPlayInCustomPayload"),
PLAY_IN_ENCHANT_ITEM("PacketPlayInEnchantItem"),
PLAY_IN_ENTITY_ACTION("PacketPlayInEntityAction"),
PLAY_IN_FLYING("PacketPlayInFlying"),
PLAY_IN_HELD_ITEM_SLOT("PacketPlayInHeldItemSlot"),
PLAY_IN_KEEP_ALIVE("PacketPlayInKeepAlive"),
PLAY_IN_LOOK("PacketPlayInLook"),
PLAY_IN_POSITION("PacketPlayInPosition"),
PLAY_IN_POSITION_LOOK("PacketPlayInPositionLook"),
PLAY_IN_SET_CREATIVE_SLOT("PacketPlayInSetCreativeSlot "),
PLAY_IN_SETTINGS("PacketPlayInSettings"),
PLAY_IN_STEER_VEHICLE("PacketPlayInSteerVehicle"),
PLAY_IN_TAB_COMPLETE("PacketPlayInTabComplete"),
PLAY_IN_TRANSACTION("PacketPlayInTransaction"),
PLAY_IN_UPDATE_SIGN("PacketPlayInUpdateSign"),
PLAY_IN_USE_ENTITY("PacketPlayInUseEntity"),
PLAY_IN_WINDOW_CLICK("PacketPlayInWindowClick"),
PLAY_OUT_ABILITIES("PacketPlayOutAbilities"),
PLAY_OUT_ANIMATION("PacketPlayOutAnimation"),
PLAY_OUT_ATTACH_ENTITY("PacketPlayOutAttachEntity"),
PLAY_OUT_BED("PacketPlayOutBed"),
PLAY_OUT_BLOCK_ACTION("PacketPlayOutBlockAction"),
PLAY_OUT_BLOCK_BREAK_ANIMATION("PacketPlayOutBlockBreakAnimation"),
PLAY_OUT_BLOCK_CHANGE("PacketPlayOutBlockChange"),
PLAY_OUT_CHAT("PacketPlayOutChat"),
PLAY_OUT_CLOSE_WINDOW("PacketPlayOutCloseWindow"),
PLAY_OUT_COLLECT("PacketPlayOutCollect"),
PLAY_OUT_CRAFT_PROGRESS_BAR("PacketPlayOutCraftProgressBar"),
PLAY_OUT_CUSTOM_PAYLOAD("PacketPlayOutCustomPayload"),
PLAY_OUT_ENTITY("PacketPlayOutEntity"),
PLAY_OUT_ENTITY_DESTROY("PacketPlayOutEntityDestroy"),
PLAY_OUT_ENTITY_EFFECT("PacketPlayOutEntityEffect"),
PLAY_OUT_ENTITY_EQUIPMENT("PacketPlayOutEntityEquipment"),
PLAY_OUT_ENTITY_HEAD_ROTATION("PacketPlayOutEntityHeadRotation"),
PLAY_OUT_ENTITY_LOOK("PacketPlayOutEntityLook"),
PLAY_OUT_ENTITY_METADATA("PacketPlayOutEntityMetadata"),
PLAY_OUT_ENTITY_STATUS("PacketPlayOutEntityStatus"),
PLAY_OUT_ENTITY_TELEPORT("PacketPlayOutEntityTeleport"),
PLAY_OUT_ENTITY_VELOCITY("PacketPlayOutEntityVelocity"),
PLAY_OUT_EXPERIENCE("PacketPlayOutExperience"),
PLAY_OUT_EXPLOSION("PacketPlayOutExplosion"),
PLAY_OUT_GAME_STATE_CHANGE("PacketPlayOutGameStateChange"),
PLAY_OUT_HELD_ITEM_SLOT("PacketPlayOutHeldItemSlot"),
PLAY_OUT_KEEP_ALIVE("PacketPlayOutKeepAlive"),
PLAY_OUT_KICK_DISCONNECT("PacketPlayOutKickDisconnect"),
PLAY_OUT_LOGIN("PacketPlayOutLogin"),
PLAY_OUT_MAP("PacketPlayOutMap"),
PLAY_OUT_MAP_CHUNK("PacketPlayOutMapChunk"),
PLAY_OUT_MAP_CHUNK_BULK("PacketPlayOutMapChunkBulk"),
PLAY_OUT_MULTI_BLOCK_CHANGE("PacketPlayOutMultiBlockChange"),
PLAY_OUT_NAMED_ENTITY_SPAWN("PacketPlayOutNamedEntitySpawn"),
PLAY_OUT_NAMED_SOUND_EFFECT("PacketPlayOutNamedSoundEffect"),
PLAY_OUT_OPEN_SIGN_EDITOR("PacketPlayOutOpenSignEditor"),
PLAY_OUT_OPEN_WINDOW("PacketPlayOutOpenWindow"),
PLAY_OUT_PLAYER_INFO("PacketPlayOutPlayerInfo"),
PLAY_OUT_POSITION("PacketPlayOutPosition"),
PLAY_OUT_REL_ENTITY_MOVE("PacketPlayOutRelEntityMove"),
PLAY_OUT_REL_ENTITY_MOVE_LOOK("PacketPlayOutRelEntityMoveLook"),
PLAY_OUT_REMOVE_ENTITY_EFFECT("PacketPlayOutRemoveEntityEffect"),
PLAY_OUT_RESPAWN("PacketPlayOutRespawn"),
PLAY_OUT_SCOREBOARD_DISPLAY_OBJECTIVE("PacketPlayOutScoreboardDisplayObjective"),
PLAY_OUT_SCOREBOARD_OBJECTIVE("PacketPlayOutScoreboardObjective"),
PLAY_OUT_SCOREBOARD_SCORE("PacketPlayOutScoreboardScore"),
PLAY_OUT_SCOREBOARD_TEAM("PacketPlayOutScoreboardTeam"),
PLAY_OUT_SET_SLOT("PacketPlayOutSetSlot"),
PLAY_OUT_SPAWN_ENTITY("PacketPlayOutSpawnEntity"),
PLAY_OUT_SPAWN_ENTITY_EXPERIENCE_ORB("PacketPlayOutSpawnEntityExperienceOrb"),
PLAY_OUT_SPAWN_ENTITY_LIVING("PacketPlayOutSpawnEntityLiving"),
PLAY_OUT_SPAWN_ENTITY_PAINTING("PacketPlayOutSpawnEntityPainting"),
PLAY_OUT_SPAWN_ENTITY_WEATHER("PacketPlayOutSpawnEntityWeather"),
PLAY_OUT_SPAWN_POSITION("PacketPlayOutSpawnPosition"),
PLAY_OUT_STATISTIC("PacketPlayOutStatistic"),
PLAY_OUT_TAB_COMPLETE("PacketPlayOutTabComplete"),
PLAY_OUT_TILE_ENTITY_DATA("PacketPlayOutTileEntityData"),
PLAY_OUT_TRANSACTION("PacketPlayOutTransaction"),
PLAY_OUT_UPDATE_ATTRIBUTES("PacketPlayOutUpdateAttributes"),
PLAY_OUT_UPDATE_HEALTH("PacketPlayOutUpdateHealth"),
PLAY_OUT_UPDATE_SIGN("PacketPlayOutUpdateSign"),
PLAY_OUT_UPDATE_TIME("PacketPlayOutUpdateTime"),
PLAY_OUT_WINDOW_ITEMS("PacketPlayOutWindowItems"),
PLAY_OUT_WORLD_EVENT("PacketPlayOutWorldEvent"),
PLAY_OUT_WORLD_PARTICLES("PacketPlayOutWorldParticles"),
STATUS_IN_PING("PacketStatusInPing"),
STATUS_IN_START("PacketStatusInStart"),
STATUS_OUT_PONG("PacketStatusOutPong"),
STATUS_OUT_SERVER_INFO("PacketStatusOutServerInfo");
private static final Map<String, PacketType> NAME_MAP = new HashMap<>();
private final String name;
private Class<?> packet;
// Initialize map for quick name lookup
static {
for (PacketType type : values()) {
NAME_MAP.put(type.name, type);
}
}
/**
* Construct a new packet type
*
* @param name Name of this packet
*/
private PacketType(String name) {
this.name = name;
}
/**
* Returns the name of this packet type
*
* @return The name
*/
public String getName() {
return name;
}
/**
* Returns the class of this packet
*
* @return The packet class
* @throws ClassNotFoundException If the packet class cannot be found (the name differs in your Bukkit version)
*/
public Class<?> getPacket() throws ClassNotFoundException {
return packet == null ? (packet = PackageType.MINECRAFT_SERVER.getClass(name)) : packet;
}
}
}

View File

@@ -0,0 +1,56 @@
package fr.arthurdanjou.template.utils.reflection;
import org.bukkit.Bukkit;
import java.util.Locale;
import java.util.Optional;
public class ScoreBoardReflection {
public static final String OBC_PACKAGE = "org.bukkit.craftbukkit";
public static final String NMS_PACKAGE = "net.minecraft.server";
public static final String VERSION = Bukkit.getServer().getClass().getPackage().getName().substring(OBC_PACKAGE.length() + 1);
private ScoreBoardReflection() {
throw new UnsupportedOperationException();
}
public static String nmsClassName(String className) {
return NMS_PACKAGE + '.' + VERSION + '.' + className;
}
public static Class<?> nmsClass(String className) throws ClassNotFoundException {
return Class.forName(nmsClassName(className));
}
public static Optional<Class<?>> nmsOptionalClass(String className) {
return optionalClass(nmsClassName(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();
}
}
@SuppressWarnings("unchecked")
public static <E extends Enum<E>> E enumValueOf(Class<?> enumClass, String enumName) {
return Enum.valueOf((Class<E>) enumClass, enumName.toUpperCase(Locale.ROOT));
}
}

View File

@@ -0,0 +1,45 @@
package fr.arthurdanjou.template.utils.team;
import fr.arthurdanjou.template.team.TeamUnit;
import lombok.Getter;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
@Getter
public class Team {
private final TeamUnit teamUnit;
private final Set<UUID> players;
public Team(TeamUnit teamUnit) {
this.teamUnit = teamUnit;
this.players = new HashSet<>();
}
public String getPrefix() {
return this.teamUnit.getPrefix();
}
public String getName() {
return this.teamUnit.getName();
}
public String getColoredName() {
return this.teamUnit.getColoredName();
}
public String getColor() {
return this.teamUnit.getColor();
}
public int getSize() {
return this.players.size();
}
public boolean isEmpty() {
return this.players.isEmpty();
}
}

View File

@@ -0,0 +1,483 @@
package fr.arthurdanjou.template.utils.team;
import fr.arthurdanjou.template.team.TeamUnit;
import fr.arthurdanjou.template.utils.reflection.Reflection;
import lombok.Getter;
import org.bukkit.ChatColor;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList;
public class TeamHandler {
@Getter
private ConcurrentLinkedQueue<VTeam> teams;
private List<OfflinePlayer> receivers;
/**
* Constructor
*/
public TeamHandler() {
this.teams = new ConcurrentLinkedQueue<>();
this.receivers = new ArrayList<>();
}
/**
* Add receiver
*
* @param offlinePlayer Player
*
* @return {@code true} if success
*/
public boolean addReceiver(OfflinePlayer offlinePlayer) {
if(!offlinePlayer.isOnline())
return false;
this.removeFromAllTeams(offlinePlayer);
this.receivers.add(offlinePlayer);
Player p = offlinePlayer.getPlayer();
this.sendAllTeams(p);
return true;
}
/**
* Remove receiver
*
* @param offlinePlayer Player
*
* @return {@code true} if success
*/
public boolean removeReceiver(OfflinePlayer offlinePlayer) {
this.receivers.remove(offlinePlayer);
this.removeFromAllTeams(offlinePlayer);
if(offlinePlayer.isOnline())
this.removeAllTeams(offlinePlayer.getPlayer());
return true;
}
/**
* Create a new scoreboard team
*
* @param name Team name
* @param display Team display name
*
* @return Team instance
*/
public VTeam createNewTeam(String name, String display) {
return new VTeam(name, display);
}
/**
* Add a given player to the given team
*
* @param p Player
* @param team Team
*
* @return {@code true} is success
*/
public boolean addPlayerToTeam(OfflinePlayer p, VTeam team) {
return this.addPlayerToTeam(p.getName(), team);
}
/**
* Add a given player to the given team
*
* @param p Player
* @param team Team
*
* @return {@code true} is success
*/
public boolean addPlayerToTeam(String p, VTeam team) {
team.addPlayer(p);
for(OfflinePlayer offlinePlayer : this.receivers) {
if(!offlinePlayer.isOnline())
continue;
RawTeam.addPlayerToTeam(offlinePlayer.getPlayer(), team, p);
}
return true;
}
/**
* Remove a given player from the given team
*
* @param offlinePlayer Player
* @param team Team
*
* @return {@code true} is success
*/
public boolean removePlayerFromTeam(OfflinePlayer offlinePlayer, VTeam team) {
team.removePlayer(offlinePlayer);
for(OfflinePlayer player : this.receivers) {
if(!player.isOnline())
continue;
RawTeam.removePlayerFromTeam(player.getPlayer(), team, player.getName());
}
return true;
}
/**
* Add a team to the scoreboard
*
* @param vt Team
*
*/
public void addTeam(VTeam vt) {
this.teams.add(vt);
this.sendToAllTeam(vt);
}
/**
* Remove a team from the scoreboard
*
* @param teamName Team
*
* @return {@code true} is success
*/
public boolean removeTeam(String teamName) {
VTeam vt = this.getTeamByName(teamName);
if(vt == null)
return false;
this.removeToAllTeam(vt);
return true;
}
/**
* Add a given player to all teams
*
* @param offlinePlayer Player
*
* @return {@code true} is success
*/
private boolean removeFromAllTeams(OfflinePlayer offlinePlayer) {
return this.removeFromAllTeams(offlinePlayer.getName());
}
/**
* Add a given player to all teams
*
* @param player Player
*
* @return {@code true} is success
*/
private boolean removeFromAllTeams(String player) {
for (VTeam team : teams) {
for (String op : team.getPlayers()) {
if (player.equals(op)) {
team.removePlayer(op);
for (OfflinePlayer p : this.receivers) {
if(!p.isOnline())
continue;
RawTeam.removePlayerFromTeam(p.getPlayer(), team, op);
return true;
}
}
}
}
return false;
}
/**
* Send scoreboard's teams to a given player
*
* @param p Player
*/
private void sendAllTeams(Player p) {
for (VTeam vt : this.teams)
this.sendTeam(p, vt);
}
/**
* Remove scoreboard's teams from a given player
*
* @param p Player
*/
private void removeAllTeams(Player p) {
for (VTeam vt : this.teams)
this.removeTeam(p, vt);
}
/**
* Send a given scoreboard's team to all players
*
* @param vt Team
*/
private void sendToAllTeam(VTeam vt) {
for(OfflinePlayer offlinePlayer : this.receivers) {
if(!offlinePlayer.isOnline())
continue;
this.sendTeam(offlinePlayer.getPlayer(), vt);
}
}
/**
* Remove a given scoreboard's team from all players
*
* @param vt Team
*/
private void removeToAllTeam(VTeam vt) {
for(OfflinePlayer offlinePlayer : this.receivers) {
if(!offlinePlayer.isOnline())
continue;
this.removeTeam(offlinePlayer.getPlayer(), vt);
}
}
/**
* Send a given team to the given player
*
* @param p Player
* @param vt Team
*/
private void sendTeam(Player p, VTeam vt) {
RawTeam.createTeam(p, vt);
RawTeam.sendTeam(p, vt);
}
/**
* Remove a given team from the given player
*
* @param p Player
* @param vt Team
*/
private void removeTeam(Player p, VTeam vt) {
RawTeam.removeTeam(p, vt);
}
/**
* Get the player's scoreboard team
*
* @param p Player
*
* @return Team
*/
public VTeam getTeamByPlayer(Player p) {
for (VTeam n : this.teams)
if (n.contains(p.getName()))
return n;
return null;
}
/**
* Get a scoreboard team by it's name
*
* @param name Team's name
*
* @return Team
*/
public VTeam getTeamByName(String name) {
for (VTeam n : this.teams)
if (n.getName().equals(name))
return n;
return null;
}
public VTeam getTeam(TeamUnit team) {
return getTeamByName(team.getName());
}
public static class RawTeam {
public static void createTeam(Player player, VTeam team) {
Reflection.sendPacket(player, makePacket(team, new ArrayList<>(), 0));
}
public static void sendTeam(Player player, VTeam team) {
Reflection.sendPacket(player, makePacket(team, 3));
}
public static void removeTeam(Player player, VTeam team) {
Reflection.sendPacket(player, makePacket(team, new ArrayList<>(), 1));
}
public static void changeTeam(Player player, VTeam team) {
Reflection.sendPacket(player, makePacket(team, new ArrayList<>(), 2));
}
public static void addPlayerToTeam(Player receiver, VTeam team, Player toadd) {
addPlayerToTeam(receiver, team, toadd.getName());
}
public static void addPlayerToTeam(Player receiver, VTeam team, String toadd) {
Reflection.sendPacket(receiver, makePacket(team, Collections.singletonList(toadd), 3));
}
public static void removePlayerFromTeam(Player receiver, VTeam team, Player toremove) {
removePlayerFromTeam(receiver, team, toremove.getName());
}
public static void removePlayerFromTeam(Player receiver, VTeam team, String toremove) {
if (team.players.contains(toremove))
Reflection.sendPacket(receiver, makePacket(team, Collections.singletonList(toremove), 4));
}
private static Object makePacket(VTeam team, List<String> news, int n) {
//0: Création de team
//1: Suppression de team
//2: Changements infos de la team
//3: Ajout d'un joueur
//4: Suppression d'un joueur
if (news == null)
news = new ArrayList<>();
try {
PacketPlayOutScoreboardTeam packet = new PacketPlayOutScoreboardTeam();
Reflection.setValue(packet, "a", team.getRealName()); // Team display name
Reflection.setValue(packet, "b", new ChatComponentText(team.getDisplayName())); // Team display name
Reflection.setValue(packet, "c", new ChatComponentText(team.getPrefix())); // Team prefix
Reflection.setValue(packet, "d", new ChatComponentText(team.getSuffix())); // Team suffix
Reflection.setValue(packet, "e", team.getHideToOtherTeams() ? "hideForOtherTeams" : "always"); // Name tag visible
Reflection.setValue(packet, "f", "never"); // Collision rule
// Reflection.setValue(packet, "g", news.size()); // Player count
Reflection.setValue(packet, "h", news); // Players
// Reflection.setValue(packet, "i", n); // Action id
// Reflection.setValue(packet, "j", 0); // Friendly fire
Reflection.setValue(packet, "i", n); // Action id
Reflection.setValue(packet, "j", 0); // Friendly fire
return packet;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static Object makePacket(VTeam team, int n)
{
return makePacket(team, team.getPlayers(), n);
}
}
public class VTeam {
private String name = "";
private String realName = "";
private String display = "";
private String prefix = ChatColor.GRAY + "";
private String suffix = "";
private boolean hideToOtherTeams = false;
private final CopyOnWriteArrayList<String> players = new CopyOnWriteArrayList<>();
public VTeam(String name, String display) {
this.name = name;
this.display = display;
}
public VTeam(String name, String realName, String display, String prefix, String suffix) {
this.name = name;
this.realName = realName;
this.display = display;
this.prefix = prefix;
this.suffix = suffix;
}
public String getName() {
return this.name;
}
public String getDisplayName() {
return this.display;
}
public void setDisplayName(String dn) {
this.display = dn;
}
public String getPrefix()
{
return this.prefix;
}
public void setPrefix(String p)
{
this.prefix = p.substring(0, Math.min(p.length(), 16));
}
public String getSuffix()
{
return this.suffix;
}
public void setSuffix(String s)
{
this.suffix = s.substring(0, Math.min(s.length(), 16));
}
public boolean contains(OfflinePlayer op)
{
return this.players.contains(op.getName());
}
public boolean contains(String op) {
return this.players.contains(op);
}
public List<String> getPlayers() {
return this.players;
}
public void addPlayer(OfflinePlayer op) {
this.players.add(op.getName());
}
public void addPlayer(String op) {
this.players.add(op);
}
public void removePlayer(OfflinePlayer op) {
if (this.contains(op))
this.players.remove(op.getName());
}
public void removePlayer(String op) {
this.players.remove(op);
}
public int getSize() {
return this.players.size();
}
public String getRealName() {
return this.realName;
}
public void setRealName(String realName) {
this.realName = realName;
}
public void setHideToOtherTeams(boolean hideToOtherTeams) {
this.hideToOtherTeams = hideToOtherTeams;
}
public boolean getHideToOtherTeams() {
return this.hideToOtherTeams;
}
}
}