Compare commits
26 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7c9bf21ced | ||
![]() |
f4cb5f58e5 | ||
![]() |
3fde89e1ca | ||
![]() |
5701a20cc8 | ||
![]() |
97e17cba3f | ||
![]() |
8acef93d7d | ||
![]() |
353f97a5e8 | ||
![]() |
a3f0fcf5aa | ||
![]() |
cb10fa6720 | ||
![]() |
1c2d966791 | ||
![]() |
092aa33303 | ||
![]() |
a740b34e4e | ||
![]() |
b5d1079a55 | ||
![]() |
7b1c03ff69 | ||
![]() |
fcc025a1a2 | ||
![]() |
9620b230e2 | ||
![]() |
67b2488976 | ||
![]() |
6ac5c1de0e | ||
![]() |
eb5b2beb41 | ||
![]() |
4082e51aa2 | ||
![]() |
d912715741 | ||
![]() |
3b25897490 | ||
![]() |
b058f5f658 | ||
![]() |
7feda4b517 | ||
![]() |
6295fa609c | ||
![]() |
a1cd3a476b |
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@ -7,8 +7,6 @@ on:
|
|||||||
# Triggers the workflow on push or pull request events but only for the master branch
|
# Triggers the workflow on push or pull request events but only for the master branch
|
||||||
push:
|
push:
|
||||||
branches: [ master ]
|
branches: [ master ]
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
|
|
||||||
# Allows you to run this workflow manually from the Actions tab
|
# Allows you to run this workflow manually from the Actions tab
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
@ -39,11 +37,17 @@ jobs:
|
|||||||
jar_url=$(curl https://hub.spigotmc.org/nexus/content/repositories/snapshots/org/spigotmc/spigot-api/1.17-R0.1-SNAPSHOT/ | grep "shaded.jar\"" | tail -1 | sed "s/.*href=\"\(.*\)\".*/\1/g")
|
jar_url=$(curl https://hub.spigotmc.org/nexus/content/repositories/snapshots/org/spigotmc/spigot-api/1.17-R0.1-SNAPSHOT/ | grep "shaded.jar\"" | tail -1 | sed "s/.*href=\"\(.*\)\".*/\1/g")
|
||||||
curl $jar_url > spigot-shaded.jar
|
curl $jar_url > spigot-shaded.jar
|
||||||
find -name "*.java" > sources
|
find -name "*.java" > sources
|
||||||
javac -cp spigot-shaded.jar @sources -d build
|
curl -s https://github.com/GabrielTofvesson/SpigotWizCompat/releases | grep "latest/SpigotWizCompat-.*jar" | cut -d : -f 2,3 | sed "s/[^\"]*\"\\([^\"]*\\)\".*/https:\\/\\/github.com\\/\\1/g" | wget -O SpigotWizCompat.jar -qi -
|
||||||
|
javac -Xlint:deprecation -classpath "spigot-shaded.jar:SpigotWizCompat.jar" @sources -d build
|
||||||
cp res/* build
|
cp res/* build
|
||||||
rm sources
|
rm sources
|
||||||
cd build
|
cd build
|
||||||
jar cvf build.jar *
|
jar cvf build.jar *
|
||||||
|
|
||||||
|
- name: Rename Artifacts
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
mv build.jar $(sha1sum build.jar | sed "s/\([^ ]*\) .*/SpigotInvTweaks-\\1.jar/g")
|
||||||
|
|
||||||
- uses: "marvinpinto/action-automatic-releases@latest"
|
- uses: "marvinpinto/action-automatic-releases@latest"
|
||||||
with:
|
with:
|
||||||
@ -52,4 +56,4 @@ jobs:
|
|||||||
prerelease: true
|
prerelease: true
|
||||||
title: "CI Build"
|
title: "CI Build"
|
||||||
files: |
|
files: |
|
||||||
build/build.jar
|
build/SpigotInvTweaks-*.jar
|
||||||
|
9
.idea/libraries/SpigotWizCompat.xml
generated
Normal file
9
.idea/libraries/SpigotWizCompat.xml
generated
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<component name="libraryTable">
|
||||||
|
<library name="SpigotWizCompat">
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$PROJECT_DIR$/lib/SpigotWizCompat.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
|
</component>
|
@ -9,5 +9,7 @@
|
|||||||
<orderEntry type="jdk" jdkName="16" jdkType="JavaSDK" />
|
<orderEntry type="jdk" jdkName="16" jdkType="JavaSDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="spigot-1.15.5-R0.1" level="project" />
|
<orderEntry type="library" name="spigot-1.15.5-R0.1" level="project" />
|
||||||
|
<orderEntry type="library" name="SpigotWizCompat" level="project" />
|
||||||
|
<orderEntry type="library" name="SpigotWizCompat" level="project" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
@ -9,4 +9,6 @@ search:
|
|||||||
searchRadiusY: 8
|
searchRadiusY: 8
|
||||||
searchRadiusZ: 8
|
searchRadiusZ: 8
|
||||||
ghostClick: true
|
ghostClick: true
|
||||||
capitator: true
|
capitator: true
|
||||||
|
capitatorHungerPerBlock: 0.03125
|
||||||
|
capitatorMinHunger: 3
|
@ -1,8 +1,10 @@
|
|||||||
name: InventoryTweaks
|
name: InventoryTweaks
|
||||||
version: 1.3.3
|
version: 1.4.1
|
||||||
author: IKEA_Jesus
|
author: IKEA_Jesus
|
||||||
main: dev.w1zzrd.invtweaks.InvTweaksPlugin
|
main: dev.w1zzrd.invtweaks.InvTweaksPlugin
|
||||||
api-version: 1.13
|
api-version: 1.13
|
||||||
|
depend:
|
||||||
|
- WizCompat
|
||||||
commands:
|
commands:
|
||||||
sort:
|
sort:
|
||||||
description: Sort chest you are looking at
|
description: Sort chest you are looking at
|
||||||
@ -16,7 +18,23 @@ commands:
|
|||||||
description: Search for a given item in all nearby inventories
|
description: Search for a given item in all nearby inventories
|
||||||
usage: /<command> {item type}
|
usage: /<command> {item type}
|
||||||
permission: invtweaks.search
|
permission: invtweaks.search
|
||||||
|
find:
|
||||||
|
description: Show all chests that contain a given item
|
||||||
|
usage: /<command> {item type}
|
||||||
|
permission: invtweaks.find
|
||||||
capitator:
|
capitator:
|
||||||
description: Toggle tree capitation for an axe in your main hand
|
description: Toggle tree capitation for an axe in your main hand
|
||||||
usage: /<command>
|
usage: /<command>
|
||||||
permission: invtweaks.capitator
|
permission: invtweaks.capitator
|
||||||
|
chestname:
|
||||||
|
description: Add a floating nametag to a chest
|
||||||
|
usage: /<command> {name}
|
||||||
|
permission: invtweaks.spawnfake
|
||||||
|
growup:
|
||||||
|
description: Age baby animals
|
||||||
|
usage: /<command> [radius]
|
||||||
|
permission: invtweaks.growup
|
||||||
|
rewool:
|
||||||
|
description: Put the wool back on sheep
|
||||||
|
usage: /<command> [radius]
|
||||||
|
permission: invtweaks.rewool
|
@ -1,93 +0,0 @@
|
|||||||
package dev.w1zzrd.invtweaks;
|
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.configuration.InvalidConfigurationException;
|
|
||||||
import org.bukkit.configuration.file.FileConfiguration;
|
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
|
||||||
import org.bukkit.configuration.serialization.ConfigurationSerializable;
|
|
||||||
import org.bukkit.plugin.Plugin;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manager for persistent data storage for a plugin
|
|
||||||
*/
|
|
||||||
public class DataStore {
|
|
||||||
|
|
||||||
private static final Logger logger = Bukkit.getLogger();
|
|
||||||
|
|
||||||
private final File storeFile;
|
|
||||||
private final FileConfiguration config;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a data store with the given name. This will attempt to load a yaml file named after the store
|
|
||||||
* @param storeName Name of the store to load. File will be named storeName + ".yml"
|
|
||||||
* @param plugin Plugin to associate the data store with
|
|
||||||
*/
|
|
||||||
public DataStore(final String storeName, final Plugin plugin) {
|
|
||||||
storeFile = new File(plugin.getDataFolder(), storeName + ".yml");
|
|
||||||
config = YamlConfiguration.loadConfiguration(storeFile);
|
|
||||||
|
|
||||||
// Save config in case it doesn't exist
|
|
||||||
saveData();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load a value from the data store
|
|
||||||
* @param path Path in the file to load the data from
|
|
||||||
* @param defaultValue Getter for a default value, in case the data does not exist in the store
|
|
||||||
* @param <T> Type of the data to load
|
|
||||||
* @return Data at the given path, if available, else the default value
|
|
||||||
*/
|
|
||||||
public <T extends ConfigurationSerializable> T loadData(final String path, final DefaultGetter<T> defaultValue) {
|
|
||||||
final T value = (T) config.get(path);
|
|
||||||
return value == null ? defaultValue.get() : value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save data at a given path in the store
|
|
||||||
* @param path Path to store data at
|
|
||||||
* @param value Data to store
|
|
||||||
* @param <T> Type of {@link ConfigurationSerializable} data to store
|
|
||||||
*/
|
|
||||||
public <T extends ConfigurationSerializable> void storeData(final String path, final T value) {
|
|
||||||
config.set(path, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Save the current data store in memory to persistent memory
|
|
||||||
*/
|
|
||||||
public void saveData() {
|
|
||||||
try {
|
|
||||||
config.save(storeFile);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.log(Level.SEVERE, Logger.GLOBAL_LOGGER_NAME + " Could not save data due to an I/O error", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reload data store from persistent memory, overwriting any current state
|
|
||||||
*/
|
|
||||||
public void loadData() {
|
|
||||||
try {
|
|
||||||
config.load(storeFile);
|
|
||||||
} catch (IOException | InvalidConfigurationException e) {
|
|
||||||
logger.log(Level.SEVERE, Logger.GLOBAL_LOGGER_NAME + " Could not load data due to an I/O error", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Functional interface for constructing default values
|
|
||||||
* @param <T> Type to construct
|
|
||||||
*/
|
|
||||||
public interface DefaultGetter<T> {
|
|
||||||
/**
|
|
||||||
* Instantiate default value
|
|
||||||
* @return Default value that was instantiated
|
|
||||||
*/
|
|
||||||
T get();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +1,23 @@
|
|||||||
package dev.w1zzrd.invtweaks;
|
package dev.w1zzrd.invtweaks;
|
||||||
|
|
||||||
import dev.w1zzrd.invtweaks.command.CapitatorCommand;
|
import dev.w1zzrd.invtweaks.command.*;
|
||||||
import dev.w1zzrd.invtweaks.command.MagnetCommandExecutor;
|
|
||||||
import dev.w1zzrd.invtweaks.command.SearchCommandExecutor;
|
|
||||||
import dev.w1zzrd.invtweaks.command.SortCommandExecutor;
|
|
||||||
import dev.w1zzrd.invtweaks.enchantment.CapitatorEnchantment;
|
import dev.w1zzrd.invtweaks.enchantment.CapitatorEnchantment;
|
||||||
|
import dev.w1zzrd.invtweaks.feature.NamedChestManager;
|
||||||
import dev.w1zzrd.invtweaks.listener.*;
|
import dev.w1zzrd.invtweaks.listener.*;
|
||||||
|
import dev.w1zzrd.invtweaks.serialization.ChestNameConfig;
|
||||||
import dev.w1zzrd.invtweaks.serialization.MagnetConfig;
|
import dev.w1zzrd.invtweaks.serialization.MagnetConfig;
|
||||||
import dev.w1zzrd.invtweaks.serialization.MagnetData;
|
import dev.w1zzrd.invtweaks.serialization.MagnetData;
|
||||||
import dev.w1zzrd.invtweaks.serialization.SearchConfig;
|
import dev.w1zzrd.invtweaks.serialization.SearchConfig;
|
||||||
import dev.w1zzrd.invtweaks.serialization.UUIDList;
|
import dev.w1zzrd.spigot.wizcompat.enchantment.EnchantmentRegistryEntry;
|
||||||
|
import dev.w1zzrd.spigot.wizcompat.enchantment.ServerEnchantmentRegistry;
|
||||||
|
import dev.w1zzrd.spigot.wizcompat.serialization.PersistentData;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.NamespacedKey;
|
import org.bukkit.NamespacedKey;
|
||||||
import org.bukkit.configuration.serialization.ConfigurationSerialization;
|
import org.bukkit.configuration.serialization.ConfigurationSerialization;
|
||||||
import org.bukkit.enchantments.Enchantment;
|
|
||||||
import org.bukkit.event.HandlerList;
|
import org.bukkit.event.HandlerList;
|
||||||
import org.bukkit.plugin.PluginManager;
|
import org.bukkit.plugin.PluginManager;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@ -42,10 +40,14 @@ public final class InvTweaksPlugin extends JavaPlugin {
|
|||||||
private SortCommandExecutor sortCommandExecutor;
|
private SortCommandExecutor sortCommandExecutor;
|
||||||
private MagnetCommandExecutor magnetCommandExecutor;
|
private MagnetCommandExecutor magnetCommandExecutor;
|
||||||
private SearchCommandExecutor searchCommandExecutor;
|
private SearchCommandExecutor searchCommandExecutor;
|
||||||
|
private NamedChestCommand namedChestCommandExecutor;
|
||||||
|
private FindCommandExecutor findCommandExecutor;
|
||||||
private CapitatorCommand capitatorCommand;
|
private CapitatorCommand capitatorCommand;
|
||||||
private DataStore data;
|
private GrowUpCommand growUpCommand;
|
||||||
private NamespacedKey capitatorEnchantmentKey;
|
private ReWoolCommand reWoolCommand;
|
||||||
private Enchantment capitatorEnchantment;
|
private PersistentData data;
|
||||||
|
private NamedChestManager chestManager;
|
||||||
|
private EnchantmentRegistryEntry<CapitatorEnchantment> capitatorEnchantment = null;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
@ -82,59 +84,28 @@ public final class InvTweaksPlugin extends JavaPlugin {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a reference to the persistent data store object for this plugin
|
* Get a reference to the persistent data store object for this plugin
|
||||||
* @return An instance of {@link DataStore} for this plugin
|
* @return An instance of {@link PersistentData} for this plugin
|
||||||
*/
|
*/
|
||||||
public DataStore getPersistentData() {
|
public PersistentData getPersistentData() {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initEnchantments() {
|
private void initEnchantments() {
|
||||||
final boolean activateCapitator = getConfig().getBoolean("capitator", true);
|
if (getConfig().getBoolean("capitator", true))
|
||||||
|
capitatorEnchantment = ServerEnchantmentRegistry.registerEnchantment(
|
||||||
if (activateCapitator) {
|
this,
|
||||||
capitatorEnchantmentKey = new NamespacedKey(this, ENCHANTMENT_CAPITATOR_NAME);
|
new CapitatorEnchantment(
|
||||||
capitatorEnchantment = new CapitatorEnchantment(ENCHANTMENT_CAPITATOR_NAME, capitatorEnchantmentKey);
|
ENCHANTMENT_CAPITATOR_NAME,
|
||||||
}
|
new NamespacedKey(this, ENCHANTMENT_CAPITATOR_NAME)
|
||||||
|
)
|
||||||
try {
|
);
|
||||||
final Field acceptingField = Enchantment.class.getDeclaredField("acceptingNew");
|
|
||||||
acceptingField.setAccessible(true);
|
|
||||||
|
|
||||||
acceptingField.set(null, true);
|
|
||||||
|
|
||||||
if (activateCapitator)
|
|
||||||
Enchantment.registerEnchantment(capitatorEnchantment);
|
|
||||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void disableEnchantments() {
|
private void disableEnchantments() {
|
||||||
final boolean activateCapitator = getConfig().getBoolean("capitator", true);
|
if (getConfig().getBoolean("capitator", true))
|
||||||
|
ServerEnchantmentRegistry.unRegisterEnchantment(this, capitatorEnchantment);
|
||||||
try {
|
|
||||||
final Field byKeyField = Enchantment.class.getDeclaredField("byKey");
|
|
||||||
final Field byNameField = Enchantment.class.getDeclaredField("byName");
|
|
||||||
|
|
||||||
byKeyField.setAccessible(true);
|
|
||||||
byNameField.setAccessible(true);
|
|
||||||
|
|
||||||
final Object byKey = byKeyField.get(null);
|
|
||||||
final Object byName = byNameField.get(null);
|
|
||||||
|
|
||||||
if (byKey instanceof final Map<?, ?> byKeyMap && byName instanceof final Map<?, ?> byNameMap) {
|
|
||||||
|
|
||||||
if (activateCapitator) {
|
|
||||||
byKeyMap.remove(capitatorEnchantmentKey);
|
|
||||||
byNameMap.remove(ENCHANTMENT_CAPITATOR_NAME);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
capitatorEnchantment = null;
|
capitatorEnchantment = null;
|
||||||
capitatorEnchantmentKey = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -152,7 +123,14 @@ public final class InvTweaksPlugin extends JavaPlugin {
|
|||||||
pluginManager.registerEvents(new SortListener(), this);
|
pluginManager.registerEvents(new SortListener(), this);
|
||||||
pluginManager.registerEvents(new MagnetismListener(magnetCommandExecutor), this);
|
pluginManager.registerEvents(new MagnetismListener(magnetCommandExecutor), this);
|
||||||
pluginManager.registerEvents(new TabCompletionListener(), this);
|
pluginManager.registerEvents(new TabCompletionListener(), this);
|
||||||
pluginManager.registerEvents(new TreeCapitatorListener(activateCapitator ? capitatorEnchantment : null), this);
|
pluginManager.registerEvents(new TreeCapitatorListener(
|
||||||
|
activateCapitator ? capitatorEnchantment.getEnchantment() : null,
|
||||||
|
getConfig().getDouble("capitatorHungerPerBlock"),
|
||||||
|
getConfig().getInt("capitatorMinHunger")
|
||||||
|
), this);
|
||||||
|
pluginManager.registerEvents(new PlayerMoveRenderListener(chestManager), this);
|
||||||
|
pluginManager.registerEvents(new ChestBreakListener(chestManager), this);
|
||||||
|
pluginManager.registerEvents(new SignEditListener(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -172,14 +150,22 @@ public final class InvTweaksPlugin extends JavaPlugin {
|
|||||||
sortCommandExecutor = new SortCommandExecutor();
|
sortCommandExecutor = new SortCommandExecutor();
|
||||||
magnetCommandExecutor = new MagnetCommandExecutor(this, "magnet", getPersistentData());
|
magnetCommandExecutor = new MagnetCommandExecutor(this, "magnet", getPersistentData());
|
||||||
searchCommandExecutor = new SearchCommandExecutor(this, "search");
|
searchCommandExecutor = new SearchCommandExecutor(this, "search");
|
||||||
|
namedChestCommandExecutor = new NamedChestCommand(chestManager);
|
||||||
|
findCommandExecutor = new FindCommandExecutor(this);
|
||||||
|
growUpCommand = new GrowUpCommand();
|
||||||
|
reWoolCommand = new ReWoolCommand();
|
||||||
|
|
||||||
if (activateCapitator)
|
if (activateCapitator)
|
||||||
capitatorCommand = new CapitatorCommand(capitatorEnchantment);
|
capitatorCommand = new CapitatorCommand(capitatorEnchantment.getEnchantment());
|
||||||
|
|
||||||
// TODO: Bind command by annotation
|
// TODO: Bind command by annotation
|
||||||
Objects.requireNonNull(getCommand("sort")).setExecutor(sortCommandExecutor);
|
Objects.requireNonNull(getCommand("sort")).setExecutor(sortCommandExecutor);
|
||||||
Objects.requireNonNull(getCommand("magnet")).setExecutor(magnetCommandExecutor);
|
Objects.requireNonNull(getCommand("magnet")).setExecutor(magnetCommandExecutor);
|
||||||
Objects.requireNonNull(getCommand("search")).setExecutor(searchCommandExecutor);
|
Objects.requireNonNull(getCommand("search")).setExecutor(searchCommandExecutor);
|
||||||
|
Objects.requireNonNull(getCommand("chestname")).setExecutor(namedChestCommandExecutor);
|
||||||
|
Objects.requireNonNull(getCommand("find")).setExecutor(findCommandExecutor);
|
||||||
|
Objects.requireNonNull(getCommand("growup")).setExecutor(growUpCommand);
|
||||||
|
Objects.requireNonNull(getCommand("rewool")).setExecutor(reWoolCommand);
|
||||||
|
|
||||||
if (activateCapitator)
|
if (activateCapitator)
|
||||||
Objects.requireNonNull(getCommand("capitator")).setExecutor(capitatorCommand);
|
Objects.requireNonNull(getCommand("capitator")).setExecutor(capitatorCommand);
|
||||||
@ -192,6 +178,8 @@ public final class InvTweaksPlugin extends JavaPlugin {
|
|||||||
magnetCommandExecutor.onDisable();
|
magnetCommandExecutor.onDisable();
|
||||||
|
|
||||||
capitatorCommand = null;
|
capitatorCommand = null;
|
||||||
|
findCommandExecutor = null;
|
||||||
|
namedChestCommandExecutor = null;
|
||||||
searchCommandExecutor = null;
|
searchCommandExecutor = null;
|
||||||
magnetCommandExecutor = null;
|
magnetCommandExecutor = null;
|
||||||
sortCommandExecutor = null;
|
sortCommandExecutor = null;
|
||||||
@ -205,8 +193,11 @@ public final class InvTweaksPlugin extends JavaPlugin {
|
|||||||
private void registerSerializers() {
|
private void registerSerializers() {
|
||||||
ConfigurationSerialization.registerClass(MagnetConfig.class);
|
ConfigurationSerialization.registerClass(MagnetConfig.class);
|
||||||
ConfigurationSerialization.registerClass(MagnetData.class);
|
ConfigurationSerialization.registerClass(MagnetData.class);
|
||||||
ConfigurationSerialization.registerClass(UUIDList.class);
|
|
||||||
ConfigurationSerialization.registerClass(SearchConfig.class);
|
ConfigurationSerialization.registerClass(SearchConfig.class);
|
||||||
|
ConfigurationSerialization.registerClass(ChestNameConfig.class);
|
||||||
|
ConfigurationSerialization.registerClass(ChestNameConfig.ChestNameWorldEntry.class);
|
||||||
|
ConfigurationSerialization.registerClass(ChestNameConfig.ChestNameWorldEntry.ChestNameChunkEntry.class);
|
||||||
|
ConfigurationSerialization.registerClass(ChestNameConfig.ChestNameWorldEntry.ChestNameChunkEntry.ChestNameConfigEntry.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -215,9 +206,12 @@ public final class InvTweaksPlugin extends JavaPlugin {
|
|||||||
* @see #registerSerializers()
|
* @see #registerSerializers()
|
||||||
*/
|
*/
|
||||||
private void unregisterSerializers() {
|
private void unregisterSerializers() {
|
||||||
|
ConfigurationSerialization.registerClass(ChestNameConfig.ChestNameWorldEntry.ChestNameChunkEntry.ChestNameConfigEntry.class);
|
||||||
|
ConfigurationSerialization.registerClass(ChestNameConfig.ChestNameWorldEntry.ChestNameChunkEntry.class);
|
||||||
|
ConfigurationSerialization.registerClass(ChestNameConfig.ChestNameWorldEntry.class);
|
||||||
|
ConfigurationSerialization.registerClass(ChestNameConfig.class);
|
||||||
ConfigurationSerialization.unregisterClass(MagnetConfig.class);
|
ConfigurationSerialization.unregisterClass(MagnetConfig.class);
|
||||||
ConfigurationSerialization.unregisterClass(MagnetData.class);
|
ConfigurationSerialization.unregisterClass(MagnetData.class);
|
||||||
ConfigurationSerialization.unregisterClass(UUIDList.class);
|
|
||||||
ConfigurationSerialization.unregisterClass(SearchConfig.class);
|
ConfigurationSerialization.unregisterClass(SearchConfig.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,13 +226,17 @@ public final class InvTweaksPlugin extends JavaPlugin {
|
|||||||
saveConfig();
|
saveConfig();
|
||||||
|
|
||||||
// Implicit load
|
// Implicit load
|
||||||
data = new DataStore(PERSISTENT_DATA_NAME, this);
|
data = new PersistentData(PERSISTENT_DATA_NAME, this);
|
||||||
|
|
||||||
|
chestManager = new NamedChestManager(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* De-activate and finalize persistent data storage sources and handlers
|
* De-activate and finalize persistent data storage sources and handlers
|
||||||
*/
|
*/
|
||||||
private void disablePersistentData() {
|
private void disablePersistentData() {
|
||||||
|
chestManager = null;
|
||||||
|
|
||||||
data.saveData();
|
data.saveData();
|
||||||
data = null;
|
data = null;
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package dev.w1zzrd.invtweaks.command;
|
package dev.w1zzrd.invtweaks.command;
|
||||||
|
|
||||||
|
import dev.w1zzrd.spigot.wizcompat.command.CommandUtils;
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
import org.bukkit.command.CommandExecutor;
|
import org.bukkit.command.CommandExecutor;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
package dev.w1zzrd.invtweaks.command;
|
|
||||||
|
|
||||||
import net.md_5.bungee.api.ChatColor;
|
|
||||||
import net.md_5.bungee.api.chat.BaseComponent;
|
|
||||||
import net.md_5.bungee.api.chat.TextComponent;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
|
|
||||||
public final class CommandUtils {
|
|
||||||
private CommandUtils() { throw new UnsupportedOperationException("Functional class"); }
|
|
||||||
|
|
||||||
public static boolean assertTrue(final boolean condition, final String message, final CommandSender sender) {
|
|
||||||
if (!condition) {
|
|
||||||
final TextComponent errorMessage = new TextComponent(message);
|
|
||||||
errorMessage.setColor(ChatColor.DARK_RED);
|
|
||||||
sender.spigot().sendMessage(errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return !condition;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BaseComponent errorMessage(final String message) {
|
|
||||||
final BaseComponent component = new TextComponent(message);
|
|
||||||
component.setColor(ChatColor.DARK_RED);
|
|
||||||
return component;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BaseComponent successMessage(final String message) {
|
|
||||||
final BaseComponent component = new TextComponent(message);
|
|
||||||
component.setColor(ChatColor.GREEN);
|
|
||||||
return component;
|
|
||||||
}
|
|
||||||
}
|
|
43
src/dev/w1zzrd/invtweaks/command/Commands.java
Normal file
43
src/dev/w1zzrd/invtweaks/command/Commands.java
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package dev.w1zzrd.invtweaks.command;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static dev.w1zzrd.spigot.wizcompat.command.CommandUtils.errorMessage;
|
||||||
|
|
||||||
|
public final class Commands {
|
||||||
|
private Commands() { throw new UnsupportedOperationException("Functional class"); }
|
||||||
|
|
||||||
|
public static double getCommandRadius(final CommandSender sender, final String[] args, final int radiusIndex) {
|
||||||
|
try {
|
||||||
|
if (args.length > radiusIndex)
|
||||||
|
return Double.parseDouble(args[radiusIndex]);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
sender.spigot().sendMessage(errorMessage(String.format("\"%s\" is not a number", args[radiusIndex])));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Double.NaN;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends Entity> Stream<T> getCommandEntityRadius(
|
||||||
|
final CommandSender sender,
|
||||||
|
final String[] args,
|
||||||
|
final int radiusIndex,
|
||||||
|
final Class<T> entityType
|
||||||
|
) {
|
||||||
|
final double radius = Commands.getCommandRadius(sender, args, 0);
|
||||||
|
if (Double.compare(radius, Double.NaN) == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return
|
||||||
|
(radius >= 0 ?
|
||||||
|
((Player) sender).getNearbyEntities(radius, radius, radius).stream() :
|
||||||
|
Bukkit.getWorlds().stream().flatMap(it -> it.getEntities().stream()))
|
||||||
|
.filter(it -> entityType.isAssignableFrom(it.getClass()))
|
||||||
|
.map(it -> (T) it);
|
||||||
|
}
|
||||||
|
}
|
@ -1,32 +0,0 @@
|
|||||||
package dev.w1zzrd.invtweaks.command;
|
|
||||||
|
|
||||||
import org.bukkit.command.CommandExecutor;
|
|
||||||
import org.bukkit.configuration.serialization.ConfigurationSerializable;
|
|
||||||
import org.bukkit.plugin.Plugin;
|
|
||||||
|
|
||||||
public abstract class ConfigurableCommandExecutor<T extends ConfigurationSerializable> implements CommandExecutor {
|
|
||||||
private final Plugin plugin;
|
|
||||||
private final String path;
|
|
||||||
private T config;
|
|
||||||
|
|
||||||
public ConfigurableCommandExecutor(
|
|
||||||
final Plugin plugin,
|
|
||||||
final String path
|
|
||||||
) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
this.path = path;
|
|
||||||
reloadConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reloadConfig() {
|
|
||||||
config = (T) plugin.getConfig().get(path, plugin.getConfig().getDefaults().get(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected T getConfig() {
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Plugin getPlugin() {
|
|
||||||
return plugin;
|
|
||||||
}
|
|
||||||
}
|
|
98
src/dev/w1zzrd/invtweaks/command/FindCommandExecutor.java
Normal file
98
src/dev/w1zzrd/invtweaks/command/FindCommandExecutor.java
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
package dev.w1zzrd.invtweaks.command;
|
||||||
|
|
||||||
|
import dev.w1zzrd.spigot.wizcompat.command.CommandUtils;
|
||||||
|
import org.bukkit.*;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.BlockState;
|
||||||
|
import org.bukkit.block.Container;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static dev.w1zzrd.invtweaks.listener.TabCompletionListener.getMaterialMatching;
|
||||||
|
import static dev.w1zzrd.spigot.wizcompat.command.CommandUtils.assertTrue;
|
||||||
|
import static dev.w1zzrd.spigot.wizcompat.packet.EntityCreator.*;
|
||||||
|
|
||||||
|
public final class FindCommandExecutor implements CommandExecutor {
|
||||||
|
private static final int SEARCH_RADIUS = 3;
|
||||||
|
private static final long DESPAWN_WAIT = 20 * 10;
|
||||||
|
|
||||||
|
private final Plugin plugin;
|
||||||
|
|
||||||
|
public FindCommandExecutor(final Plugin plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
|
Material targetMaterial;
|
||||||
|
|
||||||
|
if (assertTrue(sender instanceof Player, "Command can only be run by players", sender) ||
|
||||||
|
assertTrue(args.length == 1, "Exactly one argument is expected", sender) ||
|
||||||
|
assertTrue((targetMaterial = getMaterialMatching(args[0])) != null, String.format("Unknown item/block: %s", args[0]), sender))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
final Player player = (Player) sender;
|
||||||
|
|
||||||
|
final List<BlockState> matches = searchChunks(
|
||||||
|
player.getLocation().getChunk(),
|
||||||
|
SEARCH_RADIUS,
|
||||||
|
Material.CHEST, Material.TRAPPED_CHEST, Material.SHULKER_BOX
|
||||||
|
);
|
||||||
|
|
||||||
|
final List<Integer> entities = matches.stream()
|
||||||
|
.filter(it -> Arrays.stream(((Container)it).getSnapshotInventory().getContents()).filter(Objects::nonNull).map(ItemStack::getType).anyMatch(targetMaterial::equals))
|
||||||
|
.map(state -> spawnMarker(player, state.getLocation().add(0.5, 0.2, 0.5)))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (assertTrue(entities.size() > 0, "No containers neardby contain that item/block", sender))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Bukkit.getScheduler().runTaskLater(plugin, () -> {
|
||||||
|
entities.forEach(it -> sendEntityDespawnPacket(player, it));
|
||||||
|
}, DESPAWN_WAIT);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int spawnMarker(final Player target, final Location location) {
|
||||||
|
final Object entity = createFakeSlime(target);
|
||||||
|
setSlimeSize(entity, 1);
|
||||||
|
setEntityCollision(entity, false);
|
||||||
|
setEntityInvisible(entity, true);
|
||||||
|
setEntityInvulnerable(entity, true);
|
||||||
|
setEntityGlowing(entity, true);
|
||||||
|
setEntityLocation(entity, location.getX(), location.getY(), location.getZ(), 0f, 0f);
|
||||||
|
|
||||||
|
sendEntitySpawnPacket(target, entity);
|
||||||
|
sendEntityMetadataPacket(target, entity);
|
||||||
|
|
||||||
|
return getEntityID(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<BlockState> searchChunks(final Chunk middle, final int radius, final Material... targets) {
|
||||||
|
final int xMin = middle.getX() - radius;
|
||||||
|
final int xMax = middle.getX() + radius;
|
||||||
|
final int zMin = middle.getZ() - radius;
|
||||||
|
final int zMax = middle.getZ() + radius;
|
||||||
|
|
||||||
|
final World sourceWorld = middle.getWorld();
|
||||||
|
|
||||||
|
final List<Material> targetMaterials = Arrays.asList(targets);
|
||||||
|
final ArrayList<BlockState> collect = new ArrayList<>();
|
||||||
|
for (int x = xMin; x <= xMax; ++x)
|
||||||
|
for (int z = zMin; z <= zMax; ++z)
|
||||||
|
Arrays.stream(sourceWorld.getChunkAt(x, z).getTileEntities()).filter(it -> targetMaterials.contains(it.getType())).forEach(collect::add);
|
||||||
|
|
||||||
|
return collect;
|
||||||
|
}
|
||||||
|
}
|
28
src/dev/w1zzrd/invtweaks/command/GrowUpCommand.java
Normal file
28
src/dev/w1zzrd/invtweaks/command/GrowUpCommand.java
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package dev.w1zzrd.invtweaks.command;
|
||||||
|
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Ageable;
|
||||||
|
import org.bukkit.entity.Breedable;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static dev.w1zzrd.spigot.wizcompat.command.CommandUtils.assertTrue;
|
||||||
|
|
||||||
|
public class GrowUpCommand implements CommandExecutor {
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
|
if (assertTrue(args.length == 0 || sender instanceof Player, "Only players can run this command", sender))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
final Stream<Breedable> breedables = Commands.getCommandEntityRadius(sender, args, 0, Breedable.class);
|
||||||
|
if (breedables == null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
breedables.filter(it -> !it.getAgeLock() && !it.isAdult()).forEach(Ageable::setAdult);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,13 @@
|
|||||||
package dev.w1zzrd.invtweaks.command;
|
package dev.w1zzrd.invtweaks.command;
|
||||||
|
|
||||||
import dev.w1zzrd.invtweaks.DataStore;
|
|
||||||
import dev.w1zzrd.invtweaks.serialization.MagnetConfig;
|
import dev.w1zzrd.invtweaks.serialization.MagnetConfig;
|
||||||
import dev.w1zzrd.invtweaks.serialization.MagnetData;
|
import dev.w1zzrd.invtweaks.serialization.MagnetData;
|
||||||
|
import dev.w1zzrd.spigot.wizcompat.command.ConfigurableCommandExecutor;
|
||||||
|
import dev.w1zzrd.spigot.wizcompat.serialization.PersistentData;
|
||||||
import net.md_5.bungee.api.chat.TextComponent;
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
import org.bukkit.command.CommandExecutor;
|
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.entity.Item;
|
import org.bukkit.entity.Item;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
@ -28,7 +28,7 @@ public class MagnetCommandExecutor extends ConfigurableCommandExecutor<MagnetCon
|
|||||||
|
|
||||||
private static final Logger logger = Bukkit.getLogger();
|
private static final Logger logger = Bukkit.getLogger();
|
||||||
|
|
||||||
private final DataStore data;
|
private final PersistentData data;
|
||||||
private final MagnetData magnetData;
|
private final MagnetData magnetData;
|
||||||
|
|
||||||
private int divIndex = 0;
|
private int divIndex = 0;
|
||||||
@ -39,7 +39,7 @@ public class MagnetCommandExecutor extends ConfigurableCommandExecutor<MagnetCon
|
|||||||
* Initialize the magnet executor and manger
|
* Initialize the magnet executor and manger
|
||||||
* @param plugin Owner plugin for this executor
|
* @param plugin Owner plugin for this executor
|
||||||
*/
|
*/
|
||||||
public MagnetCommandExecutor(final Plugin plugin, final String path, final DataStore data) {
|
public MagnetCommandExecutor(final Plugin plugin, final String path, final PersistentData data) {
|
||||||
super(plugin, path);
|
super(plugin, path);
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.magnetData = data.loadData("magnets", MagnetData::blank);
|
this.magnetData = data.loadData("magnets", MagnetData::blank);
|
||||||
|
52
src/dev/w1zzrd/invtweaks/command/NamedChestCommand.java
Normal file
52
src/dev/w1zzrd/invtweaks/command/NamedChestCommand.java
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package dev.w1zzrd.invtweaks.command;
|
||||||
|
|
||||||
|
import dev.w1zzrd.invtweaks.feature.NamedChestManager;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.Chest;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import static dev.w1zzrd.invtweaks.listener.PlayerMoveRenderListener.RENDER_RADIUS;
|
||||||
|
import static dev.w1zzrd.spigot.wizcompat.command.CommandUtils.assertTrue;
|
||||||
|
|
||||||
|
public final class NamedChestCommand implements CommandExecutor {
|
||||||
|
|
||||||
|
private final NamedChestManager manager;
|
||||||
|
|
||||||
|
public NamedChestCommand(final NamedChestManager manager) {
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
|
if (assertTrue(sender instanceof Player && ((Player) sender).isOnline(), "Command can only be run by a player!", sender))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (assertTrue(args.length <= 1, "Too many arguments for command", sender))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
final Player player = (Player) sender;
|
||||||
|
|
||||||
|
final Block block = player.getTargetBlockExact(10);
|
||||||
|
|
||||||
|
if (assertTrue(block != null && (block.getType() == Material.CHEST || block.getType() == Material.TRAPPED_CHEST), "You must be targeting a chest", sender))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
final Chest chest = (Chest) block.getState();
|
||||||
|
|
||||||
|
if (args.length == 0) {
|
||||||
|
manager.removeTag(chest);
|
||||||
|
} else {
|
||||||
|
if (manager.hasNamedChest(chest))
|
||||||
|
manager.removeTag(chest);
|
||||||
|
|
||||||
|
manager.addTag(chest, args[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.renderTags(chest.getChunk(), RENDER_RADIUS);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
27
src/dev/w1zzrd/invtweaks/command/ReWoolCommand.java
Normal file
27
src/dev/w1zzrd/invtweaks/command/ReWoolCommand.java
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package dev.w1zzrd.invtweaks.command;
|
||||||
|
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.CommandExecutor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.entity.Sheep;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static dev.w1zzrd.spigot.wizcompat.command.CommandUtils.assertTrue;
|
||||||
|
|
||||||
|
public class ReWoolCommand implements CommandExecutor {
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||||
|
if (assertTrue(args.length == 0 || sender instanceof Player, "Only players can run this command", sender))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
final Stream<Sheep> sheep = Commands.getCommandEntityRadius(sender, args, 0, Sheep.class);
|
||||||
|
if (sheep == null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
sheep.forEach(it -> it.setSheared(false));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ package dev.w1zzrd.invtweaks.command;
|
|||||||
|
|
||||||
import dev.w1zzrd.invtweaks.InvTweaksPlugin;
|
import dev.w1zzrd.invtweaks.InvTweaksPlugin;
|
||||||
import dev.w1zzrd.invtweaks.serialization.SearchConfig;
|
import dev.w1zzrd.invtweaks.serialization.SearchConfig;
|
||||||
|
import dev.w1zzrd.spigot.wizcompat.command.ConfigurableCommandExecutor;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.*;
|
import org.bukkit.block.*;
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
@ -14,10 +15,11 @@ import java.lang.reflect.Field;
|
|||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import static dev.w1zzrd.invtweaks.command.CommandUtils.assertTrue;
|
|
||||||
import static dev.w1zzrd.invtweaks.listener.TabCompletionListener.getMaterialMatching;
|
import static dev.w1zzrd.invtweaks.listener.TabCompletionListener.getMaterialMatching;
|
||||||
|
import static dev.w1zzrd.spigot.wizcompat.command.CommandUtils.assertTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for executions of /search command
|
* Handler for executions of /search command
|
||||||
@ -47,23 +49,23 @@ public class SearchCommandExecutor extends ConfigurableCommandExecutor<SearchCon
|
|||||||
assertTrue((targetMaterial = getMaterialMatching(args[0])) != null, String.format(ERR_UNKNOWN, args[0]), sender)
|
assertTrue((targetMaterial = getMaterialMatching(args[0])) != null, String.format(ERR_UNKNOWN, args[0]), sender)
|
||||||
) return true;
|
) return true;
|
||||||
|
|
||||||
assert targetMaterial != null;
|
|
||||||
assert sender instanceof Player;
|
|
||||||
final Player player = (Player) sender;
|
final Player player = (Player) sender;
|
||||||
|
final Location playerLocation = player.getLocation();
|
||||||
|
|
||||||
final SearchConfig config = getConfig();
|
final SearchConfig config = getConfig();
|
||||||
|
|
||||||
final List<BlockState> matches = searchBlocks(
|
final List<BlockState> matches = searchChunks(
|
||||||
player.getLocation(),
|
playerLocation.getChunk(),
|
||||||
player.getWorld(),
|
config.getSearchRadiusX(),
|
||||||
config.getSearchRadiusX(), config.getSearchRadiusY(), config.getSearchRadiusZ(),
|
Material.CHEST, Material.TRAPPED_CHEST, Material.SHULKER_BOX
|
||||||
Material.CHEST, Material.SHULKER_BOX
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Ensure we found inventory-holding blocks
|
// Ensure we found inventory-holding blocks
|
||||||
if (assertTrue(matches.size() != 0, ERR_NO_INVENTORIES, sender))
|
if (assertTrue(matches.size() != 0, ERR_NO_INVENTORIES, sender))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
matches.sort(Comparator.comparingDouble(state -> state.getLocation().distanceSquared(playerLocation)));
|
||||||
|
|
||||||
final InventoryHolder result;
|
final InventoryHolder result;
|
||||||
|
|
||||||
FIND_RESULT:
|
FIND_RESULT:
|
||||||
@ -145,7 +147,24 @@ public class SearchCommandExecutor extends ConfigurableCommandExecutor<SearchCon
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<BlockState> searchBlocks(
|
private static List<BlockState> searchChunks(final Chunk middle, final int radius, final Material... targets) {
|
||||||
|
final int xMin = middle.getX() - radius;
|
||||||
|
final int xMax = middle.getX() + radius;
|
||||||
|
final int zMin = middle.getZ() - radius;
|
||||||
|
final int zMax = middle.getZ() + radius;
|
||||||
|
|
||||||
|
final World sourceWorld = middle.getWorld();
|
||||||
|
|
||||||
|
final List<Material> targetMaterials = Arrays.asList(targets);
|
||||||
|
final ArrayList<BlockState> collect = new ArrayList<>();
|
||||||
|
for (int x = xMin; x <= xMax; ++x)
|
||||||
|
for (int z = zMin; z <= zMax; ++z)
|
||||||
|
Arrays.stream(sourceWorld.getChunkAt(x, z).getTileEntities()).filter(it -> targetMaterials.contains(it.getType())).forEach(collect::add);
|
||||||
|
|
||||||
|
return collect;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<BlockState> searchBlocks(
|
||||||
final Location centre,
|
final Location centre,
|
||||||
final World world,
|
final World world,
|
||||||
final int rx,
|
final int rx,
|
||||||
|
@ -19,8 +19,8 @@ import java.util.Optional;
|
|||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import static dev.w1zzrd.invtweaks.InvTweaksPlugin.LOG_PLUGIN_NAME;
|
import static dev.w1zzrd.invtweaks.InvTweaksPlugin.LOG_PLUGIN_NAME;
|
||||||
|
import static dev.w1zzrd.spigot.wizcompat.command.CommandUtils.assertTrue;
|
||||||
import static org.bukkit.Material.*;
|
import static org.bukkit.Material.*;
|
||||||
import static dev.w1zzrd.invtweaks.command.CommandUtils.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for executions of /sort command
|
* Handler for executions of /sort command
|
||||||
|
471
src/dev/w1zzrd/invtweaks/feature/NamedChestManager.java
Normal file
471
src/dev/w1zzrd/invtweaks/feature/NamedChestManager.java
Normal file
@ -0,0 +1,471 @@
|
|||||||
|
package dev.w1zzrd.invtweaks.feature;
|
||||||
|
|
||||||
|
import dev.w1zzrd.invtweaks.serialization.ChestNameConfig;
|
||||||
|
import dev.w1zzrd.spigot.wizcompat.packet.EntityCreator;
|
||||||
|
import dev.w1zzrd.spigot.wizcompat.serialization.PersistentData;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Chunk;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.Chest;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static dev.w1zzrd.spigot.wizcompat.block.Chests.*;
|
||||||
|
import static dev.w1zzrd.spigot.wizcompat.packet.EntityCreator.*;
|
||||||
|
|
||||||
|
public final class NamedChestManager {
|
||||||
|
private static final String PATH_NAMED_CHESTS = "namedChests";
|
||||||
|
|
||||||
|
private final RenderRegistry renders = new RenderRegistry();
|
||||||
|
|
||||||
|
private final ChestNameConfig config;
|
||||||
|
|
||||||
|
public NamedChestManager(final PersistentData data) {
|
||||||
|
config = data.loadData(PATH_NAMED_CHESTS, ChestNameConfig::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasNamedChest(final World world, final Location loc) {
|
||||||
|
return getChestNameAt(world, loc) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasNamedChest(final Location loc) {
|
||||||
|
return hasNamedChest(Objects.requireNonNull(loc.getWorld()), loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasNamedChest(final Chest chest) {
|
||||||
|
final Block left = getLeftChest(chest);
|
||||||
|
|
||||||
|
return hasNamedChest(left.getWorld(), left.getLocation());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getChestName(final Block block) {
|
||||||
|
return getChestNameAt(block.getWorld(), getLeftChest((Chest)block.getState()).getLocation());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setChestName(final Block block, final String name) {
|
||||||
|
addChestName(block.getWorld(), block.getLocation(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addChestName(final World world, final Location location, final String name) {
|
||||||
|
config.getEntry(world.getUID()).add(location, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getChestNameAt(final World world, final Location location) {
|
||||||
|
return config.getEntry(world.getUID()).getName(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getChestNameAt(final Location location) {
|
||||||
|
return getChestNameAt(Objects.requireNonNull(location.getWorld()), location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void untrackPlayer(final Player player) {
|
||||||
|
renders.removeRender(player.getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderTags(final Chunk chunk, final int chunkRadius) {
|
||||||
|
chunk.getWorld()
|
||||||
|
.getPlayers()
|
||||||
|
.stream()
|
||||||
|
.filter(it -> {
|
||||||
|
final Chunk playerChunk = it.getLocation().getChunk();
|
||||||
|
|
||||||
|
return Math.abs(playerChunk.getX() - chunk.getX()) <= chunkRadius && Math.abs(playerChunk.getZ() - chunk.getZ()) <= chunkRadius;
|
||||||
|
})
|
||||||
|
.forEach(player -> renderTags(player, chunkRadius));
|
||||||
|
|
||||||
|
final ChestNameConfig.ChestNameWorldEntry.ChestNameChunkEntry chestChunk = config.getChunkEntry(
|
||||||
|
chunk.getWorld().getUID(),
|
||||||
|
chunk.getX(),
|
||||||
|
chunk.getZ()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (chestChunk != null)
|
||||||
|
chestChunk.setDirty(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderTags(final Player target, final int chunkRadius) {
|
||||||
|
final UUID worldID = target.getWorld().getUID();
|
||||||
|
|
||||||
|
renders.updateRenders(
|
||||||
|
target,
|
||||||
|
chunkRadius,
|
||||||
|
addedChunk -> {
|
||||||
|
final ChestNameConfig.ChestNameWorldEntry.ChestNameChunkEntry chunk = config.getChunkEntry(worldID, addedChunk.getRender().x(), addedChunk.getRender().z());
|
||||||
|
if (chunk == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
final int baseX = chunk.getX() << 4;
|
||||||
|
final int baseZ = chunk.getZ() << 4;
|
||||||
|
|
||||||
|
chunk.streamEntries().forEach(entry -> {
|
||||||
|
final Object entity = entry.getEntity(() -> {
|
||||||
|
final Location loc = getCenterChestLocation(target.getWorld().getBlockAt(baseX + entry.getChunkX(), entry.getY(), baseZ + entry.getChunkZ()));
|
||||||
|
|
||||||
|
final Object newEntity = createFakeSlime(target);
|
||||||
|
setEntityCollision(newEntity, false);
|
||||||
|
setEntityInvulnerable(newEntity, true);
|
||||||
|
setEntityLocation(newEntity, loc.getX(), loc.getY(), loc.getZ(), 0f, 0f);
|
||||||
|
setEntityCustomName(newEntity, entry.getName());
|
||||||
|
setEntityCustomNameVisible(newEntity, true);
|
||||||
|
|
||||||
|
return newEntity;
|
||||||
|
});
|
||||||
|
|
||||||
|
sendEntitySpawnPacket(target, entity);
|
||||||
|
sendEntityMetadataPacket(target, entity);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
removedChunk -> {
|
||||||
|
final ChestNameConfig.ChestNameWorldEntry.ChestNameChunkEntry chunk = config.getChunkEntry(worldID, removedChunk.getRender().x(), removedChunk.getRender().z());
|
||||||
|
if (chunk == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sendEntityDespawnPackets(
|
||||||
|
target,
|
||||||
|
chunk.streamEntries()
|
||||||
|
.map(entry -> entry.getEntity(() -> null))
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.mapToInt(EntityCreator::getEntityID).toArray()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeTag(final World world, final Location location) {
|
||||||
|
final ChestNameConfig.ChestNameWorldEntry.ChestNameChunkEntry.ChestNameConfigEntry entry = config.getEntryAt(world.getUID(), location);
|
||||||
|
|
||||||
|
if (entry != null) {
|
||||||
|
final ChunkRenderEntry chunk = renders.getChunk(location.getChunk().getX(), location.getChunk().getZ(), false);
|
||||||
|
|
||||||
|
if (chunk != null) {
|
||||||
|
final ChestNameConfig.ChestNameWorldEntry.ChestNameChunkEntry chestChunk = config.getChunkEntry(world.getUID(), chunk.getRender().x(), chunk.getRender().z());
|
||||||
|
|
||||||
|
if (chestChunk != null)
|
||||||
|
chestChunk.removeEntry(entry);
|
||||||
|
|
||||||
|
chunk.streamRenders().forEach(it -> {
|
||||||
|
final Player player = Bukkit.getPlayer(it.getRender());
|
||||||
|
|
||||||
|
if (player == null)
|
||||||
|
renders.removeRender(it.getRender());
|
||||||
|
else
|
||||||
|
sendEntityDespawnPacket(player, getEntityID(entry.getEntity(() -> null)));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (chestChunk != null) {
|
||||||
|
chestChunk.setDirty(false);
|
||||||
|
|
||||||
|
// Make sure we don't leave blank data in persistent data file
|
||||||
|
final ChestNameConfig.ChestNameWorldEntry worldEntry = config.getEntry(world.getUID());
|
||||||
|
worldEntry.deleteEmptyChunk(chestChunk);
|
||||||
|
config.deleteEmptyWorld(worldEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeTag(final Location location) {
|
||||||
|
removeTag(Objects.requireNonNull(location.getWorld()), location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTag(final World world, final Location location, final String name) {
|
||||||
|
config.getEntry(world.getUID(), true).add(location, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTag(final Chest chest, final String name) {
|
||||||
|
final Block left = getLeftChest(chest);
|
||||||
|
|
||||||
|
addTag(left.getWorld(), left.getLocation(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeTag(final Chest chest) {
|
||||||
|
final Block left = getLeftChest(chest);
|
||||||
|
|
||||||
|
removeTag(left.getWorld(), left.getLocation());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Location getCenterChestLocation(final Block chestBlock) {
|
||||||
|
if (isDoubleChest(chestBlock)) {
|
||||||
|
final Location left = getBlockCenter(getLeftChest((Chest) chestBlock.getState()));
|
||||||
|
final Location right = getBlockCenter(getRightChest((Chest) chestBlock.getState()));
|
||||||
|
|
||||||
|
return new Location(left.getWorld(), (left.getX() + right.getX()) / 2.0, left.getY() + 0.2, (left.getZ() + right.getZ()) / 2.0);
|
||||||
|
} else {
|
||||||
|
return getBlockCenter(chestBlock).add(0.0, 0.2, 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Location getBlockCenter(final Block block) {
|
||||||
|
return block.getLocation().add(0.5, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class RenderRegistry {
|
||||||
|
private final List<PlayerRenderEntry> playerRegistry = new ArrayList<>();
|
||||||
|
private final List<ChunkRenderEntry> chunkRegistry = new ArrayList<>();
|
||||||
|
|
||||||
|
public void addRender(final Player target, final int chunkX, final int chunkZ) {
|
||||||
|
final PlayerRenderEntry player = getPlayer(target.getUniqueId(), true);
|
||||||
|
final ChunkRenderEntry chunk = getChunk(chunkX, chunkZ, true);
|
||||||
|
|
||||||
|
assert player != null;
|
||||||
|
player.addRender(chunk);
|
||||||
|
|
||||||
|
assert chunk != null;
|
||||||
|
chunk.addRender(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeRender(final Player target) {
|
||||||
|
removeRender(target.getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeRender(final UUID offlinePlayer) {
|
||||||
|
final PlayerRenderEntry player = getPlayer(offlinePlayer, false);
|
||||||
|
|
||||||
|
if (player != null) {
|
||||||
|
player.streamRenders().forEach(this::doRemoveChunk);
|
||||||
|
doRemovePlayer(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeRender(final int chunkX, final int chunkZ) {
|
||||||
|
final ChunkRenderEntry chunk = getChunk(chunkX, chunkZ, false);
|
||||||
|
|
||||||
|
if (chunk != null) {
|
||||||
|
chunk.streamRenders().forEach(this::doRemovePlayer);
|
||||||
|
doRemoveChunk(chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doRemoveChunk(final ChunkRenderEntry chunk) {
|
||||||
|
final int index = Collections.binarySearch(chunkRegistry, chunk);
|
||||||
|
|
||||||
|
if (index >= 0)
|
||||||
|
chunkRegistry.remove(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doRemovePlayer(final PlayerRenderEntry player) {
|
||||||
|
final int index = Collections.binarySearch(playerRegistry, player);
|
||||||
|
|
||||||
|
if (index >= 0)
|
||||||
|
playerRegistry.remove(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateRenders(final Player target, final int chunkRadius, final ChunkEntryChangeHandler onAdd, final ChunkEntryChangeHandler onRemove) {
|
||||||
|
final PlayerRenderEntry player = getPlayer(target.getUniqueId(), true);
|
||||||
|
final UUID worldID = target.getWorld().getUID();
|
||||||
|
final int chunkX = target.getLocation().getBlockX() >> 4;
|
||||||
|
final int chunkZ = target.getLocation().getBlockZ() >> 4;
|
||||||
|
|
||||||
|
final int xMax = chunkX + chunkRadius;
|
||||||
|
final int xMin = chunkX - chunkRadius;
|
||||||
|
final int zMax = chunkZ + chunkRadius;
|
||||||
|
final int zMin = chunkZ - chunkRadius;
|
||||||
|
|
||||||
|
assert player != null;
|
||||||
|
final List<ChunkRenderEntry> toRemove = new ArrayList<>();
|
||||||
|
player.streamRenders()
|
||||||
|
.filter(chunk -> {
|
||||||
|
final ChestNameConfig.ChestNameWorldEntry.ChestNameChunkEntry chestChunk = config.getChunkEntry(worldID, chunk.getRender().x(), chunk.getRender().z());
|
||||||
|
|
||||||
|
return chunk.getRender().x() > xMax ||
|
||||||
|
chunk.getRender().x() < xMin ||
|
||||||
|
chunk.getRender().z() > zMax ||
|
||||||
|
chunk.getRender().z() < zMin ||
|
||||||
|
(chestChunk != null && chestChunk.isDirty());
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.forEach(chunk -> {
|
||||||
|
toRemove.add(chunk);
|
||||||
|
onRemove.onChange(chunk);
|
||||||
|
});
|
||||||
|
toRemove.forEach(player::removeRender);
|
||||||
|
|
||||||
|
for (int x = xMin; x <= xMax; ++x)
|
||||||
|
for (int z = zMin; z <= zMax; ++z) {
|
||||||
|
final ChunkRenderEntry chunk = getChunk(x, z, true);
|
||||||
|
|
||||||
|
assert chunk != null;
|
||||||
|
if (player.addRender(chunk))
|
||||||
|
onAdd.onChange(chunk);
|
||||||
|
|
||||||
|
chunk.addRender(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream<PlayerRenderEntry> streamPlayers() {
|
||||||
|
return playerRegistry.stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream<ChestNameConfig.ChestNameWorldEntry.ChestNameChunkEntry> streamEntries(final Player target) {
|
||||||
|
final PlayerRenderEntry player = getPlayer(target.getUniqueId(), false);
|
||||||
|
final UUID worldID = target.getWorld().getUID();
|
||||||
|
|
||||||
|
if (player == null)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
return player.streamRenders()
|
||||||
|
.map(chunkEntry -> config.getChunkEntry(worldID, chunkEntry.getRender().x(), chunkEntry.getRender().z()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ChunkRenderEntry getChunk(final int chunkX, final int chunkZ, final boolean addIfMissing) {
|
||||||
|
final ChunkRenderEntry find = new ChunkRenderEntry(chunkX, chunkZ);
|
||||||
|
|
||||||
|
final int index = Collections.binarySearch(chunkRegistry, find);
|
||||||
|
|
||||||
|
if (index >= 0)
|
||||||
|
return chunkRegistry.get(index);
|
||||||
|
else if (addIfMissing) {
|
||||||
|
chunkRegistry.add(-(index + 1), find);
|
||||||
|
return find;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayerRenderEntry getPlayer(final UUID player, final boolean addIfMissing) {
|
||||||
|
final PlayerRenderEntry find = new PlayerRenderEntry(player);
|
||||||
|
|
||||||
|
final int index = Collections.binarySearch(playerRegistry, find);
|
||||||
|
|
||||||
|
if (index >= 0)
|
||||||
|
return playerRegistry.get(index);
|
||||||
|
else if (addIfMissing) {
|
||||||
|
playerRegistry.add(-(index + 1), find);
|
||||||
|
return find;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private interface RenderEntry<T extends Comparable<T>, R> extends Comparable<RenderEntry<T, R>> {
|
||||||
|
T getRender();
|
||||||
|
boolean addRender(final R r);
|
||||||
|
boolean removeRender(final R r);
|
||||||
|
boolean containsRender(final R r);
|
||||||
|
Stream<R> streamRenders();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default int compareTo(final RenderEntry<T, R> o) {
|
||||||
|
return getRender().compareTo(o.getRender());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class PlayerRenderEntry implements RenderEntry<UUID, ChunkRenderEntry> {
|
||||||
|
private final UUID player;
|
||||||
|
private final List<ChunkRenderEntry> chunks = new ArrayList<>();
|
||||||
|
|
||||||
|
public PlayerRenderEntry(final UUID player) {
|
||||||
|
this.player = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UUID getRender() {
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addRender(final ChunkRenderEntry chunk) {
|
||||||
|
final int index = Collections.binarySearch(chunks, chunk);
|
||||||
|
|
||||||
|
if (index < 0) {
|
||||||
|
chunks.add(-(index + 1), chunk);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeRender(final ChunkRenderEntry chunk) {
|
||||||
|
final int index = Collections.binarySearch(chunks, chunk);
|
||||||
|
|
||||||
|
if (index >= 0) {
|
||||||
|
chunks.remove(index);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsRender(final ChunkRenderEntry chunkRenderEntry) {
|
||||||
|
return Collections.binarySearch(chunks, chunkRenderEntry) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<ChunkRenderEntry> streamRenders() {
|
||||||
|
return chunks.stream();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static final class ChunkRenderEntry implements RenderEntry<ChunkRenderEntry.ChunkCoordinate, PlayerRenderEntry> {
|
||||||
|
private final ChunkCoordinate coords;
|
||||||
|
private final List<PlayerRenderEntry> players = new ArrayList<>();
|
||||||
|
|
||||||
|
public ChunkRenderEntry(final int chunkX, final int chunkZ) {
|
||||||
|
coords = new ChunkCoordinate(chunkX, chunkZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChunkCoordinate getRender() {
|
||||||
|
return coords;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addRender(final PlayerRenderEntry player) {
|
||||||
|
final int index = Collections.binarySearch(players, player);
|
||||||
|
|
||||||
|
if (index < 0) {
|
||||||
|
players.add(-(index + 1), player);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeRender(final PlayerRenderEntry player) {
|
||||||
|
final int index = Collections.binarySearch(players, player);
|
||||||
|
|
||||||
|
if (index >= 0) {
|
||||||
|
players.remove(index);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsRender(final PlayerRenderEntry chunkRenderEntry) {
|
||||||
|
return Collections.binarySearch(players, chunkRenderEntry) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<PlayerRenderEntry> streamRenders() {
|
||||||
|
return players.stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
public record ChunkCoordinate(int x, int z) implements Comparable<ChunkCoordinate> {
|
||||||
|
@Override
|
||||||
|
public int compareTo(final ChunkCoordinate o) {
|
||||||
|
final int compX = Integer.compare(x, o.x);
|
||||||
|
|
||||||
|
if (compX == 0)
|
||||||
|
return Integer.compare(z, o.z);
|
||||||
|
|
||||||
|
return compX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface ChunkEntryChangeHandler {
|
||||||
|
void onChange(final ChunkRenderEntry chunk);
|
||||||
|
}
|
||||||
|
}
|
43
src/dev/w1zzrd/invtweaks/listener/ChestBreakListener.java
Normal file
43
src/dev/w1zzrd/invtweaks/listener/ChestBreakListener.java
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package dev.w1zzrd.invtweaks.listener;
|
||||||
|
|
||||||
|
import dev.w1zzrd.invtweaks.feature.NamedChestManager;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.block.Block;
|
||||||
|
import org.bukkit.block.Chest;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.block.BlockBreakEvent;
|
||||||
|
import org.bukkit.event.block.BlockEvent;
|
||||||
|
import org.bukkit.event.block.BlockPlaceEvent;
|
||||||
|
|
||||||
|
import static dev.w1zzrd.invtweaks.listener.PlayerMoveRenderListener.RENDER_RADIUS;
|
||||||
|
import static dev.w1zzrd.spigot.wizcompat.block.Chests.getLeftChest;
|
||||||
|
|
||||||
|
public final class ChestBreakListener implements Listener {
|
||||||
|
|
||||||
|
private final NamedChestManager manager;
|
||||||
|
|
||||||
|
public ChestBreakListener(final NamedChestManager manager) {
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onChestBreak(final BlockBreakEvent event) {
|
||||||
|
if (!event.isCancelled())
|
||||||
|
processBlockEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onChestPlace(final BlockPlaceEvent event) {
|
||||||
|
if (!event.isCancelled())
|
||||||
|
processBlockEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processBlockEvent(final BlockEvent event) {
|
||||||
|
if (event.getBlock().getType() == Material.CHEST || event.getBlock().getType() == Material.TRAPPED_CHEST) {
|
||||||
|
final Chest chest = (Chest) event.getBlock().getState();
|
||||||
|
manager.removeTag(chest);
|
||||||
|
manager.renderTags(chest.getChunk(), RENDER_RADIUS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
package dev.w1zzrd.invtweaks.listener;
|
||||||
|
|
||||||
|
import dev.w1zzrd.invtweaks.feature.NamedChestManager;
|
||||||
|
import org.bukkit.Chunk;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
|
import org.bukkit.event.player.PlayerMoveEvent;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public final class PlayerMoveRenderListener implements Listener {
|
||||||
|
public static final int RENDER_RADIUS = 3;
|
||||||
|
|
||||||
|
private final List<Chunk> trackers = new ArrayList<>();
|
||||||
|
private final List<UUID> tracked = new ArrayList<>();
|
||||||
|
private final NamedChestManager manager;
|
||||||
|
|
||||||
|
public PlayerMoveRenderListener(final NamedChestManager manager) {
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerMove(final PlayerMoveEvent event) {
|
||||||
|
final int index = Collections.binarySearch(tracked, event.getPlayer().getUniqueId());
|
||||||
|
final Player who = event.getPlayer();
|
||||||
|
final Chunk chunk = who.getLocation().getChunk();
|
||||||
|
|
||||||
|
if (index < 0) {
|
||||||
|
final int actualIndex = -(index + 1);
|
||||||
|
trackers.add(actualIndex, chunk);
|
||||||
|
tracked.add(actualIndex, who.getUniqueId());
|
||||||
|
triggerRender(who);
|
||||||
|
}
|
||||||
|
else if (!trackers.get(index).equals(event.getPlayer().getLocation().getChunk())) {
|
||||||
|
trackers.set(index, chunk);
|
||||||
|
triggerRender(event.getPlayer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerJoin(final PlayerJoinEvent event) {
|
||||||
|
final int index = Collections.binarySearch(tracked, event.getPlayer().getUniqueId());
|
||||||
|
|
||||||
|
// Should always be true
|
||||||
|
if (index < 0) {
|
||||||
|
trackers.add(-(index + 1), event.getPlayer().getLocation().getChunk());
|
||||||
|
tracked.add(-(index + 1), event.getPlayer().getUniqueId());
|
||||||
|
triggerRender(event.getPlayer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerLeave(final PlayerQuitEvent event) {
|
||||||
|
final int index = Collections.binarySearch(tracked, event.getPlayer().getUniqueId());
|
||||||
|
|
||||||
|
// Should always be true
|
||||||
|
if (index >= 0) {
|
||||||
|
trackers.remove(index);
|
||||||
|
tracked.remove(index);
|
||||||
|
manager.untrackPlayer(event.getPlayer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void triggerRender(final Player player) {
|
||||||
|
manager.renderTags(player, RENDER_RADIUS);
|
||||||
|
}
|
||||||
|
}
|
21
src/dev/w1zzrd/invtweaks/listener/SignEditListener.java
Normal file
21
src/dev/w1zzrd/invtweaks/listener/SignEditListener.java
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package dev.w1zzrd.invtweaks.listener;
|
||||||
|
|
||||||
|
import dev.w1zzrd.spigot.wizcompat.packet.Players;
|
||||||
|
import org.bukkit.block.Sign;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.block.Action;
|
||||||
|
import org.bukkit.event.player.PlayerInteractEvent;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class SignEditListener implements Listener {
|
||||||
|
@EventHandler
|
||||||
|
public void onSignClick(final PlayerInteractEvent event) {
|
||||||
|
if(event.getAction() == Action.RIGHT_CLICK_BLOCK &&
|
||||||
|
Objects.requireNonNull(event.getClickedBlock()).getState() instanceof Sign &&
|
||||||
|
event.getPlayer().isSneaking()) { // Sneak-right-click to edit sign
|
||||||
|
Players.openSignEditor(event.getPlayer(), event.getClickedBlock().getLocation());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -112,9 +112,16 @@ public class TabCompletionListener implements Listener {
|
|||||||
public static Material getMaterialMatching(final String arg) {
|
public static Material getMaterialMatching(final String arg) {
|
||||||
final List<Material> mats = getAllMaterialsMatching(arg).collect(Collectors.toList());
|
final List<Material> mats = getAllMaterialsMatching(arg).collect(Collectors.toList());
|
||||||
|
|
||||||
|
if (mats.size() == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (mats.size() == 1)
|
||||||
|
return mats.get(0);
|
||||||
|
|
||||||
return mats.stream()
|
return mats.stream()
|
||||||
.filter(it -> multiNS ? arg.equals(it.getKey().toString()) : arg.equals(it.getKey().getKey()))
|
.filter(it -> multiNS || arg.contains(":") ? arg.equals(it.getKey().toString()) : arg.equals(it.getKey().getKey()))
|
||||||
.findFirst().orElse(mats.size() == 1 ? mats.get(0) : null);
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,12 @@ import org.bukkit.inventory.ItemStack;
|
|||||||
import org.bukkit.inventory.meta.Damageable;
|
import org.bukkit.inventory.meta.Damageable;
|
||||||
import org.bukkit.inventory.meta.ItemMeta;
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import java.util.*;
|
import static dev.w1zzrd.spigot.wizcompat.command.CommandUtils.errorMessage;
|
||||||
|
|
||||||
import static org.bukkit.Material.*;
|
import static org.bukkit.Material.*;
|
||||||
|
|
||||||
public class TreeCapitatorListener implements Listener {
|
public class TreeCapitatorListener implements Listener {
|
||||||
@ -27,13 +30,38 @@ public class TreeCapitatorListener implements Listener {
|
|||||||
JUNGLE_LOG,
|
JUNGLE_LOG,
|
||||||
DARK_OAK_LOG,
|
DARK_OAK_LOG,
|
||||||
SPRUCE_LOG,
|
SPRUCE_LOG,
|
||||||
|
CRIMSON_STEM,
|
||||||
|
WARPED_STEM,
|
||||||
|
ACACIA_WOOD,
|
||||||
|
OAK_WOOD,
|
||||||
|
BIRCH_WOOD,
|
||||||
|
JUNGLE_WOOD,
|
||||||
|
DARK_OAK_WOOD,
|
||||||
|
SPRUCE_WOOD,
|
||||||
|
CRIMSON_STEM,
|
||||||
|
WARPED_STEM,
|
||||||
|
CRIMSON_HYPHAE,
|
||||||
|
WARPED_HYPHAE,
|
||||||
|
STRIPPED_ACACIA_LOG,
|
||||||
|
STRIPPED_OAK_LOG,
|
||||||
|
STRIPPED_BIRCH_LOG,
|
||||||
|
STRIPPED_JUNGLE_LOG,
|
||||||
|
STRIPPED_DARK_OAK_LOG,
|
||||||
|
STRIPPED_SPRUCE_LOG,
|
||||||
|
STRIPPED_CRIMSON_STEM,
|
||||||
|
STRIPPED_WARPED_STEM,
|
||||||
|
STRIPPED_CRIMSON_HYPHAE,
|
||||||
|
STRIPPED_WARPED_HYPHAE,
|
||||||
|
|
||||||
ACACIA_LEAVES,
|
ACACIA_LEAVES,
|
||||||
OAK_LEAVES,
|
OAK_LEAVES,
|
||||||
BIRCH_LEAVES,
|
BIRCH_LEAVES,
|
||||||
JUNGLE_LEAVES,
|
JUNGLE_LEAVES,
|
||||||
DARK_OAK_LEAVES,
|
DARK_OAK_LEAVES,
|
||||||
SPRUCE_LEAVES
|
SPRUCE_LEAVES,
|
||||||
|
NETHER_WART_BLOCK,
|
||||||
|
WARPED_WART_BLOCK,
|
||||||
|
SHROOMLIGHT
|
||||||
);
|
);
|
||||||
|
|
||||||
private static final List<Material> leaves = Arrays.asList(
|
private static final List<Material> leaves = Arrays.asList(
|
||||||
@ -42,13 +70,20 @@ public class TreeCapitatorListener implements Listener {
|
|||||||
BIRCH_LEAVES,
|
BIRCH_LEAVES,
|
||||||
JUNGLE_LEAVES,
|
JUNGLE_LEAVES,
|
||||||
DARK_OAK_LEAVES,
|
DARK_OAK_LEAVES,
|
||||||
SPRUCE_LEAVES
|
SPRUCE_LEAVES,
|
||||||
|
NETHER_WART_BLOCK,
|
||||||
|
WARPED_WART_BLOCK,
|
||||||
|
SHROOMLIGHT
|
||||||
);
|
);
|
||||||
|
|
||||||
private final Enchantment capitatorEnchantment;
|
private final Enchantment capitatorEnchantment;
|
||||||
|
private final double hungerPerBlock;
|
||||||
|
private final int minHunger;
|
||||||
|
|
||||||
public TreeCapitatorListener(final Enchantment capitatorEnchantment) {
|
public TreeCapitatorListener(final Enchantment capitatorEnchantment, final double hungerPerBlock, final int minHunger) {
|
||||||
this.capitatorEnchantment = capitatorEnchantment;
|
this.capitatorEnchantment = capitatorEnchantment;
|
||||||
|
this.hungerPerBlock = hungerPerBlock;
|
||||||
|
this.minHunger = minHunger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
@ -57,6 +92,12 @@ public class TreeCapitatorListener implements Listener {
|
|||||||
if (event.isCancelled() || !handTool.containsEnchantment(capitatorEnchantment))
|
if (event.isCancelled() || !handTool.containsEnchantment(capitatorEnchantment))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Check if capitator functionality is prevented by hunger
|
||||||
|
if (event.getPlayer().getFoodLevel() < minHunger) {
|
||||||
|
event.getPlayer().spigot().sendMessage(errorMessage("You are too tired to fell the tree"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (handTool.getItemMeta() instanceof final Damageable tool) {
|
if (handTool.getItemMeta() instanceof final Damageable tool) {
|
||||||
if (!leaves.contains(event.getBlock().getType()) && targetMaterials.contains(event.getBlock().getType())) {
|
if (!leaves.contains(event.getBlock().getType()) && targetMaterials.contains(event.getBlock().getType())) {
|
||||||
int logBreakCount = 0;
|
int logBreakCount = 0;
|
||||||
@ -76,13 +117,18 @@ public class TreeCapitatorListener implements Listener {
|
|||||||
event.getPlayer().playSound(event.getPlayer().getLocation(), Sound.ENTITY_ITEM_BREAK, 1.0f, 1.0f);
|
event.getPlayer().playSound(event.getPlayer().getLocation(), Sound.ENTITY_ITEM_BREAK, 1.0f, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (event.getPlayer().getGameMode() == GameMode.ADVENTURE || event.getPlayer().getGameMode() == GameMode.SURVIVAL) {
|
||||||
|
final int hunger = (int) Math.round(hungerPerBlock * logBreakCount);
|
||||||
|
event.getPlayer().setFoodLevel(Math.max(0, event.getPlayer().getFoodLevel() - hunger));
|
||||||
|
}
|
||||||
|
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getMaxUses(final ItemStack stack, final Damageable tool) {
|
private static int getMaxUses(final ItemStack stack, final Damageable tool) {
|
||||||
return ((stack.getEnchantmentLevel(Enchantment.DURABILITY) + 1) * (stack.getType().getMaxDurability()) - tool.getDamage());
|
return ((stack.getEnchantmentLevel(Enchantment.DURABILITY) + 1) * (stack.getType().getMaxDurability()) - tool.getDamage()) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean applyDamage(final ItemStack stack, final int brokenBlocks) {
|
private static boolean applyDamage(final ItemStack stack, final int brokenBlocks) {
|
||||||
|
367
src/dev/w1zzrd/invtweaks/serialization/ChestNameConfig.java
Normal file
367
src/dev/w1zzrd/invtweaks/serialization/ChestNameConfig.java
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
package dev.w1zzrd.invtweaks.serialization;
|
||||||
|
|
||||||
|
import dev.w1zzrd.spigot.wizcompat.serialization.SimpleReflectiveConfigItem;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class ChestNameConfig extends SimpleReflectiveConfigItem {
|
||||||
|
|
||||||
|
private List<ChestNameWorldEntry> worldEntries;
|
||||||
|
|
||||||
|
public ChestNameConfig(Map<String, Object> mappings) {
|
||||||
|
super(mappings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChestNameConfig() {
|
||||||
|
this(Collections.emptyMap());
|
||||||
|
worldEntries = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChestNameWorldEntry getEntry(final UUID worldID, final boolean addIfMissing) {
|
||||||
|
final int index = indexOf(worldID);
|
||||||
|
|
||||||
|
if (index >= 0)
|
||||||
|
return worldEntries.get(index);
|
||||||
|
else if (addIfMissing) {
|
||||||
|
final ChestNameWorldEntry entry = new ChestNameWorldEntry(worldID);
|
||||||
|
worldEntries.add(-(index + 1), entry);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChestNameWorldEntry getEntry(final UUID worldID) {
|
||||||
|
return getEntry(worldID, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChestNameWorldEntry.ChestNameChunkEntry getChunkEntry(final UUID worldID, final int chunkX, final int chunkZ) {
|
||||||
|
final int index = indexOf(worldID);
|
||||||
|
|
||||||
|
if (index >= 0)
|
||||||
|
return worldEntries.get(index).getChunk(chunkX, chunkZ);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChestNameWorldEntry.ChestNameChunkEntry.ChestNameConfigEntry getEntryAt(final UUID worldID, final Location location) {
|
||||||
|
final ChestNameWorldEntry.ChestNameChunkEntry chunk = getChunkEntry(worldID, location.getBlockX() >> 4, location.getBlockZ() >> 4);
|
||||||
|
|
||||||
|
if (chunk != null) {
|
||||||
|
return chunk.getEntry(location.getBlockX(), location.getBlockY(), location.getBlockZ(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(final UUID worldID) {
|
||||||
|
return getEntry(worldID) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(final UUID worldID) {
|
||||||
|
getEntry(worldID);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(final UUID worldID) {
|
||||||
|
final int index = indexOf(worldID);
|
||||||
|
|
||||||
|
if (index >= 0)
|
||||||
|
worldEntries.remove(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int indexOf(final UUID worldID) {
|
||||||
|
return Collections.binarySearch(worldEntries, new ChestNameWorldEntry(worldID));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteEmptyWorld(final ChestNameWorldEntry world) {
|
||||||
|
if (!world.hasEntries()) {
|
||||||
|
final int index = Collections.binarySearch(worldEntries, world);
|
||||||
|
|
||||||
|
if (index >= 0)
|
||||||
|
worldEntries.remove(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static final class ChestNameWorldEntry extends SimpleReflectiveConfigItem implements Comparable<ChestNameWorldEntry> {
|
||||||
|
private String worldIDStr;
|
||||||
|
private final transient UUID worldID;
|
||||||
|
private List<ChestNameChunkEntry> chunks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Required constructor for deserializing data
|
||||||
|
*
|
||||||
|
* @param mappings Data to deserialize
|
||||||
|
*/
|
||||||
|
public ChestNameWorldEntry(Map<String, Object> mappings) {
|
||||||
|
super(mappings);
|
||||||
|
worldID = UUID.fromString(worldIDStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChestNameWorldEntry(final UUID worldID) {
|
||||||
|
super(Collections.emptyMap());
|
||||||
|
this.worldID = worldID;
|
||||||
|
worldIDStr = worldID.toString();
|
||||||
|
chunks = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getWorldID() {
|
||||||
|
return worldID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(final ChestNameWorldEntry o) {
|
||||||
|
return getWorldID().compareTo(o.getWorldID());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName(final Location location) {
|
||||||
|
final ChestNameChunkEntry chunk = getChunk(location, false);
|
||||||
|
|
||||||
|
if (chunk == null)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
return chunk.getName(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(final Location location) {
|
||||||
|
return getName(location) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasEntries() {
|
||||||
|
return chunks.size() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(final Location location, final String name) {
|
||||||
|
Objects.requireNonNull(getChunk(location, true)).add(location, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(final Location location) {
|
||||||
|
final int index = indexOf(location);
|
||||||
|
|
||||||
|
if (index >= 0)
|
||||||
|
chunks.remove(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int indexOf(final Location location) {
|
||||||
|
return Collections.binarySearch(chunks, new ChestNameChunkEntry(location));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int indexOf(final int chunkX, final int chunkZ) {
|
||||||
|
return Collections.binarySearch(chunks, new ChestNameChunkEntry(chunkX, chunkZ));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ChestNameChunkEntry getChunk(final Location location, final boolean addIfMissing) {
|
||||||
|
final int index = indexOf(location);
|
||||||
|
|
||||||
|
if (index >= 0)
|
||||||
|
return chunks.get(index);
|
||||||
|
else if (addIfMissing) {
|
||||||
|
final ChestNameChunkEntry entry = new ChestNameChunkEntry(location);
|
||||||
|
chunks.add(-(index + 1), entry);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ChestNameChunkEntry getChunk(final int chunkX, final int chunkZ) {
|
||||||
|
final int index = indexOf(chunkX, chunkZ);
|
||||||
|
|
||||||
|
if (index >= 0)
|
||||||
|
return chunks.get(index);
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearChunk(final Location location) {
|
||||||
|
final int index = indexOf(location);
|
||||||
|
|
||||||
|
if (index >= 0)
|
||||||
|
chunks.remove(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteEmptyChunk(final ChestNameChunkEntry chunk) {
|
||||||
|
if (!chunk.hasEntries()) {
|
||||||
|
final int index = Collections.binarySearch(chunks, chunk);
|
||||||
|
|
||||||
|
if (index >= 0)
|
||||||
|
chunks.remove(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class ChestNameChunkEntry extends SimpleReflectiveConfigItem implements Comparable<ChestNameChunkEntry> {
|
||||||
|
private int x, z;
|
||||||
|
private List<ChestNameConfigEntry> entries;
|
||||||
|
private transient boolean dirty = false;
|
||||||
|
|
||||||
|
public ChestNameChunkEntry(Map<String, Object> mappings) {
|
||||||
|
super(mappings);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChestNameChunkEntry(final int x, final int z) {
|
||||||
|
super(Collections.emptyMap());
|
||||||
|
this.x = x;
|
||||||
|
this.z = z;
|
||||||
|
entries = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
ChestNameChunkEntry(final Location location) {
|
||||||
|
// Convert world coordinates to chunk coordinates
|
||||||
|
this(location.getBlockX() >> 4, location.getBlockZ() >> 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getX() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getZ() {
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDirty() {
|
||||||
|
return dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDirty(final boolean dirty) {
|
||||||
|
this.dirty = dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasEntries() {
|
||||||
|
return entries.size() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(final int x, final int y, final int z, final String name) {
|
||||||
|
final ChestNameConfigEntry check = getEntry(x, y, z, true);
|
||||||
|
|
||||||
|
assert check != null;
|
||||||
|
check.setName(name);
|
||||||
|
|
||||||
|
setDirty(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(final Location location, final String name) {
|
||||||
|
add(location.getBlockX(), location.getBlockY(), location.getBlockZ(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName(final Location location) {
|
||||||
|
final ChestNameConfigEntry entry = getEntry(location.getBlockX(), location.getBlockY(), location.getBlockZ(), false);
|
||||||
|
|
||||||
|
if (entry == null)
|
||||||
|
return null;
|
||||||
|
else
|
||||||
|
return entry.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ChestNameConfigEntry getEntry(final int x, final int y, final int z, final boolean createIfMissing) {
|
||||||
|
final ChestNameConfigEntry find = new ChestNameConfigEntry(x, y, z);
|
||||||
|
|
||||||
|
final int index = indexOf(find);
|
||||||
|
|
||||||
|
if (index >= 0)
|
||||||
|
return entries.get(index);
|
||||||
|
else if (createIfMissing) {
|
||||||
|
entries.add(-(index + 1), find);
|
||||||
|
return find;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeEntry(final ChestNameConfigEntry entry) {
|
||||||
|
final int index = indexOf(entry);
|
||||||
|
|
||||||
|
if (index >= 0) {
|
||||||
|
entries.remove(index);
|
||||||
|
setDirty(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream<ChestNameConfigEntry> streamEntries() {
|
||||||
|
return entries.stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int indexOf(final ChestNameConfigEntry find) {
|
||||||
|
return Collections.binarySearch(entries, find);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(final ChestNameChunkEntry o) {
|
||||||
|
final int compX = Integer.compare(x, o.x);
|
||||||
|
|
||||||
|
if (compX == 0)
|
||||||
|
return Integer.compare(z, o.z);
|
||||||
|
|
||||||
|
return compX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class ChestNameConfigEntry extends SimpleReflectiveConfigItem implements Comparable<ChestNameConfigEntry> {
|
||||||
|
private transient Object entity;
|
||||||
|
private transient int locInt;
|
||||||
|
private String loc;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public ChestNameConfigEntry(Map<String, Object> mappings) {
|
||||||
|
super(mappings);
|
||||||
|
locInt = Integer.parseInt(loc, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChestNameConfigEntry(final int x, final int y, final int z, final String name) {
|
||||||
|
super(Collections.emptyMap());
|
||||||
|
locInt = ((y & 0xFFF) << 8) | ((x & 0xF) << 4) | (z & 0xF);
|
||||||
|
loc = Integer.toString(locInt, 16);
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChestNameConfigEntry(final int x, final int y, final int z) {
|
||||||
|
this(x, y, z, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChestNameConfigEntry(final Location location, final String name) {
|
||||||
|
this(location.getBlockX() & 0xF, location.getBlockY(), location.getBlockZ() & 0xF, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChestNameConfigEntry(final Location location) {
|
||||||
|
this(location, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getEntity(final EntityCreator creator) {
|
||||||
|
if (entity == null)
|
||||||
|
entity = creator.createFakeEntity();
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getChunkX() {
|
||||||
|
return (locInt >>> 4) & 0xF;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getChunkZ() {
|
||||||
|
return locInt & 0xF;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getY() {
|
||||||
|
return (locInt >>> 8) & 0xFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setName(final String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(final ChestNameConfigEntry o) {
|
||||||
|
return Integer.compare(locInt, o.locInt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface EntityCreator {
|
||||||
|
Object createFakeEntity();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package dev.w1zzrd.invtweaks.serialization;
|
package dev.w1zzrd.invtweaks.serialization;
|
||||||
|
|
||||||
|
import dev.w1zzrd.spigot.wizcompat.serialization.SimpleReflectiveConfigItem;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package dev.w1zzrd.invtweaks.serialization;
|
package dev.w1zzrd.invtweaks.serialization;
|
||||||
|
|
||||||
|
import dev.w1zzrd.spigot.wizcompat.serialization.SimpleReflectiveConfigItem;
|
||||||
|
import dev.w1zzrd.spigot.wizcompat.serialization.UUIDList;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package dev.w1zzrd.invtweaks.serialization;
|
package dev.w1zzrd.invtweaks.serialization;
|
||||||
|
|
||||||
|
import dev.w1zzrd.spigot.wizcompat.serialization.SimpleReflectiveConfigItem;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class SearchConfig extends SimpleReflectiveConfigItem {
|
public class SearchConfig extends SimpleReflectiveConfigItem {
|
||||||
|
@ -1,126 +0,0 @@
|
|||||||
package dev.w1zzrd.invtweaks.serialization;
|
|
||||||
|
|
||||||
import org.bukkit.configuration.serialization.ConfigurationSerializable;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configuration serializable type for automatically serializing/deserializing fields
|
|
||||||
*/
|
|
||||||
public class SimpleReflectiveConfigItem implements ConfigurationSerializable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Required constructor for deserializing data
|
|
||||||
* @param mappings Data to deserialize
|
|
||||||
*/
|
|
||||||
public SimpleReflectiveConfigItem(final Map<String, Object> mappings) {
|
|
||||||
deserializeMapped(mappings);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, Object> serialize() {
|
|
||||||
final HashMap<String, Object> values = new HashMap<>();
|
|
||||||
Arrays.stream(getClass().getDeclaredFields())
|
|
||||||
.filter(it -> !Modifier.isTransient(it.getModifiers()) && !Modifier.isStatic(it.getModifiers()))
|
|
||||||
.forEach(it -> {
|
|
||||||
try {
|
|
||||||
it.setAccessible(true);
|
|
||||||
values.put(it.getName(), it.get(this));
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deserialize mapped data by name
|
|
||||||
* @param mappings Data to deserialize
|
|
||||||
*/
|
|
||||||
private void deserializeMapped(final Map<String, Object> mappings) {
|
|
||||||
for (final Field field : getClass().getDeclaredFields()) {
|
|
||||||
if (Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers()))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Try to find mappings by field name
|
|
||||||
if (mappings.containsKey(field.getName()))
|
|
||||||
parse(mappings.get(field.getName()), field, this);
|
|
||||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
|
||||||
// This shouldn't happen
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempt to parse a value such that it can be stored in the given field
|
|
||||||
* @param value Value to parse
|
|
||||||
* @param field Field to store value in
|
|
||||||
* @param instance Configuration object for which to parse the vaue
|
|
||||||
* @throws IllegalAccessException Should never be thrown
|
|
||||||
* @throws InvocationTargetException Should never be thrown
|
|
||||||
*/
|
|
||||||
private static void parse(final Object value, final Field field, final Object instance) throws IllegalAccessException, InvocationTargetException {
|
|
||||||
field.setAccessible(true);
|
|
||||||
|
|
||||||
if (field.getType().isPrimitive() && value == null)
|
|
||||||
throw new NullPointerException("Attempt to assign null to a primitive field");
|
|
||||||
|
|
||||||
final Class<?> boxed = getBoxedType(field.getType());
|
|
||||||
|
|
||||||
if (boxed.isAssignableFrom(value.getClass())) {
|
|
||||||
field.set(instance, value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value instanceof String) {
|
|
||||||
final Method parser = locateParser(boxed, field.getType().isPrimitive() ? field.getType() : null);
|
|
||||||
if (parser != null)
|
|
||||||
field.set(instance, parser.invoke(null, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IllegalArgumentException(String.format("No defined parser for value \"%s\"", value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converter for boxed primitives
|
|
||||||
* @param cls Primitive type
|
|
||||||
* @return Boxed type for primitive type, else the given type
|
|
||||||
*/
|
|
||||||
private static Class<?> getBoxedType(final Class<?> cls) {
|
|
||||||
if (cls == int.class) return Integer.class;
|
|
||||||
else if (cls == double.class) return Double.class;
|
|
||||||
else if (cls == long.class) return Long.class;
|
|
||||||
else if (cls == float.class) return Float.class;
|
|
||||||
else if (cls == char.class) return Character.class;
|
|
||||||
else if (cls == byte.class) return Byte.class;
|
|
||||||
else if (cls == short.class) return Short.class;
|
|
||||||
else if (cls == boolean.class) return Boolean.class;
|
|
||||||
else return cls;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempt to find a parser method for the given type
|
|
||||||
* @param cls Type to find parser for
|
|
||||||
* @param prim Primitive type find parser for (if applicable)
|
|
||||||
* @return Static method which accepts a {@link String} argument and returns the desired type
|
|
||||||
*/
|
|
||||||
private static Method locateParser(final Class<?> cls, final Class<?> prim) {
|
|
||||||
for (final Method method : cls.getDeclaredMethods()) {
|
|
||||||
final Class<?>[] params = method.getParameterTypes();
|
|
||||||
if ((method.getName().startsWith("parse" + cls.getSimpleName()) || method.getName().equals("fromString")) &&
|
|
||||||
Modifier.isStatic(method.getModifiers()) &&
|
|
||||||
method.getReturnType().equals(prim != null ? prim : cls) &&
|
|
||||||
params.length == 1 && params[0].equals(String.class))
|
|
||||||
method.setAccessible(true);
|
|
||||||
return method;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
package dev.w1zzrd.invtweaks.serialization;
|
|
||||||
|
|
||||||
import org.bukkit.configuration.serialization.ConfigurationSerializable;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serializable dataclass holding a collection of {@link UUID} objects
|
|
||||||
*/
|
|
||||||
public class UUIDList implements ConfigurationSerializable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is public to decrease performance overhead
|
|
||||||
*/
|
|
||||||
public final List<UUID> uuids;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrap a backing list of {@link UUID} objects to enable configuration serialization
|
|
||||||
* @param backingList Modifiable, backing list of {@link UUID} objects
|
|
||||||
*/
|
|
||||||
public UUIDList(final List<UUID> backingList) {
|
|
||||||
uuids = backingList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a blank list of {@link UUID} objects
|
|
||||||
*/
|
|
||||||
public UUIDList() {
|
|
||||||
this(new ArrayList<>());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deserialize serialized UUID strings
|
|
||||||
* @param values Data to deserialize
|
|
||||||
*/
|
|
||||||
public UUIDList(final Map<String, Object> values) {
|
|
||||||
this();
|
|
||||||
if (values.containsKey("values"))
|
|
||||||
uuids.addAll(((Collection<String>)values.get("values")).stream().map(UUID::fromString).collect(Collectors.toSet()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, Object> serialize() {
|
|
||||||
return Collections.singletonMap("values", uuids.stream().map(UUID::toString).collect(Collectors.toList()));
|
|
||||||
}
|
|
||||||
}
|
|
243
test_env.sh
243
test_env.sh
@ -1,243 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
MIN_JAVA_VERSION=15
|
|
||||||
[ -z "$JAVA_PATH" ] && JAVA_PATH=$(which java)
|
|
||||||
[ -z "$PYTHON_PATH" ] && PYTHON_PATH=$(which python3)
|
|
||||||
[ -z "$SHELL" ] && SHELL=/bin/bash
|
|
||||||
|
|
||||||
SCRIPT_RUN_NAME="start.sh"
|
|
||||||
SCRIPT_DEBUG_NAME="start_debug.sh"
|
|
||||||
|
|
||||||
# JVM options graciously "borrowed" from the PaperMC Timings JVM Tuning page
|
|
||||||
# https://aikar.co/2018/07/02/tuning-the-jvm-g1gc-garbage-collector-flags-for-minecraft/
|
|
||||||
MC_AUTORUN_FLAGS="-XX:+UnlockExperimentalVMOptions -Xmx1G -Xms512M -XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 -XX:+DisableExplicitGC -XX:+AlwaysPreTouch -XX:G1NewSizePercent=30 -XX:G1MaxNewSizePercent=40 -XX:G1HeapRegionSize=8M -XX:G1ReservePercent=20 -XX:G1HeapWastePercent=5 -XX:G1MixedGCCountTarget=4 -XX:InitiatingHeapOccupancyPercent=15 -XX:G1MixedGCLiveThresholdPercent=90 -XX:G1RSetUpdatingPauseTimePercent=5 -XX:SurvivorRatio=32 -XX:+PerfDisableSharedMem -XX:MaxTenuringThreshold=1"
|
|
||||||
MC_VERSION="1.16"
|
|
||||||
ENV_DIR="$(pwd)/server"
|
|
||||||
PAPER_JAR_FILE="paper.jar"
|
|
||||||
PLUGIN_PATH=""
|
|
||||||
UPDATE_PAPER=true
|
|
||||||
AUTORUN=""
|
|
||||||
EULA=false
|
|
||||||
|
|
||||||
print_help() {
|
|
||||||
echo -e "Paper test server deployment script\nArguments:"
|
|
||||||
echo " -v [VERSION] - Specify minecraft version to download"
|
|
||||||
echo " -p [PATH] - Path to plugin to debug"
|
|
||||||
echo " -d [PATH] - Path to directory where server will be deployed"
|
|
||||||
echo " -j [PATH] - Java executable path"
|
|
||||||
echo " --python [PATH] - Python3 executable path"
|
|
||||||
echo " --no-update - Don't download updates for Paper if jar file already exists"
|
|
||||||
echo " -n - Same as --no-update"
|
|
||||||
echo " -s [type] - Autorun server after deployment. Type is \"debug\" to start with remote debugging, else \"run\""
|
|
||||||
echo " -e - Accepts the EULA for the deployed server (user must accept it in a provided prompt)"
|
|
||||||
echo " -h - Show this help prompt"
|
|
||||||
echo -e "Example:\n $0 -v 1.16 -p out/plugin.jar -s debug -e"
|
|
||||||
}
|
|
||||||
|
|
||||||
print_error() {
|
|
||||||
echo "[ERROR] $1" >&2
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
print_info() {
|
|
||||||
if [ $# -eq 2 ]
|
|
||||||
then
|
|
||||||
echo -e "$1" "[TEST_ENV] $2"
|
|
||||||
else
|
|
||||||
echo -e "[TEST_ENV] $1"
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
SKIP=false
|
|
||||||
CUR=""
|
|
||||||
TARGET=""
|
|
||||||
for arg in "$@"
|
|
||||||
do
|
|
||||||
if $SKIP
|
|
||||||
then
|
|
||||||
eval "$TARGET"="$arg"
|
|
||||||
SKIP=false
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
CUR="$arg"
|
|
||||||
|
|
||||||
case "$arg" in
|
|
||||||
"-h")
|
|
||||||
print_help
|
|
||||||
exit
|
|
||||||
;;
|
|
||||||
|
|
||||||
"-v")
|
|
||||||
TARGET="MC_VERSION"
|
|
||||||
SKIP=true
|
|
||||||
;;
|
|
||||||
|
|
||||||
"-p")
|
|
||||||
TARGET="PLUGIN_PATH"
|
|
||||||
SKIP=true
|
|
||||||
;;
|
|
||||||
|
|
||||||
"-d")
|
|
||||||
TARGET="ENV_DIR"
|
|
||||||
SKIP=true
|
|
||||||
;;
|
|
||||||
|
|
||||||
"-j")
|
|
||||||
TARGET="JAVA_PATH"
|
|
||||||
SKIP=true
|
|
||||||
;;
|
|
||||||
|
|
||||||
"--python")
|
|
||||||
TARGET="PYTHON_PATH"
|
|
||||||
SKIP=true
|
|
||||||
;;
|
|
||||||
|
|
||||||
"-n" | "--no-update")
|
|
||||||
UPDATE_PAPER=false
|
|
||||||
;;
|
|
||||||
|
|
||||||
"-s")
|
|
||||||
TARGET="AUTORUN"
|
|
||||||
SKIP=true
|
|
||||||
;;
|
|
||||||
|
|
||||||
"-e")
|
|
||||||
EULA=true
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
print_error "Unknown argument: $arg"
|
|
||||||
exit
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
if $SKIP
|
|
||||||
then
|
|
||||||
print_error "Malformed parameter $CUR"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
case "$AUTORUN" in
|
|
||||||
"run")
|
|
||||||
AUTORUN="$SCRIPT_RUN_NAME"
|
|
||||||
;;
|
|
||||||
"debug")
|
|
||||||
AUTORUN="$SCRIPT_DEBUG_NAME"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
print_error "Unknown autorun argument \"$AUTORUN\""
|
|
||||||
exit
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
check_java_version() {
|
|
||||||
version=$("$JAVA_PATH" -version 2>&1 | head -n 1 | sed "s/java version \"\\([0-9]*\\)\\..*/\\1/g")
|
|
||||||
if [ "$version" -lt "$MIN_JAVA_VERSION" ]
|
|
||||||
then
|
|
||||||
print_error "Java version is too low! Minimum is $MIN_JAVA_VERSION (found $version)"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
print_info "Found Java version $version"
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
ensure_python3() {
|
|
||||||
if [ -z "$PYTHON_PATH" ]
|
|
||||||
then
|
|
||||||
print_error "Could not locate Python3 which is required for automatic server deployment: please install it"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
print_info "Found Python3 binary"
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
get_paper() {
|
|
||||||
|
|
||||||
print_info "Polling all versions of paper for MC $MC_VERSION"
|
|
||||||
|
|
||||||
link=$(curl -s "https://papermc.io/api/v2/projects/paper/version_group/$MC_VERSION/builds" | python3 -c "import sys, json; versions=json.load(sys.stdin); print('https://papermc.io/api/v2/projects/paper/versions/' + str(versions['builds'][-1]['version']) + '/builds/' + str(versions['builds'][-1]['build']) + '/downloads/' + str(versions['builds'][-1]['downloads']['application']['name']))")
|
|
||||||
|
|
||||||
if [ -f "new_$PAPER_JAR_FILE" ]
|
|
||||||
then
|
|
||||||
rm -f "new_$PAPER_JAR_FILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
print_info "Downloading from pulled link: $link"
|
|
||||||
|
|
||||||
result=$(curl -o "new_$PAPER_JAR_FILE" -s -w "%{http_code}" "$link")
|
|
||||||
|
|
||||||
if [ "$result" -eq 200 ] && [ -f "new_$PAPER_JAR_FILE" ]
|
|
||||||
then
|
|
||||||
rm -f "$PAPER_JAR_FILE"
|
|
||||||
mv "new_$PAPER_JAR_FILE" "$PAPER_JAR_FILE"
|
|
||||||
print_info "Downloaded latest version of paper"
|
|
||||||
return 1
|
|
||||||
else
|
|
||||||
print_info "Could not download new version of paper"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
check_java_version || exit
|
|
||||||
ensure_python3 || exit
|
|
||||||
|
|
||||||
|
|
||||||
if ! [ -d "$ENV_DIR/plugins" ]
|
|
||||||
then
|
|
||||||
mkdir -p "$ENV_DIR/plugins" || (print_error "Could not create server directory at $ENV_DIR" && exit)
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$PLUGIN_PATH" ]
|
|
||||||
then
|
|
||||||
print_info "Copying plugin to server directory..."
|
|
||||||
cp "$PLUGIN_PATH" "$ENV_DIR/plugins" || (print_error "Could not copy targeted plugin to test environment" && exit)
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd "$ENV_DIR" || (print_error "Could not enter directory $ENV_DIR" && exit)
|
|
||||||
{ ! [ -f "$PAPER_JAR_FILE" ] || $UPDATE_PAPER; } && get_paper && { ! [ -f "$PAPER_JAR_FILE" ]; } && exit
|
|
||||||
|
|
||||||
[ -f "$PAPER_JAR_FILE" ] || exit
|
|
||||||
|
|
||||||
if ! [ -f "$SCRIPT_RUN_NAME" ]
|
|
||||||
then
|
|
||||||
echo -e "#!/bin/bash\njava $MC_AUTORUN_FLAGS -jar \"$PAPER_JAR_FILE\" nogui" > "$SCRIPT_RUN_NAME"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! [ -f "$SCRIPT_DEBUG_NAME" ]
|
|
||||||
then
|
|
||||||
echo -e "#!/bin/bash\njava -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 -jar \"$PAPER_JAR_FILE\" nogui" > "$SCRIPT_DEBUG_NAME"
|
|
||||||
fi
|
|
||||||
|
|
||||||
print_info "Test environment successfully prepared!"
|
|
||||||
|
|
||||||
# Disable case-sensitivity for conditional expressions
|
|
||||||
shopt -s nocaseglob
|
|
||||||
|
|
||||||
# No need to ask user to accept EULA if it is already accepted
|
|
||||||
if $EULA && ! { [ -f "eula.txt" ] && [[ "$(cat eula.txt)" =~ eula=true ]]; }
|
|
||||||
then
|
|
||||||
print_info -n "Do you accept the terms of the Minecraft End User License Agreement? (https://account.mojang.com/documents/minecraft_eula) [y/N]: "
|
|
||||||
read -r ACCEPTS
|
|
||||||
|
|
||||||
case "$ACCEPTS" in
|
|
||||||
"y"* | "Y"*)
|
|
||||||
echo "eula=true" > eula.txt
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
print_info "EULA was not accepted"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Re-enable case-sensitivity, just in case
|
|
||||||
shopt -u nocaseglob
|
|
||||||
|
|
||||||
if [ -n "$AUTORUN" ]
|
|
||||||
then
|
|
||||||
print_info "Autorun was specified. Starting server..."
|
|
||||||
"$SHELL" "$AUTORUN"
|
|
||||||
fi
|
|
Loading…
x
Reference in New Issue
Block a user