diff options
author | Lorenz <ESs95s3P5z8Pheb> | 2022-07-07 00:31:50 +0200 |
---|---|---|
committer | Lorenz <ESs95s3P5z8Pheb> | 2022-07-07 00:31:50 +0200 |
commit | 99773d6a593c444151503de315f127bea6f74d49 (patch) | |
tree | 9ee1ae505e5f82aba62f10c882af85a3acd6e483 /src/main/java/at/lorenz/mod | |
download | skyhanni-99773d6a593c444151503de315f127bea6f74d49.tar.gz skyhanni-99773d6a593c444151503de315f127bea6f74d49.tar.bz2 skyhanni-99773d6a593c444151503de315f127bea6f74d49.zip |
init lorenz mod
Diffstat (limited to 'src/main/java/at/lorenz/mod')
24 files changed, 2169 insertions, 0 deletions
diff --git a/src/main/java/at/lorenz/mod/GuiContainerHook.kt b/src/main/java/at/lorenz/mod/GuiContainerHook.kt new file mode 100644 index 000000000..55b30e964 --- /dev/null +++ b/src/main/java/at/lorenz/mod/GuiContainerHook.kt @@ -0,0 +1,61 @@ +package at.lorenz.mod + +import at.lorenz.mod.events.GuiContainerEvent +import at.lorenz.mod.events.GuiContainerEvent.CloseWindowEvent +import at.lorenz.mod.events.GuiContainerEvent.SlotClickEvent +import net.minecraft.client.gui.inventory.GuiContainer +import net.minecraft.inventory.Slot +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo + +class GuiContainerHook(guiAny: Any) { + + val gui: GuiContainer + + init { + gui = guiAny as GuiContainer + } + + fun closeWindowPressed(ci: CallbackInfo) { + if (CloseWindowEvent(gui, gui.inventorySlots).postAndCatch()) ci.cancel() + } + + fun backgroundDrawn(mouseX: Int, mouseY: Int, partialTicks: Float, ci: CallbackInfo) { + GuiContainerEvent.BackgroundDrawnEvent( + gui, + gui.inventorySlots, + mouseX, + mouseY, + partialTicks + ).postAndCatch() + } + + fun foregroundDrawn(mouseX: Int, mouseY: Int, partialTicks: Float, ci: CallbackInfo) { + GuiContainerEvent.ForegroundDrawnEvent(gui, gui.inventorySlots, mouseX, mouseY, partialTicks).postAndCatch() + } + + fun onDrawSlot(slot: Slot, ci: CallbackInfo) { + if (GuiContainerEvent.DrawSlotEvent.Pre( + gui, + gui.inventorySlots, + slot + ).postAndCatch() + ) ci.cancel() + } + + fun onDrawSlotPost(slot: Slot, ci: CallbackInfo) { + GuiContainerEvent.DrawSlotEvent.Post(gui, gui.inventorySlots, slot).postAndCatch() + } + + fun onMouseClick(slot: Slot?, slotId: Int, clickedButton: Int, clickType: Int, ci: CallbackInfo) { + if ( + SlotClickEvent( + gui, + gui.inventorySlots, + slot, + slotId, + clickedButton, + clickType + ).postAndCatch() + ) ci.cancel() + } +}
\ No newline at end of file diff --git a/src/main/java/at/lorenz/mod/HideNotClickableItems.kt b/src/main/java/at/lorenz/mod/HideNotClickableItems.kt new file mode 100644 index 000000000..3f94a1479 --- /dev/null +++ b/src/main/java/at/lorenz/mod/HideNotClickableItems.kt @@ -0,0 +1,444 @@ +package at.lorenz.mod + +import at.lorenz.mod.bazaar.BazaarApi +import at.lorenz.mod.config.LorenzConfig +import at.lorenz.mod.utils.LorenzUtils.Companion.removeColorCodes +import at.lorenz.mod.events.GuiContainerEvent +import at.lorenz.mod.utils.ItemUtils +import at.lorenz.mod.utils.ItemUtils.Companion.cleanName +import at.lorenz.mod.utils.ItemUtils.Companion.getLore +import at.lorenz.mod.utils.LorenzColor +import at.lorenz.mod.utils.LorenzUtils +import at.lorenz.mod.utils.RenderUtil.Companion.highlight +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.inventory.GuiChest +import net.minecraft.client.renderer.GlStateManager +import net.minecraft.inventory.ContainerChest +import net.minecraft.item.ItemStack +import net.minecraftforge.event.entity.player.ItemTooltipEvent +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import org.lwjgl.opengl.GL11 + +class HideNotClickableItems { + + private var hideReason = "" + + private var lastClickTime = 0L + private var bypassUntil = 0L + + @SubscribeEvent + fun onBackgroundDrawn(event: GuiContainerEvent.BackgroundDrawnEvent) { + if (isDisabled()) return + if (event.gui !is GuiChest) return + val guiChest = event.gui + val chest = guiChest.inventorySlots as ContainerChest + val chestName = chest.lowerChestInventory.displayName.unformattedText.trim() + + val lightingState = GL11.glIsEnabled(GL11.GL_LIGHTING) + GlStateManager.disableLighting() + GlStateManager.color(1f, 1f, 1f, 1f) + + for (slot in chest.inventorySlots) { + if (slot == null) continue + + if (slot.slotNumber == slot.slotIndex) continue + if (slot.stack == null) continue + + if (hide(chestName, slot.stack)) { + slot highlight LorenzColor.GRAY + } + } + + if (lightingState) GlStateManager.enableLighting() + } + + @SubscribeEvent + fun onDrawSlot(event: GuiContainerEvent.DrawSlotEvent.Pre) { + if (isDisabled()) return + if (event.gui !is GuiChest) return + val guiChest = event.gui + val chest = guiChest.inventorySlots as ContainerChest + val chestName = chest.lowerChestInventory.displayName.unformattedText.trim() + + val slot = event.slot + if (slot.slotNumber == slot.slotIndex) return + if (slot.stack == null) return + + val stack = slot.stack + + if (hide(chestName, stack)) { + event.isCanceled = true + } + } + + @SubscribeEvent + fun onTooltip(event: ItemTooltipEvent) { + if (isDisabled()) return + if (event.toolTip == null) return + val guiChest = Minecraft.getMinecraft().currentScreen + if (guiChest !is GuiChest) return + val chest = guiChest.inventorySlots as ContainerChest + val chestName = chest.lowerChestInventory.displayName.unformattedText.trim() + + val stack = event.itemStack + if (ItemUtils.getItemsInOpenChest().contains(stack)) return + + if (hide(chestName, stack)) { + val first = event.toolTip[0] + event.toolTip.clear() + event.toolTip.add("§7" + first.removeColorCodes()) + event.toolTip.add("") + if (hideReason == "") { + event.toolTip.add("§4No hide reason!") + LorenzUtils.warning("Not hide reason for not clickable item!") + } else { + event.toolTip.add("§c$hideReason") + } + } + } + + @SubscribeEvent + fun onSlotClick(event: GuiContainerEvent.SlotClickEvent) { + if (isDisabled()) return + if (event.gui !is GuiChest) return + val guiChest = event.gui + val chest = guiChest.inventorySlots as ContainerChest + val chestName = chest.lowerChestInventory.displayName.unformattedText.trim() + + val slot = event.slot ?: return + + if (slot.slotNumber == slot.slotIndex) return + if (slot.stack == null) return + + val stack = slot.stack + + if (hide(chestName, stack)) { + event.isCanceled = true +// SoundQueue.addToQueue("note.bass", 0.5f, 1f) + + if (System.currentTimeMillis() > lastClickTime + 5_000) { + lastClickTime = System.currentTimeMillis() +// EssentialAPI.getNotifications() +// .push( +// "§cThis item cannot be moved!", +// "§eClick here §fto disable this feature for 10 seconds!\n" + +// "§fOr disable it forever: §6/st §f+ '§6Hide Not Clickable Items§f'.", +// 5f, +// action = { +// bypassUntil = System.currentTimeMillis() + 10_000 +// }) + } + return + } + } + + private fun isDisabled(): Boolean { + if (bypassUntil > System.currentTimeMillis()) return true + + return !LorenzConfig.hideNotClickableItems + } + + private fun hide(chestName: String, stack: ItemStack): Boolean { + hideReason = "" + return when { + hideNpcSell(chestName, stack) -> true + hideChestBackpack(chestName, stack) -> true + hideSalvage(chestName, stack) -> true + hideTrade(chestName, stack) -> true + hideBazaarOrAH(chestName, stack) -> true + hideAccessoryBag(chestName, stack) -> true + hideSackOfSacks(chestName, stack) -> true + hideFishingBag(chestName, stack) -> true + hidePotionBag(chestName, stack) -> true + + else -> false + } + } + + private fun hidePotionBag(chestName: String, stack: ItemStack): Boolean { + if (!chestName.startsWith("Potion Bag")) return false + + val name = stack.cleanName() + if (isSkyBlockMenuItem(name)) { + hideReason = "The SkyBlock Menu cannot be put into the potion bag!" + return true + } + + if (stack.cleanName().endsWith(" Potion")) return false + + hideReason = "This item is not a potion!" + return true + } + + private fun hideFishingBag(chestName: String, stack: ItemStack): Boolean { + if (!chestName.startsWith("Fishing Bag")) return false + + val name = stack.cleanName() + if (isSkyBlockMenuItem(name)) { + hideReason = "The SkyBlock Menu cannot be put into the fishing bag!" + return true + } + + if (stack.cleanName().endsWith(" Bait")) return false + + hideReason = "This item is not a fishing bait!" + return true + } + + private fun hideSackOfSacks(chestName: String, stack: ItemStack): Boolean { + if (!chestName.startsWith("Sack of Sacks")) return false + + val name = stack.cleanName() + if (ItemUtils.isSack(name)) return false + if (isSkyBlockMenuItem(name)) return false + + hideReason = "This item is not a sack!" + return true + } + + private fun hideAccessoryBag(chestName: String, stack: ItemStack): Boolean { + if (!chestName.startsWith("Accessory Bag")) return false + + if (stack.getLore().any { it.contains("ACCESSORY") }) return false + if (isSkyBlockMenuItem(stack.cleanName())) return false + + hideReason = "This item is not an accessory!" + return true + } + + private fun hideTrade(chestName: String, stack: ItemStack): Boolean { + if (!chestName.startsWith("You ")) return false + + if (ItemUtils.isCoOpSoulBound(stack)) { + hideReason = "Coop-Soulbound items cannot be traded!" + return true + } + + val name = stack.cleanName() + + if (ItemUtils.isSack(name)) { + hideReason = "Sacks cannot be traded!" + return true + } + + if (isSkyBlockMenuItem(name)) { + hideReason = "The SkyBlock Menu cannot be traded!" + return true + } + + val result = when { + name.contains("Personal Deletor") -> true + name.contains("Day Crystal") -> true + name.contains("Night Crystal") -> true + name.contains("Cat Talisman") -> true + name.contains("Lynx Talisman") -> true + name.contains("Cheetah Talisman") -> true + else -> false + } + + if (result) hideReason = "This item cannot be traded!" + return result + } + + private fun hideNpcSell(chestName: String, stack: ItemStack): Boolean { + if (chestName != "Trades" && chestName != "Ophelia") return false + + var name = stack.cleanName() + val size = stack.stackSize + val amountText = " x$size" + if (name.endsWith(amountText)) { + name = name.substring(0, name.length - amountText.length) + } + + if (isSkyBlockMenuItem(name)) { + hideReason = "The SkyBlock Menu cannot be sold at the NPC!" + return true + } + + 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 + + "Fairy's Galoshes" -> return false + } + + if (name.startsWith("Music Disc")) return false + } + + hideReason = "This item should not be sold at the NPC!" + return true + } + + private fun hideChestBackpack(chestName: String, stack: ItemStack): Boolean { + if (!chestName.contains("Ender Chest") && !chestName.contains("Backpack")) return false + + val name = stack.cleanName() + + if (isSkyBlockMenuItem(name)) { + hideReason = "The SkyBlock Menu cannot be put into the storage!" + return true + } + if (ItemUtils.isSack(name)) { + hideReason = "Sacks cannot be put into the storage!" + 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 + } + + if (result) hideReason = "Bags cannot be put into the storage!" + return result + } + + private fun hideSalvage(chestName: String, stack: ItemStack): Boolean { + if (chestName != "Salvage Item") return false + + val name = stack.cleanName() + + val armorSets = listOf( + "Zombie Knight", + "Heavy", + "Zombie Soldier", + "Skeleton Grunt", + "Skeleton Soldier", + "Zombie Commander", + "Skeleton Master", + "Sniper", + "Skeletor", + "Rotten", + ) + + val items = mutableListOf<String>() + for (armor in armorSets) { + items.add("$armor Helmet") + items.add("$armor Chestplate") + items.add("$armor Leggings") + items.add("$armor Boots") + } + + items.add("Zombie Soldier Cutlass") + items.add("Silent Death") + items.add("Zombie Knight Sword") + items.add("Conjuring") + items.add("Dreadlord Sword") + items.add("Soulstealer Bow") + items.add("Machine Gun Bow") + items.add("Earth Shard") + items.add("Zombie Commander Whip") + + for (item in items) { + if (name.endsWith(" $item")) { + + if (ItemUtils.isRecombobulated(stack)) { + hideReason = "This item should not be salvaged! (Recombobulated)" + return true + } +// val rarity = stack.getSkyBlockRarity() +// if (rarity == ItemRarity.LEGENDARY || rarity == ItemRarity.MYTHIC) { +// hideReason = "This item should not be salvaged! (Rarity)" +// return true +// } + + return false + } + } + + if (isSkyBlockMenuItem(name)) { + hideReason = "The SkyBlock Menu cannot be salvaged!" + return true + } + + hideReason = "This item cannot be salvaged!" + return true + } + + private fun hideBazaarOrAH(chestName: String, stack: ItemStack): Boolean { + val bazaarInventory = BazaarApi.isBazaarInventory(chestName) + + val auctionHouseInventory = + chestName == "Co-op Auction House" || chestName == "Auction House" || chestName == "Create BIN Auction" || chestName == "Create Auction" + if (!bazaarInventory && !auctionHouseInventory) return false + + + val displayName = stack.displayName + + if (isSkyBlockMenuItem(displayName.removeColorCodes())) { + if (bazaarInventory) hideReason = "The SkyBlock Menu is not a Bazaar Product!" + if (auctionHouseInventory) hideReason = "The SkyBlock Menu cannot be auctioned!" + return true + } + + if (bazaarInventory != BazaarApi.isBazaarItem(displayName)) { + if (bazaarInventory) hideReason = "This item is not a Bazaar Product!" + if (auctionHouseInventory) hideReason = "Bazaar Products cannot be auctioned!" + + return true + } + + if (isNotAuctionable(stack)) return true + + return false + } + + private fun isNotAuctionable(stack: ItemStack): Boolean { + if (ItemUtils.isCoOpSoulBound(stack)) { + hideReason = "Coop-Soulbound items cannot be auctioned!" + return true + } + + val name = stack.cleanName() + + if (ItemUtils.isSack(name)) { + hideReason = "Sacks cannot be auctioned!" + return true + } + + val result = when { + name.contains("Personal Deletor") -> true + name.contains("Day Crystal") -> true + name.contains("Night Crystal") -> true + + name.contains("Cat Talisman") -> true + name.contains("Lynx Talisman") -> true + name.contains("Cheetah Talisman") -> true + + name.contains("Hoe of Great Tilling") -> true + name.contains("Hoe of Greater Tilling") -> true + name.contains("InfiniDirt") -> true + name.contains("Prismapump") -> true + name.contains("Mathematical Hoe Blueprint") -> true + name.contains("Basket of Seeds") -> true + name.contains("Nether Wart Pouch") -> true + + name.contains("Carrot Hoe") -> true + name.contains("Sugar Cane Hoe") -> true + name.contains("Nether Warts Hoe") -> true + name.contains("Potato Hoe") -> true + name.contains("Melon Dicer") -> true + name.contains("Pumpkin Dicer") -> true + name.contains("Coco Chopper") -> true + name.contains("Wheat Hoe") -> true + + else -> false + } + + if (result) hideReason = "This item cannot be auctioned!" + return result + } + + private fun isSkyBlockMenuItem(name: String): Boolean = name == "SkyBlock Menu (Right Click)" +} diff --git a/src/main/java/at/lorenz/mod/bazaar/BazaarApi.kt b/src/main/java/at/lorenz/mod/bazaar/BazaarApi.kt new file mode 100644 index 000000000..28ce228e2 --- /dev/null +++ b/src/main/java/at/lorenz/mod/bazaar/BazaarApi.kt @@ -0,0 +1,59 @@ +package at.lorenz.mod.bazaar + +import at.lorenz.mod.utils.LorenzUtils + +class BazaarApi { + + companion object { + private val bazaarMap = mutableMapOf<String, BazaarData>() + + fun isBazaarInventory(inventoryName: String): Boolean { + if (inventoryName.contains(" ➜ ") && !inventoryName.contains("Museum")) return true + if (BazaarOrderHelper.isBazaarOrderInventory(inventoryName)) return true + + return when (inventoryName) { + "Your Bazaar Orders" -> true + "How many do you want?" -> true + "How much do you want to pay?" -> true + "Confirm Buy Order" -> true + "Confirm Instant Buy" -> true + "At what price are you selling?" -> true + "Confirm Sell Offer" -> true + "Order options" -> true + + else -> false + } + } + + fun getCleanBazaarName(name: String): String { + if (name.endsWith(" Gemstone")) { + return name.substring(6) + } + if (name.startsWith("§")) { + return name.substring(2) + } + + return name + } + + fun getBazaarDataForName(name: String): BazaarData { + if (bazaarMap.containsKey(name)) { + val bazaarData = bazaarMap[name] + if (bazaarData != null) { + return bazaarData + } + LorenzUtils.error("Bazaar data is null for item '$name'") + } + throw Error("no bz data found for name '$name'") + } + + fun isBazaarItem(name: String): Boolean { + val bazaarName = getCleanBazaarName(name) + return bazaarMap.containsKey(bazaarName) + } + } + + init { + BazaarDataGrabber(bazaarMap).start() + } +}
\ No newline at end of file diff --git a/src/main/java/at/lorenz/mod/bazaar/BazaarData.kt b/src/main/java/at/lorenz/mod/bazaar/BazaarData.kt new file mode 100644 index 000000000..a9f75370c --- /dev/null +++ b/src/main/java/at/lorenz/mod/bazaar/BazaarData.kt @@ -0,0 +1,3 @@ +package at.lorenz.mod.bazaar + +data class BazaarData(val apiName: String, val itemName: String, val sellPrice: Double, val buyPrice: Double)
\ No newline at end of file diff --git a/src/main/java/at/lorenz/mod/bazaar/BazaarDataGrabber.kt b/src/main/java/at/lorenz/mod/bazaar/BazaarDataGrabber.kt new file mode 100644 index 000000000..78341e05c --- /dev/null +++ b/src/main/java/at/lorenz/mod/bazaar/BazaarDataGrabber.kt @@ -0,0 +1,116 @@ +package at.lorenz.mod.bazaar + +import at.lorenz.mod.utils.APIUtil +import at.lorenz.mod.utils.LorenzUtils +import at.lorenz.mod.utils.LorenzUtils.Companion.round +import kotlin.concurrent.fixedRateTimer + +internal class BazaarDataGrabber(private var bazaarMap: MutableMap<String, BazaarData>) { + + companion object { + private val itemNames = mutableMapOf<String, String>() + + private var lastData = "" + var lastTime = 0L + var blockNoChange = false + var currentlyUpdating = false + } + + private fun loadItemNames(): Boolean { + currentlyUpdating = true + try { + val itemsData = APIUtil.getJSONResponse("https://api.hypixel.net/resources/skyblock/items") + for (element in itemsData["items"].asJsonArray) { + val jsonObject = element.asJsonObject + val name = jsonObject["name"].asString + val id = jsonObject["id"].asString + itemNames[id] = name + } + currentlyUpdating = false + return true + } catch (e: Throwable) { + e.printStackTrace() + LorenzUtils.error("Error while trying to read bazaar item list from api: " + e.message) + currentlyUpdating = false + return false + } + } + + fun start() { + fixedRateTimer(name = "lorenz-bazaar-update", period = 1000L) { + //TODO add +// if (!LorenzUtils.inSkyBlock) { +// return@fixedRateTimer +// } + + if (currentlyUpdating) { + LorenzUtils.error("Bazaar update took too long! Error?") + return@fixedRateTimer + } + + if (itemNames.isEmpty()) { + if (!loadItemNames()) { + return@fixedRateTimer + } + } + checkIfUpdateNeeded() + } + } + + private fun checkIfUpdateNeeded() { + if (lastData != "") { + if (System.currentTimeMillis() - lastTime > 9_000) { + blockNoChange = true + } else { + if (blockNoChange) { + return + } + } + } + + currentlyUpdating = true + updateBazaarData() + currentlyUpdating = false + } + + private fun updateBazaarData() { + val bazaarData = APIUtil.getJSONResponse("https://api.hypixel.net/skyblock/bazaar") + if (bazaarData.toString() != lastData) { + lastData = bazaarData.toString() + lastTime = System.currentTimeMillis() + } + + val products = bazaarData["products"].asJsonObject + + for (entry in products.entrySet()) { + val apiName = entry.key + + if (apiName == "ENCHANTED_CARROT_ON_A_STICK") continue + if (apiName == "BAZAAR_COOKIE") continue + + val itemData = entry.value.asJsonObject + + val itemName = itemNames.getOrDefault(apiName, null) + if (itemName == null) { + LorenzUtils.error("Bazaar item name is null for '$apiName'! Restart to fix this problem!") + continue + } + + val sellPrice: Double = try { + itemData["sell_summary"].asJsonArray[0].asJsonObject["pricePerUnit"].asDouble.round(1) + } catch (e: Exception) { +// LorenzUtils.warning("Bazaar buy order for $itemName not found!") + 0.0 + } + val buyPrice: Double = try { + itemData["buy_summary"].asJsonArray[0].asJsonObject["pricePerUnit"].asDouble.round(1) + } catch (e: Exception) { +// LorenzUtils.warning("Bazaar sell offers for $itemName not found!") + 0.0 + } + + val data = BazaarData(apiName, itemName, sellPrice, buyPrice) + bazaarMap[itemName] = data + } + } +}
\ No newline at end of file diff --git a/src/main/java/at/lorenz/mod/bazaar/BazaarOrderHelper.kt b/src/main/java/at/lorenz/mod/bazaar/BazaarOrderHelper.kt new file mode 100644 index 000000000..0daa12b7d --- /dev/null +++ b/src/main/java/at/lorenz/mod/bazaar/BazaarOrderHelper.kt @@ -0,0 +1,87 @@ +package at.lorenz.mod.bazaar + +import at.lorenz.mod.config.LorenzConfig +import at.lorenz.mod.events.GuiContainerEvent +import at.lorenz.mod.utils.ItemUtils.Companion.getLore +import at.lorenz.mod.utils.LorenzColor +import at.lorenz.mod.utils.RenderUtil.Companion.highlight +import net.minecraft.client.gui.inventory.GuiChest +import net.minecraft.client.renderer.GlStateManager +import net.minecraft.inventory.ContainerChest +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +import org.lwjgl.opengl.GL11 + +class BazaarOrderHelper { + + companion object { + fun isBazaarOrderInventory(inventoryName: String): Boolean = when (inventoryName) { + "Your Bazaar Orders" -> true + "Co-op Bazaar Orders" -> true + else -> false + } + } + + @SubscribeEvent + fun onBackgroundDrawn(event: GuiContainerEvent.BackgroundDrawnEvent) { + if (!LorenzConfig.lorenzBazaarOrderHelper) return + if (event.gui !is GuiChest) return + val guiChest = event.gui + val chest = guiChest.inventorySlots as ContainerChest + val inventoryName = chest.lowerChestInventory.displayName.unformattedText.trim() + + if (!isBazaarOrderInventory(inventoryName)) return + val lightingState = GL11.glIsEnabled(GL11.GL_LIGHTING) + GlStateManager.disableLighting() + GlStateManager.color(1f, 1f, 1f, 1f) + + out@ for (slot in chest.inventorySlots) { + if (slot == null) continue + if (slot.slotNumber != slot.slotIndex) continue + if (slot.stack == null) continue + + val stack = slot.stack + val displayName = stack.displayName + val isSelling = displayName.startsWith("§6§lSELL§7: ") + val isBuying = displayName.startsWith("§a§lBUY§7: ") + if (!isSelling && !isBuying) continue + + val text = displayName.split("§7: ")[1] + val name = BazaarApi.getCleanBazaarName(text) + val data = BazaarApi.getBazaarDataForName(name) + val buyPrice = data.buyPrice + val sellPrice = data.sellPrice + + val itemLore = stack.getLore() + for (line in itemLore) { + if (line.startsWith("§7Filled:")) { + if (line.endsWith(" §a§l100%!")) { + slot highlight LorenzColor.GREEN + continue@out + } + } + } + for (line in itemLore) { + if (line.startsWith("§7Price per unit:")) { + var text = line.split(": §6")[1] + text = text.substring(0, text.length - 6) + text = text.replace(",", "") + val price = text.toDouble() + if (isSelling) { + if (buyPrice < price) { + slot highlight LorenzColor.GOLD + continue@out + } + } else { + if (sellPrice > price) { + slot highlight LorenzColor.GOLD + continue@out + } + } + + } + } + } + + if (lightingState) GlStateManager.enableLighting() + } +} diff --git a/src/main/java/at/lorenz/mod/chat/ChatFilter.kt b/src/main/java/at/lorenz/mod/chat/ChatFilter.kt new file mode 100644 index 000000000..8e2160928 --- /dev/null +++ b/src/main/java/at/lorenz/mod/chat/ChatFilter.kt @@ -0,0 +1,279 @@ +package at.lorenz.mod.chat + +import at.lorenz.mod.config.LorenzConfig +import at.lorenz.mod.utils.LorenzUtils +import at.lorenz.mod.utils.LorenzUtils.Companion.matchRegex +import at.lorenz.mod.events.LorenzChatEvent +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +class ChatFilter { + + @SubscribeEvent + fun onChatMessage(event: LorenzChatEvent) { + if (!LorenzConfig.chatFilter) return + + val blockReason = block(event.message) + if (blockReason != "") { + event.blockedReason = blockReason + } + } + + private fun block(message: String): String = when { + message.startsWith("§aYou are playing on profile: §e") -> "profile"//TODO move into own class + lobby(message) -> "lobby" + empty(message) -> "empty" + warping(message) -> "warping" + welcome(message) -> "welcome" + guild(message) -> "guild" + PlayerChatFilter.shouldBlock(message) -> "player_chat" + killCombo(message) -> "kill_combo" + bazaarAndAHMiniMessages(message) -> "bz_ah_minis" + watchdogAnnouncement(message) -> "watchdog" + slayer(message) -> "slayer" + slayerDrop(message) -> "slayer_drop" + uselessDrop(message) -> "useless_drop" + uselessNotification(message) -> "useless_notification" + party(message) -> "party" + money(message) -> "money" + winterIsland(message) -> "winter_island" + uselessWarning(message) -> "useless_warning" + friendJoin(message) -> "friend_join" + + + else -> "" + } + + private fun friendJoin(message: String): Boolean { + return when { + message.matchRegex("§aFriend > §r(.*) §r§e(joined|left).") -> { + true + } + else -> false + } + + } + + private fun uselessNotification(message: String): Boolean { + return when { + message == "§eYour previous §r§6Plasmaflux Power Orb §r§ewas removed!" -> true + + else -> false + } + } + + private fun uselessWarning(message: String): Boolean = when { + message == "§cYou are sending commands too fast! Please slow down." -> true//TODO prevent in the future + message == "§cYou can't use this while in combat!" -> true + message == "§cYou can not modify your equipped armor set!" -> true + message == "§cPlease wait a few seconds between refreshing!" -> true + message == "§cThis item is not salvageable!" -> true//prevent in the future + message == "§cPlace a Dungeon weapon or armor piece above the anvil to salvage it!" -> true + message == "§cWhoa! Slow down there!" -> true + message == "§cWait a moment before confirming!" -> true + message == "§cYou need to be out of combat for 3 seconds before opening the SkyBlock Menu!" -> true//TODO prevent in the future + + else -> false + } + + private fun uselessDrop(message: String): Boolean { + when { + message.matchRegex("§6§lRARE DROP! §r§aEnchanted Ender Pearl (.*)") -> return true + + message.matchRegex("§6§lRARE DROP! §r§fCarrot (.*)") -> return true + message.matchRegex("§6§lRARE DROP! §r§fPotato (.*)") -> return true + + message.matchRegex("§6§lRARE DROP! §r§9Machine Gun Bow (.*)") -> return true + message.matchRegex("§6§lRARE DROP! §r§5Earth Shard (.*)") -> return true + message.matchRegex("§6§lRARE DROP! §r§5Zombie Lord Chestplate (.*)") -> return true + } + + return false + } + + private fun winterIsland(message: String): Boolean = when { + message.matchRegex(" §r§f☃ §r§7§r(.*) §r§7mounted a §r§fSnow Cannon§r§7!") -> true + + else -> false + } + + private fun money(message: String): Boolean { + if (isBazaar(message)) return true + if (isAuctionHouse(message)) return true + + return false + } + + private fun isAuctionHouse(message: String): Boolean { + if (message == "§b-----------------------------------------------------") return true + if (message == "§eVisit the Auction House to collect your item!") return true + + return false + } + + private fun isBazaar(message: String): Boolean { + if (message.matchRegex("§eBuy Order Setup! §r§a(.*)§r§7x (.*) §r§7for §r§6(.*) coins§r§7.")) return true + if (message.matchRegex("§eSell Offer Setup! §r§a(.*)§r§7x (.*) §r§7for §r§6(.*) coins§r§7.")) return true + if (message.matchRegex("§cCancelled! §r§7Refunded §r§6(.*) coins §r§7from cancelling buy order!")) return true + if (message.matchRegex("§cCancelled! §r§7Refunded §r§a(.*)§r§7x (.*) §r§7from cancelling sell offer!")) return true + + return false + } + + private fun party(message: String): Boolean { + if (message == "§9§m-----------------------------") return true + if (message == "§9§m-----------------------------------------------------") return true + + return false + } + + private fun slayerDrop(message: String): Boolean { + //Revenant + if (message.matchRegex("§b§lRARE DROP! §r§7\\(§r§f§r§9Revenant Viscera§r§7\\) (.*)")) return true + if (message.matchRegex("§b§lRARE DROP! §r§7\\(§r§f§r§7(.*)x §r§f§r§9Foul Flesh§r§7\\) (.*)")) return true + if (message.matchRegex("§b§lRARE DROP! §r§7\\(§r§f§r§9Foul Flesh§r§7\\) (.*)")) return true + if (message.matchRegex("§6§lRARE DROP! §r§5Golden Powder (.*)")) return true + if (message.matchRegex("§9§lVERY RARE DROP! §r§7\\(§r§f§r§2(.*) Pestilence Rune I§r§7\\) (.*)")) { + LorenzUtils.debug("check regex for this blocked message!") + return true + } + if (message.matchRegex("§5§lVERY RARE DROP! §r§7\\(§r§f§r§5Revenant Catalyst§r§7\\) (.*)")) return true + if (message.matchRegex("§5§lVERY RARE DROP! §r§7\\(§r§f§r§9Undead Catalyst§r§7\\) (.*)")) return true + + //Enderman + if (message.matchRegex("§b§lRARE DROP! §r§7\\(§r§f§r§7(.*)x §r§f§r§aTwilight Arrow Poison§r§7\\) (.*)")) return true + if (message.matchRegex("§9§lVERY RARE DROP! §r§7\\(§r§fMana Steal I§r§7\\) (.*)")) return true + if (message.matchRegex("§5§lVERY RARE DROP! §r§7\\(§r§f§r§5Sinful Dice§r§7\\) (.*)")) return true + if (message.matchRegex("§9§lVERY RARE DROP! §r§7\\(§r§f§r§9Null Atom§r§7\\) (.*)")) return true + if (message.matchRegex("§9§lVERY RARE DROP! §r§7\\(§r§f§r§5Transmission Tuner§r§7\\) (.*)")) return true + if (message.matchRegex("§9§lVERY RARE DROP! §r§7\\(§r§fMana Steal I§r§7\\) (.*)")) return true + if (message.matchRegex("§9§lVERY RARE DROP! §r§7\\(§r§f§r§5◆ Endersnake Rune I§r§7\\) (.*)")) return true + if (message.matchRegex("§d§lCRAZY RARE DROP! §r§7\\(§r§f§r§fPocket Espresso Machine§r§7\\) (.*)")) return true + if (message.matchRegex("§5§lVERY RARE DROP! §r§7\\(§r§f§r§5◆ End Rune I§r§7\\) (.*)")) return true + + return false + } + + private fun slayer(message: String): Boolean { + //start + if (message.matchRegex(" §r§5§lSLAYER QUEST STARTED!")) return true + if (message.matchRegex(" §5§l» §7Slay §c(.*) Combat XP §7worth of (.*)§7.")) return true + + //end + if (message.matchRegex(" §r§a§lSLAYER QUEST COMPLETE!")) return true + if (message == " §r§6§lNICE! SLAYER BOSS SLAIN!") return true + if (message.matchRegex(" §r§e(.*)Slayer LVL 9 §r§5- §r§a§lLVL MAXED OUT!")) return true + if (message.matchRegex(" §r§5§l» §r§7Talk to Maddox to claim your (.*) Slayer XP!")) return true + + + if (message == "§eYou received kill credit for assisting on a slayer miniboss!") return true + + if (message == "§e✆ Ring... ") return true + if (message == "§e✆ Ring... Ring... ") return true + if (message == "§e✆ Ring... Ring... Ring... ") return true + + return false + } + + private fun watchdogAnnouncement(message: String): Boolean = when { + message == "§4[WATCHDOG ANNOUNCEMENT]" -> true + message.matchRegex("§fWatchdog has banned §r§c§l(.*)§r§f players in the last 7 days.") -> true + message.matchRegex("§fStaff have banned an additional §r§c§l(.*)§r§f in the last 7 days.") -> true + message == "§cBlacklisted modifications are a bannable offense!" -> true + else -> false + } + + private fun bazaarAndAHMiniMessages(message: String): Boolean = when (message) { + "§7Putting item in escrow...", + "§7Putting goods in escrow...", + "§7Putting coins in escrow...", + + //Auction House + "§7Setting up the auction...", + "§7Processing purchase...", + "§7Claiming order...", + "§7Processing bid...", + "§7Claiming BIN auction...", + + //Bazaar + "§7Submitting sell offer...", + "§7Submitting buy order...", + "§7Executing instant sell...", + "§7Executing instant buy...", + + //Bank + "§8Depositing coins...", + "§8Withdrawing coins..." -> true + else -> false + } + + private fun killCombo(message: String): Boolean { + //§a§l+5 Kill Combo §r§8+§r§b3% §r§b? Magic Find + return when { + message.matchRegex("§.§l\\+(.*) Kill Combo §r§8\\+(.*)") -> true + message.matchRegex("§cYour Kill Combo has expired! You reached a (.*) Kill Combo!") -> true + else -> false + } + } + + private fun lobby(message: String): Boolean = when { + //player join + message.matchRegex("(.*) §6joined the lobby!") -> true + message.matchRegex(" §b>§c>§a>§r (.*) §6joined the lobby!§r §a<§c<§b<") -> true + + //mystery box + message.matchRegex("§b✦ §r(.*) §r§7found a §r§e(.*) §r§bMystery Box§r§7!") -> true + message.matchRegex("§b✦ §r(.*) §r§7found (a|an) §r(.*) §r§7in a §r§aMystery Box§r§7!") -> true + + //prototype + message.contains("§r§6§lWelcome to the Prototype Lobby§r") -> true + message == " §r§f§l➤ §r§6You have reached your Hype limit! Add Hype to Prototype Lobby minigames by right-clicking with the Hype Diamond!" -> true + + //hypixel tournament notifications + message.contains("§r§e§6§lHYPIXEL§e is hosting a §b§lBED WARS DOUBLES§e tournament!") -> true + message.contains("§r§e§6§lHYPIXEL BED WARS DOUBLES§e tournament is live!") -> true + + //other + message.contains("§aYou are still radiating with §bGenerosity§r§a!") -> true + else -> false + } + + private fun guild(message: String): Boolean = when { + message.matchRegex("§2Guild > (.*) §r§e(joined|left).") -> true + message.matchRegex("§aYou earned §r§2(.*) GEXP §r§afrom playing SkyBlock!") -> true + message.matchRegex("§aYou earned §r§2(.*) GEXP §r§a\\+ §r§e(.*) Event EXP §r§afrom playing SkyBlock!") -> true + message == "§b§m-----------------------------------------------------" -> true + else -> false + } + + private fun welcome(message: String): Boolean = message == "§eWelcome to §r§aHypixel SkyBlock§r§e!" + + private fun warping(message: String): Boolean = when { + message.matchRegex("§7Sending to server (.*)\\.\\.\\.") -> true + message.matchRegex("§7Request join for Hub (.*)\\.\\.\\.") -> true + message.matchRegex("§7Request join for Dungeon Hub #(.*)\\.\\.\\.") -> true + message == "§7Warping..." -> true + message == "§7Warping you to your SkyBlock island..." -> true + message == "§7Warping using transfer token..." -> true + + //visiting other players + message == "§7Finding player..." -> true + message == "§7Sending a visit request..." -> true + + //warp portals on public islands (canvas room - flower house, election room - community center, void sepulture - the end) + message.matchRegex("§dWarped to (.*)§r§d!") -> true + else -> false + } + + private fun empty(message: String): Boolean = when (message) { + "§8 §r§8 §r§1 §r§3 §r§3 §r§7 §r§8 ", + + "§f §r§f §r§1 §r§0 §r§2 §r§4§r§f §r§f §r§2 §r§0 §r§4 §r§8§r§0§r§1§r§0§r§1§r§2§r§f§r§f§r§0§r§1§r§3§r§4§r§f§r§f§r§0§r§1§r§5§r§f§r§f§r§0§r§1§r§6§r§f§r§f§r§0§r§1§r§8§r§9§r§a§r§b§r§f§r§f§r§0§r§1§r§7§r§f§r§f§r§3 §r§9 §r§2 §r§0 §r§0 §r§1§r§3 §r§9 §r§2 §r§0 §r§0 §r§2§r§3 §r§9 §r§2 §r§0 §r§0 §r§3§r§0§r§0§r§1§r§f§r§e§r§0§r§0§r§2§r§f§r§e§r§0§r§0§r§3§r§4§r§5§r§6§r§7§r§8§r§f§r§e§r§3 §r§6 §r§3 §r§6 §r§3 §r§6 §r§e§r§3 §r§6 §r§3 §r§6 §r§3 §r§6 §r§d", + + "§f §r§r§r§f §r§r§r§1 §r§r§r§0 §r§r§r§2 §r§r§r§f §r§r§r§f §r§r§r§2 §r§r§r§0 §r§r§r§4 §r§r§r§3 §r§r§r§9 §r§r§r§2 §r§r§r§0 §r§r§r§0 §r§r§r§3 §r§r§r§9 §r§r§r§2 §r§r§r§0 §r§r§r§0 §r§r§r§3 §r§r§r§9 §r§r§r§2 §r§r§r§0 §r§r§r§0 ", + + "", + "§f", + "§c" -> true + else -> false + } +} diff --git a/src/main/java/at/lorenz/mod/chat/ChatManager.kt b/src/main/java/at/lorenz/mod/chat/ChatManager.kt new file mode 100644 index 000000000..a85643e1b --- /dev/null +++ b/src/main/java/at/lorenz/mod/chat/ChatManager.kt @@ -0,0 +1,47 @@ +package at.lorenz.mod.chat + +import at.lorenz.mod.utils.LorenzLogger +import at.lorenz.mod.events.LorenzChatEvent +import at.lorenz.mod.utils.LorenzUtils +import net.minecraftforge.client.event.ClientChatReceivedEvent +import net.minecraftforge.fml.common.eventhandler.EventPriority +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +class ChatManager { + + private val loggerAll = LorenzLogger("chat/filter_all") + private val loggerFiltered = LorenzLogger("chat/filter_blocked") + private val loggerAllowed = LorenzLogger("chat/filter_allowed") + private val loggerFilteredTypes = mutableMapOf<String, LorenzLogger>() + + @SubscribeEvent(priority = EventPriority.LOW, receiveCanceled = true) + fun onChatPacket(event: ClientChatReceivedEvent) { + val messageComponent = event.message + + val message = LorenzUtils.stripVanillaMessage(messageComponent.formattedText) + if (event.type.toInt() == 2) { +// val actionBarEvent = LorenzActionBarEvent(message) +// actionBarEvent.postAndCatch() + } else { + + val chatEvent = LorenzChatEvent(message, messageComponent) + chatEvent.postAndCatch() + + val blockReason = chatEvent.blockedReason.uppercase() + if (blockReason != "") { + event.isCanceled = true + loggerFiltered.log("[$blockReason] $message") + loggerAll.log("[$blockReason] $message") + loggerFilteredTypes.getOrPut(blockReason) { LorenzLogger("chat/filter_blocked/$blockReason") } + .log(message) + return + } + + if (!message.startsWith("§f{\"server\":\"")) { + loggerAllowed.log(message) + loggerAll.log("[allowed] $message") + } + + } + } +}
\ No newline at end of file diff --git a/src/main/java/at/lorenz/mod/chat/PlayerChatFilter.kt b/src/main/java/at/lorenz/mod/chat/PlayerChatFilter.kt new file mode 100644 index 000000000..427b25e37 --- /dev/null +++ b/src/main/java/at/lorenz/mod/chat/PlayerChatFilter.kt @@ -0,0 +1,78 @@ +package at.lorenz.mod.chat + +import at.lorenz.mod.utils.LorenzLogger +import at.lorenz.mod.utils.LorenzUtils +import at.lorenz.mod.utils.LorenzUtils.Companion.removeColorCodes +import at.lorenz.mod.events.PlayerSendChatEvent + +class PlayerChatFilter { + + companion object { + val loggerPlayerChat = LorenzLogger("chat/player") + + fun shouldBlock(originalMessage: String): Boolean { + val split: List<String> = if (originalMessage.contains("§7§r§7: ")) { + originalMessage.split("§7§r§7: ") + } else if (originalMessage.contains("§f: ")) { + originalMessage.split("§f: ") + } else { + return false + } + + var rawName = split[0] + val message = split[1] + + val channel: PlayerMessageChannel + if (rawName.startsWith("§9Party §8> ")) { + channel = PlayerMessageChannel.PARTY + rawName = rawName.substring(12) + } else if (rawName.startsWith("§2Guild > ")) { + channel = PlayerMessageChannel.GUILD + rawName = rawName.substring(10) + } else if (rawName.startsWith("§bCo-op > ")) { + channel = PlayerMessageChannel.COOP + rawName = rawName.substring(10) + } else { + channel = PlayerMessageChannel.ALL + } + + val nameSplit = rawName.split(" ") + val first = nameSplit[0] + + val last = nameSplit.last() + val name = if (last.endsWith("]")) { + nameSplit[nameSplit.size - 2] + } else { + last + } + + if (first != name) { + if (!first.contains("VIP") && !first.contains("MVP")) { + //TODO support yt + admin + return false + } + } + + send(channel, name.removeColorCodes(), message.removeColorCodes()) + return true + } + + private fun send(channel: PlayerMessageChannel, name: String, message: String) { + loggerPlayerChat.log("[$channel] $name: $message") + val event = PlayerSendChatEvent(channel, name, message) + event.postAndCatch() + + if (event.cancelledReason != "") { + loggerPlayerChat.log("cancelled: " + event.cancelledReason) + } else { + val finalMessage = event.message + if (finalMessage != message) { + loggerPlayerChat.log("message changed: $finalMessage") + } + + val prefix = channel.prefix + LorenzUtils.chat("$prefix §b$name §f$finalMessage") + } + } + } +}
\ No newline at end of file diff --git a/src/main/java/at/lorenz/mod/chat/PlayerMessageChannel.kt b/src/main/java/at/lorenz/mod/chat/PlayerMessageChannel.kt new file mode 100644 index 000000000..0e44b88f0 --- /dev/null +++ b/src/main/java/at/lorenz/mod/chat/PlayerMessageChannel.kt @@ -0,0 +1,10 @@ +package at.lorenz.mod.chat + +enum class PlayerMessageChannel(val prefix: String) { + + ALL("§fA>"), + ALL_ADVERTISEMENT("§8A>"), + PARTY("§9P>"), + GUILD("§2G>"), + COOP("§bCC>"), +}
\ No newline at end of file diff --git a/src/main/java/at/lorenz/mod/config/LorenzConfig.kt b/src/main/java/at/lorenz/mod/config/LorenzConfig.kt new file mode 100644 index 000000000..67268b6ca --- /dev/null +++ b/src/main/java/at/lorenz/mod/config/LorenzConfig.kt @@ -0,0 +1,12 @@ +package at.lorenz.mod.config + +class LorenzConfig { + + companion object { + val chatFilter = true + val dungeonHideAnnoyingMessages = true + val hideNotClickableItems = true + + val lorenzBazaarOrderHelper = true + } +}
\ No newline at end of file diff --git a/src/main/java/at/lorenz/mod/dungeon/DungeonChatFilter.kt b/src/main/java/at/lorenz/mod/dungeon/DungeonChatFilter.kt new file mode 100644 index 000000000..86fc67cfd --- /dev/null +++ b/src/main/java/at/lorenz/mod/dungeon/DungeonChatFilter.kt @@ -0,0 +1,259 @@ +package at.lorenz.mod.dungeon + +import at.lorenz.mod.config.LorenzConfig +import at.lorenz.mod.events.LorenzChatEvent +import at.lorenz.mod.utils.LorenzUtils.Companion.matchRegex +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +class DungeonChatFilter { + + @SubscribeEvent + fun onChatMessage(event: LorenzChatEvent) { + if (!LorenzConfig.dungeonHideAnnoyingMessages) return + + val blockReason = block(event.message) + if (blockReason != "") { + event.blockedReason = "dungeon_$blockReason" + } + } + + private fun block(message: String): String { + when { + isPrepare(message) -> return "prepare" + isStart(message) -> return "start" + } + + //TODO add +// if (!LorenzUtils.inDungeon) return "" + + return when { + isKey(message) -> "key" + isDoor(message) -> "door" + isPickup(message) -> "pickup" + isReminder(message) -> "reminder" + isBuff(message) -> "buff" + isNotPossible(message) -> "not_possible" + isDamage(message) -> "damage" + isAbility(message) -> "ability" + isPuzzle(message) -> "puzzle" + isBoss(message) -> "boss" + isEnd(message) -> "end" + //TODO add +// DungeonMilestoneDisplay.isMilestoneMessage(message) -> "milestone" + + else -> "" + } + } + + private fun isDoor(message: String): Boolean = message == "§cThe §r§c§lBLOOD DOOR§r§c has been opened!" + + private fun isBoss(message: String): Boolean { + when { + message.matchRegex("§([cd4])\\[BOSS] (.*)") -> { + when { + message.contains(" The Watcher§r§f: ") -> return true + message.contains(" Bonzo§r§f: ") -> return true + message.contains(" Scarf§r§f:") -> return true + message.contains("Professor§r§f") -> return true + message.contains(" Livid§r§f: ") || message.contains(" Enderman§r§f: ") -> return true + message.contains(" Thorn§r§f: ") -> return true + message.contains(" Sadan§r§f: ") -> return true + message.contains(" Maxor§r§c: ") -> return true + message.contains(" Storm§r§c: ") -> return true + message.contains(" Goldor§r§c: ") -> return true + message.contains(" Necron§r§c: ") -> return true + message.contains(" §r§4§kWither King§r§c:") -> return true + + message.endsWith(" Necron§r§c: That is enough, fool!") -> return true + message.endsWith(" Necron§r§c: Adventurers! Be careful of who you are messing with..") -> return true + message.endsWith(" Necron§r§c: Before I have to deal with you myself.") -> return true + } + } + + //M7 - Dragons + message == "§cThe Crystal withers your soul as you hold it in your hands!" -> return true + message == "§cIt doesn't seem like that is supposed to go there." -> return true + } + return false + } + + private fun isEnd(message: String): Boolean = when { + message.matchRegex("(.*) §r§eunlocked §r§d(.*) Essence §r§8x(.*)§r§e!") -> true + message.matchRegex(" §r§d(.*) Essence §r§8x(.*)") -> true + message.endsWith(" Experience §r§b(Team Bonus)") -> true + else -> false + } + + private fun isAbility(message: String): Boolean = when { + message == "§a§r§6Guided Sheep §r§ais now available!" -> true + message.matchRegex("§7Your Guided Sheep hit §r§c(.*) §r§7enemy for §r§c(.*) §r§7damage.") -> true + message == "§6Rapid Fire§r§a is ready to use! Press §r§6§lDROP§r§a to activate it!" -> true + message == "§6Castle of Stone§r§a is ready to use! Press §r§6§lDROP§r§a to activate it!" -> true + + + message.matchRegex("§a§lBUFF! §fYou were splashed by (.*) §fwith §r§cHealing VIII§r§f!") -> true + message.matchRegex("§aYou were healed for (.*) health by (.*)§a!") -> true + message.matchRegex("§aYou gained (.*) HP worth of absorption for 3s from §r(.*)§r§a!") -> true + message.matchRegex("§c(.*) §r§epicked up your (.*) Orb!") -> true + message.matchRegex("§cThis ability is on cooldown for (.*)s.") -> true + message.matchRegex("§a§l(.*) healed you for (.*) health!") -> true + message == "§aYou used your §r§6Mining Speed Boost §r§aPickaxe Ability!" -> true + message == "§cYour Mining Speed Boost has expired!" -> true + message == "§a§r§6Mining Speed Boost §r§ais now available!" -> true + message.matchRegex("§eYour bone plating reduced the damage you took by §r§c(.*)§r§e!") -> true + message.matchRegex("(.*) §r§eformed a tether with you!") -> true + message.matchRegex("§eYour tether with (.*) §r§ehealed you for §r§a(.*) §r§ehealth.") -> true + message.matchRegex("§7Your Implosion hit §r§c(.*) §r§7enemy for §r§c(.*) §r§7damage.") -> true + + message.matchRegex("§eYour §r§6Spirit Pet §r§ehealed (.*) §r§efor §r§a(.*) §r§ehealth!") -> true + message.matchRegex("§eYour §r§6Spirit Pet §r§ehit (.*) enemy for §r§c(.*) §r§edamage.") -> true + + message == "§dCreeper Veil §r§aActivated!" -> true + message == "§dCreeper Veil §r§cDe-activated!" -> true + message.matchRegex("§cYou need at least (.*) mana to activate this!") -> true + + message.matchRegex( + "§eYou were healed for §r§a(.*)§r§e health by §r(.*)§r§e's §r§9Healing Bow§r§e and " + "gained §r§c\\+(.*) Strength§r§e for 10 seconds." + ) -> true + message.matchRegex("(.*)§r§a granted you §r§c(.*) §r§astrength for §r§e20 §r§aseconds!") -> true + + message.matchRegex("§eYour fairy healed §r§ayourself §r§efor §r§a(.*) §r§ehealth!") -> true + message.matchRegex("§eYour fairy healed §r(.*) §r§efor §r§a(.*) §r§ehealth!") -> true + message.matchRegex("(.*) fairy healed you for §r§a(.*) §r§ehealth!") -> true + + else -> false + } + + private fun isDamage(message: String): Boolean = when { + message == "§cMute silenced you!" -> true + message.matchRegex("(.*) §r§aused §r(.*) §r§aon you!") -> true + message.matchRegex("§cThe (.*)§r§c struck you for (.*) damage!") -> true + message.matchRegex("§cThe (.*) hit you for (.*) damage!") -> true + message.matchRegex("§7(.*) struck you for §r§c(.*)§r§7 damage.") -> true + message.matchRegex("(.*) hit you for §r§c(.*)§r§7 damage.") -> true + message.matchRegex("(.*) hit you for §r§c(.*)§r§7 true damage.") -> true + message.matchRegex("§7(.*) exploded, hitting you for §r§c(.*)§r§7 damage.") -> true + message.matchRegex("(.*)§r§c hit you with §r(.*) §r§cfor (.*) damage!") -> true + message.matchRegex("(.*)§r§a struck you for §r§c(.*)§r§a damage!") -> true + message.matchRegex("(.*)§r§c struck you for (.*)!") -> true + + //TODO abstract if more "burnt" messages are found + message.matchRegex("§7The Mage's Magma burnt you for §r§c(.*)§r§7 true damage.") -> true + + message.matchRegex("§7Your (.*) hit §r§c(.*) §r§7(enemy|enemies) for §r§c(.*) §r§7damage.") -> true + else -> false + } + + private fun isNotPossible(message: String): Boolean = when (message) { + "§cYou cannot hit the silverfish while it's moving!", + "§cYou cannot move the silverfish in that direction!", + "§cThere are blocks in the way!", + "§cThis chest has already been searched!", + "§cThis lever has already been used.", + "§cYou cannot do that in this room!", + "§cYou do not have the key for this door!", + "§cYou have already opened this dungeon chest!", + "§cYou cannot use abilities in this room!", + "§cA mystical force in this room prevents you from using that ability!" -> true + + else -> false + } + + private fun isBuff(message: String): Boolean = when { + message.matchRegex("§6§lDUNGEON BUFF! (.*) §r§ffound a §r§dBlessing of (.*)§r§f!(.*)") -> true + message.matchRegex("§6§lDUNGEON BUFF! §r§fYou found a §r§dBlessing of (.*)§r§f!(.*)") -> true + message.matchRegex("§6§lDUNGEON BUFF! §r§fA §r§dBlessing of (.*)§r§f was found! (.*)") -> true + message.matchRegex("§eA §r§a§r§dBlessing of (.*)§r§e was picked up!") -> true + message.matchRegex("(.*) §r§ehas obtained §r§a§r§dBlessing of (.*)§r§e!") -> true + message.matchRegex(" §r§7(Grants|Granted) you §r§a(.*) Strength §r§7and §r§a(.*) Crit Damage§r§7.") -> true + message.matchRegex(" §r§7(Grants|Granted) you §r§a(.*) Defense §r§7and §r§a+(.*) Damage§r§7.") -> true + message.matchRegex(" §r§7(Grants|Granted) you §r§a(.*) HP §r§7and §r§a+(.*)% §r§7health regeneration.") -> true + message.matchRegex(" §r§7(Grants|Granted) you §r§a(.*) Intelligence §r§7and §r§a+(.*)? Speed§r§7.") -> true + message.matchRegex(" §r§7Granted you §r§a+(.*) HP§r§7, §r§a(.*) Defense§r§7, §r§a(.*) Intelligence§r§7, and §r§a(.*) Strength§r§7.") -> true + message == "§a§lBUFF! §fYou have gained §r§cHealing V§r§f!" -> true + else -> false + } + + private fun isPuzzle(message: String): Boolean = when { + message.matchRegex("§a§lPUZZLE SOLVED! (.*) §r§ewasn't fooled by §r§c(.*)§r§e! §r§4G§r§co§r§6o§r§ed§r§a §r§2j§r§bo§r§3b§r§5!") -> true + message.matchRegex("§a§lPUZZLE SOLVED! (.*) §r§etied Tic Tac Toe! §r§4G§r§co§r§6o§r§ed§r§a §r§2j§r§bo§r§3b§r§5!") -> true + message == "§4[STATUE] Oruo the Omniscient§r§f: §r§fThough I sit stationary in this prison that is §r§cThe Catacombs§r§f, my knowledge knows no bounds." -> true + message == "§4[STATUE] Oruo the Omniscient§r§f: §r§fProve your knowledge by answering 3 questions and I shall reward you in ways that transcend time!" -> true + message == "§4[STATUE] Oruo the Omniscient§r§f: §r§fAnswer incorrectly, and your moment of ineptitude will live on for generations." -> true + +// message == "§4[STATUE] Oruo the Omniscient§r§f: §r§f2 questions §r§fleft...and§r§f you will have proven your worth to me!" -> true + message == "§4[STATUE] Oruo the Omniscient§r§f: §r§f2 questions left... Then you will have proven your worth to me!" -> true + + message == "§4[STATUE] Oruo the Omniscient§r§f: §r§fOne more question!" -> true + message == "§4[STATUE] Oruo the Omniscient§r§f: §r§fI bestow upon you all the power of a hundred years!" -> true + message == "§4[STATUE] Oruo the Omniscient§r§f: §r§fYou've already proven enough to me! No need to press more of my buttons!" -> true + message == "§4[STATUE] Oruo the Omniscient§r§f: §r§fI've had enough of you and your party fiddling with my buttons. Scram!" -> true + message == "§4[STATUE] Oruo the Omniscient§r§f: §r§fEnough! My buttons are not to be pressed with such lack of grace!" -> true + message.matchRegex("§4\\[STATUE] Oruo the Omniscient§r§f: §r(.*) §r§fthinks the answer is §r§6 . §r(.*)§r§f! §r§fLock in your party's answer in my Chamber!") -> true + else -> false + } + + private fun isKey(message: String): Boolean = when { + message.matchRegex("(.*) §r§ehas obtained §r§a§r§6§r§8Wither Key§r§e!") -> true + message.matchRegex("(.*) opened a §r§8§lWITHER §r§adoor!") -> true + message.matchRegex("(.*) §r§ehas obtained §r§a§r§c§r§cBlood Key§r§e!") -> true + message.matchRegex("(.*) §r§ehas obtained §r§a§r§9Beating Heart§r§e!") -> true + message == "§5A shiver runs down your spine..." -> true + message == "§eA §r§a§r§6§r§8Wither Key§r§e was picked up!" -> true + message == "§eA §r§a§r§c§r§cBlood Key§r§e was picked up!" -> true + + else -> false + } + + private fun isReminder(message: String): Boolean = when (message) { + "§e§lRIGHT CLICK §r§7on §r§7a §r§8WITHER §r§7door§r§7 to open it. This key can only be used to open §r§a1§r§7 door!", + "§e§lRIGHT CLICK §r§7on §r§7the §r§cBLOOD DOOR§r§7 to open it. This key can only be used to open §r§a1§r§7 door!" -> true + + else -> false + } + + private fun isPickup(message: String): Boolean = when { + message.matchRegex("(.*) §r§ehas obtained §r§a§r§9Superboom TNT§r§e!") -> true + message.matchRegex("(.*) §r§ehas obtained §r§a§r§9Superboom TNT §r§8x2§r§e!") -> true + message.matchRegex("§6§lRARE DROP! §r§9Hunk of Blue Ice §r§b\\(+(.*)% Magic Find!\\)") -> true + message.matchRegex("(.*) §r§ehas obtained §r§a§r§6Revive Stone§r§e!") -> true + message.matchRegex("(.*) §r§ffound a §r§dWither Essence§r§f! Everyone gains an extra essence!") -> true + message == "§fYou found a §r§dWither Essence§r§f! Everyone gains an extra essence!" -> true + message.matchRegex("§d(.*) the Fairy§r§f: You killed me! Take this §r§6Revive Stone §r§fso that my death is not in vain!") -> true + message.matchRegex("§d(.*) the Fairy§r§f: You killed me! I'll revive you so that my death is not in vain!") -> true + message.matchRegex("§d(.*) the Fairy§r§f: You killed me! I'll revive your friend §r(.*) §r§fso that my death is not in vain!") -> true + message.matchRegex("§d(.*) the Fairy§r§f: Have a great life!") -> true + message.matchRegex( + "§c(.*) §r§eYou picked up a Ability Damage Orb from (.*) §r§ehealing you for §r§c(.*) §r§eand granting you +§r§c(.*)% §r§eAbility Damage for §r§b10 §r§eseconds." + ) -> true + message.matchRegex( + "§c(.*) §r§eYou picked up a Damage Orb from (.*) §r§ehealing you for §r§c(.*) §r§eand granting you +§r§c(.*)% §r§eDamage for §r§b10 §r§eseconds." + ) -> true + message.matchRegex("(.*) §r§ehas obtained §r§a§r§9Premium Flesh§r§e!") -> true + message.matchRegex("§6§lRARE DROP! §r§9Beating Heart §r§b(.*)") -> true + else -> false + } + + private fun isStart(message: String): Boolean = when { + message == "§e[NPC] §bMort§f: §rHere, I found this map when I first entered the dungeon." -> true + message == "§e[NPC] §bMort§f: §rYou should find it useful if you get lost." -> true + message == "§e[NPC] §bMort§f: §rGood luck." -> true + message == "§e[NPC] §bMort§f: §rTalk to me to change your class and ready up." -> true + + //§a[Berserk] §r§fMelee Damage §r§c48%§r§f -> §r§a88% + //§a[Berserk] §r§fWalk Speed §r§c38§r§f -> §r§a68 + message.matchRegex("§a(.*) §r§f(.*) §r§c(.*)§r§f -> §r§a(.*)") -> true + else -> false + } + + private fun isPrepare(message: String): Boolean = when { + message == "§aYour active Potion Effects have been paused and stored. They will be restored when you leave Dungeons! You are not allowed to use existing Potion Effects while in Dungeons." -> true + message.matchRegex("(.*) has started the dungeon countdown. The dungeon will begin in 1 minute.") -> true + message.matchRegex("§e[NPC] §bMort§f: §rTalk to me to change your class and ready up.") -> true + message.matchRegex("(.*) §a is now ready!") -> true + message.matchRegex("§aDungeon starts in (.*) seconds.") -> true + message == "§aDungeon starts in 1 second." -> true + message == "§aYou can no longer consume or splash any potions during the remainder of this Dungeon run!" -> true + else -> false + } +}
\ No newline at end of file diff --git a/src/main/java/at/lorenz/mod/events/GuiContainerEvent.kt b/src/main/java/at/lorenz/mod/events/GuiContainerEvent.kt new file mode 100644 index 000000000..4f43ab505 --- /dev/null +++ b/src/main/java/at/lorenz/mod/events/GuiContainerEvent.kt @@ -0,0 +1,54 @@ +package at.lorenz.mod.events + +import net.minecraft.client.gui.inventory.GuiContainer +import net.minecraft.inventory.Container +import net.minecraft.inventory.ContainerChest +import net.minecraft.inventory.Slot +import net.minecraftforge.fml.common.eventhandler.Cancelable + +abstract class GuiContainerEvent(open val gui: GuiContainer, open val container: Container) : LorenzEvent() { + val chestName: String by lazy { + if (container !is ContainerChest) error("Container is not a chest") + return@lazy (container as ContainerChest).lowerChestInventory.displayName.unformattedText.trim() + } + + data class BackgroundDrawnEvent( + override val gui: GuiContainer, + override val container: Container, + val mouseX: Int, + val mouseY: Int, + val partialTicks: Float + ) : GuiContainerEvent(gui, container) + + @Cancelable + data class CloseWindowEvent(override val gui: GuiContainer, override val container: Container) : + GuiContainerEvent(gui, container) + + abstract class DrawSlotEvent(gui: GuiContainer, container: Container, open val slot: Slot) : + GuiContainerEvent(gui, container) { + @Cancelable + data class Pre(override val gui: GuiContainer, override val container: Container, override val slot: Slot) : + DrawSlotEvent(gui, container, slot) + + data class Post(override val gui: GuiContainer, override val container: Container, override val slot: Slot) : + DrawSlotEvent(gui, container, slot) + } + + data class ForegroundDrawnEvent( + override val gui: GuiContainer, + override val container: Container, + val mouseX: Int, + val mouseY: Int, + val partialTicks: Float + ) : GuiContainerEvent(gui, container) + + @Cancelable + data class SlotClickEvent( + override val gui: GuiContainer, + override val container: Container, + val slot: Slot?, + val slotId: Int, + val clickedButton: Int, + val clickType: Int + ) : GuiContainerEvent(gui, container) +}
\ No newline at end of file diff --git a/src/main/java/at/lorenz/mod/events/LorenzChatEvent.kt b/src/main/java/at/lorenz/mod/events/LorenzChatEvent.kt new file mode 100644 index 000000000..3b0350918 --- /dev/null +++ b/src/main/java/at/lorenz/mod/events/LorenzChatEvent.kt @@ -0,0 +1,5 @@ +package at.lorenz.mod.events + +import net.minecraft.util.IChatComponent + +class LorenzChatEvent(val message: String, val chatComponent: IChatComponent, var blockedReason: String = "") : LorenzEvent()
\ No newline at end of file diff --git a/src/main/java/at/lorenz/mod/events/LorenzEvent.kt b/src/main/java/at/lorenz/mod/events/LorenzEvent.kt new file mode 100644 index 000000000..facb18e2a --- /dev/null +++ b/src/main/java/at/lorenz/mod/events/LorenzEvent.kt @@ -0,0 +1,20 @@ +package at.lorenz.mod.events + +import at.lorenz.mod.utils.LorenzUtils +import net.minecraftforge.common.MinecraftForge +import net.minecraftforge.fml.common.eventhandler.Event + +abstract class LorenzEvent: Event() { + val eventName by lazy { + this::class.simpleName + } + + fun postAndCatch(): Boolean { + return runCatching { + MinecraftForge.EVENT_BUS.post(this) + }.onFailure { + it.printStackTrace() + LorenzUtils.chat("§cLorenz Mod caught and logged an ${it::class.simpleName ?: "error"} at ${eventName}.") + }.getOrDefault(isCanceled) + } +}
\ No newline at end of file diff --git a/src/main/java/at/lorenz/mod/events/PlayerSendChatEvent.kt b/src/main/java/at/lorenz/mod/events/PlayerSendChatEvent.kt new file mode 100644 index 000000000..dd015e1cb --- /dev/null +++ b/src/main/java/at/lorenz/mod/events/PlayerSendChatEvent.kt @@ -0,0 +1,11 @@ +package at.lorenz.mod.events + +import at.lorenz.mod.chat.PlayerMessageChannel + + +class PlayerSendChatEvent( + val channel: PlayerMessageChannel, + val playerName: String, + var message: String, + var cancelledReason: String = "" +) : LorenzEvent()
\ No newline at end of file diff --git a/src/main/java/at/lorenz/mod/mixins/MixinGuiContainer.java b/src/main/java/at/lorenz/mod/mixins/MixinGuiContainer.java new file mode 100644 index 000000000..c9dc12fc5 --- /dev/null +++ b/src/main/java/at/lorenz/mod/mixins/MixinGuiContainer.java @@ -0,0 +1,50 @@ +package at.lorenz.mod.mixins; + +import at.lorenz.mod.GuiContainerHook; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.inventory.Slot; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(GuiContainer.class) +public abstract class MixinGuiContainer extends GuiScreen { + + @Unique + private final GuiContainerHook hook = new GuiContainerHook(this); + + @Inject(method = "keyTyped", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/entity/EntityPlayerSP;closeScreen()V", shift = At.Shift.BEFORE), cancellable = true) + private void closeWindowPressed(CallbackInfo ci) { + hook.closeWindowPressed(ci); + } + + @Inject(method = "drawScreen", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;color(FFFF)V", ordinal = 1)) + private void backgroundDrawn(int mouseX, int mouseY, float partialTicks, CallbackInfo ci) { + hook.backgroundDrawn(mouseX, mouseY, partialTicks, ci); + } + + @Inject(method = "drawScreen", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/inventory/GuiContainer;drawGuiContainerForegroundLayer(II)V", shift = At.Shift.AFTER)) + private void onForegroundDraw(int mouseX, int mouseY, float partialTicks, CallbackInfo ci) { + hook.foregroundDrawn(mouseX, mouseY, partialTicks, ci); + } + + @Inject(method = "drawSlot", at = @At("HEAD"), cancellable = true) + private void onDrawSlot(Slot slot, CallbackInfo ci) { + hook.onDrawSlot(slot, ci); + } + + @Inject(method = "drawSlot", at = @At("RETURN"), cancellable = true) + private void onDrawSlotPost(Slot slot, CallbackInfo ci) { + hook.onDrawSlotPost(slot, ci); + } + + + @Inject(method = "handleMouseClick", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/multiplayer/PlayerControllerMP;windowClick(IIIILnet/minecraft/entity/player/EntityPlayer;)Lnet/minecraft/item/ItemStack;"), cancellable = true) + private void onMouseClick(Slot slot, int slotId, int clickedButton, int clickType, CallbackInfo ci) { + hook.onMouseClick(slot, slotId, clickedButton, clickType, ci); + } + +} diff --git a/src/main/java/at/lorenz/mod/utils/APIUtil.kt b/src/main/java/at/lorenz/mod/utils/APIUtil.kt new file mode 100644 index 000000000..88d459ada --- /dev/null +++ b/src/main/java/at/lorenz/mod/utils/APIUtil.kt @@ -0,0 +1,116 @@ +package at.lorenz.mod.utils + +import com.google.gson.JsonArray +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import org.apache.http.client.config.RequestConfig +import org.apache.http.client.methods.HttpGet +import org.apache.http.impl.client.HttpClientBuilder +import org.apache.http.impl.client.HttpClients +import org.apache.http.message.BasicHeader +import org.apache.http.util.EntityUtils +import scala.util.parsing.json.JSONArray +import scala.util.parsing.json.JSONObject +import java.awt.image.BufferedImage +import java.net.HttpURLConnection +import java.net.URL +import java.security.cert.X509Certificate +import javax.imageio.ImageIO + + +object APIUtil { + private val parser = JsonParser() + +// val sslContext = SSLContexts.custom() +// .loadTrustMaterial { chain, authType -> +// isValidCert(chain, authType) +// } +// .build() +// val sslSocketFactory = SSLConnectionSocketFactoryBuilder.create() +// .setSslContext(sslContext) +// .build() + +// val cm = PoolingHttpClientConnectionManagerBuilder.create() +// .setSSLSocketFactory(sslSocketFactory) + + val builder: HttpClientBuilder = + HttpClients.custom().setUserAgent("LorenzMod") +// .setConnectionManagerShared(true) +// .setConnectionManager(cm.build()) + .setDefaultHeaders( + mutableListOf( + BasicHeader("Pragma", "no-cache"), + BasicHeader("Cache-Control", "no-cache") + ) + ) + .setDefaultRequestConfig( + RequestConfig.custom() +// .setConnectTimeout(Timeout.ofMinutes(1)) +// .setResponseTimeout(Timeout.ofMinutes(1)) + .build() + ) + .useSystemProperties() + + /** + * Taken from Elementa under MIT License + * @link https://github.com/Sk1erLLC/Elementa/blob/master/LICENSE + */ + fun URL.getImage(): BufferedImage { + val connection = this.openConnection() as HttpURLConnection + + connection.requestMethod = "GET" + connection.useCaches = true + connection.addRequestProperty("User-Agent", "LorenzMod") + connection.doOutput = true + + return ImageIO.read(connection.inputStream) + } + + fun getJSONResponse(urlString: String): JsonObject { + val client = builder.build() + try { + client.execute(HttpGet(urlString)).use { response -> + val entity = response.entity + if (entity != null) { + val retSrc = EntityUtils.toString(entity) + return parser.parse(retSrc) as JsonObject + // parsing JSON +// val result = JSONObject(retSrc) //Convert String to JSON Object +// val tokenList: JSONArray = result.getJSONArray("names") +// val oj: JSONObject = tokenList.getJSONObject(0) +// val token: String = oj.getString("name") + } + } + } catch (ex: Throwable) { + ex.printStackTrace() + LorenzUtils.error("Skytils ran into an ${ex::class.simpleName ?: "error"} whilst fetching a resource. See logs for more details.") + } finally { + client.close() + } + return JsonObject() + } + +// fun getArrayResponse(urlString: String): JsonArray { +// val client = builder.build() +// try { +// client.execute(HttpGet(urlString)).use { response -> +//// response.entity.content +// response.entity.content { entity -> +// val obj = parser.parse(EntityUtils.toString(entity)).asJsonArray +// EntityUtils.consume(entity) +// return obj +// } +// } +// } catch (ex: Throwable) { +// LorenzUtils.error("Skytils ran into an ${ex::class.simpleName ?: "error"} whilst fetching a resource. See logs for more details.") +// ex.printStackTrace() +// } finally { +// client.close() +// } +// return JsonArray() +// } + + private fun isValidCert(chain: Array<X509Certificate>, authType: String): Boolean { + return chain.any { it.issuerDN.name == "CN=R3, O=Let's Encrypt, C=US" } + } +}
\ No newline at end of file diff --git a/src/main/java/at/lorenz/mod/utils/ItemUtil.kt b/src/main/java/at/lorenz/mod/utils/ItemUtil.kt new file mode 100644 index 000000000..fc0409e31 --- /dev/null +++ b/src/main/java/at/lorenz/mod/utils/ItemUtil.kt @@ -0,0 +1,211 @@ +package at.lorenz.mod.utils + +import net.minecraft.init.Items +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NBTTagCompound +import net.minecraft.nbt.NBTTagList +import net.minecraft.nbt.NBTTagString +import net.minecraftforge.common.util.Constants +import java.util.* + +object ItemUtil { + private val PET_PATTERN = "§7\\[Lvl \\d+] (?<color>§[0-9a-fk-or]).+".toRegex() + const val NBT_INTEGER = 3 + private const val NBT_STRING = 8 + private const val NBT_LIST = 9 + private const val NBT_COMPOUND = 10 + + /** + * Returns the display name of a given item + * @author Mojang + * @param item the Item to get the display name of + * @return the display name of the item + */ + @JvmStatic + fun getDisplayName(item: ItemStack): String { + var s = item.item.getItemStackDisplayName(item) + if (item.tagCompound != null && item.tagCompound.hasKey("display", 10)) { + val nbtTagCompound = item.tagCompound.getCompoundTag("display") + if (nbtTagCompound.hasKey("Name", 8)) { + s = nbtTagCompound.getString("Name") + } + } + return s + } + + /** + * Returns the Skyblock Item ID of a given Skyblock item + * + * @author BiscuitDevelopment + * @param item the Skyblock item to check + * @return the Skyblock Item ID of this item or `null` if this isn't a valid Skyblock item + */ + @JvmStatic + fun getSkyBlockItemID(item: ItemStack?): String? { + if (item == null) { + return null + } + val extraAttributes = getExtraAttributes(item) ?: return null + return if (!extraAttributes.hasKey("id", NBT_STRING)) { + null + } else extraAttributes.getString("id") + } + + /** + * Returns the `ExtraAttributes` compound tag from the item's NBT data. + * + * @author BiscuitDevelopment + * @param item the item to get the tag from + * @return the item's `ExtraAttributes` compound tag or `null` if the item doesn't have one + */ + @JvmStatic + fun getExtraAttributes(item: ItemStack?): NBTTagCompound? { + return if (item == null || !item.hasTagCompound()) { + null + } else item.getSubCompound("ExtraAttributes", false) + } + + /** + * Returns the Skyblock Item ID of a given Skyblock Extra Attributes NBT Compound + * + * @author BiscuitDevelopment + * @param extraAttributes the NBT to check + * @return the Skyblock Item ID of this item or `null` if this isn't a valid Skyblock NBT + */ + @JvmStatic + fun getSkyBlockItemID(extraAttributes: NBTTagCompound?): String? { + if (extraAttributes != null) { + val itemId = extraAttributes.getString("id") + if (itemId.isNotEmpty()) { + return itemId + } + } + return null + } + + /** + * Returns a string list containing the nbt lore of an ItemStack, or + * an empty list if this item doesn't have a lore. The returned lore + * list is unmodifiable since it has been converted from an NBTTagList. + * + * @author BiscuitDevelopment + * @param itemStack the ItemStack to get the lore from + * @return the lore of an ItemStack as a string list + */ + @JvmStatic + fun getItemLore(itemStack: ItemStack): List<String> { + if (itemStack.hasTagCompound() && itemStack.tagCompound.hasKey("display", NBT_COMPOUND)) { + val display = itemStack.tagCompound.getCompoundTag("display") + if (display.hasKey("Lore", NBT_LIST)) { + val lore = display.getTagList("Lore", NBT_STRING) + val loreAsList = ArrayList<String>(lore.tagCount()) + for (lineNumber in 0 until lore.tagCount()) { + loreAsList.add(lore.getStringTagAt(lineNumber)) + } + return Collections.unmodifiableList(loreAsList) + } + } + return emptyList() + } + +// @JvmStatic +// fun hasRightClickAbility(itemStack: ItemStack): Boolean { +// for (line in getItemLore(itemStack)) { +// val stripped = line.stripControlCodes() +// if (stripped.startsWith("Item Ability:") && stripped.endsWith("RIGHT CLICK")) return true +// } +// return false +// } + +// /** +// * Returns the rarity of a given Skyblock item +// * Modified +// * @author BiscuitDevelopment +// * @param item the Skyblock item to check +// * @return the rarity of the item if a valid rarity is found, `null` if no rarity is found, `null` if item is `null` +// */ +// fun getRarity(item: ItemStack?): ItemRarity { +// if (item == null || !item.hasTagCompound()) { +// return ItemRarity.NONE +// } +// val display = item.getSubCompound("display", false) +// if (display == null || !display.hasKey("Lore")) { +// return ItemRarity.NONE +// } +// val lore = display.getTagList("Lore", Constants.NBT.TAG_STRING) +// val name = display.getString("Name") +// +// // Determine the item's rarity +// for (i in (lore.tagCount() - 1) downTo 0) { +// val currentLine = lore.getStringTagAt(i) +// val rarityMatcher = RARITY_PATTERN.find(currentLine) +// if (rarityMatcher != null) { +// val rarity = rarityMatcher.groups["rarity"]?.value ?: continue +// ItemRarity.values().find { +// it.rarityName == rarity.stripControlCodes().substringAfter("SHINY ") +// }?.let { +// return it +// } +// } +// } +// val petRarityMatcher = PET_PATTERN.find(name) +// if (petRarityMatcher != null) { +// val color = petRarityMatcher.groupValues.getOrNull(1) ?: return ItemRarity.NONE +// return ItemRarity.byBaseColor(color) ?: ItemRarity.NONE +// } +// +// // If the item doesn't have a valid rarity, return null +// return ItemRarity.NONE +// } + + fun isPet(item: ItemStack?): Boolean { + if (item == null || !item.hasTagCompound()) { + return false + } + val display = item.getSubCompound("display", false) + if (display == null || !display.hasKey("Lore")) { + return false + } + val name = display.getString("Name") + + return PET_PATTERN.matches(name) + } + + fun setSkullTexture(item: ItemStack, texture: String, SkullOwner: String): ItemStack { + val textureTagCompound = NBTTagCompound() + textureTagCompound.setString("Value", texture) + + val textures = NBTTagList() + textures.appendTag(textureTagCompound) + + val properties = NBTTagCompound() + properties.setTag("textures", textures) + + val skullOwner = NBTTagCompound() + skullOwner.setString("Id", SkullOwner) + skullOwner.setTag("Properties", properties) + + val nbtTag = NBTTagCompound() + nbtTag.setTag("SkullOwner", skullOwner) + + item.tagCompound = nbtTag + return item + } + + fun getSkullTexture(item: ItemStack): String? { + if (item.item != Items.skull) return null + val nbt = item.tagCompound + if (!nbt.hasKey("SkullOwner")) return null + return nbt.getCompoundTag("SkullOwner").getCompoundTag("Properties") + .getTagList("textures", Constants.NBT.TAG_COMPOUND).getCompoundTagAt(0).getString("Value") + } + + fun ItemStack.setLore(lines: List<String>): ItemStack { + setTagInfo("display", getSubCompound("display", true).apply { + setTag("Lore", NBTTagList().apply { + for (line in lines) appendTag(NBTTagString(line)) + }) + }) + return this + } +}
\ No newline at end of file diff --git a/src/main/java/at/lorenz/mod/utils/ItemUtils.kt b/src/main/java/at/lorenz/mod/utils/ItemUtils.kt new file mode 100644 index 000000000..697e57393 --- /dev/null +++ b/src/main/java/at/lorenz/mod/utils/ItemUtils.kt @@ -0,0 +1,39 @@ +package at.lorenz.mod.utils + +import at.lorenz.mod.utils.LorenzUtils.Companion.removeColorCodes +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.inventory.GuiChest +import net.minecraft.item.ItemStack + +class ItemUtils { + + companion object { + fun ItemStack.cleanName() = this.displayName.removeColorCodes() + + fun getItemsInOpenChest(): List<ItemStack> { + val list = mutableListOf<ItemStack>() + val guiChest = Minecraft.getMinecraft().currentScreen as GuiChest + val inventorySlots = guiChest.inventorySlots.inventorySlots + val skipAt = inventorySlots.size - 9 * 4 + var i = 0 + for (slot in inventorySlots) { + val stack = slot.stack + if (stack != null) { + list.add(stack) + } + i++ + if (i == skipAt) break + } + return list + } + + fun isSack(name: String): Boolean = name.endsWith(" Sack") + + fun ItemStack.getLore() = ItemUtil.getItemLore(this) + + fun isCoOpSoulBound(stack: ItemStack): Boolean = stack.getLore().any { it.contains("Co-op Soulbound") } + + fun isRecombobulated(stack: ItemStack): Boolean = stack.getLore().any { it.contains("§k") } + + } +}
\ No newline at end of file diff --git a/src/main/java/at/lorenz/mod/utils/LorenzColor.kt b/src/main/java/at/lorenz/mod/utils/LorenzColor.kt new file mode 100644 index 000000000..e60d6d8d1 --- /dev/null +++ b/src/main/java/at/lorenz/mod/utils/LorenzColor.kt @@ -0,0 +1,27 @@ +package at.lorenz.mod.utils + +import java.awt.Color + +enum class LorenzColor(private var chatColorCode: Char, private val color: Color) { + BLACK('0', Color(0, 0, 0)), + DARK_BLUE('1', Color(0, 0, 170)), + DARK_GREEN('2', Color(0, 170, 0)), + DARK_AQUA('3', Color(0, 170, 170)), + DARK_RED('4', Color(170, 0, 0)), + DARK_PURPLE('5', Color(170, 0, 170)), + GOLD('6', Color(255, 170, 0)), + GRAY('7', Color(170, 170, 170)), + DARK_GRAY('8', Color(85, 85, 85)), + BLUE('9', Color(85, 85, 255)), + GREEN('a', Color(85, 255, 85)), + AQUA('b', Color(85, 255, 255)), + RED('c', Color(255, 85, 85)), + LIGHT_PURPLE('d', Color(255, 85, 255)), + YELLOW('e', Color(255, 255, 85)), + WHITE('f', Color(255, 255, 255)), + ; + + fun getChatColor(): String = "§$chatColorCode" + + fun toColor(): Color = color +}
\ No newline at end of file diff --git a/src/main/java/at/lorenz/mod/utils/LorenzLogger.kt b/src/main/java/at/lorenz/mod/utils/LorenzLogger.kt new file mode 100644 index 000000000..1b7337224 --- /dev/null +++ b/src/main/java/at/lorenz/mod/utils/LorenzLogger.kt @@ -0,0 +1,70 @@ +package at.lorenz.mod.utils + +import at.lorenz.mod.utils.LorenzUtils.Companion.formatCurrentTime +import java.io.File +import java.io.IOException +import java.text.SimpleDateFormat +import java.util.logging.FileHandler +import java.util.logging.Formatter +import java.util.logging.LogRecord +import java.util.logging.Logger + +class LorenzLogger(filePath: String) { + private val format = SimpleDateFormat("HH:mm:ss") + private val fileName = "$PREFIX_PATH$filePath.log" + + companion object { + private var PREFIX_PATH: String + + init { + val format = SimpleDateFormat("yyyy_MM_dd/HH_mm_ss").formatCurrentTime() + PREFIX_PATH = "mods/LorenzAddons/logs/$format/" + } + } + + private lateinit var logger: Logger + + private fun getLogger(): Logger { + if (::logger.isInitialized) { + return logger + } + + val initLogger = initLogger() + this.logger = initLogger + return initLogger + } + + private fun initLogger(): Logger { + val logger = Logger.getLogger("" + System.nanoTime()) + try { + createParent(File(fileName)) + val handler = FileHandler(fileName) + handler.encoding ="utf-8" + logger.addHandler(handler) + handler.formatter = object : Formatter() { + override fun format(logRecord: LogRecord): String { + val message = logRecord.message + return format.formatCurrentTime() + " $message\n" + } + } + } catch (e: SecurityException) { + e.printStackTrace() + } catch (e: IOException) { + e.printStackTrace() + } + return logger + } + + private fun createParent(file: File) { + val parent = file.parentFile + if (parent != null) { + if (!parent.isDirectory) { + parent.mkdirs() + } + } + } + + fun log(text: String?) { + getLogger().info(text) + } +} diff --git a/src/main/java/at/lorenz/mod/utils/LorenzUtils.kt b/src/main/java/at/lorenz/mod/utils/LorenzUtils.kt new file mode 100644 index 000000000..021c055d7 --- /dev/null +++ b/src/main/java/at/lorenz/mod/utils/LorenzUtils.kt @@ -0,0 +1,91 @@ +package at.lorenz.mod.utils + +import net.minecraft.client.Minecraft +import net.minecraft.util.ChatComponentText +import org.intellij.lang.annotations.Language +import java.text.SimpleDateFormat + +class LorenzUtils { + + companion object { + const val DEBUG_PREFIX = "[Debug] §7" + + fun debug(message: String) { + internaChat(DEBUG_PREFIX + message) + } + + fun warning(message: String) { + internaChat("§cWarning! $message") + } + + fun error(message: String) { + internaChat("§4$message") + } + + fun chat(message: String) { + internaChat(message) + } + + private fun internaChat(message: String) { + val thePlayer = Minecraft.getMinecraft().thePlayer + thePlayer.addChatMessage(ChatComponentText(message)) + } + + fun String.matchRegex(@Language("RegExp") regex: String): Boolean = regex.toRegex().matches(this) + + fun String.removeColorCodes(): String { + val builder = StringBuilder() + var skipNext = false + for (c in this.toCharArray()) { + if (c == '§') { + skipNext = true + continue + } + if (skipNext) { + skipNext = false + continue + } + builder.append(c) + } + + return builder.toString() + } + + fun SimpleDateFormat.formatCurrentTime(): String = this.format(System.currentTimeMillis()) + + fun stripVanillaMessage(originalMessage: String): String { + var message = originalMessage + + while (message.startsWith("§r")) { + message = message.substring(2) + } + while (message.endsWith("§r")) { + message = message.substring(0, message.length - 2) + } + +// if (!message.startsWith(LorenzUtils.DEBUG_PREFIX + "chat api got (123)")) { +// if (message.matchRegex("(.*)§r§7 \\((.{1,3})\\)")) { +// val indexOf = message.lastIndexOf("(") +//// LorenzAddons.testLogger.log("chat api got (123)!") +//// LorenzAddons.testLogger.log("before: '$message'") +// message = message.substring(0, indexOf - 5) +//// LorenzAddons.testLogger.log("after: '$message'") +//// LorenzAddons.testLogger.log("") +//// LorenzUtils.debug("chat api got (123)") +//// } else if (message.endsWith("§r§7 (2)")) { +////// LorenzAddons.testLogger.log("other variant: '$message'") +////// LorenzAddons.testLogger.log("") +//// LorenzUtils.debug("chat api got WRONG (123)") +// } +// } + + return message + } + + fun Double.round(decimals: Int): Double { + var multiplier = 1.0 + repeat(decimals) { multiplier *= 10 } + return kotlin.math.round(this * multiplier) / multiplier + } + } +}
\ No newline at end of file diff --git a/src/main/java/at/lorenz/mod/utils/RenderUtil.kt b/src/main/java/at/lorenz/mod/utils/RenderUtil.kt new file mode 100644 index 000000000..e633461a4 --- /dev/null +++ b/src/main/java/at/lorenz/mod/utils/RenderUtil.kt @@ -0,0 +1,20 @@ +package at.lorenz.mod.utils + +import net.minecraft.client.gui.Gui +import net.minecraft.inventory.Slot + +class RenderUtil { + + companion object { + + infix fun Slot.highlight(color: LorenzColor) { + Gui.drawRect( + this.xDisplayPosition, + this.yDisplayPosition, + this.xDisplayPosition + 16, + this.yDisplayPosition + 16, + color.toColor().rgb + ) + } + } +}
\ No newline at end of file |