finish config editor

This commit is contained in:
jascotty2 2019-09-01 10:02:28 -05:00
parent 0008f54a9e
commit 927aee51bf
18 changed files with 487 additions and 160 deletions

View File

@ -45,7 +45,8 @@ public abstract class SongodaPlugin extends JavaPlugin {
@Override
public ConfigFileConfigurationAdapter getConfig() {
return config.getConfig();
System.out.println("Plugin config adapter!");
return config.getFileConfig();
}
@Override

View File

@ -1,7 +1,10 @@
package com.songoda.core.compatibility;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
@ -1120,7 +1123,8 @@ public enum LegacyMaterials {
* @return
*/
public boolean usesCompatibility() {
return compatibleMaterial != null && ServerVersion.isServerVersionBelow(compatibleMaterial.versionLessThan);
return compatibleMaterial != null && material == compatibleMaterial.material;
//return compatibleMaterial != null && ServerVersion.isServerVersionBelow(compatibleMaterial.versionLessThan);
}
/**
@ -1179,6 +1183,20 @@ public enum LegacyMaterials {
return m != null ? m : lookupMap.get(key + item.getDurability());
}
static LinkedHashSet<LegacyMaterials> all = null;
public static Set<LegacyMaterials> getAllValidItemMaterials() {
if (all == null) {
all = new LinkedHashSet();
for (LegacyMaterials mat : values()) {
if (mat.isValidItem() && !mat.usesCompatibility()) {
all.add(mat);
}
}
}
return Collections.unmodifiableSet(all);
}
/**
* Lookup a Legacy Material by its modern id name and return its associated
* Item. <br />

View File

@ -27,8 +27,10 @@ public class Comment {
}
public Comment(List<String> lines) {
if (lines != null) {
this.lines.addAll(lines);
}
}
public Comment(CommentStyle commentStyle, String... lines) {
this.commentStyle = commentStyle;
@ -37,8 +39,10 @@ public class Comment {
public Comment(CommentStyle commentStyle, List<String> lines) {
this.commentStyle = commentStyle;
if (lines != null) {
this.lines.addAll(lines);
}
}
public CommentStyle getCommentStyle() {
return commentStyle;

View File

@ -1,6 +1,7 @@
package com.songoda.core.configuration;
import com.google.common.base.Charsets;
import com.songoda.core.utils.TextUtils;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
@ -14,6 +15,8 @@ import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
@ -130,7 +133,7 @@ public class Config extends ConfigSection {
fileName = file;
}
public ConfigFileConfigurationAdapter getConfig() {
public ConfigFileConfigurationAdapter getFileConfig() {
return config;
}
@ -317,44 +320,18 @@ public class Config extends ConfigSection {
}
public boolean load() {
if (getFile().exists()) {
FileInputStream stream = null;
try {
stream = new FileInputStream(getFile());
this.load(new InputStreamReader((InputStream) stream, Charsets.UTF_16));
return true;
} catch (IOException | InvalidConfigurationException ex) {
(plugin != null ? plugin.getLogger() : Bukkit.getLogger()).log(Level.SEVERE, "Failed to load config file: " + file.getName(), ex);
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException ex) {
}
}
}
return false;
}
return true;
return load(getFile());
}
public boolean load(@NotNull File file) {
Validate.notNull(file, "File cannot be null");
if (file.exists()) {
FileInputStream stream = null;
try {
stream = new FileInputStream(file);
this.load(new InputStreamReader((InputStream) stream, Charsets.UTF_16));
try (BufferedInputStream stream = new BufferedInputStream(new FileInputStream(file))) {
Charset charset = TextUtils.detectCharset(stream, StandardCharsets.UTF_8);
this.load(new InputStreamReader(stream, charset));
return true;
} catch (IOException | InvalidConfigurationException ex) {
(plugin != null ? plugin.getLogger() : Bukkit.getLogger()).log(Level.SEVERE, "Failed to load config file: " + file.getName(), ex);
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException ex) {
}
}
}
return false;
}
@ -363,9 +340,15 @@ public class Config extends ConfigSection {
public void load(@NotNull Reader reader) throws IOException, InvalidConfigurationException {
StringBuilder builder = new StringBuilder();
try (BufferedReader input = reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader)) {
String line;
boolean firstLine = true;
while ((line = input.readLine()) != null) {
if(firstLine) {
line = line.replaceAll("[\uFEFF\uFFFE\u200B]", ""); // clear BOM markers
firstLine = false;
}
builder.append(line).append('\n');
}
}
@ -410,8 +393,8 @@ public class Config extends ConfigSection {
public void deleteNonDefaultSettings() {
// Delete old config values (thread-safe)
List<String> defaultKeys = Arrays.asList((String[]) defaults.keySet().toArray());
for(String key : (String[]) values.keySet().toArray()) {
List<String> defaultKeys = Arrays.asList(defaults.keySet().toArray(new String[0]));
for(String key : values.keySet().toArray(new String[0])) {
if(!defaultKeys.contains(key)) {
values.remove(key);
}
@ -470,7 +453,7 @@ public class Config extends ConfigSection {
file.getParentFile().mkdirs();
}
String data = this.saveToString();
try (OutputStreamWriter writer = new OutputStreamWriter((OutputStream) new FileOutputStream(file), Charsets.UTF_16);) {
try (OutputStreamWriter writer = new OutputStreamWriter((OutputStream) new FileOutputStream(file), StandardCharsets.UTF_16);) {
writer.write(data);
} catch (IOException e) {
return false;

View File

@ -1,6 +1,10 @@
package com.songoda.core.configuration;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.FileConfiguration;
@ -13,7 +17,7 @@ public class ConfigFileConfigurationAdapter extends FileConfiguration {
this.config = config;
}
public Config getConfig() {
public Config getCoreConfig() {
return config;
}
@ -36,4 +40,80 @@ public class ConfigFileConfigurationAdapter extends FileConfiguration {
public ConfigOptionsAdapter options() {
return new ConfigOptionsAdapter(config);
}
@Override
public Set<String> getKeys(boolean deep) {
return config.getKeys(deep);
}
@Override
public Map<String, Object> getValues(boolean deep) {
return config.getValues(deep);
}
@Override
public boolean contains(String path) {
return config.contains(path);
}
@Override
public boolean isSet(String path) {
return config.isSet(path);
}
@Override
public String getCurrentPath() {
return config.getCurrentPath();
}
@Override
public String getName() {
return config.getName();
}
@Override
public Configuration getRoot() {
return config;
}
@Override
public ConfigurationSection getParent() {
return null;
}
@Override
public void addDefault(String path, Object value) {
config.addDefault(path, value);
}
@Override
public ConfigurationSection getDefaultSection() {
return config.getDefaultSection();
}
@Override
public void set(String path, Object value) {
config.set(path, value);
}
@Override
public Object get(String path) {
return config.get(path);
}
@Override
public Object get(String path, Object def) {
return config.get(path, def);
}
@Override
public ConfigurationSection createSection(String path) {
return config.createSection(path);
}
@Override
public ConfigurationSection createSection(String path, Map<?, ?> map) {
return config.createSection(path, map);
}
}

View File

@ -121,6 +121,17 @@ public class ConfigSection extends MemoryConfiguration {
ConfigSection section = new ConfigSection(root, this, path, true);
synchronized (root.lock) {
root.defaults.put(fullPath + path, section);
root.defaultComments.put(fullPath + path, new Comment(comment));
}
return section;
}
@NotNull
public ConfigSection createDefaultSection(@NotNull String path, ConfigFormattingRules.CommentStyle commentStyle, String... comment) {
ConfigSection section = new ConfigSection(root, this, path, true);
synchronized (root.lock) {
root.defaults.put(fullPath + path, section);
root.defaultComments.put(fullPath + path, new Comment(commentStyle, comment));
}
return section;
}
@ -188,12 +199,24 @@ public class ConfigSection extends MemoryConfiguration {
@Override
public void addDefault(@NotNull String path, @Nullable Object value) {
synchronized (root.lock) {
// if any intermediate nodes don't exist, create them
String[] pathParts = path.split(Pattern.quote(String.valueOf(root.pathChar)));
String nodePath = "";
for (int i = 0; i < pathParts.length - 1; ++i) {
nodePath += (nodePath.isEmpty() ? pathParts[i] : root.pathChar + pathParts[i]);
if (!(root.defaults.get(nodePath) instanceof ConfigSection)) {
root.defaults.put(nodePath, new ConfigSection(root, this, nodePath, true));
}
}
root.defaults.put(fullPath + path, value);
}
}
@Override
public void addDefaults(@NotNull Map<String, Object> defaults) {
defaults.entrySet().stream().forEach(m -> root.defaults.put(fullPath + m.getKey(), m.getValue()));
//defaults.entrySet().stream().forEach(m -> root.defaults.put(fullPath + m.getKey(), m.getValue()));
defaults.entrySet().stream().forEach(m -> addDefault(m.getKey(), m.getValue()));
}
@Override

View File

@ -10,15 +10,24 @@ import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import org.bukkit.ChatColor;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.MemoryConfiguration;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.plugin.java.JavaPlugin;
/**
* Edit a configuration file for a specific plugin
*
* @since 2019-08-31
* @author jascotty2
*/
public class ConfigEditorGui extends SimplePagedGui {
final JavaPlugin plugin;
@ -53,7 +62,7 @@ public class ConfigEditorGui extends SimplePagedGui {
this.setUseHeader(true);
headerBackItem = footerBackItem = GuiUtils.getBorderItem(LegacyMaterials.GRAY_STAINED_GLASS_PANE.getItem());
final String path = node.getCurrentPath();
this.setItem(4, configItem(LegacyMaterials.FILLED_MAP, path, config, path, null));
this.setItem(4, configItem(LegacyMaterials.FILLED_MAP, !path.isEmpty() ? path : file , config, !path.isEmpty() ? path : null, null));
this.setButton(8, GuiUtils.createButtonItem(LegacyMaterials.OAK_DOOR, "Exit"), (event) -> event.player.closeInventory());
// compile list of settings
@ -89,28 +98,98 @@ public class ConfigEditorGui extends SimplePagedGui {
this.setButton(index, configItem(LegacyMaterials.CLOCK, ChatColor.YELLOW + settingKey, node, settingKey, String.valueOf((Number) val), "Click to edit this setting"),
(event) -> {
event.gui.exit();
ChatPrompt prompt = ChatPrompt.showPrompt(plugin, event.player, "Enter your new value.", response -> {
ChatPrompt.showPrompt(plugin, event.player, "Enter a new number value for " + settingKey + ":", response -> {
if (!setNumber(event.slot, settingKey, response.getMessage().trim())) {
event.player.sendMessage(ChatColor.RED + "Error: \"" + response.getMessage().trim() + "\" is not a number!");
}
}).setOnClose(() -> event.manager.showGUI(event.player, this))
.setOnCancel(() -> {event.player.sendMessage(ChatColor.RED + "Edit canceled"); event.manager.showGUI(event.player, this);});
});
prompt.setOnClose(() -> event.manager.showGUI(event.player, this));
} else if (isMaterial(val)) {
// changing a block
// isMaterial is more of a guess, to be honest.
this.setButton(index, configItem(LegacyMaterials.STONE, ChatColor.YELLOW + settingKey, node, settingKey, val.toString(), "Click to edit this setting"),
(event) -> {
SimplePagedGui paged = new SimplePagedGui(this);
paged.setTitle(ChatColor.BLUE + settingKey);
paged.setHeaderBackItem(headerBackItem).setFooterBackItem(footerBackItem).setDefaultItem(blankItem);
paged.setItem(4, configItem(LegacyMaterials.FILLED_MAP, settingKey, node, settingKey, "Choose an item to change this value to"));
int i = 9;
for(LegacyMaterials mat : LegacyMaterials.getAllValidItemMaterials()) {
paged.setButton(i++, GuiUtils.createButtonItem(mat, mat.name()), ClickType.LEFT, (matEvent) -> {
setMaterial(event.slot, settingKey, matEvent.clickedItem);
matEvent.player.closeInventory();
});
}
event.manager.showGUI(event.player, paged);
});
} else if (val instanceof String) {
// changing a "string" value (or change to a feather for writing quill)
this.setButton(index, configItem(LegacyMaterials.STRING, ChatColor.YELLOW + settingKey, node, settingKey, val.toString(), "Click to edit this setting"),
(event) -> {
event.gui.exit();
ChatPrompt.showPrompt(plugin, event.player, "Enter a new value for " + settingKey + ":", response -> {
node.set(settingKey, response.getMessage().trim());
updateValue(event.slot, settingKey);
}).setOnClose(() -> event.manager.showGUI(event.player, this))
.setOnCancel(() -> {event.player.sendMessage(ChatColor.RED + "Edit canceled"); event.manager.showGUI(event.player, this);});
});
} else if (val instanceof List) {
this.setButton(index, configItem(LegacyMaterials.WRITABLE_BOOK, ChatColor.YELLOW + settingKey, node, settingKey, String.format("(%d values)", ((List) val).size()), "Click to edit this setting"),
(event) -> {
event.manager.showGUI(event.player, (new ConfigEditorListEditorGui(this, settingKey, (List) val)).setOnClose((gui) -> {
if(((ConfigEditorListEditorGui) gui.gui).saveChanges) {
setList(event.slot, settingKey, ((ConfigEditorListEditorGui) gui.gui).value);
}
}));
});
} else {
// idk. should we display uneditable values?
}
++index;
}
}
public ConfigurationSection getCurrentNode() {
return node;
}
protected void updateValue(int clickCell, String path) {
ItemStack item = inventory.getItem(clickCell);
if(item == null || item == AIR) return;
ItemMeta meta = item.getItemMeta();
Object val = node.get(path);
if (meta != null && val != null) {
String valStr;
if (val instanceof List) {
valStr = String.format("(%d values)", ((List) val).size());
} else {
valStr = val.toString();
}
List<String> lore = meta.getLore();
if (lore == null || lore.isEmpty()) {
meta.setLore(Arrays.asList(valStr));
} else {
lore.set(0, valStr);
meta.setLore(lore);
}
item.setItemMeta(meta);
setItem(clickCell, item);
}
}
void toggle(int clickCell, String path) {
boolean val = !node.getBoolean(path);
node.set(path, val);
if(val) {
inventory.setItem(clickCell, ItemUtils.addGlow(inventory.getItem(clickCell)));
setItem(clickCell, ItemUtils.addGlow(inventory.getItem(clickCell)));
} else {
removeHighlight(clickCell);
setItem(clickCell, ItemUtils.removeGlow(inventory.getItem(clickCell)));
}
updateValue(clickCell, path);
}
boolean setNumber(int clickCell, String path, String input) {
@ -122,12 +201,28 @@ public class ConfigEditorGui extends SimplePagedGui {
} else if (node.isLong(path)) {
node.set(path, Long.parseLong(input));
}
updateValue(clickCell, path);
} catch (NumberFormatException e) {
return false;
}
return true;
}
void setMaterial(int clickCell, String path, ItemStack item) {
LegacyMaterials mat = LegacyMaterials.getMaterial(item);
if (mat == null) {
node.set(path, LegacyMaterials.STONE.name());
} else {
node.set(path, mat.name());
}
updateValue(clickCell, path);
}
void setList(int clickCell, String path, List<String> list) {
node.set(path, list);
updateValue(clickCell, path);
}
void save() {
// could also check and call saveChanges()
if (config instanceof FileConfiguration) {
@ -149,32 +244,38 @@ public class ConfigEditorGui extends SimplePagedGui {
|| value instanceof Double);
}
ItemStack configItem(LegacyMaterials type, String name, ConfigurationSection node, String path, String def) {
private boolean isMaterial(Object value) {
LegacyMaterials m;
return value instanceof String && value.toString().equals(value.toString().toUpperCase())
&& (m = LegacyMaterials.getMaterial(value.toString())) != null && m.isValidItem();
}
protected ItemStack configItem(LegacyMaterials type, String name, ConfigurationSection node, String path, String def) {
String[] info = null;
if (configSection_getCommentString != null) {
try {
Object comment = configSection_getCommentString.invoke(config, path);
Object comment = configSection_getCommentString.invoke(node, path);
if (comment != null) {
info = comment.toString().split("\n");
}
} catch (Exception ex) {
}
}
return GuiUtils.createButtonItem(LegacyMaterials.FILLED_MAP, path, info != null ? info : (def != null ? def.split("\n") : null));
return GuiUtils.createButtonItem(type, name, info != null ? info : (def != null ? def.split("\n") : null));
}
ItemStack configItem(LegacyMaterials type, String name, ConfigurationSection node, String path, String value, String def) {
protected ItemStack configItem(LegacyMaterials type, String name, ConfigurationSection node, String path, String value, String def) {
if(value == null) value = "";
String[] info = null;
if (configSection_getCommentString != null) {
try {
Object comment = configSection_getCommentString.invoke(config, path);
Object comment = configSection_getCommentString.invoke(node, path);
if (comment != null) {
info = (value + "\n" + comment.toString()).split("\n");
}
} catch (Exception ex) {
}
}
return GuiUtils.createButtonItem(LegacyMaterials.FILLED_MAP, path, info != null ? info : (def != null ? (value + "\n" + def).split("\n") : null));
return GuiUtils.createButtonItem(type, name, info != null ? info : (def != null ? (value + "\n" + def).split("\n") : null));
}
}

View File

@ -0,0 +1,72 @@
package com.songoda.core.configuration.editor;
import com.songoda.core.compatibility.LegacyMaterials;
import com.songoda.core.gui.GuiUtils;
import com.songoda.core.gui.SimplePagedGui;
import com.songoda.core.input.ChatPrompt;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.ChatColor;
import org.bukkit.event.inventory.ClickType;
/**
* Edit a string list
*
* @since 2019-08-31
* @author jascotty2
*/
public class ConfigEditorListEditorGui extends SimplePagedGui {
final ConfigEditorGui current;
public boolean saveChanges = false;
public List<String> value;
public ConfigEditorListEditorGui(ConfigEditorGui current, String key, List<String> value) {
this.current = current;
this.blankItem = current.getDefaultItem();
headerBackItem = footerBackItem = current.getHeaderBackItem();
setTitle(ChatColor.DARK_BLUE + "String List Editor");
this.setUseHeader(true);
this.setItem(4, current.configItem(LegacyMaterials.FILLED_MAP, key, current.getCurrentNode(), key, null));
this.setButton(8, GuiUtils.createButtonItem(LegacyMaterials.OAK_DOOR, "Exit"), (event) -> event.player.closeInventory());
this.value = new ArrayList(value);
this.setButton(8, GuiUtils.createButtonItem(LegacyMaterials.LAVA_BUCKET, ChatColor.RED + "Discard Changes"), (event) -> event.player.closeInventory());
this.setButton(0, GuiUtils.createButtonItem(LegacyMaterials.REDSTONE, ChatColor.GREEN + "Save"), (event) -> {
saveChanges = true;
event.player.closeInventory();
});
this.setButton(1, GuiUtils.createButtonItem(LegacyMaterials.CHEST, ChatColor.BLUE + "Add Item"),
(event) -> {
ChatPrompt.showPrompt(event.manager.getPlugin(), event.player, "Enter a new value to add:", response -> {
value.add(response.getMessage().trim());
redraw();
}).setOnClose(() -> event.manager.showGUI(event.player, this))
.setOnCancel(() -> {event.player.sendMessage(ChatColor.RED + "Edit canceled"); event.manager.showGUI(event.player, this);});
});
}
void redraw() {
page = 1;
// clear old display
for (Integer oldI : (Integer[]) cellItems.keySet().toArray()) {
if (oldI > 8) {
cellItems.remove(oldI);
}
}
// update items
int i = 9;
for (String item : value) {
final int index = i - 9;
setButton(i++, GuiUtils.createButtonItem(LegacyMaterials.PAPER, item, "Right-click to remove"), ClickType.RIGHT, (event) -> {
value.remove(index);
redraw();
});
}
// update display
showPage();
}
}

View File

@ -36,7 +36,7 @@ public class PluginConfigGui extends SimplePagedGui {
this.plugin = plugin;
// collect list of plugins
configs.put(plugin.getConfig().getConfig().getFile().getName(), plugin.getConfig().getConfig());
configs.put(plugin.getConfig().getCoreConfig().getFile().getName(), plugin.getConfig().getCoreConfig());
List<Config> more = plugin.getExtraConfig();
if (more != null && !more.isEmpty()) {
for (Config cfg : more) {

View File

@ -14,6 +14,7 @@ public class LocaleModule implements PluginInfoModule {
@Override
public void run(PluginInfo plugin) {
if(plugin.getJavaPlugin() == null || plugin.getSongodaId() <= 0) return;
JSONObject json = plugin.getJson();
try {
JSONArray files = (JSONArray) json.get("neededFiles");

View File

@ -136,9 +136,12 @@ public class Gui {
return this;
}
/**
* Close the GUI without calling onClose() and without opening any parent GUIs
*/
public void exit() {
allowClose = true;
parent = null;
open = false;
inventory.getViewers().stream()
.filter(e -> e instanceof Player)
.map(e -> (Player) e)
@ -212,6 +215,10 @@ public class Gui {
return this;
}
public ItemStack getDefaultItem() {
return blankItem;
}
public Gui setItem(int cell, ItemStack item) {
cellItems.put(cell, item);
if (open && cell >= 0 && cell < inventory.getSize()) {

View File

@ -40,6 +40,10 @@ public class GuiManager {
this.plugin = plugin;
}
public Plugin getPlugin() {
return plugin;
}
/**
* Initialize the GUI handlers
*/

View File

@ -43,6 +43,24 @@ public class SimplePagedGui extends Gui {
return this;
}
public ItemStack getHeaderBackItem() {
return headerBackItem;
}
public SimplePagedGui setHeaderBackItem(ItemStack headerBackItem) {
this.headerBackItem = headerBackItem;
return this;
}
public ItemStack getFooterBackItem() {
return footerBackItem;
}
public SimplePagedGui setFooterBackItem(ItemStack footerBackItem) {
this.footerBackItem = footerBackItem;
return this;
}
public SimplePagedGui setNextPage(ItemStack item) {
nextPage = item;
return this;

View File

@ -5,12 +5,13 @@ import com.songoda.core.gui.GuiManager;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
public class GuiClickEvent extends GuiEvent {
public final int slot;
public final boolean guiClicked;
public final ItemStack cursor;
public final ItemStack cursor, clickedItem;
public final ClickType clickType;
public final InventoryClickEvent event;
@ -19,6 +20,8 @@ public class GuiClickEvent extends GuiEvent {
this.slot = slot;
this.guiClicked = guiClicked;
this.cursor = event.getCursor();
Inventory clicked = event.getClickedInventory();
this.clickedItem = clicked == null ? null : clicked.getItem(event.getSlot());
this.clickType = event.getClick();
this.event = event;
}

View File

@ -1,17 +1,16 @@
package com.songoda.core.input;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.plugin.Plugin;

View File

@ -1,21 +1,17 @@
package com.songoda.core.locale;
import com.songoda.core.utils.TextUtils;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
@ -30,7 +26,8 @@ import org.bukkit.plugin.java.JavaPlugin;
/**
* Assists in the utilization of localization files. <br>
* Created to be used by the Songoda Team. <br>
* NOTE: Using this class in multiple plugins requires shading!
* NOTE: Using this class in multiple plugins requires shading! <br>
* Updated 2019-09-01 to support UTF encoded lang files - jascotty2
*
* @author Brianna O'Keefe - Songoda
*/
@ -188,8 +185,8 @@ public class Locale {
try (BufferedInputStream defaultIn = new BufferedInputStream(defaultFile);
BufferedInputStream existingIn = new BufferedInputStream(new FileInputStream(existingFile))) {
Charset defaultCharset = Locale.detectCharset(defaultIn);
Charset existingCharset = Locale.detectCharset(existingIn);
Charset defaultCharset = TextUtils.detectCharset(defaultIn, StandardCharsets.UTF_8);
Charset existingCharset = TextUtils.detectCharset(existingIn, StandardCharsets.UTF_8);
try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(existingFile, true), existingCharset);
BufferedReader defaultReader = new BufferedReader(new InputStreamReader(defaultIn, defaultCharset));
@ -254,9 +251,10 @@ public class Locale {
this.nodes.clear(); // Clear previous data (if any)
// guess what encoding this file is in
Charset charset = detectCharset(file);
Charset charset = TextUtils.detectCharset(file, null);
if(charset == null) {
return false;
plugin.getLogger().warning("Could not determine charset for locale \"" + this.name + "\"");
charset = StandardCharsets.UTF_8;
}
// load in the file!
@ -291,80 +289,6 @@ public class Locale {
return true;
}
protected static final List<Charset> supportedCharsets = new ArrayList();
static {
supportedCharsets.add(StandardCharsets.UTF_8); // UTF-8 BOM: EF BB BF
supportedCharsets.add(StandardCharsets.ISO_8859_1); // also starts with EF BB BF
//supportedCharsets.add(StandardCharsets.UTF_16LE); // FF FE
//supportedCharsets.add(StandardCharsets.UTF_16BE); // FE FF
//supportedCharsets.add(StandardCharsets.UTF_16);
try {
supportedCharsets.add(Charset.forName("windows-1253"));
supportedCharsets.add(Charset.forName("ISO-8859-7"));
} catch (Exception e) {
} // UnsupportedCharsetException technically can be thrown, but can also be ignored
supportedCharsets.add(StandardCharsets.US_ASCII);
}
protected static Charset detectCharset(File f) {
byte[] buffer = new byte[2048];
int read = -1;
// read the first 2kb of the file and test the file's encoding
try (FileInputStream input = new FileInputStream(f)) {
read = input.read(buffer);
} catch (Exception ex) {
return null;
}
return read != -1 ? detectCharset(buffer, read) : null;
}
protected static Charset detectCharset(BufferedInputStream reader) {
byte[] buffer = new byte[2048];
int read;
try {
reader.mark(2048);
read = reader.read(buffer);
reader.reset();
} catch (Exception ex) {
return null;
}
return read != -1 ? detectCharset(buffer, read) : null;
}
protected static Charset detectCharset(byte[] data, int len) {
// check the file header
if (len > 4) {
if (data[0] == (byte) 0xFF && data[1] == (byte) 0xFE) {
return StandardCharsets.UTF_16LE;
// FF FE 00 00 is UTF-32LE
} else if (data[0] == (byte) 0xFE && data[1] == (byte) 0xFF) {
return StandardCharsets.UTF_16BE;
// 00 00 FE FF is UTF-32BE
} else if (data[0] == (byte) 0xEF && data[1] == (byte) 0xBB && data[2] == (byte) 0xBF) { // UTF-8 with BOM, same sig as ISO-8859-1
return StandardCharsets.UTF_8;
}
}
// iterate through sets to test, and return the first that is ok
for (Charset charset : supportedCharsets) {
if (charset != null && isCharset(data, len, charset)) {
return charset;
}
}
return null;
}
protected static boolean isCharset(byte[] data, int len, Charset charset) {
try {
CharsetDecoder decoder = charset.newDecoder();
decoder.reset();
decoder.decode(ByteBuffer.wrap(data));
return true;
} catch (CharacterCodingException e) {
}
return false;
}
/**
* Supply the Message object with the plugins prefix.

View File

@ -144,6 +144,10 @@ public class ItemUtils {
* @return copy of the item without any enchantment tag
*/
public static ItemStack removeGlow(ItemStack item) {
if (ServerVersion.isServerVersionAtLeast(ServerVersion.V1_11)) {
item.removeEnchantment(Enchantment.DURABILITY);
return item;
} else {
if (item != null && item.getType() != Material.AIR && cb_CraftItemStack_asCraftMirror != null) {
try {
Object nmsStack = cb_CraftItemStack_asNMSCopy.invoke(null, item);
@ -158,6 +162,7 @@ public class ItemUtils {
Bukkit.getLogger().log(Level.SEVERE, "Failed to set glow enchantment on item: " + item, ex);
}
}
}
return item;
}

View File

@ -1,5 +1,15 @@
package com.songoda.core.utils;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.ChatColor;
public class TextUtils {
@ -24,4 +34,78 @@ public class TextUtils {
return hidden.toString();
}
protected static final List<Charset> supportedCharsets = new ArrayList();
static {
supportedCharsets.add(StandardCharsets.UTF_8); // UTF-8 BOM: EF BB BF
supportedCharsets.add(StandardCharsets.ISO_8859_1); // also starts with EF BB BF
//supportedCharsets.add(StandardCharsets.UTF_16LE); // FF FE
//supportedCharsets.add(StandardCharsets.UTF_16BE); // FE FF
//supportedCharsets.add(StandardCharsets.UTF_16);
try {
supportedCharsets.add(Charset.forName("windows-1253"));
supportedCharsets.add(Charset.forName("ISO-8859-7"));
} catch (Exception e) {
} // UnsupportedCharsetException technically can be thrown, but can also be ignored
supportedCharsets.add(StandardCharsets.US_ASCII);
}
public static Charset detectCharset(File f, Charset def) {
byte[] buffer = new byte[2048];
int read = -1;
// read the first 2kb of the file and test the file's encoding
try (FileInputStream input = new FileInputStream(f)) {
read = input.read(buffer);
} catch (Exception ex) {
return null;
}
return read != -1 ? detectCharset(buffer, read, def) : def;
}
public static Charset detectCharset(BufferedInputStream reader, Charset def) {
byte[] buffer = new byte[2048];
int read;
try {
reader.mark(2048);
read = reader.read(buffer);
reader.reset();
} catch (Exception ex) {
return null;
}
return read != -1 ? detectCharset(buffer, read, def) : def;
}
public static Charset detectCharset(byte[] data, int len, Charset def) {
// check the file header
if (len > 4) {
if (data[0] == (byte) 0xFF && data[1] == (byte) 0xFE) {
return StandardCharsets.UTF_16LE;
// FF FE 00 00 is UTF-32LE
} else if (data[0] == (byte) 0xFE && data[1] == (byte) 0xFF) {
return StandardCharsets.UTF_16BE;
// 00 00 FE FF is UTF-32BE
} else if (data[0] == (byte) 0xEF && data[1] == (byte) 0xBB && data[2] == (byte) 0xBF) { // UTF-8 with BOM, same sig as ISO-8859-1
return StandardCharsets.UTF_8;
}
}
// iterate through sets to test, and return the first that is ok
for (Charset charset : supportedCharsets) {
if (charset != null && isCharset(data, len, charset)) {
return charset;
}
}
return def;
}
public static boolean isCharset(byte[] data, int len, Charset charset) {
try {
CharsetDecoder decoder = charset.newDecoder();
decoder.reset();
decoder.decode(ByteBuffer.wrap(data));
return true;
} catch (CharacterCodingException e) {
}
return false;
}
}