summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravy Boat <thatgravyboat@gmail.com>2021-09-23 19:25:26 -0230
committerGravy Boat <thatgravyboat@gmail.com>2021-09-23 19:25:26 -0230
commit7dc51e6f1f280276d782e3c644099758ff01bdb7 (patch)
tree89bbbbcc22b100c20e752a750ccef0547c466229
parentf71b3ccee2ec9949de7e572375a45dbf1c3fadb8 (diff)
downloadRewardClaim-7dc51e6f1f280276d782e3c644099758ff01bdb7.tar.gz
RewardClaim-7dc51e6f1f280276d782e3c644099758ff01bdb7.tar.bz2
RewardClaim-7dc51e6f1f280276d782e3c644099758ff01bdb7.zip
Initial Commit
-rw-r--r--build.gradle38
-rw-r--r--settings.gradle2
-rw-r--r--src/main/java/dev/asbyth/template/ForgeTemplate.java15
-rw-r--r--src/main/kotlin/tech/thatgravyboat/rewardclaim/MappedImageCache.kt16
-rw-r--r--src/main/kotlin/tech/thatgravyboat/rewardclaim/RewardClaim.kt56
-rw-r--r--src/main/kotlin/tech/thatgravyboat/rewardclaim/RewardConfiguration.kt49
-rw-r--r--src/main/kotlin/tech/thatgravyboat/rewardclaim/RewardLanguage.kt20
-rw-r--r--src/main/kotlin/tech/thatgravyboat/rewardclaim/adapter/KotlinLanguageAdapter.kt27
-rw-r--r--src/main/kotlin/tech/thatgravyboat/rewardclaim/types/GameMode.kt42
-rw-r--r--src/main/kotlin/tech/thatgravyboat/rewardclaim/types/RewardData.kt82
-rw-r--r--src/main/kotlin/tech/thatgravyboat/rewardclaim/types/RewardImage.kt15
-rw-r--r--src/main/kotlin/tech/thatgravyboat/rewardclaim/types/RewardRarity.kt11
-rw-r--r--src/main/kotlin/tech/thatgravyboat/rewardclaim/types/StreakData.kt3
-rw-r--r--src/main/kotlin/tech/thatgravyboat/rewardclaim/types/WebData.kt60
-rw-r--r--src/main/kotlin/tech/thatgravyboat/rewardclaim/ui/RewardClaimGui.kt308
-rw-r--r--src/main/kotlin/tech/thatgravyboat/rewardclaim/ui/UIButton.kt51
-rw-r--r--src/main/kotlin/tech/thatgravyboat/rewardclaim/ui/UIPopup.kt70
-rw-r--r--src/main/kotlin/tech/thatgravyboat/rewardclaim/ui/UIReward.kt103
-rw-r--r--src/main/kotlin/tech/thatgravyboat/rewardclaim/ui/UISelectedReward.kt86
-rw-r--r--src/main/resources/mcmod.info6
-rw-r--r--src/main/resources/rewardclaim/external_link.pngbin0 -> 187 bytes
21 files changed, 1031 insertions, 29 deletions
diff --git a/build.gradle b/build.gradle
index 88b8faf..c5d186a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,12 +1,13 @@
plugins {
id "java"
id "com.github.johnrengelman.shadow" version "6.1.0"
- id "net.minecraftforge.gradle.forge" version "6f53277"
+ id "net.minecraftforge.gradle.forge" version "ddb1eb0"
+ id "org.jetbrains.kotlin.jvm" version "1.5.31"
}
version = "1.0"
-group = "dev.asbyth"
-archivesBaseName = "ForgeTemplate"
+group = "tech.thatgravyboat"
+archivesBaseName = "RewardClaim"
sourceCompatibility = targetCompatibility = 1.8
compileJava.options.encoding = 'UTF-8'
@@ -19,16 +20,22 @@ minecraft {
}
configurations {
- // Creates an extra configuration that implements `implementation` to be used later as the configuration that shades libraries
include
implementation.extendsFrom(include)
}
+repositories {
+ mavenCentral()
+ maven { url "https://repo.sk1er.club/repository/maven-public" }
+ maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
+ maven { url 'https://repo.spongepowered.org/maven' }
+ maven { url 'https://jitpack.io' }
+}
+
dependencies {
- // How to normally add a dependency (If you don't want it to be added to the jar)
- // implementation "com.example:example:1.0.0"
- // If you would like to include it (have the library inside your jar) instead use
- // include "com.example:example:1.0.0"
+ include 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.21'
+ include "gg.essential:loader-launchwrapper:1.1.1"
+ implementation "gg.essential:essential-1.8.9-forge:1457"
}
/**
@@ -66,11 +73,22 @@ task moveResources {
moveResources.dependsOn processResources
classes.dependsOn moveResources
-// This adds support to ("embed", "shade", "include") libraries into our JAR
+tasks.reobfJar.dependsOn(tasks.shadowJar)
+
shadowJar {
archiveClassifier.set('')
configurations = [project.configurations.include]
duplicatesStrategy DuplicatesStrategy.EXCLUDE
}
-jar.dependsOn shadowJar
+compileKotlin {
+ kotlinOptions {
+ jvmTarget = "1.8"
+ freeCompilerArgs = ['-Xjvm-default=enable']
+ }
+}
+compileTestKotlin {
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
+}
diff --git a/settings.gradle b/settings.gradle
index bac08c3..807a7d2 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -25,4 +25,4 @@ pluginManagement {
}
}
-rootProject.name = "ForgeTemplate" \ No newline at end of file
+rootProject.name = "RewardClaim" \ No newline at end of file
diff --git a/src/main/java/dev/asbyth/template/ForgeTemplate.java b/src/main/java/dev/asbyth/template/ForgeTemplate.java
deleted file mode 100644
index 959cdbc..0000000
--- a/src/main/java/dev/asbyth/template/ForgeTemplate.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package dev.asbyth.template;
-
-import net.minecraft.client.Minecraft;
-import net.minecraftforge.fml.common.Mod;
-import net.minecraftforge.fml.common.event.FMLInitializationEvent;
-
-@Mod(modid = "forgetemplate", name = "Forge Template", version = "1.0")
-public class ForgeTemplate {
- @Mod.EventHandler
- public void onFMLInitialization(FMLInitializationEvent event) {
- // $USER = The username of the currently logged in user.
- // Simply prints out Hello, $USER.
- System.out.println("Hello, " + Minecraft.getMinecraft().getSession().getUsername() + "!");
- }
-}
diff --git a/src/main/kotlin/tech/thatgravyboat/rewardclaim/MappedImageCache.kt b/src/main/kotlin/tech/thatgravyboat/rewardclaim/MappedImageCache.kt
new file mode 100644
index 0000000..e47c649
--- /dev/null
+++ b/src/main/kotlin/tech/thatgravyboat/rewardclaim/MappedImageCache.kt
@@ -0,0 +1,16 @@
+package tech.thatgravyboat.rewardclaim
+
+import gg.essential.elementa.components.image.ImageCache
+import java.awt.image.BufferedImage
+import java.net.URL
+
+object MappedImageCache : ImageCache {
+
+ private val IMAGES = hashMapOf<URL, BufferedImage>()
+
+ override fun get(url: URL) = IMAGES[url]
+
+ override fun set(url: URL, image: BufferedImage) {
+ IMAGES[url] = image
+ }
+} \ No newline at end of file
diff --git a/src/main/kotlin/tech/thatgravyboat/rewardclaim/RewardClaim.kt b/src/main/kotlin/tech/thatgravyboat/rewardclaim/RewardClaim.kt
new file mode 100644
index 0000000..29628ae
--- /dev/null
+++ b/src/main/kotlin/tech/thatgravyboat/rewardclaim/RewardClaim.kt
@@ -0,0 +1,56 @@
+package tech.thatgravyboat.rewardclaim
+
+import gg.essential.api.EssentialAPI
+import net.minecraft.client.gui.GuiScreenBook
+import net.minecraftforge.client.event.ClientChatReceivedEvent
+import net.minecraftforge.client.event.GuiOpenEvent
+import net.minecraftforge.common.MinecraftForge
+import net.minecraftforge.fml.common.Mod
+import net.minecraftforge.fml.common.event.FMLInitializationEvent
+import net.minecraftforge.fml.common.event.FMLPreInitializationEvent
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import tech.thatgravyboat.rewardclaim.ui.RewardClaimGui
+
+@Mod(name = "RewardClaim", modid = "gravyrewardclaim", version = "1.0.0", modLanguageAdapter = "tech.thatgravyboat.rewardclaim.adapter.KotlinLanguageAdapter")
+object ForgeTemplate {
+
+ private var rewardClaimTime: Long = 0
+
+ @Mod.EventHandler
+ fun onFMLInitialization(event: FMLInitializationEvent?) {
+ MinecraftForge.EVENT_BUS.register(this)
+ }
+
+ @Mod.EventHandler
+ fun onPreInit(event: FMLPreInitializationEvent?) {
+ RewardConfiguration.loadData()
+ }
+
+ @SubscribeEvent
+ fun onChatMessage(event: ClientChatReceivedEvent) {
+ val rewardMatcher = RewardConfiguration.rewardMessageRegex.matcher(event.message.unformattedText.trim())
+ val rewardMissedMatcher = RewardConfiguration.rewardMissedMessageRegex.matcher(event.message.unformattedText.trim())
+
+ if (rewardMatcher.matches()) {
+ EssentialAPI.getGuiUtil().openScreen(RewardClaimGui(rewardMatcher.group("id")))
+ rewardClaimTime = System.currentTimeMillis()
+ }
+ if (rewardMissedMatcher.matches()) {
+ EssentialAPI.getNotifications().push(
+ "Reward Claim Missed!",
+ "You missed a reward claim, click on this to open the reward claim gui to claim your reward.",
+ { EssentialAPI.getGuiUtil().openScreen(RewardClaimGui(rewardMissedMatcher.group("id"))) }
+ )
+ event.isCanceled = true
+ }
+ }
+
+ @SubscribeEvent
+ fun onScreen(event: GuiOpenEvent) {
+ if (EssentialAPI.getGuiUtil().openedScreen() is RewardClaimGui && event.gui is GuiScreenBook && System.currentTimeMillis() - rewardClaimTime <= 3000){
+ event.isCanceled = true
+ rewardClaimTime = 0
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/main/kotlin/tech/thatgravyboat/rewardclaim/RewardConfiguration.kt b/src/main/kotlin/tech/thatgravyboat/rewardclaim/RewardConfiguration.kt
new file mode 100644
index 0000000..3d8a79a
--- /dev/null
+++ b/src/main/kotlin/tech/thatgravyboat/rewardclaim/RewardConfiguration.kt
@@ -0,0 +1,49 @@
+package tech.thatgravyboat.rewardclaim
+
+import com.google.gson.Gson
+import com.google.gson.JsonObject
+import net.minecraft.util.JsonUtils
+import tech.thatgravyboat.rewardclaim.types.RewardImage
+import java.io.IOException
+import java.net.URL
+import java.util.*
+import java.util.regex.Pattern
+
+private val GSON = Gson()
+
+object RewardConfiguration {
+ val TEXTURES: MutableMap<String, RewardImage> = HashMap<String, RewardImage>()
+ var rewardMessageRegex: Pattern = Pattern.compile("Click the link to visit our website and claim your reward: https://rewards\\.hypixel\\.net/claim-reward/(?<id>[A-Za-z0-9]{8})")
+ var rewardMissedMessageRegex: Pattern = Pattern.compile("We noticed you haven't claimed your free Daily Reward yet!\\nTo choose your reward you have to click the link to visit our website! As a reminder, here's your link for today: https://rewards\\.hypixel\\.net/claim-reward/(?<id>[A-Za-z0-9]{8})")
+ var userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36"
+
+ fun loadData() {
+ val json = GSON.fromJson(readData(), JsonObject::class.java)
+
+ json.get("textures")?.apply {
+ for (texture in asJsonArray) {
+ val image = texture.asJsonObject
+ TEXTURES[image["id"].asString] = RewardImage(image["url"].asString, JsonUtils.getInt(image, "height", 142))
+ }
+ }
+
+ json.get("rewardRegex")?.apply { rewardMessageRegex = Pattern.compile(asString) }
+ json.get("missedRewardRegex")?.apply { rewardMissedMessageRegex = Pattern.compile(asString) }
+ json.get("userAgent")?.apply { userAgent = asString }
+ }
+
+ private fun readData(): String {
+ try {
+ Scanner(
+ URL("https://gist.githubusercontent.com/ThatGravyBoat/05cf118ea1daced936f040a41a648819/raw/2410c868444b073fd212fbed1da5a063d79dc816/data.json").openStream(),
+ "UTF-8"
+ ).use { scanner ->
+ scanner.useDelimiter("\\A")
+ return if (scanner.hasNext()) scanner.next() else ""
+ }
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+ return ""
+ }
+} \ No newline at end of file
diff --git a/src/main/kotlin/tech/thatgravyboat/rewardclaim/RewardLanguage.kt b/src/main/kotlin/tech/thatgravyboat/rewardclaim/RewardLanguage.kt
new file mode 100644
index 0000000..6361bc5
--- /dev/null
+++ b/src/main/kotlin/tech/thatgravyboat/rewardclaim/RewardLanguage.kt
@@ -0,0 +1,20 @@
+package tech.thatgravyboat.rewardclaim
+
+import java.util.regex.Pattern
+
+private val TRANSLATION_LINE_REGEX = Pattern.compile("\"(?<key>.*)\": ?\"(?<text>.*)\",?")
+
+class RewardLanguage(translationDataFromHtml: String) {
+
+ private val translations = hashMapOf<String, String>()
+
+ fun translate(key: String) = translations.getValue(key)
+
+ //We have to do it this way as this is easier than fixing all the things that isn't
+ //valid in normal json but is valid in javascript objects. Such as escaped single quotes and trailing commas.
+ init {
+ TRANSLATION_LINE_REGEX.matcher(translationDataFromHtml.replace("\\'", "'")).apply {
+ while (find()) translations[group("key")] = group("text")
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/kotlin/tech/thatgravyboat/rewardclaim/adapter/KotlinLanguageAdapter.kt b/src/main/kotlin/tech/thatgravyboat/rewardclaim/adapter/KotlinLanguageAdapter.kt
new file mode 100644
index 0000000..11b3188
--- /dev/null
+++ b/src/main/kotlin/tech/thatgravyboat/rewardclaim/adapter/KotlinLanguageAdapter.kt
@@ -0,0 +1,27 @@
+package tech.thatgravyboat.rewardclaim.adapter
+
+import net.minecraftforge.fml.common.FMLModContainer
+import net.minecraftforge.fml.common.ILanguageAdapter
+import net.minecraftforge.fml.common.ModContainer
+import net.minecraftforge.fml.relauncher.Side
+import java.lang.reflect.Field
+import java.lang.reflect.Method
+
+class KotlinLanguageAdapter : ILanguageAdapter {
+ override fun supportsStatics() = false
+
+ override fun setProxy(target: Field, proxyTarget: Class<*>, proxy: Any) {
+ target.set(proxyTarget.getDeclaredField("INSTANCE").get(null), proxy)
+ }
+
+ override fun getNewInstance(
+ container: FMLModContainer,
+ objectClass: Class<*>,
+ classLoader: ClassLoader,
+ factoryMarkedAnnotation: Method?
+ ): Any {
+ return objectClass.fields.find { it.name == "INSTANCE" }?.get(null) ?: objectClass.newInstance()
+ }
+
+ override fun setInternalProxies(mod: ModContainer?, side: Side?, loader: ClassLoader?) {}
+} \ No newline at end of file
diff --git a/src/main/kotlin/tech/thatgravyboat/rewardclaim/types/GameMode.kt b/src/main/kotlin/tech/thatgravyboat/rewardclaim/types/GameMode.kt
new file mode 100644
index 0000000..fca199b
--- /dev/null
+++ b/src/main/kotlin/tech/thatgravyboat/rewardclaim/types/GameMode.kt
@@ -0,0 +1,42 @@
+package tech.thatgravyboat.rewardclaim.types
+
+@Suppress("unused")
+enum class GameMode(val displayName: String) {
+ ERROR("Error"),
+ BEDWARS("Bed Wars"),
+ SKYWARS("SkyWars"),
+ PROTOTYPE("Prototype"),
+ SKYBLOCK("SkyBlock"),
+ MAIN("Main"),
+ MURDER_MYSTERY("Murder Mystery"),
+ HOUSING("Housing"),
+ ARCADE("Arcade"),
+ BUILD_BATTLE("Build Battle"),
+ DUELS("Duels"),
+ PIT("PIT"),
+ UHC("UHC"),
+ SPEED_UHC("Speed UHC"),
+ TNTGAMES("TNT Games"),
+ LEGACY("Classic"),
+ QUAKECRAFT("Quakecraft"),
+ WALLS("Walls"),
+ PAINTBALL("Paintball"),
+ VAMPIREZ("VampireZ"),
+ ARENA("Arena"),
+ GINGERBREAD("Turbo Kart Racers"),
+ MCGO("Cops and Crims"),
+ SURVIVAL_GAMES("Blitz SG"),
+ WALLS3("Mega Walls"),
+ SUPER_SMASH("Smash Heroes"),
+ BATTLEGROUND("Warlords");
+
+ companion object {
+ fun getModeFromId(id: String?): GameMode {
+ return try {
+ valueOf(id!!)
+ } catch (e: Exception) {
+ ERROR
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/kotlin/tech/thatgravyboat/rewardclaim/types/RewardData.kt b/src/main/kotlin/tech/thatgravyboat/rewardclaim/types/RewardData.kt
new file mode 100644
index 0000000..cc89422
--- /dev/null
+++ b/src/main/kotlin/tech/thatgravyboat/rewardclaim/types/RewardData.kt
@@ -0,0 +1,82 @@
+package tech.thatgravyboat.rewardclaim.types
+
+import tech.thatgravyboat.rewardclaim.RewardConfiguration
+import tech.thatgravyboat.rewardclaim.RewardLanguage
+import java.util.*
+import java.util.regex.Pattern
+
+
+private val ARMOR_PIECE_REGEX = Pattern.compile("^[a-z0-9_]+_([a-z]+)$", Pattern.CASE_INSENSITIVE)
+private val ARMOR_REGEX = Pattern.compile("_([a-z]+)$", Pattern.CASE_INSENSITIVE)
+
+class RewardData(val rarity: RewardRarity,
+ private val reward : String,
+ val amount : Int?,
+ val boxes : Int?,
+ private val gameMode : GameMode?,
+ private val rewardPackage : String?,
+ private val rewardKey : String?
+ ) {
+
+ fun getDisplayName(language: RewardLanguage): String {
+ rewardPackage?.let { item ->
+ if (reward.equals("housing_package", ignoreCase = true)) {
+ return "${rarity.color}${language.translate("housing.skull." + item.replace("specialoccasion_reward_card_skull_", ""))}"
+ }
+ }
+ rewardKey?.let { key ->
+ if (reward.equals("add_vanity", ignoreCase = true)) {
+ val pieceMatcher = ARMOR_PIECE_REGEX.matcher(key)
+ val armorMatcher = ARMOR_REGEX.matcher(key)
+ if ("suit" in key && pieceMatcher.find() && armorMatcher.find()) {
+ return "${rarity.color}${language.translate("vanity." + armorMatcher.group(1))} ${language.translate("vanity.armor." + pieceMatcher.group(1))}"
+ } else if ("emote" in key || "taunt" in key) {
+ return "${rarity.color}${language.translate("vanity.$key")}"
+ }
+ }
+ }
+ return if (reward.equals("tokens", ignoreCase = true) || reward.equals("coins", ignoreCase = true)) {
+ "${rarity.color}${language.translate("type.$reward").replace("{\$game}", gameMode!!.displayName)}"
+ } else {
+ "${rarity.color}${language.translate("type.$reward")}"
+ }
+ }
+
+ fun getDescription(language: RewardLanguage): String {
+ rewardKey?.let { key ->
+ if (reward.equals("add_vanity", ignoreCase = true)) {
+ when {
+ "suit" in key -> return language.translate("vanity.suits.description")
+ "emote" in key -> return language.translate("vanity.emotes.description")
+ "taunt" in key -> return language.translate("vanity.gestures.description")
+ }
+ }
+ }
+ return if (reward.equals("tokens", ignoreCase = true) || reward.equals("coins", ignoreCase = true)) {
+ "${rarity.color}${language.translate("type.$reward.description").replace("{\$game}", gameMode!!.displayName)}"
+ } else {
+ "${rarity.color}${language.translate("type.$reward.description")}"
+ }
+ }
+
+ val image: RewardImage?
+ get() {
+ var id = reward.lowercase(Locale.ROOT)
+ rewardKey?.let { key ->
+ if (id == "add_vanity") {
+ when {
+ "suit" in key -> id += "_suit"
+ "emote" in key -> id += "_emote"
+ "taunt" in key -> id += "_taunt"
+ }
+ }
+ }
+ rewardPackage?.let { item ->
+ if (id == "housing_package") {
+ val packageId = item.replace("specialoccasion_reward_card_skull_", "")
+ RewardConfiguration.TEXTURES[id + "_" + packageId]?.let { image -> return image }
+ }
+ }
+ return RewardConfiguration.TEXTURES[id]
+ }
+} \ No newline at end of file
diff --git a/src/main/kotlin/tech/thatgravyboat/rewardclaim/types/RewardImage.kt b/src/main/kotlin/tech/thatgravyboat/rewardclaim/types/RewardImage.kt
new file mode 100644
index 0000000..fc8446a
--- /dev/null
+++ b/src/main/kotlin/tech/thatgravyboat/rewardclaim/types/RewardImage.kt
@@ -0,0 +1,15 @@
+package tech.thatgravyboat.rewardclaim.types
+
+import java.net.MalformedURLException
+import java.net.URL
+
+class RewardImage(private val urlString: String, val height: Int) {
+ val url: URL?
+ get() {
+ return try {
+ URL(urlString)
+ } catch (ignored: MalformedURLException) {
+ null
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/kotlin/tech/thatgravyboat/rewardclaim/types/RewardRarity.kt b/src/main/kotlin/tech/thatgravyboat/rewardclaim/types/RewardRarity.kt
new file mode 100644
index 0000000..d4e45b1
--- /dev/null
+++ b/src/main/kotlin/tech/thatgravyboat/rewardclaim/types/RewardRarity.kt
@@ -0,0 +1,11 @@
+package tech.thatgravyboat.rewardclaim.types
+
+import gg.essential.universal.ChatColor
+import java.awt.Color
+
+enum class RewardRarity(val translationKey: String, val color: ChatColor) {
+ COMMON("rarity.common", ChatColor.WHITE),
+ RARE("rarity.rare", ChatColor.AQUA),
+ EPIC("rarity.epic", ChatColor.DARK_PURPLE),
+ LEGENDARY("rarity.legendary", ChatColor.GOLD);
+} \ No newline at end of file
diff --git a/src/main/kotlin/tech/thatgravyboat/rewardclaim/types/StreakData.kt b/src/main/kotlin/tech/thatgravyboat/rewardclaim/types/StreakData.kt
new file mode 100644
index 0000000..4a5cb98
--- /dev/null
+++ b/src/main/kotlin/tech/thatgravyboat/rewardclaim/types/StreakData.kt
@@ -0,0 +1,3 @@
+package tech.thatgravyboat.rewardclaim.types
+
+class StreakData(val progress : Int, val current : Int, val highest : Int) \ No newline at end of file
diff --git a/src/main/kotlin/tech/thatgravyboat/rewardclaim/types/WebData.kt b/src/main/kotlin/tech/thatgravyboat/rewardclaim/types/WebData.kt
new file mode 100644
index 0000000..38318d1
--- /dev/null
+++ b/src/main/kotlin/tech/thatgravyboat/rewardclaim/types/WebData.kt
@@ -0,0 +1,60 @@
+package tech.thatgravyboat.rewardclaim.types
+
+import com.google.gson.Gson
+import com.google.gson.JsonObject
+import net.minecraft.util.JsonUtils
+import tech.thatgravyboat.rewardclaim.RewardLanguage
+import java.util.regex.Matcher
+
+private val GSON = Gson()
+
+class WebData(security : Matcher, data : Matcher, i18n : Matcher) {
+
+ val securityToken : String = security.group("token")
+ val language : RewardLanguage = RewardLanguage(i18n.group("translations"))
+
+ val rewards = mutableListOf<RewardData>()
+ var streak : StreakData = StreakData(0,0,0)
+ var activeAd : Int = 0
+ var adLink : String = "https://store.hypixel.net/?utm_source=rewards-video&utm_medium=website&utm_content=TRsCiBNYY7M&utm_campaign=Rewards"
+ var skippable : Boolean = false
+ var duration : Int = 30
+
+ init {
+ val json = GSON.fromJson(data.group("data"), JsonObject::class.java)
+ json.get("rewards")?.let {
+ if (it.isJsonArray) {
+ it.asJsonArray.forEach { jsonObject ->
+ val rewardObject = jsonObject.asJsonObject
+ rewards.add(RewardData(
+ RewardRarity.valueOf(rewardObject.get("rarity").asString!!),
+ rewardObject.get("reward").asString!!,
+ if (rewardObject.has("amount")) rewardObject.get("amount").asInt else null,
+ if (rewardObject.has("intlist")) rewardObject.get("intlist").asJsonArray.size() else null,
+ if (rewardObject.has("gameType")) GameMode.getModeFromId(rewardObject.get("gameType").asString) else null,
+ JsonUtils.getString(rewardObject, "package", null),
+ JsonUtils.getString(rewardObject, "key", null),
+ ))
+ }
+ }
+ }
+
+ json.get("dailyStreak")?.let {
+ val streakObject = it.asJsonObject
+ streak = StreakData(JsonUtils.getInt(streakObject, "value", 0),
+ JsonUtils.getInt(streakObject, "score", 0),
+ JsonUtils.getInt(streakObject, "highScore", 0)
+ )
+ }
+
+ json.get("activeAd")?.let { activeAd = it.asInt }
+ json.get("ad")?.let { jsonObject ->
+ val adObject = jsonObject.asJsonObject
+ adObject.get("link")?.let { adLink = it.asString }
+ adObject.get("duration")?.let { duration = it.asInt }
+ }
+ json.get("skippable")?.let { skippable = it.asBoolean }
+
+ }
+
+} \ No newline at end of file
diff --git a/src/main/kotlin/tech/thatgravyboat/rewardclaim/ui/RewardClaimGui.kt b/src/main/kotlin/tech/thatgravyboat/rewardclaim/ui/RewardClaimGui.kt
new file mode 100644
index 0000000..8f1d535
--- /dev/null
+++ b/src/main/kotlin/tech/thatgravyboat/rewardclaim/ui/RewardClaimGui.kt
@@ -0,0 +1,308 @@
+package tech.thatgravyboat.rewardclaim.ui
+
+import gg.essential.api.utils.Multithreading.runAsync
+import gg.essential.api.utils.Multithreading.schedule
+import gg.essential.elementa.WindowScreen
+import gg.essential.elementa.components.*
+import gg.essential.elementa.constraints.CenterConstraint
+import gg.essential.elementa.dsl.*
+import gg.essential.universal.ChatColor
+import gg.essential.universal.UDesktop
+import gg.essential.vigilance.gui.VigilancePalette
+import org.apache.commons.io.IOUtils
+import tech.thatgravyboat.rewardclaim.RewardConfiguration
+import tech.thatgravyboat.rewardclaim.types.WebData
+import java.awt.Color
+import java.net.*
+import java.nio.charset.Charset
+import java.util.concurrent.TimeUnit
+import java.util.regex.Matcher
+import java.util.regex.Pattern
+
+private val BUTTON_HOVER = Color(0, 212, 105)
+
+private val SECURITY_REGEX = Pattern.compile("window\\.securityToken = \"(?<token>.*)\";")
+private val DATA_REGEX = Pattern.compile("window\\.appData = '(?<data>\\{.*})';")
+private val I18N_REGEX = Pattern.compile("window.i18n = \\{(?<translations>.*)};", Pattern.DOTALL)
+
+class RewardClaimGui(private val id : String) : WindowScreen() {
+
+ private var state: State = State.LOADING
+ private var selected = -1
+
+ //Raw Data
+ private lateinit var data: WebData
+
+ init {
+ runAsync(Runnable {
+ try {
+ val cookieManager = CookieManager()
+ CookieHandler.setDefault(cookieManager)
+ val url = URL("https://rewards.hypixel.net/claim-reward/$id")
+ (url.openConnection() as HttpURLConnection).apply {
+ requestMethod = "GET"
+ useCaches = true
+ addRequestProperty("User-Agent", RewardConfiguration.userAgent)
+ readTimeout = 15000
+ connectTimeout = 15000
+ doOutput = true
+ inputStream.use {
+ val html = IOUtils.toString(it, Charset.defaultCharset())
+ val securityMatcher: Matcher = SECURITY_REGEX.matcher(html)
+ val dataMatcher: Matcher = DATA_REGEX.matcher(html)
+ val i18nMatcher: Matcher = I18N_REGEX.matcher(html)
+ val securityFound = securityMatcher.find()
+ val dataFound = dataMatcher.find()
+ val i18nFound = i18nMatcher.find()
+
+ if (securityFound && dataFound && i18nFound) {
+ data = WebData(securityMatcher, dataMatcher, i18nMatcher)
+
+ if (data.rewards.isEmpty()) {
+ state = State.FAILED_REWARDS
+ errorPopup("Rewards were empty.")
+ }else {
+ state = State.SUCCESSFUL
+ updateElements()
+ }
+
+ if (data.skippable || data.duration == 0){
+ adPopup(true)
+ }else {
+ schedule({ adPopup(true) }, data.duration.toLong(), TimeUnit.SECONDS)
+ }
+ } else {
+ state = State.FAILED_REWARDS
+ errorPopup("Regex could not be found.\nSecurity: $securityFound\nI18n: $i18nFound\nData: $dataFound")
+ }
+ }
+ }
+ } catch (e: Exception) {
+ state = State.FAILED
+ errorPopup("Error: " + e.message)
+ e.printStackTrace()
+ }
+ })
+ adPopup(false)
+ }
+
+ private val background = UIBlock(VigilancePalette.getBackground()).constrain {
+ width = 100.percent()
+ height = 100.percent()
+ } childOf this.window
+
+ private val selectedReward = UISelectedReward(30.percent() - 0.5.pixel()) childOf background
+
+ init {
+ UIBlock(VigilancePalette.getDivider()).constrain {
+ width = 1.pixel()
+ height = 75.percent()
+ x = 50.percent() - 0.5.pixel()
+ y = 12.5.percent()
+ } childOf background
+
+ UIBlock(VigilancePalette.getDivider()).constrain {
+ width = 1.pixel()
+ height = 75.percent()
+ x = 10.percent() - 0.5.pixel()
+ y = 12.5.percent()
+ } childOf background
+
+ UIBlock(VigilancePalette.getDivider()).constrain {
+ width = 30.percent() - 1.pixel()
+ height = 1.pixel()
+ x = 15.percent()
+ y = 70.percent()
+ } childOf background
+
+ UIText("Click to claim 1 of the 3 Rewards").apply {
+ constrain {
+ x = 30.percent() - 0.5.pixel() - (getTextWidth() / 2f).pixel()
+ y = 15.percent()
+ } childOf background
+ }
+
+ UIText("Reward Options").apply {
+ constrain {
+ x = 75.percent() - (getTextWidth() / 2f).pixel()
+ y = 15.percent()
+ } childOf background
+ }
+
+ UIText("§lClaim Reward", false).let { text ->
+ UIBlock(VigilancePalette.getAccent()).let { button ->
+ button.constrain {
+ width = text.getTextWidth().pixel() + 18.pixel()
+ height = 17.pixel()
+ y = 60.percent()
+ x = 30.percent() - 0.5.pixel() - (text.getTextWidth() / 2f + 9f).pixel()
+ } childOf background
+
+ text.constrain {
+ x = CenterConstraint()
+ y = CenterConstraint()
+ } childOf button
+
+ button.onMouseEnter { setColor(BUTTON_HOVER) }
+ button.onMouseLeave { setColor(VigilancePalette.getAccent()) }
+ button.onMouseClick { event ->
+ if (event.mouseButton == 0 && selected != -1) confirmPopup()
+ event.stopPropagation()
+ }
+ }
+ }
+
+ UIText("Daily Streak").apply {
+ constrain {
+ x = 30.percent() - 0.5.pixel() - (getTextWidth() / 2f).pixel()
+ y = 72.percent()
+ } childOf background
+ }
+
+ adPopup(false)
+ }
+
+ //region Daily Streak
+
+ private val streakSubHeading = UIText(String.format("Current: %d Highest: %d", 0, 0)).apply {
+ constrain {
+ x = 30.percent() - 0.5.pixel() - (getTextWidth() / 2f).pixel()
+ y = 77.percent()
+ } childOf background
+ }
+
+ private val streaks = Array(9){ i ->
+ UICircle(5f, VigilancePalette.getDivider(), 15).let {
+ it.constrain {
+ x = ((15.percent() + (15.percent() - 0.5.pixel())) - 45.pixel()) + (it.getWidth() * i).pixel()
+ y = 83.percent()
+ } childOf background
+ }
+ }
+
+ //endregion
+
+ private val rewards = Array(3){ i -> UIReward(57.5.percent(), 30.percent() + (18 * i).percent()) childOf background}.also {
+ for (reward in it) {
+ reward.onMouseClick { event ->
+ if (event.mouseButton == 0 && state == State.SUCCESSFUL) {
+ for (j in 0..2) {
+ it[j].setSelected(it[j] == reward)
+ if (it[j] != event.currentTarget) continue
+ selected = j
+ selectedReward.updateInfo(data.rewards[selected], data.language)
+ }
+ }
+ }
+ reward.hide(true)
+ }
+ }
+
+ private fun updateElements() {
+ data.let {
+ for (i in 0 until data.streak.progress) streaks[i].setColor(VigilancePalette.getAccent())
+ streakSubHeading.setText(String.format("Current: %d Highest: %d", data.streak.current, data.streak.highest))
+ streakSubHeading.setX(30.percent() - 0.5.pixel() - (streakSubHeading.getTextWidth() / 2f).pixel())
+
+ for (i in 0 until 3) {
+ rewards[i].let {
+ it.setData(data.rewards[i], data.language)
+ it.unhide(true)
+ }
+ }
+ }
+ }
+
+ private var popup : UIPopup? = nul