feat: reworked project structure, 1.20 to 1.21.1 mappings and more
This commit is contained in:
parent
b7be5ad54e
commit
fd9676f6f5
115 changed files with 1399 additions and 248 deletions
208
src/main/java/xyz/ineanto/nicko/Nicko.java
Normal file
208
src/main/java/xyz/ineanto/nicko/Nicko.java
Normal file
|
@ -0,0 +1,208 @@
|
|||
package xyz.ineanto.nicko;
|
||||
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import xyz.ineanto.nicko.appearance.random.RandomNameFetcher;
|
||||
import xyz.ineanto.nicko.command.NickoCommand;
|
||||
import xyz.ineanto.nicko.config.Configuration;
|
||||
import xyz.ineanto.nicko.config.ConfigurationManager;
|
||||
import xyz.ineanto.nicko.event.PlayerJoinListener;
|
||||
import xyz.ineanto.nicko.event.PlayerQuitListener;
|
||||
import xyz.ineanto.nicko.language.CustomLanguage;
|
||||
import xyz.ineanto.nicko.language.Language;
|
||||
import xyz.ineanto.nicko.mapping.MappingManager;
|
||||
import xyz.ineanto.nicko.migration.ConfigurationMigrator;
|
||||
import xyz.ineanto.nicko.migration.CustomLocaleMigrator;
|
||||
import xyz.ineanto.nicko.mojang.MojangAPI;
|
||||
import xyz.ineanto.nicko.placeholder.NickoExpansion;
|
||||
import xyz.ineanto.nicko.storage.PlayerDataStore;
|
||||
import xyz.ineanto.nicko.storage.json.JSONStorage;
|
||||
import xyz.ineanto.nicko.storage.map.MapCache;
|
||||
import xyz.ineanto.nicko.storage.name.PlayerNameStore;
|
||||
import xyz.xenondevs.invui.gui.structure.Structure;
|
||||
import xyz.xenondevs.invui.item.builder.ItemBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.SimpleItem;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class Nicko extends JavaPlugin {
|
||||
private static Nicko plugin;
|
||||
|
||||
private final boolean unitTesting;
|
||||
|
||||
private MojangAPI mojangAPI;
|
||||
private PlayerDataStore dataStore;
|
||||
private ConfigurationManager configurationManager;
|
||||
private Configuration configuration;
|
||||
private CustomLanguage customLanguage;
|
||||
private PlayerNameStore nameStore;
|
||||
private RandomNameFetcher nameFetcher;
|
||||
private MappingManager mappingManager;
|
||||
private Metrics metrics;
|
||||
|
||||
public Nicko() {
|
||||
this.unitTesting = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by MockBukkit
|
||||
*/
|
||||
protected Nicko(Configuration configuration) {
|
||||
this.unitTesting = true;
|
||||
this.configuration = configuration;
|
||||
getLogger().info("Unit Testing Mode enabled.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
plugin = this;
|
||||
|
||||
configurationManager = new ConfigurationManager(getDataFolder());
|
||||
configurationManager.saveDefaultConfig();
|
||||
|
||||
dataStore = new PlayerDataStore(mojangAPI, getNickoConfig());
|
||||
mappingManager = new MappingManager();
|
||||
|
||||
if (!MinecraftVersion.TRAILS_AND_TAILS.atOrAbove() || mappingManager.getMappingForServer().isEmpty()) {
|
||||
getLogger().severe("This version (" + MinecraftVersion.getCurrentVersion().getVersion() + ") is not supported by Nicko!");
|
||||
getLogger().severe("As of version 1.0.7, Nicko only supports the latest two majors Minecraft versions. (Currently 1.20.X-1.21.X)");
|
||||
dataStore.getStorage().setError(true);
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
}
|
||||
|
||||
if (!Bukkit.getOnlineMode()) {
|
||||
getLogger().warning("Nicko has not been tested using offline mode!");
|
||||
getLogger().warning("Issues regarding Nicko being used in offline mode will be ignored for now.");
|
||||
}
|
||||
|
||||
try {
|
||||
Class.forName("io.papermc.paper.threadedregions.RegionizedServerInitEvent");
|
||||
getLogger().warning("Nicko has not been tested against Folia and might not work at all!");
|
||||
getLogger().warning("Issues regarding Nicko on Folia will be ignored for now.");
|
||||
} catch (ClassNotFoundException ignored) { }
|
||||
|
||||
getLogger().info("Loading persistence...");
|
||||
if (!dataStore.getStorage().getProvider().init()) {
|
||||
getLogger().severe("Couldn't connect to distant persistence, falling back on local persistence.");
|
||||
final JSONStorage storage = new JSONStorage();
|
||||
storage.getProvider().init();
|
||||
dataStore.setStorage(storage);
|
||||
}
|
||||
|
||||
getLogger().info("Loading cache...");
|
||||
if (!dataStore.getCache().getProvider().init()) {
|
||||
getLogger().severe("Couldn't connect to distant cache, falling back on local cache.");
|
||||
final MapCache cache = new MapCache();
|
||||
cache.getProvider().init();
|
||||
dataStore.setCache(cache);
|
||||
}
|
||||
|
||||
if (!unitTesting) {
|
||||
nameStore = new PlayerNameStore();
|
||||
mojangAPI = new MojangAPI();
|
||||
nameFetcher = new RandomNameFetcher(this);
|
||||
|
||||
new ConfigurationMigrator(this).migrate();
|
||||
|
||||
if (configuration.isCustomLocale()) {
|
||||
try {
|
||||
CustomLanguage.dumpIntoFile(Language.ENGLISH);
|
||||
customLanguage = new CustomLanguage();
|
||||
new CustomLocaleMigrator(this, customLanguage).migrate();
|
||||
getLogger().info("Successfully loaded the custom locale.");
|
||||
} catch (IOException e) {
|
||||
getLogger().severe("Failed to load the custom locale!");
|
||||
}
|
||||
}
|
||||
|
||||
final PluginCommand command = getCommand("xyz/ineanto/nicko");
|
||||
if (command != null) {
|
||||
command.setExecutor(new NickoCommand());
|
||||
}
|
||||
|
||||
Structure.addGlobalIngredient('#', new SimpleItem(new ItemBuilder(Material.AIR)));
|
||||
Structure.addGlobalIngredient('%', new SimpleItem(new ItemBuilder(Material.BLACK_STAINED_GLASS_PANE).setDisplayName(" ")));
|
||||
|
||||
if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) {
|
||||
getLogger().info("Enabling PlaceHolderAPI support...");
|
||||
new NickoExpansion(this).register();
|
||||
}
|
||||
|
||||
getServer().getPluginManager().registerEvents(new PlayerJoinListener(), this);
|
||||
getServer().getPluginManager().registerEvents(new PlayerQuitListener(), this);
|
||||
metrics = new Metrics(this, 20483);
|
||||
}
|
||||
|
||||
getLogger().info("Nicko has been enabled.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (!getDataStore().getStorage().isError()) {
|
||||
Bukkit.getOnlinePlayers().forEach(player -> dataStore.saveData(player));
|
||||
if (!dataStore.getStorage().getProvider().close()) {
|
||||
getLogger().severe("Failed to close persistence!");
|
||||
} else {
|
||||
getLogger().info("Persistence closed.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!unitTesting) {
|
||||
nameStore.clearStoredNames();
|
||||
metrics.shutdown();
|
||||
}
|
||||
getLogger().info("Nicko (Bukkit) has been disabled.");
|
||||
}
|
||||
|
||||
public static Nicko getInstance() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
public Configuration getNickoConfig() {
|
||||
try {
|
||||
if (configuration == null) {
|
||||
configuration = configurationManager.load();
|
||||
getLogger().info("Configuration file loaded.");
|
||||
}
|
||||
return configuration;
|
||||
} catch (IOException e) {
|
||||
getLogger().severe("Failed to load the configuration file!");
|
||||
getLogger().severe("It may be have been generated with an older version of Nicko.");
|
||||
getLogger().severe("Delete the configuration and restart the server please :)");
|
||||
getLogger().severe("(" + e.getMessage() + ")");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public RandomNameFetcher getNameFetcher() {
|
||||
return nameFetcher;
|
||||
}
|
||||
|
||||
public PlayerDataStore getDataStore() {
|
||||
return dataStore;
|
||||
}
|
||||
|
||||
public ConfigurationManager getConfigurationManager() {
|
||||
return configurationManager;
|
||||
}
|
||||
|
||||
public PlayerNameStore getNameStore() {
|
||||
return nameStore;
|
||||
}
|
||||
|
||||
public MojangAPI getMojangAPI() {
|
||||
return mojangAPI;
|
||||
}
|
||||
|
||||
public CustomLanguage getCustomLocale() {
|
||||
return customLanguage;
|
||||
}
|
||||
|
||||
public MappingManager getMappingManager() {
|
||||
return mappingManager;
|
||||
}
|
||||
}
|
146
src/main/java/xyz/ineanto/nicko/anvil/AnvilManager.java
Normal file
146
src/main/java/xyz/ineanto/nicko/anvil/AnvilManager.java
Normal file
|
@ -0,0 +1,146 @@
|
|||
package xyz.ineanto.nicko.anvil;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.wesjd.anvilgui.AnvilGUI;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.appearance.ActionResult;
|
||||
import xyz.ineanto.nicko.appearance.AppearanceManager;
|
||||
import xyz.ineanto.nicko.event.custom.PlayerDisguiseEvent;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.ineanto.nicko.mojang.MojangUtils;
|
||||
import xyz.ineanto.nicko.profile.NickoProfile;
|
||||
import xyz.ineanto.nicko.storage.PlayerDataStore;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class AnvilManager {
|
||||
private final Player player;
|
||||
private final AppearanceManager appearanceManager;
|
||||
private final PlayerDataStore dataStore = Nicko.getInstance().getDataStore();
|
||||
private final NickoProfile profile;
|
||||
private final PlayerLanguage playerLanguage;
|
||||
|
||||
public AnvilManager(Player player) {
|
||||
this.player = player;
|
||||
this.playerLanguage = new PlayerLanguage(player);
|
||||
|
||||
final Optional<NickoProfile> optionalProfile = dataStore.getData(player.getUniqueId());
|
||||
this.profile = optionalProfile.orElse(NickoProfile.EMPTY_PROFILE.clone());
|
||||
this.appearanceManager = new AppearanceManager(player);
|
||||
}
|
||||
|
||||
public void openNameThenSkinAnvil() {
|
||||
getNameThenSkinAnvil().open(player);
|
||||
}
|
||||
|
||||
public void openSkinAnvil() {
|
||||
getSkinAnvil().open(player);
|
||||
}
|
||||
|
||||
public void openNameAnvil() {
|
||||
getNameAnvil().open(player);
|
||||
}
|
||||
|
||||
private AnvilGUI.Builder getNameThenSkinAnvil() {
|
||||
return new AnvilGUI.Builder()
|
||||
.plugin(Nicko.getInstance())
|
||||
.itemLeft(getLeftItem(false))
|
||||
.interactableSlots(AnvilGUI.Slot.OUTPUT)
|
||||
.onClick((slot, snapshot) -> {
|
||||
if (slot == AnvilGUI.Slot.OUTPUT) {
|
||||
if (MojangUtils.isUsernameInvalid(snapshot.getText())) {
|
||||
return Collections.singletonList(AnvilGUI.ResponseAction.replaceInputText("Invalid username!"));
|
||||
} else {
|
||||
profile.setName(snapshot.getText());
|
||||
openSkinAnvil();
|
||||
return Collections.singletonList(AnvilGUI.ResponseAction.close());
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
})
|
||||
.text("New name...");
|
||||
}
|
||||
|
||||
private AnvilGUI.Builder getNameAnvil() {
|
||||
return new AnvilGUI.Builder()
|
||||
.plugin(Nicko.getInstance())
|
||||
.itemLeft(getLeftItem(false))
|
||||
.interactableSlots(AnvilGUI.Slot.OUTPUT)
|
||||
.onClick((slot, snapshot) -> {
|
||||
if (slot == AnvilGUI.Slot.OUTPUT) {
|
||||
if (MojangUtils.isUsernameInvalid(snapshot.getText())) {
|
||||
return Collections.singletonList(AnvilGUI.ResponseAction.replaceInputText("Invalid username!"));
|
||||
} else {
|
||||
profile.setName(snapshot.getText());
|
||||
dataStore.updateCache(player.getUniqueId(), profile);
|
||||
return sendResultAndClose(false);
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
})
|
||||
.text("New name...");
|
||||
}
|
||||
|
||||
private AnvilGUI.Builder getSkinAnvil() {
|
||||
return new AnvilGUI.Builder()
|
||||
.plugin(Nicko.getInstance())
|
||||
.itemLeft(getLeftItem(true))
|
||||
.interactableSlots(AnvilGUI.Slot.OUTPUT)
|
||||
.onClick((slot, snapshot) -> {
|
||||
if (slot == AnvilGUI.Slot.OUTPUT) {
|
||||
if (MojangUtils.isUsernameInvalid(snapshot.getText())) {
|
||||
return Collections.singletonList(AnvilGUI.ResponseAction.replaceInputText("Invalid username!"));
|
||||
} else {
|
||||
profile.setSkin(snapshot.getText());
|
||||
dataStore.updateCache(player.getUniqueId(), profile);
|
||||
return sendResultAndClose(true);
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
})
|
||||
.text("New skin...");
|
||||
}
|
||||
|
||||
private List<AnvilGUI.ResponseAction> sendResultAndClose(boolean skinChange) {
|
||||
final PlayerDisguiseEvent event = new PlayerDisguiseEvent(player, profile.getSkin(), player.getName());
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) { return Collections.singletonList(AnvilGUI.ResponseAction.close()); }
|
||||
|
||||
final ActionResult actionResult = appearanceManager.updatePlayer(skinChange, false);
|
||||
if (!actionResult.isError()) {
|
||||
player.sendMessage(playerLanguage.translate(LanguageKey.Event.Appearance.Set.OK, true));
|
||||
} else {
|
||||
player.sendMessage(
|
||||
playerLanguage.translate(
|
||||
LanguageKey.Event.Appearance.Set.ERROR,
|
||||
true,
|
||||
playerLanguage.translate(actionResult.getErrorKey(), false)
|
||||
));
|
||||
}
|
||||
return Collections.singletonList(AnvilGUI.ResponseAction.close());
|
||||
}
|
||||
|
||||
private ItemStack getLeftItem(boolean skin) {
|
||||
final ItemStack item = new ItemStack(Material.PAPER);
|
||||
final ItemMeta meta = item.getItemMeta();
|
||||
|
||||
if (meta != null) {
|
||||
if (skin) {
|
||||
meta.displayName(Component.text(playerLanguage.translate(LanguageKey.GUI.NEW_SKIN, false)));
|
||||
} else {
|
||||
meta.displayName(Component.text(playerLanguage.translate(LanguageKey.GUI.NEW_NAME, false)));
|
||||
}
|
||||
}
|
||||
|
||||
item.setItemMeta(meta);
|
||||
return item;
|
||||
}
|
||||
}
|
35
src/main/java/xyz/ineanto/nicko/appearance/ActionResult.java
Normal file
35
src/main/java/xyz/ineanto/nicko/appearance/ActionResult.java
Normal file
|
@ -0,0 +1,35 @@
|
|||
package xyz.ineanto.nicko.appearance;
|
||||
|
||||
public class ActionResult {
|
||||
private final String errorKey;
|
||||
private boolean error = false;
|
||||
|
||||
public static ActionResult ok() {
|
||||
return new ActionResult();
|
||||
}
|
||||
|
||||
public static ActionResult error() {
|
||||
return new ActionResult(null);
|
||||
}
|
||||
|
||||
public static ActionResult error(String errorMessage) {
|
||||
return new ActionResult(errorMessage);
|
||||
}
|
||||
|
||||
private ActionResult() {
|
||||
this.errorKey = null;
|
||||
}
|
||||
|
||||
private ActionResult(String errorMessage) {
|
||||
this.errorKey = errorMessage;
|
||||
this.error = true;
|
||||
}
|
||||
|
||||
public boolean isError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public String getErrorKey() {
|
||||
return errorKey;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,204 @@
|
|||
package xyz.ineanto.nicko.appearance;
|
||||
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.wrappers.*;
|
||||
import com.google.common.collect.Multimap;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.ineanto.nicko.mapping.Mapping;
|
||||
import xyz.ineanto.nicko.mojang.MojangAPI;
|
||||
import xyz.ineanto.nicko.mojang.MojangSkin;
|
||||
import xyz.ineanto.nicko.profile.NickoProfile;
|
||||
import xyz.ineanto.nicko.storage.PlayerDataStore;
|
||||
import xyz.ineanto.nicko.storage.name.PlayerNameStore;
|
||||
import xyz.ineanto.nicko.wrapper.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class AppearanceManager {
|
||||
private final Nicko instance = Nicko.getInstance();
|
||||
private final PlayerDataStore dataStore = instance.getDataStore();
|
||||
private final PlayerNameStore nameStore = instance.getNameStore();
|
||||
|
||||
private final Player player;
|
||||
|
||||
public AppearanceManager(Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public ActionResult reset() {
|
||||
final NickoProfile profile = getNickoProfile();
|
||||
final String defaultName = nameStore.getStoredName(player);
|
||||
profile.setName(defaultName);
|
||||
profile.setSkin(defaultName);
|
||||
dataStore.getCache().cache(player.getUniqueId(), profile);
|
||||
|
||||
final ActionResult result = updatePlayer(true, true);
|
||||
if (!result.isError()) {
|
||||
profile.setName(null);
|
||||
profile.setSkin(null);
|
||||
dataStore.getCache().cache(player.getUniqueId(), profile);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public ActionResult updatePlayer(boolean skinChange, boolean reset) {
|
||||
final NickoProfile profile = getNickoProfile();
|
||||
final String displayName = profile.getName() == null ? player.getName() : profile.getName();
|
||||
final WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(player).withName(displayName);
|
||||
final ActionResult result = updateGameProfileSkin(gameProfile, skinChange, reset);
|
||||
if (!result.isError()) {
|
||||
updateMetadata();
|
||||
updateTabList(gameProfile, displayName);
|
||||
respawnPlayerViaMappings();
|
||||
respawnEntityForOthers();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public ActionResult updateForOthers(boolean skinChange, boolean reset) {
|
||||
final NickoProfile profile = getNickoProfile();
|
||||
final String displayName = profile.getName() == null ? player.getName() : profile.getName();
|
||||
final WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(player).withName(displayName);
|
||||
final ActionResult result = updateGameProfileSkin(gameProfile, skinChange, reset);
|
||||
if (!result.isError()) {
|
||||
updateMetadata();
|
||||
updateTabList(gameProfile, displayName);
|
||||
respawnEntityForOthers();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private NickoProfile getNickoProfile() {
|
||||
final Optional<NickoProfile> optionalProfile = dataStore.getData(player.getUniqueId());
|
||||
return optionalProfile.orElse(NickoProfile.EMPTY_PROFILE.clone());
|
||||
}
|
||||
|
||||
public void respawnEntityForOthers() {
|
||||
final NickoProfile nickoProfile = getNickoProfile();
|
||||
if (!nickoProfile.hasData()) return;
|
||||
|
||||
final WrapperPlayServerEntityDestroy destroy = new WrapperPlayServerEntityDestroy();
|
||||
final WrapperPlayServerSpawnEntity spawn = new WrapperPlayServerSpawnEntity();
|
||||
destroy.setEntityIds(IntList.of(player.getEntityId()));
|
||||
spawn.setEntityId(player.getEntityId());
|
||||
spawn.setLocation(player.getLocation());
|
||||
spawn.setPlayerId(player.getUniqueId());
|
||||
Bukkit.getOnlinePlayers().stream().filter(receiver -> receiver.getUniqueId() != player.getUniqueId()).forEach(receiver -> {
|
||||
destroy.sendPacket(receiver);
|
||||
spawn.sendPacket(receiver);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private ActionResult updateGameProfileSkin(WrappedGameProfile gameProfile, boolean skinChange, boolean reset) {
|
||||
final NickoProfile profile = getNickoProfile();
|
||||
|
||||
if (skinChange) {
|
||||
Optional<MojangSkin> skin;
|
||||
try {
|
||||
final MojangAPI mojangAPI = Nicko.getInstance().getMojangAPI();
|
||||
final Optional<String> uuid = mojangAPI.getUUID(profile.getSkin());
|
||||
if (uuid.isPresent()) {
|
||||
skin = reset ? mojangAPI.getSkinWithoutCaching(uuid.get()) : mojangAPI.getSkin(uuid.get());
|
||||
if (skin.isPresent()) {
|
||||
final MojangSkin skinResult = skin.get();
|
||||
final Multimap<String, WrappedSignedProperty> properties = gameProfile.getProperties();
|
||||
properties.get("textures").clear();
|
||||
properties.put("textures", new WrappedSignedProperty("textures", skinResult.value(), skinResult.signature()));
|
||||
} else {
|
||||
reset();
|
||||
return ActionResult.error(LanguageKey.Error.MOJANG_SKIN);
|
||||
}
|
||||
} else {
|
||||
reset();
|
||||
return ActionResult.error(LanguageKey.Error.MOJANG_NAME);
|
||||
}
|
||||
return ActionResult.ok();
|
||||
} catch (ExecutionException e) {
|
||||
return ActionResult.error(LanguageKey.Error.CACHE);
|
||||
} catch (IOException e) {
|
||||
reset();
|
||||
return ActionResult.error(LanguageKey.Error.MOJANG_NAME);
|
||||
} catch (InterruptedException e) {
|
||||
return ActionResult.error("Unknown error");
|
||||
}
|
||||
}
|
||||
return ActionResult.ok();
|
||||
}
|
||||
|
||||
private void updateMetadata() {
|
||||
final WrappedDataWatcher entityWatcher = WrappedDataWatcher.getEntityWatcher(player);
|
||||
entityWatcher.setObject(17, (byte) 0x7f, true);
|
||||
}
|
||||
|
||||
private void respawnPlayerViaMappings() {
|
||||
final Optional<Mapping> mapping = instance.getMappingManager().getMappingForServer();
|
||||
if (mapping.isEmpty()) return;
|
||||
mapping.get().respawn(player);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private void respawnPlayerViaProtocolLib() {
|
||||
final World world = player.getWorld();
|
||||
final boolean wasFlying = player.isFlying();
|
||||
final boolean wasAllowedToFly = player.getAllowFlight();
|
||||
final int foodLevel = player.getFoodLevel();
|
||||
final WrapperPlayServerRespawn respawn = new WrapperPlayServerRespawn();
|
||||
//respawn.setDimension(world);
|
||||
respawn.setSeed(world.getSeed());
|
||||
respawn.setGameMode(player.getGameMode());
|
||||
respawn.setPreviousGameMode(player.getGameMode());
|
||||
respawn.setCopyMetadata(true);
|
||||
//respawn.sendPacket(player);
|
||||
player.teleport(player.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
|
||||
player.setAllowFlight(wasAllowedToFly);
|
||||
player.setFlying(wasFlying);
|
||||
player.updateInventory();
|
||||
player.sendHealthUpdate();
|
||||
player.setFoodLevel(foodLevel);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void updateTabList(WrappedGameProfile gameProfile, String displayName) {
|
||||
final WrapperPlayerServerPlayerInfo add = new WrapperPlayerServerPlayerInfo();
|
||||
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
|
||||
final WrapperPlayerServerPlayerInfoRemove remove = new WrapperPlayerServerPlayerInfoRemove();
|
||||
final EnumSet<EnumWrappers.PlayerInfoAction> actions = EnumSet.of(
|
||||
EnumWrappers.PlayerInfoAction.ADD_PLAYER,
|
||||
EnumWrappers.PlayerInfoAction.INITIALIZE_CHAT,
|
||||
EnumWrappers.PlayerInfoAction.UPDATE_LISTED,
|
||||
EnumWrappers.PlayerInfoAction.UPDATE_DISPLAY_NAME,
|
||||
EnumWrappers.PlayerInfoAction.UPDATE_GAME_MODE,
|
||||
EnumWrappers.PlayerInfoAction.UPDATE_LATENCY);
|
||||
remove.setUUIDs(List.of(player.getUniqueId()));
|
||||
remove.broadcastPacket();
|
||||
add.setActions(actions);
|
||||
} else {
|
||||
final WrapperPlayerServerPlayerInfo remove = new WrapperPlayerServerPlayerInfo();
|
||||
remove.setActions(EnumSet.of(EnumWrappers.PlayerInfoAction.REMOVE_PLAYER));
|
||||
add.setActions(EnumSet.of(EnumWrappers.PlayerInfoAction.ADD_PLAYER));
|
||||
remove.broadcastPacket();
|
||||
}
|
||||
|
||||
add.setData(List.of(new PlayerInfoData(
|
||||
player.getUniqueId(),
|
||||
player.getPing(),
|
||||
true,
|
||||
EnumWrappers.NativeGameMode.fromBukkit(player.getGameMode()),
|
||||
gameProfile,
|
||||
WrappedChatComponent.fromText(displayName),
|
||||
WrappedRemoteChatSessionData.fromPlayer(player)
|
||||
)));
|
||||
add.broadcastPacket();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package xyz.ineanto.nicko.appearance.random;
|
||||
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class RandomNameFetcher {
|
||||
private final Nicko instance;
|
||||
|
||||
public RandomNameFetcher(Nicko instance) {
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
public String getRandomUsername() {
|
||||
final InputStream resource = instance.getResource("names.txt");
|
||||
final List<List<String>> records = new ArrayList<>();
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource))) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
final String[] values = line.split("\n");
|
||||
records.add(Arrays.asList(values));
|
||||
}
|
||||
return records.get(new Random().nextInt(records.size() - 1)).getFirst();
|
||||
} catch (IOException e) {
|
||||
instance.getLogger().severe("Unable to fetch random names.");
|
||||
return "Ineanto";
|
||||
}
|
||||
}
|
||||
}
|
28
src/main/java/xyz/ineanto/nicko/command/NickoCommand.java
Normal file
28
src/main/java/xyz/ineanto/nicko/command/NickoCommand.java
Normal file
|
@ -0,0 +1,28 @@
|
|||
package xyz.ineanto.nicko.command;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.ineanto.nicko.gui.HomeGUI;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
|
||||
public class NickoCommand implements CommandExecutor {
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
||||
if (sender instanceof Player player) {
|
||||
if (player.isOp() || player.hasPermission("nicko.use") || player.hasPermission("nicko.*")) {
|
||||
new HomeGUI(player).open();
|
||||
} else {
|
||||
final PlayerLanguage playerLanguage = new PlayerLanguage(player);
|
||||
player.sendMessage(playerLanguage.translate(LanguageKey.Error.PERMISSION, true));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
sender.sendMessage("This plugin can only be used in-game. Sorry!");
|
||||
return false;
|
||||
}
|
||||
}
|
54
src/main/java/xyz/ineanto/nicko/config/Configuration.java
Normal file
54
src/main/java/xyz/ineanto/nicko/config/Configuration.java
Normal file
|
@ -0,0 +1,54 @@
|
|||
package xyz.ineanto.nicko.config;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import xyz.ineanto.nicko.version.Version;
|
||||
|
||||
public class Configuration {
|
||||
public static final Version VERSION = new Version(1, 0, 9);
|
||||
public static final Configuration DEFAULT = new Configuration(VERSION.toString(),
|
||||
DefaultDataSources.SQL_EMPTY,
|
||||
DefaultDataSources.REDIS_EMPTY,
|
||||
false);
|
||||
|
||||
private final transient Version versionObject;
|
||||
|
||||
@JsonProperty("version")
|
||||
private final String version;
|
||||
@JsonProperty("sql")
|
||||
private final SQLDataSourceConfiguration sqlConfiguration;
|
||||
@JsonProperty("redis")
|
||||
private final DataSourceConfiguration redisConfiguration;
|
||||
@JsonProperty("customLocale")
|
||||
private final Boolean customLocale;
|
||||
|
||||
public Configuration(@JsonProperty("version") String version,
|
||||
@JsonProperty("sql") SQLDataSourceConfiguration sqlConfiguration,
|
||||
@JsonProperty("redis") DataSourceConfiguration redisConfiguration,
|
||||
@JsonProperty("customLocale") Boolean customLocale) {
|
||||
this.version = version;
|
||||
this.versionObject = Version.fromString(version);
|
||||
this.sqlConfiguration = sqlConfiguration;
|
||||
this.redisConfiguration = redisConfiguration;
|
||||
this.customLocale = customLocale;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public Version getVersionObject() {
|
||||
return versionObject;
|
||||
}
|
||||
|
||||
public SQLDataSourceConfiguration getSqlConfiguration() {
|
||||
return sqlConfiguration;
|
||||
}
|
||||
|
||||
public DataSourceConfiguration getRedisConfiguration() {
|
||||
return redisConfiguration;
|
||||
}
|
||||
|
||||
public Boolean isCustomLocale() {
|
||||
return customLocale;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package xyz.ineanto.nicko.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
public class ConfigurationManager {
|
||||
private final ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
|
||||
private final File file;
|
||||
private final File backupFile;
|
||||
|
||||
public ConfigurationManager(File directory) {
|
||||
final String date = Instant.now()
|
||||
.atZone(ZoneId.systemDefault())
|
||||
.format(DateTimeFormatter.ofPattern("dd-MM-yyyy"));
|
||||
this.file = new File(directory, "config.yml");
|
||||
this.backupFile = new File(directory, "config.old-" + date + ".yml");
|
||||
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
mapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
|
||||
}
|
||||
|
||||
public void save(Configuration configuration) throws IOException {
|
||||
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
|
||||
mapper.writeValue(writer, configuration);
|
||||
writer.flush();
|
||||
}
|
||||
}
|
||||
|
||||
public void saveDefaultConfig() {
|
||||
if (!file.exists()) {
|
||||
try {
|
||||
final InputStream input = getClass().getResourceAsStream("/config.yml");
|
||||
if (input != null) {
|
||||
Files.createDirectories(file.getParentFile().toPath());
|
||||
Files.createFile(file.toPath());
|
||||
Files.copy(input, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Configuration load() throws IOException {
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
|
||||
return mapper.readValue(reader, Configuration.class);
|
||||
}
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public File getBackupFile() {
|
||||
return backupFile;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package xyz.ineanto.nicko.config;
|
||||
|
||||
public class DataSourceConfiguration {
|
||||
private final boolean enabled;
|
||||
private final String address;
|
||||
private final Integer port;
|
||||
private final String username;
|
||||
private final String password;
|
||||
|
||||
public DataSourceConfiguration(boolean enabled, String address, Integer port, String username, String password) {
|
||||
this.enabled = enabled;
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public DataSourceConfiguration() { this(false, "", 0, "", ""); }
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public Integer getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DataSourceConfiguration{" +
|
||||
"enabled=" + enabled +
|
||||
", address='" + address + '\'' +
|
||||
", port=" + port +
|
||||
", username='" + username + '\'' +
|
||||
", password='" + password + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package xyz.ineanto.nicko.config;
|
||||
|
||||
public class DefaultDataSources {
|
||||
public static final DataSourceConfiguration REDIS_EMPTY = new DataSourceConfiguration(false, "127.0.0.1", 6379, "", "");
|
||||
public static final SQLDataSourceConfiguration MARIADB_EMPTY = new SQLDataSourceConfiguration(false, "127.0.0.1", 3306, "root", "", true);
|
||||
public static final SQLDataSourceConfiguration SQL_EMPTY = new SQLDataSourceConfiguration(false, "127.0.0.1", 3306, "root", "", false);
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package xyz.ineanto.nicko.config;
|
||||
|
||||
public class SQLDataSourceConfiguration extends DataSourceConfiguration {
|
||||
private final boolean mariadb;
|
||||
|
||||
public SQLDataSourceConfiguration() { this(false, "", 0, "", "", true); }
|
||||
|
||||
public SQLDataSourceConfiguration(boolean enabled, String address, Integer port, String username, String password, boolean mariadb) {
|
||||
super(enabled, address, port, username, password);
|
||||
this.mariadb = mariadb;
|
||||
}
|
||||
|
||||
public boolean isMariadb() {
|
||||
return mariadb;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package xyz.ineanto.nicko.event;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.appearance.ActionResult;
|
||||
import xyz.ineanto.nicko.appearance.AppearanceManager;
|
||||
import xyz.ineanto.nicko.gui.PlayerCheckGUI;
|
||||
import xyz.ineanto.nicko.gui.PlayerCheckGUIData;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.ineanto.nicko.profile.NickoProfile;
|
||||
import xyz.ineanto.nicko.storage.PlayerDataStore;
|
||||
import xyz.ineanto.nicko.storage.name.PlayerNameStore;
|
||||
import xyz.xenondevs.invui.window.Window;
|
||||
import xyz.xenondevs.invui.window.WindowManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class PlayerJoinListener implements Listener {
|
||||
private final Logger logger = Logger.getLogger("PlayerJoinListener");
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
final Player player = event.getPlayer();
|
||||
final Nicko instance = Nicko.getInstance();
|
||||
final PlayerLanguage playerLanguage = new PlayerLanguage(player);
|
||||
final PlayerNameStore nameStore = instance.getNameStore();
|
||||
final PlayerDataStore dataStore = instance.getDataStore();
|
||||
nameStore.storeName(player);
|
||||
|
||||
final Optional<NickoProfile> optionalProfile = dataStore.getData(player.getUniqueId());
|
||||
optionalProfile.ifPresentOrElse(profile -> {
|
||||
// Random Skin on connection feature
|
||||
if (profile.isRandomSkin()) {
|
||||
final String name = instance.getNameFetcher().getRandomUsername();
|
||||
final String skin = instance.getNameFetcher().getRandomUsername();
|
||||
profile.setName(name);
|
||||
profile.setSkin(skin);
|
||||
dataStore.updateCache(player.getUniqueId(), profile);
|
||||
}
|
||||
|
||||
if (profile.hasData()) {
|
||||
final AppearanceManager appearanceManager = new AppearanceManager(player);
|
||||
final boolean needsASkinChange = profile.getSkin() != null && !profile.getSkin().equals(player.getName());
|
||||
final ActionResult actionResult = appearanceManager.updatePlayer(needsASkinChange, false);
|
||||
if (!actionResult.isError()) {
|
||||
player.sendMessage(playerLanguage.translateWithWhoosh(LanguageKey.Event.Appearance.Restore.OK));
|
||||
} else {
|
||||
player.sendMessage(
|
||||
playerLanguage.translateWithOops(LanguageKey.Event.Appearance.Restore.ERROR,
|
||||
playerLanguage.translate(actionResult.getErrorKey(), false)
|
||||
));
|
||||
}
|
||||
}
|
||||
}, () -> instance.getLogger().warning("Failed to load data for " + player.getName()));
|
||||
|
||||
for (Player online : Bukkit.getOnlinePlayers().stream().filter(op -> op.getUniqueId() != player.getUniqueId()).toList()) {
|
||||
final Optional<NickoProfile> optionalOnlinePlayerProfile = dataStore.getData(online.getUniqueId());
|
||||
|
||||
optionalOnlinePlayerProfile.ifPresent(profile -> {
|
||||
final AppearanceManager appearanceManager = new AppearanceManager(online);
|
||||
final boolean needsASkinChange = profile.getSkin() != null && !profile.getSkin().equals(online.getName());
|
||||
final ActionResult actionResult = appearanceManager.updateForOthers(needsASkinChange, false);
|
||||
if (actionResult.isError()) {
|
||||
logger.warning("Something wrong happened while updating players to joining player (" + actionResult.getErrorKey() + ")");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked") final ArrayList<UUID> viewers = (ArrayList<UUID>) PlayerCheckGUIData.VIEWERS.clone();
|
||||
viewers.forEach(uuid -> {
|
||||
final Player windowWatcher = Bukkit.getPlayer(uuid);
|
||||
final Window openWindow = WindowManager.getInstance().getOpenWindow(windowWatcher);
|
||||
if (openWindow != null) {
|
||||
final PlayerCheckGUI gui = new PlayerCheckGUI(windowWatcher, Bukkit.getOnlinePlayers());
|
||||
openWindow.close();
|
||||
gui.open();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package xyz.ineanto.nicko.event;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.appearance.ActionResult;
|
||||
import xyz.ineanto.nicko.gui.PlayerCheckGUI;
|
||||
import xyz.ineanto.nicko.gui.PlayerCheckGUIData;
|
||||
import xyz.xenondevs.invui.window.Window;
|
||||
import xyz.xenondevs.invui.window.WindowManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PlayerQuitListener implements Listener {
|
||||
@EventHandler
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
final Player player = event.getPlayer();
|
||||
final ActionResult result = Nicko.getInstance().getDataStore().saveData(player);
|
||||
if (result.isError()) {
|
||||
Nicko.getInstance().getLogger().warning("Failed to save data for " + player.getName());
|
||||
}
|
||||
|
||||
// This is a dirty way to do it but could be worse tbh
|
||||
@SuppressWarnings("unchecked") final ArrayList<UUID> viewers = (ArrayList<UUID>) PlayerCheckGUIData.VIEWERS.clone();
|
||||
viewers.forEach(uuid -> {
|
||||
final Player windowWatcher = Bukkit.getPlayer(uuid);
|
||||
final Window openWindow = WindowManager.getInstance().getOpenWindow(windowWatcher);
|
||||
if (openWindow != null) {
|
||||
final List<? extends Player> playersWithoutOffline = Bukkit.getOnlinePlayers()
|
||||
.stream()
|
||||
.filter(online -> online.getUniqueId() != player.getUniqueId()).collect(Collectors.toList());
|
||||
final PlayerCheckGUI gui = new PlayerCheckGUI(windowWatcher, playersWithoutOffline);
|
||||
openWindow.close();
|
||||
gui.open();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package xyz.ineanto.nicko.event.custom;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class PlayerDisguiseEvent extends Event implements Cancellable {
|
||||
private static final HandlerList HANDLERS_LIST = new HandlerList();
|
||||
private boolean isCancelled;
|
||||
private final Player player;
|
||||
private final String skin, name;
|
||||
|
||||
public PlayerDisguiseEvent(Player player, String skin, String name) {
|
||||
this.player = player;
|
||||
this.skin = skin;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return isCancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean isCancelled) {
|
||||
this.isCancelled = isCancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HandlerList getHandlers() {
|
||||
return HANDLERS_LIST;
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public String getSkin() {
|
||||
return skin;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
52
src/main/java/xyz/ineanto/nicko/gui/AdminGUI.java
Normal file
52
src/main/java/xyz/ineanto/nicko/gui/AdminGUI.java
Normal file
|
@ -0,0 +1,52 @@
|
|||
package xyz.ineanto.nicko.gui;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import xyz.ineanto.nicko.gui.items.ItemDefaults;
|
||||
import xyz.ineanto.nicko.gui.items.admin.ManageCacheItem;
|
||||
import xyz.ineanto.nicko.gui.items.admin.ManagePlayerItem;
|
||||
import xyz.ineanto.nicko.gui.items.common.GoBackItem;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.xenondevs.invui.gui.Gui;
|
||||
import xyz.xenondevs.invui.item.impl.SimpleItem;
|
||||
import xyz.xenondevs.invui.window.Window;
|
||||
|
||||
public class AdminGUI {
|
||||
private final Player player;
|
||||
private final Gui gui;
|
||||
private final String title;
|
||||
|
||||
public AdminGUI(Player player) {
|
||||
final PlayerLanguage playerLanguage = new PlayerLanguage(player);
|
||||
this.title = playerLanguage.translate(LanguageKey.GUI.Titles.ADMIN, false);
|
||||
|
||||
final HomeGUI parent = new HomeGUI(player);
|
||||
final GoBackItem backItem = new GoBackItem(player);
|
||||
final ManagePlayerItem managePlayerItem = new ManagePlayerItem(playerLanguage, player);
|
||||
|
||||
this.gui = Gui.normal()
|
||||
.setStructure(
|
||||
"# # # # # # # # #",
|
||||
"# # # S C U # # #",
|
||||
"B # # # # # # # #"
|
||||
)
|
||||
.addIngredient('S', new ManageCacheItem(playerLanguage))
|
||||
.addIngredient('C', managePlayerItem.get())
|
||||
.addIngredient('U', new SimpleItem(ItemDefaults.getUnavailableItem(playerLanguage)))
|
||||
.addIngredient('B', backItem.get(parent.getGUI(), parent.getTitle()))
|
||||
.build();
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public Gui getGUI() {
|
||||
return gui;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void open() {
|
||||
Window.single().setGui(gui).setTitle(title).open(player);
|
||||
}
|
||||
}
|
54
src/main/java/xyz/ineanto/nicko/gui/CacheManagementGUI.java
Normal file
54
src/main/java/xyz/ineanto/nicko/gui/CacheManagementGUI.java
Normal file
|
@ -0,0 +1,54 @@
|
|||
package xyz.ineanto.nicko.gui;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import xyz.ineanto.nicko.gui.items.admin.cache.CacheStatisticsItem;
|
||||
import xyz.ineanto.nicko.gui.items.admin.cache.InvalidateCacheItem;
|
||||
import xyz.ineanto.nicko.gui.items.admin.cache.InvalidateSkinItem;
|
||||
import xyz.ineanto.nicko.gui.items.common.GoBackItem;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.xenondevs.invui.gui.Gui;
|
||||
import xyz.xenondevs.invui.window.Window;
|
||||
|
||||
public class CacheManagementGUI {
|
||||
private final Player player;
|
||||
private final Gui gui;
|
||||
private final String title;
|
||||
|
||||
public CacheManagementGUI(Player player) {
|
||||
final PlayerLanguage playerLanguage = new PlayerLanguage(player);
|
||||
this.title = playerLanguage.translate(LanguageKey.GUI.Titles.CACHE, false);
|
||||
|
||||
final AdminGUI parent = new AdminGUI(player);
|
||||
final GoBackItem backItem = new GoBackItem(player);
|
||||
|
||||
final CacheStatisticsItem cacheStatisticsItem = new CacheStatisticsItem(player);
|
||||
final InvalidateCacheItem invalidateCacheItem = new InvalidateCacheItem(player);
|
||||
final InvalidateSkinItem invalidateSkinItem = new InvalidateSkinItem(player);
|
||||
|
||||
this.gui = Gui.normal()
|
||||
.setStructure(
|
||||
"# # # # # # # # #",
|
||||
"# # # S C E # # #",
|
||||
"B # # # # # # # #"
|
||||
)
|
||||
.addIngredient('B', backItem.get(parent.getGUI(), parent.getTitle()))
|
||||
.addIngredient('S', cacheStatisticsItem.get())
|
||||
.addIngredient('C', invalidateCacheItem.get())
|
||||
.addIngredient('E', invalidateSkinItem.get())
|
||||
.build();
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public Gui getGUI() {
|
||||
return gui;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void open() {
|
||||
Window.single().setGui(gui).setTitle(title).open(player);
|
||||
}
|
||||
}
|
42
src/main/java/xyz/ineanto/nicko/gui/ChoiceGUI.java
Normal file
42
src/main/java/xyz/ineanto/nicko/gui/ChoiceGUI.java
Normal file
|
@ -0,0 +1,42 @@
|
|||
package xyz.ineanto.nicko.gui;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import xyz.ineanto.nicko.gui.items.common.choice.CancelItem;
|
||||
import xyz.ineanto.nicko.gui.items.common.choice.ChoiceCallback;
|
||||
import xyz.ineanto.nicko.gui.items.common.choice.ConfirmItem;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.xenondevs.invui.gui.Gui;
|
||||
import xyz.xenondevs.invui.item.builder.ItemBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.SimpleItem;
|
||||
import xyz.xenondevs.invui.window.Window;
|
||||
|
||||
public class ChoiceGUI {
|
||||
private final Player player;
|
||||
private final Gui gui;
|
||||
private final String title;
|
||||
|
||||
public ChoiceGUI(Player player, ChoiceCallback callback) {
|
||||
final PlayerLanguage playerLanguage = new PlayerLanguage(player);
|
||||
final ConfirmItem confirmItem = new ConfirmItem(player, callback);
|
||||
final CancelItem cancelItem = new CancelItem(player, callback);
|
||||
|
||||
this.title = playerLanguage.translate(LanguageKey.GUI.Titles.CONFIRM, false);
|
||||
this.gui = Gui.normal()
|
||||
.setStructure(
|
||||
"@ @ @ @ % & & & &",
|
||||
"@ @ @ @ I & & & &",
|
||||
"@ @ @ @ % & & & &"
|
||||
)
|
||||
.addIngredient('@', confirmItem.get())
|
||||
.addIngredient('&', cancelItem.get())
|
||||
.addIngredient('I', new SimpleItem(playerLanguage.translateItem(new ItemBuilder(Material.PAPER), LanguageKey.GUI.Choice.CHOOSE)))
|
||||
.build();
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public void open() {
|
||||
Window.single().setGui(gui).setTitle(title).open(player);
|
||||
}
|
||||
}
|
65
src/main/java/xyz/ineanto/nicko/gui/HomeGUI.java
Normal file
65
src/main/java/xyz/ineanto/nicko/gui/HomeGUI.java
Normal file
|
@ -0,0 +1,65 @@
|
|||
package xyz.ineanto.nicko.gui;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import xyz.ineanto.nicko.gui.items.appearance.ChangeBothItem;
|
||||
import xyz.ineanto.nicko.gui.items.appearance.ChangeNameItem;
|
||||
import xyz.ineanto.nicko.gui.items.appearance.ChangeSkinItem;
|
||||
import xyz.ineanto.nicko.gui.items.home.*;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.xenondevs.invui.gui.Gui;
|
||||
import xyz.xenondevs.invui.window.Window;
|
||||
|
||||
public class HomeGUI {
|
||||
private final Player player;
|
||||
private final Gui gui;
|
||||
private final String title;
|
||||
|
||||
public HomeGUI(Player player) {
|
||||
final String[] dynamicStructure = new String[]{
|
||||
"# # # # D # # # #",
|
||||
"A # # N B S # # #",
|
||||
"E P # # # # # # R"};
|
||||
|
||||
if (!player.isOp() || !player.hasPermission("nicko.admin")) {
|
||||
dynamicStructure[2] = dynamicStructure[2].replace("A", "#");
|
||||
}
|
||||
|
||||
final PlayerLanguage playerLanguage = new PlayerLanguage(player);
|
||||
this.title = playerLanguage.translate(LanguageKey.GUI.Titles.HOME, false);
|
||||
|
||||
final ExitItem exitItem = new ExitItem(player);
|
||||
final ResetItem resetItem = new ResetItem(player);
|
||||
final ChangeNameItem changeNameItem = new ChangeNameItem(player);
|
||||
final ChangeBothItem changeBothItem = new ChangeBothItem(player);
|
||||
final ChangeSkinItem changeSkinItem = new ChangeSkinItem(player);
|
||||
final SettingsAccessItem settingsAccessItem = new SettingsAccessItem(player);
|
||||
final AdminAccessItem adminAccessItem = new AdminAccessItem(player);
|
||||
final RandomSkinItem randomSkinItem = new RandomSkinItem(player);
|
||||
|
||||
this.gui = Gui.normal()
|
||||
.setStructure(dynamicStructure)
|
||||
.addIngredient('E', exitItem.get())
|
||||
.addIngredient('R', resetItem.get())
|
||||
.addIngredient('N', changeNameItem.get())
|
||||
.addIngredient('B', changeBothItem.get())
|
||||
.addIngredient('S', changeSkinItem.get())
|
||||
.addIngredient('P', settingsAccessItem.get())
|
||||
.addIngredient('A', adminAccessItem.get())
|
||||
.addIngredient('D', randomSkinItem.get())
|
||||
.build();
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public Gui getGUI() {
|
||||
return gui;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void open() {
|
||||
Window.single().setGui(gui).setTitle(title).open(player);
|
||||
}
|
||||
}
|
69
src/main/java/xyz/ineanto/nicko/gui/InvalidateSkinGUI.java
Normal file
69
src/main/java/xyz/ineanto/nicko/gui/InvalidateSkinGUI.java
Normal file
|
@ -0,0 +1,69 @@
|
|||
package xyz.ineanto.nicko.gui;
|
||||
|
||||
import xyz.ineanto.nicko.gui.items.common.GoBackItem;
|
||||
import xyz.ineanto.nicko.gui.items.common.ScrollUpItem;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.ineanto.nicko.mojang.MojangSkin;
|
||||
import xyz.xenondevs.invui.gui.Gui;
|
||||
import xyz.xenondevs.invui.gui.ScrollGui;
|
||||
import xyz.xenondevs.invui.gui.structure.Markers;
|
||||
import xyz.xenondevs.invui.item.Item;
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.gui.items.admin.cache.CacheEntryItem;
|
||||
import xyz.ineanto.nicko.gui.items.common.ScrollDownItem;
|
||||
import org.bukkit.entity.Player;
|
||||
import xyz.xenondevs.invui.window.Window;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class InvalidateSkinGUI {
|
||||
private final Player player;
|
||||
private final Gui gui;
|
||||
private final String title;
|
||||
|
||||
public InvalidateSkinGUI(Player player) {
|
||||
final PlayerLanguage playerLanguage = new PlayerLanguage(player);
|
||||
this.title = playerLanguage.translate(LanguageKey.GUI.Titles.INVALIDATE_SKIN, false);
|
||||
|
||||
final ConcurrentMap<String, Optional<MojangSkin>> skins = Nicko.getInstance().getMojangAPI().getSkinCache().asMap();
|
||||
final List<String> loadedSkins = skins.entrySet().stream()
|
||||
.filter(entry -> entry.getValue().isPresent())
|
||||
.map(Map.Entry::getKey)
|
||||
.toList();
|
||||
|
||||
final List<Item> items = loadedSkins.stream()
|
||||
.map(uuid -> new CacheEntryItem(playerLanguage, uuid))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
final CacheManagementGUI parent = new CacheManagementGUI(player);
|
||||
final ScrollUpItem scrollUpItem = new ScrollUpItem(playerLanguage);
|
||||
final ScrollDownItem scrollDownItem = new ScrollDownItem(playerLanguage);
|
||||
final GoBackItem backItem = new GoBackItem(player);
|
||||
|
||||
gui = ScrollGui.items(guiItemBuilder -> {
|
||||
guiItemBuilder.setStructure(
|
||||
"x x x x x x x x U",
|
||||
"x x x x x x x x #",
|
||||
"x x x x x x x x #",
|
||||
"x x x x x x x x #",
|
||||
"x x x x x x x x D",
|
||||
"B % % % % % % % %");
|
||||
guiItemBuilder.addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL);
|
||||
guiItemBuilder.addIngredient('U', scrollUpItem);
|
||||
guiItemBuilder.addIngredient('D', scrollDownItem);
|
||||
guiItemBuilder.addIngredient('B', backItem.get(parent.getGUI(), parent.getTitle()));
|
||||
guiItemBuilder.setContent(items);
|
||||
});
|
||||
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public void open() {
|
||||
Window.single().setGui(gui).setTitle(title).open(player);
|
||||
}
|
||||
}
|
68
src/main/java/xyz/ineanto/nicko/gui/PlayerCheckGUI.java
Normal file
68
src/main/java/xyz/ineanto/nicko/gui/PlayerCheckGUI.java
Normal file
|
@ -0,0 +1,68 @@
|
|||
package xyz.ineanto.nicko.gui;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import xyz.ineanto.nicko.gui.items.admin.check.PlayerInformationItem;
|
||||
import xyz.ineanto.nicko.gui.items.common.GoBackItem;
|
||||
import xyz.ineanto.nicko.gui.items.common.ScrollDownItem;
|
||||
import xyz.ineanto.nicko.gui.items.common.ScrollUpItem;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.xenondevs.invui.gui.Gui;
|
||||
import xyz.xenondevs.invui.gui.ScrollGui;
|
||||
import xyz.xenondevs.invui.gui.structure.Markers;
|
||||
import xyz.xenondevs.invui.item.Item;
|
||||
import xyz.xenondevs.invui.window.Window;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PlayerCheckGUI {
|
||||
private final Player player;
|
||||
private final Gui gui;
|
||||
private final String title;
|
||||
|
||||
public PlayerCheckGUI(Player player, Collection<? extends Player> players) {
|
||||
final PlayerLanguage playerLanguage = new PlayerLanguage(player);
|
||||
this.title = playerLanguage.translate(LanguageKey.GUI.Titles.CHECK, false);
|
||||
|
||||
final List<Item> items = players.stream()
|
||||
.map(Entity::getUniqueId)
|
||||
.map(Bukkit::getPlayer)
|
||||
.filter(Objects::nonNull)
|
||||
.map(mappedPlayer -> new PlayerInformationItem(playerLanguage, mappedPlayer))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
final AdminGUI parent = new AdminGUI(player);
|
||||
final GoBackItem backItem = new GoBackItem(player);
|
||||
final ScrollUpItem scrollUpItem = new ScrollUpItem(playerLanguage);
|
||||
final ScrollDownItem scrollDownItem = new ScrollDownItem(playerLanguage);
|
||||
|
||||
gui = ScrollGui.items(guiItemBuilder -> {
|
||||
guiItemBuilder.setStructure(
|
||||
"x x x x x x x x U",
|
||||
"x x x x x x x x #",
|
||||
"x x x x x x x x #",
|
||||
"x x x x x x x x #",
|
||||
"x x x x x x x x D",
|
||||
"B % % % % % % % %");
|
||||
guiItemBuilder.addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL);
|
||||
guiItemBuilder.addIngredient('U', scrollUpItem);
|
||||
guiItemBuilder.addIngredient('D', scrollDownItem);
|
||||
guiItemBuilder.addIngredient('B', backItem.get(parent.getGUI(), parent.getTitle()));
|
||||
guiItemBuilder.setContent(items);
|
||||
});
|
||||
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public void open() {
|
||||
final Window.Builder.Normal.Single window = Window.single().setGui(gui).setTitle(title);
|
||||
window.addOpenHandler(() -> PlayerCheckGUIData.VIEWERS.add(player.getUniqueId()));
|
||||
window.addCloseHandler(() -> PlayerCheckGUIData.VIEWERS.remove(player.getUniqueId()));
|
||||
window.open(player);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package xyz.ineanto.nicko.gui;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PlayerCheckGUIData {
|
||||
public static final ArrayList<UUID> VIEWERS = new ArrayList<>();
|
||||
}
|
44
src/main/java/xyz/ineanto/nicko/gui/SettingsGUI.java
Normal file
44
src/main/java/xyz/ineanto/nicko/gui/SettingsGUI.java
Normal file
|
@ -0,0 +1,44 @@
|
|||
package xyz.ineanto.nicko.gui;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import xyz.ineanto.nicko.gui.items.common.GoBackItem;
|
||||
import xyz.ineanto.nicko.gui.items.settings.LanguageCyclingItem;
|
||||
import xyz.ineanto.nicko.gui.items.settings.RandomSkinCyclingItem;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.xenondevs.invui.gui.Gui;
|
||||
import xyz.xenondevs.invui.window.Window;
|
||||
|
||||
public class SettingsGUI {
|
||||
private final Player player;
|
||||
private final Gui gui;
|
||||
private final String title;
|
||||
|
||||
public SettingsGUI(Player player) {
|
||||
final String[] dynamicStructure = new String[]{
|
||||
"# # # # # # # # #",
|
||||
"# # # L # R # # #",
|
||||
"B # # # # # # # #"
|
||||
};
|
||||
|
||||
final PlayerLanguage playerLanguage = new PlayerLanguage(player);
|
||||
this.title = playerLanguage.translate(LanguageKey.GUI.Titles.SETTINGS, false);
|
||||
|
||||
final HomeGUI parent = new HomeGUI(player);
|
||||
final LanguageCyclingItem languageItem = new LanguageCyclingItem(player);
|
||||
final RandomSkinCyclingItem skinItem = new RandomSkinCyclingItem(player);
|
||||
final GoBackItem backItem = new GoBackItem(player);
|
||||
|
||||
this.gui = Gui.normal()
|
||||
.setStructure(dynamicStructure)
|
||||
.addIngredient('B', backItem.get(parent.getGUI(), parent.getTitle()))
|
||||
.addIngredient('L', languageItem.get())
|
||||
.addIngredient('R', skinItem.get())
|
||||
.build();
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public void open() {
|
||||
Window.single().setGui(gui).setTitle(title).open(player);
|
||||
}
|
||||
}
|
22
src/main/java/xyz/ineanto/nicko/gui/items/ItemDefaults.java
Normal file
22
src/main/java/xyz/ineanto/nicko/gui/items/ItemDefaults.java
Normal file
|
@ -0,0 +1,22 @@
|
|||
package xyz.ineanto.nicko.gui.items;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.xenondevs.invui.item.builder.AbstractItemBuilder;
|
||||
import xyz.xenondevs.invui.item.builder.ItemBuilder;
|
||||
import xyz.xenondevs.invui.item.builder.SkullBuilder;
|
||||
|
||||
public class ItemDefaults {
|
||||
public static AbstractItemBuilder<?> getErrorSkullItem(PlayerLanguage playerLanguage, String key, Object... args) {
|
||||
// "Missing Value" (Valve's signature missing texture) Texture Value
|
||||
final SkullBuilder.HeadTexture headTexture = new SkullBuilder.HeadTexture("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjNmZTU5YjJhMWQyYmYzMjcwNDA2OGVmYzg2MGM3NWY5MjEyYzIzMTBiNDNkMDdjNGJiYTRiNGViMjM0ZTY4NCJ9fX0=");
|
||||
final SkullBuilder builder = new SkullBuilder(headTexture);
|
||||
return playerLanguage.translateItem(builder, key, args);
|
||||
}
|
||||
|
||||
public static AbstractItemBuilder<?> getUnavailableItem(PlayerLanguage playerLanguage) {
|
||||
final ItemBuilder builder = new ItemBuilder(Material.RED_TERRACOTTA);
|
||||
return playerLanguage.translateItem(builder, LanguageKey.GUI.UNAVAILABLE);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package xyz.ineanto.nicko.gui.items.admin;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.gui.CacheManagementGUI;
|
||||
import xyz.ineanto.nicko.gui.items.ItemDefaults;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.xenondevs.invui.item.builder.ItemBuilder;
|
||||
import xyz.xenondevs.invui.item.builder.SkullBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.AsyncItem;
|
||||
import xyz.xenondevs.invui.item.impl.SuppliedItem;
|
||||
import xyz.xenondevs.invui.util.MojangApiUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ManageCacheItem extends AsyncItem {
|
||||
public ManageCacheItem(PlayerLanguage playerLanguage) {
|
||||
super(new SuppliedItem(() -> {
|
||||
final ItemBuilder builder = new ItemBuilder(Material.PAINTING);
|
||||
return playerLanguage.translateItem(builder, LanguageKey.GUI.LOADING);
|
||||
}, (click -> true)).getItemProvider(),
|
||||
() -> {
|
||||
try {
|
||||
final SkullBuilder builder = new SkullBuilder("Notch");
|
||||
return playerLanguage.translateItem(builder, LanguageKey.GUI.Admin.MANAGE_CACHE);
|
||||
} catch (MojangApiUtils.MojangApiException | IOException e) {
|
||||
Nicko.getInstance().getLogger().warning("Unable to get Head texture for Notch! (GUI/ManageCache)");
|
||||
return ItemDefaults.getErrorSkullItem(playerLanguage, LanguageKey.GUI.Admin.MANAGE_CACHE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleClick(@NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) {
|
||||
if (clickType.isLeftClick() || clickType.isRightClick()) {
|
||||
event.getView().close();
|
||||
new CacheManagementGUI(player).open();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package xyz.ineanto.nicko.gui.items.admin;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import xyz.ineanto.nicko.gui.PlayerCheckGUI;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.xenondevs.invui.item.builder.ItemBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.SuppliedItem;
|
||||
|
||||
public class ManagePlayerItem {
|
||||
private final Player player;
|
||||
private final PlayerLanguage playerLanguage;
|
||||
|
||||
public ManagePlayerItem(PlayerLanguage playerLanguage, Player player) {
|
||||
this.playerLanguage = playerLanguage;
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public SuppliedItem get() {
|
||||
return new SuppliedItem(() -> {
|
||||
final ItemBuilder builder = new ItemBuilder(Material.WRITABLE_BOOK);
|
||||
return playerLanguage.translateItem(builder, LanguageKey.GUI.Admin.MANAGE_PLAYER);
|
||||
}, click -> {
|
||||
new PlayerCheckGUI(player, Bukkit.getOnlinePlayers()).open();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
69
src/main/java/xyz/ineanto/nicko/gui/items/admin/cache/CacheEntryItem.java
vendored
Normal file
69
src/main/java/xyz/ineanto/nicko/gui/items/admin/cache/CacheEntryItem.java
vendored
Normal file
|
@ -0,0 +1,69 @@
|
|||
package xyz.ineanto.nicko.gui.items.admin.cache;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.gui.ChoiceGUI;
|
||||
import xyz.ineanto.nicko.gui.InvalidateSkinGUI;
|
||||
import xyz.ineanto.nicko.gui.items.ItemDefaults;
|
||||
import xyz.ineanto.nicko.gui.items.common.choice.ChoiceCallback;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.mojang.MojangAPI;
|
||||
import xyz.xenondevs.invui.item.builder.ItemBuilder;
|
||||
import xyz.xenondevs.invui.item.builder.SkullBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.AsyncItem;
|
||||
import xyz.xenondevs.invui.util.MojangApiUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
public class CacheEntryItem extends AsyncItem {
|
||||
private final String name;
|
||||
private final String uuid;
|
||||
private final MojangAPI mojangAPI = Nicko.getInstance().getMojangAPI();
|
||||
|
||||
public CacheEntryItem(PlayerLanguage playerLanguage, String uuid) {
|
||||
super(new ItemBuilder(Material.PAINTING)
|
||||
.setDisplayName(
|
||||
Component.text(playerLanguage.translate(LanguageKey.GUI.LOADING, false)).content()
|
||||
),
|
||||
() -> {
|
||||
final String dashedUuid = uuid.replaceAll("(.{8})(.{4})(.{4})(.{4})(.+)", "$1-$2-$3-$4-$5");
|
||||
final UUID uuidObject = UUID.fromString(dashedUuid);
|
||||
try {
|
||||
final SkullBuilder skull = new SkullBuilder(uuidObject);
|
||||
return playerLanguage.translateItem(skull, LanguageKey.GUI.Admin.Cache.ENTRY, Nicko.getInstance().getMojangAPI().getUUIDName(uuid));
|
||||
} catch (MojangApiUtils.MojangApiException | IOException e) {
|
||||
Nicko.getInstance().getLogger().warning("Unable to get Head texture for specified UUID (" + uuid + ")! (GUI/Cache/Entry)");
|
||||
return ItemDefaults.getErrorSkullItem(playerLanguage, LanguageKey.GUI.Admin.Cache.ENTRY, Nicko.getInstance().getMojangAPI().getUUIDName(uuid));
|
||||
}
|
||||
});
|
||||
this.uuid = uuid;
|
||||
this.name = mojangAPI.getUUIDName(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleClick(@NotNull ClickType click, @NotNull Player player, @NotNull InventoryClickEvent event) {
|
||||
if (click.isLeftClick() || click.isRightClick()) {
|
||||
event.getView().close();
|
||||
new ChoiceGUI(player, new ChoiceCallback() {
|
||||
@Override
|
||||
public void onConfirm() {
|
||||
final PlayerLanguage playerLanguage = new PlayerLanguage(player);
|
||||
player.sendMessage(playerLanguage.translateWithWhoosh(LanguageKey.Event.Admin.Cache.INVALIDATE_ENTRY, name));
|
||||
mojangAPI.eraseFromCache(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
new InvalidateSkinGUI(player).open();
|
||||
}
|
||||
}).open();
|
||||
}
|
||||
}
|
||||
}
|
35
src/main/java/xyz/ineanto/nicko/gui/items/admin/cache/CacheStatisticsItem.java
vendored
Normal file
35
src/main/java/xyz/ineanto/nicko/gui/items/admin/cache/CacheStatisticsItem.java
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
package xyz.ineanto.nicko.gui.items.admin.cache;
|
||||
|
||||
import com.google.common.cache.CacheStats;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.ineanto.nicko.mojang.MojangSkin;
|
||||
import xyz.xenondevs.invui.item.builder.ItemBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.SuppliedItem;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class CacheStatisticsItem {
|
||||
private final PlayerLanguage playerLanguage;
|
||||
|
||||
public CacheStatisticsItem(Player player) {
|
||||
this.playerLanguage = new PlayerLanguage(player);
|
||||
}
|
||||
|
||||
public SuppliedItem get() {
|
||||
return new SuppliedItem(() -> {
|
||||
final ItemBuilder builder = new ItemBuilder(Material.BOOK);
|
||||
final LoadingCache<String, Optional<MojangSkin>> cache = Nicko.getInstance().getMojangAPI().getSkinCache();
|
||||
final CacheStats stats = cache.stats();
|
||||
|
||||
return playerLanguage.translateItem(builder, LanguageKey.GUI.Admin.Cache.STATISTICS,
|
||||
stats.requestCount(),
|
||||
Math.round(cache.size())
|
||||
);
|
||||
}, (event) -> true);
|
||||
}
|
||||
}
|
37
src/main/java/xyz/ineanto/nicko/gui/items/admin/cache/InvalidateCacheItem.java
vendored
Normal file
37
src/main/java/xyz/ineanto/nicko/gui/items/admin/cache/InvalidateCacheItem.java
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
package xyz.ineanto.nicko.gui.items.admin.cache;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.xenondevs.invui.item.builder.ItemBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.SuppliedItem;
|
||||
|
||||
public class InvalidateCacheItem {
|
||||
private final PlayerLanguage playerLanguage;
|
||||
|
||||
public InvalidateCacheItem(Player player) {
|
||||
this.playerLanguage = new PlayerLanguage(player);
|
||||
}
|
||||
|
||||
public SuppliedItem get() {
|
||||
return new SuppliedItem(() -> {
|
||||
final ItemBuilder builder = new ItemBuilder(Material.TNT);
|
||||
return playerLanguage.translateItem(builder, LanguageKey.GUI.Admin.Cache.INVALIDATE_CACHE);
|
||||
}, (click) -> {
|
||||
final ClickType clickType = click.getClickType();
|
||||
if (clickType.isLeftClick() || clickType.isRightClick()) {
|
||||
click.getEvent().getView().close();
|
||||
|
||||
final Player player = click.getPlayer();
|
||||
final PlayerLanguage playerLanguage = new PlayerLanguage(player);
|
||||
player.sendMessage(playerLanguage.translateWithWhoosh(LanguageKey.Event.Admin.Cache.INVALIDATE_CACHE));
|
||||
Nicko.getInstance().getMojangAPI().getSkinCache().invalidateAll();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
33
src/main/java/xyz/ineanto/nicko/gui/items/admin/cache/InvalidateSkinItem.java
vendored
Normal file
33
src/main/java/xyz/ineanto/nicko/gui/items/admin/cache/InvalidateSkinItem.java
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
package xyz.ineanto.nicko.gui.items.admin.cache;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import xyz.ineanto.nicko.gui.InvalidateSkinGUI;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.xenondevs.invui.item.builder.ItemBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.SuppliedItem;
|
||||
|
||||
public class InvalidateSkinItem {
|
||||
private final PlayerLanguage playerLanguage;
|
||||
|
||||
public InvalidateSkinItem(Player player) {
|
||||
this.playerLanguage = new PlayerLanguage(player);
|
||||
}
|
||||
|
||||
public SuppliedItem get() {
|
||||
return new SuppliedItem(() -> {
|
||||
final ItemBuilder builder = new ItemBuilder(Material.PAPER);
|
||||
return playerLanguage.translateItem(builder, LanguageKey.GUI.Admin.Cache.INVALIDATE_SKIN);
|
||||
}, (click) -> {
|
||||
final ClickType clickType = click.getClickType();
|
||||
if (clickType.isLeftClick() || clickType.isRightClick()) {
|
||||
click.getEvent().getView().close();
|
||||
new InvalidateSkinGUI(click.getPlayer()).open();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package xyz.ineanto.nicko.gui.items.admin.check;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.appearance.AppearanceManager;
|
||||
import xyz.ineanto.nicko.gui.ChoiceGUI;
|
||||
import xyz.ineanto.nicko.gui.PlayerCheckGUI;
|
||||
import xyz.ineanto.nicko.gui.items.ItemDefaults;
|
||||
import xyz.ineanto.nicko.gui.items.common.choice.ChoiceCallback;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.ineanto.nicko.profile.NickoProfile;
|
||||
import xyz.xenondevs.invui.item.builder.AbstractItemBuilder;
|
||||
import xyz.xenondevs.invui.item.builder.ItemBuilder;
|
||||
import xyz.xenondevs.invui.item.builder.SkullBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.AsyncItem;
|
||||
import xyz.xenondevs.invui.item.impl.SuppliedItem;
|
||||
import xyz.xenondevs.invui.util.MojangApiUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
||||
public class PlayerInformationItem extends AsyncItem {
|
||||
private final Player target;
|
||||
private final NickoProfile profile;
|
||||
private final PlayerLanguage playerLanguage;
|
||||
|
||||
public PlayerInformationItem(PlayerLanguage playerLanguage, Player target) {
|
||||
super(new SuppliedItem(() -> {
|
||||
final ItemBuilder builder = new ItemBuilder(Material.PAINTING);
|
||||
return playerLanguage.translateItem(builder, LanguageKey.GUI.LOADING);
|
||||
}, (click -> true)).getItemProvider(), () -> {
|
||||
try {
|
||||
final SkullBuilder skull = new SkullBuilder(target.getUniqueId());
|
||||
final Optional<NickoProfile> optionalProfile = Nicko.getInstance().getDataStore().getData(target.getUniqueId());
|
||||
|
||||
if (optionalProfile.isPresent()) {
|
||||
final NickoProfile profile = optionalProfile.get();
|
||||
final AbstractItemBuilder<?> headItem = playerLanguage.translateItem(skull, LanguageKey.GUI.Admin.CHECK,
|
||||
target.getName(),
|
||||
(profile.hasData() ? "<green>✔</green>" : "<red>❌</red>"),
|
||||
(profile.getName() == null ? "<grey>N/A<grey>" : profile.getName()),
|
||||
(profile.getSkin() == null ? "<grey>N/A</grey>" : profile.getSkin()));
|
||||
|
||||
if (!profile.hasData()) {
|
||||
// Remove the last 2 lines of the lore.
|
||||
headItem.removeLoreLine(headItem.getLore().size() - 1);
|
||||
headItem.removeLoreLine(headItem.getLore().size() - 1);
|
||||
}
|
||||
|
||||
return headItem;
|
||||
}
|
||||
} catch (MojangApiUtils.MojangApiException | IOException e) {
|
||||
Nicko.getInstance().getLogger().severe("Unable to get head for specified UUID ( " + target.getUniqueId() + ")! (GUI/PlayerCheck)");
|
||||
}
|
||||
|
||||
return ItemDefaults.getErrorSkullItem(playerLanguage, LanguageKey.GUI.Admin.CHECK,
|
||||
"§c§l?!?", "§7N/A", "§7N/A", "§7N/A"
|
||||
);
|
||||
});
|
||||
this.playerLanguage = playerLanguage;
|
||||
this.target = target;
|
||||
this.profile = Nicko.getInstance().getDataStore().getData(target.getUniqueId()).orElse(NickoProfile.EMPTY_PROFILE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleClick(@NotNull ClickType click, @NotNull Player player, @NotNull InventoryClickEvent event) {
|
||||
if (click.isLeftClick() || click.isRightClick()) {
|
||||
if (profile.hasData()) {
|
||||
event.getView().close();
|
||||
new ChoiceGUI(player, new ChoiceCallback() {
|
||||
@Override
|
||||
public void onConfirm() {
|
||||
final AppearanceManager appearanceManager = new AppearanceManager(target);
|
||||
appearanceManager.reset();
|
||||
player.sendMessage(playerLanguage.translate(LanguageKey.Event.Admin.Check.REMOVE_SKIN, true, target.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
new PlayerCheckGUI(player, Bukkit.getOnlinePlayers()).open();
|
||||
}
|
||||
}).open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package xyz.ineanto.nicko.gui.items.appearance;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import xyz.ineanto.nicko.anvil.AnvilManager;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.xenondevs.invui.item.builder.ItemBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.SuppliedItem;
|
||||
|
||||
public class ChangeBothItem {
|
||||
private final PlayerLanguage playerLanguage;
|
||||
|
||||
public ChangeBothItem(Player player) {
|
||||
this.playerLanguage = new PlayerLanguage(player);
|
||||
}
|
||||
|
||||
public SuppliedItem get() {
|
||||
return new SuppliedItem(() -> {
|
||||
final ItemBuilder builder = new ItemBuilder(Material.TOTEM_OF_UNDYING);
|
||||
return playerLanguage.translateItem(builder, LanguageKey.GUI.Home.CHANGE_BOTH);
|
||||
}, click -> {
|
||||
final ClickType clickType = click.getClickType();
|
||||
if (clickType.isLeftClick() || clickType.isRightClick()) {
|
||||
click.getEvent().getView().close();
|
||||
final AnvilManager manager = new AnvilManager(click.getPlayer());
|
||||
manager.openNameThenSkinAnvil();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package xyz.ineanto.nicko.gui.items.appearance;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import xyz.ineanto.nicko.anvil.AnvilManager;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.xenondevs.invui.item.builder.ItemBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.SuppliedItem;
|
||||
|
||||
public class ChangeNameItem {
|
||||
private final PlayerLanguage playerLanguage;
|
||||
|
||||
public ChangeNameItem(Player player) {
|
||||
this.playerLanguage = new PlayerLanguage(player);
|
||||
}
|
||||
|
||||
public SuppliedItem get() {
|
||||
return new SuppliedItem(() -> {
|
||||
final ItemBuilder builder = new ItemBuilder(Material.NAME_TAG);
|
||||
return playerLanguage.translateItem(builder, LanguageKey.GUI.Home.CHANGE_NAME);
|
||||
}, click -> {
|
||||
final ClickType clickType = click.getClickType();
|
||||
if (clickType.isLeftClick() || clickType.isRightClick()) {
|
||||
click.getEvent().getView().close();
|
||||
final AnvilManager manager = new AnvilManager(click.getPlayer());
|
||||
manager.openNameAnvil();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package xyz.ineanto.nicko.gui.items.appearance;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.anvil.AnvilManager;
|
||||
import xyz.ineanto.nicko.gui.items.ItemDefaults;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.xenondevs.invui.item.builder.SkullBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.SuppliedItem;
|
||||
import xyz.xenondevs.invui.util.MojangApiUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ChangeSkinItem {
|
||||
private final PlayerLanguage playerLanguage;
|
||||
private final Player player;
|
||||
|
||||
public ChangeSkinItem(Player player) {
|
||||
this.playerLanguage = new PlayerLanguage(player);
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public SuppliedItem get() {
|
||||
return new SuppliedItem(() -> {
|
||||
try {
|
||||
final SkullBuilder builder = new SkullBuilder(player.getName());
|
||||
return playerLanguage.translateItem(builder, LanguageKey.GUI.Home.CHANGE_SKIN);
|
||||
} catch (MojangApiUtils.MojangApiException | IOException e) {
|
||||
Nicko.getInstance().getLogger().warning("Unable to get Head texture for specified player (" + player.getName() + ")! (GUI/Home)");
|
||||
return ItemDefaults.getErrorSkullItem(playerLanguage, LanguageKey.GUI.Home.CHANGE_SKIN);
|
||||
}
|
||||
}, click -> {
|
||||
final ClickType clickType = click.getClickType();
|
||||
if (clickType.isLeftClick() || clickType.isRightClick()) {
|
||||
click.getEvent().getView().close();
|
||||
final AnvilManager manager = new AnvilManager(click.getPlayer());
|
||||
manager.openSkinAnvil();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package xyz.ineanto.nicko.gui.items.common;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.xenondevs.invui.gui.Gui;
|
||||
import xyz.xenondevs.invui.item.builder.ItemBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.SuppliedItem;
|
||||
import xyz.xenondevs.invui.window.Window;
|
||||
|
||||
public class GoBackItem {
|
||||
private final PlayerLanguage playerLanguage;
|
||||
|
||||
public GoBackItem(Player player) {
|
||||
this.playerLanguage = new PlayerLanguage(player);
|
||||
}
|
||||
|
||||
public SuppliedItem get(Gui gui, String parentTitle) {
|
||||
return new SuppliedItem(() -> {
|
||||
final ItemBuilder builder = new ItemBuilder(Material.ARROW);
|
||||
return playerLanguage.translateItem(builder, LanguageKey.GUI.GO_BACK);
|
||||
}, click -> {
|
||||
click.getEvent().getView().close();
|
||||
Window.single().setGui(gui).setTitle(parentTitle).open(click.getPlayer());
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package xyz.ineanto.nicko.gui.items.common;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.Material;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.ineanto.nicko.language.Translation;
|
||||
import xyz.xenondevs.invui.gui.ScrollGui;
|
||||
import xyz.xenondevs.invui.item.ItemProvider;
|
||||
import xyz.xenondevs.invui.item.builder.ItemBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.controlitem.ScrollItem;
|
||||
|
||||
public class ScrollDownItem extends ScrollItem {
|
||||
final PlayerLanguage playerLanguage;
|
||||
|
||||
public ScrollDownItem(PlayerLanguage playerLanguage) {
|
||||
super(1);
|
||||
this.playerLanguage = playerLanguage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemProvider getItemProvider(ScrollGui gui) {
|
||||
final ItemBuilder builder = new ItemBuilder(Material.GREEN_STAINED_GLASS_PANE);
|
||||
final Translation translation = playerLanguage.translateAndReplace(LanguageKey.GUI.SCROLL_DOWN);
|
||||
builder.setDisplayName(Component.text(translation.name()).content());
|
||||
if (!gui.canScroll(1)) {
|
||||
// Lore serialization
|
||||
translation.lore().replaceAll(s -> {
|
||||
final Component deserializedLoreLine = MiniMessage.miniMessage().deserialize(s);
|
||||
return LegacyComponentSerializer.legacySection().serialize(deserializedLoreLine);
|
||||
});
|
||||
translation.lore().forEach(builder::addLoreLines);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package xyz.ineanto.nicko.gui.items.common;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.Material;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.ineanto.nicko.language.Translation;
|
||||
import xyz.xenondevs.invui.gui.ScrollGui;
|
||||
import xyz.xenondevs.invui.item.ItemProvider;
|
||||
import xyz.xenondevs.invui.item.builder.ItemBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.controlitem.ScrollItem;
|
||||
|
||||
public class ScrollUpItem extends ScrollItem {
|
||||
final PlayerLanguage playerLanguage;
|
||||
|
||||
public ScrollUpItem(PlayerLanguage playerLanguage) {
|
||||
super(-1);
|
||||
this.playerLanguage = playerLanguage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemProvider getItemProvider(ScrollGui gui) {
|
||||
final ItemBuilder builder = new ItemBuilder(Material.RED_STAINED_GLASS_PANE);
|
||||
final Translation translation = playerLanguage.translateAndReplace(LanguageKey.GUI.SCROLL_UP);
|
||||
builder.setDisplayName(Component.text(translation.name()).content());
|
||||
if (!gui.canScroll(-1)) {
|
||||
// Lore serialization
|
||||
translation.lore().replaceAll(s -> {
|
||||
final Component deserializedLoreLine = MiniMessage.miniMessage().deserialize(s);
|
||||
return LegacyComponentSerializer.legacySection().serialize(deserializedLoreLine);
|
||||
});
|
||||
translation.lore().forEach(builder::addLoreLines);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package xyz.ineanto.nicko.gui.items.common.choice;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.xenondevs.invui.item.builder.ItemBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.SuppliedItem;
|
||||
|
||||
public class CancelItem {
|
||||
private final PlayerLanguage playerLanguage;
|
||||
private final ChoiceCallback callback;
|
||||
|
||||
public CancelItem(Player player, ChoiceCallback callback) {
|
||||
this.playerLanguage = new PlayerLanguage(player);
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
public SuppliedItem get() {
|
||||
return new SuppliedItem(() -> {
|
||||
final ItemBuilder builder = new ItemBuilder(Material.RED_STAINED_GLASS_PANE);
|
||||
return playerLanguage.translateItem(builder, LanguageKey.GUI.Choice.CANCEL);
|
||||
}, click -> {
|
||||
click.getEvent().getView().close();
|
||||
callback.onCancel();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package xyz.ineanto.nicko.gui.items.common.choice;
|
||||
|
||||
public interface ChoiceCallback {
|
||||
void onConfirm();
|
||||
|
||||
void onCancel();
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package xyz.ineanto.nicko.gui.items.common.choice;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.xenondevs.invui.item.builder.ItemBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.SuppliedItem;
|
||||
|
||||
public class ConfirmItem {
|
||||
private final PlayerLanguage playerLanguage;
|
||||
private final ChoiceCallback callback;
|
||||
|
||||
public ConfirmItem(Player player, ChoiceCallback callback) {
|
||||
this.playerLanguage = new PlayerLanguage(player);
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
public SuppliedItem get() {
|
||||
return new SuppliedItem(() -> {
|
||||
final ItemBuilder builder = new ItemBuilder(Material.GREEN_STAINED_GLASS_PANE);
|
||||
return playerLanguage.translateItem(builder, LanguageKey.GUI.Choice.CONFIRM);
|
||||
}, click -> {
|
||||
click.getEvent().getView().close();
|
||||
callback.onConfirm();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package xyz.ineanto.nicko.gui.items.home;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import xyz.ineanto.nicko.gui.AdminGUI;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.xenondevs.invui.item.builder.ItemBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.SuppliedItem;
|
||||
|
||||
public class AdminAccessItem {
|
||||
private final PlayerLanguage playerLanguage;
|
||||
|
||||
public AdminAccessItem(Player player) {
|
||||
this.playerLanguage = new PlayerLanguage(player);
|
||||
}
|
||||
|
||||
public SuppliedItem get() {
|
||||
return new SuppliedItem(() -> {
|
||||
final ItemBuilder builder = new ItemBuilder(Material.COMMAND_BLOCK_MINECART);
|
||||
return playerLanguage.translateItem(builder, LanguageKey.GUI.Home.ADMIN);
|
||||
}, click -> {
|
||||
final ClickType clickType = click.getClickType();
|
||||
if (clickType.isLeftClick() || clickType.isRightClick()) {
|
||||
click.getEvent().getView().close();
|
||||
new AdminGUI(click.getPlayer()).open();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
31
src/main/java/xyz/ineanto/nicko/gui/items/home/ExitItem.java
Normal file
31
src/main/java/xyz/ineanto/nicko/gui/items/home/ExitItem.java
Normal file
|
@ -0,0 +1,31 @@
|
|||
package xyz.ineanto.nicko.gui.items.home;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.xenondevs.invui.item.builder.ItemBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.SuppliedItem;
|
||||
|
||||
public class ExitItem {
|
||||
private final PlayerLanguage playerLanguage;
|
||||
|
||||
public ExitItem(Player player) {
|
||||
this.playerLanguage = new PlayerLanguage(player);
|
||||
}
|
||||
|
||||
public SuppliedItem get() {
|
||||
return new SuppliedItem(() -> {
|
||||
final ItemBuilder builder = new ItemBuilder(Material.OAK_DOOR);
|
||||
return playerLanguage.translateItem(builder, LanguageKey.GUI.EXIT);
|
||||
}, click -> {
|
||||
click.getEvent().getView().close();
|
||||
final ClickType clickType = click.getClickType();
|
||||
if (clickType.isLeftClick() || clickType.isRightClick()) {
|
||||
click.getEvent().getView().close();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package xyz.ineanto.nicko.gui.items.home;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.appearance.ActionResult;
|
||||
import xyz.ineanto.nicko.appearance.AppearanceManager;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.ineanto.nicko.profile.NickoProfile;
|
||||
import xyz.xenondevs.invui.item.builder.SkullBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.SuppliedItem;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class RandomSkinItem {
|
||||
private final PlayerLanguage playerLanguage;
|
||||
private final Nicko instance;
|
||||
|
||||
public RandomSkinItem(Player player) {
|
||||
this.instance = Nicko.getInstance();
|
||||
this.playerLanguage = new PlayerLanguage(player);
|
||||
}
|
||||
|
||||
public SuppliedItem get() {
|
||||
return new SuppliedItem(() -> {
|
||||
final SkullBuilder.HeadTexture texture = new SkullBuilder.HeadTexture("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzgzMTEzOGMyMDYxMWQzMDJjNDIzZmEzMjM3MWE3NDNkMTc0MzdhMTg5NzNjMzUxOTczNDQ3MGE3YWJiNCJ9fX0=");
|
||||
final SkullBuilder builder = new SkullBuilder(texture);
|
||||
return playerLanguage.translateItem(builder, LanguageKey.GUI.Home.RANDOM_SKIN);
|
||||
}, (event) -> {
|
||||
final Player player = event.getPlayer();
|
||||
final ClickType clickType = event.getClickType();
|
||||
if (clickType.isLeftClick() || clickType.isRightClick()) {
|
||||
final Optional<NickoProfile> optionalProfile = NickoProfile.get(player);
|
||||
optionalProfile.ifPresent(profile -> {
|
||||
final String name = instance.getNameFetcher().getRandomUsername();
|
||||
final String skin = instance.getNameFetcher().getRandomUsername();
|
||||
profile.setName(name);
|
||||
profile.setSkin(skin);
|
||||
instance.getDataStore().updateCache(player.getUniqueId(), profile);
|
||||
|
||||
final AppearanceManager appearanceManager = new AppearanceManager(player);
|
||||
final ActionResult result = appearanceManager.updatePlayer(true, false);
|
||||
if (!result.isError()) {
|
||||
player.sendMessage(playerLanguage.translate(LanguageKey.Event.Appearance.Set.OK, true));
|
||||
} else {
|
||||
player.sendMessage(playerLanguage.translate(
|
||||
LanguageKey.Event.Appearance.Set.ERROR,
|
||||
true,
|
||||
playerLanguage.translate(result.getErrorKey(), false)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package xyz.ineanto.nicko.gui.items.home;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import xyz.ineanto.nicko.appearance.AppearanceManager;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.ineanto.nicko.profile.NickoProfile;
|
||||
import xyz.xenondevs.invui.item.builder.ItemBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.SuppliedItem;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class ResetItem {
|
||||
private final PlayerLanguage playerLanguage;
|
||||
|
||||
public ResetItem(Player player) {
|
||||
this.playerLanguage = new PlayerLanguage(player);
|
||||
}
|
||||
|
||||
public SuppliedItem get() {
|
||||
return new SuppliedItem(() -> {
|
||||
final ItemBuilder builder = new ItemBuilder(Material.TNT);
|
||||
return playerLanguage.translateItem(builder, LanguageKey.GUI.Home.RESET);
|
||||
}, (event) -> {
|
||||
final Player player = event.getPlayer();
|
||||
final ClickType clickType = event.getClickType();
|
||||
if (clickType.isLeftClick() || clickType.isRightClick()) {
|
||||
final Optional<NickoProfile> optionalProfile = NickoProfile.get(player);
|
||||
optionalProfile.ifPresent(profile -> {
|
||||
if (!profile.hasData()) {
|
||||
player.sendMessage(playerLanguage.translateWithOops(LanguageKey.Event.Appearance.Remove.MISSING));
|
||||
event.getEvent().getView().close();
|
||||
return;
|
||||
}
|
||||
|
||||
final AppearanceManager appearanceManager = new AppearanceManager(player);
|
||||
if (!appearanceManager.reset().isError()) {
|
||||
player.sendMessage(playerLanguage.translateWithWhoosh(LanguageKey.Event.Appearance.Remove.OK));
|
||||
} else {
|
||||
player.sendMessage(playerLanguage.translateWithOops(LanguageKey.Event.Appearance.Remove.ERROR));
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package xyz.ineanto.nicko.gui.items.home;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import xyz.ineanto.nicko.gui.SettingsGUI;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.xenondevs.invui.item.builder.ItemBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.SuppliedItem;
|
||||
|
||||
public class SettingsAccessItem {
|
||||
private final PlayerLanguage playerLanguage;
|
||||
|
||||
public SettingsAccessItem(Player player) {
|
||||
this.playerLanguage = new PlayerLanguage(player);
|
||||
}
|
||||
|
||||
public SuppliedItem get() {
|
||||
return new SuppliedItem(() -> {
|
||||
final ItemBuilder builder = new ItemBuilder(Material.COMPARATOR);
|
||||
return playerLanguage.translateItem(builder, LanguageKey.GUI.Home.SETTINGS);
|
||||
}, click -> {
|
||||
final ClickType clickType = click.getClickType();
|
||||
if (clickType.isLeftClick() || clickType.isRightClick()) {
|
||||
click.getEvent().getView().close();
|
||||
new SettingsGUI(click.getPlayer()).open();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package xyz.ineanto.nicko.gui.items.settings;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.entity.Player;
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.gui.SettingsGUI;
|
||||
import xyz.ineanto.nicko.language.Language;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.ineanto.nicko.language.Translation;
|
||||
import xyz.ineanto.nicko.profile.NickoProfile;
|
||||
import xyz.ineanto.nicko.storage.PlayerDataStore;
|
||||
import xyz.xenondevs.invui.item.ItemProvider;
|
||||
import xyz.xenondevs.invui.item.builder.ItemBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.AbstractItem;
|
||||
import xyz.xenondevs.invui.item.impl.CycleItem;
|
||||
import xyz.xenondevs.invui.item.impl.SimpleItem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class LanguageCyclingItem {
|
||||
private final Player player;
|
||||
private final ItemProvider[] providers;
|
||||
private final PlayerLanguage playerLanguage;
|
||||
|
||||
public LanguageCyclingItem(Player player) {
|
||||
this.player = player;
|
||||
this.playerLanguage = new PlayerLanguage(player);
|
||||
this.providers = getItems();
|
||||
}
|
||||
|
||||
public AbstractItem get() {
|
||||
final PlayerDataStore dataStore = Nicko.getInstance().getDataStore();
|
||||
final Optional<NickoProfile> profile = dataStore.getData(player.getUniqueId());
|
||||
if (profile.isPresent()) {
|
||||
final NickoProfile nickoProfile = profile.get();
|
||||
int localeOrdinal = nickoProfile.getLocale().ordinal();
|
||||
return CycleItem.withStateChangeHandler((observer, integer) -> {
|
||||
observer.playSound(player, Sound.UI_BUTTON_CLICK, 1f, 0.707107f); // 0.707107 ~= C
|
||||
nickoProfile.setLocale(Language.values()[integer]);
|
||||
player.getOpenInventory().close();
|
||||
if (dataStore.updateCache(player.getUniqueId(), nickoProfile).isError()) {
|
||||
player.sendMessage(playerLanguage.translate(LanguageKey.Event.Settings.ERROR, true));
|
||||
} else {
|
||||
new SettingsGUI(player).open();
|
||||
}
|
||||
}, localeOrdinal, providers);
|
||||
}
|
||||
|
||||
return new SimpleItem(ItemProvider.EMPTY);
|
||||
}
|
||||
|
||||
private ItemProvider generateItem(Language language, List<Language> languages) {
|
||||
final ItemBuilder builder = new ItemBuilder(Material.OAK_SIGN);
|
||||
final Translation translation = playerLanguage.translateAndReplace(LanguageKey.GUI.Settings.LANGUAGE);
|
||||
final Translation cyclingChoicesTranslation = playerLanguage.translateAndReplace(LanguageKey.GUI.Settings.CYCLING_CHOICES);
|
||||
|
||||
builder.setDisplayName(Component.text(translation.name()).content());
|
||||
for (Language value : languages) {
|
||||
if (language != value) {
|
||||
builder.addLoreLines("§7> " + value.getName());
|
||||
} else {
|
||||
builder.addLoreLines("§6§l> §f" + value.getName());
|
||||
}
|
||||
}
|
||||
|
||||
cyclingChoicesTranslation.lore().replaceAll(s -> {
|
||||
final Component deserializedLoreLine = MiniMessage.miniMessage().deserialize(s);
|
||||
return LegacyComponentSerializer.legacySection().serialize(deserializedLoreLine);
|
||||
});
|
||||
cyclingChoicesTranslation.lore().forEach(builder::addLoreLines);
|
||||
return builder;
|
||||
}
|
||||
|
||||
private ItemProvider[] getItems() {
|
||||
final Nicko instance = Nicko.getInstance();
|
||||
final ArrayList<ItemProvider> items = new ArrayList<>();
|
||||
final ArrayList<Language> localesToGenerate = new ArrayList<>();
|
||||
|
||||
Collections.addAll(localesToGenerate, Language.values());
|
||||
if (!instance.getNickoConfig().isCustomLocale()) {
|
||||
localesToGenerate.remove(Language.CUSTOM);
|
||||
}
|
||||
localesToGenerate.forEach(locale -> items.add(generateItem(locale, localesToGenerate)));
|
||||
return items.toArray(new ItemProvider[]{});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package xyz.ineanto.nicko.gui.items.settings;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.entity.Player;
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.language.PlayerLanguage;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.ineanto.nicko.language.Translation;
|
||||
import xyz.ineanto.nicko.profile.NickoProfile;
|
||||
import xyz.ineanto.nicko.storage.PlayerDataStore;
|
||||
import xyz.xenondevs.invui.item.ItemProvider;
|
||||
import xyz.xenondevs.invui.item.builder.SkullBuilder;
|
||||
import xyz.xenondevs.invui.item.impl.AbstractItem;
|
||||
import xyz.xenondevs.invui.item.impl.CycleItem;
|
||||
import xyz.xenondevs.invui.item.impl.SimpleItem;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class RandomSkinCyclingItem {
|
||||
private final Player player;
|
||||
private final ItemProvider[] providers;
|
||||
private final PlayerLanguage playerLanguage;
|
||||
|
||||
public RandomSkinCyclingItem(Player player) {
|
||||
this.player = player;
|
||||
this.playerLanguage = new PlayerLanguage(player);
|
||||
this.providers = new ItemProvider[]{
|
||||
getItemProviderForValue(true),
|
||||
getItemProviderForValue(false)
|
||||
};
|
||||
}
|
||||
|
||||
public AbstractItem get() {
|
||||
final PlayerDataStore dataStore = Nicko.getInstance().getDataStore();
|
||||
final Optional<NickoProfile> profile = dataStore.getData(player.getUniqueId());
|
||||
if (profile.isPresent()) {
|
||||
final NickoProfile nickoProfile = profile.get();
|
||||
int localeOrdinal = nickoProfile.isRandomSkin() ? 0 : 1;
|
||||
return CycleItem.withStateChangeHandler((observer, integer) -> {
|
||||
observer.playSound(player, Sound.UI_BUTTON_CLICK, 1f, 0.707107f); // 0.707107 ~= C
|
||||
nickoProfile.setRandomSkin(integer != 1);
|
||||
if (dataStore.updateCache(player.getUniqueId(), nickoProfile).isError()) {
|
||||
player.sendMessage(playerLanguage.translate(LanguageKey.Event.Settings.ERROR, true));
|
||||
player.getOpenInventory().close();
|
||||
}
|
||||
}, localeOrdinal, providers);
|
||||
}
|
||||
|
||||
return new SimpleItem(ItemProvider.EMPTY);
|
||||
}
|
||||
|
||||
private ItemProvider getItemProviderForValue(boolean enabled) {
|
||||
final SkullBuilder.HeadTexture texture = new SkullBuilder.HeadTexture("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzgzMTEzOGMyMDYxMWQzMDJjNDIzZmEzMjM3MWE3NDNkMTc0MzdhMTg5NzNjMzUxOTczNDQ3MGE3YWJiNCJ9fX0=");
|
||||
final SkullBuilder builder = new SkullBuilder(texture);
|
||||
final Translation randomSkinTranslation = playerLanguage.translateAndReplace(LanguageKey.GUI.Settings.RANDOM_SKIN);
|
||||
final Translation toggleableTranslation = playerLanguage.translateAndReplace(LanguageKey.GUI.Settings.TOGGLEABLE_BUTTON,
|
||||
(enabled ? "§7>§c" : "§6§l>§c§l"),
|
||||
(enabled ? "§6§l>§a§l" : "§7>§a")
|
||||
);
|
||||
final Translation cyclingChoicesTranslation = playerLanguage.translateAndReplace(LanguageKey.GUI.Settings.CYCLING_CHOICES);
|
||||
|
||||
builder.setDisplayName(randomSkinTranslation.name());
|
||||
toggleableTranslation.lore().forEach(builder::addLoreLines);
|
||||
|
||||
cyclingChoicesTranslation.lore().replaceAll(s -> {
|
||||
final Component deserializedLoreLine = MiniMessage.miniMessage().deserialize(s);
|
||||
return LegacyComponentSerializer.legacySection().serialize(deserializedLoreLine);
|
||||
});
|
||||
cyclingChoicesTranslation.lore().forEach(builder::addLoreLines);
|
||||
return builder;
|
||||
}
|
||||
}
|
61
src/main/java/xyz/ineanto/nicko/language/CustomLanguage.java
Normal file
61
src/main/java/xyz/ineanto/nicko/language/CustomLanguage.java
Normal file
|
@ -0,0 +1,61 @@
|
|||
package xyz.ineanto.nicko.language;
|
||||
|
||||
import com.github.jsixface.YamlConfig;
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.version.Version;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
|
||||
public class CustomLanguage {
|
||||
private static final File directory = new File(Nicko.getInstance().getDataFolder(), "/locale/");
|
||||
private static final File file = new File(directory, "locale.yml");
|
||||
|
||||
private final String version;
|
||||
private final Version versionObject;
|
||||
private final YamlConfig yamlFile;
|
||||
|
||||
public CustomLanguage() throws IOException {
|
||||
this.yamlFile = new YamlConfig(new FileInputStream(file));
|
||||
this.version = yamlFile.getString("version");
|
||||
this.versionObject = Version.fromString(version);
|
||||
}
|
||||
|
||||
public static void dumpIntoFile(Language language) throws IOException {
|
||||
if (language == Language.CUSTOM) return;
|
||||
if (file.exists()) return;
|
||||
if (!directory.exists()) directory.mkdirs();
|
||||
|
||||
final String localeFileName = language.getCode() + ".yml";
|
||||
try {
|
||||
final InputStream resource = Nicko.getInstance().getResource(localeFileName);
|
||||
Files.copy(resource, file.toPath());
|
||||
resource.close();
|
||||
} catch (IOException e) {
|
||||
Nicko.getInstance().getLogger().severe("Unable to dump Locale: " + language.getCode() + "!");
|
||||
}
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public Version getVersionObject() {
|
||||
return versionObject;
|
||||
}
|
||||
|
||||
public YamlConfig getYamlFile() {
|
||||
return yamlFile;
|
||||
}
|
||||
|
||||
public File getDirectory() {
|
||||
return directory;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
}
|
36
src/main/java/xyz/ineanto/nicko/language/Language.java
Normal file
36
src/main/java/xyz/ineanto/nicko/language/Language.java
Normal file
|
@ -0,0 +1,36 @@
|
|||
package xyz.ineanto.nicko.language;
|
||||
|
||||
import xyz.ineanto.nicko.version.Version;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public enum Language implements Serializable {
|
||||
ENGLISH("en", "English"),
|
||||
FRENCH("fr", "Français"),
|
||||
CUSTOM("cm", "Server Custom");
|
||||
|
||||
public static final Version VERSION = new Version(1, 1, 4);
|
||||
|
||||
private final String code;
|
||||
private transient final String name;
|
||||
|
||||
Language(String code, String name) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public static Language fromCode(String code) {
|
||||
for (Language value : values()) {
|
||||
if (code.equals(value.code)) return value;
|
||||
}
|
||||
return ENGLISH;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
142
src/main/java/xyz/ineanto/nicko/language/LanguageKey.java
Normal file
142
src/main/java/xyz/ineanto/nicko/language/LanguageKey.java
Normal file
|
@ -0,0 +1,142 @@
|
|||
package xyz.ineanto.nicko.language;
|
||||
|
||||
public class LanguageKey {
|
||||
public static final String PREFIX = "prefix";
|
||||
public static final String WHOOSH = "whoosh";
|
||||
public static final String OOPS = "oops";
|
||||
|
||||
public static class Error {
|
||||
public static final String ERROR_KEY = "error.";
|
||||
|
||||
public static final String PERMISSION = ERROR_KEY + "permission";
|
||||
public static final String CACHE = ERROR_KEY + "cache";
|
||||
public static final String MOJANG_NAME = ERROR_KEY + "mojang_name";
|
||||
public static final String MOJANG_SKIN = ERROR_KEY + "mojang_skin";
|
||||
}
|
||||
|
||||
public static class Event {
|
||||
private static final String EVENT_KEY = "event.";
|
||||
|
||||
public static class Admin {
|
||||
private static final String ADMIN_KEY = EVENT_KEY + "admin.";
|
||||
|
||||
public static class Cache {
|
||||
private static final String CACHE_KEY = ADMIN_KEY + "cache.";
|
||||
|
||||
public static final String INVALIDATE_CACHE = CACHE_KEY + "invalidate_cache";
|
||||
public static final String INVALIDATE_ENTRY = CACHE_KEY + "invalidate_entry";
|
||||
|
||||
}
|
||||
|
||||
public static class Check {
|
||||
private static final String CHECK_KEY = ADMIN_KEY + "check.";
|
||||
|
||||
public static final String REMOVE_SKIN = CHECK_KEY + "remove_skin";
|
||||
}
|
||||
}
|
||||
|
||||
public static class Settings {
|
||||
private static final String SETTINGS_KEY = EVENT_KEY + "settings.";
|
||||
|
||||
public static final String ERROR = SETTINGS_KEY + "error";
|
||||
}
|
||||
|
||||
public static class Appearance {
|
||||
private static final String APPEARANCE_KEY = EVENT_KEY + "appearance.";
|
||||
|
||||
public static class Set {
|
||||
public static final String SET_KEY = APPEARANCE_KEY + "set.";
|
||||
|
||||
public static final String OK = SET_KEY + "ok";
|
||||
public static final String ERROR = SET_KEY + "error";
|
||||
}
|
||||
|
||||
public static class Remove {
|
||||
private static final String REMOVE_KEY = APPEARANCE_KEY + "remove.";
|
||||
|
||||
public static final String OK = REMOVE_KEY + "ok";
|
||||
public static final String MISSING = REMOVE_KEY + "missing";
|
||||
public static final String ERROR = REMOVE_KEY + "error";
|
||||
}
|
||||
|
||||
public static class Restore {
|
||||
private static final String RESTORE_KEY = APPEARANCE_KEY + "restore.";
|
||||
|
||||
public static final String OK = RESTORE_KEY + "ok";
|
||||
public static final String ERROR = RESTORE_KEY + "error";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class GUI {
|
||||
private static final String GUI_KEY = "gui.";
|
||||
|
||||
public static final String EXIT = GUI_KEY + "exit";
|
||||
public static final String GO_BACK = GUI_KEY + "go_back";
|
||||
public static final String UNAVAILABLE = GUI_KEY + "unavailable";
|
||||
public static final String LOADING = GUI_KEY + "loading";
|
||||
public static final String SCROLL_UP = GUI_KEY + "scroll_up";
|
||||
public static final String SCROLL_DOWN = GUI_KEY + "scroll_down";
|
||||
public static final String NEW_SKIN = GUI_KEY + "new_skin";
|
||||
public static final String NEW_NAME = GUI_KEY + "new_name";
|
||||
|
||||
public static class Titles {
|
||||
public static final String TITLE_KEY = GUI_KEY + "title.";
|
||||
|
||||
public static final String HOME = TITLE_KEY + "home";
|
||||
public static final String SETTINGS = TITLE_KEY + "settings";
|
||||
public static final String ADMIN = TITLE_KEY + "admin";
|
||||
public static final String CHECK = TITLE_KEY + "check";
|
||||
public static final String CONFIRM = TITLE_KEY + "confirm";
|
||||
public static final String CACHE = TITLE_KEY + "cache";
|
||||
public static final String INVALIDATE_SKIN = TITLE_KEY + "invalidate_skin";
|
||||
}
|
||||
|
||||
public static class Choice {
|
||||
private static final String CHOICE_KEY = GUI_KEY + "choice.";
|
||||
|
||||
public static final String CONFIRM = CHOICE_KEY + "confirm";
|
||||
public static final String CHOOSE = CHOICE_KEY + "choose";
|
||||
public static final String CANCEL = CHOICE_KEY + "cancel";
|
||||
}
|
||||
|
||||
public static class Home {
|
||||
private static final String HOME_KEY = GUI_KEY + "home.";
|
||||
|
||||
public static final String ADMIN = HOME_KEY + "admin";
|
||||
public static final String CHANGE_NAME = HOME_KEY + "change_name";
|
||||
public static final String CHANGE_SKIN = HOME_KEY + "change_skin";
|
||||
public static final String CHANGE_BOTH = HOME_KEY + "change_both";
|
||||
public static final String RESET = HOME_KEY + "reset";
|
||||
public static final String RANDOM_SKIN = HOME_KEY + "random_skin";
|
||||
public static final String SETTINGS = HOME_KEY + "settings";
|
||||
}
|
||||
|
||||
public static class Settings {
|
||||
private static final String SETTINGS_KEY = GUI_KEY + "settings.";
|
||||
|
||||
public static final String CYCLING_CHOICES = SETTINGS_KEY + "cycling_choices";
|
||||
public static final String TOGGLEABLE_BUTTON = SETTINGS_KEY + "toggleable_button";
|
||||
|
||||
public static final String LANGUAGE = SETTINGS_KEY + "language";
|
||||
public static final String RANDOM_SKIN = SETTINGS_KEY + "random_skin";
|
||||
}
|
||||
|
||||
public static class Admin {
|
||||
private static final String ADMIN_KEY = GUI_KEY + "admin.";
|
||||
|
||||
public static final String MANAGE_CACHE = ADMIN_KEY + "manage_cache";
|
||||
public static final String MANAGE_PLAYER = ADMIN_KEY + "manage_player";
|
||||
public static final String CHECK = ADMIN_KEY + "check";
|
||||
|
||||
public static class Cache {
|
||||
private static final String CACHE_KEY = ADMIN_KEY + "cache.";
|
||||
|
||||
public static final String STATISTICS = CACHE_KEY + "statistics";
|
||||
public static final String INVALIDATE_CACHE = CACHE_KEY + "invalidate_cache";
|
||||
public static final String INVALIDATE_SKIN = CACHE_KEY + "invalidate_skin";
|
||||
public static final String ENTRY = CACHE_KEY + "entry";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
181
src/main/java/xyz/ineanto/nicko/language/PlayerLanguage.java
Normal file
181
src/main/java/xyz/ineanto/nicko/language/PlayerLanguage.java
Normal file
|
@ -0,0 +1,181 @@
|
|||
package xyz.ineanto.nicko.language;
|
||||
|
||||
import com.github.jsixface.YamlConfig;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.entity.Player;
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.profile.NickoProfile;
|
||||
import xyz.xenondevs.invui.item.builder.AbstractItemBuilder;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.*;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class PlayerLanguage {
|
||||
private final MessageFormat formatter = new MessageFormat("");
|
||||
private final Logger logger = Logger.getLogger("I18N");
|
||||
private final Nicko instance = Nicko.getInstance();
|
||||
private final Pattern replacementPattern = Pattern.compile("(?ms)\\{\\d+}");
|
||||
private final YamlConfig yamlConfig;
|
||||
private final Language playerLanguage;
|
||||
|
||||
public PlayerLanguage(Player player) {
|
||||
final Optional<NickoProfile> optionalProfile = NickoProfile.get(player);
|
||||
this.playerLanguage = optionalProfile.map(NickoProfile::getLocale).orElse(Language.ENGLISH);
|
||||
this.yamlConfig = getYamlConfig();
|
||||
}
|
||||
|
||||
public PlayerLanguage(Language language) {
|
||||
this.playerLanguage = language;
|
||||
this.yamlConfig = getYamlConfig();
|
||||
}
|
||||
|
||||
public AbstractItemBuilder<?> translateItem(AbstractItemBuilder<?> item, String key, Object... args) {
|
||||
final Translation translation = translateAndReplace(key, args);
|
||||
|
||||
// Name serialization
|
||||
final Component deserializedName = MiniMessage.miniMessage().deserialize(translation.name());
|
||||
final String serializedName = LegacyComponentSerializer.legacySection().serialize(deserializedName);
|
||||
|
||||
// Lore serialization
|
||||
translation.lore().replaceAll(s -> {
|
||||
final Component deserializedLoreLine = MiniMessage.miniMessage().deserialize(s);
|
||||
return LegacyComponentSerializer.legacySection().serialize(deserializedLoreLine);
|
||||
});
|
||||
|
||||
item.setDisplayName(serializedName);
|
||||
translation.lore().forEach(item::addLoreLines);
|
||||
return item;
|
||||
}
|
||||
|
||||
public Translation translateAndReplace(String key, Object... args) {
|
||||
final String nameKey = key + ".name";
|
||||
final String loreKey = key + ".lore";
|
||||
final String name = readString(nameKey);
|
||||
final ArrayList<String> lore = readList(loreKey);
|
||||
|
||||
if (name == null && lore == null) {
|
||||
Nicko.getInstance().getLogger().warning(nameKey + " doesn't exists! Is your language file outdated?");
|
||||
return new Translation(nameKey, new ArrayList<>(List.of(loreKey)));
|
||||
}
|
||||
|
||||
// Add all elements to a list
|
||||
final ArrayList<String> toTranslate = new ArrayList<>();
|
||||
if (name != null) {
|
||||
toTranslate.add(name);
|
||||
}
|
||||
if (lore != null && !lore.isEmpty()) {
|
||||
toTranslate.addAll(lore);
|
||||
}
|
||||
|
||||
// Set starting index to 0
|
||||
int lineIndex = 0;
|
||||
int replacementIndex = 0;
|
||||
|
||||
// While iterator next value exists/isn't null
|
||||
final Iterator<String> iterator = toTranslate.iterator();
|
||||
while (iterator.hasNext() && iterator.next() != null) {
|
||||
// Get the current line
|
||||
final String currentLine = toTranslate.get(lineIndex);
|
||||
|
||||
// If the line doesn't contain {i}, skip it
|
||||
final Matcher matcher = replacementPattern.matcher(currentLine);
|
||||
if (!matcher.find()) {
|
||||
lineIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If it does, replace the content with the args at position replacementIndex
|
||||
if (replacementIndex < args.length && args[replacementIndex] != null) {
|
||||
// Replace it with the corresponding varargs index
|
||||
toTranslate.set(lineIndex, currentLine.replace("{" + replacementIndex + "}", args[replacementIndex].toString()));
|
||||
replacementIndex++;
|
||||
}
|
||||
|
||||
// Increment the index
|
||||
lineIndex++;
|
||||
}
|
||||
|
||||
if (name == null && !lore.isEmpty()) {
|
||||
// Empty name, valid lore
|
||||
return new Translation(null, toTranslate);
|
||||
} else if (name != null && (lore == null || lore.isEmpty())) {
|
||||
// Valid name, empty lore
|
||||
return new Translation(toTranslate.getFirst(), new ArrayList<>(Collections.emptyList()));
|
||||
} else {
|
||||
// Valid name, valid lore
|
||||
return new Translation(toTranslate.getFirst(), new ArrayList<>(toTranslate.subList(1, toTranslate.size())));
|
||||
}
|
||||
}
|
||||
|
||||
public String translate(String key, boolean prefix, Object... arguments) {
|
||||
final String translation = readStringWithMiniMessage(key);
|
||||
|
||||
try {
|
||||
formatter.applyPattern(translation);
|
||||
return (prefix ? getPrefix() + " " : "") + formatter.format(arguments);
|
||||
} catch (Exception e) {
|
||||
return (prefix ? getPrefix() + " " : "") + key;
|
||||
}
|
||||
}
|
||||
|
||||
public String translateWithWhoosh(String key, Object... arguments) {
|
||||
final String translation = readStringWithMiniMessage(key);
|
||||
|
||||
try {
|
||||
formatter.applyPattern(translation);
|
||||
return getWhoosh() + " " + formatter.format(arguments);
|
||||
} catch (Exception e) {
|
||||
return getWhoosh() + " " + key;
|
||||
}
|
||||
}
|
||||
|
||||
public String translateWithOops(String key, Object... arguments) {
|
||||
final String translation = readStringWithMiniMessage(key);
|
||||
|
||||
try {
|
||||
formatter.applyPattern(translation);
|
||||
return getOops() + " " + formatter.format(arguments);
|
||||
} catch (Exception e) {
|
||||
return getOops() + " " + key;
|
||||
}
|
||||
}
|
||||
|
||||
private String readString(String key) {
|
||||
return yamlConfig.getString(key);
|
||||
}
|
||||
|
||||
private String readStringWithMiniMessage(String key) {
|
||||
return LegacyComponentSerializer.legacySection().serialize(MiniMessage.miniMessage().deserialize(readString(key)));
|
||||
}
|
||||
|
||||
private ArrayList<String> readList(String key) {
|
||||
return yamlConfig.getStringList(key);
|
||||
}
|
||||
|
||||
private String getPrefix() {
|
||||
return readStringWithMiniMessage(LanguageKey.PREFIX);
|
||||
}
|
||||
|
||||
private String getWhoosh() {
|
||||
return readStringWithMiniMessage(LanguageKey.WHOOSH);
|
||||
}
|
||||
|
||||
private String getOops() {
|
||||
return readStringWithMiniMessage(LanguageKey.OOPS);
|
||||
}
|
||||
|
||||
private YamlConfig getYamlConfig() {
|
||||
if (playerLanguage == Language.CUSTOM) {
|
||||
return instance.getCustomLocale().getYamlFile();
|
||||
} else {
|
||||
final InputStream resource = instance.getResource(playerLanguage.getCode() + ".yml");
|
||||
return new YamlConfig(resource);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package xyz.ineanto.nicko.language;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public record Translation(String name, ArrayList<String> lore) {}
|
27
src/main/java/xyz/ineanto/nicko/mapping/MappingManager.java
Normal file
27
src/main/java/xyz/ineanto/nicko/mapping/MappingManager.java
Normal file
|
@ -0,0 +1,27 @@
|
|||
package xyz.ineanto.nicko.mapping;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import xyz.ineanto.nicko.mapping.v1_20.Mapping1_20;
|
||||
import xyz.ineanto.nicko.mapping.v1_20_2.Mapping1_20_2;
|
||||
import xyz.ineanto.nicko.mapping.v1_20_4.Mapping1_20_4;
|
||||
import xyz.ineanto.nicko.mapping.v1_20_6.Mapping1_20_6;
|
||||
import xyz.ineanto.nicko.mapping.v1_21.Mapping1_21;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class MappingManager {
|
||||
private final List<Mapping> mappings = List.of(
|
||||
new Mapping1_20(),
|
||||
new Mapping1_20_2(),
|
||||
new Mapping1_20_4(),
|
||||
new Mapping1_20_6(),
|
||||
new Mapping1_21()
|
||||
);
|
||||
|
||||
public Optional<Mapping> getMappingForServer() {
|
||||
return mappings.stream()
|
||||
.filter(mapping -> mapping.supportedVersions().contains(Bukkit.getMinecraftVersion()))
|
||||
.findFirst();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package xyz.ineanto.nicko.migration;
|
||||
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.config.Configuration;
|
||||
import xyz.ineanto.nicko.config.ConfigurationManager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
|
||||
public class ConfigurationMigrator implements Migrator {
|
||||
private final Nicko instance;
|
||||
|
||||
public ConfigurationMigrator(Nicko instance) {
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrate() {
|
||||
final Configuration configuration = instance.getNickoConfig();
|
||||
final ConfigurationManager configurationManager = instance.getConfigurationManager();
|
||||
|
||||
// Migrate configuration (1.0.8-RC1)
|
||||
if (configuration.getVersion() == null
|
||||
|| configuration.getVersion().isEmpty()
|
||||
|| configuration.getVersionObject().compareTo(Configuration.VERSION) != 0) {
|
||||
instance.getLogger().info("Migrating configuration file to match the current version...");
|
||||
try {
|
||||
Files.copy(configurationManager.getFile().toPath(), configurationManager.getBackupFile().toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
if (configurationManager.getFile().delete()) {
|
||||
configurationManager.saveDefaultConfig();
|
||||
instance.getLogger().info("Successfully migrated your configuration file!");
|
||||
} else {
|
||||
instance.getLogger().severe("Failed to migrate your configuration!");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
instance.getLogger().severe("Failed to migrate your configuration!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package xyz.ineanto.nicko.migration;
|
||||
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.language.CustomLanguage;
|
||||
import xyz.ineanto.nicko.language.Language;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
public class CustomLocaleMigrator implements Migrator {
|
||||
private final Nicko instance;
|
||||
private final CustomLanguage customLanguage;
|
||||
|
||||
public CustomLocaleMigrator(Nicko instance, CustomLanguage customLanguage) {
|
||||
this.instance = instance;
|
||||
this.customLanguage = customLanguage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrate() {
|
||||
// Migrate custom locale (1.1.0-RC1)
|
||||
if (customLanguage.getVersionObject() == null
|
||||
|| customLanguage.getVersion().isEmpty()
|
||||
|| customLanguage.getVersionObject().compareTo(Language.VERSION) != 0) {
|
||||
instance.getLogger().info("Migrating the custom locale (" + customLanguage.getVersion() + ") to match the current version (" + Language.VERSION + ")...");
|
||||
|
||||
final String date = Instant.now().atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ofPattern("dd-MM-yyyy"));
|
||||
final File backupFile = new File(customLanguage.getDirectory(), "locale-" + date + ".yml");
|
||||
|
||||
try {
|
||||
Files.copy(customLanguage.getFile().toPath(), backupFile.toPath());
|
||||
if (customLanguage.getFile().delete()) {
|
||||
CustomLanguage.dumpIntoFile(Language.ENGLISH);
|
||||
instance.getLogger().info("Successfully migrated the custom locale.");
|
||||
} else {
|
||||
instance.getLogger().severe("Failed to migrate the custom locale!");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
instance.getLogger().severe("Failed to migrate the custom locale!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
src/main/java/xyz/ineanto/nicko/migration/Migrator.java
Normal file
5
src/main/java/xyz/ineanto/nicko/migration/Migrator.java
Normal file
|
@ -0,0 +1,5 @@
|
|||
package xyz.ineanto.nicko.migration;
|
||||
|
||||
public interface Migrator {
|
||||
void migrate();
|
||||
}
|
155
src/main/java/xyz/ineanto/nicko/mojang/MojangAPI.java
Normal file
155
src/main/java/xyz/ineanto/nicko/mojang/MojangAPI.java
Normal file
|
@ -0,0 +1,155 @@
|
|||
package xyz.ineanto.nicko.mojang;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class MojangAPI {
|
||||
public static final String URL_NAME = "https://api.mojang.com/users/profiles/minecraft/{name}";
|
||||
public static final String URL_SKIN = "https://sessionserver.mojang.com/session/minecraft/profile/{uuid}?unsigned=false";
|
||||
|
||||
private final Logger logger = Logger.getLogger("MojangAPI");
|
||||
private final HashMap<String, String> uuidToName = new HashMap<>();
|
||||
private final ExecutorService worker = Executors.newFixedThreadPool(6);
|
||||
|
||||
private final CacheLoader<String, Optional<MojangSkin>> skinLoader = new CacheLoader<>() {
|
||||
@Nonnull
|
||||
public Optional<MojangSkin> load(@Nonnull String uuid) throws Exception {
|
||||
return getSkinFromMojang(uuid);
|
||||
}
|
||||
};
|
||||
|
||||
private final LoadingCache<String, Optional<MojangSkin>> skinCache = CacheBuilder
|
||||
.newBuilder()
|
||||
.recordStats()
|
||||
.expireAfterWrite(24, TimeUnit.HOURS)
|
||||
.build(skinLoader);
|
||||
|
||||
private final CacheLoader<String, Optional<String>> uuidLoader = new CacheLoader<>() {
|
||||
@Nonnull
|
||||
public Optional<String> load(@Nonnull String name) throws Exception {
|
||||
return getUUIDFromMojang(name);
|
||||
}
|
||||
};
|
||||
|
||||
private final LoadingCache<String, Optional<String>> uuidCache = CacheBuilder
|
||||
.newBuilder()
|
||||
.expireAfterWrite(2, TimeUnit.DAYS)
|
||||
.build(uuidLoader);
|
||||
|
||||
public Optional<MojangSkin> getSkin(String uuid) throws IOException, ExecutionException {
|
||||
return skinCache.get(uuid);
|
||||
}
|
||||
|
||||
public Optional<MojangSkin> getSkinWithoutCaching(String uuid) throws IOException, ExecutionException, InterruptedException {
|
||||
return getSkinFromMojang(uuid);
|
||||
}
|
||||
|
||||
public Optional<String> getUUID(String name) throws IOException, ExecutionException {
|
||||
return uuidCache.get(name);
|
||||
}
|
||||
|
||||
private Optional<String> getUUIDFromMojang(String name) throws ExecutionException, InterruptedException {
|
||||
final String parametrizedUrl = URL_NAME.replace("{name}", name);
|
||||
final JsonObject object = getRequestToUrl(parametrizedUrl);
|
||||
if (hasNoError(object)) {
|
||||
final JsonElement idObject = object.get("id");
|
||||
final String uuid = idObject.getAsString();
|
||||
final Optional<String> uuidOptional = Optional.of(uuid);
|
||||
uuidCache.put(name, uuidOptional);
|
||||
uuidToName.put(uuid, name);
|
||||
return uuidOptional;
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public void eraseFromCache(String uuid) {
|
||||
skinCache.invalidate(uuid);
|
||||
uuidToName.remove(uuid);
|
||||
uuidCache.invalidate(uuid);
|
||||
}
|
||||
|
||||
private Optional<MojangSkin> getSkinFromMojang(String uuid) throws ExecutionException, InterruptedException {
|
||||
final String parametrizedUrl = URL_SKIN.replace("{uuid}", uuid);
|
||||
final JsonObject object = getRequestToUrl(parametrizedUrl);
|
||||
if (hasNoError(object)) {
|
||||
final MojangSkin skin = MojangSkin.buildFromJson(object);
|
||||
return Optional.of(skin);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public String getUUIDName(String uuid) {
|
||||
return uuidToName.get(uuid);
|
||||
}
|
||||
|
||||
private JsonObject getRequestToUrl(String parametrizedUrl) throws ExecutionException, InterruptedException {
|
||||
return worker.submit(() -> {
|
||||
final URL url = URI.create(parametrizedUrl).toURL();
|
||||
final HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
|
||||
con.setDoInput(true);
|
||||
con.setRequestMethod("GET");
|
||||
|
||||
switch (con.getResponseCode()) {
|
||||
case 404:
|
||||
case 400:
|
||||
logger.warning("Failed to parse request: Invalid Name");
|
||||
return getErrorObject();
|
||||
case 429:
|
||||
logger.warning("Failed to parse request: The connection is throttled.");
|
||||
return getErrorObject();
|
||||
case 200:
|
||||
final BufferedReader input = new BufferedReader(new InputStreamReader(con.getInputStream()));
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
String line;
|
||||
while ((line = input.readLine()) != null) {
|
||||
builder.append(line);
|
||||
}
|
||||
|
||||
try {
|
||||
final JsonElement jsonElt = JsonParser.parseString(builder.toString());
|
||||
return jsonElt.getAsJsonObject();
|
||||
} catch (JsonParseException | IllegalStateException exception) {
|
||||
logger.warning("Failed to parse request (" + parametrizedUrl + ")!");
|
||||
return getErrorObject();
|
||||
}
|
||||
default:
|
||||
logger.warning("Unhandled response code from Mojang: " + con.getResponseCode());
|
||||
return getErrorObject();
|
||||
}
|
||||
}).get();
|
||||
}
|
||||
|
||||
private JsonObject getErrorObject() {
|
||||
final JsonObject errorObject = new JsonObject();
|
||||
errorObject.addProperty("error", "An error occurred.");
|
||||
return errorObject;
|
||||
}
|
||||
|
||||
private boolean hasNoError(JsonObject object) {
|
||||
return object.get("error") == null;
|
||||
}
|
||||
|
||||
public LoadingCache<String, Optional<MojangSkin>> getSkinCache() {
|
||||
return skinCache;
|
||||
}
|
||||
}
|
12
src/main/java/xyz/ineanto/nicko/mojang/MojangSkin.java
Normal file
12
src/main/java/xyz/ineanto/nicko/mojang/MojangSkin.java
Normal file
|
@ -0,0 +1,12 @@
|
|||
package xyz.ineanto.nicko.mojang;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public record MojangSkin(String value, String signature) {
|
||||
public static MojangSkin buildFromJson(JsonObject object) {
|
||||
final JsonObject properties = object.get("properties").getAsJsonArray().get(0).getAsJsonObject();
|
||||
final String value = properties.get("value").getAsString();
|
||||
final String signature = properties.get("signature").getAsString();
|
||||
return new MojangSkin(value, signature);
|
||||
}
|
||||
}
|
26
src/main/java/xyz/ineanto/nicko/mojang/MojangUtils.java
Normal file
26
src/main/java/xyz/ineanto/nicko/mojang/MojangUtils.java
Normal file
|
@ -0,0 +1,26 @@
|
|||
package xyz.ineanto.nicko.mojang;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MojangUtils {
|
||||
public static boolean isUsernameInvalid(String username) {
|
||||
return !Pattern.matches("^\\w{3,16}$", username);
|
||||
}
|
||||
|
||||
public static UUID fromTrimmed(String trimmedUUID) throws IllegalArgumentException {
|
||||
if (trimmedUUID == null) throw new IllegalArgumentException();
|
||||
StringBuilder builder = new StringBuilder(trimmedUUID.trim());
|
||||
/* Backwards adding to avoid index adjustments */
|
||||
try {
|
||||
builder.insert(20, "-");
|
||||
builder.insert(16, "-");
|
||||
builder.insert(12, "-");
|
||||
builder.insert(8, "-");
|
||||
} catch (StringIndexOutOfBoundsException e) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
return UUID.fromString(builder.toString());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package xyz.ineanto.nicko.placeholder;
|
||||
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.profile.NickoProfile;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public class NickoExpansion extends PlaceholderExpansion {
|
||||
|
||||
private final Nicko instance;
|
||||
|
||||
public NickoExpansion(Nicko instance) {
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getIdentifier() {
|
||||
return "xyz/ineanto/nicko";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getAuthor() {
|
||||
return "Ineanto";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getVersion() {
|
||||
return "1.0.0";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean persist() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String onPlaceholderRequest(Player player, @NotNull String params) {
|
||||
if (player == null) return null;
|
||||
|
||||
String name, skin, locale;
|
||||
boolean randomSkin;
|
||||
|
||||
name = skin = player.getName();
|
||||
locale = "N/A";
|
||||
randomSkin = false;
|
||||
|
||||
final Optional<NickoProfile> optionalProfile = instance.getDataStore().getData(player.getUniqueId());
|
||||
if (optionalProfile.isPresent()) {
|
||||
final NickoProfile profile = optionalProfile.get();
|
||||
if (profile.hasData()) {
|
||||
if (profile.getName() != null) {
|
||||
name = profile.getName();
|
||||
}
|
||||
|
||||
if (profile.getSkin() != null) {
|
||||
skin = profile.getSkin();
|
||||
}
|
||||
}
|
||||
locale = profile.getLocale().getName();
|
||||
randomSkin = profile.isRandomSkin();
|
||||
}
|
||||
|
||||
return switch (params) {
|
||||
case "name" -> name;
|
||||
case "skin" -> skin;
|
||||
case "locale" -> locale;
|
||||
case "random_skin" -> String.valueOf(randomSkin);
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
}
|
91
src/main/java/xyz/ineanto/nicko/profile/NickoProfile.java
Normal file
91
src/main/java/xyz/ineanto/nicko/profile/NickoProfile.java
Normal file
|
@ -0,0 +1,91 @@
|
|||
package xyz.ineanto.nicko.profile;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.language.Language;
|
||||
import xyz.ineanto.nicko.storage.PlayerDataStore;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class NickoProfile implements Cloneable {
|
||||
public static final PlayerDataStore dataStore = Nicko.getInstance().getDataStore();
|
||||
public static final NickoProfile EMPTY_PROFILE = new NickoProfile(null, null, Language.ENGLISH, true);
|
||||
|
||||
private String name;
|
||||
private String skin;
|
||||
private Language language;
|
||||
private boolean randomSkin;
|
||||
|
||||
public NickoProfile(String name, String skin, Language language, boolean randomSkin) {
|
||||
this.name = name;
|
||||
this.skin = skin;
|
||||
this.language = language;
|
||||
this.randomSkin = randomSkin;
|
||||
}
|
||||
|
||||
public static Optional<NickoProfile> get(Player player) {
|
||||
return dataStore.getData(player.getUniqueId());
|
||||
}
|
||||
|
||||
public static Optional<NickoProfile> get(UUID uuid) {
|
||||
return dataStore.getData(uuid);
|
||||
}
|
||||
|
||||
public boolean hasData() {
|
||||
return name != null || skin != null;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getSkin() {
|
||||
return skin;
|
||||
}
|
||||
|
||||
public void setSkin(String skin) {
|
||||
this.skin = skin;
|
||||
}
|
||||
|
||||
public Language getLocale() {
|
||||
return language;
|
||||
}
|
||||
|
||||
public void setLocale(Language language) {
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
public boolean isRandomSkin() {
|
||||
return randomSkin;
|
||||
}
|
||||
|
||||
public void setRandomSkin(boolean randomSkin) {
|
||||
this.randomSkin = randomSkin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NickoProfile{" +
|
||||
"name='" + name + '\'' +
|
||||
", skin='" + skin + '\'' +
|
||||
", locale=" + language +
|
||||
", randomSkin=" + randomSkin +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public NickoProfile clone() {
|
||||
Object o;
|
||||
try {
|
||||
o = super.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return (NickoProfile) o;
|
||||
}
|
||||
}
|
29
src/main/java/xyz/ineanto/nicko/storage/Cache.java
Normal file
29
src/main/java/xyz/ineanto/nicko/storage/Cache.java
Normal file
|
@ -0,0 +1,29 @@
|
|||
package xyz.ineanto.nicko.storage;
|
||||
|
||||
import xyz.ineanto.nicko.appearance.ActionResult;
|
||||
import xyz.ineanto.nicko.profile.NickoProfile;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class Cache {
|
||||
private boolean error = false;
|
||||
|
||||
public abstract CacheProvider getProvider();
|
||||
|
||||
public abstract ActionResult cache(UUID uuid, NickoProfile profile);
|
||||
|
||||
public abstract boolean isCached(UUID uuid);
|
||||
|
||||
public abstract Optional<NickoProfile> retrieve(UUID uuid);
|
||||
|
||||
public abstract ActionResult delete(UUID uuid);
|
||||
|
||||
public boolean isError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public void setError(boolean error) {
|
||||
this.error = error;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package xyz.ineanto.nicko.storage;
|
||||
|
||||
public interface CacheProvider {
|
||||
boolean init();
|
||||
|
||||
boolean close();
|
||||
}
|
106
src/main/java/xyz/ineanto/nicko/storage/PlayerDataStore.java
Normal file
106
src/main/java/xyz/ineanto/nicko/storage/PlayerDataStore.java
Normal file
|
@ -0,0 +1,106 @@
|
|||
package xyz.ineanto.nicko.storage;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import xyz.ineanto.nicko.appearance.ActionResult;
|
||||
import xyz.ineanto.nicko.config.Configuration;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.ineanto.nicko.mojang.MojangAPI;
|
||||
import xyz.ineanto.nicko.mojang.MojangUtils;
|
||||
import xyz.ineanto.nicko.profile.NickoProfile;
|
||||
import xyz.ineanto.nicko.storage.json.JSONStorage;
|
||||
import xyz.ineanto.nicko.storage.map.MapCache;
|
||||
import xyz.ineanto.nicko.storage.mariadb.MariaDBStorage;
|
||||
import xyz.ineanto.nicko.storage.mysql.MySQLStorage;
|
||||
import xyz.ineanto.nicko.storage.redis.RedisCache;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class PlayerDataStore {
|
||||
private final MojangAPI mojangAPI;
|
||||
|
||||
private Storage storage;
|
||||
private Cache cache;
|
||||
|
||||
public PlayerDataStore(MojangAPI mojangAPI, Configuration configuration) {
|
||||
this.mojangAPI = mojangAPI;
|
||||
this.storage = configuration.getSqlConfiguration().isEnabled() ?
|
||||
configuration.getSqlConfiguration().isMariadb() ? new MariaDBStorage(configuration) : new MySQLStorage(configuration)
|
||||
: new JSONStorage();
|
||||
this.cache = configuration.getRedisConfiguration().isEnabled() ? new RedisCache(configuration) : new MapCache();
|
||||
}
|
||||
|
||||
public ActionResult updateCache(UUID uuid, NickoProfile profile) {
|
||||
if (storage.isError() || cache.isError()) {
|
||||
return ActionResult.error(LanguageKey.Error.CACHE);
|
||||
}
|
||||
|
||||
getCache().cache(uuid, profile);
|
||||
return ActionResult.ok();
|
||||
}
|
||||
|
||||
public Optional<NickoProfile> getData(UUID uuid) {
|
||||
if (storage.isError() || cache.isError()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
if (cache.isCached(uuid)) {
|
||||
return cache.retrieve(uuid);
|
||||
} else if (storage.isStored(uuid)) {
|
||||
final Optional<NickoProfile> retrievedProfile = storage.retrieve(uuid);
|
||||
retrievedProfile.ifPresent(profile -> cache.cache(uuid, profile));
|
||||
return retrievedProfile;
|
||||
} else {
|
||||
final NickoProfile newProfile = NickoProfile.EMPTY_PROFILE.clone();
|
||||
cache.cache(uuid, newProfile);
|
||||
return Optional.of(newProfile);
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<NickoProfile> getOfflineData(String name) {
|
||||
if (storage.isError() || cache.isError()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
try {
|
||||
final Optional<String> uuidTrimmed = mojangAPI.getUUID(name);
|
||||
if (uuidTrimmed.isPresent()) {
|
||||
final UUID uuid = MojangUtils.fromTrimmed(uuidTrimmed.get());
|
||||
return getData(uuid);
|
||||
}
|
||||
return Optional.empty();
|
||||
} catch (IOException | ExecutionException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public ActionResult saveData(Player player) {
|
||||
if (storage.isError()) return ActionResult.error();
|
||||
if (cache.isError()) return ActionResult.error();
|
||||
if (!cache.isCached(player.getUniqueId())) return ActionResult.error();
|
||||
|
||||
final Optional<NickoProfile> cachedProfile = cache.retrieve(player.getUniqueId());
|
||||
if (cachedProfile.isEmpty()) return ActionResult.error();
|
||||
|
||||
cache.delete(player.getUniqueId());
|
||||
return storage.store(player.getUniqueId(), cachedProfile.get());
|
||||
}
|
||||
|
||||
public Storage getStorage() {
|
||||
return storage;
|
||||
}
|
||||
|
||||
public void setStorage(Storage storage) {
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
public Cache getCache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
public void setCache(Cache cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
}
|
29
src/main/java/xyz/ineanto/nicko/storage/Storage.java
Normal file
29
src/main/java/xyz/ineanto/nicko/storage/Storage.java
Normal file
|
@ -0,0 +1,29 @@
|
|||
package xyz.ineanto.nicko.storage;
|
||||
|
||||
import xyz.ineanto.nicko.appearance.ActionResult;
|
||||
import xyz.ineanto.nicko.profile.NickoProfile;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class Storage {
|
||||
private boolean error = false;
|
||||
|
||||
public abstract StorageProvider getProvider();
|
||||
|
||||
public abstract ActionResult store(UUID uuid, NickoProfile profile);
|
||||
|
||||
public abstract boolean isStored(UUID uuid);
|
||||
|
||||
public abstract Optional<NickoProfile> retrieve(UUID uuid);
|
||||
|
||||
public abstract ActionResult delete(UUID uuid);
|
||||
|
||||
public boolean isError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public void setError(boolean error) {
|
||||
this.error = error;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package xyz.ineanto.nicko.storage;
|
||||
|
||||
public interface StorageProvider {
|
||||
boolean init();
|
||||
|
||||
boolean close();
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
package xyz.ineanto.nicko.storage.json;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import xyz.ineanto.nicko.Nicko;
|
||||
import xyz.ineanto.nicko.appearance.ActionResult;
|
||||
import xyz.ineanto.nicko.profile.NickoProfile;
|
||||
import xyz.ineanto.nicko.storage.Storage;
|
||||
import xyz.ineanto.nicko.storage.StorageProvider;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class JSONStorage extends Storage {
|
||||
private final Logger logger = Logger.getLogger("JSONStorage");
|
||||
private final Gson gson = new GsonBuilder().serializeNulls().setPrettyPrinting().create();
|
||||
private final File directory = new File(Nicko.getInstance().getDataFolder() + "/players/");
|
||||
|
||||
private JSONStorageProvider provider;
|
||||
|
||||
@Override
|
||||
public StorageProvider getProvider() {
|
||||
if (provider == null) {
|
||||
provider = new JSONStorageProvider(directory);
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult store(UUID uuid, NickoProfile profile) {
|
||||
final String profileToJson = gson.toJson(profile);
|
||||
final File file = new File(directory, uuid.toString() + ".json");
|
||||
|
||||
try {
|
||||
if (checkFileExists(file)) {
|
||||
try (FileWriter fileWriter = new FileWriter(file)) {
|
||||
try (BufferedWriter writer = new BufferedWriter(fileWriter)) {
|
||||
writer.write(profileToJson);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.warning("Could not write to file.");
|
||||
return ActionResult.error();
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.warning("Could not create file.");
|
||||
return ActionResult.error();
|
||||
}
|
||||
|
||||
return ActionResult.ok();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStored(UUID uuid) {
|
||||
final File directory = new File(Nicko.getInstance().getDataFolder() + "/players/");
|
||||
final File file = new File(directory, uuid.toString() + ".json");
|
||||
return file.exists();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<NickoProfile> retrieve(UUID uuid) {
|
||||
final File directory = new File(Nicko.getInstance().getDataFolder() + "/players/");
|
||||
final File file = new File(directory, uuid.toString() + ".json");
|
||||
try (FileReader fileReader = new FileReader(file)) {
|
||||
try (BufferedReader reader = new BufferedReader(fileReader)) {
|
||||
final NickoProfile value = gson.fromJson(reader, NickoProfile.class);
|
||||
return Optional.of(value);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult delete(UUID uuid) {
|
||||
final File directory = new File(Nicko.getInstance().getDataFolder() + "/players/");
|
||||
final File file = new File(directory, uuid.toString() + ".json");
|
||||
if (file.delete() || !file.exists()) {
|
||||
return ActionResult.ok();
|
||||
}
|
||||
return ActionResult.error();
|
||||
}
|
||||
|
||||
private boolean checkFileExists(File file) throws IOException {
|
||||
if (!file.exists()) {
|
||||
return file.createNewFile();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package xyz.ineanto.nicko.storage.json;
|
||||
|
||||
import xyz.ineanto.nicko.storage.StorageProvider;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class JSONStorageProvider implements StorageProvider {
|
||||
private final File directory;
|
||||
|
||||
public JSONStorageProvider(File directory) {
|
||||
this.directory = directory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean init()
|
||||
{
|
||||
return directory.exists() || directory.mkdirs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean close() { return true; }
|
||||
}
|
51
src/main/java/xyz/ineanto/nicko/storage/map/MapCache.java
Normal file
51
src/main/java/xyz/ineanto/nicko/storage/map/MapCache.java
Normal file
|
@ -0,0 +1,51 @@
|
|||
package xyz.ineanto.nicko.storage.map;
|
||||
|
||||
import xyz.ineanto.nicko.appearance.ActionResult;
|
||||
import xyz.ineanto.nicko.profile.NickoProfile;
|
||||
import xyz.ineanto.nicko.storage.Cache;
|
||||
import xyz.ineanto.nicko.storage.CacheProvider;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class MapCache extends Cache {
|
||||
private MapCacheProvider provider;
|
||||
|
||||
@Override
|
||||
public CacheProvider getProvider() {
|
||||
if (provider == null) {
|
||||
provider = new MapCacheProvider();
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult cache(UUID uuid, NickoProfile profile) {
|
||||
final HashMap<UUID, NickoProfile> profiles = provider.getMap();
|
||||
profiles.put(uuid, profile);
|
||||
return ActionResult.ok();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCached(UUID uuid) {
|
||||
final HashMap<UUID, NickoProfile> profiles = provider.getMap();
|
||||
return profiles.containsKey(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<NickoProfile> retrieve(UUID uuid) {
|
||||
final HashMap<UUID, NickoProfile> profiles = provider.getMap();
|
||||
if (isCached(uuid)) {
|
||||
return Optional.of(profiles.get(uuid));
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult delete(UUID uuid) {
|
||||
final HashMap<UUID, NickoProfile> profiles = provider.getMap();
|
||||
profiles.remove(uuid);
|
||||
return ActionResult.ok();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package xyz.ineanto.nicko.storage.map;
|
||||
|
||||
import xyz.ineanto.nicko.profile.NickoProfile;
|
||||
import xyz.ineanto.nicko.storage.CacheProvider;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
public class MapCacheProvider implements CacheProvider {
|
||||
private HashMap<UUID, NickoProfile> profiles;
|
||||
|
||||
@Override
|
||||
public boolean init() {
|
||||
if (profiles == null) {
|
||||
profiles = new HashMap<>();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean close() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public HashMap<UUID, NickoProfile> getMap() {
|
||||
return profiles;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
package xyz.ineanto.nicko.storage.mariadb;
|
||||
|
||||
import xyz.ineanto.nicko.appearance.ActionResult;
|
||||
import xyz.ineanto.nicko.config.Configuration;
|
||||
import xyz.ineanto.nicko.language.Language;
|
||||
import xyz.ineanto.nicko.profile.NickoProfile;
|
||||
import xyz.ineanto.nicko.storage.Storage;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class MariaDBStorage extends Storage {
|
||||
private final Logger logger = Logger.getLogger("SQLStorage");
|
||||
private final Configuration configuration;
|
||||
|
||||
private MariaDBStorageProvider provider;
|
||||
|
||||
public MariaDBStorage(Configuration configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MariaDBStorageProvider getProvider() {
|
||||
if (provider == null) {
|
||||
provider = new MariaDBStorageProvider(configuration);
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult store(UUID uuid, NickoProfile profile) {
|
||||
final Connection connection = getProvider().getConnection();
|
||||
if (connection == null) return ActionResult.error();
|
||||
|
||||
try {
|
||||
final PreparedStatement statement = isStored(uuid) ?
|
||||
getUpdateStatement(connection, uuid, profile) : getInsertStatement(connection, uuid, profile);
|
||||
statement.executeUpdate();
|
||||
return ActionResult.ok();
|
||||
} catch (SQLException e) {
|
||||
logger.warning("Couldn't send SQL Request: " + e.getMessage());
|
||||
return ActionResult.error();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStored(UUID uuid) {
|
||||
final Connection connection = getProvider().getConnection();
|
||||
if (connection == null) return false;
|
||||
|
||||
try {
|
||||
final String sql = "SELECT uuid FROM nicko.DATA WHERE uuid = ?";
|
||||
|
||||
final PreparedStatement statement = connection.prepareStatement(sql);
|
||||
statement.setString(1, uuid.toString());
|
||||
|
||||
final ResultSet resultSet = statement.executeQuery();
|
||||
return resultSet.next();
|
||||
} catch (SQLException e) {
|
||||
logger.warning("Couldn't check if data is present: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<NickoProfile> retrieve(UUID uuid) {
|
||||
final Connection connection = getProvider().getConnection();
|
||||
if (connection == null) return Optional.empty();
|
||||
if (!isStored(uuid)) return Optional.empty();
|
||||
|
||||
try {
|
||||
final String sql = "SELECT * FROM nicko.DATA WHERE uuid = ?";
|
||||
|
||||
final PreparedStatement statement = connection.prepareStatement(sql);
|
||||
statement.setString(1, uuid.toString());
|
||||
|
||||
final ResultSet resultSet = statement.executeQuery();
|
||||
String name = "";
|
||||
String skin = "";
|
||||
String locale = "";
|
||||
boolean bungeecord = false;
|
||||
while (resultSet.next()) {
|
||||
name = resultSet.getString("name");
|
||||
skin = resultSet.getString("skin");
|
||||
locale = resultSet.getString("locale");
|
||||
bungeecord = resultSet.getBoolean("bungeecord");
|
||||
}
|
||||
|
||||
final NickoProfile profile = new NickoProfile(name, skin, Language.fromCode(locale), bungeecord);
|
||||
return Optional.of(profile);
|
||||
} catch (SQLException e) {
|
||||
logger.warning("Couldn't fetch profile: " + e.getMessage());
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult delete(UUID uuid) {
|
||||
final Connection connection = getProvider().getConnection();
|
||||
if (connection == null) return ActionResult.error();
|
||||
|
||||
try {
|
||||
final String sql = "DELETE FROM nicko.DATA WHERE uuid = ?";
|
||||
final PreparedStatement statement = connection.prepareStatement(sql);
|
||||
statement.setString(1, uuid.toString());
|
||||
int rows = statement.executeUpdate();
|
||||
return (rows == 1 ? ActionResult.ok() : ActionResult.error());
|
||||
} catch (SQLException e) {
|
||||
logger.warning("Couldn't delete profile: " + e.getMessage());
|
||||
return ActionResult.error();
|
||||
}
|
||||
}
|
||||
|
||||
private PreparedStatement getInsertStatement(Connection connection, UUID uuid, NickoProfile profile) throws SQLException {
|
||||
final String sql = "INSERT IGNORE INTO nicko.DATA (`uuid`, `name`, `skin`, `locale`, `bungeecord`) VALUES (?, ?, ?, ?, ?)";
|
||||
final PreparedStatement statement = connection.prepareStatement(sql);
|
||||
statement.setString(1, uuid.toString());
|
||||
statement.setString(2, profile.getName() == null ? null : profile.getName());
|
||||
statement.setString(3, profile.getSkin() == null ? null : profile.getSkin());
|
||||
statement.setString(4, profile.getLocale().getCode());
|
||||
statement.setBoolean(5, profile.isRandomSkin());
|
||||
return statement;
|
||||
}
|
||||
|
||||
private PreparedStatement getUpdateStatement(Connection connection, UUID uuid, NickoProfile profile) throws SQLException {
|
||||
final String sql = "UPDATE nicko.DATA SET name = ?, skin = ?, locale = ?, bungeecord = ? WHERE uuid = ?";
|
||||
final PreparedStatement statement = connection.prepareStatement(sql);
|
||||
statement.setString(1, profile.getName() == null ? null : profile.getName());
|
||||
statement.setString(2, profile.getSkin() == null ? null : profile.getSkin());
|
||||
statement.setString(3, profile.getLocale().getCode());
|
||||
statement.setBoolean(4, profile.isRandomSkin());
|
||||
statement.setString(5, uuid.toString());
|
||||
return statement;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package xyz.ineanto.nicko.storage.mariadb;
|
||||
|
||||
import org.mariadb.jdbc.MariaDbDataSource;
|
||||
import xyz.ineanto.nicko.config.Configuration;
|
||||
import xyz.ineanto.nicko.config.DataSourceConfiguration;
|
||||
import xyz.ineanto.nicko.storage.StorageProvider;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class MariaDBStorageProvider implements StorageProvider {
|
||||
private final Logger logger = Logger.getLogger("MariaDBStorageProvider");
|
||||
private final Configuration configuration;
|
||||
|
||||
private Connection connection;
|
||||
|
||||
private final String schemaName = "xyz/ineanto/nicko";
|
||||
|
||||
public MariaDBStorageProvider(Configuration configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean init() {
|
||||
try {
|
||||
final MariaDbDataSource dataSource = new MariaDbDataSource();
|
||||
final DataSourceConfiguration sqlConfiguration = configuration.getSqlConfiguration();
|
||||
dataSource.setUrl("jdbc:mariadb://" + sqlConfiguration.getAddress() + ":" + sqlConfiguration.getPort());
|
||||
dataSource.setUser(sqlConfiguration.getUsername());
|
||||
dataSource.setPassword(sqlConfiguration.getPassword());
|
||||
connection = dataSource.getConnection();
|
||||
connection.setAutoCommit(true);
|
||||
final boolean initialized = connection != null && !connection.isClosed();
|
||||
|
||||
if (!initialized) return false;
|
||||
|
||||
createDatabase();
|
||||
createTable();
|
||||
return true;
|
||||
} catch (SQLException e) {
|
||||
logger.severe("Couldn't establish a connection to the MariaDB database: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean close() {
|
||||
if (connection == null) { return true; }
|
||||
try {
|
||||
connection.close();
|
||||
return connection.isClosed();
|
||||
} catch (SQLException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void createTable() throws SQLException {
|
||||
final Connection connection = getConnection();
|
||||
final String query = "CREATE TABLE IF NOT EXISTS %s.DATA ".replace("%s", schemaName) +
|
||||
"(uuid varchar(36) NOT NULL," +
|
||||
"name varchar(16)," +
|
||||
"skin varchar(16)," +
|
||||
"locale char(2) NOT NULL," +
|
||||
"bungeecord boolean NOT NULL," +
|
||||
"PRIMARY KEY (uuid))";
|
||||
|
||||
final PreparedStatement statement = connection.prepareStatement(query);
|
||||
statement.executeUpdate();
|
||||
}
|
||||
|
||||
private void createDatabase() throws SQLException {
|
||||
final Connection connection = getConnection();
|
||||
final String query = "CREATE DATABASE IF NOT EXISTS %s".replace("%s", schemaName);
|
||||
|
||||
final PreparedStatement statement = connection.prepareStatement(query);
|
||||
statement.executeUpdate();
|
||||
}
|
||||
|
||||
public Connection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
}
|
140
src/main/java/xyz/ineanto/nicko/storage/mysql/MySQLStorage.java
Normal file
140
src/main/java/xyz/ineanto/nicko/storage/mysql/MySQLStorage.java
Normal file
|
@ -0,0 +1,140 @@
|
|||
package xyz.ineanto.nicko.storage.mysql;
|
||||
|
||||
import xyz.ineanto.nicko.appearance.ActionResult;
|
||||
import xyz.ineanto.nicko.config.Configuration;
|
||||
import xyz.ineanto.nicko.language.Language;
|
||||
import xyz.ineanto.nicko.profile.NickoProfile;
|
||||
import xyz.ineanto.nicko.storage.Storage;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class MySQLStorage extends Storage {
|
||||
private final Logger logger = Logger.getLogger("SQLStorage");
|
||||
private final Configuration configuration;
|
||||
|
||||
private MySQLStorageProvider provider;
|
||||
|
||||
public MySQLStorage(Configuration configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MySQLStorageProvider getProvider() {
|
||||
if (provider == null) {
|
||||
provider = new MySQLStorageProvider(configuration);
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult store(UUID uuid, NickoProfile profile) {
|
||||
final Connection connection = getProvider().getConnection();
|
||||
if (connection == null) return ActionResult.error();
|
||||
|
||||
try {
|
||||
final PreparedStatement statement = isStored(uuid) ?
|
||||
getUpdateStatement(connection, uuid, profile) : getInsertStatement(connection, uuid, profile);
|
||||
statement.executeUpdate();
|
||||
return ActionResult.ok();
|
||||
} catch (SQLException e) {
|
||||
logger.warning("Couldn't send SQL Request: " + e.getMessage());
|
||||
return ActionResult.error();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStored(UUID uuid) {
|
||||
final Connection connection = getProvider().getConnection();
|
||||
if (connection == null) return false;
|
||||
|
||||
try {
|
||||
final String sql = "SELECT uuid FROM nicko.DATA WHERE uuid = ?";
|
||||
|
||||
final PreparedStatement statement = connection.prepareStatement(sql);
|
||||
statement.setString(1, uuid.toString());
|
||||
|
||||
final ResultSet resultSet = statement.executeQuery();
|
||||
return resultSet.next();
|
||||
} catch (SQLException e) {
|
||||
logger.warning("Couldn't check if data is present: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<NickoProfile> retrieve(UUID uuid) {
|
||||
final Connection connection = getProvider().getConnection();
|
||||
if (connection == null) return Optional.empty();
|
||||
if (!isStored(uuid)) return Optional.empty();
|
||||
|
||||
try {
|
||||
final String sql = "SELECT * FROM nicko.DATA WHERE uuid = ?";
|
||||
|
||||
final PreparedStatement statement = connection.prepareStatement(sql);
|
||||
statement.setString(1, uuid.toString());
|
||||
|
||||
final ResultSet resultSet = statement.executeQuery();
|
||||
String name = "";
|
||||
String skin = "";
|
||||
String locale = "";
|
||||
boolean bungeecord = false;
|
||||
while (resultSet.next()) {
|
||||
name = resultSet.getString("name");
|
||||
skin = resultSet.getString("skin");
|
||||
locale = resultSet.getString("locale");
|
||||
bungeecord = resultSet.getBoolean("bungeecord");
|
||||
}
|
||||
|
||||
final NickoProfile profile = new NickoProfile(name, skin, Language.fromCode(locale), bungeecord);
|
||||
return Optional.of(profile);
|
||||
} catch (SQLException e) {
|
||||
logger.warning("Couldn't fetch profile: " + e.getMessage());
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult delete(UUID uuid) {
|
||||
final Connection connection = getProvider().getConnection();
|
||||
if (connection == null) return ActionResult.error();
|
||||
|
||||
try {
|
||||
final String sql = "DELETE FROM nicko.DATA WHERE uuid = ?";
|
||||
final PreparedStatement statement = connection.prepareStatement(sql);
|
||||
statement.setString(1, uuid.toString());
|
||||
int rows = statement.executeUpdate();
|
||||
return (rows == 1 ? ActionResult.ok() : ActionResult.error());
|
||||
} catch (SQLException e) {
|
||||
logger.warning("Couldn't delete profile: " + e.getMessage());
|
||||
return ActionResult.error();
|
||||
}
|
||||
}
|
||||
|
||||
private PreparedStatement getInsertStatement(Connection connection, UUID uuid, NickoProfile profile) throws SQLException {
|
||||
final String sql = "INSERT IGNORE INTO nicko.DATA (`uuid`, `name`, `skin`, `locale`, `bungeecord`) VALUES (?, ?, ?, ?, ?)";
|
||||
final PreparedStatement statement = connection.prepareStatement(sql);
|
||||
statement.setString(1, uuid.toString());
|
||||
statement.setString(2, profile.getName() == null ? null : profile.getName());
|
||||
statement.setString(3, profile.getSkin() == null ? null : profile.getSkin());
|
||||
statement.setString(4, profile.getLocale().getCode());
|
||||
statement.setBoolean(5, profile.isRandomSkin());
|
||||
return statement;
|
||||
}
|
||||
|
||||
private PreparedStatement getUpdateStatement(Connection connection, UUID uuid, NickoProfile profile) throws SQLException {
|
||||
final String sql = "UPDATE nicko.DATA SET name = ?, skin = ?, locale = ?, bungeecord = ? WHERE uuid = ?";
|
||||
final PreparedStatement statement = connection.prepareStatement(sql);
|
||||
statement.setString(1, profile.getName() == null ? null : profile.getName());
|
||||
statement.setString(2, profile.getSkin() == null ? null : profile.getSkin());
|
||||
statement.setString(3, profile.getLocale().getCode());
|
||||
statement.setBoolean(4, profile.isRandomSkin());
|
||||
statement.setString(5, uuid.toString());
|
||||
return statement;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package xyz.ineanto.nicko.storage.mysql;
|
||||
|
||||
import com.mysql.cj.jdbc.MysqlDataSource;
|
||||
import xyz.ineanto.nicko.config.Configuration;
|
||||
import xyz.ineanto.nicko.config.DataSourceConfiguration;
|
||||
import xyz.ineanto.nicko.storage.StorageProvider;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class MySQLStorageProvider implements StorageProvider {
|
||||
private final Logger logger = Logger.getLogger("MySQLStorageProvider");
|
||||
private final Configuration configuration;
|
||||
|
||||
private Connection connection;
|
||||
|
||||
private final String schemaName = "xyz/ineanto/nicko";
|
||||
|
||||
public MySQLStorageProvider(Configuration configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean init() {
|
||||
try {
|
||||
final MysqlDataSource dataSource = new MysqlDataSource();
|
||||
final DataSourceConfiguration sqlConfiguration = configuration.getSqlConfiguration();
|
||||
dataSource.setUrl("jdbc:mysql://" + sqlConfiguration.getAddress() + ":" + sqlConfiguration.getPort());
|
||||
dataSource.setUser(sqlConfiguration.getUsername());
|
||||
dataSource.setPassword(sqlConfiguration.getPassword());
|
||||
connection = dataSource.getConnection();
|
||||
connection.setAutoCommit(true);
|
||||
final boolean initialized = connection != null && !connection.isClosed();
|
||||
|
||||
if (!initialized) return false;
|
||||
|
||||
createDatabase();
|
||||
createTable();
|
||||
return true;
|
||||
} catch (SQLException e) {
|
||||
logger.severe("Couldn't establish a connection to the MySQL database: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean close() {
|
||||
if (connection == null) { return true; }
|
||||
try {
|
||||
connection.close();
|
||||
return connection.isClosed();
|
||||
} catch (SQLException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void createTable() throws SQLException {
|
||||
final Connection connection = getConnection();
|
||||
final String query = "CREATE TABLE IF NOT EXISTS %s.DATA ".replace("%s", schemaName) +
|
||||
"(uuid varchar(36) NOT NULL," +
|
||||
"name varchar(16)," +
|
||||
"skin varchar(16)," +
|
||||
"locale char(2) NOT NULL," +
|
||||
"bungeecord boolean NOT NULL," +
|
||||
"PRIMARY KEY (uuid))";
|
||||
|
||||
final PreparedStatement statement = connection.prepareStatement(query);
|
||||
statement.executeUpdate();
|
||||
}
|
||||
|
||||
private void createDatabase() throws SQLException {
|
||||
final Connection connection = getConnection();
|
||||
final String query = "CREATE DATABASE IF NOT EXISTS %s".replace("%s", schemaName);
|
||||
|
||||
final PreparedStatement statement = connection.prepareStatement(query);
|
||||
statement.executeUpdate();
|
||||
}
|
||||
|
||||
public Connection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package xyz.ineanto.nicko.storage.name;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PlayerNameStore {
|
||||
private final HashMap<UUID, String> names = new HashMap<>();
|
||||
|
||||
public void storeName(Player player) {
|
||||
if (!isNameStored(player)) {
|
||||
names.put(player.getUniqueId(), player.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public String getStoredName(Player player) {
|
||||
return names.get(player.getUniqueId());
|
||||
}
|
||||
|
||||
private boolean isNameStored(Player player) {
|
||||
return names.containsKey(player.getUniqueId());
|
||||
}
|
||||
|
||||
public void clearStoredNames() {
|
||||
names.clear();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package xyz.ineanto.nicko.storage.redis;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.exceptions.JedisException;
|
||||
import xyz.ineanto.nicko.appearance.ActionResult;
|
||||
import xyz.ineanto.nicko.config.Configuration;
|
||||
import xyz.ineanto.nicko.language.LanguageKey;
|
||||
import xyz.ineanto.nicko.profile.NickoProfile;
|
||||
import xyz.ineanto.nicko.storage.Cache;
|
||||
import xyz.ineanto.nicko.storage.CacheProvider;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class RedisCache extends Cache {
|
||||
private final Gson gson = new GsonBuilder()
|
||||
.serializeNulls()
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
private final Configuration configuration;
|
||||
private RedisCacheProvider provider;
|
||||
|
||||
public RedisCache(Configuration configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheProvider getProvider() {
|
||||
if (provider == null) {
|
||||
provider = new RedisCacheProvider(configuration);
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult cache(UUID uuid, NickoProfile profile) {
|
||||
try (Jedis jedis = provider.getJedis()) {
|
||||
jedis.set("nicko:" + uuid.toString(), gson.toJson(profile));
|
||||
return ActionResult.ok();
|
||||
} catch (JedisException exception) {
|
||||
return ActionResult.error(LanguageKey.Error.CACHE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCached(UUID uuid) {
|
||||
try (Jedis jedis = provider.getJedis()) {
|
||||
return jedis.exists("nicko:" + uuid.toString());
|
||||
} catch (JedisException exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<NickoProfile> retrieve(UUID uuid) {
|
||||
try (Jedis jedis = provider.getJedis()) {
|
||||
// 08/29/23: what the fuck was I talking about?
|
||||
// old_todo (Ineanto, 05/20/23): Check if cached before because Jedis returns a bulk reply so this is unsafe
|
||||
final String data = jedis.get("nicko:" + uuid.toString());
|
||||
final NickoProfile profile = gson.fromJson(data, NickoProfile.class);
|
||||
return Optional.of(profile);
|
||||
} catch (JedisException exception) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult delete(UUID uuid) {
|
||||
try (Jedis jedis = provider.getJedis()) {
|
||||
jedis.del("nicko:" + uuid.toString());
|
||||
return ActionResult.ok();
|
||||
} catch (JedisException exception) {
|
||||
return ActionResult.error(LanguageKey.Error.CACHE);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package xyz.ineanto.nicko.storage.redis;
|
||||
|
||||
import redis.clients.jedis.exceptions.JedisConnectionException;
|
||||
import xyz.ineanto.nicko.config.Configuration;
|
||||
import xyz.ineanto.nicko.config.DataSourceConfiguration;
|
||||
import xyz.ineanto.nicko.storage.CacheProvider;
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.JedisPool;
|
||||
|
||||
public class RedisCacheProvider implements CacheProvider {
|
||||
private final Configuration configuration;
|
||||
private JedisPool pool;
|
||||
|
||||
public RedisCacheProvider(Configuration configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean init() {
|
||||
final DataSourceConfiguration redisConfiguration = configuration.getRedisConfiguration();
|
||||
pool = new JedisPool(redisConfiguration.getAddress(), redisConfiguration.getPort());
|
||||
try {
|
||||
return !pool.isClosed() && pool.getResource() != null;
|
||||
} catch (JedisConnectionException exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean close() {
|
||||
pool.close();
|
||||
return pool.isClosed();
|
||||
}
|
||||
|
||||
public Jedis getJedis() {
|
||||
return pool.getResource();
|
||||
}
|
||||
}
|
33
src/main/java/xyz/ineanto/nicko/version/Version.java
Normal file
33
src/main/java/xyz/ineanto/nicko/version/Version.java
Normal file
|
@ -0,0 +1,33 @@
|
|||
package xyz.ineanto.nicko.version;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
public record Version(int major, int minor, int patch) implements Comparable<Version> {
|
||||
@Override
|
||||
public int compareTo(@NotNull Version otherVersion) {
|
||||
final Comparator<Version> comparator = Comparator
|
||||
.comparingInt(Version::major)
|
||||
.thenComparingInt(Version::minor)
|
||||
.thenComparingInt(Version::patch);
|
||||
return comparator.compare(this, otherVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return major + "." + minor + "." + patch;
|
||||
}
|
||||
|
||||
public static Version fromString(String versionString) {
|
||||
if (versionString == null || versionString.isEmpty()) { return new Version(0, 0, 0); }
|
||||
final String[] split = versionString.split("\\.");
|
||||
try {
|
||||
return new Version(Integer.parseInt(split[0]),
|
||||
Integer.parseInt(split[1]),
|
||||
Integer.parseInt(split[2]));
|
||||
} catch (NumberFormatException exception) {
|
||||
return new Version(0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
73
src/main/java/xyz/ineanto/nicko/wrapper/AbstractPacket.java
Normal file
73
src/main/java/xyz/ineanto/nicko/wrapper/AbstractPacket.java
Normal file
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* PacketWrapper - ProtocolLib wrappers for Minecraft packets
|
||||
* Copyright (C) dmulloy2 <http://dmulloy2.net>
|
||||
* Copyright (C) Kristian S. Strangeland
|
||||
* <p>
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* <p>
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* <p>
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package xyz.ineanto.nicko.wrapper;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.google.common.base.Objects;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public abstract class AbstractPacket {
|
||||
protected PacketContainer handle;
|
||||
|
||||
/**
|
||||
* Constructs a new strongly typed wrapper for the given packet.
|
||||
*
|
||||
* @param handle - handle to the raw packet data.
|
||||
* @param type - the packet type.
|
||||
*/
|
||||
protected AbstractPacket(PacketContainer handle, PacketType type) {
|
||||
// Make sure we're given a valid packet
|
||||
if (handle == null)
|
||||
throw new IllegalArgumentException("Packet handle cannot be NULL.");
|
||||
if (!Objects.equal(handle.getType(), type))
|
||||
throw new IllegalArgumentException(handle.getHandle()
|
||||
+ " is not a packet of type " + type);
|
||||
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a handle to the raw packet data.
|
||||
*
|
||||
* @return Raw packet data.
|
||||
*/
|
||||
public PacketContainer getHandle() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the current packet to the given receiver.
|
||||
*
|
||||
* @param receiver - the receiver.
|
||||
* @throws RuntimeException If the packet cannot be sent.
|
||||
*/
|
||||
public void sendPacket(Player receiver) {
|
||||
ProtocolLibrary.getProtocolManager().sendServerPacket(receiver,
|
||||
getHandle());
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the current packet to all online players.
|
||||
*/
|
||||
public void broadcastPacket() {
|
||||
ProtocolLibrary.getProtocolManager().broadcastServerPacket(getHandle());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package xyz.ineanto.nicko.wrapper;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.wrappers.Converters;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
|
||||
/**
|
||||
* Sent by the server to the client to remove one or more entities.
|
||||
*/
|
||||
public class WrapperPlayServerEntityDestroy extends AbstractPacket {
|
||||
|
||||
/**
|
||||
* The packet type that is wrapped by this wrapper.
|
||||
*/
|
||||
public static final PacketType TYPE = PacketType.Play.Server.ENTITY_DESTROY;
|
||||
|
||||
public WrapperPlayServerEntityDestroy() {
|
||||
super(new PacketContainer(TYPE), TYPE);
|
||||
handle.getModifier().writeDefaults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of entity ids to remove
|
||||
*
|
||||
* @param value New value for field 'entityIds'
|
||||
*/
|
||||
public void setEntityIds(IntList value) {
|
||||
this.handle.getModifier().withType(IntList.class, Converters.passthrough(IntList.class)).writeSafely(0, value);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
package xyz.ineanto.nicko.wrapper;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.InternalStructure;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers;
|
||||
import com.comphenix.protocol.wrappers.MinecraftKey;
|
||||
import com.google.common.hash.Hashing;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.World;
|
||||
|
||||
/**
|
||||
* PacketPlayServerRespawn Wrapper class (1.20.X to 1.21.X)
|
||||
* <p>
|
||||
* In 1.20.2, all the fields were merged inside a
|
||||
* single "CommonPlayerSpawnInfo" record.
|
||||
*
|
||||
* @author inenato (w/ additional help from lukalt), based on work from dmulloy2 and Kristian S. Strangeland
|
||||
*/
|
||||
public class WrapperPlayServerRespawn extends AbstractPacket {
|
||||
public static final PacketType TYPE = PacketType.Play.Server.RESPAWN;
|
||||
|
||||
private InternalStructure spawnInfoStructure = null;
|
||||
|
||||
public WrapperPlayServerRespawn() {
|
||||
super(new PacketContainer(TYPE), TYPE);
|
||||
handle.getModifier().writeDefaults();
|
||||
if (MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
|
||||
spawnInfoStructure = handle.getStructures().read(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void setDimension(World value) {
|
||||
final MinecraftVersion v1_20_5 = new MinecraftVersion(1, 20, 5);
|
||||
|
||||
if (!MinecraftVersion.getCurrentVersion().isAtLeast(v1_20_5)) {
|
||||
// 1.20 - 1.20.4
|
||||
final StructureModifier<InternalStructure> structureModifier = spawnInfoStructure == null ?
|
||||
handle.getStructures() : spawnInfoStructure.getStructures();
|
||||
|
||||
final StructureModifier<World> worldStructureModifier = spawnInfoStructure == null ?
|
||||
handle.getWorldKeys() : spawnInfoStructure.getWorldKeys();
|
||||
|
||||
final InternalStructure dimensionType = structureModifier.read(0);
|
||||
dimensionType.getMinecraftKeys().writeSafely(0, new MinecraftKey("minecraft", "dimension_type"));
|
||||
dimensionType.getMinecraftKeys().writeSafely(1, new MinecraftKey("minecraft", "overworld"));
|
||||
structureModifier.writeSafely(0, dimensionType);
|
||||
worldStructureModifier.writeSafely(0, value);
|
||||
} else {
|
||||
// 1.20.5 to 1.21.1
|
||||
|
||||
// why is life so hard?
|
||||
final Class<?> commonPlayerInfoClazz = MinecraftReflection.getMinecraftClass("network.protocol.game.CommonPlayerSpawnInfo");
|
||||
try {
|
||||
final Object commonSpawnData = Accessors.getFieldAccessor(TYPE.getPacketClass(), commonPlayerInfoClazz, true).getField()
|
||||
.get(this);
|
||||
final MinecraftKey key = MinecraftKey.fromHandle(
|
||||
Accessors.getFieldAccessor(
|
||||
commonPlayerInfoClazz,
|
||||
MinecraftReflection.getResourceKey(),
|
||||
true
|
||||
)
|
||||
.get(spawnInfoStructure));
|
||||
|
||||
Accessors.getFieldAccessor(
|
||||
commonPlayerInfoClazz,
|
||||
MinecraftReflection.getResourceKey(),
|
||||
true
|
||||
)
|
||||
.set(commonSpawnData, key);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setGameMode(GameMode value) {
|
||||
if (!MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
|
||||
// 1.20 to 1.20.1
|
||||
handle.getGameModes().writeSafely(0, EnumWrappers.NativeGameMode.fromBukkit(value));
|
||||
return;
|
||||
}
|
||||
|
||||
spawnInfoStructure.getGameModes().writeSafely(0, EnumWrappers.NativeGameMode.fromBukkit(value));
|
||||
}
|
||||
|
||||
public void setPreviousGameMode(GameMode value) {
|
||||
if (!MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
|
||||
// 1.20 to 1.20.1
|
||||
handle.getGameModes().writeSafely(1, EnumWrappers.NativeGameMode.fromBukkit(value));
|
||||
return;
|
||||
}
|
||||
|
||||
spawnInfoStructure.getGameModes().writeSafely(1, EnumWrappers.NativeGameMode.fromBukkit(value));
|
||||
}
|
||||
|
||||
public void setCopyMetadata(boolean value) {
|
||||
if (!MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) return;
|
||||
|
||||
// 1.20 to 1.20.1
|
||||
handle.getBooleans().writeSafely(0, value);
|
||||
}
|
||||
|
||||
public void setSeed(long value) {
|
||||
if (!MinecraftVersion.CONFIG_PHASE_PROTOCOL_UPDATE.atOrAbove()) {
|
||||
// 1.20 to 1.20.1
|
||||
handle.getLongs().writeSafely(0, Hashing.sha256().hashLong(value).asLong());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
package xyz.ineanto.nicko.wrapper;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* This packet is sent by the server when a player comes into visible range, not when a player joins.
|
||||
*/
|
||||
public class WrapperPlayServerSpawnEntity extends AbstractPacket {
|
||||
/**
|
||||
* The packet type that is wrapped by this wrapper.
|
||||
*/
|
||||
public static final PacketType TYPE = PacketType.Play.Server.SPAWN_ENTITY;
|
||||
|
||||
/**
|
||||
* Constructors a new wrapper for the specified packet
|
||||
*/
|
||||
public WrapperPlayServerSpawnEntity() {
|
||||
super(new PacketContainer(TYPE), TYPE);
|
||||
handle.getModifier().writeDefaults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entity id of the player
|
||||
*
|
||||
* @param value New value for field 'entityId'
|
||||
*/
|
||||
public void setEntityId(int value) {
|
||||
this.handle.getIntegers().writeSafely(0, value);
|
||||
this.handle.getEntityTypeModifier().writeSafely(0, EntityType.PLAYER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the unique id of the player
|
||||
*
|
||||
* @param value New value for field 'playerId'
|
||||
*/
|
||||
public void setPlayerId(UUID value) {
|
||||
this.handle.getUUIDs().writeSafely(0, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of field 'x'
|
||||
*
|
||||
* @param value New value for field 'x'
|
||||
*/
|
||||
public void setX(double value) {
|
||||
this.handle.getDoubles().writeSafely(0, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of field 'y'
|
||||
*
|
||||
* @param value New value for field 'y'
|
||||
*/
|
||||
public void setY(double value) {
|
||||
this.handle.getDoubles().writeSafely(1, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of field 'z'
|
||||
*
|
||||
* @param value New value for field 'z'
|
||||
*/
|
||||
public void setZ(double value) {
|
||||
this.handle.getDoubles().write(2, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the discrete rotation around the y-axis (yaw)
|
||||
*
|
||||
* @param value New value for field 'yRot'
|
||||
*/
|
||||
public void setYRotRaw(byte value) {
|
||||
this.handle.getBytes().writeSafely(0, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the discrete rotation around the x-axis (pitch)
|
||||
*
|
||||
* @param value New value for field 'xRot'
|
||||
*/
|
||||
public void setXRotRaw(byte value) {
|
||||
this.handle.getBytes().writeSafely(1, value);
|
||||
}
|
||||
|
||||
public void setLocation(@Nonnull Location location) {
|
||||
setX(location.getX());
|
||||
setY(location.getY());
|
||||
setZ(location.getZ());
|
||||
setYRotRaw(degreesToAngle(location.getYaw()));
|
||||
setXRotRaw(degreesToAngle(location.getPitch()));
|
||||
}
|
||||
|
||||
private byte degreesToAngle(float degree) {
|
||||
return (byte)((int)(degree * 256.0F / 360.0F));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package xyz.ineanto.nicko.wrapper;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.wrappers.EnumWrappers;
|
||||
import com.comphenix.protocol.wrappers.PlayerInfoData;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Up-to-date version of the Wrapper class
|
||||
* for the PlayerServerPlayerInfo.
|
||||
*
|
||||
* @author ineanto, based on work from dmulloy2 and Kristian S. Strangeland
|
||||
*/
|
||||
public class WrapperPlayerServerPlayerInfo extends AbstractPacket {
|
||||
public static final PacketType TYPE = PacketType.Play.Server.PLAYER_INFO;
|
||||
|
||||
public WrapperPlayerServerPlayerInfo() {
|
||||
super(new PacketContainer(TYPE), TYPE);
|
||||
handle.getModifier().writeDefaults();
|
||||
}
|
||||
|
||||
public void setActions(Set<EnumWrappers.PlayerInfoAction> value) {
|
||||
if (MinecraftVersion.FEATURE_PREVIEW_UPDATE.atOrAbove()) {
|
||||
handle.getPlayerInfoActions().writeSafely(0, value);
|
||||
} else {
|
||||
handle.getPlayerInfoAction().writeSafely(0, value.stream().iterator().next()); // Get the first Value.
|
||||
}
|
||||
}
|
||||
|
||||
public void setData(List<PlayerInfoData> value) {
|
||||
handle.getPlayerInfoDataLists().writeSafely(1, value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package xyz.ineanto.nicko.wrapper;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Up-to-date version of the Wrapper class
|
||||
* for the PlayerServerPlayerInfoRemove.
|
||||
*
|
||||
* @author ineanto, based on work from dmulloy2 and Kristian S. Strangeland
|
||||
*/
|
||||
|
||||
public class WrapperPlayerServerPlayerInfoRemove extends AbstractPacket {
|
||||
public static final PacketType TYPE = PacketType.Play.Server.PLAYER_INFO_REMOVE;
|
||||
|
||||
public WrapperPlayerServerPlayerInfoRemove() {
|
||||
super(new PacketContainer(TYPE), TYPE);
|
||||
handle.getModifier().writeDefaults();
|
||||
}
|
||||
|
||||
public void setUUIDs(List<UUID> value) {
|
||||
handle.getUUIDLists().writeSafely(0, value);
|
||||
}
|
||||
}
|
50
src/main/resources/config.yml
Normal file
50
src/main/resources/config.yml
Normal file
|
@ -0,0 +1,50 @@
|
|||
# Nicko ${version} - Config:
|
||||
|
||||
# Specifies the configuration version, don't change.
|
||||
version: "1.2.0"
|
||||
|
||||
# Nicko will copy the English locale as "lang.yml"
|
||||
# and will use the translations in that file when "Server Custom"
|
||||
# is selected as the player's locale.
|
||||
# Accepted values: false (Disabled), true (Enabled)
|
||||
customLocale: false
|
||||
|
||||
sql:
|
||||
# Indicates wherever the data will be stored locally
|
||||
# inside a .json file or in an SQL database.
|
||||
# Accepted values: false (Disabled), true (Enabled)
|
||||
enabled: false
|
||||
# Toggles between the MariaDB and MySQL drivers.
|
||||
# If you use the MySQL database engine, switch this to off.
|
||||
# Accepted values: false (Disabled), true (Enabled)
|
||||
mariadb: true
|
||||
# SQL database's address
|
||||
# Accepted values: valid IP address (e.g. localhost, 127.0.0.1)
|
||||
address: "localhost"
|
||||
# SQL database's port
|
||||
# Accepted values: valid integer (e.g. 3306, 25565)
|
||||
port: 3306
|
||||
# SQL database's username.
|
||||
# Accepted values: any string
|
||||
username: "username"
|
||||
# SQL database's password.
|
||||
# Accepted values: any string
|
||||
password: "password"
|
||||
|
||||
redis:
|
||||
# Indicates wherever the data will be stored through
|
||||
# Redis to transfer whenever a player switches server.
|
||||
# Accepted values: false (Disabled), true (Enabled)
|
||||
enabled: false
|
||||
# Redis server's address
|
||||
# Accepted values: valid IP address (e.g.: localhost, 127.0.0.1)
|
||||
address: "localhost"
|
||||
# Redis server's port
|
||||
# Accepted values: valid integer (e.g. 3306, 25565)
|
||||
port: 6379
|
||||
# Redis server's username
|
||||
# Accepted values: any string
|
||||
username: "username"
|
||||
# Redis server's password
|
||||
# Accepted values: any string
|
||||
password: "password"
|
153
src/main/resources/en.yml
Normal file
153
src/main/resources/en.yml
Normal file
|
@ -0,0 +1,153 @@
|
|||
# Nicko ${version} - Language File:
|
||||
|
||||
# Specifies the configuration version, don't change.
|
||||
version: "1.2.0"
|
||||
|
||||
prefix: "<b><gradient:#01a97c:#8ffd54>NICKO</gradient></b>"
|
||||
whoosh: "<b><gradient:#01a97c:#8ffd54>WHOOSH!</gradient></b>"
|
||||
oops: "<b><color:#ff4640>OOPS!</color></b>"
|
||||
|
||||
error:
|
||||
permission: "<gray>You're missing the permission to do that.</gray>"
|
||||
invalid_username: "<gray>This is an invalid Minecraft username.</gray>"
|
||||
mojang_name: "<gray>There's is not Minecraft account with this username.</gray>"
|
||||
mojang_skin: "<gray>This Minecraft account has no skin.</gray>"
|
||||
cache: "<gray>Unable to get data from the cache.</gray>"
|
||||
|
||||
event:
|
||||
settings:
|
||||
error: "<gray>Wasn''t able to update your settings! ({0})</gray>"
|
||||
appearance:
|
||||
set:
|
||||
error: "<gray>Wasn''t able to apply your disguise! ({0})</gray>"
|
||||
ok: "<gray>You're now disguised.</gray>"
|
||||
restore:
|
||||
error: "<gray>Wasn''t able to apply the previous disguise! ({0})</gray>"
|
||||
ok: "<gray>Disguise restored from last time.</gray>"
|
||||
remove:
|
||||
error: "<gray>Wasn''t able to remove your disguise!.</gray>"
|
||||
missing: "<gray>You''re not currently disguised.</gray>"
|
||||
ok: "<gray>Undisguised successfully.</gray>"
|
||||
admin:
|
||||
cache:
|
||||
invalidate_cache: "<gray>Cache purged.</gray>"
|
||||
invalidate_entry: "<gray>{0} was purged.</gray>"
|
||||
check:
|
||||
remove_skin: "<gray>Skin removed from player.</gray>"
|
||||
|
||||
gui:
|
||||
title:
|
||||
home: "Nicko - Home"
|
||||
settings: "Settings"
|
||||
admin: "Administration"
|
||||
check: "Player Management"
|
||||
confirm: "Are you sure?"
|
||||
cache: "Cache Management"
|
||||
invalidate_skin: "Purge cache..."
|
||||
|
||||
exit:
|
||||
name: "Exit"
|
||||
go_back:
|
||||
name: "Back"
|
||||
unavailable:
|
||||
name: "Unavailable"
|
||||
lore:
|
||||
- "<gray><i>This button is disabled.</i></gray>"
|
||||
error:
|
||||
name: "Error!"
|
||||
lore:
|
||||
- "<gray>The item failed to load, but it might still work.</gray>"
|
||||
loading:
|
||||
name: "<gray><i>Loading...</i></gray>"
|
||||
choice:
|
||||
confirm:
|
||||
name: "<green>Confirm</green>"
|
||||
choose:
|
||||
name: "<gold><i>Choose an option...</i></gold>"
|
||||
cancel:
|
||||
name: "<red>Cancel</red>"
|
||||
scroll_up:
|
||||
name: "Scroll up"
|
||||
lore:
|
||||
- "<dark_gray><i>(You can't scroll any higher.)</i></dark_gray>"
|
||||
scroll_down:
|
||||
name: "Scroll down"
|
||||
lore:
|
||||
- "<dark_gray><i>(You can't scroll any further down.)</i></dark_gray>"
|
||||
new_skin:
|
||||
name: "New skin..."
|
||||
new_name:
|
||||
name: "New name..."
|
||||
home:
|
||||
admin:
|
||||
name: "Administration panel"
|
||||
lore:
|
||||
- "<gray>Configure and manage Nicko.</gray>"
|
||||
settings:
|
||||
name: "Settings"
|
||||
lore:
|
||||
- "<gray>Fine tune your experience with Nicko.</gray>"
|
||||
change_name:
|
||||
name: "Change your <gold>nickname</gold>"
|
||||
change_skin:
|
||||
name: "Change your <gold>skin</gold>"
|
||||
change_both:
|
||||
name: "Change <gold>both</gold>"
|
||||
random_skin:
|
||||
name: "<rainbow>Get a random appearance!</rainbow>"
|
||||
reset:
|
||||
name: "Reset appearance"
|
||||
lore:
|
||||
- "<gray>Completely remove your disguise.</gray>"
|
||||
admin:
|
||||
manage_cache:
|
||||
name: "Manage the <gold>skin</gold> cache..."
|
||||
lore:
|
||||
- "<gray>View and manage the skin cache.</gray>"
|
||||
manage_player:
|
||||
name: "Inspect a player..."
|
||||
lore:
|
||||
- "<gray>See players' disguise information.</gray>"
|
||||
check:
|
||||
name: "<gold>{0}</gold>"
|
||||
lore:
|
||||
- "<red>Nicked:</red> {1}"
|
||||
- "<red>Name:</red> <gold>{2}</gold>"
|
||||
- "<red>Skin:</red> <gold>{3}</gold>"
|
||||
- " "
|
||||
- "<gray><i>Click to remove skin!</i></gray>"
|
||||
cache:
|
||||
statistics:
|
||||
name: "Statistics"
|
||||
lore:
|
||||
- "Request count: <aqua>{0}</aqua>"
|
||||
- "Number of skin cached: <aqua>{1}</aqua>"
|
||||
- "<dark_gray><i>Cache is cleared every 24 hours.</i></dark_gray>"
|
||||
invalidate_cache:
|
||||
name: "Invalidate cache"
|
||||
lore:
|
||||
- "<red><i>NOT RECOMMENDED</i></red>"
|
||||
- "<gray>Invalidate the entirety of the skin cache.</gray>"
|
||||
- "<gray>This doesn't reset player's disguises.</gray>"
|
||||
invalidate_skin:
|
||||
name: "Invalidate a skin..."
|
||||
lore:
|
||||
- "<gray>Select a specific skin to invalidate.</gray>"
|
||||
- "<gray>Useful if a skin has been recently updated.</gray>"
|
||||
entry:
|
||||
name: "<gold>{0}</gold>"
|
||||
lore:
|
||||
- "<gray>Click to invalidate...</gray>"
|
||||
settings:
|
||||
toggleable_button:
|
||||
lore:
|
||||
- "{0} Disabled"
|
||||
- "{1} Enabled"
|
||||
cycling_choices:
|
||||
lore:
|
||||
- "<gray><i>Cycle through the values</i></gray>"
|
||||
- "<gray><i>by left or right clicking.</i></gray>"
|
||||
language:
|
||||
name: "Language"
|
||||
random_skin:
|
||||
name: "Random skin on login"
|
155
src/main/resources/fr.yml
Normal file
155
src/main/resources/fr.yml
Normal file
|
@ -0,0 +1,155 @@
|
|||
# Nicko ${version} - Fichier de langue:
|
||||
|
||||
# Précise la version de la configuration, ne pas changer.
|
||||
version: "1.2.0"
|
||||
|
||||
prefix: "<b><gradient:#01a97c:#8ffd54>NICKO</gradient></b>"
|
||||
whoosh: "<b><gradient:#01a97c:#8ffd54>WHOOSH!</gradient></b>"
|
||||
oops: "<b><color:#ff4640>OOPS!</color></b>"
|
||||
|
||||
error:
|
||||
permission: "<gray>Vous n'avez pas la permission de faire cela.<gray>"
|
||||
invalid_username: "<gray>Nom d'utilisateur Minecraft invalide.<gray>"
|
||||
mojang_name: "<gray>Aucun compte Minecraft associé à ce nom d'utilisateur.<gray>"
|
||||
mojang_skin: "<gray>Ce compte Minecraft n'a pas de skin.<gray>"
|
||||
cache: "<gray>Impossible de récupérer les données depuis le cache.<gray>"
|
||||
|
||||
event:
|
||||
settings:
|
||||
error: "<gray>Impossible de mettre à jour vos paramètres ! ({0})</gray>"
|
||||
appearance:
|
||||
set:
|
||||
error: "<gray>Impossible d''appliquer votre déguisement ! ({0})</gray>"
|
||||
ok: "<gray>Déguisement appliqué avec succès.</gray>"
|
||||
restore:
|
||||
error: "<gray>Impossible d''appliquer le précédent déguisement ! ({0})</gray>"
|
||||
ok: "<gray>Votre précédent déguisement a été appliqué.<gray>"
|
||||
remove:
|
||||
error: "<gray>Impossible de retirer votre déguisement.</gray>"
|
||||
missing: "<gray>Vous n''avez pas de déguisement.</gray>"
|
||||
ok: "<gray>Déguisement retiré.</gray>"
|
||||
admin:
|
||||
cache:
|
||||
invalidate_cache: "<gray>Cache complet invalidé.</gray>"
|
||||
invalidate_entry: "<gray>{0} a été invalidé.</gray>"
|
||||
check:
|
||||
remove_skin: "<gray>Déguisement retiré au joueur.</gray>"
|
||||
|
||||
gui:
|
||||
title:
|
||||
home: "Nicko - Accueil"
|
||||
settings: "Paramètres"
|
||||
admin: "Administration"
|
||||
check: "Gestion des Joueurs"
|
||||
confirm: "Êtes-vous sûr ?"
|
||||
cache: "Gestion du Cache"
|
||||
invalidate_skin: "Purge du cache..."
|
||||
|
||||
exit:
|
||||
name: "Quitter"
|
||||
go_back:
|
||||
name: "Retour"
|
||||
unavailable:
|
||||
name: "Indisponible"
|
||||
lore:
|
||||
- "<gray><i>Ce boutton est désactivé.</i></gray>"
|
||||
error:
|
||||
name: "Erreur !"
|
||||
lore:
|
||||
- "<gray>La texture de l'objet n'a pas chargé</gray>"
|
||||
- "<gray>correctement mais il fonctionne encore.</gray>"
|
||||
loading:
|
||||
name: "<gray><i>Chargement...</i></gray>"
|
||||
choice:
|
||||
confirm:
|
||||
name: "<green>Confirmer</green>"
|
||||
choose:
|
||||
name: "<gold><i>Choisissez une option...</i></gold>"
|
||||
cancel:
|
||||
name: "<red>Annuler</red>"
|
||||
scroll_up:
|
||||
name: "Défiler vers le haut"
|
||||
lore:
|
||||
- "<dark_gray><i>(Impossible de défiler plus haut.)</i></dark_gray>"
|
||||
scroll_down:
|
||||
name: "Défiler vers le bas"
|
||||
lore:
|
||||
- "<dark_gray><i>(Impossible de défiler plus bas.)</i></dark_gray>"
|
||||
new_skin:
|
||||
name: "Nouveau skin..."
|
||||
new_name:
|
||||
name: "Nouveau nom..."
|
||||
home:
|
||||
admin:
|
||||
name: "Panel d'administration"
|
||||
lore:
|
||||
- "<gray>Configurez et gérez Nicko.</gray>"
|
||||
settings:
|
||||
name: "Paramètres"
|
||||
lore:
|
||||
- "<gray>Gérez votre expérience avec Nicko.</gray>"
|
||||
change_name:
|
||||
name: "Changer le <gold>pseudo</gold>"
|
||||
change_skin:
|
||||
name: "Changer le <gold>skin</gold>"
|
||||
change_both:
|
||||
name: "Changer les <gold>deux</gold>"
|
||||
random_skin:
|
||||
name: "<rainbow>Obtenir une apparence aléatoire !</rainbow>"
|
||||
reset:
|
||||
name: "Réinitialiser l'apparence"
|
||||
lore:
|
||||
- "<gray>Supprime complètement votre déguisement.</gray>"
|
||||
admin:
|
||||
manage_cache:
|
||||
name: "Gérer le cache de <gold>skin...</gold>"
|
||||
lore:
|
||||
- "<gray>Consultez et gérez le cache de skin.</gray>"
|
||||
manage_player:
|
||||
name: "Vérifier un joueur..."
|
||||
lore:
|
||||
- "<gray>Vérifiez les informations de déguisement d'un joueur.</gray>"
|
||||
check:
|
||||
name: "<gold>{0}</gold>"
|
||||
lore:
|
||||
- "<red>Déguisé:</red> {1}"
|
||||
- "<red>Nom:</red> <gold>{2}</gold>"
|
||||
- "<red>Skin:</red> <gold>{3}</gold>"
|
||||
- " "
|
||||
- "<gray><i>Cliquez pour retirer le skin !</i></gray>"
|
||||
cache:
|
||||
statistics:
|
||||
name: "Statistiques"
|
||||
lore:
|
||||
- "Nombre de requêtes: <aqua>{0}</aqua>"
|
||||
- "Nb. de skin dans le cache: <aqua>{1}</aqua>"
|
||||
- "<dark_gray><i>Le cache est vidé toutes les 24 heures.</i></dark_gray>"
|
||||
invalidate_cache:
|
||||
name: "Purger le cache"
|
||||
lore:
|
||||
- "<red><i>DÉCONSEILLÉ</i></red>"
|
||||
- "<gray>Purge l'entièreté du cache des skin.</gray>"
|
||||
- "<gray>Ne retire pas les déguisements des joueurs.</gray>"
|
||||
invalidate_skin:
|
||||
name: "Invalider un skin..."
|
||||
lore:
|
||||
- "<gray>Sélectionnez une apparence spécifique à</gray>"
|
||||
- "<gray>invalider. Utile dans le cas où un skin</gray>"
|
||||
- "<gray>a récemment été mis à jour.</gray>"
|
||||
entry:
|
||||
name: "<gold>{0}</gold>"
|
||||
lore:
|
||||
- "<gray>Cliquez pour invalider...</gray>"
|
||||
settings:
|
||||
toggleable_button:
|
||||
lore:
|
||||
- "{0} Désactivé"
|
||||
- "{1} Activé"
|
||||
cycling_choices:
|
||||
lore:
|
||||
- "<gray><i>Parcourez les valeurs</i></gray>"
|
||||
- "<gray><i>avec un clique gauche/droit.</i></gray>"
|
||||
language:
|
||||
name: "Langage"
|
||||
random_skin:
|
||||
name: "Apparence aléatoire à la connexion"
|
490
src/main/resources/names.txt
Normal file
490
src/main/resources/names.txt
Normal file
|
@ -0,0 +1,490 @@
|
|||
w4nderlost
|
||||
TooParanoids
|
||||
Der_OG_31er
|
||||
9xxDaRkShAdOwxx9
|
||||
giiiaan_
|
||||
Jqstinnn
|
||||
Tillysboy92
|
||||
AlwaysCello
|
||||
SyndrexG0D
|
||||
Peypeycake
|
||||
ThePerjurer
|
||||
Tioe
|
||||
Elternbaum
|
||||
BarkersRover_16
|
||||
pebsso
|
||||
cyrus6950
|
||||
Bigest_guy
|
||||
RV0REU
|
||||
R379
|
||||
Shetell
|
||||
_HEAPASS_
|
||||
Iamaloner21
|
||||
TheFardoxGamerHD
|
||||
Flyboi43
|
||||
Cha0smusik
|
||||
kat00
|
||||
Infreat
|
||||
Crummymoofin
|
||||
MijnVriend
|
||||
momsrightkidney
|
||||
dmacrado
|
||||
Elephantman321
|
||||
ii_hamoudi_YT
|
||||
FaurePavane
|
||||
ambiezzz
|
||||
XD_Bandit695_XD
|
||||
Nabingo
|
||||
Cyl0re
|
||||
ku5
|
||||
SrAragon
|
||||
StarlightDream9
|
||||
CJ5370
|
||||
rainbees
|
||||
KeroTheWolf
|
||||
Andrews9722
|
||||
cursed_Assyrian
|
||||
yamateni
|
||||
ProgramEXE
|
||||
exprso
|
||||
harrypanda
|
||||
LookerMD
|
||||
migykins
|
||||
Wintrous
|
||||
ZzGaBi
|
||||
Flayber
|
||||
Grenixal
|
||||
maeve_wells
|
||||
Creeper10fr
|
||||
10Chairs
|
||||
2525lock
|
||||
Shqipe
|
||||
XenitsuZen
|
||||
Berno17_
|
||||
wolle1313
|
||||
HalfDogHalfCat1
|
||||
NachoGarcia
|
||||
popsicoal
|
||||
NemesiSevil2006
|
||||
AnywayOj
|
||||
Tanko12345
|
||||
Samdweck
|
||||
LYRECODE123
|
||||
Resulten
|
||||
SirBastii
|
||||
Maku056
|
||||
ItzArnizzz
|
||||
Brsh3620
|
||||
Masonita
|
||||
kapplanium
|
||||
shoezo
|
||||
Mansur203
|
||||
Waterboy15217
|
||||
redragonne
|
||||
ghko325
|
||||
HopePVP_tw7
|
||||
xtka
|
||||
NimwenxZ
|
||||
Hiro0408
|
||||
PanderaWz
|
||||
Shesu
|
||||
_Aniste_NY_
|
||||
Besceste
|
||||
3ee3
|
||||
ArcticGalaxy123
|
||||
snooze_mingo
|
||||
LizzyLomnh
|
||||
FaZeChulupa
|
||||
LineZeeK
|
||||
liabilaty
|
||||
BlackSheep1610
|
||||
Simif69
|
||||
Aficionado
|
||||
riekin
|
||||
XLuggas
|
||||
MathExams
|
||||
6fq
|
||||
Marveel
|
||||
lolme51
|
||||
TaioSayUwU
|
||||
Fonklift
|
||||
blvw
|
||||
Po1204
|
||||
Pierre_Rabbit
|
||||
mifimasters
|
||||
MrRidge1
|
||||
arnqen
|
||||
Nick_cage102
|
||||
Geo_9918
|
||||
SSShudder
|
||||
Nicolas_Mom
|
||||
WolfMatrix101
|
||||
frictionless_
|
||||
Gughik
|
||||
gold_dragon_4
|
||||
nealxero
|
||||
ClonedPickle
|
||||
SourWatermelonxX
|
||||
devilunion
|
||||
Daryifuny
|
||||
joaomarcos11
|
||||
Dekeef
|
||||
PadfootTheDog
|
||||
DarkScopez80
|
||||
flowers220
|
||||
Gaiiya
|
||||
The_Yeet_
|
||||
juuuuwu
|
||||
MrMafioz
|
||||
Surpasses
|
||||
TypicalOwen
|
||||
0lober
|
||||
Zerfixy
|
||||
Sunny3803b
|
||||
neostanley
|
||||
Creeper_Kart
|
||||
minestin
|
||||
Goldenfredster
|
||||
Vju
|
||||
MrArchI_YT
|
||||
Casper1709
|
||||
Backiii_
|
||||
DrCreate
|
||||
Nova_Lux1
|
||||
Jayvin_Blanco
|
||||
ShadowMan1770
|
||||
0KOPO40K
|
||||
Silk_Altermann69
|
||||
Alu____
|
||||
Honey_MilkExe112
|
||||
T0XEI
|
||||
CB_13
|
||||
Paragorn
|
||||
HaGiang
|
||||
Shivendra8i
|
||||
Mayflower47
|
||||
Not_Someguy
|
||||
raxx111bg
|
||||
IceyGlaceon
|
||||
grasseffect
|
||||
PoopTNTpvp
|
||||
codecyber
|
||||
Shrumpkin13
|
||||
Shqdown
|
||||
Cmartin82
|
||||
KTCXD_5p0tty
|
||||
rockesalt
|
||||
goldjeong
|
||||
TheRealAKD
|
||||
NamiSwaaan
|
||||
yaaratol
|
||||
Dikiy_Flexer
|
||||
PoLecker24
|
||||
_SakuraTree
|
||||
PikkOgP0rno
|
||||
HawksFang
|
||||
BlueWinterWolf
|
||||
hskmerk
|
||||
gurmaw
|
||||
Lunggor
|
||||
clashfield
|
||||
Zelaste
|
||||
ACommonMouse
|
||||
TJ_Mystery
|
||||
Dizzy3312
|
||||
Raindropsss
|
||||
minecraftxiaoju
|
||||
sachilovebbh
|
||||
Celina_LaZyCxt
|
||||
firered6
|
||||
55000000
|
||||
Illunarnati
|
||||
Jedidiah2003
|
||||
setomz1
|
||||
basically_e
|
||||
TommyGreif02
|
||||
Bongrip42069
|
||||
Coco_Keopi
|
||||
Lt_Colt
|
||||
Kuurotta
|
||||
GqmeKnight
|
||||
WinCo_Foods
|
||||
FKDLZ
|
||||
IanPumpkin
|
||||
tastywtf
|
||||
natedawg0
|
||||
ZQLFenyx
|
||||
GamerMaster110
|
||||
papajobi9
|
||||
Yucaroon
|
||||
Xion_69
|
||||
AirJaw
|
||||
funfun321234
|
||||
khalleesii
|
||||
Pozisch
|
||||
thorso15
|
||||
kyumisoup
|
||||
Leonqrd
|
||||
BmwDreamer
|
||||
TehRos
|
||||
pitplayer69
|
||||
_ve0
|
||||
Miss_Yuka
|
||||
I_am_a_Bucket
|
||||
nicolas_bean
|
||||
xHorizonGC
|
||||
RTX3060_
|
||||
Borec188
|
||||
c8to
|
||||
megan3groCENG
|
||||
ventriloquize
|
||||
galczin
|
||||
Scorch3dEarth3d
|
||||
nightstarLP
|
||||
VittyGam3r
|
||||
GLaDOS__
|
||||
hydrelo
|
||||
JustACarter
|
||||
MikeDropperPlay
|
||||
NorthernWest
|
||||
_Skelesam_
|
||||
ZX_Style
|
||||
Tamas_Boi
|
||||
taylub
|
||||
VyacheslavO_O
|
||||
trippyaubs
|
||||
udtpic
|
||||
Lunarglow
|
||||
Stoolman
|
||||
legendary_meow
|
||||
Loganii
|
||||
CaptainStain56
|
||||
FoofieGeto
|
||||
Judgeavapl
|
||||
OneBigDigger
|
||||
Sorem13
|
||||
Raisonneur
|
||||
IlkoalI
|
||||
Naxa
|
||||
craigbabyonebay
|
||||
NicholasG04
|
||||
UtopianCrisi
|
||||
CalamarPasGenti
|
||||
ryluh_
|
||||
Aceslimz
|
||||
Howardygh99927
|
||||
brandon257
|
||||
MarcoswildHD
|
||||
x_XSkylerX_x
|
||||
Bronco09
|
||||
That_Kookie_
|
||||
Danigtz
|
||||
Ricky_lol
|
||||
999keyt
|
||||
Thilow15
|
||||
Difesito
|
||||
ostehovl
|
||||
isacano_12
|
||||
big_esra
|
||||
secretbaguette
|
||||
MrCommunism
|
||||
Jekube
|
||||
GrandeMaster
|
||||
DrGrip
|
||||
TheArnek
|
||||
JacsMars
|
||||
Lliam14
|
||||
MrGameandWatch84
|
||||
Rinzap
|
||||
XrazzeD
|
||||
ukknown
|
||||
ZohanPrent
|
||||
Naspo
|
||||
Rajem
|
||||
VepiGHG
|
||||
matoureal
|
||||
BrianChen87
|
||||
Jrocky
|
||||
stivo999
|
||||
Des_cole
|
||||
ReqGames
|
||||
Kingja1912
|
||||
issssyy
|
||||
Apache424
|
||||
Nick_Zockt_
|
||||
Mr_Haider_10
|
||||
ValentineBun
|
||||
fedorPro228
|
||||
xTilz
|
||||
blockbuster02k
|
||||
4ck
|
||||
FrostedTree
|
||||
VegasTortoise
|
||||
ZBellaV12121
|
||||
paypales
|
||||
qDread
|
||||
itsRiven
|
||||
i8oreo
|
||||
_kimcream_
|
||||
Phisuss
|
||||
Oscargray
|
||||
Elsiff
|
||||
callofdutydog00
|
||||
BruhTheMoment
|
||||
Pazmaa
|
||||
MythicalOak
|
||||
komuchi33
|
||||
awf4
|
||||
Jacobsaurus21
|
||||
itsjohannaa
|
||||
Jello12
|
||||
Adrien183
|
||||
jajazzywazzy
|
||||
Jorjie22
|
||||
SuperBrawlr5788
|
||||
KaraageV
|
||||
_Hanime_tv
|
||||
Padilhao
|
||||
Tikkas
|
||||
ordinaryducky
|
||||
Mothytix95
|
||||
renopotouwu
|
||||
1000voices
|
||||
niclas05
|
||||
Felipstein
|
||||
DoutorBauer
|
||||
FireballPlaysMC
|
||||
vapelordsheep
|
||||
Aboain
|
||||
ImGrexy
|
||||
Aivokolo
|
||||
SuperAmazing101
|
||||
va75a77a
|
||||
_DawYans_
|
||||
AceT1223
|
||||
Livvyboo7
|
||||
Saaaaaaaaaaaaans
|
||||
okaychill
|
||||
AwesomeBro1122
|
||||
absolutesnek
|
||||
jogie5000
|
||||
curtainSenpai
|
||||
gabbyisaloser
|
||||
gamergurl6969
|
||||
ZeusDk
|
||||
FranaRibas
|
||||
Discoboss
|
||||
SYZ_1
|
||||
Nakoe
|
||||
FIU_Captive
|
||||
xSiFan_
|
||||
ilyDeathxz
|
||||
da_fipsi
|
||||
Lochy
|
||||
The_Lavie37
|
||||
Tonion
|
||||
vnvrchy
|
||||
xX0meg4Xx
|
||||
haohxtheone
|
||||
VtTronic
|
||||
xDaniGum
|
||||
tikkelill
|
||||
DatYoshi
|
||||
eyehamstewpit
|
||||
nicholas460_
|
||||
Memsly445
|
||||
nugunugu
|
||||
AndreSlayz
|
||||
jashik1
|
||||
Qweenoftheocean
|
||||
Coltable
|
||||
treblaclef
|
||||
Kisaxx
|
||||
69Dxddy69
|
||||
RaulCuh
|
||||
3Wheat
|
||||
_OscarTheGrouch
|
||||
oIsqk
|
||||
Blockbusterweng
|
||||
AntoineDegauss
|
||||
ValeIsTheBest
|
||||
SwaggyCrabby
|
||||
DieOfSanity
|
||||
SirenMC
|
||||
Jade_Jewel
|
||||
Tropic44
|
||||
666splendor
|
||||
TallnessTallness
|
||||
breadgang9827
|
||||
Muffin_Worlds
|
||||
DedicatedVeggie
|
||||
Gonzalox_7
|
||||
datrandomasian
|
||||
Chasemon01
|
||||
Nyavix
|
||||
Lonely_Summers
|
||||
_RoveN
|
||||
ok_kyro
|
||||
LN_hunter
|
||||
saharsabz
|
||||
Roselilianna
|
||||
Gadx
|
||||
xtytan
|
||||
RoRo_levosgien88
|
||||
Bowsesqe_21
|
||||
Bennett528
|
||||
TheShipSailsWest
|
||||
KaiserGaming
|
||||
Layna_Shinozaki
|
||||
OP_greatly
|
||||
D3rpTaco
|
||||
Loufink
|
||||
Jorlmungus
|
||||
Snichol1801
|
||||
Ludixeo
|
||||
Imoeto
|
||||
MarshallNebunu
|
||||
crazycrystals
|
||||
Parapatus
|
||||
HahaDani
|
||||
MrQuaring
|
||||
DonTurnt
|
||||
SailorRoberts101
|
||||
FluffieBear
|
||||
TripleThick
|
||||
KingSparta
|
||||
MummysHome
|
||||
Cooga3
|
||||
Technosista
|
||||
Youmerstudios
|
||||
SkyyRaine
|
||||
criss102
|
||||
mrfailt
|
||||
CraftingBasic
|
||||
qnxkdifh
|
||||
Igorex2k20
|
||||
LaLisette
|
||||
ReBoredGamer
|
||||
warlordwest
|
||||
ExoTemporal
|
||||
KingLonmc
|
||||
666Horus
|
||||
IslandCity2
|
||||
TheBigSavage1
|
||||
Trishke2003
|
||||
skyrowin
|
||||
Krissy3D
|
||||
AntonWTobias
|
||||
SaddyWasTaken
|
||||
Ahoy_Peko_Hao_Yo
|
||||
T4nTr1Ss
|
||||
aleciolike
|
||||
ninja_shenley
|
||||
Lordmord1337
|
||||
eatmypoopfather
|
||||
Ktanner
|
||||
The42OWeedGOD
|
||||
CooperBee
|
||||
_MikuMiku_
|
||||
althume
|
||||
Tr3bba93
|
19
src/main/resources/plugin.yml
Normal file
19
src/main/resources/plugin.yml
Normal file
|
@ -0,0 +1,19 @@
|
|||
name: Nicko
|
||||
main: xyz.ineanto.nicko.Nicko
|
||||
version: ${version}
|
||||
author: Ineanto
|
||||
description: "The feature packed, next generation disguise plugin for Minecraft."
|
||||
api-version: 1.20
|
||||
softdepend: [ PlaceholderAPI ]
|
||||
load: POSTWORLD
|
||||
commands:
|
||||
nicko:
|
||||
description: "Opens Nicko's GUI."
|
||||
permission: nicko.use
|
||||
permissions:
|
||||
nicko.*:
|
||||
default: op
|
||||
children:
|
||||
- nicko.use
|
||||
nicko.use:
|
||||
default: false
|
Loading…
Add table
Add a link
Reference in a new issue