diff --git a/src/Client/ConfigWindow.java b/src/Client/ConfigWindow.java index 67ad86af..83b0818f 100644 --- a/src/Client/ConfigWindow.java +++ b/src/Client/ConfigWindow.java @@ -18,12 +18,6 @@ */ package Client; -import Client.KeybindSet.KeyModifier; -import Game.Camera; -import Game.Client; -import Game.Game; -import Game.KeyboardHandler; -import Game.Replay; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; @@ -82,6 +76,12 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.nimbus.NimbusLookAndFeel; +import Client.KeybindSet.KeyModifier; +import Game.Camera; +import Game.Client; +import Game.Game; +import Game.KeyboardHandler; +import Game.Replay; /** * GUI designed for the RSCPlus client that manages configuration options and keybind values from @@ -138,6 +138,7 @@ public class ConfigWindow { private JSpinner generalPanelClientSizeXSpinner; private JSpinner generalPanelClientSizeYSpinner; private JCheckBox generalPanelCheckUpdates; + private JCheckBox generalPanelAccountSecurityCheckbox; private JCheckBox generalPanelWelcomeEnabled; // private JCheckBox generalPanelChatHistoryCheckbox; private JCheckBox generalPanelCombatXPMenuCheckbox; @@ -226,7 +227,6 @@ public class ConfigWindow { private JCheckBox streamingPanelIPAtLoginCheckbox; private JCheckBox streamingPanelSaveLoginCheckbox; private JCheckBox streamingPanelStartLoginCheckbox; - private JCheckBox streamingPanelAccountSecurityCheckbox; private JCheckBox streamingPanelSpeedrunnerCheckbox; // private JTextField streamingPanelSpeedrunnerUsernameTextField; @@ -557,6 +557,12 @@ public void actionPerformed(ActionEvent e) { addCheckbox("Check for rscplus updates from GitHub at launch", generalPanel); generalPanelCheckUpdates.setToolTipText( "When enabled, rscplus will check for client updates before launching the game and install them when prompted"); + + generalPanelAccountSecurityCheckbox = + addCheckbox( + "Show Account Creation and Security Settings (Requires restart for Account Creation and Recovery)", generalPanel); + generalPanelAccountSecurityCheckbox.setToolTipText( + "Makes old RSC account creation, password recovery and in-game security settings"); generalPanelWelcomeEnabled = addCheckbox("Remind you how to open the Settings every time you log in", generalPanel); @@ -1304,12 +1310,6 @@ public void stateChanged(ChangeEvent e) { streamingPanelStartLoginCheckbox.setToolTipText( "Starts the game at the login screen and return to it on logout"); - streamingPanelAccountSecurityCheckbox = - addCheckbox( - "Show Account Creation and Security Settings (Requires restart)", streamingPanel); - streamingPanelAccountSecurityCheckbox.setToolTipText( - "Makes old RSC account creation, password recovery and in-game security settings"); - JLabel spacerLabel = new JLabel("

 

"); streamingPanel.add(spacerLabel); @@ -2182,6 +2182,8 @@ public void synchronizeGuiValues() { generalPanelClientSizeYSpinner.setValue( Settings.CUSTOM_CLIENT_SIZE_Y.get(Settings.currentProfile)); generalPanelCheckUpdates.setSelected(Settings.CHECK_UPDATES.get(Settings.currentProfile)); + generalPanelAccountSecurityCheckbox.setSelected( + Settings.SHOW_ACCOUNT_SECURITY_SETTINGS.get(Settings.currentProfile)); generalPanelWelcomeEnabled.setSelected( Settings.REMIND_HOW_TO_OPEN_SETTINGS.get(Settings.currentProfile)); // generalPanelChatHistoryCheckbox.setSelected(Settings.LOAD_CHAT_HISTORY.get(Settings.currentProfile)); // TODO: Implement this feature @@ -2358,8 +2360,6 @@ public void synchronizeGuiValues() { Settings.SAVE_LOGININFO.get(Settings.currentProfile)); streamingPanelStartLoginCheckbox.setSelected( Settings.START_LOGINSCREEN.get(Settings.currentProfile)); - streamingPanelAccountSecurityCheckbox.setSelected( - Settings.SHOW_ACCOUNT_SECURITY_SETTINGS.get(Settings.currentProfile)); streamingPanelSpeedrunnerCheckbox.setSelected( Settings.SPEEDRUNNER_MODE_ACTIVE.get(Settings.currentProfile)); // streamingPanelSpeedrunnerUsernameTextField.setText(Settings.SPEEDRUNNER_USERNAME.get(Settings.currentProfile)); @@ -2409,6 +2409,8 @@ public void saveSettings() { Settings.currentProfile, ((SpinnerNumberModel) (generalPanelClientSizeYSpinner.getModel())).getNumber().intValue()); Settings.CHECK_UPDATES.put(Settings.currentProfile, generalPanelCheckUpdates.isSelected()); + Settings.SHOW_ACCOUNT_SECURITY_SETTINGS.put( + Settings.currentProfile, generalPanelAccountSecurityCheckbox.isSelected()); Settings.REMIND_HOW_TO_OPEN_SETTINGS.put( Settings.currentProfile, generalPanelWelcomeEnabled.isSelected()); // Settings.LOAD_CHAT_HISTORY.put(Settings.currentProfile, @@ -2568,8 +2570,6 @@ public void saveSettings() { Settings.currentProfile, streamingPanelSaveLoginCheckbox.isSelected()); Settings.START_LOGINSCREEN.put( Settings.currentProfile, streamingPanelStartLoginCheckbox.isSelected()); - Settings.SHOW_ACCOUNT_SECURITY_SETTINGS.put( - Settings.currentProfile, streamingPanelAccountSecurityCheckbox.isSelected()); Settings.SPEEDRUNNER_MODE_ACTIVE.put( Settings.currentProfile, streamingPanelSpeedrunnerCheckbox.isSelected()); // Settings.SPEEDRUNNER_USERNAME.put( diff --git a/src/Client/JClassPatcher.java b/src/Client/JClassPatcher.java index 1e0a4438..95ea2e2b 100644 --- a/src/Client/JClassPatcher.java +++ b/src/Client/JClassPatcher.java @@ -18,7 +18,6 @@ */ package Client; -import Client.Settings.Dir; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; @@ -34,6 +33,7 @@ import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldInsnNode; import org.objectweb.asm.tree.FieldNode; +import org.objectweb.asm.tree.FrameNode; import org.objectweb.asm.tree.IincInsnNode; import org.objectweb.asm.tree.InsnNode; import org.objectweb.asm.tree.IntInsnNode; @@ -47,6 +47,7 @@ import org.objectweb.asm.util.Printer; import org.objectweb.asm.util.Textifier; import org.objectweb.asm.util.TraceMethodVisitor; +import Client.Settings.Dir; /** Singleton class which hooks variables and patches classes. */ public class JClassPatcher { @@ -162,7 +163,7 @@ private void patchGeneric(ClassNode node) { hookClassVariable( methodNode, "e", - "Ob", + "x", "Ljava/lang/String;", "Game/Client", "pm_enteredText", @@ -172,7 +173,7 @@ private void patchGeneric(ClassNode node) { hookClassVariable( methodNode, "client", - "Ob", + "x", "Ljava/lang/String;", "Game/Client", "pm_enteredText", @@ -182,7 +183,7 @@ private void patchGeneric(ClassNode node) { hookClassVariable( methodNode, "e", - "x", + "Ob", "Ljava/lang/String;", "Game/Client", "pm_text", @@ -192,13 +193,54 @@ private void patchGeneric(ClassNode node) { hookClassVariable( methodNode, "client", - "x", + "Ob", "Ljava/lang/String;", "Game/Client", "pm_text", "Ljava/lang/String;", true, true); + + hookClassVariable( + methodNode, + "e", + "e", + "Ljava/lang/String;", + "Game/Client", + "modal_enteredText", + "Ljava/lang/String;", + true, + true); + hookClassVariable( + methodNode, + "client", + "e", + "Ljava/lang/String;", + "Game/Client", + "modal_enteredText", + "Ljava/lang/String;", + true, + true); + hookClassVariable( + methodNode, + "e", + "Cb", + "Ljava/lang/String;", + "Game/Client", + "modal_text", + "Ljava/lang/String;", + true, + true); + hookClassVariable( + methodNode, + "client", + "Cb", + "Ljava/lang/String;", + "Game/Client", + "modal_text", + "Ljava/lang/String;", + true, + true); hookClassVariable( methodNode, @@ -210,6 +252,17 @@ private void patchGeneric(ClassNode node) { "Ljava/lang/Object;", true, false); + + hookClassVariable( + methodNode, + "client", + "Xb", + "Ljava/awt/Graphics;", + "Game/Renderer", + "graphicsInstance", + "Ljava/awt/Graphics;", + true, + false); hookClassVariable(methodNode, "ba", "u", "I", "Game/Renderer", "width", "I", false, true); hookClassVariable(methodNode, "ba", "k", "I", "Game/Renderer", "height", "I", false, true); @@ -281,6 +334,8 @@ private void patchGeneric(ClassNode node) { methodNode, "client", "Wd", "I", "Game/Renderer", "width", "I", false, true); hookClassVariable( methodNode, "client", "Oi", "I", "Game/Renderer", "height_client", "I", false, true); + hookClassVariable( + methodNode, "client", "tg", "I", "Game/Renderer", "sprite_media", "I", true, false); hookClassVariable(methodNode, "e", "m", "I", "Game/Renderer", "width", "I", false, true); hookClassVariable(methodNode, "e", "a", "I", "Game/Renderer", "height", "I", false, true); @@ -743,6 +798,15 @@ private void patchGeneric(ClassNode node) { hookClassVariable( methodNode, "client", "Zb", "I", "Game/Client", "login_delay", "I", true, false); + + hookClassVariable( + methodNode, "client", "Kg", "Z", "Game/Client", "showAppearanceChange", "Z", true, false); + + hookClassVariable( + methodNode, "client", "Qi", "I", "Game/Client", "controlLoginTop", "I", true, false); + + hookClassVariable( + methodNode, "client", "td", "I", "Game/Client", "controlLoginBottom", "I", true, false); } } @@ -1025,6 +1089,26 @@ private void patchClient(ClassNode node) { break; } } + + insnNodeList = methodNode.instructions.iterator(); + while (insnNodeList.hasNext()) { + AbstractInsnNode insnNode = insnNodeList.next(); + if (insnNode.getOpcode() == Opcodes.INVOKESPECIAL + && ((MethodInsnNode) insnNode).name.equals("t") + && ((MethodInsnNode) insnNode).desc.equals("(I)V")) { + //draw any extra panels when starting game + VarInsnNode call = (VarInsnNode) insnNode.getNext(); + methodNode.instructions.insertBefore( + call, + new MethodInsnNode( + Opcodes.INVOKESTATIC, + "Game/Client", + "initCreateExtraPanelsHook", + "()V", + false)); + break; + } + } } // handlePacket if (methodNode.name.equals("a") && methodNode.desc.equals("(III)V")) { @@ -1241,6 +1325,44 @@ private void patchClient(ClassNode node) { } } } + + insnNodeList = methodNode.instructions.iterator(); + + while (insnNodeList.hasNext()) { + AbstractInsnNode insnNode = insnNodeList.next(); + AbstractInsnNode nextNode = insnNode.getNext(); + AbstractInsnNode twoNextNode = nextNode.getNext(); + + if (nextNode == null || twoNextNode == null) break; + + if (insnNode.getOpcode() == Opcodes.ICONST_0 + && nextNode.getOpcode() == Opcodes.ISTORE + && ((VarInsnNode) nextNode).var == 2) { + LabelNode label = new LabelNode(); + InsnNode call = (InsnNode) insnNode; + + methodNode.instructions.insertBefore(call, new VarInsnNode(Opcodes.ALOAD, 0)); + methodNode.instructions.insertBefore( + call, new FieldInsnNode(Opcodes.GETFIELD, "client", "Bb", "I")); + methodNode.instructions.insertBefore(call, new VarInsnNode(Opcodes.ALOAD, 0)); + methodNode.instructions.insertBefore( + call, new FieldInsnNode(Opcodes.GETFIELD, "client", "xb", "I")); + methodNode.instructions.insertBefore(call, new VarInsnNode(Opcodes.ALOAD, 0)); + methodNode.instructions.insertBefore( + call, new FieldInsnNode(Opcodes.GETFIELD, "client", "Qb", "I")); + methodNode.instructions.insertBefore(call, new VarInsnNode(Opcodes.ALOAD, 0)); + methodNode.instructions.insertBefore( + call, new FieldInsnNode(Opcodes.GETFIELD, "client", "I", "I")); + methodNode.instructions.insertBefore( + call, + new MethodInsnNode( + Opcodes.INVOKESTATIC, "Game/Client", "gameInputHook", "(IIII)Z", false)); + methodNode.instructions.insertBefore(call, new JumpInsnNode(Opcodes.IFGT, label)); + methodNode.instructions.insertBefore(call, new InsnNode(Opcodes.RETURN)); + methodNode.instructions.insertBefore(call, label); + break; + } + } insnNodeList = methodNode.instructions.iterator(); @@ -1771,6 +1893,30 @@ private void patchClient(ClassNode node) { break; } } + + //conditionally render any new panels? + insnNodeList = methodNode.instructions.iterator(); + while (insnNodeList.hasNext()) { + AbstractInsnNode insnNode = insnNodeList.next(); + AbstractInsnNode nextNode = insnNode.getNext(); + + if (insnNode.getOpcode() == Opcodes.ALOAD + && ((VarInsnNode) insnNode).var == 0 + && nextNode.getOpcode() == Opcodes.GETFIELD + && ((FieldInsnNode) nextNode).name.equals("Qk")) { + LabelNode label = new LabelNode(); + VarInsnNode call = (VarInsnNode) insnNode; + + methodNode.instructions.insertBefore( + call, + new MethodInsnNode( + Opcodes.INVOKESTATIC, "Game/Client", "drawGameHook", "()Z", false)); + methodNode.instructions.insertBefore(call, new JumpInsnNode(Opcodes.IFGT, label)); + methodNode.instructions.insertBefore(call, new InsnNode(Opcodes.RETURN)); + methodNode.instructions.insertBefore(call, label); + break; + } + } } if (methodNode.name.equals("a") && methodNode.desc.equals("(IIZ)Z")) { Iterator insnNodeList = methodNode.instructions.iterator(); @@ -2240,6 +2386,34 @@ else if (pos == 1) { "([Ljava/lang/String;I)V")); } } + + + //hook onto some "other" opcode received + insnNodeList = methodNode.instructions.iterator(); + while (insnNodeList.hasNext()) { + AbstractInsnNode insnNode = insnNodeList.next(); + AbstractInsnNode nextNode = insnNode.getNext(); + LabelNode label = new LabelNode(); + + if (nextNode == null) continue; + if (insnNode.getOpcode() == Opcodes.LDC + && insnNode instanceof LdcInsnNode + && ((LdcInsnNode) insnNode).cst instanceof Integer + && ((Integer) ((LdcInsnNode) insnNode).cst).equals(2097151) + && nextNode.getOpcode() == Opcodes.ACONST_NULL) { + LdcInsnNode call = (LdcInsnNode) insnNode; + methodNode.instructions.insertBefore(call, new VarInsnNode(Opcodes.ILOAD, 1)); + methodNode.instructions.insertBefore(call, new VarInsnNode(Opcodes.ILOAD, 3)); + methodNode.instructions.insertBefore( + call, + new MethodInsnNode( + Opcodes.INVOKESTATIC, "Game/Client", "newOpcodeReceivedHook", "(II)Z", false)); + methodNode.instructions.insertBefore(call, new JumpInsnNode(Opcodes.IFGT, label)); + methodNode.instructions.insertBefore(call, new InsnNode(Opcodes.RETURN)); + methodNode.instructions.insertBefore(call, label); + break; + } + } } // hook onto selected menu option if (methodNode.name.equals("G") && methodNode.desc.equals("(I)V")) { @@ -2355,20 +2529,6 @@ else if (pos == 1) { && prevNode.getOpcode() == Opcodes.GETFIELD && ((FieldInsnNode) prevNode).owner.equals("client") && ((FieldInsnNode) prevNode).name.equals("ge")) { - methodNode.instructions.insertBefore(prevNode, new VarInsnNode(Opcodes.ALOAD, 0)); - methodNode.instructions.insertBefore(prevNode, new TypeInsnNode(Opcodes.NEW, "qa")); - methodNode.instructions.insertBefore(prevNode, new InsnNode(Opcodes.DUP)); - methodNode.instructions.insertBefore(prevNode, new VarInsnNode(Opcodes.ALOAD, 0)); - methodNode.instructions.insertBefore( - prevNode, new FieldInsnNode(Opcodes.GETFIELD, "client", "li", "Lba;")); - methodNode.instructions.insertBefore(prevNode, new IntInsnNode(Opcodes.BIPUSH, 50)); - methodNode.instructions.insertBefore( - prevNode, - new MethodInsnNode(Opcodes.INVOKESPECIAL, "qa", "", "(Lua;I)V", false)); - methodNode.instructions.insertBefore( - prevNode, - new FieldInsnNode( - Opcodes.PUTSTATIC, "Game/Client", "panelRegister", "Ljava/lang/Object;")); methodNode.instructions.insertBefore(prevNode, new VarInsnNode(Opcodes.ILOAD, 1)); methodNode.instructions.insertBefore( prevNode, @@ -2381,6 +2541,34 @@ else if (pos == 1) { break; } } + + // Another hook to get added "I've lost my password" + insnNodeList = methodNode.instructions.iterator(); + + while (insnNodeList.hasNext()) { + AbstractInsnNode insnNode = insnNodeList.next(); + AbstractInsnNode nextNode = insnNode.getNext(); + + if (nextNode == null) break; + + if (insnNode.getOpcode() == Opcodes.INVOKEVIRTUAL + && nextNode.getOpcode() == Opcodes.PUTFIELD + && ((FieldInsnNode) nextNode).owner.equals("client") + && ((FieldInsnNode) nextNode).name.equals("Xi")) { + AbstractInsnNode call = nextNode.getNext(); + methodNode.instructions.insertBefore(call, new VarInsnNode(Opcodes.ILOAD, 1)); + methodNode.instructions.insertBefore(call, new VarInsnNode(Opcodes.ILOAD, 2)); + methodNode.instructions.insertBefore( + call, + new MethodInsnNode( + Opcodes.INVOKESTATIC, + "Game/AccountManagement", + "panel_login_hook", + "(II)V", + false)); + break; + } + } } if (methodNode.name.equals("k") && methodNode.desc.equals("(I)V")) { // pre-game display hook @@ -2458,8 +2646,54 @@ else if (pos == 1) { "account_panels_key_hook", "(II)V", false)); + break; } } + + insnNodeList = methodNode.instructions.iterator(); + + while (insnNodeList.hasNext()) { + AbstractInsnNode insnNode = insnNodeList.next(); + AbstractInsnNode nextNode = insnNode.getNext(); + AbstractInsnNode twoNextNode = nextNode.getNext(); + AbstractInsnNode findNode, nextFindNode, call; + LabelNode labelNode, exitNode; + + if (nextNode == null || twoNextNode == null) break; + + if (insnNode.getOpcode() == Opcodes.ALOAD + && ((VarInsnNode) insnNode).var == 0 + && nextNode.getOpcode() == Opcodes.GETFIELD + && ((FieldInsnNode) nextNode).name.equals("qg") + && twoNextNode.getOpcode() == Opcodes.ICONST_M1) { + + findNode = twoNextNode; + while (findNode.getOpcode() != Opcodes.IF_ICMPEQ) { + // find part of checking is logged in + findNode = findNode.getNext(); + } + nextFindNode = findNode.getNext(); + + labelNode = ((JumpInsnNode)findNode).label; + exitNode = ((JumpInsnNode)nextFindNode).label; + call = insnNode.getPrevious(); + + methodNode.instructions.insertBefore(call, new VarInsnNode(Opcodes.ALOAD, 0)); + methodNode.instructions.insertBefore( + call, new FieldInsnNode(Opcodes.GETFIELD, "client", "qg", "I")); + methodNode.instructions.insertBefore(call, new VarInsnNode(Opcodes.ILOAD, 2)); + methodNode.instructions.insertBefore( + call, + new MethodInsnNode( + Opcodes.INVOKESTATIC, + "Game/AccountManagement", + "ingame_keyhandler_hook", + "(II)I")); + methodNode.instructions.insertBefore(insnNode, new JumpInsnNode(Opcodes.IFNE, exitNode)); + + break; + } + } } if (methodNode.name.equals("x") && methodNode.desc.equals("(I)V")) { // Login button press hook, from login panel @@ -2484,6 +2718,34 @@ else if (pos == 1) { methodNode.instructions.insertBefore( findNode, new MethodInsnNode(Opcodes.INVOKESTATIC, "Game/Client", "login_hook", "()V", false)); + + + // Lost password press hook, from login panel + insnNodeList = methodNode.instructions.iterator(); + + while (insnNodeList.hasNext()) { + AbstractInsnNode insnNode = insnNodeList.next(); + AbstractInsnNode nextNode = insnNode.getNext(); + AbstractInsnNode twoNextNode = nextNode.getNext(); + LabelNode label = new LabelNode(); + + if (nextNode == null || twoNextNode == null) break; + + if (insnNode.getOpcode() == Opcodes.GETFIELD + && ((FieldInsnNode) insnNode).name.equals("Ih") + && nextNode.getOpcode() == Opcodes.BIPUSH + && ((IntInsnNode) nextNode).operand == -88 + && twoNextNode.getOpcode() == Opcodes.INVOKEVIRTUAL) { + + FrameNode call = (FrameNode) (twoNextNode.getNext().getNext()); + methodNode.instructions.insertBefore( + call, + new MethodInsnNode( + Opcodes.INVOKESTATIC, "Game/Client", "loginOtherButtonCheckHook", "()V", false)); + break; + } + } + // Register button press hook insnNodeList = methodNode.instructions.iterator(); @@ -2552,7 +2814,6 @@ else if (pos == 1) { methodNode.instructions.insertBefore(insnNode, new VarInsnNode(Opcodes.ALOAD, 0)); methodNode.instructions.insertBefore( insnNode, new FieldInsnNode(Opcodes.GETFIELD, "client", "xb", "I")); - methodNode.instructions.insertBefore(insnNode, new IntInsnNode(Opcodes.SIPUSH, -9989)); methodNode.instructions.insertBefore(insnNode, new VarInsnNode(Opcodes.ALOAD, 0)); methodNode.instructions.insertBefore( insnNode, new FieldInsnNode(Opcodes.GETFIELD, "client", "Qb", "I")); @@ -2565,11 +2826,247 @@ else if (pos == 1) { Opcodes.INVOKESTATIC, "Game/AccountManagement", "account_panels_input_hook", - "(IIIII)V", + "(IIII)V", false)); } } } + if (methodNode.name.equals("b") && methodNode.desc.equals("(IZ)V")) { + // move down original text "to change your contact details, etc" since should be 5 px down + Iterator insnNodeList = methodNode.instructions.iterator(); + while (insnNodeList.hasNext()) { + AbstractInsnNode insnNode = insnNodeList.next(); + AbstractInsnNode nextNode = insnNode.getNext(); + AbstractInsnNode twoNextNode = nextNode.getNext(); + + if (nextNode == null || twoNextNode == null) break; + + if (insnNode.getOpcode() == Opcodes.GETSTATIC + && ((FieldInsnNode) insnNode).name.equals("il") + && nextNode.getOpcode() == Opcodes.SIPUSH + && ((IntInsnNode) nextNode).operand == 145) { + methodNode.instructions.insertBefore(insnNode.getPrevious().getPrevious(), new IincInsnNode(7, 5)); + break; + } + } + + // correct the offset of clicking with previous text correction + insnNodeList = methodNode.instructions.iterator(); + while (insnNodeList.hasNext()) { + AbstractInsnNode insnNode = insnNodeList.next(); + AbstractInsnNode nextNode = insnNode.getNext(); + AbstractInsnNode twoNextNode = nextNode.getNext(); + + if (nextNode == null || twoNextNode == null) break; + + if (insnNode.getOpcode() == Opcodes.IINC + && ((IincInsnNode) insnNode).var == 7 + && ((IincInsnNode) insnNode).incr == 15 + && nextNode.getOpcode() == Opcodes.IINC + && ((IincInsnNode) nextNode).var == 7 + && ((IincInsnNode) nextNode).incr == 15 + && twoNextNode.getOpcode() == Opcodes.IINC + && ((IincInsnNode) twoNextNode).var == 7 + && ((IincInsnNode) twoNextNode).incr == 15) { + methodNode.instructions.insertBefore(insnNode, new IincInsnNode(7, 5)); + break; + } + } + + // move up text "always logout when you finish" if in tutorial island + insnNodeList = methodNode.instructions.iterator(); + while (insnNodeList.hasNext()) { + AbstractInsnNode insnNode = insnNodeList.next(); + AbstractInsnNode nextNode = insnNode.getNext(); + AbstractInsnNode targetNode; + + if (nextNode == null) break; + + if (insnNode.getOpcode() == Opcodes.GETSTATIC + && ((FieldInsnNode) insnNode).name.equals("il") + && nextNode.getOpcode() == Opcodes.SIPUSH + && ((IntInsnNode) nextNode).operand == 134) { + targetNode = nextNode; + while (targetNode.getOpcode() != Opcodes.IINC + || ((IincInsnNode) targetNode).incr != 15) { + // find the part of the += 15 jump inside skip tutorial + targetNode = targetNode.getNext(); + } + ((IincInsnNode) targetNode).incr = 10; // should have been 10 instead of 15 + break; + } + } + + // move up click pos "always logout when you finish" if in tutorial island + insnNodeList = methodNode.instructions.iterator(); + while (insnNodeList.hasNext()) { + AbstractInsnNode insnNode = insnNodeList.next(); + AbstractInsnNode targetNode; + + if (insnNode.getOpcode() == Opcodes.IINC + && ((IincInsnNode) insnNode).incr == 35) { + targetNode = insnNode; + while (targetNode.getOpcode() != Opcodes.IINC + || ((IincInsnNode) targetNode).incr != 5) { + // start section of click for skip tutorial + targetNode = targetNode.getNext(); + } + + while (targetNode.getOpcode() != Opcodes.IINC + || ((IincInsnNode) targetNode).incr != 15) { + // end section of click for skip tutorial + targetNode = targetNode.getNext(); + } + ((IincInsnNode) targetNode).incr = 10; // should have been 10 instead of 15 + break; + } + } + + // bigger "Ypos" click area for options menu because when player in tutorial island, the menu goes further down and wasn't + // adapted since it was introduced + insnNodeList = methodNode.instructions.iterator(); + while (insnNodeList.hasNext()) { + AbstractInsnNode insnNode = insnNodeList.next(); + IntInsnNode targetNode; + + if (insnNode.getOpcode() == Opcodes.SIPUSH + && ((IntInsnNode) insnNode).operand == -266) { + targetNode = (IntInsnNode)insnNode; + targetNode.operand = -286; + break; + } + } + + // Options menu hook + insnNodeList = methodNode.instructions.iterator(); + + while (insnNodeList.hasNext()) { + AbstractInsnNode insnNode = insnNodeList.next(); + AbstractInsnNode nextNode = insnNode.getNext(); + AbstractInsnNode startNode; + AbstractInsnNode targetNode; + + if (nextNode == null) break; + + if (insnNode.getOpcode() == Opcodes.SIPUSH && ((IntInsnNode) insnNode).operand == 145) { + startNode = targetNode = insnNode; + while (startNode.getOpcode() != Opcodes.ALOAD) { + // find aload0, to insert jump to skip "To change your contact details,password, + // recovery questions, etc..please select 'account management'" + startNode = startNode.getPrevious(); + } + + while (targetNode.getOpcode() != Opcodes.SIPUSH + || ((IntInsnNode) targetNode).operand != 139) { + // find "Privacy settings. Will be applied to" keymarker to indicate block to jump to + targetNode = targetNode.getNext(); + } + // back off to find the corresponding aload0 + while (targetNode.getOpcode() != Opcodes.ALOAD) { + targetNode = targetNode.getPrevious(); + } + + LabelNode label = new LabelNode(); + + methodNode.instructions.insertBefore( + startNode, + new MethodInsnNode( + Opcodes.INVOKESTATIC, "Game/Client", "showSecuritySettings", "()Z")); + methodNode.instructions.insertBefore(startNode, new JumpInsnNode(Opcodes.IFGT, label)); + + methodNode.instructions.insertBefore(targetNode, label); + methodNode.instructions.insertBefore(targetNode, new VarInsnNode(Opcodes.ILOAD, 6)); + methodNode.instructions.insertBefore(targetNode, new VarInsnNode(Opcodes.ILOAD, 7)); + methodNode.instructions.insertBefore(targetNode, new VarInsnNode(Opcodes.ALOAD, 0)); + methodNode.instructions.insertBefore( + targetNode, new FieldInsnNode(Opcodes.GETFIELD, "client", "I", "I")); + methodNode.instructions.insertBefore(targetNode, new VarInsnNode(Opcodes.ALOAD, 0)); + methodNode.instructions.insertBefore( + targetNode, new FieldInsnNode(Opcodes.GETFIELD, "client", "xb", "I")); + methodNode.instructions.insertBefore( + targetNode, + new MethodInsnNode( + Opcodes.INVOKESTATIC, + "Game/AccountManagement", + "options_security_hook", + "(IIII)I")); + methodNode.instructions.insertBefore(targetNode, new VarInsnNode(Opcodes.ILOAD, 7)); + methodNode.instructions.insertBefore(targetNode, new InsnNode(Opcodes.IADD)); + methodNode.instructions.insertBefore(targetNode, new VarInsnNode(Opcodes.ISTORE, 7)); + + break; + } + } + + // Options menu click hook + insnNodeList = methodNode.instructions.iterator(); + + while (insnNodeList.hasNext()) { + AbstractInsnNode insnNode = insnNodeList.next(); + AbstractInsnNode nextNode = insnNode.getNext(); + AbstractInsnNode twoNextNode = nextNode.getNext(); + AbstractInsnNode startNode; + AbstractInsnNode targetNode; + AbstractInsnNode call; + + if (nextNode == null || twoNextNode == null) break; + + if (insnNode.getOpcode() == Opcodes.IINC + && ((IincInsnNode) insnNode).var == 7 + && ((IincInsnNode) insnNode).incr == 15 + && nextNode.getOpcode() == Opcodes.IINC + && ((IincInsnNode) nextNode).var == 7 + && ((IincInsnNode) nextNode).incr == 15 + && twoNextNode.getOpcode() == Opcodes.IINC + && ((IincInsnNode) twoNextNode).var == 7 + && ((IincInsnNode) twoNextNode).incr == 15) { + // start part of for clicking "To change your contact details,password, recovery + // questions, etc..please select 'account management'" + startNode = targetNode = insnNode.getPrevious(); //at this point got corrected with the 5px offset + + while (targetNode.getOpcode() != Opcodes.IINC + || ((IincInsnNode) targetNode).incr != 35 + || targetNode.getNext().getOpcode() != Opcodes.ICONST_0) { + // find part close to click "Privacy settings. Will be applied to" + targetNode = targetNode.getNext(); + } + targetNode = targetNode.getNext(); // iconst0 + + LabelNode label = new LabelNode(); + + methodNode.instructions.insertBefore( + startNode, + new MethodInsnNode( + Opcodes.INVOKESTATIC, "Game/Client", "showSecuritySettings", "()Z")); + methodNode.instructions.insertBefore(startNode, new JumpInsnNode(Opcodes.IFGT, label)); + + methodNode.instructions.insertBefore(targetNode, label); + methodNode.instructions.insertBefore(targetNode, new VarInsnNode(Opcodes.ILOAD, 6)); + methodNode.instructions.insertBefore(targetNode, new VarInsnNode(Opcodes.ILOAD, 7)); + methodNode.instructions.insertBefore(targetNode, new VarInsnNode(Opcodes.ALOAD, 0)); + methodNode.instructions.insertBefore( + targetNode, new FieldInsnNode(Opcodes.GETFIELD, "client", "I", "I")); + methodNode.instructions.insertBefore(targetNode, new VarInsnNode(Opcodes.ALOAD, 0)); + methodNode.instructions.insertBefore( + targetNode, new FieldInsnNode(Opcodes.GETFIELD, "client", "xb", "I")); + methodNode.instructions.insertBefore(targetNode, new VarInsnNode(Opcodes.ALOAD, 0)); + methodNode.instructions.insertBefore( + targetNode, new FieldInsnNode(Opcodes.GETFIELD, "client", "Cf", "I")); + methodNode.instructions.insertBefore( + targetNode, + new MethodInsnNode( + Opcodes.INVOKESTATIC, + "Game/AccountManagement", + "options_security_click_hook", + "(IIIII)I")); + methodNode.instructions.insertBefore(targetNode, new VarInsnNode(Opcodes.ILOAD, 7)); + methodNode.instructions.insertBefore(targetNode, new InsnNode(Opcodes.IADD)); + methodNode.instructions.insertBefore(targetNode, new VarInsnNode(Opcodes.ISTORE, 7)); + + break; + } + } + } if (methodNode.name.equals("a") && methodNode.desc.equals("(ZI)V")) { // Disconnect hook (::closecon) Iterator insnNodeList = methodNode.instructions.iterator(); @@ -2907,6 +3404,64 @@ else if (pos == 1) { } } + if (methodNode.name.equals("I") && methodNode.desc.equals("(I)V")) { + // menu ui + Iterator insnNodeList = methodNode.instructions.iterator(); + while (insnNodeList.hasNext()) { + AbstractInsnNode insnNode = insnNodeList.next(); + AbstractInsnNode nextNode = insnNode.getNext(); + AbstractInsnNode findNode; + LabelNode targetNode; + LabelNode labelNode; + + if (nextNode == null) break; + + if (insnNode.getOpcode() == Opcodes.INVOKESPECIAL + && ((MethodInsnNode) insnNode).name.equals("d") + && ((MethodInsnNode) insnNode).desc.equals("(B)V") + && nextNode.getOpcode() == Opcodes.ILOAD + && ((VarInsnNode) nextNode).var == 4) { + findNode = nextNode.getNext(); + targetNode = ((JumpInsnNode) findNode).label; + + // find last else to insert else if + while (findNode.getOpcode() != Opcodes.ICONST_1 + || findNode.getNext().getOpcode() != Opcodes.ISTORE) { + findNode = findNode.getNext(); + } + + labelNode = new LabelNode(); + methodNode.instructions.insertBefore(findNode, labelNode); + + methodNode.instructions.insertBefore( + labelNode, + new FieldInsnNode( + Opcodes.GETSTATIC, "Game/AccountManagement", "panelPasswordChangeMode", "I")); + methodNode.instructions.insertBefore( + labelNode, new JumpInsnNode(Opcodes.IFEQ, labelNode)); + methodNode.instructions.insertBefore(labelNode, new VarInsnNode(Opcodes.ALOAD, 0)); + methodNode.instructions.insertBefore( + labelNode, new FieldInsnNode(Opcodes.GETFIELD, "client", "I", "I")); + methodNode.instructions.insertBefore(labelNode, new VarInsnNode(Opcodes.ALOAD, 0)); + methodNode.instructions.insertBefore( + labelNode, new FieldInsnNode(Opcodes.GETFIELD, "client", "xb", "I")); + methodNode.instructions.insertBefore(labelNode, new VarInsnNode(Opcodes.ALOAD, 0)); + methodNode.instructions.insertBefore( + labelNode, new FieldInsnNode(Opcodes.GETFIELD, "client", "Cf", "I")); + methodNode.instructions.insertBefore( + labelNode, + new MethodInsnNode( + Opcodes.INVOKESTATIC, + "Game/AccountManagement", + "draw_change_pass_hook", + "(III)V", + false)); + methodNode.instructions.insertBefore( + labelNode, new JumpInsnNode(Opcodes.GOTO, targetNode)); + } + } + } + // hookTracer(node, methodNode); } } diff --git a/src/Client/Settings.java b/src/Client/Settings.java index ef0ed86b..512dce80 100644 --- a/src/Client/Settings.java +++ b/src/Client/Settings.java @@ -18,13 +18,6 @@ */ package Client; -import Client.KeybindSet.KeyModifier; -import Game.Camera; -import Game.Client; -import Game.Game; -import Game.KeyboardHandler; -import Game.Renderer; -import Game.Replay; import java.awt.Cursor; import java.awt.Point; import java.awt.image.BufferedImage; @@ -36,6 +29,13 @@ import java.util.Comparator; import java.util.HashMap; import java.util.Properties; +import Client.KeybindSet.KeyModifier; +import Game.Camera; +import Game.Client; +import Game.Game; +import Game.KeyboardHandler; +import Game.Renderer; +import Game.Replay; /** Manages storing, loading, and changing settings. */ public class Settings { @@ -69,6 +69,8 @@ public class Settings { public static HashMap CUSTOM_CLIENT_SIZE_X = new HashMap(); public static HashMap CUSTOM_CLIENT_SIZE_Y = new HashMap(); public static HashMap CHECK_UPDATES = new HashMap(); + public static HashMap SHOW_ACCOUNT_SECURITY_SETTINGS = + new HashMap(); public static HashMap REMIND_HOW_TO_OPEN_SETTINGS = new HashMap(); public static HashMap LOAD_CHAT_HISTORY = new HashMap(); @@ -160,8 +162,6 @@ public class Settings { public static HashMap SHOW_LOGIN_IP_ADDRESS = new HashMap(); public static HashMap SAVE_LOGININFO = new HashMap(); public static HashMap START_LOGINSCREEN = new HashMap(); - public static HashMap SHOW_ACCOUNT_SECURITY_SETTINGS = - new HashMap(); public static HashMap SPEEDRUNNER_MODE_ACTIVE = new HashMap(); // public static HashMap SPEEDRUNNER_USERNAME = new HashMap(); @@ -270,6 +270,19 @@ public static void definePresets(Properties props) { CHECK_UPDATES.put("all", true); CHECK_UPDATES.put( "custom", getPropBoolean(props, "check_updates", CHECK_UPDATES.get("default"))); + + SHOW_ACCOUNT_SECURITY_SETTINGS.put("vanilla", false); + SHOW_ACCOUNT_SECURITY_SETTINGS.put("vanilla_resizable", false); + SHOW_ACCOUNT_SECURITY_SETTINGS.put("lite", true); + SHOW_ACCOUNT_SECURITY_SETTINGS.put("default", true); + SHOW_ACCOUNT_SECURITY_SETTINGS.put("heavy", true); + SHOW_ACCOUNT_SECURITY_SETTINGS.put("all", true); + SHOW_ACCOUNT_SECURITY_SETTINGS.put( + "custom", + getPropBoolean( + props, + "show_account_security_settings", + SHOW_ACCOUNT_SECURITY_SETTINGS.get("default"))); REMIND_HOW_TO_OPEN_SETTINGS.put("vanilla", false); REMIND_HOW_TO_OPEN_SETTINGS.put("vanilla_resizable", false); @@ -1005,19 +1018,6 @@ public static void definePresets(Properties props) { START_LOGINSCREEN.put( "custom", getPropBoolean(props, "start_loginscreen", START_LOGINSCREEN.get("default"))); - SHOW_ACCOUNT_SECURITY_SETTINGS.put("vanilla", false); - SHOW_ACCOUNT_SECURITY_SETTINGS.put("vanilla_resizable", false); - SHOW_ACCOUNT_SECURITY_SETTINGS.put("lite", true); - SHOW_ACCOUNT_SECURITY_SETTINGS.put("default", true); - SHOW_ACCOUNT_SECURITY_SETTINGS.put("heavy", true); - SHOW_ACCOUNT_SECURITY_SETTINGS.put("all", true); - SHOW_ACCOUNT_SECURITY_SETTINGS.put( - "custom", - getPropBoolean( - props, - "show_account_security_settings", - SHOW_ACCOUNT_SECURITY_SETTINGS.get("default"))); - SPEEDRUNNER_MODE_ACTIVE.put("vanilla", false); SPEEDRUNNER_MODE_ACTIVE.put("vanilla_resizable", false); SPEEDRUNNER_MODE_ACTIVE.put("lite", false); @@ -1541,6 +1541,9 @@ public static void save(String preset) { props.setProperty("custom_client_size_x", Integer.toString(CUSTOM_CLIENT_SIZE_X.get(preset))); props.setProperty("custom_client_size_y", Integer.toString(CUSTOM_CLIENT_SIZE_Y.get(preset))); props.setProperty("check_updates", Boolean.toString(CHECK_UPDATES.get(preset))); + props.setProperty( + "show_account_security_settings", + Boolean.toString(SHOW_ACCOUNT_SECURITY_SETTINGS.get(preset))); props.setProperty( "welcome_enabled", Boolean.toString(REMIND_HOW_TO_OPEN_SETTINGS.get(preset))); props.setProperty("load_chat_history", Boolean.toString(LOAD_CHAT_HISTORY.get(preset))); @@ -1637,9 +1640,6 @@ public static void save(String preset) { props.setProperty("show_logindetails", Boolean.toString(SHOW_LOGIN_IP_ADDRESS.get(preset))); props.setProperty("save_logininfo", Boolean.toString(SAVE_LOGININFO.get(preset))); props.setProperty("start_loginscreen", Boolean.toString(START_LOGINSCREEN.get(preset))); - props.setProperty( - "show_account_security_settings", - Boolean.toString(SHOW_ACCOUNT_SECURITY_SETTINGS.get(preset))); props.setProperty("speedrun_active", Boolean.toString(SPEEDRUNNER_MODE_ACTIVE.get(preset))); // props.setProperty("speedrun_username", Settings.SPEEDRUNNER_USERNAME.get(preset)); diff --git a/src/Client/Util.java b/src/Client/Util.java index 1c411948..e0df68ae 100644 --- a/src/Client/Util.java +++ b/src/Client/Util.java @@ -18,8 +18,6 @@ */ package Client; -import Game.Replay; -import Game.ReplayQueue; import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.File; @@ -32,6 +30,8 @@ import java.util.Locale; import java.util.zip.CRC32; import java.util.zip.GZIPInputStream; +import Game.Replay; +import Game.ReplayQueue; /** A miscellaneous utility class */ public class Util { @@ -415,4 +415,17 @@ public static void int_put(byte[] buffer, int offset, int num) { buffer[offset + 2] = (byte) (num >> 8); buffer[offset + 3] = (byte) num; } + + /** RSC175 - format a string to only have letters and numbers, with maxlength */ + public static String formatString(String s, int maxLen) { + String lowerString = s.toLowerCase(); + String res = ""; + for (int i = 0; i < lowerString.length() && i < maxLen; ++i) { + char ch = lowerString.charAt(i); + if (ch >= 'a' && ch <= 'z' || ch >= '0' && ch <= '9') { + res = String.valueOf(res) + ch; + } + } + return res; + } } diff --git a/src/Game/AccountManagement.java b/src/Game/AccountManagement.java index d71418be..f198327c 100644 --- a/src/Game/AccountManagement.java +++ b/src/Game/AccountManagement.java @@ -18,37 +18,47 @@ */ package Game; +import java.math.BigInteger; import Client.CRC16; import Client.Logger; import Client.Settings; import Client.Util; -import java.math.BigInteger; public class AccountManagement { + public static int panelPasswordChangeMode; + public static String oldPassword; + public static String newPassword; + public static int customQuestionEntry = -1; + + public static String recoveryQuestions[] = new String[]{"Where were you born?", "What was your first teachers name?", "What is your fathers middle name?", "Who was your first best friend?", "What is your favourite vacation spot?", "What is your mothers middle name?", "What was your first pets name?", "What was the name of your first school?", "What is your mothers maiden name?", "Who was your first boyfriend/girlfriend?", "What was the first computer game you purchased?", "Who is your favourite actor/actress?", "Who is your favourite author?", "Who is your favourite musician?", "Who is your favourite cartoon character?", "What is your favourite book?", "What is your favourite food?", "What is your favourite movie?"}; + public static int recoveryIndices[] = new int[] { 0, 1, 2, 3, 4 }; + + // § SECTION stream IO interaction § + /** Intended to be RSC127 compatible */ public static void register(String user, String pass) { if (Client.login_delay > 0) { - Client.setLoginMessage("Connecting to server", "Please wait..."); + Client.setResponseMessage("Connecting to server", "Please wait..."); try { Client.shadowSleep(11200, 2000L); } catch (Exception e) { } - Client.setLoginMessage("Please try again later", "Sorry! The server is currently full."); + Client.setResponseMessage("Please try again later", "Sorry! The server is currently full."); } else { try { Client.username_login = user; Client.password_login = pass; String formatPass = Client.formatText(pass, 20); - Client.setLoginMessage("Connecting to server", "Please wait..."); + Client.setResponseMessage("Connecting to server", "Please wait..."); // int port = Client.autologin_timeout <= 1 ? Client.serverjag_port : // Replay.connection_port; int port = Replay.connection_port; StreamUtil.initializeStream(Client.server_address, port); StreamUtil.setStreamMaxRetries(Client.maxRetries); - int session_id = 0; // TODO: should read session ID here, triggered by TCP handshake + Client.session_id = 0; // TODO: should read int to get session ID here, triggered by TCP handshake StreamUtil.newPacket(2); Object buffer = StreamUtil.getStreamBuffer(); @@ -57,9 +67,11 @@ public static void register(String user, String pass) { // Put Username long formatUser = Util.username2hash(Client.username_login); StreamUtil.putLongTo(buffer, formatUser); + + /** If we wanted a RSC175 would need to get added just below a putShort based on refererid / limit30 */ // Put Password - enc_cred_put(buffer, formatPass, session_id); + enc_cred_put(buffer, formatPass, Client.session_id); // In 235 putRandom of "random.dat" is 24 bytes, but for 127 is expected 4 bytes. Object randBlock = StreamUtil.getNewBuffer(24); @@ -79,41 +91,264 @@ public static void register(String user, String pass) { if (response == 2) { AccountManagement.success_register(); } else if (response == 3) { - Client.setLoginMessage("Please choose another username", "Username already taken."); + Client.setResponseMessage("Please choose another username", "Username already taken."); } else if (response == 4) { - Client.setLoginMessage("Wait 60 seconds then retry", "That username is already in use."); + Client.setResponseMessage("Wait 60 seconds then retry", "That username is already in use."); } else if (response == 5) { - Client.setLoginMessage("Please reload this page", "The client has been updated."); + Client.setResponseMessage("Please reload this page", "The client has been updated."); } else if (response == 6) { - Client.setLoginMessage( + Client.setResponseMessage( "Your ip-address is already in use", "You may only use 1 character at once."); } else if (response == 7) { - Client.setLoginMessage("Please try again in 5 minutes", "Login attempts exceeded!"); + Client.setResponseMessage("Please try again in 5 minutes", "Login attempts exceeded!"); } else if (response == 11) { - Client.setLoginMessage("for cheating or abuse", "Account has been temporarily disabled"); + Client.setResponseMessage("for cheating or abuse", "Account has been temporarily disabled"); } else if (response == 12) { - Client.setLoginMessage("for cheating or abuse", "Account has been permanently disabled"); + Client.setResponseMessage("for cheating or abuse", "Account has been permanently disabled"); } else if (response == 13) { - Client.setLoginMessage("Please choose another username", "Username already taken."); + Client.setResponseMessage("Please choose another username", "Username already taken."); } else if (response == 14) { - Client.setLoginMessage("Please try again later", "Sorry! The server is currently full."); + Client.setResponseMessage("Please try again later", "Sorry! The server is currently full."); Client.login_delay = 1500; } else if (response == 15) { - Client.setLoginMessage("to login to this server", "You need a members account"); + Client.setResponseMessage("to login to this server", "You need a members account"); } else if (response == 16) { - Client.setLoginMessage( + Client.setResponseMessage( "to access member-only features", "Please login to a members server"); } else { - Client.setLoginMessage( + Client.setResponseMessage( "Check your internet settings", "Sorry! Unable to connect to server."); } } catch (Exception e) { Logger.Game(String.valueOf(e)); - Client.setLoginMessage( + Client.setResponseMessage( "Check your internet settings", "Sorry! Unable to connect to server."); } } } + + /** Intended to be RSC175 compatible. It keeps block of reading questions generated from RSC127 ("~:") + * In order to render them "properly" */ + public static void forgotPass(String user) { + Client.setResponseMessage("Connecting to server", "Please wait..."); + + try { + int port = Replay.connection_port; + StreamUtil.initializeStream(Client.server_address, port); + StreamUtil.setStreamMaxRetries(Client.maxRetries); + + Client.session_id = 0; // TODO: should read int to get session ID here, triggered by TCP handshake + + StreamUtil.newPacket(4); + Object buffer = StreamUtil.getStreamBuffer(); + StreamUtil.putLongTo(buffer, Util.username2hash(user)); + StreamUtil.flushPacket(); + + StreamUtil.readShort(); // Unknown what data this contained, client doesn't use it. + int response = StreamUtil.readStream(); + Logger.Game("Getpq response: " + response); + if (response == 0) { + Client.setResponseMessage("", "Sorry, the recovery questions for this user have not been set"); + return; + } + + String question; + int idx; + for (int i = 0; i < 5; ++i) { + int length = StreamUtil.readStream(); + byte[] arr = new byte[5000]; + StreamUtil.readBytes(arr, length); + question = new String(arr, 0, length); + + // This 'if' section is for parsing questions set in RSC127 + if (question.startsWith("~:")) { + question = question.substring(2); + idx = 0; + + try { + idx = Integer.parseInt(question); + } catch (Exception ex) { } + + question = recoveryQuestions[idx]; + } + + Panel.setControlText(Client.panelRecovery, Client.controlRecoveryQuestion[i], question); + } + + if (Client.failedRecovery) { + Client.setResponseMessage("", "Sorry, you have already attempted 1 recovery, try again later"); + return; + } + + Client.login_screen = Client.SCREEN_PASSWORD_RECOVERY; + Panel.setControlText(Client.panelRecovery, Client.controlRecoveryTop, + "@yel@To prove this is your account please provide the answers to"); + Panel.setControlText(Client.panelRecovery, Client.controlRecoveryBottom, + "@yel@your security questions. You will then be able to reset your password"); + + for (int i = 0; i < 5; ++i) { + Panel.setControlText(Client.panelRecovery, Client.controlRecoveryInput[i], ""); + } + + Panel.setControlText(Client.panelRecovery, Client.recoverOldPassInput, ""); + Panel.setControlText(Client.panelRecovery, Client.recoverNewPassInput, ""); + Panel.setControlText(Client.panelRecovery, Client.recoverConfirmPassInput, ""); + return; + } catch (Exception e) { + Client.setResponseMessage("Check your internet settings", "Sorry! Unable to connect to server."); + return; + } + } + + /** Intended to be RSC175 compatible. Different from RSC127 */ + public static void recover() { + Client.setResponseMessage("Connecting to server", "Please wait..."); + + try { + int port = Replay.connection_port; + StreamUtil.initializeStream(Client.server_address, port); + StreamUtil.setStreamMaxRetries(Client.maxRetries); + + Client.session_id = 0; // TODO: should read int to get session ID here, triggered by TCP handshake + + String oldPass = Client.formatText(Panel.getControlText(Client.panelRecovery, Client.recoverOldPassInput), 20); + String newPass = Client.formatText(Panel.getControlText(Client.panelRecovery, Client.recoverNewPassInput), 20); + + StreamUtil.newPacket(8); + Object buffer = StreamUtil.getStreamBuffer(); + StreamUtil.putLongTo(buffer, Util.username2hash(Panel.getControlText(Client.panelLogin, Client.loginUserInput))); + + // In 235 putRandom of "random.dat" is 24 bytes, but for 127 is expected 4 bytes. + Object randBlock = StreamUtil.getNewBuffer(24); + StreamUtil.putRandom(randBlock); + byte[] randArr = StreamUtil.getBufferByteArray(randBlock); + CRC16 sum = new CRC16(); + sum.update(randArr); + int rand = (int) sum.getValue(); + StreamUtil.putIntTo(buffer, rand); + + enc_cred_put(buffer, oldPass + newPass, Client.session_id); + + for (int i=0; i < 5; ++i) { + /** In RSC127 it would have sent an encrypted "hash" like of answers, but seems in later revisions + * incl. up to RSC175 was changed to just send the encrypted answers, probably due to collisions + * in RSC127 hasher function */ + String answer = Panel.getControlText(Client.panelRecovery, Client.controlRecoveryInput[i]); + answer = Util.formatString(answer, 50); + StreamUtil.putByteTo(buffer, (byte)answer.length()); + enc_cred_put(buffer, answer, Client.session_id); + } + + StreamUtil.flushPacket(); + + StreamUtil.readStream(); // Unknown what data this contained, client doesn't use it. + int response = StreamUtil.readStream(); + Logger.Game("Recover response: " + response); + if (response == 0) { + Client.login_screen = Client.SCREEN_USERNAME_PASSWORD_LOGIN; + Client.setResponseMessage("", "Sorry, recovery failed. You may try again in 1 hour"); + Client.failedRecovery = true; + return; + } + + if (response == 1) { + Client.login_screen = Client.SCREEN_USERNAME_PASSWORD_LOGIN; + Client.setResponseMessage("", "Your pass has been reset. You may now use the new pass to login"); + return; + } + + Client.login_screen = Client.SCREEN_USERNAME_PASSWORD_LOGIN; + Client.setResponseMessage("", "Recovery failed! Attempts exceeded?"); + return; + } catch (Exception e) { + e.printStackTrace(); + Client.setResponseMessage("Check your internet settings", "Sorry! Unable to connect to server."); + } + } + + /** Intended to be RSC175 compatible. Can be also used under RSC127 */ + public static void sendPassChange(String oldPwd, String newPwd) { + String oldPass = Client.formatText(oldPwd, 20); + String newPass = Client.formatText(newPwd, 20); + + StreamUtil.newPacket(25); + + Object buffer = StreamUtil.getStreamBuffer(); + enc_cred_put(buffer, oldPass + newPass, Client.session_id); + + StreamUtil.sendPacket(); + } + + /** Intended to be RSC175 compatible. Different from RSC127 */ + public static void sendRecoveryQuestions() { + StreamUtil.newPacket(208); + Object buffer = StreamUtil.getStreamBuffer(); + + for (int idx = 0; idx < 5; ++idx) { + String question = Client.controlRecoveryText[idx]; + if (question == null || question.length() == 0) { + question = String.valueOf(idx + 1); + } + if (question.length() > 50) { + question = question.substring(0, 50); + } + String answer = Panel.getControlText(Client.panelRecoveryQuestions, Client.controlAnswerInput[idx]); + answer = Util.formatString(answer, 50); + + StreamUtil.putByteTo(buffer, (byte)question.length()); + StreamUtil.putRegStrTo(buffer, question); + /** In RSC127 it would have sent an encrypted "hash" like of answers, but seems in later revisions + * incl. up to RSC175 was changed to just send the encrypted answers, probably due to collisions + * in RSC127 hasher function */ + StreamUtil.putByteTo(buffer, (byte)answer.length()); + enc_cred_put(buffer, answer, Client.session_id); + } + + StreamUtil.sendPacket(); + } + + /** Intended to be RSC175 compatible. Not present in RSC127 */ + public static void sendContactDetails(String fullName, String zipCode, String country, String email) { + StreamUtil.newPacket(253); + Object buffer = StreamUtil.getStreamBuffer(); + + StreamUtil.putByteTo(buffer, (byte)fullName.length()); + StreamUtil.putRegStrTo(buffer, fullName); + StreamUtil.putByteTo(buffer, (byte)zipCode.length()); + StreamUtil.putRegStrTo(buffer, zipCode); + StreamUtil.putByteTo(buffer, (byte)country.length()); + StreamUtil.putRegStrTo(buffer, country); + StreamUtil.putByteTo(buffer, (byte)email.length()); + StreamUtil.putRegStrTo(buffer, email); + + StreamUtil.sendPacket(); + } + + public static boolean processPacket(int opcode, int psize) { + boolean processed = false; + if (Settings.SHOW_ACCOUNT_SECURITY_SETTINGS.get(Settings.currentProfile) || (Replay.isPlaying || Replay.isSeeking || Replay.isRestarting)) { + if (opcode == 224) { // Opcode is intended to be RSC175 compatible. The RSC127 would have appended "~:" to recoveryQuestions[recoveryIndices[idx]] and adapted accordingly the control text + Client.showRecoveryQuestions = true; + for (int questionIndex = 0; questionIndex < 5; ++questionIndex) { + recoveryIndices[questionIndex] = questionIndex; + Client.controlRecoveryText[questionIndex] = recoveryQuestions[recoveryIndices[questionIndex]]; + Panel.setControlText( + Client.panelRecoveryQuestions,Client.controlAnswerInput[questionIndex], ""); + Panel.setControlText( + Client.panelRecoveryQuestions,Client.controlRecoveryIns[questionIndex], questionIndex + 1 + ": " + Client.controlRecoveryText[questionIndex]); + } + processed = true; + } else if (opcode == 232) { //Opcode is intended to be RSC175 compatible. Not present in RSC127 + Client.showContactDetails = true; + Panel.setControlText(Client.controlContactDetails, Client.fullNameInput, ""); + Panel.setControlText(Client.controlContactDetails, Client.zipCodeInput, ""); + Panel.setControlText(Client.controlContactDetails, Client.countryInput, ""); + Panel.setControlText(Client.controlContactDetails, Client.emailInput, ""); + processed = true; + } + } + return processed; + } public static void enc_cred_put(Object buffer, String str, int sessionId) { byte[] data = str.getBytes(); @@ -129,11 +364,11 @@ public static void enc_cred_put(Object buffer, String str, int sessionId) { // Put session ID Util.int_put(block, 4, sessionId); - for (int var9 = 0; var9 < 7; ++var9) { - if (i + var9 < len) { - block[8 + var9] = data[i + var9]; + for (int j = 0; j < 7; ++j) { + if (i + j < len) { + block[8 + j] = data[i + j]; } else { - block[8 + var9] = 32; + block[8 + j] = 32; } } @@ -174,6 +409,7 @@ public static void pregame_hook() { Panel.drawPanel(Client.panelRegister); } else if (Client.login_screen == Client.SCREEN_PASSWORD_RECOVERY) { Client.clearScreen(); + Panel.drawPanel(Client.panelRecovery); } } catch (Exception e) { e.printStackTrace(); @@ -186,13 +422,45 @@ public static void response_display_hook(String text1, String text2) { Panel.setControlText(Client.panelRegister, Client.controlRegister, text1 + " " + text2); } - if (Client.login_screen == Client.SCREEN_PASSWORD_RECOVERY) {} + if (Client.login_screen == Client.SCREEN_PASSWORD_RECOVERY) { + Panel.setControlText(Client.panelRecovery, Client.controlRecoveryTop, text1); + Panel.setControlText(Client.panelRecovery, Client.controlRecoveryBottom, text2); + } // continue checking if == 2 (login screen) with existing code } catch (Exception e) { e.printStackTrace(); } } + + public static boolean pending_render() { + boolean processed = false; + if (Client.showRecoveryQuestions) { + Client.setInterlace(false); + Client.clearScreen(); + Panel.drawPanel(Client.panelRecoveryQuestions); + if (customQuestionEntry != -1) { + int yPos = 150; + Renderer.drawBox(26, yPos, 460, 60, 0); + Renderer.drawBoxBorder(26, yPos, 460, 60, 0xFFFFFF); + yPos += 22; + Renderer.drawStringCenter("Please enter your question", 256, yPos, 4, 0xFFFFFF); + yPos += 25; + Renderer.drawStringCenter(Client.pm_enteredText + "*", 256, yPos, 4, 0xFFFFFF); + } + Renderer.drawSprite(0, Renderer.height_client, Renderer.sprite_media + 22); + Client.drawGraphics(); + processed = true; + } else if (Client.showContactDetails) { + Client.setInterlace(false); + Client.clearScreen(); + Panel.drawPanel(Client.panelContactDetails); + Renderer.drawSprite(0, Renderer.height_client, Renderer.sprite_media + 22); + Client.drawGraphics(); + processed = true; + } + return processed; + } public static void welcome_new_user_hook() { try { @@ -221,70 +489,470 @@ public static void panel_welcome_hook(int n) { Client.registerButton = Panel.addButtonTo(Client.panelWelcome, 86, 40 + 250, 100, 35); } - int var1 = 70; + Client.panelRegister = Panel.createPanel(50); + int yPos = 70; Client.controlRegister = Panel.addCenterTextTo( Client.panelRegister, 256, - var1 + 8, + yPos + 8, "To create an account please enter all the requested details", 4, true); - int var2 = var1 + 25; - Panel.addButtonBackTo(Client.panelRegister, 256, var2 + 17, 250, 34); - Panel.addCenterTextTo(Client.panelRegister, 256, var2 + 8, "Choose a Username", 4, false); + yPos += 25; + Panel.addButtonBackTo(Client.panelRegister, 256, yPos + 17, 250, 34); + Panel.addCenterTextTo(Client.panelRegister, 256, yPos + 8, "Choose a Username", 4, false); Client.chooseUserInput = - Panel.addInputTo(Client.panelRegister, n, 256, var2 + 25, 200, 40, 4, 320, false, false); + Panel.addInputTo(Client.panelRegister, 256, yPos + 25, 200, 40, 4, 320, false, false); Panel.setFocus(Client.panelRegister, Client.chooseUserInput); - var2 += 40; - Panel.addButtonBackTo(Client.panelRegister, 141, var2 + 17, 220, 34); - Panel.addCenterTextTo(Client.panelRegister, 141, var2 + 8, "Choose a Password", 4, false); + yPos += 40; + Panel.addButtonBackTo(Client.panelRegister, 141, yPos + 17, 220, 34); + Panel.addCenterTextTo(Client.panelRegister, 141, yPos + 8, "Choose a Password", 4, false); Client.choosePasswordInput = - Panel.addInputTo(Client.panelRegister, n, 141, var2 + 25, 220, 40, 4, 20, true, false); - Panel.addButtonBackTo(Client.panelRegister, 371, var2 + 17, 220, 34); - Panel.addCenterTextTo(Client.panelRegister, 371, var2 + 8, "Confirm Password", 4, false); + Panel.addInputTo(Client.panelRegister, 141, yPos + 25, 220, 40, 4, 20, true, false); + Panel.addButtonBackTo(Client.panelRegister, 371, yPos + 17, 220, 34); + Panel.addCenterTextTo(Client.panelRegister, 371, yPos + 8, "Confirm Password", 4, false); Client.chooseConfirmPassInput = - Panel.addInputTo(Client.panelRegister, n, 371, var2 + 25, 220, 40, 4, 20, true, false); - var2 += 40; - var2 += 20; - Client.acceptTermsCheckbox = Panel.addCheckboxTo(Client.panelRegister, 60, var2, 14); + Panel.addInputTo(Client.panelRegister, 371, yPos + 25, 220, 40, 4, 20, true, false); + yPos += 40; + yPos += 20; + Client.acceptTermsCheckbox = Panel.addCheckboxTo(Client.panelRegister, 60, yPos, 14); Panel.addTextTo( Client.panelRegister, 75, - var2, + yPos, "I have read and agree to the terms+conditions listed at:", 4, true); - var2 += 15; + yPos += 15; Panel.addCenterTextTo( - Client.panelRegister, 256, var2, "http://www.runescape.com/runeterms.html", 4, true); - var2 += 20; - Panel.addButtonBackTo(Client.panelRegister, 156, var2 + 17, 150, 34); - Panel.addCenterTextTo(Client.panelRegister, 156, var2 + 17, "Submit", 5, false); + Client.panelRegister, 256, yPos, "http://www.runescape.com/runeterms.html", 4, true); + yPos += 20; + Panel.addButtonBackTo(Client.panelRegister, 156, yPos + 17, 150, 34); + Panel.addCenterTextTo(Client.panelRegister, 156, yPos + 17, "Submit", 5, false); Client.chooseSubmitRegisterButton = - Panel.addButtonTo(Client.panelRegister, 156, var2 + 17, 150, 34); - Panel.addButtonBackTo(Client.panelRegister, 356, var2 + 17, 150, 34); - Panel.addCenterTextTo(Client.panelRegister, 356, var2 + 17, "Cancel", 5, false); + Panel.addButtonTo(Client.panelRegister, 156, yPos + 17, 150, 34); + Panel.addButtonBackTo(Client.panelRegister, 356, yPos + 17, 150, 34); + Panel.addCenterTextTo(Client.panelRegister, 356, yPos + 17, "Cancel", 5, false); Client.chooseCancelRegisterButton = - Panel.addButtonTo(Client.panelRegister, 356, var2 + 17, 150, 34); + Panel.addButtonTo(Client.panelRegister, 356, yPos + 17, 150, 34); } catch (Exception e) { e.printStackTrace(); } } + + public static void panel_login_hook(int n, int startingYPos) { + try { + if (Settings.SHOW_ACCOUNT_SECURITY_SETTINGS.get(Settings.currentProfile)) { + int yPos = startingYPos + 30; + Panel.addButtonBackTo(Client.panelLogin, 410, yPos, 160, 25); + Panel.addCenterTextTo(Client.panelLogin, 410, yPos, "I've lost my password", 4, false); + Client.loginLostPasswordButton = Panel.addButtonTo(Client.panelLogin, 410, yPos, 160, 25); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void create_account_recovery() { + try { + Client.panelRecovery = Panel.createPanel(100); + int yPos = 10; + Client.controlRecoveryTop = Panel.addCenterTextTo(Client.panelRecovery, 256, yPos, "@yel@To prove this is your account please provide the answers to", 1, true); + yPos += 15; + Client.controlRecoveryBottom = Panel.addCenterTextTo(Client.panelRecovery, 256, yPos, "@yel@your security questions. You will then be able to reset your password", 1, true); + yPos += 35; + + for (int i = 0; i < 5; ++i) { + Panel.addButtonBackTo(Client.panelRecovery, 256, yPos, 410, 30); + Client.controlRecoveryQuestion[i] = Panel.addCenterTextTo(Client.panelRecovery, 256, yPos - 7, i + 1 + ": question?", 1, true); + Client.controlRecoveryInput[i] = Panel.addInputTo(Client.panelRecovery, 256, yPos + 7, 310, 30, 1, 80, true, true); + yPos += 35; + } + + Panel.setFocus(Client.panelRecovery, Client.controlRecoveryInput[0]); + Panel.addButtonBackTo(Client.panelRecovery, 256, yPos, 410, 30); + Panel.addCenterTextTo(Client.panelRecovery, 256, yPos - 7, "If you know it, enter a previous password used on this account", 1, true); + Client.recoverOldPassInput = Panel.addInputTo(Client.panelRecovery, 256, yPos + 7, 310, 30, 1, 80, true, true); + yPos += 35; + Panel.addButtonBackTo(Client.panelRecovery, 151, yPos, 200, 30); + Panel.addCenterTextTo(Client.panelRecovery, 151, yPos - 7, "Choose a NEW password", 1, true); + Client.recoverNewPassInput = Panel.addInputTo(Client.panelRecovery, 146, yPos + 7, 200, 30, 1, 80, true, true); + Panel.addButtonBackTo(Client.panelRecovery, 361, yPos, 200, 30); + Panel.addCenterTextTo(Client.panelRecovery, 361, yPos - 7, "Confirm new password", 1, true); + Client.recoverConfirmPassInput = Panel.addInputTo(Client.panelRecovery, 366, yPos + 7, 200, 30, 1, 80, true, true); + yPos += 35; + Panel.addButtonBackTo(Client.panelRecovery, 201, yPos, 100, 30); + Panel.addCenterTextTo(Client.panelRecovery, 201, yPos, "Submit", 4, true); + Client.chooseSubmitRecoveryButton = Panel.addButtonTo(Client.panelRecovery, 201, yPos, 100, 30); + Panel.addButtonBackTo(Client.panelRecovery, 311, yPos, 100, 30); + Panel.addCenterTextTo(Client.panelRecovery, 311, yPos, "Cancel", 4, true); + Client.chooseCancelRecoveryButton = Panel.addButtonTo(Client.panelRecovery, 311, yPos, 100, 30); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void create_recovery_questions() { + try { + Client.panelRecoveryQuestions = Panel.createPanel(100); + int yPos = 8; + Client.controlRecoveryQuestions = Panel.addCenterTextTo( + Client.panelRecoveryQuestions,256, yPos, "@yel@Please provide 5 security questions in case you lose your password", 1, true); + yPos += 22; + Panel.addCenterTextTo( + Client.panelRecoveryQuestions,256, yPos, "If you ever lose your password, you will need these to prove you own your account.", 1, true); + yPos += 13; + Panel.addCenterTextTo( + Client.panelRecoveryQuestions,256, yPos, "Your answers are encrypted and are ONLY used for password recovery purposes.", 1, true); + yPos += 22; + Panel.addCenterTextTo( + Client.panelRecoveryQuestions,256, yPos, "@ora@IMPORTANT:@whi@ To recover your password you must give the EXACT same answers you", 1, true); + yPos += 13; + Panel.addCenterTextTo( + Client.panelRecoveryQuestions,256, yPos, "give here. If you think you might forget an answer, or someone else could guess the", 1, true); + yPos += 13; + Panel.addCenterTextTo( + Client.panelRecoveryQuestions,256, yPos, "answer, then press the 'different question' button to get a better question.", 1, true); + yPos += 35; + for (int questionIndex = 0; questionIndex < 5; ++questionIndex) { + Panel.addButtonBackTo(Client.panelRecoveryQuestions,170, yPos, 310, 30); + Client.controlRecoveryText[questionIndex] = recoveryQuestions[recoveryIndices[questionIndex]]; + Client.controlRecoveryIns[questionIndex] = Panel.addCenterTextTo( + Client.panelRecoveryQuestions,170, yPos - 7, questionIndex + 1 + ": " + recoveryQuestions[recoveryIndices[questionIndex]], 1, true); + Client.controlAnswerInput[questionIndex] = Panel.addInputTo(Client.panelRecoveryQuestions, 170, yPos + 7, 310, 30, 1, 80, false, true); + Panel.addButtonBackTo(Client.panelRecoveryQuestions,370, yPos, 80, 30); + Panel.addCenterTextTo( + Client.panelRecoveryQuestions,370, yPos - 7, "Different", 1, true); + Panel.addCenterTextTo( + Client.panelRecoveryQuestions,370, yPos + 7, "Question", 1, true); + Client.controlQuestion[questionIndex] = Panel.addButtonTo(Client.panelRecoveryQuestions,370, yPos, 80, 30); + Panel.addButtonBackTo(Client.panelRecoveryQuestions,455, yPos, 80, 30); + Panel.addCenterTextTo( + Client.panelRecoveryQuestions,455, yPos - 7, "Enter own", 1, true); + Panel.addCenterTextTo( + Client.panelRecoveryQuestions,455, yPos + 7, "Question", 1, true); + Client.controlCustomQuestion[questionIndex] = Panel.addButtonTo(Client.panelRecoveryQuestions,455, yPos, 80, 30); + yPos += 35; + } + Panel.setFocus(Client.panelRecoveryQuestions, Client.controlAnswerInput[0]); + yPos += 10; + Panel.addButtonBackTo(Client.panelRecoveryQuestions,256, yPos, 250, 30); + Panel.addCenterTextTo( + Client.panelRecoveryQuestions,256, yPos, "Click here when finished", 4, true); + Client.chooseFinishSetRecoveryButton = Panel.addButtonTo(Client.panelRecoveryQuestions,256, yPos, 250, 30); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void create_contact_details() { + try { + Client.panelContactDetails = Panel.createPanel(100); + int xPos = 256; + int boxWidth = 400; + int yPos = 25; + Client.controlContactDetails = + Panel.addCenterTextTo( + Client.panelContactDetails,256, yPos, "@yel@Please supply your contact details", 5, true); + yPos += 30; + Panel.addCenterTextTo( + Client.panelContactDetails,256, yPos, "We need this information to provide an efficient customer support service ", 1, true); + yPos += 15; + Panel.addCenterTextTo( + Client.panelContactDetails,256, yPos, "and also to work out where to locate future RuneScape servers.", 1, true); + yPos += 25; + Panel.addCenterTextTo( + Client.panelContactDetails,256, yPos, "We know some people are concerned about entering their email address on", 1, true); + yPos += 15; + Panel.addCenterTextTo( + Client.panelContactDetails,255, yPos, "websites, and for this reason we take our users privacy very seriously.", 1, true); + yPos += 15; + Panel.addCenterTextTo( + Client.panelContactDetails,256, yPos, "For our full policy please click the relevant link below this game window", 1, true); + yPos += 40; + Panel.addButtonBackTo(Client.panelContactDetails,xPos, yPos, boxWidth, 30); + Panel.addCenterTextTo( + Client.panelContactDetails,xPos, yPos - 7, "Full name", 1, true); + Client.fullNameInput = Panel.addInputTo(Client.panelContactDetails,xPos, yPos + 7, boxWidth, 30, 1, 80, false, true); + yPos += 35; + Panel.addButtonBackTo(Client.panelContactDetails,xPos, yPos, boxWidth, 30); + Panel.addCenterTextTo( + Client.panelContactDetails,xPos, yPos - 7, "Postcode/Zipcode", 1, true); + Client.zipCodeInput = Panel.addInputTo(Client.panelContactDetails,xPos, yPos + 7, boxWidth, 30, 1, 80, false, true); + yPos += 35; + Panel.addButtonBackTo(Client.panelContactDetails,xPos, yPos, boxWidth, 30); + Panel.addCenterTextTo( + Client.panelContactDetails,xPos, yPos - 7, "Country", 1, true); + Client.countryInput = Panel.addInputTo(Client.panelContactDetails,xPos, yPos + 7, boxWidth, 30, 1, 80, false, true); + yPos += 35; + Panel.addButtonBackTo(Client.panelContactDetails,xPos, yPos, boxWidth, 30); + Panel.addCenterTextTo( + Client.panelContactDetails,xPos, yPos - 7, "Email address", 1, true); + Client.emailInput = Panel.addInputTo(Client.panelContactDetails,xPos, yPos + 7, boxWidth, 30, 1, 80, false, true); + yPos += 35; + Panel.addButtonBackTo(Client.panelContactDetails,xPos, yPos, 100, 30); + Panel.addCenterTextTo( + Client.panelContactDetails,xPos, yPos, "Submit", 4, true); + Client.chooseSubmitContactDetailsButton = Panel.addButtonTo(Client.panelContactDetails,xPos, yPos, 100, 30); + Panel.setFocus(Client.panelContactDetails,Client.fullNameInput); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static int options_security_hook(int xPos, int yPos, int mouseX, int mouseY) { + short uiWidth = 196; + int currYPos = yPos; + if (Settings.SHOW_ACCOUNT_SECURITY_SETTINGS.get(Settings.currentProfile)) { //Security settings -> Change password, recovery questions, Change contact details from RSC175 + //currYPos += 5; //this is not needed since was generally patched to line up options panel correctly + Renderer.drawString("Security settings", xPos, currYPos, 1, 0); + currYPos += 15; + int textColor = 0xFFFFFF; + if (mouseX > xPos + && mouseX < xPos + uiWidth + && mouseY > currYPos - 12 + && mouseY < currYPos + 4) { + textColor = 0xFFFF00; // yellow + } + Renderer.drawString("Change password", xPos, currYPos, 1, textColor); + currYPos += 15; + textColor = 0xFFFFFF; + if (mouseX > xPos + && mouseX < xPos + uiWidth + && mouseY > currYPos - 12 + && mouseY < currYPos + 4) { + textColor = 0xFFFF00; // yellow + } + Renderer.drawString("Change recovery questions", xPos, currYPos, 1, textColor); + currYPos += 15; + textColor = 0xFFFFFF; + if (mouseX > xPos + && mouseX < xPos + uiWidth + && mouseY > currYPos - 12 + && mouseY < currYPos + 4) { + textColor = 0xFFFF00; // yellow + } + Renderer.drawString("Change contact details", xPos, currYPos, 1, textColor); + currYPos += 15; + currYPos += 5; + } + return currYPos - yPos; + } + + public static int options_security_click_hook( + int xPos, int yPos, int mouseX, int mouseY, int mouseButtonClick) { + short uiWidth = 196; + int currYPos = yPos; + if (Settings.SHOW_ACCOUNT_SECURITY_SETTINGS.get(Settings.currentProfile)) { //Security settings -> Change password, recovery questions, Change contact details from RSC175 + currYPos += 15; + currYPos += 20; + if (mouseX > xPos + && mouseX < xPos + uiWidth + && mouseY > currYPos - 12 + && mouseY < currYPos + 4 + && mouseButtonClick == 1) { + AccountManagement.panelPasswordChangeMode = 6; + Client.modal_enteredText = ""; + Client.modal_text = ""; + } + currYPos += 15; + if (mouseX > xPos + && mouseX < xPos + uiWidth + && mouseY > currYPos - 12 + && mouseY < currYPos + 4 + && mouseButtonClick == 1) { + StreamUtil.newPacket(197); // this opcode is "Change recovery questions" as found in RSC175 + StreamUtil.sendPacket(); + } + currYPos += 15; + if (mouseX > xPos + && mouseX < xPos + uiWidth + && mouseY > currYPos - 12 + && mouseY < currYPos + 4 + && mouseButtonClick == 1) { + StreamUtil.newPacket(247); // this opcode is "Change contact details" as found in RSC175 + StreamUtil.sendPacket(); + } + currYPos += 15; + currYPos += 35; + } + return currYPos - yPos; + } + + public static void draw_change_pass_hook(int mouseX, int mouseY, int mouseButtonClick) { + if (mouseButtonClick != 0) { + mouseButtonClick = 0; + if (mouseX < Renderer.width / 2 - 150 || mouseY < Renderer.height / 2 - 32 + || mouseX > Renderer.width / 2 + 150 || mouseY > Renderer.height / 2 + 28) { + panelPasswordChangeMode = 0; + return; + } + } + + int yPos = Renderer.height / 2 - 32; + Renderer.drawBox(Renderer.width / 2 - 150, yPos, 300, 60, 0); + Renderer.drawBoxBorder(Renderer.width / 2 - 150, yPos, 300, 60, 0xFFFFFF); + yPos += 22; + String displayString; + int i; + if (panelPasswordChangeMode == 6) { + Renderer.drawStringCenter("Please enter your current password", Renderer.width / 2, yPos, 4, 0xFFFFFF); + yPos += 25; + displayString = "*"; + + for (i = 0; i < Client.modal_enteredText.length(); ++i) { + displayString = "X" + displayString; + } + + Renderer.drawStringCenter(displayString, Renderer.width / 2, yPos, 4, 0xFFFFFF); + if (Client.modal_text.length() > 0) { + oldPassword = Client.modal_text; + Client.modal_enteredText = ""; + Client.modal_text = ""; + panelPasswordChangeMode = 1; + return; + } + } else if (panelPasswordChangeMode == 1) { + Renderer.drawStringCenter("Please enter your new password", Renderer.width / 2, yPos, 4, 0xFFFFFF); + yPos += 25; + displayString = "*"; + + for (i = 0; i < Client.modal_enteredText.length(); ++i) { + displayString = "X" + displayString; + } + + Renderer.drawStringCenter(displayString, Renderer.width / 2, yPos, 4, 0xFFFFFF); + if (Client.modal_text.length() > 0) { + newPassword = Client.modal_text; + Client.modal_enteredText = ""; + Client.modal_text = ""; + panelPasswordChangeMode = 2; + if (newPassword.length() < 5) { + panelPasswordChangeMode = 5; + return; + } + if (newPassword.trim().equalsIgnoreCase(Panel.getControlText(Client.panelLogin, Client.loginUserInput).trim())) { + panelPasswordChangeMode = 7; + return; + } + return; + } + } else if (panelPasswordChangeMode == 2) { + Renderer.drawStringCenter("Enter password again to confirm", Renderer.width / 2, yPos, 4, 0xFFFFFF); + yPos += 25; + displayString = "*"; + + for (i = 0; i < Client.modal_enteredText.length(); ++i) { + displayString = "X" + displayString; + } + + Renderer.drawStringCenter(displayString, Renderer.width / 2, yPos, 4, 0xFFFFFF); + if (Client.modal_text.length() > 0) { + if (Client.modal_text.equalsIgnoreCase(newPassword)) { + panelPasswordChangeMode = 4; + sendPassChange(oldPassword, newPassword); + return; + } + + panelPasswordChangeMode = 3; + return; + } + } else { + if (panelPasswordChangeMode == 3) { + Renderer.drawStringCenter("Passwords do not match!", Renderer.width / 2, yPos, 4, 0xFFFFFF); + yPos += 25; + Renderer.drawStringCenter("Press any key to close", Renderer.width / 2, yPos, 4, 0xFFFFFF); + return; + } + + if (panelPasswordChangeMode == 4) { + Renderer.drawStringCenter("Ok, your request has been sent", Renderer.width / 2, yPos, 4, 0xFFFFFF); + yPos += 25; + Renderer.drawStringCenter("Press any key to close", Renderer.width / 2, yPos, 4, 0xFFFFFF); + return; + } + + if (panelPasswordChangeMode == 5) { + Renderer.drawStringCenter("Password must be at", Renderer.width / 2, yPos, 4, 0xFFFFFF); + yPos += 25; + Renderer.drawStringCenter("least 5 letters long", Renderer.width / 2, yPos, 4, 0xFFFFFF); + return; + } + + if (panelPasswordChangeMode == 7) { + Renderer.drawStringCenter("Your password must not be", Renderer.width / 2, yPos, 4, 0xFFFFFF); + yPos += 25; + Renderer.drawStringCenter("the same as your username", Renderer.width / 2, yPos, 4, 0xFFFFFF); + return; + } + } + } + + public static void processForgotPassword() { + if (Settings.SHOW_ACCOUNT_SECURITY_SETTINGS.get(Settings.currentProfile)) { + if (Panel.isSelected(Client.panelLogin, Client.loginLostPasswordButton)) { + String user = Panel.getControlText(Client.panelLogin, Client.loginUserInput); + if (user.trim().length() == 0) { + Client.setResponseMessage("", "You must enter your username to recover your password"); + return; + } + + Client.username_login = user; + forgotPass(user); + } + } + + } // § SECTION hook key and mouse consumption § + /* + * Intercept the part of checking if other panels should consume key event in game, + * we may have logged in flag distinct than 0, but is already checked after this hook, + * so 0 is returned to continue on that flow + * Password change states 3, 4, 5 any 7 are final, so they are changed to 0 and a non-zero + * response is given to break out of trying to consume key on other panels + * For any other non-zero password change state, non-zero response given + */ + public static int ingame_keyhandler_hook(int loggedIn, int key) { + if (loggedIn != 1) return 0; + + if (Client.showRecoveryQuestions) { + if (customQuestionEntry != -1) return 1; + Panel.handleKey(Client.panelRecoveryQuestions, key); + return 1; + } + + if (Client.showContactDetails) { + Panel.handleKey(Client.panelContactDetails, key); + return 1; + } + + if (panelPasswordChangeMode == 3 || panelPasswordChangeMode == 4 || panelPasswordChangeMode == 5 || panelPasswordChangeMode == 7) { + panelPasswordChangeMode = 0; + return 1; + } + + return panelPasswordChangeMode > 0 ? 1 : 0; + } + public static void account_panels_key_hook(int a, int key) { try { if (Client.login_screen == Client.SCREEN_REGISTER_NEW_ACCOUNT) { Panel.handleKey(Client.panelRegister, key); } + + if (Client.login_screen == Client.SCREEN_PASSWORD_RECOVERY) { + Panel.handleKey(Client.panelRecovery, key); + } } catch (Exception e) { e.printStackTrace(); } } - public static void account_panels_input_hook(int n1, int mouseY, int a, int n3, int mouseX) { + public static void account_panels_input_hook(int n1, int mouseY, int n3, int mouseX) { try { if (Client.login_screen == Client.SCREEN_REGISTER_NEW_ACCOUNT) { Panel.handleMouse(Client.panelRegister, n1, mouseY, n3, mouseX); @@ -328,6 +996,13 @@ public static void account_panels_input_hook(int n1, int mouseY, int a, int n3, "@yel@Your password must be at least 5 letters long"); return; } + + if (Panel.getControlText(Client.panelRegister, Client.choosePasswordInput).trim().equalsIgnoreCase(Panel.getControlText(Client.panelRegister, Client.chooseUserInput).trim())) { + Panel.setControlText( + Client.panelRegister, + Client.controlRegister, "@yel@Your password must not be the same as your username!"); + return; + } if (Panel.isToggle(Client.panelRegister, Client.acceptTermsCheckbox) == 0) { Panel.setControlText( @@ -356,8 +1031,155 @@ public static void account_panels_input_hook(int n1, int mouseY, int a, int n3, return; } } + + if (Client.login_screen == Client.SCREEN_PASSWORD_RECOVERY) { + Panel.handleMouse(Client.panelRecovery, n1, mouseY, n3, mouseX); + if (Panel.isSelected(Client.panelRecovery, Client.chooseSubmitRecoveryButton)) { + String newPass = Panel.getControlText(Client.panelRecovery, Client.recoverNewPassInput); + String confirmPass = Panel.getControlText(Client.panelRecovery, Client.recoverConfirmPassInput); + if (!newPass.equalsIgnoreCase(confirmPass)) { + Client.setResponseMessage("", "@yel@The two new passwords entered are not the same as each other!"); + return; + } + + if (newPass.length() < 5) { + Client.setResponseMessage("", "@yel@Your new password must be at least 5 letters long"); + return; + } + + recover(); + } + + if (Panel.isSelected(Client.panelRecovery, Client.chooseCancelRecoveryButton)) { + Client.login_screen = Client.SCREEN_CLICK_TO_LOGIN; + } + } } catch (Exception e) { e.printStackTrace(); } } + + public static void recovery_questions_input(int n1, int mouseY, int n3, int mouseX) { + try { + if (customQuestionEntry != -1) { + if (Client.pm_text.length() > 0) { + Client.controlRecoveryText[customQuestionEntry] = Client.pm_text; + Panel.setControlText( + Client.panelRecoveryQuestions,Client.controlRecoveryIns[customQuestionEntry], customQuestionEntry + 1 + ": " + Client.controlRecoveryText[customQuestionEntry]); + Panel.setControlText( + Client.panelRecoveryQuestions,Client.controlAnswerInput[customQuestionEntry], ""); + customQuestionEntry = -1; + } + } else { + Panel.handleMouse(Client.panelRecoveryQuestions, n1, mouseY, n3, mouseX); + + int cursorIndex; + for (int questionIndex = 0; questionIndex < 5; ++questionIndex) { + if (Panel.isSelected(Client.panelRecoveryQuestions, Client.controlQuestion[questionIndex])) { + boolean hasDistinctQuestions = false; + + while (!hasDistinctQuestions) { + recoveryIndices[questionIndex] = (recoveryIndices[questionIndex] + 1) % recoveryQuestions.length; + hasDistinctQuestions = true; + + for (cursorIndex = 0; cursorIndex < 5; ++cursorIndex) { + if (cursorIndex != questionIndex && recoveryIndices[cursorIndex] == recoveryIndices[questionIndex]) { + hasDistinctQuestions = false; + } + } + } + + Client.controlRecoveryText[questionIndex] = recoveryQuestions[recoveryIndices[questionIndex]]; + Panel.setControlText(Client.panelRecoveryQuestions,Client.controlRecoveryIns[questionIndex], questionIndex + 1 + ": " + recoveryQuestions[recoveryIndices[questionIndex]]); + Panel.setControlText(Client.panelRecoveryQuestions,Client.controlAnswerInput[questionIndex], ""); + } + } + + for (int i = 0; i < 5; ++i) { + if (Panel.isSelected(Client.panelRecoveryQuestions,Client.controlCustomQuestion[i])) { + customQuestionEntry = i; + Client.pm_enteredText = ""; + Client.pm_text = ""; + } + } + + if (Panel.isSelected(Client.panelRecoveryQuestions,Client.chooseFinishSetRecoveryButton)) { + cursorIndex = 0; + + while (true) { + if (cursorIndex >= 5) { + for (int outerIdx = 0; outerIdx < 5; ++outerIdx) { + String checkedAnswer = Panel.getControlText(Client.panelRecoveryQuestions, Client.controlAnswerInput[outerIdx]); + + for (int innerIdx = 0; innerIdx < outerIdx; ++innerIdx) { + String questionAnswer = Panel.getControlText(Client.panelRecoveryQuestions, Client.controlAnswerInput[innerIdx]); + if (checkedAnswer.equalsIgnoreCase(questionAnswer)) { + Panel.setControlText(Client.panelRecoveryQuestions,Client.controlRecoveryQuestions, "@yel@Each question must have a different answer"); + return; + } + } + } + + sendRecoveryQuestions(); //this generates & sends RSC175 opcode 208 + + for (int questionIndex = 0; questionIndex < 5; ++questionIndex) { + recoveryIndices[questionIndex] = questionIndex; + Client.controlRecoveryText[questionIndex] = recoveryQuestions[recoveryIndices[questionIndex]]; + Panel.setControlText(Client.panelRecoveryQuestions,Client.controlAnswerInput[questionIndex], ""); + Panel.setControlText(Client.panelRecoveryQuestions,Client.controlRecoveryIns[questionIndex], questionIndex + 1 + ": " + Client.controlRecoveryText[questionIndex]); + } + + Client.clearScreen(); + Client.showRecoveryQuestions = false; + break; + } + + String chkAnswer = Panel.getControlText(Client.panelRecoveryQuestions, Client.controlAnswerInput[cursorIndex]); + if (chkAnswer == null || chkAnswer.length() < 3) { + Panel.setControlText(Client.panelRecoveryQuestions,Client.controlRecoveryQuestions, "@yel@Please provide a longer answer to question: " + (cursorIndex + 1)); + return; + } + + ++cursorIndex; + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static void contact_details_input(int n1, int mouseY, int n3, int mouseX) { + try { + Panel.handleMouse(Client.panelContactDetails, n1, mouseY, n3, mouseX); + if (Panel.isSelected(Client.panelContactDetails, Client.fullNameInput)) { + Panel.setFocus(Client.panelContactDetails, Client.zipCodeInput); + } + if (Panel.isSelected(Client.panelContactDetails, Client.zipCodeInput)) { + Panel.setFocus(Client.panelContactDetails, Client.countryInput); + } + if (Panel.isSelected(Client.panelContactDetails, Client.countryInput)) { + Panel.setFocus(Client.panelContactDetails, Client.emailInput); + } + if (Panel.isSelected(Client.panelContactDetails, Client.emailInput)) { + Panel.setFocus(Client.panelContactDetails, Client.fullNameInput); + } + if (!Panel.isSelected(Client.panelContactDetails, Client.chooseSubmitContactDetailsButton)) { + return; + } + String name = Panel.getControlText(Client.panelContactDetails, Client.fullNameInput); + String zipcode = Panel.getControlText(Client.panelContactDetails, Client.zipCodeInput); + String country = Panel.getControlText(Client.panelContactDetails, Client.countryInput); + String email = Panel.getControlText(Client.panelContactDetails, Client.emailInput); + if (name != null && name.length() != 0 && zipcode != null && zipcode.length() != 0 && country != null && country.length() != 0 && email != null && email.length() != 0) { + sendContactDetails(name, zipcode, country, email); //this generates & sends RSC175 opcode 253 + Client.clearScreen(); + Client.showContactDetails = false; + return; + } + Panel.setControlText(Client.panelContactDetails, Client.controlContactDetails, "@yel@Please fill in all the requested details"); + } catch (Exception e) { + e.printStackTrace(); + } + } } diff --git a/src/Game/Client.java b/src/Game/Client.java index 98211abc..7f02179c 100644 --- a/src/Game/Client.java +++ b/src/Game/Client.java @@ -19,18 +19,6 @@ package Game; import static Replay.game.constants.Game.itemActionMap; - -import Client.JClassPatcher; -import Client.JConfig; -import Client.KeybindSet; -import Client.Launcher; -import Client.Logger; -import Client.NotificationsHandler; -import Client.NotificationsHandler.NotifType; -import Client.Settings; -import Client.Speedrun; -import Client.TwitchIRC; -import Replay.game.constants.Game.ItemAction; import java.applet.Applet; import java.io.BufferedReader; import java.io.BufferedWriter; @@ -52,6 +40,17 @@ import java.util.List; import java.util.Map; import javax.swing.JOptionPane; +import Client.JClassPatcher; +import Client.JConfig; +import Client.KeybindSet; +import Client.Launcher; +import Client.Logger; +import Client.NotificationsHandler; +import Client.NotificationsHandler.NotifType; +import Client.Settings; +import Client.Speedrun; +import Client.TwitchIRC; +import Replay.game.constants.Game.ItemAction; /** * This class prepares the client for login, handles chat messages, and performs player related @@ -185,6 +184,8 @@ public class Client { public static String pm_text; public static String pm_enteredText; public static String lastpm_username = null; + public static String modal_text; + public static String modal_enteredText; public static String mouseText = ""; @@ -310,12 +311,20 @@ public class Client { public static int login_delay; public static String server_address; public static int serverjag_port; + public static int session_id; + public static boolean failedRecovery = false; public static Object panelWelcome; public static Object panelLogin; public static Object panelRegister; + public static Object panelRecovery; + public static Object panelRecoveryQuestions; + public static Object panelContactDetails; + public static int controlLoginTop; + public static int controlLoginBottom; public static int loginUserInput; public static int loginPassInput; + public static int loginLostPasswordButton; public static int registerButton; public static int controlRegister; public static int chooseUserInput; @@ -324,6 +333,31 @@ public class Client { public static int acceptTermsCheckbox; public static int chooseSubmitRegisterButton; public static int chooseCancelRegisterButton; + public static int controlRecoveryTop; + public static int controlRecoveryBottom; + public static int controlRecoveryQuestion[] = new int[5]; + public static int controlRecoveryInput[] = new int[5]; + public static int recoverOldPassInput; + public static int recoverNewPassInput; + public static int recoverConfirmPassInput; + public static int chooseSubmitRecoveryButton; + public static int chooseCancelRecoveryButton; + public static int controlRecoveryQuestions; + public static String controlRecoveryText[] = new String[5]; + public static int controlRecoveryIns[] = new int[5]; + public static int controlAnswerInput[] = new int[5]; + public static int controlQuestion[] = new int[5]; + public static int controlCustomQuestion[] = new int[5]; + public static int chooseFinishSetRecoveryButton; + public static int controlContactDetails; + public static int fullNameInput; + public static int zipCodeInput; + public static int countryInput; + public static int emailInput; + public static int chooseSubmitContactDetailsButton; + public static boolean showAppearanceChange; + public static boolean showRecoveryQuestions; + public static boolean showContactDetails; /** * Iterates through {@link #strings} array and checks if various conditions are met. Used for @@ -516,6 +550,16 @@ public static boolean skipToLogin() { return skipToLogin || Settings.START_LOGINSCREEN.get(Settings.currentProfile); } + /** + * Reference for JClassPatcher and any other required. Indicates if should show account and + * security settings + * + * @return + */ + public static boolean showSecuritySettings() { + return Settings.SHOW_ACCOUNT_SECURITY_SETTINGS.get(Settings.currentProfile); + } + /** * Method that gets called when starting game, normally would go to Welcome screen but if no world * configured (using RSC+ for replay mode) skip directly to login for replays @@ -844,7 +888,7 @@ public static void error_game_hook(String s) { */ public static void keep_login_info_hook() { Client.login_screen = SCREEN_USERNAME_PASSWORD_LOGIN; - setLoginMessage("Please enter your username and password", ""); + setResponseMessage("Please enter your username and password", ""); Panel.setFocus(Client.panelLogin, Client.loginUserInput); } @@ -895,9 +939,62 @@ public static void isLoadingHook(boolean isLoading) { } } } + + /** + * General extra check for new received opcodes + * Send false if has finished processing + * @param opcode - Packet opcode + * @param psize - Packet size + * @return false to indicate no more processing is needed + */ + public static boolean newOpcodeReceivedHook(int opcode, int psize) { + boolean needsProcess = true; + + if (AccountManagement.processPacket(opcode, psize)) { + needsProcess = false; + } + + return needsProcess; + } + + /** + * General in game input hook for new added elements + * return true to indicate continue checking conditions in the original gameInput() method + */ + public static boolean gameInputHook(int n1, int mouseY, int n3, int mouseX) { + boolean continueFlow = true; + + if (Client.showRecoveryQuestions) { + AccountManagement.recovery_questions_input(n1, mouseY, n3, mouseX); + continueFlow = false; + } else if (Client.showContactDetails) { + AccountManagement.contact_details_input(n1, mouseY, n3, mouseX); + continueFlow = false; + } + + return continueFlow; + } + + public static void loginOtherButtonCheckHook() { + AccountManagement.processForgotPassword(); + } + + /** + * General drawGame hook for new added panels + * return true to indicate continue checking conditions in the original drawGame() method + */ + public static boolean drawGameHook() { + boolean continueFlow = true; + + if (AccountManagement.pending_render()) { + continueFlow = false; + } + + return continueFlow; + } public static void resetLoginMessage() { - setLoginMessage("Please enter your username and password", ""); + setResponseMessage("Please enter your username and password", ""); } /** Stores the user's display name in {@link #player_name}. */ @@ -1261,16 +1358,35 @@ public static synchronized void displayMessage(String message, int chat_type) { } /** - * Sets the client text above the login information on the login screen. + * Sets the client text response status. + * In the login screen this is the information shown above the login controls + * In the register and recover screens is the replacement of control text in the + * respective panels * * @param line1 the bottom line of text - * @param line2 the top line of text + * @param line2 the top part of text */ - public static void setLoginMessage(String line1, String line2) { + public static void setResponseMessage(String line1, String line2) { if (Reflection.setLoginText == null) return; try { - Reflection.setLoginText.invoke(Client.instance, (byte) -49, line2, line1); + if (Client.login_screen == Client.SCREEN_USERNAME_PASSWORD_LOGIN) { + if (line1 == null || line1.length() == 0) { + Panel.setControlText( + Client.panelLogin, Client.controlLoginTop, ""); + Panel.setControlText( + Client.panelLogin, Client.controlLoginBottom, line2); + } else { + Reflection.setLoginText.invoke(Client.instance, (byte) -49, line2, line1); + } + } else if (Client.login_screen == Client.SCREEN_PASSWORD_RECOVERY) { + Panel.setControlText( + Client.panelRecovery, Client.controlRecoveryTop, line2); + Panel.setControlText( + Client.panelRecovery, Client.controlRecoveryBottom, line1); + } else if (Client.login_screen == Client.SCREEN_REGISTER_NEW_ACCOUNT) { + Panel.setControlText(Client.panelRegister, Client.controlRegister, line2 + " " + line1); + } } catch (Exception e) { } } @@ -1373,6 +1489,24 @@ public static void clearScreen() { } catch (Exception e) { } } + + public static void setInterlace(boolean value) { + if (Reflection.interlace == null) return; + + try { + Reflection.interlace.set(Renderer.instance, value); + } catch (Exception e) { + } + } + + public static void drawGraphics() { + if (Reflection.drawGraphics == null) return; + + try { + Reflection.drawGraphics.invoke(Renderer.instance, Renderer.graphicsInstance, 0, 256, 0); + } catch (Exception e) { + } + } public static void preGameDisplay() { if (Reflection.preGameDisplay == null) return; @@ -1479,6 +1613,12 @@ public static void retroFPSHook(Object surfaceInstance) { } } } + + public static void initCreateExtraPanelsHook() { + AccountManagement.create_account_recovery(); + AccountManagement.create_recovery_questions(); + AccountManagement.create_contact_details(); + } public static int attack_menu_hook(int cmpVar) { if (Settings.ATTACK_ALWAYS_LEFT_CLICK.get(Settings.currentProfile) @@ -2337,7 +2477,10 @@ public static boolean isInterfaceOpen() { || show_duelconfirm || show_report != 0 || show_friends != 0 - || show_sleeping; + || show_sleeping + || showAppearanceChange + || showRecoveryQuestions + || showContactDetails; } /** @@ -2397,7 +2540,7 @@ class LoginMessageHandler implements Runnable { public void run() { try { Thread.sleep(5); - Client.setLoginMessage(Client.loginMessageBottom, Client.loginMessageTop); + Client.setResponseMessage(Client.loginMessageBottom, Client.loginMessageTop); } catch (InterruptedException e) { Logger.Error( "The login message thread was interrupted unexpectedly! Perhaps the game crashed or was killed?"); diff --git a/src/Game/Panel.java b/src/Game/Panel.java index d4dbb943..bbe80b5e 100644 --- a/src/Game/Panel.java +++ b/src/Game/Panel.java @@ -31,6 +31,18 @@ public class Panel { public static int control_text_size; public static boolean control_use_alt_color; public static String control_text; + + public static Object createPanel(int maxElements) { + if (Reflection.panel == null) return null; + + Object result = null; + try { + result = Reflection.panel.newInstance(Renderer.instance, maxElements); + } catch (Exception e) { + } + + return result; + } public static void drawPanel(Object panelSource) { if (Reflection.drawPanel == null) return; @@ -173,7 +185,6 @@ public static int addButtonTo(Object panelSource, int xPos, int yPos, int width, public static int addInputTo( Object panelSource, - int n, int xPos, int yPos, int width, @@ -190,7 +201,7 @@ public static int addInputTo( (int) Reflection.addInput.invoke( panelSource, - n - 3845, + 0, capacity, width, isBackground, diff --git a/src/Game/Reflection.java b/src/Game/Reflection.java index 68487fe6..5a340e24 100644 --- a/src/Game/Reflection.java +++ b/src/Game/Reflection.java @@ -18,20 +18,21 @@ */ package Game; -import Client.JClassLoader; -import Client.Launcher; -import Client.Logger; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; +import Client.JClassLoader; +import Client.Launcher; +import Client.Logger; /** Loads and sets fields and methods found in the vanilla RSC jar's classes */ public class Reflection { public static Field gameReference = null; + public static Constructor panel = null; public static Constructor stream = null; public static Constructor buffer = null; @@ -81,9 +82,12 @@ public class Reflection { public static Method itemClick = null; public static Method menuGen = null; public static Method drawBox = null; + public static Method drawBoxBorder = null; public static Method drawString = null; + public static Method drawStringCenter = null; public static Method drawLineHoriz = null; public static Method drawLineVert = null; + public static Method drawSprite = null; public static Method newPacket = null; public static Method putByte = null; @@ -99,6 +103,7 @@ public class Reflection { public static Method flushPacket = null; public static Method initIsaac = null; public static Method readResponse = null; + public static Method readBytes = null; public static Field maxRetriesField = null; public static Field bufferField = null; public static Field bufferOffset = null; @@ -108,7 +113,9 @@ public class Reflection { public static Method getNpc = null; public static Method getPlayer = null; + public static Field interlace = null; public static Method clearScreen = null; + public static Method drawGraphics = null; public static Method preGameDisplay = null; public static Method resetTimings = null; public static Method formatText = null; @@ -130,6 +137,7 @@ public class Reflection { public static Method handleKey = null; // Constructor descriptions + private static final String PANEL = "qa(ua,int)"; private static final String STREAM = "da(java.net.Socket,e) throws java.io.IOException"; private static final String BUFFER = "tb(int)"; @@ -151,10 +159,14 @@ public class Reflection { private static final String MENUGEN = "final void wb.a(int,int,boolean,java.lang.String,java.lang.String)"; private static final String DRAWBOX = "final void ua.a(int,byte,int,int,int,int)"; + private static final String DRAWBOXBORDER = "final void ua.e(int,int,int,int,int,int)"; private static final String DRAWSTRING = "final void ua.a(java.lang.String,int,int,int,boolean,int)"; + private static final String DRAWSTRINGCENTER = + "final void ua.a(int,java.lang.String,int,int,int,int)"; private static final String DRAWLINEHORIZ = "final void ua.b(int,int,int,int,byte)"; private static final String DRAWLINEVERT = "final void ua.b(int,int,int,int,int)"; + private static final String DRAWSPRITE = "final void ua.b(int,int,int,int)"; private static final String NEWPACKET = "final void b.b(int,int)"; private static final String PUTBYTE = "final void tb.c(int,int)"; @@ -171,6 +183,7 @@ public class Reflection { private static final String FLUSHPACKET = "final void b.a(int) throws java.io.IOException"; private static final String INIT_ISAAC = "final void b.a(byte,int[])"; private static final String READ_RESPONSE = "final int da.b(boolean) throws java.io.IOException"; + private static final String READ_BYTES = "final void da.a(byte[],int,int,int) throws java.io.IOException"; private static final String CREATE_SOCKET = "private final java.net.Socket client.a(int,int,java.lang.String) throws java.io.IOException"; @@ -178,6 +191,7 @@ public class Reflection { private static final String GETPLAYER = "private final ta client.d(int,int)"; private static final String CLEARSCREEN = "final void ua.a(boolean)"; + private static final String DRAWGRAPHICS = "final void ua.a(java.awt.Graphics,int,int,int)"; private static final String PREGAME_DISPLAY = "private final void client.k(int)"; private static final String RESET_TIMINGS = "final void e.c(int)"; @@ -289,7 +303,7 @@ public static void Load() { leftMethods.addAll( Arrays.asList( - NEWPACKET, LOSECONNECTION, SENDPACKET, FLUSHPACKET, INIT_ISAAC, READ_RESPONSE)); + NEWPACKET, LOSECONNECTION, SENDPACKET, FLUSHPACKET, INIT_ISAAC, READ_RESPONSE, READ_BYTES)); while (c != null && leftMethods.size() > 0) { methods = c.getDeclaredMethods(); for (Method method : methods) { @@ -331,6 +345,13 @@ public static void Load() { leftMethods.remove(READ_RESPONSE); continue; } + if (leftMethods.contains(READ_BYTES) + && method.toGenericString().equals(READ_BYTES)) { + readBytes = method; + Logger.Info("Found readBytes"); + leftMethods.remove(READ_BYTES); + continue; + } } c = c.getSuperclass(); } @@ -480,6 +501,7 @@ public static void Load() { // Renderer c = classLoader.loadClass("ua"); + interlace = c.getDeclaredField("i"); methods = c.getDeclaredMethods(); for (Method method : methods) { if (method.toGenericString().equals(SETGAMEBOUNDS)) { @@ -490,14 +512,26 @@ public static void Load() { clearScreen = method; Logger.Info("Found clearScreen"); } + if (method.toGenericString().equals(DRAWGRAPHICS)) { + drawGraphics = method; + Logger.Info("Found drawGraphics"); + } if (method.toGenericString().equals(DRAWBOX)) { drawBox = method; Logger.Info("Found drawBox"); } + if (method.toGenericString().equals(DRAWBOXBORDER)) { + drawBoxBorder = method; + Logger.Info("Found drawBoxBorder"); + } if (method.toGenericString().equals(DRAWSTRING)) { drawString = method; Logger.Info("Found drawString"); } + if (method.toGenericString().equals(DRAWSTRINGCENTER)) { + drawStringCenter = method; + Logger.Info("Found drawStringCenter"); + } if (method.toGenericString().equals(DRAWLINEHORIZ)) { drawLineHoriz = method; Logger.Info("Found drawLineHoriz"); @@ -506,6 +540,10 @@ public static void Load() { drawLineVert = method; Logger.Info("Found drawLineVert"); } + if (method.toGenericString().equals(DRAWSPRITE)) { + drawSprite = method; + Logger.Info("Found drawSprite"); + } } // Character @@ -605,6 +643,13 @@ public static void Load() { Logger.Info("Found handleKey"); } } + constructors = c.getDeclaredConstructors(); + for (Constructor constructor : constructors) { + if (constructor.toGenericString().equals(PANEL)) { + panel = constructor; + Logger.Info("Found panel"); + } + } c = classLoader.loadClass("wb"); methods = c.getDeclaredMethods(); @@ -655,9 +700,12 @@ public static void Load() { if (itemClick != null) itemClick.setAccessible(true); if (menuGen != null) menuGen.setAccessible(true); if (drawBox != null) drawBox.setAccessible(true); + if (drawBoxBorder != null) drawBoxBorder.setAccessible(true); if (drawString != null) drawString.setAccessible(true); + if (drawStringCenter != null) drawStringCenter.setAccessible(true); if (drawLineHoriz != null) drawLineHoriz.setAccessible(true); if (drawLineVert != null) drawLineVert.setAccessible(true); + if (drawSprite != null) drawSprite.setAccessible(true); if (stream != null) stream.setAccessible(true); if (newPacket != null) newPacket.setAccessible(true); if (putByte != null) putByte.setAccessible(true); @@ -673,6 +721,7 @@ public static void Load() { if (flushPacket != null) flushPacket.setAccessible(true); if (initIsaac != null) initIsaac.setAccessible(true); if (readResponse != null) readResponse.setAccessible(true); + if (readBytes != null) readBytes.setAccessible(true); if (bufferField != null) bufferField.setAccessible(true); if (bufferOffset != null) bufferOffset.setAccessible(true); if (bufferByteArray != null) bufferByteArray.setAccessible(true); @@ -681,11 +730,14 @@ public static void Load() { if (buffer != null) buffer.setAccessible(true); if (getNpc != null) getNpc.setAccessible(true); if (getPlayer != null) getPlayer.setAccessible(true); + if (interlace != null) interlace.setAccessible(true); if (clearScreen != null) clearScreen.setAccessible(true); + if (drawGraphics != null) drawGraphics.setAccessible(true); if (preGameDisplay != null) preGameDisplay.setAccessible(true); if (resetTimings != null) resetTimings.setAccessible(true); if (formatText != null) formatText.setAccessible(true); if (putRandom != null) putRandom.setAccessible(true); + if (panel != null) panel.setAccessible(true); if (addButtonBack != null) addButtonBack.setAccessible(true); if (addCenterText != null) addCenterText.setAccessible(true); if (addButton != null) addButton.setAccessible(true); diff --git a/src/Game/Renderer.java b/src/Game/Renderer.java index 8c99b726..a263f4a2 100644 --- a/src/Game/Renderer.java +++ b/src/Game/Renderer.java @@ -18,12 +18,6 @@ */ package Game; -import Client.Launcher; -import Client.Logger; -import Client.NotificationsHandler; -import Client.NotificationsHandler.NotifType; -import Client.Settings; -import Client.Util; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Dimension; @@ -50,16 +44,24 @@ import java.util.Iterator; import java.util.List; import javax.imageio.ImageIO; +import Client.Launcher; +import Client.Logger; +import Client.NotificationsHandler; +import Client.NotificationsHandler.NotifType; +import Client.Settings; +import Client.Util; /** Handles rendering overlays and client adjustments based on window size */ public class Renderer { public static Object instance = null; + public static Graphics graphicsInstance = null; public static int width; public static int height; public static int height_client; public static int[] pixels; + public static int sprite_media; public static int fps; public static float alpha_time; @@ -1015,7 +1017,8 @@ public static void present(Graphics g, Image image) { // TODO: This will need to be adjusted when the login screen is resizable Rectangle bounds; if (Client.login_screen == Client.SCREEN_USERNAME_PASSWORD_LOGIN) { - bounds = new Rectangle(512 - 148, 346 - 36, 48, 16); + bounds = new Rectangle(512 - 100, 216, 48, 16); + //bounds = new Rectangle(512 - 148, 346 - 36, 48, 16); } else if (Client.login_screen == Client.SCREEN_CLICK_TO_LOGIN) { bounds = new Rectangle(512 - 140, 288, 48, 16); } else { @@ -1803,6 +1806,51 @@ public static void drawPlayerControlShape(Graphics2D g, int x, int y, int height } } + public static void drawBox(int x, int y, int w, int h, int color) { + if (Reflection.drawBox == null) return; + + try { + Reflection.drawBox.invoke(instance, x, (byte) -127, color, y, h, w); + } catch (Exception e) { + } + } + + public static void drawBoxBorder(int x, int y, int w, int h, int color) { + if (Reflection.drawBoxBorder == null) return; + + try { + Reflection.drawBoxBorder.invoke(instance, x, w, y, 27785, h, color); + } catch (Exception e) { + } + } + + public static void drawString(String text, int x, int y, int font, int color) { + if (Reflection.drawString == null) return; + + try { + Reflection.drawString.invoke(instance, text, x, y, color, false, font); + } catch (Exception e) { + } + } + + public static void drawStringCenter(String text, int x, int y, int font, int color) { + if (Reflection.drawStringCenter == null) return; + + try { + Reflection.drawStringCenter.invoke(instance, x, text, color, 0, font, y); + } catch (Exception e) { + } + } + + public static void drawSprite(int x, int y, int id) { + if (Reflection.drawSprite == null) return; + + try { + Reflection.drawSprite.invoke(instance, -1, id, y, x); + } catch (Exception e) { + } + } + public static void takeScreenshot(boolean quiet) { quietScreenshot = quiet; screenshot = true; diff --git a/src/Game/StreamUtil.java b/src/Game/StreamUtil.java index 1705e42b..97613f6d 100644 --- a/src/Game/StreamUtil.java +++ b/src/Game/StreamUtil.java @@ -57,6 +57,15 @@ public static void flushPacket() { } } + public static void sendPacket() { + if (Reflection.sendPacket == null) return; + + try { + Reflection.sendPacket.invoke(Client.clientStream, 21294); + } catch (Exception e) { + } + } + public static void initIsaac(int[] keys) { if (Reflection.initIsaac == null) return; @@ -152,6 +161,40 @@ public static int readStream() { return response; } + + public static int readByte() { + if (Reflection.readResponse == null) return -1; + return readStream(); + } + + public static int readShort() { + if (Reflection.readResponse == null) return -1; + int i = readByte(); + int j = readByte(); + return i * 256 + j; + } + + public static int readInt() { + if (Reflection.readResponse == null) return -1; + int i = readShort(); + int j = readShort(); + return i * 65536 + j; + } + + public static void readBytes(byte[] byteArr, int length) { + readBytes(byteArr, 0, length); + } + + public static void readBytes(byte[] byteArr, int offset, int length) { + if (Reflection.readBytes == null) return; + int response = -1; + + try { + Reflection.readBytes.invoke(Client.clientStream, byteArr, length, offset, 123); + } catch (Exception e) { + } + + } public static void putBytesTo(Object buffer, byte[] block, int start, int offset) { if (Reflection.putBytes == null) return; @@ -203,6 +246,11 @@ public static void putLongTo(Object buffer, long l) { putIntTo(buffer, (int) (l & -1L)); } + /** + * Put a string terminated by '\0' + * @param buffer + * @param st + */ public static void putStrTo(Object buffer, String st) { if (Reflection.putStr == null) return; @@ -211,6 +259,16 @@ public static void putStrTo(Object buffer, String st) { } catch (Exception e) { } } + + /** + * Put a regular string and not terminate it with '\0' + * @param buffer + * @param st + */ + public static void putRegStrTo(Object buffer, String st) { + byte[] stringArray = st.getBytes(); + putBytesTo(buffer, stringArray, 0, stringArray.length); + } public static void encrypt(Object buffer, BigInteger exponent, BigInteger modulus) { if (Reflection.encrypt == null) return;