From de97f55968d183cc7d76aad87e3b27d382bfdbc9 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 31 May 2020 01:59:47 +1000 Subject: 1.5 --- .../moulberry/notenoughupdates/NEUManager.java | 1119 ++++++++++++++++++++ 1 file changed, 1119 insertions(+) create mode 100644 src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java (limited to 'src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java') diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java b/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java new file mode 100644 index 00000000..08939cca --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java @@ -0,0 +1,1119 @@ +package io.github.moulberry.notenoughupdates; + +import com.google.common.io.CharSource; +import com.google.gson.*; +import io.github.moulberry.notenoughupdates.options.Options; +import io.github.moulberry.notenoughupdates.util.HypixelApi; +import javafx.scene.control.Alert; +import net.minecraft.client.Minecraft; +import net.minecraft.init.Items; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.*; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.ResourceLocation; +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.input.ReaderInputStream; +import org.apache.commons.lang3.tuple.Pair; +import org.lwjgl.opengl.Display; + +import javax.swing.*; +import java.io.*; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.text.NumberFormat; +import java.util.*; +import java.util.zip.GZIPInputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +public class NEUManager { + + private final NotEnoughUpdates neu; + public final NEUIO neuio; + public final Gson gson; + + private TreeMap itemMap = new TreeMap<>(); + + private TreeMap> tagWordMap = new TreeMap<>(); + private TreeMap>> titleWordMap = new TreeMap<>(); + private TreeMap>> loreWordMap = new TreeMap<>(); + + public String viewItemAttemptID = null; + public long viewItemAttemptTime = 0; + + public String currentProfile = ""; + public final HypixelApi hypixelApi = new HypixelApi(); + + private ResourceLocation wkZip = new ResourceLocation("notenoughupdates:wkhtmltox.zip"); + private Map itemstackCache = new HashMap<>(); + + private static final String AUCTIONS_PRICE_URL = "https://moulberry.github.io/files/auc_avg_jsons/average_3day.json.gz"; + private JsonObject auctionPricesJson = null; + private long auctionLastUpdate = 0; + + private HashMap craftCost = new HashMap<>(); + + private HashMap> usagesMap = new HashMap<>(); + + public File configLocation; + private File itemsLocation; + private File itemShaLocation; + private JsonObject itemShaConfig; + private File configFile; + public Options config; + + public NEUManager(NotEnoughUpdates neu, NEUIO neuio, File configLocation) { + this.neu = neu; + this.configLocation = configLocation; + this.neuio = neuio; + + GsonBuilder gsonBuilder = new GsonBuilder().setPrettyPrinting(); + gsonBuilder.registerTypeAdapter(Options.Option.class, Options.createSerializer()); + gsonBuilder.registerTypeAdapter(Options.Option.class, Options.createDeserializer()); + gson = gsonBuilder.create(); + + this.configFile = new File(configLocation, "config.json"); + try { + configFile.createNewFile(); + config = Options.loadFromFile(gson, configFile); + } catch(Exception e) { + config = new Options(); + } + + this.itemsLocation = new File(configLocation, "items"); + itemsLocation.mkdir(); + + this.itemShaLocation = new File(configLocation, "itemSha.json"); + try { + itemShaLocation.createNewFile(); + itemShaConfig = getJsonFromFile(itemShaLocation); + if(itemShaConfig == null) itemShaConfig = new JsonObject(); + } catch(IOException e) { } + + File wkShell = new File(configLocation, "wkhtmltox/bin/wkhtmltoimage"); + if(!wkShell.exists()) { + try { + InputStream is = Minecraft.getMinecraft().getResourceManager().getResource(wkZip).getInputStream(); + unzip(is, configLocation); + } catch (IOException e) { + } + } + + //Unused code, used to automatically grab items from auctions. Leaving here in case I need it. + /*try { + for(int j=0; j<=89; j++) { + JsonObject auctions0 = getJsonFromFile(new File(configLocation, "auctions/auctions"+j+".json")); + + JsonArray arr = auctions0.getAsJsonArray("auctions"); + for(int i=0; i tag.toString().length()) { + writeItemJson(internalname, itemid, displayname, lore, info, clickcommand, damage, tag); + } + } + + } catch(Exception e) { + } + } + } + } catch(Exception e) { + e.printStackTrace(); + } + + throw new RuntimeException();*/ + } + + public class CraftInfo { + public boolean fromRecipe = false; + public float craftCost = -1; + } + + public CraftInfo getCraftCost(String internalname) { + if(craftCost.containsKey(internalname)) { + return craftCost.get(internalname); + } else { + CraftInfo ci = new CraftInfo(); + + JsonObject auctionInfo = getItemAuctionInfo(internalname); + JsonObject bazaarInfo = getBazaarInfo(internalname); + + if(bazaarInfo != null) { + float bazaarInstantBuyPrice = bazaarInfo.get("curr_buy").getAsFloat(); + ci.craftCost = bazaarInstantBuyPrice; + } + if(auctionInfo != null) { + float auctionPrice = auctionInfo.get("price").getAsFloat() / auctionInfo.get("count").getAsFloat(); + if(ci.craftCost < 0 || auctionPrice < ci.craftCost) { + ci.craftCost = auctionPrice; + } + } + JsonObject item = getItemInformation().get(internalname); + if(item != null && item.has("recipe")) { + float craftPrice = 0; + JsonObject recipe = item.get("recipe").getAsJsonObject(); + + String[] x = {"1","2","3"}; + String[] y = {"A","B","C"}; + for(int i=0; i<9; i++) { + String name = y[i/3]+x[i%3]; + String itemS = recipe.get(name).getAsString(); + if(itemS.length() == 0) continue; + + int count = 1; + if(itemS != null && itemS.split(":").length == 2) { + count = Integer.valueOf(itemS.split(":")[1]); + itemS = itemS.split(":")[0]; + } + float compCost = getCraftCost(itemS).craftCost * count; + if(compCost < 0) { + craftCost.put(internalname, ci); + return ci; + } else { + craftPrice += compCost; + } + } + + if(ci.craftCost < 0 || craftPrice < ci.craftCost) { + ci.craftCost = craftPrice; + ci.fromRecipe = true; + } + } + craftCost.put(internalname, ci); + return ci; + } + } + + public void saveConfig() throws IOException { + config.saveToFile(gson, configFile); + } + + public void updatePrices() { + if(System.currentTimeMillis() - auctionLastUpdate > 1000*60*30) { //30 minutes + craftCost.clear(); + System.out.println("UPDATING PRICE INFORMATION"); + auctionLastUpdate = System.currentTimeMillis(); + try(Reader inReader = new InputStreamReader(new GZIPInputStream(new URL(AUCTIONS_PRICE_URL).openStream()))) { + auctionPricesJson = gson.fromJson(inReader, JsonObject.class); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public JsonObject getAuctionPricesJson() { + return auctionPricesJson; + } + + public JsonObject getItemAuctionInfo(String internalname) { + JsonElement e = auctionPricesJson.get("prices").getAsJsonObject().get(internalname); + if(e == null) { + return null; + } + return e.getAsJsonObject(); + } + + public JsonObject getBazaarInfo(String internalname) { + JsonElement e = auctionPricesJson.get("bazaar").getAsJsonObject().get(internalname); + if(e == null) { + return null; + } + return e.getAsJsonObject(); + } + + public float getCostOfEnchants(String internalname, NBTTagCompound tag) { + float costOfEnchants = 0; + JsonObject info = getItemAuctionInfo(internalname); + if(info == null || !info.has("price")) { + return 0; + } + float price = getItemAuctionInfo(internalname).get("price").getAsFloat(); + if(tag.hasKey("ExtraAttributes")) { + NBTTagCompound ea = tag.getCompoundTag("ExtraAttributes"); + if(ea.hasKey("enchantments")) { + JsonObject ench_prices = auctionPricesJson.get("ench_prices").getAsJsonObject(); + + NBTTagCompound enchs = ea.getCompoundTag("enchantments"); + for(String ench : enchs.getKeySet()) { + int level = enchs.getInteger(ench); + + for(Map.Entry entry : ench_prices.entrySet()) { + if(matchEnch(ench, level, entry.getKey())) { + costOfEnchants += entry.getValue().getAsJsonObject().get("A").getAsFloat()*price + + entry.getValue().getAsJsonObject().get("B").getAsFloat(); + break; + } + } + } + } + } + return costOfEnchants; + } + + private boolean matchEnch(String ench, int level, String id) { + String idEnch = id.split(":")[0]; + String idLevel = id.split(":")[1]; + + if(!ench.equals(idEnch)) { + return false; + } + + if(String.valueOf(level).equals(idLevel)) { + return true; + } + + if(idLevel.startsWith("LE")) { + int idLevelI = Integer.valueOf(idLevel.substring(2)); + return level <= idLevelI; + } else if(idLevel.startsWith("GE")) { + int idLevelI = Integer.valueOf(idLevel.substring(2)); + return level >= idLevelI; + } + + return false; + } + + /** + * Parses a file in to a JsonObject. + */ + public JsonObject getJsonFromFile(File file) throws IOException { + InputStream in = new FileInputStream(file); + BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); + JsonObject json = gson.fromJson(reader, JsonObject.class); + return json; + } + + /** + * Called when the game is first loaded. Compares the local repository to the github repository and handles + * the downloading of new/updated files. This then calls the "loadItem" method for every item in the local + * repository. + */ + public void loadItemInformation() { + if(config.autoupdate.value) { + JOptionPane pane = new JOptionPane("Getting items to download from remote repository."); + JDialog dialog = pane.createDialog("NotEnoughUpdates Remote Sync"); + dialog.setModal(false); + //dialog.setVisible(true); + + if (Display.isActive()) dialog.toFront(); + + HashMap oldShas = new HashMap<>(); + for (Map.Entry entry : itemShaConfig.entrySet()) { + if (new File(itemsLocation, entry.getKey() + ".json").exists()) { + oldShas.put(entry.getKey() + ".json", entry.getValue().getAsString()); + } + } + Map changedFiles = neuio.getChangedItems(oldShas); + + if (changedFiles != null) { + for (Map.Entry changedFile : changedFiles.entrySet()) { + itemShaConfig.addProperty(changedFile.getKey().substring(0, changedFile.getKey().length() - 5), + changedFile.getValue()); + } + try { + writeJson(itemShaConfig, itemShaLocation); + } catch (IOException e) { + } + } + + if (Display.isActive()) dialog.toFront(); + + if (changedFiles != null && changedFiles.size() <= 20) { + Map downloads = neuio.getItemsDownload(changedFiles.keySet()); + + String startMessage = "NotEnoughUpdates: Syncing with remote repository ("; + int downloaded = 0; + + for (Map.Entry entry : downloads.entrySet()) { + pane.setMessage(startMessage + (++downloaded) + "/" + downloads.size() + ")\nCurrent: " + entry.getKey()); + dialog.pack(); + dialog.setVisible(true); + if (Display.isActive()) dialog.toFront(); + + File item = new File(itemsLocation, entry.getKey()); + try { + item.createNewFile(); + } catch (IOException e) { + } + try (BufferedInputStream inStream = new BufferedInputStream(new URL(entry.getValue()).openStream()); + FileOutputStream fileOutputStream = new FileOutputStream(item)) { + byte dataBuffer[] = new byte[1024]; + int bytesRead; + while ((bytesRead = inStream.read(dataBuffer, 0, 1024)) != -1) { + fileOutputStream.write(dataBuffer, 0, bytesRead); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } else { + //TODO: Store hard-coded value somewhere else + String dlUrl = "https://github.com/Moulberry/NotEnoughUpdates-REPO/archive/master.zip"; + + pane.setMessage("Downloading NEU Master Archive. (DL# >20)"); + dialog.pack(); + dialog.setVisible(true); + if (Display.isActive()) dialog.toFront(); + + File itemsZip = new File(configLocation, "neu-items-master.zip"); + try { + itemsZip.createNewFile(); + } catch (IOException e) { + } + try (BufferedInputStream inStream = new BufferedInputStream(new URL(dlUrl).openStream()); + FileOutputStream fileOutputStream = new FileOutputStream(itemsZip)) { + byte dataBuffer[] = new byte[1024]; + int bytesRead; + while ((bytesRead = inStream.read(dataBuffer, 0, 1024)) != -1) { + fileOutputStream.write(dataBuffer, 0, bytesRead); + } + } catch (IOException e) { + e.printStackTrace(); + } + + pane.setMessage("Unzipping NEU Master Archive."); + dialog.pack(); + dialog.setVisible(true); + if (Display.isActive()) dialog.toFront(); + + unzipIgnoreFirstFolder(itemsZip.getAbsolutePath(), configLocation.getAbsolutePath()); + } + + dialog.dispose(); + } + + for(File f : itemsLocation.listFiles()) { + loadItem(f.getName().substring(0, f.getName().length()-5)); + } + } + + /** + * Loads the item in to the itemMap and also stores various words associated with this item + * in to titleWordMap and loreWordMap. These maps are used in the searching algorithm. + * @param internalName + */ + public void loadItem(String internalName) { + itemstackCache.remove(internalName); + try { + JsonObject json = getJsonFromFile(new File(itemsLocation, internalName + ".json")); + if(json == null) { + return; + } + itemMap.put(internalName, json); + + if(json.has("recipe")) { + JsonObject recipe = json.get("recipe").getAsJsonObject(); + + String[] x = {"1","2","3"}; + String[] y = {"A","B","C"}; + for(int i=0; i<9; i++) { + String name = y[i/3]+x[i%3]; + String itemS = recipe.get(name).getAsString(); + if(itemS != null && itemS.split(":").length == 2) { + itemS = itemS.split(":")[0]; + } + + if(!usagesMap.containsKey(itemS)) { + usagesMap.put(itemS, new HashSet<>()); + } + usagesMap.get(itemS).add(internalName); + } + } + + if(json.has("displayname")) { + int wordIndex=0; + for(String str : json.get("displayname").getAsString().split(" ")) { + str = clean(str); + if(!titleWordMap.containsKey(str)) { + titleWordMap.put(str, new HashMap<>()); + } + if(!titleWordMap.get(str).containsKey(internalName)) { + titleWordMap.get(str).put(internalName, new ArrayList<>()); + } + titleWordMap.get(str).get(internalName).add(wordIndex); + wordIndex++; + } + } + + if(json.has("lore")) { + int wordIndex=0; + for(JsonElement element : json.get("lore").getAsJsonArray()) { + for(String str : element.getAsString().split(" ")) { + str = clean(str); + if(!loreWordMap.containsKey(str)) { + loreWordMap.put(str, new HashMap<>()); + } + if(!loreWordMap.get(str).containsKey(internalName)) { + loreWordMap.get(str).put(internalName, new ArrayList<>()); + } + loreWordMap.get(str).get(internalName).add(wordIndex); + wordIndex++; + } + } + } + } catch(IOException e) { + e.printStackTrace(); + } + } + + /** + * Searches a string for a query. This method is used to mimic the behaviour of the + * more complex map-based search function. This method is used for the chest-item-search feature. + */ + public boolean searchString(String toSearch, String query) { + int lastMatch = -1; + + toSearch = clean(toSearch).toLowerCase(); + query = clean(query).toLowerCase(); + String[] splitToSeach = toSearch.split(" "); + out: + for(String s : query.split(" ")) { + for(int i=0; i search(String query) { + LinkedHashSet results = new LinkedHashSet<>(); + if(query.startsWith("title:")) { + query = query.substring(6); + results.addAll(new TreeSet<>(search(query, titleWordMap))); + } else if(query.startsWith("desc:")) { + query = query.substring(5); + results.addAll(new TreeSet<>(search(query, loreWordMap))); + } else if(query.startsWith("id:")) { + query = query.substring(3); + results.addAll(new TreeSet<>(subMapWithKeysThatAreSuffixes(query.toUpperCase(), itemMap).keySet())); + } else { + if(!query.trim().contains(" ")) { + StringBuilder sb = new StringBuilder(); + for(char c : query.toCharArray()) { + sb.append(c).append(" "); + } + results.addAll(new TreeSet<>(search(sb.toString(), titleWordMap))); + } + results.addAll(new TreeSet<>(search(query, titleWordMap))); + results.addAll(new TreeSet<>(search(query, loreWordMap))); + } + return results; + } + + /** + * Splits a search query into an array of strings delimited by a space character. Then, matches the query to + * the start of words in the various maps (title & lore). The small query does not need to match the whole entry + * of the map, only the beginning. eg. "ench" and "encha" will both match "enchanted". All sub queries must + * follow a word matching the previous sub query. eg. "ench po" will match "enchanted pork" but will not match + * "pork enchanted". + */ + private Set search(String query, TreeMap>> wordMap) { + HashMap> matches = null; + + query = clean(query).toLowerCase(); + for(String queryWord : query.split(" ")) { + HashMap> matchesToKeep = new HashMap<>(); + for(HashMap> wordMatches : subMapWithKeysThatAreSuffixes(queryWord, wordMap).values()) { + if(wordMatches != null && !wordMatches.isEmpty()) { + if(matches == null) { + //Copy all wordMatches to titleMatches + for(String internalname : wordMatches.keySet()) { + if(!matchesToKeep.containsKey(internalname)) { + matchesToKeep.put(internalname, new ArrayList<>()); + } + matchesToKeep.get(internalname).addAll(wordMatches.get(internalname)); + } + } else { + for(String internalname : matches.keySet()) { + if(wordMatches.containsKey(internalname)) { + for(Integer newIndex : wordMatches.get(internalname)) { + if(matches.get(internalname).contains(newIndex-1)) { + if(!matchesToKeep.containsKey(internalname)) { + matchesToKeep.put(internalname, new ArrayList<>()); + } + matchesToKeep.get(internalname).add(newIndex); + } + } + } + } + } + } + } + if(matchesToKeep.isEmpty()) return new HashSet<>(); + matches = matchesToKeep; + } + + return matches.keySet(); + } + + /** + * From https://stackoverflow.com/questions/10711494/get-values-in-treemap-whose-string-keys-start-with-a-pattern + */ + public Map subMapWithKeysThatAreSuffixes(String prefix, NavigableMap map) { + if ("".equals(prefix)) return map; + String lastKey = createLexicographicallyNextStringOfTheSameLenght(prefix); + return map.subMap(prefix, true, lastKey, false); + } + + String createLexicographicallyNextStringOfTheSameLenght(String input) { + final int lastCharPosition = input.length()-1; + String inputWithoutLastChar = input.substring(0, lastCharPosition); + char lastChar = input.charAt(lastCharPosition) ; + char incrementedLastChar = (char) (lastChar + 1); + return inputWithoutLastChar+incrementedLastChar; + } + + private String clean(String str) { + return str.replaceAll("(\u00a7.)|[^0-9a-zA-Z ]", "").toLowerCase().trim(); + } + + public void showRecipe(JsonObject item) { + if(item.has("useneucraft") && item.get("useneucraft").getAsBoolean()) { + displayGuiItemRecipe(item.get("internalname").getAsString(), ""); + } else if(item.has("clickcommand")) { + String clickcommand = item.get("clickcommand").getAsString(); + + if(clickcommand.equals("viewrecipe")) { + neu.sendChatMessage( + "/" + clickcommand + " " + + item.get("internalname").getAsString().split(";")[0]); + viewItemAttemptID = item.get("internalname").getAsString(); + viewItemAttemptTime = System.currentTimeMillis(); + } else if(clickcommand.equals("viewpotion")) { + neu.sendChatMessage( + "/" + clickcommand + " " + + item.get("internalname").getAsString().split(";")[0].toLowerCase()); + viewItemAttemptID = item.get("internalname").getAsString(); + viewItemAttemptTime = System.currentTimeMillis(); + } + } + } + + /** + * Takes an item stack and produces a JsonObject. This is used in the item editor. + */ + public JsonObject getJsonForItem(ItemStack stack) { + NBTTagCompound tag = stack.getTagCompound() == null ? new NBTTagCompound() : stack.getTagCompound(); + + //Item lore + String[] lore = new String[0]; + if(tag.hasKey("display", 10)) { + NBTTagCompound display = tag.getCompoundTag("display"); + + if(display.hasKey("Lore", 9)) { + NBTTagList list = display.getTagList("Lore", 8); + lore = new String[list.tagCount()]; + for(int i=0; i 0 && (lore[lore.length-1].contains("Click to view recipes!") || + lore[lore.length-1].contains("Click to view recipe!"))) { + String[] lore2 = new String[lore.length-2]; + System.arraycopy(lore, 0, lore2, 0, lore.length-2); + lore = lore2; + } + + JsonObject json = new JsonObject(); + json.addProperty("itemid", stack.getItem().getRegistryName()); + json.addProperty("displayname", stack.getDisplayName()); + json.addProperty("nbttag", tag.toString()); + json.addProperty("damage", stack.getItemDamage()); + + JsonArray jsonlore = new JsonArray(); + for(String line : lore) { + jsonlore.add(new JsonPrimitive(line)); + } + json.add("lore", jsonlore); + + return json; + } + + public String getInternalNameForItem(ItemStack stack) { + NBTTagCompound tag = stack.getTagCompound(); + //Internal id + if(tag != null && tag.hasKey("ExtraAttributes", 10)) { + NBTTagCompound ea = tag.getCompoundTag("ExtraAttributes"); + + if(ea.hasKey("id", 8)) { + return ea.getString("id").replaceAll(":", "-"); + } + } + return null; + } + + //Currently unused in production. + public void writeItemToFile(ItemStack stack) { + String internalname = getInternalNameForItem(stack); + + if(internalname == null) { + return; + } + + JsonObject json = getJsonForItem(stack); + json.addProperty("internalname", internalname); + json.addProperty("clickcommand", ""); + json.addProperty("modver", NotEnoughUpdates.VERSION); + + try { + writeJson(json, new File(itemsLocation, internalname+".json")); + } catch (IOException e) {} + + loadItem(internalname); //Collection: Cocoa Beans VII + } + + public JsonObject createItemJson(String internalname, String itemid, String displayname, String[] lore, + String crafttext, String infoType, String[] info, + String clickcommand, int damage, NBTTagCompound nbttag) { + return createItemJson(new JsonObject(), internalname, itemid, displayname, lore, crafttext, infoType, info, clickcommand, damage, nbttag); + } + + public JsonObject createItemJson(JsonObject base, String internalname, String itemid, String displayname, String[] lore, + String crafttext, String infoType, String[] info, + String clickcommand, int damage, NBTTagCompound nbttag) { + if(internalname == null || internalname.isEmpty()) { + return null; + } + + JsonObject json = gson.fromJson(gson.toJson(base, JsonObject.class), JsonObject.class); + json.addProperty("internalname", internalname); + json.addProperty("itemid", itemid); + json.addProperty("displayname", displayname); + json.addProperty("crafttext", crafttext); + json.addProperty("clickcommand", clickcommand); + json.addProperty("damage", damage); + json.addProperty("nbttag", nbttag.toString()); + json.addProperty("modver", NotEnoughUpdates.VERSION); + json.addProperty("infoType", infoType.toString()); + + if(info != null && info.length > 0) { + JsonArray jsoninfo = new JsonArray(); + for (String line : info) { + jsoninfo.add(new JsonPrimitive(line)); + } + json.add("info", jsoninfo); + } + + JsonArray jsonlore = new JsonArray(); + for(String line : lore) { + jsonlore.add(new JsonPrimitive(line)); + } + json.add("lore", jsonlore); + + return json; + } + + public boolean writeItemJson(String internalname, String itemid, String displayname, String[] lore, String crafttext, + String infoType, String[] info, String clickcommand, int damage, NBTTagCompound nbttag) { + return writeItemJson(new JsonObject(), internalname, itemid, displayname, lore, crafttext, infoType, info, clickcommand, damage, nbttag); + } + + public boolean writeItemJson(JsonObject base, String internalname, String itemid, String displayname, String[] lore, + String crafttext, String infoType, String[] info, String clickcommand, int damage, NBTTagCompound nbttag) { + JsonObject json = createItemJson(base, internalname, itemid, displayname, lore, crafttext, infoType, info, clickcommand, damage, nbttag); + if(json == null) { + return false; + } + + try { + writeJsonDefaultDir(json, internalname+".json"); + } catch(IOException e) { + return false; + } + + loadItem(internalname); + return true; + } + + public boolean uploadItemJson(String internalname, String itemid, String displayname, String[] lore, String crafttext, String infoType, String[] info, + String clickcommand, int damage, NBTTagCompound nbttag) { + JsonObject json = createItemJson(internalname, itemid, displayname, lore, crafttext, infoType, info, clickcommand, damage, nbttag); + if(json == null) { + return false; + } + + String username = Minecraft.getMinecraft().thePlayer.getName(); + String newBranchName = UUID.randomUUID().toString().substring(0, 8) + "-" + internalname + "-" + username; + String prTitle = internalname + "-" + username; + String prBody = "Internal name: " + internalname + "\nSubmitted by: " + username; + String file = "items/"+internalname+".json"; + if(!neuio.createNewRequest(newBranchName, prTitle, prBody, file, gson.toJson(json))) { + return false; + } + + try { + writeJsonDefaultDir(json, internalname+".json"); + } catch(IOException e) { + return false; + } + + loadItem(internalname); + return true; + } + + public void writeJson(JsonObject json, File file) throws IOException { + file.createNewFile(); + + try(BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) { + writer.write(gson.toJson(json)); + } + } + + public void writeJsonDefaultDir(JsonObject json, String filename) throws IOException { + File file = new File(itemsLocation, filename); + writeJson(json, file); + } + + public TreeMap getItemInformation() { + return itemMap; + } + + /** + * Stolen from https://www.journaldev.com/960/java-unzip-file-example + */ + private static void unzipIgnoreFirstFolder(String zipFilePath, String destDir) { + File dir = new File(destDir); + // create output directory if it doesn't exist + if(!dir.exists()) dir.mkdirs(); + FileInputStream fis; + //buffer for read and write data to file + byte[] buffer = new byte[1024]; + try { + fis = new FileInputStream(zipFilePath); + ZipInputStream zis = new ZipInputStream(fis); + ZipEntry ze = zis.getNextEntry(); + while(ze != null){ + if(!ze.isDirectory()) { + String fileName = ze.getName(); + fileName = fileName.substring(fileName.split("/")[0].length()+1); + File newFile = new File(destDir + File.separator + fileName); + //create directories for sub directories in zip + new File(newFile.getParent()).mkdirs(); + FileOutputStream fos = new FileOutputStream(newFile); + int len; + while ((len = zis.read(buffer)) > 0) { + fos.write(buffer, 0, len); + } + fos.close(); + } + //close this ZipEntry + zis.closeEntry(); + ze = zis.getNextEntry(); + } + //close last ZipEntry + zis.closeEntry(); + zis.close(); + fis.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Stolen from https://www.journaldev.com/960/java-unzip-file-example + */ + private static void unzip(InputStream src, File dest) { + //buffer for read and write data to file + byte[] buffer = new byte[1024]; + try { + ZipInputStream zis = new ZipInputStream(src); + ZipEntry ze = zis.getNextEntry(); + while(ze != null){ + if(!ze.isDirectory()) { + String fileName = ze.getName(); + File newFile = new File(dest, fileName); + //create directories for sub directories in zip + new File(newFile.getParent()).mkdirs(); + FileOutputStream fos = new FileOutputStream(newFile); + int len; + while ((len = zis.read(buffer)) > 0) { + fos.write(buffer, 0, len); + } + fos.close(); + } + //close this ZipEntry + zis.closeEntry(); + ze = zis.getNextEntry(); + } + //close last ZipEntry + zis.closeEntry(); + zis.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public ItemStack jsonToStack(JsonObject json) { + if(itemstackCache.containsKey(json.get("internalname").getAsString())) { + return itemstackCache.get(json.get("internalname").getAsString()).copy(); + } + ItemStack stack = new ItemStack(Item.itemRegistry.getObject( + new ResourceLocation(json.get("itemid").getAsString()))); + + if(stack.getItem() == null) { + stack = new ItemStack(Items.diamond, 1, 10); //Purple broken texture item + } else { + if(json.has("damage")) { + stack.setItemDamage(json.get("damage").getAsInt()); + } + + if(json.has("nbttag")) { + try { + NBTTagCompound tag = JsonToNBT.getTagFromJson(json.get("nbttag").getAsString()); + stack.setTagCompound(tag); + } catch(NBTException e) { + } + } + + if(json.has("lore")) { + NBTTagCompound display = stack.getTagCompound().getCompoundTag("display"); + NBTTagList lore = new NBTTagList(); + for(JsonElement line : json.get("lore").getAsJsonArray()) { + String lineStr = line.getAsString(); + if(!lineStr.contains("Click to view recipes!") && + !lineStr.contains("Click to view recipe!")) { + lore.appendTag(new NBTTagString(lineStr)); + } + } + display.setTag("Lore", lore); + NBTTagCompound tag = stack.getTagCompound(); + tag.setTag("display", display); + stack.setTagCompound(tag); + } + } + + itemstackCache.put(json.get("internalname").getAsString(), stack); + return stack; + } + + public boolean displayGuiItemUsages(String internalName, String text) { + List craftMatrices = new ArrayList<>(); + List results = new ArrayList<>(); + + if(!usagesMap.containsKey(internalName)) { + return false; + } + + for(String internalNameResult : usagesMap.get(internalName)) { + JsonObject item = getItemInformation().get(internalNameResult); + results.add(item); + + if(item != null && item.has("recipe")) { + JsonObject recipe = item.get("recipe").getAsJsonObject(); + + ItemStack[] craftMatrix = new ItemStack[9]; + + String[] x = {"1","2","3"}; + String[] y = {"A","B","C"}; + for(int i=0; i<9; i++) { + String name = y[i/3]+x[i%3]; + String itemS = recipe.get(name).getAsString(); + int count = 1; + if(itemS != null && itemS.split(":").length == 2) { + count = Integer.valueOf(itemS.split(":")[1]); + itemS = itemS.split(":")[0]; + } + JsonObject craft = getItemInformation().get(itemS); + if(craft != null) { + ItemStack stack = jsonToStack(craft); + stack.stackSize = count; + craftMatrix[i] = stack; + } + } + + craftMatrices.add(craftMatrix); + } + } + + if(craftMatrices.size() > 0) { + Minecraft.getMinecraft().displayGuiScreen(new GuiItemUsages(craftMatrices, results, text, this)); + return true; + } + return false; + } + + public boolean displayGuiItemRecipe(String internalName, String text) { + JsonObject item = getItemInformation().get(internalName); + if(item != null && item.has("recipe")) { + JsonObject recipe = item.get("recipe").getAsJsonObject(); + + ItemStack[] craftMatrix = new ItemStack[9]; + + String[] x = {"1","2","3"}; + String[] y = {"A","B","C"}; + for(int i=0; i<9; i++) { + String name = y[i/3]+x[i%3]; + String itemS = recipe.get(name).getAsString(); + int count = 1; + if(itemS != null && itemS.split(":").length == 2) { + count = Integer.valueOf(itemS.split(":")[1]); + itemS = itemS.split(":")[0]; + } + JsonObject craft = getItemInformation().get(itemS); + if(craft != null) { + ItemStack stack = jsonToStack(craft); + stack.stackSize = count; + craftMatrix[i] = stack; + } + } + + Minecraft.getMinecraft().displayGuiScreen(new GuiItemRecipe(craftMatrix, item, text, this)); + return true; + } + return false; + } + + public boolean failViewItem(String text) { + if(viewItemAttemptID != null && !viewItemAttemptID.isEmpty()) { + if(System.currentTimeMillis() - viewItemAttemptTime < 500) { + return displayGuiItemRecipe(viewItemAttemptID, text); + } + } + return false; + } + + public File getWebFile(String url) { + File f = new File(configLocation, "tmp/"+Base64.getEncoder().encodeToString(url.getBytes())+".html"); + if(f.exists()) { + return f; + } + + try { + f.getParentFile().mkdirs(); + f.createNewFile(); + f.deleteOnExit(); + } catch (IOException e) { + return null; + } + try (BufferedInputStream inStream = new BufferedInputStream(new URL(url+"?action=raw&templates=expand").openStream()); + FileOutputStream fileOutputStream = new FileOutputStream(f)) { + byte dataBuffer[] = new byte[1024]; + int bytesRead; + while ((bytesRead = inStream.read(dataBuffer, 0, 1024)) != -1) { + fileOutputStream.write(dataBuffer, 0, bytesRead); + } + } catch (IOException e) { + e.printStackTrace(); + return null; + } + + return f; + } + +} -- cgit