diff --git a/src/languages/italian.yml b/src/languages/italian.yml deleted file mode 100644 index e69de29..0000000 diff --git a/src/nl/marido/deluxeheads/DeluxeHeads.java b/src/nl/marido/deluxeheads/DeluxeHeads.java index bb144c0..57a6a2e 100644 --- a/src/nl/marido/deluxeheads/DeluxeHeads.java +++ b/src/nl/marido/deluxeheads/DeluxeHeads.java @@ -40,6 +40,9 @@ import nl.marido.deluxeheads.economy.ItemEconomy; import nl.marido.deluxeheads.economy.NoEconomy; import nl.marido.deluxeheads.economy.PlayerPointsEconomy; import nl.marido.deluxeheads.economy.VaultEconomy; +import nl.marido.deluxeheads.handlers.HeadNamer; +import nl.marido.deluxeheads.handlers.LegacyIDs; +import nl.marido.deluxeheads.handlers.UpdateChecker; import nl.marido.deluxeheads.menu.ui.InventoryMenu; import nl.marido.deluxeheads.oldmenu.ClickInventory; import nl.marido.deluxeheads.util.Clock; @@ -110,12 +113,12 @@ public class DeluxeHeads extends JavaPlugin implements Listener { String currentVersion = UpdateChecker.getCurrentVersion(); String latestVersion = UpdateChecker.getLatestVersion(); if (!UpdateChecker.isNewerVersion(latestVersion)) { - String newversion = Lang.Updater.newVersion().getSingle(); + String newversion = Lang.Updater.newVersion().toString(); newversion = newversion.replaceAll("%version%", currentVersion); severe(newversion); return; } - String oldversion = Lang.Updater.oldVersion().getSingle(); + String oldversion = Lang.Updater.oldVersion().toString(); oldversion = oldversion.replaceAll("%version%", currentVersion); warning(oldversion); } catch (IOException e) { diff --git a/src/nl/marido/deluxeheads/Search.java b/src/nl/marido/deluxeheads/Search.java deleted file mode 100644 index b3d4cab..0000000 --- a/src/nl/marido/deluxeheads/Search.java +++ /dev/null @@ -1,296 +0,0 @@ -package nl.marido.deluxeheads; - -import java.util.*; - -import nl.marido.deluxeheads.cache.CacheHead; -import nl.marido.deluxeheads.util.Checks; - -public final class Search { - - private Query query; - private Query reusableQuery; - private double threshold; - - private int[][] editDis; - private int editDisDim1; - private int editDisDim2; - - private List substrings = new ArrayList<>(); - - private Search(String query, double threshold) { - this.query = new Query(query, toWords(query)); - this.substrings = new ArrayList<>(); - this.reusableQuery = new Query("", null); - this.threshold = threshold; - - getReusableArray(query.length() + 1, 38); - } - - private int[][] getReusableArray(int dim1, int dim2) { - if(dim1 <= editDisDim1 && dim2 <= editDisDim2) - return editDis; - - dim1 = Math.max(dim1, editDisDim1); - dim2 = Math.max(dim2, editDisDim2); - - editDis = new int[dim1][dim2]; - editDisDim1 = dim1; - editDisDim2 = dim2; - - return editDis; - } - - private void appendSubstring(int index, String string, int start, int end) { - if(index < substrings.size()) { - substrings.get(index).reuse(string, start, end); - } else { - substrings.add(new Substring(string, start, end)); - } - } - - public List toWords(String string) { - int len = string.length(); - - int wordCount = 0; - int lastSplit = 0; - boolean inWord = false; - - for(int index = 0; index < len; ++index) { - char ch = string.charAt(index); - - if(ch == ' ') { - if(inWord) { - appendSubstring(wordCount, string, lastSplit, index); - wordCount += 1; - lastSplit = index + 1; - } - - inWord = false; - } else { - inWord = true; - } - } - - if(inWord) { - appendSubstring(wordCount, string, lastSplit, len); - wordCount += 1; - } - - return substrings.subList(0, wordCount); - } - - public Query reuseQuery(String string) { - return reusableQuery.reuse(string, toWords(string)); - } - - public List checkAll(Iterable heads) { - List matches = new ArrayList<>(); - - for(CacheHead head : heads) { - double relevance = calculateRelevance(query, head); - - if(relevance <= threshold) - continue; - - matches.add(new Match(head, relevance)); - } - - Collections.sort(matches); - - List results = new ArrayList<>(); - - for(Match match : matches) { - results.add(match.subject); - } - - return results; - } - - private double calculateRelevance(Query query, CacheHead head) { - double relevance = calculateRelevance(query, reuseQuery(head.getName())); - - for(String tag : head.getTags()) { - relevance = Math.max(relevance, 0.8 * calculateRelevance(query, reuseQuery(tag))); - } - - return relevance; - } - - private double calculateRelevance(Query query, Query subject) { - double similarity = calcSimilarity(query.string, subject.string); - - double wordSimilarity = 0d; - double aggregate = 0d; - int count = 0; - - for(Substring queryWord : query.words) { - double querySimilarity = 0d; - - for(Substring subjectWord : subject.words) { - querySimilarity = Math.max(querySimilarity, calcSimilarity(queryWord, subjectWord)); - } - - aggregate += querySimilarity; - count += 1; - - wordSimilarity = Math.max(wordSimilarity, querySimilarity); - } - - if(count > 0) { - wordSimilarity = 0.9d * wordSimilarity + 0.1d * (aggregate / count); - } - - return Math.max(similarity, wordSimilarity); - } - - private double calcSimilarity(Substring query, Substring subject) { - int len1 = query.length(); - int len2 = subject.length(); - - // len1+1, len2+1, because finally return dp[len1][len2] - int[][] dp = getReusableArray(len1 + 1, len2 + 1); - - for (int i = 0; i <= len1; i++) { - dp[i][0] = i; - } - - for (int j = 0; j <= len2; j++) { - dp[0][j] = j; - } - - //iterate though, and check last char - for (int i = 0; i < len1; i++) { - char c1 = query.charAt(i); - for (int j = 0; j < len2; j++) { - char c2 = subject.charAt(j); - - //if last two chars equal - if (c1 == c2) { - //update dp value for +1 length - dp[i + 1][j + 1] = dp[i][j]; - } else { - int replace = dp[i][j] + 1; - int insert = dp[i][j + 1] + 1; - int delete = dp[i + 1][j] + 1; - - int min = replace > insert ? insert : replace; - min = delete > min ? min : delete; - dp[i + 1][j + 1] = min; - } - } - } - - int editDistance = dp[len1][len2]; - - if(editDistance == 0) - return 1; - - return 0.75d * (double) (query.length() - editDistance) / (double) query.length(); - } - - private final static class Match implements Comparable { - - public final CacheHead subject; - public final double relevance; - - private Match(CacheHead subject, double relevance) { - this.subject = subject; - this.relevance = relevance; - } - - @Override - public int compareTo(Match other) { - return Double.compare(other.relevance, relevance); - } - } - - private final class Query { - - public Substring string; - public List words; - - public Query(String string, List words) { - this.string = new Substring(string); - this.words = words; - } - - public Query reuse(String string, List words) { - this.string.reuse(string); - this.words = words; - - return this; - } - } - - private static class Substring { - - public String string; - public int start; - public int end; - - public Substring(String string) { - this(string, 0, string.length()); - } - - public Substring(String string, int start, int end) { - reuse(string, start, end); - } - - public Substring reuse(String string) { - return reuse(string, 0, string.length()); - } - - public Substring reuse(String string, int start, int end) { - Checks.ensureNonNull(string, "string"); - - this.string = string; - this.moveTo(start, end); - - return this; - } - - public void moveTo(int start, int end) { - Checks.ensureTrue(start >= 0, "start must be >= 0"); - Checks.ensureTrue(end >= start, "end must be >= start"); - Checks.ensureTrue(end <= string.length(), "end must be <= to the length of string"); - - this.start = start; - this.end = end; - } - - public char charAt(int index) { - if(index < 0) - throw new IndexOutOfBoundsException("index cannot be negative"); - if(index >= length()) - throw new IndexOutOfBoundsException("index must be less than the strings length"); - - char ch = string.charAt(start + index); - - return (char) (ch >= 'A' && ch <= 'Z' ? ch + ('a' - 'A') : ch); - } - - public int length() { - return end - start; - } - - @Override - public String toString() { - return string.substring(start, end); - } - - } - - /** - * Search over the list of heads and find all heads with a relevance above a certain threshold. - * Will simplify the query string in an attempt to improve matches. - * - * @param query The search term. - * @param heads The heads we are checking for matches. - * @param threshold The threshold relevance that a head must have to be matched. - * @return All heads sorted by relevance that have a relevance greater than the threshold. - */ - public static List searchHeads(String query, Iterable heads, double threshold) { - return new Search(query, threshold).checkAll(heads); - } - -} diff --git a/src/nl/marido/deluxeheads/cache/CacheFile.java b/src/nl/marido/deluxeheads/cache/CacheFile.java index 1110f88..2e7b753 100644 --- a/src/nl/marido/deluxeheads/cache/CacheFile.java +++ b/src/nl/marido/deluxeheads/cache/CacheFile.java @@ -22,7 +22,7 @@ import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import nl.marido.deluxeheads.DeluxeHeads; -import nl.marido.deluxeheads.Search; +import nl.marido.deluxeheads.handlers.Search; import nl.marido.deluxeheads.util.Checks; import nl.marido.deluxeheads.util.IOUtils; diff --git a/src/nl/marido/deluxeheads/HeadNamer.java b/src/nl/marido/deluxeheads/handlers/HeadNamer.java similarity index 98% rename from src/nl/marido/deluxeheads/HeadNamer.java rename to src/nl/marido/deluxeheads/handlers/HeadNamer.java index 3ca9600..f99d5ab 100644 --- a/src/nl/marido/deluxeheads/HeadNamer.java +++ b/src/nl/marido/deluxeheads/handlers/HeadNamer.java @@ -1,4 +1,4 @@ -package nl.marido.deluxeheads; +package nl.marido.deluxeheads.handlers; import java.util.ArrayList; import java.util.List; @@ -27,6 +27,7 @@ import org.bukkit.plugin.RegisteredListener; import com.mojang.authlib.GameProfile; import net.sothatsit.blockstore.BlockStoreApi; +import nl.marido.deluxeheads.DeluxeHeads; import nl.marido.deluxeheads.cache.CacheHead; import nl.marido.deluxeheads.volatilecode.ItemNBT; import nl.marido.deluxeheads.volatilecode.Items; diff --git a/src/nl/marido/deluxeheads/LegacyIDs.java b/src/nl/marido/deluxeheads/handlers/LegacyIDs.java similarity index 96% rename from src/nl/marido/deluxeheads/LegacyIDs.java rename to src/nl/marido/deluxeheads/handlers/LegacyIDs.java index 28a6828..7bf90e6 100644 --- a/src/nl/marido/deluxeheads/LegacyIDs.java +++ b/src/nl/marido/deluxeheads/handlers/LegacyIDs.java @@ -1,4 +1,4 @@ -package nl.marido.deluxeheads; +package nl.marido.deluxeheads.handlers; import java.io.BufferedReader; import java.io.BufferedWriter; @@ -14,6 +14,7 @@ import java.util.Map; import org.bukkit.Material; +import nl.marido.deluxeheads.DeluxeHeads; import nl.marido.deluxeheads.util.Checks; public class LegacyIDs { diff --git a/src/nl/marido/deluxeheads/LiveHead.java b/src/nl/marido/deluxeheads/handlers/LiveHead.java similarity index 89% rename from src/nl/marido/deluxeheads/LiveHead.java rename to src/nl/marido/deluxeheads/handlers/LiveHead.java index 0364b2a..b261d63 100644 --- a/src/nl/marido/deluxeheads/LiveHead.java +++ b/src/nl/marido/deluxeheads/handlers/LiveHead.java @@ -1,10 +1,11 @@ -package nl.marido.deluxeheads; +package nl.marido.deluxeheads.handlers; import java.util.List; import org.bukkit.Location; import org.bukkit.scheduler.BukkitRunnable; +import nl.marido.deluxeheads.DeluxeHeads; import nl.marido.deluxeheads.api.DeluxeHeadsAPI.Head; public class LiveHead { diff --git a/src/nl/marido/deluxeheads/handlers/Search.java b/src/nl/marido/deluxeheads/handlers/Search.java new file mode 100644 index 0000000..c7545f9 --- /dev/null +++ b/src/nl/marido/deluxeheads/handlers/Search.java @@ -0,0 +1,274 @@ +package nl.marido.deluxeheads.handlers; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import nl.marido.deluxeheads.cache.CacheHead; +import nl.marido.deluxeheads.util.Checks; + +public final class Search { + + private Query query; + private Query reusableQuery; + private double threshold; + + private int[][] editDis; + private int editDisDim1; + private int editDisDim2; + + private List substrings = new ArrayList<>(); + + private Search(String query, double threshold) { + this.query = new Query(query, toWords(query)); + this.substrings = new ArrayList<>(); + this.reusableQuery = new Query("", null); + this.threshold = threshold; + + getReusableArray(query.length() + 1, 38); + } + + private int[][] getReusableArray(int dim1, int dim2) { + if (dim1 <= editDisDim1 && dim2 <= editDisDim2) + return editDis; + + dim1 = Math.max(dim1, editDisDim1); + dim2 = Math.max(dim2, editDisDim2); + + editDis = new int[dim1][dim2]; + editDisDim1 = dim1; + editDisDim2 = dim2; + + return editDis; + } + + private void appendSubstring(int index, String string, int start, int end) { + if (index < substrings.size()) { + substrings.get(index).reuse(string, start, end); + } else { + substrings.add(new Substring(string, start, end)); + } + } + + public List toWords(String string) { + int len = string.length(); + + int wordCount = 0; + int lastSplit = 0; + boolean inWord = false; + + for (int index = 0; index < len; ++index) { + char ch = string.charAt(index); + + if (ch == ' ') { + if (inWord) { + appendSubstring(wordCount, string, lastSplit, index); + wordCount += 1; + lastSplit = index + 1; + } + + inWord = false; + } else { + inWord = true; + } + } + + if (inWord) { + appendSubstring(wordCount, string, lastSplit, len); + wordCount += 1; + } + + return substrings.subList(0, wordCount); + } + + public Query reuseQuery(String string) { + return reusableQuery.reuse(string, toWords(string)); + } + + public List checkAll(Iterable heads) { + List matches = new ArrayList<>(); + + for (CacheHead head : heads) { + double relevance = calculateRelevance(query, head); + + if (relevance <= threshold) + continue; + + matches.add(new Match(head, relevance)); + } + + Collections.sort(matches); + + List results = new ArrayList<>(); + + for (Match match : matches) { + results.add(match.subject); + } + + return results; + } + + private double calculateRelevance(Query query, CacheHead head) { + double relevance = calculateRelevance(query, reuseQuery(head.getName())); + + for (String tag : head.getTags()) { + relevance = Math.max(relevance, 0.8 * calculateRelevance(query, reuseQuery(tag))); + } + + return relevance; + } + + private double calculateRelevance(Query query, Query subject) { + double similarity = calcSimilarity(query.string, subject.string); + + double wordSimilarity = 0d; + double aggregate = 0d; + int count = 0; + + for (Substring queryWord : query.words) { + double querySimilarity = 0d; + for (Substring subjectWord : subject.words) { + querySimilarity = Math.max(querySimilarity, calcSimilarity(queryWord, subjectWord)); + } + aggregate += querySimilarity; + count += 1; + wordSimilarity = Math.max(wordSimilarity, querySimilarity); + } + if (count > 0) { + wordSimilarity = 0.9d * wordSimilarity + 0.1d * (aggregate / count); + } + return Math.max(similarity, wordSimilarity); + } + + private double calcSimilarity(Substring query, Substring subject) { + int len1 = query.length(); + int len2 = subject.length(); + int[][] dp = getReusableArray(len1 + 1, len2 + 1); + for (int i = 0; i <= len1; i++) { + dp[i][0] = i; + } + for (int j = 0; j <= len2; j++) { + dp[0][j] = j; + } + for (int i = 0; i < len1; i++) { + char c1 = query.charAt(i); + for (int j = 0; j < len2; j++) { + char c2 = subject.charAt(j); + if (c1 == c2) { + dp[i + 1][j + 1] = dp[i][j]; + } else { + int replace = dp[i][j] + 1; + int insert = dp[i][j + 1] + 1; + int delete = dp[i + 1][j] + 1; + int min = replace > insert ? insert : replace; + min = delete > min ? min : delete; + dp[i + 1][j + 1] = min; + } + } + } + int editDistance = dp[len1][len2]; + if (editDistance == 0) + return 1; + return 0.75d * (double) (query.length() - editDistance) / (double) query.length(); + } + + private final static class Match implements Comparable { + + public final CacheHead subject; + public final double relevance; + + private Match(CacheHead subject, double relevance) { + this.subject = subject; + this.relevance = relevance; + } + + @Override + public int compareTo(Match other) { + return Double.compare(other.relevance, relevance); + } + } + + private final class Query { + + public Substring string; + public List words; + + public Query(String string, List words) { + this.string = new Substring(string); + this.words = words; + } + + public Query reuse(String string, List words) { + this.string.reuse(string); + this.words = words; + return this; + } + } + + private static class Substring { + + public String string; + public int start; + public int end; + + public Substring(String string) { + this(string, 0, string.length()); + } + + public Substring(String string, int start, int end) { + reuse(string, start, end); + } + + public Substring reuse(String string) { + return reuse(string, 0, string.length()); + } + + public Substring reuse(String string, int start, int end) { + Checks.ensureNonNull(string, "string"); + this.string = string; + this.moveTo(start, end); + return this; + } + + public void moveTo(int start, int end) { + Checks.ensureTrue(start >= 0, "start must be >= 0"); + Checks.ensureTrue(end >= start, "end must be >= start"); + Checks.ensureTrue(end <= string.length(), "end must be <= to the length of string"); + this.start = start; + this.end = end; + } + + public char charAt(int index) { + if (index < 0) + throw new IndexOutOfBoundsException("index cannot be negative"); + if (index >= length()) + throw new IndexOutOfBoundsException("index must be less than the strings length"); + char ch = string.charAt(start + index); + return (char) (ch >= 'A' && ch <= 'Z' ? ch + ('a' - 'A') : ch); + } + + public int length() { + return end - start; + } + + @Override + public String toString() { + return string.substring(start, end); + } + + } + + /** + * Search over the list of heads and find all heads with a relevance above a certain threshold. + * Will simplify the query string in an attempt to improve matches. + * + * @param query The search term. + * @param heads The heads we are checking for matches. + * @param threshold The threshold relevance that a head must have to be matched. + * @return All heads sorted by relevance that have a relevance greater than the threshold. + */ + public static List searchHeads(String query, Iterable heads, double threshold) { + return new Search(query, threshold).checkAll(heads); + } + +} diff --git a/src/nl/marido/deluxeheads/UpdateChecker.java b/src/nl/marido/deluxeheads/handlers/UpdateChecker.java similarity index 89% rename from src/nl/marido/deluxeheads/UpdateChecker.java rename to src/nl/marido/deluxeheads/handlers/UpdateChecker.java index ecba0f2..1edcc0d 100644 --- a/src/nl/marido/deluxeheads/UpdateChecker.java +++ b/src/nl/marido/deluxeheads/handlers/UpdateChecker.java @@ -1,4 +1,4 @@ -package nl.marido.deluxeheads; +package nl.marido.deluxeheads.handlers; import java.io.BufferedReader; import java.io.IOException; @@ -6,9 +6,11 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; +import nl.marido.deluxeheads.DeluxeHeads; + public class UpdateChecker { - private static final String versionURL = "https://api.spigotmc.org/legacy/update.php?resource=13402"; + private static String versionURL = "https://api.spigotmc.org/legacy/update.php?resource=13402"; public static String getCurrentVersion() { return DeluxeHeads.getInstance().getDescription().getVersion(); diff --git a/src/plugin.yml b/src/plugin.yml index 8854d0e..384790b 100644 --- a/src/plugin.yml +++ b/src/plugin.yml @@ -2,7 +2,7 @@ main: nl.marido.deluxeheads.DeluxeHeads author: Marido name: DeluxeHeads description: Enhance your server with over 17,000 awesome unique heads with amazing features. -version: 2.1.4 +version: 2.1.5 api-version: 1.13 softdepend: [Vault, PlayerPoints, BlockStore] loadbefore: [DeluxeMenus]