diff options
Diffstat (limited to 'loader/src/main/java')
24 files changed, 916 insertions, 199 deletions
diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/LetsEncrypt.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/LetsEncrypt.java new file mode 100644 index 00000000..7d75d571 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/LetsEncrypt.java @@ -0,0 +1,78 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2023 cyoung06 (syeyoung) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package kr.syeyoung.dungeonsguide.launcher; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManagerFactory; +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Objects; + +// Smh minecraft default launcher letsencrypt +public class LetsEncrypt { + public static SSLSocketFactory LETS_ENCRYPT; + + static { + try { + LETS_ENCRYPT = letsEncryptAddedFactory(); + } catch (KeyStoreException | IOException | CertificateException | NoSuchAlgorithmException | + KeyManagementException e) { + throw new RuntimeException(e); + } + } + + private static SSLSocketFactory letsEncryptAddedFactory() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, KeyManagementException { + String keyStoreLocation = System.getProperty( "javax.net.ssl.trustStore", Paths.get(System.getProperty("java.home"), "lib", "security", "cacerts").toString()); + String keyStorePassword = System.getProperty( "javax.net.ssl.trustStorePassword", "" ); // You might ask, "THE DEFAULT PASSWORD IS changeit". But in fact, just loading keystore does not require a key!! https://stackoverflow.com/a/42363257 + String keyStoreType = System.getProperty("javax.net.ssl.trustStoreType", KeyStore.getDefaultType()); + + char[] charArr = keyStorePassword.isEmpty() ? null : keyStorePassword.toCharArray(); + + KeyStore keyStore = KeyStore.getInstance(keyStoreType); + try (InputStream readStream = Files.newInputStream(Paths.get(keyStoreLocation))) { + keyStore.load(readStream, charArr); + } + + if (keyStore.getCertificate("ISRGRootX1") == null) { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + try (InputStream caInput = LetsEncrypt.class.getResourceAsStream("/isrgrootx1.der")) { + Certificate crt = cf.generateCertificate(caInput); + keyStore.setCertificateEntry("ISRGRootX1", crt); + } + } + + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(keyStore); + + SSLContext context = SSLContext.getInstance( "TLS" ); + context.init( null, tmf.getTrustManagers(), null); + return context.getSocketFactory(); + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/DgAuthUtil.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/DgAuthUtil.java index 2c59dd2c..d1abadca 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/DgAuthUtil.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/auth/DgAuthUtil.java @@ -21,6 +21,7 @@ package kr.syeyoung.dungeonsguide.launcher.auth; import com.mojang.authlib.GameProfile; import com.mojang.authlib.exceptions.AuthenticationException; import com.mojang.authlib.minecraft.MinecraftSessionService; +import kr.syeyoung.dungeonsguide.launcher.LetsEncrypt; import kr.syeyoung.dungeonsguide.launcher.Main; import kr.syeyoung.dungeonsguide.launcher.auth.token.AuthToken; import kr.syeyoung.dungeonsguide.launcher.auth.token.DGAuthToken; @@ -37,6 +38,7 @@ import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; +import javax.net.ssl.HttpsURLConnection; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; @@ -97,7 +99,8 @@ public class DgAuthUtil { public static String requestAuth() throws IOException { GameProfile profile = Minecraft.getMinecraft().getSession().getProfile(); - HttpURLConnection connection = (HttpURLConnection) new URL(Main.DOMAIN + "/auth/v2/requestAuth").openConnection(); + HttpsURLConnection connection = (HttpsURLConnection) new URL(Main.DOMAIN + "/auth/v2/requestAuth").openConnection(); + connection.setSSLSocketFactory(LetsEncrypt.LETS_ENCRYPT); connection.setRequestProperty("User-Agent", "DungeonsGuide/4.0"); connection.setRequestProperty("Content-Type", "application/json"); connection.setConnectTimeout(1000); @@ -147,7 +150,8 @@ public class DgAuthUtil { * @throws AuthServerException when auth server throws error */ public static AuthToken verifyAuth(String tempToken, byte[] encSecret) throws IOException { - HttpURLConnection urlConnection = (HttpURLConnection) new URL(Main.DOMAIN + "/auth/v2/authenticate").openConnection(); + HttpsURLConnection urlConnection = (HttpsURLConnection) new URL(Main.DOMAIN + "/auth/v2/authenticate").openConnection(); + urlConnection.setSSLSocketFactory(LetsEncrypt.LETS_ENCRYPT); urlConnection.setRequestMethod("POST"); urlConnection.setRequestProperty("User-Agent", "DungeonsGuide/4.0"); urlConnection.setRequestProperty("Content-Type", "application/json"); @@ -173,7 +177,8 @@ public class DgAuthUtil { } public static AuthToken acceptNewPrivacyPolicy(String tempToken, long version) throws IOException { - HttpURLConnection urlConnection = (HttpURLConnection) new URL(Main.DOMAIN + "/auth/v2/acceptPrivacyPolicy").openConnection(); + HttpsURLConnection urlConnection = (HttpsURLConnection) new URL(Main.DOMAIN + "/auth/v2/acceptPrivacyPolicy").openConnection(); + urlConnection.setSSLSocketFactory(LetsEncrypt.LETS_ENCRYPT); urlConnection.setRequestMethod("POST"); urlConnection.setRequestProperty("User-Agent", "DungeonsGuide/4.0"); urlConnection.setRequestProperty("Content-Type", "application/json"); diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/branch/UpdateRetrieverUtil.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/branch/UpdateRetrieverUtil.java index 0414129c..ef282f1e 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/branch/UpdateRetrieverUtil.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/branch/UpdateRetrieverUtil.java @@ -18,6 +18,7 @@ package kr.syeyoung.dungeonsguide.launcher.branch; +import kr.syeyoung.dungeonsguide.launcher.LetsEncrypt; import kr.syeyoung.dungeonsguide.launcher.Main; import kr.syeyoung.dungeonsguide.launcher.auth.AuthManager; import kr.syeyoung.dungeonsguide.launcher.exceptions.AssetNotFoundException; @@ -29,6 +30,7 @@ import org.apache.commons.io.IOUtils; import org.json.JSONArray; import org.json.JSONObject; +import javax.net.ssl.HttpsURLConnection; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; @@ -47,7 +49,8 @@ public class UpdateRetrieverUtil { } public static List<UpdateBranch> getUpdateBranches() throws IOException { - HttpURLConnection connection = (HttpURLConnection) new URL(Main.DOMAIN + "/updates/").openConnection(); + HttpsURLConnection connection = (HttpsURLConnection) new URL(Main.DOMAIN + "/updates/").openConnection(); + connection.setSSLSocketFactory(LetsEncrypt.LETS_ENCRYPT); connection.setRequestProperty("User-Agent", "DungeonsGuide/4.0"); connection.setRequestProperty("Authorization", "Bearer "+ AuthManager.getInstance().getWorkingTokenOrThrow()); connection.setRequestMethod("GET"); @@ -78,7 +81,8 @@ public class UpdateRetrieverUtil { } public static List<Update> getLatestUpdates(long branchId, int page) throws IOException { - HttpURLConnection connection = (HttpURLConnection) new URL(Main.DOMAIN + "/updates/"+branchId+"/?page="+page).openConnection(); + HttpsURLConnection connection = (HttpsURLConnection) new URL(Main.DOMAIN + "/updates/"+branchId+"/?page="+page).openConnection(); + connection.setSSLSocketFactory(LetsEncrypt.LETS_ENCRYPT); connection.setRequestProperty("User-Agent", "DungeonsGuide/4.0"); connection.setRequestMethod("GET"); connection.setConnectTimeout(1000); @@ -120,7 +124,8 @@ public class UpdateRetrieverUtil { } public static Update getUpdate(long branchId, long updateId) throws IOException { - HttpURLConnection connection = (HttpURLConnection) new URL(Main.DOMAIN + "/updates/"+branchId+"/"+updateId).openConnection(); + HttpsURLConnection connection = (HttpsURLConnection) new URL(Main.DOMAIN + "/updates/"+branchId+"/"+updateId).openConnection(); + connection.setSSLSocketFactory(LetsEncrypt.LETS_ENCRYPT); connection.setRequestProperty("User-Agent", "DungeonsGuide/4.0"); connection.setRequestProperty("Authorization", "Bearer "+ AuthManager.getInstance().getWorkingTokenOrThrow()); connection.setRequestMethod("GET"); @@ -162,7 +167,8 @@ public class UpdateRetrieverUtil { try { - HttpURLConnection connection = (HttpURLConnection) new URL(Main.DOMAIN + "/updates/" + update.getBranchId() + "/" + update.getId() + "/" + asset.getAssetId()).openConnection(); + HttpsURLConnection connection = (HttpsURLConnection) new URL(Main.DOMAIN + "/updates/" + update.getBranchId() + "/" + update.getId() + "/" + asset.getAssetId()).openConnection(); + connection.setSSLSocketFactory(LetsEncrypt.LETS_ENCRYPT); connection.setRequestProperty("User-Agent", "DungeonsGuide/4.0"); connection.setRequestMethod("GET"); connection.setRequestProperty("Authorization", "Bearer " + AuthManager.getInstance().getWorkingTokenOrThrow()); @@ -181,7 +187,8 @@ public class UpdateRetrieverUtil { throw new ResponseParsingException(payload, e); } try { - connection = (HttpURLConnection) new URL(url).openConnection(); + connection = (HttpsURLConnection) new URL(url).openConnection(); + connection.setSSLSocketFactory(LetsEncrypt.LETS_ENCRYPT); connection.setRequestProperty("User-Agent", "DungeonsGuide/4.0"); connection.setConnectTimeout(1000); connection.setReadTimeout(5000); diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/Context.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/Context.java index b59c2c2b..a5ac459b 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/Context.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/Context.java @@ -21,7 +21,6 @@ package kr.syeyoung.dungeonsguide.launcher.guiv2; import java.util.HashMap; import java.util.Map; -// TODO: get rid of this and change it with tree walking. public class Context { public Map<String, Object> CONTEXT = new HashMap<>(); diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/DomElement.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/DomElement.java index 9487b21c..90f17921 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/DomElement.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/DomElement.java @@ -27,7 +27,7 @@ import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.DrawNothingRenderer; import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; -import kr.syeyoung.dungeonsguide.mod.utils.cursor.EnumCursor; +import kr.syeyoung.dungeonsguide.launcher.util.cursor.EnumCursor; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/GuiScreenAdapter.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/GuiScreenAdapter.java index 03c60397..d4d46834 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/GuiScreenAdapter.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/GuiScreenAdapter.java @@ -22,8 +22,8 @@ import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Rect; import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; -import kr.syeyoung.dungeonsguide.mod.utils.cursor.EnumCursor; -import kr.syeyoung.dungeonsguide.mod.utils.cursor.GLCursors; +import kr.syeyoung.dungeonsguide.launcher.util.cursor.EnumCursor; +import kr.syeyoung.dungeonsguide.launcher.util.cursor.GLCursors; import lombok.Getter; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/RootDom.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/RootDom.java index d7c732f3..a759f982 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/RootDom.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/RootDom.java @@ -20,7 +20,7 @@ package kr.syeyoung.dungeonsguide.launcher.guiv2; import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.SingleChildPassingLayouter; import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.SingleChildRenderer; -import kr.syeyoung.dungeonsguide.mod.utils.cursor.EnumCursor; +import kr.syeyoung.dungeonsguide.launcher.util.cursor.EnumCursor; import lombok.Getter; import lombok.Setter; diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Button.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Button.java index 73b0e21d..77ceb167 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Button.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/Button.java @@ -29,7 +29,7 @@ import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedWidget; import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Bind; import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Passthrough; -import kr.syeyoung.dungeonsguide.mod.utils.cursor.EnumCursor; +import kr.syeyoung.dungeonsguide.launcher.util.cursor.EnumCursor; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.util.ResourceLocation; diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/CircularRect.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/CircularRect.java index 18738d42..24f74963 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/CircularRect.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/CircularRect.java @@ -27,8 +27,8 @@ import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.SingleChildRenderer; import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; -import kr.syeyoung.dungeonsguide.mod.shader.ShaderManager; -import kr.syeyoung.dungeonsguide.mod.shader.ShaderProgram; +import kr.syeyoung.dungeonsguide.launcher.shader.ShaderManager; +import kr.syeyoung.dungeonsguide.launcher.shader.ShaderProgram; import net.minecraft.client.Minecraft; import org.lwjgl.opengl.GL20; diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/CompatLayer.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/CompatLayer.java deleted file mode 100644 index 69ad5652..00000000 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/CompatLayer.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod - * Copyright (C) 2023 cyoung06 (syeyoung) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ - -package kr.syeyoung.dungeonsguide.launcher.guiv2.elements; - -import kr.syeyoung.dungeonsguide.mod.gui.MPanel; -import kr.syeyoung.dungeonsguide.mod.gui.elements.MTooltip; -import kr.syeyoung.dungeonsguide.launcher.guiv2.DomElement; -import kr.syeyoung.dungeonsguide.launcher.guiv2.Widget; -import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.popups.AbsLocationPopup; -import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.popups.PopupMgr; -import kr.syeyoung.dungeonsguide.launcher.guiv2.layouter.Layouter; -import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.ConstraintBox; -import kr.syeyoung.dungeonsguide.launcher.guiv2.primitive.Size; -import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; -import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; -import kr.syeyoung.dungeonsguide.mod.utils.cursor.EnumCursor; -import net.minecraft.client.Minecraft; -import org.lwjgl.opengl.GL11; - -import java.awt.*; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class CompatLayer extends Widget implements Layouter, Renderer { - private MPanel panel; - private Rectangle force; - - public CompatLayer(MPanel panel) { - this(panel, null); - } - - List<AbsLocationPopup> tooltips = new ArrayList<>(); - public CompatLayer(MPanel panel, Rectangle force) { - this.panel = panel; - this.force = force; - panel.setParent(new MPanel() { - @Override - public void setCursor(EnumCursor enumCursor) { - getDomElement().setCursor(enumCursor); - } - - @Override - public int getTooltipsOpen() { - return 0; - } - - @Override - public void openTooltip(MTooltip mPanel) { - Rectangle bounds = mPanel.getBounds(); - AbsLocationPopup absLocationPopup = new AbsLocationPopup(bounds.getX(), bounds.getY(), new CompatLayer(mPanel, bounds), true); - PopupMgr.getPopupMgr(getDomElement()).openPopup(absLocationPopup, (a) -> {tooltips.remove(absLocationPopup);}); - tooltips.add(absLocationPopup); - } - }); - } - - @Override - public void onUnmount() { - super.onUnmount(); - tooltips.forEach(PopupMgr.getPopupMgr(getDomElement())::closePopup); - } - - @Override - public List<Widget> build(DomElement buildContext) { - return Collections.emptyList(); - } - - - - @Override - public boolean mouseClicked(int absMouseX, int absMouseY, double relMouseX, double relMouseY, int mouseButton) { - getDomElement().obtainFocus(); - double scale = getDomElement().getAbsBounds().getWidth() / getDomElement().getSize().getWidth(); - return panel.mouseClicked0( (int)(absMouseX / scale), (int)(absMouseY / scale), (int)relMouseX,(int) relMouseY, mouseButton); - } - - @Override - public void mouseClickMove(int absMouseX, int absMouseY, double relMouseX, double relMouseY, int clickedMouseButton, long timeSinceLastClick) { - double scale = getDomElement().getAbsBounds().getWidth() / getDomElement().getSize().getWidth(); - panel.mouseClickMove0( (int)(absMouseX / scale), (int)(absMouseY / scale), (int)relMouseX, (int)relMouseY, clickedMouseButton, timeSinceLastClick); - } - - @Override - public void mouseReleased(int absMouseX, int absMouseY, double relMouseX, double relMouseY, int state) { - double scale = getDomElement().getAbsBounds().getWidth() / getDomElement().getSize().getWidth(); - panel.mouseReleased0( (int)(absMouseX / scale), (int)(absMouseY / scale), (int)relMouseX,(int) relMouseY, state); - } - - @Override - public boolean mouseMoved(int absMouseX, int absMouseY, double relMouseX0, double relMouseY0) { - double scale = getDomElement().getAbsBounds().getWidth() / getDomElement().getSize().getWidth(); - panel.mouseMoved0( (int)(absMouseX / scale), (int)(absMouseY / scale), (int)relMouseX0, (int)relMouseY0); - return true; - } - - @Override - public boolean mouseScrolled(int absMouseX, int absMouseY, double relMouseX0, double relMouseY0, int scrollAmount) { - double scale = getDomElement().getAbsBounds().getWidth() / getDomElement().getSize().getWidth(); - panel.mouseScrolled0( (int)(absMouseX / scale), (int)(absMouseY / scale), (int)relMouseX0, (int)relMouseY0, scrollAmount); - return true; - } - - @Override - public void keyPressed(char typedChar, int keyCode) { - panel.keyPressed0(typedChar, keyCode); - } - - @Override - public void keyReleased(char typedChar, int keyCode) { - panel.keyReleased0(typedChar, keyCode); - } - - @Override - public void keyHeld(char typedChar, int keyCode) { - panel.keyHeld0(typedChar, keyCode); - } - - @Override - public Size layout(DomElement buildContext, ConstraintBox constraintBox) { - Dimension dimension = force == null ? panel.getPreferredSize() : force.getSize(); - panel.resize((int) constraintBox.getMaxWidth(), (int) constraintBox.getMaxHeight()); - if (panel.getBounds().getWidth() != 0) dimension = panel.getSize(); - - panel.setBounds(new Rectangle(0,0, (int) dimension.getWidth(), (int) dimension.getHeight())); - return new Size(dimension.getWidth(), dimension.getHeight()); - } - - @Override - public void doRender(int absMouseX, int absMouseY, double relMouseX, double relMouseY, float partialTicks, RenderingContext context, DomElement buildContext) { - double scale = getDomElement().getAbsBounds().getWidth() / getDomElement().getSize().getWidth(); - - Rectangle originalRect = context.currentClip(); - Rectangle rectangle = originalRect == null ? null : originalRect.getBounds(); - boolean isNotNull = rectangle != null; - if (rectangle == null) rectangle = new Rectangle(0,0,Minecraft.getMinecraft().displayWidth, Minecraft.getMinecraft().displayHeight); - - rectangle.y = Minecraft.getMinecraft().displayHeight - rectangle.y - rectangle.height; - rectangle.width /= scale; - rectangle.height /= scale; - rectangle.x /= scale; - rectangle.y /= scale; - -// System.out.println(rectangle); - panel.render0(scale, new Point((int) (getDomElement().getAbsBounds().getX() / scale), (int) (getDomElement().getAbsBounds().getY() /scale)), - rectangle, (int)(absMouseX / scale), (int)(absMouseY / scale), (int)relMouseX, (int)relMouseY, partialTicks); - - if (isNotNull) { - GL11.glEnable(GL11.GL_SCISSOR_TEST); - GL11.glScissor(originalRect.x, originalRect.y, originalRect.width, originalRect.height); - } - } -} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/RoundRect.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/RoundRect.java index 217972e5..051bbe4b 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/RoundRect.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/RoundRect.java @@ -26,8 +26,8 @@ import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.SingleChildRenderer; import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; -import kr.syeyoung.dungeonsguide.mod.shader.ShaderManager; -import kr.syeyoung.dungeonsguide.mod.shader.ShaderProgram; +import kr.syeyoung.dungeonsguide.launcher.shader.ShaderManager; +import kr.syeyoung.dungeonsguide.launcher.shader.ShaderProgram; import net.minecraft.client.Minecraft; import org.lwjgl.opengl.GL20; diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/TextField.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/TextField.java index 5cc977c8..dff6da58 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/TextField.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/TextField.java @@ -29,7 +29,7 @@ import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.Renderer; import kr.syeyoung.dungeonsguide.launcher.guiv2.renderer.RenderingContext; import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedExportOnlyWidget; import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; -import kr.syeyoung.dungeonsguide.mod.utils.cursor.EnumCursor; +import kr.syeyoung.dungeonsguide.launcher.util.cursor.EnumCursor; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.Gui; diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/ToggleButton.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/ToggleButton.java index 8b4e7256..78814199 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/ToggleButton.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/ToggleButton.java @@ -29,7 +29,7 @@ import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.AnnotatedWidget; import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Bind; import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Export; import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.annotations.Passthrough; -import kr.syeyoung.dungeonsguide.mod.utils.cursor.EnumCursor; +import kr.syeyoung.dungeonsguide.launcher.util.cursor.EnumCursor; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.util.ResourceLocation; diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/image/ImageTexture.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/image/ImageTexture.java index e5ceec72..79ca20c9 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/image/ImageTexture.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/image/ImageTexture.java @@ -19,7 +19,6 @@ package kr.syeyoung.dungeonsguide.launcher.guiv2.elements.image; -import kr.syeyoung.dungeonsguide.mod.DungeonsGuide; import lombok.Data; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.GlStateManager; @@ -148,9 +147,9 @@ public class ImageTexture { tessellator.draw(); } - public static final ExecutorService executorService = Executors.newFixedThreadPool(3, DungeonsGuide.THREAD_FACTORY); + public static final ExecutorService executorService = Executors.newFixedThreadPool(3); public static final Map<String, ImageTexture> imageMap = new HashMap<>(); - public static final Logger logger = LogManager.getLogger("DG-ImageLoader"); + public static final Logger logger = LogManager.getLogger("DG-Loader-ImageLoader"); public static void loadImage(String url, Consumer<ImageTexture> callback) { if (imageMap.containsKey(url)) { callback.accept(imageMap.get(url)); diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/styles/ITextStyle.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/styles/ITextStyle.java index 48cee96f..4d62522f 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/styles/ITextStyle.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/elements/richtext/styles/ITextStyle.java @@ -36,17 +36,17 @@ public interface ITextStyle { Boolean isShadow(); - kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.shaders.Shader getBackgroundShader(); + kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.shaders.Shader getBackgroundShader(); - kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.shaders.Shader getTextShader(); + kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.shaders.Shader getTextShader(); - kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.shaders.Shader getStrikeThroughShader(); + kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.shaders.Shader getStrikeThroughShader(); - kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.shaders.Shader getUnderlineShader(); + kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.shaders.Shader getUnderlineShader(); - kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.shaders.Shader getOutlineShader(); + kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.shaders.Shader getOutlineShader(); - kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.shaders.Shader getShadowShader(); + kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.shaders.Shader getShadowShader(); - kr.syeyoung.dungeonsguide.mod.guiv2.elements.richtext.fonts.FontRenderer getFontRenderer(); + kr.syeyoung.dungeonsguide.launcher.guiv2.elements.richtext.fonts.FontRenderer getFontRenderer(); } diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/DomElementRegistry.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/DomElementRegistry.java index 8ca03ab5..4a4eff58 100644 --- a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/DomElementRegistry.java +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/guiv2/xml/DomElementRegistry.java @@ -26,6 +26,7 @@ import kr.syeyoung.dungeonsguide.launcher.guiv2.view.TestView; import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.data.Parser; import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.data.ParserException; import kr.syeyoung.dungeonsguide.launcher.guiv2.xml.data.W3CBackedParser; +import kr.syeyoung.dungeonsguide.launcher.guiv2.elements.*; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.IResource; import net.minecraft.util.ResourceLocation; diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/shader/ShaderManager.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/shader/ShaderManager.java new file mode 100644 index 00000000..f0a7a79a --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/shader/ShaderManager.java @@ -0,0 +1,128 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2023 cyoung06 (syeyoung) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package kr.syeyoung.dungeonsguide.launcher.shader; + +import net.minecraft.client.Minecraft; +import net.minecraft.util.ResourceLocation; +import org.apache.commons.io.IOUtils; +import org.lwjgl.opengl.GL20; + +import java.io.InputStream; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class ShaderManager { + private static final Map<String, ShaderProgram> shaders = new HashMap<>(); + + public static void onResourceReload() { + for (ShaderProgram value : shaders.values()) { + value.close(); + } + Set<String> keySet = new HashSet<>(shaders.keySet()); + shaders.clear(); + for (String s : keySet) { + loadShader(s); + } + } + + public static void unload() { + for (ShaderProgram value : shaders.values()) { + value.close(); + } + } + + public static ShaderProgram getShader(String str) { + if (shaders.containsKey(str)) return shaders.get(str); + return loadShader(str); + } + + private static ShaderProgram loadShader(String name) { + int vertex = -1; + String sourceVert = getShaderSource(name+".vert"); + String sourceFrag = getShaderSource(name + ".frag"); + if (sourceVert == null && sourceFrag == null) { + shaders.put(name, null); // shortcut + return null; + } + if (sourceVert != null) { + vertex = GL20.glCreateShader(GL20.GL_VERTEX_SHADER); + System.out.println(sourceVert); + GL20.glShaderSource(vertex, sourceVert); + GL20.glCompileShader(vertex); + + if (GL20.glGetShaderi(vertex, 35713) == 0) { + System.err.println(GL20.glGetShaderInfoLog(vertex, 100)); + GL20.glDeleteShader(vertex); + return null; + } + } + + int fragment = -1; + if (sourceFrag != null) { + fragment = GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER); + System.out.println(sourceFrag); + GL20.glShaderSource(fragment, sourceFrag); + GL20.glCompileShader(fragment); + + if (GL20.glGetShaderi(fragment, 35713) == 0) { + System.err.println(GL20.glGetShaderInfoLog(fragment, 100)); + if (vertex != -1) + GL20.glDeleteShader(vertex); + GL20.glDeleteShader(fragment); + return null; + } + } + + + int program = GL20.glCreateProgram(); + if (vertex != -1) GL20.glAttachShader(program, vertex); + if (fragment != -1) GL20.glAttachShader(program, fragment); + + GL20.glLinkProgram(program); + + if (vertex != -1) GL20.glDeleteShader(vertex); + if (fragment != -1) GL20.glDeleteShader(fragment); + + if (GL20.glGetProgrami(program, 35714) == 0) { + System.err.println(GL20.glGetProgramInfoLog(program, 100)); + return null; + } + GL20.glValidateProgram(program); + if (GL20.glGetProgrami(program, 35715) == 0) { + System.err.println(GL20.glGetProgramInfoLog(program, 100)); + return null; + } + ShaderProgram shaderProgram = new ShaderProgram(name, program); + shaders.put(name, shaderProgram); + return shaderProgram; + } + + private static String getShaderSource(String name) { + ResourceLocation location = new ResourceLocation( + "dungeons_guide_loader:"+name + ); + try (InputStream is = Minecraft.getMinecraft().getResourceManager().getResource(location).getInputStream()) { + return IOUtils.toString(is); + } catch (Exception e) { + } + return null; + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/shader/ShaderProgram.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/shader/ShaderProgram.java new file mode 100644 index 00000000..10b6ade1 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/shader/ShaderProgram.java @@ -0,0 +1,126 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2023 cyoung06 (syeyoung) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package kr.syeyoung.dungeonsguide.launcher.shader; + +import lombok.AllArgsConstructor; +import org.lwjgl.opengl.GL20; +import org.lwjgl.util.vector.Matrix2f; + +import javax.vecmath.Matrix3f; +import javax.vecmath.Matrix4f; +import java.nio.FloatBuffer; + +@AllArgsConstructor +public class ShaderProgram { + private String shaderName; + private int shaderId; + + private void ensureInitialized() { + if (shaderId == -1) throw new IllegalStateException("ShaderProgram not initialized"); + } + + public void useShader() { + ensureInitialized(); + GL20.glUseProgram(shaderId); + } + + public int getUniformLocation(String name) { + ensureInitialized(); + return GL20.glGetUniformLocation(shaderId, name); + } + + public void uploadUniform(String name, int val) { + GL20.glUniform1i(getUniformLocation(name), val); + } + + public void uploadUniform(String name, int v1, int v2) { + GL20.glUniform2i(getUniformLocation(name), v1, v2); + } + + public void uploadUniform(String name, int v1, int v2, int v3) { + GL20.glUniform3i(getUniformLocation(name), v1, v2, v3); + } + + public void uploadUniform(String name, int v1, int v2, int v3, int v4) { + GL20.glUniform4i(getUniformLocation(name), v1, v2, v3, v4); + } + + public void uploadUniform(String name, float val) { + GL20.glUniform1f(getUniformLocation(name), val); + } + + public void uploadUniform(String name, float v1, float v2) { + GL20.glUniform2f(getUniformLocation(name), v1, v2); + } + + public void uploadUniform(String name, float v1, float v2, float v3) { + GL20.glUniform3f(getUniformLocation(name), v1, v2, v3); + } + + public void uploadUniform(String name, float v1, float v2, float v3, float v4) { + GL20.glUniform4f(getUniformLocation(name), v1, v2, v3, v4); + } + + public void uploadUniform(String name, Matrix2f matrix2f) { + FloatBuffer floatBuffer = FloatBuffer.allocate(4); + matrix2f.store(floatBuffer); + GL20.glUniformMatrix2(getUniformLocation(name), false, floatBuffer); + } + + public void uploadUniform(String name, Matrix3f matrix3f) { + FloatBuffer floatBuffer = FloatBuffer.allocate(9); + floatBuffer.put(matrix3f.m00); + floatBuffer.put(matrix3f.m01); + floatBuffer.put(matrix3f.m02); + floatBuffer.put(matrix3f.m10); + floatBuffer.put(matrix3f.m11); + floatBuffer.put(matrix3f.m12); + floatBuffer.put(matrix3f.m20); + floatBuffer.put(matrix3f.m21); + floatBuffer.put(matrix3f.m22); + GL20.glUniformMatrix3(getUniformLocation(name), false, floatBuffer); + } + + public void uploadUniform(String name, Matrix4f matrix4f) { + FloatBuffer floatBuffer = FloatBuffer.allocate(16); + floatBuffer.put(matrix4f.m00); + floatBuffer.put(matrix4f.m01); + floatBuffer.put(matrix4f.m02); + floatBuffer.put(matrix4f.m03); + floatBuffer.put(matrix4f.m10); + floatBuffer.put(matrix4f.m11); + floatBuffer.put(matrix4f.m12); + floatBuffer.put(matrix4f.m13); + floatBuffer.put(matrix4f.m20); + floatBuffer.put(matrix4f.m21); + floatBuffer.put(matrix4f.m22); + floatBuffer.put(matrix4f.m23); + floatBuffer.put(matrix4f.m30); + floatBuffer.put(matrix4f.m31); + floatBuffer.put(matrix4f.m32); + floatBuffer.put(matrix4f.m33); + GL20.glUniformMatrix4(getUniformLocation(name), false, floatBuffer); + } + + public void close() { + if (shaderId == -1) return; + GL20.glDeleteProgram(shaderId); + shaderId = -1; + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/util/cursor/CursorReader.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/util/cursor/CursorReader.java new file mode 100644 index 00000000..34c40964 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/util/cursor/CursorReader.java @@ -0,0 +1,95 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2021 cyoung06 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package kr.syeyoung.dungeonsguide.launcher.util.cursor; + +import com.google.common.io.LittleEndianDataInputStream; +import com.twelvemonkeys.imageio.plugins.bmp.CURImageReader; +import lombok.Data; + +import javax.imageio.ImageIO; +import javax.imageio.stream.ImageInputStream; +import java.awt.image.BufferedImage; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +public class CursorReader { + public static List<CursorData> readFromInputStream(InputStream inputStream) throws IOException { + LittleEndianDataInputStream dataInputStream = new LittleEndianDataInputStream(new BufferedInputStream(inputStream)); + dataInputStream.mark(Integer.MAX_VALUE); + + + int magicValue = dataInputStream.readUnsignedShort(); + if (magicValue != 0) throw new RuntimeException("Invalid Cursor file"); + int type = dataInputStream.readUnsignedShort(); + if (type != 2) throw new RuntimeException("not cursor"); + int size = dataInputStream.readShort(); + + List<CursorData> directoryList = new ArrayList<>(); + for (int i = 0; i < size; i++) { + + CursorData directory = new CursorData(); + directory.setWidth((short) dataInputStream.readUnsignedByte()); + directory.setHeight((short) dataInputStream.readUnsignedByte()); + directory.setColorCnt((short) dataInputStream.readUnsignedByte()); + directory.setMagicValue(dataInputStream.readByte()); + directory.setXHotSpot(dataInputStream.readShort()); + directory.setYHotSpot(dataInputStream.readShort()); + directory.setSizeBitmap(dataInputStream.readInt() & 0x00000000ffffffffL); + directory.setOffset(dataInputStream.readInt() & 0x00000000ffffffffL); + + directoryList.add(directory); + } + dataInputStream.reset(); + + try (ImageInputStream imageInputStream = ImageIO.createImageInputStream(dataInputStream)) { + CURImageReader imageReader = new CURImageReader(); + imageReader.setInput(imageInputStream); + + for (int i = 0; i < directoryList.size(); i++) { + directoryList.get(i).setBufferedImage(imageReader.read(i)); + } + } + inputStream.close(); + + return directoryList; + } + + private static void setIntLittleEndian(byte[] bytes, int index, int value) { + byte[] ins = new byte[] { + (byte) ((value >> 24)& 0xFF), (byte) ((value >> 16)& 0xFF), (byte) ((value >> 8)& 0xFF), (byte) (value & 0xFF) + }; + bytes[index+3] = ins[0]; + bytes[index+2] = ins[1]; + bytes[index+1] = ins[2]; + bytes[index] = ins[3]; + } + + + @Data + public static class CursorData { + private short width, height, colorCnt, magicValue; + private int xHotSpot, yHotSpot; + private long sizeBitmap; + private long offset; + private BufferedImage bufferedImage; + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/util/cursor/EnumCursor.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/util/cursor/EnumCursor.java new file mode 100644 index 00000000..8bea67b9 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/util/cursor/EnumCursor.java @@ -0,0 +1,79 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2021 cyoung06 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package kr.syeyoung.dungeonsguide.launcher.util.cursor; + +import lombok.AllArgsConstructor; +import lombok.Getter; + + +@AllArgsConstructor +@Getter +public enum EnumCursor { + /** there are these hidden cursors for macos + * [NSCursor _windowResizeEastCursor] + * [NSCursor _windowResizeWestCursor] + * [NSCursor _windowResizeEastWestCursor] + * [NSCursor _windowResizeNorthCursor] + * [NSCursor _windowResizeSouthCursor] + * [NSCursor _windowResizeNorthSouthCursor] + * [NSCursor _windowResizeNorthEastCursor] + * [NSCursor _windowResizeNorthWestCursor] + * [NSCursor _windowResizeSouthEastCursor] + * [NSCursor _windowResizeSouthWestCursor] + * [NSCursor _windowResizeNorthEastSouthWestCursor] + * [NSCursor _windowResizeNorthWestSouthEastCursor] + * https://stackoverflow.com/questions/10733228/native-osx-lion-resize-cursor-for-custom-nswindow-or-nsview + */ + DEFAULT(32512,68,"arrowCursor","arrowCursor.cur"), + POINTING_HAND(32649,60,"pointingHandCursor", "pointingHandCursor.cur"), + OPEN_HAND(32646,58,"openHandCursor","openHandCursor.cur"), + CLOSED_HAND(32646,52,"closedHandCursor","closedHandCursor.cur"), + BEAM_CURSOR(32513, 152, "IBeamCursor", "IBeamCursor.cur"), + + MOVE_BAR_LEFT(32644, 70,"resizeLeftCursor", "resizeLeftCursor.cur"), + MOVE_BAR_RIGHT(32644, 96,"resizeRightCursor", "resizeRightCursor.cur"), + MOVE_BAR_LEFT_RIGHT(32644, 108, "resizeLeftRightCursor", "resizeLeftRightCursor.cur"), + MOVE_BAR_UP(32645,138,"resizeUpCursor", "resizeUpCursor.cur"), + MOVE_BAR_DOWN(32645,16,"resizeDownCursor", "resizeDownCursor.cur"), + MOVE_BAR_UP_DOWN(32645,116,"resizeUpDownCursor", "resizeUpDownCursor.cur"), + + RESIZE_LEFT(32644, 70,"_windowResizeWestCursor", "resizeLeftCursor.cur"), + RESIZE_RIGHT(32644, 96,"_windowResizeEastCursor", "resizeRightCursor.cur"), + RESIZE_LEFT_RIGHT(32644, 108, "_windowResizeEastWestCursor", "resizeLeftRightCursor.cur"), + RESIZE_UP(32645,138,"_windowResizeNorthCursor", "resizeUpCursor.cur"), + RESIZE_DOWN(32645,16,"_windowResizeSouthCursor", "resizeDownCursor.cur"), + RESIZE_UP_DOWN(32645,116,"_windowResizeNorthSouthCursor", "resizeUpDownCursor.cur"), + + + RESIZE_TL(32642, 134, "_windowResizeNorthWestCursor", "resizeNW.cur"), + RESIZE_DR(32642, 14, "_windowResizeSouthEastCursor", "resizeSE.cur"), + RESIZE_TLDR(32642, 14, "_windowResizeNorthWestSouthEastCursor", "resizeNWSE.cur"), + RESIZE_TR(32643, 136, "_windowResizeNorthEastCursor", "resizeNE.cur"), + RESIZE_DL(32643, 12, "_windowResizeSouthWestCursor", "resizeSW.cur"), + RESIZE_TRDL(32643, 12, "_windowResizeNorthEastSouthWestCursor", "resizeNESW.cur"), + CROSS(32515, 34,"crosshairCursor", "crosshairCursor.cur"), + NOT_ALLOWED(32648, -1,"operationNotAllowedCursor", "operationNotAllowedCursor.cur"), + TEST(-1, -1, null, "testnonexistant.cur"); + + + private int windows; + private int linux; + private String macos; + private String altFileName; +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/util/cursor/Foundation.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/util/cursor/Foundation.java new file mode 100644 index 00000000..2a44e4ad --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/util/cursor/Foundation.java @@ -0,0 +1,33 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2021 cyoung06 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package kr.syeyoung.dungeonsguide.launcher.util.cursor; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.Pointer; + + +public interface Foundation extends Library { + Foundation INSTANCE = (Foundation) Native.loadLibrary("Foundation", + Foundation.class); + + Pointer objc_getClass(String className); + Pointer sel_registerName(String selectorName); + Pointer objc_msgSend(Pointer receiver, Pointer selector, Object... args); +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/util/cursor/GLCursors.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/util/cursor/GLCursors.java new file mode 100644 index 00000000..b0e10351 --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/util/cursor/GLCursors.java @@ -0,0 +1,232 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2021 cyoung06 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package kr.syeyoung.dungeonsguide.launcher.util.cursor; + + +import com.google.common.base.Throwables; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import net.minecraft.client.Minecraft; +import net.minecraft.util.MathHelper; +import net.minecraft.util.ResourceLocation; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.lwjgl.BufferUtils; +import org.lwjgl.LWJGLException; +import org.lwjgl.LWJGLUtil; +import org.lwjgl.input.Cursor; +import sun.misc.Unsafe; + +import java.awt.image.BufferedImage; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + + +@SuppressWarnings("unsafe") +public class GLCursors { + + static Logger logger = LogManager.getLogger("DG-GlCursors"); + + @SuppressWarnings("unsafe") + static boolean verbose = false; + + private static Unsafe unsafe; + private static Class cursorElement; + private static Constructor constructor; + private static Field cursorField; + + private static Map<EnumCursor, Cursor> enumCursorCursorMap = new HashMap<>(); + + + static { + try { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + unsafe = (Unsafe) f.get(null); + cursorElement = Class.forName("org.lwjgl.input.Cursor$CursorElement"); + constructor = cursorElement.getDeclaredConstructor(Object.class, long.class, long.class); + constructor.setAccessible(true); + cursorField = Cursor.class.getDeclaredField("cursors"); + cursorField.setAccessible(true); + } catch (NoSuchFieldException | IllegalAccessException | ClassNotFoundException | NoSuchMethodException e) { + e.printStackTrace(); + } + } + + public static void setupCursors() { + if (enumCursorCursorMap.size() != 0) return; + int platform = LWJGLUtil.getPlatform(); + for (EnumCursor value : EnumCursor.values()) { + Cursor c = null; + try { + switch(platform) { + case LWJGLUtil.PLATFORM_WINDOWS: + if (value.getWindows() != -1) + c = createCursorWindows(value.getWindows()); + break; + case LWJGLUtil.PLATFORM_LINUX: + if (value.getLinux() != -1) + c = createCursorLinux(value.getLinux()); + break; + case LWJGLUtil.PLATFORM_MACOSX: + if (value.getMacos() != null) + c = createCursorMac(value.getMacos()); + break; + } + } catch (Throwable e) { + if(verbose) logger.error("Error occurred while loading cursor: {}", value); + e.printStackTrace(); + } + try { + if (c == null) { + int hotspotX = 0, hotspotY = 0; + BufferedImage bufferedImage = null; + int minC = Cursor.getMinCursorSize(), maxC = Cursor.getMaxCursorSize(); + try { + ResourceLocation cursorInfo = new ResourceLocation("dungeons_guide_loader:cursors/"+value.getAltFileName()); + List<CursorReader.CursorData> cursorDataList = CursorReader.readFromInputStream(Minecraft.getMinecraft().getResourceManager().getResource(cursorInfo).getInputStream()); + List<CursorReader.CursorData> cursorDataList2 = cursorDataList.stream() + .filter(cdata -> cdata.getBufferedImage() != null) + .filter(cdata -> minC <= cdata.getHeight() && cdata.getHeight() <= maxC && minC <= cdata.getWidth() && cdata.getWidth() <= maxC) + .sorted(Comparator.comparingInt(CursorReader.CursorData::getWidth)).collect(Collectors.toList()); + + CursorReader.CursorData cursorData = + cursorDataList2.size() == 0 ? cursorDataList.get(0) : cursorDataList2.get(0); + if(verbose) logger.info(cursorData); + bufferedImage = cursorData.getBufferedImage(); + hotspotX = cursorData.getXHotSpot(); + hotspotY = cursorData.getYHotSpot(); + } catch (Throwable t) { + if(verbose) logger.error("loading cursor failed with message, {}", String.valueOf(Throwables.getRootCause(t))); + } + + + int width = bufferedImage == null ? 16 : bufferedImage.getWidth(); + int height = bufferedImage == null ? 16 : bufferedImage.getHeight(); + int effWidth = MathHelper.clamp_int(width, Cursor.getMinCursorSize(), Cursor.getMaxCursorSize()); + int effHeight = MathHelper.clamp_int(height, Cursor.getMinCursorSize(), Cursor.getMaxCursorSize()); + int length = effHeight * effWidth; + IntBuffer intBuffer = BufferUtils.createIntBuffer(length); + for (int i = 0; i < length; i++) { + int x = i % effWidth; + int y = i / effWidth; + if (bufferedImage == null) { + intBuffer.put(0xFFFFFFFF); + } else if (x >= width || y >= height) { + intBuffer.put(0); + } else { + intBuffer.put(bufferedImage.getRGB(x, height - y - 1)); + } + } + intBuffer.flip(); + c = new Cursor(effWidth, effHeight, hotspotX, height - hotspotY - 1,1,intBuffer, null); + } + } catch (Throwable e) { + if(verbose) logger.error("Error occurred while loading cursor from resource: "+value); + e.printStackTrace(); + } + if (c != null) { + try { + Object arr = cursorField.get(c); + Object cursor = Array.get(arr, 0); + for (Field declaredField : cursor.getClass().getDeclaredFields()) { + declaredField.setAccessible(true); + Object obj = declaredField.get(cursor); + if(verbose) logger.info(declaredField.getName()+": "+obj+" - "+(obj instanceof ByteBuffer)); + if (obj instanceof ByteBuffer) { + ByteBuffer b = (ByteBuffer) declaredField.get(cursor); + StringBuilder sb = new StringBuilder("Contents: "); + for (int i = 0; i < b.limit(); i++) { + sb.append(Integer.toHexString(b.get(i) & 0xFF)).append(" "); + } + if(verbose) logger.info(sb.toString()); + } + } + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + + enumCursorCursorMap.put(value, c); + } + } + } + + public static Cursor getCursor(EnumCursor enumCursor) { + return enumCursorCursorMap.get(enumCursor); + } + + private static Cursor createCursorWindows(int cursor) throws LWJGLException, InstantiationException, InvocationTargetException, IllegalAccessException { + User32 user32 = User32.INSTANCE; + Pointer hIcon = user32 + .LoadCursorW(Pointer.NULL, cursor); + long ptrVal = Pointer.nativeValue(hIcon); + ByteBuffer handle = BufferUtils.createByteBuffer(Native.POINTER_SIZE); // Why does it have to be direct? well it crashes without it. + if (handle.order() == ByteOrder.LITTLE_ENDIAN) { + for (int i = 0; i < Native.POINTER_SIZE; i++) { + byte value = (byte) ((ptrVal >> i * 8) & 0xFF); + handle.put(value); + } + } else { + for (int i = Native.POINTER_SIZE; i >= 0; i++) { + byte value = (byte) ((ptrVal >> i * 8) & 0xFF); + handle.put(value); + } + } + handle.position(0); + return createCursor(handle); + } + private static Cursor createCursorLinux(int cursor) throws LWJGLException, InstantiationException, InvocationTargetException, IllegalAccessException { + X11.Display display = X11.INSTANCE.XOpenDisplay(null); + Pointer fontCursor = X11.INSTANCE.XCreateFontCursor(display, cursor); + long iconPtr = Pointer.nativeValue(fontCursor); + + return createCursor(iconPtr); + } + private static Cursor createCursorMac(String cursor) throws LWJGLException, InstantiationException, InvocationTargetException, IllegalAccessException { + // trust me, it's horrible. + Foundation foundation = Foundation.INSTANCE; + Pointer nsCursor = foundation.objc_getClass("NSCursor"); + Pointer selector = foundation.sel_registerName(cursor); + Pointer thePointer = foundation.objc_msgSend(nsCursor, selector); + long iconPtr = Pointer.nativeValue(thePointer); + + return createCursor(iconPtr); + } + + + private static Cursor createCursor(Object handle) throws IllegalAccessException, InvocationTargetException, InstantiationException { + // Yes. I had no way. + Cursor ADANGEROUSOBJECT = (Cursor) unsafe.allocateInstance(Cursor.class); + Object cursorElement = constructor.newInstance(handle, 0, LWJGLUtil.getPlatform() == LWJGLUtil.PLATFORM_LINUX ? -1 : System.currentTimeMillis()); + Object array = Array.newInstance(GLCursors.cursorElement, 1); + Array.set(array, 0, cursorElement); + cursorField.set(ADANGEROUSOBJECT, array); + return ADANGEROUSOBJECT; + } +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/util/cursor/User32.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/util/cursor/User32.java new file mode 100644 index 00000000..4dffc35f --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/util/cursor/User32.java @@ -0,0 +1,67 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2021 cyoung06 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package kr.syeyoung.dungeonsguide.launcher.util.cursor; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.Pointer; + +public interface User32 extends Library { + + public static User32 INSTANCE = (User32) Native + .loadLibrary("User32", User32.class); + + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_ARROW = 32512; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_IBEAM = 32513; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_WAIT = 32514; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_CROSS = 32515; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_UPARROW = 32516; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_SIZENWSE = 32642; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_SIZENESW = 32643; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_SIZEWE = 32644; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_SIZENS = 32645; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_SIZEALL = 32646; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_NO = 32648; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_HAND = 32649; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_APPSTARTING = 32650; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_HELP = 32651; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_ICON = 32641; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_SIZE = 32640; + + /** http://msdn.microsoft.com/en-us/library/ms648391(VS.85).aspx */ + public Pointer LoadCursorW(Pointer hInstance, + int lpCursorName); + +} diff --git a/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/util/cursor/X11.java b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/util/cursor/X11.java new file mode 100644 index 00000000..a9a96fef --- /dev/null +++ b/loader/src/main/java/kr/syeyoung/dungeonsguide/launcher/util/cursor/X11.java @@ -0,0 +1,37 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2021 cyoung06 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package kr.syeyoung.dungeonsguide.launcher.util.cursor; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.PointerType; + +public interface X11 extends Library { + X11 INSTANCE = (X11) Native.loadLibrary("X11", X11.class); + public Pointer XCreateFontCursor(Display display, + int shape); + public Display XOpenDisplay(String var1); + + public static class Display extends PointerType { + public Display() { + } + } + +} |