A RuneTek3 client (377) that is deobfuscated, converted to Kotlin, and includes QoL improvements.
0

Configure Feed

Select the types of activity you want to include in your feed.

feat: sound

+614 -136
+1 -4
.gitignore
··· 1 1 # Package Files # 2 - *.jar 3 2 *.war 4 3 *.ear 5 4 *.class ··· 40 39 # IntelliJ 41 40 /out/ 42 41 /build/ 42 + /lib/ 43 43 44 44 # mpeltonen/sbt-idea plugin 45 45 .idea_modules/ ··· 53 53 crashlytics-build.properties 54 54 55 55 # Gradle 56 - gradlew 57 - gradlew.bat 58 - /gradle/ 59 56 .gradle/ 60 57 61 58 # Config
+23
.gitlab-ci.yml
··· 1 + stages: 2 + - build 3 + 4 + variables: 5 + GRADLE_OPTS: "-Dorg.gradle.daemon=false" 6 + 7 + build: 8 + stage: build 9 + image: eclipse-temurin:21-jdk-alpine 10 + before_script: 11 + - chmod +x gradlew 12 + script: 13 + - ./gradlew clean jar 14 + artifacts: 15 + paths: 16 + - build/libs/377.jar 17 + expire_in: 90 days 18 + cache: 19 + key: 20 + files: 21 + - build.gradle 22 + paths: 23 + - .gradle/
+3
META-INF/MANIFEST.MF
··· 1 + Manifest-Version: 1.0 2 + Main-Class: com.jagex.runescape.Game 3 +
+13 -5
build.gradle
··· 8 8 } 9 9 10 10 dependencies { 11 - implementation 'org.yaml:snakeyaml:1.25' 12 - testImplementation('org.junit.jupiter:junit-jupiter:5.5.2') 11 + implementation 'org.yaml:snakeyaml:2.0' 12 + testImplementation 'org.junit.jupiter:junit-jupiter:5.10.2' 13 + testImplementation 'org.testng:testng:7.9.0' 13 14 } 14 15 15 16 test { 16 17 useJUnitPlatform() 17 - testLogging { 18 - events "passed", "skipped", "failed" 18 + } 19 + 20 + jar { 21 + manifest { 22 + attributes 'Main-Class': 'com.jagex.runescape.Game' 19 23 } 24 + from { 25 + configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } 26 + } 27 + duplicatesStrategy = DuplicatesStrategy.EXCLUDE 20 28 } 21 29 22 - task run(type: JavaExec) { 30 + tasks.register('run', JavaExec) { 23 31 mainClass = 'com.jagex.runescape.Game' 24 32 classpath = sourceSets.main.runtimeClasspath 25 33 }
gradle/wrapper/gradle-wrapper.jar

This is a binary file and will not be displayed.

+7
gradle/wrapper/gradle-wrapper.properties
··· 1 + distributionBase=GRADLE_USER_HOME 2 + distributionPath=wrapper/dists 3 + distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip 4 + networkTimeout=10000 5 + validateDistributionUrl=true 6 + zipStoreBase=GRADLE_USER_HOME 7 + zipStorePath=wrapper/dists
+251
gradlew
··· 1 + #!/bin/sh 2 + 3 + # 4 + # Copyright © 2015-2021 the original authors. 5 + # 6 + # Licensed under the Apache License, Version 2.0 (the "License"); 7 + # you may not use this file except in compliance with the License. 8 + # You may obtain a copy of the License at 9 + # 10 + # https://www.apache.org/licenses/LICENSE-2.0 11 + # 12 + # Unless required by applicable law or agreed to in writing, software 13 + # distributed under the License is distributed on an "AS IS" BASIS, 14 + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 + # See the License for the specific language governing permissions and 16 + # limitations under the License. 17 + # 18 + # SPDX-License-Identifier: Apache-2.0 19 + # 20 + 21 + ############################################################################## 22 + # 23 + # Gradle start up script for POSIX generated by Gradle. 24 + # 25 + # Important for running: 26 + # 27 + # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 + # noncompliant, but you have some other compliant shell such as ksh or 29 + # bash, then to run this script, type that shell name before the whole 30 + # command line, like: 31 + # 32 + # ksh Gradle 33 + # 34 + # Busybox and similar reduced shells will NOT work, because this script 35 + # requires all of these POSIX shell features: 36 + # * functions; 37 + # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 + # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 + # * compound commands having a testable exit status, especially «case»; 40 + # * various built-in commands including «command», «set», and «ulimit». 41 + # 42 + # Important for patching: 43 + # 44 + # (2) This script targets any POSIX shell, so it avoids extensions provided 45 + # by Bash, Ksh, etc; in particular arrays are avoided. 46 + # 47 + # The "traditional" practice of packing multiple parameters into a 48 + # space-separated string is a well documented source of bugs and security 49 + # problems, so this is (mostly) avoided, by progressively accumulating 50 + # options in "$@", and eventually passing that to Java. 51 + # 52 + # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 + # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 + # see the in-line comments for details. 55 + # 56 + # There are tweaks for specific operating systems such as AIX, CygWin, 57 + # Darwin, MinGW, and NonStop. 58 + # 59 + # (3) This script is generated from the Groovy template 60 + # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 + # within the Gradle project. 62 + # 63 + # You can find Gradle at https://github.com/gradle/gradle/. 64 + # 65 + ############################################################################## 66 + 67 + # Attempt to set APP_HOME 68 + 69 + # Resolve links: $0 may be a link 70 + app_path=$0 71 + 72 + # Need this for daisy-chained symlinks. 73 + while 74 + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 + [ -h "$app_path" ] 76 + do 77 + ls=$( ls -ld "$app_path" ) 78 + link=${ls#*' -> '} 79 + case $link in #( 80 + /*) app_path=$link ;; #( 81 + *) app_path=$APP_HOME$link ;; 82 + esac 83 + done 84 + 85 + # This is normally unused 86 + # shellcheck disable=SC2034 87 + APP_BASE_NAME=${0##*/} 88 + # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 + APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit 90 + 91 + # Use the maximum available, or set MAX_FD != -1 to use that value. 92 + MAX_FD=maximum 93 + 94 + warn () { 95 + echo "$*" 96 + } >&2 97 + 98 + die () { 99 + echo 100 + echo "$*" 101 + echo 102 + exit 1 103 + } >&2 104 + 105 + # OS specific support (must be 'true' or 'false'). 106 + cygwin=false 107 + msys=false 108 + darwin=false 109 + nonstop=false 110 + case "$( uname )" in #( 111 + CYGWIN* ) cygwin=true ;; #( 112 + Darwin* ) darwin=true ;; #( 113 + MSYS* | MINGW* ) msys=true ;; #( 114 + NONSTOP* ) nonstop=true ;; 115 + esac 116 + 117 + CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 + 119 + 120 + # Determine the Java command to use to start the JVM. 121 + if [ -n "$JAVA_HOME" ] ; then 122 + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 + # IBM's JDK on AIX uses strange locations for the executables 124 + JAVACMD=$JAVA_HOME/jre/sh/java 125 + else 126 + JAVACMD=$JAVA_HOME/bin/java 127 + fi 128 + if [ ! -x "$JAVACMD" ] ; then 129 + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 + 131 + Please set the JAVA_HOME variable in your environment to match the 132 + location of your Java installation." 133 + fi 134 + else 135 + JAVACMD=java 136 + if ! command -v java >/dev/null 2>&1 137 + then 138 + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 139 + 140 + Please set the JAVA_HOME variable in your environment to match the 141 + location of your Java installation." 142 + fi 143 + fi 144 + 145 + # Increase the maximum file descriptors if we can. 146 + if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 147 + case $MAX_FD in #( 148 + max*) 149 + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 150 + # shellcheck disable=SC2039,SC3045 151 + MAX_FD=$( ulimit -H -n ) || 152 + warn "Could not query maximum file descriptor limit" 153 + esac 154 + case $MAX_FD in #( 155 + '' | soft) :;; #( 156 + *) 157 + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 158 + # shellcheck disable=SC2039,SC3045 159 + ulimit -n "$MAX_FD" || 160 + warn "Could not set maximum file descriptor limit to $MAX_FD" 161 + esac 162 + fi 163 + 164 + # Collect all arguments for the java command, stacking in reverse order: 165 + # * args from the command line 166 + # * the main class name 167 + # * -classpath 168 + # * -D...appname settings 169 + # * --module-path (only if needed) 170 + # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 171 + 172 + # For Cygwin or MSYS, switch paths to Windows format before running java 173 + if "$cygwin" || "$msys" ; then 174 + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 175 + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 176 + 177 + JAVACMD=$( cygpath --unix "$JAVACMD" ) 178 + 179 + # Now convert the arguments - kludge to limit ourselves to /bin/sh 180 + for arg do 181 + if 182 + case $arg in #( 183 + -*) false ;; # don't mess with options #( 184 + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 185 + [ -e "$t" ] ;; #( 186 + *) false ;; 187 + esac 188 + then 189 + arg=$( cygpath --path --ignore --mixed "$arg" ) 190 + fi 191 + # Roll the args list around exactly as many times as the number of 192 + # args, so each arg winds up back in the position where it started, but 193 + # possibly modified. 194 + # 195 + # NB: a `for` loop captures its iteration list before it begins, so 196 + # changing the positional parameters here affects neither the number of 197 + # iterations, nor the values presented in `arg`. 198 + shift # remove old arg 199 + set -- "$@" "$arg" # push replacement arg 200 + done 201 + fi 202 + 203 + 204 + # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 205 + DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 206 + 207 + # Collect all arguments for the java command: 208 + # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 209 + # and any embedded shellness will be escaped. 210 + # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 211 + # treated as '${Hostname}' itself on the command line. 212 + 213 + set -- \ 214 + "-Dorg.gradle.appname=$APP_BASE_NAME" \ 215 + -classpath "$CLASSPATH" \ 216 + org.gradle.wrapper.GradleWrapperMain \ 217 + "$@" 218 + 219 + # Stop when "xargs" is not available. 220 + if ! command -v xargs >/dev/null 2>&1 221 + then 222 + die "xargs is not available" 223 + fi 224 + 225 + # Use "xargs" to parse quoted args. 226 + # 227 + # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 228 + # 229 + # In Bash we could simply go: 230 + # 231 + # readarray ARGS < <( xargs -n1 <<<"$var" ) && 232 + # set -- "${ARGS[@]}" "$@" 233 + # 234 + # but POSIX shell has neither arrays nor command substitution, so instead we 235 + # post-process each arg (as a line of input to sed) to backslash-escape any 236 + # character that might be a shell metacharacter, then use eval to reverse 237 + # that process (while maintaining the separation between arguments), and wrap 238 + # the whole thing up as a single "set" statement. 239 + # 240 + # This will of course break if any of these variables contains a newline or 241 + # an unmatched quote. 242 + # 243 + 244 + eval "set -- $( 245 + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 246 + xargs -n1 | 247 + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 248 + tr '\n' ' ' 249 + )" '"$@"' 250 + 251 + exec "$JAVACMD" "$@"
+94
gradlew.bat
··· 1 + @rem 2 + @rem Copyright 2015 the original author or authors. 3 + @rem 4 + @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 + @rem you may not use this file except in compliance with the License. 6 + @rem You may obtain a copy of the License at 7 + @rem 8 + @rem https://www.apache.org/licenses/LICENSE-2.0 9 + @rem 10 + @rem Unless required by applicable law or agreed to in writing, software 11 + @rem distributed under the License is distributed on an "AS IS" BASIS, 12 + @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 + @rem See the License for the specific language governing permissions and 14 + @rem limitations under the License. 15 + @rem 16 + @rem SPDX-License-Identifier: Apache-2.0 17 + @rem 18 + 19 + @if "%DEBUG%"=="" @echo off 20 + @rem ########################################################################## 21 + @rem 22 + @rem Gradle startup script for Windows 23 + @rem 24 + @rem ########################################################################## 25 + 26 + @rem Set local scope for the variables with windows NT shell 27 + if "%OS%"=="Windows_NT" setlocal 28 + 29 + set DIRNAME=%~dp0 30 + if "%DIRNAME%"=="" set DIRNAME=. 31 + @rem This is normally unused 32 + set APP_BASE_NAME=%~n0 33 + set APP_HOME=%DIRNAME% 34 + 35 + @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 + for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 + 38 + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 + set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 + 41 + @rem Find java.exe 42 + if defined JAVA_HOME goto findJavaFromJavaHome 43 + 44 + set JAVA_EXE=java.exe 45 + %JAVA_EXE% -version >NUL 2>&1 46 + if %ERRORLEVEL% equ 0 goto execute 47 + 48 + echo. 1>&2 49 + echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 + echo. 1>&2 51 + echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 + echo location of your Java installation. 1>&2 53 + 54 + goto fail 55 + 56 + :findJavaFromJavaHome 57 + set JAVA_HOME=%JAVA_HOME:"=% 58 + set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 + 60 + if exist "%JAVA_EXE%" goto execute 61 + 62 + echo. 1>&2 63 + echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 + echo. 1>&2 65 + echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 + echo location of your Java installation. 1>&2 67 + 68 + goto fail 69 + 70 + :execute 71 + @rem Setup the command line 72 + 73 + set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 + 75 + 76 + @rem Execute Gradle 77 + "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 + 79 + :end 80 + @rem End local scope for the variables with windows NT shell 81 + if %ERRORLEVEL% equ 0 goto mainEnd 82 + 83 + :fail 84 + rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 + rem the _cmd.exe /c_ return code! 86 + set EXIT_CODE=%ERRORLEVEL% 87 + if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 + if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 + exit /b %EXIT_CODE% 90 + 91 + :mainEnd 92 + if "%OS%"=="Windows_NT" endlocal 93 + 94 + :omega
+107 -65
src/main/java/com/jagex/runescape/Game.java
··· 60 60 public class Game extends GameShell { 61 61 62 62 63 + public static int VERSION = 377; 63 64 public static final int[][] playerColours = { 64 65 {6798, 107, 10283, 16, 4797, 7744, 5799, 4634, 33697, 22433, 2983, 54193}, 65 66 {8741, 12, 64030, 43162, 7735, 8404, 1701, 38430, 24094, 10153, 56621, 4783, 1341, 16578, 35003, 25239}, ··· 549 550 550 551 public static void main(String[] args) { 551 552 try { 552 - System.out.println("RS2 user client - release #" + 377); 553 + System.out.println("RS2 user client - release #" + VERSION); 553 554 Game game = new Game(); 554 555 Configuration.read(); 555 556 game.world = 1; ··· 557 558 game.setHighMemory(); 558 559 game.memberServer = true; 559 560 SignLink.storeId = 32; 560 - SignLink.initialize(InetAddress.getLocalHost()); 561 + SignLink.initialize(InetAddress.getByName(Configuration.SERVER_ADDRESS)); 561 562 game.initializeApplication(765, 503); 562 563 } catch (Exception exception) { 563 564 } ··· 692 693 } 693 694 694 695 private void adjustMidiVolume(boolean flag, byte byte0, int volume) { 695 - SignLink.midiVolume = volume; 696 - if (flag) 697 - SignLink.midi = "voladjust"; 696 + // volume arrives as hundredths-of-dB: 0, -400, -800, -1200 697 + // Map to MIDI CC7 range (0-127): 0 dB = 127, -400 = 96, -800 = 64, -1200 = 32 698 + int midiVol = 127 + (volume * 95 / 1200); 699 + if (midiVol < 0) midiVol = 0; 700 + if (midiVol > 127) midiVol = 127; 701 + SignLink.setVolume(midiVol); 698 702 } 699 703 700 704 private void itemSearch(String input) { ··· 778 782 if (players[id] == null) { 779 783 players[id] = new Player(); 780 784 781 - if (cachedAppearances[id] != null) 785 + if (cachedAppearances[id] != null) { 786 + System.out.println("[PLAYER] Adding new player [id=" + id + "] with cached appearance"); 782 787 players[id].updateAppearance(cachedAppearances[id]); 788 + } else { 789 + System.out.println("[PLAYER] Adding new player [id=" + id + "] without appearance"); 790 + } 783 791 } 784 792 785 793 playerList[localPlayerCount++] = id; ··· 801 809 if (y > 15) 802 810 y -= 32; 803 811 804 - player.setPosition(localPlayer.pathX[0] + x, localPlayer.pathY[0] + y, discardQueue == 1); 812 + int absX = localPlayer.pathX[0] + x; 813 + int absY = localPlayer.pathY[0] + y; 814 + System.out.println("[PLAYER] New player [id=" + id + "] positioned at X: " + absX + ", Y: " + absY); 815 + player.setPosition(absX, absY, discardQueue == 1); 805 816 } 806 817 807 818 buffer.finishBitAccess(); ··· 859 870 } 860 871 861 872 private void openErrorWebPage(String s) { 862 - System.out.println(s); 873 + System.out.println("[ERROR] " + s); 863 874 try { 864 875 // getAppletContext().showDocument(new URL(getCodeBase(), "loaderror_" + s + ".html")); 865 876 } catch (Exception exception) { ··· 1908 1919 useJaggrab = Configuration.JAGGRAB_ENABLED; 1909 1920 1910 1921 if (!useJaggrab) { 1922 + System.out.println("[JAGGRAB] JAGGRAB disabled, using direct URL: " + request); 1911 1923 return SignLink.openURL(request); 1912 1924 } 1913 1925 ··· 1920 1932 jaggrabSocket = null; 1921 1933 } 1922 1934 1923 - byte[] buffer = String.format("JAGGRAB /%s\n\n", request).getBytes(); 1935 + String jaggrabRequest = String.format("JAGGRAB /%s-%s", request, Game.VERSION); 1936 + System.out.println("[JAGGRAB] Connecting to " + getCodeBase().getHost() + ":" + Configuration.JAGGRAB_PORT); 1937 + System.out.println("[JAGGRAB] Request: " + jaggrabRequest); 1938 + byte[] buffer = (jaggrabRequest + "\n\n").getBytes(); 1924 1939 jaggrabSocket = openSocket(Configuration.JAGGRAB_PORT); 1925 - 1926 1940 jaggrabSocket.setSoTimeout(10000); 1927 1941 jaggrabSocket.getOutputStream().write(buffer); 1942 + System.out.println("[JAGGRAB] Request sent, awaiting response"); 1928 1943 1929 1944 return new DataInputStream(jaggrabSocket.getInputStream()); 1930 1945 } ··· 1943 1958 if (opcode == -1) { 1944 1959 gameConnection.read(buffer.buffer, 0, 1); 1945 1960 opcode = buffer.buffer[0] & 0xff; 1946 - if (incomingRandom != null) 1947 - opcode = opcode - incomingRandom.nextInt() & 0xff; 1961 + //if (incomingRandom != null) 1962 + // opcode = opcode - incomingRandom.nextInt() & 0xff; 1963 + System.out.println("[PACKET] Received opcode: " + opcode + " (size: " + PacketConstants.PACKET_SIZES[opcode] + ")"); 1948 1964 packetSize = PacketConstants.PACKET_SIZES[opcode]; 1949 1965 available--; 1950 1966 } ··· 2141 2157 int slot = buffer.getUnsignedInvertedByte(); 2142 2158 String option = buffer.getString(); 2143 2159 int alwaysOnTop = buffer.getUnsignedByte(); 2144 - System.out.println(slot); 2145 - System.out.println(option); 2146 - System.out.println(alwaysOnTop); 2147 2160 if (slot >= 1 && slot <= 5) { 2148 2161 if (option.equalsIgnoreCase("null")) 2149 2162 option = null; ··· 2711 2724 if (UPDATE_ACTIVE_MAP_REGION.equals(opcode)) { 2712 2725 tmpChunkY = buffer.getUnsignedShortBE(); 2713 2726 tmpChunkX = buffer.getUnsignedNegativeOffsetShortLE(); 2727 + System.out.println("[MAP] Updating region to chunk (" + tmpChunkX + "," + tmpChunkY + ")"); 2714 2728 loadGeneratedMap = false; 2715 2729 } 2716 2730 if (CONSTRUCT_MAP_REGION.equals(opcode)) { ··· 3135 3149 logout(); 3136 3150 } catch (IOException _ex) { 3137 3151 dropClient(); 3152 + } catch (ArrayIndexOutOfBoundsException ex) { 3153 + String s1 = "BoundsError (T2) - " + opcode + "," + secondLastOpcode + "," + thirdLastOpcode + " - " + packetSize + "," 3154 + + (nextTopLeftTileX + localPlayer.pathX[0]) + "," 3155 + + (nextTopRightTileY + localPlayer.pathY[0]) + " - "; 3156 + for (int j16 = 0; j16 < packetSize && j16 < 50; j16++) 3157 + s1 = s1 + buffer.buffer[j16] + ","; 3158 + 3159 + SignLink.reportError(s1); 3160 + logout(); 3161 + 3162 + ex.printStackTrace(); 3138 3163 } catch (Exception exception) { 3139 3164 String s1 = "T2 - " + opcode + "," + secondLastOpcode + "," + thirdLastOpcode + " - " + packetSize + "," 3140 3165 + (nextTopLeftTileX + localPlayer.pathX[0]) + "," ··· 3539 3564 if ((mask & 0x20) != 0) 3540 3565 mask += buffer.getUnsignedByte() << 8; 3541 3566 3567 + System.out.println("[PLAYER] Update block for player [id=" + id + "] with mask: 0x" + Integer.toHexString(mask)); 3542 3568 parsePlayerBlock(id, player, mask, buffer); 3543 3569 } 3544 3570 } ··· 3552 3578 return; 3553 3579 3554 3580 MovementType moveType = MovementType.values()[buffer.getBits(2)]; 3581 + System.out.println("[MOVEMENT] Local player movement type: " + moveType); 3555 3582 3556 3583 3557 3584 if (moveType == MovementType.NONE) { ··· 3561 3588 3562 3589 if (moveType == MovementType.WALK) { 3563 3590 int direction = buffer.getBits(3); 3591 + System.out.println("[MOVEMENT] Local player walking - direction: " + direction); 3564 3592 3565 3593 localPlayer.move(direction, false); 3566 3594 ··· 3573 3601 3574 3602 if (moveType == MovementType.RUN) { 3575 3603 int direction1 = buffer.getBits(3); 3576 - 3577 - localPlayer.move(direction1, true); 3578 - 3579 3604 int direction2 = buffer.getBits(3); 3605 + System.out.println("[MOVEMENT] Local player running - direction1: " + direction1 + ", direction2: " + direction2); 3580 3606 3607 + localPlayer.move(direction1, true); 3581 3608 localPlayer.move(direction2, true); 3582 3609 3583 3610 int blockUpdateRequired = buffer.getBits(1); ··· 3593 3620 int localY = buffer.getBits(7); 3594 3621 int localX = buffer.getBits(7); 3595 3622 int blockUpdateRequired = buffer.getBits(1); 3623 + 3624 + System.out.println("[MOVEMENT] Local player teleporting - X: " + localX + ", Y: " + localY + ", plane: " + plane + ", discardQueue: " + discardWalkingQueue); 3596 3625 3597 3626 if (blockUpdateRequired == 1) 3598 3627 updatedPlayers[updatedPlayerCount++] = maxPlayerIndex; ··· 4480 4509 int archiveLength = buffer.getMediumBE() + 6; 4481 4510 int archiveRead = 6; 4482 4511 archiveBuffer = new byte[archiveLength]; 4512 + System.out.println("[JAGGRAB] Archive size: " + archiveLength + " bytes (" + displayName + ")"); 4483 4513 4484 4514 System.arraycopy(bytes, 0, archiveBuffer, 0, 6); 4485 4515 ··· 4506 4536 } 4507 4537 4508 4538 jaggrabStream.close(); 4539 + System.out.println("[JAGGRAB] Download complete: " + archiveLength + " bytes (" + displayName + ")"); 4509 4540 4510 4541 try { 4511 4542 if (stores[0] != null) ··· 4519 4550 archiveCrc.update(archiveBuffer); 4520 4551 4521 4552 int calculatedCrc = (int) archiveCrc.getValue(); 4553 + System.out.println("[JAGGRAB] CRC validation: expected=" + expectedCrc + ", calculated=" + calculatedCrc); 4522 4554 4523 4555 if (calculatedCrc != expectedCrc) { 4524 4556 archiveBuffer = null; 4525 4557 attempts++; 4526 4558 error = "Checksum error: " + calculatedCrc; 4559 + System.out.println("[JAGGRAB] CRC mismatch! Attempt " + attempts); 4527 4560 } 4528 4561 } 4529 4562 } catch (IOException ex) { ··· 4676 4709 if (animation == 65535) 4677 4710 animation = -1; 4678 4711 4712 + System.out.println("[PLAYER] Animation update for player [id=" + id + "] - animation: " + animation); 4713 + 4679 4714 int delay = buffer.getUnsignedPreNegativeOffsetByte(); 4680 4715 4681 4716 if (animation == player.emoteAnimation && animation != -1) { ··· 4703 4738 4704 4739 if ((mask & 0x10) != 0) { 4705 4740 player.forcedChat = buffer.getString(); 4741 + System.out.println("[PLAYER] Forced chat for player [id=" + id + "]: \"" + player.forcedChat + "\""); 4706 4742 4707 4743 if (player.forcedChat.charAt(0) == '~') { 4708 4744 player.forcedChat = player.forcedChat.substring(1); ··· 4724 4760 player.moveCycleEnd = buffer.getUnsignedShortBE() + pulseCycle; 4725 4761 player.moveCycleStart = buffer.getUnsignedNegativeOffsetShortBE() + pulseCycle; 4726 4762 player.moveDirection = buffer.getUnsignedByte(); 4763 + System.out.println("[MOVEMENT] Forced movement for player [id=" + id + "] - from (" + player.movementStartX + "," + player.movementStartY + ") to (" + player.movementEndX + "," + player.movementEndY + ")"); 4727 4764 4728 4765 player.resetPath(); 4729 4766 } 4730 4767 4731 4768 if ((mask & 1) != 0) { 4732 4769 player.faceActor = buffer.getUnsignedNegativeOffsetShortBE(); 4733 - 4734 4770 if (player.faceActor == 65535) 4735 4771 player.faceActor = -1; 4772 + System.out.println("[PLAYER] Face actor for player [id=" + id + "] - facing: " + player.faceActor); 4736 4773 } 4737 4774 4738 4775 if ((mask & 2) != 0) { 4739 4776 player.faceX = buffer.getUnsignedShortBE(); 4740 4777 player.faceY = buffer.getUnsignedShortBE(); 4778 + System.out.println("[PLAYER] Face coords for player [id=" + id + "] - facing (" + player.faceX + "," + player.faceY + ")"); 4741 4779 } 4742 4780 4743 4781 if ((mask & 0x200) != 0) { ··· 4753 4791 4754 4792 if (player.graphic == 65535) 4755 4793 player.graphic = -1; 4794 + 4795 + System.out.println("[PLAYER] Spot graphic for player [id=" + id + "] - graphic: " + player.graphic + ", height: " + player.spotGraphicHeight); 4756 4796 } 4757 4797 4758 4798 if ((mask & 4) != 0) { ··· 4763 4803 buffer.getBytesReverse(bytes, 0, size); 4764 4804 4765 4805 cachedAppearances[id] = appearance; 4806 + System.out.println("[APPEARANCE] Updating appearance for player [id=" + id + "] (size: " + size + " bytes)"); 4766 4807 4767 4808 player.updateAppearance(appearance); 4768 4809 } ··· 6297 6338 drawLoginScreen(true); 6298 6339 } 6299 6340 6341 + System.out.println("[CLIENT] Connecting to " + getCodeBase().getHost() + 6342 + ":" + (Configuration.GAME_PORT + portOffset)); 6343 + System.out.println("[CLIENT] Port offset: " + portOffset); 6300 6344 gameConnection = new BufferedConnection(this, openSocket(Configuration.GAME_PORT + portOffset)); 6345 + System.out.println("[CLIENT] Connection established"); 6301 6346 long base37name = TextUtils.nameToLong(username); 6302 6347 int hash = (int) (base37name >> 16 & 31L); 6348 + System.out.println("[CLIENT] Player hash: " + hash); 6303 6349 outBuffer.currentPosition = 0; 6304 6350 6351 + System.out.println("[LOGIN] Sending handshake (opcode 14)"); 6305 6352 outBuffer.putByte(14); 6306 6353 outBuffer.putByte(hash); 6307 6354 gameConnection.write(2, 0, outBuffer.buffer); ··· 6311 6358 6312 6359 int responseCode = gameConnection.read(); 6313 6360 int initialResponseCode = responseCode; 6361 + System.out.println("[LOGIN] Initial response code: " + initialResponseCode); 6314 6362 6315 6363 if (responseCode == 0) { 6316 6364 gameConnection.read(buffer.buffer, 0, 8); 6317 6365 6318 6366 buffer.currentPosition = 0; 6319 6367 serverSeed = buffer.getLongBE(); 6368 + System.out.println("[LOGIN] Server seed received: " + serverSeed); 6320 6369 int[] seed = new int[4]; 6321 6370 6322 6371 seed[0] = (int) (Math.random() * 99999999D); 6323 6372 seed[1] = (int) (Math.random() * 99999999D); 6324 6373 seed[2] = (int) (serverSeed >> 32); 6325 6374 seed[3] = (int) serverSeed; 6375 + System.out.println("[LOGIN] Client seeds: [" + seed[0] + ", " + seed[1] + ", " + seed[2] + ", " + seed[3] + "]"); 6326 6376 6327 6377 outBuffer.currentPosition = 0; 6328 6378 ··· 6335 6385 outBuffer.putString(username); 6336 6386 outBuffer.putString(password); 6337 6387 6338 - outBuffer.encrypt(Configuration.RSA_MODULUS, Configuration.RSA_PUBLIC_KEY); 6388 + if (Configuration.RSA_ENABLED == true) { 6389 + outBuffer.encrypt(Configuration.RSA_MODULUS, Configuration.RSA_PUBLIC_KEY); 6390 + } 6339 6391 6340 6392 tempBuffer.currentPosition = 0; 6341 6393 ··· 6352 6404 for (int i = 0; i < 9; i++) 6353 6405 tempBuffer.putIntBE(archiveHashes[i]); 6354 6406 6407 + System.out.println("[LOGIN] Login packet size: " + tempBuffer.currentPosition + " bytes"); 6355 6408 tempBuffer.putBytes(outBuffer.buffer, 0, outBuffer.currentPosition); 6356 6409 6357 - outBuffer.random = new ISAACCipher(seed); 6410 + //outBuffer.random = new ISAACCipher(seed); 6358 6411 6359 6412 for (int i = 0; i < 4; i++) 6360 6413 seed[i] += 50; ··· 6366 6419 responseCode = gameConnection.read(); 6367 6420 } 6368 6421 6422 + System.out.println("[LOGIN] Final response code: " + responseCode); 6423 + 6369 6424 if (responseCode == 1) { 6425 + System.out.println("[LOGIN] Exchange data - will retry in 2 seconds"); 6370 6426 try { 6371 6427 Thread.sleep(2000L); 6372 6428 } catch (Exception ignored) { ··· 6377 6433 } 6378 6434 6379 6435 if (responseCode == 2) { 6436 + System.out.println("[LOGIN] Login successful!"); 6380 6437 playerRights = gameConnection.read(); 6381 6438 accountFlagged = gameConnection.read() == 1; 6439 + System.out.println("[LOGIN] Player rights: " + playerRights); 6440 + System.out.println("[LOGIN] Account flagged: " + accountFlagged); 6382 6441 lastClickTime = 0L; 6383 6442 duplicateClickCount = 0; 6384 6443 mouseCapturer.coord = 0; ··· 6674 6733 return; 6675 6734 } 6676 6735 } else { 6677 - System.out.println("response:" + responseCode); 6736 + System.out.println("[LOGIN] Login failed with unexpected response code: " + responseCode); 6678 6737 6679 6738 statusLineOne = "Unexpected server response"; 6680 6739 statusLineTwo = "Please try using a different world."; 6681 6740 return; 6682 6741 } 6683 6742 } catch (IOException ex) { 6743 + System.out.println("[ERROR] Connection failed: " + ex.getMessage()); 6744 + ex.printStackTrace(); 6684 6745 statusLineOne = ""; 6685 6746 } 6686 6747 ··· 7170 7231 drawLoadingText(20, "Connecting to web server"); 7171 7232 7172 7233 try { 7173 - DataInputStream stream = openJaggrabStream("crc" + (int) (Math.random() * 99999999D) + "-" + 377); 7234 + DataInputStream stream = openJaggrabStream("crc" + (int) (Math.random() * 99999999D) + "-" + VERSION); 7174 7235 Buffer jaggrab = new Buffer(new byte[40]); 7175 7236 7176 7237 stream.readFully(jaggrab.buffer, 0, 40); ··· 8650 8711 8651 8712 private void updateOtherPlayerMovement(Buffer buffer) { 8652 8713 int playerCount = buffer.getBits(8); 8714 + System.out.println("[PLAYER] Other players in scene: " + playerCount + " (local player count: " + localPlayerCount + ")"); 8653 8715 8654 8716 if (playerCount < localPlayerCount) { 8655 8717 for (int i = playerCount; i < localPlayerCount; i++) ··· 10474 10536 SignLink.midi = "stop"; 10475 10537 } 10476 10538 10477 - private void adjustMidiVolume(boolean updateMidi, int volume) { 10478 - SignLink.setVolume(volume); 10479 - if (updateMidi) { 10480 - SignLink.midi = "voladjust"; 10481 - } 10482 - } 10483 10539 10484 10540 public void playSound(int id, int type, int delay, int volume) { 10485 10541 sound[currentSound] = id; ··· 10819 10875 } 10820 10876 10821 10877 private void infoDump() { 10822 - System.out.println("============"); 10823 - System.out.println("flame-cycle:" + flameCycle); 10878 + System.out.println("[DEBUG] ============ Client State Dump ============"); 10879 + System.out.println("[DEBUG] Flame cycle: " + flameCycle); 10824 10880 if (onDemandRequester != null) 10825 - System.out.println("Od-cycle:" + onDemandRequester.cycle); 10826 - System.out.println("loop-cycle:" + pulseCycle); 10827 - System.out.println("draw-cycle:" + drawCycle); 10828 - System.out.println("ptype:" + opcode); 10829 - System.out.println("psize:" + packetSize); 10881 + System.out.println("[DEBUG] OnDemand cycle: " + onDemandRequester.cycle); 10882 + System.out.println("[DEBUG] Loop cycle: " + pulseCycle); 10883 + System.out.println("[DEBUG] Draw cycle: " + drawCycle); 10884 + System.out.println("[DEBUG] Current packet opcode: " + opcode); 10885 + System.out.println("[DEBUG] Current packet size: " + packetSize); 10830 10886 if (gameConnection != null) 10831 10887 gameConnection.printDebug(); 10832 10888 super.dumpRequested = true; ··· 11780 11836 11781 11837 private void processAudio() { 11782 11838 for (int index = 0; index < currentSound; index++) { 11783 - //if (soundDelay[index] <= 0) { 11784 - boolean played = false; 11785 11839 try { 11786 11840 Buffer stream = SoundTrack.data(sound[index], soundType[index]); 11787 - new SoundPlayer(new ByteArrayInputStream(stream.buffer, 0, stream.currentPosition), soundType[index], soundDelay[index]); 11788 - if (System.currentTimeMillis() + (long) (stream.currentPosition / 22) > lastSoundTime 11789 - + (long) (lastSoundPosition / 22)) { 11790 - lastSoundPosition = stream.currentPosition; 11791 - lastSoundTime = System.currentTimeMillis(); 11792 - if (method116(stream.currentPosition, stream.buffer)) { 11793 - lastSound = sound[index]; 11794 - lastSoundType = soundType[index]; 11795 - } else { 11796 - played = true; 11797 - } 11798 - 11841 + if (stream != null && stream.currentPosition > 0) { 11842 + // Copy synthesized data — SoundTrack uses a shared static buffer 11843 + // that gets overwritten by each subsequent sound synthesis 11844 + byte[] soundData = new byte[stream.currentPosition]; 11845 + System.arraycopy(stream.buffer, 0, soundData, 0, stream.currentPosition); 11846 + new SoundPlayer(new ByteArrayInputStream(soundData, 0, soundData.length), 11847 + soundType[index], soundDelay[index]); 11799 11848 } 11800 11849 } catch (Exception exception) { 11801 11850 if (SignLink.reportError) { ··· 11806 11855 outBuffer.putShortBE(-1); 11807 11856 } 11808 11857 } 11809 - if (!played || soundDelay[index] == -5) { 11810 - currentSound--; 11811 - for (int j = index; j < currentSound; j++) { 11812 - sound[j] = sound[j + 1]; 11813 - soundType[j] = soundType[j + 1]; 11814 - soundDelay[j] = soundDelay[j + 1]; 11815 - soundVolume[j] = soundVolume[j + 1]; 11816 - } 11817 - 11818 - index--; 11819 - } else { 11820 - soundDelay[index] = -5; 11858 + // Always remove processed sound from queue 11859 + currentSound--; 11860 + for (int j = index; j < currentSound; j++) { 11861 + sound[j] = sound[j + 1]; 11862 + soundType[j] = soundType[j + 1]; 11863 + soundDelay[j] = soundDelay[j + 1]; 11864 + soundVolume[j] = soundVolume[j + 1]; 11821 11865 } 11822 - /*} else { 11823 - soundDelay[index]--; 11824 - }*/ 11866 + index--; 11825 11867 } 11826 11868 if (previousSong > 0) { 11827 11869 previousSong -= 20;
+26 -6
src/main/java/com/jagex/runescape/config/Configuration.java
··· 3 3 4 4 import java.io.File; 5 5 import java.io.FileInputStream; 6 + import java.io.InputStream; 6 7 import java.math.BigInteger; 7 8 import java.util.Map; 8 9 ··· 12 13 public static void read() { 13 14 try { 14 15 final Yaml yaml = new Yaml(); 15 - final FileInputStream inputStream = new FileInputStream(new File("./config/client-config.yaml")); 16 + InputStream inputStream = null; 17 + 18 + // Filesystem config overrides bundled defaults 19 + File externalConfig = new File("./config/client-config.yaml"); 20 + if (externalConfig.exists()) { 21 + inputStream = new FileInputStream(externalConfig); 22 + System.out.println("Loaded config from ./config/client-config.yaml"); 23 + } else { 24 + inputStream = Configuration.class.getResourceAsStream("/client-config.yaml"); 25 + if (inputStream != null) { 26 + System.out.println("Loaded bundled config from jar"); 27 + } 28 + } 29 + 30 + if (inputStream == null) { 31 + System.out.println("No config found, using defaults."); 32 + return; 33 + } 34 + 16 35 final Map<String, Object> obj = yaml.load(inputStream); 36 + inputStream.close(); 17 37 18 38 final Map<String, Object> net = (Map<String, Object>) obj.get("net"); 19 39 final Map<String, Object> cache = (Map<String, Object>) obj.get("cache"); ··· 46 66 PASSWORD = ""; 47 67 } 48 68 } catch(Exception e) { 49 - System.out.println("Unable to load client-config.yaml. Please use EXAMPLE-client-config.yaml to create one."); 69 + System.out.println("Unable to load config: " + e.getMessage()); 50 70 } 51 71 } 52 72 ··· 68 88 /** 69 89 * Port for establishing a connection to the on demand service. 70 90 */ 71 - public static int ONDEMAND_PORT = 43594; 91 + public static int ONDEMAND_PORT = 43596; 72 92 73 93 /** 74 94 * Port for establishing a connection to the update server. ··· 84 104 /** 85 105 * Whether or not the update server should be used. 86 106 */ 87 - public static boolean JAGGRAB_ENABLED = false; 107 + public static boolean JAGGRAB_ENABLED = true; 88 108 89 109 /** 90 110 * Whether or not the network packets should be encrypted. ··· 104 124 /** 105 125 * Use static username/password pair. 106 126 */ 107 - public static boolean USE_STATIC_DETAILS = true; 127 + public static boolean USE_STATIC_DETAILS = false; 108 128 109 129 /** 110 130 * Static username and password ··· 121 141 /** 122 142 * Always light up teleports 123 143 */ 124 - public static boolean FREE_TELEPORTS = true; 144 + public static boolean FREE_TELEPORTS = false; 125 145 126 146 /** 127 147 * When rightclicking objects show id and location
+8
src/main/java/com/jagex/runescape/media/renderable/actor/Player.java
··· 255 255 public void updateAppearance(Buffer buffer) { 256 256 buffer.currentPosition = 0; 257 257 gender = buffer.getUnsignedByte(); 258 + System.out.println("Got Gender: " + gender); 258 259 isSkulled = buffer.getByte(); 260 + 261 + System.out.println("Got Skulled: " + isSkulled); 259 262 headIcon = buffer.getByte(); 263 + 264 + System.out.println("HeadIcon: " + headIcon); 260 265 npcDefinition = null; 261 266 teamId = 0; 262 267 for(int index = 0; index < 12; index++) { ··· 307 312 if(super.runAnimationId == 65535) 308 313 super.runAnimationId = -1; 309 314 playerName = TextUtils.formatName(TextUtils.longToName(buffer.getLongBE())); 315 + System.out.println("Name: " + playerName); 310 316 combatLevel = buffer.getUnsignedByte(); 317 + System.out.println("Combat Level: " + combatLevel); 311 318 skillLevel = buffer.getUnsignedShortBE(); 319 + System.out.println("SkillLevel: " + skillLevel); 312 320 visible = true; 313 321 appearanceHash = 0L; 314 322 int k1 = appearance[5];
+3 -1
src/main/java/com/jagex/runescape/net/Buffer.java
··· 38 38 } 39 39 40 40 public void putOpcode(int opcode) { 41 - buffer[currentPosition++] = (byte) (opcode + random.nextInt()); 41 + System.out.println("[PACKET] Sending opcode: " + opcode); 42 + //buffer[currentPosition++] = (byte) (opcode + random.nextInt()); 43 + buffer[currentPosition++] = (byte) opcode; 42 44 } 43 45 44 46 public void putByte(int value) {
+10
src/main/java/com/jagex/runescape/net/requester/OnDemandRequester.java
··· 85 85 int id = ((inputBuffer[1] & 0xff) << 8) + (inputBuffer[2] & 0xff); 86 86 int size = ((inputBuffer[3] & 0xff) << 8) + (inputBuffer[4] & 0xff); 87 87 int chunk = inputBuffer[5] & 0xff; 88 + System.out.println("[ONDEMAND] Response [type=" + type + ", id=" + id + ", size=" + size + ", chunk=" + chunk + "]"); 88 89 onDemandNode = null; 89 90 for (OnDemandNode ondemandnode = (OnDemandNode) toRequest.first(); ondemandnode != null; ondemandnode = (OnDemandNode) toRequest.next()) { 90 91 if (ondemandnode.type == type && ondemandnode.id == id) ··· 127 128 } 128 129 for (int i = 0; i < toRead; i += inputStream.read(buffer, i + bufferOffset, toRead - i)); 129 130 if (toRead + offset >= buffer.length && onDemandNode != null) { 131 + System.out.println("[ONDEMAND] File complete [type=" + onDemandNode.type + ", id=" + onDemandNode.id + ", size=" + buffer.length + " bytes]"); 130 132 if (client.stores[0] != null) 131 133 client.stores[onDemandNode.type + 1].put(buffer.length, buffer, 132 134 onDemandNode.id); ··· 567 569 if (currentTime - lastSocketOpen < 4000L) 568 570 return; 569 571 lastSocketOpen = currentTime; 572 + System.out.println("[ONDEMAND] Connecting to port " + (Configuration.ONDEMAND_PORT + client.portOffset)); 570 573 socket = client.openSocket(Configuration.ONDEMAND_PORT + client.portOffset); 571 574 inputStream = socket.getInputStream(); 572 575 outputStream = socket.getOutputStream(); 573 576 outputStream.write(15); 577 + byte[] version = new byte[2]; 578 + version[0] = (byte) (Game.VERSION >> 8); 579 + version[1] = (byte) Game.VERSION; 580 + outputStream.write(version, 0, 2); 581 + System.out.println("[ONDEMAND] Handshake sent (opcode 15, version " + Game.VERSION + ")"); 574 582 for (int i = 0; i < 8; i++) 575 583 inputStream.read(); 584 + System.out.println("[ONDEMAND] Connection established"); 576 585 577 586 idleCycles = 0; 578 587 } ··· 585 594 inputBuffer[3] = 1; 586 595 else 587 596 inputBuffer[3] = 0; 597 + System.out.println("[ONDEMAND] Requesting [type=" + onDemandNode.type + ", id=" + onDemandNode.id + ", priority=" + inputBuffer[3] + "]"); 588 598 outputStream.write(inputBuffer, 0, 4); 589 599 sinceKeepAlive = 0; 590 600 requestFails = -10000;
+10 -54
src/main/java/com/jagex/runescape/sound/SoundPlayer.java
··· 6 6 import javax.sound.sampled.Clip; 7 7 import javax.sound.sampled.DataLine; 8 8 import javax.sound.sampled.FloatControl; 9 + import com.jagex.runescape.util.SignLink; 9 10 10 11 public class SoundPlayer implements Runnable { 11 12 ··· 16 17 private InputStream soundStream; 17 18 private Thread player; 18 19 private int delay; 19 - private int soundLevel; 20 20 public static int volume; 21 21 22 - /** 23 - * Initializes the sound player. 24 - * @param stream 25 - * @param level 26 - * @param delay 27 - */ 28 22 public SoundPlayer(InputStream stream, int level, int delay) { 29 - if (level == 0 || volume == 4 || level - volume <= 0) { 23 + if (volume == 4) { 30 24 return; 31 25 } 32 26 this.soundStream = stream; 33 - this.soundLevel = level; 34 27 this.delay = delay; 35 28 player = new Thread(this); 36 29 player.start(); 37 30 } 38 31 39 - /** 40 - * Plays the sound. 41 - */ 42 32 @Override 43 33 public void run() { 44 34 try { ··· 46 36 info = new DataLine.Info(Clip.class, stream.getFormat()); 47 37 sound = (Clip) AudioSystem.getLine(info); 48 38 sound.open(stream); 49 - FloatControl volume = (FloatControl) sound.getControl(FloatControl.Type.MASTER_GAIN); 50 - volume.setValue(getDecibels(soundLevel - getVolume())); 39 + FloatControl gainControl = (FloatControl) sound.getControl(FloatControl.Type.MASTER_GAIN); 40 + float gain = SignLink.waveVolume / 100.0f; 41 + float min = gainControl.getMinimum(); 42 + float max = gainControl.getMaximum(); 43 + if (gain < min) gain = min; 44 + if (gain > max) gain = max; 45 + gainControl.setValue(gain); 51 46 if (delay > 0) { 52 47 Thread.sleep(delay); 53 48 } ··· 65 60 } 66 61 } 67 62 68 - /** 69 - * Sets the client's volume level. 70 - * @param level 71 - */ 72 63 public static void setVolume(int level) { 73 64 volume = level; 74 65 } 75 66 76 - /** 77 - * Returns the client's volume level. 78 - */ 79 67 public static int getVolume() { 80 68 return volume; 81 69 } 82 - 83 - /** 84 - * Returns the decibels for a given volume level. 85 - * @param level 86 - * @return 87 - */ 88 - public float getDecibels(int level) { 89 - switch (level) { 90 - case 1: 91 - return (float) -80.0; 92 - case 2: 93 - return (float) -70.0; 94 - case 3: 95 - return (float) -60.0; 96 - case 4: 97 - return (float) -50.0; 98 - case 5: 99 - return (float) -40.0; 100 - case 6: 101 - return (float) -30.0; 102 - case 7: 103 - return (float) -20.0; 104 - case 8: 105 - return (float) -10.0; 106 - case 9: 107 - return (float) -0.0; 108 - case 10: 109 - return (float) 6.0; 110 - default: 111 - return (float) 0.0; 112 - } 113 - } 114 - } 70 + }
+36
src/main/java/com/jagex/runescape/util/SignLink.java
··· 90 90 91 91 for (int idx = 0; idx < 5; idx++) 92 92 cacheIndex[idx] = new RandomAccessFile(directory + "main_file_cache.idx" + idx, "rw"); 93 + 93 94 } catch (Exception exception) { 94 95 exception.printStackTrace(); 95 96 } ··· 188 189 189 190 try { 190 191 if (music != null) { 192 + if (fadeMidi == 1 && synthesizer != null) { 193 + fadeOutMidi(); 194 + } 191 195 music.stop(); 192 196 music.close(); 193 197 } 194 198 195 199 playMidi(midi); 200 + 201 + if (fadeMidi == 1 && synthesizer != null) { 202 + fadeInMidi(); 203 + } 196 204 } catch (Exception ex) { 197 205 ex.printStackTrace(); 198 206 } ··· 262 270 263 271 music.setLoopCount(Sequencer.LOOP_CONTINUOUSLY); 264 272 music.start(); 273 + } 274 + 275 + private void fadeOutMidi() { 276 + try { 277 + int steps = 40; 278 + int savedVolume = midiVolume; 279 + for (int i = steps; i >= 0; i--) { 280 + int vol = savedVolume * i / steps; 281 + setVolume(vol); 282 + Thread.sleep(60); 283 + } 284 + midiVolume = savedVolume; 285 + } catch (Exception ignored) { 286 + } 287 + } 288 + 289 + private void fadeInMidi() { 290 + try { 291 + int steps = 30; 292 + int targetVolume = midiVolume; 293 + setVolume(0); 294 + for (int i = 1; i <= steps; i++) { 295 + int vol = targetVolume * i / steps; 296 + setVolume(vol); 297 + Thread.sleep(50); 298 + } 299 + } catch (Exception ignored) { 300 + } 265 301 } 266 302 267 303 /**
+21
src/main/resources/client-config.yaml
··· 1 + net: 2 + address: 127.0.0.1 3 + game_port: 43594 4 + ondemand_port: 43594 5 + jaggrab_port: 43595 6 + http_port: 8080 7 + cache: 8 + cacheDir: .377cache 9 + jaggrabEnabled: true 10 + rsa: 11 + rsaEnabled: false 12 + rsaPub: 65537 13 + rsaModulus: 170266381807335046121774073514220583891686029487165562794998484549236036467227923571770256617931840775621072487838687650522710227973331693237285456731778528244126984080232314114323601116304887478969296070648644633713088027922830600712492972687351204275625149978223159432963210789506993409208545916714905193639 14 + login: 15 + useStaticCredentials: false 16 + username: 17 + password: 18 + game: 19 + roofsEnabled: true 20 + freeTeleports: false 21 + debugContext: false
+1 -1
src/test/java/com/jagex/runescape/net/BufferTests.java
··· 2 2 3 3 import org.junit.jupiter.api.Assertions; 4 4 import org.junit.jupiter.api.DisplayName; 5 - import org.junit.jupiter.api.Test; 6 5 import org.junit.jupiter.params.ParameterizedTest; 7 6 import org.junit.jupiter.params.provider.CsvSource; 7 + import org.testng.annotations.Test; 8 8 9 9 import java.util.Random; 10 10