mirror of
https://github.com/songoda/SongodaCore.git
synced 2024-11-23 18:45:34 +01:00
Merge branch 'Testing' into 'master'
Full Core Library See merge request Songoda/songodaupdater!1
This commit is contained in:
commit
af2e1b8cab
21
.gitlab-ci.yml
Normal file
21
.gitlab-ci.yml
Normal file
@ -0,0 +1,21 @@
|
||||
stages:
|
||||
- build
|
||||
|
||||
variables:
|
||||
name: "SongodaCore"
|
||||
path: "/builds/$CI_PROJECT_PATH"
|
||||
version: "2.0"
|
||||
|
||||
build:
|
||||
stage: build
|
||||
image: maven:3.5.3-jdk-8
|
||||
script:
|
||||
- find $path/ -type f -name "*.xml" -print0 | xargs -0 sed -i -e s/maven-version-number/$version/g
|
||||
- find $path/ -type f -name "*.yml" -print0 | xargs -0 sed -i -e s/maven-version-number/$version/g
|
||||
- mvn clean package
|
||||
- find $path/ -depth -path '*original*' -delete
|
||||
- mv $path/target/*.jar $path/
|
||||
artifacts:
|
||||
name: $name-$version
|
||||
paths:
|
||||
- "$path/*.jar"
|
44
SongodaCore.iml
Normal file
44
SongodaCore.iml
Normal file
@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
|
||||
<output url="file://$MODULE_DIR$/target/classes" />
|
||||
<output-test url="file://$MODULE_DIR$/target/test-classes" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Maven: org.spigotmc:spigot:1.14.4" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.gmail.filoghost.holographicdisplays:holographicdisplays-api:2.3.2" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.sainttx.holograms:Holograms:2.9.1" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: net.tnemc:Reserve:0.1.3.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.black_ixx:playerpoints:2.1.4" level="project" />
|
||||
<orderEntry type="library" name="Maven: net.milkbowl:vault:1.7.1" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.25" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.slf4j:slf4j-nop:1.7.25" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.zaxxer:HikariCP:3.2.0" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.xerial:sqlite-jdbc:3.23.1" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.sk89q.worldguard:worldguard-bukkit:7.0.1-SNAPSHOT" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.bukkit:bukkit:1.14.4-R0.1-SNAPSHOT" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: commons-lang:commons-lang:2.6" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.guava:guava:21.0" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.code.gson:gson:2.8.0" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.yaml:snakeyaml:1.23" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.sk89q.worldedit:worldedit-bukkit:7.0.1-SNAPSHOT" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.sk89q.worldguard:worldguard-core:7.0.1-SNAPSHOT" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.sk89q.worldguard.worldguard-libs:core:7.0.1-SNAPSHOT" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.sk89q.worldedit:worldedit-core:7.0.1-SNAPSHOT" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.sk89q.worldedit.worldedit-libs:core:7.0.1-SNAPSHOT" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: de.schlichtherle:truezip:6.8.3" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.mozilla:rhino:1.7.11" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.google.code.findbugs:jsr305:1.3.9" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.flywaydb:flyway-core:3.0" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: io.papermc:paperlib:1.0.2" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: com.sk89q:commandbook:2.3" level="project" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="Maven: org.bstats:bstats-bukkit:1.5" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.songoda:UltimateStacker:1.2.8" level="project" />
|
||||
<orderEntry type="library" name="Maven: com.bgsoftware:WildStacker:2-9-0" level="project" />
|
||||
<orderEntry type="library" name="Maven: uk.antiperson:stackmob:4-0-2" level="project" />
|
||||
</component>
|
||||
</module>
|
120
pom.xml
120
pom.xml
@ -1,11 +1,11 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0">
|
||||
<groupId>com.songoda</groupId>
|
||||
<artifactId>SongodaUpdater</artifactId>
|
||||
<artifactId>SongodaCore</artifactId>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<version>1</version>
|
||||
<version>maven-version-number</version>
|
||||
<build>
|
||||
<defaultGoal>clean install</defaultGoal>
|
||||
<finalName>SongodaUpdater-${project.version}</finalName>
|
||||
<finalName>SongodaCore-${project.version}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
@ -21,14 +21,124 @@
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>private</id>
|
||||
<url>http://repo.songoda.com/artifactory/private/</url>
|
||||
<url>https://repo.songoda.com/artifactory/private/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.destroystokyo.papermc</groupId>
|
||||
<artifactId>paper</artifactId>
|
||||
<version>1.14.4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!--dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot</artifactId>
|
||||
<version>1.14</version>
|
||||
<version>1.14.4</version>
|
||||
</dependency-->
|
||||
<dependency>
|
||||
<groupId>com.gmail.filoghost.holographicdisplays</groupId>
|
||||
<artifactId>holographicdisplays-api</artifactId>
|
||||
<version>2.3.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sainttx.holograms</groupId>
|
||||
<artifactId>Holograms</artifactId>
|
||||
<version>2.9.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.tnemc</groupId>
|
||||
<artifactId>Reserve</artifactId>
|
||||
<version>0.1.3.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.black_ixx</groupId>
|
||||
<artifactId>PlayerPoints</artifactId>
|
||||
<version>2.1.4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.milkbowl</groupId>
|
||||
<artifactId>VaultAPI</artifactId>
|
||||
<version>1.7.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>us.myles.viaversion-bukkit</groupId>
|
||||
<artifactId>ViaVersion</artifactId>
|
||||
<version>2.1.3</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>protocolsupport</groupId>
|
||||
<artifactId>ProtocolSupport</artifactId>
|
||||
<version>4.29</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.googlecode.json-simple</groupId>
|
||||
<artifactId>json-simple</artifactId>
|
||||
<version>1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
<version>13.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>1.25</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.25</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-nop</artifactId>
|
||||
<version>1.7.25</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
<version>3.2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.23.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sk89q.worldguard</groupId>
|
||||
<artifactId>worldguard-bukkit</artifactId>
|
||||
<version>7.0.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sk89q.worldedit</groupId>
|
||||
<artifactId>worldedit-bukkit</artifactId>
|
||||
<version>7.0.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.songoda</groupId>
|
||||
<artifactId>UltimateStacker</artifactId>
|
||||
<version>1.9.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.bgsoftware</groupId>
|
||||
<artifactId>WildStacker</artifactId>
|
||||
<version>2-9-0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>uk.antiperson</groupId>
|
||||
<artifactId>stackmob</artifactId>
|
||||
<version>4-0-2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
393
src/main/java/com/songoda/core/SongodaCore.java
Normal file
393
src/main/java/com/songoda/core/SongodaCore.java
Normal file
@ -0,0 +1,393 @@
|
||||
package com.songoda.core;
|
||||
|
||||
import com.songoda.core.core.PluginInfo;
|
||||
import com.songoda.core.core.LocaleModule;
|
||||
import com.songoda.core.core.PluginInfoModule;
|
||||
import com.songoda.core.core.SongodaCoreCommand;
|
||||
import com.songoda.core.core.SongodaCoreDiagCommand;
|
||||
import com.songoda.core.commands.CommandManager;
|
||||
import com.songoda.core.compatibility.ClientVersion;
|
||||
import com.songoda.core.compatibility.LegacyMaterials;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.event.server.PluginDisableEvent;
|
||||
import org.bukkit.event.server.PluginEnableEvent;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
public class SongodaCore {
|
||||
|
||||
private final static String prefix = "[SongodaCore]";
|
||||
|
||||
/**
|
||||
* Whenever we make a major change to the core GUI, updater,
|
||||
* or other function used by the core, increment this number
|
||||
*/
|
||||
private final static int coreRevision = 2;
|
||||
private final static int updaterVersion = 1;
|
||||
|
||||
private final static Set<PluginInfo> registeredPlugins = new HashSet<>();
|
||||
|
||||
private static SongodaCore INSTANCE = null;
|
||||
private JavaPlugin piggybackedPlugin;
|
||||
private CommandManager commandManager;
|
||||
private EventListener loginListener;
|
||||
private ShadedEventListener shadingListener;
|
||||
|
||||
public static boolean hasShading() {
|
||||
// sneaky hack to check the package name since maven tries to re-shade all references to the package string
|
||||
return !SongodaCore.class.getPackage().getName().equals(new String(new char[]{'c','o','m','.','s','o','n','g','o','d','a','.','c','o','r','e'}));
|
||||
}
|
||||
|
||||
public static void registerPlugin(JavaPlugin plugin, int pluginID, LegacyMaterials icon) {
|
||||
registerPlugin(plugin, pluginID, icon == null ? "STONE" : icon.name());
|
||||
}
|
||||
|
||||
public static void registerPlugin(JavaPlugin plugin, int pluginID, String icon) {
|
||||
if(INSTANCE == null) {
|
||||
// First: are there any other instances of SongodaCore active?
|
||||
for (Class<?> clazz : Bukkit.getServicesManager().getKnownServices()) {
|
||||
if(clazz.getSimpleName().equals("SongodaCore")) {
|
||||
try {
|
||||
// test to see if we're up to date
|
||||
int otherVersion = (int) clazz.getMethod("getCoreVersion").invoke(null);
|
||||
if(otherVersion >= getCoreVersion()) {
|
||||
// use the active service
|
||||
clazz.getMethod("registerPlugin", JavaPlugin.class, int.class, String.class).invoke(null, plugin, pluginID, icon);
|
||||
|
||||
if(hasShading()) {
|
||||
(INSTANCE = new SongodaCore()).piggybackedPlugin = plugin;
|
||||
INSTANCE.shadingListener = new ShadedEventListener();
|
||||
Bukkit.getPluginManager().registerEvents(INSTANCE.shadingListener, plugin);
|
||||
}
|
||||
} else {
|
||||
// we are newer than the registered service: steal all of its registrations
|
||||
// grab the old core's registrations
|
||||
List otherPlugins = (List) clazz.getMethod("getPlugins").invoke(null);
|
||||
// destroy the old core
|
||||
Object oldCore = clazz.getMethod("getInstance").invoke(null);
|
||||
Method destruct = clazz.getDeclaredMethod("destroy");
|
||||
destruct.setAccessible(true);
|
||||
destruct.invoke(oldCore);
|
||||
// register ourselves as the SongodaCore service!
|
||||
INSTANCE = new SongodaCore(plugin);
|
||||
INSTANCE.init();
|
||||
INSTANCE.register(plugin, pluginID, icon);
|
||||
Bukkit.getServicesManager().register(SongodaCore.class, INSTANCE, plugin, ServicePriority.Normal);
|
||||
// we need (JavaPlugin plugin, int pluginID, String icon) for our object
|
||||
if(!otherPlugins.isEmpty()) {
|
||||
Object testSubject = otherPlugins.get(0);
|
||||
Class otherPluginInfo = testSubject.getClass();
|
||||
Method otherPluginInfo_getJavaPlugin = otherPluginInfo.getMethod("getJavaPlugin");
|
||||
Method otherPluginInfo_getSongodaId = otherPluginInfo.getMethod("getSongodaId");
|
||||
Method otherPluginInfo_getCoreIcon = otherPluginInfo.getMethod("getCoreIcon");
|
||||
for(Object other : otherPlugins) {
|
||||
INSTANCE.register(
|
||||
(JavaPlugin) otherPluginInfo_getJavaPlugin.invoke(other),
|
||||
(int) otherPluginInfo_getSongodaId.invoke(other),
|
||||
(String) otherPluginInfo_getCoreIcon.invoke(other));
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// register ourselves as the SongodaCore service!
|
||||
INSTANCE = new SongodaCore(plugin);
|
||||
INSTANCE.init();
|
||||
INSTANCE.register(plugin, pluginID, icon);
|
||||
Bukkit.getServicesManager().register(SongodaCore.class, INSTANCE, plugin, ServicePriority.Normal);
|
||||
}
|
||||
}
|
||||
|
||||
SongodaCore() {
|
||||
commandManager = null;
|
||||
}
|
||||
|
||||
SongodaCore(JavaPlugin javaPlugin) {
|
||||
piggybackedPlugin = javaPlugin;
|
||||
commandManager = new CommandManager(piggybackedPlugin);
|
||||
loginListener = new EventListener();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
shadingListener = new ShadedEventListener();
|
||||
commandManager.registerCommandDynamically(new SongodaCoreCommand())
|
||||
.addSubCommand(new SongodaCoreDiagCommand());
|
||||
Bukkit.getPluginManager().registerEvents(loginListener, piggybackedPlugin);
|
||||
Bukkit.getPluginManager().registerEvents(shadingListener, piggybackedPlugin);
|
||||
// we aggressevely want to own this command
|
||||
tasks.add(Bukkit.getScheduler().runTaskLaterAsynchronously(piggybackedPlugin, ()->{CommandManager.registerCommandDynamically(piggybackedPlugin, "songoda", commandManager, commandManager);}, 10 * 60 * 1));
|
||||
tasks.add(Bukkit.getScheduler().runTaskLaterAsynchronously(piggybackedPlugin, ()->{CommandManager.registerCommandDynamically(piggybackedPlugin, "songoda", commandManager, commandManager);}, 20 * 60 * 1));
|
||||
tasks.add(Bukkit.getScheduler().runTaskLaterAsynchronously(piggybackedPlugin, ()->{CommandManager.registerCommandDynamically(piggybackedPlugin, "songoda", commandManager, commandManager);}, 20 * 60 * 2));
|
||||
tasks.add(Bukkit.getScheduler().runTaskLaterAsynchronously(piggybackedPlugin, ()->registerAllPlugins(), 20 * 60 * 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to yield this core to a newer core
|
||||
*/
|
||||
private void destroy() {
|
||||
Bukkit.getServicesManager().unregister(SongodaCore.class, INSTANCE);
|
||||
tasks.stream().filter(task -> task != null && !task.isCancelled())
|
||||
.forEach(task -> Bukkit.getScheduler().cancelTask(task.getTaskId()));
|
||||
HandlerList.unregisterAll(loginListener);
|
||||
if(!hasShading()) {
|
||||
HandlerList.unregisterAll(shadingListener);
|
||||
}
|
||||
registeredPlugins.clear();
|
||||
commandManager = null;
|
||||
loginListener = null;
|
||||
}
|
||||
private ArrayList<BukkitTask> tasks = new ArrayList();
|
||||
|
||||
/**
|
||||
* Register plugins that may not have been updated yet
|
||||
*/
|
||||
private void registerAllPlugins() {
|
||||
PluginManager pm = Bukkit.getPluginManager();
|
||||
String p;
|
||||
if (!isRegistered(p = "EpicAnchors") && pm.isPluginEnabled(p)) {
|
||||
register((JavaPlugin) pm.getPlugin(p), 31, LegacyMaterials.END_PORTAL_FRAME.name());
|
||||
}
|
||||
if (!isRegistered(p = "EpicBosses") && pm.isPluginEnabled(p)) {
|
||||
register((JavaPlugin) pm.getPlugin(p), 19, LegacyMaterials.ZOMBIE_SPAWN_EGG.name());
|
||||
}
|
||||
if (!isRegistered(p = "EpicEnchants") && pm.isPluginEnabled(p)) {
|
||||
register((JavaPlugin) pm.getPlugin(p), 67, LegacyMaterials.DIAMOND_SWORD.name());
|
||||
}
|
||||
if (!isRegistered(p = "EpicFarming") && pm.isPluginEnabled(p)) {
|
||||
register((JavaPlugin) pm.getPlugin(p), 21, LegacyMaterials.WHEAT.name());
|
||||
}
|
||||
if (!isRegistered(p = "EpicFurnaces") && pm.isPluginEnabled(p)) {
|
||||
register((JavaPlugin) pm.getPlugin(p), 22, LegacyMaterials.FURNACE.name());
|
||||
}
|
||||
if (!isRegistered(p = "EpicHeads") && pm.isPluginEnabled(p)) {
|
||||
register((JavaPlugin) pm.getPlugin(p), 26, LegacyMaterials.PLAYER_HEAD.name());
|
||||
}
|
||||
if (!isRegistered(p = "EpicHoppers") && pm.isPluginEnabled(p)) {
|
||||
register((JavaPlugin) pm.getPlugin(p), 15, LegacyMaterials.HOPPER.name());
|
||||
}
|
||||
if (!isRegistered(p = "EpicLevels") && pm.isPluginEnabled(p)) {
|
||||
register((JavaPlugin) pm.getPlugin(p), 44, LegacyMaterials.NETHER_STAR.name());
|
||||
}
|
||||
if (!isRegistered(p = "EpicSpawners") && pm.isPluginEnabled(p)) {
|
||||
register((JavaPlugin) pm.getPlugin(p), 13, LegacyMaterials.SPAWNER.name());
|
||||
}
|
||||
if (!isRegistered(p = "EpicVouchers") && pm.isPluginEnabled(p)) {
|
||||
register((JavaPlugin) pm.getPlugin(p), 25, LegacyMaterials.EMERALD.name());
|
||||
}
|
||||
if (!isRegistered(p = "FabledSkyBlock") && pm.isPluginEnabled(p)) {
|
||||
register((JavaPlugin) pm.getPlugin(p), 17, LegacyMaterials.GRASS_BLOCK.name());
|
||||
}
|
||||
if (!isRegistered(p = "UltimateCatcher") && pm.isPluginEnabled(p)) {
|
||||
register((JavaPlugin) pm.getPlugin(p), 51, LegacyMaterials.EGG.name());
|
||||
}
|
||||
if (!isRegistered(p = "UltimateClaims") && pm.isPluginEnabled(p)) {
|
||||
register((JavaPlugin) pm.getPlugin(p), 65, LegacyMaterials.CHEST.name());
|
||||
}
|
||||
if (!isRegistered(p = "UltimateFishing") && pm.isPluginEnabled(p)) {
|
||||
register((JavaPlugin) pm.getPlugin(p), 59, LegacyMaterials.COD.name());
|
||||
}
|
||||
if (!isRegistered(p = "UltimateKits") && pm.isPluginEnabled(p)) {
|
||||
register((JavaPlugin) pm.getPlugin(p), 14, LegacyMaterials.BEACON.name());
|
||||
}
|
||||
if (!isRegistered(p = "UltimateModeration") && pm.isPluginEnabled(p)) {
|
||||
register((JavaPlugin) pm.getPlugin(p), 29, LegacyMaterials.DIAMOND_CHESTPLATE.name());
|
||||
}
|
||||
if (!isRegistered(p = "UltimateRepairing") && pm.isPluginEnabled(p)) {
|
||||
register((JavaPlugin) pm.getPlugin(p), 20, LegacyMaterials.ANVIL.name());
|
||||
}
|
||||
if (!isRegistered(p = "UltimateStacker") && pm.isPluginEnabled(p)) {
|
||||
register((JavaPlugin) pm.getPlugin(p), 16, LegacyMaterials.IRON_INGOT.name());
|
||||
}
|
||||
if (!isRegistered(p = "UltimateTimber") && pm.isPluginEnabled(p)) {
|
||||
register((JavaPlugin) pm.getPlugin(p), 18, LegacyMaterials.IRON_AXE.name());
|
||||
}
|
||||
}
|
||||
|
||||
private void register(JavaPlugin plugin, int pluginID, String icon) {
|
||||
System.out.println(getPrefix() + "Hooked " + plugin.getName() + ".");
|
||||
PluginInfo info = new PluginInfo(plugin, pluginID, icon);
|
||||
// don't forget to check for language pack updates ;)
|
||||
info.addModule(new LocaleModule());
|
||||
registeredPlugins.add(info);
|
||||
tasks.add(Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, () -> update(info), 60L));
|
||||
}
|
||||
|
||||
private void update(PluginInfo plugin) {
|
||||
try {
|
||||
URL url = new URL("https://update.songoda.com/index.php?plugin=" + plugin.getSongodaId()
|
||||
+ "&version=" + plugin.getJavaPlugin().getDescription().getVersion()
|
||||
+ "&updaterVersion=" + updaterVersion);
|
||||
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
|
||||
urlConnection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11");
|
||||
urlConnection.setRequestProperty("Accept", "*/*");
|
||||
urlConnection.setConnectTimeout(5000);
|
||||
InputStream is = urlConnection.getInputStream();
|
||||
InputStreamReader isr = new InputStreamReader(is);
|
||||
|
||||
int numCharsRead;
|
||||
char[] charArray = new char[1024];
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while ((numCharsRead = isr.read(charArray)) > 0) {
|
||||
sb.append(charArray, 0, numCharsRead);
|
||||
}
|
||||
urlConnection.disconnect();
|
||||
|
||||
String jsonString = sb.toString();
|
||||
JSONObject json = (JSONObject) new JSONParser().parse(jsonString);
|
||||
|
||||
plugin.setLatestVersion((String) json.get("latestVersion"));
|
||||
plugin.setMarketplaceLink((String) json.get("link"));
|
||||
plugin.setNotification((String) json.get("notification"));
|
||||
plugin.setChangeLog((String) json.get("changeLog"));
|
||||
|
||||
plugin.setJson(json);
|
||||
|
||||
for (PluginInfoModule module : plugin.getModules()) {
|
||||
module.run(plugin);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
final String er = e.getMessage();
|
||||
System.out.println("Connection with Songoda servers failed: " + (er.contains("URL") ? er.substring(0, er.indexOf("URL") + 3) : er));
|
||||
} catch (ParseException e) {
|
||||
System.out.println("Failed to parse json.");
|
||||
}
|
||||
}
|
||||
|
||||
public static List<PluginInfo> getPlugins() {
|
||||
return new ArrayList<>(registeredPlugins);
|
||||
}
|
||||
|
||||
public static int getCoreVersion() {
|
||||
return coreRevision;
|
||||
}
|
||||
|
||||
public static int getUpdaterVersion() {
|
||||
return updaterVersion;
|
||||
}
|
||||
|
||||
public static String getPrefix() {
|
||||
return prefix + " ";
|
||||
}
|
||||
|
||||
public static boolean isRegistered(String plugin) {
|
||||
return registeredPlugins.stream().anyMatch(p -> p.getJavaPlugin().getName().equalsIgnoreCase(plugin));
|
||||
}
|
||||
|
||||
public static JavaPlugin getHijackedPlugin() {
|
||||
return INSTANCE == null ? null : INSTANCE.piggybackedPlugin;
|
||||
}
|
||||
|
||||
public static SongodaCore getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private static class ShadedEventListener implements Listener {
|
||||
boolean via = false;
|
||||
boolean proto = false;
|
||||
|
||||
ShadedEventListener() {
|
||||
if ((via = Bukkit.getPluginManager().isPluginEnabled("ViaVersion"))) {
|
||||
Bukkit.getOnlinePlayers().forEach(p -> ClientVersion.onLoginVia(p));
|
||||
} else if ((proto = Bukkit.getPluginManager().isPluginEnabled("ProtocolSupport"))) {
|
||||
Bukkit.getOnlinePlayers().forEach(p -> ClientVersion.onLoginProtocol(p));
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
void onLogin(PlayerLoginEvent event) {
|
||||
if (via) {
|
||||
ClientVersion.onLoginVia(event.getPlayer());
|
||||
} else if (proto) {
|
||||
ClientVersion.onLoginProtocol(event.getPlayer());
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
void onLogout(PlayerQuitEvent event) {
|
||||
if (via) {
|
||||
ClientVersion.onLogout(event.getPlayer());
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
void onEnable(PluginEnableEvent event) {
|
||||
// technically shouldn't have online players here, but idk
|
||||
if (!via && (via = event.getPlugin().getName().equals("ViaVersion"))) {
|
||||
Bukkit.getOnlinePlayers().forEach(p -> ClientVersion.onLoginVia(p));
|
||||
} else if (!proto && (proto = event.getPlugin().getName().equals("ProtocolSupport"))) {
|
||||
Bukkit.getOnlinePlayers().forEach(p -> ClientVersion.onLoginProtocol(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class EventListener implements Listener {
|
||||
final HashMap<UUID, Long> lastCheck = new HashMap();
|
||||
|
||||
@EventHandler
|
||||
void onLogin(PlayerLoginEvent event) {
|
||||
final Player player = event.getPlayer();
|
||||
// don't spam players with update checks
|
||||
long now = System.currentTimeMillis();
|
||||
Long last = lastCheck.get(player.getUniqueId());
|
||||
if(last != null && now - 10000 < last) return;
|
||||
lastCheck.put(player.getUniqueId(), now);
|
||||
// is this player good to revieve update notices?
|
||||
if (!event.getPlayer().isOp() && !player.hasPermission("songoda.updatecheck")) return;
|
||||
// check for updates! ;)
|
||||
for (PluginInfo plugin : getPlugins()) {
|
||||
if (plugin.getNotification() != null && plugin.getJavaPlugin().isEnabled())
|
||||
Bukkit.getScheduler().runTaskLaterAsynchronously(plugin.getJavaPlugin(), () ->
|
||||
player.sendMessage("[" + plugin.getJavaPlugin().getName() + "] " + plugin.getNotification()), 10L);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
void onDisable(PluginDisableEvent event) {
|
||||
// don't track disabled plugins
|
||||
PluginInfo pi = registeredPlugins.stream().filter(p -> event.getPlugin() == p.getJavaPlugin()).findFirst().orElse(null);
|
||||
if (pi != null) {
|
||||
registeredPlugins.remove(pi);
|
||||
}
|
||||
if (event.getPlugin() == piggybackedPlugin) {
|
||||
// uh-oh! Abandon ship!!
|
||||
Bukkit.getServicesManager().unregisterAll(piggybackedPlugin);
|
||||
// can we move somewhere else?
|
||||
if ((pi = registeredPlugins.stream().findFirst().orElse(null)) != null) {
|
||||
// move ourselves to this plugin
|
||||
piggybackedPlugin = pi.getJavaPlugin();
|
||||
Bukkit.getServicesManager().register(SongodaCore.class, INSTANCE, piggybackedPlugin, ServicePriority.Normal);
|
||||
Bukkit.getPluginManager().registerEvents(loginListener, piggybackedPlugin);
|
||||
Bukkit.getPluginManager().registerEvents(shadingListener, piggybackedPlugin);
|
||||
CommandManager.registerCommandDynamically(piggybackedPlugin, "songoda", commandManager, commandManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
145
src/main/java/com/songoda/core/SongodaPlugin.java
Normal file
145
src/main/java/com/songoda/core/SongodaPlugin.java
Normal file
@ -0,0 +1,145 @@
|
||||
package com.songoda.core;
|
||||
|
||||
import com.songoda.core.configuration.Config;
|
||||
import com.songoda.core.configuration.ConfigFileConfigurationAdapter;
|
||||
import com.songoda.core.locale.Locale;
|
||||
import com.songoda.core.utils.Metrics;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
/**
|
||||
* REMINDER: When converting plugins to use this, REMOVE METRICS <br>
|
||||
* Must not have two instances of Metrics enabled!
|
||||
*
|
||||
* @author jascotty2
|
||||
*/
|
||||
public abstract class SongodaPlugin extends JavaPlugin {
|
||||
|
||||
protected Locale locale;
|
||||
protected Config config = new Config(this);
|
||||
|
||||
protected ConsoleCommandSender console = Bukkit.getConsoleSender();
|
||||
private boolean emergencyStop = false;
|
||||
|
||||
public abstract void onPluginLoad();
|
||||
|
||||
public abstract void onPluginEnable();
|
||||
|
||||
public abstract void onPluginDisable();
|
||||
|
||||
/**
|
||||
* Called after reloadConfig() is called
|
||||
*/
|
||||
public abstract void onConfigReload();
|
||||
|
||||
/**
|
||||
* Any other plugin configuration files used by the plugin.
|
||||
*
|
||||
* @return a list of Configs that are used in addition to the main config.
|
||||
*/
|
||||
public abstract List<Config> getExtraConfig();
|
||||
|
||||
@Override
|
||||
public ConfigFileConfigurationAdapter getConfig() {
|
||||
return config.getFileConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reloadConfig() {
|
||||
config.load();
|
||||
onConfigReload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveConfig() {
|
||||
config.save();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onLoad() {
|
||||
try {
|
||||
onPluginLoad();
|
||||
} catch (Throwable t) {
|
||||
getLogger().log(Level.SEVERE, "Unexpected error while loading " + getDescription().getName() + ": Disabling plugin!", t);
|
||||
emergencyStop = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onEnable() {
|
||||
if(emergencyStop) {
|
||||
setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
console.sendMessage(ChatColor.GREEN + "=============================");
|
||||
console.sendMessage(String.format("%s%s %s by %sSongoda <3!", ChatColor.GRAY.toString(),
|
||||
getDescription().getName(), getDescription().getVersion(), ChatColor.DARK_PURPLE.toString()));
|
||||
console.sendMessage(String.format("%sAction: %s%s%s...", ChatColor.GRAY.toString(),
|
||||
ChatColor.GREEN.toString(), "Enabling", ChatColor.GRAY.toString()));
|
||||
|
||||
try {
|
||||
locale = Locale.loadDefaultLocale(this, "en_US");
|
||||
// plugin setup
|
||||
onPluginEnable();
|
||||
// Start Metrics
|
||||
Metrics.start(this);
|
||||
} catch (Throwable t) {
|
||||
getLogger().log(Level.SEVERE, "Unexpected error while loading " + getDescription().getName() + ": Disabling plugin!", t);
|
||||
emergencyStop = true;
|
||||
setEnabled(false);
|
||||
console.sendMessage(ChatColor.RED + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||
return;
|
||||
}
|
||||
|
||||
console.sendMessage(ChatColor.GREEN + "=============================");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onDisable() {
|
||||
if (emergencyStop) {
|
||||
return;
|
||||
}
|
||||
console.sendMessage(ChatColor.GREEN + "=============================");
|
||||
console.sendMessage(String.format("%s%s %s by %sSongoda <3!", ChatColor.GRAY.toString(),
|
||||
getDescription().getName(), getDescription().getVersion(), ChatColor.DARK_PURPLE.toString()));
|
||||
console.sendMessage(String.format("%sAction: %s%s%s...", ChatColor.GRAY.toString(),
|
||||
ChatColor.RED.toString(), "Disabling", ChatColor.GRAY.toString()));
|
||||
onPluginDisable();
|
||||
console.sendMessage(ChatColor.GREEN + "=============================");
|
||||
}
|
||||
|
||||
public ConsoleCommandSender getConsole() {
|
||||
return console;
|
||||
}
|
||||
|
||||
public Locale getLocale() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the plugin's locale to a specific language
|
||||
*
|
||||
* @param localeName locale to use, eg "en_US"
|
||||
* @param reload optionally reload the loaded locale if the locale didn't
|
||||
* change
|
||||
* @return true if the locale exists and was loaded successfully
|
||||
*/
|
||||
public boolean setLocale(String localeName, boolean reload) {
|
||||
if (locale != null && locale.getName().equals(localeName)) {
|
||||
return !reload || locale.reloadMessages();
|
||||
} else {
|
||||
Locale l = Locale.loadLocale(this, localeName);
|
||||
if (l != null) {
|
||||
locale = l;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
57
src/main/java/com/songoda/core/commands/AbstractCommand.java
Normal file
57
src/main/java/com/songoda/core/commands/AbstractCommand.java
Normal file
@ -0,0 +1,57 @@
|
||||
package com.songoda.core.commands;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractCommand {
|
||||
|
||||
private final boolean noConsole;
|
||||
private boolean hasArgs = false;
|
||||
|
||||
private final List<String> subCommand = new ArrayList<>();
|
||||
|
||||
protected AbstractCommand(boolean noConsole, String... command) {
|
||||
this.subCommand.addAll(Arrays.asList(command));
|
||||
this.noConsole = noConsole;
|
||||
}
|
||||
|
||||
protected AbstractCommand(boolean noConsole, boolean hasArgs, String... command) {
|
||||
this.subCommand.addAll(Arrays.asList(command));
|
||||
|
||||
this.hasArgs = hasArgs;
|
||||
this.noConsole = noConsole;
|
||||
}
|
||||
|
||||
public final List<String> getCommands() {
|
||||
return Collections.unmodifiableList(subCommand);
|
||||
}
|
||||
|
||||
public final void addSubCommand(String command) {
|
||||
subCommand.add(command);
|
||||
}
|
||||
|
||||
protected abstract ReturnType runCommand(CommandSender sender, String... args);
|
||||
|
||||
protected abstract List<String> onTab(CommandSender sender, String... args);
|
||||
|
||||
public abstract String getPermissionNode();
|
||||
|
||||
public abstract String getSyntax();
|
||||
|
||||
public abstract String getDescription();
|
||||
|
||||
public boolean hasArgs() {
|
||||
return hasArgs;
|
||||
}
|
||||
|
||||
public boolean isNoConsole() {
|
||||
return noConsole;
|
||||
}
|
||||
|
||||
public static enum ReturnType {SUCCESS, FAILURE, SYNTAX_ERROR}
|
||||
}
|
||||
|
275
src/main/java/com/songoda/core/commands/CommandManager.java
Normal file
275
src/main/java/com/songoda/core/commands/CommandManager.java
Normal file
@ -0,0 +1,275 @@
|
||||
package com.songoda.core.commands;
|
||||
|
||||
import com.songoda.core.compatibility.ServerProject;
|
||||
import com.songoda.core.compatibility.ServerVersion;
|
||||
import com.songoda.core.utils.TextUtils;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.SimpleCommandMap;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public class CommandManager implements CommandExecutor, TabCompleter {
|
||||
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
private final HashMap<String, SimpleNestedCommand> commands = new HashMap<>();
|
||||
private boolean allowLooseCommands = false;
|
||||
|
||||
public CommandManager(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public Set<String> getCommands() {
|
||||
return Collections.unmodifiableSet(commands.keySet());
|
||||
}
|
||||
|
||||
public List<String> getSubCommands(String command) {
|
||||
SimpleNestedCommand nested = command == null ? null : commands.get(command.toLowerCase());
|
||||
return nested == null ? Collections.EMPTY_LIST : nested.children.keySet().stream().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public Set<AbstractCommand> getAllCommands() {
|
||||
HashSet<AbstractCommand> all = new HashSet();
|
||||
commands.values().stream()
|
||||
.filter(c -> c.parent != null && !all.contains(c.parent))
|
||||
.forEach(c -> {
|
||||
all.add(c.parent);
|
||||
c.children.values().stream()
|
||||
.filter(s -> !all.contains(s))
|
||||
.forEach(s -> all.add(s));
|
||||
});
|
||||
return all;
|
||||
}
|
||||
|
||||
public CommandManager registerCommandDynamically(String command) {
|
||||
CommandManager.registerCommandDynamically(plugin, command, this, this);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Test compatibility. Seems to fail in 1.8
|
||||
*/
|
||||
public SimpleNestedCommand registerCommandDynamically(AbstractCommand abstractCommand) {
|
||||
SimpleNestedCommand nested = new SimpleNestedCommand(abstractCommand);
|
||||
abstractCommand.getCommands().stream().forEach(cmd -> {
|
||||
CommandManager.registerCommandDynamically(plugin, cmd, this, this);
|
||||
commands.put(cmd.toLowerCase(), nested);
|
||||
PluginCommand pcmd = plugin.getCommand(cmd);
|
||||
if(pcmd != null) {
|
||||
pcmd.setExecutor(this);
|
||||
pcmd.setTabCompleter(this);
|
||||
} else {
|
||||
plugin.getLogger().warning("Failed to register command: /" + cmd);
|
||||
}
|
||||
});
|
||||
return nested;
|
||||
}
|
||||
|
||||
public SimpleNestedCommand addCommand(AbstractCommand abstractCommand) {
|
||||
SimpleNestedCommand nested = new SimpleNestedCommand(abstractCommand);
|
||||
abstractCommand.getCommands().stream().forEach(cmd -> {
|
||||
commands.put(cmd.toLowerCase(), nested);
|
||||
PluginCommand pcmd = plugin.getCommand(cmd);
|
||||
if(pcmd != null) {
|
||||
pcmd.setExecutor(this);
|
||||
pcmd.setTabCompleter(this);
|
||||
} else {
|
||||
plugin.getLogger().warning("Failed to register command: /" + cmd);
|
||||
}
|
||||
});
|
||||
return nested;
|
||||
}
|
||||
|
||||
public CommandManager addCommands(AbstractCommand... abstractCommands) {
|
||||
for (AbstractCommand abstractCommand : abstractCommands)
|
||||
addCommand(abstractCommand);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandManager setExecutor(String command) {
|
||||
plugin.getCommand(command).setExecutor(this);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandManager setUseClosestCommand(boolean bool) {
|
||||
allowLooseCommands = bool;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender commandSender, Command command, String label, String[] args) {
|
||||
// grab the specific command that's being called
|
||||
SimpleNestedCommand nested = commands.get(command.getName().toLowerCase());
|
||||
if(nested != null) {
|
||||
// check to see if we're trying to call a sub-command
|
||||
if(args.length != 0 && !nested.children.isEmpty()) {
|
||||
String subCmd = getSubCommand(nested, args);
|
||||
if(subCmd != null) {
|
||||
// we have a subcommand to use!
|
||||
AbstractCommand sub = nested.children.get(subCmd);
|
||||
// adjust the arguments to match - BREAKING!!
|
||||
int i = subCmd.indexOf(' ') == -1 ? 1 : 2;
|
||||
String[] newArgs = new String[args.length - i];
|
||||
System.arraycopy(args, i, newArgs, 0, newArgs.length);
|
||||
// now process the command
|
||||
processRequirements(sub, commandSender, newArgs);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// if we've gotten this far, then just use the command we have
|
||||
if(nested.parent != null) {
|
||||
processRequirements(nested.parent, commandSender, args);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
commandSender.sendMessage(TextUtils.formatText("&7The command you entered does not exist or is spelt incorrectly."));
|
||||
return true;
|
||||
}
|
||||
|
||||
private String getSubCommand(SimpleNestedCommand nested, String[] args) {
|
||||
String cmd = args[0].toLowerCase();
|
||||
if(nested.children.containsKey(cmd))
|
||||
return cmd;
|
||||
String match = null;
|
||||
// support for two-argument subcommands
|
||||
if(args.length >= 2 && nested.children.keySet().stream().anyMatch(k -> k.indexOf(' ') != -1)) {
|
||||
cmd = String.join(" ", args[0], args[1]);
|
||||
if(nested.children.containsKey(cmd))
|
||||
return cmd;
|
||||
}
|
||||
// if we don't have a subcommand, should we search for one?
|
||||
if (allowLooseCommands) {
|
||||
// do a "closest match"
|
||||
int count = 0;
|
||||
for (String c : nested.children.keySet()) {
|
||||
if (c.startsWith(cmd)) {
|
||||
match = c;
|
||||
if (++count > 1) {
|
||||
// there can only be one!
|
||||
match = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
private void processRequirements(AbstractCommand command, CommandSender sender, String[] args) {
|
||||
if (!(sender instanceof Player) && command.isNoConsole()) {
|
||||
sender.sendMessage("&cYou must be a player to use this command...");
|
||||
return;
|
||||
}
|
||||
if (command.getPermissionNode() == null || sender.hasPermission(command.getPermissionNode())) {
|
||||
AbstractCommand.ReturnType returnType = command.runCommand(sender, args);
|
||||
if (returnType == AbstractCommand.ReturnType.SYNTAX_ERROR) {
|
||||
sender.sendMessage(TextUtils.formatText("&cInvalid Syntax!"));
|
||||
sender.sendMessage(TextUtils.formatText("&7The valid syntax is: &6" + command.getSyntax() + "&7."));
|
||||
}
|
||||
return;
|
||||
}
|
||||
sender.sendMessage(TextUtils.formatText("&cYou do not have permission to do that."));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
// grab the specific command that's being called
|
||||
SimpleNestedCommand nested = commands.get(command.getName().toLowerCase());
|
||||
if(nested != null) {
|
||||
if(args.length == 0) {
|
||||
return nested.parent != null ? nested.parent.onTab(sender, args) : null;
|
||||
}
|
||||
// check for each sub-command that they have access to
|
||||
final boolean op = sender.isOp();
|
||||
final boolean console = !(sender instanceof Player);
|
||||
if(args.length == 1) {
|
||||
// suggest sub-commands that this user has access to
|
||||
final String arg = args[0].toLowerCase();
|
||||
return nested.children.entrySet().stream()
|
||||
.filter(e -> !console || !e.getValue().isNoConsole())
|
||||
.filter(e -> e.getKey().startsWith(arg))
|
||||
.filter(e -> op || e.getValue().getPermissionNode() == null || sender.hasPermission(e.getValue().getPermissionNode()))
|
||||
.map(e -> e.getKey())
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
// more than one arg, let's check to see if we have a command here
|
||||
String subCmd = getSubCommand(nested, args);
|
||||
AbstractCommand sub;
|
||||
if(subCmd != null && (sub = nested.children.get(subCmd)) != null
|
||||
&& (!console || !sub.isNoConsole())
|
||||
&& (op || sub.getPermissionNode() == null || sender.hasPermission(sub.getPermissionNode()))) {
|
||||
// adjust the arguments to match - BREAKING!!
|
||||
int i = subCmd.indexOf(' ') == -1 ? 1 : 2;
|
||||
String[] newArgs = new String[args.length - i];
|
||||
System.arraycopy(args, i, newArgs, 0, newArgs.length);
|
||||
// we're good to go!
|
||||
return fetchList(sub, newArgs, sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
|
||||
private List<String> fetchList(AbstractCommand abstractCommand, String[] args, CommandSender sender) {
|
||||
List<String> list = abstractCommand.onTab(sender, args);
|
||||
String str = args[args.length - 1];
|
||||
if (list != null && str != null && str.length() >= 1) {
|
||||
try {
|
||||
list.removeIf(s -> !s.toLowerCase().startsWith(str.toLowerCase()));
|
||||
} catch (UnsupportedOperationException ignored) {
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static void registerCommandDynamically(Plugin plugin, String command, CommandExecutor executor, TabCompleter tabManager) {
|
||||
try {
|
||||
// Retrieve the SimpleCommandMap from the server
|
||||
Class<?> classCraftServer = Bukkit.getServer().getClass();
|
||||
Field fieldCommandMap = classCraftServer.getDeclaredField("commandMap");
|
||||
fieldCommandMap.setAccessible(true);
|
||||
SimpleCommandMap commandMap = (SimpleCommandMap) fieldCommandMap.get(Bukkit.getServer());
|
||||
|
||||
// Construct a new Command object
|
||||
Constructor<PluginCommand> constructorPluginCommand = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class);
|
||||
constructorPluginCommand.setAccessible(true);
|
||||
PluginCommand commandObject = constructorPluginCommand.newInstance(command, plugin);
|
||||
|
||||
// If we're on Paper 1.8, we need to register timings (spigot creates timings on init, paper creates it on register)
|
||||
// later versions of paper create timings if needed when the command is executed
|
||||
if(ServerProject.isServer(ServerProject.PAPER) && ServerVersion.isServerVersionBelow(ServerVersion.V1_9)) {
|
||||
commandObject.timings = co.aikar.timings.TimingsManager.getCommandTiming(plugin.getName().toLowerCase(), commandObject);
|
||||
}
|
||||
|
||||
// Set command action
|
||||
commandObject.setExecutor(executor);
|
||||
|
||||
// Set tab complete
|
||||
commandObject.setTabCompleter(tabManager);
|
||||
|
||||
// Register the command
|
||||
Field fieldKnownCommands = SimpleCommandMap.class.getDeclaredField("knownCommands");
|
||||
fieldKnownCommands.setAccessible(true);
|
||||
Map<String, Command> knownCommands = (Map<String, Command>) fieldKnownCommands.get(commandMap);
|
||||
knownCommands.put(command, commandObject);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.songoda.core.commands;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class SimpleNestedCommand {
|
||||
|
||||
final AbstractCommand parent;
|
||||
final HashMap<String, AbstractCommand> children = new HashMap();
|
||||
|
||||
protected SimpleNestedCommand(AbstractCommand parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public SimpleNestedCommand addSubCommand(AbstractCommand command) {
|
||||
command.getCommands().stream().forEach(cmd -> children.put(cmd.toLowerCase(), command));
|
||||
return this;
|
||||
}
|
||||
|
||||
public SimpleNestedCommand addSubCommands(AbstractCommand... commands) {
|
||||
Stream.of(commands).forEach(command -> command.getCommands().stream().forEach(cmd -> children.put(cmd.toLowerCase(), command)));
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
101
src/main/java/com/songoda/core/compatibility/ClientVersion.java
Normal file
101
src/main/java/com/songoda/core/compatibility/ClientVersion.java
Normal file
@ -0,0 +1,101 @@
|
||||
package com.songoda.core.compatibility;
|
||||
|
||||
import com.songoda.core.SongodaCore;
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Handy reference for checking a connected client's Minecraft version<br>
|
||||
* NOTE: this is automatically initialized through SongodaCore
|
||||
*/
|
||||
public class ClientVersion {
|
||||
|
||||
static HashMap<UUID, ServerVersion> players = new HashMap();
|
||||
|
||||
/**
|
||||
* Check to see what client version this player is connected to the server
|
||||
* with. Note that if a player is connecting with a newer client than the server,
|
||||
* this value will simply be the server version.
|
||||
*
|
||||
* @param player Player to check
|
||||
* @return ServerVersion that matches this player's Minecraft version
|
||||
*/
|
||||
public static ServerVersion getClientVersion(Player player) {
|
||||
if (player == null || !players.containsKey(player.getUniqueId())) {
|
||||
return ServerVersion.getServerVersion();
|
||||
}
|
||||
return players.get(player.getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Do Not Use: This is handled by SongodaCore.
|
||||
*/
|
||||
@Deprecated
|
||||
public static void onLoginProtocol(Player p) {
|
||||
Bukkit.getScheduler().runTaskLater(SongodaCore.getHijackedPlugin(), ()-> {
|
||||
if(p.isOnline()) {
|
||||
final int version = protocolsupport.api.ProtocolSupportAPI.getProtocolVersion(p).getId();
|
||||
players.put(p.getUniqueId(), protocolToVersion(version));
|
||||
}
|
||||
}, 20);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do Not Use: This is handled by SongodaCore.
|
||||
*/
|
||||
@Deprecated
|
||||
public static void onLoginVia(Player p) {
|
||||
Bukkit.getScheduler().runTaskLater(SongodaCore.getHijackedPlugin(), ()-> {
|
||||
if(p.isOnline()) {
|
||||
final int version = us.myles.ViaVersion.api.Via.getAPI().getPlayerVersion(p.getUniqueId());
|
||||
players.put(p.getUniqueId(), protocolToVersion(version));
|
||||
}
|
||||
}, 20);
|
||||
}
|
||||
|
||||
private static ServerVersion protocolToVersion(int version) {
|
||||
// https://github.com/ViaVersion/ViaVersion/blob/master/common/src/main/java/us/myles/ViaVersion/api/protocol/ProtocolVersion.java
|
||||
// https://github.com/ProtocolSupport/ProtocolSupport/blob/master/src/protocolsupport/api/ProtocolVersion.java
|
||||
switch (version) {
|
||||
case 4:
|
||||
case 5:
|
||||
return ServerVersion.V1_7;
|
||||
case 47:
|
||||
return ServerVersion.V1_8;
|
||||
case 107:
|
||||
case 108:
|
||||
case 109:
|
||||
case 110:
|
||||
return ServerVersion.V1_9;
|
||||
case 210:
|
||||
return ServerVersion.V1_10;
|
||||
case 315:
|
||||
case 316:
|
||||
return ServerVersion.V1_11;
|
||||
case 335:
|
||||
case 338:
|
||||
case 340:
|
||||
return ServerVersion.V1_12;
|
||||
case 393:
|
||||
case 401:
|
||||
case 404:
|
||||
return ServerVersion.V1_13;
|
||||
case 477:
|
||||
case 485:
|
||||
case 490:
|
||||
case 498:
|
||||
return ServerVersion.V1_14;
|
||||
}
|
||||
return version > 498 ? ServerVersion.getServerVersion() : ServerVersion.UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do Not Use: This is handled by SongodaCore.
|
||||
*/
|
||||
@Deprecated
|
||||
public static void onLogout(Player p) {
|
||||
players.remove(p.getUniqueId());
|
||||
}
|
||||
}
|
@ -0,0 +1,956 @@
|
||||
package com.songoda.core.compatibility;
|
||||
|
||||
import org.bukkit.Sound;
|
||||
|
||||
/**
|
||||
* Sounds that are compatible with server versions 1.7+ <br>
|
||||
* TODO: This needs work.
|
||||
* Finished 1.8, finished 1.9 blocks, resume with 1.9 entities<br>
|
||||
* Between 1.8 and 1.9, all sounds were renamed, and between 1.12 and 1.13, some
|
||||
* sounds were renamed. New sounds have been added by different versions, as
|
||||
* well. The intent of this class is to provide either the correct sound or a
|
||||
* near equivalent for the current server.
|
||||
*
|
||||
* @since 2019-08-25
|
||||
* @author jascotty2
|
||||
*/
|
||||
public enum CompatibleSounds {
|
||||
|
||||
// some of these values are missing an API value..
|
||||
// would using the raw strings be better?
|
||||
// 1.8 list:
|
||||
// https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/mapping-and-modding-tutorials/2213619-1-8-all-playsound-sound-arguments
|
||||
AMBIENT_CAVE(ServerVersion.V1_9, v("AMBIENCE_CAVE")),
|
||||
AMBIENT_UNDERWATER_ENTER,
|
||||
AMBIENT_UNDERWATER_EXIT,
|
||||
AMBIENT_UNDERWATER_LOOP,
|
||||
AMBIENT_UNDERWATER_LOOP_ADDITIONS,
|
||||
AMBIENT_UNDERWATER_LOOP_ADDITIONS_RARE,
|
||||
AMBIENT_UNDERWATER_LOOP_ADDITIONS_ULTRA_RARE,
|
||||
BLOCK_ANVIL_BREAK(ServerVersion.V1_9, v("ANVIL_BREAK")),
|
||||
BLOCK_ANVIL_DESTROY(ServerVersion.V1_9, v("ANVIL_BREAK", true)),
|
||||
BLOCK_ANVIL_FALL(ServerVersion.V1_9, v("ANVIL_LAND", true)),
|
||||
BLOCK_ANVIL_HIT(ServerVersion.V1_9, v("ANVIL_LAND", true)),
|
||||
BLOCK_ANVIL_LAND(ServerVersion.V1_9, v("ANVIL_LAND")),
|
||||
BLOCK_ANVIL_PLACE(ServerVersion.V1_9, v("ANVIL_LAND", true)),
|
||||
BLOCK_ANVIL_STEP(ServerVersion.V1_9, v("ANVIL_LAND", true)),
|
||||
BLOCK_ANVIL_USE("ANVIL_USE"),
|
||||
BLOCK_BAMBOO_BREAK(ServerVersion.V1_14, v(ServerVersion.V1_9, "BLOCK_WOOD_BREAK"), v(ServerVersion.V1_8, "DIG_WOOD")),
|
||||
BLOCK_BAMBOO_FALL,
|
||||
BLOCK_BAMBOO_HIT,
|
||||
BLOCK_BAMBOO_PLACE,
|
||||
BLOCK_BAMBOO_SAPLING_BREAK,
|
||||
BLOCK_BAMBOO_SAPLING_HIT,
|
||||
BLOCK_BAMBOO_SAPLING_PLACE,
|
||||
BLOCK_BAMBOO_STEP,
|
||||
BLOCK_BARREL_CLOSE,
|
||||
BLOCK_BARREL_OPEN,
|
||||
BLOCK_BEACON_ACTIVATE,
|
||||
BLOCK_BEACON_AMBIENT,
|
||||
BLOCK_BEACON_DEACTIVATE,
|
||||
BLOCK_BEACON_POWER_SELECT,
|
||||
BLOCK_BELL_RESONATE,
|
||||
BLOCK_BELL_USE,
|
||||
BLOCK_BLASTFURNACE_FIRE_CRACKLE,
|
||||
BLOCK_BREWING_STAND_BREW(ServerVersion.V1_9, v("LAVA_POP", true)),
|
||||
BLOCK_BUBBLE_COLUMN_BUBBLE_POP,
|
||||
BLOCK_BUBBLE_COLUMN_UPWARDS_AMBIENT,
|
||||
BLOCK_BUBBLE_COLUMN_UPWARDS_INSIDE,
|
||||
BLOCK_BUBBLE_COLUMN_WHIRLPOOL_AMBIENT,
|
||||
BLOCK_BUBBLE_COLUMN_WHIRLPOOL_INSIDE,
|
||||
BLOCK_CAMPFIRE_CRACKLE,
|
||||
BLOCK_CHEST_CLOSE(ServerVersion.V1_9, v("CHEST_CLOSE")),
|
||||
BLOCK_CHEST_LOCKED(ServerVersion.V1_9, v("CHEST_CLOSE", true)),
|
||||
BLOCK_CHEST_OPEN(ServerVersion.V1_9, v("CHEST_OPEN")),
|
||||
BLOCK_CHORUS_FLOWER_DEATH(ServerVersion.V1_9, v(null, true)), // I have no idea..
|
||||
BLOCK_CHORUS_FLOWER_GROW(ServerVersion.V1_9, v(null, true)), // I have no idea..
|
||||
BLOCK_COMPARATOR_CLICK(ServerVersion.V1_9, v("WOOD_CLICK", true)),
|
||||
BLOCK_COMPOSTER_EMPTY,
|
||||
BLOCK_COMPOSTER_FILL,
|
||||
BLOCK_COMPOSTER_FILL_SUCCESS,
|
||||
BLOCK_COMPOSTER_READY,
|
||||
BLOCK_CONDUIT_ACTIVATE,
|
||||
BLOCK_CONDUIT_AMBIENT,
|
||||
BLOCK_CONDUIT_AMBIENT_SHORT,
|
||||
BLOCK_CONDUIT_ATTACK_TARGET,
|
||||
BLOCK_CONDUIT_DEACTIVATE,
|
||||
BLOCK_CORAL_BLOCK_BREAK,
|
||||
BLOCK_CORAL_BLOCK_FALL,
|
||||
BLOCK_CORAL_BLOCK_HIT,
|
||||
BLOCK_CORAL_BLOCK_PLACE,
|
||||
BLOCK_CORAL_BLOCK_STEP,
|
||||
BLOCK_CROP_BREAK,
|
||||
BLOCK_DISPENSER_DISPENSE(ServerVersion.V1_9, v("SHOOT_ARROW", true)),
|
||||
BLOCK_DISPENSER_FAIL(ServerVersion.V1_9, v("WOOD_CLICK", true)),
|
||||
BLOCK_DISPENSER_LAUNCH(ServerVersion.V1_9, v("GHAST_FIREBALL", true)),
|
||||
BLOCK_ENCHANTMENT_TABLE_USE,
|
||||
BLOCK_ENDER_CHEST_CLOSE(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_ENDERCHEST_CLOSE"), v("CHEST_CLOSE", true)),
|
||||
BLOCK_ENDER_CHEST_OPEN(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_ENDERCHEST_OPEN"), v("CHEST_OPEN", true)),
|
||||
BLOCK_END_GATEWAY_SPAWN(ServerVersion.V1_9, v("PORTAL_TRIGGER", true)),
|
||||
BLOCK_END_PORTAL_FRAME_FILL,
|
||||
BLOCK_END_PORTAL_SPAWN,
|
||||
BLOCK_FENCE_GATE_CLOSE("DOOR_CLOSE"),
|
||||
BLOCK_FENCE_GATE_OPEN("DOOR_OPEN"),
|
||||
BLOCK_FIRE_AMBIENT("FIRE"),
|
||||
BLOCK_FIRE_EXTINGUISH("FIZZ"),
|
||||
BLOCK_FURNACE_FIRE_CRACKLE(ServerVersion.V1_9, v("FIRE", true)),
|
||||
BLOCK_GLASS_BREAK("GLASS"),
|
||||
BLOCK_GLASS_FALL(ServerVersion.V1_9, v("STEP_GRASS", true)),
|
||||
BLOCK_GLASS_HIT(ServerVersion.V1_9, v("STEP_GRASS", true)),
|
||||
BLOCK_GLASS_PLACE(ServerVersion.V1_9, v("STEP_GRASS", true)),
|
||||
BLOCK_GLASS_STEP(ServerVersion.V1_9, v("STEP_STONE", true)),
|
||||
BLOCK_GRASS_BREAK("DIG_GRASS"),
|
||||
BLOCK_GRASS_FALL(ServerVersion.V1_9, v("STEP_GRASS", true)),
|
||||
BLOCK_GRASS_HIT(ServerVersion.V1_9, v("STEP_GRASS", true)),
|
||||
BLOCK_GRASS_PLACE(ServerVersion.V1_9, v("STEP_GRASS", true)),
|
||||
BLOCK_GRASS_STEP("STEP_GRASS"),
|
||||
BLOCK_GRAVEL_BREAK("DIG_GRAVEL"),
|
||||
BLOCK_GRAVEL_FALL(ServerVersion.V1_9, v("STEP_GRAVEL", true)),
|
||||
BLOCK_GRAVEL_HIT(ServerVersion.V1_9, v("STEP_GRAVEL", true)),
|
||||
BLOCK_GRAVEL_PLACE(ServerVersion.V1_9, v("STEP_GRAVEL", true)),
|
||||
BLOCK_GRAVEL_STEP("STEP_GRAVEL"),
|
||||
BLOCK_GRINDSTONE_USE,
|
||||
BLOCK_IRON_DOOR_CLOSE("DOOR_CLOSE"),
|
||||
BLOCK_IRON_DOOR_OPEN("DOOR_OPEN"),
|
||||
BLOCK_IRON_TRAPDOOR_CLOSE("DOOR_CLOSE"),
|
||||
BLOCK_IRON_TRAPDOOR_OPEN("DOOR_OPEN"),
|
||||
BLOCK_LADDER_BREAK(ServerVersion.V1_9, v(null, true)),
|
||||
BLOCK_LADDER_FALL(ServerVersion.V1_9, v("STEP_LADDER", true)),
|
||||
BLOCK_LADDER_HIT(ServerVersion.V1_9, v(null, true)),
|
||||
BLOCK_LADDER_PLACE(ServerVersion.V1_9, v(null, true)),
|
||||
BLOCK_LADDER_STEP("STEP_LADDER"),
|
||||
BLOCK_LANTERN_BREAK,
|
||||
BLOCK_LANTERN_FALL,
|
||||
BLOCK_LANTERN_HIT,
|
||||
BLOCK_LANTERN_PLACE,
|
||||
BLOCK_LANTERN_STEP,
|
||||
BLOCK_LAVA_AMBIENT("LAVA"),
|
||||
BLOCK_LAVA_EXTINGUISH("FIZZ"),
|
||||
BLOCK_LAVA_POP("LAVA_POP"),
|
||||
BLOCK_LEVER_CLICK("WOOD_CLICK"),
|
||||
BLOCK_LILY_PAD_PLACE(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_WATERLILY_PLACE"), v(null, true)),
|
||||
BLOCK_METAL_BREAK(ServerVersion.V1_9, v(null, true)),
|
||||
BLOCK_METAL_FALL(ServerVersion.V1_9, v(null, true)),
|
||||
BLOCK_METAL_HIT(ServerVersion.V1_9, v(null, true)),
|
||||
BLOCK_METAL_PLACE(ServerVersion.V1_9, v(null, true)),
|
||||
BLOCK_METAL_PRESSURE_PLATE_CLICK_OFF(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_METAL_PRESSUREPLATE_CLICK_OFF"), v("WOOD_CLICK", true)),
|
||||
BLOCK_METAL_PRESSURE_PLATE_CLICK_ON(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_METAL_PRESSUREPLATE_CLICK_ON"), v("WOOD_CLICK", true)),
|
||||
BLOCK_METAL_STEP(ServerVersion.V1_9, v("STEP_STONE", true)),
|
||||
BLOCK_NETHER_WART_BREAK,
|
||||
BLOCK_NOTE_BLOCK_BANJO(ServerVersion.V1_14, v(ServerVersion.V1_13, "BLOCK_NOTE_BLOCK_GUITAR", true), v(ServerVersion.V1_9, "BLOCK_NOTE_GUITAR", true), v("NOTE_BASS_GUITAR", true)),
|
||||
BLOCK_NOTE_BLOCK_BASEDRUM(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_NOTE_BASEDRUM"), v("NOTE_BASS_DRUM")),
|
||||
BLOCK_NOTE_BLOCK_BASS(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_NOTE_BASS"), v("NOTE_BASS")),
|
||||
BLOCK_NOTE_BLOCK_BELL(ServerVersion.V1_13, v(ServerVersion.V1_12, "BLOCK_NOTE_BELL"), v(ServerVersion.V1_9, "BLOCK_NOTE_HARP", true), v("NOTE_PLING", true)),
|
||||
BLOCK_NOTE_BLOCK_BIT(ServerVersion.V1_14, v(ServerVersion.V1_13, "BLOCK_NOTE_BLOCK_BELL", true), v(ServerVersion.V1_12, "BLOCK_NOTE_BELL", true), v(ServerVersion.V1_9, "BLOCK_NOTE_HARP", true), v("NOTE_PLING", true)),
|
||||
BLOCK_NOTE_BLOCK_CHIME(ServerVersion.V1_13, v(ServerVersion.V1_12, "BLOCK_NOTE_CHIME"), v(ServerVersion.V1_9, "BLOCK_NOTE_PLING", true), v("NOTE_PLING", true)),
|
||||
BLOCK_NOTE_BLOCK_COW_BELL(ServerVersion.V1_14, v(ServerVersion.V1_13, "BLOCK_NOTE_BLOCK_BELL", true), v(ServerVersion.V1_12, "BLOCK_NOTE_BELL"), v(ServerVersion.V1_9, "BLOCK_NOTE_HARP", true), v("NOTE_PLING", true)),
|
||||
BLOCK_NOTE_BLOCK_DIDGERIDOO(ServerVersion.V1_14, v(ServerVersion.V1_13, "BLOCK_NOTE_BLOCK_FLUTE", true), v(ServerVersion.V1_12, "BLOCK_NOTE_FLUTE", true), v(ServerVersion.V1_9, "BLOCK_NOTE_HARP", true), v("NOTE_BASS_GUITAR", true)),
|
||||
BLOCK_NOTE_BLOCK_FLUTE(ServerVersion.V1_13, v(ServerVersion.V1_12, "BLOCK_NOTE_FLUTE"), v(ServerVersion.V1_9, "BLOCK_NOTE_HARP", true), v("NOTE_BASS_GUITAR", true)),
|
||||
BLOCK_NOTE_BLOCK_GUITAR(ServerVersion.V1_13, v(ServerVersion.V1_12, "BLOCK_NOTE_GUITAR"), v(ServerVersion.V1_9, "BLOCK_NOTE_HARP", true), v("NOTE_BASS_GUITAR")), // This value disappeared from the API from 1.9-1.11 (returned in 12)
|
||||
BLOCK_NOTE_BLOCK_HARP(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_NOTE_HARP"), v("NOTE_PIANO")),
|
||||
BLOCK_NOTE_BLOCK_HAT(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_NOTE_HAT"), v("NOTE_STICKS")),
|
||||
BLOCK_NOTE_BLOCK_IRON_XYLOPHONE(ServerVersion.V1_14, v(ServerVersion.V1_13, "BLOCK_NOTE_BLOCK_XYLOPHONE", true), v(ServerVersion.V1_9, "BLOCK_NOTE_XYLOPHONE", true), v("NOTE_PLING", true)),
|
||||
BLOCK_NOTE_BLOCK_PLING(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_NOTE_PLING"), v("NOTE_PLING")),
|
||||
BLOCK_NOTE_BLOCK_SNARE(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_NOTE_SNARE"), v("NOTE_SNARE_DRUM")),
|
||||
BLOCK_NOTE_BLOCK_XYLOPHONE(ServerVersion.V1_13, v(ServerVersion.V1_12, "BLOCK_NOTE_XYLOPHONE"), v(ServerVersion.V1_9, "BLOCK_NOTE_PLING", true), v("NOTE_PLING", true)),
|
||||
BLOCK_PISTON_CONTRACT("PISTON_RETRACT"),
|
||||
BLOCK_PISTON_EXTEND("PISTON_EXTEND"),
|
||||
BLOCK_PORTAL_AMBIENT("PORTAL"),
|
||||
BLOCK_PORTAL_TRAVEL("PORTAL_TRAVEL"),
|
||||
BLOCK_PORTAL_TRIGGER("PORTAL_TRIGGER"),
|
||||
BLOCK_PUMPKIN_CARVE,
|
||||
BLOCK_REDSTONE_TORCH_BURNOUT("FIZZ"),
|
||||
BLOCK_SAND_BREAK("DIG_SAND"),
|
||||
BLOCK_SAND_FALL(ServerVersion.V1_9, v("STEP_SAND", true)),
|
||||
BLOCK_SAND_HIT(ServerVersion.V1_9, v("STEP_SAND", true)),
|
||||
BLOCK_SAND_PLACE(ServerVersion.V1_9, v("STEP_SAND", true)),
|
||||
BLOCK_SAND_STEP("STEP_SAND"),
|
||||
BLOCK_SCAFFOLDING_BREAK,
|
||||
BLOCK_SCAFFOLDING_FALL,
|
||||
BLOCK_SCAFFOLDING_HIT,
|
||||
BLOCK_SCAFFOLDING_PLACE,
|
||||
BLOCK_SCAFFOLDING_STEP,
|
||||
BLOCK_SHULKER_BOX_CLOSE("CHEST_CLOSE"),
|
||||
BLOCK_SHULKER_BOX_OPEN("CHEST_OPEN"),
|
||||
BLOCK_SLIME_BLOCK_BREAK(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_SLIME_BREAK"), v(null, true)),
|
||||
BLOCK_SLIME_BLOCK_FALL(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_SLIME_FALL"), v(null, true)),
|
||||
BLOCK_SLIME_BLOCK_HIT(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_SLIME_HIT"), v(null, true)),
|
||||
BLOCK_SLIME_BLOCK_PLACE(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_SLIME_PLACE"), v(null, true)),
|
||||
BLOCK_SLIME_BLOCK_STEP(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_SLIME_STEP"), v(null, true)),
|
||||
BLOCK_SMOKER_SMOKE,
|
||||
BLOCK_SNOW_BREAK("DIG_SNOW"),
|
||||
BLOCK_SNOW_FALL(ServerVersion.V1_9, v("STEP_SNOW", true)),
|
||||
BLOCK_SNOW_HIT(ServerVersion.V1_9, v("STEP_SNOW", true)),
|
||||
BLOCK_SNOW_PLACE(ServerVersion.V1_9, v("STEP_SNOW", true)),
|
||||
BLOCK_SNOW_STEP("STEP_SNOW"),
|
||||
BLOCK_STONE_BREAK("DIG_STONE"),
|
||||
BLOCK_STONE_BUTTON_CLICK_OFF(ServerVersion.V1_9, v("WOOD_CLICK", true)),
|
||||
BLOCK_STONE_BUTTON_CLICK_ON(ServerVersion.V1_9, v("WOOD_CLICK", true)),
|
||||
BLOCK_STONE_FALL,
|
||||
BLOCK_STONE_HIT,
|
||||
BLOCK_STONE_PLACE,
|
||||
BLOCK_STONE_PRESSURE_PLATE_CLICK_OFF,
|
||||
BLOCK_STONE_PRESSURE_PLATE_CLICK_ON,
|
||||
BLOCK_STONE_STEP("STEP_STONE"),
|
||||
BLOCK_SWEET_BERRY_BUSH_BREAK,
|
||||
BLOCK_SWEET_BERRY_BUSH_PLACE,
|
||||
BLOCK_TRIPWIRE_ATTACH(ServerVersion.V1_9, v("WOOD_CLICK", true)),
|
||||
BLOCK_TRIPWIRE_CLICK_OFF(ServerVersion.V1_9, v("WOOD_CLICK", true)),
|
||||
BLOCK_TRIPWIRE_CLICK_ON(ServerVersion.V1_9, v("WOOD_CLICK", true)),
|
||||
BLOCK_TRIPWIRE_DETACH(ServerVersion.V1_9, v("WOOD_CLICK", true)),
|
||||
BLOCK_WATER_AMBIENT("WATER"),
|
||||
BLOCK_WET_GRASS_BREAK,
|
||||
BLOCK_WET_GRASS_FALL,
|
||||
BLOCK_WET_GRASS_HIT,
|
||||
BLOCK_WET_GRASS_PLACE,
|
||||
BLOCK_WET_GRASS_STEP,
|
||||
BLOCK_WOODEN_BUTTON_CLICK_OFF(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_WOOD_BUTTON_CLICK_ON"), v("WOOD_CLICK")),
|
||||
BLOCK_WOODEN_BUTTON_CLICK_ON(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_WOOD_BUTTON_CLICK_ON"), v("WOOD_CLICK")),
|
||||
BLOCK_WOODEN_DOOR_CLOSE("DOOR_CLOSE"),
|
||||
BLOCK_WOODEN_DOOR_OPEN("DOOR_OPEN"),
|
||||
BLOCK_WOODEN_PRESSURE_PLATE_CLICK_OFF(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_WOOD_PRESSUREPLATE_CLICK_OFF"), v("WOOD_CLICK", true)),
|
||||
BLOCK_WOODEN_PRESSURE_PLATE_CLICK_ON(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_WOOD_PRESSUREPLATE_CLICK_ON"), v("WOOD_CLICK", true)),
|
||||
BLOCK_WOODEN_TRAPDOOR_CLOSE("DOOR_OPEN"),
|
||||
BLOCK_WOODEN_TRAPDOOR_OPEN("DOOR_OPEN"),
|
||||
BLOCK_WOOD_BREAK("DIG_WOOD"),
|
||||
BLOCK_WOOD_FALL(ServerVersion.V1_9, v("STEP_WOOD", true)),
|
||||
BLOCK_WOOD_HIT(ServerVersion.V1_9, v("STEP_WOOD", true)),
|
||||
BLOCK_WOOD_PLACE(ServerVersion.V1_9, v("STEP_WOOD", true)),
|
||||
BLOCK_WOOD_STEP("STEP_WOOD"),
|
||||
BLOCK_WOOL_BREAK(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_CLOTH_BREAK"), v("DIG_WOOL")),
|
||||
BLOCK_WOOL_FALL(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_CLOTH_STEP", true), v("STEP_WOOL", true)),
|
||||
BLOCK_WOOL_HIT(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_CLOTH_STEP", true), v("STEP_WOOL", true)),
|
||||
BLOCK_WOOL_PLACE(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_CLOTH_STEP", true), v("STEP_WOOL", true)),
|
||||
BLOCK_WOOL_STEP(ServerVersion.V1_13, v(ServerVersion.V1_9, "BLOCK_CLOTH_STEP"), v("STEP_WOOL")),
|
||||
ENCHANT_THORNS_HIT,
|
||||
ENTITY_ARMOR_STAND_BREAK,
|
||||
ENTITY_ARMOR_STAND_FALL,
|
||||
ENTITY_ARMOR_STAND_HIT,
|
||||
ENTITY_ARMOR_STAND_PLACE,
|
||||
ENTITY_ARROW_HIT("SUCCESSFUL_HIT"), // these may be reversed..
|
||||
ENTITY_ARROW_HIT_PLAYER("ARROW_HIT"),
|
||||
ENTITY_ARROW_SHOOT("SHOOT_ARROW"),
|
||||
ENTITY_BAT_AMBIENT("BAT_IDLE"),
|
||||
ENTITY_BAT_DEATH("BAT_DEATH"),
|
||||
ENTITY_BAT_HURT("BAT_HURT"),
|
||||
ENTITY_BAT_LOOP("BAT_LOOP"),
|
||||
ENTITY_BAT_TAKEOFF("BAT_TAKEOFF"),
|
||||
ENTITY_BLAZE_AMBIENT("BLAZE_BREATH"),
|
||||
ENTITY_BLAZE_BURN("BLAZE_HIT"),
|
||||
ENTITY_BLAZE_DEATH("BLAZE_DEATH"),
|
||||
ENTITY_BLAZE_HURT("BLAZE_HIT"),
|
||||
ENTITY_BLAZE_SHOOT,
|
||||
ENTITY_BOAT_PADDLE_LAND,
|
||||
ENTITY_BOAT_PADDLE_WATER,
|
||||
ENTITY_CAT_AMBIENT(ServerVersion.V1_9, v("CAT_MEOW")),
|
||||
ENTITY_CAT_BEG_FOR_FOOD(ServerVersion.V1_14, v(ServerVersion.V1_9, "ENTITY_CAT_AMBIENT", true), v("CAT_MEOW", true)),
|
||||
ENTITY_CAT_DEATH,
|
||||
ENTITY_CAT_EAT(ServerVersion.V1_14, v(ServerVersion.V1_9, "ENTITY_GENERIC_EAT"), v("EAT")),
|
||||
ENTITY_CAT_HISS("CAT_HISS"),
|
||||
ENTITY_CAT_HURT("CAT_HIT"),
|
||||
ENTITY_CAT_PURR("CAT_PURR"),
|
||||
ENTITY_CAT_PURREOW("CAT_PURREOW"),
|
||||
ENTITY_CAT_STRAY_AMBIENT,
|
||||
ENTITY_CHICKEN_AMBIENT("CHICKEN_IDLE"),
|
||||
ENTITY_CHICKEN_DEATH,
|
||||
ENTITY_CHICKEN_EGG("CHICKEN_EGG_POP"),
|
||||
ENTITY_CHICKEN_HURT("CHICKEN_HURT"),
|
||||
ENTITY_CHICKEN_STEP("CHICKEN_WALK"),
|
||||
ENTITY_COD_AMBIENT,
|
||||
ENTITY_COD_DEATH,
|
||||
ENTITY_COD_FLOP,
|
||||
ENTITY_COD_HURT,
|
||||
ENTITY_COW_AMBIENT("COW_IDLE"),
|
||||
ENTITY_COW_DEATH("COW_HURT"),
|
||||
ENTITY_COW_HURT("COW_HURT"),
|
||||
ENTITY_COW_MILK,
|
||||
ENTITY_COW_STEP("COW_WALK"),
|
||||
ENTITY_CREEPER_DEATH("CREEPER_DEATH"),
|
||||
ENTITY_CREEPER_HURT,
|
||||
ENTITY_CREEPER_PRIMED("CREEPER_HISS"),
|
||||
ENTITY_DOLPHIN_AMBIENT,
|
||||
ENTITY_DOLPHIN_AMBIENT_WATER,
|
||||
ENTITY_DOLPHIN_ATTACK,
|
||||
ENTITY_DOLPHIN_DEATH,
|
||||
ENTITY_DOLPHIN_EAT(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_GENERIC_EAT", true), v("EAT", true)),
|
||||
ENTITY_DOLPHIN_HURT,
|
||||
ENTITY_DOLPHIN_JUMP,
|
||||
ENTITY_DOLPHIN_PLAY,
|
||||
ENTITY_DOLPHIN_SPLASH(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_GENERIC_SPLASH", true), v("SPLASH", true)),
|
||||
ENTITY_DOLPHIN_SWIM,
|
||||
ENTITY_DONKEY_AMBIENT("DONKEY_IDLE"),
|
||||
ENTITY_DONKEY_ANGRY("DONKEY_ANGRY"),
|
||||
ENTITY_DONKEY_CHEST,
|
||||
ENTITY_DONKEY_DEATH("DONKEY_DEATH"),
|
||||
ENTITY_DONKEY_HURT("DONKEY_HIT"),
|
||||
ENTITY_DRAGON_FIREBALL_EXPLODE,
|
||||
ENTITY_DROWNED_AMBIENT,
|
||||
ENTITY_DROWNED_AMBIENT_WATER,
|
||||
ENTITY_DROWNED_DEATH,
|
||||
ENTITY_DROWNED_DEATH_WATER,
|
||||
ENTITY_DROWNED_HURT,
|
||||
ENTITY_DROWNED_HURT_WATER,
|
||||
ENTITY_DROWNED_SHOOT,
|
||||
ENTITY_DROWNED_STEP,
|
||||
ENTITY_DROWNED_SWIM,
|
||||
ENTITY_EGG_THROW,
|
||||
ENTITY_ELDER_GUARDIAN_AMBIENT,
|
||||
ENTITY_ELDER_GUARDIAN_AMBIENT_LAND,
|
||||
ENTITY_ELDER_GUARDIAN_CURSE,
|
||||
ENTITY_ELDER_GUARDIAN_DEATH,
|
||||
ENTITY_ELDER_GUARDIAN_DEATH_LAND,
|
||||
ENTITY_ELDER_GUARDIAN_FLOP,
|
||||
ENTITY_ELDER_GUARDIAN_HURT,
|
||||
ENTITY_ELDER_GUARDIAN_HURT_LAND,
|
||||
ENTITY_ENDERMAN_AMBIENT(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_ENDERMEN_AMBIENT"), v("ENDERMAN_IDLE")),
|
||||
ENTITY_ENDERMAN_DEATH(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_ENDERMEN_DEATH"), v("ENDERMAN_DEATH")),
|
||||
ENTITY_ENDERMAN_HURT(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_ENDERMEN_HURT"), v("ENDERMAN_HIT")),
|
||||
ENTITY_ENDERMAN_SCREAM(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_ENDERMEN_SCREAM"), v("ENDERMAN_SCREAM")),
|
||||
ENTITY_ENDERMAN_STARE(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_ENDERMEN_STARE"), v("ENDERMAN_STARE")),
|
||||
ENTITY_ENDERMAN_TELEPORT(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_ENDERMEN_TELEPORT"), v("ENDERMAN_TELEPORT")),
|
||||
ENTITY_ENDERMITE_AMBIENT,
|
||||
ENTITY_ENDERMITE_DEATH,
|
||||
ENTITY_ENDERMITE_HURT,
|
||||
ENTITY_ENDERMITE_STEP,
|
||||
ENTITY_ENDER_DRAGON_AMBIENT(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_ENDERDRAGON_AMBIENT"), v("ENDERDRAGON_GROWL", true)),
|
||||
ENTITY_ENDER_DRAGON_DEATH(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_ENDERDRAGON_DEATH"), v("ENDERDRAGON_DEATH")),
|
||||
ENTITY_ENDER_DRAGON_FLAP(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_ENDERDRAGON_FLAP"), v("ENDERDRAGON_WINGS")),
|
||||
ENTITY_ENDER_DRAGON_GROWL(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_ENDERDRAGON_GROWL"), v("ENDERDRAGON_GROWL")),
|
||||
ENTITY_ENDER_DRAGON_HURT(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_ENDERDRAGON_HURT"), v("ENDERDRAGON_HIT")),
|
||||
ENTITY_ENDER_DRAGON_SHOOT(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_ENDERDRAGON_SHOOT"), v("GHAST_FIREBALL", true)),
|
||||
ENTITY_ENDER_EYE_DEATH(ServerVersion.V1_13, v(ServerVersion.V1_12, "ENTITY_ENDEREYE_DEATH"), v(ServerVersion.V1_9, "BLOCK_GLASS_BREAK", true), v("GLASS", true)),
|
||||
ENTITY_ENDER_EYE_LAUNCH(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_ENDEREYE_LAUNCH"), v(ServerVersion.V1_9, "BLOCK_PORTAL_TRIGGER", true), v("PORTAL_TRIGGER", true)),
|
||||
ENTITY_ENDER_PEARL_THROW,
|
||||
ENTITY_EVOKER_AMBIENT,
|
||||
ENTITY_EVOKER_CAST_SPELL,
|
||||
ENTITY_EVOKER_CELEBRATE,
|
||||
ENTITY_EVOKER_DEATH,
|
||||
ENTITY_EVOKER_FANGS_ATTACK,
|
||||
ENTITY_EVOKER_HURT,
|
||||
ENTITY_EVOKER_PREPARE_ATTACK,
|
||||
ENTITY_EVOKER_PREPARE_SUMMON,
|
||||
ENTITY_EVOKER_PREPARE_WOLOLO,
|
||||
ENTITY_EXPERIENCE_BOTTLE_THROW,
|
||||
ENTITY_EXPERIENCE_ORB_PICKUP("ORB_PICKUP"),
|
||||
ENTITY_FIREWORK_ROCKET_BLAST(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_FIREWORK_BLAST"), v("FIREWORK_BLAST")),
|
||||
ENTITY_FIREWORK_ROCKET_BLAST_FAR(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_FIREWORK_BLAST_FAR"), v("FIREWORK_BLAST2")),
|
||||
ENTITY_FIREWORK_ROCKET_LARGE_BLAST(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_FIREWORK_LARGE_BLAST"), v("FIREWORK_LARGE_BLAST")),
|
||||
ENTITY_FIREWORK_ROCKET_LARGE_BLAST_FAR(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_FIREWORK_LARGE_BLAST_FAR"), v("FIREWORK_LARGE_BLAST2")),
|
||||
ENTITY_FIREWORK_ROCKET_LAUNCH(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_FIREWORK_LAUNCH"), v("FIREWORK_LAUNCH")),
|
||||
ENTITY_FIREWORK_ROCKET_SHOOT(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_FIREWORK_SHOOT"), v("FIREWORK_LAUNCH", true)),
|
||||
ENTITY_FIREWORK_ROCKET_TWINKLE(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_FIREWORK_TWINKLE"), v("FIREWORK_TWINKLE")),
|
||||
ENTITY_FIREWORK_ROCKET_TWINKLE_FAR(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_FIREWORK_TWINKLE_FAR"), v("FIREWORK_TWINKLE2")),
|
||||
ENTITY_FISHING_BOBBER_RETRIEVE,
|
||||
ENTITY_FISHING_BOBBER_SPLASH,
|
||||
ENTITY_FISHING_BOBBER_THROW,
|
||||
ENTITY_FISH_SWIM,
|
||||
ENTITY_FOX_AGGRO,
|
||||
ENTITY_FOX_AMBIENT,
|
||||
ENTITY_FOX_BITE,
|
||||
ENTITY_FOX_DEATH,
|
||||
ENTITY_FOX_EAT(ServerVersion.V1_14, v(ServerVersion.V1_9, "ENTITY_GENERIC_EAT", true), v("EAT", true)),
|
||||
ENTITY_FOX_HURT,
|
||||
ENTITY_FOX_SCREECH,
|
||||
ENTITY_FOX_SLEEP,
|
||||
ENTITY_FOX_SNIFF,
|
||||
ENTITY_FOX_SPIT,
|
||||
ENTITY_GENERIC_BIG_FALL("FALL_BIG"),
|
||||
ENTITY_GENERIC_BURN,
|
||||
ENTITY_GENERIC_DEATH,
|
||||
ENTITY_GENERIC_DRINK("DRINK"),
|
||||
ENTITY_GENERIC_EAT("EAT"),
|
||||
ENTITY_GENERIC_EXPLODE("EXPLODE"),
|
||||
ENTITY_GENERIC_EXTINGUISH_FIRE("FIZZ"),
|
||||
ENTITY_GENERIC_HURT("HURT_FLESH"),
|
||||
ENTITY_GENERIC_SMALL_FALL("FALL_SMALL"),
|
||||
ENTITY_GENERIC_SPLASH(ServerVersion.V1_9, v("SPLASH", true)),
|
||||
ENTITY_GENERIC_SWIM,
|
||||
ENTITY_GHAST_AMBIENT("GHAST_MOAN"),
|
||||
ENTITY_GHAST_DEATH("GHAST_DEATH"),
|
||||
ENTITY_GHAST_HURT("GHAST_SCREAM2"),
|
||||
ENTITY_GHAST_SCREAM("GHAST_SCREAM"),
|
||||
ENTITY_GHAST_SHOOT("GHAST_FIREBALL"),
|
||||
ENTITY_GHAST_WARN("GHAST_CHARGE"),
|
||||
ENTITY_GUARDIAN_AMBIENT,
|
||||
ENTITY_GUARDIAN_AMBIENT_LAND,
|
||||
ENTITY_GUARDIAN_ATTACK,
|
||||
ENTITY_GUARDIAN_DEATH,
|
||||
ENTITY_GUARDIAN_DEATH_LAND,
|
||||
ENTITY_GUARDIAN_FLOP,
|
||||
ENTITY_GUARDIAN_HURT,
|
||||
ENTITY_GUARDIAN_HURT_LAND,
|
||||
ENTITY_HORSE_AMBIENT("HORSE_IDLE"),
|
||||
ENTITY_HORSE_ANGRY("HORSE_ANGRY"),
|
||||
ENTITY_HORSE_ARMOR("HORSE_ARMOR"),
|
||||
ENTITY_HORSE_BREATHE("HORSE_BREATHE"),
|
||||
ENTITY_HORSE_DEATH("HORSE_DEATH"),
|
||||
ENTITY_HORSE_EAT("EAT"),
|
||||
ENTITY_HORSE_GALLOP("HORSE_GALLOP"),
|
||||
ENTITY_HORSE_HURT("HORSE_HIT"),
|
||||
ENTITY_HORSE_JUMP("HORSE_JUMP"),
|
||||
ENTITY_HORSE_LAND("HORSE_LAND"),
|
||||
ENTITY_HORSE_SADDLE("HORSE_SADDLE"),
|
||||
ENTITY_HORSE_STEP("HORSE_SOFT"),
|
||||
ENTITY_HORSE_STEP_WOOD("HORSE_WOOD"),
|
||||
ENTITY_HOSTILE_BIG_FALL,
|
||||
ENTITY_HOSTILE_DEATH,
|
||||
ENTITY_HOSTILE_HURT,
|
||||
ENTITY_HOSTILE_SMALL_FALL,
|
||||
ENTITY_HOSTILE_SPLASH(ServerVersion.V1_9, v("SPLASH", true)),
|
||||
ENTITY_HOSTILE_SWIM,
|
||||
ENTITY_HUSK_AMBIENT,
|
||||
ENTITY_HUSK_CONVERTED_TO_ZOMBIE,
|
||||
ENTITY_HUSK_DEATH,
|
||||
ENTITY_HUSK_HURT,
|
||||
ENTITY_HUSK_STEP,
|
||||
ENTITY_ILLUSIONER_AMBIENT,
|
||||
ENTITY_ILLUSIONER_CAST_SPELL,
|
||||
ENTITY_ILLUSIONER_DEATH,
|
||||
ENTITY_ILLUSIONER_HURT,
|
||||
ENTITY_ILLUSIONER_MIRROR_MOVE,
|
||||
ENTITY_ILLUSIONER_PREPARE_BLINDNESS,
|
||||
ENTITY_ILLUSIONER_PREPARE_MIRROR,
|
||||
ENTITY_IRON_GOLEM_ATTACK(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_IRONGOLEM_ATTACK"), v("IRONGOLEM_THROW")),
|
||||
ENTITY_IRON_GOLEM_DEATH(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_IRONGOLEM_DEATH"), v("IRONGOLEM_DEATH")),
|
||||
ENTITY_IRON_GOLEM_HURT(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_IRONGOLEM_HURT"), v("IRONGOLEM_HIT")),
|
||||
ENTITY_IRON_GOLEM_STEP(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_IRONGOLEM_STEP"), v("IRONGOLEM_WALK")),
|
||||
ENTITY_ITEM_BREAK("ITEM_BREAK"),
|
||||
ENTITY_ITEM_FRAME_ADD_ITEM,
|
||||
ENTITY_ITEM_FRAME_BREAK,
|
||||
ENTITY_ITEM_FRAME_PLACE,
|
||||
ENTITY_ITEM_FRAME_REMOVE_ITEM,
|
||||
ENTITY_ITEM_FRAME_ROTATE_ITEM,
|
||||
ENTITY_ITEM_PICKUP("ITEM_PICKUP"),
|
||||
ENTITY_LEASH_KNOT_BREAK,
|
||||
ENTITY_LEASH_KNOT_PLACE,
|
||||
ENTITY_LIGHTNING_BOLT_IMPACT(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_LIGHTNING_IMPACT"), v("AMBIENCE_THUNDER", true)),
|
||||
ENTITY_LIGHTNING_BOLT_THUNDER(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_LIGHTNING_THUNDER"), v("AMBIENCE_THUNDER")),
|
||||
ENTITY_LINGERING_POTION_THROW,
|
||||
ENTITY_LLAMA_AMBIENT,
|
||||
ENTITY_LLAMA_ANGRY,
|
||||
ENTITY_LLAMA_CHEST,
|
||||
ENTITY_LLAMA_DEATH,
|
||||
ENTITY_LLAMA_EAT("EAT"),
|
||||
ENTITY_LLAMA_HURT,
|
||||
ENTITY_LLAMA_SPIT,
|
||||
ENTITY_LLAMA_STEP,
|
||||
ENTITY_LLAMA_SWAG,
|
||||
ENTITY_MAGMA_CUBE_DEATH, // ENTITY_MAGMACUBE_DEATH
|
||||
ENTITY_MAGMA_CUBE_DEATH_SMALL,
|
||||
ENTITY_MAGMA_CUBE_HURT, // ENTITY_MAGMACUBE_HURT
|
||||
ENTITY_MAGMA_CUBE_HURT_SMALL,
|
||||
ENTITY_MAGMA_CUBE_JUMP(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_MAGMACUBE_JUMP"), v("MAGMACUBE_JUMP")),
|
||||
ENTITY_MAGMA_CUBE_SQUISH(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_MAGMACUBE_SQUISH"), v("MAGMACUBE_WALK")),
|
||||
ENTITY_MAGMA_CUBE_SQUISH_SMALL(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_MAGMACUBE_SQUISH", true), v("MAGMACUBE_WALK2")),
|
||||
ENTITY_MINECART_INSIDE("MINECART_INSIDE"),
|
||||
ENTITY_MINECART_RIDING("MINECART_BASE"),
|
||||
ENTITY_MOOSHROOM_CONVERT,
|
||||
ENTITY_MOOSHROOM_EAT(ServerVersion.V1_14, v(ServerVersion.V1_9, "ENTITY_GENERIC_EAT", true), v("EAT", true)),
|
||||
ENTITY_MOOSHROOM_MILK,
|
||||
ENTITY_MOOSHROOM_SHEAR,
|
||||
ENTITY_MOOSHROOM_SUSPICIOUS_MILK,
|
||||
ENTITY_MULE_AMBIENT,
|
||||
ENTITY_MULE_CHEST,
|
||||
ENTITY_MULE_DEATH,
|
||||
ENTITY_MULE_HURT,
|
||||
ENTITY_OCELOT_AMBIENT,
|
||||
ENTITY_OCELOT_DEATH,
|
||||
ENTITY_OCELOT_HURT,
|
||||
ENTITY_PAINTING_BREAK,
|
||||
ENTITY_PAINTING_PLACE,
|
||||
ENTITY_PANDA_AGGRESSIVE_AMBIENT,
|
||||
ENTITY_PANDA_AMBIENT,
|
||||
ENTITY_PANDA_BITE,
|
||||
ENTITY_PANDA_CANT_BREED,
|
||||
ENTITY_PANDA_DEATH,
|
||||
ENTITY_PANDA_EAT(ServerVersion.V1_14, v(ServerVersion.V1_9, "ENTITY_GENERIC_EAT", true), v("EAT", true)),
|
||||
ENTITY_PANDA_HURT,
|
||||
ENTITY_PANDA_PRE_SNEEZE,
|
||||
ENTITY_PANDA_SNEEZE,
|
||||
ENTITY_PANDA_STEP,
|
||||
ENTITY_PANDA_WORRIED_AMBIENT,
|
||||
ENTITY_PARROT_AMBIENT,
|
||||
ENTITY_PARROT_DEATH,
|
||||
ENTITY_PARROT_EAT(ServerVersion.V1_12, v(ServerVersion.V1_9, "ENTITY_GENERIC_EAT", true), v("EAT", true)),
|
||||
ENTITY_PARROT_FLY,
|
||||
ENTITY_PARROT_HURT,
|
||||
ENTITY_PARROT_IMITATE_BLAZE,
|
||||
ENTITY_PARROT_IMITATE_CREEPER,
|
||||
ENTITY_PARROT_IMITATE_DROWNED,
|
||||
ENTITY_PARROT_IMITATE_ELDER_GUARDIAN,
|
||||
ENTITY_PARROT_IMITATE_ENDERMAN,
|
||||
ENTITY_PARROT_IMITATE_ENDERMITE,
|
||||
ENTITY_PARROT_IMITATE_ENDER_DRAGON,
|
||||
ENTITY_PARROT_IMITATE_EVOKER,
|
||||
ENTITY_PARROT_IMITATE_GHAST,
|
||||
ENTITY_PARROT_IMITATE_GUARDIAN,
|
||||
ENTITY_PARROT_IMITATE_HUSK,
|
||||
ENTITY_PARROT_IMITATE_ILLUSIONER,
|
||||
ENTITY_PARROT_IMITATE_MAGMA_CUBE,
|
||||
ENTITY_PARROT_IMITATE_PANDA,
|
||||
ENTITY_PARROT_IMITATE_PHANTOM,
|
||||
ENTITY_PARROT_IMITATE_PILLAGER,
|
||||
ENTITY_PARROT_IMITATE_POLAR_BEAR,
|
||||
ENTITY_PARROT_IMITATE_RAVAGER,
|
||||
ENTITY_PARROT_IMITATE_SHULKER,
|
||||
ENTITY_PARROT_IMITATE_SILVERFISH,
|
||||
ENTITY_PARROT_IMITATE_SKELETON,
|
||||
ENTITY_PARROT_IMITATE_SLIME,
|
||||
ENTITY_PARROT_IMITATE_SPIDER,
|
||||
ENTITY_PARROT_IMITATE_STRAY,
|
||||
ENTITY_PARROT_IMITATE_VEX,
|
||||
ENTITY_PARROT_IMITATE_VINDICATOR,
|
||||
ENTITY_PARROT_IMITATE_WITCH,
|
||||
ENTITY_PARROT_IMITATE_WITHER,
|
||||
ENTITY_PARROT_IMITATE_WITHER_SKELETON,
|
||||
ENTITY_PARROT_IMITATE_WOLF,
|
||||
ENTITY_PARROT_IMITATE_ZOMBIE,
|
||||
ENTITY_PARROT_IMITATE_ZOMBIE_PIGMAN,
|
||||
ENTITY_PARROT_IMITATE_ZOMBIE_VILLAGER,
|
||||
ENTITY_PARROT_STEP,
|
||||
ENTITY_PHANTOM_AMBIENT,
|
||||
ENTITY_PHANTOM_BITE,
|
||||
ENTITY_PHANTOM_DEATH,
|
||||
ENTITY_PHANTOM_FLAP,
|
||||
ENTITY_PHANTOM_HURT,
|
||||
ENTITY_PHANTOM_SWOOP,
|
||||
ENTITY_PIG_AMBIENT("PIG_IDLE"),
|
||||
ENTITY_PIG_DEATH("PIG_DEATH"),
|
||||
ENTITY_PIG_HURT,
|
||||
ENTITY_PIG_SADDLE,
|
||||
ENTITY_PIG_STEP("PIG_WALK"),
|
||||
ENTITY_PILLAGER_AMBIENT,
|
||||
ENTITY_PILLAGER_CELEBRATE,
|
||||
ENTITY_PILLAGER_DEATH,
|
||||
ENTITY_PILLAGER_HURT,
|
||||
ENTITY_PLAYER_ATTACK_CRIT,
|
||||
ENTITY_PLAYER_ATTACK_KNOCKBACK,
|
||||
ENTITY_PLAYER_ATTACK_NODAMAGE,
|
||||
ENTITY_PLAYER_ATTACK_STRONG,
|
||||
ENTITY_PLAYER_ATTACK_SWEEP,
|
||||
ENTITY_PLAYER_ATTACK_WEAK,
|
||||
ENTITY_PLAYER_BIG_FALL,
|
||||
ENTITY_PLAYER_BREATH,
|
||||
ENTITY_PLAYER_BURP("BURP"),
|
||||
ENTITY_PLAYER_DEATH,
|
||||
ENTITY_PLAYER_HURT,
|
||||
ENTITY_PLAYER_HURT_DROWN,
|
||||
ENTITY_PLAYER_HURT_ON_FIRE,
|
||||
ENTITY_PLAYER_HURT_SWEET_BERRY_BUSH,
|
||||
ENTITY_PLAYER_LEVELUP("LEVEL_UP"),
|
||||
ENTITY_PLAYER_SMALL_FALL,
|
||||
ENTITY_PLAYER_SPLASH("SPLASH"),
|
||||
ENTITY_PLAYER_SPLASH_HIGH_SPEED(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_GENERIC_SPLASH"), v("SPLASH2", true)),
|
||||
ENTITY_PLAYER_SWIM("SWIM"),
|
||||
ENTITY_POLAR_BEAR_AMBIENT,
|
||||
ENTITY_POLAR_BEAR_AMBIENT_BABY,
|
||||
ENTITY_POLAR_BEAR_DEATH,
|
||||
ENTITY_POLAR_BEAR_HURT,
|
||||
ENTITY_POLAR_BEAR_STEP,
|
||||
ENTITY_POLAR_BEAR_WARNING,
|
||||
ENTITY_PUFFER_FISH_AMBIENT,
|
||||
ENTITY_PUFFER_FISH_BLOW_OUT,
|
||||
ENTITY_PUFFER_FISH_BLOW_UP,
|
||||
ENTITY_PUFFER_FISH_DEATH,
|
||||
ENTITY_PUFFER_FISH_FLOP,
|
||||
ENTITY_PUFFER_FISH_HURT,
|
||||
ENTITY_PUFFER_FISH_STING,
|
||||
ENTITY_RABBIT_AMBIENT,
|
||||
ENTITY_RABBIT_ATTACK,
|
||||
ENTITY_RABBIT_DEATH,
|
||||
ENTITY_RABBIT_HURT,
|
||||
ENTITY_RABBIT_JUMP,
|
||||
ENTITY_RAVAGER_AMBIENT,
|
||||
ENTITY_RAVAGER_ATTACK,
|
||||
ENTITY_RAVAGER_CELEBRATE,
|
||||
ENTITY_RAVAGER_DEATH,
|
||||
ENTITY_RAVAGER_HURT,
|
||||
ENTITY_RAVAGER_ROAR,
|
||||
ENTITY_RAVAGER_STEP,
|
||||
ENTITY_RAVAGER_STUNNED,
|
||||
ENTITY_SALMON_AMBIENT,
|
||||
ENTITY_SALMON_DEATH,
|
||||
ENTITY_SALMON_FLOP,
|
||||
ENTITY_SALMON_HURT,
|
||||
ENTITY_SHEEP_AMBIENT("SHEEP_IDLE"),
|
||||
ENTITY_SHEEP_DEATH,
|
||||
ENTITY_SHEEP_HURT,
|
||||
ENTITY_SHEEP_SHEAR("SHEEP_SHEAR"),
|
||||
ENTITY_SHEEP_STEP("SHEEP_WALK"),
|
||||
ENTITY_SHULKER_AMBIENT,
|
||||
ENTITY_SHULKER_BULLET_HIT,
|
||||
ENTITY_SHULKER_BULLET_HURT,
|
||||
ENTITY_SHULKER_CLOSE,
|
||||
ENTITY_SHULKER_DEATH,
|
||||
ENTITY_SHULKER_HURT,
|
||||
ENTITY_SHULKER_HURT_CLOSED,
|
||||
ENTITY_SHULKER_OPEN,
|
||||
ENTITY_SHULKER_SHOOT,
|
||||
ENTITY_SHULKER_TELEPORT,
|
||||
ENTITY_SILVERFISH_AMBIENT("SILVERFISH_IDLE"),
|
||||
ENTITY_SILVERFISH_DEATH("SILVERFISH_KILL"),
|
||||
ENTITY_SILVERFISH_HURT("SILVERFISH_HIT"),
|
||||
ENTITY_SILVERFISH_STEP("SILVERFISH_WALK"),
|
||||
ENTITY_SKELETON_AMBIENT("SKELETON_IDLE"),
|
||||
ENTITY_SKELETON_DEATH("SKELETON_DEATH"),
|
||||
ENTITY_SKELETON_HORSE_AMBIENT("HORSE_SKELETON_IDLE"),
|
||||
ENTITY_SKELETON_HORSE_AMBIENT_WATER,
|
||||
ENTITY_SKELETON_HORSE_DEATH("HORSE_SKELETON_DEATH"),
|
||||
ENTITY_SKELETON_HORSE_GALLOP_WATER,
|
||||
ENTITY_SKELETON_HORSE_HURT("HORSE_SKELETON_HIT"),
|
||||
ENTITY_SKELETON_HORSE_JUMP_WATER,
|
||||
ENTITY_SKELETON_HORSE_STEP_WATER,
|
||||
ENTITY_SKELETON_HORSE_SWIM,
|
||||
ENTITY_SKELETON_HURT("SKELETON_HURT"),
|
||||
ENTITY_SKELETON_SHOOT(ServerVersion.V1_9, v("SHOOT_ARROW", true)),
|
||||
ENTITY_SKELETON_STEP("SKELETON_WALK"),
|
||||
ENTITY_SLIME_ATTACK("SLIME_ATTACK"),
|
||||
ENTITY_SLIME_DEATH,
|
||||
ENTITY_SLIME_DEATH_SMALL,
|
||||
ENTITY_SLIME_HURT,
|
||||
ENTITY_SLIME_HURT_SMALL,
|
||||
ENTITY_SLIME_JUMP("SLIME_WALK"), // Not sure which is 1 or 2
|
||||
ENTITY_SLIME_JUMP_SMALL(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_SLIME_JUMP", true), v("SLIME_WALK", true)),
|
||||
ENTITY_SLIME_SQUISH("SLIME_WALK2"),
|
||||
ENTITY_SLIME_SQUISH_SMALL(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_SLIME_JUMP", true), v("SLIME_WALK2", true)),
|
||||
ENTITY_SNOWBALL_THROW,
|
||||
ENTITY_SNOW_GOLEM_AMBIENT,
|
||||
ENTITY_SNOW_GOLEM_DEATH,
|
||||
ENTITY_SNOW_GOLEM_HURT,
|
||||
ENTITY_SNOW_GOLEM_SHOOT(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_SNOWMAN_SHOOT"), v("SHOOT_ARROW", true)), // this is missing from 1.8 API for some reason
|
||||
ENTITY_SPIDER_AMBIENT("SPIDER_IDLE"),
|
||||
ENTITY_SPIDER_DEATH("SPIDER_DEATH"),
|
||||
ENTITY_SPIDER_HURT,
|
||||
ENTITY_SPIDER_STEP("SPIDER_WALK"),
|
||||
ENTITY_SPLASH_POTION_BREAK,
|
||||
ENTITY_SPLASH_POTION_THROW,
|
||||
ENTITY_SQUID_AMBIENT,
|
||||
ENTITY_SQUID_DEATH,
|
||||
ENTITY_SQUID_HURT,
|
||||
ENTITY_SQUID_SQUIRT,
|
||||
ENTITY_STRAY_AMBIENT,
|
||||
ENTITY_STRAY_DEATH,
|
||||
ENTITY_STRAY_HURT,
|
||||
ENTITY_STRAY_STEP,
|
||||
ENTITY_TNT_PRIMED("FUSE"),
|
||||
ENTITY_TROPICAL_FISH_AMBIENT,
|
||||
ENTITY_TROPICAL_FISH_DEATH,
|
||||
ENTITY_TROPICAL_FISH_FLOP,
|
||||
ENTITY_TROPICAL_FISH_HURT,
|
||||
ENTITY_TURTLE_AMBIENT_LAND,
|
||||
ENTITY_TURTLE_DEATH,
|
||||
ENTITY_TURTLE_DEATH_BABY,
|
||||
ENTITY_TURTLE_EGG_BREAK,
|
||||
ENTITY_TURTLE_EGG_CRACK,
|
||||
ENTITY_TURTLE_EGG_HATCH,
|
||||
ENTITY_TURTLE_HURT,
|
||||
ENTITY_TURTLE_HURT_BABY,
|
||||
ENTITY_TURTLE_LAY_EGG,
|
||||
ENTITY_TURTLE_SHAMBLE,
|
||||
ENTITY_TURTLE_SHAMBLE_BABY,
|
||||
ENTITY_TURTLE_SWIM,
|
||||
ENTITY_VEX_AMBIENT,
|
||||
ENTITY_VEX_CHARGE,
|
||||
ENTITY_VEX_DEATH,
|
||||
ENTITY_VEX_HURT,
|
||||
ENTITY_VILLAGER_AMBIENT("VILLAGER_IDLE"),
|
||||
ENTITY_VILLAGER_CELEBRATE,
|
||||
ENTITY_VILLAGER_DEATH("VILLAGER_DEATH"),
|
||||
ENTITY_VILLAGER_HURT("VILLAGER_HIT"),
|
||||
ENTITY_VILLAGER_NO("VILLAGER_NO"),
|
||||
ENTITY_VILLAGER_TRADE(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_VILLAGER_TRADING"), v("VILLAGER_HAGGLE")),
|
||||
ENTITY_VILLAGER_WORK_ARMORER,
|
||||
ENTITY_VILLAGER_WORK_BUTCHER,
|
||||
ENTITY_VILLAGER_WORK_CARTOGRAPHER,
|
||||
ENTITY_VILLAGER_WORK_CLERIC,
|
||||
ENTITY_VILLAGER_WORK_FARMER,
|
||||
ENTITY_VILLAGER_WORK_FISHERMAN,
|
||||
ENTITY_VILLAGER_WORK_FLETCHER,
|
||||
ENTITY_VILLAGER_WORK_LEATHERWORKER,
|
||||
ENTITY_VILLAGER_WORK_LIBRARIAN,
|
||||
ENTITY_VILLAGER_WORK_MASON,
|
||||
ENTITY_VILLAGER_WORK_SHEPHERD,
|
||||
ENTITY_VILLAGER_WORK_TOOLSMITH,
|
||||
ENTITY_VILLAGER_WORK_WEAPONSMITH,
|
||||
ENTITY_VILLAGER_YES("VILLAGER_YES"),
|
||||
ENTITY_VINDICATOR_AMBIENT,
|
||||
ENTITY_VINDICATOR_CELEBRATE,
|
||||
ENTITY_VINDICATOR_DEATH,
|
||||
ENTITY_VINDICATOR_HURT,
|
||||
ENTITY_WANDERING_TRADER_AMBIENT,
|
||||
ENTITY_WANDERING_TRADER_DEATH,
|
||||
ENTITY_WANDERING_TRADER_DISAPPEARED,
|
||||
ENTITY_WANDERING_TRADER_DRINK_MILK(ServerVersion.V1_14, v(ServerVersion.V1_9, "ENTITY_GENERIC_DRINK", true), v("DRINK", true)),
|
||||
ENTITY_WANDERING_TRADER_DRINK_POTION(ServerVersion.V1_14, v(ServerVersion.V1_9, "ENTITY_GENERIC_DRINK", true), v("DRINK", true)),
|
||||
ENTITY_WANDERING_TRADER_HURT,
|
||||
ENTITY_WANDERING_TRADER_NO,
|
||||
ENTITY_WANDERING_TRADER_REAPPEARED,
|
||||
ENTITY_WANDERING_TRADER_TRADE,
|
||||
ENTITY_WANDERING_TRADER_YES,
|
||||
ENTITY_WITCH_AMBIENT,
|
||||
ENTITY_WITCH_CELEBRATE,
|
||||
ENTITY_WITCH_DEATH,
|
||||
ENTITY_WITCH_DRINK("DRINK"),
|
||||
ENTITY_WITCH_HURT,
|
||||
ENTITY_WITCH_THROW,
|
||||
ENTITY_WITHER_AMBIENT("WITHER_IDLE"),
|
||||
ENTITY_WITHER_BREAK_BLOCK("WITHER_SPAWN"),
|
||||
ENTITY_WITHER_DEATH("WITHER_DEATH"),
|
||||
ENTITY_WITHER_HURT("WITHER_HURT"),
|
||||
ENTITY_WITHER_SHOOT("WITHER_SHOOT"),
|
||||
ENTITY_WITHER_SKELETON_AMBIENT,
|
||||
ENTITY_WITHER_SKELETON_DEATH,
|
||||
ENTITY_WITHER_SKELETON_HURT,
|
||||
ENTITY_WITHER_SKELETON_STEP,
|
||||
ENTITY_WITHER_SPAWN,
|
||||
ENTITY_WOLF_AMBIENT("WOLF_BARK"),
|
||||
ENTITY_WOLF_DEATH("WOLF_DEATH"),
|
||||
ENTITY_WOLF_GROWL("WOLF_GROWL"),
|
||||
ENTITY_WOLF_HOWL("WOLF_HOWL"),
|
||||
ENTITY_WOLF_HURT("WOLF_HURT"),
|
||||
ENTITY_WOLF_PANT("WOLF_PANT"),
|
||||
ENTITY_WOLF_SHAKE("WOLF_SHAKE"),
|
||||
ENTITY_WOLF_STEP("WOLF_WALK"),
|
||||
ENTITY_WOLF_WHINE("WOLF_WHINE"),
|
||||
ENTITY_ZOMBIE_AMBIENT("ZOMBIE_IDLE"),
|
||||
ENTITY_ZOMBIE_ATTACK_IRON_DOOR(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_ZOMBIE_ATTACK_IRON_DOOR", true), v("ZOMBIE_METAL")),
|
||||
ENTITY_ZOMBIE_ATTACK_WOODEN_DOOR(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_ZOMBIE_ATTACK_DOOR_WOOD", true), v("ZOMBIE_WOOD")),
|
||||
ENTITY_ZOMBIE_BREAK_WOODEN_DOOR(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_ZOMBIE_BREAK_DOOR_WOOD", true), v("ZOMBIE_WOODBREAK")),
|
||||
ENTITY_ZOMBIE_CONVERTED_TO_DROWNED,
|
||||
ENTITY_ZOMBIE_DEATH("ZOMBIE_DEATH"),
|
||||
ENTITY_ZOMBIE_DESTROY_EGG,
|
||||
ENTITY_ZOMBIE_HORSE_AMBIENT("HORSE_ZOMBIE_IDLE"),
|
||||
ENTITY_ZOMBIE_HORSE_DEATH("HORSE_ZOMBIE_DEATH"),
|
||||
ENTITY_ZOMBIE_HORSE_HURT("HORSE_ZOMBIE_HIT"),
|
||||
ENTITY_ZOMBIE_HURT("ZOMBIE_HURT"),
|
||||
ENTITY_ZOMBIE_INFECT("ZOMBIE_INFECT"),
|
||||
ENTITY_ZOMBIE_PIGMAN_AMBIENT(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_ZOMBIE_PIG_AMBIENT", true), v("ZOMBIE_PIG_IDLE")),
|
||||
ENTITY_ZOMBIE_PIGMAN_ANGRY(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_ZOMBIE_PIG_ANGRY", true), v("ZOMBIE_PIG_ANGRY")),
|
||||
ENTITY_ZOMBIE_PIGMAN_DEATH(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_ZOMBIE_PIG_DEATH", true), v("ZOMBIE_PIG_DEATH")),
|
||||
ENTITY_ZOMBIE_PIGMAN_HURT(ServerVersion.V1_13, v(ServerVersion.V1_9, "ENTITY_ZOMBIE_PIG_HURT", true), v("ZOMBIE_PIG_HURT")),
|
||||
ENTITY_ZOMBIE_STEP("ZOMBIE_WALK"),
|
||||
ENTITY_ZOMBIE_VILLAGER_AMBIENT,
|
||||
ENTITY_ZOMBIE_VILLAGER_CONVERTED("ZOMBIE_REMEDY"),
|
||||
ENTITY_ZOMBIE_VILLAGER_CURE("ZOMBIE_UNFECT"),
|
||||
ENTITY_ZOMBIE_VILLAGER_DEATH,
|
||||
ENTITY_ZOMBIE_VILLAGER_HURT,
|
||||
ENTITY_ZOMBIE_VILLAGER_STEP,
|
||||
EVENT_RAID_HORN,
|
||||
ITEM_ARMOR_EQUIP_CHAIN(ServerVersion.V1_9, v("SUCCESSFUL_HIT", true)),
|
||||
ITEM_ARMOR_EQUIP_DIAMOND(ServerVersion.V1_9, v("SUCCESSFUL_HIT", true)),
|
||||
ITEM_ARMOR_EQUIP_ELYTRA(ServerVersion.V1_11, v(ServerVersion.V1_9, "ITEM_ARMOR_EQUIP_GENERIC", true), v("SUCCESSFUL_HIT", true)),
|
||||
ITEM_ARMOR_EQUIP_GENERIC(ServerVersion.V1_9, v("SUCCESSFUL_HIT", true)),
|
||||
ITEM_ARMOR_EQUIP_GOLD(ServerVersion.V1_9, v("SUCCESSFUL_HIT", true)),
|
||||
ITEM_ARMOR_EQUIP_IRON(ServerVersion.V1_9, v("SUCCESSFUL_HIT", true)),
|
||||
ITEM_ARMOR_EQUIP_LEATHER(ServerVersion.V1_9, v("SUCCESSFUL_HIT", true)),
|
||||
ITEM_ARMOR_EQUIP_TURTLE(ServerVersion.V1_13, v(ServerVersion.V1_9, "ITEM_ARMOR_EQUIP_GENERIC", true), v("SUCCESSFUL_HIT", true)),
|
||||
ITEM_AXE_STRIP,
|
||||
ITEM_BOOK_PAGE_TURN,
|
||||
ITEM_BOOK_PUT,
|
||||
ITEM_BOTTLE_EMPTY,
|
||||
ITEM_BOTTLE_FILL,
|
||||
ITEM_BOTTLE_FILL_DRAGONBREATH,
|
||||
ITEM_BUCKET_EMPTY,
|
||||
ITEM_BUCKET_EMPTY_FISH,
|
||||
ITEM_BUCKET_EMPTY_LAVA,
|
||||
ITEM_BUCKET_FILL,
|
||||
ITEM_BUCKET_FILL_FISH,
|
||||
ITEM_BUCKET_FILL_LAVA,
|
||||
ITEM_CHORUS_FRUIT_TELEPORT,
|
||||
ITEM_CROP_PLANT,
|
||||
ITEM_CROSSBOW_HIT,
|
||||
ITEM_CROSSBOW_LOADING_END,
|
||||
ITEM_CROSSBOW_LOADING_MIDDLE,
|
||||
ITEM_CROSSBOW_LOADING_START,
|
||||
ITEM_CROSSBOW_QUICK_CHARGE_1,
|
||||
ITEM_CROSSBOW_QUICK_CHARGE_2,
|
||||
ITEM_CROSSBOW_QUICK_CHARGE_3,
|
||||
ITEM_CROSSBOW_SHOOT,
|
||||
ITEM_ELYTRA_FLYING,
|
||||
ITEM_FIRECHARGE_USE,
|
||||
ITEM_FLINTANDSTEEL_USE("FIRE_IGNITE"),
|
||||
ITEM_HOE_TILL,
|
||||
ITEM_NETHER_WART_PLANT,
|
||||
ITEM_SHIELD_BLOCK,
|
||||
ITEM_SHIELD_BREAK,
|
||||
ITEM_SHOVEL_FLATTEN,
|
||||
ITEM_SWEET_BERRIES_PICK_FROM_BUSH,
|
||||
ITEM_TOTEM_USE,
|
||||
ITEM_TRIDENT_HIT,
|
||||
ITEM_TRIDENT_HIT_GROUND,
|
||||
ITEM_TRIDENT_RETURN,
|
||||
ITEM_TRIDENT_RIPTIDE_1,
|
||||
ITEM_TRIDENT_RIPTIDE_2,
|
||||
ITEM_TRIDENT_RIPTIDE_3,
|
||||
ITEM_TRIDENT_THROW,
|
||||
ITEM_TRIDENT_THUNDER,
|
||||
MUSIC_CREATIVE,
|
||||
MUSIC_CREDITS,
|
||||
MUSIC_DISC_11(ServerVersion.V1_13, v(ServerVersion.V1_9, "RECORD_11"), v("WOOD_CLICK", true)),
|
||||
MUSIC_DISC_13(ServerVersion.V1_13, v(ServerVersion.V1_9, "RECORD_13"), v("WOOD_CLICK", true)),
|
||||
MUSIC_DISC_BLOCKS(ServerVersion.V1_13, v(ServerVersion.V1_9, "RECORD_BLOCKS"), v("WOOD_CLICK", true)),
|
||||
MUSIC_DISC_CAT(ServerVersion.V1_13, v(ServerVersion.V1_9, "RECORD_CAT"), v("WOOD_CLICK", true)),
|
||||
MUSIC_DISC_CHIRP(ServerVersion.V1_13, v(ServerVersion.V1_9, "RECORD_CHIRP"), v("WOOD_CLICK", true)),
|
||||
MUSIC_DISC_FAR(ServerVersion.V1_13, v(ServerVersion.V1_9, "RECORD_FAR"), v("WOOD_CLICK", true)),
|
||||
MUSIC_DISC_MALL(ServerVersion.V1_13, v(ServerVersion.V1_9, "RECORD_MALL"), v("WOOD_CLICK", true)),
|
||||
MUSIC_DISC_MELLOHI(ServerVersion.V1_13, v(ServerVersion.V1_9, "RECORD_MELLOHI"), v("WOOD_CLICK", true)),
|
||||
MUSIC_DISC_STAL(ServerVersion.V1_13, v(ServerVersion.V1_9, "RECORD_STAL"), v("WOOD_CLICK", true)),
|
||||
MUSIC_DISC_STRAD(ServerVersion.V1_13, v(ServerVersion.V1_9, "RECORD_STRAD"), v("WOOD_CLICK", true)),
|
||||
MUSIC_DISC_WAIT(ServerVersion.V1_13, v(ServerVersion.V1_9, "RECORD_WAIT"), v("WOOD_CLICK", true)),
|
||||
MUSIC_DISC_WARD(ServerVersion.V1_13, v(ServerVersion.V1_9, "RECORD_WARD"), v("WOOD_CLICK", true)), // records are missing from 1.8 API
|
||||
MUSIC_DRAGON,
|
||||
MUSIC_END,
|
||||
MUSIC_GAME,
|
||||
MUSIC_MENU,
|
||||
MUSIC_NETHER,
|
||||
MUSIC_UNDER_WATER,
|
||||
UI_BUTTON_CLICK("CLICK"),
|
||||
UI_CARTOGRAPHY_TABLE_TAKE_RESULT,
|
||||
UI_LOOM_SELECT_PATTERN,
|
||||
UI_LOOM_TAKE_RESULT,
|
||||
UI_STONECUTTER_SELECT_RECIPE,
|
||||
UI_STONECUTTER_TAKE_RESULT,
|
||||
UI_TOAST_CHALLENGE_COMPLETE,
|
||||
UI_TOAST_IN,
|
||||
UI_TOAST_OUT,
|
||||
WEATHER_RAIN("AMBIENCE_RAIN"),
|
||||
WEATHER_RAIN_ABOVE;
|
||||
|
||||
protected /*final*/ Sound sound;
|
||||
protected /*final*/ boolean compatibilityMode;
|
||||
protected static final boolean DEBUG = false;
|
||||
|
||||
private CompatibleSounds() {
|
||||
// This could get risky, since we haven't finished this
|
||||
//sound = Sound.valueOf(name());
|
||||
Sound find = null;
|
||||
for (Sound s : Sound.values()) {
|
||||
if (s.name().equals(name())) {
|
||||
find = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(DEBUG && find == null) {
|
||||
System.out.println("Sound for " + name() + " Not found!");
|
||||
}
|
||||
sound = find;
|
||||
compatibilityMode = find == null;
|
||||
}
|
||||
|
||||
// if the sound ony ever changed from 1.8 -> 1.9
|
||||
private CompatibleSounds(String compatibility_18) {
|
||||
try {
|
||||
compatibilityMode = false;
|
||||
if (ServerVersion.isServerVersionBelow(ServerVersion.V1_9)) {
|
||||
sound = Sound.valueOf(compatibility_18);
|
||||
} else {
|
||||
sound = Sound.valueOf(name());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("ERROR loading " + name());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private CompatibleSounds(Version... versions) {
|
||||
try {
|
||||
for (Version v : versions) {
|
||||
if (v.sound != null && ServerVersion.isServerVersionAtLeast(v.version)) {
|
||||
sound = Sound.valueOf(v.sound);
|
||||
compatibilityMode = v.compatibilityMode;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
System.out.println("ERROR loading " + name());
|
||||
for (Version v : versions) {
|
||||
System.out.println(v.version + " - " + v.sound);
|
||||
}
|
||||
e.printStackTrace();
|
||||
}
|
||||
Sound find = null;
|
||||
for (Sound s : Sound.values()) {
|
||||
if (s.name().equals(name())) {
|
||||
find = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sound = find;
|
||||
compatibilityMode = find == null;
|
||||
}
|
||||
|
||||
private CompatibleSounds(ServerVersion minVersion, Version... versions) {
|
||||
try {
|
||||
if (ServerVersion.isServerVersionAtLeast(minVersion)) {
|
||||
// should be good to use this sound
|
||||
sound = Sound.valueOf(name());
|
||||
compatibilityMode = false;
|
||||
} else {
|
||||
for (Version v : versions) {
|
||||
if (v.sound != null && ServerVersion.isServerVersionAtLeast(v.version)) {
|
||||
sound = Sound.valueOf(v.sound);
|
||||
compatibilityMode = v.compatibilityMode;
|
||||
return;
|
||||
}
|
||||
}
|
||||
sound = null;
|
||||
compatibilityMode = false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("ERROR loading " + name() + " (" + minVersion);
|
||||
for (Version v : versions) {
|
||||
System.out.println(v.version + " - " + v.sound);
|
||||
}
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Bukkit API sound that matches this sound
|
||||
*
|
||||
* @return Returns either the matching sound or a similar sound
|
||||
*/
|
||||
public Sound getSound() {
|
||||
return sound != null ? sound : UI_BUTTON_CLICK.sound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if this sound is available on this server.
|
||||
*
|
||||
* @return Returns false if we are using a different sound.
|
||||
*/
|
||||
public boolean isMatch() {
|
||||
return !compatibilityMode;
|
||||
}
|
||||
|
||||
private static Version v(String sound) {
|
||||
return new Version(ServerVersion.UNKNOWN, sound, false);
|
||||
}
|
||||
|
||||
private static Version v(ServerVersion version, String sound) {
|
||||
return new Version(version, sound, false);
|
||||
}
|
||||
|
||||
private static Version v(ServerVersion version, String sound, boolean compatibility) {
|
||||
return new Version(version, sound, compatibility);
|
||||
}
|
||||
|
||||
private static Version v(String sound, boolean compatibility) {
|
||||
return new Version(ServerVersion.UNKNOWN, sound, compatibility);
|
||||
}
|
||||
|
||||
private static class Version {
|
||||
|
||||
final ServerVersion version;
|
||||
final String sound;
|
||||
final boolean compatibilityMode;
|
||||
|
||||
public Version(ServerVersion version, String sound, boolean compatibility) {
|
||||
this.version = version;
|
||||
this.sound = sound;
|
||||
this.compatibilityMode = compatibility;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,590 @@
|
||||
package com.songoda.core.compatibility;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
/**
|
||||
* Near-Materials for older servers 1.7+
|
||||
* @since 2019-08-23
|
||||
* @author jascotty2
|
||||
*/
|
||||
public enum LegacyAnalouges {
|
||||
|
||||
ACACIA_BOAT(ServerVersion.V1_9, "BOAT"),
|
||||
ACACIA_BUTTON(ServerVersion.V1_13, "WOOD_BUTTON"),
|
||||
ACACIA_DOOR(ServerVersion.V1_8, "WOOD_DOOR"), // TODO? ACACIA_DOOR & WOODEN_DOOR are the legacy block variants
|
||||
ACACIA_FENCE(ServerVersion.V1_8, "FENCE"),
|
||||
ACACIA_FENCE_GATE(ServerVersion.V1_8, "FENCE_GATE"),
|
||||
ACACIA_PRESSURE_PLATE(ServerVersion.V1_13, "WOOD_PLATE"),
|
||||
ACACIA_SIGN(ServerVersion.V1_14, "SIGN", "SIGN"),
|
||||
ACACIA_TRAPDOOR(ServerVersion.V1_13, "TRAP_DOOR"),
|
||||
ACACIA_WALL_SIGN(ServerVersion.V1_14, "WALL_SIGN"),
|
||||
ANDESITE(ServerVersion.V1_8, "STONE"),
|
||||
ANDESITE_SLAB(ServerVersion.V1_14, "STONE_SLAB", "STEP", (byte) 0),
|
||||
ANDESITE_STAIRS(ServerVersion.V1_14, "STONE_BRICK_STAIRS", "SMOOTH_STAIRS"),
|
||||
ANDESITE_WALL(ServerVersion.V1_14, "COBBLESTONE_WALL", "COBBLE_WALL"),
|
||||
ARMOR_STAND(ServerVersion.V1_8, "STICK"), // idk, we just need *something*
|
||||
BAMBOO(ServerVersion.V1_14, "SUGAR_CANE", "SUGAR_CANE_BLOCK"),
|
||||
BAMBOO_SAPLING(ServerVersion.V1_14, "SUGAR_CANE"),
|
||||
BARREL(ServerVersion.V1_14, "TRAPPED_CHEST"),
|
||||
BARRIER(ServerVersion.V1_8, "STAINED_GLASS", (byte) 14), // plain glass would make more sense if this were to be a block..
|
||||
BEETROOT(ServerVersion.V1_9, "RAW_BEEF"),
|
||||
BEETROOT_SEEDS(ServerVersion.V1_9, "SEEDS"),
|
||||
BEETROOT_SOUP(ServerVersion.V1_9, "MUSHROOM_SOUP"),
|
||||
BEETROOTS(ServerVersion.V1_9, "CROPS"),
|
||||
BELL(ServerVersion.V1_14, "GOLD_BLOCK", "GOLD_BLOCK"),
|
||||
BIRCH_BOAT(ServerVersion.V1_9, "BOAT"),
|
||||
BIRCH_BUTTON(ServerVersion.V1_13, "WOOD_BUTTON"),
|
||||
BIRCH_DOOR(ServerVersion.V1_8, "WOOD_DOOR"),
|
||||
BIRCH_FENCE(ServerVersion.V1_8, "FENCE"),
|
||||
BIRCH_FENCE_GATE(ServerVersion.V1_8, "FENCE_GATE"),
|
||||
BIRCH_PRESSURE_PLATE(ServerVersion.V1_13, "WOOD_PLATE"),
|
||||
BIRCH_SIGN(ServerVersion.V1_14, "SIGN", "SIGN"),
|
||||
BIRCH_STAIRS(ServerVersion.V1_13, "WOOD_STAIRS"),
|
||||
BIRCH_TRAPDOOR(ServerVersion.V1_13, "TRAP_DOOR"),
|
||||
BIRCH_WALL_SIGN(ServerVersion.V1_14, "WALL_SIGN"),
|
||||
BLACK_BANNER(ServerVersion.V1_8, "SIGN"),
|
||||
BLACK_BED(ServerVersion.V1_12, "BED"),
|
||||
BLACK_CONCRETE(ServerVersion.V1_12, "STAINED_CLAY", (byte) 15),
|
||||
BLACK_CONCRETE_POWDER(ServerVersion.V1_12, "STAINED_CLAY", (byte) 15),
|
||||
BLACK_DYE(ServerVersion.V1_14, "INK_SAC", "INK_SACK", (byte) 0),
|
||||
BLACK_GLAZED_TERRACOTTA(ServerVersion.V1_12, "STAINED_CLAY", (byte) 15),
|
||||
BLACK_SHULKER_BOX(ServerVersion.V1_11, "ENDER_CHEST"),
|
||||
BLACK_WALL_BANNER(ServerVersion.V1_8, "WALL_SIGN"),
|
||||
BLAST_FURNACE(ServerVersion.V1_14, "FURNACE"),
|
||||
BLUE_BANNER(ServerVersion.V1_8, "SIGN"),
|
||||
BLUE_BED(ServerVersion.V1_12, "BED"),
|
||||
BLUE_CONCRETE(ServerVersion.V1_12, "STAINED_CLAY", (byte) 11),
|
||||
BLUE_CONCRETE_POWDER(ServerVersion.V1_12, "STAINED_CLAY", (byte) 11),
|
||||
BLUE_DYE(ServerVersion.V1_14, "LAPIS_LAZULI", "INK_SACK", (byte) 4),
|
||||
BLUE_GLAZED_TERRACOTTA(ServerVersion.V1_12, "STAINED_CLAY", (byte) 11),
|
||||
BLUE_ICE(ServerVersion.V1_13, "PACKED_ICE"),
|
||||
BLUE_SHULKER_BOX(ServerVersion.V1_11, "ENDER_CHEST"),
|
||||
BLUE_WALL_BANNER(ServerVersion.V1_8, "WALL_SIGN"),
|
||||
BONE_BLOCK(ServerVersion.V1_13, "QUARTZ_BLOCK"),
|
||||
BRAIN_CORAL(ServerVersion.V1_13, "WOOL", (byte) 6),
|
||||
BRAIN_CORAL_BLOCK(ServerVersion.V1_13, "WOOL", (byte) 6),
|
||||
BRAIN_CORAL_FAN(ServerVersion.V1_13, "WOOL", (byte) 6),
|
||||
BRAIN_CORAL_WALL_FAN(ServerVersion.V1_13, "WOOL", (byte) 6),
|
||||
BRICK_WALL(ServerVersion.V1_14, "COBBLESTONE_WALL", "COBBLE_WALL"),
|
||||
BROWN_BANNER(ServerVersion.V1_8, "SIGN"),
|
||||
BROWN_BED(ServerVersion.V1_12, "BED"),
|
||||
BROWN_CONCRETE(ServerVersion.V1_12, "STAINED_CLAY", (byte) 12),
|
||||
BROWN_CONCRETE_POWDER(ServerVersion.V1_12, "STAINED_CLAY", (byte) 12),
|
||||
BROWN_DYE(ServerVersion.V1_14, "COCOA_BEANS", "INK_SACK", (byte) 3),
|
||||
BROWN_GLAZED_TERRACOTTA(ServerVersion.V1_12, "STAINED_CLAY", (byte) 12),
|
||||
BROWN_SHULKER_BOX(ServerVersion.V1_11, "ENDER_CHEST"),
|
||||
BROWN_WALL_BANNER(ServerVersion.V1_8, "WALL_SIGN"),
|
||||
BUBBLE_COLUMN(ServerVersion.V1_13, "WATER"),
|
||||
BUBBLE_CORAL(ServerVersion.V1_13, "WOOL", (byte) 2),
|
||||
BUBBLE_CORAL_BLOCK(ServerVersion.V1_13, "WOOL", (byte) 2),
|
||||
BUBBLE_CORAL_FAN(ServerVersion.V1_13, "WOOL", (byte) 2),
|
||||
BUBBLE_CORAL_WALL_FAN(ServerVersion.V1_13, "WOOL", (byte) 2),
|
||||
CAMPFIRE(ServerVersion.V1_14, "FURNACE", "BURNING_FURNACE"),
|
||||
CARTOGRAPHY_TABLE(ServerVersion.V1_14, "BOOKSHELF"),
|
||||
CAT_SPAWN_EGG(ServerVersion.V1_14, "OCELOT_SPAWN_EGG", "MONSTER_EGG", (byte) 98),
|
||||
CAVE_AIR(ServerVersion.V1_13, "AIR"),
|
||||
CHAIN_COMMAND_BLOCK(ServerVersion.V1_9, "COMMAND"),
|
||||
CHISELED_RED_SANDSTONE(ServerVersion.V1_8, "SANDSTONE", (byte) 1),
|
||||
CHORUS_FLOWER(ServerVersion.V1_9, "WOOL", (byte) 2),
|
||||
CHORUS_FRUIT(ServerVersion.V1_9, "APPLE"),
|
||||
CHORUS_PLANT(ServerVersion.V1_9, "WOOL", (byte) 10),
|
||||
COARSE_DIRT(ServerVersion.V1_8, "DIRT"),
|
||||
COD_BUCKET(ServerVersion.V1_13, "WATER_BUCKET"),
|
||||
COD_SPAWN_EGG(ServerVersion.V1_13, "MONSTER_EGG", (byte) 60),
|
||||
COMPOSTER(ServerVersion.V1_14, "FLOWER_POT", "FLOWER_POT_ITEM"),
|
||||
CONDUIT(ServerVersion.V1_13, "MELON"),
|
||||
COOKED_MUTTON(ServerVersion.V1_8, "COOKED_BEEF"),
|
||||
COOKED_RABBIT(ServerVersion.V1_8, "COOKED_BEEF"),
|
||||
CORNFLOWER(ServerVersion.V1_14, "BLUE_ORCHID", "RED_ROSE", (byte) 1),
|
||||
CREEPER_BANNER_PATTERN(ServerVersion.V1_14, "PAPER"),
|
||||
CROSSBOW(ServerVersion.V1_14, "BOW"),
|
||||
CUT_RED_SANDSTONE(ServerVersion.V1_13, "RED_SANDSTONE", (byte) 2, ServerVersion.V1_8, "SANDSTONE", (byte) 0),
|
||||
CUT_RED_SANDSTONE_SLAB(ServerVersion.V1_14, "STONE_SLAB", "STEP", (byte) 0),
|
||||
CUT_SANDSTONE(ServerVersion.V1_13, "SANDSTONE", (byte) 2),
|
||||
CUT_SANDSTONE_SLAB(ServerVersion.V1_14, "STONE_SLAB", "STEP", (byte) 0),
|
||||
CYAN_BANNER(ServerVersion.V1_8, "SIGN"),
|
||||
CYAN_BED(ServerVersion.V1_12, "BED"),
|
||||
CYAN_CONCRETE(ServerVersion.V1_12, "STAINED_CLAY", (byte) 9),
|
||||
CYAN_CONCRETE_POWDER(ServerVersion.V1_12, "STAINED_CLAY", (byte) 9),
|
||||
CYAN_GLAZED_TERRACOTTA(ServerVersion.V1_12, "STAINED_CLAY", (byte) 9),
|
||||
CYAN_SHULKER_BOX(ServerVersion.V1_11, "ENDER_CHEST"),
|
||||
CYAN_WALL_BANNER(ServerVersion.V1_8, "WALL_SIGN"),
|
||||
DARK_OAK_BOAT(ServerVersion.V1_9, "BOAT"),
|
||||
DARK_OAK_BUTTON(ServerVersion.V1_13, "WOOD_BUTTON"),
|
||||
DARK_OAK_DOOR(ServerVersion.V1_8, "WOOD_DOOR"),
|
||||
DARK_OAK_FENCE(ServerVersion.V1_8, "FENCE"),
|
||||
DARK_OAK_FENCE_GATE(ServerVersion.V1_8, "FENCE_GATE"),
|
||||
DARK_OAK_PRESSURE_PLATE(ServerVersion.V1_13, "WOOD_PLATE"),
|
||||
DARK_OAK_SIGN(ServerVersion.V1_14, "SIGN"),
|
||||
DARK_OAK_TRAPDOOR(ServerVersion.V1_13, "TRAP_DOOR"),
|
||||
DARK_OAK_WALL_SIGN(ServerVersion.V1_14, "WALL_SIGN"),
|
||||
DARK_PRISMARINE(ServerVersion.V1_8, "WOOL", (byte) 7),
|
||||
DARK_PRISMARINE_SLAB(ServerVersion.V1_13, "STEP", (byte) 0),
|
||||
DARK_PRISMARINE_STAIRS(ServerVersion.V1_13, "NETHER_BRICK_STAIRS"),
|
||||
DEAD_BRAIN_CORAL(ServerVersion.V1_13, "COBBLESTONE"),
|
||||
DEAD_BRAIN_CORAL_BLOCK(ServerVersion.V1_13, "COBBLESTONE"),
|
||||
DEAD_BRAIN_CORAL_FAN(ServerVersion.V1_13, "COBBLESTONE"),
|
||||
DEAD_BRAIN_CORAL_WALL_FAN(ServerVersion.V1_13, "COBBLESTONE"),
|
||||
DEAD_BUBBLE_CORAL(ServerVersion.V1_13, "COBBLESTONE"),
|
||||
DEAD_BUBBLE_CORAL_BLOCK(ServerVersion.V1_13, "COBBLESTONE"),
|
||||
DEAD_BUBBLE_CORAL_FAN(ServerVersion.V1_13, "COBBLESTONE"),
|
||||
DEAD_BUBBLE_CORAL_WALL_FAN(ServerVersion.V1_13, "COBBLESTONE"),
|
||||
DEAD_FIRE_CORAL(ServerVersion.V1_13, "COBBLESTONE"),
|
||||
DEAD_FIRE_CORAL_BLOCK(ServerVersion.V1_13, "COBBLESTONE"),
|
||||
DEAD_FIRE_CORAL_FAN(ServerVersion.V1_13, "COBBLESTONE"),
|
||||
DEAD_FIRE_CORAL_WALL_FAN(ServerVersion.V1_13, "COBBLESTONE"),
|
||||
DEAD_HORN_CORAL(ServerVersion.V1_13, "COBBLESTONE"),
|
||||
DEAD_HORN_CORAL_BLOCK(ServerVersion.V1_13, "COBBLESTONE"),
|
||||
DEAD_HORN_CORAL_FAN(ServerVersion.V1_13, "COBBLESTONE"),
|
||||
DEAD_HORN_CORAL_WALL_FAN(ServerVersion.V1_13, "COBBLESTONE"),
|
||||
DEAD_TUBE_CORAL(ServerVersion.V1_13, "COBBLESTONE"),
|
||||
DEAD_TUBE_CORAL_BLOCK(ServerVersion.V1_13, "COBBLESTONE"),
|
||||
DEAD_TUBE_CORAL_FAN(ServerVersion.V1_13, "COBBLESTONE"), // these could also be a dead_bush ?
|
||||
DEAD_TUBE_CORAL_WALL_FAN(ServerVersion.V1_13, "COBBLESTONE"),
|
||||
DEBUG_STICK(ServerVersion.V1_13, "STICK"),
|
||||
DIORITE(ServerVersion.V1_8, "STONE"),
|
||||
DIORITE_SLAB(ServerVersion.V1_14, "STONE_SLAB", "STEP", (byte) 0),
|
||||
DIORITE_STAIRS(ServerVersion.V1_14, "STONE_BRICK_STAIRS", "SMOOTH_STAIRS"),
|
||||
DIORITE_WALL(ServerVersion.V1_14, "COBBLESTONE_WALL", "COBBLE_WALL"),
|
||||
DOLPHIN_SPAWN_EGG(ServerVersion.V1_13, "MONSTER_EGG", (byte) 0),
|
||||
DONKEY_SPAWN_EGG(ServerVersion.V1_11, "MONSTER_EGG", (byte) 100),
|
||||
DRAGON_BREATH(ServerVersion.V1_9, "POTION", (byte) 0), // or maybe glowstone..
|
||||
DRAGON_HEAD(ServerVersion.V1_9, "SKULL_ITEM", (byte) 4),
|
||||
DRAGON_WALL_HEAD(ServerVersion.V1_9, "SKULL", (byte) 4),
|
||||
DRIED_KELP(ServerVersion.V1_13, "POTATO_ITEM"),
|
||||
DRIED_KELP_BLOCK(ServerVersion.V1_13, "HAY_BLOCK"),
|
||||
DROWNED_SPAWN_EGG(ServerVersion.V1_13, "MONSTER_EGG", (byte) 54),
|
||||
ELDER_GUARDIAN_SPAWN_EGG(ServerVersion.V1_11, "MONSTER_EGG", (byte) 51),
|
||||
ELYTRA(ServerVersion.V1_9, "IRON_CHESTPLATE"),
|
||||
END_CRYSTAL(ServerVersion.V1_9, "STAINED_GLASS", (byte) 0),
|
||||
END_GATEWAY(ServerVersion.V1_9, "BEACON"),
|
||||
END_PORTAL(ServerVersion.V1_9, "PORTAL"),
|
||||
END_PORTAL_FRAME(ServerVersion.V1_9, "SANDSTONE", (byte) 1),
|
||||
END_ROD(ServerVersion.V1_9, "STAINED_GLASS_PANE", (byte) 0),
|
||||
END_STONE(ServerVersion.V1_9, "SANDSTONE", (byte) 0),
|
||||
END_STONE_BRICK_SLAB(ServerVersion.V1_14, "STONE_SLAB", "STEP", (byte) 0),
|
||||
END_STONE_BRICK_STAIRS(ServerVersion.V1_14, "SANDSTONE_STAIRS"),
|
||||
END_STONE_BRICK_WALL(ServerVersion.V1_14, "COBBLESTONE_WALL", "COBBLE_WALL"),
|
||||
END_STONE_BRICKS(ServerVersion.V1_9, "SANDSTONE", (byte) 2),
|
||||
ENDERMITE_SPAWN_EGG(ServerVersion.V1_8, "MONSTER_EGG", (byte) 60),
|
||||
EVOKER_SPAWN_EGG(ServerVersion.V1_11, "MONSTER_EGG", (byte) 51),
|
||||
FILLED_MAP(ServerVersion.V1_13, "EMPTY_MAP"),
|
||||
FIRE_CORAL(ServerVersion.V1_13, "WOOL", (byte) 14),
|
||||
FIRE_CORAL_BLOCK(ServerVersion.V1_13, "WOOL", (byte) 14),
|
||||
FIRE_CORAL_FAN(ServerVersion.V1_13, "WOOL", (byte) 14),
|
||||
FIRE_CORAL_WALL_FAN(ServerVersion.V1_13, "WOOL", (byte) 14),
|
||||
FLETCHING_TABLE(ServerVersion.V1_14, "CRAFTING_TABLE", "WORKBENCH"),
|
||||
FLOWER_BANNER_PATTERN(ServerVersion.V1_14, "PAPER"),
|
||||
FOX_SPAWN_EGG(ServerVersion.V1_14, "OCELOT_SPAWN_EGG", "MONSTER_EGG", (byte) 98),
|
||||
FROSTED_ICE(ServerVersion.V1_13, "ICE"),
|
||||
GLOBE_BANNER_PATTERN(ServerVersion.V1_14, "PAPER"),
|
||||
GRANITE(ServerVersion.V1_8, "STONE"),
|
||||
GRANITE_SLAB(ServerVersion.V1_14, "STONE_SLAB", "STEP", (byte) 0),
|
||||
GRANITE_STAIRS(ServerVersion.V1_14, "STONE_BRICK_STAIRS", "SMOOTH_STAIRS"),
|
||||
GRANITE_WALL(ServerVersion.V1_14, "COBBLESTONE_WALL", "COBBLE_WALL"),
|
||||
GRASS_PATH(ServerVersion.V1_9, "DIRT", (byte) 1, ServerVersion.V1_8, "DIRT"),
|
||||
GRAY_BANNER(ServerVersion.V1_8, "SIGN"),
|
||||
GRAY_BED(ServerVersion.V1_12, "BED"),
|
||||
GRAY_CONCRETE(ServerVersion.V1_12, "STAINED_CLAY", (byte) 7),
|
||||
GRAY_CONCRETE_POWDER(ServerVersion.V1_12, "STAINED_CLAY", (byte) 7),
|
||||
GRAY_GLAZED_TERRACOTTA(ServerVersion.V1_12, "STAINED_CLAY", (byte) 7),
|
||||
GRAY_SHULKER_BOX(ServerVersion.V1_11, "ENDER_CHEST"),
|
||||
GRAY_WALL_BANNER(ServerVersion.V1_8, "WALL_SIGN"),
|
||||
GREEN_BANNER(ServerVersion.V1_8, "SIGN"),
|
||||
GREEN_BED(ServerVersion.V1_12, "BED"),
|
||||
GREEN_CONCRETE(ServerVersion.V1_12, "STAINED_CLAY", (byte) 13),
|
||||
GREEN_CONCRETE_POWDER(ServerVersion.V1_12, "STAINED_CLAY", (byte) 13),
|
||||
GREEN_GLAZED_TERRACOTTA(ServerVersion.V1_12, "STAINED_CLAY", (byte) 13),
|
||||
GREEN_SHULKER_BOX(ServerVersion.V1_11, "ENDER_CHEST"),
|
||||
GREEN_WALL_BANNER(ServerVersion.V1_8, "WALL_SIGN"),
|
||||
GRINDSTONE(ServerVersion.V1_14, "ANVIL"),
|
||||
GUARDIAN_SPAWN_EGG(ServerVersion.V1_8, "MONSTER_EGG", (byte) 51),
|
||||
HEART_OF_THE_SEA(ServerVersion.V1_13, "DIAMOND"),
|
||||
HORN_CORAL(ServerVersion.V1_13, "WOOL", (byte) 4),
|
||||
HORN_CORAL_BLOCK(ServerVersion.V1_13, "WOOL", (byte) 4),
|
||||
HORN_CORAL_FAN(ServerVersion.V1_13, "WOOL", (byte) 4),
|
||||
HORN_CORAL_WALL_FAN(ServerVersion.V1_13, "WOOL", (byte) 4),
|
||||
HUSK_SPAWN_EGG(ServerVersion.V1_11, "MONSTER_EGG", (byte) 54),
|
||||
IRON_NUGGET(ServerVersion.V1_11, "IRON_INGOT"),
|
||||
IRON_TRAPDOOR(ServerVersion.V1_8, "TRAP_DOOR"),
|
||||
JIGSAW(ServerVersion.V1_14, "ANVIL"),
|
||||
JUNGLE_BOAT(ServerVersion.V1_9, "BOAT"),
|
||||
JUNGLE_BUTTON(ServerVersion.V1_13, "WOOD_BUTTON"),
|
||||
JUNGLE_DOOR(ServerVersion.V1_8, "WOOD_DOOR"),
|
||||
JUNGLE_FENCE(ServerVersion.V1_8, "FENCE"),
|
||||
JUNGLE_FENCE_GATE(ServerVersion.V1_8, "FENCE_GATE"),
|
||||
JUNGLE_PRESSURE_PLATE(ServerVersion.V1_13, "WOOD_PLATE"),
|
||||
JUNGLE_SIGN(ServerVersion.V1_14, "SIGN"),
|
||||
JUNGLE_TRAPDOOR(ServerVersion.V1_13, "TRAP_DOOR"),
|
||||
JUNGLE_WALL_SIGN(ServerVersion.V1_14, "WALL_SIGN"),
|
||||
KELP(ServerVersion.V1_13, "POTATO_ITEM"),
|
||||
KELP_PLANT(ServerVersion.V1_13, "WATER"), // idk.
|
||||
KNOWLEDGE_BOOK(ServerVersion.V1_12, "BOOK"),
|
||||
LANTERN(ServerVersion.V1_14, "GLOWSTONE"),
|
||||
LEATHER_HORSE_ARMOR(ServerVersion.V1_14, "IRON_BARDING"),
|
||||
LECTERN(ServerVersion.V1_14, "BOOKSHELF"),
|
||||
LIGHT_BLUE_BANNER(ServerVersion.V1_8, "SIGN"),
|
||||
LIGHT_BLUE_BED(ServerVersion.V1_12, "BED"),
|
||||
LIGHT_BLUE_CONCRETE(ServerVersion.V1_12, "STAINED_CLAY", (byte) 3),
|
||||
LIGHT_BLUE_CONCRETE_POWDER(ServerVersion.V1_12, "STAINED_CLAY", (byte) 3),
|
||||
LIGHT_BLUE_GLAZED_TERRACOTTA(ServerVersion.V1_12, "STAINED_CLAY", (byte) 3),
|
||||
LIGHT_BLUE_SHULKER_BOX(ServerVersion.V1_11, "ENDER_CHEST"),
|
||||
LIGHT_BLUE_WALL_BANNER(ServerVersion.V1_8, "WALL_SIGN"),
|
||||
LIGHT_GRAY_BANNER(ServerVersion.V1_8, "SIGN"),
|
||||
LIGHT_GRAY_BED(ServerVersion.V1_12, "BED"),
|
||||
LIGHT_GRAY_CONCRETE(ServerVersion.V1_12, "STAINED_CLAY", (byte) 8),
|
||||
LIGHT_GRAY_CONCRETE_POWDER(ServerVersion.V1_12, "STAINED_CLAY", (byte) 8),
|
||||
LIGHT_GRAY_GLAZED_TERRACOTTA(ServerVersion.V1_12, "STAINED_CLAY", (byte) 8),
|
||||
LIGHT_GRAY_SHULKER_BOX(ServerVersion.V1_11, "ENDER_CHEST"),
|
||||
LIGHT_GRAY_WALL_BANNER(ServerVersion.V1_8, "WALL_SIGN"),
|
||||
LILY_OF_THE_VALLEY(ServerVersion.V1_14, "AZURE_BLUET", "RED_ROSE", (byte) 3),
|
||||
LIME_BANNER(ServerVersion.V1_8, "SIGN"),
|
||||
LIME_BED(ServerVersion.V1_12, "BED"),
|
||||
LIME_CONCRETE(ServerVersion.V1_12, "STAINED_CLAY", (byte) 5),
|
||||
LIME_CONCRETE_POWDER(ServerVersion.V1_12, "STAINED_CLAY", (byte) 5),
|
||||
LIME_GLAZED_TERRACOTTA(ServerVersion.V1_12, "STAINED_CLAY", (byte) 5),
|
||||
LIME_SHULKER_BOX(ServerVersion.V1_11, "ENDER_CHEST"),
|
||||
LIME_WALL_BANNER(ServerVersion.V1_8, "WALL_SIGN"),
|
||||
LINGERING_POTION(ServerVersion.V1_9, "POTION", (byte) 0),
|
||||
LLAMA_SPAWN_EGG(ServerVersion.V1_11, "MONSTER_EGG", (byte) 0),
|
||||
LOOM(ServerVersion.V1_14, "CRAFTING_TABLE", "WORKBENCH"),
|
||||
MAGENTA_BANNER(ServerVersion.V1_8, "SIGN"),
|
||||
MAGENTA_BED(ServerVersion.V1_12, "BED"),
|
||||
MAGENTA_CONCRETE(ServerVersion.V1_12, "STAINED_CLAY", (byte) 2),
|
||||
MAGENTA_CONCRETE_POWDER(ServerVersion.V1_12, "STAINED_CLAY", (byte) 2),
|
||||
MAGENTA_GLAZED_TERRACOTTA(ServerVersion.V1_12, "STAINED_CLAY", (byte) 2),
|
||||
MAGENTA_SHULKER_BOX(ServerVersion.V1_11, "ENDER_CHEST"),
|
||||
MAGENTA_WALL_BANNER(ServerVersion.V1_8, "WALL_SIGN"),
|
||||
MAGMA_BLOCK(ServerVersion.V1_10, "NETHER_BRICK"),
|
||||
MOJANG_BANNER_PATTERN(ServerVersion.V1_14, "PAPER"),
|
||||
MOSSY_COBBLESTONE_SLAB(ServerVersion.V1_14, "COBBLESTONE_SLAB", "STEP", (byte) 3),
|
||||
MOSSY_COBBLESTONE_STAIRS(ServerVersion.V1_14, "COBBLESTONE_STAIRS"),
|
||||
MOSSY_COBBLESTONE_WALL(ServerVersion.V1_14, "COBBLESTONE_WALL", "COBBLE_WALL"),
|
||||
MOSSY_STONE_BRICK_SLAB(ServerVersion.V1_14, "BRICK_SLAB", "STEP", (byte) 4),
|
||||
MOSSY_STONE_BRICK_STAIRS(ServerVersion.V1_14, "STONE_BRICK_STAIRS", "SMOOTH_STAIRS"),
|
||||
MOSSY_STONE_BRICK_WALL(ServerVersion.V1_14, "COBBLESTONE_WALL", "COBBLE_WALL"),
|
||||
MULE_SPAWN_EGG(ServerVersion.V1_11, "MONSTER_EGG", (byte) 100),
|
||||
MUTTON(ServerVersion.V1_8, "RAW_BEEF"),
|
||||
NAUTILUS_SHELL(ServerVersion.V1_13, "SNOW_BALL"),
|
||||
NETHER_BRICK_FENCE(ServerVersion.V1_13, "FENCE"),
|
||||
NETHER_BRICK_WALL(ServerVersion.V1_14, "COBBLESTONE_WALL", "COBBLE_WALL"),
|
||||
NETHER_WART_BLOCK(ServerVersion.V1_10, "NETHERRACK"),
|
||||
OAK_BOAT(ServerVersion.V1_9, "BOAT"),
|
||||
OBSERVER(ServerVersion.V1_11, "DISPENSER"),
|
||||
ORANGE_BANNER(ServerVersion.V1_8, "SIGN"),
|
||||
ORANGE_BED(ServerVersion.V1_12, "BED"),
|
||||
ORANGE_CONCRETE(ServerVersion.V1_12, "STAINED_CLAY", (byte) 1),
|
||||
ORANGE_CONCRETE_POWDER(ServerVersion.V1_12, "STAINED_CLAY", (byte) 1),
|
||||
ORANGE_GLAZED_TERRACOTTA(ServerVersion.V1_12, "STAINED_CLAY", (byte) 1),
|
||||
ORANGE_SHULKER_BOX(ServerVersion.V1_11, "ENDER_CHEST"),
|
||||
ORANGE_WALL_BANNER(ServerVersion.V1_8, "WALL_SIGN"),
|
||||
PANDA_SPAWN_EGG(ServerVersion.V1_14, "COW_SPAWN_EGG", "MONSTER_EGG", (byte) 92),
|
||||
PARROT_SPAWN_EGG(ServerVersion.V1_12, "MONSTER_EGG", (byte) 65),
|
||||
PHANTOM_MEMBRANE(ServerVersion.V1_13, "FEATHER"),
|
||||
PHANTOM_SPAWN_EGG(ServerVersion.V1_13, "MONSTER_EGG", (byte) 65),
|
||||
PILLAGER_SPAWN_EGG(ServerVersion.V1_14, "VILLAGER_SPAWN_EGG", "MONSTER_EGG", (byte) 120),
|
||||
PINK_BANNER(ServerVersion.V1_8, "SIGN"),
|
||||
PINK_BED(ServerVersion.V1_12, "BED"),
|
||||
PINK_CONCRETE(ServerVersion.V1_12, "STAINED_CLAY", (byte) 6),
|
||||
PINK_CONCRETE_POWDER(ServerVersion.V1_12, "STAINED_CLAY", (byte) 6),
|
||||
PINK_GLAZED_TERRACOTTA(ServerVersion.V1_12, "STAINED_CLAY", (byte) 6),
|
||||
PINK_SHULKER_BOX(ServerVersion.V1_11, "ENDER_CHEST"),
|
||||
PINK_WALL_BANNER(ServerVersion.V1_8, "WALL_SIGN"),
|
||||
PODZOL(ServerVersion.V1_8, "DIRT"),
|
||||
POLAR_BEAR_SPAWN_EGG(ServerVersion.V1_10, "MONSTER_EGG", (byte) 0),
|
||||
POLISHED_ANDESITE(ServerVersion.V1_8, "STONE"),
|
||||
POLISHED_ANDESITE_SLAB(ServerVersion.V1_14, "STONE_SLAB", "STEP", (byte) 0),
|
||||
POLISHED_ANDESITE_STAIRS(ServerVersion.V1_14, "STONE_BRICK_STAIRS", "SMOOTH_STAIRS"),
|
||||
POLISHED_DIORITE(ServerVersion.V1_8, "STONE"),
|
||||
POLISHED_DIORITE_SLAB(ServerVersion.V1_14, "STONE_SLAB", "STEP", (byte) 0),
|
||||
POLISHED_DIORITE_STAIRS(ServerVersion.V1_14, "STONE_BRICK_STAIRS", "SMOOTH_STAIRS"),
|
||||
POLISHED_GRANITE(ServerVersion.V1_8, "STONE"),
|
||||
POLISHED_GRANITE_SLAB(ServerVersion.V1_14, "STONE_SLAB", "STEP", (byte) 0),
|
||||
POLISHED_GRANITE_STAIRS(ServerVersion.V1_14, "STONE_BRICK_STAIRS", "SMOOTH_STAIRS"),
|
||||
POPPED_CHORUS_FRUIT(ServerVersion.V1_9, "GOLDEN_APPLE"),
|
||||
POTTED_ACACIA_SAPLING(ServerVersion.V1_13, "FLOWER_POT_ITEM"),
|
||||
POTTED_ALLIUM(ServerVersion.V1_13, "FLOWER_POT_ITEM"),
|
||||
POTTED_AZURE_BLUET(ServerVersion.V1_13, "FLOWER_POT_ITEM"),
|
||||
POTTED_BAMBOO(ServerVersion.V1_14, "FLOWER_POT", "FLOWER_POT_ITEM"),
|
||||
POTTED_BIRCH_SAPLING(ServerVersion.V1_13, "FLOWER_POT_ITEM"),
|
||||
POTTED_BLUE_ORCHID(ServerVersion.V1_13, "FLOWER_POT_ITEM"),
|
||||
POTTED_BROWN_MUSHROOM(ServerVersion.V1_13, "FLOWER_POT_ITEM"),
|
||||
POTTED_CACTUS(ServerVersion.V1_13, "FLOWER_POT_ITEM"),
|
||||
POTTED_CORNFLOWER(ServerVersion.V1_14, "FLOWER_POT", "FLOWER_POT_ITEM"),
|
||||
POTTED_DANDELION(ServerVersion.V1_13, "FLOWER_POT_ITEM"),
|
||||
POTTED_DARK_OAK_SAPLING(ServerVersion.V1_13, "FLOWER_POT_ITEM"),
|
||||
POTTED_DEAD_BUSH(ServerVersion.V1_13, "FLOWER_POT_ITEM"),
|
||||
POTTED_FERN(ServerVersion.V1_13, "FLOWER_POT_ITEM"),
|
||||
POTTED_JUNGLE_SAPLING(ServerVersion.V1_13, "FLOWER_POT_ITEM"),
|
||||
POTTED_LILY_OF_THE_VALLEY(ServerVersion.V1_14, "FLOWER_POT", "FLOWER_POT_ITEM"),
|
||||
POTTED_OAK_SAPLING(ServerVersion.V1_13, "FLOWER_POT_ITEM"),
|
||||
POTTED_ORANGE_TULIP(ServerVersion.V1_13, "FLOWER_POT_ITEM"),
|
||||
POTTED_OXEYE_DAISY(ServerVersion.V1_13, "FLOWER_POT_ITEM"),
|
||||
POTTED_PINK_TULIP(ServerVersion.V1_13, "FLOWER_POT_ITEM"),
|
||||
POTTED_POPPY(ServerVersion.V1_13, "FLOWER_POT_ITEM"),
|
||||
POTTED_RED_MUSHROOM(ServerVersion.V1_13, "FLOWER_POT_ITEM"),
|
||||
POTTED_RED_TULIP(ServerVersion.V1_13, "FLOWER_POT_ITEM"),
|
||||
POTTED_SPRUCE_SAPLING(ServerVersion.V1_13, "FLOWER_POT_ITEM"),
|
||||
POTTED_WHITE_TULIP(ServerVersion.V1_13, "FLOWER_POT_ITEM"),
|
||||
POTTED_WITHER_ROSE(ServerVersion.V1_14, "FLOWER_POT", "FLOWER_POT_ITEM"),
|
||||
PRISMARINE(ServerVersion.V1_8, "WOOL", (byte) 9),
|
||||
PRISMARINE_BRICK_SLAB(ServerVersion.V1_14, "STONE_SLAB", "STEP", (byte) 0),
|
||||
PRISMARINE_BRICK_STAIRS(ServerVersion.V1_13, "SMOOTH_STAIRS"),
|
||||
PRISMARINE_BRICKS(ServerVersion.V1_8, "WOOL", (byte) 9),
|
||||
PRISMARINE_CRYSTALS(ServerVersion.V1_8, "CLAY_BALL"),
|
||||
PRISMARINE_SHARD(ServerVersion.V1_8, "FLINT"),
|
||||
PRISMARINE_SLAB(ServerVersion.V1_13, "STEP", (byte) 0),
|
||||
PRISMARINE_STAIRS(ServerVersion.V1_13, "COBBLESTONE_STAIRS"),
|
||||
PRISMARINE_WALL(ServerVersion.V1_14, "COBBLESTONE_WALL", "COBBLE_WALL"),
|
||||
PUFFERFISH_BUCKET(ServerVersion.V1_13, "WATER_BUCKET"),
|
||||
PUFFERFISH_SPAWN_EGG(ServerVersion.V1_13, "MONSTER_EGG", (byte) 60),
|
||||
PURPLE_BANNER(ServerVersion.V1_8, "SIGN"),
|
||||
PURPLE_BED(ServerVersion.V1_12, "BED"),
|
||||
PURPLE_CONCRETE(ServerVersion.V1_12, "STAINED_CLAY", (byte) 10),
|
||||
PURPLE_CONCRETE_POWDER(ServerVersion.V1_12, "STAINED_CLAY", (byte) 10),
|
||||
PURPLE_GLAZED_TERRACOTTA(ServerVersion.V1_12, "STAINED_CLAY", (byte) 10),
|
||||
PURPLE_SHULKER_BOX(ServerVersion.V1_11, "ENDER_CHEST"),
|
||||
PURPLE_WALL_BANNER(ServerVersion.V1_8, "WALL_SIGN"),
|
||||
PURPUR_BLOCK(ServerVersion.V1_9, "WOOL", (byte) 2),
|
||||
PURPUR_PILLAR(ServerVersion.V1_9, "WOOL", (byte) 2),
|
||||
PURPUR_SLAB(ServerVersion.V1_9, "STEP", (byte) 0),
|
||||
PURPUR_STAIRS(ServerVersion.V1_9, "BRICK_STAIRS"),
|
||||
RABBIT(ServerVersion.V1_8, "RAW_BEEF"),
|
||||
RABBIT_FOOT(ServerVersion.V1_8, "ROTTEN_FLESH"),
|
||||
RABBIT_HIDE(ServerVersion.V1_8, "ROTTEN_FLESH"),
|
||||
RABBIT_SPAWN_EGG(ServerVersion.V1_8, "MONSTER_EGG", (byte) 0),
|
||||
RABBIT_STEW(ServerVersion.V1_8, "MUSHROOM_SOUP"),
|
||||
RAVAGER_SPAWN_EGG(ServerVersion.V1_14, "COW_SPAWN_EGG", "MONSTER_EGG", (byte) 92),
|
||||
RED_BANNER(ServerVersion.V1_8, "SIGN"),
|
||||
RED_BED(ServerVersion.V1_12, "BED"),
|
||||
RED_CONCRETE(ServerVersion.V1_12, "STAINED_CLAY", (byte) 14),
|
||||
RED_CONCRETE_POWDER(ServerVersion.V1_12, "STAINED_CLAY", (byte) 14),
|
||||
RED_GLAZED_TERRACOTTA(ServerVersion.V1_12, "STAINED_CLAY", (byte) 14),
|
||||
RED_NETHER_BRICK_SLAB(ServerVersion.V1_14, "STONE_SLAB", "STEP", (byte) 0),
|
||||
RED_NETHER_BRICK_STAIRS(ServerVersion.V1_14, "NETHER_BRICK_STAIRS"),
|
||||
RED_NETHER_BRICK_WALL(ServerVersion.V1_14, "COBBLESTONE_WALL", "COBBLE_WALL"),
|
||||
RED_NETHER_BRICKS(ServerVersion.V1_10, "NETHER_BRICK"),
|
||||
RED_SANDSTONE(ServerVersion.V1_8, "SANDSTONE", (byte) 0),
|
||||
RED_SANDSTONE_SLAB(ServerVersion.V1_8, "STEP", (byte) 0),
|
||||
RED_SANDSTONE_STAIRS(ServerVersion.V1_8, "SANDSTONE_STAIRS"),
|
||||
RED_SANDSTONE_WALL(ServerVersion.V1_14, "COBBLESTONE_WALL", "COBBLE_WALL"),
|
||||
RED_SHULKER_BOX(ServerVersion.V1_11, "ENDER_CHEST"),
|
||||
RED_WALL_BANNER(ServerVersion.V1_8, "WALL_SIGN"),
|
||||
REDSTONE_WALL_TORCH(ServerVersion.V1_13, "REDSTONE_TORCH_ON"),
|
||||
REPEATING_COMMAND_BLOCK(ServerVersion.V1_9, "COMMAND"),
|
||||
SALMON_BUCKET(ServerVersion.V1_13, "WATER_BUCKET"),
|
||||
SALMON_SPAWN_EGG(ServerVersion.V1_13, "MONSTER_EGG", (byte) 60),
|
||||
SANDSTONE_WALL(ServerVersion.V1_14, "COBBLESTONE_WALL", "COBBLE_WALL"),
|
||||
SCAFFOLDING(ServerVersion.V1_14, "LADDER"),
|
||||
SCUTE(ServerVersion.V1_13, "SLIME_BALL"),
|
||||
SEA_LANTERN(ServerVersion.V1_8, "GLOWSTONE"),
|
||||
SEA_PICKLE(ServerVersion.V1_13, "CACTUS"),
|
||||
SEAGRASS(ServerVersion.V1_13, "LONG_GRASS", (byte) 1),
|
||||
SHIELD(ServerVersion.V1_9, "BANNER", (byte) 3, ServerVersion.V1_8, "WOOD_DOOR"),
|
||||
SHULKER_BOX(ServerVersion.V1_13, "ENDER_CHEST"),
|
||||
SHULKER_SHELL(ServerVersion.V1_11, "SUGAR"),
|
||||
SHULKER_SPAWN_EGG(ServerVersion.V1_9, "MONSTER_EGG", (byte) 0),
|
||||
SKELETON_HORSE_SPAWN_EGG(ServerVersion.V1_11, "MONSTER_EGG", (byte) 0),
|
||||
SKULL_BANNER_PATTERN(ServerVersion.V1_14, "PAPER"),
|
||||
SLIME_BLOCK(ServerVersion.V1_8, "WOOL", (byte) 5),
|
||||
SMITHING_TABLE(ServerVersion.V1_14, "CRAFTING_TABLE", "WORKBENCH"),
|
||||
SMOKER(ServerVersion.V1_14, "FURNACE"),
|
||||
SMOOTH_QUARTZ(ServerVersion.V1_13, "QUARTZ_BLOCK", (byte) 0),
|
||||
SMOOTH_QUARTZ_SLAB(ServerVersion.V1_14, "QUARTZ_SLAB", "STEP", (byte) 7),
|
||||
SMOOTH_QUARTZ_STAIRS(ServerVersion.V1_14, "QUARTZ_STAIRS"),
|
||||
SMOOTH_RED_SANDSTONE(ServerVersion.V1_13, "RED_SANDSTONE", (byte) 0, ServerVersion.V1_8, "SANDSTONE", (byte) 0),
|
||||
SMOOTH_RED_SANDSTONE_SLAB(ServerVersion.V1_14, "SANDSTONE_SLAB", "STEP", (byte) 1),
|
||||
SMOOTH_RED_SANDSTONE_STAIRS(ServerVersion.V1_14, "RED_SANDSTONE_STAIRS", ServerVersion.V1_8, "SANDSTONE_STAIRS"),
|
||||
SMOOTH_SANDSTONE_SLAB(ServerVersion.V1_14, "SANDSTONE_SLAB", "STEP", (byte) 1),
|
||||
SMOOTH_SANDSTONE_STAIRS(ServerVersion.V1_14, "SANDSTONE_STAIRS"),
|
||||
SMOOTH_STONE(ServerVersion.V1_13, "STONE"), // DOUBLE_STEP is a closer texture match
|
||||
SMOOTH_STONE_SLAB(ServerVersion.V1_14, "STONE_SLAB", "STEP", (byte) 0),
|
||||
SPECTRAL_ARROW(ServerVersion.V1_9, "ARROW"),
|
||||
SPRUCE_BOAT(ServerVersion.V1_9, "BOAT"),
|
||||
SPRUCE_BUTTON(ServerVersion.V1_13, "WOOD_BUTTON"),
|
||||
SPRUCE_DOOR(ServerVersion.V1_8, "WOOD_DOOR"),
|
||||
SPRUCE_FENCE(ServerVersion.V1_8, "FENCE"),
|
||||
SPRUCE_FENCE_GATE(ServerVersion.V1_8, "FENCE_GATE"),
|
||||
SPRUCE_PRESSURE_PLATE(ServerVersion.V1_13, "WOOD_PLATE"),
|
||||
SPRUCE_SIGN(ServerVersion.V1_14, "SIGN"),
|
||||
SPRUCE_TRAPDOOR(ServerVersion.V1_13, "TRAP_DOOR"),
|
||||
SPRUCE_WALL_SIGN(ServerVersion.V1_14, "WALL_SIGN"),
|
||||
//STONE(ServerVersion.V1_8, "STONE"), // funny how that happened, heh. Non-data to data to non-data again
|
||||
STONE_BRICK_WALL(ServerVersion.V1_14, "COBBLESTONE_WALL", "COBBLE_WALL"),
|
||||
STONE_SLAB(ServerVersion.V1_13, "STEP", (byte) 0),
|
||||
STONE_STAIRS(ServerVersion.V1_14, "STONE_BRICK_STAIRS", "SMOOTH_STAIRS"),
|
||||
STONECUTTER(ServerVersion.V1_14, "ANVIL"),
|
||||
STRAY_SPAWN_EGG(ServerVersion.V1_11, "MONSTER_EGG", (byte) 0),
|
||||
STRIPPED_ACACIA_LOG(ServerVersion.V1_13, "LOG_2", (byte) 0),
|
||||
STRIPPED_ACACIA_WOOD(ServerVersion.V1_13, "LOG_2", (byte) 12),
|
||||
STRIPPED_BIRCH_LOG(ServerVersion.V1_13, "LOG", (byte) 2),
|
||||
STRIPPED_BIRCH_WOOD(ServerVersion.V1_13, "LOG", (byte) 14),
|
||||
STRIPPED_DARK_OAK_LOG(ServerVersion.V1_13, "LOG_2", (byte) 1),
|
||||
STRIPPED_DARK_OAK_WOOD(ServerVersion.V1_13, "LOG_2", (byte) 13),
|
||||
STRIPPED_JUNGLE_LOG(ServerVersion.V1_13, "LOG", (byte) 3),
|
||||
STRIPPED_JUNGLE_WOOD(ServerVersion.V1_13, "LOG", (byte) 15),
|
||||
STRIPPED_OAK_LOG(ServerVersion.V1_13, "LOG", (byte) 0),
|
||||
STRIPPED_OAK_WOOD(ServerVersion.V1_13, "LOG", (byte) 12),
|
||||
STRIPPED_SPRUCE_LOG(ServerVersion.V1_13, "LOG", (byte) 1),
|
||||
STRIPPED_SPRUCE_WOOD(ServerVersion.V1_13, "LOG", (byte) 13),
|
||||
STRUCTURE_BLOCK(ServerVersion.V1_9, "COMMAND"),
|
||||
STRUCTURE_VOID(ServerVersion.V1_10, "BARRIER", ServerVersion.V1_8, "STAINED_GLASS", (byte) 14), // Block would be air, but these make more sense as an item
|
||||
SUSPICIOUS_STEW(ServerVersion.V1_14, "MUSHROOM_SOUP"),
|
||||
SWEET_BERRIES(ServerVersion.V1_14, "POTATO", "POTATO_ITEM"),
|
||||
SWEET_BERRY_BUSH(ServerVersion.V1_14, "OAK_LEAVES", "LEAVES", (byte) 3),
|
||||
TALL_SEAGRASS(ServerVersion.V1_13, "LONG_GRASS", (byte) 1),
|
||||
TIPPED_ARROW(ServerVersion.V1_9, "ARROW"),
|
||||
TOTEM_OF_UNDYING(ServerVersion.V1_11, "CHAINMAIL_CHESTPLATE"),
|
||||
TRADER_LLAMA_SPAWN_EGG(ServerVersion.V1_14, "LLAMA_SPAWN_EGG", "MONSTER_EGG", (byte) 103), // todo? should we change the item here? llamas are v1.11+
|
||||
TRIDENT(ServerVersion.V1_13, "ARROW"),
|
||||
TROPICAL_FISH_BUCKET(ServerVersion.V1_13, "WATER_BUCKET"),
|
||||
TROPICAL_FISH_SPAWN_EGG(ServerVersion.V1_13, "MONSTER_EGG", (byte) 60),
|
||||
TUBE_CORAL(ServerVersion.V1_13, "WOOL", (byte) 11),
|
||||
TUBE_CORAL_BLOCK(ServerVersion.V1_13, "WOOL", (byte) 11),
|
||||
TUBE_CORAL_FAN(ServerVersion.V1_13, "WOOL", (byte) 11),
|
||||
TUBE_CORAL_WALL_FAN(ServerVersion.V1_13, "WOOL", (byte) 11),
|
||||
TURTLE_EGG(ServerVersion.V1_13, "DRAGON_EGG"),
|
||||
TURTLE_HELMET(ServerVersion.V1_13, "LEATHER_HELMET"), // would be cool to color it green..
|
||||
TURTLE_SPAWN_EGG(ServerVersion.V1_13, "MONSTER_EGG", (byte) 60),
|
||||
VEX_SPAWN_EGG(ServerVersion.V1_11, "MONSTER_EGG", (byte) 0),
|
||||
VINDICATOR_SPAWN_EGG(ServerVersion.V1_11, "MONSTER_EGG", (byte) 0),
|
||||
VOID_AIR(ServerVersion.V1_13, "AIR"),
|
||||
WALL_TORCH(ServerVersion.V1_13, "TORCH"),
|
||||
WANDERING_TRADER_SPAWN_EGG(ServerVersion.V1_14, "VILLAGER_SPAWN_EGG", "MONSTER_EGG", (byte) 120),
|
||||
WHITE_BANNER(ServerVersion.V1_8, "SIGN"),
|
||||
WHITE_BED(ServerVersion.V1_12, "BED"),
|
||||
WHITE_CONCRETE(ServerVersion.V1_12, "STAINED_CLAY", (byte) 0),
|
||||
WHITE_CONCRETE_POWDER(ServerVersion.V1_12, "STAINED_CLAY", (byte) 0),
|
||||
WHITE_GLAZED_TERRACOTTA(ServerVersion.V1_12, "STAINED_CLAY", (byte) 0),
|
||||
WHITE_SHULKER_BOX(ServerVersion.V1_11, "ENDER_CHEST"),
|
||||
WHITE_WALL_BANNER(ServerVersion.V1_8, "WALL_SIGN"),
|
||||
WITHER_ROSE(ServerVersion.V1_14, "POPPY", "RED_ROSE", (byte) 0),
|
||||
WITHER_SKELETON_SPAWN_EGG(ServerVersion.V1_11, "MONSTER_EGG", (byte) 0),
|
||||
YELLOW_BANNER(ServerVersion.V1_8, "SIGN"),
|
||||
YELLOW_BED(ServerVersion.V1_12, "BED"),
|
||||
YELLOW_CONCRETE(ServerVersion.V1_12, "STAINED_CLAY", (byte) 4),
|
||||
YELLOW_CONCRETE_POWDER(ServerVersion.V1_12, "STAINED_CLAY", (byte) 4),
|
||||
YELLOW_GLAZED_TERRACOTTA(ServerVersion.V1_12, "STAINED_CLAY", (byte) 4),
|
||||
YELLOW_SHULKER_BOX(ServerVersion.V1_11, "ENDER_CHEST"),
|
||||
YELLOW_WALL_BANNER(ServerVersion.V1_8, "WALL_SIGN"),
|
||||
ZOMBIE_HORSE_SPAWN_EGG(ServerVersion.V1_11, "MONSTER_EGG", (byte) 0),
|
||||
ZOMBIE_VILLAGER_SPAWN_EGG(ServerVersion.V1_11, "MONSTER_EGG", (byte) 0),
|
||||
|
||||
;
|
||||
|
||||
final ServerVersion versionLessThan;
|
||||
final String modernMaterial;
|
||||
final String legacyMaterial;
|
||||
final Byte legacyData;
|
||||
final ServerVersion legacyMinimumVersion;
|
||||
final String compatibleMaterial;
|
||||
final Byte compatibleData;
|
||||
final Material material;
|
||||
final Byte data;
|
||||
|
||||
// map to speed up name->material lookups
|
||||
private static final Map<String, LegacyAnalouges> lookupMap = new HashMap();
|
||||
|
||||
static {
|
||||
for (LegacyAnalouges m : values()) {
|
||||
lookupMap.put(m.name(), m);
|
||||
}
|
||||
}
|
||||
|
||||
public static LegacyAnalouges lookupAnalouge(String material) {
|
||||
return lookupMap.get(material);
|
||||
}
|
||||
|
||||
private LegacyAnalouges(ServerVersion versionLessThan, String legacyMaterial, byte legacyData) {
|
||||
this(versionLessThan, null, legacyMaterial, legacyData, null, null, null);
|
||||
}
|
||||
|
||||
private LegacyAnalouges(ServerVersion versionLessThan, String legacyMaterial) {
|
||||
this(versionLessThan, null, legacyMaterial, null, null, null, null);
|
||||
}
|
||||
|
||||
private LegacyAnalouges(ServerVersion versionLessThan, String modernAnalouge, String legacyMaterial) {
|
||||
this(versionLessThan, modernAnalouge, legacyMaterial, null, null, null, null);
|
||||
}
|
||||
|
||||
private LegacyAnalouges(ServerVersion versionLessThan, String modernAnalouge, String legacyMaterial, byte legacyData) {
|
||||
this(versionLessThan, modernAnalouge, legacyMaterial, legacyData, null, null, null);
|
||||
}
|
||||
|
||||
private LegacyAnalouges(ServerVersion versionLessThan, String legacyMaterial, byte legacyData, ServerVersion legacyMinimum, String compatMaterial, byte compatData) {
|
||||
this(versionLessThan, null, legacyMaterial, legacyData, legacyMinimum, compatMaterial, compatData);
|
||||
}
|
||||
|
||||
private LegacyAnalouges(ServerVersion versionLessThan, String legacyMaterial, ServerVersion legacyMinimum, String compatMaterial, byte compatData) {
|
||||
this(versionLessThan, null, legacyMaterial, null, legacyMinimum, compatMaterial, compatData);
|
||||
}
|
||||
|
||||
private LegacyAnalouges(ServerVersion versionLessThan, String legacyMaterial, byte legacyData, ServerVersion legacyMinimum, String compatMaterial) {
|
||||
this(versionLessThan, null, legacyMaterial, legacyData, legacyMinimum, compatMaterial, null);
|
||||
}
|
||||
|
||||
private LegacyAnalouges(ServerVersion versionLessThan, String legacyMaterial, ServerVersion legacyMinimum, String compatMaterial) {
|
||||
this(versionLessThan, null, legacyMaterial, null, legacyMinimum, compatMaterial, null);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param versionLessThan AKA, what server version was this material added to minecraft?
|
||||
* @param modernAnalouge post-1.13 material name, if applicable
|
||||
* @param legacyMaterial pre-1.13 material name
|
||||
* @param legacyData data for defining specific legacy items
|
||||
*/
|
||||
private LegacyAnalouges(ServerVersion versionLessThan, String modernAnalouge, String legacyMaterial, Byte legacyData, ServerVersion legacyMinimum, String compatMaterial, Byte compatData) {
|
||||
this.versionLessThan = versionLessThan;
|
||||
this.modernMaterial = modernAnalouge;
|
||||
this.legacyMaterial = legacyMaterial;
|
||||
this.legacyData = legacyData;
|
||||
|
||||
this.legacyMinimumVersion = legacyMinimum;
|
||||
this.compatibleMaterial = compatMaterial;
|
||||
this.compatibleData = compatData;
|
||||
|
||||
if (ServerVersion.isServerVersionBelow(versionLessThan)) {
|
||||
if(legacyMinimumVersion != null && ServerVersion.isServerVersionBelow(legacyMinimumVersion)) {
|
||||
// fallback material not available, so use its fallback
|
||||
material = Material.getMaterial(compatibleMaterial);
|
||||
data = compatibleData;
|
||||
} else if (modernMaterial == null || ServerVersion.isServerVersionBelow(ServerVersion.V1_13)) {
|
||||
// use legacy material if on legacy
|
||||
material = Material.getMaterial(legacyMaterial);
|
||||
data = legacyData;
|
||||
} else if (modernMaterial != null) {
|
||||
material = Material.getMaterial(modernMaterial);
|
||||
data = null;
|
||||
} else {
|
||||
material = null;
|
||||
data = null;
|
||||
}
|
||||
} else {
|
||||
material = null;
|
||||
data = null;
|
||||
}
|
||||
}
|
||||
|
||||
public Material getMaterial() {
|
||||
return material;
|
||||
}
|
||||
|
||||
public boolean usesData() {
|
||||
return data != null;
|
||||
}
|
||||
|
||||
public byte getData() {
|
||||
return data == null ? 0 : data;
|
||||
}
|
||||
|
||||
public ItemStack getItem() {
|
||||
if (material == null) {
|
||||
return null;
|
||||
}
|
||||
return data != null ? new ItemStack(material, 1, data) : new ItemStack(material);
|
||||
}
|
||||
}
|
2110
src/main/java/com/songoda/core/compatibility/LegacyMaterials.java
Normal file
2110
src/main/java/com/songoda/core/compatibility/LegacyMaterials.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,254 @@
|
||||
package com.songoda.core.compatibility;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Particle effects for servers 1.8 and below
|
||||
* @since 2019-08-23
|
||||
* @author jascotty2
|
||||
*/
|
||||
public class LegacyParticleEffects {
|
||||
|
||||
public static enum Type {
|
||||
|
||||
EXPLOSION_NORMAL("explode"),
|
||||
EXPLOSION_LARGE("largeexplode"),
|
||||
EXPLOSION_HUGE("hugeexplosion"),
|
||||
FIREWORKS_SPARK("fireworksSpark"),
|
||||
WATER_BUBBLE("bubble"),
|
||||
WATER_SPLASH("splash"),
|
||||
WATER_WAKE("wake", ServerVersion.V1_8),
|
||||
SUSPENDED("suspended"),
|
||||
SUSPENDED_DEPTH("depthsuspend"),
|
||||
CRIT("crit"),
|
||||
CRIT_MAGIC("magicCrit"),
|
||||
SMOKE_NORMAL("smoke"),
|
||||
SMOKE_LARGE("largesmoke"),
|
||||
SPELL("spell"),
|
||||
SPELL_INSTANT("instantSpell"),
|
||||
SPELL_MOB("mobSpell"),
|
||||
SPELL_MOB_AMBIENT("mobSpellAmbient"),
|
||||
SPELL_WITCH("witchMagic"),
|
||||
DRIP_WATER("dripWater"),
|
||||
DRIP_LAVA("dripLava"),
|
||||
VILLAGER_ANGRY("angryVillager"),
|
||||
VILLAGER_HAPPY("happyVillager"),
|
||||
TOWN_AURA("townaura"),
|
||||
NOTE("note"),
|
||||
PORTAL("portal"),
|
||||
ENCHANTMENT_TABLE("enchantmenttable"),
|
||||
FLAME("flame"),
|
||||
LAVA("lava"),
|
||||
FOOTSTEP("footstep"),
|
||||
CLOUD("cloud"),
|
||||
REDSTONE("reddust"),
|
||||
SNOWBALL("snowballpoof"),
|
||||
SNOW_SHOVEL("snowshovel"),
|
||||
SLIME("slime"),
|
||||
HEART("heart"),
|
||||
BARRIER("barrier", ServerVersion.V1_8),
|
||||
/**
|
||||
* Used when a block is broken
|
||||
*/
|
||||
ITEM_CRACK("iconcrack_"),
|
||||
BLOCK_CRACK("blockcrack_", ServerVersion.V1_8),
|
||||
BLOCK_DUST("blockdust_", ServerVersion.V1_8),
|
||||
WATER_DROP("droplet", ServerVersion.V1_8),
|
||||
ITEM_TAKE("take", ServerVersion.V1_8),
|
||||
MOB_APPEARANCE("mobappearance", ServerVersion.V1_8),
|
||||
TOOL_BREAK("tilecrack_", ServerVersion.UNKNOWN, ServerVersion.V1_7);
|
||||
|
||||
public final String name;
|
||||
public final ServerVersion minVersion;
|
||||
public final ServerVersion maxVersion;
|
||||
|
||||
private Type(String name) {
|
||||
this.name = name;
|
||||
this.minVersion = ServerVersion.UNKNOWN;
|
||||
this.maxVersion = null;
|
||||
}
|
||||
|
||||
private Type(String name, ServerVersion minVersion) {
|
||||
this.name = name;
|
||||
this.minVersion = minVersion;
|
||||
this.maxVersion = null;
|
||||
}
|
||||
|
||||
private Type(String name, ServerVersion minVersion, ServerVersion maxVersion) {
|
||||
this.name = name;
|
||||
this.minVersion = minVersion;
|
||||
this.maxVersion = maxVersion;
|
||||
}
|
||||
|
||||
public static Type getById(String id) {
|
||||
for (Type t : Type.values()) {
|
||||
if (t.name.equalsIgnoreCase(id) || t.name().equalsIgnoreCase(id)) {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static final String version = getNMSVersion();
|
||||
private static boolean enabled = true;
|
||||
private static Class mc_packetPlayOutWorldParticlesClazz;
|
||||
private static Class cb_craftPlayerClazz;
|
||||
private static Method cb_craftPlayer_getHandle;
|
||||
private static Class mc_entityPlayerClazz;
|
||||
private static Class mc_playerConnectionClazz;
|
||||
private static Field mc_entityPlayer_playerConnection;
|
||||
private static Class mc_PacketInterface;
|
||||
private static Method mc_playerConnection_sendPacket;
|
||||
private static Class mc_EnumParticle;
|
||||
private static Method mc_EnumParticle_valueOf;
|
||||
|
||||
static {
|
||||
try {
|
||||
// lower versions use "Packet63WorldParticles"
|
||||
if (!version.startsWith("v1_7") && !version.startsWith("v1_8")) {
|
||||
mc_packetPlayOutWorldParticlesClazz = Class.forName("net.minecraft.server." + version + ".Packet63WorldParticles");
|
||||
} else {
|
||||
mc_packetPlayOutWorldParticlesClazz = Class.forName("net.minecraft.server." + version + ".PacketPlayOutWorldParticles");
|
||||
}
|
||||
cb_craftPlayerClazz = Class.forName("org.bukkit.craftbukkit." + version + ".entity.CraftPlayer");
|
||||
cb_craftPlayer_getHandle = cb_craftPlayerClazz.getDeclaredMethod("getHandle");
|
||||
mc_entityPlayerClazz = Class.forName("net.minecraft.server." + version + ".EntityPlayer");
|
||||
mc_entityPlayer_playerConnection = mc_entityPlayerClazz.getDeclaredField("playerConnection");
|
||||
mc_playerConnectionClazz = Class.forName("net.minecraft.server." + version + ".PlayerConnection");
|
||||
mc_PacketInterface = Class.forName("net.minecraft.server." + version + ".Packet");
|
||||
mc_playerConnection_sendPacket = mc_playerConnectionClazz.getDeclaredMethod("sendPacket", mc_PacketInterface);
|
||||
if (version.startsWith("v1_8")) {
|
||||
// Aren't worrying about anything after 1.8 in this class here
|
||||
mc_EnumParticle = Class.forName("net.minecraft.server." + version + ".EnumParticle");
|
||||
mc_EnumParticle_valueOf = mc_EnumParticle.getDeclaredMethod("valueOf", String.class);
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
Logger.getAnonymousLogger().log(Level.WARNING, "Problem preparing particle packets (disabling further packets)", ex);
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static String getNMSVersion() {
|
||||
String ver = Bukkit.getServer().getClass().getPackage().getName();
|
||||
return ver.substring(ver.lastIndexOf('.') + 1);
|
||||
}
|
||||
|
||||
public static void createParticle(Location l, Type e) {
|
||||
createParticle(l, e, 0F, 0F, 0F, 1, 3, null);
|
||||
}
|
||||
|
||||
public static void createParticle(Location l, Type e, List<Player> localOnly) {
|
||||
createParticle(l, e, 0F, 0F, 0F, 1, 3, localOnly);
|
||||
}
|
||||
|
||||
public static void createParticle(Location l, Type e, float effectSpeed, int amountOfParticles) {
|
||||
createParticle(l, e, 0F, 0F, 0F, effectSpeed, amountOfParticles, null);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param l exact location to spawn the particle
|
||||
* @param e particle effect type
|
||||
* @param xx for notes, this is a value 0-1 for the color ([0-24]/24), for
|
||||
* redstone this is the red value 0-1 ([0-255]/255).
|
||||
* Otherwise this is the distance for particles to fly on the x-axis.
|
||||
* @param yy for redstone this is the green value 0-1 ([0-255]/255)
|
||||
* Otherwise this is the distance for particles to fly on the y-axis.
|
||||
* @param zz for redstone this is the blue value 0-1 ([0-255]/255)
|
||||
* Otherwise this is the distance for particles to fly on the z-axis.
|
||||
* @param effectSpeed particle effect speed
|
||||
* @param amountOfParticles extra particles to spawn (client-side)
|
||||
* @param localOnly list of players to send the packets to, or null for all players
|
||||
*/
|
||||
public static void createParticle(Location l, Type e, float xx, float yy, float zz, float effectSpeed, int amountOfParticles, List<Player> localOnly) {
|
||||
if (!enabled || e == null || effectSpeed < 0 || amountOfParticles < 0
|
||||
|| !ServerVersion.isServerVersionAtLeast(e.minVersion)
|
||||
|| (e.maxVersion != null && !ServerVersion.isServerVersionBelow(e.maxVersion))) {
|
||||
return;
|
||||
}
|
||||
final int rangeSquared = 256; // apparently there is no way to override this (unless to make smaller, of course)
|
||||
// collect a list of players to send this packet to
|
||||
List<Player> sendTo = new ArrayList();
|
||||
if (localOnly == null) {
|
||||
for (Player p : l.getWorld().getPlayers()) {
|
||||
if (p.getLocation().distanceSquared(l) <= rangeSquared) {
|
||||
sendTo.add(p);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
final World w = l.getWorld();
|
||||
for (Player p : localOnly) {
|
||||
if (p.getWorld() == w && p.getLocation().distanceSquared(l) <= rangeSquared) {
|
||||
sendTo.add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sendTo.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// Make an instance of the packet!
|
||||
Object sPacket = mc_packetPlayOutWorldParticlesClazz.newInstance();
|
||||
for (Field field : sPacket.getClass().getDeclaredFields()) {
|
||||
// Set those fields we need to be accessible!
|
||||
field.setAccessible(true);
|
||||
final String fieldName = field.getName();
|
||||
// Set them to what we want!
|
||||
if (fieldName.equals("a")) {
|
||||
// we're just going to assume it's either 1.7 or 1.8
|
||||
if (version.startsWith("v1_8")) {
|
||||
// 1.8 uses an Enum
|
||||
field.set(sPacket, mc_EnumParticle_valueOf.invoke(null, e.name()));
|
||||
} else {
|
||||
field.set(sPacket, e.name);
|
||||
}
|
||||
} else if (fieldName.equals("b")) {
|
||||
field.setFloat(sPacket, (float) l.getX()); // x
|
||||
} else if (fieldName.equals("c")) {
|
||||
field.setFloat(sPacket, (float) l.getY()); // y
|
||||
} else if (fieldName.equals("d")) {
|
||||
field.setFloat(sPacket, (float) l.getZ()); // z
|
||||
} else if (fieldName.equals("e")) {
|
||||
field.setFloat(sPacket, xx); // xOffset
|
||||
} else if (fieldName.equals("f")) {
|
||||
field.setFloat(sPacket, yy); // yOffset
|
||||
} else if (fieldName.equals("g")) {
|
||||
field.setFloat(sPacket, zz); // zOffset
|
||||
} else if (fieldName.equals("h")) {
|
||||
field.setFloat(sPacket, effectSpeed);
|
||||
} else if (fieldName.equals("i")) {
|
||||
field.setInt(sPacket, amountOfParticles);
|
||||
}
|
||||
/*
|
||||
1.8 also includes other data:
|
||||
j = boolean, set if view distance is increased from 256 to 65536
|
||||
k = int[] for packet data (like block type for ITEM_CRACK)
|
||||
*/
|
||||
}
|
||||
// send it on its way!
|
||||
for (Player p : sendTo) {
|
||||
sendPacket(sPacket, p);
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
Logger.getAnonymousLogger().log(Level.WARNING, "Problem preparing a particle packet (disabling further packets)", ex);
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void sendPacket(Object packet, Player to) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
|
||||
Object cbPlayer = cb_craftPlayer_getHandle.invoke(to);
|
||||
Object mcConnection = mc_entityPlayer_playerConnection.get(cbPlayer);
|
||||
mc_playerConnection_sendPacket.invoke(mcConnection, packet);
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.songoda.core.compatibility;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Random;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
|
||||
public class LegacyPotionEffects {
|
||||
|
||||
private LegacyPotionEffects() {
|
||||
}
|
||||
|
||||
protected final static Random rand = new Random();
|
||||
|
||||
private final static HashMap<Integer, String> potionEffectNames = new HashMap() {
|
||||
{
|
||||
put(PotionEffectType.SPEED.getId(), "Speed");
|
||||
put(PotionEffectType.SLOW.getId(), "Slowness");
|
||||
put(PotionEffectType.FAST_DIGGING.getId(), "Haste");
|
||||
put(PotionEffectType.SLOW_DIGGING.getId(), "Mining Fatigue");
|
||||
put(PotionEffectType.INCREASE_DAMAGE.getId(), "Strength");
|
||||
put(PotionEffectType.WEAKNESS.getId(), "Weakness");
|
||||
put(PotionEffectType.HEAL.getId(), "Instant Health");
|
||||
put(PotionEffectType.HARM.getId(), "Instant Damage");
|
||||
put(PotionEffectType.JUMP.getId(), "Jump Boost");
|
||||
put(PotionEffectType.CONFUSION.getId(), "Nausea");
|
||||
put(PotionEffectType.REGENERATION.getId(), "Regeneration");
|
||||
put(PotionEffectType.DAMAGE_RESISTANCE.getId(), "Resistance");
|
||||
put(PotionEffectType.FIRE_RESISTANCE.getId(), "Fire Resistance");
|
||||
put(PotionEffectType.WATER_BREATHING.getId(), "Water Breathing");
|
||||
put(PotionEffectType.INVISIBILITY.getId(), "Invisibility");
|
||||
put(PotionEffectType.BLINDNESS.getId(), "Blindness");
|
||||
put(PotionEffectType.NIGHT_VISION.getId(), "Night Vision");
|
||||
put(PotionEffectType.HUNGER.getId(), "Hunger");
|
||||
put(PotionEffectType.POISON.getId(), "Poison");
|
||||
put(PotionEffectType.WITHER.getId(), "Wither");
|
||||
put(PotionEffectType.HEALTH_BOOST.getId(), "Health Boost");
|
||||
put(PotionEffectType.ABSORPTION.getId(), "Absorption");
|
||||
put(PotionEffectType.SATURATION.getId(), "Saturation");
|
||||
}
|
||||
};
|
||||
|
||||
public static String getEffectName(PotionEffectType e) {
|
||||
if (e == null) {
|
||||
return "null";
|
||||
}
|
||||
final String n = potionEffectNames.get(e.getId());
|
||||
return n == null ? e.getName() : n;
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package com.songoda.core.compatibility;
|
||||
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.Effect;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Particle;
|
||||
import org.bukkit.block.BlockFace;
|
||||
|
||||
public class ParticleHandler {
|
||||
|
||||
public static void redstoneParticles(Location location, int red, int green, int blue) {
|
||||
redstoneParticles(location, red, green, blue, 1F, 1, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawn colored redstone particles
|
||||
*
|
||||
* @param location area to spawn the particle in
|
||||
* @param red red value 0-255
|
||||
* @param green green value 0-255
|
||||
* @param blue blue value 0-255
|
||||
* @param size (1.13+) size of the particles
|
||||
* @param count how many particles to spawn
|
||||
* @param radius how far to spread out the particles from location
|
||||
*/
|
||||
public static void redstoneParticles(Location location, int red, int green, int blue, float size, int count, float radius) {
|
||||
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13)) {
|
||||
float xx = (float) (0 + (Math.random() * 1));
|
||||
float yy = (float) (0 + (Math.random() * 1));
|
||||
float zz = (float) (0 + (Math.random() * 1));
|
||||
location.getWorld().spawnParticle(Particle.REDSTONE, location, count, xx, yy, zz, 1, new Particle.DustOptions(Color.fromBGR(blue, green, red), size));
|
||||
} else if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_9)) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
float xx = (float) (radius * (Math.random() - Math.random()));
|
||||
float yy = (float) (radius * (Math.random() - Math.random()));
|
||||
float zz = (float) (radius * (Math.random() - Math.random()));
|
||||
Location at = location.clone().add(xx, yy, zz);
|
||||
location.getWorld().spawnParticle(Particle.REDSTONE, at, 0, red / 255F, green / 255F, blue / 255F, size); // particle, location, count, red, green, blue, extra data
|
||||
}
|
||||
} else {
|
||||
// WE NEED MAGIC!
|
||||
for (int i = 0; i < count; i++) {
|
||||
float xx = (float) (radius * (Math.random() - Math.random()));
|
||||
float yy = (float) (radius * (Math.random() - Math.random()));
|
||||
float zz = (float) (radius * (Math.random() - Math.random()));
|
||||
Location at = location.clone().add(xx, yy, zz);
|
||||
LegacyParticleEffects.createParticle(at, LegacyParticleEffects.Type.REDSTONE,
|
||||
red / 255F, green / 255F, blue / 255F, 1F, 0, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void bonemealSmoke(Location l) {
|
||||
final org.bukkit.World w = l.getWorld();
|
||||
w.playEffect(l, Effect.SMOKE, BlockFace.SOUTH_EAST);
|
||||
w.playEffect(l, Effect.SMOKE, BlockFace.SOUTH);
|
||||
w.playEffect(l, Effect.SMOKE, BlockFace.SOUTH_WEST);
|
||||
w.playEffect(l, Effect.SMOKE, BlockFace.EAST);
|
||||
w.playEffect(l, Effect.SMOKE, BlockFace.SELF);
|
||||
w.playEffect(l, Effect.SMOKE, BlockFace.WEST);
|
||||
w.playEffect(l, Effect.SMOKE, BlockFace.NORTH_EAST);
|
||||
w.playEffect(l, Effect.SMOKE, BlockFace.NORTH);
|
||||
w.playEffect(l, Effect.SMOKE, BlockFace.NORTH_WEST);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package com.songoda.core.compatibility;
|
||||
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
public enum ServerProject {
|
||||
|
||||
UNKNOWN, CRAFTBUKKIT, SPIGOT, PAPER, GLOWSTONE;
|
||||
private static ServerProject serverProject = checkProject();
|
||||
|
||||
private static ServerProject checkProject() {
|
||||
String serverPath = Bukkit.getServer().getClass().getName();
|
||||
if (serverPath.contains("glowstone")) {
|
||||
return GLOWSTONE;
|
||||
}
|
||||
// paper used to be called "paperclip"
|
||||
try {
|
||||
Class.forName("com.destroystokyo.paperclip.Paperclip");
|
||||
return PAPER;
|
||||
} catch (ClassNotFoundException ex) {
|
||||
}
|
||||
try {
|
||||
Class.forName("com.destroystokyo.paper.PaperConfig");
|
||||
return PAPER;
|
||||
} catch (ClassNotFoundException ex) {
|
||||
}
|
||||
try {
|
||||
Class.forName("org.spigotmc.SpigotConfig");
|
||||
return SPIGOT;
|
||||
} catch (ClassNotFoundException ex) {
|
||||
}
|
||||
return serverPath.contains("craftbukkit") ? CRAFTBUKKIT : UNKNOWN;
|
||||
}
|
||||
|
||||
public static ServerProject getServerVersion() {
|
||||
return serverProject;
|
||||
}
|
||||
|
||||
public static boolean isServer(ServerProject version) {
|
||||
return serverProject == version;
|
||||
}
|
||||
|
||||
public static boolean isServer(ServerProject... versions) {
|
||||
return ArrayUtils.contains(versions, serverProject);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package com.songoda.core.compatibility;
|
||||
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
public enum ServerVersion {
|
||||
|
||||
UNKNOWN, V1_7, V1_8, V1_9, V1_10, V1_11, V1_12, V1_13, V1_14, V1_15, V1_16, V1_17, V1_18, V1_19, V1_20;
|
||||
|
||||
private final static String serverPackagePath = Bukkit.getServer().getClass().getPackage().getName();
|
||||
private final static String serverPackageVersion = serverPackagePath.substring(serverPackagePath.lastIndexOf('.') + 1);
|
||||
private static ServerVersion serverVersion = UNKNOWN;
|
||||
private static String serverReleaseVersion;
|
||||
|
||||
static {
|
||||
for (ServerVersion version : values()) {
|
||||
if (serverPackageVersion.toUpperCase().startsWith(version.name())) {
|
||||
serverVersion = version;
|
||||
serverReleaseVersion = serverPackageVersion.substring(version.name().length() + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isLessThan(ServerVersion other) {
|
||||
return this.ordinal() < other.ordinal();
|
||||
}
|
||||
|
||||
public boolean isGreaterThan(ServerVersion other) {
|
||||
return this.ordinal() > other.ordinal();
|
||||
}
|
||||
|
||||
public static String getServerVersionString() {
|
||||
return serverPackageVersion;
|
||||
}
|
||||
|
||||
public static String getVersionReleaseNumber() {
|
||||
return serverReleaseVersion;
|
||||
}
|
||||
|
||||
public static ServerVersion getServerVersion() {
|
||||
return serverVersion;
|
||||
}
|
||||
|
||||
public static boolean isServerVersion(ServerVersion version) {
|
||||
return serverVersion == version;
|
||||
}
|
||||
|
||||
public static boolean isServerVersion(ServerVersion... versions) {
|
||||
return ArrayUtils.contains(versions, serverVersion);
|
||||
}
|
||||
|
||||
public static boolean isServerVersionAtLeast(ServerVersion version) {
|
||||
return serverVersion.ordinal() >= version.ordinal();
|
||||
}
|
||||
|
||||
public static boolean isServerVersionBelow(ServerVersion version) {
|
||||
return serverVersion.ordinal() < version.ordinal();
|
||||
}
|
||||
}
|
102
src/main/java/com/songoda/core/configuration/Comment.java
Normal file
102
src/main/java/com/songoda/core/configuration/Comment.java
Normal file
@ -0,0 +1,102 @@
|
||||
package com.songoda.core.configuration;
|
||||
|
||||
import com.songoda.core.configuration.ConfigFormattingRules.CommentStyle;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* A comment for a configuration key
|
||||
*
|
||||
* @since 2019-08-28
|
||||
* @author jascotty2
|
||||
*/
|
||||
public class Comment {
|
||||
|
||||
final List<String> lines = new ArrayList();
|
||||
CommentStyle commentStyle = null;
|
||||
|
||||
public Comment() {
|
||||
}
|
||||
|
||||
public Comment(String... lines) {
|
||||
this.lines.addAll(Arrays.asList(lines));
|
||||
}
|
||||
|
||||
public Comment(List<String> lines) {
|
||||
if (lines != null) {
|
||||
this.lines.addAll(lines);
|
||||
}
|
||||
}
|
||||
|
||||
public Comment(CommentStyle commentStyle, String... lines) {
|
||||
this.commentStyle = commentStyle;
|
||||
this.lines.addAll(Arrays.asList(lines));
|
||||
}
|
||||
|
||||
public Comment(CommentStyle commentStyle, List<String> lines) {
|
||||
this.commentStyle = commentStyle;
|
||||
if (lines != null) {
|
||||
this.lines.addAll(lines);
|
||||
}
|
||||
}
|
||||
|
||||
public CommentStyle getCommentStyle() {
|
||||
return commentStyle;
|
||||
}
|
||||
|
||||
public void setCommentStyle(CommentStyle commentStyle) {
|
||||
this.commentStyle = commentStyle;
|
||||
}
|
||||
|
||||
public List<String> getLines() {
|
||||
return lines;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return lines.isEmpty() ? "" : lines.stream().collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
public void writeComment(Writer output, int offset, CommentStyle defaultStyle) throws IOException {
|
||||
CommentStyle style = commentStyle != null ? commentStyle : defaultStyle;
|
||||
int minSpacing = 0, borderSpacing = 0;
|
||||
// first draw the top of the comment
|
||||
if (style.drawBorder) {
|
||||
// grab the longest line in the list of lines
|
||||
minSpacing = lines.stream().max((s1, s2) -> s1.length() - s2.length()).get().length();
|
||||
borderSpacing = minSpacing + style.commentPrefix.length() + style.commentSuffix.length();
|
||||
// draw the first line
|
||||
output.write((new String(new char[offset])).replace('\0', ' ') + (new String(new char[borderSpacing + 2])).replace('\0', '#') + "\n");
|
||||
if (style.drawSpace) {
|
||||
output.write((new String(new char[offset])).replace('\0', ' ')
|
||||
+ "#" + style.spacePrefixTop
|
||||
+ (new String(new char[borderSpacing - style.spacePrefixTop.length() - style.spaceSuffixTop.length()])).replace('\0', style.spaceCharTop)
|
||||
+ style.spaceSuffixTop + "#\n");
|
||||
}
|
||||
} else if (style.drawSpace) {
|
||||
output.write((new String(new char[offset])).replace('\0', ' ') + "#\n");
|
||||
}
|
||||
// then the actual comment lines
|
||||
for (String line : lines) {
|
||||
// todo? should we auto-wrap comment lines that are longer than 80 characters?
|
||||
output.write((new String(new char[offset])).replace('\0', ' ') + "#" + style.commentPrefix
|
||||
+ (minSpacing == 0 ? line : line + (new String(new char[minSpacing - line.length()])).replace('\0', ' ')) + style.commentSuffix + (style.drawBorder ? "#\n" : "\n"));
|
||||
}
|
||||
// now draw the bottom of the comment border
|
||||
if (style.drawBorder) {
|
||||
if (style.drawSpace) {
|
||||
output.write((new String(new char[offset])).replace('\0', ' ')
|
||||
+ "#" + style.spacePrefixBottom
|
||||
+ (new String(new char[borderSpacing - style.spacePrefixBottom.length() - style.spaceSuffixBottom.length()])).replace('\0', style.spaceCharBottom)
|
||||
+ style.spaceSuffixBottom + "#\n");
|
||||
}
|
||||
output.write((new String(new char[offset])).replace('\0', ' ') + (new String(new char[borderSpacing + 2])).replace('\0', '#') + "\n");
|
||||
} else if (style.drawSpace) {
|
||||
output.write((new String(new char[offset])).replace('\0', ' ') + "#\n");
|
||||
}
|
||||
}
|
||||
}
|
587
src/main/java/com/songoda/core/configuration/Config.java
Normal file
587
src/main/java/com/songoda/core/configuration/Config.java
Normal file
@ -0,0 +1,587 @@
|
||||
package com.songoda.core.configuration;
|
||||
|
||||
import com.songoda.core.utils.TextUtils;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.InvalidConfigurationException;
|
||||
import org.bukkit.configuration.file.YamlConstructor;
|
||||
import org.bukkit.configuration.file.YamlRepresenter;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.error.YAMLException;
|
||||
import org.yaml.snakeyaml.representer.Representer;
|
||||
|
||||
/**
|
||||
* Configuration settings for a plugin
|
||||
*
|
||||
* @since 2019-08-28
|
||||
* @author jascotty2
|
||||
*/
|
||||
public class Config extends ConfigSection {
|
||||
|
||||
/*
|
||||
Serialization notes:
|
||||
// implements ConfigurationSerializable:
|
||||
//public Map<String, Object> serialize();
|
||||
|
||||
// Class must contain one of:
|
||||
// public static Object deserialize(@NotNull Map<String, ?> args);
|
||||
// public static valueOf(Map<String, ?> args);
|
||||
// public new (Map<String, ?> args)
|
||||
*/
|
||||
protected static final String COMMENT_PREFIX = "# ";
|
||||
protected static final String BLANK_CONFIG = "{}\n";
|
||||
|
||||
protected File file;
|
||||
protected final ConfigFileConfigurationAdapter config = new ConfigFileConfigurationAdapter(this);
|
||||
final String dirName, fileName;
|
||||
final Plugin plugin;
|
||||
final DumperOptions yamlOptions = new DumperOptions();
|
||||
final Representer yamlRepresenter = new YamlRepresenter();
|
||||
final Yaml yaml = new Yaml(new YamlConstructor(), yamlRepresenter, yamlOptions);
|
||||
SaveTask saveTask;
|
||||
Timer autosaveTimer;
|
||||
////////////// Config settings ////////////////
|
||||
/**
|
||||
* save file whenever a change is made
|
||||
*/
|
||||
boolean autosave = false;
|
||||
/**
|
||||
* time in seconds to start a save after a change is made
|
||||
*/
|
||||
int autosaveInterval = 60;
|
||||
/**
|
||||
* remove nodes not defined in defaults
|
||||
*/
|
||||
boolean autoremove = false;
|
||||
/**
|
||||
* load comments when loading the file
|
||||
* TODO
|
||||
*/
|
||||
boolean loadComments = false;
|
||||
/**
|
||||
* Default comment applied to config nodes
|
||||
*/
|
||||
ConfigFormattingRules.CommentStyle defaultNodeCommentFormat = ConfigFormattingRules.CommentStyle.SIMPLE;
|
||||
/**
|
||||
* Default comment applied to section nodes
|
||||
*/
|
||||
ConfigFormattingRules.CommentStyle defaultSectionCommentFormat = ConfigFormattingRules.CommentStyle.SPACED;
|
||||
/**
|
||||
* Extra lines to put between root nodes
|
||||
*/
|
||||
int rootNodeSpacing = 1;
|
||||
/**
|
||||
* Extra lines to put in front of comments. <br>
|
||||
* This is separate from rootNodeSpacing, if applicable.
|
||||
*/
|
||||
int commentSpacing = 1;
|
||||
|
||||
public Config(@NotNull File file) {
|
||||
this.plugin = null;
|
||||
this.file = file.getAbsoluteFile();
|
||||
dirName = null;
|
||||
fileName = null;
|
||||
}
|
||||
|
||||
public Config(@NotNull Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
dirName = null;
|
||||
fileName = null;
|
||||
}
|
||||
|
||||
public Config(@NotNull Plugin plugin, @NotNull String file) {
|
||||
this.plugin = plugin;
|
||||
dirName = null;
|
||||
fileName = file;
|
||||
}
|
||||
|
||||
public Config(@NotNull Plugin plugin, @NotNull String directory, @NotNull String file) {
|
||||
this.plugin = plugin;
|
||||
dirName = directory;
|
||||
fileName = file;
|
||||
}
|
||||
|
||||
public ConfigFileConfigurationAdapter getFileConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
if (file == null) {
|
||||
if (dirName != null) {
|
||||
this.file = new File(plugin.getDataFolder() + dirName, fileName != null ? fileName : "config.yml");
|
||||
} else {
|
||||
this.file = new File(plugin.getDataFolder(), fileName != null ? fileName : "config.yml");
|
||||
}
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
public boolean getAutosave() {
|
||||
return autosave;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the configuration automatically save whenever it's been changed? <br>
|
||||
* All saves are done asynchronously, so this should not impact server performance.
|
||||
*
|
||||
* @param autosave set to true if autosaving is enabled.
|
||||
* @return this class
|
||||
*/
|
||||
@NotNull
|
||||
public Config setAutosave(boolean autosave) {
|
||||
this.autosave = autosave;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getAutosaveInterval() {
|
||||
return autosaveInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* If autosave is enabled, this is the delay between a change and when the save is started. <br>
|
||||
* If the configuration is changed within this period, the timer is not reset.
|
||||
*
|
||||
* @param autosaveInterval time in seconds
|
||||
* @return this class
|
||||
*/
|
||||
public Config setAutosaveInterval(int autosaveInterval) {
|
||||
this.autosaveInterval = autosaveInterval;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean getAutoremove() {
|
||||
return autoremove;
|
||||
}
|
||||
|
||||
/**
|
||||
* This setting is used to prevent users to from adding extraneous settings
|
||||
* to the config and to remove deprecated settings. <br>
|
||||
* If this is enabled, the config will delete any nodes that are not defined
|
||||
* as a default setting.
|
||||
*
|
||||
* @param autoremove Remove settings that don't exist as defaults
|
||||
* @return this class
|
||||
*/
|
||||
@NotNull
|
||||
public Config setAutoremove(boolean autoremove) {
|
||||
this.autoremove = autoremove;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default comment applied to config nodes
|
||||
*/
|
||||
public ConfigFormattingRules.CommentStyle getDefaultNodeCommentFormat() {
|
||||
return defaultNodeCommentFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default comment applied to config nodes
|
||||
*
|
||||
* @return this config
|
||||
*/
|
||||
public Config setDefaultNodeCommentFormat(ConfigFormattingRules.CommentStyle defaultNodeCommentFormat) {
|
||||
this.defaultNodeCommentFormat = defaultNodeCommentFormat;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default comment applied to section nodes
|
||||
*/
|
||||
public ConfigFormattingRules.CommentStyle getDefaultSectionCommentFormat() {
|
||||
return defaultSectionCommentFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default comment applied to section nodes
|
||||
*
|
||||
* @return this config
|
||||
*/
|
||||
public Config setDefaultSectionCommentFormat(ConfigFormattingRules.CommentStyle defaultSectionCommentFormat) {
|
||||
this.defaultSectionCommentFormat = defaultSectionCommentFormat;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra lines to put between root nodes
|
||||
*/
|
||||
public int getRootNodeSpacing() {
|
||||
return rootNodeSpacing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra lines to put between root nodes
|
||||
*
|
||||
* @return this config
|
||||
*/
|
||||
public Config setRootNodeSpacing(int rootNodeSpacing) {
|
||||
this.rootNodeSpacing = rootNodeSpacing;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra lines to put in front of comments. <br>
|
||||
* This is separate from rootNodeSpacing, if applicable.
|
||||
*/
|
||||
public int getCommentSpacing() {
|
||||
return commentSpacing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extra lines to put in front of comments. <br>
|
||||
* This is separate from rootNodeSpacing, if applicable.
|
||||
*
|
||||
* @return this config
|
||||
*/
|
||||
public Config setCommentSpacing(int commentSpacing) {
|
||||
this.commentSpacing = commentSpacing;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Config setHeader(@NotNull String... description) {
|
||||
if (description.length == 0) {
|
||||
configComments.remove(null);
|
||||
} else {
|
||||
configComments.put(null, new Comment(description));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Config setHeader(@Nullable ConfigFormattingRules.CommentStyle commentStyle, @NotNull String... description) {
|
||||
if (description.length == 0) {
|
||||
configComments.remove(null);
|
||||
} else {
|
||||
configComments.put(null, new Comment(commentStyle, description));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Config setHeader(@Nullable List<String> description) {
|
||||
if (description == null || description.isEmpty()) {
|
||||
configComments.remove(null);
|
||||
} else {
|
||||
configComments.put(null, new Comment(description));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Config setHeader(@Nullable ConfigFormattingRules.CommentStyle commentStyle, @Nullable List<String> description) {
|
||||
if (description == null || description.isEmpty()) {
|
||||
configComments.remove(null);
|
||||
} else {
|
||||
configComments.put(null, new Comment(commentStyle, description));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<String> getHeader() {
|
||||
if (configComments.containsKey(null)) {
|
||||
return configComments.get(null).getLines();
|
||||
} else {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean load() {
|
||||
return load(getFile());
|
||||
}
|
||||
|
||||
public boolean load(@NotNull File file) {
|
||||
Validate.notNull(file, "File cannot be null");
|
||||
if (file.exists()) {
|
||||
try (BufferedInputStream stream = new BufferedInputStream(new FileInputStream(file))) {
|
||||
Charset charset = TextUtils.detectCharset(stream, StandardCharsets.UTF_8);
|
||||
this.load(new InputStreamReader(stream, charset));
|
||||
return true;
|
||||
} catch (IOException | InvalidConfigurationException ex) {
|
||||
(plugin != null ? plugin.getLogger() : Bukkit.getLogger()).log(Level.SEVERE, "Failed to load config file: " + file.getName(), ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void load(@NotNull Reader reader) throws IOException, InvalidConfigurationException {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
try (BufferedReader input = reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader)) {
|
||||
String line;
|
||||
boolean firstLine = true;
|
||||
while ((line = input.readLine()) != null) {
|
||||
if(firstLine) {
|
||||
line = line.replaceAll("[\uFEFF\uFFFE\u200B]", ""); // clear BOM markers
|
||||
firstLine = false;
|
||||
}
|
||||
builder.append(line).append('\n');
|
||||
}
|
||||
}
|
||||
this.loadFromString(builder.toString());
|
||||
}
|
||||
|
||||
public void loadFromString(@NotNull String contents) throws InvalidConfigurationException {
|
||||
Map input;
|
||||
try {
|
||||
input = (Map) this.yaml.load(contents);
|
||||
} catch (YAMLException e2) {
|
||||
throw new InvalidConfigurationException(e2);
|
||||
} catch (ClassCastException e3) {
|
||||
throw new InvalidConfigurationException("Top level is not a Map.");
|
||||
}
|
||||
if (input != null) {
|
||||
if(loadComments) {
|
||||
this.parseComments(contents, input);
|
||||
}
|
||||
this.convertMapsToSections(input, this);
|
||||
}
|
||||
}
|
||||
|
||||
protected void convertMapsToSections(@NotNull Map<?, ?> input, @NotNull ConfigSection section) {
|
||||
for (Map.Entry<?, ?> entry : input.entrySet()) {
|
||||
String key = entry.getKey().toString();
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof Map) {
|
||||
this.convertMapsToSections((Map) value, section.createSection(key));
|
||||
continue;
|
||||
}
|
||||
section.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
protected void parseComments(@NotNull String contents, @NotNull Map<?, ?> input) {
|
||||
// TODO?
|
||||
// if starts with a comment, load all nonbreaking comments as a header
|
||||
// then load all comments and assign to the next valid node loaded
|
||||
// (Only load comments that are on their own line)
|
||||
}
|
||||
|
||||
public void deleteNonDefaultSettings() {
|
||||
// Delete old config values (thread-safe)
|
||||
List<String> defaultKeys = Arrays.asList(defaults.keySet().toArray(new String[0]));
|
||||
for(String key : values.keySet().toArray(new String[0])) {
|
||||
if(!defaultKeys.contains(key)) {
|
||||
values.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onChange() {
|
||||
if (autosave) {
|
||||
delaySave();
|
||||
}
|
||||
}
|
||||
|
||||
public void delaySave() {
|
||||
// save async even if no plugin or if plugin disabled
|
||||
if (changed && saveTask == null) {
|
||||
autosaveTimer = new Timer((plugin != null ? plugin.getName() + "-ConfigSave-" : "ConfigSave-") + getFile().getName());
|
||||
autosaveTimer.schedule(saveTask = new SaveTask(), autosaveInterval * 1000L);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean saveChanges() {
|
||||
boolean saved = true;
|
||||
if (changed) {
|
||||
saved = save();
|
||||
}
|
||||
if(saveTask != null) {
|
||||
//Close Threads
|
||||
saveTask.cancel();
|
||||
autosaveTimer.cancel();
|
||||
saveTask = null;
|
||||
autosaveTimer = null;
|
||||
}
|
||||
return saved;
|
||||
}
|
||||
|
||||
public boolean save() {
|
||||
if(saveTask != null) {
|
||||
//Close Threads
|
||||
saveTask.cancel();
|
||||
autosaveTimer.cancel();
|
||||
saveTask = null;
|
||||
autosaveTimer = null;
|
||||
}
|
||||
return save(getFile());
|
||||
}
|
||||
|
||||
public boolean save(@NotNull String file) {
|
||||
Validate.notNull(file, "File cannot be null");
|
||||
return this.save(new File(file));
|
||||
}
|
||||
|
||||
public boolean save(@NotNull File file) {
|
||||
Validate.notNull(file, "File cannot be null");
|
||||
if (!file.getParentFile().exists()) {
|
||||
file.getParentFile().mkdirs();
|
||||
}
|
||||
String data = this.saveToString();
|
||||
try (OutputStreamWriter writer = new OutputStreamWriter((OutputStream) new FileOutputStream(file), StandardCharsets.UTF_16);) {
|
||||
writer.write(data);
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String saveToString() {
|
||||
try {
|
||||
if(autoremove) {
|
||||
deleteNonDefaultSettings();
|
||||
}
|
||||
yamlOptions.setIndent(indentation);
|
||||
yamlOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
yamlRepresenter.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
StringWriter str = new StringWriter();
|
||||
Comment header = configComments.get(null);
|
||||
if (header != null) {
|
||||
header.writeComment(str, 0, ConfigFormattingRules.CommentStyle.SPACED);
|
||||
str.write("\n"); // add one space after the header
|
||||
}
|
||||
String dump = yaml.dump(this.getValues(false));
|
||||
if (!dump.equals(BLANK_CONFIG)) {
|
||||
writeComments(dump, str);
|
||||
}
|
||||
return str.toString();
|
||||
} catch (Throwable ex) {
|
||||
Logger.getLogger(Config.class.getName()).log(Level.SEVERE, "Error saving config", ex);
|
||||
delaySave();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
protected final Pattern yamlNode = Pattern.compile("^( *)([^:\\{\\}\\[\\],&\\*#\\?\\|\\-<>=!%@`]+):(.*)$");
|
||||
|
||||
protected void writeComments(String data, Writer out) throws IOException {
|
||||
// line-by-line apply line spacing formatting and comments per-node
|
||||
BufferedReader in = new BufferedReader(new StringReader(data));
|
||||
String line;
|
||||
boolean insideScalar = false;
|
||||
boolean firstNode = true;
|
||||
int index = 0;
|
||||
LinkedList<String> currentPath = new LinkedList();
|
||||
while ((line = in.readLine()) != null) {
|
||||
// ignore comments and empty lines (there shouldn't be any, but just in case)
|
||||
if (line.trim().startsWith("#") || line.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check to see if this is a line that we can process
|
||||
int lineOffset = getOffset(line);
|
||||
insideScalar &= lineOffset <= index;
|
||||
Matcher m;
|
||||
if (!insideScalar && (m = yamlNode.matcher(line)).find()) {
|
||||
// we found a config node! ^.^
|
||||
// check to see what the full path is
|
||||
int depth = (m.group(1).length() / indentation);
|
||||
while (depth < currentPath.size()) {
|
||||
currentPath.removeLast();
|
||||
}
|
||||
currentPath.add(m.group(2));
|
||||
String path = currentPath.stream().collect(Collectors.joining(String.valueOf(pathChar)));
|
||||
|
||||
// if this is a root-level node, apply extra spacing if we aren't the first node
|
||||
if (!firstNode && depth == 0 && rootNodeSpacing > 0) {
|
||||
out.write((new String(new char[rootNodeSpacing])).replace("\0", "\n")); // yes it's silly, but it works :>
|
||||
}
|
||||
firstNode = false; // we're no longer on the first node
|
||||
|
||||
// insert the relavant comment
|
||||
Comment comment = getComment(path);
|
||||
if (comment != null) {
|
||||
// add spacing between previous nodes and comments
|
||||
if (depth != 0) {
|
||||
out.write((new String(new char[commentSpacing])).replace("\0", "\n"));
|
||||
}
|
||||
|
||||
// formatting style for this node
|
||||
ConfigFormattingRules.CommentStyle style = comment.getCommentStyle();
|
||||
if (style == null) {
|
||||
// check to see what type of node this is
|
||||
if (!m.group(3).trim().isEmpty()) {
|
||||
// setting node
|
||||
style = defaultNodeCommentFormat;
|
||||
} else {
|
||||
// probably a section? (need to peek ahead to check if this is a list)
|
||||
in.mark(1000);
|
||||
String nextLine = in.readLine().trim();
|
||||
in.reset();
|
||||
if (nextLine.startsWith("-")) {
|
||||
// not a section :P
|
||||
style = defaultNodeCommentFormat;
|
||||
} else {
|
||||
style = defaultSectionCommentFormat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write it down!
|
||||
comment.writeComment(out, lineOffset, style);
|
||||
}
|
||||
// ignore scalars
|
||||
index = lineOffset;
|
||||
if (m.group(3).trim().equals("|") || m.group(3).trim().equals(">")) {
|
||||
insideScalar = true;
|
||||
}
|
||||
}
|
||||
|
||||
out.write(line);
|
||||
out.write("\n");
|
||||
}
|
||||
}
|
||||
|
||||
protected static int getOffset(String s) {
|
||||
char[] chars = s.toCharArray();
|
||||
for (int i = 0; i < chars.length; ++i) {
|
||||
if (chars[i] != ' ') {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
class SaveTask extends TimerTask {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
saveChanges();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
package com.songoda.core.configuration;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.bukkit.configuration.Configuration;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.InvalidConfigurationException;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
|
||||
public class ConfigFileConfigurationAdapter extends FileConfiguration {
|
||||
|
||||
final Config config;
|
||||
|
||||
public ConfigFileConfigurationAdapter(Config config) {
|
||||
super(config);
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public Config getCoreConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String saveToString() {
|
||||
return config.saveToString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFromString(String string) throws InvalidConfigurationException {
|
||||
config.loadFromString(string);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String buildHeader() {
|
||||
return "#" + config.getHeader().stream().collect(Collectors.joining("\n#"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigOptionsAdapter options() {
|
||||
return new ConfigOptionsAdapter(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getKeys(boolean deep) {
|
||||
return config.getKeys(deep);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getValues(boolean deep) {
|
||||
return config.getValues(deep);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String path) {
|
||||
return config.contains(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSet(String path) {
|
||||
return config.isSet(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentPath() {
|
||||
return config.getCurrentPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return config.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration getRoot() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigurationSection getParent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDefault(String path, Object value) {
|
||||
config.addDefault(path, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigurationSection getDefaultSection() {
|
||||
return config.getDefaultSection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(String path, Object value) {
|
||||
config.set(path, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(String path) {
|
||||
return config.get(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(String path, Object def) {
|
||||
return config.get(path, def);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigurationSection createSection(String path) {
|
||||
return config.createSection(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigurationSection createSection(String path, Map<?, ?> map) {
|
||||
return config.createSection(path, map);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package com.songoda.core.configuration;
|
||||
|
||||
public class ConfigFormattingRules {
|
||||
|
||||
int spacesBetweenMainCategories;
|
||||
int spacesBetweenValues;
|
||||
CommentStyle rootCommentStyle = CommentStyle.BLOCKSPACED;
|
||||
CommentStyle mainCategoryCommentStyle = CommentStyle.SPACED;
|
||||
|
||||
public static enum CommentStyle {
|
||||
|
||||
/**
|
||||
* # Comment
|
||||
*/
|
||||
SIMPLE(false, false, " ", ""),
|
||||
/**
|
||||
* # <br />
|
||||
* # Comment <br />
|
||||
* # <br />
|
||||
*/
|
||||
SPACED(false, true, " ", ""),
|
||||
/**
|
||||
* ########### <br />
|
||||
* # Comment # <br />
|
||||
* ########### <br />
|
||||
*/
|
||||
BLOCKED(true, false, " ", " "),
|
||||
/**
|
||||
* ############# <br />
|
||||
* #|¯¯¯¯¯¯¯¯¯|# <br />
|
||||
* #| Comment |# <br />
|
||||
* #|_________|# <br />
|
||||
* ############# <br />
|
||||
*/
|
||||
BLOCKSPACED(true, true, "|\u00AF", '\u00AF', "\u00AF|", "| ", " |", "|_", '_', "_|");
|
||||
|
||||
final boolean drawBorder, drawSpace;
|
||||
final String commentPrefix, spacePrefixTop, spacePrefixBottom;
|
||||
final String commentSuffix, spaceSuffixTop, spaceSuffixBottom;
|
||||
final char spaceCharTop, spaceCharBottom;
|
||||
|
||||
private CommentStyle(boolean drawBorder, boolean drawSpace,
|
||||
String spacePrefixTop, char spaceCharTop, String spaceSuffixTop,
|
||||
String commentPrefix, String commentSuffix,
|
||||
String spacePrefixBottom, char spaceCharBottom, String spaceSuffixBottom) {
|
||||
this.drawBorder = drawBorder;
|
||||
this.drawSpace = drawSpace;
|
||||
this.commentPrefix = commentPrefix;
|
||||
this.spacePrefixTop = spacePrefixTop;
|
||||
this.spacePrefixBottom = spacePrefixBottom;
|
||||
this.commentSuffix = commentSuffix;
|
||||
this.spaceSuffixTop = spaceSuffixTop;
|
||||
this.spaceSuffixBottom = spaceSuffixBottom;
|
||||
this.spaceCharTop = spaceCharTop;
|
||||
this.spaceCharBottom = spaceCharBottom;
|
||||
}
|
||||
|
||||
private CommentStyle(boolean drawBorder, boolean drawSpace, String commentPrefix, String commentSuffix) {
|
||||
this.drawBorder = drawBorder;
|
||||
this.drawSpace = drawSpace;
|
||||
this.commentPrefix = commentPrefix;
|
||||
this.commentSuffix = commentSuffix;
|
||||
this.spacePrefixTop = this.spacePrefixBottom = "";
|
||||
this.spaceCharTop = this.spaceCharBottom = ' ';
|
||||
this.spaceSuffixTop = this.spaceSuffixBottom = "";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package com.songoda.core.configuration;
|
||||
|
||||
import java.util.List;
|
||||
import org.bukkit.configuration.file.FileConfigurationOptions;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ConfigOptionsAdapter extends FileConfigurationOptions {
|
||||
|
||||
final ConfigSection config;
|
||||
|
||||
public ConfigOptionsAdapter(ConfigSection config) {
|
||||
super(config);
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public Config getConfig() {
|
||||
return (Config) config.root;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ConfigFileConfigurationAdapter configuration() {
|
||||
return new ConfigFileConfigurationAdapter((Config) config.root);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ConfigOptionsAdapter copyDefaults(boolean value) {
|
||||
// we always copy new values
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ConfigOptionsAdapter pathSeparator(char value) {
|
||||
((Config) config.root).setPathSeparator(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ConfigOptionsAdapter header(@Nullable String value) {
|
||||
if (value == null) {
|
||||
((Config) config.root).setHeader((List) null);
|
||||
} else {
|
||||
((Config) config.root).setHeader(value.split("\n"));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ConfigOptionsAdapter copyHeader(boolean value) {
|
||||
if (!value) {
|
||||
((Config) config.root).setHeader((List) null);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public int indent() {
|
||||
return ((Config) config.root).getIndent();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigOptionsAdapter indent(int value) {
|
||||
((Config) config.root).setIndent(value);
|
||||
return this;
|
||||
}
|
||||
}
|
636
src/main/java/com/songoda/core/configuration/ConfigSection.java
Normal file
636
src/main/java/com/songoda/core/configuration/ConfigSection.java
Normal file
@ -0,0 +1,636 @@
|
||||
package com.songoda.core.configuration;
|
||||
|
||||
import com.songoda.core.compatibility.LegacyMaterials;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import org.bukkit.configuration.Configuration;
|
||||
import org.bukkit.configuration.MemoryConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Configuration for a specific node
|
||||
*
|
||||
* @since 2019-08-28
|
||||
* @author jascotty2
|
||||
*/
|
||||
public class ConfigSection extends MemoryConfiguration {
|
||||
|
||||
final String fullPath, nodeKey;
|
||||
final ConfigSection root;
|
||||
final ConfigSection parent;
|
||||
protected int indentation = 2; // between 2 and 9 (inclusive)
|
||||
protected char pathChar = '.';
|
||||
final HashMap<String, Comment> configComments;
|
||||
final HashMap<String, Comment> defaultComments;
|
||||
final LinkedHashMap<String, Object> defaults;
|
||||
final LinkedHashMap<String, Object> values;
|
||||
/**
|
||||
* Internal root state: if any configuration value has changed from file state
|
||||
*/
|
||||
boolean changed = false;
|
||||
final boolean isDefault;
|
||||
final Object lock = new Object();
|
||||
|
||||
ConfigSection() {
|
||||
this.root = this;
|
||||
this.parent = null;
|
||||
isDefault = false;
|
||||
nodeKey = fullPath = "";
|
||||
configComments = new HashMap();
|
||||
defaultComments = new HashMap();
|
||||
defaults = new LinkedHashMap();
|
||||
values = new LinkedHashMap();
|
||||
}
|
||||
|
||||
ConfigSection(ConfigSection root, ConfigSection parent, String nodeKey, boolean isDefault) {
|
||||
this.root = root;
|
||||
this.parent = parent;
|
||||
this.nodeKey = nodeKey;
|
||||
this.fullPath = nodeKey != null ? parent.fullPath + nodeKey + root.pathChar : parent.fullPath;
|
||||
this.isDefault = isDefault;
|
||||
configComments = defaultComments = null;
|
||||
defaults = null;
|
||||
values = null;
|
||||
}
|
||||
|
||||
public int getIndent() {
|
||||
return root.indentation;
|
||||
}
|
||||
|
||||
public void setIndent(int indentation) {
|
||||
root.indentation = indentation;
|
||||
}
|
||||
|
||||
protected void onChange() {
|
||||
if (parent != null) {
|
||||
root.onChange();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the character used to separate configuration nodes. <br>
|
||||
* IMPORTANT: Do not change this after loading or adding ConfigurationSections!
|
||||
*
|
||||
* @param pathChar character to use
|
||||
*/
|
||||
public void setPathSeparator(char pathChar) {
|
||||
if (!root.values.isEmpty() || !root.defaults.isEmpty())
|
||||
throw new RuntimeException("Path change after config initialization");
|
||||
root.pathChar = pathChar;
|
||||
}
|
||||
|
||||
public char getPathSeparator() {
|
||||
return root.pathChar;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The full key for this section node
|
||||
*/
|
||||
public String getKey() {
|
||||
return !fullPath.endsWith(String.valueOf(root.pathChar)) ? fullPath : fullPath.substring(0, fullPath.length() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The specific key that was used from the last node to get to this node
|
||||
*/
|
||||
public String getNodeKey() {
|
||||
return nodeKey;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigSection createDefaultSection(@NotNull String path) {
|
||||
ConfigSection section = new ConfigSection(root, this, path, true);
|
||||
synchronized (root.lock) {
|
||||
root.defaults.put(fullPath + path, section);
|
||||
}
|
||||
return section;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigSection createDefaultSection(@NotNull String path, String... comment) {
|
||||
ConfigSection section = new ConfigSection(root, this, path, true);
|
||||
synchronized (root.lock) {
|
||||
root.defaults.put(fullPath + path, section);
|
||||
root.defaultComments.put(fullPath + path, new Comment(comment));
|
||||
}
|
||||
return section;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigSection createDefaultSection(@NotNull String path, ConfigFormattingRules.CommentStyle commentStyle, String... comment) {
|
||||
ConfigSection section = new ConfigSection(root, this, path, true);
|
||||
synchronized (root.lock) {
|
||||
root.defaults.put(fullPath + path, section);
|
||||
root.defaultComments.put(fullPath + path, new Comment(commentStyle, comment));
|
||||
}
|
||||
return section;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigSection setComment(@NotNull String path, @Nullable ConfigFormattingRules.CommentStyle commentStyle, String... lines) {
|
||||
return setComment(path, commentStyle, lines.length == 0 ? (List) null : Arrays.asList(lines));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigSection setComment(@NotNull String path, @Nullable ConfigFormattingRules.CommentStyle commentStyle, @Nullable List<String> lines) {
|
||||
synchronized (root.lock) {
|
||||
if (isDefault) {
|
||||
root.defaultComments.put(fullPath + path, lines != null ? new Comment(commentStyle, lines) : null);
|
||||
} else {
|
||||
root.configComments.put(fullPath + path, lines != null ? new Comment(commentStyle, lines) : null);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigSection setDefaultComment(@NotNull String path, String... lines) {
|
||||
return setDefaultComment(path, lines.length == 0 ? (List) null : Arrays.asList(lines));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigSection setDefaultComment(@NotNull String path, @Nullable List<String> lines) {
|
||||
synchronized (root.lock) {
|
||||
root.defaultComments.put(fullPath + path, new Comment(lines));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigSection setDefaultComment(@NotNull String path, ConfigFormattingRules.CommentStyle commentStyle, String... lines) {
|
||||
return setDefaultComment(path, commentStyle, lines.length == 0 ? (List) null : Arrays.asList(lines));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigSection setDefaultComment(@NotNull String path, ConfigFormattingRules.CommentStyle commentStyle, @Nullable List<String> lines) {
|
||||
synchronized (root.lock) {
|
||||
root.defaultComments.put(fullPath + path, new Comment(commentStyle, lines));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Comment getComment(@NotNull String path) {
|
||||
Comment result = root.configComments.get(fullPath + path);
|
||||
if (result == null) {
|
||||
result = root.defaultComments.get(fullPath + path);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getCommentString(@NotNull String path) {
|
||||
Comment result = root.configComments.get(fullPath + path);
|
||||
if (result == null) {
|
||||
result = root.defaultComments.get(fullPath + path);
|
||||
}
|
||||
return result != null ? result.toString() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDefault(@NotNull String path, @Nullable Object value) {
|
||||
synchronized (root.lock) {
|
||||
// if any intermediate nodes don't exist, create them
|
||||
String[] pathParts = path.split(Pattern.quote(String.valueOf(root.pathChar)));
|
||||
String nodePath = "";
|
||||
for (int i = 0; i < pathParts.length - 1; ++i) {
|
||||
nodePath += (nodePath.isEmpty() ? pathParts[i] : root.pathChar + pathParts[i]);
|
||||
if (!(root.defaults.get(nodePath) instanceof ConfigSection)) {
|
||||
root.defaults.put(nodePath, new ConfigSection(root, this, nodePath, true));
|
||||
}
|
||||
}
|
||||
root.defaults.put(fullPath + path, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDefaults(@NotNull Map<String, Object> defaults) {
|
||||
//defaults.entrySet().stream().forEach(m -> root.defaults.put(fullPath + m.getKey(), m.getValue()));
|
||||
defaults.entrySet().stream().forEach(m -> addDefault(m.getKey(), m.getValue()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaults(Configuration c) {
|
||||
if (fullPath.isEmpty()) {
|
||||
root.defaults.clear();
|
||||
} else {
|
||||
root.defaults.keySet().stream()
|
||||
.filter(k -> k.startsWith(fullPath))
|
||||
.forEach(k -> root.defaults.remove(k));
|
||||
}
|
||||
addDefaults(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigSection getDefaults() {
|
||||
return new ConfigSection(root, this, null, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigSection getDefaultSection() {
|
||||
return new ConfigSection(root, this, null, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigOptionsAdapter options() {
|
||||
return new ConfigOptionsAdapter(root);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<String> getKeys(boolean deep) {
|
||||
LinkedHashSet<String> result = new LinkedHashSet();
|
||||
int pathIndex = fullPath.lastIndexOf(root.pathChar);
|
||||
if (deep) {
|
||||
result.addAll(root.defaults.keySet().stream()
|
||||
.filter(k -> k.startsWith(fullPath))
|
||||
.map(k -> !k.endsWith(String.valueOf(root.pathChar)) ? k.substring(pathIndex + 1) : k.substring(pathIndex + 1, k.length() - 1))
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new)));
|
||||
result.addAll(root.values.keySet().stream()
|
||||
.filter(k -> k.startsWith(fullPath))
|
||||
.map(k -> !k.endsWith(String.valueOf(root.pathChar)) ? k.substring(pathIndex + 1) : k.substring(pathIndex + 1, k.length() - 1))
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new)));
|
||||
} else {
|
||||
result.addAll(root.defaults.keySet().stream()
|
||||
.filter(k -> k.startsWith(fullPath) && k.lastIndexOf(root.pathChar) == pathIndex + 1)
|
||||
.map(k -> !k.endsWith(String.valueOf(root.pathChar)) ? k.substring(pathIndex + 1) : k.substring(pathIndex + 1, k.length() - 1))
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new)));
|
||||
result.addAll(root.values.keySet().stream()
|
||||
.filter(k -> k.startsWith(fullPath) && k.lastIndexOf(root.pathChar) == pathIndex)
|
||||
.map(k -> !k.endsWith(String.valueOf(root.pathChar)) ? k.substring(pathIndex + 1) : k.substring(pathIndex + 1, k.length() - 1))
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<String, Object> getValues(boolean deep) {
|
||||
LinkedHashMap<String, Object> result = new LinkedHashMap();
|
||||
int pathIndex = fullPath.lastIndexOf(root.pathChar);
|
||||
if (deep) {
|
||||
result.putAll((Map<String, Object>) root.defaults.entrySet().stream()
|
||||
.filter(k -> k.getKey().startsWith(fullPath))
|
||||
.collect(Collectors.toMap(
|
||||
e -> !e.getKey().endsWith(String.valueOf(root.pathChar)) ? e.getKey().substring(pathIndex + 1) : e.getKey().substring(pathIndex + 1, e.getKey().length() - 1),
|
||||
e -> e.getValue(),
|
||||
(v1, v2) -> { throw new IllegalStateException(); }, // never going to be merging keys
|
||||
LinkedHashMap::new)));
|
||||
result.putAll((Map<String, Object>) root.values.entrySet().stream()
|
||||
.filter(k -> k.getKey().startsWith(fullPath))
|
||||
.collect(Collectors.toMap(
|
||||
e -> !e.getKey().endsWith(String.valueOf(root.pathChar)) ? e.getKey().substring(pathIndex + 1) : e.getKey().substring(pathIndex + 1, e.getKey().length() - 1),
|
||||
e -> e.getValue(),
|
||||
(v1, v2) -> { throw new IllegalStateException(); }, // never going to be merging keys
|
||||
LinkedHashMap::new)));
|
||||
} else {
|
||||
result.putAll((Map<String, Object>) root.defaults.entrySet().stream()
|
||||
.filter(k -> k.getKey().startsWith(fullPath) && k.getKey().lastIndexOf(root.pathChar) == pathIndex)
|
||||
.collect(Collectors.toMap(
|
||||
e -> !e.getKey().endsWith(String.valueOf(root.pathChar)) ? e.getKey().substring(pathIndex + 1) : e.getKey().substring(pathIndex + 1, e.getKey().length() - 1),
|
||||
e -> e.getValue(),
|
||||
(v1, v2) -> { throw new IllegalStateException(); }, // never going to be merging keys
|
||||
LinkedHashMap::new)));
|
||||
result.putAll((Map<String, Object>) root.values.entrySet().stream()
|
||||
.filter(k -> k.getKey().startsWith(fullPath) && k.getKey().lastIndexOf(root.pathChar) == pathIndex)
|
||||
.collect(Collectors.toMap(
|
||||
e -> !e.getKey().endsWith(String.valueOf(root.pathChar)) ? e.getKey().substring(pathIndex + 1) : e.getKey().substring(pathIndex + 1, e.getKey().length() - 1),
|
||||
e -> e.getValue(),
|
||||
(v1, v2) -> { throw new IllegalStateException(); }, // never going to be merging keys
|
||||
LinkedHashMap::new)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<ConfigSection> getSections(String path) {
|
||||
ConfigSection rootSection = getConfigurationSection(path);
|
||||
if (rootSection == null) {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
ArrayList<ConfigSection> result = new ArrayList();
|
||||
rootSection.getKeys(false).stream()
|
||||
.map(key -> rootSection.get(key))
|
||||
.filter(object -> object != null && object instanceof ConfigSection)
|
||||
.forEachOrdered(object -> result.add((ConfigSection) object));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@NotNull String path) {
|
||||
return root.defaults.containsKey(fullPath + path) || root.values.containsKey(fullPath + path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@NotNull String path, boolean ignoreDefault) {
|
||||
return (!ignoreDefault && root.defaults.containsKey(fullPath + path)) || root.values.containsKey(fullPath + path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSet(@NotNull String path) {
|
||||
return root.defaults.get(fullPath + path) != null || root.values.get(fullPath + path) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentPath() {
|
||||
return fullPath.isEmpty() ? "" : fullPath.substring(0, fullPath.length() - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
if (fullPath.isEmpty())
|
||||
return "";
|
||||
String[] parts = fullPath.split(Pattern.quote(String.valueOf(root.pathChar)));
|
||||
return parts[parts.length - 1];
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigSection getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigSection getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object get(@NotNull String path) {
|
||||
Object result = root.values.get(fullPath + path);
|
||||
if (result == null) {
|
||||
result = root.defaults.get(fullPath + path);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object get(@NotNull String path, @Nullable Object def) {
|
||||
Object result = root.values.get(fullPath + path);
|
||||
return result != null ? result : def;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(@NotNull String path, @Nullable Object value) {
|
||||
if (isDefault) {
|
||||
root.defaults.put(fullPath + path, value);
|
||||
} else {
|
||||
synchronized (root.lock) {
|
||||
if (value != null) {
|
||||
root.changed |= root.values.put(fullPath + path, value) != value;
|
||||
} else {
|
||||
root.changed |= root.values.remove(fullPath + path) != null;
|
||||
}
|
||||
}
|
||||
onChange();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigSection set(@NotNull String path, @Nullable Object value, String ... comment) {
|
||||
set(path, value);
|
||||
return setComment(path, null, comment);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigSection set(@NotNull String path, @Nullable Object value, List<String> comment) {
|
||||
set(path, value);
|
||||
return setComment(path, null, comment);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigSection set(@NotNull String path, @Nullable Object value, @Nullable ConfigFormattingRules.CommentStyle commentStyle, String ... comment) {
|
||||
set(path, value);
|
||||
return setComment(path, commentStyle, comment);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigSection set(@NotNull String path, @Nullable Object value, @Nullable ConfigFormattingRules.CommentStyle commentStyle, List<String> comment) {
|
||||
set(path, value);
|
||||
return setComment(path, commentStyle, comment);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigSection setDefault(@NotNull String path, @Nullable Object value) {
|
||||
addDefault(path, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigSection setDefault(@NotNull String path, @Nullable Object value, String ... comment) {
|
||||
addDefault(path, value);
|
||||
return setDefaultComment(path, comment);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigSection setDefault(@NotNull String path, @Nullable Object value, List<String> comment) {
|
||||
addDefault(path, value);
|
||||
return setDefaultComment(path, comment);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigSection setDefault(@NotNull String path, @Nullable Object value, ConfigFormattingRules.CommentStyle commentStyle, String ... comment) {
|
||||
addDefault(path, value);
|
||||
return setDefaultComment(path, commentStyle, comment);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigSection setDefault(@NotNull String path, @Nullable Object value, ConfigFormattingRules.CommentStyle commentStyle, List<String> comment) {
|
||||
addDefault(path, value);
|
||||
return setDefaultComment(path, commentStyle, comment);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ConfigSection createSection(@NotNull String path) {
|
||||
ConfigSection section = new ConfigSection(root, this, path, false);
|
||||
synchronized(root.lock) {
|
||||
root.values.put(fullPath + path, section);
|
||||
}
|
||||
root.changed = true;
|
||||
onChange();
|
||||
return section;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigSection createSection(@NotNull String path, String... comment) {
|
||||
return createSection(path, null, comment.length == 0 ? (List) null : Arrays.asList(comment));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigSection createSection(@NotNull String path, @Nullable List<String> comment) {
|
||||
return createSection(path, null, comment);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigSection createSection(@NotNull String path, @Nullable ConfigFormattingRules.CommentStyle commentStyle, String... comment) {
|
||||
return createSection(path, commentStyle, comment.length == 0 ? (List) null : Arrays.asList(comment));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public ConfigSection createSection(@NotNull String path, @Nullable ConfigFormattingRules.CommentStyle commentStyle, @Nullable List<String> comment) {
|
||||
ConfigSection section = new ConfigSection(root, this, path, false);
|
||||
synchronized (root.lock) {
|
||||
root.values.put(fullPath + path, section);
|
||||
}
|
||||
setComment(path, commentStyle, comment);
|
||||
root.changed = true;
|
||||
onChange();
|
||||
return section;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ConfigSection createSection(@NotNull String path, Map<?, ?> map) {
|
||||
ConfigSection section = new ConfigSection(root, this, path, false);
|
||||
synchronized (root.lock) {
|
||||
root.values.put(fullPath + path, section);
|
||||
}
|
||||
for (Map.Entry<?, ?> entry : map.entrySet()) {
|
||||
if (entry.getValue() instanceof Map) {
|
||||
section.createSection(entry.getKey().toString(), (Map) entry.getValue());
|
||||
continue;
|
||||
}
|
||||
section.set(entry.getKey().toString(), entry.getValue());
|
||||
}
|
||||
root.changed = true;
|
||||
onChange();
|
||||
return section;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getString(@NotNull String path) {
|
||||
Object result = get(path);
|
||||
return result != null ? result.toString() : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getString(@NotNull String path, @Nullable String def) {
|
||||
Object result = get(path);
|
||||
return result != null ? result.toString() : def;
|
||||
}
|
||||
|
||||
public char getChar(@NotNull String path) {
|
||||
Object result = get(path);
|
||||
return result != null && !result.toString().isEmpty() ? result.toString().charAt(0) : '\0';
|
||||
}
|
||||
|
||||
public char getChar(@NotNull String path, char def) {
|
||||
Object result = get(path);
|
||||
return result != null && !result.toString().isEmpty() ? result.toString().charAt(0) : def;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(@NotNull String path) {
|
||||
Object result = get(path);
|
||||
return result instanceof Number ? ((Number) result).intValue() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(@NotNull String path, int def) {
|
||||
Object result = get(path);
|
||||
return result instanceof Number ? ((Number) result).intValue() : def;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(@NotNull String path) {
|
||||
Object result = get(path);
|
||||
return result instanceof Boolean ? (Boolean) result : false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean(@NotNull String path, boolean def) {
|
||||
Object result = get(path);
|
||||
return result instanceof Boolean ? (Boolean) result : def;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(@NotNull String path) {
|
||||
Object result = get(path);
|
||||
return result instanceof Number ? ((Number) result).doubleValue() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(@NotNull String path, double def) {
|
||||
Object result = get(path);
|
||||
return result instanceof Number ? ((Number) result).doubleValue() : def;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(@NotNull String path) {
|
||||
Object result = get(path);
|
||||
return result instanceof Number ? ((Number) result).longValue(): 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(@NotNull String path, long def) {
|
||||
Object result = get(path);
|
||||
return result instanceof Number ? ((Number) result).longValue() : def;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<?> getList(@NotNull String path) {
|
||||
Object result = get(path);
|
||||
return result instanceof List ? (List) result : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<?> getList(@NotNull String path, @Nullable List<?> def) {
|
||||
Object result = get(path);
|
||||
return result instanceof List ? (List) result : def;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public LegacyMaterials getMaterial(@NotNull String path) {
|
||||
String val = getString(path);
|
||||
LegacyMaterials mat = val != null ? LegacyMaterials.getMaterial(val) : null;
|
||||
return mat;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public LegacyMaterials getMaterial(@NotNull String path, @Nullable LegacyMaterials def) {
|
||||
String val = getString(path);
|
||||
LegacyMaterials mat = val != null ? LegacyMaterials.getMaterial(val) : null;
|
||||
return mat != null ? mat : def;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> T getObject(@NotNull String path, @NotNull Class<T> clazz) {
|
||||
Object result = get(path);
|
||||
return result != null && clazz.isInstance(result) ? clazz.cast(result) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> T getObject(@NotNull String path, @NotNull Class<T> clazz, @Nullable T def) {
|
||||
Object result = get(path);
|
||||
return result != null && clazz.isInstance(result) ? clazz.cast(result) : def;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigSection getConfigurationSection(@NotNull String path) {
|
||||
Object result = get(path);
|
||||
return result instanceof ConfigSection ? (ConfigSection) result : null;
|
||||
}
|
||||
|
||||
public ConfigSection getOrCreateConfigurationSection(@NotNull String path) {
|
||||
Object result = get(path);
|
||||
return result instanceof ConfigSection ? (ConfigSection) result : createSection(path);
|
||||
}
|
||||
}
|
126
src/main/java/com/songoda/core/configuration/ConfigSetting.java
Normal file
126
src/main/java/com/songoda/core/configuration/ConfigSetting.java
Normal file
@ -0,0 +1,126 @@
|
||||
package com.songoda.core.configuration;
|
||||
|
||||
import com.songoda.core.compatibility.LegacyMaterials;
|
||||
import java.util.List;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ConfigSetting {
|
||||
|
||||
final Config config;
|
||||
final String key;
|
||||
|
||||
public ConfigSetting(@NotNull Config config, @NotNull String key) {
|
||||
this.config = config;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public ConfigSetting(@NotNull Config config, @NotNull String key, @NotNull Object defaultValue, String ... comment) {
|
||||
this.config = config;
|
||||
this.key = key;
|
||||
config.setDefault(key, defaultValue, comment);
|
||||
}
|
||||
|
||||
public ConfigSetting(@NotNull Config config, @NotNull String key, @NotNull Object defaultValue, ConfigFormattingRules.CommentStyle commentStyle, String ... comment) {
|
||||
this.config = config;
|
||||
this.key = key;
|
||||
config.setDefault(key, defaultValue, commentStyle, comment);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public List<Integer> getIntegerList() {
|
||||
return config.getIntegerList(key);
|
||||
}
|
||||
|
||||
public List<String> getStringList() {
|
||||
return config.getStringList(key);
|
||||
}
|
||||
|
||||
public boolean getBoolean() {
|
||||
return config.getBoolean(key);
|
||||
}
|
||||
|
||||
public boolean getBoolean(boolean def) {
|
||||
return config.getBoolean(key, def);
|
||||
}
|
||||
|
||||
public int getInt() {
|
||||
return config.getInt(key);
|
||||
}
|
||||
|
||||
public int getInt(int def) {
|
||||
return config.getInt(key, def);
|
||||
}
|
||||
|
||||
public long getLong() {
|
||||
return config.getLong(key);
|
||||
}
|
||||
|
||||
public long getLong(long def) {
|
||||
return config.getLong(key, def);
|
||||
}
|
||||
|
||||
public double getDouble() {
|
||||
return config.getDouble(key);
|
||||
}
|
||||
|
||||
public double getDouble(double def) {
|
||||
return config.getDouble(key, def);
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return config.getString(key);
|
||||
}
|
||||
|
||||
public String getString(String def) {
|
||||
return config.getString(key, def);
|
||||
}
|
||||
|
||||
public Object getObject() {
|
||||
return config.get(key);
|
||||
}
|
||||
|
||||
public Object getObject(Object def) {
|
||||
return config.get(key, def);
|
||||
}
|
||||
|
||||
public <T> T getObject(@NotNull Class<T> clazz) {
|
||||
return config.getObject(key, clazz);
|
||||
}
|
||||
|
||||
public <T> T getObject(@NotNull Class<T> clazz, @Nullable T def) {
|
||||
return config.getObject(key, clazz, def);
|
||||
}
|
||||
|
||||
public char getChar() {
|
||||
return config.getChar(key);
|
||||
}
|
||||
|
||||
public char getChar(char def) {
|
||||
return config.getChar(key, def);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public LegacyMaterials getMaterial() {
|
||||
LegacyMaterials m = getMaterial(null);
|
||||
return m != null ? m : LegacyMaterials.STONE;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public LegacyMaterials getMaterial(@Nullable LegacyMaterials def) {
|
||||
//return config.getMaterial(key, def);
|
||||
String val = config.getString(key);
|
||||
LegacyMaterials mat = val != null ? LegacyMaterials.getMaterial(val) : null;
|
||||
|
||||
if (mat == null) {
|
||||
System.out.println(String.format("Config value \"%s\" has an invalid material name: \"%s\"", key, val));
|
||||
}
|
||||
|
||||
return mat != null ? mat : def;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,280 @@
|
||||
package com.songoda.core.configuration.editor;
|
||||
|
||||
import com.songoda.core.compatibility.LegacyMaterials;
|
||||
import com.songoda.core.gui.Gui;
|
||||
import com.songoda.core.gui.GuiUtils;
|
||||
import com.songoda.core.gui.SimplePagedGui;
|
||||
import com.songoda.core.input.ChatPrompt;
|
||||
import com.songoda.core.utils.ItemUtils;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.MemoryConfiguration;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
/**
|
||||
* Edit a configuration file for a specific plugin
|
||||
*
|
||||
* @since 2019-08-31
|
||||
* @author jascotty2
|
||||
*/
|
||||
public class ConfigEditorGui extends SimplePagedGui {
|
||||
|
||||
final JavaPlugin plugin;
|
||||
final String file;
|
||||
final MemoryConfiguration config;
|
||||
final ConfigurationSection node;
|
||||
Method configSection_getCommentString = null;
|
||||
List<String> sections = new ArrayList();
|
||||
List<String> settings = new ArrayList();
|
||||
|
||||
public ConfigEditorGui(JavaPlugin plugin, Gui parent, String file, MemoryConfiguration config) {
|
||||
this(plugin, parent, file, config, config);
|
||||
setOnClose((gui) -> save());
|
||||
}
|
||||
|
||||
public ConfigEditorGui(JavaPlugin plugin, Gui parent, String file, MemoryConfiguration config, ConfigurationSection node) {
|
||||
super(parent);
|
||||
this.plugin = plugin;
|
||||
this.file = file;
|
||||
this.config = config;
|
||||
this.node = node;
|
||||
this.blankItem = GuiUtils.getBorderItem(LegacyMaterials.LIGHT_GRAY_STAINED_GLASS_PANE);
|
||||
|
||||
// if we have a ConfigSection, we can also grab comments
|
||||
try {
|
||||
configSection_getCommentString = node.getClass().getDeclaredMethod("getCommentString", String.class);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
|
||||
// decorate header
|
||||
this.setTitle(ChatColor.DARK_BLUE + file);
|
||||
this.setUseHeader(true);
|
||||
headerBackItem = footerBackItem = GuiUtils.getBorderItem(LegacyMaterials.GRAY_STAINED_GLASS_PANE.getItem());
|
||||
final String path = node.getCurrentPath();
|
||||
this.setItem(4, configItem(LegacyMaterials.FILLED_MAP, !path.isEmpty() ? path : file , config, !path.isEmpty() ? path : null, ChatColor.BLACK.toString()));
|
||||
this.setButton(8, GuiUtils.createButtonItem(LegacyMaterials.OAK_DOOR, "Exit"), (event) -> event.player.closeInventory());
|
||||
|
||||
// compile list of settings
|
||||
for (String key : node.getKeys(false)) {
|
||||
if (node.isConfigurationSection(key)) {
|
||||
sections.add(key);
|
||||
} else {
|
||||
settings.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
// next we need to display the config settings
|
||||
int index = 9;
|
||||
for (final String sectionKey : sections) {
|
||||
setButton(index++, configItem(LegacyMaterials.WRITABLE_BOOK, ChatColor.YELLOW + sectionKey, node, sectionKey, "Click to open this section"),
|
||||
(event) -> event.manager.showGUI(event.player, new ConfigEditorGui(plugin, this, file, config, node.getConfigurationSection(sectionKey))));
|
||||
}
|
||||
|
||||
// now display individual settings
|
||||
for (final String settingKey : settings) {
|
||||
final Object val = node.get(settingKey);
|
||||
if(val == null) continue;
|
||||
else if (val instanceof Boolean) {
|
||||
// toggle switch
|
||||
setButton(index, configItem(LegacyMaterials.LEVER, ChatColor.YELLOW + settingKey, node, settingKey, String.valueOf((Boolean) val), "Click to toggle this setting"),
|
||||
(event) -> this.toggle(event.slot, settingKey));
|
||||
if ((Boolean) val) {
|
||||
highlightItem(index);
|
||||
}
|
||||
} else if (isNumber(val)) {
|
||||
// number dial
|
||||
this.setButton(index, configItem(LegacyMaterials.CLOCK, ChatColor.YELLOW + settingKey, node, settingKey, String.valueOf((Number) val), "Click to edit this setting"),
|
||||
(event) -> {
|
||||
event.gui.exit();
|
||||
ChatPrompt.showPrompt(plugin, event.player, "Enter a new number value for " + settingKey + ":", response -> {
|
||||
if (!setNumber(event.slot, settingKey, response.getMessage().trim())) {
|
||||
event.player.sendMessage(ChatColor.RED + "Error: \"" + response.getMessage().trim() + "\" is not a number!");
|
||||
}
|
||||
}).setOnClose(() -> event.manager.showGUI(event.player, this))
|
||||
.setOnCancel(() -> {event.player.sendMessage(ChatColor.RED + "Edit canceled"); event.manager.showGUI(event.player, this);});
|
||||
});
|
||||
} else if (isMaterial(val)) {
|
||||
// changing a block
|
||||
// isMaterial is more of a guess, to be honest.
|
||||
setButton(index, configItem(LegacyMaterials.STONE, ChatColor.YELLOW + settingKey, node, settingKey, val.toString(), "Click to edit this setting"),
|
||||
(event) -> {
|
||||
SimplePagedGui paged = new SimplePagedGui(this);
|
||||
paged.setTitle(ChatColor.BLUE + settingKey);
|
||||
paged.setHeaderBackItem(headerBackItem).setFooterBackItem(footerBackItem).setDefaultItem(blankItem);
|
||||
paged.setItem(4, configItem(LegacyMaterials.FILLED_MAP, settingKey, node, settingKey, "Choose an item to change this value to"));
|
||||
int i = 9;
|
||||
for(LegacyMaterials mat : LegacyMaterials.getAllValidItemMaterials()) {
|
||||
paged.setButton(i++, GuiUtils.createButtonItem(mat, mat.name()), ClickType.LEFT, (matEvent) -> {
|
||||
setMaterial(event.slot, settingKey, matEvent.clickedItem);
|
||||
matEvent.player.closeInventory();
|
||||
});
|
||||
}
|
||||
event.manager.showGUI(event.player, paged);
|
||||
});
|
||||
|
||||
} else if (val instanceof String) {
|
||||
// changing a "string" value (or change to a feather for writing quill)
|
||||
setButton(index, configItem(LegacyMaterials.STRING, ChatColor.YELLOW + settingKey, node, settingKey, val.toString(), "Click to edit this setting"),
|
||||
(event) -> {
|
||||
event.gui.exit();
|
||||
ChatPrompt.showPrompt(plugin, event.player, "Enter a new value for " + settingKey + ":", response -> {
|
||||
node.set(settingKey, response.getMessage().trim());
|
||||
updateValue(event.slot, settingKey);
|
||||
}).setOnClose(() -> event.manager.showGUI(event.player, this))
|
||||
.setOnCancel(() -> {event.player.sendMessage(ChatColor.RED + "Edit canceled"); event.manager.showGUI(event.player, this);});
|
||||
});
|
||||
} else if (val instanceof List) {
|
||||
setButton(index, configItem(LegacyMaterials.WRITABLE_BOOK, ChatColor.YELLOW + settingKey, node, settingKey, String.format("(%d values)", ((List) val).size()), "Click to edit this setting"),
|
||||
(event) -> {
|
||||
event.manager.showGUI(event.player, (new ConfigEditorListEditorGui(this, settingKey, (List) val)).setOnClose((gui) -> {
|
||||
if(((ConfigEditorListEditorGui) gui.gui).saveChanges) {
|
||||
setList(event.slot, settingKey, ((ConfigEditorListEditorGui) gui.gui).values);
|
||||
}
|
||||
}));
|
||||
});
|
||||
} else {
|
||||
// idk. should we display uneditable values?
|
||||
}
|
||||
|
||||
++index;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ConfigurationSection getCurrentNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
protected void updateValue(int clickCell, String path) {
|
||||
ItemStack item = inventory.getItem(clickCell);
|
||||
if(item == null || item == AIR) return;
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
Object val = node.get(path);
|
||||
if (meta != null && val != null) {
|
||||
String valStr;
|
||||
if (val instanceof List) {
|
||||
valStr = String.format("(%d values)", ((List) val).size());
|
||||
} else {
|
||||
valStr = val.toString();
|
||||
}
|
||||
List<String> lore = meta.getLore();
|
||||
if (lore == null || lore.isEmpty()) {
|
||||
meta.setLore(Arrays.asList(valStr));
|
||||
} else {
|
||||
lore.set(0, valStr);
|
||||
meta.setLore(lore);
|
||||
}
|
||||
item.setItemMeta(meta);
|
||||
setItem(clickCell, item);
|
||||
}
|
||||
}
|
||||
|
||||
void toggle(int clickCell, String path) {
|
||||
boolean val = !node.getBoolean(path);
|
||||
node.set(path, val);
|
||||
if(val) {
|
||||
setItem(clickCell, ItemUtils.addGlow(inventory.getItem(clickCell)));
|
||||
} else {
|
||||
setItem(clickCell, ItemUtils.removeGlow(inventory.getItem(clickCell)));
|
||||
}
|
||||
updateValue(clickCell, path);
|
||||
}
|
||||
|
||||
boolean setNumber(int clickCell, String path, String input) {
|
||||
try {
|
||||
if (node.isInt(path)) {
|
||||
node.set(path, Integer.parseInt(input));
|
||||
} else if (node.isDouble(path)) {
|
||||
node.set(path, Double.parseDouble(input));
|
||||
} else if (node.isLong(path)) {
|
||||
node.set(path, Long.parseLong(input));
|
||||
}
|
||||
updateValue(clickCell, path);
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void setMaterial(int clickCell, String path, ItemStack item) {
|
||||
LegacyMaterials mat = LegacyMaterials.getMaterial(item);
|
||||
if (mat == null) {
|
||||
node.set(path, LegacyMaterials.STONE.name());
|
||||
} else {
|
||||
node.set(path, mat.name());
|
||||
}
|
||||
updateValue(clickCell, path);
|
||||
}
|
||||
|
||||
void setList(int clickCell, String path, List<String> list) {
|
||||
node.set(path, list);
|
||||
updateValue(clickCell, path);
|
||||
}
|
||||
|
||||
void save() {
|
||||
// could also check and call saveChanges()
|
||||
if (config instanceof FileConfiguration) {
|
||||
try {
|
||||
((FileConfiguration) config).save(new File(plugin.getDataFolder(), file));
|
||||
} catch (IOException ex) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Failed to save config changes to " + file, ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
plugin.reloadConfig();
|
||||
}
|
||||
|
||||
private boolean isNumber(Object value) {
|
||||
return value != null && (
|
||||
value instanceof Long
|
||||
|| value instanceof Integer
|
||||
|| value instanceof Float
|
||||
|| value instanceof Double);
|
||||
}
|
||||
|
||||
private boolean isMaterial(Object value) {
|
||||
LegacyMaterials m;
|
||||
return value instanceof String && value.toString().equals(value.toString().toUpperCase())
|
||||
&& (m = LegacyMaterials.getMaterial(value.toString())) != null && m.isValidItem();
|
||||
}
|
||||
|
||||
protected ItemStack configItem(LegacyMaterials type, String name, ConfigurationSection node, String path, String def) {
|
||||
String[] info = null;
|
||||
if (configSection_getCommentString != null) {
|
||||
try {
|
||||
Object comment = configSection_getCommentString.invoke(node, path);
|
||||
if (comment != null) {
|
||||
info = comment.toString().split("\n");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
}
|
||||
return GuiUtils.createButtonItem(type, name, info != null ? info : (def != null ? def.split("\n") : null));
|
||||
}
|
||||
|
||||
protected ItemStack configItem(LegacyMaterials type, String name, ConfigurationSection node, String path, String value, String def) {
|
||||
if(value == null) value = "";
|
||||
String[] info = null;
|
||||
if (configSection_getCommentString != null) {
|
||||
try {
|
||||
Object comment = configSection_getCommentString.invoke(node, path);
|
||||
if (comment != null) {
|
||||
info = (value + "\n" + comment.toString()).split("\n");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
}
|
||||
return GuiUtils.createButtonItem(type, name, info != null ? info : (def != null ? (value + "\n" + def).split("\n") : null));
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
package com.songoda.core.configuration.editor;
|
||||
|
||||
import com.songoda.core.compatibility.LegacyMaterials;
|
||||
import com.songoda.core.gui.GuiUtils;
|
||||
import com.songoda.core.gui.SimplePagedGui;
|
||||
import com.songoda.core.input.ChatPrompt;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
|
||||
/**
|
||||
* Edit a string list
|
||||
*
|
||||
* @since 2019-08-31
|
||||
* @author jascotty2
|
||||
*/
|
||||
public class ConfigEditorListEditorGui extends SimplePagedGui {
|
||||
|
||||
final ConfigEditorGui current;
|
||||
|
||||
public boolean saveChanges = false;
|
||||
public List<String> values;
|
||||
|
||||
public ConfigEditorListEditorGui(ConfigEditorGui current, String key, List<String> val) {
|
||||
super(current);
|
||||
this.current = current;
|
||||
this.blankItem = current.getDefaultItem();
|
||||
headerBackItem = footerBackItem = current.getHeaderBackItem();
|
||||
setTitle(ChatColor.DARK_BLUE + "String List Editor");
|
||||
this.setUseHeader(true);
|
||||
this.setItem(4, current.configItem(LegacyMaterials.FILLED_MAP, key, current.getCurrentNode(), key, null));
|
||||
this.setButton(8, GuiUtils.createButtonItem(LegacyMaterials.OAK_DOOR, "Exit"), (event) -> event.player.closeInventory());
|
||||
this.values = new ArrayList(val);
|
||||
|
||||
this.setButton(8, GuiUtils.createButtonItem(LegacyMaterials.LAVA_BUCKET, ChatColor.RED + "Discard Changes"), (event) -> event.player.closeInventory());
|
||||
this.setButton(0, GuiUtils.createButtonItem(LegacyMaterials.REDSTONE, ChatColor.GREEN + "Save"), (event) -> {
|
||||
saveChanges = true;
|
||||
event.player.closeInventory();
|
||||
});
|
||||
this.setButton(1, GuiUtils.createButtonItem(LegacyMaterials.CHEST, ChatColor.BLUE + "Add Item"),
|
||||
(event) -> {
|
||||
event.gui.exit();
|
||||
ChatPrompt.showPrompt(event.manager.getPlugin(), event.player, "Enter a new value to add:", response -> {
|
||||
values.add(response.getMessage().trim());
|
||||
redraw();
|
||||
}).setOnClose(() -> {event.manager.showGUI(event.player, this); })
|
||||
.setOnCancel(() -> {event.player.sendMessage(ChatColor.RED + "Edit canceled"); event.manager.showGUI(event.player, this);});
|
||||
});
|
||||
|
||||
redraw();
|
||||
}
|
||||
|
||||
void redraw() {
|
||||
page = 1;
|
||||
// clear old display
|
||||
if(inventory != null) {
|
||||
for(Integer i : cellItems.keySet().toArray(new Integer[0])) {
|
||||
if(i > 8) {
|
||||
cellItems.remove(i);
|
||||
conditionalButtons.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update items
|
||||
int i = 9;
|
||||
for (String item : values) {
|
||||
final int index = i - 9;
|
||||
setButton(i++, GuiUtils.createButtonItem(LegacyMaterials.PAPER, item, "Right-click to remove"), ClickType.RIGHT, (event) -> {
|
||||
values.remove(index);
|
||||
redraw();
|
||||
});
|
||||
}
|
||||
// update display
|
||||
update();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
package com.songoda.core.configuration.editor;
|
||||
|
||||
import com.songoda.core.SongodaPlugin;
|
||||
import com.songoda.core.compatibility.LegacyMaterials;
|
||||
import com.songoda.core.configuration.Config;
|
||||
import com.songoda.core.gui.Gui;
|
||||
import com.songoda.core.gui.GuiUtils;
|
||||
import com.songoda.core.gui.SimplePagedGui;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.configuration.MemoryConfiguration;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
/**
|
||||
* Edit all configuration files for a specific plugin
|
||||
*
|
||||
* @since 2019-08-31
|
||||
* @author jascotty2
|
||||
*/
|
||||
public class PluginConfigGui extends SimplePagedGui {
|
||||
|
||||
final JavaPlugin plugin;
|
||||
LinkedHashMap<String, MemoryConfiguration> configs = new LinkedHashMap();
|
||||
|
||||
public PluginConfigGui(SongodaPlugin plugin) {
|
||||
this(plugin, null);
|
||||
}
|
||||
|
||||
public PluginConfigGui(SongodaPlugin plugin, Gui parent) {
|
||||
super(parent);
|
||||
this.plugin = plugin;
|
||||
|
||||
// collect list of plugins
|
||||
configs.put(plugin.getConfig().getCoreConfig().getFile().getName(), plugin.getConfig().getCoreConfig());
|
||||
List<Config> more = plugin.getExtraConfig();
|
||||
if (more != null && !more.isEmpty()) {
|
||||
for (Config cfg : more) {
|
||||
configs.put(cfg.getFile().getName(), cfg);
|
||||
}
|
||||
}
|
||||
init();
|
||||
}
|
||||
|
||||
public PluginConfigGui(JavaPlugin plugin) {
|
||||
this(plugin, null);
|
||||
}
|
||||
|
||||
public PluginConfigGui(JavaPlugin plugin, Gui parent) {
|
||||
super(parent);
|
||||
this.plugin = plugin;
|
||||
|
||||
// collect list of plugins
|
||||
configs.put("config.yml", plugin.getConfig());
|
||||
|
||||
try {
|
||||
// can we also grab extra config from this mysterious plugin?
|
||||
Object more = plugin.getClass().getDeclaredMethod("getExtraConfig").invoke(plugin);
|
||||
if (more instanceof List && !((List) more).isEmpty()) {
|
||||
try {
|
||||
// if we have the getExtraConfig function, we should also be able to get the file
|
||||
Method method_Config_getFile = ((List) more).get(0).getClass().getDeclaredMethod("getFile");
|
||||
for (Object cfg : ((List) more)) {
|
||||
configs.put(((File) method_Config_getFile.invoke(cfg)).getName(), (MemoryConfiguration) cfg);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
// include a failsafe, I guess
|
||||
((List) more).forEach(cfg -> configs.put("(File " + configs.size() + ")", (MemoryConfiguration) cfg));
|
||||
}
|
||||
}
|
||||
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
|
||||
// I guess not!
|
||||
}
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
this.blankItem = GuiUtils.getBorderItem(LegacyMaterials.LIGHT_GRAY_STAINED_GLASS_PANE);
|
||||
|
||||
// decorate header
|
||||
this.setTitle(ChatColor.DARK_BLUE + plugin.getName() + " Plugin Config");
|
||||
this.setUseHeader(true);
|
||||
headerBackItem = footerBackItem = GuiUtils.getBorderItem(LegacyMaterials.GRAY_STAINED_GLASS_PANE.getItem());
|
||||
this.setButton(8, GuiUtils.createButtonItem(LegacyMaterials.OAK_DOOR, "Exit"), (event) -> event.player.closeInventory());
|
||||
|
||||
// List out all config files that this plugin has
|
||||
int i = 9;
|
||||
for (Map.Entry<String, MemoryConfiguration> config : configs.entrySet()) {
|
||||
this.setButton(i++, GuiUtils.createButtonItem(LegacyMaterials.BOOK, ChatColor.YELLOW + config.getKey(), "Click to edit this config"),
|
||||
(event) -> event.manager.showGUI(event.player, new ConfigEditorGui(plugin, this, config.getKey(), config.getValue())));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
44
src/main/java/com/songoda/core/core/LocaleModule.java
Normal file
44
src/main/java/com/songoda/core/core/LocaleModule.java
Normal file
@ -0,0 +1,44 @@
|
||||
package com.songoda.core.core;
|
||||
|
||||
import com.songoda.core.core.PluginInfoModule;
|
||||
import com.songoda.core.locale.Locale;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
public class LocaleModule implements PluginInfoModule {
|
||||
|
||||
@Override
|
||||
public void run(PluginInfo plugin) {
|
||||
if(plugin.getJavaPlugin() == null || plugin.getSongodaId() <= 0) return;
|
||||
JSONObject json = plugin.getJson();
|
||||
try {
|
||||
JSONArray files = (JSONArray) json.get("neededFiles");
|
||||
for (Object o : files) {
|
||||
JSONObject file = (JSONObject) o;
|
||||
|
||||
if (file.get("type").equals("locale")) {
|
||||
downloadLocale(plugin, (String) file.get("link"), (String) file.get("name"));
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
void downloadLocale(PluginInfo plugin, String link, String fileName) throws MalformedURLException, IOException {
|
||||
URL url = new URL(link);
|
||||
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
|
||||
urlConnection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11");
|
||||
urlConnection.setRequestProperty("Accept", "*/*");
|
||||
urlConnection.setConnectTimeout(5000);
|
||||
|
||||
Locale.saveLocale(plugin.getJavaPlugin(), urlConnection.getInputStream(), fileName);
|
||||
|
||||
urlConnection.disconnect();
|
||||
}
|
||||
}
|
@ -1,25 +1,32 @@
|
||||
package com.songoda.update;
|
||||
package com.songoda.core.core;
|
||||
|
||||
import com.songoda.core.compatibility.LegacyMaterials;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class Plugin {
|
||||
public final class PluginInfo {
|
||||
|
||||
private JavaPlugin javaPlugin;
|
||||
private int songodaId;
|
||||
private List<Module> modules = new ArrayList<>();
|
||||
protected final JavaPlugin javaPlugin;
|
||||
protected final int songodaId;
|
||||
protected final String coreIcon;
|
||||
protected final LegacyMaterials icon;
|
||||
private final List<PluginInfoModule> modules = new ArrayList<>();
|
||||
private boolean hasUpdate = false;
|
||||
private String latestVersion;
|
||||
private String notification;
|
||||
private String changeLog;
|
||||
private String marketplaceLink;
|
||||
private JSONObject json;
|
||||
|
||||
public Plugin(JavaPlugin javaPlugin, int songodaId) {
|
||||
public PluginInfo(JavaPlugin javaPlugin, int songodaId, String icon) {
|
||||
this.javaPlugin = javaPlugin;
|
||||
this.songodaId = songodaId;
|
||||
this.coreIcon = icon;
|
||||
this.icon = LegacyMaterials.getMaterial(icon);
|
||||
}
|
||||
|
||||
public String getLatestVersion() {
|
||||
@ -28,6 +35,7 @@ public class Plugin {
|
||||
|
||||
public void setLatestVersion(String latestVersion) {
|
||||
this.latestVersion = latestVersion;
|
||||
hasUpdate = latestVersion != null && !latestVersion.isEmpty() && !javaPlugin.getDescription().getVersion().equalsIgnoreCase(latestVersion);
|
||||
}
|
||||
|
||||
public String getNotification() {
|
||||
@ -38,6 +46,14 @@ public class Plugin {
|
||||
this.notification = notification;
|
||||
}
|
||||
|
||||
public boolean hasUpdate() {
|
||||
return hasUpdate;
|
||||
}
|
||||
|
||||
public void setHasUpdate(boolean hasUpdate) {
|
||||
this.hasUpdate = hasUpdate;
|
||||
}
|
||||
|
||||
public String getChangeLog() {
|
||||
return changeLog;
|
||||
}
|
||||
@ -62,13 +78,13 @@ public class Plugin {
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
public Module addModule(Module module) {
|
||||
public PluginInfoModule addModule(PluginInfoModule module) {
|
||||
modules.add(module);
|
||||
return module;
|
||||
}
|
||||
|
||||
public List<Module> getModules() {
|
||||
return new ArrayList<>(modules);
|
||||
public List<PluginInfoModule> getModules() {
|
||||
return Collections.unmodifiableList(modules);
|
||||
}
|
||||
|
||||
public JavaPlugin getJavaPlugin() {
|
||||
@ -78,4 +94,8 @@ public class Plugin {
|
||||
public int getSongodaId() {
|
||||
return songodaId;
|
||||
}
|
||||
|
||||
public String getCoreIcon() {
|
||||
return coreIcon;
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.songoda.core.core;
|
||||
|
||||
public interface PluginInfoModule {
|
||||
|
||||
void run(PluginInfo plugin);
|
||||
|
||||
}
|
51
src/main/java/com/songoda/core/core/SongodaCoreCommand.java
Normal file
51
src/main/java/com/songoda/core/core/SongodaCoreCommand.java
Normal file
@ -0,0 +1,51 @@
|
||||
package com.songoda.core.core;
|
||||
|
||||
import com.songoda.core.SongodaCore;
|
||||
import com.songoda.core.commands.AbstractCommand;
|
||||
import com.songoda.core.gui.GuiManager;
|
||||
import java.util.List;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class SongodaCoreCommand extends AbstractCommand {
|
||||
|
||||
protected GuiManager guiManager;
|
||||
|
||||
public SongodaCoreCommand() {
|
||||
super(false, "songoda");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReturnType runCommand(CommandSender sender, String... args) {
|
||||
if(sender instanceof Player) {
|
||||
if(guiManager == null || guiManager.isClosed()) {
|
||||
guiManager = new GuiManager(SongodaCore.getHijackedPlugin());
|
||||
}
|
||||
guiManager.showGUI((Player) sender, new SongodaCoreOverviewGUI());
|
||||
} else {
|
||||
sender.sendMessage("/songoda diag");
|
||||
}
|
||||
return ReturnType.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPermissionNode() {
|
||||
return "songoda.admin";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSyntax() {
|
||||
return "/songoda";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Displays this interface.";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> onTab(CommandSender sender, String... args) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
package com.songoda.update.command.commands;
|
||||
package com.songoda.core.core;
|
||||
|
||||
import com.songoda.update.Plugin;
|
||||
import com.songoda.update.SongodaUpdate;
|
||||
import com.songoda.update.command.AbstractCommand;
|
||||
import com.songoda.core.SongodaCore;
|
||||
import com.songoda.core.commands.AbstractCommand;
|
||||
import com.songoda.core.compatibility.ServerProject;
|
||||
import com.songoda.core.compatibility.ServerVersion;
|
||||
import com.songoda.core.utils.NMSUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
@ -11,60 +13,58 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.List;
|
||||
|
||||
public class CommandDiag extends AbstractCommand {
|
||||
|
||||
private final String name = Bukkit.getServer().getClass().getPackage().getName();
|
||||
private final String version = name.substring(name.lastIndexOf('.') + 1);
|
||||
public class SongodaCoreDiagCommand extends AbstractCommand {
|
||||
|
||||
private final DecimalFormat format = new DecimalFormat("##.##");
|
||||
|
||||
private Object serverInstance;
|
||||
private Field tpsField;
|
||||
|
||||
|
||||
public CommandDiag(AbstractCommand parent) {
|
||||
super(parent, false, "diag");
|
||||
public SongodaCoreDiagCommand() {
|
||||
super(false, "diag");
|
||||
|
||||
try {
|
||||
serverInstance = getNMSClass("MinecraftServer").getMethod("getServer").invoke(null);
|
||||
serverInstance = NMSUtils.getNMSClass("MinecraftServer").getMethod("getServer").invoke(null);
|
||||
tpsField = serverInstance.getClass().getField("recentTps");
|
||||
} catch (NoSuchFieldException | SecurityException | IllegalAccessException | IllegalArgumentException
|
||||
| InvocationTargetException | NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReturnType runCommand(SongodaUpdate instance, CommandSender sender, String... args) {
|
||||
protected ReturnType runCommand(CommandSender sender, String... args) {
|
||||
|
||||
sender.sendMessage("");
|
||||
sender.sendMessage("Songoda Diagnostics Information");
|
||||
sender.sendMessage("");
|
||||
sender.sendMessage("Plugins:");
|
||||
for (Plugin plugin : instance.getPlugins()) {
|
||||
for (PluginInfo plugin : SongodaCore.getPlugins()) {
|
||||
sender.sendMessage(plugin.getJavaPlugin().getName()
|
||||
+ " (" + plugin.getJavaPlugin().getDescription().getVersion() + ")");
|
||||
}
|
||||
sender.sendMessage("");
|
||||
sender.sendMessage("Server Version: " + Bukkit.getVersion());
|
||||
sender.sendMessage("NMS: " + ServerProject.getServerVersion() + " " + ServerVersion.getServerVersionString());
|
||||
sender.sendMessage("Operating System: " + System.getProperty("os.name"));
|
||||
sender.sendMessage("Allocated Memory: " + format.format(Runtime.getRuntime().maxMemory() / (1024 * 1024)) + "Mb");
|
||||
sender.sendMessage("Online Players: " + Bukkit.getOnlinePlayers().size());
|
||||
try {
|
||||
double[] tps = ((double[]) tpsField.get(serverInstance));
|
||||
if(tpsField != null) {
|
||||
try {
|
||||
double[] tps = ((double[]) tpsField.get(serverInstance));
|
||||
|
||||
sender.sendMessage("TPS from last 1m, 5m, 15m: " + format.format(tps[0]) + ", "
|
||||
+ format.format(tps[1]) + ", " + format.format(tps[2]));
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
sender.sendMessage("TPS from last 1m, 5m, 15m: " + format.format(tps[0]) + ", "
|
||||
+ format.format(tps[1]) + ", " + format.format(tps[2]));
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return ReturnType.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> onTab(SongodaUpdate instance, CommandSender sender, String... args) {
|
||||
protected List<String> onTab(CommandSender sender, String... args) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -82,12 +82,4 @@ public class CommandDiag extends AbstractCommand {
|
||||
public String getDescription() {
|
||||
return "Display diagnostics information.";
|
||||
}
|
||||
|
||||
private Class<?> getNMSClass(String className) {
|
||||
try {
|
||||
return Class.forName("net.minecraft.server." + version + "." + className);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
package com.songoda.core.core;
|
||||
|
||||
import com.songoda.core.SongodaCore;
|
||||
import com.songoda.core.compatibility.LegacyMaterials;
|
||||
import com.songoda.core.configuration.editor.PluginConfigGui;
|
||||
import com.songoda.core.gui.Gui;
|
||||
import com.songoda.core.gui.GuiUtils;
|
||||
import java.util.List;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
|
||||
final class SongodaCoreOverviewGUI extends Gui {
|
||||
|
||||
protected SongodaCoreOverviewGUI() {
|
||||
List<PluginInfo> plugins = SongodaCore.getPlugins();
|
||||
// could do pages, too, but don't think we'll have that many at a time for a while
|
||||
int max = (int) Math.ceil(plugins.size() / 9.);
|
||||
setRows(max);
|
||||
setTitle("Songoda Plugins");
|
||||
|
||||
// TODO: this could use some decorating
|
||||
|
||||
for (int i = 0; i < plugins.size(); i++) {
|
||||
final PluginInfo plugin = plugins.get(i);
|
||||
if (plugin.hasUpdate()) {
|
||||
setButton(i, GuiUtils.createButtonItem(plugin.icon != null ? plugin.icon : LegacyMaterials.STONE,
|
||||
ChatColor.GOLD + plugin.getJavaPlugin().getName(),
|
||||
ChatColor.GRAY + "Latest Version: " + plugin.getLatestVersion(),
|
||||
ChatColor.GRAY + "Installed Version: " + plugin.getJavaPlugin().getDescription().getVersion(),
|
||||
"",
|
||||
"Change log:",
|
||||
plugin.getChangeLog(),
|
||||
"",
|
||||
ChatColor.GOLD + "Click for the marketplace page link.",
|
||||
ChatColor.GOLD + "Right Click to edit plugin settings."
|
||||
),
|
||||
ClickType.LEFT, (event) -> event.player.sendMessage(plugin.getMarketplaceLink()));
|
||||
setAction(i, ClickType.RIGHT, (event) -> event.manager.showGUI(event.player, new PluginConfigGui(plugin.getJavaPlugin(), event.gui)));
|
||||
highlightItem(i);
|
||||
} else {
|
||||
setButton(i, GuiUtils.createButtonItem(plugin.icon != null ? plugin.icon : LegacyMaterials.STONE,
|
||||
ChatColor.GOLD + plugin.getJavaPlugin().getName(),
|
||||
ChatColor.GRAY + "Installed Version: " + plugin.getJavaPlugin().getDescription().getVersion(),
|
||||
"",
|
||||
ChatColor.GOLD + "Click for the marketplace page link.",
|
||||
ChatColor.GOLD + "Right Click to edit plugin settings."
|
||||
),
|
||||
ClickType.LEFT, (event) -> event.player.sendMessage(plugin.getMarketplaceLink()));
|
||||
setAction(i, ClickType.RIGHT, (event) -> event.manager.showGUI(event.player, new PluginConfigGui(plugin.getJavaPlugin(), event.gui)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package com.songoda.core.database;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
|
||||
public class DataManagerAbstract {
|
||||
|
||||
protected final DatabaseConnector databaseConnector;
|
||||
protected final Plugin plugin;
|
||||
|
||||
public DataManagerAbstract(DatabaseConnector databaseConnector, Plugin plugin) {
|
||||
this.databaseConnector = databaseConnector;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the prefix to be used by all table names
|
||||
*/
|
||||
public String getTablePrefix() {
|
||||
return this.plugin.getDescription().getName().toLowerCase() + '_';
|
||||
}
|
||||
|
||||
protected int lastInsertedId(Connection connection) {
|
||||
String query;
|
||||
if (this.databaseConnector instanceof SQLiteConnector) {
|
||||
query = "SELECT last_insert_rowid()";
|
||||
} else {
|
||||
query = "SELECT LAST_INSERT_ID()";
|
||||
}
|
||||
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
ResultSet result = statement.executeQuery(query);
|
||||
result.next();
|
||||
return result.getInt(1);
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public void async(Runnable runnable) {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(this.plugin, runnable);
|
||||
}
|
||||
|
||||
public void sync(Runnable runnable) {
|
||||
Bukkit.getScheduler().runTask(this.plugin, runnable);
|
||||
}
|
||||
|
||||
}
|
23
src/main/java/com/songoda/core/database/DataMigration.java
Normal file
23
src/main/java/com/songoda/core/database/DataMigration.java
Normal file
@ -0,0 +1,23 @@
|
||||
package com.songoda.core.database;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public abstract class DataMigration {
|
||||
|
||||
private final int revision;
|
||||
|
||||
public DataMigration(int revision) {
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
public abstract void migrate(Connection connection, String tablePrefix) throws SQLException;
|
||||
|
||||
/**
|
||||
* @return the revision number of this migration
|
||||
*/
|
||||
public int getRevision() {
|
||||
return this.revision;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
package com.songoda.core.database;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class DataMigrationManager {
|
||||
|
||||
private List<DataMigration> migrations;
|
||||
private DatabaseConnector databaseConnector;
|
||||
private DataManagerAbstract dataManagerAbstract;
|
||||
|
||||
public DataMigrationManager(DatabaseConnector databaseConnector, DataManagerAbstract dataManagerAbstract, DataMigration... migrations) {
|
||||
this.databaseConnector = databaseConnector;
|
||||
this.dataManagerAbstract = dataManagerAbstract;
|
||||
|
||||
this.migrations = Arrays.asList(migrations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs any needed data migrations
|
||||
*/
|
||||
public void runMigrations() {
|
||||
this.databaseConnector.connect((connection -> {
|
||||
int currentMigration = -1;
|
||||
boolean migrationsExist;
|
||||
|
||||
String query;
|
||||
if (this.databaseConnector instanceof SQLiteConnector) {
|
||||
query = "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?";
|
||||
} else {
|
||||
query = "SHOW TABLES LIKE ?";
|
||||
}
|
||||
|
||||
try (PreparedStatement statement = connection.prepareStatement(query)) {
|
||||
statement.setString(1, this.getMigrationsTableName());
|
||||
migrationsExist = statement.executeQuery().next();
|
||||
}
|
||||
|
||||
if (!migrationsExist) {
|
||||
// No migration table exists, create one
|
||||
String createTable = "CREATE TABLE " + this.getMigrationsTableName() + " (migration_version INT NOT NULL)";
|
||||
try (PreparedStatement statement = connection.prepareStatement(createTable)) {
|
||||
statement.execute();
|
||||
}
|
||||
|
||||
// Insert primary row into migration table
|
||||
String insertRow = "INSERT INTO " + this.getMigrationsTableName() + " VALUES (?)";
|
||||
try (PreparedStatement statement = connection.prepareStatement(insertRow)) {
|
||||
statement.setInt(1, -1);
|
||||
statement.execute();
|
||||
}
|
||||
} else {
|
||||
// Grab the current migration version
|
||||
String selectVersion = "SELECT migration_version FROM " + this.getMigrationsTableName();
|
||||
try (PreparedStatement statement = connection.prepareStatement(selectVersion)) {
|
||||
ResultSet result = statement.executeQuery();
|
||||
result.next();
|
||||
currentMigration = result.getInt("migration_version");
|
||||
}
|
||||
}
|
||||
|
||||
// Grab required migrations
|
||||
int finalCurrentMigration = currentMigration;
|
||||
List<DataMigration> requiredMigrations = this.migrations
|
||||
.stream()
|
||||
.filter(x -> x.getRevision() > finalCurrentMigration)
|
||||
.sorted(Comparator.comparingInt(DataMigration::getRevision))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Nothing to migrate, abort
|
||||
if (requiredMigrations.isEmpty())
|
||||
return;
|
||||
|
||||
// Migrate the data
|
||||
for (DataMigration dataMigration : requiredMigrations)
|
||||
dataMigration.migrate(connection, this.dataManagerAbstract.getTablePrefix());
|
||||
|
||||
// Set the new current migration to be the highest migrated to
|
||||
currentMigration = requiredMigrations
|
||||
.stream()
|
||||
.map(DataMigration::getRevision)
|
||||
.max(Integer::compareTo)
|
||||
.orElse(-1);
|
||||
|
||||
String updateVersion = "UPDATE " + this.getMigrationsTableName() + " SET migration_version = ?";
|
||||
try (PreparedStatement statement = connection.prepareStatement(updateVersion)) {
|
||||
statement.setInt(1, currentMigration);
|
||||
statement.execute();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the name of the migrations table
|
||||
*/
|
||||
private String getMigrationsTableName() {
|
||||
return this.dataManagerAbstract.getTablePrefix() + "migrations";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package com.songoda.core.database;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public interface DatabaseConnector {
|
||||
|
||||
/**
|
||||
* Checks if the connection to the database has been created
|
||||
*
|
||||
* @return true if the connection is created, otherwise false
|
||||
*/
|
||||
boolean isInitialized();
|
||||
|
||||
/**
|
||||
* Closes all open connections to the database
|
||||
*/
|
||||
void closeConnection();
|
||||
|
||||
/**
|
||||
* Executes a callback with a Connection passed and automatically closes it when finished
|
||||
*
|
||||
* @param callback The callback to execute once the connection is retrieved
|
||||
*/
|
||||
void connect(ConnectionCallback callback);
|
||||
|
||||
/**
|
||||
* Wraps a connection in a callback which will automagically handle catching sql errors
|
||||
*/
|
||||
interface ConnectionCallback {
|
||||
void accept(Connection connection) throws SQLException;
|
||||
}
|
||||
|
||||
}
|
50
src/main/java/com/songoda/core/database/MySQLConnector.java
Normal file
50
src/main/java/com/songoda/core/database/MySQLConnector.java
Normal file
@ -0,0 +1,50 @@
|
||||
package com.songoda.core.database;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class MySQLConnector implements DatabaseConnector {
|
||||
|
||||
private final Plugin plugin;
|
||||
private HikariDataSource hikari;
|
||||
private boolean initializedSuccessfully;
|
||||
|
||||
public MySQLConnector(Plugin plugin, String hostname, int port, String database, String username, String password, boolean useSSL) {
|
||||
this.plugin = plugin;
|
||||
|
||||
HikariConfig config = new HikariConfig();
|
||||
config.setJdbcUrl("jdbc:mysql://" + hostname + ":" + port + "/" + database + "?useSSL=" + useSSL);
|
||||
config.setUsername(username);
|
||||
config.setPassword(password);
|
||||
config.setMaximumPoolSize(3);
|
||||
|
||||
try {
|
||||
this.hikari = new HikariDataSource(config);
|
||||
this.initializedSuccessfully = true;
|
||||
} catch (Exception ex) {
|
||||
this.initializedSuccessfully = false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isInitialized() {
|
||||
return this.initializedSuccessfully;
|
||||
}
|
||||
|
||||
public void closeConnection() {
|
||||
this.hikari.close();
|
||||
}
|
||||
|
||||
public void connect(ConnectionCallback callback) {
|
||||
try (Connection connection = this.hikari.getConnection()) {
|
||||
callback.accept(connection);
|
||||
} catch (SQLException ex) {
|
||||
this.plugin.getLogger().severe("An error occurred executing a MySQL query: " + ex.getMessage());
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
58
src/main/java/com/songoda/core/database/SQLiteConnector.java
Normal file
58
src/main/java/com/songoda/core/database/SQLiteConnector.java
Normal file
@ -0,0 +1,58 @@
|
||||
package com.songoda.core.database;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class SQLiteConnector implements DatabaseConnector {
|
||||
|
||||
private final Plugin plugin;
|
||||
private final String connectionString;
|
||||
private Connection connection;
|
||||
|
||||
public SQLiteConnector(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.connectionString = "jdbc:sqlite:" + plugin.getDataFolder() + File.separator + plugin.getDescription().getName().toLowerCase() + ".db";
|
||||
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC"); // This is required to put here for Spigot 1.10 and below for some reason
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isInitialized() {
|
||||
return true; // Always available
|
||||
}
|
||||
|
||||
public void closeConnection() {
|
||||
try {
|
||||
if (this.connection != null) {
|
||||
this.connection.close();
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
this.plugin.getLogger().severe("An error occurred closing the SQLite database connection: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void connect(ConnectionCallback callback) {
|
||||
if (this.connection == null) {
|
||||
try {
|
||||
this.connection = DriverManager.getConnection(this.connectionString);
|
||||
} catch (SQLException ex) {
|
||||
this.plugin.getLogger().severe("An error occurred retrieving the SQLite database connection: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
callback.accept(this.connection);
|
||||
} catch (Exception ex) {
|
||||
this.plugin.getLogger().severe("An error occurred executing an SQLite query: " + ex.getMessage());
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
14
src/main/java/com/songoda/core/gui/BackgroundType.java
Normal file
14
src/main/java/com/songoda/core/gui/BackgroundType.java
Normal file
@ -0,0 +1,14 @@
|
||||
package com.songoda.core.gui;
|
||||
|
||||
/**
|
||||
* Background images available for use in toast messages
|
||||
*/
|
||||
public enum BackgroundType {
|
||||
ADVENTURE, END, HUSBANDRY, NETHER, STONE;
|
||||
final String key;
|
||||
|
||||
private BackgroundType() {
|
||||
this.key = "minecraft:textures/gui/advancements/backgrounds/" + name().toLowerCase() + ".png";
|
||||
}
|
||||
|
||||
}
|
509
src/main/java/com/songoda/core/gui/DoubleGui.java
Normal file
509
src/main/java/com/songoda/core/gui/DoubleGui.java
Normal file
@ -0,0 +1,509 @@
|
||||
package com.songoda.core.gui;
|
||||
|
||||
import com.songoda.core.compatibility.LegacyMaterials;
|
||||
import com.songoda.core.gui.events.GuiClickEvent;
|
||||
import com.songoda.core.gui.events.GuiDropItemEvent;
|
||||
import com.songoda.core.gui.methods.Clickable;
|
||||
import com.songoda.core.gui.methods.Closable;
|
||||
import com.songoda.core.gui.methods.Droppable;
|
||||
import com.songoda.core.gui.methods.Openable;
|
||||
import com.songoda.core.gui.methods.Pagable;
|
||||
import com.songoda.core.utils.ItemUtils;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.bukkit.entity.HumanEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
/**
|
||||
* TODO: does not restore inventory if server crashes while player inventory is open
|
||||
* Method to fix: save inv + ender slot to file, store paper in ender inv with name of cache file, check for paper item in slot when loading
|
||||
* Or just manually manage all inventories in a file and remove when restored
|
||||
*
|
||||
* @since 2019-08-25
|
||||
* @author jascotty2
|
||||
*/
|
||||
public class DoubleGui extends Gui {
|
||||
|
||||
protected int playerRows = 4;
|
||||
protected Map<Player, ItemStack[]> stash = new HashMap();
|
||||
|
||||
public DoubleGui(GuiType type) {
|
||||
super(type);
|
||||
allowDropItems = false;
|
||||
}
|
||||
|
||||
public DoubleGui(int rows) {
|
||||
super(rows);
|
||||
allowDropItems = false;
|
||||
}
|
||||
|
||||
public DoubleGui(int rows, Gui parent) {
|
||||
super(rows, parent);
|
||||
allowDropItems = false;
|
||||
}
|
||||
|
||||
public int getPlayerRows() {
|
||||
return playerRows;
|
||||
}
|
||||
|
||||
// 9 -> 0 -> 54
|
||||
// 18 -> 9 -> 63
|
||||
// 27 -> 18 -> 72
|
||||
// 0 -> 27 -> 81
|
||||
// offset required to make click translations
|
||||
int clickOffset(int cell) {
|
||||
return 54 + (cell < 9 ? cell + 27 : cell - 9);
|
||||
}
|
||||
|
||||
// offset required to make inventory translations
|
||||
int invOffset(int cell) {
|
||||
return 54 + cell;
|
||||
}
|
||||
|
||||
public DoubleGui setPlayerUnlocked(int cell) {
|
||||
unlockedCells.put(invOffset(cell), true);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DoubleGui setPlayerUnlocked(int row, int col) {
|
||||
unlockedCells.put(invOffset(col + row * 9), true);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DoubleGui setPlayerUnlocked(int cell, boolean open) {
|
||||
unlockedCells.put(invOffset(cell), open);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DoubleGui setPlayerUnlocked(int row, int col, boolean open) {
|
||||
unlockedCells.put(invOffset(col + row * 9), open);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DoubleGui setPlayerItem(int cell, ItemStack item) {
|
||||
cellItems.put(invOffset(cell), item);
|
||||
if (open && cell >= 0 && cell < 36) {
|
||||
cell = cell >= 27 ? cell - 27 : cell + 9;
|
||||
for (HumanEntity e : inventory.getViewers()) {
|
||||
e.getInventory().setItem(cell, item);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public DoubleGui setPlayerItem(int row, int col, ItemStack item) {
|
||||
int cell = col + row * 9;
|
||||
cellItems.put(invOffset(cell), item);
|
||||
if (open && cell >= 0 && cell < 36) {
|
||||
cell = cell >= 27 ? cell - 27 : cell + 9;
|
||||
for (HumanEntity e : inventory.getViewers()) {
|
||||
e.getInventory().setItem(cell, item);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public DoubleGui highlightPlayerItem(int cell) {
|
||||
final int invCell = invOffset(cell);
|
||||
ItemStack item = cellItems.get(invCell);
|
||||
if (item != null) {
|
||||
setPlayerItem(cell, ItemUtils.addGlow(item));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public DoubleGui highlightPlayerItem(int row, int col) {
|
||||
final int cell = col + row * 9;
|
||||
final int invCell = invOffset(cell);
|
||||
ItemStack item = cellItems.get(invCell);
|
||||
if (item != null) {
|
||||
setPlayerItem(cell, ItemUtils.addGlow(item));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public DoubleGui setPlayerAction(int cell, Clickable action) {
|
||||
setConditional(invOffset(cell), null, action);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DoubleGui setPlayerAction(int row, int col, Clickable action) {
|
||||
setConditional(invOffset(col + row * 9), null, action);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DoubleGui setPlayerAction(int cell, ClickType type, Clickable action) {
|
||||
setConditional(invOffset(cell), type, action);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DoubleGui setPlayerAction(int row, int col, ClickType type, Clickable action) {
|
||||
setConditional(invOffset(col + row * 9), type, action);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DoubleGui setPlayerActionForRange(int cellFirst, int cellLast, Clickable action) {
|
||||
for (int cell = cellFirst; cell <= cellLast; ++cell) {
|
||||
setConditional(invOffset(cell), null, action);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public DoubleGui setPlayerActionForRange(int cellRowFirst, int cellColFirst, int cellRowLast, int cellColLast, Clickable action) {
|
||||
final int last = cellColLast + cellRowLast * 9;
|
||||
for (int cell = cellColFirst + cellRowFirst * 9; cell <= last; ++cell) {
|
||||
setConditional(invOffset(cell), null, action);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public DoubleGui setPlayerActionForRange(int cellFirst, int cellLast, ClickType type, Clickable action) {
|
||||
for (int cell = cellFirst; cell <= cellLast; ++cell) {
|
||||
setConditional(invOffset(cell), type, action);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public DoubleGui setPlayerActionForRange(int cellRowFirst, int cellColFirst, int cellRowLast, int cellColLast, ClickType type, Clickable action) {
|
||||
final int last = cellColLast + cellRowLast * 9;
|
||||
for (int cell = cellColFirst + cellRowFirst * 9; cell <= last; ++cell) {
|
||||
setConditional(invOffset(cell), type, action);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public DoubleGui clearPlayerActions(int cell) {
|
||||
conditionalButtons.remove(cell = invOffset(cell));
|
||||
return this;
|
||||
}
|
||||
|
||||
public DoubleGui clearPlayerActions(int row, int col) {
|
||||
final int cell = invOffset(col + row * 9);
|
||||
conditionalButtons.remove(cell);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DoubleGui setPlayerButton(int cell, ItemStack item, Clickable action) {
|
||||
setPlayerItem(cell, item);
|
||||
setConditional(invOffset(cell), null, action);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DoubleGui setPlayerButton(int row, int col, ItemStack item, Clickable action) {
|
||||
final int cell = col + row * 9;
|
||||
setPlayerItem(cell, item);
|
||||
setConditional(invOffset(cell), null, action);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DoubleGui setPlayerButton(int cell, ItemStack item, ClickType type, Clickable action) {
|
||||
setItem(cell, item);
|
||||
setConditional(invOffset(cell), type, action);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DoubleGui setPlayerButton(int row, int col, ItemStack item, ClickType type, Clickable action) {
|
||||
final int cell = col + row * 9;
|
||||
setPlayerItem(cell, item);
|
||||
setConditional(invOffset(cell), type, action);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onClickPlayerInventory(GuiManager manager, Player player, Inventory openInv, InventoryClickEvent event) {
|
||||
final int cell = event.getSlot(), offsetCell = clickOffset(cell);
|
||||
Map<ClickType, Clickable> conditionals = conditionalButtons.get(offsetCell);
|
||||
Clickable button;
|
||||
if (conditionals != null
|
||||
&& ((button = conditionals.get(event.getClick())) != null || (button = conditionals.get(null)) != null)) {
|
||||
button.onClick(new GuiClickEvent(manager, this, player, event, cell, true));
|
||||
} else {
|
||||
// no event for this button
|
||||
return false;
|
||||
}
|
||||
event.setCancelled(!unlockedCells.entrySet().stream().anyMatch(e -> offsetCell == e.getKey() && e.getValue()));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onClickOutside(GuiManager manager, Player player, InventoryClickEvent event) {
|
||||
if (dropper != null) {
|
||||
return dropper.onDrop(new GuiDropItemEvent(manager, this, player, event));
|
||||
}
|
||||
// do not allow by default
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(GuiManager manager, Player player) {
|
||||
// replace the player's inventory
|
||||
ItemStack[] oldInv = player.getInventory().getContents();
|
||||
ItemStack[] newInv = new ItemStack[oldInv.length];
|
||||
|
||||
for (int i = 0; i < 36; ++i) {
|
||||
final ItemStack item = cellItems.get(invOffset(i < 9 ? i + 27 : i - 9));
|
||||
newInv[i] = item != null ? item : blankItem;
|
||||
}
|
||||
|
||||
stash.put(player, oldInv);
|
||||
player.getInventory().setContents(newInv);
|
||||
// other opening functions
|
||||
super.onOpen(manager, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(GuiManager manager, Player player) {
|
||||
// restore the player's inventory
|
||||
if (stash.containsKey(player)) {
|
||||
player.getInventory().setContents(stash.remove(player));
|
||||
player.updateInventory();
|
||||
}
|
||||
// other closing functions
|
||||
super.onClose(manager, player);
|
||||
}
|
||||
|
||||
/*
|
||||
*********************************************************
|
||||
* Other functions from GUI that we don't actually override
|
||||
*********************************************************
|
||||
*/
|
||||
@Override
|
||||
public DoubleGui setAcceptsItems(boolean acceptsItems) {
|
||||
return (DoubleGui) super.setAcceptsItems(acceptsItems);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setUnlocked(int cell) {
|
||||
return (DoubleGui) super.setUnlocked(cell);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setUnlocked(int cell, boolean open) {
|
||||
return (DoubleGui) super.setUnlocked(cell, open);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setUnlocked(int row, int col) {
|
||||
return (DoubleGui) super.setUnlocked(row, col);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setUnlocked(int row, int col, boolean open) {
|
||||
return (DoubleGui) super.setUnlocked(row, col, open);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setUnlockedRange(int cellFirst, int cellLast) {
|
||||
return (DoubleGui) super.setUnlockedRange(cellFirst, cellLast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setUnlockedRange(int cellRowFirst, int cellColFirst, int cellRowLast, int cellColLast) {
|
||||
return (DoubleGui) super.setUnlockedRange(cellRowFirst, cellColFirst, cellRowLast, cellColLast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setAllowDrops(boolean allow) {
|
||||
return (DoubleGui) super.setAllowDrops(allow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setAllowClose(boolean allow) {
|
||||
return (DoubleGui) super.setAllowClose(allow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setTitle(String title) {
|
||||
return (DoubleGui) super.setTitle(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setRows(int rows) {
|
||||
return (DoubleGui) super.setRows(rows);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setDefaultItem(ItemStack item) {
|
||||
return (DoubleGui) super.setDefaultItem(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setItem(int cell, ItemStack item) {
|
||||
return (DoubleGui) super.setItem(cell, item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setItem(int row, int col, ItemStack item) {
|
||||
return (DoubleGui) super.setItem(row, col, item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui highlightItem(int cell) {
|
||||
return (DoubleGui) super.highlightItem(cell);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui highlightItem(int row, int col) {
|
||||
return (DoubleGui) super.highlightItem(row, col);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui updateItem(int cell, String name, String... lore) {
|
||||
return (DoubleGui) super.updateItem(cell, name, lore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui updateItem(int row, int col, String name, List<String> lore) {
|
||||
return (DoubleGui) super.updateItem(col + row * 9, name, lore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui updateItem(int cell, String name, List<String> lore) {
|
||||
return (DoubleGui) super.updateItem(cell, name, lore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui updateItem(int row, int col, ItemStack itemTo, String title, String... lore) {
|
||||
return (DoubleGui) super.updateItem(col + row * 9, itemTo, title, lore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui updateItem(int cell, ItemStack itemTo, String title, String... lore) {
|
||||
return (DoubleGui) super.updateItem(cell, itemTo, title, lore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui updateItem(int row, int col, LegacyMaterials itemTo, String title, String... lore) {
|
||||
return (DoubleGui) super.updateItem(col + row * 9, itemTo, title, lore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui updateItem(int cell, LegacyMaterials itemTo, String title, String... lore) {
|
||||
return (DoubleGui) super.updateItem(cell, itemTo, title, lore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui updateItem(int row, int col, ItemStack itemTo, String title, List<String> lore) {
|
||||
return (DoubleGui) super.updateItem(col + row * 9, itemTo, title, lore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui updateItem(int cell, ItemStack itemTo, String title, List<String> lore) {
|
||||
return (DoubleGui) super.updateItem(cell, itemTo, title, lore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui updateItem(int row, int col, LegacyMaterials itemTo, String title, List<String> lore) {
|
||||
return (DoubleGui) super.updateItem(col + row * 9, itemTo, title, lore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui updateItem(int cell, LegacyMaterials itemTo, String title, List<String> lore) {
|
||||
return (DoubleGui) super.updateItem(cell, itemTo, title, lore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setAction(int cell, Clickable action) {
|
||||
return (DoubleGui) super.setAction(cell, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setAction(int row, int col, Clickable action) {
|
||||
return (DoubleGui) super.setAction(row, col, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setAction(int cell, ClickType type, Clickable action) {
|
||||
return (DoubleGui) super.setAction(cell, type, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setAction(int row, int col, ClickType type, Clickable action) {
|
||||
return (DoubleGui) super.setAction(row, col, type, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setActionForRange(int cellFirst, int cellLast, Clickable action) {
|
||||
return (DoubleGui) super.setActionForRange(cellFirst, cellLast, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setActionForRange(int cellRowFirst, int cellColFirst, int cellRowLast, int cellColLast, Clickable action) {
|
||||
return (DoubleGui) super.setActionForRange(cellRowFirst, cellColFirst, cellRowLast, cellColLast, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setActionForRange(int cellFirst, int cellLast, ClickType type, Clickable action) {
|
||||
return (DoubleGui) super.setActionForRange(cellFirst, cellLast, type, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setActionForRange(int cellRowFirst, int cellColFirst, int cellRowLast, int cellColLast, ClickType type, Clickable action) {
|
||||
return (DoubleGui) super.setActionForRange(cellRowFirst, cellColFirst, cellRowLast, cellColLast, type, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui clearActions(int cell) {
|
||||
return (DoubleGui) super.clearActions(cell);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui clearActions(int row, int col) {
|
||||
return (DoubleGui) super.clearActions(row, col);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setButton(int cell, ItemStack item, Clickable action) {
|
||||
return (DoubleGui) super.setButton(cell, item, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setButton(int row, int col, ItemStack item, Clickable action) {
|
||||
return (DoubleGui) super.setButton(row, col, item, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setButton(int cell, ItemStack item, ClickType type, Clickable action) {
|
||||
return (DoubleGui) super.setButton(cell, item, type, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setButton(int row, int col, ItemStack item, ClickType type, Clickable action) {
|
||||
return (DoubleGui) super.setButton(row, col, item, type, action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setOnOpen(Openable action) {
|
||||
return (DoubleGui) super.setOnOpen(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setOnClose(Closable action) {
|
||||
return (DoubleGui) super.setOnClose(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setOnDrop(Droppable action) {
|
||||
return (DoubleGui) super.setOnDrop(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setOnPage(Pagable action) {
|
||||
return (DoubleGui) super.setOnPage(action);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setNextPage(int row, int col, ItemStack item) {
|
||||
return (DoubleGui) super.setNextPage(row, col, item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleGui setPrevPage(int row, int col, ItemStack item) {
|
||||
return (DoubleGui) super.setPrevPage(row, col, item);
|
||||
}
|
||||
}
|
620
src/main/java/com/songoda/core/gui/Gui.java
Normal file
620
src/main/java/com/songoda/core/gui/Gui.java
Normal file
@ -0,0 +1,620 @@
|
||||
package com.songoda.core.gui;
|
||||
|
||||
import com.songoda.core.compatibility.LegacyMaterials;
|
||||
import com.songoda.core.gui.events.GuiClickEvent;
|
||||
import com.songoda.core.gui.events.GuiCloseEvent;
|
||||
import com.songoda.core.gui.events.GuiDropItemEvent;
|
||||
import com.songoda.core.gui.events.GuiOpenEvent;
|
||||
import com.songoda.core.gui.methods.Pagable;
|
||||
import com.songoda.core.gui.methods.Clickable;
|
||||
import com.songoda.core.gui.methods.Droppable;
|
||||
import com.songoda.core.gui.methods.Closable;
|
||||
import com.songoda.core.gui.methods.Openable;
|
||||
import com.songoda.core.utils.ItemUtils;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
/**
|
||||
* TODO: animated buttons
|
||||
*
|
||||
* @since 2019-08-25
|
||||
* @author jascotty2
|
||||
*/
|
||||
public class Gui {
|
||||
|
||||
protected Inventory inventory;
|
||||
protected String title;
|
||||
protected GuiType inventoryType = GuiType.STANDARD;
|
||||
protected int rows, page, pages;
|
||||
protected boolean acceptsItems = false;
|
||||
protected boolean allowDropItems = true;
|
||||
protected boolean allowClose = true;
|
||||
protected final Map<Integer, Boolean> unlockedCells = new HashMap<>();
|
||||
protected final Map<Integer, ItemStack> cellItems = new HashMap<>();
|
||||
protected final Map<Integer, Map<ClickType, Clickable>> conditionalButtons = new HashMap<>();
|
||||
protected ItemStack blankItem = GuiUtils.getBorderGlassItem();
|
||||
protected int nextPageIndex, prevPageIndex;
|
||||
protected ItemStack nextPage, prevPage;
|
||||
protected Gui parent = null;
|
||||
protected static ItemStack AIR = new ItemStack(Material.AIR);
|
||||
|
||||
protected boolean open = false;
|
||||
protected Openable opener = null;
|
||||
protected Closable closer = null;
|
||||
protected Droppable dropper = null;
|
||||
protected Pagable pager = null;
|
||||
|
||||
public Gui() {
|
||||
this.rows = 3;
|
||||
}
|
||||
|
||||
public Gui(GuiType type) {
|
||||
this.inventoryType = type;
|
||||
switch (type) {
|
||||
case HOPPER:
|
||||
case DISPENSER:
|
||||
this.rows = 1;
|
||||
break;
|
||||
default:
|
||||
this.rows = 3;
|
||||
}
|
||||
}
|
||||
|
||||
public Gui(Gui parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public Gui(int rows) {
|
||||
this.rows = Math.max(1, Math.min(6, rows));
|
||||
}
|
||||
|
||||
public Gui(int rows, Gui parent) {
|
||||
this.parent = parent;
|
||||
this.rows = Math.max(1, Math.min(6, rows));
|
||||
}
|
||||
|
||||
public List<Player> getPlayers() {
|
||||
return inventory == null ? Collections.EMPTY_LIST
|
||||
: inventory.getViewers().stream()
|
||||
.filter(e -> e instanceof Player)
|
||||
.map(e -> (Player) e)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public boolean isOpen() {
|
||||
// double check
|
||||
if (inventory != null && inventory.getViewers().isEmpty()) {
|
||||
open = false;
|
||||
}
|
||||
return open;
|
||||
}
|
||||
|
||||
public boolean getAcceptsItems() {
|
||||
return acceptsItems;
|
||||
}
|
||||
|
||||
public Gui setAcceptsItems(boolean acceptsItems) {
|
||||
this.acceptsItems = acceptsItems;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is true, then items in the player's cursor when the GUI is closed
|
||||
* will be cleared
|
||||
*/
|
||||
public boolean getAllowDrops() {
|
||||
return allowDropItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if items in the player's cursor will be cleared when the GUI is
|
||||
* closed
|
||||
*/
|
||||
public Gui setAllowDrops(boolean allow) {
|
||||
this.allowDropItems = allow;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean getAllowClose() {
|
||||
return allowClose;
|
||||
}
|
||||
|
||||
public Gui setAllowClose(boolean allow) {
|
||||
this.allowClose = allow;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the GUI without calling onClose() and without opening any parent GUIs
|
||||
*/
|
||||
public void exit() {
|
||||
allowClose = true;
|
||||
open = false;
|
||||
inventory.getViewers().stream()
|
||||
.filter(e -> e instanceof Player)
|
||||
.map(e -> (Player) e)
|
||||
.collect(Collectors.toList())
|
||||
.forEach(Player::closeInventory);
|
||||
}
|
||||
|
||||
public GuiType getType() {
|
||||
return inventoryType;
|
||||
}
|
||||
|
||||
public Gui setUnlocked(int cell) {
|
||||
unlockedCells.put(cell, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setUnlocked(int row, int col) {
|
||||
final int cell = col + row * 9;
|
||||
unlockedCells.put(cell, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setUnlockedRange(int cellFirst, int cellLast) {
|
||||
for (int cell = cellFirst; cell <= cellLast; ++cell) {
|
||||
unlockedCells.put(cell, true);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setUnlockedRange(int cellRowFirst, int cellColFirst, int cellRowLast, int cellColLast) {
|
||||
final int last = cellColLast + cellRowLast * 9;
|
||||
for (int cell = cellColFirst + cellRowFirst * 9; cell <= last; ++cell) {
|
||||
unlockedCells.put(cell, true);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setUnlocked(int cell, boolean open) {
|
||||
unlockedCells.put(cell, open);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setUnlocked(int row, int col, boolean open) {
|
||||
final int cell = col + row * 9;
|
||||
unlockedCells.put(cell, open);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setTitle(String title) {
|
||||
this.title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getRows() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
public Gui setRows(int rows) {
|
||||
switch (inventoryType) {
|
||||
case HOPPER:
|
||||
case DISPENSER:
|
||||
break;
|
||||
default:
|
||||
this.rows = Math.max(1, Math.min(6, rows));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setDefaultItem(ItemStack item) {
|
||||
blankItem = item;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemStack getDefaultItem() {
|
||||
return blankItem;
|
||||
}
|
||||
|
||||
public Gui setItem(int cell, ItemStack item) {
|
||||
cellItems.put(cell, item);
|
||||
if (open && cell >= 0 && cell < inventory.getSize()) {
|
||||
inventory.setItem(cell, item);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setItem(int row, int col, ItemStack item) {
|
||||
final int cell = col + row * 9;
|
||||
cellItems.put(cell, item);
|
||||
if (open && cell >= 0 && cell < inventory.getSize()) {
|
||||
inventory.setItem(cell, item);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui highlightItem(int cell) {
|
||||
ItemStack item = cellItems.get(cell);
|
||||
if (item != null && item.getType() != Material.AIR) {
|
||||
setItem(cell, ItemUtils.addGlow(item));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui highlightItem(int row, int col) {
|
||||
final int cell = col + row * 9;
|
||||
ItemStack item = cellItems.get(cell);
|
||||
if (item != null && item.getType() != Material.AIR) {
|
||||
setItem(cell, ItemUtils.addGlow(item));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui removeHighlight(int cell) {
|
||||
ItemStack item = cellItems.get(cell);
|
||||
if (item != null && item.getType() != Material.AIR) {
|
||||
setItem(cell, ItemUtils.removeGlow(item));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui removeHighlight(int row, int col) {
|
||||
final int cell = col + row * 9;
|
||||
ItemStack item = cellItems.get(cell);
|
||||
if (item != null && item.getType() != Material.AIR) {
|
||||
setItem(cell, ItemUtils.removeGlow(item));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui updateItem(int row, int col, String name, String... lore) {
|
||||
return updateItem(col + row * 9, name, lore);
|
||||
}
|
||||
|
||||
public Gui updateItem(int cell, String name, String... lore) {
|
||||
ItemStack item = cellItems.get(cell);
|
||||
if (item != null && item.getType() != Material.AIR) {
|
||||
setItem(cell, GuiUtils.updateItem(item, title, lore));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui updateItem(int row, int col, String name, List<String> lore) {
|
||||
return updateItem(col + row * 9, name, lore);
|
||||
}
|
||||
|
||||
public Gui updateItem(int cell, String name, List<String> lore) {
|
||||
ItemStack item = cellItems.get(cell);
|
||||
if (item != null && item.getType() != Material.AIR) {
|
||||
setItem(cell, GuiUtils.updateItem(item, title, lore));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui updateItem(int row, int col, ItemStack itemTo, String title, String... lore) {
|
||||
return updateItem(col + row * 9, itemTo, title, lore);
|
||||
}
|
||||
|
||||
public Gui updateItem(int cell, ItemStack itemTo, String title, String... lore) {
|
||||
ItemStack item = cellItems.get(cell);
|
||||
if (item != null && item.getType() != Material.AIR) {
|
||||
setItem(cell, GuiUtils.updateItem(item, itemTo, title, lore));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui updateItem(int row, int col, LegacyMaterials itemTo, String title, String... lore) {
|
||||
return updateItem(col + row * 9, itemTo, title, lore);
|
||||
}
|
||||
|
||||
public Gui updateItem(int cell, LegacyMaterials itemTo, String title, String... lore) {
|
||||
ItemStack item = cellItems.get(cell);
|
||||
if (item != null && item.getType() != Material.AIR) {
|
||||
setItem(cell, GuiUtils.updateItem(item, itemTo, title, lore));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui updateItem(int row, int col, ItemStack itemTo, String title, List<String> lore) {
|
||||
return updateItem(col + row * 9, itemTo, title, lore);
|
||||
}
|
||||
|
||||
public Gui updateItem(int cell, ItemStack itemTo, String title, List<String> lore) {
|
||||
ItemStack item = cellItems.get(cell);
|
||||
if (item != null && item.getType() != Material.AIR) {
|
||||
setItem(cell, GuiUtils.updateItem(item, itemTo, title, lore));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui updateItem(int row, int col, LegacyMaterials itemTo, String title, List<String> lore) {
|
||||
return updateItem(col + row * 9, itemTo, title, lore);
|
||||
}
|
||||
|
||||
public Gui updateItem(int cell, LegacyMaterials itemTo, String title, List<String> lore) {
|
||||
ItemStack item = cellItems.get(cell);
|
||||
if (item != null && item.getType() != Material.AIR) {
|
||||
setItem(cell, GuiUtils.updateItem(item, itemTo, title, lore));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setAction(int cell, Clickable action) {
|
||||
setConditional(cell, null, action);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setAction(int row, int col, Clickable action) {
|
||||
setConditional(col + row * 9, null, action);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setAction(int cell, ClickType type, Clickable action) {
|
||||
setConditional(cell, type, action);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setAction(int row, int col, ClickType type, Clickable action) {
|
||||
setConditional(col + row * 9, type, action);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setActionForRange(int cellFirst, int cellLast, Clickable action) {
|
||||
for (int cell = cellFirst; cell <= cellLast; ++cell) {
|
||||
setConditional(cell, null, action);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setActionForRange(int cellRowFirst, int cellColFirst, int cellRowLast, int cellColLast, Clickable action) {
|
||||
final int last = cellColLast + cellRowLast * 9;
|
||||
for (int cell = cellColFirst + cellRowFirst * 9; cell <= last; ++cell) {
|
||||
setConditional(cell, null, action);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setActionForRange(int cellFirst, int cellLast, ClickType type, Clickable action) {
|
||||
for (int cell = cellFirst; cell <= cellLast; ++cell) {
|
||||
setConditional(cell, type, action);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setActionForRange(int cellRowFirst, int cellColFirst, int cellRowLast, int cellColLast, ClickType type, Clickable action) {
|
||||
final int last = cellColLast + cellRowLast * 9;
|
||||
for (int cell = cellColFirst + cellRowFirst * 9; cell <= last; ++cell) {
|
||||
setConditional(cell, type, action);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui clearActions(int cell) {
|
||||
conditionalButtons.remove(cell);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui clearActions(int row, int col) {
|
||||
final int cell = col + row * 9;
|
||||
conditionalButtons.remove(cell);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setButton(int cell, ItemStack item, Clickable action) {
|
||||
setItem(cell, item);
|
||||
setConditional(cell, null, action);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setButton(int row, int col, ItemStack item, Clickable action) {
|
||||
final int cell = col + row * 9;
|
||||
setItem(cell, item);
|
||||
setConditional(cell, null, action);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setButton(int cell, ItemStack item, ClickType type, Clickable action) {
|
||||
setItem(cell, item);
|
||||
setConditional(cell, type, action);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setButton(int row, int col, ItemStack item, ClickType type, Clickable action) {
|
||||
final int cell = col + row * 9;
|
||||
setItem(cell, item);
|
||||
setConditional(cell, type, action);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected void setConditional(int cell, ClickType type, Clickable action) {
|
||||
Map<ClickType, Clickable> conditionals = conditionalButtons.get(cell);
|
||||
if (action != null) {
|
||||
if (conditionals == null) {
|
||||
conditionalButtons.put(cell, conditionals = new HashMap());
|
||||
}
|
||||
conditionals.put(type, action);
|
||||
}
|
||||
}
|
||||
|
||||
public Gui setOnOpen(Openable action) {
|
||||
opener = action;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setOnClose(Closable action) {
|
||||
closer = action;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setOnDrop(Droppable action) {
|
||||
dropper = action;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setOnPage(Pagable action) {
|
||||
pager = action;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setNextPage(int row, int col, ItemStack item) {
|
||||
nextPageIndex = col + row * 9;
|
||||
if (page < pages) {
|
||||
setButton(nextPageIndex, item, ClickType.LEFT, (event) -> this.nextPage());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Gui setPrevPage(int row, int col, ItemStack item) {
|
||||
prevPageIndex = col + row * 9;
|
||||
if (page > 1) {
|
||||
setButton(prevPageIndex, item, ClickType.LEFT, (event) -> this.prevPage());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void nextPage() {
|
||||
if (page < pages) {
|
||||
int lastPage = page;
|
||||
++page;
|
||||
// page switch events
|
||||
if (pager != null) {
|
||||
pager.onPageChange(this, lastPage, page);
|
||||
|
||||
// page markers
|
||||
updatePageNavigation();
|
||||
|
||||
// push new inventory to the view inventory
|
||||
// shouldn't be needed since adding inventory update to setItem
|
||||
//update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void prevPage() {
|
||||
if (page > 1) {
|
||||
int lastPage = page;
|
||||
--page;
|
||||
if (pager != null) {
|
||||
pager.onPageChange(this, lastPage, page);
|
||||
|
||||
// page markers
|
||||
updatePageNavigation();
|
||||
|
||||
// push new inventory to the view inventory
|
||||
// shouldn't be needed since adding inventory update to setItem
|
||||
//update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void updatePageNavigation() {
|
||||
if (page > 1) {
|
||||
this.setButton(prevPageIndex, prevPage, ClickType.LEFT, (event) -> this.prevPage());
|
||||
} else {
|
||||
this.setItem(prevPageIndex, null);
|
||||
this.clearActions(prevPageIndex);
|
||||
}
|
||||
if (pages > 1 && page != pages) {
|
||||
this.setButton(nextPageIndex, nextPage, ClickType.LEFT, (event) -> this.nextPage());
|
||||
} else {
|
||||
this.setItem(nextPageIndex, null);
|
||||
this.clearActions(nextPageIndex);
|
||||
}
|
||||
}
|
||||
|
||||
protected Inventory getOrCreateInventory(GuiManager manager) {
|
||||
return inventory != null ? inventory : generateInventory(manager);
|
||||
}
|
||||
|
||||
protected Inventory generateInventory(GuiManager manager) {
|
||||
final int cells = rows * 9;
|
||||
InventoryType t = inventoryType == null ? InventoryType.CHEST : inventoryType.type;
|
||||
switch (t) {
|
||||
case DISPENSER:
|
||||
case HOPPER:
|
||||
inventory = Bukkit.getServer().createInventory(new GuiHolder(manager, this), t,
|
||||
title == null ? "" : trimTitle(ChatColor.translateAlternateColorCodes('&', title)));
|
||||
break;
|
||||
default:
|
||||
inventory = Bukkit.getServer().createInventory(new GuiHolder(manager, this), cells,
|
||||
title == null ? "" : trimTitle(ChatColor.translateAlternateColorCodes('&', title)));
|
||||
}
|
||||
|
||||
for (int i = 0; i < cells; ++i) {
|
||||
final ItemStack item = cellItems.get(i);
|
||||
inventory.setItem(i, item != null ? item : blankItem);
|
||||
}
|
||||
|
||||
return inventory;
|
||||
}
|
||||
|
||||
public Gui getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
if (inventory == null) {
|
||||
return;
|
||||
}
|
||||
final int cells = rows * 9;
|
||||
for (int i = 0; i < cells; ++i) {
|
||||
final ItemStack item = cellItems.get(i);
|
||||
inventory.setItem(i, item != null ? item : blankItem);
|
||||
}
|
||||
}
|
||||
|
||||
protected static String trimTitle(String title) {
|
||||
if (title != null && title.length() > 32) {
|
||||
return title.substring(0, 31);
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
protected boolean onClickOutside(GuiManager manager, Player player, InventoryClickEvent event) {
|
||||
return dropper != null ? dropper.onDrop(new GuiDropItemEvent(manager, this, player, event)) : true;
|
||||
}
|
||||
|
||||
protected boolean onClick(GuiManager manager, Player player, Inventory inventory, InventoryClickEvent event) {
|
||||
final int cell = event.getSlot();
|
||||
Map<ClickType, Clickable> conditionals = conditionalButtons.get(cell);
|
||||
Clickable button;
|
||||
if (conditionals != null
|
||||
&& ((button = conditionals.get(event.getClick())) != null || (button = conditionals.get(null)) != null)) {
|
||||
button.onClick(new GuiClickEvent(manager, this, player, event, cell, true));
|
||||
} else {
|
||||
// no event for this button
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean onClickPlayerInventory(GuiManager manager, Player player, Inventory openInv, InventoryClickEvent event) {
|
||||
// no events for this yet
|
||||
return false;
|
||||
}
|
||||
|
||||
public void onOpen(GuiManager manager, Player player) {
|
||||
open = true;
|
||||
if (opener != null) {
|
||||
opener.onOpen(new GuiOpenEvent(manager, this, player));
|
||||
}
|
||||
}
|
||||
|
||||
public void onClose(GuiManager manager, Player player) {
|
||||
if (!allowClose) {
|
||||
manager.showGUI(player, this);
|
||||
return;
|
||||
}
|
||||
if (open && closer != null) {
|
||||
open = inventory.getViewers().isEmpty();
|
||||
closer.onClose(new GuiCloseEvent(manager, this, player));
|
||||
}
|
||||
if (parent != null) {
|
||||
manager.showGUI(player, parent);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
30
src/main/java/com/songoda/core/gui/GuiHolder.java
Normal file
30
src/main/java/com/songoda/core/gui/GuiHolder.java
Normal file
@ -0,0 +1,30 @@
|
||||
package com.songoda.core.gui;
|
||||
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
|
||||
/**
|
||||
* Internal class for marking an inventory as a GUI inventory
|
||||
*
|
||||
* @since 2019-08-25
|
||||
* @author jascotty2
|
||||
*/
|
||||
class GuiHolder implements InventoryHolder {
|
||||
|
||||
final Gui gui;
|
||||
final GuiManager manager;
|
||||
|
||||
public GuiHolder(GuiManager manager, Gui gui) {
|
||||
this.gui = gui;
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Inventory getInventory() {
|
||||
return gui.inventory;
|
||||
}
|
||||
|
||||
public Gui getGUI() {
|
||||
return gui;
|
||||
}
|
||||
}
|
197
src/main/java/com/songoda/core/gui/GuiManager.java
Normal file
197
src/main/java/com/songoda/core/gui/GuiManager.java
Normal file
@ -0,0 +1,197 @@
|
||||
package com.songoda.core.gui;
|
||||
|
||||
import com.songoda.core.compatibility.ClientVersion;
|
||||
import com.songoda.core.compatibility.CompatibleSounds;
|
||||
import com.songoda.core.compatibility.LegacyMaterials;
|
||||
import com.songoda.core.compatibility.ServerVersion;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.inventory.InventoryAction;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.inventory.InventoryCloseEvent;
|
||||
import org.bukkit.event.inventory.InventoryType.SlotType;
|
||||
import org.bukkit.event.server.PluginDisableEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Manages events for GUI screens
|
||||
*
|
||||
* @since 2019-08-25
|
||||
* @author jascotty2
|
||||
*/
|
||||
public class GuiManager {
|
||||
|
||||
final Plugin plugin;
|
||||
final UUID uuid = UUID.randomUUID(); // manager tracking to fix weird bugs from lazy programming
|
||||
final GuiListener listener = new GuiListener(this);
|
||||
final Map<Player, Gui> openInventories = new HashMap();
|
||||
private boolean initialized = false;
|
||||
private boolean shutdown = false;
|
||||
|
||||
public GuiManager(Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public Plugin getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the GUI handlers
|
||||
*/
|
||||
public void init() {
|
||||
Bukkit.getPluginManager().registerEvents(listener, plugin);
|
||||
initialized = true;
|
||||
shutdown = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if this manager cannot open any more GUI screens
|
||||
*
|
||||
* @return true if the owning plugin has shutdown
|
||||
*/
|
||||
public boolean isClosed() {
|
||||
return shutdown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and display a GUI interface for a player
|
||||
*
|
||||
* @param player player to open the interface for
|
||||
* @param gui GUI to use
|
||||
*/
|
||||
public void showGUI(Player player, Gui gui) {
|
||||
if (shutdown) {
|
||||
return;
|
||||
} else if (!initialized) {
|
||||
init();
|
||||
}
|
||||
Gui openInv = openInventories.get(player);
|
||||
if(openInv != null) {
|
||||
openInv.open = false;
|
||||
}
|
||||
Inventory inv = gui.generateInventory(this);
|
||||
player.openInventory(inv);
|
||||
gui.onOpen(this, player);
|
||||
openInventories.put(player, gui);
|
||||
}
|
||||
|
||||
public void showPopup(Player player, String message) {
|
||||
showPopup(player, message, LegacyMaterials.NETHER_STAR, BackgroundType.ADVENTURE);
|
||||
}
|
||||
|
||||
public void showPopup(Player player, String message, LegacyMaterials icon) {
|
||||
showPopup(player, message, icon, BackgroundType.ADVENTURE);
|
||||
}
|
||||
|
||||
public void showPopup(Player player, String message, LegacyMaterials icon, BackgroundType background) {
|
||||
if (ClientVersion.getClientVersion(player).isServerVersionAtLeast(ServerVersion.V1_12)) {
|
||||
PopupMessage popup = new PopupMessage(plugin, icon, message, background);
|
||||
popup.add();
|
||||
popup.grant(player);
|
||||
Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, () -> {
|
||||
popup.revoke(player);
|
||||
popup.remove();
|
||||
}, 70);
|
||||
} else if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_11)) {
|
||||
player.sendTitle("", message, 10, 70, 10);
|
||||
} else {
|
||||
player.sendTitle("", message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all active GUIs
|
||||
*/
|
||||
public void closeAll() {
|
||||
openInventories.entrySet().stream()
|
||||
.filter(e -> e.getKey().getOpenInventory().getTopInventory().getHolder() instanceof GuiHolder)
|
||||
.collect(Collectors.toList()) // to prevent concurrency exceptions
|
||||
.forEach(e -> e.getKey().closeInventory());
|
||||
openInventories.clear();
|
||||
}
|
||||
|
||||
protected static class GuiListener implements Listener {
|
||||
|
||||
final GuiManager manager;
|
||||
|
||||
public GuiListener(GuiManager manager) {
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
void onClickGUI(InventoryClickEvent event) {
|
||||
if (!(event.getWhoClicked() instanceof Player)) {
|
||||
return;
|
||||
}
|
||||
Inventory openInv = event.getInventory();
|
||||
final Player player = (Player) event.getWhoClicked();
|
||||
Gui gui;
|
||||
if (openInv.getHolder() != null && openInv.getHolder() instanceof GuiHolder
|
||||
&& ((GuiHolder) openInv.getHolder()).manager.uuid.equals(manager.uuid)) {
|
||||
gui = ((GuiHolder) openInv.getHolder()).getGUI();
|
||||
|
||||
if (event.getSlotType() == SlotType.OUTSIDE) {
|
||||
if (!gui.onClickOutside(manager, player, event)) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
} // did we click the gui or in the user's inventory?
|
||||
else if (event.getRawSlot() < gui.inventory.getSize()) {// or could use event.getClickedInventory() == gui.inventory
|
||||
// allow event if this is not a GUI element
|
||||
event.setCancelled(!gui.unlockedCells.entrySet().stream().anyMatch(e -> event.getSlot() == e.getKey() && e.getValue()));
|
||||
// process button press
|
||||
if (gui.onClick(manager, player, openInv, event)) {
|
||||
player.playSound(player.getLocation(), CompatibleSounds.UI_BUTTON_CLICK.getSound(), 1F, 1F);
|
||||
}
|
||||
} else {
|
||||
// Player clicked in the bottom inventory while GUI is open
|
||||
if (gui.onClickPlayerInventory(manager, player, openInv, event)) {
|
||||
player.playSound(player.getLocation(), CompatibleSounds.UI_BUTTON_CLICK.getSound(), 1F, 1F);
|
||||
} else if (!gui.acceptsItems || event.getAction() == InventoryAction.MOVE_TO_OTHER_INVENTORY) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
void onCloseGUI(InventoryCloseEvent event) {
|
||||
Inventory openInv = event.getInventory();
|
||||
if (openInv.getHolder() != null && openInv.getHolder() instanceof GuiHolder
|
||||
&& ((GuiHolder) openInv.getHolder()).manager.uuid.equals(manager.uuid)) {
|
||||
Gui gui = ((GuiHolder) openInv.getHolder()).getGUI();
|
||||
if(!gui.open) {
|
||||
return;
|
||||
}
|
||||
final Player player = (Player) event.getPlayer();
|
||||
if (!gui.allowDropItems) {
|
||||
player.setItemOnCursor(null);
|
||||
}
|
||||
if (manager.shutdown) {
|
||||
gui.onClose(manager, player);
|
||||
} else {
|
||||
Bukkit.getScheduler().runTaskLater(manager.plugin, () -> gui.onClose(manager, player), 1);
|
||||
}
|
||||
manager.openInventories.remove(player);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
void onDisable(PluginDisableEvent event) {
|
||||
if (event.getPlugin() == manager.plugin) {
|
||||
// uh-oh! Abandon ship!!
|
||||
manager.shutdown = true;
|
||||
manager.closeAll();
|
||||
manager.initialized = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
17
src/main/java/com/songoda/core/gui/GuiType.java
Normal file
17
src/main/java/com/songoda/core/gui/GuiType.java
Normal file
@ -0,0 +1,17 @@
|
||||
package com.songoda.core.gui;
|
||||
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
|
||||
public enum GuiType {
|
||||
|
||||
STANDARD(InventoryType.CHEST),
|
||||
DISPENSER(InventoryType.DISPENSER),
|
||||
HOPPER(InventoryType.HOPPER);
|
||||
|
||||
protected final InventoryType type;
|
||||
|
||||
private GuiType(InventoryType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
}
|
259
src/main/java/com/songoda/core/gui/GuiUtils.java
Normal file
259
src/main/java/com/songoda/core/gui/GuiUtils.java
Normal file
@ -0,0 +1,259 @@
|
||||
package com.songoda.core.gui;
|
||||
|
||||
import com.songoda.core.compatibility.LegacyMaterials;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
/**
|
||||
* @since 2019-08-25
|
||||
* @author jascotty2
|
||||
*/
|
||||
public class GuiUtils {
|
||||
|
||||
public static ItemStack getBorderGlassItem() {
|
||||
ItemStack glass = LegacyMaterials.LIGHT_BLUE_STAINED_GLASS_PANE.getItem();
|
||||
ItemMeta glassmeta = glass.getItemMeta();
|
||||
glassmeta.setDisplayName(ChatColor.BLACK.toString());
|
||||
glass.setItemMeta(glassmeta);
|
||||
return glass;
|
||||
}
|
||||
|
||||
public static ItemStack getBorderItem(ItemStack item) {
|
||||
ItemMeta glassmeta = item.getItemMeta();
|
||||
glassmeta.setDisplayName(ChatColor.BLACK.toString());
|
||||
item.setItemMeta(glassmeta);
|
||||
return item;
|
||||
}
|
||||
|
||||
public static ItemStack getBorderItem(LegacyMaterials mat) {
|
||||
ItemStack item = mat.getItem();
|
||||
ItemMeta glassmeta = item.getItemMeta();
|
||||
glassmeta.setDisplayName(ChatColor.BLACK.toString());
|
||||
item.setItemMeta(glassmeta);
|
||||
return item;
|
||||
}
|
||||
|
||||
public static List<String> getSafeLore(String... lines) {
|
||||
return getSafeLore(Arrays.asList(lines));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a lore value that will display fine on clients using auto gui scaling
|
||||
*
|
||||
* @param lines lines to format
|
||||
* @return newline and length-corrected item lore
|
||||
*/
|
||||
public static List<String> getSafeLore(List<String> lines) {
|
||||
// fix newlines
|
||||
ArrayList<String> newLore = new ArrayList();
|
||||
for (String l : lines) {
|
||||
for (String l2 : l.split("\n")) {
|
||||
if (l2.length() < 54) {
|
||||
newLore.add(l2);
|
||||
} else {
|
||||
// try to shorten the string
|
||||
String shorterString = l2;
|
||||
ChatColor lastColor = null; // todo? probably should also track formatting codes..
|
||||
int line = 0;
|
||||
while (shorterString.length() > 50) {
|
||||
int breakingSpace = -1;
|
||||
for (int i = 0; i < 50; ++i) {
|
||||
if (shorterString.charAt(i) == ChatColor.COLOR_CHAR) {
|
||||
lastColor = ChatColor.getByChar(shorterString.charAt(++i));
|
||||
} else if (shorterString.charAt(i) == ' ' || shorterString.charAt(i) == '-') {
|
||||
breakingSpace = i;
|
||||
}
|
||||
}
|
||||
if (breakingSpace == -1) {
|
||||
breakingSpace = Math.max(50, shorterString.length());
|
||||
newLore.add((line != 0 && lastColor != null ? lastColor.toString() : "") + shorterString.substring(0, breakingSpace) + "-");
|
||||
shorterString = breakingSpace == shorterString.length() ? "" : shorterString.substring(breakingSpace + 1);
|
||||
} else {
|
||||
newLore.add((line != 0 && lastColor != null ? lastColor.toString() : "") + shorterString.substring(0, breakingSpace));
|
||||
shorterString = breakingSpace == shorterString.length() ? "" : shorterString.substring(breakingSpace + 1);
|
||||
}
|
||||
++line;
|
||||
}
|
||||
if (!shorterString.isEmpty()) {
|
||||
newLore.add((line != 0 && lastColor != null ? lastColor.toString() : "") + " " + shorterString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return newLore;
|
||||
}
|
||||
|
||||
public static ItemStack createButtonItem(LegacyMaterials mat, String title, String... lore) {
|
||||
ItemStack item = mat.getItem();
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta != null) {
|
||||
meta.setDisplayName(title);
|
||||
if (lore != null) {
|
||||
meta.setLore(getSafeLore(lore));
|
||||
} else {
|
||||
meta.setLore(Collections.EMPTY_LIST);
|
||||
}
|
||||
item.setItemMeta(meta);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
public static ItemStack createButtonItem(ItemStack from, String title, String... lore) {
|
||||
ItemStack item = from.clone();
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta != null) {
|
||||
meta.setDisplayName(title);
|
||||
if (lore != null) {
|
||||
meta.setLore(getSafeLore(lore));
|
||||
} else {
|
||||
meta.setLore(Collections.EMPTY_LIST);
|
||||
}
|
||||
}
|
||||
item.setItemMeta(meta);
|
||||
return item;
|
||||
}
|
||||
|
||||
public static ItemStack createButtonItem(LegacyMaterials mat, String title, List<String> lore) {
|
||||
ItemStack item = mat.getItem();
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta != null) {
|
||||
meta.setDisplayName(title);
|
||||
if (lore != null) {
|
||||
meta.setLore(getSafeLore(lore));
|
||||
} else {
|
||||
meta.setLore(Collections.EMPTY_LIST);
|
||||
}
|
||||
}
|
||||
item.setItemMeta(meta);
|
||||
return item;
|
||||
}
|
||||
|
||||
public static ItemStack createButtonItem(ItemStack from, String title, List<String> lore) {
|
||||
ItemStack item = from.clone();
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta != null) {
|
||||
meta.setDisplayName(title);
|
||||
if (lore != null) {
|
||||
meta.setLore(getSafeLore(lore));
|
||||
} else {
|
||||
meta.setLore(Collections.EMPTY_LIST);
|
||||
}
|
||||
}
|
||||
item.setItemMeta(meta);
|
||||
return item;
|
||||
}
|
||||
|
||||
public static ItemStack updateItem(ItemStack item, String title, String... lore) {
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta != null) {
|
||||
meta.setDisplayName(title);
|
||||
if (lore != null) {
|
||||
meta.setLore(getSafeLore(lore));
|
||||
} else {
|
||||
meta.setLore(Collections.EMPTY_LIST);
|
||||
}
|
||||
}
|
||||
item.setItemMeta(meta);
|
||||
return item;
|
||||
}
|
||||
|
||||
public static ItemStack updateItem(ItemStack item, LegacyMaterials matTo, String title, String... lore) {
|
||||
if (!matTo.matches(item)) {
|
||||
item = matTo.getItem();
|
||||
}
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta != null) {
|
||||
meta.setDisplayName(title);
|
||||
if (lore != null) {
|
||||
meta.setLore(getSafeLore(lore));
|
||||
} else {
|
||||
meta.setLore(Collections.EMPTY_LIST);
|
||||
}
|
||||
}
|
||||
item.setItemMeta(meta);
|
||||
return item;
|
||||
}
|
||||
|
||||
public static ItemStack updateItem(ItemStack item, ItemStack to, String title, String... lore) {
|
||||
if (!LegacyMaterials.getMaterial(item).matches(to)) {
|
||||
item = to.clone();
|
||||
}
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta != null) {
|
||||
meta.setDisplayName(title);
|
||||
if (lore != null) {
|
||||
meta.setLore(getSafeLore(lore));
|
||||
} else {
|
||||
meta.setLore(Collections.EMPTY_LIST);
|
||||
}
|
||||
}
|
||||
item.setItemMeta(meta);
|
||||
return item;
|
||||
}
|
||||
|
||||
public static ItemStack updateItem(ItemStack item, String title, List<String> lore) {
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta != null) {
|
||||
meta.setDisplayName(title);
|
||||
if (lore != null) {
|
||||
meta.setLore(getSafeLore(lore));
|
||||
} else {
|
||||
meta.setLore(Collections.EMPTY_LIST);
|
||||
}
|
||||
}
|
||||
item.setItemMeta(meta);
|
||||
return item;
|
||||
}
|
||||
|
||||
public static ItemStack updateItem(ItemStack item, LegacyMaterials matTo, String title, List<String> lore) {
|
||||
if (!matTo.matches(item)) {
|
||||
item = matTo.getItem();
|
||||
}
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta != null) {
|
||||
meta.setDisplayName(title);
|
||||
if (lore != null) {
|
||||
meta.setLore(getSafeLore(lore));
|
||||
} else {
|
||||
meta.setLore(Collections.EMPTY_LIST);
|
||||
}
|
||||
}
|
||||
item.setItemMeta(meta);
|
||||
return item;
|
||||
}
|
||||
|
||||
public static ItemStack updateItem(ItemStack item, ItemStack to, String title, List<String> lore) {
|
||||
if (!LegacyMaterials.getMaterial(item).matches(to)) {
|
||||
item = to.clone();
|
||||
}
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta != null) {
|
||||
meta.setDisplayName(title);
|
||||
if (lore != null) {
|
||||
meta.setLore(getSafeLore(lore));
|
||||
} else {
|
||||
meta.setLore(Collections.EMPTY_LIST);
|
||||
}
|
||||
}
|
||||
item.setItemMeta(meta);
|
||||
return item;
|
||||
}
|
||||
|
||||
public static void mirrorFill(Gui gui, int row, int col, boolean mirrorRow, boolean mirrorCol, ItemStack item) {
|
||||
gui.setItem(row, col, item);
|
||||
if (mirrorRow) {
|
||||
gui.setItem(gui.rows - row - 1, col, item);
|
||||
}
|
||||
if (mirrorCol) {
|
||||
gui.setItem(row, 8 - col, item);
|
||||
}
|
||||
if (mirrorRow && mirrorCol) {
|
||||
gui.setItem(gui.rows - row - 1, 8 - col, item);
|
||||
}
|
||||
}
|
||||
}
|
190
src/main/java/com/songoda/core/gui/PopupMessage.java
Normal file
190
src/main/java/com/songoda/core/gui/PopupMessage.java
Normal file
@ -0,0 +1,190 @@
|
||||
package com.songoda.core.gui;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.songoda.core.compatibility.LegacyMaterials;
|
||||
import com.songoda.core.compatibility.ServerVersion;
|
||||
import java.util.HashSet;
|
||||
import java.util.UUID;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.chat.ComponentSerializer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.advancement.Advancement;
|
||||
import org.bukkit.advancement.AdvancementProgress;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Instance of a popup message that can be sent to a player <br>
|
||||
* Popup toast messages only work on Minecraft 1.12+ <br>
|
||||
* Calling this class on anything below 1.12 will cause ClassLoader Exceptions!
|
||||
*/
|
||||
class PopupMessage {
|
||||
|
||||
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
private static final HashSet<UUID> registeredMessages = new HashSet();
|
||||
|
||||
final UUID id = UUID.randomUUID();
|
||||
private final NamespacedKey key;
|
||||
private final TextComponent title;
|
||||
LegacyMaterials icon;
|
||||
int iconAmount = 1; // experimental, untested
|
||||
TriggerType trigger = TriggerType.IMPOSSIBLE;
|
||||
FrameType frame = FrameType.GOAL;
|
||||
BackgroundType background = BackgroundType.ADVENTURE;
|
||||
|
||||
PopupMessage(Plugin source, LegacyMaterials icon, String title) {
|
||||
this.key = new NamespacedKey(source, "popup/" + id);
|
||||
this.title = new TextComponent(title.length() < 74 ? title : (title.substring(0, 72) + "..."));
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
PopupMessage(Plugin source, LegacyMaterials icon, String title, BackgroundType background) {
|
||||
this.key = new NamespacedKey(source, "popup/" + id);
|
||||
this.title = new TextComponent(title.length() < 74 ? title : (title.substring(0, 72) + "..."));
|
||||
this.icon = icon;
|
||||
this.background = background;
|
||||
}
|
||||
|
||||
private String getJSON() {
|
||||
JsonObject json = new JsonObject();
|
||||
JsonObject advDisplay = new JsonObject();
|
||||
if (this.icon != null) {
|
||||
JsonObject displayIcon = new JsonObject();
|
||||
displayIcon.addProperty("item", "minecraft:" + this.icon.getMaterial().name().toLowerCase());
|
||||
if (this.icon.usesData()) {
|
||||
displayIcon.addProperty("data", this.icon.getData());
|
||||
}
|
||||
if (this.iconAmount > 1) {
|
||||
displayIcon.addProperty("amount", this.iconAmount); // not entirely sure if this works
|
||||
}
|
||||
advDisplay.add("icon", displayIcon);
|
||||
}
|
||||
advDisplay.add("title", gson.fromJson(ComponentSerializer.toString(this.title), JsonElement.class));
|
||||
advDisplay.addProperty("background", background.key);
|
||||
advDisplay.addProperty("description", "");
|
||||
advDisplay.addProperty("frame", this.frame.id);
|
||||
advDisplay.addProperty("announce_to_chat", false);
|
||||
advDisplay.addProperty("show_toast", true);
|
||||
advDisplay.addProperty("hidden", true);
|
||||
json.add("display", advDisplay);
|
||||
|
||||
JsonObject advCriteria = new JsonObject();
|
||||
json.add("criteria", advCriteria);
|
||||
|
||||
JsonObject advTrigger = new JsonObject();
|
||||
advTrigger.addProperty("trigger", this.trigger.getKey());
|
||||
/*if() {
|
||||
JsonObject advConditions = new JsonObject();
|
||||
// can add items to this list with [item,amount,data]
|
||||
advTrigger.add("conditions", advConditions);
|
||||
}*/
|
||||
advCriteria.add("mentioned", advTrigger);
|
||||
|
||||
return gson.toJson(json);
|
||||
}
|
||||
|
||||
protected void grant(final Player pl) {
|
||||
final Advancement adv = getAdvancement();
|
||||
final AdvancementProgress progress = pl.getAdvancementProgress(adv);
|
||||
|
||||
if (!progress.isDone())
|
||||
progress.getRemainingCriteria().forEach((crit) -> progress.awardCriteria(crit));
|
||||
}
|
||||
|
||||
protected void revoke(final Player pl) {
|
||||
final Advancement adv = getAdvancement();
|
||||
final AdvancementProgress prog = pl.getAdvancementProgress(adv);
|
||||
|
||||
if (prog.isDone())
|
||||
prog.getAwardedCriteria().forEach((crit) -> prog.revokeCriteria(crit));
|
||||
}
|
||||
|
||||
protected void add() {
|
||||
if (!registeredMessages.contains(id)) {
|
||||
registeredMessages.add(id);
|
||||
try {
|
||||
Bukkit.getUnsafe().loadAdvancement(key, getJSON());
|
||||
} catch (IllegalArgumentException e) {
|
||||
Bukkit.getLogger().warning("Failed to create popup advancement!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void remove() {
|
||||
if (registeredMessages.contains(id)) {
|
||||
registeredMessages.remove(id);
|
||||
Bukkit.getUnsafe().removeAdvancement(key);
|
||||
}
|
||||
}
|
||||
|
||||
public Advancement getAdvancement() {
|
||||
return Bukkit.getAdvancement(key);
|
||||
}
|
||||
|
||||
public static enum FrameType {
|
||||
TASK,
|
||||
CHALLENGE,
|
||||
GOAL;
|
||||
final String id;
|
||||
|
||||
private FrameType() {
|
||||
id = name().toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
public static enum TriggerType {
|
||||
ARBITRARY_PLAYER_TICK(ServerVersion.V1_13, "TICK"),
|
||||
BRED_ANIMALS,
|
||||
BREWED_POTION,
|
||||
CHANGED_DIMENSION,
|
||||
CONSTRUCT_BEACON,
|
||||
CONSUME_ITEM,
|
||||
CURED_ZOMBIE_VILLAGER,
|
||||
EFFECTS_CHANGED,
|
||||
ENCHANTED_ITEM,
|
||||
ENTER_BLOCK,
|
||||
ENTITY_HURT_PLAYER,
|
||||
ENTITY_KILLED_PLAYER,
|
||||
IMPOSSIBLE,
|
||||
INVENTORY_CHANGED,
|
||||
ITEM_DURABILITY_CHANGED,
|
||||
LEVITATION,
|
||||
LOCATION,
|
||||
NETHER_TRAVEL,
|
||||
PLACED_BLOCK,
|
||||
PLAYER_HURT_ENTITY,
|
||||
PLAYER_KILL_ENTITY,
|
||||
RECIPE_UNLOCKED,
|
||||
SLEPT_IN_BED,
|
||||
SUMMONED_ENTITY,
|
||||
TAME_ANIMAL,
|
||||
TICK,
|
||||
USED_ENDER_EYE,
|
||||
USED_TOTEM,
|
||||
VILLAGER_TRADE;
|
||||
final ServerVersion minVersion;
|
||||
final String compatible;
|
||||
final String key;
|
||||
|
||||
private TriggerType() {
|
||||
this.minVersion = ServerVersion.UNKNOWN;
|
||||
this.compatible = "";
|
||||
this.key = "minecraft:" + name().toLowerCase();
|
||||
}
|
||||
|
||||
private TriggerType(ServerVersion minVersion, String compatible) {
|
||||
this.minVersion = minVersion;
|
||||
this.compatible = compatible;
|
||||
this.key = "minecraft:" + (ServerVersion.isServerVersionAtLeast(minVersion) ? name() : compatible).toLowerCase();
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
227
src/main/java/com/songoda/core/gui/SimplePagedGui.java
Normal file
227
src/main/java/com/songoda/core/gui/SimplePagedGui.java
Normal file
@ -0,0 +1,227 @@
|
||||
package com.songoda.core.gui;
|
||||
|
||||
import com.songoda.core.compatibility.LegacyMaterials;
|
||||
import static com.songoda.core.gui.Gui.trimTitle;
|
||||
import com.songoda.core.gui.events.GuiClickEvent;
|
||||
import com.songoda.core.gui.methods.Clickable;
|
||||
import java.util.Map;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
/**
|
||||
* Paged GUI for when you aren't going to be making too many pages
|
||||
*
|
||||
* @since 2019-08-31
|
||||
* @author jascotty2
|
||||
*/
|
||||
public class SimplePagedGui extends Gui {
|
||||
|
||||
protected boolean useHeader;
|
||||
private int rowsPerPage, maxCellSlot;
|
||||
protected ItemStack headerBackItem;
|
||||
protected ItemStack footerBackItem;
|
||||
final int nextPageIndex = -4, prevPageIndex = -6;
|
||||
|
||||
public SimplePagedGui() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public SimplePagedGui(Gui parent) {
|
||||
super(parent);
|
||||
|
||||
nextPage = GuiUtils.createButtonItem(LegacyMaterials.ARROW, "Next Page");
|
||||
prevPage = GuiUtils.createButtonItem(LegacyMaterials.ARROW, "Previous Page");
|
||||
}
|
||||
|
||||
public SimplePagedGui setUseHeader(boolean useHeader) {
|
||||
this.useHeader = useHeader;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemStack getHeaderBackItem() {
|
||||
return headerBackItem;
|
||||
}
|
||||
|
||||
public SimplePagedGui setHeaderBackItem(ItemStack headerBackItem) {
|
||||
this.headerBackItem = headerBackItem;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemStack getFooterBackItem() {
|
||||
return footerBackItem;
|
||||
}
|
||||
|
||||
public SimplePagedGui setFooterBackItem(ItemStack footerBackItem) {
|
||||
this.footerBackItem = footerBackItem;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SimplePagedGui setNextPage(ItemStack item) {
|
||||
nextPage = item;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SimplePagedGui setPrevPage(ItemStack item) {
|
||||
prevPage = item;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimplePagedGui setItem(int row, int col, ItemStack item) {
|
||||
return setItem(col + row * 9, item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimplePagedGui setItem(int cell, ItemStack item) {
|
||||
// set the cell relative to the current page
|
||||
int cellIndex = page == 1 || (useHeader && cell < 9) ? cell : (cell + (page - 1) * (rowsPerPage * 9));
|
||||
|
||||
cellItems.put(cellIndex, item);
|
||||
if (open && cell >= 0 && cell < inventory.getSize()) {
|
||||
inventory.setItem(cell, item);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nextPage() {
|
||||
if (page < pages) {
|
||||
++page;
|
||||
showPage();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prevPage() {
|
||||
if (page > 1) {
|
||||
--page;
|
||||
showPage();
|
||||
}
|
||||
}
|
||||
|
||||
public void showPage() {
|
||||
int startCell = useHeader ? 9 : 0;
|
||||
int cellIndex = startCell + (page - 1) * (rowsPerPage * 9);
|
||||
|
||||
for (int i = startCell; i < (rows - 1) * 9; ++i) {
|
||||
final ItemStack item = cellItems.get(cellIndex++);
|
||||
inventory.setItem(i, item != null ? item : blankItem);
|
||||
}
|
||||
// page markers
|
||||
updatePageNavigation();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updatePageNavigation() {
|
||||
if (page > 1) {
|
||||
inventory.setItem((rows * 9) - 6, prevPage);
|
||||
this.setButton(prevPageIndex, prevPage, ClickType.LEFT, (event) -> this.prevPage());
|
||||
} else {
|
||||
inventory.setItem((rows * 9) - 6, footerBackItem != null ? footerBackItem : blankItem);
|
||||
this.setItem(prevPageIndex, null);
|
||||
this.clearActions(prevPageIndex);
|
||||
}
|
||||
if (pages > 1 && page != pages) {
|
||||
inventory.setItem((rows * 9) - 4, nextPage);
|
||||
this.setButton(nextPageIndex, nextPage, ClickType.LEFT, (event) -> this.nextPage());
|
||||
} else {
|
||||
inventory.setItem((rows * 9) - 4, footerBackItem != null ? footerBackItem : blankItem);
|
||||
this.setItem(nextPageIndex, null);
|
||||
this.clearActions(nextPageIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Inventory generateInventory(GuiManager manager) {
|
||||
// calculate pages here
|
||||
rowsPerPage = useHeader ? 4 : 5;
|
||||
maxCellSlot = (this.cellItems.isEmpty() ? 0 : this.cellItems.keySet().stream().max(Integer::compare).get()) + 1;
|
||||
int maxRows = (int) Math.ceil(maxCellSlot / 9.);
|
||||
pages = (int) Math.ceil(maxRows / rowsPerPage);
|
||||
this.setRows(maxRows + (useHeader ? 1 : 0));
|
||||
|
||||
// create inventory view
|
||||
final int cells = rows * 9;
|
||||
inventory = Bukkit.getServer().createInventory(new GuiHolder(manager, this), cells,
|
||||
title == null ? "" : trimTitle(ChatColor.translateAlternateColorCodes('&', title)));
|
||||
|
||||
// populate and return the display inventory
|
||||
page = 1;
|
||||
update();
|
||||
return inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
if (inventory == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// calculate pages here
|
||||
rowsPerPage = useHeader ? 4 : 5;
|
||||
maxCellSlot = (this.cellItems.isEmpty() ? 0 : this.cellItems.keySet().stream().max(Integer::compare).get()) + 1;
|
||||
int maxRows = Math.max((useHeader ? 1 : 0), (int) Math.ceil(maxCellSlot / 9.));
|
||||
pages = (int) Math.ceil(maxRows / rowsPerPage);
|
||||
|
||||
// create a new inventory if needed
|
||||
final int cells = rows * 9;
|
||||
boolean isNew = false;
|
||||
if (cells != inventory.getSize()) {
|
||||
this.setRows(maxRows + (useHeader ? 2 : 1));
|
||||
inventory = Bukkit.getServer().createInventory(inventory.getHolder(), cells,
|
||||
title == null ? "" : trimTitle(ChatColor.translateAlternateColorCodes('&', title)));
|
||||
isNew = true;
|
||||
}
|
||||
|
||||
// populate header
|
||||
if (useHeader) {
|
||||
for (int i = 0; i < 9; ++i) {
|
||||
final ItemStack item = cellItems.get(i);
|
||||
inventory.setItem(i, item != null ? item : (headerBackItem != null ? headerBackItem : blankItem));
|
||||
}
|
||||
}
|
||||
// last row is dedicated to pagation
|
||||
for (int i = cells - 9; i < cells; ++i) {
|
||||
inventory.setItem(i, footerBackItem != null ? footerBackItem : blankItem);
|
||||
}
|
||||
// fill out the rest of the page
|
||||
showPage();
|
||||
|
||||
if(isNew) {
|
||||
// whoopsie!
|
||||
exit();
|
||||
getPlayers().forEach(player -> ((GuiHolder) inventory.getHolder()).manager.showGUI(player, this));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onClick(GuiManager manager, Player player, Inventory inventory, InventoryClickEvent event) {
|
||||
int cell = event.getSlot();
|
||||
Map<ClickType, Clickable> conditionals;
|
||||
|
||||
if (useHeader && cell < 9) {
|
||||
conditionals = conditionalButtons.get(cell);
|
||||
} else if (cell >= (rows - 1) * 9) {
|
||||
// footer row
|
||||
conditionals = conditionalButtons.get(cell - (rows * 9));
|
||||
} else {
|
||||
int cellIndex = page == 1 || (useHeader && cell < 9) ? cell : (cell + (page - 1) * (rowsPerPage * 9));
|
||||
conditionals = conditionalButtons.get(cellIndex);
|
||||
}
|
||||
|
||||
Clickable button;
|
||||
if (conditionals != null
|
||||
&& ((button = conditionals.get(event.getClick())) != null || (button = conditionals.get(null)) != null)) {
|
||||
button.onClick(new GuiClickEvent(manager, this, player, event, cell, true));
|
||||
} else {
|
||||
// no event for this button
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
29
src/main/java/com/songoda/core/gui/events/GuiClickEvent.java
Normal file
29
src/main/java/com/songoda/core/gui/events/GuiClickEvent.java
Normal file
@ -0,0 +1,29 @@
|
||||
package com.songoda.core.gui.events;
|
||||
|
||||
import com.songoda.core.gui.Gui;
|
||||
import com.songoda.core.gui.GuiManager;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class GuiClickEvent extends GuiEvent {
|
||||
public final int slot;
|
||||
public final boolean guiClicked;
|
||||
public final ItemStack cursor, clickedItem;
|
||||
public final ClickType clickType;
|
||||
public final InventoryClickEvent event;
|
||||
|
||||
public GuiClickEvent(GuiManager manager, Gui gui, Player player, InventoryClickEvent event, int slot, boolean guiClicked) {
|
||||
super(manager, gui, player);
|
||||
this.slot = slot;
|
||||
this.guiClicked = guiClicked;
|
||||
this.cursor = event.getCursor();
|
||||
Inventory clicked = event.getClickedInventory();
|
||||
this.clickedItem = clicked == null ? null : clicked.getItem(event.getSlot());
|
||||
this.clickType = event.getClick();
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
}
|
13
src/main/java/com/songoda/core/gui/events/GuiCloseEvent.java
Normal file
13
src/main/java/com/songoda/core/gui/events/GuiCloseEvent.java
Normal file
@ -0,0 +1,13 @@
|
||||
package com.songoda.core.gui.events;
|
||||
|
||||
import com.songoda.core.gui.Gui;
|
||||
import com.songoda.core.gui.GuiManager;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class GuiCloseEvent extends GuiEvent {
|
||||
|
||||
public GuiCloseEvent(GuiManager manager, Gui gui, Player player) {
|
||||
super(manager, gui, player);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.songoda.core.gui.events;
|
||||
|
||||
import com.songoda.core.gui.Gui;
|
||||
import com.songoda.core.gui.GuiManager;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class GuiDropItemEvent extends GuiEvent {
|
||||
public final ItemStack cursor;
|
||||
public final ClickType clickType;
|
||||
public final InventoryClickEvent event;
|
||||
|
||||
public GuiDropItemEvent(GuiManager manager, Gui gui, Player player, InventoryClickEvent event) {
|
||||
super(manager, gui, player);
|
||||
this.cursor = event.getCursor();
|
||||
this.clickType = event.getClick();
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
}
|
19
src/main/java/com/songoda/core/gui/events/GuiEvent.java
Normal file
19
src/main/java/com/songoda/core/gui/events/GuiEvent.java
Normal file
@ -0,0 +1,19 @@
|
||||
package com.songoda.core.gui.events;
|
||||
|
||||
import com.songoda.core.gui.Gui;
|
||||
import com.songoda.core.gui.GuiManager;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public abstract class GuiEvent {
|
||||
|
||||
public final GuiManager manager;
|
||||
public final Gui gui;
|
||||
public final Player player;
|
||||
|
||||
public GuiEvent(GuiManager manager, Gui gui, Player player) {
|
||||
this.manager = manager;
|
||||
this.gui = gui;
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
}
|
13
src/main/java/com/songoda/core/gui/events/GuiOpenEvent.java
Normal file
13
src/main/java/com/songoda/core/gui/events/GuiOpenEvent.java
Normal file
@ -0,0 +1,13 @@
|
||||
package com.songoda.core.gui.events;
|
||||
|
||||
import com.songoda.core.gui.Gui;
|
||||
import com.songoda.core.gui.GuiManager;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class GuiOpenEvent extends GuiEvent {
|
||||
|
||||
public GuiOpenEvent(GuiManager manager, Gui gui, Player player) {
|
||||
super(manager, gui, player);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package com.songoda.core.gui.methods;
|
||||
|
||||
import com.songoda.core.gui.events.GuiClickEvent;
|
||||
|
||||
public interface Clickable {
|
||||
|
||||
void onClick(GuiClickEvent event);
|
||||
}
|
8
src/main/java/com/songoda/core/gui/methods/Closable.java
Normal file
8
src/main/java/com/songoda/core/gui/methods/Closable.java
Normal file
@ -0,0 +1,8 @@
|
||||
package com.songoda.core.gui.methods;
|
||||
|
||||
import com.songoda.core.gui.events.GuiCloseEvent;
|
||||
|
||||
public interface Closable {
|
||||
|
||||
void onClose(GuiCloseEvent event);
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package com.songoda.core.gui.methods;
|
||||
|
||||
import com.songoda.core.gui.events.GuiDropItemEvent;
|
||||
|
||||
public interface Droppable {
|
||||
|
||||
boolean onDrop(GuiDropItemEvent event);
|
||||
}
|
8
src/main/java/com/songoda/core/gui/methods/Openable.java
Normal file
8
src/main/java/com/songoda/core/gui/methods/Openable.java
Normal file
@ -0,0 +1,8 @@
|
||||
package com.songoda.core.gui.methods;
|
||||
|
||||
import com.songoda.core.gui.events.GuiOpenEvent;
|
||||
|
||||
public interface Openable {
|
||||
|
||||
void onOpen(GuiOpenEvent event);
|
||||
}
|
8
src/main/java/com/songoda/core/gui/methods/Pagable.java
Normal file
8
src/main/java/com/songoda/core/gui/methods/Pagable.java
Normal file
@ -0,0 +1,8 @@
|
||||
package com.songoda.core.gui.methods;
|
||||
|
||||
import com.songoda.core.gui.Gui;
|
||||
|
||||
public interface Pagable {
|
||||
|
||||
void onPageChange(Gui gui, int lastPage, int newPage);
|
||||
}
|
99
src/main/java/com/songoda/core/hooks/EconomyManager.java
Normal file
99
src/main/java/com/songoda/core/hooks/EconomyManager.java
Normal file
@ -0,0 +1,99 @@
|
||||
package com.songoda.core.hooks;
|
||||
|
||||
import com.songoda.core.hooks.economies.Economy;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
|
||||
/**
|
||||
* A convenience class for static access to an Economy HookManager
|
||||
*/
|
||||
public class EconomyManager {
|
||||
|
||||
private static final HookManager<Economy> manager = new HookManager(Economy.class);
|
||||
|
||||
/**
|
||||
* Load all supported economy plugins. <br />
|
||||
* Note: This method should be called in your plugin's onEnable() section
|
||||
*/
|
||||
public static void load() {
|
||||
manager.load();
|
||||
}
|
||||
|
||||
public static HookManager getManager() {
|
||||
return manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grab the default economy plugin. <br />
|
||||
* NOTE: using a default economy assumes that this library is shaded
|
||||
*
|
||||
* @return returns null if no plugin enabled
|
||||
*/
|
||||
public static Economy getEconomy() {
|
||||
return manager.getCurrentHook();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if there is a default economy loaded. <br />
|
||||
* NOTE: using a default economy assumes that this library is shaded
|
||||
*
|
||||
* @return returns false if there are no supported economy plugins
|
||||
*/
|
||||
public static boolean isEnabled() {
|
||||
return manager.isEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the economy plugin being used. <br />
|
||||
* NOTE: using a default economy assumes that this library is shaded
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getName() {
|
||||
return manager.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the given amount to a human-readable string in this currency
|
||||
* @param amt amount to display
|
||||
* @return a currency string as formatted by the economy plugin
|
||||
*/
|
||||
public static String formatEconomy(double amt) {
|
||||
return manager.isEnabled() ? manager.getCurrentHook().formatEconomy(amt) : String.valueOf(amt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a player has at least some balance available. <br />
|
||||
* NOTE: using a default economy assumes that this library is shaded
|
||||
*
|
||||
* @param player player to check
|
||||
* @param cost minimum amount this player should have
|
||||
* @return true if this player can have this amount withdrawn
|
||||
*/
|
||||
public static boolean hasBalance(OfflinePlayer player, double cost) {
|
||||
return manager.isEnabled() && manager.getCurrentHook().hasBalance(player, cost);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to withdraw an amount from a player's balance. <br />
|
||||
* NOTE: using a default economy assumes that this library is shaded
|
||||
*
|
||||
* @param player player to check
|
||||
* @param cost amount to remove from this player
|
||||
* @return true if the total amount was withdrawn successfully
|
||||
*/
|
||||
public static boolean withdrawBalance(OfflinePlayer player, double cost) {
|
||||
return manager.isEnabled() && manager.getCurrentHook().withdrawBalance(player, cost);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to add an amount to a player's balance. <br />
|
||||
* NOTE: using a default economy assumes that this library is shaded
|
||||
*
|
||||
* @param player player to check
|
||||
* @param amount amount to add to this player
|
||||
* @return true if the total amount was added successfully
|
||||
*/
|
||||
public static boolean deposit(OfflinePlayer player, double amount) {
|
||||
return manager.isEnabled() && manager.getCurrentHook().deposit(player, amount);
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package com.songoda.core.hooks;
|
||||
|
||||
import com.songoda.core.hooks.stackers.Stacker;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
|
||||
/**
|
||||
* A convenience class for static access to a Stacker HookManager
|
||||
*/
|
||||
public class EntityStackerManager {
|
||||
|
||||
private static final HookManager<Stacker> manager = new HookManager(Stacker.class);
|
||||
|
||||
/**
|
||||
* Load all supported economy plugins. <br />
|
||||
* Note: This method should be called in your plugin's onEnable() section
|
||||
*/
|
||||
public static void load() {
|
||||
manager.load();
|
||||
}
|
||||
|
||||
public static HookManager getManager() {
|
||||
return manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grab the default hologram plugin. <br />
|
||||
* NOTE: using a default hologram assumes that this library is shaded
|
||||
*
|
||||
* @return returns null if no plugin enabled
|
||||
*/
|
||||
public static Stacker getStacker() {
|
||||
return manager.getCurrentHook();
|
||||
}
|
||||
|
||||
public static boolean isStacked(LivingEntity entity) {
|
||||
return manager.isEnabled() && manager.getCurrentHook().isStacked(entity);
|
||||
}
|
||||
|
||||
public static int getSize(LivingEntity entity) {
|
||||
return manager.isEnabled() ? manager.getCurrentHook().getSize(entity) : 1;
|
||||
}
|
||||
|
||||
public static void removeOne(LivingEntity entity) {
|
||||
remove(entity, 1);
|
||||
}
|
||||
|
||||
public static void remove(LivingEntity entity, int amount) {
|
||||
if (manager.isEnabled())
|
||||
manager.getCurrentHook().remove(entity, amount);
|
||||
}
|
||||
|
||||
public static void addOne(LivingEntity entity) {
|
||||
add(entity, 1);
|
||||
}
|
||||
|
||||
public static void add(LivingEntity entity, int amount) {
|
||||
if (manager.isEnabled())
|
||||
manager.getCurrentHook().add(entity, amount);
|
||||
}
|
||||
}
|
68
src/main/java/com/songoda/core/hooks/HologramManager.java
Normal file
68
src/main/java/com/songoda/core/hooks/HologramManager.java
Normal file
@ -0,0 +1,68 @@
|
||||
package com.songoda.core.hooks;
|
||||
|
||||
import com.songoda.core.hooks.holograms.Holograms;
|
||||
import org.bukkit.Location;
|
||||
import java.util.List;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* A convenience class for static access to a Holograms HookManager
|
||||
*/
|
||||
public class HologramManager {
|
||||
|
||||
private static final HookManager<Holograms> manager = new HookManager(Holograms.class);
|
||||
|
||||
/**
|
||||
* Load all supported economy plugins.<br/>
|
||||
* Note: This method should be called in your plugin's onEnable() section
|
||||
*
|
||||
* @param plugin plugin that will be using the holograms
|
||||
*/
|
||||
public static void load(Plugin plugin) {
|
||||
manager.load(plugin);
|
||||
}
|
||||
|
||||
public static HookManager getManager() {
|
||||
return manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grab the default hologram plugin. <br />
|
||||
* NOTE: using a default hologram assumes that this library is shaded
|
||||
*
|
||||
* @return returns null if no plugin enabled
|
||||
*/
|
||||
public static Holograms getHolograms() {
|
||||
return manager.getCurrentHook();
|
||||
}
|
||||
|
||||
public static void createHologram(Location location, String line) {
|
||||
if (manager.isEnabled())
|
||||
manager.getCurrentHook().createHologram(location, line);
|
||||
}
|
||||
|
||||
public static void createHologram(Location location, List<String> lines) {
|
||||
if (manager.isEnabled())
|
||||
manager.getCurrentHook().createHologram(location, lines);
|
||||
}
|
||||
|
||||
public static void removeHologram(Location location) {
|
||||
if (manager.isEnabled())
|
||||
manager.getCurrentHook().removeHologram(location);
|
||||
}
|
||||
|
||||
public static void removeAllHolograms() {
|
||||
if (manager.isEnabled())
|
||||
manager.getCurrentHook().removeAllHolograms();
|
||||
}
|
||||
|
||||
public static void updateHologram(Location location, String line) {
|
||||
if (manager.isEnabled())
|
||||
manager.getCurrentHook().updateHologram(location, line);
|
||||
}
|
||||
|
||||
public static void updateHologram(Location location, List<String> lines) {
|
||||
if (manager.isEnabled())
|
||||
manager.getCurrentHook().updateHologram(location, lines);
|
||||
}
|
||||
}
|
18
src/main/java/com/songoda/core/hooks/Hook.java
Normal file
18
src/main/java/com/songoda/core/hooks/Hook.java
Normal file
@ -0,0 +1,18 @@
|
||||
package com.songoda.core.hooks;
|
||||
|
||||
public interface Hook {
|
||||
|
||||
/**
|
||||
* Get the name of the plugin being used
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
abstract String getName();
|
||||
|
||||
/**
|
||||
* Check to see if the economy plugin being used is active
|
||||
*
|
||||
* @return true if the plugin is loaded and active
|
||||
*/
|
||||
abstract boolean isEnabled();
|
||||
}
|
189
src/main/java/com/songoda/core/hooks/HookManager.java
Normal file
189
src/main/java/com/songoda/core/hooks/HookManager.java
Normal file
@ -0,0 +1,189 @@
|
||||
package com.songoda.core.hooks;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public class HookManager<T extends Hook> {
|
||||
|
||||
private final Class typeClass;
|
||||
private T defaultHook = null;
|
||||
private boolean loaded = false;
|
||||
private final Map<PluginHook, T> registeredHooks = new HashMap<>();
|
||||
|
||||
public HookManager(Class typeClass) {
|
||||
this.typeClass = typeClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all supported plugins.
|
||||
*/
|
||||
public void load() {
|
||||
if (!loaded) {
|
||||
registeredHooks.putAll(PluginHook.loadHooks(typeClass, null).entrySet().stream()
|
||||
.collect(Collectors.toMap(e -> e.getKey(), e -> (T) e.getValue())));
|
||||
if (!registeredHooks.isEmpty()) {
|
||||
defaultHook = (T) registeredHooks.values().iterator().next();
|
||||
}
|
||||
loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all supported plugins.
|
||||
* @param hookingPlugin plugin to pass to the hook handler
|
||||
*/
|
||||
public void load(Plugin hookingPlugin) {
|
||||
if (!loaded) {
|
||||
registeredHooks.putAll(PluginHook.loadHooks(typeClass, hookingPlugin).entrySet().stream()
|
||||
.collect(Collectors.toMap(e -> e.getKey(), e -> (T) e.getValue())));
|
||||
if (!registeredHooks.isEmpty()) {
|
||||
defaultHook = (T) registeredHooks.values().iterator().next();
|
||||
}
|
||||
loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently selected plugin hook. <br>
|
||||
* If none were set, then the first one found is used.
|
||||
*
|
||||
* @return The instance of T that was created, or null if none available.
|
||||
*/
|
||||
public T getCurrentHook() {
|
||||
return defaultHook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default hook to a different plugin, if that plugin exists. <br>
|
||||
* If the plugin is not loaded or supported,
|
||||
* the previously defined default will be used.
|
||||
*
|
||||
* @param name name of the plugin to use
|
||||
* @return true if the default was set to this plugin
|
||||
*/
|
||||
public boolean setPreferredHook(String name) {
|
||||
T hook = getHook(name);
|
||||
if (hook != null) {
|
||||
defaultHook = hook;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default hook to a different plugin, if that plugin exists. <br />
|
||||
* If the plugin is not loaded or supported,
|
||||
* the previously defined default will be used.
|
||||
*
|
||||
* @param plugin plugin to use
|
||||
* @return true if the default was set to this plugin
|
||||
*/
|
||||
public boolean setPreferredHook(PluginHook plugin) {
|
||||
T hook = getHook(plugin);
|
||||
if (hook != null) {
|
||||
defaultHook = hook;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to grab the handler for this specific plugin hook.
|
||||
*
|
||||
* @param name plugin to use
|
||||
* @return returns null if plugin is not enabled
|
||||
*/
|
||||
public T getHook(String name) {
|
||||
if (name == null)
|
||||
return null;
|
||||
final String plugin = name.trim();
|
||||
return (T) registeredHooks.get(registeredHooks.keySet().stream()
|
||||
.filter(type -> type.plugin.equalsIgnoreCase(plugin))
|
||||
.findFirst().orElse(null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to grab the handler for this specific plugin hook.
|
||||
*
|
||||
* @param hook plugin to use
|
||||
* @return returns null if plugin is not enabled
|
||||
*/
|
||||
public T getHook(PluginHook hook) {
|
||||
return registeredHooks.get(hook);
|
||||
}
|
||||
|
||||
/**
|
||||
* Grab a list of all supported and loaded plugin hooks.
|
||||
*
|
||||
* @return an immutable collection of the loaded handler instances
|
||||
*/
|
||||
public Collection<T> getRegisteredHooks() {
|
||||
return Collections.unmodifiableCollection(registeredHooks.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Grab a list of all supported and loaded plugin hooks.
|
||||
*
|
||||
* @return an immutable collection of plugin names that are loaded.
|
||||
*/
|
||||
public List<String> getRegisteredPlugins() {
|
||||
return registeredHooks.keySet().stream()
|
||||
.map(v -> v.plugin)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all supported plugins that we can hook into.
|
||||
*
|
||||
* @return an immutable collection of plugin names that can be used.
|
||||
*/
|
||||
public List<String> getPossiblePlugins() {
|
||||
return PluginHook.getHooks(typeClass).stream()
|
||||
.map(v -> v.plugin)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a specific plugin hook is enabled.
|
||||
*
|
||||
* @param name plugin to check
|
||||
* @return true if this plugin is supported and loaded
|
||||
*/
|
||||
public boolean isEnabled(String name) {
|
||||
return getHook(name) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a specific plugin hook is enabled.
|
||||
*
|
||||
* @param hook plugin to check
|
||||
* @return true if this plugin is supported and loaded
|
||||
*/
|
||||
public boolean isEnabled(PluginHook hook) {
|
||||
return registeredHooks.containsKey(hook);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if there is a default hook loaded.
|
||||
*
|
||||
* @return returns false if there are no supported plugins loaded
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return defaultHook != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the default plugin being hooked into.
|
||||
*
|
||||
* @return plugin name, or null if none enabled.
|
||||
*/
|
||||
public String getName() {
|
||||
return defaultHook != null ? defaultHook.getName() : null;
|
||||
}
|
||||
|
||||
}
|
132
src/main/java/com/songoda/core/hooks/PluginHook.java
Normal file
132
src/main/java/com/songoda/core/hooks/PluginHook.java
Normal file
@ -0,0 +1,132 @@
|
||||
package com.songoda.core.hooks;
|
||||
|
||||
import com.songoda.core.hooks.economies.Economy;
|
||||
import com.songoda.core.hooks.economies.PlayerPointsEconomy;
|
||||
import com.songoda.core.hooks.economies.ReserveEconomy;
|
||||
import com.songoda.core.hooks.economies.VaultEconomy;
|
||||
import com.songoda.core.hooks.stackers.StackMob;
|
||||
import com.songoda.core.hooks.stackers.Stacker;
|
||||
import com.songoda.core.hooks.stackers.UltimateStacker;
|
||||
import com.songoda.core.hooks.stackers.WildStacker;
|
||||
import com.songoda.core.hooks.holograms.Holograms;
|
||||
import com.songoda.core.hooks.holograms.HologramsHolograms;
|
||||
import com.songoda.core.hooks.holograms.HolographicDisplaysHolograms;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
|
||||
public final class PluginHook <T extends Class> {
|
||||
|
||||
public static final PluginHook ECO_VAULT = new PluginHook(Economy.class, "Vault", VaultEconomy.class);
|
||||
public static final PluginHook ECO_PLAYER_POINTS = new PluginHook(Economy.class, "PlayerPoints", PlayerPointsEconomy.class);
|
||||
public static final PluginHook ECO_RESERVE = new PluginHook(Economy.class, "Reserve", ReserveEconomy.class);
|
||||
public static final PluginHook STACKER_ULTIMATE = new PluginHook(Stacker.class, "UltimateStacker", UltimateStacker.class);
|
||||
public static final PluginHook STACKER_WILD = new PluginHook(Stacker.class, "WildStacker", WildStacker.class);
|
||||
public static final PluginHook STACKER_STACK_MOB = new PluginHook(Stacker.class, "StackMob", StackMob.class);
|
||||
public static final PluginHook HOLO_DISPLAYS = new PluginHook(Holograms.class, "HolographicDisplays", HolographicDisplaysHolograms.class);
|
||||
public static final PluginHook HOLO_HOLOGRAMS = new PluginHook(Holograms.class, "Holograms", HologramsHolograms.class);
|
||||
|
||||
/******* Start Manager stuff *******/
|
||||
|
||||
protected final T hookGeneric;
|
||||
protected final String plugin;
|
||||
protected final Class managerClass;
|
||||
protected static Map<Class, PluginHook> hooks;
|
||||
protected Constructor pluginConstructor;
|
||||
|
||||
private PluginHook(T type, String pluginName, Class handler) {
|
||||
if (!Hook.class.isAssignableFrom(handler)) {
|
||||
throw new RuntimeException("Tried to register a non-Hook plugin hook! " + pluginName + " -> " + handler.getName());
|
||||
}
|
||||
this.hookGeneric = type;
|
||||
this.plugin = pluginName;
|
||||
this.managerClass = handler;
|
||||
if (hooks == null) {
|
||||
hooks = new LinkedHashMap();
|
||||
}
|
||||
hooks.put(handler, this);
|
||||
// Does this class have a plugin constructor?
|
||||
try {
|
||||
pluginConstructor = type.getDeclaredConstructor(Plugin.class);
|
||||
} catch (NoSuchMethodException | SecurityException ex) {
|
||||
// nope!
|
||||
}
|
||||
}
|
||||
|
||||
protected static Map<PluginHook, Hook> loadHooks(Class type, Plugin plugin) {
|
||||
Map<PluginHook, Hook> loaded = new LinkedHashMap<>();
|
||||
PluginManager pluginManager = Bukkit.getPluginManager();
|
||||
|
||||
for (PluginHook hook : getHooks(type)) {
|
||||
if (pluginManager.isPluginEnabled(hook.plugin)) {
|
||||
Hook handler = (Hook) (plugin != null ? hook.load(plugin) : hook.load());
|
||||
if (handler != null && handler.isEnabled()) {
|
||||
loaded.put(hook, handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
protected static List<PluginHook> getHooks(Class type) {
|
||||
return hooks.entrySet().parallelStream()
|
||||
.filter(e -> e.getKey() == type || e.getValue().managerClass == type || type.isAssignableFrom(e.getKey()))
|
||||
.map(Map.Entry::getValue)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public String getPluginName() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
protected Object load() {
|
||||
try {
|
||||
return managerClass.cast(
|
||||
pluginConstructor != null
|
||||
? pluginConstructor.newInstance((Plugin) null)
|
||||
: managerClass.getConstructor().newInstance());
|
||||
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, "Unexpected Error while creating a new Hook Manager for " + plugin, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Object load(Plugin hookingPlugin) {
|
||||
try {
|
||||
return managerClass.cast(
|
||||
pluginConstructor != null
|
||||
? pluginConstructor.newInstance(hookingPlugin)
|
||||
: managerClass.getConstructor().newInstance());
|
||||
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, "Unexpected Error while creating a new Hook Manager for " + plugin, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 3;
|
||||
hash = 37 * hash + Objects.hashCode(this.plugin);
|
||||
hash = 37 * hash + Objects.hashCode(this.managerClass);
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final PluginHook<?> other = (PluginHook<?>) obj;
|
||||
return Objects.equals(this.plugin, other.plugin)
|
||||
&& Objects.equals(this.managerClass, other.managerClass);
|
||||
}
|
||||
}
|
72
src/main/java/com/songoda/core/hooks/WorldGuardHook.java
Normal file
72
src/main/java/com/songoda/core/hooks/WorldGuardHook.java
Normal file
@ -0,0 +1,72 @@
|
||||
package com.songoda.core.hooks;
|
||||
|
||||
import com.songoda.core.hooks.worldguard.WorldGuardFlagHandler;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
|
||||
public class WorldGuardHook {
|
||||
|
||||
static boolean canHook, checkedCanHook = false;
|
||||
|
||||
private static void init() {
|
||||
if(checkedCanHook) return;
|
||||
|
||||
try {
|
||||
// if this class exists, we're good to use WG classes
|
||||
Class.forName("com.sk89q.worldguard.protection.flags.Flag");
|
||||
canHook = true;
|
||||
} catch (ClassNotFoundException ex) {
|
||||
}
|
||||
|
||||
checkedCanHook = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to register a worldGuard flag (ALLOW/DENY) <br />
|
||||
* Note: This must be called before WorldGuard loads, or it will fail.
|
||||
*
|
||||
* @param flag name of the flag to set
|
||||
* @param state default value of the flag
|
||||
*/
|
||||
public static void addHook(String flag, boolean state) {
|
||||
init();
|
||||
if(canHook) {
|
||||
WorldGuardFlagHandler.addHook(flag, state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if WorldGuard is installed and hooked
|
||||
*
|
||||
* @return true if and only if WorldGuard exists and addHook() has been
|
||||
* called and added successfully
|
||||
*/
|
||||
public static boolean isEnabled() {
|
||||
init();
|
||||
return canHook && WorldGuardFlagHandler.isEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks this location to see what this flag is set to
|
||||
*
|
||||
* @param l location to check
|
||||
* @param flag ALLOW/DENY flag to check
|
||||
* @return flag state, or null if undefined
|
||||
*/
|
||||
public static Boolean getBooleanFlag(Location l, String flag) {
|
||||
init();
|
||||
return canHook ? WorldGuardFlagHandler.getBooleanFlag(l, flag) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query all regions that are in or intersect this chunk
|
||||
*
|
||||
* @param c chunk to check for regions in
|
||||
* @param flag ALLOW/DENY flag to check
|
||||
* @return flag state, or null if undefined
|
||||
*/
|
||||
public static Boolean getBooleanFlag(Chunk c, String flag) {
|
||||
init();
|
||||
return canHook ? WorldGuardFlagHandler.getBooleanFlag(c, flag) : null;
|
||||
}
|
||||
}
|
45
src/main/java/com/songoda/core/hooks/economies/Economy.java
Normal file
45
src/main/java/com/songoda/core/hooks/economies/Economy.java
Normal file
@ -0,0 +1,45 @@
|
||||
package com.songoda.core.hooks.economies;
|
||||
|
||||
import com.songoda.core.hooks.Hook;
|
||||
import java.text.DecimalFormat;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
|
||||
public abstract class Economy implements Hook {
|
||||
|
||||
/**
|
||||
* Check to see if a player has at least some balance available
|
||||
*
|
||||
* @param player player to check
|
||||
* @param cost minimum amount this player should have
|
||||
* @return true if this player can have this amount withdrawn
|
||||
*/
|
||||
public abstract boolean hasBalance(OfflinePlayer player, double cost);
|
||||
|
||||
/**
|
||||
* Try to withdraw an amount from a player's balance
|
||||
*
|
||||
* @param player player to check
|
||||
* @param cost amount to remove from this player
|
||||
* @return true if the total amount was withdrawn successfully
|
||||
*/
|
||||
public abstract boolean withdrawBalance(OfflinePlayer player, double cost);
|
||||
|
||||
/**
|
||||
* Try to add an amount to a player's balance
|
||||
*
|
||||
* @param player player to check
|
||||
* @param amount amount to add to this player
|
||||
* @return true if the total amount was added successfully
|
||||
*/
|
||||
public abstract boolean deposit(OfflinePlayer player, double amount);
|
||||
|
||||
/**
|
||||
* Format the given amount to a human-readable string in this currency
|
||||
* @param amt amount to display
|
||||
* @return a currency string as formatted by the economy plugin
|
||||
*/
|
||||
public String formatEconomy(double amt) {
|
||||
DecimalFormat formatter = new DecimalFormat(amt == Math.ceil(amt) ? "#,###" : "#,###.00");
|
||||
return "$" + formatter.format(amt);
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package com.songoda.core.hooks.economies;
|
||||
|
||||
import org.black_ixx.playerpoints.PlayerPoints;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
|
||||
public class PlayerPointsEconomy extends Economy {
|
||||
|
||||
private final PlayerPoints playerPoints;
|
||||
|
||||
public PlayerPointsEconomy() {
|
||||
this.playerPoints = (PlayerPoints) Bukkit.getServer().getPluginManager().getPlugin("PlayerPoints");
|
||||
}
|
||||
|
||||
private int convertAmount(double amount) {
|
||||
return (int) Math.ceil(amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return playerPoints.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "PlayerPoints";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBalance(OfflinePlayer player, double cost) {
|
||||
int amount = convertAmount(cost);
|
||||
return playerPoints.getAPI().look(player.getUniqueId()) >= amount;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean withdrawBalance(OfflinePlayer player, double cost) {
|
||||
int amount = convertAmount(cost);
|
||||
return playerPoints.getAPI().take(player.getUniqueId(), amount);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deposit(OfflinePlayer player, double amount) {
|
||||
int amt = convertAmount(amount);
|
||||
return playerPoints.getAPI().give(player.getUniqueId(), amt);
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package com.songoda.core.hooks.economies;
|
||||
|
||||
import net.tnemc.core.Reserve;
|
||||
import net.tnemc.core.economy.EconomyAPI;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class ReserveEconomy extends Economy {
|
||||
|
||||
EconomyAPI economyAPI;
|
||||
|
||||
public ReserveEconomy() {
|
||||
if (Reserve.instance().economyProvided())
|
||||
economyAPI = Reserve.instance().economy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return Reserve.instance().isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Reserve";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatEconomy(double amt) {
|
||||
return economyAPI.format(BigDecimal.valueOf(amt));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBalance(OfflinePlayer player, double cost) {
|
||||
return economyAPI.hasHoldings(player.getUniqueId(), new BigDecimal(cost));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean withdrawBalance(OfflinePlayer player, double cost) {
|
||||
return economyAPI.removeHoldings(player.getUniqueId(), new BigDecimal(cost));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deposit(OfflinePlayer player, double amount) {
|
||||
return economyAPI.addHoldings(player.getUniqueId(), new BigDecimal(amount));
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package com.songoda.core.hooks.economies;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
|
||||
public class VaultEconomy extends Economy {
|
||||
|
||||
private final net.milkbowl.vault.economy.Economy vault;
|
||||
|
||||
public VaultEconomy() {
|
||||
// this returns null if we have Vault with no compatibe eco plugin
|
||||
RegisteredServiceProvider<net.milkbowl.vault.economy.Economy> v = Bukkit.getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class);
|
||||
if(v != null) {
|
||||
this.vault = v.getProvider();
|
||||
} else {
|
||||
// whoopsie!
|
||||
this.vault = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return vault != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Vault";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatEconomy(double amt) {
|
||||
return vault != null ? vault.format(amt) : super.formatEconomy(amt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBalance(OfflinePlayer player, double cost) {
|
||||
return vault != null && vault.has(player, cost);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean withdrawBalance(OfflinePlayer player, double cost) {
|
||||
return vault != null && vault.withdrawPlayer(player, cost).transactionSuccess();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deposit(OfflinePlayer player, double amount) {
|
||||
return vault != null && vault.depositPlayer(player, amount).transactionSuccess();
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package com.songoda.core.hooks.holograms;
|
||||
|
||||
import com.songoda.core.hooks.Hook;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class Holograms implements Hook {
|
||||
|
||||
protected double xOffset = 0.5;
|
||||
protected double yOffset = 0.5;
|
||||
protected double zOffset = 0.5;
|
||||
|
||||
protected final JavaPlugin plugin;
|
||||
|
||||
public Holograms(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public Holograms setPositionOffset(double x, double y, double z) {
|
||||
this.xOffset = x;
|
||||
this.yOffset = y;
|
||||
this.zOffset = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Center and offset this location
|
||||
*
|
||||
* @param location location to offset
|
||||
* @return copy-safe location with the applied offset.
|
||||
*/
|
||||
protected final Location fixLocation(Location location) {
|
||||
double x = location.getX();
|
||||
double y = location.getY();
|
||||
double z = location.getZ();
|
||||
return location.clone().add((x - (int) x) + xOffset, (y - (int) y) + yOffset + defaultHeightOffset(), (z - (int) z) + zOffset);
|
||||
}
|
||||
|
||||
protected abstract double defaultHeightOffset();
|
||||
|
||||
public void createHologram(Location location, String line) {
|
||||
createHologram(location, Collections.singletonList(line));
|
||||
}
|
||||
|
||||
public abstract void createHologram(Location location, List<String> lines);
|
||||
|
||||
public abstract void removeHologram(Location location);
|
||||
|
||||
public void updateHologram(Location location, String line) {
|
||||
updateHologram(location, Collections.singletonList(line));
|
||||
}
|
||||
|
||||
public abstract void updateHologram(Location location, List<String> lines);
|
||||
|
||||
public abstract void removeAllHolograms();
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
package com.songoda.core.hooks.holograms;
|
||||
|
||||
import com.sainttx.holograms.api.Hologram;
|
||||
import com.sainttx.holograms.api.HologramPlugin;
|
||||
import com.sainttx.holograms.api.line.HologramLine;
|
||||
import com.sainttx.holograms.api.line.TextLine;
|
||||
import java.util.HashSet;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.List;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
public class HologramsHolograms extends Holograms {
|
||||
|
||||
HologramPlugin hologramPlugin;
|
||||
HashSet<String> ourHolograms = new HashSet();
|
||||
|
||||
public HologramsHolograms(JavaPlugin plugin) {
|
||||
super(plugin);
|
||||
hologramPlugin = (HologramPlugin) Bukkit.getPluginManager().getPlugin("Holograms");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Holograms";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return hologramPlugin.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double defaultHeightOffset() {
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createHologram(Location location, List<String> lines) {
|
||||
createAt(fixLocation(location), lines);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeHologram(Location location) {
|
||||
location = fixLocation(location);
|
||||
final String id = locStr(location);
|
||||
Hologram hologram = hologramPlugin.getHologramManager().getHologram(id);
|
||||
if (hologram != null) {
|
||||
hologram.despawn();
|
||||
hologramPlugin.getHologramManager().removeActiveHologram(hologram);
|
||||
}
|
||||
ourHolograms.remove(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllHolograms() {
|
||||
for(String id : ourHolograms) {
|
||||
Hologram hologram = hologramPlugin.getHologramManager().getHologram(id);
|
||||
if (hologram != null) {
|
||||
hologram.despawn();
|
||||
hologramPlugin.getHologramManager().removeActiveHologram(hologram);
|
||||
}
|
||||
}
|
||||
ourHolograms.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateHologram(Location location, List<String> lines) {
|
||||
location = fixLocation(location);
|
||||
Hologram hologram = hologramPlugin.getHologramManager().getHologram(locStr(location));
|
||||
if (hologram != null) {
|
||||
for(HologramLine line : hologram.getLines().toArray(new HologramLine[0])) {
|
||||
hologram.removeLine(line);
|
||||
}
|
||||
for (String line : lines) {
|
||||
hologram.addLine(new TextLine(hologram, line));
|
||||
}
|
||||
return;
|
||||
}
|
||||
createAt(location, lines);
|
||||
}
|
||||
|
||||
private String locStr(Location loc) {
|
||||
return String.format("%s-%d-%d-%d", loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ());
|
||||
}
|
||||
|
||||
private void createAt(Location location, List<String> lines) {
|
||||
|
||||
final String id = locStr(location);
|
||||
Hologram hologram = new Hologram(id, location);
|
||||
for (String line : lines) {
|
||||
hologram.addLine(new TextLine(hologram, line));
|
||||
}
|
||||
|
||||
hologramPlugin.getHologramManager().addActiveHologram(hologram);
|
||||
|
||||
if(!ourHolograms.contains(id))
|
||||
ourHolograms.add(id);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package com.songoda.core.hooks.holograms;
|
||||
|
||||
import com.gmail.filoghost.holographicdisplays.api.Hologram;
|
||||
import com.gmail.filoghost.holographicdisplays.api.HologramsAPI;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class HolographicDisplaysHolograms extends Holograms {
|
||||
|
||||
public HolographicDisplaysHolograms(JavaPlugin plugin) {
|
||||
super(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "HolographicDisplays";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double defaultHeightOffset() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createHologram(Location location, List<String> lines) {
|
||||
createAt(fixLocation(location), lines);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeHologram(Location location) {
|
||||
location = fixLocation(location);
|
||||
for (Hologram hologram : HologramsAPI.getHolograms(plugin)) {
|
||||
if (hologram.getX() != location.getX()
|
||||
|| hologram.getY() != location.getY()
|
||||
|| hologram.getZ() != location.getZ()) continue;
|
||||
hologram.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateHologram(Location location, List<String> lines) {
|
||||
location = fixLocation(location);
|
||||
for (Hologram hologram : HologramsAPI.getHolograms(plugin)) {
|
||||
if (hologram.getX() != location.getX()
|
||||
|| hologram.getY() != location.getY()
|
||||
|| hologram.getZ() != location.getZ()) continue;
|
||||
hologram.clearLines();
|
||||
for (String line : lines) {
|
||||
hologram.appendTextLine(line);
|
||||
}
|
||||
return;
|
||||
}
|
||||
createAt(location, lines);
|
||||
}
|
||||
|
||||
private void createAt(Location location, List<String> lines) {
|
||||
Hologram hologram = HologramsAPI.createHologram(plugin, location);
|
||||
for (String line : lines) {
|
||||
hologram.appendTextLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllHolograms() {
|
||||
HologramsAPI.getHolograms(plugin).stream().forEach(x -> x.delete());
|
||||
}
|
||||
|
||||
}
|
75
src/main/java/com/songoda/core/hooks/stackers/StackMob.java
Normal file
75
src/main/java/com/songoda/core/hooks/stackers/StackMob.java
Normal file
@ -0,0 +1,75 @@
|
||||
package com.songoda.core.hooks.stackers;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Item;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import uk.antiperson.stackmob.api.EntityManager;
|
||||
import uk.antiperson.stackmob.api.StackedEntity;
|
||||
|
||||
public class StackMob extends Stacker {
|
||||
|
||||
private final EntityManager plugin;
|
||||
|
||||
public StackMob() {
|
||||
this.plugin = new EntityManager((uk.antiperson.stackmob.StackMob) Bukkit.getPluginManager().getPlugin("StackMob"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "StackMob";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsItemStacking() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsEntityStacking() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't do it.
|
||||
*/
|
||||
@Override
|
||||
public void setItemAmount(Item item, int amount) {
|
||||
// idk, if you ignored the warnings and still use this method, we can at least try to help out
|
||||
item.getItemStack().setAmount(amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* If you use this method, you're pretty lazy. Didn't you see supportsItemStacking()?
|
||||
*/
|
||||
@Override
|
||||
public int getItemAmount(Item item) {
|
||||
return item.getItemStack().getAmount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStacked(LivingEntity entity) {
|
||||
return plugin.isStackedEntity(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize(LivingEntity entity) {
|
||||
return plugin.getStackedEntity(entity).getSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(LivingEntity entity, int amount) {
|
||||
StackedEntity stackedEntity = plugin.getStackedEntity(entity);
|
||||
stackedEntity.setSize(stackedEntity.getSize() - amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(LivingEntity entity, int amount) {
|
||||
StackedEntity stackedEntity = plugin.getStackedEntity(entity);
|
||||
stackedEntity.setSize(stackedEntity.getSize() + amount);
|
||||
}
|
||||
}
|
32
src/main/java/com/songoda/core/hooks/stackers/Stacker.java
Normal file
32
src/main/java/com/songoda/core/hooks/stackers/Stacker.java
Normal file
@ -0,0 +1,32 @@
|
||||
package com.songoda.core.hooks.stackers;
|
||||
|
||||
import com.songoda.core.hooks.Hook;
|
||||
import org.bukkit.entity.Item;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
|
||||
public abstract class Stacker implements Hook {
|
||||
|
||||
public abstract boolean supportsItemStacking();
|
||||
|
||||
public abstract boolean supportsEntityStacking();
|
||||
|
||||
public abstract void setItemAmount(Item item, int amount);
|
||||
|
||||
public abstract int getItemAmount(Item item);
|
||||
|
||||
public abstract boolean isStacked(LivingEntity entity);
|
||||
|
||||
public abstract int getSize(LivingEntity entity);
|
||||
|
||||
public void removeOne(LivingEntity entity) {
|
||||
remove(entity, 1);
|
||||
}
|
||||
|
||||
public abstract void remove(LivingEntity entity, int amount);
|
||||
|
||||
public void addOne(LivingEntity entity) {
|
||||
add(entity, 1);
|
||||
}
|
||||
|
||||
public abstract void add(LivingEntity entity, int amount);
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package com.songoda.core.hooks.stackers;
|
||||
|
||||
import com.songoda.ultimatestacker.entity.EntityStack;
|
||||
import com.songoda.ultimatestacker.utils.Methods;
|
||||
import java.lang.reflect.Method;
|
||||
import org.bukkit.entity.Item;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
|
||||
public class UltimateStacker extends Stacker {
|
||||
|
||||
private final com.songoda.ultimatestacker.UltimateStacker plugin;
|
||||
private boolean oldItemMethods = false;
|
||||
private Method oldUltimateStacker_updateItemAmount;
|
||||
|
||||
public UltimateStacker() {
|
||||
this.plugin = com.songoda.ultimatestacker.UltimateStacker.getInstance();
|
||||
try {
|
||||
oldUltimateStacker_updateItemAmount = com.songoda.ultimatestacker.utils.Methods.class.getDeclaredMethod("updateItemAmount", Item.class, int.class);
|
||||
oldItemMethods = true;
|
||||
} catch (NoSuchMethodException | SecurityException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "UltimateStacker";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return plugin.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsItemStacking() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsEntityStacking() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemAmount(Item item, int amount) {
|
||||
if (oldItemMethods) {
|
||||
// TODO: direct reference when this is re-added to the API
|
||||
try {
|
||||
oldUltimateStacker_updateItemAmount.invoke(null, item, amount);
|
||||
} catch (Exception ex) {
|
||||
item.remove(); // not the best solution, but prevents duping
|
||||
}
|
||||
} else {
|
||||
Methods.updateItemAmount(item, item.getItemStack(), amount);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemAmount(Item item) {
|
||||
return Methods.getActualItemAmount(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStacked(LivingEntity entity) {
|
||||
return plugin.getEntityStackManager().isStacked(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize(LivingEntity entity) {
|
||||
return isStacked(entity) ? plugin.getEntityStackManager().getStack(entity).getAmount() : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(LivingEntity entity, int amount) {
|
||||
EntityStack stack = plugin.getEntityStackManager().getStack(entity);
|
||||
stack.setAmount(stack.getAmount() - amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(LivingEntity entity, int amount) {
|
||||
plugin.getEntityStackManager().getStack(entity).addAmount(amount);
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package com.songoda.core.hooks.stackers;
|
||||
|
||||
import com.bgsoftware.wildstacker.api.WildStackerAPI;
|
||||
import com.bgsoftware.wildstacker.api.objects.StackedEntity;
|
||||
import org.bukkit.entity.Item;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
|
||||
public class WildStacker extends Stacker {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "WildStacker";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsItemStacking() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsEntityStacking() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemAmount(Item item, int amount) {
|
||||
WildStackerAPI.getStackedItem(item).setStackAmount(amount, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemAmount(Item item) {
|
||||
return WildStackerAPI.getItemAmount(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStacked(LivingEntity entity) {
|
||||
return WildStackerAPI.getEntityAmount(entity) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize(LivingEntity entity) {
|
||||
return WildStackerAPI.getEntityAmount(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(LivingEntity entity, int amount) {
|
||||
StackedEntity stackedEntity = WildStackerAPI.getStackedEntity(entity);
|
||||
stackedEntity.setStackAmount(stackedEntity.getStackAmount() - amount, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(LivingEntity entity, int amount) {
|
||||
StackedEntity stackedEntity = WildStackerAPI.getStackedEntity(entity);
|
||||
stackedEntity.setStackAmount(stackedEntity.getStackAmount() + amount, true);
|
||||
}
|
||||
}
|
@ -0,0 +1,326 @@
|
||||
/**
|
||||
* Hooks for adding a custom WorldGuard flag
|
||||
*
|
||||
* Note: Hooks must be added before WG loads!
|
||||
*/
|
||||
package com.songoda.core.hooks.worldguard;
|
||||
|
||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||
import com.sk89q.worldedit.math.BlockVector3;
|
||||
import com.sk89q.worldguard.WorldGuard;
|
||||
import com.sk89q.worldguard.protection.ApplicableRegionSet;
|
||||
import com.sk89q.worldguard.protection.association.RegionAssociable;
|
||||
import com.sk89q.worldguard.protection.flags.Flag;
|
||||
import com.sk89q.worldguard.protection.flags.StateFlag;
|
||||
import com.sk89q.worldguard.protection.flags.StateFlag.State;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion;
|
||||
import com.sk89q.worldguard.protection.regions.RegionContainer;
|
||||
import com.sk89q.worldguard.protection.regions.RegionQuery;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Stream;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
|
||||
public class WorldGuardFlagHandler {
|
||||
|
||||
static Boolean wgPlugin = null;
|
||||
static Object worldGuardPlugin;
|
||||
static boolean legacy_v6 = false;
|
||||
static boolean legacy_v5 = false;
|
||||
static boolean hooksInstalled = false;
|
||||
static Map<String, Object> flags = new HashMap();
|
||||
|
||||
/**
|
||||
* Attempt to register a worldGuard flag (ALLOW/DENY) <br />
|
||||
* Note: This must be called before WorldGuard loads, or it will fail.
|
||||
*
|
||||
* @param flag name of the flag to set
|
||||
* @param state default value of the flag
|
||||
*/
|
||||
public static void addHook(String flag, boolean state) {
|
||||
if (wgPlugin == null && (wgPlugin = (worldGuardPlugin = Bukkit.getPluginManager().getPlugin("WorldGuard")) != null)) {
|
||||
try {
|
||||
// if this class exists, we're on 6.0
|
||||
Class.forName("com.sk89q.worldguard.protection.flags.registry.SimpleFlagRegistry");
|
||||
legacy_v6 = true;
|
||||
} catch (ClassNotFoundException ex) {
|
||||
}
|
||||
if(!legacy_v6) {
|
||||
try {
|
||||
// if this class exists, we're on 5.x
|
||||
Class.forName("com.sk89q.worldguard.protection.flags.DefaultFlag");
|
||||
legacy_v5 = true;
|
||||
} catch (ClassNotFoundException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!wgPlugin) return;
|
||||
|
||||
if (legacy_v6 || legacy_v5) {
|
||||
addLegacyHook(flag, state);
|
||||
return;
|
||||
}
|
||||
|
||||
StateFlag addFlag = new StateFlag(flag, state);
|
||||
try {
|
||||
WorldGuard.getInstance().getFlagRegistry().register(addFlag);
|
||||
flags.put(flag, addFlag);
|
||||
} catch (Exception ex) {
|
||||
Bukkit.getServer().getLogger().log(Level.WARNING, "Could not add flag {0} to WorldGuard", addFlag.getName());
|
||||
Flag wgFlag = (StateFlag) WorldGuard.getInstance().getFlagRegistry().get(addFlag.getName());
|
||||
if (wgFlag == null) {
|
||||
wgPlugin = false;
|
||||
Bukkit.getServer().getLogger().log(Level.WARNING, "Could not hook WorldGuard");
|
||||
} else {
|
||||
flags.put(flag, wgFlag);
|
||||
Bukkit.getServer().getLogger().log(Level.WARNING, "Loaded existing {1} {0}", new Object[] {wgFlag.getName(), wgFlag.getClass().getSimpleName()});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reflection to add hooks
|
||||
private static void addLegacyHook(String flag, boolean state) {
|
||||
try {
|
||||
// 6.0 has the same classpath for StateFlag as the current version does
|
||||
// does this flag exist already?
|
||||
Class defaultFlagClazz = Class.forName("com.sk89q.worldguard.protection.flags.DefaultFlag");
|
||||
Field flagField = defaultFlagClazz.getField("flagsList");
|
||||
Flag<?>[] flagsOld = (Flag<?>[]) flagField.get(null);
|
||||
Flag wgFlag = Stream.of(flagsOld)
|
||||
.filter(f -> ((Flag<?>)f).getName().equalsIgnoreCase(flag))
|
||||
.findFirst().orElse(null);
|
||||
if (wgFlag != null) {
|
||||
// we already have one
|
||||
flags.put(flag, wgFlag);
|
||||
Bukkit.getServer().getLogger().log(Level.WARNING, "Loaded existing {1} {0}", new Object[] {wgFlag.getName(), wgFlag.getClass().getSimpleName()});
|
||||
return;
|
||||
}
|
||||
|
||||
// if not, we need to add one
|
||||
wgFlag = new StateFlag(flag, state);
|
||||
|
||||
// we need to sneak our flag into the array
|
||||
// make a copy first
|
||||
Flag<?>[] flagsNew = new Flag<?>[flagsOld.length + 1];
|
||||
System.arraycopy(flagsOld, 0, flagsNew, 0, flagsOld.length);
|
||||
|
||||
// add ours
|
||||
flagsNew[flagsNew.length - 1] = wgFlag;
|
||||
|
||||
// and put the new list into place
|
||||
setStaticField(flagField, flagsNew);
|
||||
|
||||
if(legacy_v6) {
|
||||
// register this flag in the registry
|
||||
Object flagRegistry = getPrivateField(worldGuardPlugin.getClass(), worldGuardPlugin, "flagRegistry");
|
||||
Class simpleFlagRegistryClazz = Class.forName("com.sk89q.worldguard.protection.flags.registry.SimpleFlagRegistry");
|
||||
Method registerSimpleFlagRegistry = simpleFlagRegistryClazz.getDeclaredMethod("register", Flag.class);
|
||||
registerSimpleFlagRegistry.invoke(flagRegistry, wgFlag);
|
||||
}
|
||||
|
||||
// all good!
|
||||
flags.put(flag, wgFlag);
|
||||
} catch (Exception ex) {
|
||||
//Bukkit.getServer().getLogger().log(Level.WARNING, "Failed to set legacy WorldGuard Flags", ex);
|
||||
Bukkit.getServer().getLogger().log(Level.WARNING, "Could not add flag {0} to WorldGuard", flag);
|
||||
Bukkit.getServer().getLogger().log(Level.WARNING, "Could not hook WorldGuard");
|
||||
wgPlugin = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static Object getPrivateField(Class<?> c, Object handle, String fieldName) throws Exception {
|
||||
Field field = c.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(handle);
|
||||
}
|
||||
|
||||
private static void setStaticField(Field field, Object value) throws Exception {
|
||||
field.setAccessible(true);
|
||||
Field modifier = Field.class.getDeclaredField("modifiers");
|
||||
modifier.setAccessible(true);
|
||||
modifier.setInt(field, field.getModifiers() & ~Modifier.FINAL);
|
||||
field.set(null, value);
|
||||
}
|
||||
|
||||
public static boolean isEnabled() {
|
||||
return wgPlugin != null && wgPlugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks this location to see what this flag is set to
|
||||
* @param l location to check
|
||||
* @param flag ALLOW/DENY flag to check
|
||||
* @return flag state, or null if undefined
|
||||
*/
|
||||
public static Boolean getBooleanFlag(Location l, String flag) {
|
||||
if (wgPlugin == null || !wgPlugin) return null;
|
||||
Object flagObj = flags.get(flag);
|
||||
// There's a different way to get this in the old version
|
||||
if (legacy_v6 || legacy_v5)
|
||||
return flagObj == null ? null : getBooleanFlagLegacy(l, flagObj);
|
||||
|
||||
// for convinience, we can load a flag if we don't know it
|
||||
if (flagObj == null && !legacy_v6)
|
||||
flags.put(flag, flagObj = WorldGuard.getInstance().getFlagRegistry().get(flag));
|
||||
|
||||
// so, what's up?
|
||||
if (flagObj instanceof StateFlag) {
|
||||
RegionContainer container = WorldGuard.getInstance().getPlatform().getRegionContainer();
|
||||
RegionQuery query = container.createQuery();
|
||||
com.sk89q.worldedit.util.Location loc = BukkitAdapter.adapt(l);
|
||||
return query.testState(loc, (RegionAssociable) null, (StateFlag) flagObj);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query all regions that are in or intersect this chunk
|
||||
* @param c chunk to check for regions in
|
||||
* @param flag ALLOW/DENY flag to check
|
||||
* @return flag state, or null if undefined
|
||||
*/
|
||||
public static Boolean getBooleanFlag(Chunk c, String flag) {
|
||||
if (wgPlugin == null || !wgPlugin) return null;
|
||||
Object flagObj = flags.get(flag);
|
||||
// There's a different way to get this in the old version
|
||||
if (legacy_v6 || legacy_v5)
|
||||
return flagObj == null ? null : getBooleanFlagLegacy(c, flagObj);
|
||||
|
||||
// for convinience, we can load a flag if we don't know it
|
||||
if (flagObj == null)
|
||||
flags.put(flag, flagObj = WorldGuard.getInstance().getFlagRegistry().get(flag));
|
||||
|
||||
// so, what's up?
|
||||
if (flagObj instanceof StateFlag) {
|
||||
RegionManager worldManager = WorldGuard.getInstance().getPlatform().getRegionContainer().get(BukkitAdapter.adapt(c.getWorld()));
|
||||
if (worldManager == null)
|
||||
return null;
|
||||
ProtectedCuboidRegion chunkRegion = new ProtectedCuboidRegion("__TEST__",
|
||||
BlockVector3.at(c.getX() << 4, c.getWorld().getMaxHeight(), c.getZ() << 4),
|
||||
BlockVector3.at((c.getX() << 4) + 15, 0, (c.getZ() << 4) + 15));
|
||||
ApplicableRegionSet set = worldManager.getApplicableRegions(chunkRegion);
|
||||
State result = set.queryState((RegionAssociable) null, (StateFlag) flagObj);
|
||||
if (result == null && set.size() == 0)
|
||||
return null;
|
||||
return result == State.ALLOW;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static Method legacy_getRegionManager = null;
|
||||
static Method legacy_getApplicableRegions_Region = null;
|
||||
static Method legacy_getApplicableRegions_Location = null;
|
||||
static Method legacy5_applicableRegionSet_getFlag = null;
|
||||
static Constructor legacy_newProtectedCuboidRegion;
|
||||
static Class legacy_blockVectorClazz;
|
||||
static Constructor legacy_newblockVector;
|
||||
|
||||
private static Boolean getBooleanFlagLegacy(Location l, Object flag) {
|
||||
try {
|
||||
// cache reflection methods
|
||||
if (legacy_getRegionManager == null) {
|
||||
legacy_getRegionManager = worldGuardPlugin.getClass()
|
||||
.getDeclaredMethod("getRegionManager", org.bukkit.World.class);
|
||||
legacy_getApplicableRegions_Region = RegionManager.class.getDeclaredMethod("getApplicableRegions",
|
||||
Class.forName("com.sk89q.worldguard.protection.regions.ProtectedRegion"));
|
||||
legacy_getApplicableRegions_Location = RegionManager.class.getDeclaredMethod("getApplicableRegions",
|
||||
Location.class);
|
||||
legacy_blockVectorClazz = Class.forName("com.sk89q.worldedit.BlockVector");
|
||||
legacy_newblockVector = legacy_blockVectorClazz.getConstructor(int.class, int.class, int.class);
|
||||
legacy_newProtectedCuboidRegion = Class.forName("com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion")
|
||||
.getConstructor(String.class, legacy_blockVectorClazz, legacy_blockVectorClazz);
|
||||
}
|
||||
|
||||
// grab the applicable manager for this world
|
||||
Object worldManager = (RegionManager) legacy_getRegionManager.invoke(worldGuardPlugin, l.getWorld());
|
||||
if (worldManager == null)
|
||||
return null;
|
||||
|
||||
// now look for any intersecting regions
|
||||
Object set = legacy_getApplicableRegions_Region.invoke(worldManager, l);
|
||||
|
||||
// so what's the verdict?
|
||||
State result = null;
|
||||
if(legacy_v6) {
|
||||
set = ((ApplicableRegionSet) set).queryState((RegionAssociable) null, (StateFlag) flag);
|
||||
} else {
|
||||
// v5 has a different class signature for ApplicableRegionSet
|
||||
// also doesn't have a "queryState" function
|
||||
//getFlag(T flag)
|
||||
if(legacy5_applicableRegionSet_getFlag == null) {
|
||||
legacy5_applicableRegionSet_getFlag = Class.forName("com.sk89q.worldguard.protection.ApplicableRegionSet").getMethod("getFlag", Object.class);
|
||||
}
|
||||
result = (State) legacy5_applicableRegionSet_getFlag.invoke(set, flag);
|
||||
}
|
||||
if (result == null && set != null && ((Iterable) set).iterator().hasNext())
|
||||
return null;
|
||||
return result == State.ALLOW;
|
||||
|
||||
} catch (Exception ex) {
|
||||
Bukkit.getServer().getLogger().log(Level.WARNING, "Could not grab flags from WorldGuard", ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Boolean getBooleanFlagLegacy(Chunk c, Object flag) {
|
||||
// ApplicableRegionSet and RegionManager have the same classpath as the current version
|
||||
// ProtectedCuboidRegion uses a different constructor, though
|
||||
try {
|
||||
// cache reflection methods
|
||||
if (legacy_getRegionManager == null) {
|
||||
legacy_getRegionManager = worldGuardPlugin.getClass()
|
||||
.getDeclaredMethod("getRegionManager", org.bukkit.World.class);
|
||||
legacy_getApplicableRegions_Region = RegionManager.class.getDeclaredMethod("getApplicableRegions",
|
||||
Class.forName("com.sk89q.worldguard.protection.regions.ProtectedRegion"));
|
||||
legacy_getApplicableRegions_Location = RegionManager.class.getDeclaredMethod("getApplicableRegions",
|
||||
Location.class);
|
||||
legacy_blockVectorClazz = Class.forName("com.sk89q.worldedit.BlockVector");
|
||||
legacy_newblockVector = legacy_blockVectorClazz.getConstructor(int.class, int.class, int.class);
|
||||
legacy_newProtectedCuboidRegion = Class.forName("com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion")
|
||||
.getConstructor(String.class, legacy_blockVectorClazz, legacy_blockVectorClazz);
|
||||
}
|
||||
|
||||
// grab the applicable manager for this world
|
||||
Object worldManager = (RegionManager) legacy_getRegionManager.invoke(worldGuardPlugin, c.getWorld());
|
||||
if (worldManager == null)
|
||||
return null;
|
||||
|
||||
// Create a legacy ProtectedCuboidRegion
|
||||
Object chunkRegion = legacy_newProtectedCuboidRegion.newInstance("__TEST__",
|
||||
legacy_newblockVector.newInstance(c.getX() << 4, c.getWorld().getMaxHeight(), c.getZ() << 4),
|
||||
legacy_newblockVector.newInstance((c.getX() << 4) + 15, 0, (c.getZ() << 4) + 15));
|
||||
|
||||
// now look for any intersecting regions
|
||||
Object set = legacy_getApplicableRegions_Region.invoke(worldManager, chunkRegion);
|
||||
|
||||
// so what's the verdict?
|
||||
State result = null;
|
||||
if(legacy_v6) {
|
||||
set = ((ApplicableRegionSet) set).queryState((RegionAssociable) null, (StateFlag) flag);
|
||||
} else {
|
||||
// v5 has a different class signature for ApplicableRegionSet
|
||||
// also doesn't have a "queryState" function
|
||||
//getFlag(T flag)
|
||||
if(legacy5_applicableRegionSet_getFlag == null) {
|
||||
legacy5_applicableRegionSet_getFlag = Class.forName("com.sk89q.worldguard.protection.ApplicableRegionSet").getMethod("getFlag", Flag.class);
|
||||
}
|
||||
result = (State) legacy5_applicableRegionSet_getFlag.invoke(set, flag);
|
||||
}
|
||||
if (result == null && set != null && ((Iterable) set).iterator().hasNext())
|
||||
return null;
|
||||
return result == State.ALLOW;
|
||||
|
||||
} catch (Exception ex) {
|
||||
Bukkit.getServer().getLogger().log(Level.WARNING, "Could not grab flags from WorldGuard", ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
145
src/main/java/com/songoda/core/input/ChatPrompt.java
Normal file
145
src/main/java/com/songoda/core/input/ChatPrompt.java
Normal file
@ -0,0 +1,145 @@
|
||||
package com.songoda.core.input;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
public class ChatPrompt implements Listener {
|
||||
|
||||
private static final List<UUID> registered = new ArrayList<>();
|
||||
|
||||
private final ChatConfirmHandler handler;
|
||||
private OnClose onClose = null;
|
||||
private OnCancel onCancel = null;
|
||||
private Listener listener;
|
||||
|
||||
private ChatPrompt(Player player, ChatConfirmHandler hander) {
|
||||
this.handler = hander;
|
||||
registered.add(player.getUniqueId());
|
||||
}
|
||||
|
||||
public static ChatPrompt showPrompt(Plugin plugin, Player player, ChatConfirmHandler hander) {
|
||||
ChatPrompt prompt = new ChatPrompt(player, hander);
|
||||
prompt.startListener(plugin);
|
||||
player.closeInventory();
|
||||
return prompt;
|
||||
}
|
||||
|
||||
public static ChatPrompt showPrompt(Plugin plugin, Player player, String message, ChatConfirmHandler hander) {
|
||||
ChatPrompt prompt = new ChatPrompt(player, hander);
|
||||
prompt.startListener(plugin);
|
||||
player.closeInventory();
|
||||
if (message != null)
|
||||
player.sendMessage(message);
|
||||
return prompt;
|
||||
}
|
||||
|
||||
public static boolean isRegistered(Player player) {
|
||||
return registered.contains(player.getUniqueId());
|
||||
}
|
||||
|
||||
public static boolean unregister(Player player) {
|
||||
return registered.remove(player.getUniqueId());
|
||||
}
|
||||
|
||||
public ChatPrompt setOnClose(OnClose onClose) {
|
||||
this.onClose = onClose;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ChatPrompt setOnCancel(OnCancel onCancel) {
|
||||
this.onCancel = onCancel;
|
||||
return this;
|
||||
}
|
||||
|
||||
private void startListener(Plugin plugin) {
|
||||
this.listener = new Listener() {
|
||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = false)
|
||||
public void onChat(AsyncPlayerChatEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
if (!ChatPrompt.isRegistered(player)) return;
|
||||
|
||||
ChatPrompt.unregister(player);
|
||||
event.setCancelled(true);
|
||||
|
||||
ChatConfirmEvent chatConfirmEvent = new ChatConfirmEvent(player, event.getMessage());
|
||||
|
||||
player.sendMessage("\u00BB " + event.getMessage());
|
||||
try {
|
||||
handler.onChat(chatConfirmEvent);
|
||||
} catch (Throwable t) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Failed to process chat prompt", t);
|
||||
}
|
||||
|
||||
if (onClose != null) {
|
||||
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () ->
|
||||
onClose.onClose(), 0L);
|
||||
}
|
||||
HandlerList.unregisterAll(listener);
|
||||
}
|
||||
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = false)
|
||||
public void onCancel(PlayerCommandPreprocessEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
if (!ChatPrompt.isRegistered(player)) return;
|
||||
|
||||
ChatPrompt.unregister(player);
|
||||
|
||||
if(event.getMessage().toLowerCase().startsWith("/cancel"))
|
||||
event.setCancelled(true);
|
||||
|
||||
if (onCancel != null) {
|
||||
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () ->
|
||||
onCancel.onCancel(), 0L);
|
||||
} else if (onClose != null) {
|
||||
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () ->
|
||||
onClose.onClose(), 0L);
|
||||
}
|
||||
HandlerList.unregisterAll(listener);
|
||||
}
|
||||
};
|
||||
|
||||
Bukkit.getPluginManager().registerEvents(listener, plugin);
|
||||
}
|
||||
|
||||
public static interface ChatConfirmHandler {
|
||||
void onChat(ChatConfirmEvent event);
|
||||
}
|
||||
|
||||
public static interface OnClose {
|
||||
void onClose();
|
||||
}
|
||||
|
||||
public static interface OnCancel {
|
||||
void onCancel();
|
||||
}
|
||||
|
||||
public static class ChatConfirmEvent {
|
||||
|
||||
private final Player player;
|
||||
private final String message;
|
||||
|
||||
public ChatConfirmEvent(Player player, String message) {
|
||||
this.player = player;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
147
src/main/java/com/songoda/core/input/ClickableChat.java
Normal file
147
src/main/java/com/songoda/core/input/ClickableChat.java
Normal file
@ -0,0 +1,147 @@
|
||||
package com.songoda.core.input;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.songoda.core.compatibility.ServerVersion;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Send chat packets with embedded links
|
||||
*
|
||||
* @since 2019-09-01
|
||||
* @author jascotty2
|
||||
*/
|
||||
public class ClickableChat {
|
||||
|
||||
private static final Gson gson = new GsonBuilder().create();
|
||||
List<JsonObject> textList = new ArrayList();
|
||||
|
||||
public void clear() {
|
||||
textList.clear();
|
||||
}
|
||||
|
||||
public ClickableChat addMessage(String s) {
|
||||
JsonObject txt = new JsonObject();
|
||||
txt.addProperty("text", s);
|
||||
textList.add(txt);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClickableChat addRunCommand(String text, String hoverText, String cmd) {
|
||||
JsonObject txt = new JsonObject();
|
||||
txt.addProperty("text", text);
|
||||
JsonObject hover = new JsonObject();
|
||||
hover.addProperty("action", "show_text");
|
||||
hover.addProperty("value", hoverText);
|
||||
txt.add("hoverEvent", hover);
|
||||
JsonObject click = new JsonObject();
|
||||
click.addProperty("action", "run_command");
|
||||
click.addProperty("value", cmd);
|
||||
txt.add("clickEvent", click);
|
||||
textList.add(txt);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClickableChat addPromptCommand(String text, String hoverText, String cmd) {
|
||||
JsonObject txt = new JsonObject();
|
||||
txt.addProperty("text", text);
|
||||
JsonObject hover = new JsonObject();
|
||||
hover.addProperty("action", "show_text");
|
||||
hover.addProperty("value", hoverText);
|
||||
txt.add("hoverEvent", hover);
|
||||
JsonObject click = new JsonObject();
|
||||
click.addProperty("action", "suggest_command");
|
||||
click.addProperty("value", cmd);
|
||||
txt.add("clickEvent", click);
|
||||
textList.add(txt);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClickableChat addURL(String text, String hoverText, String url) {
|
||||
JsonObject txt = new JsonObject();
|
||||
txt.addProperty("text", text);
|
||||
JsonObject hover = new JsonObject();
|
||||
hover.addProperty("action", "show_text");
|
||||
hover.addProperty("value", hoverText);
|
||||
txt.add("hoverEvent", hover);
|
||||
JsonObject click = new JsonObject();
|
||||
click.addProperty("action", "open_url");
|
||||
click.addProperty("value", url);
|
||||
txt.add("clickEvent", hover);
|
||||
textList.add(txt);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return gson.toJson(textList);
|
||||
}
|
||||
|
||||
public void sendTo(Player p) {
|
||||
if (enabled) {
|
||||
try {
|
||||
Object packet = mc_PacketPlayOutChat_new.newInstance(mc_IChatBaseComponent_ChatSerializer_a.invoke(null, this.toString()));
|
||||
Object cbPlayer = cb_craftPlayer_getHandle.invoke(p);
|
||||
Object mcConnection = mc_entityPlayer_playerConnection.get(cbPlayer);
|
||||
mc_playerConnection_sendPacket.invoke(mcConnection, packet);
|
||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Problem preparing raw chat packets (disabling further packets)", ex);
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean enabled = ServerVersion.isServerVersionAtLeast(ServerVersion.V1_8);
|
||||
|
||||
private static Method mc_IChatBaseComponent_ChatSerializer_a;
|
||||
private static Constructor mc_PacketPlayOutChat_new;
|
||||
private static Method cb_craftPlayer_getHandle;
|
||||
private static Field mc_entityPlayer_playerConnection;
|
||||
private static Method mc_playerConnection_sendPacket;
|
||||
|
||||
static {
|
||||
init();
|
||||
}
|
||||
|
||||
static void init() {
|
||||
if (enabled) {
|
||||
try {
|
||||
|
||||
final String version = ServerVersion.getServerVersionString();
|
||||
Class cb_craftPlayerClazz;
|
||||
Class mc_entityPlayerClazz;
|
||||
Class mc_playerConnectionClazz;
|
||||
Class mc_PacketInterface;
|
||||
Class mc_IChatBaseComponent;
|
||||
Class mc_IChatBaseComponent_ChatSerializer;
|
||||
Class mc_PacketPlayOutChat;
|
||||
|
||||
cb_craftPlayerClazz = Class.forName("org.bukkit.craftbukkit." + version + ".entity.CraftPlayer");
|
||||
cb_craftPlayer_getHandle = cb_craftPlayerClazz.getDeclaredMethod("getHandle");
|
||||
mc_entityPlayerClazz = Class.forName("net.minecraft.server." + version + ".EntityPlayer");
|
||||
mc_entityPlayer_playerConnection = mc_entityPlayerClazz.getDeclaredField("playerConnection");
|
||||
mc_playerConnectionClazz = Class.forName("net.minecraft.server." + version + ".PlayerConnection");
|
||||
mc_PacketInterface = Class.forName("net.minecraft.server." + version + ".Packet");
|
||||
mc_playerConnection_sendPacket = mc_playerConnectionClazz.getDeclaredMethod("sendPacket", mc_PacketInterface);
|
||||
mc_IChatBaseComponent = Class.forName("net.minecraft.server." + version + ".IChatBaseComponent");
|
||||
mc_IChatBaseComponent_ChatSerializer = Class.forName("net.minecraft.server." + version + ".IChatBaseComponent$ChatSerializer");
|
||||
mc_IChatBaseComponent_ChatSerializer_a = mc_IChatBaseComponent_ChatSerializer.getMethod("a", String.class);
|
||||
mc_PacketPlayOutChat = Class.forName("net.minecraft.server." + version + ".PacketPlayOutChat");
|
||||
mc_PacketPlayOutChat_new = mc_PacketPlayOutChat.getConstructor(mc_IChatBaseComponent);
|
||||
|
||||
} catch (Throwable ex) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Problem preparing raw chat packets (disabling further packets)", ex);
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
356
src/main/java/com/songoda/core/locale/Locale.java
Normal file
356
src/main/java/com/songoda/core/locale/Locale.java
Normal file
@ -0,0 +1,356 @@
|
||||
package com.songoda.core.locale;
|
||||
|
||||
import com.songoda.core.utils.TextUtils;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
/**
|
||||
* Assists in the utilization of localization files. <br>
|
||||
* Created to be used by the Songoda Team. <br>
|
||||
* NOTE: Using this class in multiple plugins requires shading! <br>
|
||||
* Updated 2019-09-01 to support UTF encoded lang files - jascotty2
|
||||
*
|
||||
* @author Brianna O'Keefe - Songoda
|
||||
*/
|
||||
public class Locale {
|
||||
|
||||
private static final Pattern NODE_PATTERN = Pattern.compile("^([^ ]+)\\s*=\\s*\"?(.*?)\"?$");
|
||||
private static final String FILE_EXTENSION = ".lang";
|
||||
|
||||
private final Map<String, String> nodes = new HashMap<>();
|
||||
private final Plugin plugin;
|
||||
private final File file;
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* Instantiate the Locale class for future use
|
||||
*
|
||||
* @param plugin Owning Plugin
|
||||
* @param file Location of the locale file
|
||||
* @param name The locale name for the language
|
||||
*/
|
||||
public Locale(Plugin plugin, File file, String name) {
|
||||
this.plugin = plugin;
|
||||
this.file = file;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a default-included lang file from the plugin's jar file
|
||||
*
|
||||
* @param plugin plugin to load from
|
||||
* @param name name of the default locale, eg "en_US"
|
||||
* @return returns the loaded Locale, or null if there was an error
|
||||
*/
|
||||
public static Locale loadDefaultLocale(JavaPlugin plugin, String name) {
|
||||
saveDefaultLocale(plugin, name, name);
|
||||
return loadLocale(plugin, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a locale from this plugin's locale directory
|
||||
*
|
||||
* @param plugin plugin to load from
|
||||
* @param name name of the locale, eg "en_US"
|
||||
* @return returns the loaded Locale, or null if there was an error
|
||||
*/
|
||||
public static Locale loadLocale(JavaPlugin plugin, String name) {
|
||||
File localeFolder = new File(plugin.getDataFolder(), "locales/");
|
||||
if (!localeFolder.exists()) return null;
|
||||
File localeFile = new File(localeFolder, name + FILE_EXTENSION);
|
||||
if (!localeFolder.exists()) return null;
|
||||
// found the lang file, now load it in!
|
||||
Locale l = new Locale(plugin, localeFile, name);
|
||||
if (!l.reloadMessages()) return null;
|
||||
plugin.getLogger().info("Loaded locale \"" + name + "\"");
|
||||
return l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all locales from this plugin's locale directory
|
||||
*
|
||||
* @param plugin plugin to load from
|
||||
* @return returns the loaded Locales
|
||||
*/
|
||||
public static List<Locale> loadAllLocales(JavaPlugin plugin) {
|
||||
File localeFolder = new File(plugin.getDataFolder(), "locales/");
|
||||
List<Locale> all = new ArrayList();
|
||||
for (File localeFile : localeFolder.listFiles()) {
|
||||
String fileName = localeFile.getName();
|
||||
if (!fileName.endsWith(FILE_EXTENSION)) continue;
|
||||
fileName = fileName.substring(0, fileName.lastIndexOf('.'));
|
||||
if (fileName.split("_").length != 2) continue;
|
||||
Locale l = new Locale(plugin, localeFile, fileName);
|
||||
if (l.reloadMessages()) {
|
||||
plugin.getLogger().info("Loaded locale \"" + fileName + "\"");
|
||||
all.add(l);
|
||||
}
|
||||
}
|
||||
return all;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all locale files in this plugin's locale directory
|
||||
*
|
||||
* @param plugin Plugin to check for
|
||||
*/
|
||||
public static List<String> getLocales(Plugin plugin) {
|
||||
File localeFolder = new File(plugin.getDataFolder(), "locales/");
|
||||
List<String> all = new ArrayList();
|
||||
for (File localeFile : localeFolder.listFiles()) {
|
||||
String fileName = localeFile.getName();
|
||||
if (!fileName.endsWith(FILE_EXTENSION)) continue;
|
||||
fileName = fileName.substring(0, fileName.lastIndexOf('.'));
|
||||
if (fileName.split("_").length != 2) {
|
||||
continue;
|
||||
}
|
||||
all.add(fileName);
|
||||
}
|
||||
return all;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a locale file from the Plugin's Resources to the locale folder
|
||||
*
|
||||
* @param plugin plugin owning the locale file
|
||||
* @param locale the specific locale file to save
|
||||
* @param fileName where to save the file
|
||||
* @return true if the operation was successful, false otherwise
|
||||
*/
|
||||
public static boolean saveDefaultLocale(JavaPlugin plugin, String locale, String fileName) {
|
||||
return saveLocale(plugin, plugin.getResource(locale + FILE_EXTENSION), fileName, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a locale file from an InputStream to the locale folder
|
||||
*
|
||||
* @param plugin plugin owning the locale file
|
||||
* @param in file to save
|
||||
* @param fileName the name of the file to save
|
||||
* @return true if the operation was successful, false otherwise
|
||||
*/
|
||||
public static boolean saveLocale(Plugin plugin, InputStream in, String fileName) {
|
||||
return saveLocale(plugin, in, fileName, false);
|
||||
}
|
||||
|
||||
private static boolean saveLocale(Plugin plugin, InputStream in, String fileName, boolean builtin) {
|
||||
if(in == null) return false;
|
||||
File localeFolder = new File(plugin.getDataFolder(), "locales/");
|
||||
if (!localeFolder.exists()) localeFolder.mkdirs();
|
||||
|
||||
if (!fileName.endsWith(FILE_EXTENSION))
|
||||
fileName = fileName + FILE_EXTENSION;
|
||||
|
||||
File destinationFile = new File(localeFolder, fileName);
|
||||
if (destinationFile.exists())
|
||||
return updateFiles(plugin, in, destinationFile, builtin);
|
||||
|
||||
try (OutputStream outputStream = new FileOutputStream(destinationFile)) {
|
||||
copy(in, outputStream);
|
||||
|
||||
fileName = fileName.substring(0, fileName.lastIndexOf('.'));
|
||||
|
||||
if (fileName.split("_").length != 2) return false;
|
||||
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Write new changes to existing files, if any at all
|
||||
private static boolean updateFiles(Plugin plugin, InputStream defaultFile, File existingFile, boolean builtin) {
|
||||
boolean changed = false;
|
||||
|
||||
List<String> defaultLines, existingLines;
|
||||
try (BufferedInputStream defaultIn = new BufferedInputStream(defaultFile);
|
||||
BufferedInputStream existingIn = new BufferedInputStream(new FileInputStream(existingFile))) {
|
||||
|
||||
Charset defaultCharset = TextUtils.detectCharset(defaultIn, StandardCharsets.UTF_8);
|
||||
Charset existingCharset = TextUtils.detectCharset(existingIn, StandardCharsets.UTF_8);
|
||||
|
||||
try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(existingFile, true), existingCharset);
|
||||
BufferedReader defaultReader = new BufferedReader(new InputStreamReader(defaultIn, defaultCharset));
|
||||
BufferedReader existingReader = new BufferedReader(new InputStreamReader(existingIn, existingCharset));) {
|
||||
defaultLines = defaultReader.lines().map(s -> s.replaceAll("[\uFEFF\uFFFE\u200B]", "")).collect(Collectors.toList());
|
||||
existingLines = existingReader.lines().map(s -> s.replaceAll("[\uFEFF\uFFFE\u200B]", "").split("\\s*=")[0]).collect(Collectors.toList());
|
||||
|
||||
for (String defaultValue : defaultLines) {
|
||||
|
||||
if (defaultValue.isEmpty() || defaultValue.startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String key = defaultValue.split("\\s*=")[0];
|
||||
|
||||
if (!existingLines.contains(key)) {
|
||||
if (!changed) {
|
||||
writer.write("\n\n");
|
||||
// Leave a note alerting the user of the newly added messages.
|
||||
writer.write("# New messages for " + plugin.getName() + " v" + plugin.getDescription().getVersion() + ".");
|
||||
|
||||
// If changes were found outside of the default file leave a note explaining that.
|
||||
if (!builtin) {
|
||||
writer.write("\n");
|
||||
writer.write("# These translations were found untranslated, join\n");
|
||||
writer.write("# our translation Discord https://discord.gg/f7fpZEf\n");
|
||||
writer.write("# to request an official update!\n");
|
||||
}
|
||||
}
|
||||
|
||||
writer.write("\n");
|
||||
|
||||
// re-encode to target format? (transform down, not up)
|
||||
// if (defaultCharset != existingCharset) {
|
||||
// byte[] encoded = defaultValue.getBytes(defaultCharset);
|
||||
// defaultValue = new String(encoded, 2, encoded.length - 2, existingCharset);
|
||||
// }
|
||||
writer.write(defaultValue);
|
||||
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the previous message cache and load new messages directly from file
|
||||
*
|
||||
* @return reload messages from file
|
||||
*/
|
||||
public boolean reloadMessages() {
|
||||
if (!this.file.exists()) {
|
||||
plugin.getLogger().warning("Could not find file for locale \"" + this.name + "\"");
|
||||
return false;
|
||||
}
|
||||
|
||||
this.nodes.clear(); // Clear previous data (if any)
|
||||
|
||||
// guess what encoding this file is in
|
||||
Charset charset = TextUtils.detectCharset(file, null);
|
||||
if(charset == null) {
|
||||
plugin.getLogger().warning("Could not determine charset for locale \"" + this.name + "\"");
|
||||
charset = StandardCharsets.UTF_8;
|
||||
}
|
||||
|
||||
// load in the file!
|
||||
try (FileInputStream stream = new FileInputStream(file);
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader((InputStream) stream, charset));) {
|
||||
String line;
|
||||
for (int lineNumber = 0; (line = reader.readLine()) != null; lineNumber++) {
|
||||
if (lineNumber == 0){
|
||||
// remove BOM markers, if any
|
||||
line = line.replaceAll("[\uFEFF\uFFFE\u200B]", "");
|
||||
}
|
||||
// convert to UTF-8 ?
|
||||
/*if (charset == StandardCharsets.UTF_16LE || charset == StandardCharsets.UTF_16BE) {
|
||||
byte[] encoded = line.getBytes(charset);
|
||||
line = new String(encoded, 0, encoded.length, StandardCharsets.UTF_8);
|
||||
} */
|
||||
|
||||
if ((line = line.trim()).isEmpty() || line.startsWith("#") /* Comment */) continue;
|
||||
|
||||
Matcher matcher = NODE_PATTERN.matcher(line);
|
||||
if (!matcher.find()) {
|
||||
System.err.println("Invalid locale syntax at (line=" + lineNumber + "): " + line);
|
||||
continue;
|
||||
}
|
||||
|
||||
nodes.put(matcher.group(1), matcher.group(2));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Supply the Message object with the plugins prefix.
|
||||
*
|
||||
* @param message message to be applied
|
||||
* @return applied message
|
||||
*/
|
||||
private Message supplyPrefix(Message message) {
|
||||
return message.setPrefix(this.nodes.getOrDefault("general.nametag.prefix", "[" + plugin.getName() + "]"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new unsaved Message
|
||||
*
|
||||
* @param message the message to create
|
||||
* @return the created message
|
||||
*/
|
||||
public Message newMessage(String message) {
|
||||
return supplyPrefix(new Message(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a message set for a specific node.
|
||||
*
|
||||
* @param node the node to get
|
||||
* @return the message for the specified node
|
||||
*/
|
||||
public Message getMessage(String node) {
|
||||
return this.getMessageOrDefault(node, node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a message set for a specific node
|
||||
*
|
||||
* @param node the node to get
|
||||
* @param defaultValue the default value given that a value for the node was not found
|
||||
* @return the message for the specified node. Default if none found
|
||||
*/
|
||||
public Message getMessageOrDefault(String node, String defaultValue) {
|
||||
return supplyPrefix(new Message(this.nodes.getOrDefault(node, defaultValue)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the locale name (i.e. "en_US")
|
||||
*
|
||||
* @return the locale name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
private static void copy(InputStream input, OutputStream output) {
|
||||
int n;
|
||||
byte[] buffer = new byte[1024 * 4];
|
||||
|
||||
try {
|
||||
while ((n = input.read(buffer)) != -1) {
|
||||
output.write(buffer, 0, n);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
130
src/main/java/com/songoda/core/locale/Message.java
Normal file
130
src/main/java/com/songoda/core/locale/Message.java
Normal file
@ -0,0 +1,130 @@
|
||||
package com.songoda.core.locale;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* The Message object. This holds the message to be sent
|
||||
* as well as the plugins prefix so that they can both be
|
||||
* easily manipulated then deployed
|
||||
*/
|
||||
public class Message {
|
||||
|
||||
private String prefix = null;
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* create a new message
|
||||
*
|
||||
* @param message the message text
|
||||
*/
|
||||
public Message(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format and send the held message to a player
|
||||
*
|
||||
* @param player player to send the message to
|
||||
*/
|
||||
public void sendMessage(Player player) {
|
||||
player.sendMessage(this.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Format and send the held message with the
|
||||
* appended plugin prefix to a player
|
||||
*
|
||||
* @param player player to send the message to
|
||||
*/
|
||||
public void sendPrefixedMessage(Player player) {
|
||||
player.sendMessage(this.getPrefixedMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Format and send the held message to a player
|
||||
*
|
||||
* @param sender command sender to send the message to
|
||||
*/
|
||||
public void sendMessage(CommandSender sender) {
|
||||
sender.sendMessage(this.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Format and send the held message to a player as a title message
|
||||
*
|
||||
* @param sender command sender to send the message to
|
||||
*/
|
||||
public void sendTitle(CommandSender sender) {
|
||||
if(sender instanceof Player) {
|
||||
((Player) sender).sendTitle("", this.getMessage(), 10, 20, 10);
|
||||
} else {
|
||||
sender.sendMessage(this.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format and send the held message with the
|
||||
* appended plugin prefix to a command sender
|
||||
*
|
||||
* @param sender command sender to send the message to
|
||||
*/
|
||||
public void sendPrefixedMessage(CommandSender sender) {
|
||||
sender.sendMessage(this.getPrefixedMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the held message and append the plugins
|
||||
* prefix
|
||||
*
|
||||
* @return the prefixed message
|
||||
*/
|
||||
public String getPrefixedMessage() {
|
||||
return ChatColor.translateAlternateColorCodes('&',(prefix == null ? "" : this.prefix)
|
||||
+ " " + this.message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get and format the held message
|
||||
*
|
||||
* @return the message
|
||||
*/
|
||||
public String getMessage() {
|
||||
return ChatColor.translateAlternateColorCodes('&', this.message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the held message
|
||||
*
|
||||
* @return the message
|
||||
*/
|
||||
public String getUnformattedMessage() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the provided placeholder with the provided object. <br />
|
||||
* Interchangeably Supports {@code %value%} and {@code {value}}
|
||||
*
|
||||
* @param placeholder the placeholder to replace
|
||||
* @param replacement the replacement object
|
||||
* @return the modified Message
|
||||
*/
|
||||
public Message processPlaceholder(String placeholder, Object replacement) {
|
||||
final String place = Matcher.quoteReplacement(placeholder);
|
||||
this.message = message.replaceAll("%" + place + "%|\\{" + place +"\\}", Matcher.quoteReplacement(replacement.toString()));
|
||||
return this;
|
||||
}
|
||||
|
||||
Message setPrefix(String prefix) {
|
||||
this.prefix = prefix;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.message;
|
||||
}
|
||||
}
|
851
src/main/java/com/songoda/core/utils/BlockUtils.java
Normal file
851
src/main/java/com/songoda/core/utils/BlockUtils.java
Normal file
@ -0,0 +1,851 @@
|
||||
package com.songoda.core.utils;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Effect;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
|
||||
public class BlockUtils {
|
||||
|
||||
protected static final Set<Material> DOORS;
|
||||
protected static final Set<Material> PRESSURE_PLATES;
|
||||
protected static final Set<Material> FENCE_GATES;
|
||||
protected static final Set<Material> TRAP_DOORS;
|
||||
protected static final boolean useLegacy = Material.getMaterial("OAK_LOG") == null;
|
||||
protected static Method legacySetBlockData = null;
|
||||
protected static Method legacyUpdateBlockData = null;
|
||||
|
||||
static {
|
||||
DOORS = EnumSet.noneOf(Material.class);
|
||||
PRESSURE_PLATES = EnumSet.noneOf(Material.class);
|
||||
FENCE_GATES = EnumSet.noneOf(Material.class);
|
||||
TRAP_DOORS = EnumSet.noneOf(Material.class);
|
||||
|
||||
for (Material material : Material.values()) {
|
||||
String name = material.name();
|
||||
if (name.contains("DOOR") && !name.contains("ITEM")) {
|
||||
if (name.contains("TRAP")) {
|
||||
TRAP_DOORS.add(material);
|
||||
} else {
|
||||
DOORS.add(material);
|
||||
}
|
||||
} else if (name.contains("GATE") && !name.contains("END")) {
|
||||
FENCE_GATES.add(material);
|
||||
} else if (name.contains("_PLATE")) {
|
||||
PRESSURE_PLATES.add(material);
|
||||
}
|
||||
}
|
||||
|
||||
if (useLegacy) {
|
||||
try {
|
||||
//legacyUpdateBlockData = Block.class.getDeclaredMethod("update");
|
||||
legacySetBlockData = Block.class.getDeclaredMethod("setData", byte.class);
|
||||
} catch (NoSuchMethodException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interact with this block to either update redstone or open doors
|
||||
*
|
||||
* @param b block to update
|
||||
* @return if this block's state was updated
|
||||
*/
|
||||
public static boolean tryInteract(Block b) {
|
||||
final Material bType = b.getType();
|
||||
if (isOpenable(bType)) {
|
||||
toggleDoorStates(true, b);
|
||||
return true;
|
||||
} else if(bType == Material.LEVER) {
|
||||
toggleLever(b);
|
||||
return true;
|
||||
} else if(bType.name().endsWith("_BUTTON")) {
|
||||
pressButton(b);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a pressure plate's redstone state
|
||||
* @param plate plate to update
|
||||
* @param power power to set to 0-15 (wood plates are active if greater than 0)
|
||||
*/
|
||||
public static void updatePressurePlate(Block plate, int power) {
|
||||
if (useLegacy && legacySetBlockData != null) {
|
||||
_updatePressurePlateLegacy(plate, power);
|
||||
} else {
|
||||
BlockUtilsModern._updatePressurePlateModern(plate, power);
|
||||
}
|
||||
}
|
||||
|
||||
private static void _updatePressurePlateLegacy(Block plate, int power) {
|
||||
final Material m = plate.getType();
|
||||
try {
|
||||
if (m.name().equals("GOLD_PLATE") || m.name().equals("IRON_PLATE")) {
|
||||
legacySetBlockData.invoke(plate, (byte) (power & 0x15));
|
||||
} else if (m.name().endsWith("_PLATE")) {
|
||||
legacySetBlockData.invoke(plate, (byte) (power == 0 ? 0 : 1));
|
||||
}
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
|
||||
Logger.getLogger(BlockUtils.class.getName()).log(Level.SEVERE, "Unexpected method error", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void pressButton(Block button) {
|
||||
if (useLegacy && legacySetBlockData != null) {
|
||||
_pressButtonLegacy(button);
|
||||
} else {
|
||||
BlockUtilsModern._pressButtonModern(button);
|
||||
}
|
||||
}
|
||||
|
||||
public static void releaseButton(Block button) {
|
||||
if (useLegacy && legacySetBlockData != null) {
|
||||
_releaseButtonLegacy(button);
|
||||
} else {
|
||||
BlockUtilsModern._releaseButtonModern(button);
|
||||
}
|
||||
}
|
||||
|
||||
private static void _pressButtonLegacy(Block button) {
|
||||
final Material m = button.getType();
|
||||
if(!m.name().endsWith("_BUTTON")) return;
|
||||
try {
|
||||
legacySetBlockData.invoke(button, (byte) (button.getData() | (31 & 0x8)));
|
||||
button.getState().update();
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
|
||||
Logger.getLogger(BlockUtils.class.getName()).log(Level.SEVERE, "Unexpected method error", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void _releaseButtonLegacy(Block button) {
|
||||
final Material m = button.getType();
|
||||
if(!m.name().endsWith("_BUTTON")) return;
|
||||
try {
|
||||
legacySetBlockData.invoke(button, (byte) (button.getData() & ~0x8));
|
||||
button.getState().update();
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
|
||||
Logger.getLogger(BlockUtils.class.getName()).log(Level.SEVERE, "Unexpected method error", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void toggleLever(Block lever) {
|
||||
if (useLegacy && legacySetBlockData != null) {
|
||||
_toggleLeverLegacy(lever);
|
||||
} else {
|
||||
BlockUtilsModern._toggleLeverModern(lever);
|
||||
}
|
||||
}
|
||||
|
||||
private static void _toggleLeverLegacy(Block lever) {
|
||||
final Material m = lever.getType();
|
||||
if(m != Material.LEVER) return;
|
||||
try {
|
||||
legacySetBlockData.invoke(lever, (byte) (lever.getData() ^ 0x8));
|
||||
lever.getState().update();
|
||||
//lever.getWorld().playEffect(lever.getLocation(), Effect.CLICK1, 0);
|
||||
// now we need to update the redstone around it..
|
||||
// int data = lever.getData() & ~0x8;
|
||||
// Block attached;
|
||||
// switch(data) {
|
||||
// case 0:
|
||||
// attached = lever.getRelative(BlockFace.UP);
|
||||
// break;
|
||||
// case 1:
|
||||
// attached = lever.getRelative(BlockFace.WEST);
|
||||
// break;
|
||||
// case 2:
|
||||
// attached = lever.getRelative(BlockFace.EAST);
|
||||
// break;
|
||||
// case 3:
|
||||
// attached = lever.getRelative(BlockFace.NORTH);
|
||||
// break;
|
||||
// case 4:
|
||||
// attached = lever.getRelative(BlockFace.SOUTH);
|
||||
// break;
|
||||
// case 5:
|
||||
// attached = lever.getRelative(BlockFace.DOWN);
|
||||
// break;
|
||||
// default:
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
|
||||
Logger.getLogger(BlockUtils.class.getName()).log(Level.SEVERE, "Unexpected method error", ex);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Change all of the given door states to be inverse; that is, if a door is
|
||||
* open, it will be closed afterwards. If the door is closed, it will become
|
||||
* open.
|
||||
* <p/>
|
||||
* Note that the blocks given must be the bottom block of the door.
|
||||
*
|
||||
* @param allowDoorToOpen If FALSE, and the door is currently CLOSED, it
|
||||
* will NOT be opened!
|
||||
* @param doors Blocks given must be the bottom block of the door
|
||||
*/
|
||||
public static void toggleDoorStates(boolean allowDoorToOpen, Block... doors) {
|
||||
if (useLegacy && legacySetBlockData != null) {
|
||||
_toggleDoorStatesLegacy(allowDoorToOpen, doors);
|
||||
} else {
|
||||
BlockUtilsModern._toggleDoorStatesModern(allowDoorToOpen, doors);
|
||||
}
|
||||
}
|
||||
|
||||
private static void _toggleDoorStatesLegacy(boolean allowDoorToOpen, Block... doors) {
|
||||
try {
|
||||
for (Block door : doors) {
|
||||
if (door == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean isTop = (door.getData() & 0x8) != 0;
|
||||
if (isTop) {
|
||||
// The lower half of the door contains the direction & open/close state
|
||||
door = door.getRelative(BlockFace.DOWN);
|
||||
}
|
||||
|
||||
// If we aren't allowing the door to open, check if it's already closed
|
||||
if (!allowDoorToOpen && (door.getData() & 0x4) == 0) {
|
||||
// The door is already closed and we don't want to open it
|
||||
// the bit 0x4 is set when the door is open
|
||||
continue;
|
||||
}
|
||||
|
||||
// Now xor both data values with 0x4, the flag that states if the door is open
|
||||
legacySetBlockData.invoke(door, (byte) (door.getData() ^ 0x4));
|
||||
|
||||
// Play the door open/close sound
|
||||
door.getWorld().playEffect(door.getLocation(), Effect.DOOR_TOGGLE, 0);
|
||||
}
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
|
||||
Logger.getLogger(BlockUtils.class.getName()).log(Level.SEVERE, "Unexpected method error", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the double door for the given block
|
||||
*
|
||||
* @param block
|
||||
* @return
|
||||
*/
|
||||
public static Block getDoubleDoor(Block block) {
|
||||
// TODO? if legacy, just search N/S/E/W to see if there's another door nearby
|
||||
if (!isOpenable(block.getType())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return BlockUtilsModern._getDoubleDoorModern(block);
|
||||
}
|
||||
|
||||
public static boolean isOpenable(Material m) {
|
||||
return DOORS.contains(m)
|
||||
|| FENCE_GATES.contains(m)
|
||||
|| TRAP_DOORS.contains(m);
|
||||
}
|
||||
|
||||
public static BlockFace getDoorClosedDirection(Block door) {
|
||||
return useLegacy ? _getDoorClosedDirectionLegacy(door) : BlockUtilsModern._getDoorClosedDirectionModern(door);
|
||||
}
|
||||
|
||||
private static BlockFace _getDoorClosedDirectionLegacy(Block door) {
|
||||
final Material type = door.getType();
|
||||
if (DOORS.contains(type)) {
|
||||
boolean isTop = (door.getData() & 0x8) != 0;
|
||||
if (isTop) {
|
||||
// The lower half of the door contains the direction & open/close state
|
||||
door = door.getRelative(BlockFace.DOWN);
|
||||
if (door.getType() != type) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
boolean isOpen = (door.getData() & 0x4) != 0;
|
||||
//int facing = (door.getData() & 0x3);
|
||||
// [east, south, west, north]
|
||||
boolean facingNS = (door.getData() & 0x1) != 0;
|
||||
if (facingNS) {
|
||||
return isOpen ? BlockFace.EAST : BlockFace.SOUTH;
|
||||
} else {
|
||||
return isOpen ? BlockFace.SOUTH : BlockFace.EAST;
|
||||
}
|
||||
} else if (FENCE_GATES.contains(door.getType())) {
|
||||
boolean isOpen = (door.getData() & 0x4) != 0;
|
||||
//int facing = (door.getData() & 0x3);
|
||||
// so fence gate orientations are [south, west, north, east]
|
||||
boolean facingNS = (door.getData() & 0x1) == 0;
|
||||
if (facingNS) {
|
||||
return isOpen ? BlockFace.EAST : BlockFace.SOUTH;
|
||||
} else {
|
||||
return isOpen ? BlockFace.SOUTH : BlockFace.EAST;
|
||||
}
|
||||
} else if (TRAP_DOORS.contains(door.getType())) {
|
||||
boolean isOpen = (door.getData() & 0x4) != 0;
|
||||
// [south, north, east, west]
|
||||
boolean facingNS = (door.getData() & 0x3) <= 1;
|
||||
if (facingNS) {
|
||||
return isOpen ? BlockFace.EAST : BlockFace.SOUTH;
|
||||
} else {
|
||||
return isOpen ? BlockFace.SOUTH : BlockFace.EAST;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Class<?> clazzCraftWorld, clazzCraftBlock, clazzBlockPosition;
|
||||
private static Method getHandle, updateAdjacentComparators, getNMSBlock;
|
||||
|
||||
/**
|
||||
* Manually trigger the updateAdjacentComparators method for containers
|
||||
* @param containerLocation location of the container
|
||||
*/
|
||||
public static void updateAdjacentComparators(Location containerLocation) {
|
||||
try {
|
||||
// Cache reflection.
|
||||
if (clazzCraftWorld == null) {
|
||||
String ver = Bukkit.getServer().getClass().getPackage().getName().substring(23);
|
||||
clazzCraftWorld = Class.forName("org.bukkit.craftbukkit." + ver + ".CraftWorld");
|
||||
clazzCraftBlock = Class.forName("org.bukkit.craftbukkit." + ver + ".block.CraftBlock");
|
||||
clazzBlockPosition = Class.forName("net.minecraft.server." + ver + ".BlockPosition");
|
||||
Class<?> clazzWorld = Class.forName("net.minecraft.server." + ver + ".World");
|
||||
Class<?> clazzBlock = Class.forName("net.minecraft.server." + ver + ".Block");
|
||||
|
||||
getHandle = clazzCraftWorld.getMethod("getHandle");
|
||||
updateAdjacentComparators = clazzWorld.getMethod("updateAdjacentComparators", clazzBlockPosition, clazzBlock);
|
||||
getNMSBlock = clazzCraftBlock.getDeclaredMethod("getNMSBlock");
|
||||
getNMSBlock.setAccessible(true);
|
||||
}
|
||||
|
||||
// invoke and cast objects.
|
||||
Object craftWorld = clazzCraftWorld.cast(containerLocation.getWorld());
|
||||
Object world = getHandle.invoke(craftWorld);
|
||||
Object craftBlock = clazzCraftBlock.cast(containerLocation.getBlock());
|
||||
|
||||
// Invoke final method.
|
||||
updateAdjacentComparators
|
||||
.invoke(world, clazzBlockPosition.getConstructor(double.class, double.class, double.class)
|
||||
.newInstance(containerLocation.getX(), containerLocation.getY(), containerLocation.getZ()),
|
||||
getNMSBlock.invoke(craftBlock));
|
||||
|
||||
} catch (ReflectiveOperationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean canPassThrough(Material m) {
|
||||
|
||||
switch (m.name()) {
|
||||
case "ACACIA_BUTTON":
|
||||
case "ACACIA_PRESSURE_PLATE":
|
||||
case "ACACIA_SAPLING":
|
||||
case "ACACIA_SIGN":
|
||||
case "ACACIA_WALL_SIGN":
|
||||
case "ACTIVATOR_RAIL":
|
||||
case "AIR":
|
||||
case "ATTACHED_MELON_STEM":
|
||||
case "ATTACHED_PUMPKIN_STEM":
|
||||
case "AZURE_BLUET":
|
||||
//case "BAMBOO_SAPLING":
|
||||
//case "BARRIER": // could let robots pass through barriers
|
||||
case "BEETROOTS":
|
||||
case "BIRCH_BUTTON":
|
||||
case "BIRCH_PRESSURE_PLATE":
|
||||
case "BIRCH_SAPLING":
|
||||
case "BIRCH_SIGN":
|
||||
case "BIRCH_WALL_SIGN":
|
||||
case "BLACK_WALL_BANNER":
|
||||
case "BLUE_BANNER":
|
||||
case "BLUE_ORCHID":
|
||||
case "BLUE_WALL_BANNER":
|
||||
case "BRAIN_CORAL_FAN":
|
||||
case "BRAIN_CORAL_WALL_FAN":
|
||||
case "BROWN_BANNER":
|
||||
case "BROWN_MUSHROOM":
|
||||
case "BROWN_WALL_BANNER":
|
||||
case "BUBBLE_CORAL_FAN":
|
||||
case "BUBBLE_CORAL_WALL_FAN":
|
||||
case "CARROTS":
|
||||
case "CAVE_AIR":
|
||||
case "COBWEB":
|
||||
case "CORNFLOWER":
|
||||
case "CYAN_BANNER":
|
||||
case "CYAN_WALL_BANNER":
|
||||
case "DANDELION":
|
||||
case "DARK_OAK_BUTTON":
|
||||
case "DARK_OAK_PRESSURE_PLATE":
|
||||
case "DARK_OAK_SAPLING":
|
||||
case "DARK_OAK_SIGN":
|
||||
case "DARK_OAK_WALL_SIGN":
|
||||
case "DEAD_BRAIN_CORAL_FAN":
|
||||
case "DEAD_BRAIN_CORAL_WALL_FAN":
|
||||
case "DEAD_BUBBLE_CORAL_FAN":
|
||||
case "DEAD_BUBBLE_CORAL_WALL_FAN":
|
||||
case "DEAD_BUSH":
|
||||
case "DEAD_FIRE_CORAL_FAN":
|
||||
case "DEAD_FIRE_CORAL_WALL_FAN":
|
||||
case "DEAD_HORN_CORAL_FAN":
|
||||
case "DEAD_HORN_CORAL_WALL_FAN":
|
||||
case "DEAD_TUBE_CORAL_FAN":
|
||||
case "DEAD_TUBE_CORAL_WALL_FAN":
|
||||
case "DETECTOR_RAIL":
|
||||
case "END_PORTAL":
|
||||
case "FERN":
|
||||
case "FIRE": // probably should take damage
|
||||
case "FIRE_CORAL_FAN":
|
||||
case "FIRE_CORAL_WALL_FAN":
|
||||
case "GRASS":
|
||||
case "GRAY_BANNER":
|
||||
case "GRAY_WALL_BANNER":
|
||||
case "GREEN_BANNER":
|
||||
case "GREEN_WALL_BANNER":
|
||||
case "HEAVY_WEIGHTED_PRESSURE_PLATE":
|
||||
case "HORN_CORAL_FAN":
|
||||
case "HORN_CORAL_WALL_FAN":
|
||||
case "JUNGLE_BUTTON":
|
||||
case "JUNGLE_PRESSURE_PLATE":
|
||||
case "JUNGLE_SAPLING":
|
||||
case "JUNGLE_SIGN":
|
||||
case "JUNGLE_WALL_SIGN":
|
||||
case "KELP":
|
||||
case "LADDER":
|
||||
case "LARGE_FERN":
|
||||
case "LAVA":
|
||||
case "LEVER":
|
||||
case "LIGHT_BLUE_BANNER":
|
||||
case "LIGHT_BLUE_WALL_BANNER":
|
||||
case "LIGHT_GRAY_BANNER":
|
||||
case "LIGHT_GRAY_WALL_BANNER":
|
||||
case "LIGHT_WEIGHTED_PRESSURE_PLATE":
|
||||
case "LILAC":
|
||||
case "LILY_OF_THE_VALLEY":
|
||||
case "LIME_BANNER":
|
||||
case "MAGENTA_BANNER":
|
||||
case "MAGENTA_WALL_BANNER":
|
||||
case "MELON_STEM":
|
||||
case "NETHER_PORTAL":
|
||||
case "NETHER_WART":
|
||||
case "OAK_BUTTON":
|
||||
case "OAK_PRESSURE_PLATE":
|
||||
case "OAK_SAPLING":
|
||||
case "OAK_SIGN":
|
||||
case "OAK_WALL_SIGN":
|
||||
case "ORANGE_BANNER":
|
||||
case "ORANGE_TULIP":
|
||||
case "ORANGE_WALL_BANNER":
|
||||
case "OXEYE_DAISY":
|
||||
case "PEONY":
|
||||
case "PINK_BANNER":
|
||||
case "PINK_TULIP":
|
||||
case "PINK_WALL_BANNER":
|
||||
case "POTATOES":
|
||||
case "POWERED_RAIL":
|
||||
case "PUMPKIN_STEM":
|
||||
case "PURPLE_BANNER":
|
||||
case "PURPLE_WALL_BANNER":
|
||||
case "RAIL":
|
||||
case "REDSTONE_TORCH":
|
||||
case "REDSTONE_WALL_TORCH":
|
||||
case "REDSTONE_WIRE":
|
||||
case "RED_BANNER":
|
||||
case "RED_MUSHROOM":
|
||||
case "RED_TULIP":
|
||||
case "RED_WALL_BANNER":
|
||||
case "ROSE_BUSH":
|
||||
case "SCAFFOLDING":
|
||||
case "SEAGRASS":
|
||||
case "SPRUCE_BUTTON":
|
||||
case "SPRUCE_PRESSURE_PLATE":
|
||||
case "SPRUCE_SAPLING":
|
||||
case "SPRUCE_SIGN":
|
||||
case "SPRUCE_WALL_SIGN":
|
||||
case "STONE_BUTTON":
|
||||
case "STONE_PRESSURE_PLATE":
|
||||
case "STRUCTURE_VOID":
|
||||
case "SUGAR_CANE":
|
||||
case "SUNFLOWER":
|
||||
case "SWEET_BERRY_BUSH":
|
||||
case "TALL_GRASS":
|
||||
case "TALL_SEAGRASS":
|
||||
case "TORCH":
|
||||
case "TRIPWIRE":
|
||||
case "TRIPWIRE_HOOK":
|
||||
case "TUBE_CORAL_FAN":
|
||||
case "TUBE_CORAL_WALL_FAN":
|
||||
case "VINE":
|
||||
case "VOID_AIR":
|
||||
case "WALL_TORCH":
|
||||
case "WATER":
|
||||
case "WHEAT":
|
||||
case "WHITE_BANNER":
|
||||
case "WHITE_TULIP":
|
||||
case "WHITE_WALL_BANNER":
|
||||
case "WITHER_ROSE":
|
||||
case "YELLOW_BANNER":
|
||||
case "YELLOW_WALL_BANNER":
|
||||
// Legacy values:
|
||||
case "WEB":
|
||||
case "LONG_GRASS":
|
||||
case "YELLOW_FLOWER":
|
||||
case "RED_ROSE":
|
||||
case "CROPS":
|
||||
case "SIGN_POST":
|
||||
case "RAILS":
|
||||
case "WALL_SIGN":
|
||||
case "STONE_PLATE":
|
||||
case "WOOD_PLATE":
|
||||
case "REDSTONE_TORCH_OFF":
|
||||
case "REDSTONE_TORCH_ON":
|
||||
case "SUGAR_CANE_BLOCK":
|
||||
case "PORTAL":
|
||||
case "ENDER_PORTAL":
|
||||
case "CARROT":
|
||||
case "POTATO":
|
||||
case "WOOD_BUTTON":
|
||||
case "GOLD_PLATE":
|
||||
case "IRON_PLATE":
|
||||
case "DOUBLE_PLANT":
|
||||
case "STANDING_BANNER":
|
||||
case "WALL_BANNER":
|
||||
case "BEETROOT_BLOCK":
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean canWalkTo(Material m) {
|
||||
switch (m.name()) {
|
||||
case "ACACIA_BUTTON":
|
||||
case "ACACIA_PRESSURE_PLATE":
|
||||
case "ACACIA_SAPLING":
|
||||
case "ACACIA_SIGN":
|
||||
case "ACACIA_SLAB":
|
||||
case "ACACIA_STAIRS":
|
||||
case "ACACIA_TRAPDOOR":
|
||||
case "ACACIA_WALL_SIGN":
|
||||
case "ACTIVATOR_RAIL":
|
||||
case "AIR":
|
||||
case "ANDESITE_SLAB":
|
||||
case "ANDESITE_STAIRS":
|
||||
case "ATTACHED_MELON_STEM":
|
||||
case "ATTACHED_PUMPKIN_STEM":
|
||||
case "AZURE_BLUET":
|
||||
//case "BAMBOO_SAPLING":
|
||||
//case "BARRIER": // could let robots pass through barriers
|
||||
case "BEETROOTS":
|
||||
case "BIRCH_BUTTON":
|
||||
case "BIRCH_DOOR":
|
||||
case "BIRCH_FENCE_GATE":
|
||||
case "BIRCH_PRESSURE_PLATE":
|
||||
case "BIRCH_SAPLING":
|
||||
case "BIRCH_SIGN":
|
||||
case "BIRCH_SLAB":
|
||||
case "BIRCH_STAIRS":
|
||||
case "BIRCH_TRAPDOOR":
|
||||
case "BIRCH_WALL_SIGN":
|
||||
case "BLACK_CARPET":
|
||||
case "BLACK_WALL_BANNER":
|
||||
case "BLUE_BANNER":
|
||||
case "BLUE_CARPET":
|
||||
case "BLUE_ORCHID":
|
||||
case "BLUE_WALL_BANNER":
|
||||
case "BRAIN_CORAL_FAN":
|
||||
case "BRAIN_CORAL_WALL_FAN":
|
||||
case "BRICK_SLAB":
|
||||
case "BRICK_STAIRS":
|
||||
case "BROWN_BANNER":
|
||||
case "BROWN_CARPET":
|
||||
case "BROWN_MUSHROOM":
|
||||
case "BROWN_WALL_BANNER":
|
||||
case "BUBBLE_CORAL_FAN":
|
||||
case "BUBBLE_CORAL_WALL_FAN":
|
||||
case "CAKE":
|
||||
case "CAMPFIRE": // could take damage from walking over?
|
||||
case "CARROTS":
|
||||
case "CAVE_AIR":
|
||||
case "COBBLESTONE_SLAB":
|
||||
case "COBBLESTONE_STAIRS":
|
||||
case "COBWEB":
|
||||
case "COMPARATOR":
|
||||
case "CORNFLOWER":
|
||||
case "CUT_RED_SANDSTONE_SLAB":
|
||||
case "CUT_SANDSTONE_SLAB":
|
||||
case "CYAN_BANNER":
|
||||
case "CYAN_CARPET":
|
||||
case "CYAN_WALL_BANNER":
|
||||
case "DANDELION":
|
||||
case "DARK_OAK_BUTTON":
|
||||
case "DARK_OAK_DOOR":
|
||||
case "DARK_OAK_FENCE_GATE":
|
||||
case "DARK_OAK_PRESSURE_PLATE":
|
||||
case "DARK_OAK_SAPLING":
|
||||
case "DARK_OAK_SIGN":
|
||||
case "DARK_OAK_SLAB":
|
||||
case "DARK_OAK_STAIRS":
|
||||
case "DARK_OAK_TRAPDOOR":
|
||||
case "DARK_OAK_WALL_SIGN":
|
||||
case "DARK_PRISMARINE_SLAB":
|
||||
case "DARK_PRISMARINE_STAIRS":
|
||||
case "DAYLIGHT_DETECTOR":
|
||||
case "DEAD_BRAIN_CORAL_FAN":
|
||||
case "DEAD_BRAIN_CORAL_WALL_FAN":
|
||||
case "DEAD_BUBBLE_CORAL_FAN":
|
||||
case "DEAD_BUBBLE_CORAL_WALL_FAN":
|
||||
case "DEAD_BUSH":
|
||||
case "DEAD_FIRE_CORAL_FAN":
|
||||
case "DEAD_FIRE_CORAL_WALL_FAN":
|
||||
case "DEAD_HORN_CORAL_FAN":
|
||||
case "DEAD_HORN_CORAL_WALL_FAN":
|
||||
case "DEAD_TUBE_CORAL_FAN":
|
||||
case "DEAD_TUBE_CORAL_WALL_FAN":
|
||||
case "DETECTOR_RAIL":
|
||||
case "DIORITE_SLAB":
|
||||
case "DIORITE_STAIRS":
|
||||
case "END_PORTAL":
|
||||
case "END_STONE_BRICK_SLAB":
|
||||
case "END_STONE_BRICK_STAIRS":
|
||||
case "FERN":
|
||||
case "FIRE": // probably should take damage
|
||||
case "FIRE_CORAL_FAN":
|
||||
case "FIRE_CORAL_WALL_FAN":
|
||||
case "FLOWER_POT":
|
||||
case "GRANITE_SLAB":
|
||||
case "GRANITE_STAIRS":
|
||||
case "GRASS":
|
||||
case "GRAY_BANNER":
|
||||
case "GRAY_CARPET":
|
||||
case "GRAY_WALL_BANNER":
|
||||
case "GREEN_BANNER":
|
||||
case "GREEN_WALL_BANNER":
|
||||
case "HEAVY_WEIGHTED_PRESSURE_PLATE":
|
||||
case "HORN_CORAL_FAN":
|
||||
case "HORN_CORAL_WALL_FAN":
|
||||
case "IRON_DOOR":
|
||||
case "JUNGLE_BUTTON":
|
||||
case "JUNGLE_DOOR":
|
||||
case "JUNGLE_FENCE_GATE":
|
||||
case "JUNGLE_PRESSURE_PLATE":
|
||||
case "JUNGLE_SAPLING":
|
||||
case "JUNGLE_SIGN":
|
||||
case "JUNGLE_SLAB":
|
||||
case "JUNGLE_STAIRS":
|
||||
case "JUNGLE_TRAPDOOR":
|
||||
case "JUNGLE_WALL_SIGN":
|
||||
case "KELP":
|
||||
case "LADDER":
|
||||
case "LARGE_FERN":
|
||||
case "LAVA":
|
||||
case "LEVER":
|
||||
case "LIGHT_BLUE_BANNER":
|
||||
case "LIGHT_BLUE_CARPET":
|
||||
case "LIGHT_BLUE_WALL_BANNER":
|
||||
case "LIGHT_GRAY_BANNER":
|
||||
case "LIGHT_GRAY_CARPET":
|
||||
case "LIGHT_GRAY_WALL_BANNER":
|
||||
case "LIGHT_WEIGHTED_PRESSURE_PLATE":
|
||||
case "LILAC":
|
||||
case "LILY_OF_THE_VALLEY":
|
||||
case "LILY_PAD":
|
||||
case "LIME_BANNER":
|
||||
case "LIME_CARPET":
|
||||
case "MAGENTA_BANNER":
|
||||
case "MAGENTA_CARPET":
|
||||
case "MAGENTA_WALL_BANNER":
|
||||
case "MELON_STEM":
|
||||
case "MOSSY_COBBLESTONE_SLAB":
|
||||
case "MOSSY_COBBLESTONE_STAIRS":
|
||||
case "MOSSY_STONE_BRICK_SLAB":
|
||||
case "MOSSY_STONE_BRICK_STAIRS":
|
||||
case "NETHER_BRICK_SLAB":
|
||||
case "NETHER_BRICK_STAIRS":
|
||||
case "NETHER_PORTAL":
|
||||
case "NETHER_WART":
|
||||
case "OAK_BUTTON":
|
||||
case "OAK_DOOR":
|
||||
case "OAK_FENCE_GATE":
|
||||
case "OAK_PRESSURE_PLATE":
|
||||
case "OAK_SAPLING":
|
||||
case "OAK_SIGN":
|
||||
case "OAK_SLAB":
|
||||
case "OAK_STAIRS":
|
||||
case "OAK_TRAPDOOR":
|
||||
case "OAK_WALL_SIGN":
|
||||
case "ORANGE_BANNER":
|
||||
case "ORANGE_CARPET":
|
||||
case "ORANGE_TULIP":
|
||||
case "ORANGE_WALL_BANNER":
|
||||
case "OXEYE_DAISY":
|
||||
case "PEONY":
|
||||
case "PETRIFIED_OAK_SLAB":
|
||||
case "PINK_BANNER":
|
||||
case "PINK_CARPET":
|
||||
case "PINK_TULIP":
|
||||
case "PINK_WALL_BANNER":
|
||||
case "POLISHED_ANDESITE_SLAB":
|
||||
case "POLISHED_ANDESITE_STAIRS":
|
||||
case "POLISHED_DIORITE_SLAB":
|
||||
case "POLISHED_DIORITE_STAIRS":
|
||||
case "POLISHED_GRANITE_SLAB":
|
||||
case "POLISHED_GRANITE_STAIRS":
|
||||
case "POTATOES":
|
||||
case "POTTED_ACACIA_SAPLING":
|
||||
case "POTTED_ALLIUM":
|
||||
case "POTTED_AZURE_BLUET":
|
||||
case "POTTED_BAMBOO":
|
||||
case "POTTED_BIRCH_SAPLING":
|
||||
case "POTTED_BLUE_ORCHID":
|
||||
case "POTTED_BROWN_MUSHROOM":
|
||||
case "POTTED_CACTUS":
|
||||
case "POTTED_CORNFLOWER":
|
||||
case "POTTED_DANDELION":
|
||||
case "POTTED_DARK_OAK_SAPLING":
|
||||
case "POTTED_DEAD_BUSH":
|
||||
case "POTTED_FERN":
|
||||
case "POTTED_JUNGLE_SAPLING":
|
||||
case "POTTED_LILY_OF_THE_VALLEY":
|
||||
case "POTTED_OAK_SAPLING":
|
||||
case "POTTED_ORANGE_TULIP":
|
||||
case "POTTED_OXEYE_DAISY":
|
||||
case "POTTED_PINK_TULIP":
|
||||
case "POTTED_POPPY":
|
||||
case "POTTED_RED_MUSHROOM":
|
||||
case "POTTED_RED_TULIP":
|
||||
case "POTTED_SPRUCE_SAPLING":
|
||||
case "POTTED_WHITE_TULIP":
|
||||
case "POTTED_WITHER_ROSE":
|
||||
case "POWERED_RAIL":
|
||||
case "PRISMARINE_BRICK_SLAB":
|
||||
case "PRISMARINE_BRICK_STAIRS":
|
||||
case "PRISMARINE_SLAB":
|
||||
case "PRISMARINE_STAIRS":
|
||||
case "PUMPKIN_STEM":
|
||||
case "PURPLE_BANNER":
|
||||
case "PURPLE_CARPET":
|
||||
case "PURPLE_WALL_BANNER":
|
||||
case "PURPUR_SLAB":
|
||||
case "PURPUR_STAIRS":
|
||||
case "RAIL":
|
||||
case "REDSTONE_TORCH":
|
||||
case "REDSTONE_WALL_TORCH":
|
||||
case "REDSTONE_WIRE":
|
||||
case "RED_BANNER":
|
||||
case "RED_CARPET":
|
||||
case "RED_MUSHROOM":
|
||||
case "RED_SANDSTONE_SLAB":
|
||||
case "RED_SANDSTONE_STAIRS":
|
||||
case "RED_TULIP":
|
||||
case "RED_WALL_BANNER":
|
||||
case "REPEATER":
|
||||
case "ROSE_BUSH":
|
||||
case "SANDSTONE_SLAB":
|
||||
case "SANDSTONE_STAIRS":
|
||||
case "SCAFFOLDING":
|
||||
case "SEAGRASS":
|
||||
case "SMOOTH_QUARTZ_SLAB":
|
||||
case "SMOOTH_QUARTZ_STAIRS":
|
||||
case "SMOOTH_RED_SANDSTONE_SLAB":
|
||||
case "SMOOTH_RED_SANDSTONE_STAIRS":
|
||||
case "SMOOTH_SANDSTONE_SLAB":
|
||||
case "SMOOTH_SANDSTONE_STAIRS":
|
||||
case "SMOOTH_STONE_SLAB":
|
||||
case "SPRUCE_BUTTON":
|
||||
case "SPRUCE_DOOR":
|
||||
case "SPRUCE_FENCE_GATE":
|
||||
case "SPRUCE_PRESSURE_PLATE":
|
||||
case "SPRUCE_SAPLING":
|
||||
case "SPRUCE_SIGN":
|
||||
case "SPRUCE_SLAB":
|
||||
case "SPRUCE_STAIRS":
|
||||
case "SPRUCE_TRAPDOOR":
|
||||
case "SPRUCE_WALL_SIGN":
|
||||
case "STONECUTTER": // technically can step on, so sure
|
||||
case "STONE_BRICK_SLAB":
|
||||
case "STONE_BRICK_STAIRS":
|
||||
case "STONE_BUTTON":
|
||||
case "STONE_PRESSURE_PLATE":
|
||||
case "STONE_SLAB":
|
||||
case "STONE_STAIRS":
|
||||
case "STRUCTURE_VOID":
|
||||
case "SUGAR_CANE":
|
||||
case "SUNFLOWER":
|
||||
case "SWEET_BERRY_BUSH":
|
||||
case "TALL_GRASS":
|
||||
case "TALL_SEAGRASS":
|
||||
case "TORCH":
|
||||
case "TRIPWIRE":
|
||||
case "TRIPWIRE_HOOK":
|
||||
case "TUBE_CORAL_FAN":
|
||||
case "TUBE_CORAL_WALL_FAN":
|
||||
case "VINE":
|
||||
case "VOID_AIR":
|
||||
case "WALL_TORCH":
|
||||
case "WATER":
|
||||
case "WHEAT":
|
||||
case "WHITE_BANNER":
|
||||
case "WHITE_CARPET":
|
||||
case "WHITE_TULIP":
|
||||
case "WHITE_WALL_BANNER":
|
||||
case "WITHER_ROSE":
|
||||
case "YELLOW_BANNER":
|
||||
case "YELLOW_CARPET":
|
||||
case "YELLOW_WALL_BANNER":
|
||||
// Legacy values:
|
||||
case "WEB":
|
||||
case "LONG_GRASS":
|
||||
case "YELLOW_FLOWER":
|
||||
case "RED_ROSE":
|
||||
case "STEP":
|
||||
case "WOOD_STAIRS":
|
||||
case "CROPS":
|
||||
case "SIGN_POST":
|
||||
case "RAILS":
|
||||
case "WOODEN_DOOR":
|
||||
case "WALL_SIGN":
|
||||
case "STONE_PLATE":
|
||||
case "IRON_DOOR_BLOCK":
|
||||
case "WOOD_PLATE":
|
||||
case "REDSTONE_TORCH_OFF":
|
||||
case "REDSTONE_TORCH_ON":
|
||||
case "SNOW":
|
||||
case "SUGAR_CANE_BLOCK":
|
||||
case "PORTAL":
|
||||
case "CAKE_BLOCK":
|
||||
case "DIODE_BLOCK_OFF":
|
||||
case "DIODE_BLOCK_ON":
|
||||
case "TRAP_DOOR":
|
||||
case "FENCE_GATE":
|
||||
case "SMOOTH_STAIRS":
|
||||
case "ENDER_PORTAL":
|
||||
case "WOOD_STEP":
|
||||
case "SPRUCE_WOOD_STAIRS":
|
||||
case "BIRCH_WOOD_STAIRS":
|
||||
case "JUNGLE_WOOD_STAIRS":
|
||||
case "CARROT":
|
||||
case "POTATO":
|
||||
case "WOOD_BUTTON":
|
||||
case "GOLD_PLATE":
|
||||
case "IRON_PLATE":
|
||||
case "REDSTONE_COMPARATOR_OFF":
|
||||
case "REDSTONE_COMPARATOR_ON":
|
||||
case "QUARTZ_STAIRS":
|
||||
case "DOUBLE_PLANT":
|
||||
case "STANDING_BANNER":
|
||||
case "WALL_BANNER":
|
||||
case "DAYLIGHT_DETECTOR_INVERTED":
|
||||
case "DOUBLE_STONE_SLAB2":
|
||||
case "STONE_SLAB2":
|
||||
case "BEETROOT_BLOCK":
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
161
src/main/java/com/songoda/core/utils/BlockUtilsModern.java
Normal file
161
src/main/java/com/songoda/core/utils/BlockUtilsModern.java
Normal file
@ -0,0 +1,161 @@
|
||||
package com.songoda.core.utils;
|
||||
|
||||
import org.bukkit.Effect;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.data.Bisected;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.AnaloguePowerable;
|
||||
import org.bukkit.block.data.type.Door;
|
||||
import org.bukkit.block.data.type.Gate;
|
||||
import org.bukkit.block.data.type.Switch;
|
||||
import org.bukkit.block.data.type.TrapDoor;
|
||||
|
||||
public class BlockUtilsModern {
|
||||
|
||||
protected static void _updatePressurePlateModern(Block plate, int power) {
|
||||
BlockData blockData = plate.getBlockData();
|
||||
if (blockData instanceof AnaloguePowerable) {
|
||||
AnaloguePowerable a = (AnaloguePowerable) blockData;
|
||||
a.setPower(Math.max(a.getMaximumPower(), power));
|
||||
plate.setBlockData(a);
|
||||
}
|
||||
}
|
||||
|
||||
protected static void _toggleLeverModern(Block lever) {
|
||||
BlockData blockData = lever.getBlockData();
|
||||
if (blockData instanceof Switch) {
|
||||
Switch s = (Switch) blockData;
|
||||
s.setPowered(!s.isPowered());
|
||||
lever.setBlockData(s);
|
||||
//lever.getWorld().playEffect(lever.getLocation(), Effect.CLICK1, 0);
|
||||
lever.getState().update();
|
||||
}
|
||||
}
|
||||
|
||||
protected static void _pressButtonModern(Block button) {
|
||||
BlockData blockData = button.getBlockData();
|
||||
if (blockData instanceof Switch) {
|
||||
Switch s = (Switch) blockData;
|
||||
s.setPowered(true);
|
||||
button.setBlockData(s);
|
||||
//lever.getWorld().playEffect(lever.getLocation(), Effect.CLICK1, 0);
|
||||
button.getState().update();
|
||||
}
|
||||
}
|
||||
|
||||
static void _releaseButtonModern(Block button) {
|
||||
BlockData blockData = button.getBlockData();
|
||||
if (blockData instanceof Switch) {
|
||||
Switch s = (Switch) blockData;
|
||||
s.setPowered(false);
|
||||
button.setBlockData(s);
|
||||
//lever.getWorld().playEffect(lever.getLocation(), Effect.CLICK1, 0);
|
||||
button.getState().update();
|
||||
}
|
||||
}
|
||||
|
||||
protected static void _toggleDoorStatesModern(boolean allowDoorToOpen, Block... doors) {
|
||||
for (Block door : doors) {
|
||||
BlockData blockData;
|
||||
if (door == null || !((blockData = door.getBlockData()) instanceof Door)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Door data = (Door) blockData;
|
||||
if (!allowDoorToOpen && !data.isOpen()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The lower half of the door contains the open/close state
|
||||
if (data.getHalf() == Bisected.Half.TOP) {
|
||||
Block lowerHalf = door.getRelative(BlockFace.DOWN);
|
||||
if (lowerHalf.getBlockData() instanceof Door) {
|
||||
Door lowerData = (Door) lowerHalf.getBlockData();
|
||||
lowerData.setOpen(!data.isOpen());
|
||||
lowerHalf.setBlockData(lowerData);
|
||||
}
|
||||
} else {
|
||||
data.setOpen(!data.isOpen());
|
||||
door.setBlockData(data);
|
||||
}
|
||||
|
||||
// Play the door open/close sound
|
||||
door.getWorld().playEffect(door.getLocation(), Effect.DOOR_TOGGLE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
protected static Block _getDoubleDoorModern(Block block) {
|
||||
BlockData bd = block.getBlockData();
|
||||
Block door = null;
|
||||
if (bd instanceof Door) {
|
||||
final Door d = (Door) bd;
|
||||
final BlockFace face = d.getFacing();
|
||||
if (face.getModX() == 0) {
|
||||
if (d.getHinge() == Door.Hinge.RIGHT) {
|
||||
door = block.getRelative(face.getModZ(), 0, 0);
|
||||
} else {
|
||||
door = block.getRelative(-face.getModZ(), 0, 0);
|
||||
}
|
||||
} else {
|
||||
if (d.getHinge() == Door.Hinge.RIGHT) {
|
||||
door = block.getRelative(0, 0, -face.getModX());
|
||||
} else {
|
||||
door = block.getRelative(0, 0, face.getModX());
|
||||
}
|
||||
}
|
||||
}
|
||||
return door != null && door.getBlockData() instanceof Door
|
||||
&& ((Door) door.getBlockData()).getHinge() != ((Door) bd).getHinge() ? door : null;
|
||||
}
|
||||
|
||||
protected static BlockFace _getDoorClosedDirectionModern(Block door) {
|
||||
if (BlockUtils.DOORS.contains(door.getType())) {
|
||||
BlockData bd = door.getBlockData();
|
||||
if (bd instanceof Door) {
|
||||
Door d = (Door) bd;
|
||||
|
||||
// The lower half of the door contains the open/close state
|
||||
if (d.getHalf() == Bisected.Half.TOP) {
|
||||
door = door.getRelative(BlockFace.DOWN);
|
||||
if (door.getBlockData() instanceof Door) {
|
||||
d = (Door) door.getBlockData();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
final BlockFace face = d.getFacing();
|
||||
// now we /could/ also correct for the hinge (top block), it's not needed information
|
||||
if (face.getModX() == 0) {
|
||||
return d.isOpen() ? BlockFace.EAST : BlockFace.SOUTH;
|
||||
} else {
|
||||
return d.isOpen() ? BlockFace.SOUTH : BlockFace.EAST;
|
||||
}
|
||||
}
|
||||
} else if (BlockUtils.FENCE_GATES.contains(door.getType())) {
|
||||
BlockData bd = door.getBlockData();
|
||||
if (bd instanceof Gate) {
|
||||
Gate g = (Gate) bd;
|
||||
final BlockFace face = g.getFacing();
|
||||
if (face.getModX() == 0) {
|
||||
return g.isOpen() ? BlockFace.EAST : BlockFace.SOUTH;
|
||||
} else {
|
||||
return g.isOpen() ? BlockFace.SOUTH : BlockFace.EAST;
|
||||
}
|
||||
}
|
||||
} else if (BlockUtils.TRAP_DOORS.contains(door.getType())) {
|
||||
BlockData bd = door.getBlockData();
|
||||
if (bd instanceof TrapDoor) {
|
||||
TrapDoor t = (TrapDoor) bd;
|
||||
if (!t.isOpen()) {
|
||||
return BlockFace.UP;
|
||||
} else {
|
||||
return t.getFacing();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
893
src/main/java/com/songoda/core/utils/ItemUtils.java
Normal file
893
src/main/java/com/songoda/core/utils/ItemUtils.java
Normal file
@ -0,0 +1,893 @@
|
||||
/**
|
||||
* This class uses some Minecraft code and also Paper API
|
||||
*/
|
||||
package com.songoda.core.utils;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import com.songoda.core.compatibility.LegacyMaterials;
|
||||
import com.songoda.core.compatibility.ServerVersion;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Stream;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemFlag;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.Damageable;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.inventory.meta.SkullMeta;
|
||||
|
||||
public class ItemUtils {
|
||||
|
||||
static boolean check_compatibility = false;
|
||||
static boolean can_getI18NDisplayName = true;
|
||||
|
||||
static void init() {
|
||||
check_compatibility = true;
|
||||
try {
|
||||
ItemStack.class.getMethod("getI18NDisplayName");
|
||||
} catch (NoSuchMethodException | SecurityException ex) {
|
||||
can_getI18NDisplayName = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone of org.bukkit.inventory.ItemStack.asQuantity, since it is a paper-only function
|
||||
*
|
||||
* @param item item to copy
|
||||
* @param qty amount the new ItemStack should have
|
||||
* @return a copy of the original item
|
||||
*/
|
||||
public static ItemStack getAsCopy(ItemStack item, int qty) {
|
||||
ItemStack clone = item.clone();
|
||||
clone.setAmount(qty);
|
||||
return clone;
|
||||
}
|
||||
|
||||
public static ItemStack addDamage(ItemStack item, int damage) {
|
||||
if (item == null) {
|
||||
return null;
|
||||
} else if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13)) {
|
||||
// ItemStack.setDurability(short) still works in 1.13-1.14, but use these methods now
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (meta instanceof Damageable) {
|
||||
((Damageable) meta).setDamage(((Damageable) meta).getDamage() + damage);
|
||||
item.setItemMeta(meta);
|
||||
}
|
||||
} else {
|
||||
item.setDurability((short) Math.max(0, item.getDurability() + damage));
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
static Class cb_ItemStack = NMSUtils.getCraftClass("inventory.CraftItemStack");
|
||||
static Class mc_ItemStack = NMSUtils.getNMSClass("ItemStack");
|
||||
static Class mc_NBTTagCompound = NMSUtils.getNMSClass("NBTTagCompound");
|
||||
static Class mc_NBTTagList = NMSUtils.getNMSClass("NBTTagList");
|
||||
static Class mc_NBTBase = NMSUtils.getNMSClass("NBTBase");
|
||||
static Method mc_ItemStack_getTag;
|
||||
static Method mc_ItemStack_setTag;
|
||||
static Method mc_NBTTagCompound_set;
|
||||
static Method mc_NBTTagCompound_remove;
|
||||
static Method mc_NBTTagCompound_setShort;
|
||||
static Method mc_NBTTagCompound_setString;
|
||||
static Method mc_NBTTagList_add;
|
||||
static Method cb_CraftItemStack_asNMSCopy;
|
||||
static Method cb_CraftItemStack_asCraftMirror;
|
||||
static {
|
||||
if(cb_ItemStack != null) {
|
||||
try {
|
||||
mc_ItemStack_getTag = mc_ItemStack.getDeclaredMethod("getTag");
|
||||
mc_ItemStack_setTag = mc_ItemStack.getDeclaredMethod("setTag", mc_NBTTagCompound);
|
||||
mc_NBTTagCompound_set = mc_NBTTagCompound.getDeclaredMethod("set", String.class, mc_NBTBase);
|
||||
mc_NBTTagCompound_remove = mc_NBTTagCompound.getDeclaredMethod("remove", String.class);
|
||||
mc_NBTTagCompound_setShort = mc_NBTTagCompound.getDeclaredMethod("setShort", String.class, short.class);
|
||||
mc_NBTTagCompound_setString = mc_NBTTagCompound.getDeclaredMethod("setString", String.class, String.class);
|
||||
cb_CraftItemStack_asNMSCopy = cb_ItemStack.getDeclaredMethod("asNMSCopy", ItemStack.class);
|
||||
cb_CraftItemStack_asCraftMirror = cb_ItemStack.getDeclaredMethod("asCraftMirror", mc_ItemStack);
|
||||
mc_NBTTagList_add = ServerVersion.isServerVersionAtLeast(ServerVersion.V1_14)
|
||||
? NMSUtils.getPrivateMethod(mc_NBTTagList, "a", mc_NBTBase)
|
||||
: mc_NBTTagList.getDeclaredMethod("add", mc_NBTBase);
|
||||
} catch (Exception ex) {
|
||||
Logger.getLogger(ItemUtils.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an item glow as if it contained an enchantment. <br>
|
||||
* Tested working 1.8-1.14
|
||||
*
|
||||
* @param item itemstack to create a glowing copy of
|
||||
* @return copy of item with a blank enchantment nbt tag
|
||||
*/
|
||||
public static ItemStack addGlow(ItemStack item) {
|
||||
// from 1.11 up, fake enchantments don't work without more steps
|
||||
// creating a new Enchantment involves some very involved reflection,
|
||||
// as the namespace is the same but until 1.12 requires an int, but versions after require a String
|
||||
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_11)) {
|
||||
item.addUnsafeEnchantment(Enchantment.DURABILITY, 1);
|
||||
// you can at least hide the enchantment, though
|
||||
ItemMeta m = item.getItemMeta();
|
||||
m.addItemFlags(ItemFlag.HIDE_ENCHANTS);
|
||||
item.setItemMeta(m);
|
||||
return item;
|
||||
} else {
|
||||
// hack a fake enchant onto the item
|
||||
// Confirmed works on 1.8, 1.9, 1.10
|
||||
// Does not work 1.11+ (minecraft ignores the glitched enchantment)
|
||||
if (item != null && item.getType() != Material.AIR && cb_CraftItemStack_asCraftMirror != null) {
|
||||
try {
|
||||
Object nmsStack = cb_CraftItemStack_asNMSCopy.invoke(null, item);
|
||||
Object tag = mc_ItemStack_getTag.invoke(nmsStack);
|
||||
if (tag == null) {
|
||||
tag = mc_NBTTagCompound.newInstance();
|
||||
}
|
||||
// set to have a fake enchantment
|
||||
Object enchantmentList = mc_NBTTagList.newInstance();
|
||||
/*
|
||||
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13)) {
|
||||
// Servers from 1.13 and up change the id to a string
|
||||
Object fakeEnchantment = mc_NBTTagCompound.newInstance();
|
||||
mc_NBTTagCompound_setString.invoke(fakeEnchantment, "id", "glow:glow");
|
||||
mc_NBTTagCompound_setShort.invoke(fakeEnchantment, "lvl", (short) 0);
|
||||
mc_NBTTagList_add.invoke(enchantmentList, fakeEnchantment);
|
||||
} else if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_11)) {
|
||||
// Servers from 1.11 and up require *something* in the enchantment field
|
||||
Object fakeEnchantment = mc_NBTTagCompound.newInstance();
|
||||
mc_NBTTagCompound_setShort.invoke(fakeEnchantment, "id", (short) 245);
|
||||
mc_NBTTagCompound_setShort.invoke(fakeEnchantment, "lvl", (short) 1);
|
||||
mc_NBTTagList_add.invoke(enchantmentList, fakeEnchantment);
|
||||
}//*/
|
||||
mc_NBTTagCompound_set.invoke(tag, "ench", enchantmentList);
|
||||
mc_ItemStack_setTag.invoke(nmsStack, tag);
|
||||
item = (ItemStack) cb_CraftItemStack_asCraftMirror.invoke(null, nmsStack);
|
||||
} catch (Exception ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, "Failed to set glow enchantment on item: " + item, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all enchantments, including hidden enchantments
|
||||
* @param item item to clear enchants from
|
||||
* @return copy of the item without any enchantment tag
|
||||
*/
|
||||
public static ItemStack removeGlow(ItemStack item) {
|
||||
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_11)) {
|
||||
item.removeEnchantment(Enchantment.DURABILITY);
|
||||
return item;
|
||||
} else {
|
||||
if (item != null && item.getType() != Material.AIR && cb_CraftItemStack_asCraftMirror != null) {
|
||||
try {
|
||||
Object nmsStack = cb_CraftItemStack_asNMSCopy.invoke(null, item);
|
||||
Object tag = mc_ItemStack_getTag.invoke(nmsStack);
|
||||
if (tag != null) {
|
||||
// remove enchantment list
|
||||
mc_NBTTagCompound_remove.invoke(tag, "ench");
|
||||
mc_ItemStack_setTag.invoke(nmsStack, tag);
|
||||
item = (ItemStack) cb_CraftItemStack_asCraftMirror.invoke(null, nmsStack);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, "Failed to set glow enchantment on item: " + item, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
public static String getItemName(ItemStack it) {
|
||||
if (!check_compatibility) {
|
||||
init();
|
||||
}
|
||||
if (it == null) {
|
||||
return null;
|
||||
} else if (can_getI18NDisplayName) {
|
||||
return it.getI18NDisplayName();
|
||||
} else {
|
||||
return itemName(it.getType());
|
||||
}
|
||||
}
|
||||
|
||||
static String itemName(Material mat) {
|
||||
String matName = mat.name().replace("_", " ");
|
||||
StringBuilder titleCase = new StringBuilder(matName.length());
|
||||
|
||||
Stream.of(matName.split(" ")).forEach(s -> {
|
||||
s = s.toLowerCase();
|
||||
if (s.equals("of")) {
|
||||
titleCase.append(s).append(" ");
|
||||
} else {
|
||||
char[] str = s.toCharArray();
|
||||
str[0] = Character.toUpperCase(str[0]);
|
||||
titleCase.append(new String(str)).append(" ");
|
||||
}
|
||||
});
|
||||
|
||||
return titleCase.toString().trim();
|
||||
}
|
||||
|
||||
public static ItemStack getPlayerSkull(OfflinePlayer player) {
|
||||
ItemStack head = LegacyMaterials.PLAYER_HEAD.getItem();
|
||||
if (ServerVersion.isServerVersionBelow(ServerVersion.V1_8)) {
|
||||
return head;
|
||||
}
|
||||
|
||||
SkullMeta meta = (SkullMeta) head.getItemMeta();
|
||||
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13)) {
|
||||
meta.setOwningPlayer(player);
|
||||
} else {
|
||||
meta.setOwner(player.getName());
|
||||
}
|
||||
return head;
|
||||
}
|
||||
|
||||
public static void setHeadOwner(ItemStack head, OfflinePlayer player) {
|
||||
if (ServerVersion.isServerVersionBelow(ServerVersion.V1_8) || head == null || !LegacyMaterials.PLAYER_HEAD.matches(head)) {
|
||||
return;
|
||||
}
|
||||
SkullMeta meta = (SkullMeta) head.getItemMeta();
|
||||
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_13)) {
|
||||
meta.setOwningPlayer(player);
|
||||
} else {
|
||||
meta.setOwner(player.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public static ItemStack getCustomHead(String texture) {
|
||||
ItemStack skullItem = LegacyMaterials.PLAYER_HEAD.getItem();
|
||||
if (ServerVersion.isServerVersionBelow(ServerVersion.V1_8)) {
|
||||
return skullItem;
|
||||
}
|
||||
SkullMeta sm = (SkullMeta) skullItem.getItemMeta();
|
||||
GameProfile gm;
|
||||
if (texture.endsWith("=")) {
|
||||
gm = new GameProfile(UUID.nameUUIDFromBytes(texture.getBytes()), "CustomHead");
|
||||
gm.getProperties().put("textures", new Property("texture", texture.replaceAll("=", "")));
|
||||
} else {
|
||||
gm = new GameProfile(UUID.nameUUIDFromBytes(texture.getBytes()), "CustomHead");
|
||||
byte[] encodedData = Base64.getEncoder().encode(String.format("{textures:{SKIN:{url:\"http://textures.minecraft.net/texture/%s\"}}}", texture).getBytes());
|
||||
gm.getProperties().put("textures", new Property("textures", new String(encodedData)));
|
||||
}
|
||||
|
||||
try {
|
||||
Field profileField;
|
||||
profileField = sm.getClass().getDeclaredField("profile");
|
||||
profileField.setAccessible(true);
|
||||
profileField.set(sm, gm);
|
||||
skullItem.setItemMeta(sm);
|
||||
return skullItem;
|
||||
} catch (NoSuchFieldException | IllegalAccessException | SecurityException ex) {
|
||||
throw new RuntimeException("Reflection error while setting head texture", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isSimilarMaterial(ItemStack is1, ItemStack is2) {
|
||||
LegacyMaterials mat1 = LegacyMaterials.getMaterial(is1);
|
||||
return mat1 != null && mat1 == LegacyMaterials.getMaterial(is2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if this item can be moved into a single slot in this
|
||||
* inventory. <br>
|
||||
* This returns true if there is a free slot or a slot with a matching item
|
||||
* where adding this item's amount to that item's amount will not violate
|
||||
* the maximum stack size for that item.
|
||||
*
|
||||
* @param inventory inventory to check
|
||||
* @param item item to check against
|
||||
* @return true if a free slot or single receiver slot is available
|
||||
*/
|
||||
public static boolean canMove(Inventory inventory, ItemStack item) {
|
||||
if (inventory.firstEmpty() != -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final ItemMeta itemMeta = item.getItemMeta();
|
||||
for (ItemStack stack : inventory) {
|
||||
final ItemMeta stackMeta;
|
||||
if (isSimilarMaterial(stack, item) && (stack.getAmount() + item.getAmount()) < stack.getMaxStackSize()
|
||||
&& ((itemMeta == null) == ((stackMeta = stack.getItemMeta()) == null))
|
||||
&& (itemMeta == null || Bukkit.getItemFactory().equals(itemMeta, stackMeta))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if this item can be moved into a single slot in this
|
||||
* inventory. <br>
|
||||
* This returns true if there is a free slot or a slot with a matching item
|
||||
* where adding this item's amount to that item's amount will not violate
|
||||
* the maximum stack size for that item.
|
||||
*
|
||||
* @param contents inventory to check
|
||||
* @param item item to check against
|
||||
* @return true if a free slot or single receiver slot is available
|
||||
*/
|
||||
public static boolean canMove(ItemStack[] contents, ItemStack item) {
|
||||
final ItemMeta itemMeta = item.getItemMeta();
|
||||
for (int i = 0; i < contents.length; i++) {
|
||||
final ItemStack stack = contents[i];
|
||||
if (stack == null || stack.getAmount() == 0) {
|
||||
return true;
|
||||
}
|
||||
final ItemMeta stackMeta;
|
||||
if (isSimilarMaterial(stack, item) && (stack.getAmount() + item.getAmount()) < stack.getMaxStackSize()
|
||||
&& ((itemMeta == null) == ((stackMeta = stack.getItemMeta()) == null))
|
||||
&& (itemMeta == null || Bukkit.getItemFactory().equals(itemMeta, stackMeta))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if this item can be moved into a single slot in this
|
||||
* inventory while also reserving one of the slots.<br>
|
||||
* This returns true if there is a free slot or a slot with a matching item
|
||||
* where adding this item's amount to that item's amount will not violate
|
||||
* the maximum stack size for that item.
|
||||
*
|
||||
* @param inventory inventory to check
|
||||
* @param item item to check against
|
||||
* @param reserved which slot should be reserved
|
||||
* @return true if a free slot or single receiver slot is available
|
||||
*/
|
||||
public static boolean canMoveReserved(Inventory inventory, ItemStack item, int reserved) {
|
||||
final ItemMeta itemMeta = item.getItemMeta();
|
||||
final ItemStack[] contents = inventory.getContents();
|
||||
for (int i = 0; i < contents.length; i++) {
|
||||
if (i == reserved) {
|
||||
continue;
|
||||
}
|
||||
final ItemStack stack = contents[i];
|
||||
final ItemMeta stackMeta;
|
||||
if (stack == null || stack.getAmount() == 0
|
||||
|| (isSimilarMaterial(stack, item) && (stack.getAmount() + item.getAmount()) < stack.getMaxStackSize()
|
||||
&& ((itemMeta == null) == ((stackMeta = stack.getItemMeta()) == null))
|
||||
&& (itemMeta == null || Bukkit.getItemFactory().equals(itemMeta, stackMeta)))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if this item can be moved into a single slot in this
|
||||
* inventory while also reserving one of the slots.<br>
|
||||
* This returns true if there is a free slot or a slot with a matching item
|
||||
* where adding this item's amount to that item's amount will not violate
|
||||
* the maximum stack size for that item.
|
||||
*
|
||||
* @param contents inventory to check
|
||||
* @param item item to check against
|
||||
* @param reserved which slot should be reserved
|
||||
* @return true if a free slot or single receiver slot is available
|
||||
*/
|
||||
public static boolean canMoveReserved(ItemStack[] contents, ItemStack item, int reserved) {
|
||||
final ItemMeta itemMeta = item.getItemMeta();
|
||||
for (int i = 0; i < contents.length; i++) {
|
||||
if (i == reserved) {
|
||||
continue;
|
||||
}
|
||||
final ItemStack stack = contents[i];
|
||||
if (stack == null || stack.getAmount() == 0) {
|
||||
return true;
|
||||
}
|
||||
final ItemMeta stackMeta;
|
||||
if (isSimilarMaterial(stack, item) && (stack.getAmount() + item.getAmount()) < stack.getMaxStackSize()
|
||||
&& ((itemMeta == null) == ((stackMeta = stack.getItemMeta()) == null))
|
||||
&& (itemMeta == null || Bukkit.getItemFactory().equals(itemMeta, stackMeta))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add up to a number of items to this inventory.
|
||||
*
|
||||
* @param item item to add
|
||||
* @param amountToAdd how many of this item to attempt to add
|
||||
* @param inventory a list that represents the inventory
|
||||
* @param maxSize maximum number of different items this container can hold
|
||||
* @return how many items were added
|
||||
*/
|
||||
public static int addAny(ItemStack item, int amountToAdd, List<ItemStack> inventory, int maxSize) {
|
||||
return addAny(item, amountToAdd, inventory, maxSize, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add up to a number of items to this inventory.
|
||||
*
|
||||
* @param item item to add
|
||||
* @param amountToAdd how many of this item to attempt to add
|
||||
* @param inventory a list that represents the inventory
|
||||
* @param maxSize maximum number of different items this container can hold
|
||||
* @param reserved slot to reserve - will not fill this slot
|
||||
* @return how many items were added
|
||||
*/
|
||||
public static int addAny(ItemStack item, int amountToAdd, List<ItemStack> inventory, int maxSize, int reserved) {
|
||||
int totalAdded = 0;
|
||||
if (inventory != null && item != null && amountToAdd > 0) {
|
||||
final int maxStack = item.getMaxStackSize();
|
||||
for (int i = 0; amountToAdd > 0 && i < maxSize; i++) {
|
||||
if (i == reserved) {
|
||||
continue;
|
||||
}
|
||||
final ItemStack cacheItem = i >= inventory.size() ? null : inventory.get(i);
|
||||
if (cacheItem == null || cacheItem.getAmount() == 0) {
|
||||
// free slot!
|
||||
int toAdd = Math.min(maxStack, amountToAdd);
|
||||
ItemStack item2 = item.clone();
|
||||
item2.setAmount(toAdd);
|
||||
if (i >= inventory.size()) {
|
||||
inventory.add(item2);
|
||||
} else {
|
||||
inventory.set(i, item2);
|
||||
}
|
||||
totalAdded += toAdd;
|
||||
amountToAdd -= toAdd;
|
||||
} else if (maxStack > cacheItem.getAmount() && item.isSimilar(cacheItem)) {
|
||||
// free space!
|
||||
int toAdd = Math.min(maxStack - cacheItem.getAmount(), amountToAdd);
|
||||
inventory.get(i).setAmount(toAdd + cacheItem.getAmount());
|
||||
totalAdded += toAdd;
|
||||
amountToAdd -= toAdd;
|
||||
}
|
||||
}
|
||||
}
|
||||
return totalAdded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an item to this inventory, but only if it can be added completely.
|
||||
*
|
||||
* @param item item to add
|
||||
* @param inventory a list that represents the inventory
|
||||
* @param containerSize maximum number of different items this container can
|
||||
* hold
|
||||
* @return true if the item was added
|
||||
*/
|
||||
public static boolean addItem(ItemStack item, List<ItemStack> inventory, int containerSize) {
|
||||
if (inventory == null || item == null || item.getAmount() <= 0 || containerSize <= 0) {
|
||||
return false;
|
||||
}
|
||||
return addItem(item, item.getAmount(), inventory, containerSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an item to this inventory, but only if it can be added completely.
|
||||
*
|
||||
* @param item item to add
|
||||
* @param inventory a list that represents the inventory
|
||||
* @param containerSize maximum number of different items this container can
|
||||
* hold
|
||||
* @param reserved slot to reserve - will not fill this slot
|
||||
* @return true if the item was added
|
||||
*/
|
||||
public static boolean addItem(ItemStack item, List<ItemStack> inventory, int containerSize, int reserved) {
|
||||
if (inventory == null || item == null || item.getAmount() <= 0 || containerSize <= 0) {
|
||||
return false;
|
||||
}
|
||||
return addItem(item, item.getAmount(), inventory, containerSize, reserved);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Add an item to this inventory.
|
||||
*
|
||||
* @param item item to add
|
||||
* @param amount how many of this item should be added
|
||||
* @param inventory a list that represents the inventory
|
||||
* @param containerSize maximum number of different items this container can
|
||||
* @param reserved slot to reserve - will not fill this slot hold
|
||||
* @return true if the item was added
|
||||
*/
|
||||
public static boolean addItem(ItemStack item, int amount, List<ItemStack> inventory, int containerSize, int reserved) {
|
||||
return addItem(item, amount, inventory, containerSize, reserved, null);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Add an item to this inventory, but only if it can be added completely.
|
||||
*
|
||||
* @param item item to add
|
||||
* @param amount how many of this item should be added
|
||||
* @param inventory a list that represents the inventory
|
||||
* @param containerSize maximum number of different items this container can
|
||||
* hold
|
||||
* @param reserved slot to reserve - will not fill this slot
|
||||
* @param inventorySource Material of the container
|
||||
* @return true if the item was added
|
||||
*/
|
||||
public static boolean addItem(ItemStack item, int amount, List<ItemStack> inventory, int containerSize, int reserved, Material inventorySource) {
|
||||
if (inventory == null || item == null || amount <= 0 || inventorySource == null) {
|
||||
return false;
|
||||
}
|
||||
boolean[] check = null;
|
||||
|
||||
if (inventorySource != null && inventorySource != Material.AIR) {
|
||||
// Don't transfer shulker boxes into other shulker boxes, that's a bad idea.
|
||||
if (inventorySource.name().contains("SHULKER_BOX") && item.getType().name().contains("SHULKER_BOX")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// some destination containers have special conditions
|
||||
switch (inventorySource.name()) {
|
||||
case "BREWING_STAND": {
|
||||
|
||||
// first compile a list of what slots to check
|
||||
check = new boolean[5];
|
||||
String typeStr = item.getType().name().toUpperCase();
|
||||
if (typeStr.contains("POTION") || typeStr.contains("BOTTLE")) {
|
||||
// potion bottles are the first three slots
|
||||
check[0] = check[1] = check[2] = true;
|
||||
}
|
||||
// fuel in 5th position, input in 4th
|
||||
if (item.getType() == Material.BLAZE_POWDER) {
|
||||
check[4] = true;
|
||||
} else {
|
||||
check[3] = true;
|
||||
}
|
||||
|
||||
}
|
||||
case "SMOKER":
|
||||
case "BLAST_FURNACE":
|
||||
case "BURNING_FURNACE":
|
||||
case "FURNACE": {
|
||||
|
||||
check = new boolean[3];
|
||||
|
||||
boolean isFuel = !item.getType().name().contains("LOG") && LegacyMaterials.getMaterial(item.getType()).isFuel();
|
||||
// fuel is 2nd slot, input is first
|
||||
if (isFuel) {
|
||||
|
||||
check[1] = true;
|
||||
} else {
|
||||
check[0] = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// grab the amount to move and the max item stack size
|
||||
int toAdd = item.getAmount();
|
||||
final int maxStack = item.getMaxStackSize();
|
||||
|
||||
// we can reduce calls to ItemStack.isSimilar() by caching what cells to look at
|
||||
if (check == null) {
|
||||
check = new boolean[containerSize];
|
||||
for (int i = 0; toAdd > 0 && i < check.length; i++) {
|
||||
check[i] = true;
|
||||
}
|
||||
}
|
||||
if (reserved >= 0 && check.length < reserved) {
|
||||
check[reserved] = false;
|
||||
}
|
||||
|
||||
// first verify that we can add this item
|
||||
for (int i = 0; toAdd > 0 && i < containerSize; i++) {
|
||||
if (check[i]) {
|
||||
final ItemStack cacheItem = i >= inventory.size() ? null : inventory.get(i);
|
||||
if (cacheItem == null || cacheItem.getAmount() == 0) {
|
||||
// free slot!
|
||||
toAdd -= Math.min(maxStack, toAdd);
|
||||
check[i] = true;
|
||||
} else if (maxStack > cacheItem.getAmount() && item.isSimilar(cacheItem)) {
|
||||
// free space!
|
||||
toAdd -= Math.min(maxStack - cacheItem.getAmount(), toAdd);
|
||||
check[i] = true;
|
||||
} else {
|
||||
check[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (toAdd <= 0) {
|
||||
// all good to add!
|
||||
toAdd = item.getAmount();
|
||||
for (int i = 0; toAdd > 0 && i < containerSize; i++) {
|
||||
if (!check[i]) {
|
||||
continue;
|
||||
}
|
||||
final ItemStack cacheItem = i >= inventory.size() ? null : inventory.get(i);
|
||||
if (cacheItem == null || cacheItem.getAmount() == 0) {
|
||||
// free slot!
|
||||
int adding = Math.min(maxStack, toAdd);
|
||||
ItemStack item2 = item.clone();
|
||||
item2.setAmount(adding);
|
||||
if (i >= inventory.size()) {
|
||||
inventory.add(item2);
|
||||
} else {
|
||||
inventory.set(i, item2);
|
||||
}
|
||||
toAdd -= adding;
|
||||
} else if (maxStack > cacheItem.getAmount()) {
|
||||
// free space!
|
||||
// (no need to check item.isSimilar(cacheItem), since we have that cached in check[])
|
||||
int adding = Math.min(maxStack - cacheItem.getAmount(), toAdd);
|
||||
inventory.get(i).setAmount(adding + cacheItem.getAmount());
|
||||
toAdd -= adding;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add up to a number of items to this inventory.
|
||||
*
|
||||
* @param item item to add
|
||||
* @param amountToAdd how many of this item to attempt to add
|
||||
* @param inventory a list that represents the inventory
|
||||
* @return how many items were added
|
||||
*/
|
||||
public static int addAny(ItemStack item, int amountToAdd, Inventory inventory) {
|
||||
int totalAdded = 0;
|
||||
if (inventory != null && item != null && amountToAdd > 0) {
|
||||
final int containerSize = inventory.getSize();
|
||||
final int maxStack = item.getMaxStackSize();
|
||||
for (int i = 0; amountToAdd > 0 && i < containerSize; i++) {
|
||||
final ItemStack cacheItem = inventory.getItem(i);
|
||||
if (cacheItem == null || cacheItem.getAmount() == 0) {
|
||||
// free slot!
|
||||
int toAdd = Math.min(maxStack, amountToAdd);
|
||||
ItemStack item2 = item.clone();
|
||||
item2.setAmount(toAdd);
|
||||
inventory.setItem(i, item2);
|
||||
totalAdded += toAdd;
|
||||
amountToAdd -= toAdd;
|
||||
} else if (maxStack > cacheItem.getAmount() && item.isSimilar(cacheItem)) {
|
||||
// free space!
|
||||
int toAdd = Math.min(maxStack - cacheItem.getAmount(), amountToAdd);
|
||||
cacheItem.setAmount(toAdd + cacheItem.getAmount());
|
||||
totalAdded += toAdd;
|
||||
amountToAdd -= toAdd;
|
||||
}
|
||||
}
|
||||
}
|
||||
return totalAdded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an item to this inventory, but only if it can be added completely.
|
||||
*
|
||||
* @param item item to add
|
||||
* @param inventory a list that represents the inventory hold
|
||||
* @return true if the item was added
|
||||
*/
|
||||
public static boolean addItem(ItemStack item, Inventory inventory) {
|
||||
if (inventory == null || item == null || item.getAmount() <= 0) {
|
||||
return false;
|
||||
}
|
||||
return addItem(item, item.getAmount(), inventory, -1, null);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Add an item to this inventory.
|
||||
*
|
||||
* @param item item to add
|
||||
* @param amount how many of this item should be added
|
||||
* @param inventory a list that represents the inventory
|
||||
* @param reserved slot to reserve - will not fill this slot
|
||||
* @return true if the item was added
|
||||
*/
|
||||
public static boolean addItem(ItemStack item, int amount, Inventory inventory, int reserved) {
|
||||
return addItem(item, amount, inventory, reserved, null);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Add an item to this inventory, but only if it can be added completely.
|
||||
*
|
||||
* @param item item to add
|
||||
* @param amount how many of this item should be added
|
||||
* @param inventory a list that represents the inventory
|
||||
* @param reserved slot to reserve - will not fill this slot
|
||||
* @param inventorySource Material of the container
|
||||
* @return true if the item was added
|
||||
*/
|
||||
public static boolean addItem(ItemStack item, int amount, Inventory inventory, int reserved, Material inventorySource) {
|
||||
if (inventory == null || item == null || amount <= 0 || inventorySource == null) {
|
||||
return false;
|
||||
}
|
||||
boolean[] check = null;
|
||||
|
||||
if (inventorySource != null && inventorySource != Material.AIR) {
|
||||
|
||||
// Don't transfer shulker boxes into other shulker boxes, that's a bad idea.
|
||||
if (inventorySource.name().contains("SHULKER_BOX") && item.getType().name().contains("SHULKER_BOX")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// some destination containers have special conditions
|
||||
switch (inventorySource.name()) {
|
||||
case "BREWING_STAND": {
|
||||
|
||||
// first compile a list of what slots to check
|
||||
check = new boolean[5];
|
||||
String typeStr = item.getType().name().toUpperCase();
|
||||
if (typeStr.contains("POTION") || typeStr.contains("BOTTLE")) {
|
||||
// potion bottles are the first three slots
|
||||
check[0] = check[1] = check[2] = true;
|
||||
}
|
||||
// fuel in 5th position, input in 4th
|
||||
if (item.getType() == Material.BLAZE_POWDER) {
|
||||
check[4] = true;
|
||||
} else {
|
||||
check[3] = true;
|
||||
}
|
||||
|
||||
}
|
||||
case "SMOKER":
|
||||
case "BLAST_FURNACE":
|
||||
case "BURNING_FURNACE":
|
||||
case "FURNACE": {
|
||||
|
||||
check = new boolean[3];
|
||||
|
||||
boolean isFuel = !item.getType().name().contains("LOG") && LegacyMaterials.getMaterial(item.getType()).isFuel();
|
||||
// fuel is 2nd slot, input is first
|
||||
if (isFuel) {
|
||||
|
||||
check[1] = true;
|
||||
} else {
|
||||
check[0] = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
// grab the amount to move and the max item stack size
|
||||
int toAdd = item.getAmount();
|
||||
final int maxStack = item.getMaxStackSize();
|
||||
final int containerSize = inventory.getSize();
|
||||
|
||||
// we can reduce calls to ItemStack.isSimilar() by caching what cells to look at
|
||||
if (check == null) {
|
||||
check = new boolean[containerSize];
|
||||
for (int i = 0; toAdd > 0 && i < check.length; i++) {
|
||||
check[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// first verify that we can add this item
|
||||
for (int i = 0; toAdd > 0 && i < containerSize; i++) {
|
||||
if (check[i]) {
|
||||
final ItemStack cacheItem = inventory.getItem(i);
|
||||
if (cacheItem == null || cacheItem.getAmount() == 0) {
|
||||
// free slot!
|
||||
toAdd -= Math.min(maxStack, toAdd);
|
||||
check[i] = true;
|
||||
} else if (maxStack > cacheItem.getAmount() && item.isSimilar(cacheItem)) {
|
||||
// free space!
|
||||
toAdd -= Math.min(maxStack - cacheItem.getAmount(), toAdd);
|
||||
check[i] = true;
|
||||
} else {
|
||||
check[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (toAdd <= 0) {
|
||||
// all good to add!
|
||||
toAdd = item.getAmount();
|
||||
for (int i = 0; toAdd > 0 && i < containerSize; i++) {
|
||||
if (!check[i]) {
|
||||
continue;
|
||||
}
|
||||
final ItemStack cacheItem = inventory.getItem(i);
|
||||
if (cacheItem == null || cacheItem.getAmount() == 0) {
|
||||
// free slot!
|
||||
int adding = Math.min(maxStack, toAdd);
|
||||
ItemStack item2 = item.clone();
|
||||
item2.setAmount(adding);
|
||||
inventory.setItem(i, item2);
|
||||
toAdd -= adding;
|
||||
} else if (maxStack > cacheItem.getAmount()) {
|
||||
// free space!
|
||||
// (no need to check item.isSimilar(cacheItem), since we have that cached in check[])
|
||||
int adding = Math.min(maxStack - cacheItem.getAmount(), toAdd);
|
||||
cacheItem.setAmount(adding + cacheItem.getAmount());
|
||||
toAdd -= adding;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Add an item to this inventory.
|
||||
*
|
||||
* @param item item to add
|
||||
* @param amount how many of this item should be added
|
||||
* @param inventory a list that represents the inventory
|
||||
* @param containerSize maximum number of different items this container can
|
||||
* hold
|
||||
* @return true if the item was added
|
||||
*/
|
||||
public static boolean addItem(ItemStack item, int amount, List<ItemStack> inventory, int containerSize) {
|
||||
if (inventory == null || item == null || amount <= 0 || containerSize <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// grab the amount to move and the max item stack size
|
||||
int toAdd = amount;
|
||||
final int maxStack = item.getMaxStackSize();
|
||||
boolean[] check = null;
|
||||
|
||||
// we can reduce calls to ItemStack.isSimilar() by caching what cells to look at
|
||||
if (check == null) {
|
||||
check = new boolean[containerSize];
|
||||
for (int i = 0; toAdd > 0 && i < check.length; i++) {
|
||||
check[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// first verify that we can add this item
|
||||
for (int i = 0; toAdd > 0 && i < containerSize; i++) {
|
||||
if (check[i]) {
|
||||
final ItemStack cacheItem = i >= inventory.size() ? null : inventory.get(i);
|
||||
if (cacheItem == null || cacheItem.getAmount() == 0) {
|
||||
// free slot!
|
||||
toAdd -= Math.min(maxStack, toAdd);
|
||||
check[i] = true;
|
||||
} else if (maxStack > cacheItem.getAmount() && item.isSimilar(cacheItem)) {
|
||||
// free space!
|
||||
toAdd -= Math.min(maxStack - cacheItem.getAmount(), toAdd);
|
||||
check[i] = true;
|
||||
} else {
|
||||
check[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (toAdd <= 0) {
|
||||
// all good to add!
|
||||
toAdd = item.getAmount();
|
||||
for (int i = 0; toAdd > 0 && i < containerSize; i++) {
|
||||
if (!check[i]) {
|
||||
continue;
|
||||
}
|
||||
final ItemStack cacheItem = i >= inventory.size() ? null : inventory.get(i);
|
||||
if (cacheItem == null || cacheItem.getAmount() == 0) {
|
||||
// free slot!
|
||||
int adding = Math.min(maxStack, toAdd);
|
||||
ItemStack item2 = item.clone();
|
||||
item2.setAmount(adding);
|
||||
if (i >= inventory.size()) {
|
||||
inventory.add(item2);
|
||||
} else {
|
||||
inventory.set(i, item2);
|
||||
}
|
||||
toAdd -= adding;
|
||||
} else if (maxStack > cacheItem.getAmount()) {
|
||||
// free space!
|
||||
// (no need to check item.isSimilar(cacheItem), since we have that cached in check[])
|
||||
int adding = Math.min(maxStack - cacheItem.getAmount(), toAdd);
|
||||
inventory.get(i).setAmount(adding + cacheItem.getAmount());
|
||||
toAdd -= adding;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
698
src/main/java/com/songoda/core/utils/Metrics.java
Normal file
698
src/main/java/com/songoda/core/utils/Metrics.java
Normal file
@ -0,0 +1,698 @@
|
||||
package com.songoda.core.utils;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.*;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.logging.Level;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
/**
|
||||
* bStats collects some data for plugin authors.
|
||||
* <p>
|
||||
* Check out https://bStats.org/ to learn more about bStats!
|
||||
*/
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
public class Metrics {
|
||||
|
||||
static {
|
||||
// You can use the property to disable the check in your test environment
|
||||
if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) {
|
||||
// Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D
|
||||
final String defaultPackage = new String(
|
||||
new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't'});
|
||||
final String examplePackage = new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
|
||||
// We want to make sure nobody just copy & pastes the example and use the wrong package names
|
||||
if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) {
|
||||
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The version of this bStats class
|
||||
public static final int B_STATS_VERSION = 1;
|
||||
|
||||
// The url to which the data is sent
|
||||
private static final String URL = "https://bStats.org/submitData/bukkit";
|
||||
|
||||
// Is bStats enabled on this server?
|
||||
private boolean enabled;
|
||||
|
||||
// Should failed requests be logged?
|
||||
private static boolean logFailedRequests;
|
||||
|
||||
// Should the sent data be logged?
|
||||
private static boolean logSentData;
|
||||
|
||||
// Should the response text be logged?
|
||||
private static boolean logResponseStatusText;
|
||||
|
||||
// The uuid of the server
|
||||
private static String serverUUID;
|
||||
|
||||
// The plugin
|
||||
private final Plugin plugin;
|
||||
|
||||
// A list with all custom charts
|
||||
private final List<CustomChart> charts = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param plugin The plugin which stats should be submitted.
|
||||
*/
|
||||
public static void start(Plugin plugin) {
|
||||
Metrics m = new Metrics(plugin);
|
||||
if (m.enabled) {
|
||||
boolean found = false;
|
||||
// Search for all other bStats Metrics classes to see if we are the first one
|
||||
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
|
||||
try {
|
||||
service.getField("B_STATS_VERSION"); // Our identifier :)
|
||||
found = true; // We aren't the first
|
||||
break;
|
||||
} catch (NoSuchFieldException ignored) { }
|
||||
}
|
||||
// Register this specific instance as a service
|
||||
Bukkit.getServicesManager().register(Metrics.class, m, plugin, ServicePriority.Normal);
|
||||
if (!found) {
|
||||
// We are the first!
|
||||
m.startSubmitting();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Metrics(Plugin plugin) {
|
||||
if (plugin == null) {
|
||||
throw new IllegalArgumentException("Plugin cannot be null!");
|
||||
}
|
||||
this.plugin = plugin;
|
||||
|
||||
// Get the config file
|
||||
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
|
||||
File configFile = new File(bStatsFolder, "config.yml");
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
|
||||
|
||||
// Check if the config file exists
|
||||
if (!config.isSet("serverUuid")) {
|
||||
|
||||
// Add default values
|
||||
config.addDefault("enabled", true);
|
||||
// Every server gets it's unique random id.
|
||||
config.addDefault("serverUuid", UUID.randomUUID().toString());
|
||||
// Should failed request be logged?
|
||||
config.addDefault("logFailedRequests", false);
|
||||
// Should the sent data be logged?
|
||||
config.addDefault("logSentData", false);
|
||||
// Should the response text be logged?
|
||||
config.addDefault("logResponseStatusText", false);
|
||||
|
||||
// Inform the server owners about bStats
|
||||
config.options().header(
|
||||
"bStats collects some data for plugin authors like how many servers are using their plugins.\n" +
|
||||
"To honor their work, you should not disable it.\n" +
|
||||
"This has nearly blacklist effect on the server performance!\n" +
|
||||
"Check out https://bStats.org/ to learn more :)"
|
||||
).copyDefaults(true);
|
||||
try {
|
||||
config.save(configFile);
|
||||
} catch (IOException ignored) { }
|
||||
}
|
||||
|
||||
// Load the data
|
||||
enabled = config.getBoolean("enabled", true);
|
||||
serverUUID = config.getString("serverUuid");
|
||||
logFailedRequests = config.getBoolean("logFailedRequests", false);
|
||||
logSentData = config.getBoolean("logSentData", false);
|
||||
logResponseStatusText = config.getBoolean("logResponseStatusText", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if bStats is enabled.
|
||||
*
|
||||
* @return Whether bStats is enabled or not.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a custom chart.
|
||||
*
|
||||
* @param chart The chart to add.
|
||||
*/
|
||||
public void addCustomChart(CustomChart chart) {
|
||||
if (chart == null) {
|
||||
throw new IllegalArgumentException("Chart cannot be null!");
|
||||
}
|
||||
charts.add(chart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the Scheduler which submits our data every 30 minutes.
|
||||
*/
|
||||
private void startSubmitting() {
|
||||
final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags
|
||||
timer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!plugin.isEnabled()) { // Plugin was disabled
|
||||
timer.cancel();
|
||||
return;
|
||||
}
|
||||
// Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler
|
||||
// Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;)
|
||||
Bukkit.getScheduler().runTask(plugin, () -> submitData());
|
||||
}
|
||||
}, 1000 * 60 * 5, 1000 * 60 * 30);
|
||||
// Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start
|
||||
// WARNING: Changing the frequency has blacklist effect but your plugin WILL be blocked/deleted!
|
||||
// WARNING: Just don't do it!
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the plugin specific data.
|
||||
* This method is called using Reflection.
|
||||
*
|
||||
* @return The plugin specific data.
|
||||
*/
|
||||
public JSONObject getPluginData() {
|
||||
JSONObject data = new JSONObject();
|
||||
|
||||
String pluginName = plugin.getDescription().getName();
|
||||
String pluginVersion = plugin.getDescription().getVersion();
|
||||
|
||||
data.put("pluginName", pluginName); // Append the name of the plugin
|
||||
data.put("pluginVersion", pluginVersion); // Append the version of the plugin
|
||||
JSONArray customCharts = new JSONArray();
|
||||
for (CustomChart customChart : charts) {
|
||||
// Add the data of the custom charts
|
||||
JSONObject chart = customChart.getRequestJsonObject();
|
||||
if (chart == null) { // If the chart is null, we skip it
|
||||
continue;
|
||||
}
|
||||
customCharts.add(chart);
|
||||
}
|
||||
data.put("customCharts", customCharts);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server specific data.
|
||||
*
|
||||
* @return The server specific data.
|
||||
*/
|
||||
private JSONObject getServerData() {
|
||||
// Minecraft specific data
|
||||
int playerAmount;
|
||||
try {
|
||||
// Around MC 1.8 the return type was changed to a collection from an array,
|
||||
// This fixes java.lang.NoSuchMethodError: org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
|
||||
Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
|
||||
playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class)
|
||||
? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size()
|
||||
: ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
|
||||
} catch (Exception e) {
|
||||
playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed
|
||||
}
|
||||
int onlineMode = Bukkit.getOnlineMode() ? 1 : 0;
|
||||
String bukkitVersion = Bukkit.getVersion();
|
||||
|
||||
// OS/Java specific data
|
||||
String javaVersion = System.getProperty("java.version");
|
||||
String osName = System.getProperty("os.name");
|
||||
String osArch = System.getProperty("os.arch");
|
||||
String osVersion = System.getProperty("os.version");
|
||||
int coreCount = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
JSONObject data = new JSONObject();
|
||||
|
||||
data.put("serverUUID", serverUUID);
|
||||
|
||||
data.put("playerAmount", playerAmount);
|
||||
data.put("onlineMode", onlineMode);
|
||||
data.put("bukkitVersion", bukkitVersion);
|
||||
|
||||
data.put("javaVersion", javaVersion);
|
||||
data.put("osName", osName);
|
||||
data.put("osArch", osArch);
|
||||
data.put("osVersion", osVersion);
|
||||
data.put("coreCount", coreCount);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the data and sends it afterwards.
|
||||
*/
|
||||
private void submitData() {
|
||||
final JSONObject data = getServerData();
|
||||
|
||||
JSONArray pluginData = new JSONArray();
|
||||
// Search for all other bStats Metrics classes to get their plugin data
|
||||
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
|
||||
try {
|
||||
service.getField("B_STATS_VERSION"); // Our identifier :)
|
||||
|
||||
for (RegisteredServiceProvider<?> provider : Bukkit.getServicesManager().getRegistrations(service)) {
|
||||
try {
|
||||
pluginData.add(provider.getService().getMethod("getPluginData").invoke(provider.getProvider()));
|
||||
} catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { }
|
||||
}
|
||||
} catch (NoSuchFieldException ignored) { }
|
||||
}
|
||||
|
||||
data.put("plugins", pluginData);
|
||||
|
||||
// Create a new thread for the connection to the bStats server
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
// Send the data
|
||||
sendData(plugin, data);
|
||||
} catch (Exception e) {
|
||||
// Something went wrong! :(
|
||||
if (logFailedRequests) {
|
||||
plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the data to the bStats server.
|
||||
*
|
||||
* @param plugin Any plugin. It's just used to get a logger instance.
|
||||
* @param data The data to send.
|
||||
* @throws Exception If the request failed.
|
||||
*/
|
||||
private static void sendData(Plugin plugin, JSONObject data) throws Exception {
|
||||
if (data == null) {
|
||||
throw new IllegalArgumentException("Data cannot be null!");
|
||||
}
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
throw new IllegalAccessException("This method must not be called from the main thread!");
|
||||
}
|
||||
if (logSentData) {
|
||||
plugin.getLogger().info("Sending data to bStats: " + data.toString());
|
||||
}
|
||||
HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
|
||||
|
||||
// Compress the data to save bandwidth
|
||||
byte[] compressedData = compress(data.toString());
|
||||
|
||||
// Add headers
|
||||
connection.setRequestMethod("POST");
|
||||
connection.addRequestProperty("Accept", "application/json");
|
||||
connection.addRequestProperty("Connection", "close");
|
||||
connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
|
||||
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
|
||||
connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format
|
||||
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
|
||||
|
||||
// Send data
|
||||
connection.setDoOutput(true);
|
||||
DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
|
||||
outputStream.write(compressedData);
|
||||
outputStream.flush();
|
||||
outputStream.close();
|
||||
|
||||
InputStream inputStream = connection.getInputStream();
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
builder.append(line);
|
||||
}
|
||||
bufferedReader.close();
|
||||
if (logResponseStatusText) {
|
||||
plugin.getLogger().info("Sent data to bStats and received response: " + builder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gzips the given String.
|
||||
*
|
||||
* @param str The string to gzip.
|
||||
* @return The gzipped String.
|
||||
* @throws IOException If the compression failed.
|
||||
*/
|
||||
private static byte[] compress(final String str) throws IOException {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
|
||||
gzip.write(str.getBytes(StandardCharsets.UTF_8));
|
||||
gzip.close();
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom chart.
|
||||
*/
|
||||
public static abstract class CustomChart {
|
||||
|
||||
// The id of the chart
|
||||
final String chartId;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
*/
|
||||
CustomChart(String chartId) {
|
||||
if (chartId == null || chartId.isEmpty()) {
|
||||
throw new IllegalArgumentException("ChartId cannot be null or empty!");
|
||||
}
|
||||
this.chartId = chartId;
|
||||
}
|
||||
|
||||
private JSONObject getRequestJsonObject() {
|
||||
JSONObject chart = new JSONObject();
|
||||
chart.put("chartId", chartId);
|
||||
try {
|
||||
JSONObject data = getChartData();
|
||||
if (data == null) {
|
||||
// If the data is null we don't send the chart.
|
||||
return null;
|
||||
}
|
||||
chart.put("data", data);
|
||||
} catch (Throwable t) {
|
||||
if (logFailedRequests) {
|
||||
Bukkit.getLogger().log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return chart;
|
||||
}
|
||||
|
||||
protected abstract JSONObject getChartData() throws Exception;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom simple pie.
|
||||
*/
|
||||
public static class SimplePie extends CustomChart {
|
||||
|
||||
private final Callable<String> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimplePie(String chartId, Callable<String> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
String value = callable.call();
|
||||
if (value == null || value.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("value", value);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom advanced pie.
|
||||
*/
|
||||
public static class AdvancedPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedPie(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
continue; // Skip this invalid
|
||||
}
|
||||
allSkipped = false;
|
||||
values.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom drilldown pie.
|
||||
*/
|
||||
public static class DrilldownPie extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Map<String, Integer>>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public DrilldownPie(String chartId, Callable<Map<String, Map<String, Integer>>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, Map<String, Integer>> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean reallyAllSkipped = true;
|
||||
for (Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) {
|
||||
JSONObject value = new JSONObject();
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> valueEntry : map.get(entryValues.getKey()).entrySet()) {
|
||||
value.put(valueEntry.getKey(), valueEntry.getValue());
|
||||
allSkipped = false;
|
||||
}
|
||||
if (!allSkipped) {
|
||||
reallyAllSkipped = false;
|
||||
values.put(entryValues.getKey(), value);
|
||||
}
|
||||
}
|
||||
if (reallyAllSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom single line chart.
|
||||
*/
|
||||
public static class SingleLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Integer> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SingleLineChart(String chartId, Callable<Integer> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
int value = callable.call();
|
||||
if (value == 0) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("value", value);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom multi line chart.
|
||||
*/
|
||||
public static class MultiLineChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public MultiLineChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
if (entry.getValue() == 0) {
|
||||
continue; // Skip this invalid
|
||||
}
|
||||
allSkipped = false;
|
||||
values.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom simple bar chart.
|
||||
*/
|
||||
public static class SimpleBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, Integer>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public SimpleBarChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, Integer> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
JSONArray categoryValues = new JSONArray();
|
||||
categoryValues.add(entry.getValue());
|
||||
values.put(entry.getKey(), categoryValues);
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a custom advanced bar chart.
|
||||
*/
|
||||
public static class AdvancedBarChart extends CustomChart {
|
||||
|
||||
private final Callable<Map<String, int[]>> callable;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
* @param chartId The id of the chart.
|
||||
* @param callable The callable which is used to request the chart data.
|
||||
*/
|
||||
public AdvancedBarChart(String chartId, Callable<Map<String, int[]>> callable) {
|
||||
super(chartId);
|
||||
this.callable = callable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject getChartData() throws Exception {
|
||||
JSONObject data = new JSONObject();
|
||||
JSONObject values = new JSONObject();
|
||||
Map<String, int[]> map = callable.call();
|
||||
if (map == null || map.isEmpty()) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
boolean allSkipped = true;
|
||||
for (Map.Entry<String, int[]> entry : map.entrySet()) {
|
||||
if (entry.getValue().length == 0) {
|
||||
continue; // Skip this invalid
|
||||
}
|
||||
allSkipped = false;
|
||||
JSONArray categoryValues = new JSONArray();
|
||||
for (int categoryValue : entry.getValue()) {
|
||||
categoryValues.add(categoryValue);
|
||||
}
|
||||
values.put(entry.getKey(), categoryValues);
|
||||
}
|
||||
if (allSkipped) {
|
||||
// Null = skip the chart
|
||||
return null;
|
||||
}
|
||||
data.put("values", values);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
92
src/main/java/com/songoda/core/utils/NMSUtils.java
Normal file
92
src/main/java/com/songoda/core/utils/NMSUtils.java
Normal file
@ -0,0 +1,92 @@
|
||||
package com.songoda.core.utils;
|
||||
|
||||
import com.songoda.core.compatibility.ServerVersion;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class NMSUtils {
|
||||
|
||||
public static Class<?> getNMSClass(String className) {
|
||||
try {
|
||||
String fullName = "net.minecraft.server." + ServerVersion.getServerVersionString() + "." + className;
|
||||
Class<?> clazz = Class.forName(fullName);
|
||||
return clazz;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Class<?> getCraftClass(String className) {
|
||||
try {
|
||||
String fullName = "org.bukkit.craftbukkit." + ServerVersion.getServerVersionString() + "." + className;
|
||||
Class<?> clazz = Class.forName(fullName);
|
||||
return clazz;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Method getPrivateMethod(Class<?> c, String methodName, Class<?> ... parameters) throws Exception {
|
||||
Method m = c.getDeclaredMethod(methodName, parameters);
|
||||
m.setAccessible(true);
|
||||
return m;
|
||||
}
|
||||
|
||||
public static Field getField(Class<?> clazz, String name, boolean declared) {
|
||||
try {
|
||||
Field field;
|
||||
|
||||
if (declared) {
|
||||
field = clazz.getDeclaredField(name);
|
||||
} else {
|
||||
field = clazz.getField(name);
|
||||
}
|
||||
|
||||
field.setAccessible(true);
|
||||
return field;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Object getFieldObject(Object object, Field field) {
|
||||
try {
|
||||
return field.get(object);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void setField(Object object, String fieldName, Object fieldValue, boolean declared) {
|
||||
try {
|
||||
Field field;
|
||||
|
||||
if (declared) {
|
||||
field = object.getClass().getDeclaredField(fieldName);
|
||||
} else {
|
||||
field = object.getClass().getField(fieldName);
|
||||
}
|
||||
|
||||
field.setAccessible(true);
|
||||
field.set(object, fieldValue);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendPacket(Player player, Object packet) {
|
||||
try {
|
||||
Object handle = player.getClass().getMethod("getHandle").invoke(player);
|
||||
Object playerConnection = handle.getClass().getField("playerConnection").get(handle);
|
||||
playerConnection.getClass().getMethod("sendPacket", getNMSClass("Packet")).invoke(playerConnection, packet);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
149
src/main/java/com/songoda/core/utils/PlayerUtils.java
Normal file
149
src/main/java/com/songoda/core/utils/PlayerUtils.java
Normal file
@ -0,0 +1,149 @@
|
||||
package com.songoda.core.utils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.stream.Collectors;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class PlayerUtils {
|
||||
|
||||
static Random random = new Random();
|
||||
|
||||
/**
|
||||
* Get a list of all of the players that this player can "see"
|
||||
*
|
||||
* @param sender user to check against, or null for all players
|
||||
* @param startingWith optional query to test: only players whose game names
|
||||
* start with this
|
||||
* @return list of player names that are "visible" to the player
|
||||
*/
|
||||
public static List<String> getVisiblePlayerNames(CommandSender sender, String startingWith) {
|
||||
Player player = sender instanceof Player ? (Player) sender : null;
|
||||
final String startsWith = startingWith == null || startingWith.isEmpty() ? null : startingWith.toLowerCase();
|
||||
return Bukkit.getOnlinePlayers().stream()
|
||||
.filter(p -> p != player)
|
||||
.filter(p -> startsWith == null || p.getName().toLowerCase().startsWith(startsWith))
|
||||
.filter(p -> player == null || (player.canSee(p) && p.getMetadata("vanished").isEmpty()))
|
||||
.map(Player::getName)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all of the players that this player can "see"
|
||||
*
|
||||
* @param sender user to check against, or null for all players
|
||||
* @param startingWith optional query to test: only players whose game names
|
||||
* start with this
|
||||
* @return list of player names that are "visible" to the player
|
||||
*/
|
||||
public static List<String> getVisiblePlayerDisplayNames(CommandSender sender, String startingWith) {
|
||||
Player player = sender instanceof Player ? (Player) sender : null;
|
||||
final String startsWith = startingWith == null || startingWith.isEmpty() ? null : startingWith.replaceAll("[^a-zA-Z]", "").toLowerCase();
|
||||
return Bukkit.getOnlinePlayers().stream()
|
||||
.filter(p -> p != player)
|
||||
.filter(p -> startsWith == null || p.getDisplayName().replaceAll("[^a-zA-Z]", "").toLowerCase().startsWith(startsWith))
|
||||
.filter(p -> player == null || (player.canSee(p) && p.getMetadata("vanished").isEmpty()))
|
||||
.map(Player::getDisplayName)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all of the players that this player can "see"
|
||||
*
|
||||
* @param sender user to check against, or null for all players
|
||||
* @param startingWith optional query to test: only players whose game names
|
||||
* start with this
|
||||
* @return list of players that are "visible" to the player
|
||||
*/
|
||||
public static List<Player> getVisiblePlayers(CommandSender sender, String startingWith) {
|
||||
Player player = sender instanceof Player ? (Player) sender : null;
|
||||
final String startsWith = startingWith == null || startingWith.isEmpty() ? null : startingWith.toLowerCase();
|
||||
return Bukkit.getOnlinePlayers().stream()
|
||||
.filter(p -> p != player)
|
||||
.filter(p -> startsWith == null || p.getName().toLowerCase().startsWith(startsWith))
|
||||
.filter(p -> player == null || (player.canSee(p) && p.getMetadata("vanished").isEmpty()))
|
||||
.map(p -> (Player) p)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all online player names that start with a string.
|
||||
*
|
||||
* @param us Ourselves / who is requesting the list. Will not return this player.
|
||||
* @param startsWith All names returned must start with this input string
|
||||
* @return List of matching player IGN
|
||||
*/
|
||||
public static List<String> getAllPlayers(CommandSender us, String startsWith) {
|
||||
final String arg = startsWith.toLowerCase();
|
||||
return Bukkit.getOnlinePlayers().stream()
|
||||
.filter(p -> us != p && p.getName().startsWith(arg))
|
||||
.map(Player::getName)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all online player names that start with a string.
|
||||
*
|
||||
* @param us Ourselves / who is requesting the list. Will not return this player.
|
||||
* @param startsWith All names returned must start with this input string
|
||||
* @return List of matching player display names
|
||||
*/
|
||||
public static List<String> getAllPlayersDisplay(CommandSender us, String startsWith) {
|
||||
final String arg = startsWith.replaceAll("[^a-zA-Z]", "").toLowerCase();
|
||||
return Bukkit.getOnlinePlayers().stream()
|
||||
.filter(p -> us != p && p.getDisplayName().replaceAll("[^a-zA-Z]", "").startsWith(arg))
|
||||
.map(Player::getDisplayName)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for and grab the closest match for a provided player name. <br />
|
||||
* Also checks player display names if there is not an exact match.
|
||||
*
|
||||
* @param player player to search for
|
||||
* @return Player that closest matches the input name, or null if none found
|
||||
*/
|
||||
public static Player findPlayer(String player) {
|
||||
Player found = Bukkit.getServer().getPlayer(player);
|
||||
if (found == null) {
|
||||
final String searchName = player.toLowerCase();
|
||||
final String searchDisplayName = player.replaceAll("[^a-zA-Z]", "").toLowerCase();
|
||||
int d = 999;
|
||||
for (Player p2 : Bukkit.getOnlinePlayers()) {
|
||||
final String test;
|
||||
if (p2.getName().toLowerCase().startsWith(searchName)) {
|
||||
int d2 = p2.getName().length() - searchName.length();
|
||||
if (d2 < d) {
|
||||
found = p2;
|
||||
d = d2;
|
||||
} else if (d2 == d) {
|
||||
found = null;
|
||||
}
|
||||
} else if ((test = p2.getDisplayName().replaceAll("[^a-zA-Z]", "")).toLowerCase().startsWith(searchDisplayName)) {
|
||||
int d2 = test.length() - searchDisplayName.length();
|
||||
if (d2 < d) {
|
||||
found = p2;
|
||||
d = d2;
|
||||
} else if (d2 == d) {
|
||||
found = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
public static Player getRandomPlayer() {
|
||||
final Collection<? extends Player> all = Bukkit.getOnlinePlayers();
|
||||
final Iterator<? extends Player> alli = all.iterator();
|
||||
int pick = random.nextInt(all.size());
|
||||
for (; pick > 0; --pick) {
|
||||
alli.next();
|
||||
}
|
||||
return alli.hasNext() ? alli.next() : null;
|
||||
}
|
||||
}
|
327
src/main/java/com/songoda/core/utils/ReflectionUtils.java
Normal file
327
src/main/java/com/songoda/core/utils/ReflectionUtils.java
Normal file
@ -0,0 +1,327 @@
|
||||
package com.songoda.core.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.security.CodeSource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
public class ReflectionUtils {
|
||||
|
||||
public final static double JAVA_VERSION = getVersion();
|
||||
private static String system_os = System.getProperty("os.name").toLowerCase();
|
||||
|
||||
private static double getVersion() {
|
||||
String version = System.getProperty("java.version");
|
||||
int i = version.indexOf('.');
|
||||
if (i != -1 && (i = version.indexOf('.', i + 1)) != -1) {
|
||||
return Double.parseDouble(version.substring(0, i));
|
||||
}
|
||||
return Double.NaN;
|
||||
}
|
||||
|
||||
public static File getJarFile(Class jarClass) {
|
||||
return new File(jarClass.getProtectionDomain().getCodeSource().getLocation().getPath().
|
||||
replace("%20", " ").replace("%25", "%"));
|
||||
}
|
||||
|
||||
public static void setPrivateField(Class<?> c, Object handle, String fieldName, Object value) throws Exception {
|
||||
Field f = c.getDeclaredField(fieldName);
|
||||
f.setAccessible(true);
|
||||
f.set(handle, value);
|
||||
}
|
||||
|
||||
public static Object getPrivateField(Class<?> c, Object handle, String fieldName) throws Exception {
|
||||
Field field = c.getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
return field.get(handle);
|
||||
}
|
||||
|
||||
public static Object invokePrivateMethod(Class<?> c, String methodName, Object handle, Class[] types, Object[] parameters) throws Exception {
|
||||
Method m = c.getDeclaredMethod(methodName, types);
|
||||
m.setAccessible(true);
|
||||
return m.invoke(handle, parameters);
|
||||
}
|
||||
|
||||
// does not work in JRE 8+
|
||||
private static Method getStackTraceElementMethod;
|
||||
private static Method getStackTraceDepthMethod;
|
||||
|
||||
static {
|
||||
try {
|
||||
getStackTraceElementMethod = Throwable.class.getDeclaredMethod("getStackTraceElement", int.class);
|
||||
getStackTraceElementMethod.setAccessible(true);
|
||||
getStackTraceDepthMethod = Throwable.class.getDeclaredMethod("getStackTraceDepth");
|
||||
getStackTraceDepthMethod.setAccessible(true);
|
||||
} catch (Exception ex) {
|
||||
getStackTraceElementMethod = getStackTraceDepthMethod = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If you only need one stack trace element this is faster than
|
||||
* Throwable.getStackTrace()[element], it doesn't generate the full stack
|
||||
* trace.
|
||||
*/
|
||||
public static StackTraceElement getStackTraceElement(int index) {
|
||||
try {
|
||||
Throwable dummy = new Throwable();
|
||||
|
||||
if (JAVA_VERSION >= 8 && JAVA_VERSION < 9) {
|
||||
return sun.misc.SharedSecrets.getJavaLangAccess().getStackTraceElement(dummy, index);
|
||||
// } else if (JAVA_VERSION >= 9) {
|
||||
// return StackWalker.getInstance(Collections.emptySet(), index + 1)
|
||||
// .walk(s -> s.skip(index).findFirst())
|
||||
// .orElse(null);
|
||||
} else if (getStackTraceElementMethod == null) {
|
||||
// better than nothing, right? :/
|
||||
return (new Throwable()).getStackTrace()[index];
|
||||
} else {
|
||||
if (index < (Integer) getStackTraceDepthMethod.invoke(dummy)) {
|
||||
return (StackTraceElement) getStackTraceElementMethod.invoke(new Throwable(), index);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T extends Annotation> Map<Class<?>, T> getClassesInClassPackageByAnnotation(Class<?> clazz, Class<T> annotation) throws IOException {
|
||||
final Map<Class<?>, T> foundClasses = new HashMap<>();
|
||||
for (Class<?> c : getAllClassesInClassPackage(clazz, false)) {
|
||||
T t = c.getAnnotation(annotation);
|
||||
if (t != null) {
|
||||
foundClasses.put(c, t);
|
||||
}
|
||||
}
|
||||
return foundClasses;
|
||||
}
|
||||
|
||||
public static List<Class<?>> getAllClassesInClassPackage(Class<?> clazz, boolean recursive) throws IOException {
|
||||
final List<Class<?>> packageClasses = new ArrayList<>();
|
||||
|
||||
final String clazzPackageName = clazz.getPackage().getName();
|
||||
URL dot = clazz.getResource(".");
|
||||
if (dot == null) {
|
||||
// jar file
|
||||
String packagePath = clazzPackageName.replace('.', '/');
|
||||
CodeSource src = clazz.getProtectionDomain().getCodeSource();
|
||||
if (src != null) {
|
||||
URL jar = src.getLocation();
|
||||
ZipInputStream zip = new ZipInputStream(jar.openStream());
|
||||
ZipEntry e;
|
||||
while ((e = zip.getNextEntry()) != null) {
|
||||
String name = e.getName();
|
||||
if (!name.endsWith("/") && name.startsWith(packagePath + "/")) {
|
||||
if (recursive || name.indexOf('/', packagePath.length() + 1) == -1) {
|
||||
try {
|
||||
Class<?> loadedClazz = Class.forName(name.substring(0, name.lastIndexOf('.')).replace('/', '.'));
|
||||
packageClasses.add(loadedClazz);
|
||||
} catch (ClassNotFoundException e1) {
|
||||
System.err.println("class not found: " + e1.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
String clazzPath = clazz.getResource(".").getPath();
|
||||
if (clazzPath.startsWith("/") && system_os.contains("win")) {
|
||||
clazzPath = clazzPath.substring(1);
|
||||
}
|
||||
Path packagePath = Paths.get(clazzPath);
|
||||
|
||||
Files.walkFileTree(packagePath, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
String filename = file.getName(file.getNameCount() - 1).toString();
|
||||
|
||||
if (filename.endsWith(".class")) {
|
||||
String className = filename.replace(".class", "");
|
||||
|
||||
try {
|
||||
Class<?> loadedClazz = Class.forName(
|
||||
clazzPackageName + "." + className);
|
||||
|
||||
packageClasses.add(loadedClazz);
|
||||
} catch (ClassNotFoundException e) {
|
||||
System.err.println("class not found: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
return super.visitFile(file, attrs);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return packageClasses;
|
||||
}
|
||||
|
||||
public static enum ITERATION {
|
||||
|
||||
NONE, CLASS, PACKAGE, FULL
|
||||
}
|
||||
|
||||
public static List<String> getClassNamesFromPackage(Class classInPackage) throws IOException, URISyntaxException, ClassNotFoundException {
|
||||
String classPath = classInPackage.getName();
|
||||
int packageDelim = classPath.lastIndexOf('.');
|
||||
return getClassNamesFromPackage(getJarFile(classInPackage), classPath.substring(0, packageDelim), ITERATION.NONE);
|
||||
}
|
||||
|
||||
public static List<String> getClassNamesFromPackage(String packageName) throws IOException, URISyntaxException, ClassNotFoundException {
|
||||
return getClassNamesFromPackage(packageName, ITERATION.NONE);
|
||||
}
|
||||
|
||||
public static List<String> getClassNamesFromPackage(String packageName, ITERATION iterate) throws IOException, URISyntaxException, ClassNotFoundException {
|
||||
return getClassNamesFromPackage(null, packageName, iterate);
|
||||
}
|
||||
|
||||
public static List<String> getClassNamesFromPackage(File sourceJar, String packageName, ITERATION iterate) throws IOException, URISyntaxException, ClassNotFoundException {
|
||||
// http://stackoverflow.com/questions/1456930/how-do-i-read-all-classes-from-a-java-package-in-the-classpath
|
||||
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||
URL packageURL;
|
||||
ArrayList<String> names = new ArrayList<String>();
|
||||
|
||||
if (packageName.contains("/")) {
|
||||
// load as a file
|
||||
packageURL = classLoader.getResource(packageName);
|
||||
|
||||
// todo - if there is an error, step backwards to find the first avaliable package
|
||||
if (packageURL == null && packageName.contains("/")) {
|
||||
// added - check to see if maybe trying to load a file?
|
||||
final int i = packageName.lastIndexOf('/');
|
||||
packageName = packageName.substring(0, i) + "." + packageName.substring(i + 1);
|
||||
packageURL = classLoader.getResource(packageName);
|
||||
}
|
||||
} else {
|
||||
packageName = packageName.replace(".", "/");
|
||||
packageURL = classLoader.getResource(packageName);
|
||||
|
||||
if (sourceJar == null && packageURL == null) {
|
||||
throw new IOException("Cannot open resource '" + packageName + "'");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (sourceJar == null && packageURL == null) {
|
||||
throw new IOException("Cannot open resource '" + packageName + "'");
|
||||
//} else if (packageURL.getProtocol().equals("file") || ) {
|
||||
// cannot do this..
|
||||
} else if (sourceJar != null || packageURL.getProtocol().equals("jar")) {
|
||||
// this can also be used to load jar from resources
|
||||
String jarFileName;
|
||||
JarFile jf;
|
||||
Enumeration<JarEntry> jarEntries;
|
||||
String entryName;
|
||||
|
||||
// build jar file name, then loop through zipped entries
|
||||
jarFileName = sourceJar != null ? sourceJar.getAbsolutePath() : URLDecoder.decode(packageURL.getFile(), "UTF-8");
|
||||
// changed - support for resource jar files, too
|
||||
if (jarFileName.startsWith("file:/")) {
|
||||
jarFileName = jarFileName.substring(system_os.contains("win") ? 5 : 4);
|
||||
}
|
||||
if (jarFileName.startsWith("/") && system_os.contains("win")) {
|
||||
jarFileName = jarFileName.substring(1);
|
||||
}
|
||||
if (jarFileName.contains("!")) {
|
||||
jarFileName = jarFileName.substring(0, jarFileName.indexOf("!"));
|
||||
}
|
||||
|
||||
jf = new JarFile(jarFileName);
|
||||
jarEntries = jf.entries();
|
||||
// in case of multiple sub-classes, keep track of what classes have been searched
|
||||
ArrayList<String> loaded = new ArrayList<String>();
|
||||
while (jarEntries.hasMoreElements()) {
|
||||
entryName = jarEntries.nextElement().getName();
|
||||
if (entryName.startsWith(packageName) && entryName.length() > packageName.length() && entryName.toLowerCase().endsWith(".class")) {
|
||||
if (entryName.contains(".")) {
|
||||
entryName = entryName.substring(packageName.length() + 1, entryName.lastIndexOf('.'));
|
||||
}
|
||||
// iteration test
|
||||
if (!entryName.contains("/") || (iterate == ITERATION.PACKAGE || iterate == ITERATION.FULL)) {
|
||||
|
||||
if (entryName.contains("$")) { // added - sub-package test
|
||||
// added - iteration
|
||||
if (iterate == ITERATION.CLASS || iterate == ITERATION.FULL) {
|
||||
entryName = entryName.substring(0, entryName.indexOf('$')).replace('/', '.');
|
||||
if (!loaded.contains(entryName)) {
|
||||
loaded.add(entryName);
|
||||
try {
|
||||
Class c = Class.forName(packageName.replace('/', '.') + "." + entryName);
|
||||
for (Class c2 : c.getDeclaredClasses()) {
|
||||
names.add(entryName + "." + c2.getSimpleName());
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
names.add(entryName.replace('/', '.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// hits here if running in IDE
|
||||
|
||||
// loop through files in classpath
|
||||
URI uri = new URI(packageURL.toString());
|
||||
File folder = new File(uri.getPath());
|
||||
// won't work with path which contains blank (%20)
|
||||
// File folder = new File(packageURL.getFile());
|
||||
File[] contenuti = folder.listFiles();
|
||||
// in case of multiple sub-classes, keep track of what classes have been searched
|
||||
ArrayList<String> loaded = new ArrayList<String>();
|
||||
String entryName;
|
||||
for (File actual : contenuti) {
|
||||
entryName = actual.getName();
|
||||
if (entryName.contains(".")) { // added - folder check
|
||||
entryName = entryName.substring(0, entryName.lastIndexOf('.'));
|
||||
if (entryName.contains("$")) { // added - sub-package test
|
||||
// added - iteration
|
||||
if (iterate == ITERATION.CLASS || iterate == ITERATION.FULL) {
|
||||
entryName = entryName.substring(0, entryName.indexOf('$'));
|
||||
if (!loaded.contains(entryName)) {
|
||||
loaded.add(entryName);
|
||||
Class c = Class.forName(packageName.replace('/', '.') + "." + entryName);
|
||||
for (Class c2 : c.getDeclaredClasses()) {
|
||||
names.add(entryName + "." + c2.getSimpleName());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
names.add(entryName);
|
||||
}
|
||||
} else if (iterate == ITERATION.PACKAGE || iterate == ITERATION.FULL) {
|
||||
// added - iteration
|
||||
for (String sub : getClassNamesFromPackage(packageName + "/" + entryName, iterate)) {
|
||||
names.add(entryName + "." + sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
}
|
40
src/main/java/com/songoda/core/utils/RotationUtils.java
Normal file
40
src/main/java/com/songoda/core/utils/RotationUtils.java
Normal file
@ -0,0 +1,40 @@
|
||||
package com.songoda.core.utils;
|
||||
|
||||
import org.bukkit.block.BlockFace;
|
||||
|
||||
public class RotationUtils {
|
||||
|
||||
public static float faceToYaw(BlockFace face) {
|
||||
switch (face) {
|
||||
case NORTH:
|
||||
return 180F;
|
||||
case SOUTH:
|
||||
return 0F;
|
||||
case EAST:
|
||||
return -90F;
|
||||
case WEST:
|
||||
return 90F;
|
||||
}
|
||||
return 0F;
|
||||
}
|
||||
|
||||
public static BlockFace yawToFace(float face) {
|
||||
switch ((int) Math.round((face + 360) / 90) * 90) {
|
||||
case 0:
|
||||
case 360:
|
||||
return BlockFace.SOUTH;
|
||||
case 180:
|
||||
case 540:
|
||||
return BlockFace.NORTH;
|
||||
case 270:
|
||||
case 630:
|
||||
return BlockFace.EAST;
|
||||
case 90:
|
||||
case 450:
|
||||
return BlockFace.WEST;
|
||||
}
|
||||
// idk
|
||||
return BlockFace.SOUTH;
|
||||
}
|
||||
|
||||
}
|
111
src/main/java/com/songoda/core/utils/TextUtils.java
Normal file
111
src/main/java/com/songoda/core/utils/TextUtils.java
Normal file
@ -0,0 +1,111 @@
|
||||
package com.songoda.core.utils;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.bukkit.ChatColor;
|
||||
|
||||
public class TextUtils {
|
||||
|
||||
public static String formatText(String text) {
|
||||
return formatText(text, false);
|
||||
}
|
||||
|
||||
public static String formatText(String text, boolean capitalize) {
|
||||
if (text == null || text.equals(""))
|
||||
return "";
|
||||
if (capitalize)
|
||||
text = text.substring(0, 1).toUpperCase() + text.substring(1);
|
||||
return ChatColor.translateAlternateColorCodes('&', text);
|
||||
}
|
||||
|
||||
public static String convertToInvisibleString(String s) {
|
||||
if (s == null || s.equals(""))
|
||||
return "";
|
||||
StringBuilder hidden = new StringBuilder();
|
||||
for (char c : s.toCharArray()) hidden.append(ChatColor.COLOR_CHAR + "").append(c);
|
||||
return hidden.toString();
|
||||
}
|
||||
|
||||
protected static final List<Charset> supportedCharsets = new ArrayList();
|
||||
|
||||
static {
|
||||
supportedCharsets.add(StandardCharsets.UTF_8); // UTF-8 BOM: EF BB BF
|
||||
supportedCharsets.add(StandardCharsets.ISO_8859_1); // also starts with EF BB BF
|
||||
//supportedCharsets.add(StandardCharsets.UTF_16LE); // FF FE
|
||||
//supportedCharsets.add(StandardCharsets.UTF_16BE); // FE FF
|
||||
//supportedCharsets.add(StandardCharsets.UTF_16);
|
||||
try {
|
||||
supportedCharsets.add(Charset.forName("windows-1253"));
|
||||
supportedCharsets.add(Charset.forName("ISO-8859-7"));
|
||||
} catch (Exception e) {
|
||||
} // UnsupportedCharsetException technically can be thrown, but can also be ignored
|
||||
supportedCharsets.add(StandardCharsets.US_ASCII);
|
||||
}
|
||||
|
||||
public static Charset detectCharset(File f, Charset def) {
|
||||
byte[] buffer = new byte[2048];
|
||||
int read = -1;
|
||||
// read the first 2kb of the file and test the file's encoding
|
||||
try (FileInputStream input = new FileInputStream(f)) {
|
||||
read = input.read(buffer);
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
return read != -1 ? detectCharset(buffer, read, def) : def;
|
||||
}
|
||||
|
||||
public static Charset detectCharset(BufferedInputStream reader, Charset def) {
|
||||
byte[] buffer = new byte[2048];
|
||||
int read;
|
||||
try {
|
||||
reader.mark(2048);
|
||||
read = reader.read(buffer);
|
||||
reader.reset();
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
return read != -1 ? detectCharset(buffer, read, def) : def;
|
||||
}
|
||||
|
||||
public static Charset detectCharset(byte[] data, int len, Charset def) {
|
||||
// check the file header
|
||||
if (len > 4) {
|
||||
if (data[0] == (byte) 0xFF && data[1] == (byte) 0xFE) {
|
||||
return StandardCharsets.UTF_16LE;
|
||||
// FF FE 00 00 is UTF-32LE
|
||||
} else if (data[0] == (byte) 0xFE && data[1] == (byte) 0xFF) {
|
||||
return StandardCharsets.UTF_16BE;
|
||||
// 00 00 FE FF is UTF-32BE
|
||||
} else if (data[0] == (byte) 0xEF && data[1] == (byte) 0xBB && data[2] == (byte) 0xBF) { // UTF-8 with BOM, same sig as ISO-8859-1
|
||||
return StandardCharsets.UTF_8;
|
||||
}
|
||||
}
|
||||
|
||||
// iterate through sets to test, and return the first that is ok
|
||||
for (Charset charset : supportedCharsets) {
|
||||
if (charset != null && isCharset(data, len, charset)) {
|
||||
return charset;
|
||||
}
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
public static boolean isCharset(byte[] data, int len, Charset charset) {
|
||||
try {
|
||||
CharsetDecoder decoder = charset.newDecoder();
|
||||
decoder.reset();
|
||||
decoder.decode(ByteBuffer.wrap(data));
|
||||
return true;
|
||||
} catch (CharacterCodingException e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package com.songoda.update;
|
||||
|
||||
public interface Module {
|
||||
|
||||
void run(Plugin plugin);
|
||||
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
package com.songoda.update;
|
||||
|
||||
import com.songoda.update.command.CommandManager;
|
||||
import com.songoda.update.listeners.LoginListener;
|
||||
import com.songoda.update.utils.ServerVersion;
|
||||
import org.apache.commons.lang.ArrayUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SongodaUpdate {
|
||||
|
||||
private static String prefix = "[SongodaUpdate] ";
|
||||
|
||||
private ServerVersion serverVersion = ServerVersion.fromPackageName(Bukkit.getServer().getClass().getPackage().getName());
|
||||
|
||||
|
||||
private static int version = 1;
|
||||
|
||||
private static List<Plugin> registeredPlugins = new ArrayList<>();
|
||||
|
||||
private static SongodaUpdate INSTANCE;
|
||||
|
||||
private static JavaPlugin hijackedPlugin;
|
||||
|
||||
public SongodaUpdate() {
|
||||
hijackedPlugin = registeredPlugins.get(0).getJavaPlugin();
|
||||
Bukkit.getPluginManager().registerEvents(new LoginListener(this), hijackedPlugin);
|
||||
|
||||
new CommandManager(this);
|
||||
}
|
||||
|
||||
private void update(Plugin plugin) {
|
||||
try {
|
||||
URL url = new URL("http://update.songoda.com/index.php?plugin=" + plugin.getSongodaId()
|
||||
+ "&version=" + plugin.getJavaPlugin().getDescription().getVersion()
|
||||
+ "&updaterVersion=" + version);
|
||||
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
|
||||
urlConnection.setConnectTimeout(5000);
|
||||
InputStream is = urlConnection.getInputStream();
|
||||
InputStreamReader isr = new InputStreamReader(is);
|
||||
|
||||
int numCharsRead;
|
||||
char[] charArray = new char[1024];
|
||||
StringBuffer sb = new StringBuffer();
|
||||
while ((numCharsRead = isr.read(charArray)) > 0) {
|
||||
sb.append(charArray, 0, numCharsRead);
|
||||
}
|
||||
String jsonString = sb.toString();
|
||||
JSONObject json = (JSONObject) new JSONParser().parse(jsonString);
|
||||
|
||||
plugin.setLatestVersion((String) json.get("latestVersion"));
|
||||
plugin.setMarketplaceLink((String) json.get("link"));
|
||||
plugin.setNotification((String) json.get("notification"));
|
||||
plugin.setChangeLog((String) json.get("changeLog"));
|
||||
|
||||
plugin.setJson(json);
|
||||
|
||||
for (Module module : plugin.getModules()) {
|
||||
module.run(plugin);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.out.println("Connection with Songoda servers failed...");
|
||||
} catch (ParseException e) {
|
||||
System.out.println("Failed to parse json.");
|
||||
}
|
||||
}
|
||||
|
||||
public static Plugin load(Plugin plugin) {
|
||||
registeredPlugins.add(plugin);
|
||||
System.out.println(prefix + "Hooked " + plugin.getJavaPlugin().getName() + ".");
|
||||
if (INSTANCE == null) INSTANCE = new SongodaUpdate();
|
||||
getInstance().update(plugin);
|
||||
return plugin;
|
||||
}
|
||||
|
||||
public ServerVersion getServerVersion() {
|
||||
return serverVersion;
|
||||
}
|
||||
|
||||
public boolean isServerVersion(ServerVersion version) {
|
||||
return serverVersion == version;
|
||||
}
|
||||
public boolean isServerVersion(ServerVersion... versions) {
|
||||
return ArrayUtils.contains(versions, serverVersion);
|
||||
}
|
||||
|
||||
public boolean isServerVersionAtLeast(ServerVersion version) {
|
||||
return serverVersion.ordinal() >= version.ordinal();
|
||||
}
|
||||
|
||||
public List<Plugin> getPlugins() {
|
||||
return new ArrayList<>(registeredPlugins);
|
||||
}
|
||||
|
||||
public static int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public static JavaPlugin getHijackedPlugin() {
|
||||
return hijackedPlugin;
|
||||
}
|
||||
|
||||
public static SongodaUpdate getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
package com.songoda.update.command;
|
||||
|
||||
import com.songoda.update.SongodaUpdate;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractCommand {
|
||||
|
||||
private final boolean noConsole;
|
||||
private AbstractCommand parent = null;
|
||||
private boolean hasArgs = false;
|
||||
private String command;
|
||||
|
||||
private List<String> subCommand = new ArrayList<>();
|
||||
|
||||
protected AbstractCommand(AbstractCommand parent, boolean noConsole, String... command) {
|
||||
if (parent != null) {
|
||||
this.subCommand = Arrays.asList(command);
|
||||
} else {
|
||||
this.command = Arrays.asList(command).get(0);
|
||||
}
|
||||
this.parent = parent;
|
||||
this.noConsole = noConsole;
|
||||
}
|
||||
|
||||
protected AbstractCommand(boolean noConsole, boolean hasArgs, String... command) {
|
||||
this.command = Arrays.asList(command).get(0);
|
||||
|
||||
this.hasArgs = hasArgs;
|
||||
this.noConsole = noConsole;
|
||||
}
|
||||
|
||||
public AbstractCommand getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public String getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
public List<String> getSubCommand() {
|
||||
return subCommand;
|
||||
}
|
||||
|
||||
public void addSubCommand(String command) {
|
||||
subCommand.add(command);
|
||||
}
|
||||
|
||||
protected abstract ReturnType runCommand(SongodaUpdate instance, CommandSender sender, String... args);
|
||||
|
||||
protected abstract List<String> onTab(SongodaUpdate instance, CommandSender sender, String... args);
|
||||
|
||||
public abstract String getPermissionNode();
|
||||
|
||||
public abstract String getSyntax();
|
||||
|
||||
public abstract String getDescription();
|
||||
|
||||
public boolean hasArgs() {
|
||||
return hasArgs;
|
||||
}
|
||||
|
||||
public boolean isNoConsole() {
|
||||
return noConsole;
|
||||
}
|
||||
|
||||
public enum ReturnType {SUCCESS, FAILURE, SYNTAX_ERROR}
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
package com.songoda.update.command;
|
||||
|
||||
import com.songoda.update.SongodaUpdate;
|
||||
import com.songoda.update.command.commands.CommandDiag;
|
||||
import com.songoda.update.command.commands.CommandSongoda;
|
||||
import com.songoda.update.utils.Methods;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.*;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class CommandManager implements CommandExecutor {
|
||||
|
||||
private SongodaUpdate instance;
|
||||
private TabManager tabManager;
|
||||
|
||||
private List<AbstractCommand> commands = new ArrayList<>();
|
||||
|
||||
public CommandManager(SongodaUpdate instance) {
|
||||
this.instance = instance;
|
||||
this.tabManager = new TabManager(this);
|
||||
|
||||
registerCommandDynamically("songoda", this);
|
||||
|
||||
AbstractCommand commandSongoda = addCommand(new CommandSongoda());
|
||||
addCommand(new CommandDiag(commandSongoda));
|
||||
|
||||
for (AbstractCommand abstractCommand : commands) {
|
||||
if (abstractCommand.getParent() != null) continue;
|
||||
//instance.getCommand(abstractCommand.getCommand()).setTabCompleter(tabManager);
|
||||
}
|
||||
}
|
||||
|
||||
private AbstractCommand addCommand(AbstractCommand abstractCommand) {
|
||||
commands.add(abstractCommand);
|
||||
return abstractCommand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender commandSender, Command command, String s, String[] strings) {
|
||||
for (AbstractCommand abstractCommand : commands) {
|
||||
if (abstractCommand.getCommand() != null && abstractCommand.getCommand().equalsIgnoreCase(command.getName().toLowerCase())) {
|
||||
if (strings.length == 0 || abstractCommand.hasArgs()) {
|
||||
processRequirements(abstractCommand, commandSender, strings);
|
||||
return true;
|
||||
}
|
||||
} else if (strings.length != 0 && abstractCommand.getParent() != null && abstractCommand.getParent().getCommand().equalsIgnoreCase(command.getName())) {
|
||||
String cmd = strings[0];
|
||||
String cmd2 = strings.length >= 2 ? String.join(" ", strings[0], strings[1]) : null;
|
||||
for (String cmds : abstractCommand.getSubCommand()) {
|
||||
if (cmd.equalsIgnoreCase(cmds) || (cmd2 != null && cmd2.equalsIgnoreCase(cmds))) {
|
||||
processRequirements(abstractCommand, commandSender, strings);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
commandSender.sendMessage(instance.getPrefix() + Methods.formatText("&7The command you entered does not exist or is spelt incorrectly."));
|
||||
return true;
|
||||
}
|
||||
|
||||
private void processRequirements(AbstractCommand command, CommandSender sender, String[] strings) {
|
||||
if (!(sender instanceof Player) && command.isNoConsole()) {
|
||||
sender.sendMessage("You must be a player to use this command.");
|
||||
return;
|
||||
}
|
||||
if (command.getPermissionNode() == null || sender.hasPermission(command.getPermissionNode())) {
|
||||
AbstractCommand.ReturnType returnType = command.runCommand(instance, sender, strings);
|
||||
if (returnType == AbstractCommand.ReturnType.SYNTAX_ERROR) {
|
||||
sender.sendMessage(instance.getPrefix() + Methods.formatText("&cInvalid Syntax!"));
|
||||
sender.sendMessage(instance.getPrefix() + Methods.formatText("&7The valid syntax is: &6" + command.getSyntax() + "&7."));
|
||||
}
|
||||
return;
|
||||
}
|
||||
sender.sendMessage(instance.getPrefix() + "You do not have permission to run this command.");
|
||||
}
|
||||
|
||||
public List<AbstractCommand> getCommands() {
|
||||
return Collections.unmodifiableList(commands);
|
||||
}
|
||||
|
||||
private void registerCommandDynamically(String command, CommandExecutor executor) {
|
||||
try {
|
||||
// Retrieve the SimpleCommandMap from the server
|
||||
Class<?> classCraftServer = Bukkit.getServer().getClass();
|
||||
Field fieldCommandMap = classCraftServer.getDeclaredField("commandMap");
|
||||
fieldCommandMap.setAccessible(true);
|
||||
SimpleCommandMap commandMap = (SimpleCommandMap) fieldCommandMap.get(Bukkit.getServer());
|
||||
|
||||
// Construct a new Command object
|
||||
Constructor<PluginCommand> constructorPluginCommand = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class);
|
||||
constructorPluginCommand.setAccessible(true);
|
||||
PluginCommand commandObject = constructorPluginCommand.newInstance(command, SongodaUpdate.getHijackedPlugin());
|
||||
commandObject.setExecutor(executor);
|
||||
|
||||
// Set tab complete
|
||||
commandObject.setTabCompleter(tabManager);
|
||||
|
||||
// Register the command
|
||||
Field fieldKnownCommands = SimpleCommandMap.class.getDeclaredField("knownCommands");
|
||||
fieldKnownCommands.setAccessible(true);
|
||||
Map<String, Command> knownCommands = (Map<String, Command>) fieldKnownCommands.get(commandMap);
|
||||
knownCommands.put(command, commandObject);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
package com.songoda.update.command;
|
||||
|
||||
import com.songoda.update.SongodaUpdate;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TabManager implements TabCompleter {
|
||||
|
||||
private final CommandManager commandManager;
|
||||
|
||||
TabManager(CommandManager commandManager) {
|
||||
this.commandManager = commandManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] strings) {
|
||||
for (AbstractCommand abstractCommand : commandManager.getCommands()) {
|
||||
if (abstractCommand.getCommand() != null && abstractCommand.getCommand().equalsIgnoreCase(command.getName()) && !abstractCommand.hasArgs()) {
|
||||
if (strings.length == 1) {
|
||||
List<String> subs = new ArrayList<>();
|
||||
for (AbstractCommand ac : commandManager.getCommands()) {
|
||||
if (ac.getSubCommand() == null) continue;
|
||||
subs.addAll(ac.getSubCommand());
|
||||
}
|
||||
subs.removeIf(s -> !s.toLowerCase().startsWith(strings[0].toLowerCase()));
|
||||
return subs;
|
||||
}
|
||||
} else if (strings.length != 0
|
||||
&& abstractCommand.getCommand() != null
|
||||
&& abstractCommand.getCommand().equalsIgnoreCase(command.getName().toLowerCase())) {
|
||||
String cmd = strings[0];
|
||||
String cmd2 = strings.length >= 2 ? String.join(" ", strings[0], strings[1]) : null;
|
||||
if (abstractCommand.hasArgs()) {
|
||||
return onCommand(abstractCommand, strings, sender);
|
||||
} else {
|
||||
for (String cmds : abstractCommand.getSubCommand()) {
|
||||
if (cmd.equalsIgnoreCase(cmds) || (cmd2 != null && cmd2.equalsIgnoreCase(cmds))) {
|
||||
return onCommand(abstractCommand, strings, sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private List<String> onCommand(AbstractCommand abstractCommand, String[] strings, CommandSender sender) {
|
||||
List<String> list = abstractCommand.onTab(SongodaUpdate.getInstance(), sender, strings);
|
||||
String str = strings[strings.length - 1];
|
||||
if (list != null && str != null && str.length() >= 1) {
|
||||
try {
|
||||
list.removeIf(s -> !s.toLowerCase().startsWith(str.toLowerCase()));
|
||||
} catch (UnsupportedOperationException ignored) {
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package com.songoda.update.command.commands;
|
||||
|
||||
import com.songoda.update.SongodaUpdate;
|
||||
import com.songoda.update.command.AbstractCommand;
|
||||
import com.songoda.update.gui.GUIOverview;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CommandSongoda extends AbstractCommand {
|
||||
|
||||
public CommandSongoda() {
|
||||
super(true, false, "songoda");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReturnType runCommand(SongodaUpdate instance, CommandSender sender, String... args) {
|
||||
new GUIOverview(instance, (Player) sender);
|
||||
return ReturnType.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> onTab(SongodaUpdate instance, CommandSender sender, String... args) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPermissionNode() {
|
||||
return "songoda.admin";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSyntax() {
|
||||
return "/songoda";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Displays this interface.";
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
package com.songoda.update.gui;
|
||||
|
||||
import com.songoda.update.Plugin;
|
||||
import com.songoda.update.SongodaUpdate;
|
||||
import com.songoda.update.utils.gui.AbstractGUI;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GUIOverview extends AbstractGUI {
|
||||
|
||||
private final SongodaUpdate update;
|
||||
|
||||
public GUIOverview(SongodaUpdate update, Player player) {
|
||||
super(player);
|
||||
this.update = update;
|
||||
|
||||
init("Songoda Update", 36);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void constructGUI() {
|
||||
List<Plugin> plugins = update.getPlugins();
|
||||
for (int i = 0; i < plugins.size(); i++) {
|
||||
Plugin plugin = plugins.get(i);
|
||||
|
||||
createButton(i + 9, Material.STONE, "&6" + plugin.getJavaPlugin().getName(),
|
||||
"&7Latest Version: " + plugin.getLatestVersion(),
|
||||
"&7Installed Version: " + plugin.getJavaPlugin().getDescription().getVersion(),
|
||||
"",
|
||||
"Change log:",
|
||||
plugin.getChangeLog(),
|
||||
"",
|
||||
"&6Click for the marketplace page link.");
|
||||
|
||||
registerClickable(i + 9, ((player1, inventory1, cursor, slot, type) ->
|
||||
player.sendMessage(plugin.getMarketplaceLink())));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerClickables() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerOnCloses() {
|
||||
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package com.songoda.update.listeners;
|
||||
|
||||
import com.songoda.update.Plugin;
|
||||
import com.songoda.update.SongodaUpdate;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
|
||||
public class LoginListener implements Listener {
|
||||
|
||||
private SongodaUpdate instance;
|
||||
|
||||
public LoginListener(SongodaUpdate instance) {
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onLogin(PlayerLoginEvent event) {
|
||||
if (event.getPlayer().isOp()) {
|
||||
for (Plugin plugin : instance.getPlugins()) {
|
||||
if (plugin.getNotification() != null)
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin.getJavaPlugin(), () ->
|
||||
event.getPlayer().sendMessage("[" + plugin.getJavaPlugin().getName() + "] " + plugin.getNotification()), 10L);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
package com.songoda.update.utils;
|
||||
|
||||
import com.songoda.update.SongodaUpdate;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class AbstractChatConfirm implements Listener {
|
||||
|
||||
private static final List<UUID> registered = new ArrayList<>();
|
||||
|
||||
private final Player player;
|
||||
private final ChatConfirmHandler handler;
|
||||
|
||||
private OnClose onClose = null;
|
||||
private Listener listener;
|
||||
|
||||
public AbstractChatConfirm(Player player, ChatConfirmHandler hander) {
|
||||
this.player = player;
|
||||
this.handler = hander;
|
||||
player.closeInventory();
|
||||
initializeListeners(SongodaUpdate.getHijackedPlugin());
|
||||
registered.add(player.getUniqueId());
|
||||
}
|
||||
|
||||
public static boolean isRegistered(Player player) {
|
||||
return registered.contains(player.getUniqueId());
|
||||
}
|
||||
|
||||
public static boolean unregister(Player player) {
|
||||
return registered.remove(player.getUniqueId());
|
||||
}
|
||||
|
||||
public void initializeListeners(JavaPlugin plugin) {
|
||||
|
||||
this.listener = new Listener() {
|
||||
@EventHandler
|
||||
public void onChat(AsyncPlayerChatEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
if (!AbstractChatConfirm.isRegistered(player)) return;
|
||||
|
||||
AbstractChatConfirm.unregister(player);
|
||||
event.setCancelled(true);
|
||||
|
||||
ChatConfirmEvent chatConfirmEvent = new ChatConfirmEvent(player, event.getMessage());
|
||||
|
||||
handler.onChat(chatConfirmEvent);
|
||||
|
||||
if (onClose != null) {
|
||||
onClose.onClose();
|
||||
}
|
||||
HandlerList.unregisterAll(listener);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Bukkit.getPluginManager().registerEvents(listener, SongodaUpdate.getHijackedPlugin());
|
||||
}
|
||||
|
||||
public void setOnClose(OnClose onClose) {
|
||||
this.onClose = onClose;
|
||||
}
|
||||
|
||||
public interface ChatConfirmHandler {
|
||||
void onChat(ChatConfirmEvent event);
|
||||
}
|
||||
|
||||
public interface OnClose {
|
||||
void onClose();
|
||||
}
|
||||
|
||||
public class ChatConfirmEvent {
|
||||
|
||||
private final Player player;
|
||||
private final String message;
|
||||
|
||||
public ChatConfirmEvent(Player player, String message) {
|
||||
this.player = player;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package com.songoda.update.utils;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
|
||||
public class Methods {
|
||||
|
||||
public static String formatText(String text) {
|
||||
return formatText(text, false);
|
||||
}
|
||||
|
||||
public static String formatText(String text, boolean cap) {
|
||||
if (text == null || text.equals(""))
|
||||
return "";
|
||||
if (cap)
|
||||
text = text.substring(0, 1).toUpperCase() + text.substring(1);
|
||||
return ChatColor.translateAlternateColorCodes('&', text);
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user