From afab90d021cfa33648d2498d52706ec508884654 Mon Sep 17 00:00:00 2001 From: Phoenix616 Date: Sun, 9 Sep 2018 13:24:26 +0100 Subject: [PATCH 1/3] Fix wording in of incorrect item id message --- src/main/java/com/Acrobot/ChestShop/Configuration/Messages.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/Acrobot/ChestShop/Configuration/Messages.java b/src/main/java/com/Acrobot/ChestShop/Configuration/Messages.java index 3160f01..2b5b11b 100644 --- a/src/main/java/com/Acrobot/ChestShop/Configuration/Messages.java +++ b/src/main/java/com/Acrobot/ChestShop/Configuration/Messages.java @@ -67,7 +67,7 @@ public class Messages { @PrecededBySpace public static String PLAYER_NOT_FOUND = "Player not found!"; public static String NO_PERMISSION = "You don't have permissions to do that!"; - public static String INCORRECT_ITEM_ID = "You have specified invalid item id!"; + public static String INCORRECT_ITEM_ID = "You have specified an invalid item id!"; public static String NOT_ENOUGH_PROTECTIONS = "Could not create a protection!"; @PrecededBySpace From 2459a406fe72583fffb20484d594fdd9651b1ca3 Mon Sep 17 00:00:00 2001 From: Phoenix616 Date: Sun, 9 Sep 2018 13:26:08 +0100 Subject: [PATCH 2/3] Auto-correct item code on sign. This also makes it possible to input IDs on the sign that are longer than the max sign letters that the plugin uses as that just gets auto corrected back down to the correct amount. --- .../Listeners/PreShopCreation/ItemChecker.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/Acrobot/ChestShop/Listeners/PreShopCreation/ItemChecker.java b/src/main/java/com/Acrobot/ChestShop/Listeners/PreShopCreation/ItemChecker.java index aa6d7f1..c7f6788 100644 --- a/src/main/java/com/Acrobot/ChestShop/Listeners/PreShopCreation/ItemChecker.java +++ b/src/main/java/com/Acrobot/ChestShop/Listeners/PreShopCreation/ItemChecker.java @@ -33,22 +33,17 @@ public class ItemChecker implements Listener { if (item == null) { if (Properties.ALLOW_AUTO_ITEM_FILL && itemCode.equals(AUTOFILL_CODE)) { - boolean foundItem = false; Chest chest = uBlock.findConnectedChest(event.getSign()); if (chest != null) { for (ItemStack stack : chest.getInventory().getContents()) { if (!MaterialUtil.isEmpty(stack)) { - itemCode = MaterialUtil.getSignName(stack); - - event.setSignLine(ITEM_LINE, itemCode); - foundItem = true; - + item = stack; break; } } } - if (!foundItem) { + if (item == null) { event.setSignLine(ITEM_LINE, ChatColor.BOLD + ChestShopSign.AUTOFILL_CODE); event.setOutcome(ITEM_AUTOFILL); event.getPlayer().sendMessage(Messages.prefix(Messages.CLICK_TO_AUTOFILL_ITEM)); @@ -60,10 +55,14 @@ public class ItemChecker implements Listener { } } + itemCode = MaterialUtil.getSignName(item); + if (itemCode.length() > MAXIMUM_SIGN_LETTERS) { event.setOutcome(INVALID_ITEM); return; } + + event.setSignLine(ITEM_LINE, itemCode); } private static boolean isSameItem(String newCode, ItemStack item) { From 516eefc5b67ab5048ffd3e9c888c634bf22f29ed Mon Sep 17 00:00:00 2001 From: Phoenix616 Date: Sun, 9 Sep 2018 14:37:09 +0100 Subject: [PATCH 3/3] Allow longer item names on signs by calculating the width This uses hardcoded widths for the main characters used in Minecraft and might not be precise for some characters but better than the previous 15 char hard limit. --- .../Acrobot/Breeze/Utils/MaterialUtil.java | 72 +++++++++++++------ .../com/Acrobot/Breeze/Utils/StringUtil.java | 35 +++++++++ .../PreShopCreation/ItemChecker.java | 3 +- .../Acrobot/Breeze/Tests/MaterialTest.java | 2 +- 4 files changed, 88 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/Acrobot/Breeze/Utils/MaterialUtil.java b/src/main/java/com/Acrobot/Breeze/Utils/MaterialUtil.java index 2266c83..e22cb0f 100644 --- a/src/main/java/com/Acrobot/Breeze/Utils/MaterialUtil.java +++ b/src/main/java/com/Acrobot/Breeze/Utils/MaterialUtil.java @@ -31,6 +31,9 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import static com.Acrobot.Breeze.Utils.StringUtil.getMinecraftCharWidth; +import static com.Acrobot.Breeze.Utils.StringUtil.getMinecraftStringWidth; + /** * @author Acrobot */ @@ -40,7 +43,13 @@ public class MaterialUtil { public static final boolean LONG_NAME = true; public static final boolean SHORT_NAME = false; + /** + * @deprecated Use {@link MaterialUtil#MAXIMUM_SIGN_WIDTH} + */ + @Deprecated public static final short MAXIMUM_SIGN_LETTERS = 15; + // 15 dashes fit on one sign line with the default resource pack: + public static final int MAXIMUM_SIGN_WIDTH = (short) getMinecraftStringWidth("---------------"); private static final SimpleCache MATERIAL_CACHE = new SimpleCache<>(Properties.CACHE_SIZE); @@ -172,17 +181,17 @@ public class MaterialUtil { * @return ItemStack's name */ public static String getSignName(ItemStack itemStack) { - return getName(itemStack, MAXIMUM_SIGN_LETTERS); + return getName(itemStack, MAXIMUM_SIGN_WIDTH); } /** - * Returns item's name, with a maximum length + * Returns item's name, with a maximum width * * @param itemStack ItemStack to name - * @param maxLength The max length that the name should have; 0 or below if it should be unlimited + * @param maxWidth The max width that the name should have; 0 or below if it should be unlimited * @return ItemStack's name */ - public static String getName(ItemStack itemStack, int maxLength) { + public static String getName(ItemStack itemStack, int maxWidth) { String alias = Odd.getAlias(itemStack); String itemName = alias != null ? alias : itemStack.getType().toString(); if (itemStack.getType() != Material.HUGE_MUSHROOM_2 && itemName.endsWith("_2")) { @@ -203,18 +212,18 @@ public class MaterialUtil { metaData = "#" + Metadata.getItemCode(itemStack); } - int codeLength = (data + itemName + durability + metaData).length(); + int codeWidth = getMinecraftStringWidth(data + itemName + durability + metaData); String code = data + itemName; - if (maxLength > 0 && codeLength > maxLength) { - int exceeding = codeLength - maxLength; - code = getShortenedName(code, code.length() - exceeding); + if (maxWidth > 0 && codeWidth > maxWidth) { + int exceeding = codeWidth - maxWidth; + code = getShortenedName(code, getMinecraftStringWidth(code) - exceeding); } code = StringUtil.capitalizeFirstLetter(code, '_') + durability + metaData; ItemStack codeItem = getItem(code); if (!equals(itemStack, codeItem)) { - throw new IllegalArgumentException("Cannot generate code for item " + itemStack + " with maximum length of " + maxLength + throw new IllegalArgumentException("Cannot generate code for item " + itemStack + " with maximum length of " + maxWidth + " (code " + code + " results in item " + codeItem + ")"); } @@ -225,43 +234,62 @@ public class MaterialUtil { * Get an item name shortened to a max length that is still reversable by {@link #getMaterial(String)} * * @param itemName The name of the item - * @param maxLength The max length + * @param maxWidth The max width * @return The name shortened to the max length */ - public static String getShortenedName(String itemName, int maxLength) { - if (itemName.length() <= maxLength) { + public static String getShortenedName(String itemName, int maxWidth) { + int width = getMinecraftStringWidth(itemName); + if (width <= maxWidth) { return itemName; } - int exceeding = itemName.length() - maxLength; + int exceeding = width - maxWidth; String[] itemParts = itemName.split("_"); int shortestIndex = 0; int longestIndex = 0; for (int i = 0; i < itemParts.length; i++) { - if (itemParts[longestIndex].length() < itemParts[i].length()) { + if (getMinecraftStringWidth(itemParts[longestIndex]) < getMinecraftStringWidth(itemParts[i])) { longestIndex = i; } - if (itemParts[shortestIndex].length() > itemParts[i].length()) { + if (getMinecraftStringWidth(itemParts[shortestIndex]) > getMinecraftStringWidth(itemParts[i])) { shortestIndex = i; } } - if (itemParts[longestIndex].length() - itemParts[shortestIndex].length() > exceeding) { - itemParts[longestIndex] = itemParts[longestIndex].substring(0, itemParts[longestIndex].length() - exceeding); + String longest = itemParts[longestIndex]; + String shortest = itemParts[shortestIndex]; + if (getMinecraftStringWidth(longest) - getMinecraftStringWidth(shortest) > exceeding) { + int remove = exceeding; + while (remove > 0) { + int endWidth = getMinecraftCharWidth(longest.charAt(longest.length() - 1)); + itemParts[longestIndex] = longest.substring(0, longest.length() - 1); + remove -= endWidth; + } } else { for (int i = itemParts.length - 1; i >= 0 && exceeding > 0; i--) { int remove = 0; - if (itemParts[i].length() > itemParts[shortestIndex].length()) { - remove = itemParts[i].length() - itemParts[shortestIndex].length(); + int partWidth = getMinecraftStringWidth(itemParts[i]); + int shortestWidth = getMinecraftStringWidth(shortest); + + if (partWidth > shortestWidth) { + remove = partWidth - shortestWidth; } + if (remove > exceeding) { remove = exceeding; } - itemParts[i] = itemParts[i].substring(0, itemParts[i].length() - remove); - exceeding -= remove; + + while (remove > 0) { + int endWidth = getMinecraftCharWidth(itemParts[i].charAt(itemParts[i].length() - 1)); + itemParts[i] = itemParts[i].substring(0, itemParts[i].length() - 1); + remove -= endWidth; + exceeding -= endWidth; + } } + while (exceeding > 0) { for (int i = itemParts.length - 1; i >= 0 && exceeding > 0; i--) { + int endWidth = getMinecraftCharWidth(itemParts[i].charAt(itemParts[i].length() - 1)); itemParts[i] = itemParts[i].substring(0, itemParts[i].length() - 1); - exceeding--; + exceeding -= endWidth; } } } diff --git a/src/main/java/com/Acrobot/Breeze/Utils/StringUtil.java b/src/main/java/com/Acrobot/Breeze/Utils/StringUtil.java index 84d8395..eeeaef8 100644 --- a/src/main/java/com/Acrobot/Breeze/Utils/StringUtil.java +++ b/src/main/java/com/Acrobot/Breeze/Utils/StringUtil.java @@ -77,4 +77,39 @@ public class StringUtil { return output; } + + private static String characters = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_'abcdefghijklmnopqrstuvwxyz{|}~¦ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜø£Ø׃áíóúñѪº¿®¬½¼¡«»"; + private static int[] extraWidth = {4,2,5,6,6,6,6,3,5,5,5,6,2,6,2,6,6,6,6,6,6,6,6,6,6,6,2,2,5,6,5,6,7,6,6,6,6,6,6,6,6,4,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,4,6,4,6,6,3,6,6,6,6,6,5,6,6,2,6,5,3,6,6,6,6,6,6,6,4,6,6,6,6,6,6,5,2,5,7,6,6,6,6,6,6,6,6,6,6,6,6,4,6,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,4,6,6,3,6,6,6,6,6,6,6,7,6,6,6,2,6,6,8,9,9,6,6,6,8,8,6,8,8,8,8,8,6,6,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,6,9,9,9,5,9,9,8,7,7,8,7,8,8,8,7,8,8,7,9,9,6,7,7,7,7,7,9,6,7,8,7,6,6,9,7,6,7,1}; + + /** + * Get the width that a character is displayed with in the default resource pack. + * This relies on a hardcoded character to width mapping and might not be precise in places. + * @param c The character to get the width of + * @return The width of the character (will return 10 for characters that we don't know the width of) + */ + public static int getMinecraftCharWidth(char c) { + if (c != ChatColor.COLOR_CHAR) { + int index = characters.indexOf(c); + if (index > -1) { + return extraWidth[index]; + } else { + return 10; + } + } + return 0; + } + + /** + * Get the width that a string is displayed with in the default resource pack. + * This relies on a hardcoded character to width mapping and might not be precise in places. + * @param string The string to get the width of + * @return The width of the string + */ + public static int getMinecraftStringWidth(String string) { + int width = 0; + for (char c : string.toCharArray()) { + width += getMinecraftCharWidth(c); + } + return width; + } } diff --git a/src/main/java/com/Acrobot/ChestShop/Listeners/PreShopCreation/ItemChecker.java b/src/main/java/com/Acrobot/ChestShop/Listeners/PreShopCreation/ItemChecker.java index c7f6788..3db1eb7 100644 --- a/src/main/java/com/Acrobot/ChestShop/Listeners/PreShopCreation/ItemChecker.java +++ b/src/main/java/com/Acrobot/ChestShop/Listeners/PreShopCreation/ItemChecker.java @@ -1,6 +1,7 @@ package com.Acrobot.ChestShop.Listeners.PreShopCreation; import com.Acrobot.Breeze.Utils.MaterialUtil; +import com.Acrobot.Breeze.Utils.StringUtil; import com.Acrobot.ChestShop.Configuration.Messages; import com.Acrobot.ChestShop.Configuration.Properties; import com.Acrobot.ChestShop.Events.PreShopCreationEvent; @@ -57,7 +58,7 @@ public class ItemChecker implements Listener { itemCode = MaterialUtil.getSignName(item); - if (itemCode.length() > MAXIMUM_SIGN_LETTERS) { + if (StringUtil.getMinecraftStringWidth(itemCode) > MAXIMUM_SIGN_WIDTH) { event.setOutcome(INVALID_ITEM); return; } diff --git a/src/test/java/com/Acrobot/Breeze/Tests/MaterialTest.java b/src/test/java/com/Acrobot/Breeze/Tests/MaterialTest.java index d9026c1..2d0b91a 100644 --- a/src/test/java/com/Acrobot/Breeze/Tests/MaterialTest.java +++ b/src/test/java/com/Acrobot/Breeze/Tests/MaterialTest.java @@ -29,7 +29,7 @@ public class MaterialTest { @Test public void testCodes() { for (Material material : Material.values()) { - String shortenedName = MaterialUtil.getShortenedName(material.toString(), MaterialUtil.MAXIMUM_SIGN_LETTERS); + String shortenedName = MaterialUtil.getShortenedName(material.toString(), MaterialUtil.MAXIMUM_SIGN_WIDTH); assertSame(material, MaterialUtil.getMaterial(shortenedName)); } }