aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorWyvest <45589059+Wyvest@users.noreply.github.com>2021-12-21 17:55:14 +0700
committerWyvest <45589059+Wyvest@users.noreply.github.com>2021-12-21 17:55:14 +0700
commit870da2e7fcf370233c9e64d55dd0295cec6665f0 (patch)
treeaad94b09e4cc0d8362d516a0696a0b1d1c23f6bc /src/main
downloadChatting-870da2e7fcf370233c9e64d55dd0295cec6665f0.tar.gz
Chatting-870da2e7fcf370233c9e64d55dd0295cec6665f0.tar.bz2
Chatting-870da2e7fcf370233c9e64d55dd0295cec6665f0.zip
initial commit
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/com/raeids/stratus/mixin/GuiChatMixin.java60
-rw-r--r--src/main/java/com/raeids/stratus/mixin/GuiNewChatAccessor.java15
-rw-r--r--src/main/java/com/raeids/stratus/mixin/GuiNewChatMixin.java49
-rw-r--r--src/main/kotlin/com/raeids/stratus/Stratus.kt115
-rw-r--r--src/main/kotlin/com/raeids/stratus/command/StratusCommand.kt15
-rw-r--r--src/main/kotlin/com/raeids/stratus/config/StratusConfig.kt48
-rw-r--r--src/main/kotlin/com/raeids/stratus/hook/ChatHook.kt41
-rw-r--r--src/main/kotlin/com/raeids/stratus/updater/DownloadGui.kt53
-rw-r--r--src/main/kotlin/com/raeids/stratus/updater/Updater.kt101
-rw-r--r--src/main/kotlin/com/raeids/stratus/utils/RenderHelper.kt63
-rw-r--r--src/main/resources/mcmod.info18
-rw-r--r--src/main/resources/mixins.stratus.json12
12 files changed, 590 insertions, 0 deletions
diff --git a/src/main/java/com/raeids/stratus/mixin/GuiChatMixin.java b/src/main/java/com/raeids/stratus/mixin/GuiChatMixin.java
new file mode 100644
index 0000000..be04586
--- /dev/null
+++ b/src/main/java/com/raeids/stratus/mixin/GuiChatMixin.java
@@ -0,0 +1,60 @@
+package com.raeids.stratus.mixin;
+
+import com.raeids.stratus.config.StratusConfig;
+import com.raeids.stratus.hook.ChatHookKt;
+import net.minecraft.client.gui.GuiChat;
+import net.minecraft.client.gui.GuiScreen;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(GuiChat.class)
+public abstract class GuiChatMixin extends GuiScreen {
+
+ @Inject(method = "initGui", at = @At("TAIL"))
+ private void init(CallbackInfo ci) {
+ if (StratusConfig.INSTANCE.getChatSearch()) {
+ ChatHookKt.initGui();
+ }
+ }
+
+ @Inject(method = "drawScreen", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/GuiTextField;drawTextBox()V", shift = At.Shift.AFTER))
+ private void yeah(int mouseX, int mouseY, float partialTicks, CallbackInfo ci) {
+ if (ChatHookKt.getInputField() != null) {
+ ChatHookKt.getInputField().drawTextBox();
+ }
+ }
+
+ @Inject(method = "onGuiClosed", at = @At("TAIL"))
+ private void onGuiClosed(CallbackInfo ci) {
+ ChatHookKt.setInputField(null);
+ ChatHookKt.setPrevText("");
+ }
+
+ @Inject(method = "updateScreen", at = @At("HEAD"))
+ private void updateScreen(CallbackInfo ci) {
+ ChatHookKt.updateScreen();
+ }
+
+ @Inject(method = "keyTyped", at = @At("HEAD"), cancellable = true)
+ private void keyTyped(char typedChar, int keyCode, CallbackInfo ci) {
+ if (ChatHookKt.getInputField() != null) {
+ if (ChatHookKt.getInputField().isFocused()) {
+ ci.cancel();
+ if (keyCode == 1 && ChatHookKt.getInputField().isFocused()) {
+ ChatHookKt.getInputField().setFocused(false);
+ return;
+ }
+ ChatHookKt.getInputField().textboxKeyTyped(typedChar, keyCode);
+ }
+ }
+ }
+
+ @Inject(method = "mouseClicked", at = @At("HEAD"))
+ private void mouseClicked(int mouseX, int mouseY, int mouseButton, CallbackInfo ci) {
+ if (ChatHookKt.getInputField() != null) {
+ ChatHookKt.getInputField().mouseClicked(mouseX, mouseY, mouseButton);
+ }
+ }
+}
diff --git a/src/main/java/com/raeids/stratus/mixin/GuiNewChatAccessor.java b/src/main/java/com/raeids/stratus/mixin/GuiNewChatAccessor.java
new file mode 100644
index 0000000..cc697d0
--- /dev/null
+++ b/src/main/java/com/raeids/stratus/mixin/GuiNewChatAccessor.java
@@ -0,0 +1,15 @@
+package com.raeids.stratus.mixin;
+
+import net.minecraft.client.gui.ChatLine;
+import net.minecraft.client.gui.GuiChat;
+import net.minecraft.client.gui.GuiNewChat;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+import java.util.List;
+
+@Mixin (GuiNewChat.class)
+public interface GuiNewChatAccessor {
+ @Accessor
+ List<ChatLine> getDrawnChatLines();
+}
diff --git a/src/main/java/com/raeids/stratus/mixin/GuiNewChatMixin.java b/src/main/java/com/raeids/stratus/mixin/GuiNewChatMixin.java
new file mode 100644
index 0000000..48e2525
--- /dev/null
+++ b/src/main/java/com/raeids/stratus/mixin/GuiNewChatMixin.java
@@ -0,0 +1,49 @@
+package com.raeids.stratus.mixin;
+
+import com.raeids.stratus.Stratus;
+import com.raeids.stratus.hook.ChatHookKt;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.ChatLine;
+import net.minecraft.client.gui.GuiNewChat;
+import org.spongepowered.asm.lib.Opcodes;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.ModifyVariable;
+import org.spongepowered.asm.mixin.injection.Redirect;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import java.util.List;
+
+@Mixin(GuiNewChat.class)
+public class GuiNewChatMixin {
+ @Shadow @Final private Minecraft mc;
+
+ @Inject(method = "drawChat", at = @At("HEAD"))
+ private void checkScreenshotKeybind(int j2, CallbackInfo ci) {
+ if (Stratus.INSTANCE.getKeybind().isPressed()) {
+ Stratus.INSTANCE.setDoTheThing(true);
+ }
+ }
+
+ @ModifyVariable(method = "drawChat", at = @At("HEAD"), argsOnly = true)
+ private int setUpdateCounterWhjenYes(int updateCounter) {
+ return Stratus.INSTANCE.getDoTheThing() ? 0 : updateCounter;
+ }
+
+ @ModifyVariable(method = "drawChat", at = @At("STORE"), index = 2)
+ private int setChatLimitWhenYes(int linesToDraw) {
+ return Stratus.INSTANCE.getDoTheThing()
+ ? GuiNewChat.calculateChatboxHeight(mc.gameSettings.chatHeightFocused) / 9
+ : linesToDraw;
+ }
+
+ @Shadow @Final private List<ChatLine> drawnChatLines;
+
+ @Redirect(method = "drawChat", at = @At(value = "FIELD", target = "Lnet/minecraft/client/gui/GuiNewChat;drawnChatLines:Ljava/util/List;", opcode = Opcodes.GETFIELD))
+ private List<ChatLine> injected(GuiNewChat instance) {
+ return ChatHookKt.filterMessages(drawnChatLines);
+ }
+}
diff --git a/src/main/kotlin/com/raeids/stratus/Stratus.kt b/src/main/kotlin/com/raeids/stratus/Stratus.kt
new file mode 100644
index 0000000..3c8984b
--- /dev/null
+++ b/src/main/kotlin/com/raeids/stratus/Stratus.kt
@@ -0,0 +1,115 @@
+package com.raeids.stratus
+
+import com.raeids.stratus.command.StratusCommand
+import com.raeids.stratus.config.StratusConfig
+import com.raeids.stratus.mixin.GuiNewChatAccessor
+import com.raeids.stratus.updater.Updater
+import com.raeids.stratus.utils.RenderHelper
+import gg.essential.api.EssentialAPI
+import gg.essential.universal.UDesktop
+import net.minecraft.client.Minecraft
+import net.minecraft.client.gui.FontRenderer
+import net.minecraft.client.gui.GuiChat
+import net.minecraft.client.renderer.GlStateManager
+import net.minecraft.client.settings.KeyBinding
+import net.minecraft.client.shader.Framebuffer
+import net.minecraftforge.common.MinecraftForge.EVENT_BUS
+import net.minecraftforge.fml.client.registry.ClientRegistry
+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 net.minecraftforge.fml.common.gameevent.TickEvent
+import org.lwjgl.input.Keyboard
+import java.io.File
+import java.io.IOException
+import java.text.SimpleDateFormat
+import java.util.*
+
+
+@Mod(
+ modid = Stratus.ID,
+ name = Stratus.NAME,
+ version = Stratus.VER,
+ modLanguageAdapter = "gg.essential.api.utils.KotlinAdapter"
+)
+object Stratus {
+
+ val keybind = KeyBinding("Screenshot Chat", Keyboard.KEY_NONE, "Stratus")
+ const val NAME = "@NAME@"
+ const val VER = "@VER@"
+ const val ID = "@ID@"
+ var doTheThing = false
+ lateinit var jarFile: File
+ private set
+
+ val modDir = File(File(Minecraft.getMinecraft().mcDataDir, "W-OVERFLOW"), NAME)
+
+ @Mod.EventHandler
+ fun onFMLPreInitialization(event: FMLPreInitializationEvent) {
+ if (!modDir.exists()) modDir.mkdirs()
+ jarFile = event.sourceFile
+ }
+
+ private val fileFormatter: SimpleDateFormat = SimpleDateFormat("yyyy-MM-dd_HH.mm.ss'.png'")
+
+ private fun screenshot() {
+ val hud = Minecraft.getMinecraft().ingameGUI
+ val chat = hud.chatGUI
+
+ /* Render chat fully. */
+ var w = chat.chatWidth
+ var h = chat.chatHeight
+ if ((chat as GuiNewChatAccessor).drawnChatLines.size < 20) {
+ h = (chat as GuiNewChatAccessor).drawnChatLines
+ .size * Minecraft.getMinecraft().fontRendererObj.FONT_HEIGHT
+ }
+ if (w <= 0 || h <= 0 || (chat as GuiNewChatAccessor).drawnChatLines.isEmpty()) {
+ EssentialAPI.getNotifications().push("Stratus", "Chat window is empty.")
+ return
+ }
+ val chatLines: MutableList<String> = ArrayList()
+ val fr: FontRenderer = Minecraft.getMinecraft().fontRendererObj
+ for (chatLine in (chat as GuiNewChatAccessor).drawnChatLines) chatLines.add(chatLine.chatComponent.formattedText)
+ if (chatLines.isNotEmpty()) {
+ w = fr.getStringWidth(chatLines.stream().max(Comparator.comparingInt { obj: String -> obj.length }).get())
+ }
+ val fb: Framebuffer = RenderHelper.createBindFramebuffer(w, h)
+ GlStateManager.translate(-2f, (160 - (180 - h)).toFloat(), 0f)
+ chat.drawChat(hud.updateCounter)
+ val file = File(Minecraft.getMinecraft().mcDataDir, "screenshots/chat/" + fileFormatter.format(Date()))
+ RenderHelper.screenshotFramebuffer(fb, file)
+ Minecraft.getMinecraft().entityRenderer.setupOverlayRendering()
+ Minecraft.getMinecraft().framebuffer.bindFramebuffer(true)
+ EssentialAPI.getNotifications()
+ .push("Stratus", "Chat screenshotted successfully.\nClick to open.") {
+ try {
+ UDesktop.browse(file.toURI())
+ } catch (e: IOException) {
+ e.printStackTrace()
+ } catch (e: UnsupportedOperationException) {
+ e.printStackTrace()
+ EssentialAPI.getNotifications().push("Stratus", "Could not browse!")
+ }
+ }
+ }
+
+ @Mod.EventHandler
+ fun onInitialization(event: FMLInitializationEvent) {
+ StratusConfig.preload()
+ StratusCommand.register()
+ ClientRegistry.registerKeyBinding(keybind)
+ EVENT_BUS.register(this)
+ Updater.update()
+ }
+
+ @SubscribeEvent
+ fun onTickEvent(event: TickEvent.ClientTickEvent) {
+ if (event.phase == TickEvent.Phase.START && Minecraft.getMinecraft().theWorld != null && Minecraft.getMinecraft().thePlayer != null && (Minecraft.getMinecraft().currentScreen == null || Minecraft.getMinecraft().currentScreen is GuiChat)) {
+ if (doTheThing) {
+ screenshot()
+ doTheThing = false
+ }
+ }
+ }
+}
diff --git a/src/main/kotlin/com/raeids/stratus/command/StratusCommand.kt b/src/main/kotlin/com/raeids/stratus/command/StratusCommand.kt
new file mode 100644
index 0000000..2954885
--- /dev/null
+++ b/src/main/kotlin/com/raeids/stratus/command/StratusCommand.kt
@@ -0,0 +1,15 @@
+package com.raeids.stratus.command
+
+import com.raeids.stratus.Stratus
+import com.raeids.stratus.config.StratusConfig
+import gg.essential.api.EssentialAPI
+import gg.essential.api.commands.Command
+import gg.essential.api.commands.DefaultHandler
+
+object StratusCommand : Command(Stratus.ID, true) {
+
+ @DefaultHandler
+ fun handle() {
+ EssentialAPI.getGuiUtil().openScreen(StratusConfig.gui())
+ }
+} \ No newline at end of file
diff --git a/src/main/kotlin/com/raeids/stratus/config/StratusConfig.kt b/src/main/kotlin/com/raeids/stratus/config/StratusConfig.kt
new file mode 100644
index 0000000..7a2dc65
--- /dev/null
+++ b/src/main/kotlin/com/raeids/stratus/config/StratusConfig.kt
@@ -0,0 +1,48 @@
+package com.raeids.stratus.config
+
+import com.raeids.stratus.Stratus
+import com.raeids.stratus.updater.DownloadGui
+import com.raeids.stratus.updater.Updater
+import gg.essential.api.EssentialAPI
+import gg.essential.vigilance.Vigilant
+import gg.essential.vigilance.data.Property
+import gg.essential.vigilance.data.PropertyType
+import java.io.File
+
+object StratusConfig : Vigilant(File(Stratus.modDir, "${Stratus.ID}.toml"), Stratus.NAME) {
+
+ @Property(
+ type = PropertyType.SWITCH,
+ name = "Chat Searching",
+ description = "Add a chat search bar.",
+ category = "General"
+ )
+ var chatSearch = true
+
+ @Property(
+ type = PropertyType.SWITCH,
+ name = "Show Update Notification",
+ description = "Show a notification when you start Minecraft informing you of new updates.",
+ category = "Updater"
+ )
+ var showUpdate = true
+
+ @Property(
+ type = PropertyType.BUTTON,
+ name = "Update Now",
+ description = "Update by clicking the button.",
+ category = "Updater"
+ )
+ fun update() {
+ if (Updater.shouldUpdate) EssentialAPI.getGuiUtil()
+ .openScreen(DownloadGui()) else EssentialAPI.getNotifications()
+ .push(
+ Stratus.NAME,
+ "No update had been detected at startup, and thus the update GUI has not been shown."
+ )
+ }
+
+ init {
+ initialize()
+ }
+} \ No newline at end of file
diff --git a/src/main/kotlin/com/raeids/stratus/hook/ChatHook.kt b/src/main/kotlin/com/raeids/stratus/hook/ChatHook.kt
new file mode 100644
index 0000000..c056161
--- /dev/null
+++ b/src/main/kotlin/com/raeids/stratus/hook/ChatHook.kt
@@ -0,0 +1,41 @@
+package com.raeids.stratus.hook
+
+import gg.essential.universal.wrappers.message.UTextComponent
+import net.minecraft.client.Minecraft
+import net.minecraft.client.gui.ChatLine
+import net.minecraft.client.gui.GuiTextField
+import net.minecraft.client.gui.ScaledResolution
+
+var inputField: GuiTextField? = null
+var sr: ScaledResolution? = null
+var prevText = ""
+
+fun initGui() {
+ sr = ScaledResolution(Minecraft.getMinecraft())
+ inputField = GuiTextField(
+ 694209000,
+ Minecraft.getMinecraft().fontRendererObj,
+ sr!!.scaledWidth * 4 / 5 - 1,
+ sr!!.scaledHeight - 13,
+ sr!!.scaledWidth / 5,
+ 12
+ )
+ inputField!!.maxStringLength = 100
+ inputField!!.enableBackgroundDrawing = true
+ inputField!!.isFocused = false
+ inputField!!.text = ""
+ inputField!!.setCanLoseFocus(true)
+ prevText = ""
+}
+
+fun updateScreen() {
+ inputField?.updateCursorCounter()
+}
+
+fun filterMessages(list: List<ChatLine?>?): List<ChatLine?>? {
+ if (inputField == null || list == null || inputField?.text.isNullOrBlank()) return list
+ return list.filter {
+ it != null && UTextComponent.stripFormatting(it.chatComponent.unformattedText).lowercase()
+ .contains(inputField!!.text!!.lowercase())
+ }
+} \ No newline at end of file
diff --git a/src/main/kotlin/com/raeids/stratus/updater/DownloadGui.kt b/src/main/kotlin/com/raeids/stratus/updater/DownloadGui.kt
new file mode 100644
index 0000000..d3fa69e
--- /dev/null
+++ b/src/main/kotlin/com/raeids/stratus/updater/DownloadGui.kt
@@ -0,0 +1,53 @@
+package com.raeids.stratus.updater
+
+import com.raeids.stratus.Stratus
+import gg.essential.api.EssentialAPI
+import gg.essential.api.gui.buildConfirmationModal
+import gg.essential.api.utils.Multithreading
+import gg.essential.elementa.WindowScreen
+import gg.essential.elementa.dsl.childOf
+import java.io.File
+
+class DownloadGui : WindowScreen(true, true, true, -1) {
+ override fun initScreen(width: Int, height: Int) {
+ super.initScreen(width, height)
+ EssentialAPI.getEssentialComponentFactory().buildConfirmationModal {
+ this.text = "Are you sure you want to update?"
+ this.secondaryText =
+ "(This will update from v${Stratus.VER} to ${Updater.latestTag})"
+ this.onConfirm = {
+ restorePreviousScreen()
+ Multithreading.runAsync {
+ if (Updater.download(
+ Updater.updateUrl,
+ File(
+ "mods/${Stratus.NAME}-${
+ Updater.latestTag!!.substringAfter("v")
+ }.jar"
+ )
+ ) && Updater.download(
+ "https://github.com/Wyvest/Deleter/releases/download/v1.2/Deleter-1.2.jar",
+ File(Stratus.modDir.parentFile, "Deleter-1.2.jar")
+ )
+ ) {
+ EssentialAPI.getNotifications()
+ .push(
+ Stratus.NAME,
+ "The ingame updater has successfully installed the newest version."
+ )
+ Updater.addShutdownHook()
+ Updater.shouldUpdate = false
+ } else {
+ EssentialAPI.getNotifications().push(
+ Stratus.NAME,
+ "The ingame updater has NOT installed the newest version as something went wrong."
+ )
+ }
+ }
+ }
+ this.onDeny = {
+ restorePreviousScreen()
+ }
+ } childOf this.window
+ }
+} \ No newline at end of file
diff --git a/src/main/kotlin/com/raeids/stratus/updater/Updater.kt b/src/main/kotlin/com/raeids/stratus/updater/Updater.kt
new file mode 100644
index 0000000..2f48cdb
--- /dev/null
+++ b/src/main/kotlin/com/raeids/stratus/updater/Updater.kt
@@ -0,0 +1,101 @@
+package com.raeids.stratus.updater
+
+import com.raeids.stratus.Stratus
+import com.raeids.stratus.config.StratusConfig
+import gg.essential.api.EssentialAPI
+import gg.essential.api.utils.Multithreading
+import gg.essential.api.utils.WebUtil.downloadToFile
+import gg.essential.api.utils.WebUtil.fetchJSON
+import gg.essential.universal.UDesktop.open
+import net.minecraft.client.Minecraft
+import net.minecraftforge.fml.common.versioning.DefaultArtifactVersion
+import java.io.File
+import java.io.IOException
+
+object Updater {
+ var updateUrl = ""
+ var latestTag: String? = null
+ var shouldUpdate = false
+
+ fun update() {
+ Multithreading.runAsync {
+ try {
+ val latestRelease =
+ fetchJSON("https://api.github.com/repos/W-OVERFLOW/${Stratus.ID}/releases/latest").getObject()
+ latestTag = latestRelease["tag_name"].asString
+ val currentVersion =
+ DefaultArtifactVersion(Stratus.VER.substringBefore("-"))
+ val latestVersion = DefaultArtifactVersion(latestTag!!.substringAfter("v").substringBefore("-"))
+ if (currentVersion >= latestVersion) {
+ return@runAsync
+ }
+ updateUrl =
+ latestRelease["assets"].asJsonArray[0].asJsonObject["browser_download_url"]
+ .asString
+ if (updateUrl.isNotEmpty()) {
+ if (StratusConfig.showUpdate) {
+ EssentialAPI.getNotifications().push(
+ Stratus.NAME,
+ "${Stratus.NAME} has a new update ($latestTag)! Click here to download it automatically!"
+ ) { EssentialAPI.getGuiUtil().openScreen(DownloadGui()) }
+ }
+ shouldUpdate = true
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+ }
+
+ fun download(url: String, file: File): Boolean {
+ var url = url
+ if (file.exists()) return true
+ url = url.replace(" ", "%20")
+ try {
+ downloadToFile(url, file, "${Stratus.NAME}/${Stratus.VER}")
+ } catch (e: Exception) {
+ e.printStackTrace()
+ return false
+ }
+ return file.exists()
+ }
+
+ /**
+ * Adapted from RequisiteLaunchwrapper under LGPLv3
+ * https://github.com/Qalcyo/RequisiteLaunchwrapper/blob/main/LICENSE
+ */
+ fun addShutdownHook() {
+ Runtime.getRuntime().addShutdownHook(Thread {
+ println("Opening Deleter task...")
+ try {
+ val runtime = javaRuntime
+ if (Minecraft.isRunningOnMac) {
+ open(Stratus.jarFile.parentFile)
+ }
+ val file = File(Stratus.modDir.parentFile, "Deleter-1.2.jar")
+ Runtime.getRuntime()
+ .exec("\"" + runtime + "\" -jar \"" + file.absolutePath + "\" \"" + Stratus.jarFile.absolutePath + "\"")
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ Thread.currentThread().interrupt()
+ })
+ }
+
+ /**
+ * Gets the current Java runtime being used.
+ *
+ * @link https://stackoverflow.com/a/47925649
+ */
+ @get:Throws(IOException::class)
+ val javaRuntime: String
+ get() {
+ val os = System.getProperty("os.name")
+ val java = System.getProperty("java.home") + File.separator + "bin" + File.separator +
+ if (os != null && os.lowercase().startsWith("windows")) "java.exe" else "java"
+ if (!File(java).isFile) {
+ throw IOException("Unable to find suitable java runtime at $java")
+ }
+ return java
+ }
+} \ No newline at end of file
diff --git a/src/main/kotlin/com/raeids/stratus/utils/RenderHelper.kt b/src/main/kotlin/com/raeids/stratus/utils/RenderHelper.kt
new file mode 100644
index 0000000..d2aac4d
--- /dev/null
+++ b/src/main/kotlin/com/raeids/stratus/utils/RenderHelper.kt
@@ -0,0 +1,63 @@
+package com.raeids.stratus.utils
+
+import net.minecraft.client.renderer.GlStateManager
+import net.minecraft.client.renderer.texture.TextureUtil
+import net.minecraft.client.shader.Framebuffer
+import org.lwjgl.BufferUtils
+import org.lwjgl.opengl.GL11
+import org.lwjgl.opengl.GL12
+import java.awt.image.BufferedImage
+import java.io.File
+import javax.imageio.ImageIO
+
+
+object RenderHelper {
+ /**
+ * Taken from https://github.com/Moulberry/HyChat
+ */
+ fun screenshotFramebuffer(framebuffer: Framebuffer, file: File) {
+ val w = framebuffer.framebufferWidth
+ val h = framebuffer.framebufferHeight
+ val i = w * h
+ val pixelBuffer = BufferUtils.createIntBuffer(i)
+ val pixelValues = IntArray(i)
+ GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1)
+ GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1)
+ GlStateManager.bindTexture(framebuffer.framebufferTexture)
+ GL11.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, pixelBuffer)
+ pixelBuffer[pixelValues] //Load buffer into array
+ TextureUtil.processPixelValues(pixelValues, w, h) //Flip vertically
+ val bufferedimage = BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB)
+ val j = framebuffer.framebufferTextureHeight - framebuffer.framebufferHeight
+ for (k in j until framebuffer.framebufferTextureHeight) {
+ for (l in 0 until framebuffer.framebufferWidth) {
+ bufferedimage.setRGB(l, k - j, pixelValues[k * framebuffer.framebufferTextureWidth + l])
+ }
+ }
+ try {
+ file.parentFile.mkdirs()
+ ImageIO.write(bufferedimage, "png", file)
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+
+ /**
+ * Taken from https://github.com/Moulberry/HyChat
+ */
+ fun createBindFramebuffer(w: Int, h: Int): Framebuffer {
+ val framebuffer = Framebuffer(w, h, false)
+ framebuffer.framebufferColor[0] = 0x36 / 255f
+ framebuffer.framebufferColor[1] = 0x39 / 255f
+ framebuffer.framebufferColor[2] = 0x3F / 255f
+ framebuffer.framebufferClear()
+ GlStateManager.matrixMode(5889)
+ GlStateManager.loadIdentity()
+ GlStateManager.ortho(0.0, w.toDouble(), h.toDouble(), 0.0, 1000.0, 3000.0)
+ GlStateManager.matrixMode(5888)
+ GlStateManager.loadIdentity()
+ GlStateManager.translate(0.0f, 0.0f, -2000.0f)
+ framebuffer.bindFramebuffer(true)
+ return framebuffer
+ }
+} \ No newline at end of file
diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info
new file mode 100644
index 0000000..b132edf
--- /dev/null
+++ b/src/main/resources/mcmod.info
@@ -0,0 +1,18 @@
+[
+ {
+ "modid": "${id}",
+ "name": "${name}",
+ "description": "A chat mod.",
+ "version": "${version}",
+ "mcversion": "${mcversion}",
+ "url": "",
+ "updateUrl": "",
+ "authorList": [
+ "W-OVERFLOW (raeids)"
+ ],
+ "credits": "",
+ "logoFile": "",
+ "screenshots": [],
+ "dependencies": []
+ }
+]
diff --git a/src/main/resources/mixins.stratus.json b/src/main/resources/mixins.stratus.json
new file mode 100644
index 0000000..d9c91e5
--- /dev/null
+++ b/src/main/resources/mixins.stratus.json
@@ -0,0 +1,12 @@
+{
+ "compatibilityLevel": "JAVA_8",
+ "minVersion": "0.7",
+ "package": "com.raeids.stratus.mixin",
+ "refmap": "mixins.${id}.refmap.json",
+ "mixins": [
+ "GuiChatMixin",
+ "GuiNewChatAccessor",
+ "GuiNewChatMixin"
+ ],
+ "verbose": true
+} \ No newline at end of file