commit a83f6e42c9b4959b357271fe1ea6cfe5cd0e69cc Author: Arthur DANJOU Date: Sun Dec 27 15:39:07 2020 +0100 Initial commit :rocket: diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..9ae70d6 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/../../../../../../../:\Users\arthu\Documents\Workspace\ArtTemplateMC\.idea/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..8458c8c --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..f44ba7b --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..e6ee860 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..e96534f --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ArtTemplateMC.iml b/ArtTemplateMC.iml new file mode 100644 index 0000000..78b2cc5 --- /dev/null +++ b/ArtTemplateMC.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..62f6126 --- /dev/null +++ b/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + fr.arthurdanjou + ArtTemplateMC + 1.0-SNAPSHOT + + + 15 + 15 + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + + + + org.spigotmc + spigot-api + 1.16.4-R0.1-SNAPSHOT + provided + + + org.projectlombok + lombok + 1.18.16 + + + + \ No newline at end of file diff --git a/src/main/java/fr/arthurdanjou/template/Main.java b/src/main/java/fr/arthurdanjou/template/Main.java new file mode 100644 index 0000000..07cfc8c --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/Main.java @@ -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() { + + } +} diff --git a/src/main/java/fr/arthurdanjou/template/config/ConfigFiles.java b/src/main/java/fr/arthurdanjou/template/config/ConfigFiles.java new file mode 100644 index 0000000..1c3f5fa --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/config/ConfigFiles.java @@ -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(); + } + } + +} diff --git a/src/main/java/fr/arthurdanjou/template/config/FileManager.java b/src/main/java/fr/arthurdanjou/template/config/FileManager.java new file mode 100644 index 0000000..74ba222 --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/config/FileManager.java @@ -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; + } + +} diff --git a/src/main/java/fr/arthurdanjou/template/config/lang/Lang.java b/src/main/java/fr/arthurdanjou/template/config/lang/Lang.java new file mode 100644 index 0000000..1f29c58 --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/config/lang/Lang.java @@ -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 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); + } + +} diff --git a/src/main/java/fr/arthurdanjou/template/config/lang/PlaceHolders.java b/src/main/java/fr/arthurdanjou/template/config/lang/PlaceHolders.java new file mode 100644 index 0000000..fc9ce54 --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/config/lang/PlaceHolders.java @@ -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 + "}"; + } +} diff --git a/src/main/java/fr/arthurdanjou/template/config/settings/LocationConfig.java b/src/main/java/fr/arthurdanjou/template/config/settings/LocationConfig.java new file mode 100644 index 0000000..d080f47 --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/config/settings/LocationConfig.java @@ -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); + } +} diff --git a/src/main/java/fr/arthurdanjou/template/config/settings/Settings.java b/src/main/java/fr/arthurdanjou/template/config/settings/Settings.java new file mode 100644 index 0000000..8d25ebb --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/config/settings/Settings.java @@ -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(); + } + +} diff --git a/src/main/java/fr/arthurdanjou/template/game/Game.java b/src/main/java/fr/arthurdanjou/template/game/Game.java new file mode 100644 index 0000000..a43a99c --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/game/Game.java @@ -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(); + } + } + +} diff --git a/src/main/java/fr/arthurdanjou/template/game/GameRunnable.java b/src/main/java/fr/arthurdanjou/template/game/GameRunnable.java new file mode 100644 index 0000000..fdc959d --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/game/GameRunnable.java @@ -0,0 +1,11 @@ +package fr.arthurdanjou.template.game; + +import org.bukkit.scheduler.BukkitRunnable; + +public class GameRunnable extends BukkitRunnable { + + @Override + public void run() { + + } +} diff --git a/src/main/java/fr/arthurdanjou/template/game/GameState.java b/src/main/java/fr/arthurdanjou/template/game/GameState.java new file mode 100644 index 0000000..5bb3c07 --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/game/GameState.java @@ -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); + } +} diff --git a/src/main/java/fr/arthurdanjou/template/game/StartRunnable.java b/src/main/java/fr/arthurdanjou/template/game/StartRunnable.java new file mode 100644 index 0000000..f6ae173 --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/game/StartRunnable.java @@ -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--; + } +} diff --git a/src/main/java/fr/arthurdanjou/template/listeners/PlayerListeners.java b/src/main/java/fr/arthurdanjou/template/listeners/PlayerListeners.java new file mode 100644 index 0000000..1eb1dc6 --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/listeners/PlayerListeners.java @@ -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)); + } +} diff --git a/src/main/java/fr/arthurdanjou/template/listeners/ServerListeners.java b/src/main/java/fr/arthurdanjou/template/listeners/ServerListeners.java new file mode 100644 index 0000000..8c396ec --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/listeners/ServerListeners.java @@ -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()); + } + } + +} diff --git a/src/main/java/fr/arthurdanjou/template/listeners/WorldListeners.java b/src/main/java/fr/arthurdanjou/template/listeners/WorldListeners.java new file mode 100644 index 0000000..ec06c67 --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/listeners/WorldListeners.java @@ -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)); + } + +} diff --git a/src/main/java/fr/arthurdanjou/template/team/TeamManager.java b/src/main/java/fr/arthurdanjou/template/team/TeamManager.java new file mode 100644 index 0000000..e067dc3 --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/team/TeamManager.java @@ -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(); + } + +} diff --git a/src/main/java/fr/arthurdanjou/template/team/TeamUnit.java b/src/main/java/fr/arthurdanjou/template/team/TeamUnit.java new file mode 100644 index 0000000..a45cf97 --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/team/TeamUnit.java @@ -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(); + } +} diff --git a/src/main/java/fr/arthurdanjou/template/users/User.java b/src/main/java/fr/arthurdanjou/template/users/User.java new file mode 100644 index 0000000..03f6624 --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/users/User.java @@ -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++; + } +} diff --git a/src/main/java/fr/arthurdanjou/template/users/UserManager.java b/src/main/java/fr/arthurdanjou/template/users/UserManager.java new file mode 100644 index 0000000..9dc8f34 --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/users/UserManager.java @@ -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 users = new HashSet<>(); + + public void onLogin(Player player) { + users.add(new User(player)); + } + + public Optional getUser(Player player) { + return getUser(player.getUniqueId()); + } + + public Optional getUser(UUID uuid) { + return this.users.stream().filter(user -> user.getUuid().equals(uuid)).findFirst(); + } + + public Set getAliveUsers() { + return this.users.stream().filter(User::isAlive).collect(Collectors.toSet()); + } + + public Set 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); + } + +} diff --git a/src/main/java/fr/arthurdanjou/template/utils/ItemBuilder.java b/src/main/java/fr/arthurdanjou/template/utils/ItemBuilder.java new file mode 100644 index 0000000..b34f645 --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/utils/ItemBuilder.java @@ -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 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 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 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 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; } + +} diff --git a/src/main/java/fr/arthurdanjou/template/utils/ScoreBoard.java b/src/main/java/fr/arthurdanjou/template/utils/ScoreBoard.java new file mode 100644 index 0000000..1852137 --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/utils/ScoreBoard.java @@ -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 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 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 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 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 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 oldLines = new ArrayList<>(this.lines); + this.lines.clear(); + this.lines.addAll(lines); + + int linesSize = this.lines.size(); + + try { + if (oldLines.size() != linesSize) { + List 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 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(); + } + } + +} diff --git a/src/main/java/fr/arthurdanjou/template/utils/reflection/Reflection.java b/src/main/java/fr/arthurdanjou/template/utils/reflection/Reflection.java new file mode 100644 index 0000000..50b0183 --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/utils/reflection/Reflection.java @@ -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 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 Constructor makeConstructor(Class clazz, Class... paramaterTypes) { + try { + return (Constructor) clazz.getConstructor(paramaterTypes); + } catch (NoSuchMethodException ex) { + return null; + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + public static T callConstructor(Constructor 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 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 Class getClass(String name, Class 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 + *

+ * This class is part of the ReflectionUtils 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 + *

+ * This class is part of the ReflectionUtils 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, 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 Minecraft 1.7.10 + *

+ * If this enumeration is no longer up-to-date, please let me know in my forum post + *

+ * This class is part of the ReflectionUtils 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 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; + } + } + +} diff --git a/src/main/java/fr/arthurdanjou/template/utils/reflection/ScoreBoardReflection.java b/src/main/java/fr/arthurdanjou/template/utils/reflection/ScoreBoardReflection.java new file mode 100644 index 0000000..902a01f --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/utils/reflection/ScoreBoardReflection.java @@ -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> 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> obcOptionalClass(String className) { + return optionalClass(obcClassName(className)); + } + + public static Optional> optionalClass(String className) { + try { + return Optional.of(Class.forName(className)); + } catch (ClassNotFoundException e) { + return Optional.empty(); + } + } + + @SuppressWarnings("unchecked") + public static > E enumValueOf(Class enumClass, String enumName) { + return Enum.valueOf((Class) enumClass, enumName.toUpperCase(Locale.ROOT)); + } + +} diff --git a/src/main/java/fr/arthurdanjou/template/utils/team/Team.java b/src/main/java/fr/arthurdanjou/template/utils/team/Team.java new file mode 100644 index 0000000..ab0a764 --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/utils/team/Team.java @@ -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 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(); + } + +} diff --git a/src/main/java/fr/arthurdanjou/template/utils/team/TeamHandler.java b/src/main/java/fr/arthurdanjou/template/utils/team/TeamHandler.java new file mode 100644 index 0000000..8096415 --- /dev/null +++ b/src/main/java/fr/arthurdanjou/template/utils/team/TeamHandler.java @@ -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 teams; + private List 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 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 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 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; + } + } + +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..6d6f3a0 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,6 @@ +main: fr.arthurdanjou.template.Main +name: Template +version: 1.0 +author: Arthur Danjou +api-version: 1.13 +commands: