Compare commits
42 Commits
Author | SHA1 | Date |
---|---|---|
PretzelJohn | 7e8baf8a6c | |
PretzelJohn | dfb4238c42 | |
PretzelJohn | 3522d5099b | |
HarvelsX | 071129fe16 | |
PretzelJohn | e804329777 | |
HarvelsX | 1fe04c1b5e | |
PretzelJohn | 5f279ea12e | |
mastercake10 | 411a0abc66 | |
PretzelJohn | 542eb20e8a | |
PretzelJohn | 71fc8bd4f2 | |
PretzelJohn | 03f8d1b84c | |
PretzelJohn | b8b14941df | |
PretzelJohn | 75df051d08 | |
PretzelJohn | 58d1b24ff0 | |
PretzelJohn | f13fac70fc | |
PretzelJohn | ccaefbbac7 | |
PretzelJohn | 65a8bdf267 | |
PretzelJohn | 61bcea37b9 | |
PretzelJohn | 8a46c9ab1c | |
Turjoy9 | 18e47efef4 | |
PretzelJohn | ec76d44e0d | |
PretzelJohn | 3513e97253 | |
Turjoy9 | b06f9c9871 | |
Turjoy9 | 4097d9d8f3 | |
Turjoy9 | af2cd73737 | |
PretzelJohn | d328a575aa | |
PretzelJohn | 2d97903db6 | |
PretzelJohn | 030974f065 | |
PretzelJohn | a3995b6adb | |
PretzelJohn | 72e2423741 | |
PretzelJohn | 40c31a13c7 | |
PretzelJohn | ea98a911d3 | |
PretzelJohn | f4a8ecd21c | |
PretzelJohn | dd49756014 | |
PretzelJohn | afacf4adc8 | |
PretzelJohn | 5cc49188a7 | |
PretzelJohn | 8720e840e2 | |
PretzelJohn | 3aba0ff1c7 | |
PretzelJohn | 0e63df608f | |
PretzelJohn | e708187b33 | |
PretzelJohn | 5110befd30 | |
PretzelJohn | 446e677336 |
118
README.md
118
README.md
|
@ -2,51 +2,9 @@
|
|||
<h6>by PretzelJohn</h6>
|
||||
|
||||
<h2>Description:</h2>
|
||||
<p>This Minecraft plugin limits the villager trade deals that players can get when they cure a zombie villager.</p>
|
||||
<br>
|
||||
|
||||
<h2>Commands:</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Command</th>
|
||||
<th>Alias</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/villagertradelimiter</code></td>
|
||||
<td><code>/vtl</code></td>
|
||||
<td>shows a help message</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>/villagertradelimiter reload</code></td>
|
||||
<td><code>/vtl reload</code></td>
|
||||
<td>reloads config.yml</td>
|
||||
</tr>
|
||||
</table><br>
|
||||
|
||||
<h2>Permissions:</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Permission</th>
|
||||
<th>Description</th>
|
||||
<th>Default User(s)</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>villagertradelimiter.*</td>
|
||||
<td>Allows players to use <code>/vtl</code> and <code>/vtl reload</code></td>
|
||||
<td>OP</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>villagertradelimiter.use</td>
|
||||
<td>Allows players to use <code>/vtl</code></td>
|
||||
<td>OP</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>villagertradelimiter.reload</td>
|
||||
<td>Allows players to reload config.yml and messages.yml</td>
|
||||
<td>OP</td>
|
||||
</tr>
|
||||
</table><br>
|
||||
<p>This Minecraft plugin limits the villager trade deals that players can get.<br/>Supports Spigot, Paper, and Purpur servers from 1.14.1 to the current version.<br/>Click <a href="https://www.spigotmc.org/resources/87210/">here</a> to see this plugin on Spigot.</p>
|
||||
<p>Some information has moved to the <a href="https://github.com/PretzelJohn/VillagerTradeLimiter/wiki">Wiki</a>!</p>
|
||||
<br/>
|
||||
|
||||
<h2>Config:</h2>
|
||||
<ul>
|
||||
|
@ -61,6 +19,55 @@
|
|||
<td><code>bStats:</code></td>
|
||||
<td>This helps me keep track of what server versions are being used. Please leave this set to true.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>database.mysql:</code></td>
|
||||
<td>Whether to use MySQL for the database (true) or SQLite (false)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>database.host:</code></td>
|
||||
<td>The IP address or domain name of your MySQL server. If the MySQL database is on the same server as your Minecraft server, leave this as <code>127.0.0.1</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>database.port:</code></td>
|
||||
<td>The port number of your MySQL server. Usually <code>3306</code>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>database.database:</code></td>
|
||||
<td>The name of your MySQL database, or schema. You must create a database (schema) before using this plugin!</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>database.username:</code></td>
|
||||
<td>The username to access your MySQL database.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>database.password:</code></td>
|
||||
<td>The password to access your MySQL database.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>database.encoding:</code></td>
|
||||
<td>If your MySQL database uses an encoding other than <code>utf8</code>, change this.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>database.useSSL:</code></td>
|
||||
<td>If your MySQL database can use SSL connections, set this to <code>true</code>!</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>IgnoreCitizens:</code></td>
|
||||
<td>Whether to ignore Citizens NPCs from the Citizens plugin. If set to true, Citizens NPCs won't be affected by this plugin.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>IgnoreShopkeepers:</code></td>
|
||||
<td>Whether to ignore Shopkeepers NPCs from the Shopkeepers plugin. If set to true, Shopkeepers NPCs won't be affected by this plugin.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>IgnoreHeldItems:</code></td>
|
||||
<td>A list of item types where, when the player interacts with a villager while holding one of these items, VTL doesn't affect the interaction. This is used for compatibility with other plugins, like the custom spawn eggs in <a href="https://www.spigotmc.org/resources/%E2%9C%85-safarinet-premium-mob-catcher-plugin.9732/">SafariNet</a>.<br><b>Options:</b>
|
||||
<ul>
|
||||
<li>Add material names for special items used by other plugins</li>
|
||||
<li>Remove all list items and set to [] to disable this feature</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>DisableTrading:</code></td>
|
||||
<td>Whether to disable all villager trading for all worlds, some worlds, or no worlds.<br/><strong>Options:</strong>
|
||||
|
@ -85,7 +92,8 @@
|
|||
<td>The maximum discount (%) you can get from trading/healing zombie villagers. This limits reputation-based price decreases.<br/><strong>Options:</strong>
|
||||
<ul>
|
||||
<li>Set to -1.0 to disable this feature and keep vanilla behavior</li>
|
||||
<li>Set to a number between 0.0 and 1.0 to set the maximum discount a player can get. (NOTE: 30% = 0.3)</li>
|
||||
<li>Set to a number between 0.0 and 1.0 to limit the maximum discount a player can get. (NOTE: 30% = 0.3)</li>
|
||||
<li>Set to a number above 1.0 to increase the maximum discount a player can get. (NOTE: 250% = 2.5)</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -111,7 +119,7 @@
|
|||
</table>
|
||||
</li>
|
||||
<li>
|
||||
<p>Per-item settings: (<code>Overrides:</code>)</p>
|
||||
<p>Per-item override settings: (<code>Overrides:</code>)</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Setting</th>
|
||||
|
@ -138,16 +146,20 @@
|
|||
<td>Sets the maximum number of times a player can make the trade before the villager is out of stock</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>.Item1.Material:</code><br><code>.Item2.Material:</code></td>
|
||||
<td>Sets the material of the 1st or 2nd item in the trade</td>
|
||||
<td><code>.Cooldown:</code></td>
|
||||
<td>Sets the time between restocks for the trade, and applies to ALL villagers. Once the player reaches the <code>MaxUses</code>, the cooldown begins. The trade is disabled for all villagers until the cooldown expires.<br><strong>Format:</strong> <Number><interval><br><strong>Examples:</strong> 30s = 30 seconds, 5m = 5 minutes, 4h = 4 hours, 7d = 7 days</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>.Item1.Amount:</code><br><code>.Item2.Amount:</code></td>
|
||||
<td>Sets the amount of the 1st or 2nd item in the trade</td>
|
||||
<td><code>.Item1.Material:</code><br><code>.Item2.Material:</code><br><code>.Result.Material:</code></td>
|
||||
<td>Sets the material of the 1st, 2nd, or result item in the trade<br><strong>WARNING:</strong> This cannot be undone!</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>.Item1.Amount:</code><br><code>.Item2.Amount:</code><br><code>.Result.Amount:</code></td>
|
||||
<td>Sets the amount of the 1st, 2nd, or result item in the trade<br><strong>WARNING:</strong> This cannot be undone!</td>
|
||||
</tr>
|
||||
</table>
|
||||
</li>
|
||||
<li>
|
||||
<p>For the default config.yml, see: <code>src/main/resources/config.yml</code></p>
|
||||
<p>For the default config.yml, see <code>src/main/resources/config.yml</code></p>
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.pretzel.dev</groupId>
|
||||
<artifactId>VillagerTradeLimiter</artifactId>
|
||||
<version>1.6.4</version>
|
||||
<build>
|
||||
<sourceDirectory>src</sourceDirectory>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>${java.version}</source>
|
||||
<target>${java.version}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>shade</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>de.tr7zw.changeme.nbtapi</pattern>
|
||||
<shadedPattern>com.pretzel.dev.villagertradelimiter.nms</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spigotmc-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>codemc-repo</id>
|
||||
<url>https://repo.codemc.org/repository/maven-public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.20-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>guava</artifactId>
|
||||
<groupId>com.google.guava</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>gson</artifactId>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>joml</artifactId>
|
||||
<groupId>org.joml</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>bungeecord-chat</artifactId>
|
||||
<groupId>net.md-5</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<groupId>org.yaml</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.33</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.40.1.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
</project>
|
43
pom.xml
43
pom.xml
|
@ -3,7 +3,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.pretzel.dev</groupId>
|
||||
<artifactId>VillagerTradeLimiter</artifactId>
|
||||
<version>1.4.3</version>
|
||||
<version>1.6.4</version>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
|
@ -21,6 +21,28 @@
|
|||
<target>${java.version}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>shade</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>de.tr7zw.changeme.nbtapi</pattern>
|
||||
<shadedPattern>com.pretzel.dev.villagertradelimiter.nms</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
@ -40,14 +62,31 @@
|
|||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.18.1-R0.1-SNAPSHOT</version>
|
||||
<version>1.20-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>item-nbt-api</artifactId>
|
||||
<version>2.11.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.tr7zw</groupId>
|
||||
<artifactId>functional-annotations</artifactId>
|
||||
<version>0.1-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.33</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.40.1.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -1,31 +1,47 @@
|
|||
package com.pretzel.dev.villagertradelimiter;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.lib.CommandBase;
|
||||
import com.pretzel.dev.villagertradelimiter.lib.ConfigUpdater;
|
||||
import com.pretzel.dev.villagertradelimiter.commands.CommandManager;
|
||||
import com.pretzel.dev.villagertradelimiter.commands.CommandBase;
|
||||
import com.pretzel.dev.villagertradelimiter.data.PlayerData;
|
||||
import com.pretzel.dev.villagertradelimiter.database.DatabaseManager;
|
||||
import com.pretzel.dev.villagertradelimiter.listeners.InventoryListener;
|
||||
import com.pretzel.dev.villagertradelimiter.listeners.VillagerListener;
|
||||
import com.pretzel.dev.villagertradelimiter.settings.ConfigUpdater;
|
||||
import com.pretzel.dev.villagertradelimiter.lib.Metrics;
|
||||
import com.pretzel.dev.villagertradelimiter.lib.Util;
|
||||
import com.pretzel.dev.villagertradelimiter.listeners.PlayerListener;
|
||||
import com.pretzel.dev.villagertradelimiter.settings.Lang;
|
||||
import com.pretzel.dev.villagertradelimiter.settings.Settings;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.*;
|
||||
|
||||
public class VillagerTradeLimiter extends JavaPlugin {
|
||||
public static final String PLUGIN_NAME = "VillagerTradeLimiter";
|
||||
public static final String PREFIX = ChatColor.GOLD+"["+PLUGIN_NAME+"] ";
|
||||
private static final int BSTATS_ID = 9829;
|
||||
|
||||
//Settings
|
||||
private FileConfiguration cfg;
|
||||
private Lang lang;
|
||||
private CommandManager commandManager;
|
||||
private DatabaseManager databaseManager;
|
||||
private PlayerListener playerListener;
|
||||
private HashMap<UUID, PlayerData> playerData;
|
||||
|
||||
//Initial plugin load/unload
|
||||
/** Initial plugin load/unload */
|
||||
public void onEnable() {
|
||||
//Initialize instance variables
|
||||
this.cfg = null;
|
||||
this.commandManager = new CommandManager(this);
|
||||
this.playerData = new HashMap<>();
|
||||
|
||||
//Copy default settings & load settings
|
||||
this.getConfig().options().copyDefaults();
|
||||
|
@ -41,9 +57,16 @@ public class VillagerTradeLimiter extends JavaPlugin {
|
|||
Util.consoleMsg(PREFIX+PLUGIN_NAME+" is running!");
|
||||
}
|
||||
|
||||
//Loads or reloads config.yml settings
|
||||
/** Save database on plugin stop, server stop */
|
||||
public void onDisable() {
|
||||
for(UUID uuid : playerData.keySet()) {
|
||||
this.databaseManager.savePlayer(uuid, false);
|
||||
}
|
||||
this.playerData.clear();
|
||||
}
|
||||
|
||||
/** Loads or reloads config.yml and messages.yml */
|
||||
public void loadSettings() {
|
||||
//Load config.yml
|
||||
final String mainPath = this.getDataFolder().getPath()+"/";
|
||||
final File file = new File(mainPath, "config.yml");
|
||||
try {
|
||||
|
@ -52,44 +75,52 @@ public class VillagerTradeLimiter extends JavaPlugin {
|
|||
Util.errorMsg(e);
|
||||
}
|
||||
this.cfg = YamlConfiguration.loadConfiguration(file);
|
||||
this.lang = new Lang(this, this.getTextResource("messages.yml"), mainPath);
|
||||
|
||||
//Load/reload database manager
|
||||
if(this.databaseManager == null) this.databaseManager = new DatabaseManager(this);
|
||||
else onDisable();
|
||||
this.databaseManager.load();
|
||||
}
|
||||
|
||||
/** Load and initialize the bStats class with the plugin id */
|
||||
private void loadBStats() {
|
||||
if(this.cfg.getBoolean("bStats", true)) new Metrics(this, 9829);
|
||||
}
|
||||
|
||||
//Registers plugin commands
|
||||
private void registerCommands() {
|
||||
final String reloaded = Util.replaceColors("&eVillagerTradeLimiter &ahas been reloaded!");
|
||||
final CommandBase vtl = new CommandBase("villagertradelimiter", "villagertradelimiter.use", (p,args) -> this.help(p));
|
||||
vtl.addSub(new CommandBase("reload", "villagertradelimiter.reload", (p,args) -> {
|
||||
loadSettings();
|
||||
Util.sendMsg(reloaded, p);
|
||||
}));
|
||||
this.getCommand("villagertradelimiter").setExecutor(vtl);
|
||||
this.getCommand("villagertradelimiter").setTabCompleter(vtl);
|
||||
}
|
||||
|
||||
//Registers plugin listeners
|
||||
private void registerListeners() {
|
||||
this.getServer().getPluginManager().registerEvents(new PlayerListener(this), this);
|
||||
}
|
||||
|
||||
// ------------------------- Commands -------------------------
|
||||
private void help(final Player p) {
|
||||
if(p != null) {
|
||||
if(!p.hasPermission("villagertradelimiter.use") && !p.hasPermission("villagertradelimiter.*")) return;
|
||||
p.sendMessage(ChatColor.GREEN+"VillagerTradeLimiter commands:");
|
||||
p.sendMessage(ChatColor.AQUA+"/vtl "+ChatColor.WHITE+"- shows this help message");
|
||||
Util.sendIfPermitted("villagertradelimiter.reload", ChatColor.AQUA+"/vtl reload "+ChatColor.WHITE+"- reloads config.yml", p);
|
||||
} else {
|
||||
Util.consoleMsg(ChatColor.GREEN+"VillagerTradeLimiter commands:");
|
||||
Util.consoleMsg(ChatColor.AQUA+"/vtl "+ChatColor.WHITE+"- shows this help message");
|
||||
Util.consoleMsg(ChatColor.AQUA+"/vtl reload "+ChatColor.WHITE+"- reloads config.yml");
|
||||
if(this.cfg.getBoolean("bStats", true)) {
|
||||
new Metrics(this, BSTATS_ID);
|
||||
}
|
||||
}
|
||||
|
||||
/** Registers plugin commands */
|
||||
private void registerCommands() {
|
||||
final CommandBase cmd = this.commandManager.getCommands();
|
||||
this.getCommand("villagertradelimiter").setExecutor(cmd);
|
||||
this.getCommand("villagertradelimiter").setTabCompleter(cmd);
|
||||
}
|
||||
|
||||
/** Registers plugin listeners */
|
||||
private void registerListeners() {
|
||||
final Settings settings = new Settings(this);
|
||||
this.playerListener = new PlayerListener(this, settings);
|
||||
this.getServer().getPluginManager().registerEvents(this.playerListener, this);
|
||||
this.getServer().getPluginManager().registerEvents(new InventoryListener(this, settings), this);
|
||||
this.getServer().getPluginManager().registerEvents(new VillagerListener(this, settings), this);
|
||||
}
|
||||
|
||||
|
||||
// ------------------------- Getters -------------------------
|
||||
//Returns the settings from config.yml
|
||||
/** @return the settings from config.yml */
|
||||
public FileConfiguration getCfg() { return this.cfg; }
|
||||
|
||||
/** @param path the key you want the value for
|
||||
* @return a language setting from messages.yml */
|
||||
public String getLang(final String path) { return this.lang.get(path); }
|
||||
|
||||
/** @return this plugin's player listener */
|
||||
public PlayerListener getPlayerListener() { return this.playerListener; }
|
||||
|
||||
/** @return a player's data container */
|
||||
public HashMap<UUID, PlayerData> getPlayerData() { return this.playerData; }
|
||||
|
||||
/** @return the invsee inventory's barrier block */
|
||||
public ItemStack getBarrier() { return this.commandManager.getBarrier(); }
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.pretzel.dev.villagertradelimiter.lib;
|
||||
package com.pretzel.dev.villagertradelimiter.commands;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.lib.Callback;
|
||||
import com.pretzel.dev.villagertradelimiter.lib.Util;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
|
@ -16,6 +18,11 @@ public class CommandBase implements CommandExecutor, TabCompleter {
|
|||
private final Callback<Player> callback;
|
||||
private final ArrayList<CommandBase> subs;
|
||||
|
||||
/**
|
||||
* @param name The name of the command
|
||||
* @param permission The permission required to use the command
|
||||
* @param callback The callback that is called when the command is executed
|
||||
*/
|
||||
public CommandBase(String name, String permission, Callback<Player> callback) {
|
||||
this.name = name;
|
||||
this.permission = permission;
|
||||
|
@ -23,6 +30,10 @@ public class CommandBase implements CommandExecutor, TabCompleter {
|
|||
this.subs = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param command The child command to add
|
||||
* @return The given child command
|
||||
*/
|
||||
public CommandBase addSub(CommandBase command) {
|
||||
this.subs.add(command);
|
||||
return command;
|
||||
|
@ -69,12 +80,17 @@ public class CommandBase implements CommandExecutor, TabCompleter {
|
|||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param args The arguments to be copied
|
||||
* @return The copied arguments
|
||||
*/
|
||||
private static String[] getCopy(final String[] args) {
|
||||
String[] res = new String[args.length-1];
|
||||
System.arraycopy(args, 1, res, 0, res.length);
|
||||
return res;
|
||||
}
|
||||
|
||||
/** @return The current online player list */
|
||||
private static List<String> getPlayerList() {
|
||||
final List<String> players = new ArrayList<>();
|
||||
for(Player p : Bukkit.getOnlinePlayers())
|
||||
|
@ -82,6 +98,9 @@ public class CommandBase implements CommandExecutor, TabCompleter {
|
|||
return players;
|
||||
}
|
||||
|
||||
/** @return The name of this command */
|
||||
public String getName() { return this.name; }
|
||||
|
||||
/** @return The permission required to use this command */
|
||||
public String getPermission() { return this.permission; }
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
package com.pretzel.dev.villagertradelimiter.commands;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.VillagerTradeLimiter;
|
||||
import com.pretzel.dev.villagertradelimiter.lib.Util;
|
||||
import net.md_5.bungee.api.chat.ClickEvent;
|
||||
import net.md_5.bungee.api.chat.HoverEvent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.chat.hover.content.Text;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Villager;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class CommandManager {
|
||||
private final VillagerTradeLimiter instance;
|
||||
private final ItemStack barrier;
|
||||
|
||||
/** @param instance The instance of VillagerTradeLimiter.java */
|
||||
public CommandManager(final VillagerTradeLimiter instance) {
|
||||
this.instance = instance;
|
||||
this.barrier = new ItemStack(Material.BARRIER, 1);
|
||||
ItemMeta meta = barrier.getItemMeta();
|
||||
if(meta != null) {
|
||||
meta.setDisplayName(ChatColor.RED+"Close");
|
||||
meta.setLore(Arrays.asList(ChatColor.GRAY+"Click to close", ChatColor.GRAY+"this inventory."));
|
||||
}
|
||||
barrier.setItemMeta(meta);
|
||||
}
|
||||
|
||||
/** @return The root command node, to be registered by the plugin */
|
||||
public CommandBase getCommands() {
|
||||
//Adds the /vtl command
|
||||
final CommandBase cmd = new CommandBase("villagertradelimiter", "villagertradelimiter.use", (p, args) -> showHelp(p, "help"));
|
||||
|
||||
//Adds the /vtl reload command
|
||||
cmd.addSub(new CommandBase("reload", "villagertradelimiter.reload", (p,args) -> {
|
||||
//Reload the config and lang
|
||||
instance.loadSettings();
|
||||
Util.sendMsg(instance.getLang("common.reloaded"), p);
|
||||
}));
|
||||
|
||||
//Adds the /vtl see <player> command
|
||||
cmd.addSub(new CommandBase("see", "villagertradelimiter.see", (p,args) -> {
|
||||
//Check if the command was issued via console
|
||||
if(p == null) {
|
||||
Util.sendMsg(instance.getLang("common.noconsole"), p);
|
||||
return;
|
||||
}
|
||||
|
||||
//Checks if there are enough arguments
|
||||
if(args.length < 1) {
|
||||
Util.sendMsg(instance.getLang("common.noargs"), p);
|
||||
return;
|
||||
}
|
||||
|
||||
//Get the closest villager. If a nearby villager wasn't found, send the player an error message
|
||||
Entity closestEntity = getClosestEntity(p);
|
||||
if(closestEntity == null) return;
|
||||
|
||||
//Gets the other player by name, using the first argument of the command
|
||||
OfflinePlayer otherPlayer = Bukkit.getOfflinePlayer(args[0]);
|
||||
if(!otherPlayer.isOnline() && !otherPlayer.hasPlayedBefore()) {
|
||||
Util.sendMsg(instance.getLang("see.noplayer").replace("{player}", args[0]), p);
|
||||
return;
|
||||
}
|
||||
|
||||
//Open the other player's trade view for the calling player
|
||||
Util.sendMsg(instance.getLang("see.success").replace("{player}", args[0]), p);
|
||||
instance.getPlayerListener().see((Villager)closestEntity, p, otherPlayer);
|
||||
}));
|
||||
|
||||
//Adds the /vtl invsee command
|
||||
cmd.addSub(new CommandBase("invsee", "villagertradelimiter.invsee", (p, args) -> {
|
||||
//Check if the command was issued via console
|
||||
if(p == null) {
|
||||
Util.sendMsg(instance.getLang("common.noconsole"), p);
|
||||
return;
|
||||
}
|
||||
|
||||
//Get the closest villager. If a nearby villager wasn't found, send the player an error message
|
||||
Entity closestEntity = getClosestEntity(p);
|
||||
if(closestEntity == null) return;
|
||||
|
||||
//Open the villager's inventory view for the calling player
|
||||
final Villager closestVillager = (Villager)closestEntity;
|
||||
final Inventory inventory = Bukkit.createInventory(null, 9, "Villager Inventory");
|
||||
for(ItemStack item : closestVillager.getInventory().getContents()) {
|
||||
if(item == null) continue;
|
||||
inventory.addItem(item.clone());
|
||||
}
|
||||
inventory.setItem(8, barrier);
|
||||
p.openInventory(inventory);
|
||||
}));
|
||||
return cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param player The player to get the closest entity for
|
||||
* @return The closest entity to the player, that the player is looking at
|
||||
*/
|
||||
private Entity getClosestEntity(final Player player) {
|
||||
Entity closestEntity = null;
|
||||
double closestDistance = Double.MAX_VALUE;
|
||||
for(Entity entity : player.getNearbyEntities(10, 10, 10)) {
|
||||
if(entity instanceof Villager) {
|
||||
Location eye = player.getEyeLocation();
|
||||
Vector toEntity = ((Villager) entity).getEyeLocation().toVector().subtract(eye.toVector());
|
||||
double dot = toEntity.normalize().dot(eye.getDirection());
|
||||
double distance = eye.distance(((Villager)entity).getEyeLocation());
|
||||
if(dot > 0.99D && distance < closestDistance) {
|
||||
closestEntity = entity;
|
||||
closestDistance = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(closestEntity == null) {
|
||||
Util.sendMsg(instance.getLang("see.novillager"), player);
|
||||
}
|
||||
return closestEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an interactive help message to a player via chat
|
||||
* @param p The player to show the help message to
|
||||
* @param key The key of the help message to show (in messages.yml)
|
||||
*/
|
||||
public void showHelp(final Player p, final String key) {
|
||||
for(String line : instance.getLang(key).split("\n")) {
|
||||
int i = line.indexOf("]");
|
||||
|
||||
final String[] tokens = line.substring(i+1).split(";");
|
||||
if(p == null) Util.consoleMsg(tokens[0]);
|
||||
else {
|
||||
final TextComponent text = new TextComponent(tokens[0]);
|
||||
if(tokens.length > 1) text.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(tokens[0]+"\n"+tokens[1])));
|
||||
text.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, ChatColor.stripColor(tokens[0])));
|
||||
if(p.hasPermission(line.substring(1, i))) p.spigot().sendMessage(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @return The barrier item */
|
||||
public ItemStack getBarrier() {
|
||||
return this.barrier;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package com.pretzel.dev.villagertradelimiter.data;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.lib.Util;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
public class Cooldown {
|
||||
private static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH);
|
||||
|
||||
private enum Interval {
|
||||
s(1L),
|
||||
m(60L),
|
||||
h(3600L),
|
||||
d(86400L),
|
||||
w(604800L);
|
||||
|
||||
final long factor;
|
||||
Interval(long factor) {
|
||||
this.factor = factor;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cooldownStr The cooldown time as written in config.yml (7d, 30s, 5m, etc)
|
||||
* @return The cooldown time in seconds
|
||||
*/
|
||||
public static long parseCooldown(final String cooldownStr) {
|
||||
if(cooldownStr.equals("0")) return 0;
|
||||
try {
|
||||
long time = Long.parseLong(cooldownStr.substring(0, cooldownStr.length()-1));
|
||||
String interval = cooldownStr.substring(cooldownStr.length()-1).toLowerCase();
|
||||
return time * Interval.valueOf(interval).factor;
|
||||
} catch (Exception e) {
|
||||
Util.errorMsg(e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param date The date to format
|
||||
* @return The date as a 'yyyy-MM-dd HH:mm:ss' formatted string
|
||||
*/
|
||||
public static String formatTime(Date date) {
|
||||
return FORMAT.format(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param timeStr The string to format, in 'yyyy-MM-dd HH:mm:ss' format
|
||||
* @return The date that the string represents
|
||||
*/
|
||||
public static Date parseTime(final String timeStr) {
|
||||
try {
|
||||
return FORMAT.parse(timeStr);
|
||||
} catch (ParseException e) {
|
||||
Util.errorMsg(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.pretzel.dev.villagertradelimiter.data;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.wrappers.VillagerWrapper;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class PlayerData {
|
||||
private final HashMap<String, String> tradingCooldowns;
|
||||
private VillagerWrapper tradingVillager;
|
||||
|
||||
public PlayerData() {
|
||||
this.tradingCooldowns = new HashMap<>();
|
||||
this.tradingVillager = null;
|
||||
}
|
||||
|
||||
/** @return The map of items to timestamps for the player's trading history */
|
||||
public HashMap<String, String> getTradingCooldowns() { return this.tradingCooldowns; }
|
||||
|
||||
/** @param tradingVillager The villager that this player is currently trading with */
|
||||
public void setTradingVillager(final VillagerWrapper tradingVillager) { this.tradingVillager = tradingVillager; }
|
||||
|
||||
/** @return The villager that this player is currently trading with */
|
||||
public VillagerWrapper getTradingVillager() { return this.tradingVillager; }
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package com.pretzel.dev.villagertradelimiter.database;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.lib.Callback;
|
||||
import com.pretzel.dev.villagertradelimiter.lib.Util;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public abstract class Database {
|
||||
protected final JavaPlugin instance;
|
||||
|
||||
public Database(final JavaPlugin instance) {
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
//Tests a DataSource
|
||||
public void test() {
|
||||
try {
|
||||
try (Connection conn = this.getSource().getConnection()) {
|
||||
if (!conn.isValid(1000)) throw new SQLException("Could not connect to database!");
|
||||
else Util.consoleMsg("Connected to database!");
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
Util.consoleMsg("Could not connect to database!");
|
||||
}
|
||||
}
|
||||
|
||||
//Executes a statement or query in the database
|
||||
public ArrayList<String> execute(final String sql, boolean query) {
|
||||
try(Connection conn = this.getSource().getConnection(); PreparedStatement statement = conn.prepareStatement(sql)) {
|
||||
if(query) {
|
||||
final ResultSet result = statement.executeQuery();
|
||||
int columns = result.getMetaData().getColumnCount();
|
||||
final ArrayList<String> res = new ArrayList<>();
|
||||
while(result.next()){
|
||||
String row = "";
|
||||
for(int j = 0; j < columns; j++)
|
||||
row += result.getString(j+1)+(j < columns-1?",":"");
|
||||
res.add(row);
|
||||
}
|
||||
return res;
|
||||
} else statement.execute();
|
||||
} catch (SQLException e) {
|
||||
Util.errorMsg(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public void execute(final String sql, boolean query, final Callback<ArrayList<String>> callback) {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(this.instance, () -> {
|
||||
final ArrayList<String> result = execute(sql, query);
|
||||
if(callback != null) Bukkit.getScheduler().runTask(this.instance, () -> callback.call(result));
|
||||
});
|
||||
}
|
||||
|
||||
public abstract void load(final ConfigurationSection cfg);
|
||||
public abstract boolean isMySQL();
|
||||
protected abstract DataSource getSource();
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
package com.pretzel.dev.villagertradelimiter.database;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.VillagerTradeLimiter;
|
||||
import com.pretzel.dev.villagertradelimiter.data.Cooldown;
|
||||
import com.pretzel.dev.villagertradelimiter.data.PlayerData;
|
||||
import com.pretzel.dev.villagertradelimiter.lib.Util;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.Villager;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
public class DatabaseManager {
|
||||
private static final String CREATE_TABLE_COOLDOWN =
|
||||
"CREATE TABLE IF NOT EXISTS vtl_cooldown("+
|
||||
"uuid CHAR(36) NOT NULL,"+
|
||||
"item VARCHAR(255) NOT NULL,"+
|
||||
"time TEXT NOT NULL,"+
|
||||
"PRIMARY KEY(uuid, item));";
|
||||
private static final String SELECT_ITEMS = "SELECT * FROM vtl_cooldown;";
|
||||
private static final String INSERT_ITEM = "INSERT OR IGNORE INTO vtl_cooldown(uuid,item,time) VALUES?;"; //INSERT IGNORE INTO for MySQL
|
||||
private static final String DELETE_ITEMS = "DELETE FROM vtl_cooldown WHERE uuid='?';";
|
||||
|
||||
private final VillagerTradeLimiter instance;
|
||||
private Database database;
|
||||
|
||||
public DatabaseManager(final VillagerTradeLimiter instance) {
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
public void load() {
|
||||
final ConfigurationSection cfg = instance.getCfg().getConfigurationSection("database");
|
||||
if(cfg == null) {
|
||||
Util.consoleMsg("Database settings missing from config.yml!");
|
||||
this.database = null;
|
||||
return;
|
||||
}
|
||||
boolean mysql = cfg.getBoolean("mysql", false);
|
||||
if(this.database != null && ((mysql && this.database.isMySQL()) || (!mysql && !this.database.isMySQL()))) this.database.load(cfg);
|
||||
else this.database = (mysql?new MySQL(instance, cfg):new SQLite(instance));
|
||||
this.database.execute(CREATE_TABLE_COOLDOWN, false);
|
||||
|
||||
//Loads all the data
|
||||
this.database.execute(SELECT_ITEMS, true, (result,args) -> {
|
||||
if(result != null) {
|
||||
for(String row : result) {
|
||||
final String[] tokens = row.split(",");
|
||||
|
||||
UUID uuid = UUID.fromString(tokens[0]);
|
||||
String item = tokens[1];
|
||||
final Date date = Cooldown.parseTime(tokens[2]);
|
||||
if(date == null) continue;
|
||||
long time = date.getTime();
|
||||
|
||||
PlayerData data = instance.getPlayerData().get(uuid);
|
||||
if(data == null) {
|
||||
data = new PlayerData();
|
||||
instance.getPlayerData().put(uuid, data);
|
||||
}
|
||||
|
||||
String key = (Bukkit.getEntity(uuid) instanceof Villager ? "Restock" : "Cooldown");
|
||||
String cooldownStr = instance.getCfg().getString(key, "0");
|
||||
cooldownStr = instance.getCfg().getString("Overrides."+item+"."+key, cooldownStr);
|
||||
long cooldown = Cooldown.parseCooldown(cooldownStr);
|
||||
|
||||
final Date now = Date.from(Instant.now());
|
||||
if(cooldown != 0 && now.getTime()/1000L < time/1000L + cooldown) {
|
||||
data.getTradingCooldowns().put(item, Cooldown.formatTime(date));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void savePlayer(final UUID uuid, boolean async) {
|
||||
if(this.database == null) return;
|
||||
|
||||
//Delete existing rows for player
|
||||
final String uuidStr = uuid.toString();
|
||||
if(async) this.database.execute(DELETE_ITEMS.replace("?", uuidStr), false, (result,args) -> save(uuid, true));
|
||||
else {
|
||||
this.database.execute(DELETE_ITEMS.replace("?", uuidStr), false);
|
||||
save(uuid, false);
|
||||
}
|
||||
}
|
||||
private void save(final UUID uuid, boolean async) {
|
||||
//Insert new rows for player pages
|
||||
final PlayerData playerData = instance.getPlayerData().get(uuid);
|
||||
if(playerData == null) return;
|
||||
|
||||
String values = "";
|
||||
for(String item : playerData.getTradingCooldowns().keySet()) {
|
||||
final String time = playerData.getTradingCooldowns().get(item);
|
||||
|
||||
if(!values.isEmpty()) values += ",";
|
||||
values += "('"+uuid+"','"+item+"','"+time+"')";
|
||||
}
|
||||
if(values.isEmpty()) return;
|
||||
String sql = INSERT_ITEM.replace("?", values);
|
||||
if(this.database.isMySQL()) sql = sql.replace(" OR ", " ");
|
||||
if(async) this.database.execute(sql, false, (result,args) -> {});
|
||||
else this.database.execute(sql, false);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package com.pretzel.dev.villagertradelimiter.database;
|
||||
|
||||
import com.mysql.cj.jdbc.MysqlConnectionPoolDataSource;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class MySQL extends Database {
|
||||
private final MysqlConnectionPoolDataSource source;
|
||||
|
||||
public MySQL(final JavaPlugin instance, final ConfigurationSection cfg) {
|
||||
super(instance);
|
||||
this.source = new MysqlConnectionPoolDataSource();
|
||||
this.load(cfg);
|
||||
}
|
||||
|
||||
public void load(final ConfigurationSection cfg) {
|
||||
this.source.setServerName(cfg.getString("host", "localhost"));
|
||||
this.source.setPort(cfg.getInt("port", 3306));
|
||||
this.source.setDatabaseName(cfg.getString("database", "sagas_holo"));
|
||||
this.source.setUser(cfg.getString("username", "root"));
|
||||
this.source.setPassword(cfg.getString("password", "root"));
|
||||
try {
|
||||
this.source.setCharacterEncoding(cfg.getString("encoding", "utf8"));
|
||||
this.source.setUseSSL(cfg.getBoolean("useSSL", false));
|
||||
} catch (SQLException ignored) {}
|
||||
|
||||
this.test();
|
||||
}
|
||||
|
||||
public boolean isMySQL() { return true; }
|
||||
public DataSource getSource() { return this.source; }
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package com.pretzel.dev.villagertradelimiter.database;
|
||||
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.sqlite.javax.SQLiteConnectionPoolDataSource;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
public class SQLite extends Database {
|
||||
private final SQLiteConnectionPoolDataSource source;
|
||||
|
||||
public SQLite(final JavaPlugin instance) {
|
||||
super(instance);
|
||||
this.source = new SQLiteConnectionPoolDataSource();
|
||||
this.load(null);
|
||||
}
|
||||
|
||||
public void load(final ConfigurationSection cfg) {
|
||||
this.source.setUrl("jdbc:sqlite:"+instance.getDataFolder().getPath()+"/database.db");
|
||||
this.test();
|
||||
}
|
||||
|
||||
public boolean isMySQL() { return false; }
|
||||
public DataSource getSource() { return this.source; }
|
||||
}
|
|
@ -1,5 +1,10 @@
|
|||
package com.pretzel.dev.villagertradelimiter.lib;
|
||||
|
||||
public interface Callback<T> {
|
||||
void call(T result, String[] args);
|
||||
/**
|
||||
* Callback function
|
||||
* @param result Any type of result to be passed into the callback function
|
||||
* @param args Any extra arguments to be passed into the callback function
|
||||
*/
|
||||
void call(T result, String... args);
|
||||
}
|
||||
|
|
|
@ -8,34 +8,55 @@ import java.io.FileWriter;
|
|||
import java.io.Reader;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Villager;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
|
||||
public class Util {
|
||||
//Sends a message to the sender of a command
|
||||
public static void sendMsg(String msg, Player p) {
|
||||
if(p == null) consoleMsg(msg);
|
||||
else p.sendMessage(msg);
|
||||
/**
|
||||
* Sends a message to the sender of a command
|
||||
* @param msg The message to send
|
||||
* @param player The player (or console) to sent the message to
|
||||
*/
|
||||
public static void sendMsg(String msg, Player player) {
|
||||
if(player == null) consoleMsg(msg);
|
||||
else player.sendMessage(msg);
|
||||
}
|
||||
|
||||
//Sends a message to a player if they have permission
|
||||
/**
|
||||
* Sends a message to a player if they have permission
|
||||
* @param perm The name of the permission to check
|
||||
* @param msg The message to send
|
||||
* @param player The player (or console) to sent the message to
|
||||
*/
|
||||
public static void sendIfPermitted(String perm, String msg, Player player) {
|
||||
if(player.hasPermission(perm)) player.sendMessage(msg);
|
||||
}
|
||||
|
||||
//Sends a message to the console
|
||||
/**
|
||||
* Sends a message to the console
|
||||
* @param msg The message to send
|
||||
*/
|
||||
public static void consoleMsg(String msg) {
|
||||
if(msg != null) Bukkit.getServer().getConsoleSender().sendMessage(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the color tags to bukkit color tags
|
||||
* @param in The string to replace color tags for
|
||||
* @return The string with replaced colors
|
||||
*/
|
||||
public static String replaceColors(String in) {
|
||||
if(in == null) return null;
|
||||
return in.replace("&", "\u00A7");
|
||||
}
|
||||
|
||||
//Sends an error message to the console
|
||||
/**
|
||||
* Sends an error message to the console
|
||||
* @param e The error to send a message for
|
||||
*/
|
||||
public static void errorMsg(Exception e) {
|
||||
String error = e.toString();
|
||||
for(StackTraceElement x : e.getStackTrace()) {
|
||||
|
@ -44,26 +65,47 @@ public class Util {
|
|||
consoleMsg(ChatColor.RED+"ERROR: "+error);
|
||||
}
|
||||
|
||||
//Returns whether a player is a Citizens NPC or not
|
||||
public static boolean isNPC(Player player) {
|
||||
return player.hasMetadata("NPC");
|
||||
/**
|
||||
* Checks whether an entity is a Citizens NPC or not
|
||||
* @param entity The entity to check
|
||||
* @return True if the entity is an NPC, false otherwise
|
||||
*/
|
||||
public static boolean isNPC(Entity entity) {
|
||||
return entity.hasMetadata("NPC");
|
||||
}
|
||||
|
||||
//Returns whether a villager is a Citizens NPC or not
|
||||
public static boolean isNPC(Villager villager) {
|
||||
return villager.hasMetadata("NPC");
|
||||
/**
|
||||
* Returns whether an entity is a shopkeeper NPC or not
|
||||
* @param entity The villager to check
|
||||
* @return True if the villager is a shopkeeper, false otherwise
|
||||
*/
|
||||
public static boolean isShopkeeper(Entity entity) {
|
||||
return entity.hasMetadata("shopkeeper");
|
||||
}
|
||||
|
||||
//Converts an int array to a string
|
||||
public static String intArrayToString(int[] arr) {
|
||||
/**
|
||||
* Combines the elements of an int[] into a string
|
||||
* @param arr The int[] to combine
|
||||
* @param separator (optional) The string to place between elements of the int[]
|
||||
* @return The combined string of the int[]
|
||||
*/
|
||||
public static String intArrayToString(int[] arr, String separator) {
|
||||
String res = "";
|
||||
for(int a : arr) { res += a+""; }
|
||||
for(int a : arr) { res += a+separator; }
|
||||
return res;
|
||||
}
|
||||
public static String intArrayToString(int[] arr) {
|
||||
return intArrayToString(arr, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the lines of a file into a String[]
|
||||
* @param reader The file reader
|
||||
* @return The lines of the file, as a String[]
|
||||
*/
|
||||
public static String[] readFile(Reader reader) {
|
||||
String out = "";
|
||||
BufferedReader br = null;
|
||||
BufferedReader br;
|
||||
try {
|
||||
br = new BufferedReader(reader);
|
||||
String line;
|
||||
|
@ -76,6 +118,12 @@ public class Util {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the lines of a file into a String[]
|
||||
* @param file The file
|
||||
* @return The lines of the file, as a String[]
|
||||
*/
|
||||
public static String[] readFile(File file) {
|
||||
try {
|
||||
return readFile(new FileReader(file));
|
||||
|
@ -85,8 +133,13 @@ public class Util {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a String to a file
|
||||
* @param file The file to write the String to
|
||||
* @param out The String to write to the file
|
||||
*/
|
||||
public static void writeFile(File file, String out) {
|
||||
BufferedWriter bw = null;
|
||||
BufferedWriter bw;
|
||||
try {
|
||||
bw = new BufferedWriter(new FileWriter(file));
|
||||
bw.write(out);
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
package com.pretzel.dev.villagertradelimiter.listeners;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.VillagerTradeLimiter;
|
||||
import com.pretzel.dev.villagertradelimiter.data.Cooldown;
|
||||
import com.pretzel.dev.villagertradelimiter.data.PlayerData;
|
||||
import com.pretzel.dev.villagertradelimiter.settings.Settings;
|
||||
import com.pretzel.dev.villagertradelimiter.wrappers.VillagerWrapper;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Villager;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.inventory.InventoryCloseEvent;
|
||||
import org.bukkit.event.inventory.InventoryType;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.MerchantRecipe;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
|
||||
public class InventoryListener implements Listener {
|
||||
|
||||
private final VillagerTradeLimiter instance;
|
||||
private final Settings settings;
|
||||
|
||||
/**
|
||||
* @param instance The instance of VillagerTradeLimiter.java
|
||||
* @param settings The settings instance
|
||||
*/
|
||||
public InventoryListener(final VillagerTradeLimiter instance, final Settings settings) {
|
||||
this.instance = instance;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
/** Handles when a player clicks in an inventory window */
|
||||
@EventHandler
|
||||
public void onPlayerClickInventory(final InventoryClickEvent event) {
|
||||
if(event.getInventory().getType() != InventoryType.CHEST) return;
|
||||
if(event.getInventory().getSize() != 9) return;
|
||||
if(!(event.getWhoClicked() instanceof Player)) return;
|
||||
if(settings.shouldSkipNPC(event.getWhoClicked())) return; //Skips NPCs
|
||||
ItemStack barrier = event.getInventory().getItem(8);
|
||||
if(barrier == null || !barrier.isSimilar(instance.getBarrier())) return;
|
||||
|
||||
//If the inventory matches the invsee inventory, cancel click events
|
||||
event.setCancelled(true);
|
||||
if(event.getCurrentItem() != null && event.getCurrentItem().isSimilar(instance.getBarrier())) {
|
||||
Bukkit.getScheduler().runTaskLater(instance, () -> event.getWhoClicked().closeInventory(), 0L);
|
||||
}
|
||||
}
|
||||
|
||||
/** Handles when a player stops trading with a villager */
|
||||
@EventHandler
|
||||
public void onPlayerStopTrading(final InventoryCloseEvent event) {
|
||||
//Don't do anything unless the player is actually finished trading with a villager
|
||||
if(event.getInventory().getType() != InventoryType.MERCHANT) return;
|
||||
if(!(event.getPlayer() instanceof Player)) return;
|
||||
if(!(event.getInventory().getHolder() instanceof Villager)) return;
|
||||
final Player player = (Player)event.getPlayer();
|
||||
final Villager villager = (Villager)event.getInventory().getHolder();
|
||||
if(settings.shouldSkipNPC(player) || settings.shouldSkipNPC(villager)) return; //Skips NPCs
|
||||
|
||||
//Reset the villager's NBT data when a player is finished trading
|
||||
final PlayerData playerData = instance.getPlayerData().get(player.getUniqueId());
|
||||
if(playerData == null) return;
|
||||
|
||||
final VillagerWrapper villagerWrapper = playerData.getTradingVillager();
|
||||
if(villagerWrapper == null) return;
|
||||
playerData.setTradingVillager(null);
|
||||
villagerWrapper.reset();
|
||||
}
|
||||
|
||||
/** Handles when a player successfully trades with a villager */
|
||||
@EventHandler
|
||||
public void onPlayerMakeTrade(final InventoryClickEvent event) {
|
||||
if(event.getInventory().getType() != InventoryType.MERCHANT) return;
|
||||
if(!(event.getInventory().getHolder() instanceof Villager)) return;
|
||||
if(!(event.getWhoClicked() instanceof Player)) return;
|
||||
if(event.getRawSlot() != 2) return;
|
||||
final Player player = (Player)event.getWhoClicked();
|
||||
final Villager villager = (Villager)event.getInventory().getHolder();
|
||||
if(settings.shouldSkipNPC(player) || settings.shouldSkipNPC(villager)) return; //Skips NPCs
|
||||
|
||||
//Get the items involved in the trade
|
||||
final ItemStack result = event.getCurrentItem();
|
||||
ItemStack ingredient1 = event.getInventory().getItem(0);
|
||||
ItemStack ingredient2 = event.getInventory().getItem(1);
|
||||
if(result == null || result.getType() == Material.AIR) return;
|
||||
if(ingredient1 == null) ingredient1 = new ItemStack(Material.AIR, 1);
|
||||
if(ingredient2 == null) ingredient2 = new ItemStack(Material.AIR, 1);
|
||||
|
||||
//Check if there is a cooldown set for the trade
|
||||
final ConfigurationSection overrides = instance.getCfg().getConfigurationSection("Overrides");
|
||||
if(overrides == null) return;
|
||||
|
||||
final String type = settings.getType(result, ingredient1, ingredient2);
|
||||
String cooldownStr = instance.getCfg().getString("Cooldown", "0");
|
||||
cooldownStr = overrides.getString(type+".Cooldown", cooldownStr);
|
||||
String restockStr = instance.getCfg().getString("Restock", "0");
|
||||
restockStr = overrides.getString(type+".Restock", restockStr);
|
||||
|
||||
if(cooldownStr.equals("0") && restockStr.equals("0")) return;
|
||||
|
||||
//Get the selected recipe by the items in the slots
|
||||
final MerchantRecipe selectedRecipe = getSelectedRecipe(villager, ingredient1, ingredient2, result);
|
||||
if(selectedRecipe == null) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
//Add a cooldown to the trade if the player has reached the max uses
|
||||
final PlayerData playerData = instance.getPlayerData().get(player.getUniqueId());
|
||||
final PlayerData villagerData = instance.getPlayerData().get(villager.getUniqueId());
|
||||
if(playerData == null || playerData.getTradingVillager() == null) return;
|
||||
Bukkit.getScheduler().runTaskLater(instance, () -> {
|
||||
int uses = selectedRecipe.getUses();
|
||||
final String time = Cooldown.formatTime(Date.from(Instant.now()));
|
||||
if(uses >= selectedRecipe.getMaxUses()) {
|
||||
if(!playerData.getTradingCooldowns().containsKey(type)) {
|
||||
playerData.getTradingCooldowns().put(type, time);
|
||||
}
|
||||
if(villagerData != null && !villagerData.getTradingCooldowns().containsKey(type)) {
|
||||
villagerData.getTradingCooldowns().put(type, time);
|
||||
}
|
||||
}
|
||||
}, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param villager The villager to get the recipe from
|
||||
* @param ingredient1 The item in the first ingredient slot of the trade interface
|
||||
* @param ingredient2 The item in the second ingredient slot of the trade interface
|
||||
* @param result The item in the result slot of the trade interface
|
||||
* @return The villager's recipe that matches the items in the slots
|
||||
*/
|
||||
private MerchantRecipe getSelectedRecipe(final Villager villager, final ItemStack ingredient1, final ItemStack ingredient2, final ItemStack result) {
|
||||
for(MerchantRecipe recipe : villager.getRecipes()) {
|
||||
final ItemStack item1 = recipe.getIngredients().get(0);
|
||||
final ItemStack item2 = recipe.getIngredients().get(1);
|
||||
if(!recipe.getResult().isSimilar(result)) continue;
|
||||
if((item1.isSimilar(ingredient1) && item2.isSimilar(ingredient2)) || (item1.isSimilar(ingredient2) && item2.isSimilar(ingredient1)))
|
||||
return recipe;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,258 +1,274 @@
|
|||
package com.pretzel.dev.villagertradelimiter.listeners;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.VillagerTradeLimiter;
|
||||
import com.pretzel.dev.villagertradelimiter.data.Cooldown;
|
||||
import com.pretzel.dev.villagertradelimiter.data.PlayerData;
|
||||
import com.pretzel.dev.villagertradelimiter.lib.Util;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.*;
|
||||
import com.pretzel.dev.villagertradelimiter.settings.Settings;
|
||||
import com.pretzel.dev.villagertradelimiter.wrappers.IngredientWrapper;
|
||||
import com.pretzel.dev.villagertradelimiter.wrappers.PlayerWrapper;
|
||||
import com.pretzel.dev.villagertradelimiter.wrappers.RecipeWrapper;
|
||||
import com.pretzel.dev.villagertradelimiter.wrappers.VillagerWrapper;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.enchantments.EnchantmentWrapper;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Villager;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerInteractEntityEvent;
|
||||
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class PlayerListener implements Listener {
|
||||
private static final Material[] MATERIALS = new Material[] { Material.IRON_HELMET, Material.IRON_CHESTPLATE, Material.IRON_LEGGINGS, Material.IRON_BOOTS, Material.BELL, Material.CHAINMAIL_HELMET, Material.CHAINMAIL_CHESTPLATE, Material.CHAINMAIL_LEGGINGS, Material.CHAINMAIL_BOOTS, Material.SHIELD, Material.DIAMOND_HELMET, Material.DIAMOND_CHESTPLATE, Material.DIAMOND_LEGGINGS, Material.DIAMOND_BOOTS, Material.FILLED_MAP, Material.FISHING_ROD, Material.LEATHER_HELMET, Material.LEATHER_CHESTPLATE, Material.LEATHER_LEGGINGS, Material.LEATHER_BOOTS, Material.LEATHER_HORSE_ARMOR, Material.SADDLE, Material.ENCHANTED_BOOK, Material.STONE_AXE, Material.STONE_SHOVEL, Material.STONE_PICKAXE, Material.STONE_HOE, Material.IRON_AXE, Material.IRON_SHOVEL, Material.IRON_PICKAXE, Material.DIAMOND_AXE, Material.DIAMOND_SHOVEL, Material.DIAMOND_PICKAXE, Material.DIAMOND_HOE, Material.DIAMOND_SWORD};
|
||||
private static final Material[] MAX_USES_12 = new Material[]{Material.IRON_HELMET, Material.IRON_CHESTPLATE, Material.IRON_LEGGINGS, Material.IRON_BOOTS, Material.IRON_INGOT, Material.BELL, Material.CHAINMAIL_HELMET, Material.CHAINMAIL_CHESTPLATE, Material.CHAINMAIL_LEGGINGS, Material.CHAINMAIL_BOOTS, Material.LAVA_BUCKET, Material.DIAMOND, Material.SHIELD, Material.RABBIT_STEW, Material.DRIED_KELP_BLOCK, Material.SWEET_BERRIES, Material.MAP, Material.FILLED_MAP, Material.COMPASS, Material.ITEM_FRAME, Material.GLOBE_BANNER_PATTERN, Material.WHITE_BANNER, Material.LIGHT_GRAY_BANNER, Material.GRAY_BANNER, Material.BLACK_BANNER, Material.BROWN_BANNER, Material.ORANGE_BANNER, Material.YELLOW_BANNER, Material.LIME_BANNER, Material.GREEN_BANNER, Material.CYAN_BANNER, Material.BLUE_BANNER, Material.LIGHT_BLUE_BANNER, Material.PURPLE_BANNER, Material.MAGENTA_BANNER, Material.PINK_BANNER, Material.RED_BANNER, Material.WHITE_BED, Material.LIGHT_GRAY_BED, Material.GRAY_BED, Material.BLACK_BED, Material.BROWN_BED, Material.ORANGE_BED, Material.YELLOW_BED, Material.LIME_BED, Material.GREEN_BED, Material.CYAN_BED, Material.BLUE_BED, Material.LIGHT_BLUE_BED, Material.PURPLE_BED, Material.MAGENTA_BED, Material.PINK_BED, Material.RED_BED, Material.REDSTONE, Material.GOLD_INGOT, Material.LAPIS_LAZULI, Material.RABBIT_FOOT, Material.GLOWSTONE, Material.SCUTE, Material.GLASS_BOTTLE, Material.ENDER_PEARL, Material.NETHER_WART, Material.EXPERIENCE_BOTTLE, Material.PUMPKIN, Material.PUMPKIN_PIE, Material.MELON, Material.COOKIE, Material.CAKE, Material.SUSPICIOUS_STEW, Material.GOLDEN_CARROT, Material.GLISTERING_MELON_SLICE, Material.CAMPFIRE, Material.TROPICAL_FISH, Material.PUFFERFISH, Material.BIRCH_BOAT, Material.ACACIA_BOAT, Material.OAK_BOAT, Material.DARK_OAK_BOAT, Material.SPRUCE_BOAT, Material.JUNGLE_BOAT, Material.ARROW, Material.FLINT, Material.STRING, Material.TRIPWIRE_HOOK, Material.TIPPED_ARROW, Material.LEATHER_HELMET, Material.LEATHER_CHESTPLATE, Material.LEATHER_LEGGINGS, Material.LEATHER_BOOTS, Material.LEATHER, Material.RABBIT_HIDE, Material.LEATHER_HORSE_ARMOR, Material.SADDLE, Material.BOOK, Material.ENCHANTED_BOOK, Material.BOOKSHELF, Material.INK_SAC, Material.GLASS, Material.WRITABLE_BOOK, Material.CLOCK, Material.NAME_TAG, Material.QUARTZ, Material.QUARTZ_PILLAR, Material.QUARTZ_BLOCK, Material.TERRACOTTA, Material.WHITE_TERRACOTTA, Material.LIGHT_GRAY_TERRACOTTA, Material.GRAY_TERRACOTTA, Material.BLACK_TERRACOTTA, Material.BROWN_TERRACOTTA, Material.ORANGE_TERRACOTTA, Material.YELLOW_TERRACOTTA, Material.LIME_TERRACOTTA, Material.GREEN_TERRACOTTA, Material.CYAN_TERRACOTTA, Material.BLUE_TERRACOTTA, Material.LIGHT_BLUE_TERRACOTTA, Material.PURPLE_TERRACOTTA, Material.MAGENTA_TERRACOTTA, Material.PINK_TERRACOTTA, Material.RED_TERRACOTTA, Material.WHITE_GLAZED_TERRACOTTA, Material.LIGHT_GRAY_GLAZED_TERRACOTTA, Material.GRAY_GLAZED_TERRACOTTA, Material.BLACK_GLAZED_TERRACOTTA, Material.BROWN_GLAZED_TERRACOTTA, Material.ORANGE_GLAZED_TERRACOTTA, Material.YELLOW_GLAZED_TERRACOTTA, Material.LIME_GLAZED_TERRACOTTA, Material.GREEN_GLAZED_TERRACOTTA, Material.CYAN_GLAZED_TERRACOTTA, Material.BLUE_GLAZED_TERRACOTTA, Material.LIGHT_BLUE_GLAZED_TERRACOTTA, Material.PURPLE_GLAZED_TERRACOTTA, Material.MAGENTA_GLAZED_TERRACOTTA, Material.PINK_GLAZED_TERRACOTTA, Material.RED_GLAZED_TERRACOTTA, Material.SHEARS, Material.PAINTING, Material.STONE_AXE, Material.STONE_SHOVEL, Material.STONE_PICKAXE, Material.STONE_HOE};
|
||||
private static final Material[] MAX_USES_3 = new Material[]{Material.DIAMOND_HELMET, Material.DIAMOND_CHESTPLATE, Material.DIAMOND_LEGGINGS, Material.DIAMOND_BOOTS, Material.DIAMOND_SWORD, Material.DIAMOND_AXE, Material.DIAMOND_SHOVEL, Material.DIAMOND_PICKAXE, Material.DIAMOND_HOE, Material.IRON_SWORD, Material.IRON_AXE, Material.IRON_SHOVEL, Material.IRON_PICKAXE, Material.FISHING_ROD, Material.BOW, Material.CROSSBOW};
|
||||
|
||||
private final VillagerTradeLimiter instance;
|
||||
private final Settings settings;
|
||||
|
||||
public PlayerListener(VillagerTradeLimiter instance) {
|
||||
/**
|
||||
* @param instance The instance of VillagerTradeLimiter.java
|
||||
* @param settings The settings instance
|
||||
*/
|
||||
public PlayerListener(final VillagerTradeLimiter instance, final Settings settings) {
|
||||
this.instance = instance;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
//Handles villager trading event
|
||||
/** Handles when a player begins trading with a villager */
|
||||
@EventHandler
|
||||
public void onPlayerInteract(PlayerInteractEntityEvent event) {
|
||||
if(!(event.getRightClicked() instanceof Villager)) return;
|
||||
public void onPlayerBeginTrading(final PlayerInteractEntityEvent event) {
|
||||
if(event.isCancelled()) return; //Skips when event is already cancelled
|
||||
if(!(event.getRightClicked() instanceof Villager)) return; //Skips non-villager entities
|
||||
|
||||
final Player player = event.getPlayer();
|
||||
final Villager villager = (Villager)event.getRightClicked();
|
||||
if(Util.isNPC(villager)) return; //Skips NPCs
|
||||
if(villager.getProfession() == Villager.Profession.NONE || villager.getProfession() == Villager.Profession.NITWIT) return; //Skips non-trading villagers
|
||||
if(villager.getRecipeCount() == 0) return; //Skips non-trading villagers
|
||||
|
||||
//Skips when the villager is in a disabled world
|
||||
if(instance.getCfg().getStringList("DisableWorlds").contains(villager.getWorld().getName())) return;
|
||||
|
||||
//Skips when player is holding an ignored item
|
||||
ItemStack heldItem = player.getInventory().getItem(event.getHand());
|
||||
if(heldItem != null) {
|
||||
Material heldItemType = heldItem.getType();
|
||||
for(String ignoredType : instance.getCfg().getStringList("IgnoreHeldItems")) {
|
||||
if(heldItemType.equals(Material.matchMaterial(ignoredType))) return;
|
||||
}
|
||||
}
|
||||
if(settings.shouldSkipNPC(event.getPlayer()) || settings.shouldSkipNPC(villager)) return; //Skips NPCs
|
||||
if(villager.getProfession() == Villager.Profession.NONE || villager.getProfession() == Villager.Profession.NITWIT || villager.getRecipeCount() == 0) return; //Skips non-trading villagers
|
||||
|
||||
//DisableTrading feature
|
||||
if(instance.getCfg().isBoolean("DisableTrading")) {
|
||||
//If all trading is disabled
|
||||
if(instance.getCfg().getBoolean("DisableTrading", false)) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
List<String> disabledWorlds = instance.getCfg().getStringList("DisableTrading");
|
||||
for(String world : disabledWorlds) {
|
||||
if(event.getPlayer().getWorld().getName().equals(world)) {
|
||||
//If trading in the world the player is in is disabled
|
||||
final List<String> disabledWorlds = instance.getCfg().getStringList("DisableTrading");
|
||||
final String world = event.getPlayer().getWorld().getName();
|
||||
for(String disabledWorld : disabledWorlds) {
|
||||
if(world.equals(disabledWorld)) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Player player = event.getPlayer();
|
||||
if(Util.isNPC(player)) return; //Skips NPCs
|
||||
this.hotv(player);
|
||||
this.maxDiscount(villager, player);
|
||||
this.maxDemand(villager);
|
||||
//Cancel the original event, and open the adjusted trade view
|
||||
//event.setCancelled(true);
|
||||
if(!instance.getPlayerData().containsKey(player.getUniqueId())) {
|
||||
instance.getPlayerData().put(player.getUniqueId(), new PlayerData());
|
||||
}
|
||||
if(!instance.getPlayerData().containsKey(villager.getUniqueId())) {
|
||||
instance.getPlayerData().put(villager.getUniqueId(), new PlayerData());
|
||||
}
|
||||
|
||||
this.see(villager, player, player);
|
||||
}
|
||||
|
||||
//Hero of the Village effect limiter feature
|
||||
private void hotv(final Player player) {
|
||||
final PotionEffectType effect = PotionEffectType.HERO_OF_THE_VILLAGE;
|
||||
if(!player.hasPotionEffect(effect)) return; //Skips when player doesn't have HotV
|
||||
|
||||
final int maxHeroLevel = instance.getCfg().getInt("MaxHeroLevel", 1);
|
||||
if(maxHeroLevel == 0) player.removePotionEffect(effect);
|
||||
if(maxHeroLevel <= 0) return; //Skips when disabled in config.yml
|
||||
|
||||
final PotionEffect pot = player.getPotionEffect(effect);
|
||||
if(pot == null) return;
|
||||
if(pot.getAmplifier() > maxHeroLevel-1) {
|
||||
player.removePotionEffect(effect);
|
||||
player.addPotionEffect(new PotionEffect(effect, pot.getDuration(), maxHeroLevel-1));
|
||||
/**
|
||||
* Opens the villager's trading menu, with the adjusted trades of another player (or the same player)
|
||||
* @param villager The villager whose trades you want to see
|
||||
* @param player The player who calls the command, or the player that has begun trading
|
||||
* @param other The other player to view trades for, or the player that has just begun trading
|
||||
*/
|
||||
public void see(final Villager villager, final Player player, final OfflinePlayer other) {
|
||||
//Skips when the villager is in a disabled world
|
||||
if(instance.getCfg().getStringList("DisableWorlds").contains(villager.getWorld().getName())) {
|
||||
Util.sendMsg(instance.getLang("see.noworld"), player);
|
||||
return;
|
||||
}
|
||||
|
||||
//Wraps the villager and player into wrapper classes
|
||||
final VillagerWrapper villagerWrapper = new VillagerWrapper(villager);
|
||||
final PlayerWrapper otherWrapper = new PlayerWrapper(other);
|
||||
final Player otherPlayer = otherWrapper.getPlayer();
|
||||
if(settings.shouldSkipNPC(player) || settings.shouldSkipNPC(villager) || otherPlayer == null || settings.shouldSkipNPC(otherPlayer)) return; //Skips NPCs
|
||||
|
||||
final PlayerData playerData = instance.getPlayerData().get(other.getUniqueId());
|
||||
if(playerData != null) playerData.setTradingVillager(villagerWrapper);
|
||||
|
||||
//Checks if the version is old, before the 1.16 UUID changes
|
||||
String version = instance.getServer().getClass().getPackage().getName();
|
||||
boolean isOld = version.contains("1_13_") || version.contains("1_14_") || version.contains("1_15_");
|
||||
|
||||
//Calculates the player's total reputation and Hero of the Village discount
|
||||
int totalReputation = villagerWrapper.getTotalReputation(villagerWrapper, otherWrapper, isOld);
|
||||
double hotvDiscount = getHotvDiscount(otherWrapper);
|
||||
|
||||
//Adjusts the recipe prices, MaxUses, and ingredients
|
||||
final List<RecipeWrapper> recipes = villagerWrapper.getRecipes();
|
||||
for(RecipeWrapper recipe : recipes) {
|
||||
//Set the special price (discount)
|
||||
recipe.setSpecialPrice(getDiscount(recipe, totalReputation, hotvDiscount));
|
||||
|
||||
//Set ingredient materials and amounts
|
||||
final ConfigurationSection override = settings.getOverride(recipe.getItemStack("buy"), recipe.getItemStack("sell"));
|
||||
if(override != null) {
|
||||
setIngredient(override.getConfigurationSection("Item1"), recipe.getIngredient1());
|
||||
setIngredient(override.getConfigurationSection("Item2"), recipe.getIngredient2());
|
||||
setIngredient(override.getConfigurationSection("Result"), recipe.getResult());
|
||||
}
|
||||
|
||||
//Set the maximum number of uses (trades/restock)
|
||||
recipe.setMaxUses(getMaxUses(recipe, other));
|
||||
}
|
||||
|
||||
//Open the villager's trading menu
|
||||
player.openMerchant(villager, false);
|
||||
}
|
||||
|
||||
//Sets an ingredient for a trade
|
||||
private void setIngredient(ConfigurationSection item, NBTCompound recipe, String nbtKey, String itemKey) {
|
||||
if(!item.contains(itemKey) || recipe.getCompound(nbtKey).getString("id").equals("minecraft:air")) return;
|
||||
if(item.contains(itemKey+".Material")) {
|
||||
recipe.getCompound(nbtKey).setString("id", "minecraft:" + item.getString(itemKey+".Material"));
|
||||
}
|
||||
if(item.contains(itemKey+".Amount")) {
|
||||
int cost = item.getInt(itemKey+".Amount");
|
||||
cost = Math.min(cost, 64);
|
||||
cost = Math.max(cost, 1);
|
||||
recipe.getCompound(nbtKey).setInteger("Count", cost);
|
||||
}
|
||||
/**
|
||||
* @param recipe The recipe to get the base price for
|
||||
* @return The initial price of a recipe/trade, before any discounts are applied
|
||||
*/
|
||||
private int getBasePrice(final RecipeWrapper recipe) {
|
||||
int basePrice = recipe.getIngredient1().getItemStack().getAmount();
|
||||
basePrice = settings.fetchInt(recipe, "Item1.Amount", basePrice);
|
||||
return Math.min(Math.max(basePrice, 1), 64);
|
||||
}
|
||||
|
||||
//MaxDiscount feature - limits the lowest discounted price to a % of the base price
|
||||
private void maxDiscount(final Villager villager, final Player player) {
|
||||
int majorPositiveValue = 0, minorPositiveValue = 0, tradingValue = 0, minorNegativeValue = 0, majorNegativeValue = 0;
|
||||
|
||||
NBTEntity nbtEntity = new NBTEntity(villager);
|
||||
final NBTEntity playerNBT = new NBTEntity(player);
|
||||
final String playerUUID = Util.intArrayToString(playerNBT.getIntArray("UUID"));
|
||||
if(nbtEntity.hasKey("Gossips")) {
|
||||
NBTCompoundList gossips = nbtEntity.getCompoundList("Gossips");
|
||||
for(NBTCompound gossip : gossips) {
|
||||
final String type = gossip.getString("Type");
|
||||
final String targetUUID = Util.intArrayToString(gossip.getIntArray("Target"));
|
||||
final int value = gossip.getInteger("Value");
|
||||
if(targetUUID.equals(playerUUID)) {
|
||||
switch (type) {
|
||||
case "trading": tradingValue = value; break;
|
||||
case "minor_positive": minorPositiveValue = value; break;
|
||||
case "minor_negative": minorNegativeValue = value; break;
|
||||
case "major_positive": majorPositiveValue = value; break;
|
||||
case "major_negative": majorNegativeValue = value; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final ConfigurationSection overrides = instance.getCfg().getConfigurationSection("Overrides");
|
||||
final NBTEntity villagerNBT = new NBTEntity(villager);
|
||||
NBTCompoundList recipes = villagerNBT.getCompound("Offers").getCompoundList("Recipes");
|
||||
for(final NBTCompound recipe : recipes) {
|
||||
final int ingredientAmount = recipe.getCompound("buy").getInteger("Count");
|
||||
final float priceMultiplier = this.getPriceMultiplier(recipe);
|
||||
final int valueModifier = 5 * majorPositiveValue + minorPositiveValue + tradingValue - minorNegativeValue - 5 * majorNegativeValue;
|
||||
final float finalValue = ingredientAmount - priceMultiplier * valueModifier;
|
||||
|
||||
boolean disabled = false;
|
||||
double maxDiscount = instance.getCfg().getDouble("MaxDiscount", 0.3);
|
||||
int maxUses = instance.getCfg().getInt("MaxUses", -1);
|
||||
if (overrides != null) {
|
||||
for (final String override : overrides.getKeys(false)) {
|
||||
final ConfigurationSection item = this.getItem(recipe, override);
|
||||
if (item != null) {
|
||||
//Set whether trade is disabled and max discount
|
||||
disabled = item.getBoolean("Disabled", false);
|
||||
maxDiscount = item.getDouble("MaxDiscount", maxDiscount);
|
||||
maxUses = item.getInt("MaxUses", maxUses);
|
||||
|
||||
//Set 1st and 2nd ingredients
|
||||
setIngredient(item, recipe, "buy", "Item1");
|
||||
setIngredient(item, recipe, "buyB", "Item2");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Set max uses
|
||||
if(maxUses >= 0) {
|
||||
recipe.setInteger("maxUses", maxUses);
|
||||
} else {
|
||||
if(disabled) recipe.setInteger("maxUses", 0);
|
||||
else {
|
||||
int uses = 16;
|
||||
Material buyMaterial = recipe.getItemStack("buy").getType();
|
||||
Material sellMaterial = recipe.getItemStack("sell").getType();
|
||||
if (Arrays.asList(MAX_USES_12).contains(buyMaterial) || Arrays.asList(MAX_USES_12).contains(sellMaterial)) {
|
||||
uses = 12;
|
||||
} else if (Arrays.asList(MAX_USES_3).contains(buyMaterial) || Arrays.asList(MAX_USES_3).contains(sellMaterial)) {
|
||||
uses = 3;
|
||||
}
|
||||
recipe.setInteger("maxUses", uses);
|
||||
}
|
||||
}
|
||||
|
||||
//Set max discount
|
||||
if (maxDiscount >= 0.0 && maxDiscount <= 1.0) {
|
||||
if (finalValue < ingredientAmount * (1.0 - maxDiscount) && finalValue != ingredientAmount) {
|
||||
recipe.setFloat("priceMultiplier", ingredientAmount * (float) maxDiscount / valueModifier);
|
||||
} else {
|
||||
recipe.setFloat("priceMultiplier", priceMultiplier);
|
||||
}
|
||||
} else {
|
||||
recipe.setFloat("priceMultiplier", priceMultiplier);
|
||||
}
|
||||
/**
|
||||
* @param recipe The recipe to get the demand for
|
||||
* @return The current value of the demand for the given recipe
|
||||
*/
|
||||
private int getDemand(final RecipeWrapper recipe) {
|
||||
int demand = recipe.getDemand();
|
||||
int maxDemand = settings.fetchInt(recipe, "MaxDemand", -1);
|
||||
if(maxDemand < 0) maxDemand = 1000000;
|
||||
if(demand > maxDemand) {
|
||||
recipe.setDemand(maxDemand);
|
||||
return maxDemand;
|
||||
}
|
||||
return demand;
|
||||
}
|
||||
|
||||
//MaxDemand feature - limits demand-based price increases
|
||||
private void maxDemand(final Villager villager) {
|
||||
final NBTEntity villagerNBT = new NBTEntity(villager);
|
||||
final ConfigurationSection overrides = instance.getCfg().getConfigurationSection("Overrides");
|
||||
if (villagerNBT.hasKey("Offers")) {
|
||||
NBTCompoundList recipes = villagerNBT.getCompound("Offers").getCompoundList("Recipes");
|
||||
for (NBTCompound recipe : recipes) {
|
||||
final int demand = recipe.getInteger("demand");
|
||||
int maxDemand = instance.getCfg().getInt("MaxDemand", -1);
|
||||
if (overrides != null) {
|
||||
for (String override : overrides.getKeys(false)) {
|
||||
final ConfigurationSection item = this.getItem(recipe, override);
|
||||
if(item != null) {
|
||||
maxDemand = item.getInt("MaxDemand", maxDemand);
|
||||
break;
|
||||
/**
|
||||
* @param recipe The recipe to get the discount for
|
||||
* @param totalReputation The player's total reputation from a villager's gossips
|
||||
* @param hotvDiscount The total discount from the Hero of the Village effect
|
||||
* @return The total discount for the recipe, which is added to the base price to get the final price
|
||||
*/
|
||||
private int getDiscount(final RecipeWrapper recipe, int totalReputation, double hotvDiscount) {
|
||||
//Calculates the total discount
|
||||
int basePrice = getBasePrice(recipe);
|
||||
int demand = getDemand(recipe);
|
||||
float priceMultiplier = recipe.getPriceMultiplier();
|
||||
int discount = -(int)(totalReputation * priceMultiplier) - (int)(hotvDiscount * basePrice) + Math.max(0, (int)(demand * priceMultiplier * basePrice));
|
||||
|
||||
double maxDiscount = settings.fetchDouble(recipe, "MaxDiscount", 0.3); //0.1
|
||||
if(maxDiscount >= 0.0 && maxDiscount <= 1.0) {
|
||||
//Change the discount to the smaller MaxDiscount
|
||||
if(basePrice + discount < basePrice * (1.0 - maxDiscount)) {
|
||||
discount = -(int)(basePrice * maxDiscount);
|
||||
}
|
||||
} else if(maxDiscount > 1.0) {
|
||||
//Change the discount to the larger MaxDiscount
|
||||
//TODO: Allow for better fine-tuning
|
||||
discount = (int)(discount * maxDiscount);
|
||||
}
|
||||
return discount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param recipe The recipe to get the MaxUses for
|
||||
* @return The current maximum number of times a player can make a trade before the villager restocks
|
||||
*/
|
||||
private int getMaxUses(final RecipeWrapper recipe, final OfflinePlayer player) {
|
||||
int uses = recipe.getMaxUses();
|
||||
int maxUses = settings.fetchInt(recipe, "MaxUses", -1);
|
||||
boolean disabled = settings.fetchBoolean(recipe, "Disabled", false);
|
||||
|
||||
//Disables the trade if the player has an active cooldown for the trade
|
||||
final PlayerData playerData = instance.getPlayerData().get(player.getUniqueId());
|
||||
if(playerData != null && playerData.getTradingVillager() != null) {
|
||||
final ConfigurationSection overrides = instance.getCfg().getConfigurationSection("Overrides");
|
||||
if(overrides != null) {
|
||||
final String type = settings.getType(recipe.getItemStack("sell"), recipe.getItemStack("buy"), recipe.getItemStack("buyB"));
|
||||
final String global = instance.getCfg().getString("Cooldown", "0");
|
||||
final String local = overrides.getString(type+".Cooldown", global);
|
||||
if(type != null && !local.equals("0")) {
|
||||
if(playerData.getTradingCooldowns().containsKey(type)) {
|
||||
final Date now = Date.from(Instant.now());
|
||||
final Date lastTrade = Cooldown.parseTime(playerData.getTradingCooldowns().get(type));
|
||||
long cooldown = Cooldown.parseCooldown(local);
|
||||
if(lastTrade != null && (now.getTime()/1000L >= lastTrade.getTime()/1000L + cooldown)) {
|
||||
playerData.getTradingCooldowns().remove(type);
|
||||
} else {
|
||||
maxUses = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(maxDemand >= 0 && demand > maxDemand) {
|
||||
recipe.setInteger("demand", maxDemand);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(maxUses < 0) maxUses = uses;
|
||||
if(disabled) maxUses = 0;
|
||||
return maxUses;
|
||||
}
|
||||
|
||||
//Returns the price multiplier for a given trade
|
||||
private float getPriceMultiplier(final NBTCompound recipe) {
|
||||
float p = 0.05f;
|
||||
final Material type = recipe.getItemStack("sell").getType();
|
||||
for(int length = MATERIALS.length, i = 0; i < length; ++i) {
|
||||
if(type == MATERIALS[i]) {
|
||||
p = 0.2f;
|
||||
break;
|
||||
}
|
||||
/**
|
||||
* @param playerWrapper The wrapped player to check the hotv effect for
|
||||
* @return The Hero of the Village discount factor, adjusted by config
|
||||
*/
|
||||
private double getHotvDiscount(final PlayerWrapper playerWrapper) {
|
||||
final Player player = playerWrapper.getPlayer();
|
||||
if(player == null) return 0.0;
|
||||
|
||||
final PotionEffectType effectType = PotionEffectType.HERO_OF_THE_VILLAGE;
|
||||
if(!player.hasPotionEffect(effectType)) return 0.0;
|
||||
|
||||
final PotionEffect effect = player.getPotionEffect(effectType);
|
||||
if(effect == null) return 0.0;
|
||||
|
||||
//Calculates the discount factor from the player's current effect level or the defined maximum
|
||||
int heroLevel = effect.getAmplifier()+1;
|
||||
final int maxHeroLevel = instance.getCfg().getInt("MaxHeroLevel", -1);
|
||||
if(maxHeroLevel == 0 || heroLevel == 0) return 0.0;
|
||||
if(maxHeroLevel > 0 && heroLevel > maxHeroLevel) {
|
||||
heroLevel = maxHeroLevel;
|
||||
}
|
||||
return p;
|
||||
return 0.0625*(heroLevel-1) + 0.3;
|
||||
}
|
||||
|
||||
//Returns the configured settings for a trade
|
||||
private ConfigurationSection getItem(final NBTCompound recipe, final String k) {
|
||||
final ConfigurationSection item = instance.getCfg().getConfigurationSection("Overrides."+k);
|
||||
if(item == null) return null;
|
||||
|
||||
if(!k.contains("_")) {
|
||||
//Return the item if the item name is valid
|
||||
if(this.verify(recipe, Material.matchMaterial(k))) return item;
|
||||
return null;
|
||||
/**
|
||||
* @param item The config section that contains the settings for Item1, Item2, or Result items in the trade
|
||||
* @param ingredient The respective ingredient to change, based on config.yml
|
||||
*/
|
||||
private void setIngredient(final ConfigurationSection item, final IngredientWrapper ingredient) {
|
||||
if(item == null) return;
|
||||
ItemStack previous = ingredient.getItemStack();
|
||||
Material material = Material.matchMaterial(item.getString("Material", previous.getType().getKey().getKey()));
|
||||
if (material != null) {
|
||||
ingredient.setItemStack(new ItemStack(
|
||||
material,
|
||||
item.getInt("Amount", previous.getAmount())
|
||||
));
|
||||
}
|
||||
|
||||
final String[] words = k.split("_");
|
||||
try {
|
||||
//Return the enchanted book item if there's a number in the item name
|
||||
final int level = Integer.parseInt(words[words.length-1]);
|
||||
if(recipe.getItemStack("sell").getType() == Material.ENCHANTED_BOOK) {
|
||||
final EnchantmentStorageMeta meta = (EnchantmentStorageMeta) recipe.getItemStack("sell").getItemMeta();
|
||||
final Enchantment enchantment = EnchantmentWrapper.getByKey(NamespacedKey.minecraft(k.substring(0, k.lastIndexOf("_"))));
|
||||
if (meta == null || enchantment == null) return null;
|
||||
if (meta.hasStoredEnchant(enchantment) && meta.getStoredEnchantLevel(enchantment) == level) return item;
|
||||
}
|
||||
} catch(NumberFormatException e) {
|
||||
//Return the item if the item name is valid
|
||||
if(this.verify(recipe, Material.matchMaterial(k)))
|
||||
return item;
|
||||
return null;
|
||||
} catch(Exception e2) {
|
||||
//Send an error message
|
||||
Util.errorMsg(e2);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//Verifies that an item exists in the villager's trade
|
||||
private boolean verify(final NBTCompound recipe, final Material material) {
|
||||
return ((recipe.getItemStack("sell").getType() == material) || (recipe.getItemStack("buy").getType() == material));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
package com.pretzel.dev.villagertradelimiter.listeners;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.pretzel.dev.villagertradelimiter.VillagerTradeLimiter;
|
||||
import com.pretzel.dev.villagertradelimiter.data.Cooldown;
|
||||
import com.pretzel.dev.villagertradelimiter.data.PlayerData;
|
||||
import com.pretzel.dev.villagertradelimiter.settings.Settings;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Villager;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.VillagerAcquireTradeEvent;
|
||||
import org.bukkit.event.entity.VillagerCareerChangeEvent;
|
||||
import org.bukkit.event.entity.VillagerReplenishTradeEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.MerchantRecipe;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class VillagerListener implements Listener {
|
||||
private final VillagerTradeLimiter instance;
|
||||
private final Settings settings;
|
||||
|
||||
/**
|
||||
* @param instance The instance of VillagerTradeLimiter.java
|
||||
* @param settings The settings instance
|
||||
*/
|
||||
public VillagerListener(final VillagerTradeLimiter instance, final Settings settings) {
|
||||
this.instance = instance;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
/** Handles villager promotions */
|
||||
@EventHandler
|
||||
public void onVillagerPromotion(final VillagerAcquireTradeEvent event) {
|
||||
//Gets the items in the trade
|
||||
final MerchantRecipe recipe = event.getRecipe();
|
||||
List<ItemStack> items = recipe.getIngredients();
|
||||
items.add(recipe.getResult());
|
||||
|
||||
//Gets the disabled item list from config
|
||||
List<String> disabledItems = instance.getCfg().getStringList("DisableItems");
|
||||
|
||||
//Checks each item if it should be removed from the trade list
|
||||
for(ItemStack item : items) {
|
||||
for(String disabledItem : disabledItems) {
|
||||
if(disabledItem.equalsIgnoreCase(item.getType().name())) {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Handles villager profession change **/
|
||||
@EventHandler
|
||||
public void onVillagerChangeProfession(final VillagerCareerChangeEvent event) {
|
||||
//Gets the new profession
|
||||
final Villager.Profession profession = event.getProfession();
|
||||
|
||||
//Gets the disabled profession list from config
|
||||
List<String> disabledProfessions = instance.getCfg().getStringList("DisableProfessions");
|
||||
|
||||
//Changes the new profession to none if disabled in config
|
||||
for(String disabledProfession : disabledProfessions) {
|
||||
if(disabledProfession.equalsIgnoreCase(profession.name())) {
|
||||
event.setProfession(Villager.Profession.NONE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Handles villager restocks */
|
||||
@EventHandler
|
||||
public void onVillagerRestock(final VillagerReplenishTradeEvent event) {
|
||||
if(!(event.getEntity() instanceof Villager)) return;
|
||||
final Villager villager = (Villager)event.getEntity();
|
||||
if(settings.shouldSkipNPC(villager)) return; //Skips NPCs
|
||||
|
||||
//Get the items involved in the restock
|
||||
final MerchantRecipe recipe = event.getRecipe();
|
||||
final ItemStack result = recipe.getResult();
|
||||
ItemStack ingredient1 = Iterables.get(recipe.getIngredients(), 0, new ItemStack(Material.AIR));
|
||||
ItemStack ingredient2 = Iterables.get(recipe.getIngredients(), 1, new ItemStack(Material.AIR));
|
||||
final String type = settings.getType(result, ingredient1, ingredient2);
|
||||
|
||||
//Get the villager's data container
|
||||
final UUID uuid = villager.getUniqueId();
|
||||
final PlayerData villagerData = instance.getPlayerData().get(uuid);
|
||||
if(villagerData == null) return;
|
||||
|
||||
//Get the time of the last trade, restock cooldown setting, and now
|
||||
final String lastTradeStr = villagerData.getTradingCooldowns().get(type);
|
||||
if(lastTradeStr == null) return;
|
||||
|
||||
String cooldownStr = instance.getCfg().getString("Restock", "0");
|
||||
cooldownStr = instance.getCfg().getString("Overrides."+type+".Restock", cooldownStr);
|
||||
|
||||
final Date now = Date.from(Instant.now());
|
||||
final Date lastTrade = Cooldown.parseTime(lastTradeStr);
|
||||
if(lastTrade == null) return;
|
||||
final long cooldown = Cooldown.parseCooldown(cooldownStr);
|
||||
|
||||
//Cancel the event if there is an active restock cooldown, otherwise remove the restock cooldown
|
||||
if(now.getTime()/1000L >= lastTrade.getTime()/1000L + cooldown) {
|
||||
villagerData.getTradingCooldowns().remove(type);
|
||||
} else {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,839 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
|
||||
|
||||
/**
|
||||
* Base class representing NMS Compounds. For a standalone implementation check
|
||||
* {@link NBTContainer}
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
public class NBTCompound {
|
||||
|
||||
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
|
||||
private final Lock readLock = readWriteLock.readLock();
|
||||
private final Lock writeLock = readWriteLock.writeLock();
|
||||
|
||||
private String compundName;
|
||||
private NBTCompound parent;
|
||||
|
||||
protected NBTCompound(NBTCompound owner, String name) {
|
||||
this.compundName = name;
|
||||
this.parent = owner;
|
||||
}
|
||||
|
||||
protected Lock getReadLock() {
|
||||
return readLock;
|
||||
}
|
||||
|
||||
protected Lock getWriteLock() {
|
||||
return writeLock;
|
||||
}
|
||||
|
||||
protected void saveCompound() {
|
||||
if (parent != null)
|
||||
parent.saveCompound();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The Compound name
|
||||
*/
|
||||
public String getName() {
|
||||
return compundName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The NMS Compound behind this Object
|
||||
*/
|
||||
public Object getCompound() {
|
||||
return parent.getCompound();
|
||||
}
|
||||
|
||||
protected void setCompound(Object compound) {
|
||||
parent.setCompound(compound);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The parent Compound
|
||||
*/
|
||||
public NBTCompound getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges all data from comp into this compound. This is done in one action, so
|
||||
* it also works with Tiles/Entities
|
||||
*
|
||||
* @param comp
|
||||
*/
|
||||
public void mergeCompound(NBTCompound comp) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
NBTReflectionUtil.mergeOtherNBTCompound(this, comp);
|
||||
saveCompound();
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void setString(String key, String value) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_STRING, key, value);
|
||||
saveCompound();
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter
|
||||
*
|
||||
* @param key
|
||||
* @return The stored value or NMS fallback
|
||||
*/
|
||||
public String getString(String key) {
|
||||
try {
|
||||
readLock.lock();
|
||||
return (String) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_STRING, key);
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
protected String getContent(String key) {
|
||||
return NBTReflectionUtil.getContent(this, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void setInteger(String key, Integer value) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_INT, key, value);
|
||||
saveCompound();
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter
|
||||
*
|
||||
* @param key
|
||||
* @return The stored value or NMS fallback
|
||||
*/
|
||||
public Integer getInteger(String key) {
|
||||
try {
|
||||
readLock.lock();
|
||||
return (Integer) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_INT, key);
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void setDouble(String key, Double value) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_DOUBLE, key, value);
|
||||
saveCompound();
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter
|
||||
*
|
||||
* @param key
|
||||
* @return The stored value or NMS fallback
|
||||
*/
|
||||
public Double getDouble(String key) {
|
||||
try {
|
||||
readLock.lock();
|
||||
return (Double) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_DOUBLE, key);
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void setByte(String key, Byte value) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_BYTE, key, value);
|
||||
saveCompound();
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter
|
||||
*
|
||||
* @param key
|
||||
* @return The stored value or NMS fallback
|
||||
*/
|
||||
public Byte getByte(String key) {
|
||||
try {
|
||||
readLock.lock();
|
||||
return (Byte) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_BYTE, key);
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void setShort(String key, Short value) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_SHORT, key, value);
|
||||
saveCompound();
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter
|
||||
*
|
||||
* @param key
|
||||
* @return The stored value or NMS fallback
|
||||
*/
|
||||
public Short getShort(String key) {
|
||||
try {
|
||||
readLock.lock();
|
||||
return (Short) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_SHORT, key);
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void setLong(String key, Long value) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_LONG, key, value);
|
||||
saveCompound();
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter
|
||||
*
|
||||
* @param key
|
||||
* @return The stored value or NMS fallback
|
||||
*/
|
||||
public Long getLong(String key) {
|
||||
try {
|
||||
readLock.lock();
|
||||
return (Long) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_LONG, key);
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void setFloat(String key, Float value) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_FLOAT, key, value);
|
||||
saveCompound();
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter
|
||||
*
|
||||
* @param key
|
||||
* @return The stored value or NMS fallback
|
||||
*/
|
||||
public Float getFloat(String key) {
|
||||
try {
|
||||
readLock.lock();
|
||||
return (Float) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_FLOAT, key);
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void setByteArray(String key, byte[] value) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_BYTEARRAY, key, value);
|
||||
saveCompound();
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter
|
||||
*
|
||||
* @param key
|
||||
* @return The stored value or NMS fallback
|
||||
*/
|
||||
public byte[] getByteArray(String key) {
|
||||
try {
|
||||
readLock.lock();
|
||||
return (byte[]) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_BYTEARRAY, key);
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void setIntArray(String key, int[] value) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_INTARRAY, key, value);
|
||||
saveCompound();
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter
|
||||
*
|
||||
* @param key
|
||||
* @return The stored value or NMS fallback
|
||||
*/
|
||||
public int[] getIntArray(String key) {
|
||||
try {
|
||||
readLock.lock();
|
||||
return (int[]) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_INTARRAY, key);
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void setBoolean(String key, Boolean value) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_BOOLEAN, key, value);
|
||||
saveCompound();
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
protected void set(String key, Object val) {
|
||||
NBTReflectionUtil.set(this, key, val);
|
||||
saveCompound();
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter
|
||||
*
|
||||
* @param key
|
||||
* @return The stored value or NMS fallback
|
||||
*/
|
||||
public Boolean getBoolean(String key) {
|
||||
try {
|
||||
readLock.lock();
|
||||
return (Boolean) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_BOOLEAN, key);
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses Gson to store an {@link Serializable} Object
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void setObject(String key, Object value) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
NBTReflectionUtil.setObject(this, key, value);
|
||||
saveCompound();
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses Gson to retrieve a stored Object
|
||||
*
|
||||
* @param key
|
||||
* @param type Class of the Object
|
||||
* @return The created Object or null if empty
|
||||
*/
|
||||
public <T> T getObject(String key, Class<T> type) {
|
||||
try {
|
||||
readLock.lock();
|
||||
return NBTReflectionUtil.getObject(this, key, type);
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save an ItemStack as a compound under a given key
|
||||
*
|
||||
* @param key
|
||||
* @param item
|
||||
*/
|
||||
public void setItemStack(String key, ItemStack item) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
removeKey(key);
|
||||
addCompound(key).mergeCompound(NBTItem.convertItemtoNBT(item));
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an ItemStack that was saved at the given key
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public ItemStack getItemStack(String key) {
|
||||
try {
|
||||
readLock.lock();
|
||||
NBTCompound comp = getCompound(key);
|
||||
return NBTItem.convertNBTtoItem(comp);
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public void setUUID(String key, UUID value) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
NBTReflectionUtil.setData(this, ReflectionMethod.COMPOUND_SET_UUID, key, value);
|
||||
saveCompound();
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter
|
||||
*
|
||||
* @param key
|
||||
* @return The stored value or NMS fallback
|
||||
*/
|
||||
public UUID getUUID(String key) {
|
||||
try {
|
||||
readLock.lock();
|
||||
return (UUID) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_UUID, key);
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key
|
||||
* @return True if the key is set
|
||||
*/
|
||||
public Boolean hasKey(String key) {
|
||||
try {
|
||||
readLock.lock();
|
||||
Boolean b = (Boolean) NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_HAS_KEY, key);
|
||||
if (b == null)
|
||||
return false;
|
||||
return b;
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key Deletes the given Key
|
||||
*/
|
||||
public void removeKey(String key) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
NBTReflectionUtil.remove(this, key);
|
||||
saveCompound();
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Set of all stored Keys
|
||||
*/
|
||||
public Set<String> getKeys() {
|
||||
try {
|
||||
readLock.lock();
|
||||
return NBTReflectionUtil.getKeys(this);
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a subCompound, or returns it if already provided
|
||||
*
|
||||
* @param name Key to use
|
||||
* @return The subCompound Object
|
||||
*/
|
||||
public NBTCompound addCompound(String name) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
if (getType(name) == NBTType.NBTTagCompound)
|
||||
return getCompound(name);
|
||||
NBTReflectionUtil.addNBTTagCompound(this, name);
|
||||
NBTCompound comp = getCompound(name);
|
||||
if (comp == null)
|
||||
throw new NbtApiException("Error while adding Compound, got null!");
|
||||
saveCompound();
|
||||
return comp;
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return The Compound instance or null
|
||||
*/
|
||||
public NBTCompound getCompound(String name) {
|
||||
try {
|
||||
readLock.lock();
|
||||
if (getType(name) != NBTType.NBTTagCompound)
|
||||
return null;
|
||||
NBTCompound next = new NBTCompound(this, name);
|
||||
if (NBTReflectionUtil.valideCompound(next))
|
||||
return next;
|
||||
return null;
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The same as addCompound, just with a name that better reflects what it does
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public NBTCompound getOrCreateCompound(String name) {
|
||||
return addCompound(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return The retrieved String List
|
||||
*/
|
||||
public NBTList<String> getStringList(String name) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
NBTList<String> list = NBTReflectionUtil.getList(this, name, NBTType.NBTTagString, String.class);
|
||||
saveCompound();
|
||||
return list;
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return The retrieved Integer List
|
||||
*/
|
||||
public NBTList<Integer> getIntegerList(String name) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
NBTList<Integer> list = NBTReflectionUtil.getList(this, name, NBTType.NBTTagInt, Integer.class);
|
||||
saveCompound();
|
||||
return list;
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return The retrieved Float List
|
||||
*/
|
||||
public NBTList<Float> getFloatList(String name) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
NBTList<Float> list = NBTReflectionUtil.getList(this, name, NBTType.NBTTagFloat, Float.class);
|
||||
saveCompound();
|
||||
return list;
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return The retrieved Double List
|
||||
*/
|
||||
public NBTList<Double> getDoubleList(String name) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
NBTList<Double> list = NBTReflectionUtil.getList(this, name, NBTType.NBTTagDouble, Double.class);
|
||||
saveCompound();
|
||||
return list;
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return The retrieved Long List
|
||||
*/
|
||||
public NBTList<Long> getLongList(String name) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
NBTList<Long> list = NBTReflectionUtil.getList(this, name, NBTType.NBTTagLong, Long.class);
|
||||
saveCompound();
|
||||
return list;
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of the list, null if not a list
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public NBTType getListType(String name) {
|
||||
try {
|
||||
readLock.lock();
|
||||
if (getType(name) != NBTType.NBTTagList)
|
||||
return null;
|
||||
return NBTReflectionUtil.getListType(this, name);
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return The retrieved Compound List
|
||||
*/
|
||||
public NBTCompoundList getCompoundList(String name) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
NBTCompoundList list = (NBTCompoundList) NBTReflectionUtil.getList(this, name, NBTType.NBTTagCompound,
|
||||
NBTListCompound.class);
|
||||
saveCompound();
|
||||
return list;
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return The type of the given stored key or null
|
||||
*/
|
||||
public NBTType getType(String name) {
|
||||
try {
|
||||
readLock.lock();
|
||||
if (MinecraftVersion.getVersion() == MinecraftVersion.MC1_7_R4) {
|
||||
Object nbtbase = NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET, name);
|
||||
if(nbtbase == null)
|
||||
return null;
|
||||
return NBTType.valueOf((byte)ReflectionMethod.COMPOUND_OWN_TYPE.run(nbtbase));
|
||||
}
|
||||
Object o = NBTReflectionUtil.getData(this, ReflectionMethod.COMPOUND_GET_TYPE, name);
|
||||
if (o == null)
|
||||
return null;
|
||||
return NBTType.valueOf((byte) o);
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void writeCompound(OutputStream stream) {
|
||||
try {
|
||||
writeLock.lock();
|
||||
NBTReflectionUtil.writeApiNBT(this, stream);
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
/*
|
||||
* StringBuilder result = new StringBuilder(); for (String key : getKeys()) {
|
||||
* result.append(toString(key)); } return result.toString();
|
||||
*/
|
||||
return asNBTString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Just use toString()
|
||||
* @param key
|
||||
* @return A string representation of the given key
|
||||
*/
|
||||
@Deprecated
|
||||
public String toString(String key) {
|
||||
/*
|
||||
* StringBuilder result = new StringBuilder(); NBTCompound compound = this;
|
||||
* while (compound.getParent() != null) { result.append(" "); compound =
|
||||
* compound.getParent(); } if (this.getType(key) == NBTType.NBTTagCompound) {
|
||||
* return this.getCompound(key).toString(); } else { return result + "-" + key +
|
||||
* ": " + getContent(key) + System.lineSeparator(); }
|
||||
*/
|
||||
return asNBTString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Just use toString()
|
||||
* @return A {@link String} representation of the NBT in Mojang JSON. This is different from normal JSON!
|
||||
*/
|
||||
@Deprecated
|
||||
public String asNBTString() {
|
||||
try {
|
||||
readLock.lock();
|
||||
Object comp = NBTReflectionUtil.gettoCompount(getCompound(), this);
|
||||
if (comp == null)
|
||||
return "{}";
|
||||
return comp.toString();
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return toString().hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a deep compare to check if everything is the same
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if(obj instanceof NBTCompound) {
|
||||
NBTCompound other = (NBTCompound) obj;
|
||||
if(getKeys().equals(other.getKeys())) {
|
||||
for(String key : getKeys()) {
|
||||
if(!isEqual(this, other, key)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static boolean isEqual(NBTCompound compA, NBTCompound compB, String key) {
|
||||
if(compA.getType(key) != compB.getType(key))return false;
|
||||
switch(compA.getType(key)) {
|
||||
case NBTTagByte:
|
||||
return compA.getByte(key).equals(compB.getByte(key));
|
||||
case NBTTagByteArray:
|
||||
return Arrays.equals(compA.getByteArray(key), compB.getByteArray(key));
|
||||
case NBTTagCompound:
|
||||
return compA.getCompound(key).equals(compB.getCompound(key));
|
||||
case NBTTagDouble:
|
||||
return compA.getDouble(key).equals(compB.getDouble(key));
|
||||
case NBTTagEnd:
|
||||
return true; //??
|
||||
case NBTTagFloat:
|
||||
return compA.getFloat(key).equals(compB.getFloat(key));
|
||||
case NBTTagInt:
|
||||
return compA.getInteger(key).equals(compB.getInteger(key));
|
||||
case NBTTagIntArray:
|
||||
return Arrays.equals(compA.getIntArray(key), compB.getIntArray(key));
|
||||
case NBTTagList:
|
||||
return NBTReflectionUtil.getEntry(compA, key).toString().equals(NBTReflectionUtil.getEntry(compB, key).toString()); // Just string compare the 2 lists
|
||||
case NBTTagLong:
|
||||
return compA.getLong(key).equals(compB.getLong(key));
|
||||
case NBTTagShort:
|
||||
return compA.getShort(key).equals(compB.getShort(key));
|
||||
case NBTTagString:
|
||||
return compA.getString(key).equals(compB.getString(key));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ClassWrapper;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
|
||||
|
||||
/**
|
||||
* {@link NBTListCompound} implementation for NBTLists
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
public class NBTCompoundList extends NBTList<NBTListCompound> {
|
||||
|
||||
protected NBTCompoundList(NBTCompound owner, String name, NBTType type, Object list) {
|
||||
super(owner, name, type, list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new Compound to the end of the List and returns it.
|
||||
*
|
||||
* @return The added {@link NBTListCompound}
|
||||
*/
|
||||
public NBTListCompound addCompound() {
|
||||
return (NBTListCompound) addCompound(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a copy of the Compound to the end of the List and returns it.
|
||||
* When null is given, a new Compound will be created
|
||||
*
|
||||
* @param comp
|
||||
* @return
|
||||
*/
|
||||
public NBTCompound addCompound(NBTCompound comp) {
|
||||
try {
|
||||
Object compound = ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().newInstance();
|
||||
if (MinecraftVersion.getVersion().getVersionId() >= MinecraftVersion.MC1_14_R1.getVersionId()) {
|
||||
ReflectionMethod.LIST_ADD.run(listObject, size(), compound);
|
||||
} else {
|
||||
ReflectionMethod.LEGACY_LIST_ADD.run(listObject, compound);
|
||||
}
|
||||
getParent().saveCompound();
|
||||
NBTListCompound listcomp = new NBTListCompound(this, compound);
|
||||
if(comp != null){
|
||||
listcomp.mergeCompound(comp);
|
||||
}
|
||||
return listcomp;
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new Compound to the end of the List.
|
||||
*
|
||||
*
|
||||
* @deprecated Please use addCompound!
|
||||
* @param empty
|
||||
* @return True, if compound was added
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public boolean add(NBTListCompound empty) {
|
||||
return addCompound(empty) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, NBTListCompound element) {
|
||||
if (element != null) {
|
||||
throw new NbtApiException("You need to pass null! ListCompounds from other lists won't work.");
|
||||
}
|
||||
try {
|
||||
Object compound = ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().newInstance();
|
||||
if (MinecraftVersion.getVersion().getVersionId() >= MinecraftVersion.MC1_14_R1.getVersionId()) {
|
||||
ReflectionMethod.LIST_ADD.run(listObject, index, compound);
|
||||
} else {
|
||||
ReflectionMethod.LEGACY_LIST_ADD.run(listObject, compound);
|
||||
}
|
||||
super.getParent().saveCompound();
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTListCompound get(int index) {
|
||||
try {
|
||||
Object compound = ReflectionMethod.LIST_GET_COMPOUND.run(listObject, index);
|
||||
return new NBTListCompound(this, compound);
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBTListCompound set(int index, NBTListCompound element) {
|
||||
throw new NbtApiException("This method doesn't work in the ListCompound context.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object asTag(NBTListCompound object) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ClassWrapper;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ObjectCreator;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
|
||||
|
||||
/**
|
||||
* A Standalone {@link NBTCompound} implementation. All data is just kept inside
|
||||
* this Object.
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
public class NBTContainer extends NBTCompound {
|
||||
|
||||
private Object nbt;
|
||||
|
||||
/**
|
||||
* Creates an empty, standalone NBTCompound
|
||||
*/
|
||||
public NBTContainer() {
|
||||
super(null, null);
|
||||
nbt = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes in any NMS Compound to wrap it
|
||||
*
|
||||
* @param nbt
|
||||
*/
|
||||
public NBTContainer(Object nbt) {
|
||||
super(null, null);
|
||||
if (nbt == null) {
|
||||
throw new NullPointerException("The NBT-Object can't be null!");
|
||||
}
|
||||
if (!ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().isAssignableFrom(nbt.getClass())) {
|
||||
throw new NbtApiException("The object '" + nbt.getClass() + "' is not a valid NBT-Object!");
|
||||
}
|
||||
this.nbt = nbt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads in a NBT InputStream
|
||||
*
|
||||
* @param inputsteam
|
||||
*/
|
||||
public NBTContainer(InputStream inputsteam) {
|
||||
super(null, null);
|
||||
this.nbt = NBTReflectionUtil.readNBT(inputsteam);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses in a NBT String to a standalone {@link NBTCompound}. Can throw a
|
||||
* {@link NbtApiException} in case something goes wrong.
|
||||
*
|
||||
* @param nbtString
|
||||
*/
|
||||
public NBTContainer(String nbtString) {
|
||||
super(null, null);
|
||||
if (nbtString == null) {
|
||||
throw new NullPointerException("The String can't be null!");
|
||||
}
|
||||
try {
|
||||
nbt = ReflectionMethod.PARSE_NBT.run(null, nbtString);
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException("Unable to parse Malformed Json!", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCompound() {
|
||||
return nbt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCompound(Object tag) {
|
||||
nbt = tag;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ClassWrapper;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
|
||||
|
||||
/**
|
||||
* Double implementation for NBTLists
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
public class NBTDoubleList extends NBTList<Double> {
|
||||
|
||||
protected NBTDoubleList(NBTCompound owner, String name, NBTType type, Object list) {
|
||||
super(owner, name, type, list);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object asTag(Double object) {
|
||||
try {
|
||||
Constructor<?> con = ClassWrapper.NMS_NBTTAGDOUBLE.getClazz().getDeclaredConstructor(double.class);
|
||||
con.setAccessible(true);
|
||||
return con.newInstance(object);
|
||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
|
||||
| NoSuchMethodException | SecurityException e) {
|
||||
throw new NbtApiException("Error while wrapping the Object " + object + " to it's NMS object!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double get(int index) {
|
||||
try {
|
||||
Object obj = ReflectionMethod.LIST_GET.run(listObject, index);
|
||||
return Double.valueOf(obj.toString());
|
||||
} catch (NumberFormatException nf) {
|
||||
return 0d;
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms;
|
||||
|
||||
import org.bukkit.entity.Entity;
|
||||
|
||||
import de.tr7zw.annotations.FAUtil;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.annotations.AvailableSince;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.annotations.CheckUtil;
|
||||
|
||||
/**
|
||||
* NBT class to access vanilla tags from Entities. Entities don't support custom
|
||||
* tags. Use the NBTInjector for custom tags. Changes will be instantly applied
|
||||
* to the Entity, use the merge method to do many things at once.
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
public class NBTEntity extends NBTCompound {
|
||||
|
||||
private final Entity ent;
|
||||
|
||||
/**
|
||||
* @param entity Any valid Bukkit Entity
|
||||
*/
|
||||
public NBTEntity(Entity entity) {
|
||||
super(null, null);
|
||||
if (entity == null) {
|
||||
throw new NullPointerException("Entity can't be null!");
|
||||
}
|
||||
ent = entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCompound() {
|
||||
return NBTReflectionUtil.getEntityNBTTagCompound(NBTReflectionUtil.getNMSEntity(ent));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setCompound(Object compound) {
|
||||
NBTReflectionUtil.setEntityNBTTag(compound, NBTReflectionUtil.getNMSEntity(ent));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the NBTCompound used by spigots PersistentDataAPI. This method is only
|
||||
* available for 1.14+!
|
||||
*
|
||||
* @return NBTCompound containing the data of the PersistentDataAPI
|
||||
*/
|
||||
@AvailableSince(version = MinecraftVersion.MC1_14_R1)
|
||||
public NBTCompound getPersistentDataContainer() {
|
||||
FAUtil.check(this::getPersistentDataContainer, CheckUtil::isAvaliable);
|
||||
return new NBTPersistentDataContainer(ent.getPersistentDataContainer());
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ObjectCreator;
|
||||
|
||||
/**
|
||||
* {@link NBTCompound} implementation backed by a {@link File}
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
public class NBTFile extends NBTCompound {
|
||||
|
||||
private final File file;
|
||||
private Object nbt;
|
||||
|
||||
/**
|
||||
* Creates a NBTFile that uses @param file to store it's data. If this file
|
||||
* exists, the data will be loaded.
|
||||
*
|
||||
* @param file
|
||||
* @throws IOException
|
||||
*/
|
||||
public NBTFile(File file) throws IOException {
|
||||
super(null, null);
|
||||
if (file == null) {
|
||||
throw new NullPointerException("File can't be null!");
|
||||
}
|
||||
this.file = file;
|
||||
if (file.exists()) {
|
||||
FileInputStream inputsteam = new FileInputStream(file);
|
||||
nbt = NBTReflectionUtil.readNBT(inputsteam);
|
||||
} else {
|
||||
nbt = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the data to the file
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void save() throws IOException {
|
||||
try {
|
||||
getWriteLock().lock();
|
||||
if (!file.exists()) {
|
||||
file.getParentFile().mkdirs();
|
||||
if (!file.createNewFile())
|
||||
throw new IOException("Unable to create file at " + file.getAbsolutePath());
|
||||
}
|
||||
FileOutputStream outStream = new FileOutputStream(file);
|
||||
NBTReflectionUtil.writeNBT(nbt, outStream);
|
||||
} finally {
|
||||
getWriteLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The File used to store the data
|
||||
*/
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCompound() {
|
||||
return nbt;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setCompound(Object compound) {
|
||||
nbt = compound;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ClassWrapper;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
|
||||
|
||||
/**
|
||||
* Float implementation for NBTLists
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
public class NBTFloatList extends NBTList<Float> {
|
||||
|
||||
protected NBTFloatList(NBTCompound owner, String name, NBTType type, Object list) {
|
||||
super(owner, name, type, list);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object asTag(Float object) {
|
||||
try {
|
||||
Constructor<?> con = ClassWrapper.NMS_NBTTAGFLOAT.getClazz().getDeclaredConstructor(float.class);
|
||||
con.setAccessible(true);
|
||||
return con.newInstance(object);
|
||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
|
||||
| NoSuchMethodException | SecurityException e) {
|
||||
throw new NbtApiException("Error while wrapping the Object " + object + " to it's NMS object!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(int index) {
|
||||
try {
|
||||
Object obj = ReflectionMethod.LIST_GET.run(listObject, index);
|
||||
return Float.valueOf(obj.toString());
|
||||
} catch (NumberFormatException nf) {
|
||||
return 0f;
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ClassWrapper;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
|
||||
|
||||
/**
|
||||
* Integer implementation for NBTLists
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
public class NBTIntegerList extends NBTList<Integer> {
|
||||
|
||||
protected NBTIntegerList(NBTCompound owner, String name, NBTType type, Object list) {
|
||||
super(owner, name, type, list);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object asTag(Integer object) {
|
||||
try {
|
||||
Constructor<?> con = ClassWrapper.NMS_NBTTAGINT.getClazz().getDeclaredConstructor(int.class);
|
||||
con.setAccessible(true);
|
||||
return con.newInstance(object);
|
||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
|
||||
| NoSuchMethodException | SecurityException e) {
|
||||
throw new NbtApiException("Error while wrapping the Object " + object + " to it's NMS object!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer get(int index) {
|
||||
try {
|
||||
Object obj = ReflectionMethod.LIST_GET.run(listObject, index);
|
||||
return Integer.valueOf(obj.toString());
|
||||
} catch (NumberFormatException nf) {
|
||||
return 0;
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,176 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
|
||||
|
||||
/**
|
||||
* NBT class to access vanilla/custom tags on ItemStacks. This class doesn't
|
||||
* autosave to the Itemstack, use getItem to get the changed ItemStack
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
public class NBTItem extends NBTCompound {
|
||||
|
||||
private ItemStack bukkitItem;
|
||||
private boolean directApply;
|
||||
private ItemStack originalSrcStack = null;
|
||||
|
||||
/**
|
||||
* Constructor for NBTItems. The ItemStack will be cloned!
|
||||
*
|
||||
* @param item
|
||||
*/
|
||||
public NBTItem(ItemStack item) {
|
||||
this(item, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for NBTItems. The ItemStack will be cloned! If directApply is true,
|
||||
* all changed will be mapped to the original item. Changes to the NBTItem will overwrite changes done
|
||||
* to the original item in that case.
|
||||
*
|
||||
* @param item
|
||||
* @param directApply
|
||||
*/
|
||||
public NBTItem(ItemStack item, boolean directApply) {
|
||||
super(null, null);
|
||||
if (item == null || item.getType() == Material.AIR) {
|
||||
throw new NullPointerException("ItemStack can't be null/Air!");
|
||||
}
|
||||
this.directApply = directApply;
|
||||
bukkitItem = item.clone();
|
||||
if(directApply) {
|
||||
this.originalSrcStack = item;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCompound() {
|
||||
return NBTReflectionUtil.getItemRootNBTTagCompound(ReflectionMethod.ITEMSTACK_NMSCOPY.run(null, bukkitItem));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setCompound(Object compound) {
|
||||
Object stack = ReflectionMethod.ITEMSTACK_NMSCOPY.run(null, bukkitItem);
|
||||
ReflectionMethod.ITEMSTACK_SET_TAG.run(stack, compound);
|
||||
bukkitItem = (ItemStack) ReflectionMethod.ITEMSTACK_BUKKITMIRROR.run(null, stack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply stored NBT tags to the provided ItemStack.
|
||||
* <p>
|
||||
* Note: This will completely override current item's {@link ItemMeta}.
|
||||
* If you still want to keep the original item's NBT tags, see
|
||||
* {@link #mergeNBT(ItemStack)} and {@link #mergeCustomNBT(ItemStack)}.
|
||||
*
|
||||
* @param item ItemStack that should get the new NBT data
|
||||
*/
|
||||
public void applyNBT(ItemStack item) {
|
||||
if (item == null || item.getType() == Material.AIR) {
|
||||
throw new NullPointerException("ItemStack can't be null/Air!");
|
||||
}
|
||||
NBTItem nbti = new NBTItem(new ItemStack(item.getType()));
|
||||
nbti.mergeCompound(this);
|
||||
item.setItemMeta(nbti.getItem().getItemMeta());
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge all NBT tags to the provided ItemStack.
|
||||
*
|
||||
* @param item ItemStack that should get the new NBT data
|
||||
*/
|
||||
public void mergeNBT(ItemStack item) {
|
||||
NBTItem nbti = new NBTItem(item);
|
||||
nbti.mergeCompound(this);
|
||||
item.setItemMeta(nbti.getItem().getItemMeta());
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge only custom (non-vanilla) NBT tags to the provided ItemStack.
|
||||
*
|
||||
* @param item ItemStack that should get the new NBT data
|
||||
*/
|
||||
public void mergeCustomNBT(ItemStack item) {
|
||||
if (item == null || item.getType() == Material.AIR) {
|
||||
throw new NullPointerException("ItemStack can't be null/Air!");
|
||||
}
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
NBTReflectionUtil.getUnhandledNBTTags(meta).putAll(NBTReflectionUtil.getUnhandledNBTTags(bukkitItem.getItemMeta()));
|
||||
item.setItemMeta(meta);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* True, if the item has any tags now known for this item type.
|
||||
*
|
||||
* @return true when custom tags are present
|
||||
*/
|
||||
public boolean hasCustomNbtData() {
|
||||
ItemMeta meta = bukkitItem.getItemMeta();
|
||||
return !NBTReflectionUtil.getUnhandledNBTTags(meta).isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all custom (non-vanilla) NBT tags from the NBTItem.
|
||||
*/
|
||||
public void clearCustomNBT() {
|
||||
ItemMeta meta = bukkitItem.getItemMeta();
|
||||
NBTReflectionUtil.getUnhandledNBTTags(meta).clear();
|
||||
bukkitItem.setItemMeta(meta);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The modified ItemStack
|
||||
*/
|
||||
public ItemStack getItem() {
|
||||
return bukkitItem;
|
||||
}
|
||||
|
||||
protected void setItem(ItemStack item) {
|
||||
bukkitItem = item;
|
||||
}
|
||||
|
||||
/**
|
||||
* This may return true even when the NBT is empty.
|
||||
*
|
||||
* @return Does the ItemStack have a NBTCompound.
|
||||
*/
|
||||
public boolean hasNBTData() {
|
||||
return getCompound() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method that converts {@link ItemStack} to {@link NBTContainer} with
|
||||
* all it's data like Material, Damage, Amount and Tags.
|
||||
*
|
||||
* @param item
|
||||
* @return Standalone {@link NBTContainer} with the Item's data
|
||||
*/
|
||||
public static NBTContainer convertItemtoNBT(ItemStack item) {
|
||||
return NBTReflectionUtil.convertNMSItemtoNBTCompound(ReflectionMethod.ITEMSTACK_NMSCOPY.run(null, item));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to do the inverse to "convertItemtoNBT". Creates an
|
||||
* {@link ItemStack} using the {@link NBTCompound}
|
||||
*
|
||||
* @param comp
|
||||
* @return ItemStack using the {@link NBTCompound}'s data
|
||||
*/
|
||||
public static ItemStack convertNBTtoItem(NBTCompound comp) {
|
||||
return (ItemStack) ReflectionMethod.ITEMSTACK_BUKKITMIRROR.run(null,
|
||||
NBTReflectionUtil.convertNBTCompoundtoNMSItem(comp));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveCompound() {
|
||||
if(directApply) {
|
||||
applyNBT(originalSrcStack);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,429 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
|
||||
|
||||
/**
|
||||
* Abstract List implementation for ListCompounds
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public abstract class NBTList<T> implements List<T> {
|
||||
|
||||
private String listName;
|
||||
private NBTCompound parent;
|
||||
private NBTType type;
|
||||
protected Object listObject;
|
||||
|
||||
protected NBTList(NBTCompound owner, String name, NBTType type, Object list) {
|
||||
parent = owner;
|
||||
listName = name;
|
||||
this.type = type;
|
||||
this.listObject = list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Name of this list-compound
|
||||
*/
|
||||
public String getName() {
|
||||
return listName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The Compound's parent Object
|
||||
*/
|
||||
public NBTCompound getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
protected void save() {
|
||||
parent.set(listName, listObject);
|
||||
}
|
||||
|
||||
protected abstract Object asTag(T object);
|
||||
|
||||
@Override
|
||||
public boolean add(T element) {
|
||||
try {
|
||||
parent.getWriteLock().lock();
|
||||
if (MinecraftVersion.getVersion().getVersionId() >= MinecraftVersion.MC1_14_R1.getVersionId()) {
|
||||
ReflectionMethod.LIST_ADD.run(listObject, size(), asTag(element));
|
||||
} else {
|
||||
ReflectionMethod.LEGACY_LIST_ADD.run(listObject, asTag(element));
|
||||
}
|
||||
save();
|
||||
return true;
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException(ex);
|
||||
} finally {
|
||||
parent.getWriteLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, T element) {
|
||||
try {
|
||||
parent.getWriteLock().lock();
|
||||
if (MinecraftVersion.getVersion().getVersionId() >= MinecraftVersion.MC1_14_R1.getVersionId()) {
|
||||
ReflectionMethod.LIST_ADD.run(listObject, index, asTag(element));
|
||||
} else {
|
||||
ReflectionMethod.LEGACY_LIST_ADD.run(listObject, asTag(element));
|
||||
}
|
||||
save();
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException(ex);
|
||||
} finally {
|
||||
parent.getWriteLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T set(int index, T element) {
|
||||
try {
|
||||
parent.getWriteLock().lock();
|
||||
T prev = get(index);
|
||||
ReflectionMethod.LIST_SET.run(listObject, index, asTag(element));
|
||||
save();
|
||||
return prev;
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException(ex);
|
||||
} finally {
|
||||
parent.getWriteLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public T remove(int i) {
|
||||
try {
|
||||
parent.getWriteLock().lock();
|
||||
T old = get(i);
|
||||
ReflectionMethod.LIST_REMOVE_KEY.run(listObject, i);
|
||||
save();
|
||||
return old;
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException(ex);
|
||||
} finally {
|
||||
parent.getWriteLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public int size() {
|
||||
try {
|
||||
parent.getReadLock().lock();
|
||||
return (int) ReflectionMethod.LIST_SIZE.run(listObject);
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException(ex);
|
||||
} finally {
|
||||
parent.getReadLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The type that this list contains
|
||||
*/
|
||||
public NBTType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
while (!isEmpty()) {
|
||||
remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
try {
|
||||
parent.getReadLock().lock();
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (o.equals(get(i)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
parent.getReadLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(Object o) {
|
||||
try {
|
||||
parent.getReadLock().lock();
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (o.equals(get(i)))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
} finally {
|
||||
parent.getReadLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends T> c) {
|
||||
try {
|
||||
parent.getWriteLock().lock();
|
||||
int size = size();
|
||||
for (T ele : c) {
|
||||
add(ele);
|
||||
}
|
||||
return size != size();
|
||||
} finally {
|
||||
parent.getWriteLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, Collection<? extends T> c) {
|
||||
try {
|
||||
parent.getWriteLock().lock();
|
||||
int size = size();
|
||||
for (T ele : c) {
|
||||
add(index++, ele);
|
||||
}
|
||||
return size != size();
|
||||
} finally {
|
||||
parent.getWriteLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(Collection<?> c) {
|
||||
try {
|
||||
parent.getReadLock().lock();
|
||||
for (Object ele : c) {
|
||||
if (!contains(ele))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} finally {
|
||||
parent.getReadLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lastIndexOf(Object o) {
|
||||
try {
|
||||
parent.getReadLock().lock();
|
||||
int index = -1;
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (o.equals(get(i)))
|
||||
index = i;
|
||||
}
|
||||
return index;
|
||||
} finally {
|
||||
parent.getReadLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
try {
|
||||
parent.getWriteLock().lock();
|
||||
int size = size();
|
||||
for (Object obj : c) {
|
||||
remove(obj);
|
||||
}
|
||||
return size != size();
|
||||
} finally {
|
||||
parent.getWriteLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
try {
|
||||
parent.getWriteLock().lock();
|
||||
int size = size();
|
||||
for (Object obj : c) {
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (!obj.equals(get(i))) {
|
||||
remove(i--);
|
||||
}
|
||||
}
|
||||
}
|
||||
return size != size();
|
||||
} finally {
|
||||
parent.getWriteLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
try {
|
||||
parent.getWriteLock().lock();
|
||||
int size = size();
|
||||
int id = -1;
|
||||
while ((id = indexOf(o)) != -1) {
|
||||
remove(id);
|
||||
}
|
||||
return size != size();
|
||||
} finally {
|
||||
parent.getWriteLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return new Iterator<T>() {
|
||||
|
||||
private int index = -1;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return size() > index + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (!hasNext())
|
||||
throw new NoSuchElementException();
|
||||
return get(++index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
NBTList.this.remove(index);
|
||||
index--;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<T> listIterator() {
|
||||
return listIterator(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<T> listIterator(int startIndex) {
|
||||
final NBTList<T> list = this;
|
||||
return new ListIterator<T>() {
|
||||
|
||||
int index = startIndex - 1;
|
||||
|
||||
@Override
|
||||
public void add(T e) {
|
||||
list.add(index, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return size() > index + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPrevious() {
|
||||
return index >= 0 && index <= size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (!hasNext())
|
||||
throw new NoSuchElementException();
|
||||
return get(++index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextIndex() {
|
||||
return index + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T previous() {
|
||||
if (!hasPrevious())
|
||||
throw new NoSuchElementException("Id: " + (index - 1));
|
||||
return get(index--);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int previousIndex() {
|
||||
return index - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
list.remove(index);
|
||||
index--;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(T e) {
|
||||
list.set(index, e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
try {
|
||||
parent.getReadLock().lock();
|
||||
Object[] ar = new Object[size()];
|
||||
for (int i = 0; i < size(); i++)
|
||||
ar[i] = get(i);
|
||||
return ar;
|
||||
} finally {
|
||||
parent.getReadLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <E> E[] toArray(E[] a) {
|
||||
try {
|
||||
parent.getReadLock().lock();
|
||||
E[] ar = Arrays.copyOf(a, size());
|
||||
Arrays.fill(ar, null);
|
||||
Class<?> arrayclass = a.getClass().getComponentType();
|
||||
for (int i = 0; i < size(); i++) {
|
||||
T obj = get(i);
|
||||
if (arrayclass.isInstance(obj)) {
|
||||
ar[i] = (E) get(i);
|
||||
} else {
|
||||
throw new ArrayStoreException("The array does not match the objects stored in the List.");
|
||||
}
|
||||
}
|
||||
return ar;
|
||||
} finally {
|
||||
parent.getReadLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> subList(int fromIndex, int toIndex) {
|
||||
try {
|
||||
parent.getReadLock().lock();
|
||||
ArrayList<T> list = new ArrayList<>();
|
||||
for (int i = fromIndex; i < toIndex; i++)
|
||||
list.add(get(i));
|
||||
return list;
|
||||
} finally {
|
||||
parent.getReadLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
parent.getReadLock().lock();
|
||||
return listObject.toString();
|
||||
} finally {
|
||||
parent.getReadLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms;
|
||||
|
||||
/**
|
||||
* Cut down version of the {@link NBTCompound} for inside
|
||||
* {@link NBTCompoundList} This Compound implementation is missing the ability
|
||||
* for further subCompounds and Lists. This class probably will change in the
|
||||
* future
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
public class NBTListCompound extends NBTCompound {
|
||||
|
||||
private NBTList<?> owner;
|
||||
private Object compound;
|
||||
|
||||
protected NBTListCompound(NBTList<?> parent, Object obj) {
|
||||
super(null, null);
|
||||
owner = parent;
|
||||
compound = obj;
|
||||
}
|
||||
|
||||
public NBTList<?> getListParent() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCompound() {
|
||||
return compound;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setCompound(Object compound) {
|
||||
this.compound = compound;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveCompound() {
|
||||
owner.save();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ClassWrapper;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
|
||||
|
||||
/**
|
||||
* Long implementation for NBTLists
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
public class NBTLongList extends NBTList<Long> {
|
||||
|
||||
protected NBTLongList(NBTCompound owner, String name, NBTType type, Object list) {
|
||||
super(owner, name, type, list);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object asTag(Long object) {
|
||||
try {
|
||||
Constructor<?> con = ClassWrapper.NMS_NBTTAGLONG.getClazz().getDeclaredConstructor(long.class);
|
||||
con.setAccessible(true);
|
||||
return con.newInstance(object);
|
||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
|
||||
| NoSuchMethodException | SecurityException e) {
|
||||
throw new NbtApiException("Error while wrapping the Object " + object + " to it's NMS object!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long get(int index) {
|
||||
try {
|
||||
Object obj = ReflectionMethod.LIST_GET.run(listObject, index);
|
||||
return Long.valueOf(obj.toString().replace("L", ""));
|
||||
} catch (NumberFormatException nf) {
|
||||
return 0l;
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.bukkit.persistence.PersistentDataContainer;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
|
||||
|
||||
public class NBTPersistentDataContainer extends NBTCompound {
|
||||
|
||||
private final PersistentDataContainer container;
|
||||
|
||||
protected NBTPersistentDataContainer(PersistentDataContainer container) {
|
||||
super(null, null);
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCompound() {
|
||||
return ReflectionMethod.CRAFT_PERSISTENT_DATA_CONTAINER_TO_TAG.run(container);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setCompound(Object compound) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<Object, Object> map = (Map<Object, Object>) ReflectionMethod.CRAFT_PERSISTENT_DATA_CONTAINER_GET_MAP.run(container);
|
||||
map.clear();
|
||||
ReflectionMethod.CRAFT_PERSISTENT_DATA_CONTAINER_PUT_ALL.run(container, compound);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,625 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.GsonWrapper;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ClassWrapper;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ObjectCreator;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
|
||||
|
||||
/**
|
||||
* Utility class for translating NBTApi calls to reflections into NMS code All
|
||||
* methods are allowed to throw {@link NbtApiException}
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
public class NBTReflectionUtil {
|
||||
|
||||
private static Field field_unhandledTags = null;
|
||||
|
||||
static {
|
||||
try {
|
||||
field_unhandledTags = ClassWrapper.CRAFT_METAITEM.getClazz().getDeclaredField("unhandledTags");
|
||||
field_unhandledTags.setAccessible(true);
|
||||
} catch (NoSuchFieldException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hidden constructor
|
||||
*/
|
||||
private NBTReflectionUtil() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the NMS Entity for a given Bukkit Entity
|
||||
*
|
||||
* @param entity Bukkit Entity
|
||||
* @return NMS Entity
|
||||
*/
|
||||
public static Object getNMSEntity(Entity entity) {
|
||||
try {
|
||||
return ReflectionMethod.CRAFT_ENTITY_GET_HANDLE.run(ClassWrapper.CRAFT_ENTITY.getClazz().cast(entity));
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Exception while getting the NMS Entity from a Bukkit Entity!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads in a InputStream as NMS Compound
|
||||
*
|
||||
* @param stream InputStream of any NBT file
|
||||
* @return NMS Compound
|
||||
*/
|
||||
public static Object readNBT(InputStream stream) {
|
||||
try {
|
||||
return ReflectionMethod.NBTFILE_READ.run(null, stream);
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Exception while reading a NBT File!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a NMS Compound to an OutputStream
|
||||
*
|
||||
* @param nbt NMS Compound
|
||||
* @param stream Stream to write to
|
||||
* @return ???
|
||||
*/
|
||||
public static Object writeNBT(Object nbt, OutputStream stream) {
|
||||
try {
|
||||
return ReflectionMethod.NBTFILE_WRITE.run(null, nbt, stream);
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Exception while writing NBT!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a Compound to an OutputStream
|
||||
*
|
||||
* @param comp Compound
|
||||
* @param stream Stream to write to
|
||||
*/
|
||||
public static void writeApiNBT(NBTCompound comp, OutputStream stream) {
|
||||
try {
|
||||
Object nbttag = comp.getCompound();
|
||||
if (nbttag == null) {
|
||||
nbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
if (!valideCompound(comp))
|
||||
return;
|
||||
Object workingtag = gettoCompount(nbttag, comp);
|
||||
ReflectionMethod.NBTFILE_WRITE.run(null, workingtag, stream);
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Exception while writing NBT!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates getOrCreateTag. If an Item doesn't yet have a Tag, it will return a
|
||||
* new empty tag.
|
||||
*
|
||||
* @param nmsitem
|
||||
* @return NMS Compound
|
||||
*/
|
||||
public static Object getItemRootNBTTagCompound(Object nmsitem) {
|
||||
try {
|
||||
Object answer = ReflectionMethod.NMSITEM_GETTAG.run(nmsitem);
|
||||
return answer != null ? answer : ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Exception while getting an Itemstack's NBTCompound!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts {@link NBTCompound} to NMS ItemStacks
|
||||
*
|
||||
* @param nbtcompound Any valid {@link NBTCompound}
|
||||
* @return NMS ItemStack
|
||||
*/
|
||||
public static Object convertNBTCompoundtoNMSItem(NBTCompound nbtcompound) {
|
||||
try {
|
||||
Object nmsComp = gettoCompount(nbtcompound.getCompound(), nbtcompound);
|
||||
if (MinecraftVersion.getVersion().getVersionId() >= MinecraftVersion.MC1_11_R1.getVersionId()) {
|
||||
return ObjectCreator.NMS_COMPOUNDFROMITEM.getInstance(nmsComp);
|
||||
} else {
|
||||
return ReflectionMethod.NMSITEM_CREATESTACK.run(null, nmsComp);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Exception while converting NBTCompound to NMS ItemStack!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts NMS ItemStacks to {@link NBTContainer}
|
||||
*
|
||||
* @param nmsitem NMS ItemStack
|
||||
* @return {@link NBTContainer} with all the data
|
||||
*/
|
||||
public static NBTContainer convertNMSItemtoNBTCompound(Object nmsitem) {
|
||||
try {
|
||||
Object answer = ReflectionMethod.NMSITEM_SAVE.run(nmsitem, ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance());
|
||||
return new NBTContainer(answer);
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Exception while converting NMS ItemStack to NBTCompound!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a live copy of non-vanilla NBT tags.
|
||||
*
|
||||
* @param meta ItemMeta from which tags should be retrieved
|
||||
* @return Map containing unhandled (custom) NBT tags
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Map<String, Object> getUnhandledNBTTags(ItemMeta meta) {
|
||||
try {
|
||||
return (Map<String, Object>) field_unhandledTags.get(meta);
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Exception while getting unhandled tags from ItemMeta!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Vanilla NBT Compound from a given NMS Entity
|
||||
*
|
||||
* @param nmsEntity
|
||||
* @return NMS NBT Compound
|
||||
*/
|
||||
public static Object getEntityNBTTagCompound(Object nmsEntity) {
|
||||
try {
|
||||
Object nbt = ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().newInstance();
|
||||
Object answer = ReflectionMethod.NMS_ENTITY_GET_NBT.run(nmsEntity, nbt);
|
||||
if (answer == null)
|
||||
answer = nbt;
|
||||
return answer;
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Exception while getting NBTCompound from NMS Entity!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all Vanilla tags from a NMS Compound into a NMS Entity
|
||||
*
|
||||
* @param nbtTag
|
||||
* @param nmsEntity
|
||||
* @return The NMS Entity
|
||||
*/
|
||||
public static Object setEntityNBTTag(Object nbtTag, Object nmsEntity) {
|
||||
try {
|
||||
ReflectionMethod.NMS_ENTITY_SET_NBT.run(nmsEntity, nbtTag);
|
||||
return nmsEntity;
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException("Exception while setting the NBTCompound of an Entity", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the NMS Compound from a given TileEntity
|
||||
*
|
||||
* @param tile
|
||||
* @return NMS Compound with the Vanilla data
|
||||
*/
|
||||
public static Object getTileEntityNBTTagCompound(BlockState tile) {
|
||||
try {
|
||||
Object cworld = ClassWrapper.CRAFT_WORLD.getClazz().cast(tile.getWorld());
|
||||
Object nmsworld = ReflectionMethod.CRAFT_WORLD_GET_HANDLE.run(cworld);
|
||||
Object o = null;
|
||||
if(MinecraftVersion.getVersion() == MinecraftVersion.MC1_7_R4) {
|
||||
o = ReflectionMethod.NMS_WORLD_GET_TILEENTITY_1_7_10.run(nmsworld, tile.getX(), tile.getY(), tile.getZ());
|
||||
}else {
|
||||
Object pos = ObjectCreator.NMS_BLOCKPOSITION.getInstance(tile.getX(), tile.getY(), tile.getZ());
|
||||
o = ReflectionMethod.NMS_WORLD_GET_TILEENTITY.run(nmsworld, pos);
|
||||
}
|
||||
|
||||
Object answer = null;
|
||||
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_18_R1)) {
|
||||
answer = ReflectionMethod.TILEENTITY_GET_NBT_1181.run(o);
|
||||
} else {
|
||||
answer = ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().newInstance();
|
||||
ReflectionMethod.TILEENTITY_GET_NBT.run(o, answer);
|
||||
}
|
||||
if (answer == null) {
|
||||
throw new NbtApiException("Unable to get NBTCompound from TileEntity! " + tile + " " + o);
|
||||
}
|
||||
return answer;
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Exception while getting NBTCompound from TileEntity!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets Vanilla tags from a NMS Compound to a TileEntity
|
||||
*
|
||||
* @param tile
|
||||
* @param comp
|
||||
*/
|
||||
public static void setTileEntityNBTTagCompound(BlockState tile, Object comp) {
|
||||
try {
|
||||
Object cworld = ClassWrapper.CRAFT_WORLD.getClazz().cast(tile.getWorld());
|
||||
Object nmsworld = ReflectionMethod.CRAFT_WORLD_GET_HANDLE.run(cworld);
|
||||
Object o = null;
|
||||
if(MinecraftVersion.getVersion() == MinecraftVersion.MC1_7_R4) {
|
||||
o = ReflectionMethod.NMS_WORLD_GET_TILEENTITY_1_7_10.run(nmsworld, tile.getX(), tile.getY(), tile.getZ());
|
||||
}else {
|
||||
Object pos = ObjectCreator.NMS_BLOCKPOSITION.getInstance(tile.getX(), tile.getY(), tile.getZ());
|
||||
o = ReflectionMethod.NMS_WORLD_GET_TILEENTITY.run(nmsworld, pos);
|
||||
}
|
||||
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_17_R1)) {
|
||||
ReflectionMethod.TILEENTITY_SET_NBT.run(o, comp);
|
||||
}else if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_16_R1)) {
|
||||
Object blockData = ReflectionMethod.TILEENTITY_GET_BLOCKDATA.run(o);
|
||||
ReflectionMethod.TILEENTITY_SET_NBT_LEGACY1161.run(o, blockData, comp);
|
||||
}else {
|
||||
ReflectionMethod.TILEENTITY_SET_NBT_LEGACY1151.run(o, comp);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Exception while setting NBTData for a TileEntity!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the subCompound with a given name from a NMS Compound
|
||||
*
|
||||
* @param compound
|
||||
* @param name
|
||||
* @return NMS Compound or null
|
||||
*/
|
||||
public static Object getSubNBTTagCompound(Object compound, String name) {
|
||||
try {
|
||||
if ((boolean) ReflectionMethod.COMPOUND_HAS_KEY.run(compound, name)) {
|
||||
return ReflectionMethod.COMPOUND_GET_COMPOUND.run(compound, name);
|
||||
} else {
|
||||
throw new NbtApiException("Tried getting invalide compound '" + name + "' from '" + compound + "'!");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Exception while getting NBT subcompounds!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a subCompound with a given name in the given NMS Compound
|
||||
*
|
||||
* @param comp
|
||||
* @param name
|
||||
*/
|
||||
public static void addNBTTagCompound(NBTCompound comp, String name) {
|
||||
if (name == null) {
|
||||
remove(comp, name);
|
||||
return;
|
||||
}
|
||||
Object nbttag = comp.getCompound();
|
||||
if (nbttag == null) {
|
||||
nbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
if (!valideCompound(comp)) {
|
||||
return;
|
||||
}
|
||||
Object workingtag = gettoCompount(nbttag, comp);
|
||||
try {
|
||||
ReflectionMethod.COMPOUND_SET.run(workingtag, name,
|
||||
ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz().newInstance());
|
||||
comp.setCompound(nbttag);
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Exception while adding a Compound!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the Compound is correctly linked to it's roots
|
||||
*
|
||||
* @param comp
|
||||
* @return true if this is a valide Compound, else false
|
||||
*/
|
||||
public static Boolean valideCompound(NBTCompound comp) {
|
||||
Object root = comp.getCompound();
|
||||
if (root == null) {
|
||||
root = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
return (gettoCompount(root, comp)) != null;
|
||||
}
|
||||
|
||||
protected static Object gettoCompount(Object nbttag, NBTCompound comp) {
|
||||
Deque<String> structure = new ArrayDeque<>();
|
||||
while (comp.getParent() != null) {
|
||||
structure.add(comp.getName());
|
||||
comp = comp.getParent();
|
||||
}
|
||||
while (!structure.isEmpty()) {
|
||||
String target = structure.pollLast();
|
||||
nbttag = getSubNBTTagCompound(nbttag, target);
|
||||
if (nbttag == null) {
|
||||
throw new NbtApiException("Unable to find tag '" + target + "' in " + nbttag);
|
||||
}
|
||||
}
|
||||
return nbttag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the second {@link NBTCompound} into the first one
|
||||
*
|
||||
* @param comp Target for the merge
|
||||
* @param nbtcompoundSrc Data to merge
|
||||
*/
|
||||
public static void mergeOtherNBTCompound(NBTCompound comp, NBTCompound nbtcompoundSrc) {
|
||||
Object rootnbttag = comp.getCompound();
|
||||
if (rootnbttag == null) {
|
||||
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
if (!valideCompound(comp))
|
||||
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
|
||||
Object workingtag = gettoCompount(rootnbttag, comp);
|
||||
Object rootnbttagSrc = nbtcompoundSrc.getCompound();
|
||||
if (rootnbttagSrc == null) {
|
||||
rootnbttagSrc = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
if (!valideCompound(nbtcompoundSrc))
|
||||
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
|
||||
Object workingtagSrc = gettoCompount(rootnbttagSrc, nbtcompoundSrc);
|
||||
try {
|
||||
ReflectionMethod.COMPOUND_MERGE.run(workingtag, workingtagSrc);
|
||||
comp.setCompound(rootnbttag);
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Exception while merging two NBTCompounds!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content for a given key inside a Compound
|
||||
*
|
||||
* @param comp
|
||||
* @param key
|
||||
* @return Content saved under this key
|
||||
*/
|
||||
public static String getContent(NBTCompound comp, String key) {
|
||||
Object rootnbttag = comp.getCompound();
|
||||
if (rootnbttag == null) {
|
||||
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
if (!valideCompound(comp))
|
||||
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
|
||||
Object workingtag = gettoCompount(rootnbttag, comp);
|
||||
try {
|
||||
return ReflectionMethod.COMPOUND_GET.run(workingtag, key).toString();
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Exception while getting the Content for key '" + key + "'!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a key in a {@link NBTCompound} to a given value
|
||||
*
|
||||
* @param comp
|
||||
* @param key
|
||||
* @param val
|
||||
*/
|
||||
public static void set(NBTCompound comp, String key, Object val) {
|
||||
if (val == null) {
|
||||
remove(comp, key);
|
||||
return;
|
||||
}
|
||||
Object rootnbttag = comp.getCompound();
|
||||
if (rootnbttag == null) {
|
||||
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
if (!valideCompound(comp)) {
|
||||
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
|
||||
}
|
||||
Object workingtag = gettoCompount(rootnbttag, comp);
|
||||
try {
|
||||
ReflectionMethod.COMPOUND_SET.run(workingtag, key, val);
|
||||
comp.setCompound(rootnbttag);
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Exception while setting key '" + key + "' to '" + val + "'!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the List saved with a given key.
|
||||
*
|
||||
* @param comp
|
||||
* @param key
|
||||
* @param type
|
||||
* @param clazz
|
||||
* @return The list at that key. Null if it's an invalide type
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> NBTList<T> getList(NBTCompound comp, String key, NBTType type, Class<T> clazz) {
|
||||
Object rootnbttag = comp.getCompound();
|
||||
if (rootnbttag == null) {
|
||||
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
if (!valideCompound(comp))
|
||||
return null;
|
||||
Object workingtag = gettoCompount(rootnbttag, comp);
|
||||
try {
|
||||
Object nbt = ReflectionMethod.COMPOUND_GET_LIST.run(workingtag, key, type.getId());
|
||||
if (clazz == String.class) {
|
||||
return (NBTList<T>) new NBTStringList(comp, key, type, nbt);
|
||||
} else if (clazz == NBTListCompound.class) {
|
||||
return (NBTList<T>) new NBTCompoundList(comp, key, type, nbt);
|
||||
} else if (clazz == Integer.class) {
|
||||
return (NBTList<T>) new NBTIntegerList(comp, key, type, nbt);
|
||||
} else if (clazz == Float.class) {
|
||||
return (NBTList<T>) new NBTFloatList(comp, key, type, nbt);
|
||||
} else if (clazz == Double.class) {
|
||||
return (NBTList<T>) new NBTDoubleList(comp, key, type, nbt);
|
||||
} else if (clazz == Long.class) {
|
||||
return (NBTList<T>) new NBTLongList(comp, key, type, nbt);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException("Exception while getting a list with the type '" + type + "'!", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static NBTType getListType(NBTCompound comp, String key) {
|
||||
Object rootnbttag = comp.getCompound();
|
||||
if (rootnbttag == null) {
|
||||
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
if (!valideCompound(comp))
|
||||
return null;
|
||||
Object workingtag = gettoCompount(rootnbttag, comp);
|
||||
try {
|
||||
Object nbt = ReflectionMethod.COMPOUND_GET.run(workingtag, key);
|
||||
String fieldname = "type";
|
||||
if(MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_17_R1)) {
|
||||
fieldname = "w";
|
||||
}
|
||||
Field f = nbt.getClass().getDeclaredField(fieldname);
|
||||
f.setAccessible(true);
|
||||
return NBTType.valueOf(f.getByte(nbt));
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException("Exception while getting the list type!", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static Object getEntry(NBTCompound comp, String key) {
|
||||
Object rootnbttag = comp.getCompound();
|
||||
if (rootnbttag == null) {
|
||||
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
if (!valideCompound(comp))
|
||||
return null;
|
||||
Object workingtag = gettoCompount(rootnbttag, comp);
|
||||
try {
|
||||
Object nbt = ReflectionMethod.COMPOUND_GET.run(workingtag, key);
|
||||
return nbt;
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException("Exception while getting an Entry!", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses Gson to set a {@link Serializable} value in a Compound
|
||||
*
|
||||
* @param comp
|
||||
* @param key
|
||||
* @param value
|
||||
*/
|
||||
public static void setObject(NBTCompound comp, String key, Object value) {
|
||||
if (!MinecraftVersion.hasGsonSupport())
|
||||
return;
|
||||
try {
|
||||
String json = GsonWrapper.getString(value);
|
||||
setData(comp, ReflectionMethod.COMPOUND_SET_STRING, key, json);
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException("Exception while setting the Object '" + value + "'!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses Gson to load back a {@link Serializable} object from the Compound
|
||||
*
|
||||
* @param comp
|
||||
* @param key
|
||||
* @param type
|
||||
* @return The loaded Object or null, if not found
|
||||
*/
|
||||
public static <T> T getObject(NBTCompound comp, String key, Class<T> type) {
|
||||
if (!MinecraftVersion.hasGsonSupport())
|
||||
return null;
|
||||
String json = (String) getData(comp, ReflectionMethod.COMPOUND_GET_STRING, key);
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
return GsonWrapper.deserializeJson(json, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given key
|
||||
*
|
||||
* @param comp
|
||||
* @param key
|
||||
*/
|
||||
public static void remove(NBTCompound comp, String key) {
|
||||
Object rootnbttag = comp.getCompound();
|
||||
if (rootnbttag == null) {
|
||||
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
if (!valideCompound(comp))
|
||||
return;
|
||||
Object workingtag = gettoCompount(rootnbttag, comp);
|
||||
ReflectionMethod.COMPOUND_REMOVE_KEY.run(workingtag, key);
|
||||
comp.setCompound(rootnbttag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Keyset inside this Compound
|
||||
*
|
||||
* @param comp
|
||||
* @return Set of all keys
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Set<String> getKeys(NBTCompound comp) {
|
||||
Object rootnbttag = comp.getCompound();
|
||||
if (rootnbttag == null) {
|
||||
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
if (!valideCompound(comp))
|
||||
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
|
||||
Object workingtag = gettoCompount(rootnbttag, comp);
|
||||
return (Set<String>) ReflectionMethod.COMPOUND_GET_KEYS.run(workingtag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets data inside the Compound
|
||||
*
|
||||
* @param comp
|
||||
* @param type
|
||||
* @param key
|
||||
* @param data
|
||||
*/
|
||||
public static void setData(NBTCompound comp, ReflectionMethod type, String key, Object data) {
|
||||
if (data == null) {
|
||||
remove(comp, key);
|
||||
return;
|
||||
}
|
||||
Object rootnbttag = comp.getCompound();
|
||||
if (rootnbttag == null) {
|
||||
rootnbttag = ObjectCreator.NMS_NBTTAGCOMPOUND.getInstance();
|
||||
}
|
||||
if (!valideCompound(comp))
|
||||
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
|
||||
Object workingtag = gettoCompount(rootnbttag, comp);
|
||||
type.run(workingtag, key, data);
|
||||
comp.setCompound(rootnbttag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets data from the Compound
|
||||
*
|
||||
* @param comp
|
||||
* @param type
|
||||
* @param key
|
||||
* @return The value or default fallback from NMS
|
||||
*/
|
||||
public static Object getData(NBTCompound comp, ReflectionMethod type, String key) {
|
||||
Object rootnbttag = comp.getCompound();
|
||||
if (rootnbttag == null) {
|
||||
return null;
|
||||
}
|
||||
if (!valideCompound(comp))
|
||||
throw new NbtApiException("The Compound wasn't able to be linked back to the root!");
|
||||
Object workingtag = gettoCompount(rootnbttag, comp);
|
||||
return type.run(workingtag, key);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ClassWrapper;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings.ReflectionMethod;
|
||||
|
||||
/**
|
||||
* String implementation for NBTLists
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
public class NBTStringList extends NBTList<String> {
|
||||
|
||||
protected NBTStringList(NBTCompound owner, String name, NBTType type, Object list) {
|
||||
super(owner, name, type, list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(int index) {
|
||||
try {
|
||||
return (String) ReflectionMethod.LIST_GET_STRING.run(listObject, index);
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object asTag(String object) {
|
||||
try {
|
||||
Constructor<?> con = ClassWrapper.NMS_NBTTAGSTRING.getClazz().getDeclaredConstructor(String.class);
|
||||
con.setAccessible(true);
|
||||
return con.newInstance(object);
|
||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
|
||||
| NoSuchMethodException | SecurityException e) {
|
||||
throw new NbtApiException("Error while wrapping the Object " + object + " to it's NMS object!", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms;
|
||||
|
||||
import org.bukkit.block.BlockState;
|
||||
|
||||
import de.tr7zw.annotations.FAUtil;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.annotations.AvailableSince;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.annotations.CheckUtil;
|
||||
|
||||
/**
|
||||
* NBT class to access vanilla tags from TileEntities. TileEntities don't
|
||||
* support custom tags. Use the NBTInjector for custom tags. Changes will be
|
||||
* instantly applied to the Tile, use the merge method to do many things at
|
||||
* once.
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
public class NBTTileEntity extends NBTCompound {
|
||||
|
||||
private final BlockState tile;
|
||||
|
||||
/**
|
||||
* @param tile BlockState from any TileEntity
|
||||
*/
|
||||
public NBTTileEntity(BlockState tile) {
|
||||
super(null, null);
|
||||
if (tile == null || (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_8_R3) && !tile.isPlaced())) {
|
||||
throw new NullPointerException("Tile can't be null/not placed!");
|
||||
}
|
||||
this.tile = tile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCompound() {
|
||||
return NBTReflectionUtil.getTileEntityNBTTagCompound(tile);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setCompound(Object compound) {
|
||||
NBTReflectionUtil.setTileEntityNBTTagCompound(tile, compound);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the NBTCompound used by spigots PersistentDataAPI. This method is only
|
||||
* available for 1.14+!
|
||||
*
|
||||
* @return NBTCompound containing the data of the PersistentDataAPI
|
||||
*/
|
||||
@AvailableSince(version = MinecraftVersion.MC1_14_R1)
|
||||
public NBTCompound getPersistentDataContainer() {
|
||||
FAUtil.check(this::getPersistentDataContainer, CheckUtil::isAvaliable);
|
||||
if (hasKey("PublicBukkitValues")) {
|
||||
return getCompound("PublicBukkitValues");
|
||||
} else {
|
||||
NBTContainer container = new NBTContainer();
|
||||
container.addCompound("PublicBukkitValues").setString("__nbtapi",
|
||||
"Marker to make the PersistentDataContainer have content");
|
||||
mergeCompound(container);
|
||||
return getCompound("PublicBukkitValues");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms;
|
||||
|
||||
/**
|
||||
* Enum of all NBT Types Minecraft contains
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public enum NBTType {
|
||||
NBTTagEnd(0),
|
||||
NBTTagByte(1),
|
||||
NBTTagShort(2),
|
||||
NBTTagInt(3),
|
||||
NBTTagLong(4),
|
||||
NBTTagFloat(5),
|
||||
NBTTagDouble(6),
|
||||
NBTTagByteArray(7),
|
||||
NBTTagIntArray(11),
|
||||
NBTTagString(8),
|
||||
NBTTagList(9),
|
||||
NBTTagCompound(10);
|
||||
|
||||
NBTType(int i) {
|
||||
id = i;
|
||||
}
|
||||
|
||||
private final int id;
|
||||
|
||||
/**
|
||||
* @return Id used by Minecraft internally
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id Internal Minecraft id
|
||||
* @return Enum representing the id, NBTTagEnd for invalide ids
|
||||
*/
|
||||
public static NBTType valueOf(int id) {
|
||||
for (NBTType t : values())
|
||||
if (t.getId() == id)
|
||||
return t;
|
||||
return NBTType.NBTTagEnd;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms;
|
||||
|
||||
/**
|
||||
* A generic {@link RuntimeException} that can be thrown by most methods in the
|
||||
* NBTAPI.
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
public class NbtApiException extends RuntimeException {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = -993309714559452334L;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public NbtApiException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param message
|
||||
* @param cause
|
||||
* @param enableSuppression
|
||||
* @param writableStackTrace
|
||||
*/
|
||||
public NbtApiException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param message
|
||||
* @param cause
|
||||
*/
|
||||
public NbtApiException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param message
|
||||
*/
|
||||
public NbtApiException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cause
|
||||
*/
|
||||
public NbtApiException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,388 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms.utils;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
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 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.Collection;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import static com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion.getLogger;
|
||||
|
||||
/**
|
||||
* bStats collects some data for plugin authors.
|
||||
* <p>
|
||||
* Check out https://bStats.org/ to learn more about bStats!
|
||||
*
|
||||
* This class is modified by tr7zw to work when the api is shaded into other peoples plugins.
|
||||
*/
|
||||
public class ApiMetricsLite {
|
||||
|
||||
private static final String PLUGINNAME = "ItemNBTAPI"; // DO NOT CHANGE THE NAME! else it won't link the data on bStats
|
||||
|
||||
// The version of this bStats class
|
||||
public static final int B_STATS_VERSION = 1;
|
||||
|
||||
// The version of the NBT-Api bStats
|
||||
public static final int NBT_BSTATS_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 Plugin plugin;
|
||||
|
||||
/**
|
||||
* Class constructor.
|
||||
*
|
||||
*/
|
||||
public ApiMetricsLite() {
|
||||
|
||||
// The register method just uses any enabled plugin it can find to register. This *shouldn't* cause any problems, since the plugin isn't used any other way.
|
||||
// Register our service
|
||||
for(Plugin plug : Bukkit.getPluginManager().getPlugins()) {
|
||||
plugin = plug;
|
||||
if(plugin != null)
|
||||
break;
|
||||
}
|
||||
if(plugin == null) {
|
||||
return;// Didn't find any plugin that could work
|
||||
}
|
||||
|
||||
// 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 no 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
|
||||
serverUUID = config.getString("serverUuid");
|
||||
logFailedRequests = config.getBoolean("logFailedRequests", false);
|
||||
enabled = config.getBoolean("enabled", true);
|
||||
logSentData = config.getBoolean("logSentData", false);
|
||||
logResponseStatusText = config.getBoolean("logResponseStatusText", false);
|
||||
if (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("NBT_BSTATS_VERSION"); // Create only one instance of the nbt-api bstats.
|
||||
return;
|
||||
} catch (NoSuchFieldException ignored) { }
|
||||
try {
|
||||
service.getField("B_STATS_VERSION"); // Our identifier :)
|
||||
found = true; // We aren't the first
|
||||
break;
|
||||
} catch (NoSuchFieldException ignored) { }
|
||||
}
|
||||
boolean fFound = found;
|
||||
// Register our service
|
||||
if(Bukkit.isPrimaryThread()){
|
||||
Bukkit.getServicesManager().register(ApiMetricsLite.class, this, plugin, ServicePriority.Normal);
|
||||
if (!fFound) {
|
||||
getLogger().info("[NBTAPI] Using the plugin '" + plugin.getName() + "' to create a bStats instance!");
|
||||
// We are the first!
|
||||
startSubmitting();
|
||||
}
|
||||
}else{
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
Bukkit.getServicesManager().register(ApiMetricsLite.class, this, plugin, ServicePriority.Normal);
|
||||
if (!fFound) {
|
||||
getLogger().info("[NBTAPI] Using the plugin '" + plugin.getName() + "' to create a bStats instance!");
|
||||
// We are the first!
|
||||
startSubmitting();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if bStats is enabled.
|
||||
*
|
||||
* @return Whether bStats is enabled or not.
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
}, 1000l * 60l * 5l, 1000l * 60l * 30l);
|
||||
// Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start
|
||||
// WARNING: Changing the frequency has no 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();
|
||||
|
||||
data.addProperty("pluginName", PLUGINNAME); // Append the name of the plugin
|
||||
data.addProperty("pluginVersion", MinecraftVersion.VERSION); // Append the version of the plugin
|
||||
data.add("customCharts", new JsonArray());
|
||||
|
||||
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();
|
||||
String bukkitName = Bukkit.getName();
|
||||
|
||||
// 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.addProperty("serverUUID", serverUUID);
|
||||
|
||||
data.addProperty("playerAmount", playerAmount);
|
||||
data.addProperty("onlineMode", onlineMode);
|
||||
data.addProperty("bukkitVersion", bukkitVersion);
|
||||
data.addProperty("bukkitName", bukkitName);
|
||||
|
||||
data.addProperty("javaVersion", javaVersion);
|
||||
data.addProperty("osName", osName);
|
||||
data.addProperty("osArch", osArch);
|
||||
data.addProperty("osVersion", osVersion);
|
||||
data.addProperty("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 {
|
||||
Object plugin = provider.getService().getMethod("getPluginData").invoke(provider.getProvider());
|
||||
if (plugin instanceof JsonObject) {
|
||||
pluginData.add((JsonObject) plugin);
|
||||
} else { // old bstats version compatibility
|
||||
try {
|
||||
Class<?> jsonObjectJsonSimple = Class.forName("org.json.simple.JSONObject");
|
||||
if (plugin.getClass().isAssignableFrom(jsonObjectJsonSimple)) {
|
||||
Method jsonStringGetter = jsonObjectJsonSimple.getDeclaredMethod("toJSONString");
|
||||
jsonStringGetter.setAccessible(true);
|
||||
String jsonString = (String) jsonStringGetter.invoke(plugin);
|
||||
JsonObject object = new JsonParser().parse(jsonString).getAsJsonObject();
|
||||
pluginData.add(object);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
// minecraft version 1.14+
|
||||
if (logFailedRequests) {
|
||||
getLogger().log(Level.WARNING, "[NBTAPI][BSTATS] Encountered exception while posting request!", e);
|
||||
// Not using the plugins logger since the plugin isn't the plugin containing the NBT-Api most of the time
|
||||
//this.plugin.getLogger().log(Level.SEVERE, "Encountered unexpected exception ", e);
|
||||
}
|
||||
continue; // continue looping since we cannot do any other thing.
|
||||
}
|
||||
}
|
||||
} catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {
|
||||
}
|
||||
}
|
||||
} catch (NoSuchFieldException ignored) { }
|
||||
}
|
||||
|
||||
data.add("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) {
|
||||
getLogger().log(Level.WARNING, "[NBTAPI][BSTATS] Could not submit plugin stats of " + plugin.getName(), e);
|
||||
// Not using the plugins logger since the plugin isn't the plugin containing the NBT-Api most of the time
|
||||
//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) {
|
||||
System.out.println("[NBTAPI][BSTATS] Sending data to bStats: " + data.toString());
|
||||
// Not using the plugins logger since the plugin isn't the plugin containing the NBT-Api most of the time
|
||||
//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) {
|
||||
getLogger().info("[NBTAPI][BSTATS] Sent data to bStats and received response: " + builder.toString());
|
||||
// Not using the plugins logger since the plugin isn't the plugin containing the NBT-Api most of the time
|
||||
//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 new byte[0];
|
||||
}
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
|
||||
gzip.write(str.getBytes(StandardCharsets.UTF_8));
|
||||
gzip.close();
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms.utils;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.nms.NbtApiException;
|
||||
|
||||
/**
|
||||
* Helper class for 1.7 servers without Gson
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
public class GsonWrapper {
|
||||
|
||||
/**
|
||||
* Private constructor
|
||||
*/
|
||||
private GsonWrapper() {
|
||||
|
||||
}
|
||||
|
||||
private static final Gson gson = new Gson();
|
||||
|
||||
/**
|
||||
* Turns Objects into Json Strings
|
||||
*
|
||||
* @param obj
|
||||
* @return Json, representing the Object
|
||||
*/
|
||||
public static String getString(Object obj) {
|
||||
return gson.toJson(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Object of the given type using the Json String
|
||||
*
|
||||
* @param json
|
||||
* @param type
|
||||
* @return Object that got created, or null if the json is null
|
||||
*/
|
||||
public static <T> T deserializeJson(String json, Class<T> type) {
|
||||
try {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
T obj = gson.fromJson(json, type);
|
||||
return type.cast(obj);
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException("Error while converting json to " + type.getName(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,210 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms.utils;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
/**
|
||||
* This class acts as the "Brain" of the NBTApi. It contains the main logger for
|
||||
* other classes,registers bStats and checks rather Maven shading was done
|
||||
* correctly.
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public enum MinecraftVersion {
|
||||
UNKNOWN(Integer.MAX_VALUE), // Use the newest known mappings
|
||||
MC1_7_R4(174), MC1_8_R3(183), MC1_9_R1(191), MC1_9_R2(192), MC1_10_R1(1101), MC1_11_R1(1111), MC1_12_R1(1121),
|
||||
MC1_13_R1(1131), MC1_13_R2(1132), MC1_14_R1(1141), MC1_15_R1(1151), MC1_16_R1(1161), MC1_16_R2(1162), MC1_16_R3(1163), MC1_17_R1(1171), MC1_18_R1(1181, true);
|
||||
|
||||
private static MinecraftVersion version;
|
||||
private static Boolean hasGsonSupport;
|
||||
private static boolean bStatsDisabled = false;
|
||||
private static boolean disablePackageWarning = false;
|
||||
private static boolean updateCheckDisabled = false;
|
||||
/**
|
||||
* Logger used by the api
|
||||
*/
|
||||
private static Logger logger = Logger.getLogger("NBTAPI");
|
||||
|
||||
// NBT-API Version
|
||||
protected static final String VERSION = "2.9.0-SNAPSHOT";
|
||||
|
||||
private final int versionId;
|
||||
private final boolean mojangMapping;
|
||||
|
||||
MinecraftVersion(int versionId) {
|
||||
this(versionId, false);
|
||||
}
|
||||
|
||||
MinecraftVersion(int versionId, boolean mojangMapping) {
|
||||
this.versionId = versionId;
|
||||
this.mojangMapping = mojangMapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A simple comparable Integer, representing the version.
|
||||
*/
|
||||
public int getVersionId() {
|
||||
return versionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if method names are in Mojang format and need to be remapped internally
|
||||
*/
|
||||
public boolean isMojangMapping() {
|
||||
return mojangMapping;
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
if(this == UNKNOWN) {
|
||||
return values()[values().length-1].name().replace("MC", "v");
|
||||
}
|
||||
return this.name().replace("MC", "v");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current versions is at least the given Version
|
||||
*
|
||||
* @param version The minimum version
|
||||
* @return
|
||||
*/
|
||||
public static boolean isAtLeastVersion(MinecraftVersion version) {
|
||||
return getVersion().getVersionId() >= version.getVersionId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current versions newer (not equal) than the given version
|
||||
*
|
||||
* @param version The minimum version
|
||||
* @return
|
||||
*/
|
||||
public static boolean isNewerThan(MinecraftVersion version) {
|
||||
return getVersion().getVersionId() > version.getVersionId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for this servers MinecraftVersion. Also init's bStats and checks the
|
||||
* shading.
|
||||
*
|
||||
* @return The enum for the MinecraftVersion this server is running
|
||||
*/
|
||||
public static MinecraftVersion getVersion() {
|
||||
if (version != null) {
|
||||
return version;
|
||||
}
|
||||
final String ver = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
|
||||
logger.info("[NBTAPI] Found Spigot: " + ver + "! Trying to find NMS support");
|
||||
try {
|
||||
version = MinecraftVersion.valueOf(ver.replace("v", "MC"));
|
||||
} catch (IllegalArgumentException ex) {
|
||||
version = MinecraftVersion.UNKNOWN;
|
||||
}
|
||||
if (version != UNKNOWN) {
|
||||
logger.info("[NBTAPI] NMS support '" + version.name() + "' loaded!");
|
||||
} else {
|
||||
logger.warning("[NBTAPI] Wasn't able to find NMS Support! Some functions may not work!");
|
||||
}
|
||||
init();
|
||||
return version;
|
||||
}
|
||||
|
||||
private static void init() {
|
||||
try {
|
||||
if (hasGsonSupport() && !bStatsDisabled)
|
||||
new ApiMetricsLite();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.WARNING, "[NBTAPI] Error enabling Metrics!", ex);
|
||||
}
|
||||
|
||||
if (hasGsonSupport() && !updateCheckDisabled)
|
||||
new Thread(() -> {
|
||||
try {
|
||||
VersionChecker.checkForUpdates();
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.WARNING, "[NBTAPI] Error while checking for updates!", ex);
|
||||
}
|
||||
}).start();
|
||||
// Maven's Relocate is clever and changes strings, too. So we have to use this
|
||||
// little "trick" ... :D (from bStats)
|
||||
final String defaultPackage = new String(new byte[] { 'd', 'e', '.', 't', 'r', '7', 'z', 'w', '.', 'c', 'h',
|
||||
'a', 'n', 'g', 'e', 'm', 'e', '.', 'n', 'b', 't', 'a', 'p', 'i', '.', 'u', 't', 'i', 'l', 's' });
|
||||
if (!disablePackageWarning && MinecraftVersion.class.getPackage().getName().equals(defaultPackage)) {
|
||||
logger.warning(
|
||||
"#########################################- NBTAPI -#########################################");
|
||||
logger.warning(
|
||||
"The NBT-API package has not been moved! This *will* cause problems with other plugins containing");
|
||||
logger.warning(
|
||||
"a different version of the api! Please read the guide on the plugin page on how to get the");
|
||||
logger.warning(
|
||||
"Maven Shade plugin to relocate the api to your personal location! If you are not the developer,");
|
||||
logger.warning("please check your plugins and contact their developer, so he can fix this issue.");
|
||||
logger.warning(
|
||||
"#########################################- NBTAPI -#########################################");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True, if Gson is usable
|
||||
*/
|
||||
public static boolean hasGsonSupport() {
|
||||
if (hasGsonSupport != null) {
|
||||
return hasGsonSupport;
|
||||
}
|
||||
try {
|
||||
logger.info("[NBTAPI] Found Gson: " + Class.forName("com.google.gson.Gson"));
|
||||
hasGsonSupport = true;
|
||||
} catch (Exception ex) {
|
||||
logger.info("[NBTAPI] Gson not found! This will not allow the usage of some methods!");
|
||||
hasGsonSupport = false;
|
||||
}
|
||||
return hasGsonSupport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calling this function before the NBT-Api is used will disable bStats stats
|
||||
* collection. Please consider not to do that, since it won't affect your plugin
|
||||
* and helps the NBT-Api developer to see api's demand.
|
||||
*/
|
||||
public static void disableBStats() {
|
||||
bStatsDisabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the update check. Uses Spiget to get the current version and prints
|
||||
* a warning when outdated.
|
||||
*/
|
||||
public static void disableUpdateCheck() {
|
||||
updateCheckDisabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forcefully disables the log message for plugins not shading the API to
|
||||
* another location. This may be helpful for networks or development
|
||||
* environments, but please don't use it for plugins that are uploaded to
|
||||
* Spigotmc.
|
||||
*/
|
||||
public static void disablePackageWarning() {
|
||||
disablePackageWarning = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Logger used by the NBT-API
|
||||
*/
|
||||
public static Logger getLogger() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the NBT-API logger with a custom implementation.
|
||||
*
|
||||
* @param logger The new logger(can not be null!)
|
||||
*/
|
||||
public static void replaceLogger(Logger logger) {
|
||||
if(logger == null)throw new NullPointerException("Logger can not be null!");
|
||||
MinecraftVersion.logger = logger;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms.utils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.nms.NbtApiException;
|
||||
|
||||
public final class ReflectionUtil {
|
||||
|
||||
private static Field field_modifiers;
|
||||
|
||||
static {
|
||||
try {
|
||||
field_modifiers = Field.class.getDeclaredField("modifiers");
|
||||
field_modifiers.setAccessible(true);
|
||||
} catch (NoSuchFieldException ex) {
|
||||
try {
|
||||
// This hacky workaround is for newer jdk versions 11+?
|
||||
Method fieldGetter = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class);
|
||||
fieldGetter.setAccessible(true);
|
||||
Field[] fields = (Field[]) fieldGetter.invoke(Field.class, false);
|
||||
for (Field f : fields)
|
||||
if (f.getName().equals("modifiers")) {
|
||||
field_modifiers = f;
|
||||
field_modifiers.setAccessible(true);
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new NbtApiException(e);
|
||||
}
|
||||
}
|
||||
if (field_modifiers == null) {
|
||||
throw new NbtApiException("Unable to init the modifiers Field.");
|
||||
}
|
||||
}
|
||||
|
||||
public static Field makeNonFinal(Field field) throws IllegalArgumentException, IllegalAccessException {
|
||||
int mods = field.getModifiers();
|
||||
if (Modifier.isFinal(mods)) {
|
||||
field_modifiers.set(field, mods & ~Modifier.FINAL);
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
public static void setFinal(Object obj, Field field, Object newValue)
|
||||
throws IllegalArgumentException, IllegalAccessException {
|
||||
field.setAccessible(true);
|
||||
field = makeNonFinal(field);
|
||||
field.set(obj, newValue);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms.utils;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.nms.NBTItem;
|
||||
|
||||
/**
|
||||
* This class uses the Spiget API to check for updates
|
||||
*
|
||||
*/
|
||||
public class VersionChecker {
|
||||
|
||||
private static final String USER_AGENT = "nbt-api Version check";
|
||||
private static final String REQUEST_URL = "https://api.spiget.org/v2/resources/7939/versions?size=100";
|
||||
|
||||
protected static void checkForUpdates() throws Exception {
|
||||
URL url = new URL(REQUEST_URL);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.addRequestProperty("User-Agent", USER_AGENT);// Set
|
||||
// User-Agent
|
||||
|
||||
// If you're not sure if the request will be successful,
|
||||
// you need to check the response code and use #getErrorStream if it
|
||||
// returned an error code
|
||||
InputStream inputStream = connection.getInputStream();
|
||||
InputStreamReader reader = new InputStreamReader(inputStream);
|
||||
|
||||
// This could be either a JsonArray or JsonObject
|
||||
JsonElement element = new JsonParser().parse(reader);
|
||||
if (element.isJsonArray()) {
|
||||
// Is JsonArray
|
||||
JsonArray updates = (JsonArray) element;
|
||||
JsonObject latest = (JsonObject) updates.get(updates.size() - 1);
|
||||
int versionDifference = getVersionDifference(latest.get("name").getAsString());
|
||||
if (versionDifference == -1) { // Outdated
|
||||
MinecraftVersion.getLogger().log(Level.WARNING,
|
||||
"[NBTAPI] The NBT-API at '" + NBTItem.class.getPackage() + "' seems to be outdated!");
|
||||
MinecraftVersion.getLogger().log(Level.WARNING, "[NBTAPI] Current Version: '" + MinecraftVersion.VERSION
|
||||
+ "' Newest Version: " + latest.get("name").getAsString() + "'");
|
||||
MinecraftVersion.getLogger().log(Level.WARNING,
|
||||
"[NBTAPI] Please update the nbt-api or the plugin that contains the api!");
|
||||
|
||||
} else if (versionDifference == 0) {
|
||||
MinecraftVersion.getLogger().log(Level.INFO, "[NBTAPI] The NBT-API seems to be up-to-date!");
|
||||
} else if (versionDifference == 1) {
|
||||
MinecraftVersion.getLogger().log(Level.WARNING, "[NBTAPI] The NBT-API at '" + NBTItem.class.getPackage()
|
||||
+ "' seems to be a future Version, not yet released on Spigot/CurseForge!");
|
||||
MinecraftVersion.getLogger().log(Level.WARNING, "[NBTAPI] Current Version: '" + MinecraftVersion.VERSION
|
||||
+ "' Newest Version: " + latest.get("name").getAsString() + "'");
|
||||
}
|
||||
} else {
|
||||
// wut?!
|
||||
MinecraftVersion.getLogger().log(Level.WARNING,
|
||||
"[NBTAPI] Error when looking for Updates! Got non Json Array: '" + element.toString() + "'");
|
||||
}
|
||||
}
|
||||
|
||||
// -1 = we are outdated
|
||||
// 0 = up to date
|
||||
// 1 = using a future version
|
||||
// This method is only able to compare the Format 0.0.0(-SNAPSHOT)
|
||||
private static int getVersionDifference(String version) {
|
||||
String current = MinecraftVersion.VERSION;
|
||||
if (current.equals(version))
|
||||
return 0;
|
||||
String pattern = "\\.";
|
||||
if (current.split(pattern).length != 3 || version.split(pattern).length != 3)
|
||||
return -1;
|
||||
int curMaj = Integer.parseInt(current.split(pattern)[0]);
|
||||
int curMin = Integer.parseInt(current.split(pattern)[1]);
|
||||
String curPatch = current.split(pattern)[2];
|
||||
int relMaj = Integer.parseInt(version.split(pattern)[0]);
|
||||
int relMin = Integer.parseInt(version.split(pattern)[1]);
|
||||
String relPatch = version.split(pattern)[2];
|
||||
if (curMaj < relMaj)
|
||||
return -1;
|
||||
if (curMaj > relMaj)
|
||||
return 1;
|
||||
if (curMin < relMin)
|
||||
return -1;
|
||||
if (curMin > relMin)
|
||||
return 1;
|
||||
int curPatchN = Integer.parseInt(curPatch.split("-")[0]);
|
||||
int relPatchN = Integer.parseInt(relPatch.split("-")[0]);
|
||||
if (curPatchN < relPatchN)
|
||||
return -1;
|
||||
if (curPatchN > relPatchN)
|
||||
return 1;
|
||||
if (!relPatch.contains("-") && curPatch.contains("-"))
|
||||
return -1; // Release has no - but we do = We use a Snapshot of the
|
||||
// release
|
||||
if (relPatch.contains("-") && curPatch.contains("-"))
|
||||
return 0; // Release and cur are Snapshots/alpha/beta
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms.utils.annotations;
|
||||
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion;
|
||||
|
||||
@Retention(RUNTIME)
|
||||
@Target({ METHOD })
|
||||
public @interface AvailableSince {
|
||||
|
||||
MinecraftVersion version();
|
||||
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms.utils.annotations;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.nms.NbtApiException;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion;
|
||||
|
||||
public class CheckUtil {
|
||||
|
||||
public static boolean isAvaliable(Method method) {
|
||||
if(MinecraftVersion.getVersion().getVersionId() < method.getAnnotation(AvailableSince.class).version().getVersionId())
|
||||
throw new NbtApiException("The Method '" + method.getName() + "' is only avaliable for the Versions " + method.getAnnotation(AvailableSince.class).version() + "+, but still got called!");
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings;
|
||||
|
||||
import static com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion.getLogger;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion;
|
||||
|
||||
/**
|
||||
* Wraps NMS and CRAFT classes
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public enum ClassWrapper {
|
||||
CRAFT_ITEMSTACK(PackageWrapper.CRAFTBUKKIT, "inventory.CraftItemStack", null, null),
|
||||
CRAFT_METAITEM(PackageWrapper.CRAFTBUKKIT, "inventory.CraftMetaItem", null, null),
|
||||
CRAFT_ENTITY(PackageWrapper.CRAFTBUKKIT, "entity.CraftEntity", null, null),
|
||||
CRAFT_WORLD(PackageWrapper.CRAFTBUKKIT, "CraftWorld", null, null),
|
||||
CRAFT_PERSISTENTDATACONTAINER(PackageWrapper.CRAFTBUKKIT, "persistence.CraftPersistentDataContainer",
|
||||
MinecraftVersion.MC1_14_R1, null),
|
||||
NMS_NBTBASE(PackageWrapper.NMS, "NBTBase", null, null, "net.minecraft.nbt", "net.minecraft.nbt.Tag"),
|
||||
NMS_NBTTAGSTRING(PackageWrapper.NMS, "NBTTagString", null, null, "net.minecraft.nbt", "net.minecraft.nbt.StringTag"),
|
||||
NMS_NBTTAGINT(PackageWrapper.NMS, "NBTTagInt", null, null, "net.minecraft.nbt", "net.minecraft.nbt.IntTag"),
|
||||
NMS_NBTTAGFLOAT(PackageWrapper.NMS, "NBTTagFloat", null, null, "net.minecraft.nbt", "net.minecraft.nbt.FloatTag"),
|
||||
NMS_NBTTAGDOUBLE(PackageWrapper.NMS, "NBTTagDouble", null, null, "net.minecraft.nbt", "net.minecraft.nbt.DoubleTag"),
|
||||
NMS_NBTTAGLONG(PackageWrapper.NMS, "NBTTagLong", null, null, "net.minecraft.nbt", "net.minecraft.nbt.LongTag"),
|
||||
NMS_ITEMSTACK(PackageWrapper.NMS, "ItemStack", null, null, "net.minecraft.world.item", "net.minecraft.world.item.ItemStack"),
|
||||
NMS_NBTTAGCOMPOUND(PackageWrapper.NMS, "NBTTagCompound", null, null, "net.minecraft.nbt", "net.minecraft.nbt.CompoundTag"),
|
||||
NMS_NBTTAGLIST(PackageWrapper.NMS, "NBTTagList", null, null, "net.minecraft.nbt", "net.minecraft.nbt.ListTag"),
|
||||
NMS_NBTCOMPRESSEDSTREAMTOOLS(PackageWrapper.NMS, "NBTCompressedStreamTools", null, null, "net.minecraft.nbt", "net.minecraft.nbt.NbtIo"),
|
||||
NMS_MOJANGSONPARSER(PackageWrapper.NMS, "MojangsonParser", null, null, "net.minecraft.nbt", "net.minecraft.nbt.TagParser"),
|
||||
NMS_TILEENTITY(PackageWrapper.NMS, "TileEntity", null, null, "net.minecraft.world.level.block.entity", "net.minecraft.world.level.block.entity.BlockEntity"),
|
||||
NMS_BLOCKPOSITION(PackageWrapper.NMS, "BlockPosition", MinecraftVersion.MC1_8_R3, null, "net.minecraft.core", "net.minecraft.core.BlockPos"),
|
||||
NMS_WORLDSERVER(PackageWrapper.NMS, "WorldServer", null, null, "net.minecraft.server.level", "net.minecraft.server.level.ServerLevel"),
|
||||
NMS_MINECRAFTSERVER(PackageWrapper.NMS, "MinecraftServer", null, null, "net.minecraft.server", "net.minecraft.server.MinecraftServer"),
|
||||
NMS_WORLD(PackageWrapper.NMS, "World", null, null, "net.minecraft.world.level", "net.minecraft.world.level.Level"),
|
||||
NMS_ENTITY(PackageWrapper.NMS, "Entity", null, null, "net.minecraft.world.entity", "net.minecraft.world.entity.Entity"),
|
||||
NMS_ENTITYTYPES(PackageWrapper.NMS, "EntityTypes", null, null, "net.minecraft.world.entity", "net.minecraft.world.entity.EntityType"),
|
||||
NMS_REGISTRYSIMPLE(PackageWrapper.NMS, "RegistrySimple", MinecraftVersion.MC1_11_R1, MinecraftVersion.MC1_12_R1),
|
||||
NMS_REGISTRYMATERIALS(PackageWrapper.NMS, "RegistryMaterials", null, null, "net.minecraft.core", "net.minecraft.core.MappedRegistry"),
|
||||
NMS_IREGISTRY(PackageWrapper.NMS, "IRegistry", null, null, "net.minecraft.core", "net.minecraft.core.Registry"),
|
||||
NMS_MINECRAFTKEY(PackageWrapper.NMS, "MinecraftKey", MinecraftVersion.MC1_8_R3, null, "net.minecraft.resources", "net.minecraft.resources.ResourceKey"),
|
||||
NMS_GAMEPROFILESERIALIZER(PackageWrapper.NMS, "GameProfileSerializer", null, null, "net.minecraft.nbt", "net.minecraft.nbt.NbtUtils"),
|
||||
NMS_IBLOCKDATA(PackageWrapper.NMS, "IBlockData", MinecraftVersion.MC1_8_R3, null,
|
||||
"net.minecraft.world.level.block.state", "net.minecraft.world.level.block.state.BlockState"),
|
||||
GAMEPROFILE(PackageWrapper.NONE, "com.mojang.authlib.GameProfile", MinecraftVersion.MC1_8_R3, null);
|
||||
|
||||
private Class<?> clazz;
|
||||
private boolean enabled = false;
|
||||
private final String mojangName;
|
||||
|
||||
ClassWrapper(PackageWrapper packageId, String clazzName, MinecraftVersion from, MinecraftVersion to) {
|
||||
this(packageId, clazzName, from, to, null, null);
|
||||
}
|
||||
|
||||
ClassWrapper(PackageWrapper packageId, String clazzName, MinecraftVersion from, MinecraftVersion to,
|
||||
String mojangMap, String mojangName) {
|
||||
this.mojangName = mojangName;
|
||||
if (from != null && MinecraftVersion.getVersion().getVersionId() < from.getVersionId()) {
|
||||
return;
|
||||
}
|
||||
if (to != null && MinecraftVersion.getVersion().getVersionId() > to.getVersionId()) {
|
||||
return;
|
||||
}
|
||||
enabled = true;
|
||||
try {
|
||||
if (MinecraftVersion.isAtLeastVersion(MinecraftVersion.MC1_17_R1) && mojangMap != null) {
|
||||
clazz = Class.forName(mojangMap + "." + clazzName);
|
||||
} else if (packageId == PackageWrapper.NONE) {
|
||||
clazz = Class.forName(clazzName);
|
||||
} else {
|
||||
String version = MinecraftVersion.getVersion().getPackageName();
|
||||
clazz = Class.forName(packageId.getUri() + "." + version + "." + clazzName);
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
getLogger().log(Level.WARNING, "[NBTAPI] Error while trying to resolve the class '" + clazzName + "'!", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The wrapped class
|
||||
*/
|
||||
public Class<?> getClazz() {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Is this class available in this Version
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Package+Class name used by Mojang
|
||||
*/
|
||||
public String getMojangName() {
|
||||
return mojangName;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Temporary solution to hold Mojang to unmapped Spigot mappings.
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
public class MojangToMapping {
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static Map<String, String> MC1_18R1 = new HashMap<String, String>() {
|
||||
|
||||
{
|
||||
put("net.minecraft.nbt.CompoundTag#contains(java.lang.String)", "e");
|
||||
put("net.minecraft.nbt.CompoundTag#getCompound(java.lang.String)", "p");
|
||||
put("net.minecraft.nbt.CompoundTag#getList(java.lang.String,int)", "c");
|
||||
put("net.minecraft.nbt.CompoundTag#putByteArray(java.lang.String,byte[])", "a");
|
||||
put("net.minecraft.nbt.CompoundTag#getDouble(java.lang.String)", "k");
|
||||
put("net.minecraft.nbt.CompoundTag#putDouble(java.lang.String,double)", "a");
|
||||
put("net.minecraft.nbt.CompoundTag#getByteArray(java.lang.String)", "m");
|
||||
put("net.minecraft.nbt.CompoundTag#putInt(java.lang.String,int)", "a");
|
||||
put("net.minecraft.nbt.CompoundTag#getIntArray(java.lang.String)", "n");
|
||||
put("net.minecraft.nbt.CompoundTag#remove(java.lang.String)", "r");
|
||||
put("net.minecraft.nbt.CompoundTag#get(java.lang.String)", "c");
|
||||
put("net.minecraft.nbt.CompoundTag#put(java.lang.String,net.minecraft.nbt.Tag)", "a");
|
||||
put("net.minecraft.nbt.CompoundTag#putBoolean(java.lang.String,boolean)", "a");
|
||||
put("net.minecraft.nbt.CompoundTag#getTagType(java.lang.String)", "d");
|
||||
put("net.minecraft.nbt.CompoundTag#putLong(java.lang.String,long)", "a");
|
||||
put("net.minecraft.nbt.CompoundTag#getString(java.lang.String)", "l");
|
||||
put("net.minecraft.nbt.CompoundTag#getInt(java.lang.String)", "h");
|
||||
put("net.minecraft.nbt.CompoundTag#putString(java.lang.String,java.lang.String)", "a");
|
||||
put("net.minecraft.nbt.CompoundTag#put(java.lang.String,net.minecraft.nbt.Tag)", "a");
|
||||
put("net.minecraft.nbt.CompoundTag#getByte(java.lang.String)", "f");
|
||||
put("net.minecraft.nbt.CompoundTag#putIntArray(java.lang.String,int[])", "a");
|
||||
put("net.minecraft.nbt.CompoundTag#getShort(java.lang.String)", "g");
|
||||
put("net.minecraft.nbt.CompoundTag#putByte(java.lang.String,byte)", "a");
|
||||
put("net.minecraft.nbt.CompoundTag#getAllKeys()", "d");
|
||||
put("net.minecraft.nbt.CompoundTag#getAllKeys()", "d");
|
||||
put("net.minecraft.nbt.CompoundTag#putUUID(java.lang.String,java.util.UUID)", "a");
|
||||
put("net.minecraft.nbt.CompoundTag#putShort(java.lang.String,short)", "a");
|
||||
put("net.minecraft.nbt.CompoundTag#getLong(java.lang.String)", "i");
|
||||
put("net.minecraft.nbt.CompoundTag#putFloat(java.lang.String,float)", "a");
|
||||
put("net.minecraft.nbt.CompoundTag#getBoolean(java.lang.String)", "q");
|
||||
put("net.minecraft.nbt.CompoundTag#getUUID(java.lang.String)", "a");
|
||||
put("net.minecraft.nbt.CompoundTag#getFloat(java.lang.String)", "j");
|
||||
put("net.minecraft.nbt.ListTag#addTag(int,net.minecraft.nbt.Tag)", "b");
|
||||
put("net.minecraft.nbt.ListTag#setTag(int,net.minecraft.nbt.Tag)", "a");
|
||||
put("net.minecraft.nbt.ListTag#getString(int)", "j");
|
||||
put("net.minecraft.nbt.ListTag#remove(int)", "remove");
|
||||
put("net.minecraft.nbt.ListTag#getCompound(int)", "a");
|
||||
put("net.minecraft.nbt.ListTag#size()", "size");
|
||||
put("net.minecraft.nbt.ListTag#get(int)", "get");
|
||||
put("net.minecraft.nbt.NbtIo#readCompressed(java.io.InputStream)", "a");
|
||||
put("net.minecraft.nbt.NbtIo#writeCompressed(net.minecraft.nbt.CompoundTag,java.io.OutputStream)", "a");
|
||||
put("net.minecraft.nbt.NbtUtils#readGameProfile(net.minecraft.nbt.CompoundTag)", "a");
|
||||
put("net.minecraft.nbt.NbtUtils#writeGameProfile(net.minecraft.nbt.CompoundTag,com.mojang.authlib.GameProfile)", "a");
|
||||
put("net.minecraft.nbt.TagParser#parseTag(java.lang.String)", "a");
|
||||
put("net.minecraft.world.entity.Entity#getEncodeId()", "bk");
|
||||
put("net.minecraft.world.entity.Entity#load(net.minecraft.nbt.CompoundTag)", "g");
|
||||
put("net.minecraft.world.entity.Entity#saveWithoutId(net.minecraft.nbt.CompoundTag)", "f");
|
||||
put("net.minecraft.world.item.ItemStack#setTag(net.minecraft.nbt.CompoundTag)", "c");
|
||||
put("net.minecraft.world.item.ItemStack#getTag()", "s");
|
||||
put("net.minecraft.world.item.ItemStack#save(net.minecraft.nbt.CompoundTag)", "b");
|
||||
put("net.minecraft.world.level.block.entity.BlockEntity#saveWithId()", "n");
|
||||
put("net.minecraft.world.level.block.entity.BlockEntity#getBlockState()", "q");
|
||||
put("net.minecraft.world.level.block.entity.BlockEntity#load(net.minecraft.nbt.CompoundTag)", "a");
|
||||
put("net.minecraft.server.level.ServerLevel#getBlockEntity(net.minecraft.core.BlockPos)", "c_");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public static Map<String, String> getMapping(){
|
||||
return MC1_18R1;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.nms.NbtApiException;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion;
|
||||
|
||||
import static com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion.getLogger;
|
||||
|
||||
/**
|
||||
* This Enum wraps Constructors for NMS classes
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public enum ObjectCreator {
|
||||
NMS_NBTTAGCOMPOUND(null, null, ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()),
|
||||
NMS_BLOCKPOSITION(null, null, ClassWrapper.NMS_BLOCKPOSITION.getClazz(), int.class, int.class, int.class),
|
||||
NMS_COMPOUNDFROMITEM(MinecraftVersion.MC1_11_R1, null, ClassWrapper.NMS_ITEMSTACK.getClazz(), ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()),;
|
||||
|
||||
private Constructor<?> construct;
|
||||
private Class<?> targetClass;
|
||||
|
||||
ObjectCreator(MinecraftVersion from, MinecraftVersion to, Class<?> clazz, Class<?>... args) {
|
||||
if (clazz == null)
|
||||
return;
|
||||
if (from != null && MinecraftVersion.getVersion().getVersionId() < from.getVersionId())
|
||||
return;
|
||||
if (to != null && MinecraftVersion.getVersion().getVersionId() > to.getVersionId())
|
||||
return;
|
||||
try {
|
||||
this.targetClass = clazz;
|
||||
construct = clazz.getDeclaredConstructor(args);
|
||||
construct.setAccessible(true);
|
||||
} catch (Exception ex) {
|
||||
getLogger().log(Level.SEVERE, "Unable to find the constructor for the class '" + clazz.getName() + "'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Object instance with given args
|
||||
*
|
||||
* @param args
|
||||
* @return Object created
|
||||
*/
|
||||
public Object getInstance(Object... args) {
|
||||
try {
|
||||
return construct.newInstance(args);
|
||||
} catch (Exception ex) {
|
||||
throw new NbtApiException("Exception while creating a new instance of '" + targetClass + "'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings;
|
||||
|
||||
/**
|
||||
* Package enum
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public enum PackageWrapper {
|
||||
NMS(new String(new byte[] {'n', 'e', 't', '.', 'm', 'i', 'n', 'e', 'c', 'r', 'a', 'f', 't', '.', 's', 'e', 'r', 'v', 'e', 'r'})),
|
||||
CRAFTBUKKIT(new String(new byte[] {'o', 'r', 'g', '.', 'b', 'u', 'k', 'k', 'i', 't', '.', 'c', 'r', 'a', 'f', 't', 'b', 'u', 'k', 'k', 'i', 't'})),
|
||||
NONE("")
|
||||
;
|
||||
|
||||
private final String uri;
|
||||
|
||||
private PackageWrapper(String uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The Uri for that package
|
||||
*/
|
||||
public String getUri() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,225 +0,0 @@
|
|||
package com.pretzel.dev.villagertradelimiter.nms.utils.nmsmappings;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.nms.NbtApiException;
|
||||
import com.pretzel.dev.villagertradelimiter.nms.utils.MinecraftVersion;
|
||||
|
||||
/**
|
||||
* This class caches method reflections, keeps track of method name changes between versions and allows early checking for problems
|
||||
*
|
||||
* @author tr7zw
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("javadoc")
|
||||
public enum ReflectionMethod {
|
||||
|
||||
COMPOUND_SET_FLOAT(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, float.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setFloat"), new Since(MinecraftVersion.MC1_18_R1, "putFloat(java.lang.String,float)")),
|
||||
COMPOUND_SET_STRING(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setString"), new Since(MinecraftVersion.MC1_18_R1, "putString(java.lang.String,java.lang.String)")),
|
||||
COMPOUND_SET_INT(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, int.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setInt"), new Since(MinecraftVersion.MC1_18_R1, "putInt(java.lang.String,int)")),
|
||||
COMPOUND_SET_BYTEARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, byte[].class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setByteArray"), new Since(MinecraftVersion.MC1_18_R1, "putByteArray(java.lang.String,byte[])")),
|
||||
COMPOUND_SET_INTARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, int[].class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setIntArray"), new Since(MinecraftVersion.MC1_18_R1, "putIntArray(java.lang.String,int[])")),
|
||||
COMPOUND_SET_LONG(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, long.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setLong"), new Since(MinecraftVersion.MC1_18_R1, "putLong(java.lang.String,long)")),
|
||||
COMPOUND_SET_SHORT(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, short.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setShort"), new Since(MinecraftVersion.MC1_18_R1, "putShort(java.lang.String,short)")),
|
||||
COMPOUND_SET_BYTE(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, byte.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setByte"), new Since(MinecraftVersion.MC1_18_R1, "putByte(java.lang.String,byte)")),
|
||||
COMPOUND_SET_DOUBLE(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, double.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setDouble"), new Since(MinecraftVersion.MC1_18_R1, "putDouble(java.lang.String,double)")),
|
||||
COMPOUND_SET_BOOLEAN(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, boolean.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setBoolean"), new Since(MinecraftVersion.MC1_18_R1, "putBoolean(java.lang.String,boolean)")),
|
||||
COMPOUND_SET_UUID(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, UUID.class}, MinecraftVersion.MC1_16_R1, new Since(MinecraftVersion.MC1_16_R1, "a"), new Since(MinecraftVersion.MC1_18_R1, "putUUID(java.lang.String,java.util.UUID)")),
|
||||
COMPOUND_MERGE(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "a"), new Since(MinecraftVersion.MC1_18_R1, "put(java.lang.String,net.minecraft.nbt.Tag)")),
|
||||
COMPOUND_SET(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, ClassWrapper.NMS_NBTBASE.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "set"), new Since(MinecraftVersion.MC1_18_R1, "put(java.lang.String,net.minecraft.nbt.Tag)")),
|
||||
COMPOUND_GET(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "get"), new Since(MinecraftVersion.MC1_18_R1, "get(java.lang.String)")),
|
||||
COMPOUND_GET_LIST(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class, int.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getList"), new Since(MinecraftVersion.MC1_18_R1, "getList(java.lang.String,int)")),
|
||||
COMPOUND_OWN_TYPE(ClassWrapper.NMS_NBTBASE, new Class[]{}, MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getTypeId")), // Only needed for 1.7.10 getType
|
||||
|
||||
COMPOUND_GET_FLOAT(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getFloat"), new Since(MinecraftVersion.MC1_18_R1, "getFloat(java.lang.String)")),
|
||||
COMPOUND_GET_STRING(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getString"), new Since(MinecraftVersion.MC1_18_R1, "getString(java.lang.String)")),
|
||||
COMPOUND_GET_INT(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getInt"), new Since(MinecraftVersion.MC1_18_R1, "getInt(java.lang.String)")),
|
||||
COMPOUND_GET_BYTEARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getByteArray"), new Since(MinecraftVersion.MC1_18_R1, "getByteArray(java.lang.String)")),
|
||||
COMPOUND_GET_INTARRAY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getIntArray"), new Since(MinecraftVersion.MC1_18_R1, "getIntArray(java.lang.String)")),
|
||||
COMPOUND_GET_LONG(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getLong"), new Since(MinecraftVersion.MC1_18_R1, "getLong(java.lang.String)")),
|
||||
COMPOUND_GET_SHORT(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getShort"), new Since(MinecraftVersion.MC1_18_R1, "getShort(java.lang.String)")),
|
||||
COMPOUND_GET_BYTE(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getByte"), new Since(MinecraftVersion.MC1_18_R1, "getByte(java.lang.String)")),
|
||||
COMPOUND_GET_DOUBLE(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getDouble"), new Since(MinecraftVersion.MC1_18_R1, "getDouble(java.lang.String)")),
|
||||
COMPOUND_GET_BOOLEAN(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getBoolean"), new Since(MinecraftVersion.MC1_18_R1, "getBoolean(java.lang.String)")),
|
||||
COMPOUND_GET_UUID(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_16_R1, new Since(MinecraftVersion.MC1_16_R1, "a"), new Since(MinecraftVersion.MC1_18_R1, "getUUID(java.lang.String)")),
|
||||
COMPOUND_GET_COMPOUND(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getCompound"), new Since(MinecraftVersion.MC1_18_R1, "getCompound(java.lang.String)")),
|
||||
|
||||
NMSITEM_GETTAG(ClassWrapper.NMS_ITEMSTACK, new Class[] {}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getTag"), new Since(MinecraftVersion.MC1_18_R1, "getTag()")),
|
||||
NMSITEM_SAVE(ClassWrapper.NMS_ITEMSTACK, new Class[] {ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "save"), new Since(MinecraftVersion.MC1_18_R1, "save(net.minecraft.nbt.CompoundTag)")),
|
||||
NMSITEM_CREATESTACK(ClassWrapper.NMS_ITEMSTACK, new Class[] {ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_10_R1, new Since(MinecraftVersion.MC1_7_R4, "createStack")),
|
||||
|
||||
COMPOUND_REMOVE_KEY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "remove"), new Since(MinecraftVersion.MC1_18_R1, "remove(java.lang.String)")),
|
||||
COMPOUND_HAS_KEY(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "hasKey"), new Since(MinecraftVersion.MC1_18_R1, "contains(java.lang.String)")),
|
||||
COMPOUND_GET_TYPE(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{String.class}, MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "b"), new Since(MinecraftVersion.MC1_9_R1, "d"), new Since(MinecraftVersion.MC1_15_R1, "e"), new Since(MinecraftVersion.MC1_16_R1, "d"), new Since(MinecraftVersion.MC1_18_R1, "getTagType(java.lang.String)")),
|
||||
COMPOUND_GET_KEYS(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "c"), new Since(MinecraftVersion.MC1_13_R1, "getKeys"), new Since(MinecraftVersion.MC1_18_R1, "getAllKeys()")),
|
||||
|
||||
LISTCOMPOUND_GET_KEYS(ClassWrapper.NMS_NBTTAGCOMPOUND, new Class[]{}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "c"), new Since(MinecraftVersion.MC1_13_R1, "getKeys"), new Since(MinecraftVersion.MC1_18_R1, "getAllKeys()")), // FIXME ?!?
|
||||
LIST_REMOVE_KEY(ClassWrapper.NMS_NBTTAGLIST, new Class[]{int.class}, MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "a"), new Since(MinecraftVersion.MC1_9_R1, "remove"), new Since(MinecraftVersion.MC1_18_R1, "remove(int)")),
|
||||
LIST_SIZE(ClassWrapper.NMS_NBTTAGLIST, new Class[]{}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "size"), new Since(MinecraftVersion.MC1_18_R1, "size()")),
|
||||
LIST_SET(ClassWrapper.NMS_NBTTAGLIST, new Class[]{int.class, ClassWrapper.NMS_NBTBASE.getClazz()}, MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "a"), new Since(MinecraftVersion.MC1_13_R1, "set"), new Since(MinecraftVersion.MC1_18_R1, "setTag(int,net.minecraft.nbt.Tag)")),
|
||||
LEGACY_LIST_ADD(ClassWrapper.NMS_NBTTAGLIST, new Class[]{ClassWrapper.NMS_NBTBASE.getClazz()}, MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_13_R2, new Since(MinecraftVersion.MC1_7_R4, "add")),
|
||||
LIST_ADD(ClassWrapper.NMS_NBTTAGLIST, new Class[]{int.class, ClassWrapper.NMS_NBTBASE.getClazz()}, MinecraftVersion.MC1_14_R1, new Since(MinecraftVersion.MC1_14_R1, "add"), new Since(MinecraftVersion.MC1_18_R1, "addTag(int,net.minecraft.nbt.Tag)")),
|
||||
LIST_GET_STRING(ClassWrapper.NMS_NBTTAGLIST, new Class[]{int.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getString"), new Since(MinecraftVersion.MC1_18_R1, "getString(int)")),
|
||||
LIST_GET_COMPOUND(ClassWrapper.NMS_NBTTAGLIST, new Class[]{int.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "get"), new Since(MinecraftVersion.MC1_18_R1, "getCompound(int)")),
|
||||
LIST_GET(ClassWrapper.NMS_NBTTAGLIST, new Class[]{int.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "get"), new Since(MinecraftVersion.MC1_8_R3, "g"), new Since(MinecraftVersion.MC1_9_R1, "h"), new Since(MinecraftVersion.MC1_12_R1, "i"), new Since(MinecraftVersion.MC1_13_R1, "get"), new Since(MinecraftVersion.MC1_18_R1, "get(int)")),
|
||||
|
||||
ITEMSTACK_SET_TAG(ClassWrapper.NMS_ITEMSTACK, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "setTag"), new Since(MinecraftVersion.MC1_18_R1, "setTag(net.minecraft.nbt.CompoundTag)")),
|
||||
ITEMSTACK_NMSCOPY(ClassWrapper.CRAFT_ITEMSTACK, new Class[]{ItemStack.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "asNMSCopy")),
|
||||
ITEMSTACK_BUKKITMIRROR(ClassWrapper.CRAFT_ITEMSTACK, new Class[]{ClassWrapper.NMS_ITEMSTACK.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "asCraftMirror")),
|
||||
|
||||
CRAFT_WORLD_GET_HANDLE(ClassWrapper.CRAFT_WORLD, new Class[]{}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getHandle")),
|
||||
NMS_WORLD_GET_TILEENTITY(ClassWrapper.NMS_WORLDSERVER, new Class[]{ClassWrapper.NMS_BLOCKPOSITION.getClazz()}, MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "getTileEntity"), new Since(MinecraftVersion.MC1_18_R1, "getBlockEntity(net.minecraft.core.BlockPos)")),
|
||||
NMS_WORLD_SET_TILEENTITY(ClassWrapper.NMS_WORLDSERVER, new Class[]{ClassWrapper.NMS_BLOCKPOSITION.getClazz(), ClassWrapper.NMS_TILEENTITY.getClazz()}, MinecraftVersion.MC1_8_R3, MinecraftVersion.MC1_16_R3, new Since(MinecraftVersion.MC1_8_R3, "setTileEntity")),
|
||||
NMS_WORLD_REMOVE_TILEENTITY(ClassWrapper.NMS_WORLDSERVER, new Class[]{ClassWrapper.NMS_BLOCKPOSITION.getClazz()}, MinecraftVersion.MC1_8_R3, MinecraftVersion.MC1_17_R1, new Since(MinecraftVersion.MC1_8_R3, "t"), new Since(MinecraftVersion.MC1_9_R1, "s"), new Since(MinecraftVersion.MC1_13_R1, "n"), new Since(MinecraftVersion.MC1_14_R1, "removeTileEntity")),
|
||||
|
||||
NMS_WORLD_GET_TILEENTITY_1_7_10(ClassWrapper.NMS_WORLDSERVER, new Class[]{int.class, int.class, int.class}, MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getTileEntity")),
|
||||
|
||||
TILEENTITY_LOAD_LEGACY191(ClassWrapper.NMS_TILEENTITY, new Class[]{ClassWrapper.NMS_MINECRAFTSERVER.getClazz(), ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_9_R1, MinecraftVersion.MC1_9_R1, new Since(MinecraftVersion.MC1_9_R1, "a")), //FIXME: No Spigot mapping!
|
||||
TILEENTITY_LOAD_LEGACY183(ClassWrapper.NMS_TILEENTITY, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_8_R3, MinecraftVersion.MC1_9_R2, new Since(MinecraftVersion.MC1_8_R3, "c"), new Since(MinecraftVersion.MC1_9_R1, "a"), new Since(MinecraftVersion.MC1_9_R2, "c")), //FIXME: No Spigot mapping!
|
||||
TILEENTITY_LOAD_LEGACY1121(ClassWrapper.NMS_TILEENTITY, new Class[]{ClassWrapper.NMS_WORLD.getClazz(), ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_10_R1, MinecraftVersion.MC1_12_R1, new Since(MinecraftVersion.MC1_10_R1, "a"), new Since(MinecraftVersion.MC1_12_R1, "create")),
|
||||
TILEENTITY_LOAD_LEGACY1151(ClassWrapper.NMS_TILEENTITY, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_13_R1, MinecraftVersion.MC1_15_R1, new Since(MinecraftVersion.MC1_12_R1, "create")),
|
||||
TILEENTITY_LOAD(ClassWrapper.NMS_TILEENTITY, new Class[]{ClassWrapper.NMS_IBLOCKDATA.getClazz(), ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_16_R1, MinecraftVersion.MC1_16_R3, new Since(MinecraftVersion.MC1_16_R1, "create")),
|
||||
|
||||
TILEENTITY_GET_NBT(ClassWrapper.NMS_TILEENTITY, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_17_R1, new Since(MinecraftVersion.MC1_7_R4, "b"), new Since(MinecraftVersion.MC1_9_R1, "save")),
|
||||
TILEENTITY_GET_NBT_1181(ClassWrapper.NMS_TILEENTITY, new Class[]{}, MinecraftVersion.MC1_18_R1, new Since(MinecraftVersion.MC1_18_R1, "saveWithId()")),
|
||||
TILEENTITY_SET_NBT_LEGACY1151(ClassWrapper.NMS_TILEENTITY, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, MinecraftVersion.MC1_15_R1, new Since(MinecraftVersion.MC1_7_R4, "a"), new Since(MinecraftVersion.MC1_12_R1, "load")),
|
||||
TILEENTITY_SET_NBT_LEGACY1161(ClassWrapper.NMS_TILEENTITY, new Class[]{ClassWrapper.NMS_IBLOCKDATA.getClazz(), ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_16_R1, MinecraftVersion.MC1_16_R3, new Since(MinecraftVersion.MC1_16_R1, "load")),
|
||||
TILEENTITY_SET_NBT(ClassWrapper.NMS_TILEENTITY, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_17_R1, new Since(MinecraftVersion.MC1_16_R1, "load"), new Since(MinecraftVersion.MC1_18_R1, "load(net.minecraft.nbt.CompoundTag)")),
|
||||
TILEENTITY_GET_BLOCKDATA(ClassWrapper.NMS_TILEENTITY, new Class[]{}, MinecraftVersion.MC1_16_R1, new Since(MinecraftVersion.MC1_16_R1, "getBlock"), new Since(MinecraftVersion.MC1_18_R1, "getBlockState()")),
|
||||
|
||||
CRAFT_ENTITY_GET_HANDLE(ClassWrapper.CRAFT_ENTITY, new Class[]{}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "getHandle")),
|
||||
NMS_ENTITY_SET_NBT(ClassWrapper.NMS_ENTITY, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "f"), new Since(MinecraftVersion.MC1_16_R1, "load"), new Since(MinecraftVersion.MC1_18_R1, "load(net.minecraft.nbt.CompoundTag)")),
|
||||
NMS_ENTITY_GET_NBT(ClassWrapper.NMS_ENTITY, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "e"), new Since(MinecraftVersion.MC1_12_R1, "save"), new Since(MinecraftVersion.MC1_18_R1, "saveWithoutId(net.minecraft.nbt.CompoundTag)")),
|
||||
NMS_ENTITY_GETSAVEID(ClassWrapper.NMS_ENTITY, new Class[]{}, MinecraftVersion.MC1_14_R1,new Since(MinecraftVersion.MC1_14_R1, "getSaveID"), new Since(MinecraftVersion.MC1_18_R1, "getEncodeId()")),
|
||||
|
||||
NBTFILE_READ(ClassWrapper.NMS_NBTCOMPRESSEDSTREAMTOOLS, new Class[]{InputStream.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "a"), new Since(MinecraftVersion.MC1_18_R1, "readCompressed(java.io.InputStream)")),
|
||||
NBTFILE_WRITE(ClassWrapper.NMS_NBTCOMPRESSEDSTREAMTOOLS, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), OutputStream.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "a"), new Since(MinecraftVersion.MC1_18_R1, "writeCompressed(net.minecraft.nbt.CompoundTag,java.io.OutputStream)")),
|
||||
|
||||
PARSE_NBT(ClassWrapper.NMS_MOJANGSONPARSER, new Class[]{String.class}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "parse"), new Since(MinecraftVersion.MC1_18_R1, "parseTag(java.lang.String)")),
|
||||
REGISTRY_KEYSET (ClassWrapper.NMS_REGISTRYSIMPLE, new Class[]{}, MinecraftVersion.MC1_11_R1, MinecraftVersion.MC1_13_R1, new Since(MinecraftVersion.MC1_11_R1, "keySet")),
|
||||
REGISTRY_GET (ClassWrapper.NMS_REGISTRYSIMPLE, new Class[]{Object.class}, MinecraftVersion.MC1_11_R1, MinecraftVersion.MC1_13_R1, new Since(MinecraftVersion.MC1_11_R1, "get")),
|
||||
REGISTRY_SET (ClassWrapper.NMS_REGISTRYSIMPLE, new Class[]{Object.class, Object.class}, MinecraftVersion.MC1_11_R1, MinecraftVersion.MC1_13_R1, new Since(MinecraftVersion.MC1_11_R1, "a")), //FIXME: No Spigot mapping!
|
||||
REGISTRY_GET_INVERSE (ClassWrapper.NMS_REGISTRYMATERIALS, new Class[]{Object.class}, MinecraftVersion.MC1_11_R1, MinecraftVersion.MC1_13_R1, new Since(MinecraftVersion.MC1_11_R1, "b")), //FIXME: No Spigot mapping!
|
||||
REGISTRYMATERIALS_KEYSET (ClassWrapper.NMS_REGISTRYMATERIALS, new Class[]{}, MinecraftVersion.MC1_13_R1, MinecraftVersion.MC1_17_R1, new Since(MinecraftVersion.MC1_13_R1, "keySet")),
|
||||
REGISTRYMATERIALS_GET (ClassWrapper.NMS_REGISTRYMATERIALS, new Class[]{ClassWrapper.NMS_MINECRAFTKEY.getClazz()}, MinecraftVersion.MC1_13_R1, MinecraftVersion.MC1_17_R1, new Since(MinecraftVersion.MC1_13_R1, "get")),
|
||||
REGISTRYMATERIALS_GETKEY (ClassWrapper.NMS_REGISTRYMATERIALS, new Class[]{Object.class}, MinecraftVersion.MC1_13_R2, MinecraftVersion.MC1_17_R1, new Since(MinecraftVersion.MC1_13_R2, "getKey")),
|
||||
|
||||
GAMEPROFILE_DESERIALIZE (ClassWrapper.NMS_GAMEPROFILESERIALIZER, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_7_R4, new Since(MinecraftVersion.MC1_7_R4, "deserialize"), new Since(MinecraftVersion.MC1_18_R1, "readGameProfile(net.minecraft.nbt.CompoundTag)")),
|
||||
GAMEPROFILE_SERIALIZE (ClassWrapper.NMS_GAMEPROFILESERIALIZER, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz(), ClassWrapper.GAMEPROFILE.getClazz()}, MinecraftVersion.MC1_8_R3, new Since(MinecraftVersion.MC1_8_R3, "serialize"), new Since(MinecraftVersion.MC1_18_R1, "writeGameProfile(net.minecraft.nbt.CompoundTag,com.mojang.authlib.GameProfile)")),
|
||||
|
||||
CRAFT_PERSISTENT_DATA_CONTAINER_TO_TAG (ClassWrapper.CRAFT_PERSISTENTDATACONTAINER, new Class[]{}, MinecraftVersion.MC1_14_R1, new Since(MinecraftVersion.MC1_14_R1, "toTagCompound")),
|
||||
CRAFT_PERSISTENT_DATA_CONTAINER_GET_MAP (ClassWrapper.CRAFT_PERSISTENTDATACONTAINER, new Class[]{}, MinecraftVersion.MC1_14_R1, new Since(MinecraftVersion.MC1_14_R1, "getRaw")),
|
||||
CRAFT_PERSISTENT_DATA_CONTAINER_PUT_ALL (ClassWrapper.CRAFT_PERSISTENTDATACONTAINER, new Class[]{ClassWrapper.NMS_NBTTAGCOMPOUND.getClazz()}, MinecraftVersion.MC1_14_R1, new Since(MinecraftVersion.MC1_14_R1, "putAll")),
|
||||
;
|
||||
|
||||
private MinecraftVersion removedAfter;
|
||||
private Since targetVersion;
|
||||
private Method method;
|
||||
private boolean loaded = false;
|
||||
private boolean compatible = false;
|
||||
private String methodName = null;
|
||||
private ClassWrapper parentClassWrapper;
|
||||
|
||||
ReflectionMethod(ClassWrapper targetClass, Class<?>[] args, MinecraftVersion addedSince, MinecraftVersion removedAfter, Since... methodnames){
|
||||
this.removedAfter = removedAfter;
|
||||
this.parentClassWrapper = targetClass;
|
||||
if(!MinecraftVersion.isAtLeastVersion(addedSince) || (this.removedAfter != null && MinecraftVersion.isNewerThan(removedAfter)))return;
|
||||
compatible = true;
|
||||
MinecraftVersion server = MinecraftVersion.getVersion();
|
||||
Since target = methodnames[0];
|
||||
for(Since s : methodnames){
|
||||
if(s.version.getVersionId() <= server.getVersionId() && target.version.getVersionId() < s.version.getVersionId())
|
||||
target = s;
|
||||
}
|
||||
targetVersion = target;
|
||||
String targetMethodName = targetVersion.name;
|
||||
try{
|
||||
if(targetVersion.version.isMojangMapping())
|
||||
targetMethodName = MojangToMapping.getMapping().getOrDefault(targetClass.getMojangName() + "#" + targetVersion.name, "Unmapped" + targetVersion.name);
|
||||
method = targetClass.getClazz().getDeclaredMethod(targetMethodName, args);
|
||||
method.setAccessible(true);
|
||||
loaded = true;
|
||||
methodName = targetVersion.name;
|
||||
}catch(NullPointerException | NoSuchMethodException | SecurityException ex){
|
||||
try{
|
||||
if(targetVersion.version.isMojangMapping())
|
||||
targetMethodName = MojangToMapping.getMapping().getOrDefault(targetClass.getMojangName() + "#" + targetVersion.name, "Unmapped" + targetVersion.name);
|
||||
method = targetClass.getClazz().getMethod(targetMethodName, args);
|
||||
method.setAccessible(true);
|
||||
loaded = true;
|
||||
methodName = targetVersion.name;
|
||||
}catch(NullPointerException | NoSuchMethodException | SecurityException ex2){
|
||||
System.out.println("[NBTAPI] Unable to find the method '" + targetMethodName + "' in '" + (targetClass.getClazz() == null ? targetClass.getMojangName() : targetClass.getClazz().getSimpleName()) + "' Args: " + Arrays.toString(args) + " Enum: " + this); //NOSONAR This gets loaded before the logger is loaded
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ReflectionMethod(ClassWrapper targetClass, Class<?>[] args, MinecraftVersion addedSince, Since... methodnames){
|
||||
this(targetClass, args, addedSince, null, methodnames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the method on a given target object using the given args.
|
||||
*
|
||||
* @param target
|
||||
* @param args
|
||||
* @return Value returned by the method
|
||||
*/
|
||||
public Object run(Object target, Object... args){
|
||||
if(method == null)
|
||||
throw new NbtApiException("Method not loaded! '" + this + "'");
|
||||
try{
|
||||
return method.invoke(target, args);
|
||||
}catch(Exception ex){
|
||||
throw new NbtApiException("Error while calling the method '" + methodName + "', loaded: " + loaded + ", Enum: " + this + " Passed Class: " + target.getClass(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The MethodName, used in this Minecraft Version
|
||||
*/
|
||||
public String getMethodName() {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Has this method been linked
|
||||
*/
|
||||
public boolean isLoaded() {
|
||||
return loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Is this method available in this Minecraft Version
|
||||
*/
|
||||
public boolean isCompatible() {
|
||||
return compatible;
|
||||
}
|
||||
|
||||
public Since getSelectedVersionInfo() {
|
||||
return targetVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Get Wrapper of the parent class
|
||||
*/
|
||||
public ClassWrapper getParentClassWrapper() {
|
||||
return parentClassWrapper;
|
||||
}
|
||||
|
||||
public static class Since{
|
||||
public final MinecraftVersion version;
|
||||
public final String name;
|
||||
public Since(MinecraftVersion version, String name) {
|
||||
this.version = version;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.pretzel.dev.villagertradelimiter.lib;
|
||||
package com.pretzel.dev.villagertradelimiter.settings;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
|
@ -1,4 +1,4 @@
|
|||
package com.pretzel.dev.villagertradelimiter.lib;
|
||||
package com.pretzel.dev.villagertradelimiter.settings;
|
||||
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package com.pretzel.dev.villagertradelimiter.settings;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.lib.Util;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
|
||||
public class Lang {
|
||||
private final FileConfiguration def;
|
||||
private FileConfiguration cfg;
|
||||
|
||||
/**
|
||||
* @param plugin The Bukkit/Spigot/Paper plugin instance
|
||||
* @param reader The file reader for the default messages.yml file (located in the src/main/resources)
|
||||
* @param path The file path for the active messages.yml file (located on the server in plugins/[plugin name])
|
||||
*/
|
||||
public Lang(final Plugin plugin, final Reader reader, final String path) {
|
||||
//Gets the default values, puts them in a temp file, and loads them as a FileConfiguration
|
||||
String[] defLines = Util.readFile(reader);
|
||||
String def = "";
|
||||
if(defLines == null) defLines = new String[0];
|
||||
for(String line : defLines) def += line+"\n";
|
||||
final File defFile = new File(path, "temp.yml");
|
||||
Util.writeFile(defFile, def);
|
||||
this.def = YamlConfiguration.loadConfiguration(defFile);
|
||||
|
||||
//Gets the active values and loads them as a FileConfiguration
|
||||
File file = new File(path,"messages.yml");
|
||||
try {
|
||||
if(file.createNewFile()) Util.writeFile(file, def);
|
||||
} catch (Exception e) {
|
||||
Util.errorMsg(e);
|
||||
}
|
||||
|
||||
this.cfg = null;
|
||||
try {
|
||||
ConfigUpdater.update(plugin, "messages.yml", file);
|
||||
} catch (IOException e) {
|
||||
Util.errorMsg(e);
|
||||
}
|
||||
this.cfg = YamlConfiguration.loadConfiguration(file);
|
||||
defFile.delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key The key (or path) of the section in messages.yml (e.g, common.reloaded)
|
||||
* @return The String value in messages.yml that is mapped to the given key
|
||||
*/
|
||||
public String get(final String key) {
|
||||
return get(key, def.getString("help", ""));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key The key (or path) of the section in messages.yml (e.g, common.reloaded)
|
||||
* @param def The default value to return if the key is not found
|
||||
* @return The String value in messages.yml that is mapped to the given key, or the given default value if the key was not found
|
||||
*/
|
||||
public String get(final String key, final String def) {
|
||||
return Util.replaceColors(this.cfg.getString(key, def));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
package com.pretzel.dev.villagertradelimiter.settings;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.VillagerTradeLimiter;
|
||||
import com.pretzel.dev.villagertradelimiter.lib.Util;
|
||||
import com.pretzel.dev.villagertradelimiter.wrappers.RecipeWrapper;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.enchantments.EnchantmentWrapper;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class Settings {
|
||||
private final VillagerTradeLimiter instance;
|
||||
|
||||
/** @param instance The instance of VillagerTradeLimiter.java */
|
||||
public Settings(final VillagerTradeLimiter instance) { this.instance = instance; }
|
||||
|
||||
/**
|
||||
* @param entity The entity to check the NPC status of
|
||||
* @return True if the entity is an NPC and config is set to ignore NPCs
|
||||
*/
|
||||
public boolean shouldSkipNPC(final Entity entity) {
|
||||
if(entity == null) return true;
|
||||
if(instance.getCfg().getBoolean("IgnoreCitizens", true) && Util.isNPC(entity)) return true;
|
||||
return instance.getCfg().getBoolean("IgnoreShopkeepers", true) && Util.isShopkeeper(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param recipe The wrapped recipe to fetch any overrides for
|
||||
* @param key The key where the fetched value is stored in config.yml (e.g, DisableTrading)
|
||||
* @param defaultValue The default boolean value to use if the key does not exist
|
||||
* @return A boolean value that has the most specific value possible between the global setting and the overrides settings
|
||||
*/
|
||||
public boolean fetchBoolean(final RecipeWrapper recipe, String key, boolean defaultValue) {
|
||||
boolean global = instance.getCfg().getBoolean(key, defaultValue);
|
||||
final ConfigurationSection override = getOverride(recipe.getItemStack("buy"), recipe.getItemStack("sell"));
|
||||
if(override != null) return override.getBoolean(key, global);
|
||||
return global;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param recipe The wrapped recipe to fetch any overrides for
|
||||
* @param key The key where the fetched value is stored in config.yml (e.g, MaxDemand)
|
||||
* @param defaultValue The default integer value to use if the key does not exist
|
||||
* @return An integer value that has the most specific value possible between the global setting and the overrides settings
|
||||
*/
|
||||
public int fetchInt(final RecipeWrapper recipe, String key, int defaultValue) {
|
||||
int global = instance.getCfg().getInt(key, defaultValue);
|
||||
final ConfigurationSection override = getOverride(recipe.getItemStack("buy"), recipe.getItemStack("sell"));
|
||||
if(override != null) return override.getInt(key, global);
|
||||
return global;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param recipe The wrapped recipe to fetch any overrides for
|
||||
* @param key The key where the fetched value is stored in config.yml (e.g, MaxDiscount)
|
||||
* @param defaultValue The default double value to use if the key does not exist
|
||||
* @return A double value that has the most specific value possible between the global setting and the overrides settings
|
||||
*/
|
||||
public double fetchDouble(final RecipeWrapper recipe, String key, double defaultValue) {
|
||||
double global = instance.getCfg().getDouble(key, defaultValue);
|
||||
final ConfigurationSection override = getOverride(recipe.getItemStack("buy"), recipe.getItemStack("sell"));
|
||||
if(override != null) return override.getDouble(key, global);
|
||||
return global;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param result The itemstack for the recipe's result
|
||||
* @param ingredient1 The itemstack for the recipe's first ingredient
|
||||
* @param ingredient2 The itemstack for the recipe's second ingredient
|
||||
* @return The matched type of the item, if any
|
||||
*/
|
||||
public String getType(final ItemStack result, final ItemStack ingredient1, final ItemStack ingredient2) {
|
||||
final String resultType = result.getType().name().toLowerCase();
|
||||
final String ingredient1Type = ingredient1.getType().name().toLowerCase();
|
||||
final String ingredient2Type = ingredient2.getType().name().toLowerCase();
|
||||
final String defaultType;
|
||||
if(result.getType() == Material.EMERALD) {
|
||||
if(ingredient1.getType() == Material.BOOK || ingredient1.getType() == Material.AIR) {
|
||||
defaultType = ingredient2Type;
|
||||
} else {
|
||||
defaultType = ingredient1Type;
|
||||
}
|
||||
} else {
|
||||
defaultType = resultType;
|
||||
}
|
||||
|
||||
if(result.getType() == Material.ENCHANTED_BOOK) {
|
||||
final EnchantmentStorageMeta meta = (EnchantmentStorageMeta) result.getItemMeta();
|
||||
if(meta == null) return defaultType;
|
||||
for(Enchantment key : meta.getStoredEnchants().keySet()) {
|
||||
if (key != null) {
|
||||
final String itemType = key.getKey().getKey() +"_"+meta.getStoredEnchantLevel(key);
|
||||
if(getItem(ingredient1, result, itemType) != null) return itemType;
|
||||
}
|
||||
}
|
||||
return defaultType;
|
||||
}
|
||||
|
||||
final ItemStack ingredient = (ingredient1.getType() == Material.AIR ? ingredient2 : ingredient1);
|
||||
if(getItem(ingredient, result, resultType) != null) return resultType;
|
||||
if(getItem(ingredient, result, ingredient1Type) != null) return ingredient1Type;
|
||||
if(getItem(ingredient, result, ingredient2Type) != null) return ingredient2Type;
|
||||
return defaultType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param buy The first ingredient of the recipe
|
||||
* @param sell The result of the recipe
|
||||
* @return The corresponding override config section for the recipe, if it exists, or null
|
||||
*/
|
||||
public ConfigurationSection getOverride(final ItemStack buy, ItemStack sell) {
|
||||
final ConfigurationSection overrides = instance.getCfg().getConfigurationSection("Overrides");
|
||||
if(overrides == null) return null;
|
||||
for(final String override : overrides.getKeys(false)) {
|
||||
final ConfigurationSection item = this.getItem(buy, sell, override);
|
||||
if(item != null) return item;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param buy The first ingredient of the recipe
|
||||
* @param sell The result of the recipe
|
||||
* @param key The key where the override settings are stored in config.yml
|
||||
* @return The corresponding override config section for the recipe, if it exists, or null
|
||||
*/
|
||||
public ConfigurationSection getItem(final ItemStack buy, final ItemStack sell, String key) {
|
||||
final ConfigurationSection item = instance.getCfg().getConfigurationSection("Overrides."+key);
|
||||
if(item == null) return null;
|
||||
|
||||
String match = "both";
|
||||
if(!key.contains("_")) {
|
||||
//Return the item if the item name is valid
|
||||
if(verify(buy, sell, match, Material.matchMaterial(key))) return item;
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] words = key.split("_");
|
||||
if(words[words.length-1].equalsIgnoreCase("left")) {
|
||||
match = "left";
|
||||
key = key.replace("_left", "");
|
||||
words = Arrays.copyOf(words, words.length-1);
|
||||
} else if(words[words.length-1].equalsIgnoreCase("right")) {
|
||||
match = "right";
|
||||
key = key.replace("_right", "");
|
||||
words = Arrays.copyOf(words, words.length-1);
|
||||
}
|
||||
|
||||
try {
|
||||
//Return the enchanted book item if there's a number in the item name
|
||||
final int level = Integer.parseInt(words[words.length-1]);
|
||||
if(sell.getType() == Material.ENCHANTED_BOOK) {
|
||||
final EnchantmentStorageMeta meta = (EnchantmentStorageMeta) sell.getItemMeta();
|
||||
final Enchantment enchantment = EnchantmentWrapper.getByKey(NamespacedKey.minecraft(key.substring(0, key.lastIndexOf("_"))));
|
||||
if(meta == null || enchantment == null) return null;
|
||||
if(meta.hasStoredEnchant(enchantment) && meta.getStoredEnchantLevel(enchantment) == level) return item;
|
||||
}
|
||||
} catch(NumberFormatException e) {
|
||||
//Return the item if the item name is valid
|
||||
if(verify(buy, sell, match, Material.matchMaterial(key))) return item;
|
||||
return null;
|
||||
} catch(Exception e2) {
|
||||
//Send an error message
|
||||
Util.errorMsg(e2);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param buy The first ingredient of the recipe
|
||||
* @param sell The result of the recipe
|
||||
* @param material The material to compare the recipe against
|
||||
* @return True if a recipe matches an override section, false otherwise
|
||||
*/
|
||||
private boolean verify(final ItemStack buy, final ItemStack sell, final String match, final Material material) {
|
||||
if(match.equals("left")) {
|
||||
if(buy == null) return false;
|
||||
return buy.getType().equals(material);
|
||||
} else if(match.equals("right")) {
|
||||
if(sell == null) return false;
|
||||
return sell.getType().equals(material);
|
||||
}
|
||||
if(buy == null && sell == null) return false;
|
||||
if(buy == null) return sell.getType() == material;
|
||||
if(sell == null) return buy.getType() == material;
|
||||
return ((buy.getType() == material) || (sell.getType() == material));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package com.pretzel.dev.villagertradelimiter.wrappers;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.lib.Util;
|
||||
import de.tr7zw.changeme.nbtapi.NBTCompound;
|
||||
|
||||
public class GossipWrapper {
|
||||
private final NBTCompound gossip;
|
||||
|
||||
public enum GossipType {
|
||||
MAJOR_NEGATIVE(-5),
|
||||
MINOR_NEGATIVE(-1),
|
||||
TRADING(1),
|
||||
MINOR_POSITIVE(1),
|
||||
MAJOR_POSITIVE(5),
|
||||
OTHER(0);
|
||||
|
||||
private final int weight;
|
||||
GossipType(int weight) { this.weight = weight; }
|
||||
int getWeight() { return this.weight; }
|
||||
}
|
||||
|
||||
/** @param gossip The NBTCompound that contains the villager's NBT data of the gossip */
|
||||
public GossipWrapper(final NBTCompound gossip) { this.gossip = gossip; }
|
||||
|
||||
/** @return The GossipType of this gossip: MAJOR_NEGATIVE, MINOR_NEGATIVE, TRADING, MINOR_POSITIVE, MAJOR_POSITIVE, or OTHER if not found */
|
||||
public GossipType getType() {
|
||||
try {
|
||||
return GossipType.valueOf(gossip.getString("Type").toUpperCase());
|
||||
} catch (IllegalArgumentException e) {
|
||||
return GossipType.OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param isOld Whether the server is older than 1.16 or not. Minecraft changed how UUID's are represented in 1.16
|
||||
* @return A string representation of the target UUID, for use when matching the target UUID to a player's UUID
|
||||
*/
|
||||
public String getTargetUUID(final boolean isOld) {
|
||||
//BEFORE 1.16 (< 1.16)
|
||||
if(isOld) return gossip.getLong("TargetMost")+";"+gossip.getLong("TargetLeast");
|
||||
|
||||
//AFTER 1.16 (>= 1.16)
|
||||
return Util.intArrayToString(gossip.getIntArray("Target"));
|
||||
}
|
||||
|
||||
/** @return The strength of this gossip, which is a value between 0 and: 25, 100, or 200, depending on the gossip type */
|
||||
public int getValue() { return gossip.getInteger("Value"); }
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package com.pretzel.dev.villagertradelimiter.wrappers;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBTCompound;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
public class IngredientWrapper {
|
||||
private final NBTCompound recipe;
|
||||
private final String key;
|
||||
private final ItemStack itemStack;
|
||||
|
||||
/**
|
||||
* @param recipe The NBTCompound that contains the recipe's NBT data of the ingredient
|
||||
* @param key The key under which the recipe is located
|
||||
*/
|
||||
public IngredientWrapper(final NBTCompound recipe, final String key) {
|
||||
this.recipe = recipe;
|
||||
this.key = key;
|
||||
this.itemStack = getItemStack();
|
||||
}
|
||||
|
||||
/** @return The {@link ItemStack} representing the data in the recipe */
|
||||
public ItemStack getItemStack() {
|
||||
return recipe.getItemStack(key);
|
||||
}
|
||||
|
||||
/** @param itemStack The {@link ItemStack} which will replace the item in the recipe */
|
||||
public void setItemStack(final ItemStack itemStack) {
|
||||
recipe.setItemStack(key, itemStack);
|
||||
}
|
||||
|
||||
/** Resets the material ID and the amount of this ingredient to default values */
|
||||
public void reset() {
|
||||
setItemStack(itemStack);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package com.pretzel.dev.villagertradelimiter.wrappers;
|
||||
|
||||
import com.pretzel.dev.villagertradelimiter.lib.Util;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class PlayerWrapper {
|
||||
private final OfflinePlayer player;
|
||||
|
||||
/** @param player The offline player that this wrapper wraps */
|
||||
public PlayerWrapper(final OfflinePlayer player) { this.player = player; }
|
||||
|
||||
/**
|
||||
* @param isOld Whether the server is older than 1.16 or not. Minecraft changed how UUID's are represented in 1.16
|
||||
* @return A string representation of the player's UUID, for use when matching the player's UUID to a gossip's target UUID
|
||||
*/
|
||||
public String getUUID(final boolean isOld) {
|
||||
final UUID uuid = player.getUniqueId();
|
||||
|
||||
//BEFORE 1.16 (< 1.16)
|
||||
if(isOld) return uuid.getMostSignificantBits()+";"+uuid.getLeastSignificantBits();
|
||||
|
||||
//AFTER 1.16 (>= 1.16)
|
||||
final String uuidString = uuid.toString().replace("-", "");
|
||||
int[] intArray = new int[4];
|
||||
for(int i = 0; i < 4; i++) {
|
||||
intArray[i] = (int)Long.parseLong(uuidString.substring(8*i, 8*(i+1)), 16);
|
||||
}
|
||||
return Util.intArrayToString(intArray);
|
||||
}
|
||||
|
||||
/** @return The regular, online player of this wrapper's offline player, or null if the player is not online */
|
||||
public Player getPlayer() { return player.getPlayer(); }
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package com.pretzel.dev.villagertradelimiter.wrappers;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBTCompound;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class RecipeWrapper {
|
||||
//A list of all the items with a default MaxUses of 12 and 3, respectively
|
||||
private static final Material[] MAX_USES_12 = new Material[]{Material.IRON_HELMET, Material.IRON_CHESTPLATE, Material.IRON_LEGGINGS, Material.IRON_BOOTS, Material.IRON_INGOT, Material.BELL, Material.CHAINMAIL_HELMET, Material.CHAINMAIL_CHESTPLATE, Material.CHAINMAIL_LEGGINGS, Material.CHAINMAIL_BOOTS, Material.LAVA_BUCKET, Material.DIAMOND, Material.SHIELD, Material.RABBIT_STEW, Material.DRIED_KELP_BLOCK, Material.SWEET_BERRIES, Material.MAP, Material.FILLED_MAP, Material.COMPASS, Material.ITEM_FRAME, Material.GLOBE_BANNER_PATTERN, Material.WHITE_BANNER, Material.LIGHT_GRAY_BANNER, Material.GRAY_BANNER, Material.BLACK_BANNER, Material.BROWN_BANNER, Material.ORANGE_BANNER, Material.YELLOW_BANNER, Material.LIME_BANNER, Material.GREEN_BANNER, Material.CYAN_BANNER, Material.BLUE_BANNER, Material.LIGHT_BLUE_BANNER, Material.PURPLE_BANNER, Material.MAGENTA_BANNER, Material.PINK_BANNER, Material.RED_BANNER, Material.WHITE_BED, Material.LIGHT_GRAY_BED, Material.GRAY_BED, Material.BLACK_BED, Material.BROWN_BED, Material.ORANGE_BED, Material.YELLOW_BED, Material.LIME_BED, Material.GREEN_BED, Material.CYAN_BED, Material.BLUE_BED, Material.LIGHT_BLUE_BED, Material.PURPLE_BED, Material.MAGENTA_BED, Material.PINK_BED, Material.RED_BED, Material.REDSTONE, Material.GOLD_INGOT, Material.LAPIS_LAZULI, Material.RABBIT_FOOT, Material.GLOWSTONE, Material.SCUTE, Material.GLASS_BOTTLE, Material.ENDER_PEARL, Material.NETHER_WART, Material.EXPERIENCE_BOTTLE, Material.PUMPKIN, Material.PUMPKIN_PIE, Material.MELON, Material.COOKIE, Material.CAKE, Material.SUSPICIOUS_STEW, Material.GOLDEN_CARROT, Material.GLISTERING_MELON_SLICE, Material.CAMPFIRE, Material.TROPICAL_FISH, Material.PUFFERFISH, Material.BIRCH_BOAT, Material.ACACIA_BOAT, Material.OAK_BOAT, Material.DARK_OAK_BOAT, Material.SPRUCE_BOAT, Material.JUNGLE_BOAT, Material.ARROW, Material.FLINT, Material.STRING, Material.TRIPWIRE_HOOK, Material.TIPPED_ARROW, Material.LEATHER_HELMET, Material.LEATHER_CHESTPLATE, Material.LEATHER_LEGGINGS, Material.LEATHER_BOOTS, Material.LEATHER, Material.RABBIT_HIDE, Material.LEATHER_HORSE_ARMOR, Material.SADDLE, Material.BOOK, Material.ENCHANTED_BOOK, Material.BOOKSHELF, Material.INK_SAC, Material.GLASS, Material.WRITABLE_BOOK, Material.CLOCK, Material.NAME_TAG, Material.QUARTZ, Material.QUARTZ_PILLAR, Material.QUARTZ_BLOCK, Material.TERRACOTTA, Material.WHITE_TERRACOTTA, Material.LIGHT_GRAY_TERRACOTTA, Material.GRAY_TERRACOTTA, Material.BLACK_TERRACOTTA, Material.BROWN_TERRACOTTA, Material.ORANGE_TERRACOTTA, Material.YELLOW_TERRACOTTA, Material.LIME_TERRACOTTA, Material.GREEN_TERRACOTTA, Material.CYAN_TERRACOTTA, Material.BLUE_TERRACOTTA, Material.LIGHT_BLUE_TERRACOTTA, Material.PURPLE_TERRACOTTA, Material.MAGENTA_TERRACOTTA, Material.PINK_TERRACOTTA, Material.RED_TERRACOTTA, Material.WHITE_GLAZED_TERRACOTTA, Material.LIGHT_GRAY_GLAZED_TERRACOTTA, Material.GRAY_GLAZED_TERRACOTTA, Material.BLACK_GLAZED_TERRACOTTA, Material.BROWN_GLAZED_TERRACOTTA, Material.ORANGE_GLAZED_TERRACOTTA, Material.YELLOW_GLAZED_TERRACOTTA, Material.LIME_GLAZED_TERRACOTTA, Material.GREEN_GLAZED_TERRACOTTA, Material.CYAN_GLAZED_TERRACOTTA, Material.BLUE_GLAZED_TERRACOTTA, Material.LIGHT_BLUE_GLAZED_TERRACOTTA, Material.PURPLE_GLAZED_TERRACOTTA, Material.MAGENTA_GLAZED_TERRACOTTA, Material.PINK_GLAZED_TERRACOTTA, Material.RED_GLAZED_TERRACOTTA, Material.SHEARS, Material.PAINTING, Material.STONE_AXE, Material.STONE_SHOVEL, Material.STONE_PICKAXE, Material.STONE_HOE};
|
||||
private static final Material[] MAX_USES_3 = new Material[]{Material.DIAMOND_HELMET, Material.DIAMOND_CHESTPLATE, Material.DIAMOND_LEGGINGS, Material.DIAMOND_BOOTS, Material.DIAMOND_SWORD, Material.DIAMOND_AXE, Material.DIAMOND_SHOVEL, Material.DIAMOND_PICKAXE, Material.DIAMOND_HOE, Material.IRON_SWORD, Material.IRON_AXE, Material.IRON_SHOVEL, Material.IRON_PICKAXE, Material.FISHING_ROD, Material.BOW, Material.CROSSBOW};
|
||||
|
||||
private final NBTCompound recipe;
|
||||
private final IngredientWrapper ingredient1;
|
||||
private final IngredientWrapper ingredient2;
|
||||
private final IngredientWrapper result;
|
||||
private final int specialPrice;
|
||||
|
||||
/** @param recipe The NBTCompound that contains the villager's NBT data of the recipe */
|
||||
public RecipeWrapper(final NBTCompound recipe) {
|
||||
this.recipe = recipe;
|
||||
this.ingredient1 = new IngredientWrapper(recipe, "buy");
|
||||
this.ingredient2 = new IngredientWrapper(recipe, "buyB");
|
||||
this.result = new IngredientWrapper(recipe, "sell");
|
||||
this.specialPrice = getSpecialPrice();
|
||||
}
|
||||
|
||||
/** @param demand The demand, which increases prices if you buy too often. Negative values are ignored. */
|
||||
public void setDemand(int demand) { recipe.setInteger("demand", demand); }
|
||||
|
||||
/** @param specialPrice The discount, which is added to the base price. A negative value will decrease the price, and a positive value will increase the price. */
|
||||
public void setSpecialPrice(int specialPrice) { recipe.setInteger("specialPrice", specialPrice); }
|
||||
|
||||
/** @param maxUses The maximum number of times a player can make a trade before the villager restocks */
|
||||
public void setMaxUses(int maxUses) { recipe.setInteger("maxUses", maxUses); }
|
||||
|
||||
/** Resets the recipe back to its default state */
|
||||
public void reset() {
|
||||
this.setSpecialPrice(this.specialPrice);
|
||||
this.ingredient1.reset();
|
||||
this.ingredient2.reset();
|
||||
this.result.reset();
|
||||
|
||||
int maxUses = 16;
|
||||
Material buyMaterial = recipe.getItemStack("buy").getType();
|
||||
Material sellMaterial = recipe.getItemStack("sell").getType();
|
||||
if(Arrays.asList(MAX_USES_12).contains(buyMaterial) || Arrays.asList(MAX_USES_12).contains(sellMaterial)) {
|
||||
maxUses = 12;
|
||||
} else if(Arrays.asList(MAX_USES_3).contains(buyMaterial) || Arrays.asList(MAX_USES_3).contains(sellMaterial)) {
|
||||
maxUses = 3;
|
||||
}
|
||||
setMaxUses(maxUses);
|
||||
}
|
||||
|
||||
/** @return The wrapper for the first ingredient */
|
||||
public IngredientWrapper getIngredient1() { return ingredient1; }
|
||||
|
||||
/** @return The wrapper for the second ingredient */
|
||||
public IngredientWrapper getIngredient2() { return ingredient2; }
|
||||
|
||||
/** @return The wrapper for the result */
|
||||
public IngredientWrapper getResult() { return result; }
|
||||
|
||||
/** @return The demand for this recipe (increases the price when above 0) */
|
||||
public int getDemand() { return recipe.getInteger("demand"); }
|
||||
|
||||
/** @return The price multiplier for this recipe (controls how strongly gossips, demand, etc. affect the price) */
|
||||
public float getPriceMultiplier() { return recipe.getFloat("priceMultiplier"); }
|
||||
|
||||
/** @return The discount, which is added to the base price. A negative value will decrease the price, and a positive value will increase the price. */
|
||||
public int getSpecialPrice() { return recipe.getInteger("specialPrice"); }
|
||||
|
||||
/** @return The maximum number of times a player can make a trade before the villager restocks */
|
||||
public int getMaxUses() { return recipe.getInteger("maxUses"); }
|
||||
|
||||
/** @return The ItemStack representation of an ingredient or the result */
|
||||
public ItemStack getItemStack(final String key) { return recipe.getItemStack(key); }
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
package com.pretzel.dev.villagertradelimiter.wrappers;
|
||||
|
||||
import de.tr7zw.changeme.nbtapi.NBTCompound;
|
||||
import de.tr7zw.changeme.nbtapi.NBTCompoundList;
|
||||
import de.tr7zw.changeme.nbtapi.NBTEntity;
|
||||
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
|
||||
import org.bukkit.entity.Villager;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class VillagerWrapper {
|
||||
private final Villager villager;
|
||||
private final NBTEntity entity;
|
||||
private final ItemStack[] contents;
|
||||
|
||||
/** @param villager The Villager to store in this wrapper */
|
||||
public VillagerWrapper(final Villager villager) {
|
||||
this.villager = villager;
|
||||
this.entity = new NBTEntity(villager);
|
||||
this.contents = new ItemStack[villager.getInventory().getContents().length];
|
||||
for(int i = 0; i < this.contents.length; i++) {
|
||||
ItemStack item = villager.getInventory().getItem(i);
|
||||
this.contents[i] = (item == null ? null : item.clone());
|
||||
}
|
||||
}
|
||||
|
||||
/** @return a list of wrapped recipes for the villager */
|
||||
public List<RecipeWrapper> getRecipes() {
|
||||
final List<RecipeWrapper> recipes = new ArrayList<>();
|
||||
|
||||
//Add the recipes from the villager's NBT data into a list of wrapped recipes
|
||||
final NBTCompound offers = entity.getCompound("Offers");
|
||||
if(offers == null) return recipes;
|
||||
final NBTCompoundList nbtRecipes = offers.getCompoundList("Recipes");
|
||||
for(ReadWriteNBT nbtRecipe : nbtRecipes) {
|
||||
recipes.add(new RecipeWrapper((NBTCompound)nbtRecipe));
|
||||
}
|
||||
return recipes;
|
||||
}
|
||||
|
||||
/** @return A list of wrapped gossips for the villager */
|
||||
private List<GossipWrapper> getGossips() {
|
||||
final List<GossipWrapper> gossips = new ArrayList<>();
|
||||
if(!entity.hasTag("Gossips")) return gossips;
|
||||
|
||||
//Add the gossips from the villager's NBT data into a list of wrapped gossips
|
||||
final NBTCompoundList nbtGossips = entity.getCompoundList("Gossips");
|
||||
for(ReadWriteNBT nbtGossip : nbtGossips) {
|
||||
gossips.add(new GossipWrapper((NBTCompound) nbtGossip));
|
||||
}
|
||||
return gossips;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param villager The wrapped villager that contains the gossips
|
||||
* @param player The wrapped player that the gossips are about
|
||||
* @param isOld Whether the server is older than 1.16 or not. Minecraft changed how UUID's are represented in 1.16
|
||||
* @return the total reputation (from gossips) for a player
|
||||
*/
|
||||
public int getTotalReputation(@NonNull final VillagerWrapper villager, @NonNull final PlayerWrapper player, final boolean isOld) {
|
||||
int totalReputation = 0;
|
||||
|
||||
final String playerUUID = player.getUUID(isOld);
|
||||
final List<GossipWrapper> gossips = villager.getGossips();
|
||||
for(GossipWrapper gossip : gossips) {
|
||||
final GossipWrapper.GossipType type = gossip.getType();
|
||||
if(type == null || type == GossipWrapper.GossipType.OTHER) continue;
|
||||
|
||||
final String targetUUID = gossip.getTargetUUID(isOld);
|
||||
if(targetUUID.equals(playerUUID)) {
|
||||
totalReputation += gossip.getValue() * type.getWeight();
|
||||
}
|
||||
}
|
||||
return totalReputation;
|
||||
}
|
||||
|
||||
/** Resets the villager's NBT data to default */
|
||||
public void reset() {
|
||||
//Reset the recipes back to their default ingredients, MaxUses, and discounts
|
||||
for(RecipeWrapper recipe : this.getRecipes()) {
|
||||
recipe.reset();
|
||||
}
|
||||
|
||||
this.villager.getInventory().clear();
|
||||
this.villager.getInventory().setContents(this.contents);
|
||||
}
|
||||
}
|
|
@ -8,33 +8,89 @@
|
|||
# This helps me keep track of what server versions are being used. Please leave this set to true.
|
||||
bStats: true
|
||||
|
||||
# Database connection settings
|
||||
database:
|
||||
mysql: false
|
||||
host: 127.0.0.1
|
||||
port: 3306
|
||||
database: villagertradelimiter
|
||||
username: root
|
||||
password: root
|
||||
encoding: utf8
|
||||
useSSL: false
|
||||
|
||||
# Add world names for worlds that you want to have unaltered, vanilla villager trading in. Set to [] to disable this feature.
|
||||
DisableWorlds:
|
||||
- world_nether
|
||||
- world_the_end
|
||||
|
||||
# Ignore Citizens NPCs, and/or Shopkeepers NPCs if true
|
||||
IgnoreCitizens: true
|
||||
IgnoreShopkeepers: true
|
||||
|
||||
# Ignore interactions when the player is holding one of these item types (e.g. spawn_egg, name_tag)
|
||||
# Without disabling nametag, you cannot rename a villager with a profession. Do not remove name_tag if you want to retain vanilla behavior.
|
||||
# Ghast spawn egg is added to add compatibility with Safarinet plugin. If your server doesn't give ghast egg to noromal players you can ignore it.
|
||||
# Set to [] to disable this feature.
|
||||
IgnoreHeldItems:
|
||||
- "name_tag"
|
||||
- "ghast_spawn_egg"
|
||||
|
||||
# Add world names for worlds that you want to completely disable ALL villager trading. Set to [] to disable this feature.
|
||||
DisableTrading:
|
||||
- world_nether
|
||||
- world_the_end
|
||||
|
||||
# Add profession names that you want to prevent villagers from acquiring
|
||||
DisableProfessions: []
|
||||
|
||||
# Add item names that you want to prevent villagers from offering as trades.
|
||||
# This is a permanent change. The items can't be re-added to the villager's trades.
|
||||
DisableItems: []
|
||||
|
||||
# The maximum level of the "Hero of the Village" (HotV) effect that a player can have. This limits HotV price decreases.
|
||||
# * Set to -1 to disable this feature and keep vanilla behavior.
|
||||
# * Set to a number between 0 and 5 to set the maximum HotV effect level players can have
|
||||
MaxHeroLevel: 1
|
||||
# For more information, see https://minecraft.fandom.com/wiki/Hero_of_the_Village#Price_decrement
|
||||
MaxHeroLevel: -1
|
||||
|
||||
# The maximum discount (%) you can get from trading/healing zombie villagers. This limits reputation-based price decreases.
|
||||
# * Set to -1.0 to disable this feature and keep vanilla behavior
|
||||
# * Set to a number between 0.0 and 1.0 to set the maximum discount a player can get. (NOTE: 30% = 0.3)
|
||||
# * Set to a number between 0.0 and 1.0 to limit the maximum discount a player can get. (NOTE: 30% = 0.3)
|
||||
# * Set to a number above 1.0 to increase the maximum discount a player can get. (NOTE: 250% = 2.5)
|
||||
MaxDiscount: 0.3
|
||||
|
||||
# The maximum demand for all items. This limits demand-based price increases.
|
||||
# * Set to -1 to disable this feature and keep vanilla behavior
|
||||
# * Set to 0 or higher to set the maximum demand for all items
|
||||
# WARNING: The previous demand cannot be recovered if it was higher than the MaxDemand.
|
||||
# For more information, see https://minecraft.fandom.com/wiki/Trading#Economics
|
||||
MaxDemand: -1
|
||||
|
||||
# The maximum number of times a player can make any trade before a villager is out of stock.
|
||||
# * Set to -1 to disable this feature and keep vanilla behavior
|
||||
# * Set to 0 or higher to change the maximum number of uses for all items
|
||||
# For default vanilla settings, see https://minecraft.fandom.com/el/wiki/Trading#Java_Edition
|
||||
# For more information, see https://minecraft.fandom.com/el/wiki/Trading#Java_Edition
|
||||
MaxUses: -1
|
||||
|
||||
# The per-player, per-trade cooldown in real-world time.
|
||||
# After a player makes a trade <MaxUses> times, the trade will be disabled for the player until the cooldown is over.
|
||||
# * Set to 0 to disable this feature and keep vanilla behavior
|
||||
# * Set to a number and interval to add a per-player, per-trade cooldown for all trades (see below)
|
||||
# A valid cooldown follows the <Number><Interval> format, such as 7d or 30s. The valid intervals are:
|
||||
# * s = seconds (e.g. 30s)
|
||||
# * m = minutes (e.g. 10m)
|
||||
# * h = hours (e.g. 1h)
|
||||
# * d = days (e.g. 3d)
|
||||
# * w = weeks (e.g. 2w)
|
||||
Cooldown: 0
|
||||
|
||||
# The per-villager, per-trade cooldown in real-world time.
|
||||
# This is the same as Cooldown, but applies to a villager's restocking function
|
||||
# * Set to 0 to disable this feature and keep vanilla behavior
|
||||
# * Set to a number and interval to add a per-villager, per-trade cooldown for all trades (see below)
|
||||
Restock: 0
|
||||
|
||||
|
||||
#-------------------------------- PER-ITEM SETTINGS --------------------------------#
|
||||
# Override the global settings for individual items. To disable, set like this --> Overrides: none
|
||||
|
@ -51,14 +107,23 @@ Overrides:
|
|||
name_tag:
|
||||
MaxDiscount: -1.0
|
||||
MaxDemand: 60
|
||||
MaxUses: 2
|
||||
Cooldown: 7d
|
||||
Item1:
|
||||
Material: "paper"
|
||||
Material: "book"
|
||||
Amount: 64
|
||||
Item2:
|
||||
Material: "ink_sac"
|
||||
Amount: 32
|
||||
MaxUses: 64
|
||||
Amount: 48
|
||||
Result:
|
||||
Material: "name_tag"
|
||||
Amount: 2
|
||||
flint_left:
|
||||
MaxUses: 8
|
||||
flint_right:
|
||||
MaxUses: 1
|
||||
clock:
|
||||
MaxDemand: 12
|
||||
paper:
|
||||
Disabled: true
|
||||
MaxUses: 1
|
||||
Restock: 1h
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# Command help messages (description on hover). Format: [Permission]Usage;Description
|
||||
help: |-
|
||||
[villagertradelimiter.use]&a----- VTL Commands -----
|
||||
[villagertradelimiter.use]&b/vtl;&fshows this help message
|
||||
[villagertradelimiter.reload]&b/vtl reload;&freloads config.yml and messages.yml
|
||||
[villagertradelimiter.see]&b/vtl see <player>;&fshows the adjusted trades for another player
|
||||
|
||||
# Common messages:
|
||||
common:
|
||||
reloaded: "&eVillagerTradeLimiter (VTL) &ahas been reloaded!"
|
||||
noconsole: "&cYou cannot use this command from the console."
|
||||
noargs: "&cNot enough arguments! For command usage, see &b/vtl"
|
||||
|
||||
# Messages for the /vtl see <player> command:
|
||||
see:
|
||||
success: "&aShowing the adjusted trades for &b{player}&a..."
|
||||
noplayer: "&cInvalid player &b{player}&c! Please use a valid player's name."
|
||||
novillager: "&cInvalid entity! Please look at the villager you want to check."
|
||||
noworld: "&cVTL is disabled in this world!"
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
name: VillagerTradeLimiter
|
||||
author: PretzelJohn
|
||||
main: com.pretzel.dev.villagertradelimiter.VillagerTradeLimiter
|
||||
version: 1.4.3
|
||||
version: 1.6.4
|
||||
api-version: 1.14
|
||||
|
||||
commands:
|
||||
|
@ -16,10 +16,18 @@ permissions:
|
|||
children:
|
||||
villagertradelimiter.use: true
|
||||
villagertradelimiter.reload: true
|
||||
villagertradelimiter.see: true
|
||||
villagertradelimiter.invsee: true
|
||||
default: op
|
||||
villagertradelimiter.use:
|
||||
description: Allows players to use VillagerTradeLimiter.
|
||||
default: op
|
||||
villagertradelimiter.reload:
|
||||
description: Allows players to reload config.yml.
|
||||
default: op
|
||||
villagertradelimiter.see:
|
||||
description: Allows players to see the trades for another player
|
||||
default: op
|
||||
villagertradelimiter.invsee:
|
||||
description: Allows players to see inventory of a villager
|
||||
default: op
|
Loading…
Reference in New Issue