diff options
9 files changed, 393 insertions, 41 deletions
diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java index 416158d67..be6cbe41e 100644 --- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java +++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.java @@ -15,11 +15,10 @@ import at.hannibal2.skyhanni.items.HideNotClickableItems; import at.hannibal2.skyhanni.items.ItemDisplayOverlayFeatures; import at.hannibal2.skyhanni.items.abilitycooldown.ItemAbilityCooldown; import at.hannibal2.skyhanni.misc.*; +import at.hannibal2.skyhanni.repo.RepoManager; import at.hannibal2.skyhanni.test.LorenzTest; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import java.io.*; -import java.nio.charset.StandardCharsets; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; import net.minecraftforge.common.MinecraftForge; @@ -29,6 +28,9 @@ import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.TickEvent; +import java.io.*; +import java.nio.charset.StandardCharsets; + @Mod(modid = SkyHanniMod.MODID, version = SkyHanniMod.VERSION) public class SkyHanniMod { @@ -41,6 +43,7 @@ public class SkyHanniMod { private final Gson gson = new GsonBuilder().setPrettyPrinting().excludeFieldsWithoutExposeAnnotation().create(); public static File configDirectory; + public static RepoManager repo; @EventHandler public void preInit(FMLPreInitializationEvent event) { @@ -94,6 +97,9 @@ public class SkyHanniMod { saveConfig(); } Runtime.getRuntime().addShutdownHook(new Thread(this::saveConfig)); + + repo = new RepoManager(configDirectory); + repo.loadRepoInformation(); } public void saveConfig() { diff --git a/src/main/java/at/hannibal2/skyhanni/config/Features.java b/src/main/java/at/hannibal2/skyhanni/config/Features.java index 4d81e8f01..4120d6581 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/Features.java +++ b/src/main/java/at/hannibal2/skyhanni/config/Features.java @@ -13,7 +13,9 @@ import net.minecraft.client.Minecraft; public class Features { private void editOverlay(String activeConfig, int width, int height, Position position) { - Minecraft.getMinecraft().displayGuiScreen(new GuiPositionEditor(position, width, height, () -> {}, () -> {}, () -> SkyHanniMod.screenToOpen = new GuiScreenElementWrapper(new ConfigEditor(SkyHanniMod.feature, activeConfig)))); + Minecraft.getMinecraft().displayGuiScreen(new GuiPositionEditor(position, width, height, () -> { + }, () -> { + }, () -> SkyHanniMod.screenToOpen = new GuiScreenElementWrapper(new ConfigEditor(SkyHanniMod.feature, activeConfig)))); } public void executeRunnable(String runnableId) { @@ -53,9 +55,6 @@ public class Features { } @Expose - public String apiKey = ""; - - @Expose @Category(name = "Chat", desc = "Chat related features.") public Chat chat = new Chat(); @@ -80,6 +79,10 @@ public class Features { public Misc misc = new Misc(); @Expose + @Category(name = "Apis", desc = "Api Data") + public ApiData apiData = new ApiData(); + + @Expose @Category(name = "Debug", desc = "Debug and test stuff.") public Debug debug = new Debug(); @@ -293,6 +296,18 @@ public class Features { public boolean configButtonOnPause = true; } + public static class ApiData { + + @Expose + public String apiKey = ""; + + @Expose + @ConfigOption(name = "Repo Auto Update", desc = "Update the repository on every startup.") + @ConfigEditorBoolean + public boolean repoAutoUpdate = true; + + } + public static class Debug { @Expose diff --git a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.java b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.java index 74e65d0f8..8f3b798f1 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.java +++ b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.java @@ -9,7 +9,7 @@ import org.apache.commons.lang3.StringUtils; public class Commands { - private static final SimpleCommand.ProcessCommandRunnable settingsRunnable = new SimpleCommand.ProcessCommandRunnable() { + private static final SimpleCommand.ProcessCommandRunnable mainMenu = new SimpleCommand.ProcessCommandRunnable() { public void processCommand(ICommandSender sender, String[] args) { if (args.length > 0) { SkyHanniMod.screenToOpen = new GuiScreenElementWrapper(new ConfigEditor(SkyHanniMod.feature, StringUtils.join(args, " "))); @@ -20,7 +20,19 @@ public class Commands { }; public static void init() { - ClientCommandHandler.instance.registerCommand(new SimpleCommand("sh", settingsRunnable)); - ClientCommandHandler.instance.registerCommand(new SimpleCommand("skyhanni", settingsRunnable)); + ClientCommandHandler.instance.registerCommand(new SimpleCommand("sh", mainMenu)); + ClientCommandHandler.instance.registerCommand(new SimpleCommand("skyhanni", mainMenu)); + + ClientCommandHandler.instance.registerCommand(new SimpleCommand("shreloadlocalrepo", new SimpleCommand.ProcessCommandRunnable() { + public void processCommand(ICommandSender sender, String[] args) { + SkyHanniMod.repo.reloadLocalRepo(); + } + })); + + ClientCommandHandler.instance.registerCommand(new SimpleCommand("shupdaterepo", new SimpleCommand.ProcessCommandRunnable() { + public void processCommand(ICommandSender sender, String[] args) { + SkyHanniMod.repo.updateRepo(); + } + })); } } diff --git a/src/main/java/at/hannibal2/skyhanni/events/RepositoryReloadEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/RepositoryReloadEvent.kt new file mode 100644 index 000000000..841133ae9 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/events/RepositoryReloadEvent.kt @@ -0,0 +1,12 @@ +package at.hannibal2.skyhanni.events + +import at.hannibal2.skyhanni.repo.RepoUtils +import com.google.gson.Gson +import com.google.gson.JsonObject +import java.io.File + +class RepositoryReloadEvent(val repoLocation: File, val gson: Gson): LorenzEvent() { + fun getConstant(constant: String): JsonObject? { + return RepoUtils.getConstant(repoLocation, constant, gson) + } +}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/items/HideNotClickableItems.kt b/src/main/java/at/hannibal2/skyhanni/items/HideNotClickableItems.kt index fc47c73b5..61673554d 100644 --- a/src/main/java/at/hannibal2/skyhanni/items/HideNotClickableItems.kt +++ b/src/main/java/at/hannibal2/skyhanni/items/HideNotClickableItems.kt @@ -3,12 +3,14 @@ package at.hannibal2.skyhanni.items import at.hannibal2.skyhanni.SkyHanniMod import at.hannibal2.skyhanni.bazaar.BazaarApi import at.hannibal2.skyhanni.events.GuiContainerEvent +import at.hannibal2.skyhanni.events.RepositoryReloadEvent import at.hannibal2.skyhanni.utils.ItemUtils import at.hannibal2.skyhanni.utils.ItemUtils.cleanName import at.hannibal2.skyhanni.utils.ItemUtils.getLore import at.hannibal2.skyhanni.utils.LorenzColor import at.hannibal2.skyhanni.utils.LorenzUtils import at.hannibal2.skyhanni.utils.LorenzUtils.removeColorCodes +import at.hannibal2.skyhanni.utils.MultiFilter import at.hannibal2.skyhanni.utils.RenderUtils.highlight import net.minecraft.client.Minecraft import net.minecraft.client.gui.inventory.GuiChest @@ -27,6 +29,22 @@ class HideNotClickableItems { private var lastClickTime = 0L private var bypassUntil = 0L + private val hideNpcSellList = MultiFilter() + private val hideBackpackList = MultiFilter() + + @SubscribeEvent + fun onRepoReload(event: RepositoryReloadEvent) { + val constant = event.getConstant("HideNotClickableItems")!! + + try { + hideNpcSellList.load(constant["hide_npc_sell"].asJsonObject) + hideBackpackList.load(constant["hide_backpack"].asJsonObject) + } catch (e: Exception) { + e.printStackTrace() + LorenzUtils.error("error in RepositoryReloadEvent") + } + } + @SubscribeEvent fun onBackgroundDrawn(event: GuiContainerEvent.BackgroundDrawnEvent) { if (!LorenzUtils.inSkyblock) return @@ -251,27 +269,7 @@ class HideNotClickableItems { } if (!ItemUtils.isRecombobulated(stack)) { - when (name) { - "Health Potion VIII Splash Potion" -> return false - "Stone Button" -> return false - "Revive Stone" -> return false - "Premium Flesh" -> return false - "Defuse Kit" -> return false - "White Wool" -> return false - "Enchanted Wool" -> return false - "Training Weights" -> return false - "Journal Entry" -> return false - "Twilight Arrow Poison" -> return false - "Lever" -> return false - - "Fairy's Galoshes" -> return false - "Blaze Powder" -> return false - "Egg" -> return false - "Corrupted Fragment" -> return false - } - if (name.endsWith("Gem Rune I")) return false - - if (name.startsWith("Music Disc")) return false + if (hideNpcSellList.match(name)) return false } hideReason = "This item should not be sold at the NPC!" @@ -292,14 +290,15 @@ class HideNotClickableItems { return true } - val result = when { - name.endsWith(" New Year Cake Bag") -> true - name == "Nether Wart Pouch" -> true - name == "Basket of Seeds" -> true - name == "Builder's Wand" -> true - - else -> false - } + val result = hideBackpackList.match(name) +// val result = when { +// name.endsWith("New Year Cake Bag") -> true +// name == "Nether Wart Pouch" -> true +// name == "Basket of Seeds" -> true +// name == "Builder's Wand" -> true +// +// else -> false +// } if (result) hideReason = "Bags cannot be put into the storage!" return result diff --git a/src/main/java/at/hannibal2/skyhanni/misc/ApiData.kt b/src/main/java/at/hannibal2/skyhanni/misc/ApiData.kt index 5cbedd164..66db57981 100644 --- a/src/main/java/at/hannibal2/skyhanni/misc/ApiData.kt +++ b/src/main/java/at/hannibal2/skyhanni/misc/ApiData.kt @@ -17,7 +17,7 @@ class ApiData { fun onStatusBar(event: LorenzChatEvent) { val message = event.message if (message.startsWith("§aYour new API key is §r§b")) { - SkyHanniMod.feature.apiKey = message.substring(26) + SkyHanniMod.feature.apiData.apiKey = message.substring(26) LorenzUtils.chat("§b[SkyHanni] A new API Key has been detected and installed") if (currentProfileName != "") { @@ -35,7 +35,7 @@ class ApiData { private fun updateApiData() { val uuid = Minecraft.getMinecraft().thePlayer.uniqueID.toString().replace("-", "") - val apiKey = SkyHanniMod.feature.apiKey + val apiKey = SkyHanniMod.feature.apiData.apiKey if (apiKey.isEmpty()) { LorenzUtils.error("SkyHanni has no API Key set. Type /api new to reload.") @@ -72,7 +72,7 @@ class ApiData { } private fun loadProfile(playerUuid: String, profileId: String) { - val apiKey = SkyHanniMod.feature.apiKey + val apiKey = SkyHanniMod.feature.apiData.apiKey val url = "https://api.hypixel.net/skyblock/profile?key=$apiKey&profile=$profileId" val jsonObject = APIUtil.getJSONResponse(url) diff --git a/src/main/java/at/hannibal2/skyhanni/repo/RepoManager.kt b/src/main/java/at/hannibal2/skyhanni/repo/RepoManager.kt new file mode 100644 index 000000000..db9170c9b --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/repo/RepoManager.kt @@ -0,0 +1,171 @@ +package at.hannibal2.skyhanni.repo + +import at.hannibal2.skyhanni.SkyHanniMod +import at.hannibal2.skyhanni.events.RepositoryReloadEvent +import at.hannibal2.skyhanni.utils.LorenzUtils +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import com.google.gson.JsonObject +import net.minecraft.client.Minecraft +import org.apache.commons.io.FileUtils +import java.io.* +import java.net.URL +import java.nio.charset.StandardCharsets +import java.util.concurrent.CompletableFuture +import java.util.concurrent.atomic.AtomicBoolean + +class RepoManager(private val configLocation: File) { + val gson: Gson = GsonBuilder().setPrettyPrinting().create() + private var latestRepoCommit: String? = null + private val repoLocation: File = File(configLocation, "repo") + + fun loadRepoInformation() { + if (SkyHanniMod.feature.apiData.repoAutoUpdate) { + fetchRepository().thenRun(this::reloadRepository) + } else { + reloadRepository() + } + } + + private val atomicShouldManuallyReload = AtomicBoolean(false)//TODO FIX + + fun updateRepo() { + atomicShouldManuallyReload.set(true) + fetchRepository(true).thenRun { this.reloadRepository("Repo updated successful :)") } + } + + fun reloadLocalRepo() { + atomicShouldManuallyReload.set(true) + reloadRepository("Repo loaded from local files successful :)") + } + + private fun fetchRepository(command: Boolean = false): CompletableFuture<Boolean> { + return CompletableFuture.supplyAsync { + try { + val currentCommitJSON: JsonObject? = getJsonFromFile(File(configLocation, "currentCommit.json")) + latestRepoCommit = null + try { + InputStreamReader(URL(getCommitApiUrl()).openStream()) + .use { inReader -> + val commits: JsonObject = gson.fromJson(inReader, JsonObject::class.java) + latestRepoCommit = commits["sha"].asString + } + } catch (e: Exception) { + e.printStackTrace() + } + if (latestRepoCommit == null || latestRepoCommit!!.isEmpty()) return@supplyAsync false + if (File(configLocation, "repo").exists()) { + if (currentCommitJSON != null && currentCommitJSON["sha"].asString == latestRepoCommit) { + if (command) { + LorenzUtils.chat("§e[SkyHanni] §7The repo is already up to date!") + atomicShouldManuallyReload.set(false) + } + return@supplyAsync false + } + } + RepoUtils.recursiveDelete(repoLocation) + repoLocation.mkdirs() + val itemsZip = File(repoLocation, "sh-repo-main.zip") + try { + itemsZip.createNewFile() + } catch (e: IOException) { + return@supplyAsync false + } + val url = URL(getDownloadUrl(latestRepoCommit)) + val urlConnection = url.openConnection() + urlConnection.connectTimeout = 15000 + urlConnection.readTimeout = 30000 + try { + urlConnection.getInputStream().use { `is` -> + FileUtils.copyInputStreamToFile( + `is`, + itemsZip + ) + } + } catch (e: IOException) { + e.printStackTrace() + System.err.println("Failed to download SkyHanni Repo! Please report this issue to the mod creator") + if (command) { + LorenzUtils.error("An error occurred while trying to reload the repo! See logs for more info.") + } + return@supplyAsync false + } + RepoUtils.unzipIgnoreFirstFolder( + itemsZip.absolutePath, + repoLocation.absolutePath + ) + if (currentCommitJSON == null || currentCommitJSON["sha"].asString != latestRepoCommit) { + val newCurrentCommitJSON = JsonObject() + newCurrentCommitJSON.addProperty("sha", latestRepoCommit) + try { + writeJson(newCurrentCommitJSON, File(configLocation, "currentCommit.json")) + } catch (ignored: IOException) { + } + } + } catch (e: Exception) { + e.printStackTrace() + } + true + } + } + + private fun reloadRepository(answerMessage: String = ""): CompletableFuture<Void?> { + val comp = CompletableFuture<Void?>() + if (!atomicShouldManuallyReload.get()) return comp + Minecraft.getMinecraft().addScheduledTask { + try { + RepositoryReloadEvent(repoLocation, gson).postAndCatch() + comp.complete(null) + if (answerMessage.isNotEmpty()) { + LorenzUtils.chat("§e[SkyHanni] §a$answerMessage") + } + } catch (e: java.lang.Exception) { + comp.completeExceptionally(e) + LorenzUtils.error("An error occurred while trying to reload the repo! See logs for more info.") + } + } + return comp + } + + /** + * Parses a file in to a JsonObject. + */ + private fun getJsonFromFile(file: File?): JsonObject? { + try { + BufferedReader( + InputStreamReader( + FileInputStream(file), + StandardCharsets.UTF_8 + ) + ).use { reader -> + return gson.fromJson(reader, JsonObject::class.java) + } + } catch (e: java.lang.Exception) { + return null + } + } + + private fun getCommitApiUrl(): String { + val repoUser = "hannibal00212" + val repoName = "SkyHanni-REPO" + val repoBranch = "main" + return String.format("https://api.github.com/repos/%s/%s/commits/%s", repoUser, repoName, repoBranch) + } + + private fun getDownloadUrl(commitId: String?): String { + val repoUser = "hannibal00212" + val repoName = "SkyHanni-REPO" + return String.format("https://github.com/%s/%s/archive/%s.zip", repoUser, repoName, commitId) + } + + @Throws(IOException::class) + fun writeJson(json: JsonObject?, file: File) { + file.createNewFile() + BufferedWriter( + OutputStreamWriter( + FileOutputStream(file), + StandardCharsets.UTF_8 + ) + ).use { writer -> writer.write(gson.toJson(json)) } + } +}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/repo/RepoUtils.kt b/src/main/java/at/hannibal2/skyhanni/repo/RepoUtils.kt new file mode 100644 index 000000000..efe656e1c --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/repo/RepoUtils.kt @@ -0,0 +1,102 @@ +package at.hannibal2.skyhanni.repo + +import com.google.gson.Gson +import com.google.gson.JsonObject +import java.io.* +import java.nio.charset.StandardCharsets +import java.nio.file.Files +import java.util.zip.ZipInputStream + +object RepoUtils { + + fun recursiveDelete(file: File) { + if (file.isDirectory && !Files.isSymbolicLink(file.toPath())) { + for (child in file.listFiles()) { + recursiveDelete(child) + } + } + file.delete() + } + + /** + * Modified from https://www.journaldev.com/960/java-unzip-file-example + */ + fun unzipIgnoreFirstFolder(zipFilePath: String, destDir: String) { + val dir = File(destDir) + // create output directory if it doesn't exist + if (!dir.exists()) dir.mkdirs() + val fis: FileInputStream + //buffer for read and write data to file + val buffer = ByteArray(1024) + try { + fis = FileInputStream(zipFilePath) + val zis = ZipInputStream(fis) + var ze = zis.nextEntry + while (ze != null) { + if (!ze.isDirectory) { + var fileName = ze.name + fileName = fileName.substring(fileName.split("/").toTypedArray()[0].length + 1) + val newFile = File(destDir + File.separator + fileName) + //create directories for sub directories in zip + File(newFile.parent).mkdirs() + if (!isInTree(dir, newFile)) { + throw RuntimeException( + "SkyHanni detected an invalid zip file. This is a potential security risk, please report this on the SkyHanni discord." + ) + } + val fos = FileOutputStream(newFile) + var len: Int + while (zis.read(buffer).also { len = it } > 0) { + fos.write(buffer, 0, len) + } + fos.close() + } + //close this ZipEntry + zis.closeEntry() + ze = zis.nextEntry + } + //close last ZipEntry + zis.closeEntry() + zis.close() + fis.close() + } catch (e: IOException) { + e.printStackTrace() + } + } + + @Throws(IOException::class) + private fun isInTree(rootDirectory: File, file: File): Boolean { + var rootDirectory = rootDirectory + var file: File? = file + file = file!!.canonicalFile + rootDirectory = rootDirectory.canonicalFile + while (file != null) { + if (file == rootDirectory) return true + file = file.parentFile + } + return false + } + + fun getConstant(repoLocation: File, constant: String, gson: Gson): JsonObject? { + return getConstant(repoLocation, constant, gson, JsonObject::class.java) + } + + private fun <T> getConstant(repo: File, constant: String, gson: Gson, clazz: Class<T>?): T? { + if (repo.exists()) { + val jsonFile = File(repo, "constants/$constant.json") + try { + BufferedReader( + InputStreamReader( + FileInputStream(jsonFile), + StandardCharsets.UTF_8 + ) + ).use { reader -> + return gson.fromJson(reader, clazz) + } + } catch (e: Exception) { + return null + } + } + return null + } +}
\ No newline at end of file diff --git a/src/main/java/at/hannibal2/skyhanni/utils/MultiFilter.kt b/src/main/java/at/hannibal2/skyhanni/utils/MultiFilter.kt new file mode 100644 index 000000000..13968c0ae --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/MultiFilter.kt @@ -0,0 +1,35 @@ +package at.hannibal2.skyhanni.utils + +import com.google.gson.JsonObject + +class MultiFilter { + + val equals = mutableListOf<String>() + val startsWith = mutableListOf<String>() + val endsWith = mutableListOf<String>() + + fun load(hideNpcSell: JsonObject) { + equals.clear() + startsWith.clear() + endsWith.clear() + + for (element in hideNpcSell["equals"].asJsonArray) { + equals.add(element.asString) + } + for (element in hideNpcSell["startsWith"].asJsonArray) { + startsWith.add(element.asString) + } + for (element in hideNpcSell["endsWith"].asJsonArray) { + endsWith.add(element.asString) + } + } + + fun match(name: String): Boolean { + if (equals.contains(name)) return true + + if (startsWith.any { name.startsWith(it) }) return true + if (endsWith.any { name.endsWith(it) }) return true + + return false + } +}
\ No newline at end of file |