Showing preview only (604K chars total). Download the full file or copy to clipboard to get everything.
Repository: RelativityMC/raknetify
Branch: master
Commit: 76ab30a11a78
Files: 133
Total size: 553.6 KB
Directory structure:
gitextract_gavr_y8s/
├── .github/
│ └── workflows/
│ ├── build.yml
│ └── checkMappings.yml
├── .gitignore
├── .gitmodules
├── .run/
│ ├── BungeeCord Server.run.xml
│ └── Velocity Server.run.xml
├── HEADER.txt
├── Jenkinsfile
├── LICENSE
├── README.md
├── build.gradle
├── buildSrc/
│ ├── build.gradle
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── ishland/
│ └── buildscript/
│ └── ParseGItHubActionChangelog.java
├── bungee/
│ ├── build.gradle
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── ishland/
│ │ └── raknetify/
│ │ └── bungee/
│ │ ├── RaknetifyBungeePlugin.java
│ │ ├── connection/
│ │ │ ├── RakNetBungeeClientChannelEventListener.java
│ │ │ ├── RakNetBungeeConnectionUtil.java
│ │ │ ├── RakNetBungeePingUpdater.java
│ │ │ ├── RakNetBungeeServerChannelEventListener.java
│ │ │ └── StripFrameHandler.java
│ │ └── init/
│ │ ├── BungeeRaknetifyServer.java
│ │ └── InjectedSet.java
│ └── resources/
│ └── bungee.yml
├── common/
│ ├── build.gradle
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── ishland/
│ │ └── raknetify/
│ │ └── common/
│ │ ├── Constants.java
│ │ ├── connection/
│ │ │ ├── ByteBufCopyDecoder.java
│ │ │ ├── FrameDataBlocker.java
│ │ │ ├── MetricsSynchronizationHandler.java
│ │ │ ├── MultiChannelingStreamingCompression.java
│ │ │ ├── MultiChannellingEncryption.java
│ │ │ ├── NoFlush.java
│ │ │ ├── PacketEncryptionManager.java
│ │ │ ├── RakNetConnectionUtil.java
│ │ │ ├── RakNetSimpleMultiChannelCodec.java
│ │ │ ├── RaknetifyEventLoops.java
│ │ │ ├── SimpleMetricsLogger.java
│ │ │ ├── SynchronizationLayer.java
│ │ │ └── multichannel/
│ │ │ └── CustomPayloadChannel.java
│ │ ├── data/
│ │ │ └── ProtocolMultiChannelMappings.java
│ │ ├── package-info.java
│ │ └── util/
│ │ ├── DebugUtil.java
│ │ ├── MathUtil.java
│ │ ├── NetworkInterfaceListener.java
│ │ ├── PrefixUtil.java
│ │ ├── ReflectionUtil.java
│ │ └── ThreadLocalUtil.java
│ └── resources/
│ └── raknetify-channel-mappings.json
├── fabric/
│ ├── build.gradle
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── ishland/
│ │ └── raknetify/
│ │ └── fabric/
│ │ ├── RaknetifyFabric.java
│ │ ├── common/
│ │ │ ├── client/
│ │ │ │ ├── DebugHudUtil.java
│ │ │ │ └── DebugHudUtil1_21_9.java
│ │ │ ├── compat/
│ │ │ │ ├── package-info.java
│ │ │ │ └── viafabric/
│ │ │ │ └── ViaFabricCompatInjector.java
│ │ │ ├── connection/
│ │ │ │ ├── MultiChannellingPacketCapture.java
│ │ │ │ ├── RakNetClientConnectionUtil.java
│ │ │ │ ├── RakNetCompressionCompatibilityHandler.java
│ │ │ │ ├── RakNetFabricChannelEventListener.java
│ │ │ │ ├── RakNetFabricConnectionUtil.java
│ │ │ │ ├── RakNetMultiChannel.java
│ │ │ │ ├── RakNetNetworkTransitionUtil.java
│ │ │ │ └── encryption/
│ │ │ │ └── PacketEncryptionManagerInterface.java
│ │ │ ├── package-info.java
│ │ │ ├── quirks/
│ │ │ │ └── ClientHungerManager.java
│ │ │ └── util/
│ │ │ ├── FieldSignatureParser.java
│ │ │ ├── LegacySupportUtil.java
│ │ │ ├── MultiVersionUtil.java
│ │ │ └── NetworkStates.java
│ │ └── mixin/
│ │ ├── RaknetifyFabricMixinPlugin.java
│ │ ├── access/
│ │ │ ├── IClientConnection.java
│ │ │ ├── IClientPlayNetworkHandler.java
│ │ │ ├── IDebugHudEntries.java
│ │ │ ├── INetworkState1_20_4.java
│ │ │ ├── INetworkStateInternalPacketHandler.java
│ │ │ ├── IPacketCodecDispatcher.java
│ │ │ ├── IPacketCodecDispatcherPacketType.java
│ │ │ ├── IPacketEncryptionManager.java
│ │ │ ├── IServerPlayNetworkHandler.java
│ │ │ └── IWorld.java
│ │ ├── client/
│ │ │ ├── MixinClientPlayNetworkHandler.java
│ │ │ ├── MixinConnectionScreen1.java
│ │ │ ├── MixinMultiplayerServerListPinger.java
│ │ │ ├── MixinMultiplayerServerListPinger1.java
│ │ │ ├── MixinMultiplayerServerListPinger1_20_2.java
│ │ │ ├── MixinMultiplayerServerListPinger1_20_5.java
│ │ │ ├── MixinMultiplayerServerListPinger1_21_11.java
│ │ │ └── hud/
│ │ │ └── MixinDebugHud1_21_8.java
│ │ ├── common/
│ │ │ ├── MixinCCConnect.java
│ │ │ ├── MixinClientConnection.java
│ │ │ ├── MixinClientConnection1.java
│ │ │ ├── MixinClientConnection1_20_2.java
│ │ │ ├── MixinServerAddress.java
│ │ │ ├── encryption/
│ │ │ │ ├── MixinClientConnection.java
│ │ │ │ ├── MixinPacketDecryptor.java
│ │ │ │ ├── MixinPacketEncryptionManager.java
│ │ │ │ └── MixinPacketEncryptor.java
│ │ │ └── quirks/
│ │ │ ├── MixinPlayerEntity.java
│ │ │ └── MixinSampleSubscriptionTracker.java
│ │ ├── compat/
│ │ │ ├── fabricapi/
│ │ │ │ └── MixinServerLoginNetworkAddon.java
│ │ │ ├── krypton/
│ │ │ │ └── MixinServerLoginNetworkHandler.java
│ │ │ ├── package-info.java
│ │ │ └── qsl/
│ │ │ └── MixinServerLoginNetworkAddon.java
│ │ ├── package-info.java
│ │ └── server/
│ │ ├── MixinPlayerManager1_20_1.java
│ │ ├── MixinPlayerManager1_20_2.java
│ │ ├── MixinServerCommonNetworkHandler.java
│ │ ├── MixinServerLoginNetworkHandler.java
│ │ ├── MixinServerNetworkIo.java
│ │ ├── MixinServerNetworkIo1.java
│ │ └── MixinServerPlayNetworkHandler1_20_1.java
│ └── resources/
│ ├── fabric.mod.json
│ ├── raknetify-fabric.accesswidener
│ └── raknetify-fabric.mixins.json
├── genMappings.sh
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── jitpack.yml
├── modrinth_license.txt
├── settings.gradle
└── velocity/
├── HEADER.txt
├── LICENSE
├── build.gradle
└── src/
└── main/
├── java/
│ └── com/
│ └── ishland/
│ └── raknetify/
│ └── velocity/
│ ├── RaknetifyVelocityLaunchWrapper.java
│ ├── RaknetifyVelocityPlugin.java
│ ├── connection/
│ │ ├── RakNetVelocityChannelEventListener.java
│ │ ├── RakNetVelocityConnectionUtil.java
│ │ ├── RakNetVelocityPingUpdater.java
│ │ └── RakNetVelocityServerChannelEventListener.java
│ └── init/
│ ├── VelocityPacketRegistryInjector.java
│ └── VelocityRaknetifyServer.java
└── resources/
└── velocity-plugin.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/build.yml
================================================
# Automatically build the project and run any configured tests for every push
# and submitted pull request. This can help catch issues that only occur on
# certain platforms or Java versions, and provides a first line of defence
# against bad commits.
name: build
on: [pull_request, push]
jobs:
build:
strategy:
matrix:
# Use these Java versions
java: [
21, # Current Java LTS & minimum supported by Minecraft
]
# and run on both Linux and Windows
os: [ubuntu-24.04]
runs-on: ${{ matrix.os }}
steps:
- name: checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: true
- name: validate gradle wrapper
uses: gradle/actions/wrapper-validation@v4
- name: setup jdk ${{ matrix.java }}
uses: actions/setup-java@v2
with:
distribution: 'zulu'
java-version: ${{ matrix.java }}
java-package: jdk
- uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
./.gradle/loom-cache
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: build
run: ./gradlew build
- name: upload to modrinth and curseforge
run: ./gradlew modrinth publishMainPublicationToCurseForge
if: github.ref == 'refs/heads/master'
env:
MODRINTH_TOKEN: ${{ secrets.MODRINTH_UPLOAD_TOKEN }}
CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_API_TOKEN }}
GITHUB_EVENT_RAW_PATH: ${{ github.event_path }}
continue-on-error: true
- name: capture build artifacts
if: ${{ runner.os == 'Linux' && matrix.java == '21' }} # Only upload artifacts built from latest java on one OS
uses: actions/upload-artifact@v4
with:
name: Artifacts
path: ./**/build/libs/*-all.jar
================================================
FILE: .github/workflows/checkMappings.yml
================================================
name: check mappings
on: [pull_request, push]
jobs:
checkMappings:
strategy:
matrix:
# Use these Java versions
java: [
21, # Current Java LTS & minimum supported by Minecraft
]
# and run on both Linux and Windows
os: [ubuntu-24.04]
runs-on: ${{ matrix.os }}
steps:
- name: checkout repository
uses: actions/checkout@v2
with:
fetch-depth: 0
submodules: true
- name: validate gradle wrapper
uses: gradle/actions/wrapper-validation@v4
- name: setup jdk ${{ matrix.java }}
uses: actions/setup-java@v2
with:
distribution: 'zulu'
java-version: ${{ matrix.java }}
java-package: jdk
- uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
./.gradle/loom-cache
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: "cache mappingsGen"
uses: actions/cache@v4
with:
path: |
./run-mappingsGen
key: ${{ runner.os }}-mappingsGen-${{ hashFiles('./genMappings.sh') }}
restore-keys: |
${{ runner.os }}-mappingsGen-
- name: generate mappings
env:
MAPPINGS_GEN_FRESH: true
run: ./genMappings.sh
- name: fail if mappings changed
run: |
git update-index --refresh
if ! git diff-index --quiet HEAD --; then
echo "Mappings changed, please commit them"
git diff
git diff --staged
exit 1
fi
================================================
FILE: .gitignore
================================================
# gradle
.gradle/
build/
out/
classes/
# eclipse
*.launch
# idea
.idea/
*.iml
*.ipr
*.iws
# vscode
.settings/
.vscode/
bin/
.classpath
.project
# macos
*.DS_Store
# fabric
run*/
================================================
FILE: .gitmodules
================================================
[submodule "netty-raknet"]
path = netty-raknet
url = https://github.com/RelativityMC/netty-raknet.git
================================================
FILE: .run/BungeeCord Server.run.xml
================================================
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="BungeeCord Server" type="Application" factoryName="Application">
<classpathModifications>
<entry exclude="true" path="$PROJECT_DIR$/bungee/build/classes/java/main" />
<entry exclude="true" path="$PROJECT_DIR$/bungee/build/resources/main" />
<entry exclude="true" path="$PROJECT_DIR$/common/build/classes/java/main" />
<entry exclude="true" path="$PROJECT_DIR$/common/build/resources/main" />
</classpathModifications>
<option name="MAIN_CLASS_NAME" value="net.md_5.bungee.BungeeCordLauncher" />
<module name="raknetify.bungee.main" />
<option name="VM_PARAMETERS" value="-Xmx1G -Draknetify.debug=true -Dio.netty.leakDetection.level=advanced" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/run-bungee" />
<method v="2">
<option name="Make" enabled="true" />
<option name="Gradle.BeforeRunTask" enabled="false" tasks="prepareRunBungee" externalProjectPath="$PROJECT_DIR$/bungee" vmOptions="" scriptParameters="" />
</method>
</configuration>
</component>
================================================
FILE: .run/Velocity Server.run.xml
================================================
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Velocity Server" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="com.velocitypowered.proxy.Velocity" />
<module name="com.ishland.raknetify.velocity.raknetify.velocity.main" />
<option name="VM_PARAMETERS" value="-Xmx1G -Draknetify.debug=true -Dio.netty.leakDetection.level=advanced" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/run-velocity" />
<method v="2">
<option name="Make" enabled="true" />
<option name="Gradle.BeforeRunTask" enabled="false" tasks="prepareRunVelocity" externalProjectPath="$PROJECT_DIR$/velocity" vmOptions="" scriptParameters="" />
</method>
</configuration>
</component>
================================================
FILE: HEADER.txt
================================================
This file is a part of the Raknetify project, licensed under MIT.
Copyright (c) 2022-2025 ishland
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: Jenkinsfile
================================================
pipeline {
agent { label 'slave' }
options { timestamps() }
stages {
stage('SCM-SKIP') {
steps {
scmSkip(skipPattern:'.*\\[ci skip\\].*')
}
}
stage('Build') {
tools {
jdk "OpenJDK 21"
}
steps {
withMaven(
maven: '3',
mavenLocalRepo: '.repository',
publisherStrategy: 'EXPLICIT'
) {
sh 'git fetch --tags'
sh 'git reset --hard'
sh './gradlew clean build'
}
}
post {
success {
archiveArtifacts artifacts: "**/build/libs/*-all.jar", fingerprint: true
}
failure {
cleanWs()
}
}
}
}
}
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2022-2025 ishland
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
================================================
FILE: README.md
================================================
# Raknetify
A Fabric mod / Velocity plugin / BungeeCord plugin that uses RakNet to improve multiplayer experience significantly
under unreliable and rate-limited connections.
# Features
- Higher reliability and lower latency under unreliable and rate-limited client connections.
- Uses RakNet's multiple channels with priorities to achieve higher responsiveness.
- Supports ViaVersion client-side and ViaVersion server-side.
# How to use it?
## Prerequisites
- Raknetify is designed to work on Minecraft 1.17.1+
Note: On proxies such as Velocity and BungeeCord, **unsupported client version** will cause
multi-channelling failing to initialize, causing **reduced responsiveness**.
- You need to have a UDP port opened at the same port number of your normal server port.
## Installation and usage
- Download the latest release from
[GitHub](https://github.com/RelativityMC/raknetify/releases)
[Modrinth (Fabric)](https://modrinth.com/mod/raknetify/versions)
[CurseForge (Fabric)](https://www.curseforge.com/minecraft/mc-mods/raknetify/files)
[SpigotMC (BungeeCord)](https://www.spigotmc.org/resources/raknetify-bungeecord.102509/)
or development builds from [CodeMC](https://ci.codemc.io/job/RelativityMC/job/raknetify/)
- Install the mod on both client and server. (Installation on backend servers are not needed if using on proxies)
- Prefix your server address with `raknet;` (or `raknetl;` to use high mtu) and save or connect directly.
(e.g. `raknet;example.com`)
- Enjoy!
================================================
FILE: build.gradle
================================================
plugins {
id 'fabric-loom' version '1.13-SNAPSHOT' apply false
id 'com.gradleup.shadow' version '9.2.1' apply false
id 'dev.yumi.gradle.licenser' version '2.1.1' apply false
id 'com.modrinth.minotaur' version '2.+' apply false
id 'io.github.themrmilchmann.curseforge-publish' version '0.8.0' apply false
}
subprojects {
apply plugin: 'java'
apply plugin: 'java-library'
apply plugin: 'maven-publish'
apply plugin: 'dev.yumi.gradle.licenser'
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
version = project.mod_version + "." + getVersionSuffix()
license {
rule(rootProject.file('HEADER.txt'))
}
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
maven { url 'https://repo.codemc.org/repository/maven-public' }
maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
}
// configure the maven publication
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
}
}
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
repositories {
// Add repositories to publish to here.
// Notice: This block does NOT have the same function as the block in the top level.
// The repositories here will be used for publishing your artifact, not for
// retrieving dependencies.
}
}
tasks.withType(AbstractArchiveTask) {
preserveFileTimestamps = false
reproducibleFileOrder = true
}
afterEvaluate {
if (it.plugins.hasPlugin("com.modrinth.minotaur")) {
modrinth {
token = System.getenv("MODRINTH_TOKEN") // This is the default. Remember to have the MODRINTH_TOKEN environment variable set or else this will fail, or set it to whatever you want - just make sure it stays private!
projectId = "raknetify" // This can be the project ID or the slug. Either will work!
// versionNumber = project.version + "+" + project.minecraft_version // You don't need to set this manually. Will fail if Modrinth has this version already
// versionName = project.version + " devbuild for " + project.minecraft_version
versionType = "alpha" // This is the default -- can also be `beta` or `alpha`
// uploadFile = remapShadowJar // With Loom, this MUST be set to `remapJar` instead of `jar`!
gameVersions = ["1.18", "1.18.1", "1.18.2", "1.19", "1.19.1", "1.19.2", "1.19.3", "1.19.4", "1.20", "1.20.1", "1.20.2", "1.20.3", "1.20.4", "1.20.5", "1.20.6", "1.21", "1.21.1", "1.21.2", "1.21.3", "1.21.4", "1.21.5", "1.21.6", "1.21.7", "1.21.8", "1.21.9", "1.21.10", "1.21.11"]
// loaders = ["fabric"]
changelog = com.ishland.buildscript.ParseGItHubActionChangelog.getChangelog()
}
}
}
}
afterEvaluate {
logger.lifecycle("Version String: ${project.mod_version + '.' + getVersionSuffix()}")
logger.lifecycle(com.ishland.buildscript.ParseGItHubActionChangelog.getChangelog())
}
String getVersionSuffix() {
def stdout = providers.exec {
commandLine 'git', 'describe', '--tags', '--dirty', '--broken'
}.standardOutput.asText.get()
stdout = stdout.toString().strip()
def suffix = ""
if (stdout.endsWith("-dirty")) {
stdout = stdout.substring(0, stdout.length() - "-dirty".length())
suffix = "-dirty"
}
if (stdout.indexOf('-') < 0) {
return "0" + suffix;
}
def split = stdout.split('-')
return split[split.length - 2] + suffix
}
================================================
FILE: buildSrc/build.gradle
================================================
plugins {
id 'java'
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
repositories {
mavenCentral()
}
dependencies {
// https://mvnrepository.com/artifact/com.google.code.gson/gson
implementation 'com.google.code.gson:gson:+'
}
================================================
FILE: buildSrc/src/main/java/com/ishland/buildscript/ParseGItHubActionChangelog.java
================================================
package com.ishland.buildscript;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import java.nio.file.Files;
import java.nio.file.Path;
public class ParseGItHubActionChangelog {
public static String getChangelog() throws Throwable {
final String path = System.getenv("GITHUB_EVENT_RAW_PATH");
if (path == null || path.isBlank()) return "No changelog was specified. ";
final JsonObject jsonObject = new Gson().fromJson(Files.readString(Path.of(path)), JsonObject.class);
StringBuilder builder = new StringBuilder();
builder.append("This version is uploaded automatically by GitHub Actions. \n\n")
.append("Changelog: \n");
final JsonArray commits = jsonObject.getAsJsonArray("commits");
if (commits.isEmpty()) {
builder.append("No changes detected. \n");
} else {
for (JsonElement commit : commits) {
JsonObject object = commit.getAsJsonObject();
builder.append("- ");
builder.append('[').append(object.get("id").getAsString(), 0, 8).append(']')
.append('(').append(object.get("url").getAsString()).append(')');
builder.append(' ');
builder.append(object.get("message").getAsString().split("\n")[0]);
builder.append(" - ");
builder.append(object.get("author").getAsJsonObject().get("name").getAsString());
builder.append(" ").append('\n');
}
}
return builder.toString();
}
}
================================================
FILE: bungee/build.gradle
================================================
import java.nio.file.Files
import java.nio.file.StandardCopyOption
evaluationDependsOn(":")
apply plugin: 'com.gradleup.shadow'
apply plugin: 'com.modrinth.minotaur'
base {
archivesName = project.archives_base_name + "-bungee"
}
group = project.maven_group + ".bungee"
repositories {
ivy {
url 'https://ci.md-5.net/job/'
patternLayout {
// artifact "[organization]/versions/[module]/builds/[revision]/downloads/[organization]-[module]-[revision](.[ext])"
// https://ci.md-5.net/job/BungeeCord/1636/artifact/bootstrap/target/BungeeCord.jar
artifact "[module]/[revision]/artifact/bootstrap/target/BungeeCord(.[ext])"
}
metadataSources {
it.artifact()
}
}
}
configurations {
shadowInclude
}
dependencies {
implementation 'bungee:BungeeCord:2000'
// implementation "net.md-5:bungeecord-proxy:1.+"
// compileOnly 'org.projectlombok:lombok:1.18.24' // for bungeecord sources
shadowInclude implementation(project(":common"))
}
processResources {
inputs.property "version", project.version
filesMatching("bungee.yml") {
expand "version": project.version
}
}
jar {
exclude "META-INF/LICENSE.txt"
exclude "META-INF/NOTICE.txt"
from "LICENSE"
}
shadowJar {
// dependencies {
// exclude(dependency('it.unimi.dsi:fastutil'))
// }
exclude "META-INF/LICENSE.txt"
exclude "META-INF/NOTICE.txt"
minimize()
archiveClassifier = "all"
configurations = [ project.configurations.shadowInclude ]
from "LICENSE"
}
assemble.dependsOn(shadowJar)
tasks.withType(AbstractArchiveTask) {
preserveFileTimestamps = false
reproducibleFileOrder = true
}
modrinth {
versionNumber = project.version + "+bungeecord"
versionName = project.version + " devbuild for BungeeCord"
uploadFile = shadowJar
loaders = ["bungeecord", "waterfall"]
}
task("prepareRunBungee", dependsOn: shadowJar) {
doFirst {
Files.createDirectories(rootProject.projectDir.toPath().resolve("run-bungee").resolve("plugins"))
Files.copy(shadowJar.archiveFile.getAsFile().get().toPath(), rootProject.projectDir.toPath().resolve("run-bungee").resolve("plugins").resolve("raknetify-bungee-devlaunch.jar"), StandardCopyOption.REPLACE_EXISTING)
}
}
================================================
FILE: bungee/src/main/java/com/ishland/raknetify/bungee/RaknetifyBungeePlugin.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.bungee;
import com.ishland.raknetify.bungee.connection.RakNetBungeeConnectionUtil;
import com.ishland.raknetify.bungee.init.BungeeRaknetifyServer;
import com.ishland.raknetify.common.data.ProtocolMultiChannelMappings;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.event.ServerConnectedEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.event.EventHandler;
import java.util.logging.Logger;
public class RaknetifyBungeePlugin extends Plugin implements Listener {
public static Logger LOGGER;
public static RaknetifyBungeePlugin INSTANCE;
@Override
public void onEnable() {
super.onEnable();
INSTANCE = this;
LOGGER = this.getLogger();
ProtocolMultiChannelMappings.init();
BungeeRaknetifyServer.inject();
this.getProxy().getPluginManager().registerListener(this, this);
}
@Override
public void onDisable() {
super.onDisable();
BungeeRaknetifyServer.disable();
}
@EventHandler
public void onPostLogin(PostLoginEvent evt) {
RakNetBungeeConnectionUtil.onPlayerLogin(evt);
}
@EventHandler
public void handleServerSwitch(ServerConnectedEvent evt) {
RakNetBungeeConnectionUtil.handleServerSwitch(evt);
}
}
================================================
FILE: bungee/src/main/java/com/ishland/raknetify/bungee/connection/RakNetBungeeClientChannelEventListener.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.bungee.connection;
import com.google.common.base.Preconditions;
import com.ishland.raknetify.bungee.RaknetifyBungeePlugin;
import com.ishland.raknetify.common.connection.MultiChannelingStreamingCompression;
import com.ishland.raknetify.common.connection.MultiChannellingEncryption;
import com.ishland.raknetify.common.connection.RakNetSimpleMultiChannelCodec;
import com.ishland.raknetify.common.connection.SynchronizationLayer;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import net.md_5.bungee.EncryptionUtil;
import net.md_5.bungee.netty.ChannelWrapper;
import net.md_5.bungee.netty.HandlerBoss;
import net.md_5.bungee.netty.PipelineUtils;
import net.md_5.bungee.protocol.PacketWrapper;
import net.md_5.bungee.protocol.Protocol;
import net.md_5.bungee.protocol.packet.Commands;
import net.md_5.bungee.protocol.packet.EncryptionResponse;
import net.md_5.bungee.protocol.packet.FinishConfiguration;
import net.md_5.bungee.protocol.packet.Login;
import net.md_5.bungee.protocol.packet.Respawn;
import net.md_5.bungee.protocol.packet.SetCompression;
import net.md_5.bungee.protocol.packet.StartConfiguration;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.lang.reflect.Field;
import java.security.GeneralSecurityException;
import java.util.logging.Level;
public class RakNetBungeeClientChannelEventListener extends ChannelDuplexHandler {
public static final String NAME = "raknetify-bungee-event-listener";
private SecretKey encryptionKey = null;
private boolean needResetCompression = false;
private Protocol protocol = null;
private int protocolVersion = -1;
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if (this.needResetCompression) {
RaknetifyBungeePlugin.LOGGER.info("Preventing vanilla compression as streaming compression is enabled");
// // note: this may corrupt bungeecords compression state, find a better way to do this
// ctx.channel().pipeline().replace("compress", "compress", new ChannelDuplexHandler()); // no-op
// ctx.channel().pipeline().replace("decompress", "decompress", new ChannelDuplexHandler()); // no-op
final HandlerBoss handlerBoss = ctx.channel().pipeline().get(HandlerBoss.class);
final Field channelField = HandlerBoss.class.getDeclaredField("channel");
channelField.setAccessible(true);
final ChannelWrapper channelWrapper = (ChannelWrapper) channelField.get(handlerBoss);
Preconditions.checkState(channelWrapper != null, "channelWrapper is null");
channelWrapper.setCompressionThreshold(-1);
this.needResetCompression = false;
}
if (this.encryptionKey != null) {
ctx.channel().pipeline().replace(PipelineUtils.DECRYPT_HANDLER, PipelineUtils.DECRYPT_HANDLER, new ChannelDuplexHandler());
ctx.channel().pipeline().replace(PipelineUtils.ENCRYPT_HANDLER, PipelineUtils.ENCRYPT_HANDLER, new ChannelDuplexHandler());
ctx.channel().pipeline().addBefore(MultiChannelingStreamingCompression.NAME, MultiChannellingEncryption.NAME, new MultiChannellingEncryption(encryptionKey));
this.encryptionKey = null;
}
if (msg instanceof SetCompression) {
final MultiChannelingStreamingCompression compression = ctx.channel().pipeline().get(MultiChannelingStreamingCompression.class);
if (compression != null && compression.isActive()) {
RaknetifyBungeePlugin.LOGGER.info("Preventing vanilla compression as streaming compression is enabled");
promise.setSuccess(); // swallow SetCompression packet
needResetCompression = true;
return;
}
} else if (msg instanceof Respawn || msg instanceof Login || msg instanceof StartConfiguration || msg instanceof FinishConfiguration) {
ctx.write(SynchronizationLayer.SYNC_REQUEST_OBJECT); // sync
super.write(ctx, msg, promise);
return;
} else if (msg instanceof Commands) {
ctx.write(RakNetSimpleMultiChannelCodec.SIGNAL_START_MULTICHANNEL);
super.write(ctx, msg, promise);
return;
}
super.write(ctx, msg, promise);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof PacketWrapper wrapper) {
if (wrapper.packet instanceof EncryptionResponse packet) {
try {
this.encryptionKey = getSecretUnchecked(packet);
} catch (Throwable t) {
RaknetifyBungeePlugin.LOGGER.log(Level.WARNING, "Failed to decrypt captured encryption secret, the raknetify connection is broken", t);
}
}
}
super.channelRead(ctx, msg);
}
private static SecretKey getSecretUnchecked(EncryptionResponse resp) throws GeneralSecurityException {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, EncryptionUtil.keys.getPrivate());
return new SecretKeySpec(cipher.doFinal(resp.getSharedSecret()), "AES");
}
public void setProtocol(Protocol protocol, int protocolVersion) {
this.protocol = protocol;
this.protocolVersion = protocolVersion;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
cause.printStackTrace();
}
// @Override
// public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
// super.userEventTriggered(ctx, evt);
// if (evt == VelocityConnectionEvent.COMPRESSION_ENABLED) {
// final MultiChannelingStreamingCompression compression = ctx.channel().pipeline().get(MultiChannelingStreamingCompression.class);
// if (compression != null && compression.isActive()) {
// RaknetifyVelocityPlugin.LOGGER.info("Preventing vanilla compression as streaming compression is enabled");
// ctx.channel().pipeline().replace(Connections.COMPRESSION_ENCODER, Connections.COMPRESSION_ENCODER, new ChannelDuplexHandler()); // no-op
// ctx.channel().pipeline().replace(Connections.COMPRESSION_DECODER, Connections.COMPRESSION_DECODER, new ChannelDuplexHandler()); // no-op
// }
// } else if (evt == VelocityConnectionEvent.COMPRESSION_DISABLED) {
// ctx.channel().pipeline().replace(Connections.FRAME_DECODER, Connections.FRAME_DECODER, new ChannelDuplexHandler()); // no-op
// ctx.channel().pipeline().replace(Connections.FRAME_ENCODER, Connections.FRAME_ENCODER, new ChannelDuplexHandler()); // no-op
// } else if (evt == VelocityConnectionEvent.ENCRYPTION_ENABLED) {
// Preconditions.checkState(this.encryptionKey != null, "EncryptionResponse not received yet or already consumed");
// ctx.channel().pipeline().replace(Connections.CIPHER_ENCODER, Connections.CIPHER_ENCODER, new ChannelDuplexHandler());
// ctx.channel().pipeline().replace(Connections.CIPHER_DECODER, Connections.CIPHER_DECODER, new ChannelDuplexHandler());
// ctx.channel().pipeline().addAfter(MultiChannelingStreamingCompression.NAME, MultiChannellingEncryption.NAME, new MultiChannellingEncryption(encryptionKey));
// }
// }
}
================================================
FILE: bungee/src/main/java/com/ishland/raknetify/bungee/connection/RakNetBungeeConnectionUtil.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.bungee.connection;
import com.ishland.raknetify.bungee.RaknetifyBungeePlugin;
import com.ishland.raknetify.common.Constants;
import com.ishland.raknetify.common.connection.MultiChannelingStreamingCompression;
import com.ishland.raknetify.common.connection.RakNetConnectionUtil;
import com.ishland.raknetify.common.connection.RakNetSimpleMultiChannelCodec;
import com.ishland.raknetify.common.connection.SynchronizationLayer;
import com.ishland.raknetify.common.connection.multichannel.CustomPayloadChannel;
import com.ishland.raknetify.common.data.ProtocolMultiChannelMappings;
import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.handler.codec.haproxy.HAProxyMessageDecoder;
import net.md_5.bungee.ServerConnection;
import net.md_5.bungee.UserConnection;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.event.ServerConnectedEvent;
import net.md_5.bungee.netty.ChannelWrapper;
import net.md_5.bungee.netty.PipelineUtils;
import net.md_5.bungee.protocol.MinecraftEncoder;
import net.md_5.bungee.protocol.Protocol;
import net.md_5.bungee.protocol.packet.PluginMessage;
import network.ycc.raknet.RakNet;
import java.lang.reflect.Field;
import static com.ishland.raknetify.common.util.ReflectionUtil.accessible;
public class RakNetBungeeConnectionUtil {
private static final Field USER_CONNECTION_CH;
private static final Field SERVER_CONNECTION_CH;
private static final Field ENCODER_PROTOCOL_VERSION;
static {
try {
USER_CONNECTION_CH = accessible(UserConnection.class.getDeclaredField("ch"));
SERVER_CONNECTION_CH = accessible(ServerConnection.class.getDeclaredField("ch"));
ENCODER_PROTOCOL_VERSION = accessible(MinecraftEncoder.class.getDeclaredField("protocolVersion"));
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
private RakNetBungeeConnectionUtil() {
}
public static void initChannel(Channel channel) {
if (channel.config() instanceof RakNet.Config) {
RakNetConnectionUtil.initChannel(channel);
// channel.pipeline().addAfter(MultiChannelingStreamingCompression.NAME, MultiChannellingDataCodec.NAME, new MultiChannellingDataCodec(Constants.RAKNET_GAME_PACKET_ID));
channel.pipeline().addAfter(MultiChannelingStreamingCompression.NAME, RakNetSimpleMultiChannelCodec.NAME, new RakNetSimpleMultiChannelCodec(Constants.RAKNET_GAME_PACKET_ID));
}
}
public static void postInitChannel(Channel channel, boolean isClientSide) {
if (channel.config() instanceof RakNet.Config) {
channel.pipeline().replace(PipelineUtils.TIMEOUT_HANDLER, PipelineUtils.TIMEOUT_HANDLER, new ChannelDuplexHandler()); // no-op
channel.pipeline().replace(PipelineUtils.FRAME_DECODER, PipelineUtils.FRAME_DECODER, new ChannelDuplexHandler()); // no-op
channel.pipeline().addBefore(PipelineUtils.FRAME_PREPENDER_AND_COMPRESS, StripFrameHandler.NAME, StripFrameHandler.INSTANCE); // no-op
if (channel.pipeline().get(HAProxyMessageDecoder.class) != null)
channel.pipeline().remove(HAProxyMessageDecoder.class);
// channel.pipeline().replace(PipelineUtils.WRITE_TIMEOUT_HANDLER, PipelineUtils.WRITE_TIMEOUT_HANDLER, new DebugWriteTimeoutHandler(30));
channel.pipeline().addBefore(PipelineUtils.BOSS_HANDLER, RakNetBungeeClientChannelEventListener.NAME, new RakNetBungeeClientChannelEventListener());
// System.out.println(channel.pipeline().names());
// final MultiChannellingPacketCapture handler = new MultiChannellingPacketCapture();
// channel.pipeline().addLast("raknetify-multi-channel-packet-cature", handler);
// channel.pipeline().get(MultiChannellingDataCodec.class).setCapture(handler);
}
}
public static void onPlayerLogin(PostLoginEvent evt) {
try {
final UserConnection player = (UserConnection) evt.getPlayer();
final ChannelWrapper channelWrapper = (ChannelWrapper) USER_CONNECTION_CH.get(player);
final Channel channel = channelWrapper.getHandle();
if (channel != null && channel.config() instanceof RakNet.Config config) {
// multi-channel setup
final RakNetSimpleMultiChannelCodec multiChannelCodec = channel.pipeline().get(RakNetSimpleMultiChannelCodec.class);
if (multiChannelCodec != null) {
final int protocolVersion = (int) ENCODER_PROTOCOL_VERSION.get(channel.pipeline().get(MinecraftEncoder.class));
final ProtocolMultiChannelMappings.VersionMapping versionMapping = ProtocolMultiChannelMappings.INSTANCE.mappings.get(protocolVersion);
if (versionMapping != null) {
// handle custom payload separately
final Object directionDataToClient = accessible(Protocol.class.getDeclaredField("TO_CLIENT")).get(Protocol.GAME);
final int pluginMessageId = (int) accessible(Class.forName("net.md_5.bungee.protocol.Protocol$DirectionData").getDeclaredMethod("getId", Class.class, int.class))
.invoke(directionDataToClient, PluginMessage.class, protocolVersion);
if (Constants.DEBUG) RaknetifyBungeePlugin.LOGGER.info("PluginMessage packetId=%d at version=%d".formatted(pluginMessageId, protocolVersion));
multiChannelCodec.addHandler(new CustomPayloadChannel.OverrideHandler(value -> value == pluginMessageId));
// packet id -> channel id
multiChannelCodec.addHandler(new RakNetSimpleMultiChannelCodec.PacketIdBasedOverrideHandler(
versionMapping.s2c,
"protocol version %d".formatted(protocolVersion)
));
} else {
RaknetifyBungeePlugin.LOGGER.warning("No multi-channel mapping found for protocol version %d, reduced responsiveness is expected"
.formatted(protocolVersion));
}
}
// ping update setup
channel.pipeline().addBefore(PipelineUtils.BOSS_HANDLER, RakNetBungeePingUpdater.NAME, new RakNetBungeePingUpdater(player));
RaknetifyBungeePlugin.LOGGER.info(String.format("Raknetify: %s logged in via RakNet, mtu %d", player.getName(), config.getMTU()));
}
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
public static void handleServerSwitch(ServerConnectedEvent evt) {
try {
final UserConnection player = (UserConnection) evt.getPlayer();
final Channel playerChannel = ((ChannelWrapper) USER_CONNECTION_CH.get(player)).getHandle();
if (playerChannel != null && playerChannel.config() instanceof RakNet.Config config) {
// this exists because bungeecord sends several packets to reset state before Respawn packet during server switch
playerChannel.write(SynchronizationLayer.SYNC_REQUEST_OBJECT);
// and inject into the server channel
final ServerConnection server = (ServerConnection) evt.getServer();
final Channel serverChannel = ((ChannelWrapper) SERVER_CONNECTION_CH.get(server)).getHandle();
serverChannel.pipeline().addBefore(PipelineUtils.BOSS_HANDLER, RakNetBungeeServerChannelEventListener.NAME, new RakNetBungeeServerChannelEventListener(playerChannel));
}
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
}
================================================
FILE: bungee/src/main/java/com/ishland/raknetify/bungee/connection/RakNetBungeePingUpdater.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.bungee.connection;
import com.google.common.base.Preconditions;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.concurrent.ScheduledFuture;
import net.md_5.bungee.UserConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import network.ycc.raknet.RakNet;
import network.ycc.raknet.packet.Ping;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
public class RakNetBungeePingUpdater extends ChannelDuplexHandler {
public static final String NAME = "raknetify-bungee-ping-updater";
private final UserConnection player;
ScheduledFuture<?> updateTask = null;
public RakNetBungeePingUpdater(UserConnection player) {
this.player = Objects.requireNonNull(player);
}
public void handlerAdded(ChannelHandlerContext ctx) {
if (ctx.channel().config() instanceof RakNet.Config config) {
updateTask = ctx.channel().eventLoop().scheduleAtFixedRate(
() -> player.setPing((int) ((config.getRTTNanos() + config.getRTTStdDevNanos()) / 1_000_000)),
0, 1000, TimeUnit.MILLISECONDS
);
}
}
public void handlerRemoved(ChannelHandlerContext ctx) {
if (updateTask != null) {
updateTask.cancel(false);
updateTask = null;
}
}
}
================================================
FILE: bungee/src/main/java/com/ishland/raknetify/bungee/connection/RakNetBungeeServerChannelEventListener.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.bungee.connection;
import com.google.common.base.Preconditions;
import com.ishland.raknetify.bungee.RaknetifyBungeePlugin;
import com.ishland.raknetify.common.Constants;
import com.ishland.raknetify.common.connection.SynchronizationLayer;
import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.PacketWrapper;
import net.md_5.bungee.protocol.packet.KeepAlive;
import net.md_5.bungee.protocol.packet.Respawn;
import network.ycc.raknet.RakNet;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class RakNetBungeeServerChannelEventListener extends ChannelDuplexHandler {
public static final String NAME = "raknetify-bungee-downstream-event-listener";
private final Channel clientChannel;
public RakNetBungeeServerChannelEventListener(Channel clientChannel) {
Preconditions.checkArgument(clientChannel.config() instanceof RakNet.Config);
this.clientChannel = clientChannel;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof PacketWrapper wrapper) {
final DefinedPacket packet = wrapper.packet;
if (packet instanceof Respawn) {
clientChannel.write(SynchronizationLayer.SYNC_REQUEST_OBJECT);
} else if (packet instanceof KeepAlive) {
if (Constants.DEBUG) RaknetifyBungeePlugin.LOGGER.info("Received downstream keepalive, swallowing it");
final long rttNanos = RakNet.config(clientChannel).getRTTNanos();
// ProxyServer.getInstance().getScheduler()
// .schedule(RaknetifyBungeePlugin.INSTANCE, () -> ctx.write(packet), Math.max(rttNanos - 4_000_000, 0), TimeUnit.NANOSECONDS); // reduce delay to aid scheduling overhead
ctx.channel().eventLoop().schedule(() -> ctx.writeAndFlush(packet), Math.max(rttNanos - 4_000_000, 0), TimeUnit.NANOSECONDS); // reduce delay to aid scheduling overhead
return; // prevent keepalive from being sent to clients
}
}
super.channelRead(ctx, msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
cause.printStackTrace();
for (Map.Entry<String, ChannelHandler> entry : ctx.channel().pipeline().toMap().entrySet()) {
System.out.println("%s: %s".formatted(entry.getKey(), entry.getValue().getClass().getName()));
}
}
}
================================================
FILE: bungee/src/main/java/com/ishland/raknetify/bungee/connection/StripFrameHandler.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.bungee.connection;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
import net.md_5.bungee.protocol.DefinedPacket;
@ChannelHandler.Sharable
public class StripFrameHandler extends ChannelOutboundHandlerAdapter {
public static final String NAME = "raknetify-bungee-strip-frame";
public static final StripFrameHandler INSTANCE = new StripFrameHandler();
private StripFrameHandler() {
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if (msg instanceof ByteBuf buf) {
DefinedPacket.readVarInt(buf);
ctx.write(buf.slice(), promise);
return;
}
super.write(ctx, msg, promise);
}
}
================================================
FILE: bungee/src/main/java/com/ishland/raknetify/bungee/init/BungeeRaknetifyServer.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.bungee.init;
import com.ishland.raknetify.bungee.RaknetifyBungeePlugin;
import com.ishland.raknetify.bungee.connection.RakNetBungeeConnectionUtil;
import com.ishland.raknetify.common.Constants;
import com.ishland.raknetify.common.connection.RakNetConnectionUtil;
import com.ishland.raknetify.common.util.NetworkInterfaceListener;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.FixedRecvByteBufAllocator;
import io.netty.channel.ReflectiveChannelFactory;
import io.netty.channel.socket.DatagramChannel;
import io.netty.util.AttributeKey;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.netty.PipelineUtils;
import net.md_5.bungee.query.QueryHandler;
import network.ycc.raknet.server.channel.RakNetServerChannel;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.stream.Stream;
import static com.ishland.raknetify.common.util.ReflectionUtil.accessible;
public class BungeeRaknetifyServer {
private static final int portOverride = Integer.getInteger("raknetify.bungee.portOverride", -1);
private static final Method INIT_CHANNEL;
private static final Field BUNGEE_LISTENERS_FIELD;
private static final Class<?> ACCEPTOR_CLASS;
static {
try {
INIT_CHANNEL = accessible(ChannelInitializer.class.getDeclaredMethod("initChannel", Channel.class));
BUNGEE_LISTENERS_FIELD = accessible(BungeeCord.class.getDeclaredField("listeners"));
ACCEPTOR_CLASS = Class.forName("io.netty.bootstrap.ServerBootstrap$ServerBootstrapAcceptor");
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
private static final Reference2ReferenceOpenHashMap<Channel, ReferenceOpenHashSet<ChannelFuture>> channels = new Reference2ReferenceOpenHashMap<>();
private static final ReferenceOpenHashSet<ChannelFuture> nonWildcardChannels = new ReferenceOpenHashSet<>();
private static volatile boolean active = false;
private static volatile int activeIndex = 0;
private static boolean injected = false;
private static volatile Consumer<NetworkInterfaceListener.InterfaceAddressChangeEvent> listener = null;
public static void inject() {
if (active) return;
try {
active = true;
final BungeeCord instance = (BungeeCord) ProxyServer.getInstance();
// stopAllQueryPorts();
final Set<Channel> listeners = (Set<Channel>) BUNGEE_LISTENERS_FIELD.get(instance);
if (!injected) {
BUNGEE_LISTENERS_FIELD.set(instance, new InjectedSet(listeners));
injected = true;
}
List<Throwable> errors = new ArrayList<>();
for (Channel listener : listeners) {
try {
injectChannel(instance, listener, true);
} catch (Throwable t) {
errors.add(t);
}
}
int currentActiveIndex = ++activeIndex;
listener = event -> {
if (!active) {
NetworkInterfaceListener.removeListener(listener);
}
if (currentActiveIndex != activeIndex) return; // we can't remove ourselves now, is plugin reloaded?
if (event.added()) {
for (Channel channel : channels.keySet()) {
injectChannel(instance, channel, false);
}
} else {
for (ReferenceOpenHashSet<ChannelFuture> futures : channels.values()) {
for (ObjectIterator<ChannelFuture> iterator = futures.iterator(); iterator.hasNext(); ) {
ChannelFuture future = iterator.next();
if (((InetSocketAddress) future.channel().localAddress()).getAddress().equals(event.address())) {
RaknetifyBungeePlugin.LOGGER.info("Closing Raknetify server %s".formatted(future.channel().localAddress()));
future.channel().close();
iterator.remove();
}
}
}
}
};
NetworkInterfaceListener.addListener(event ->
instance.getScheduler().schedule(RaknetifyBungeePlugin.INSTANCE, () -> listener.accept(event), 100, TimeUnit.MILLISECONDS));
if (!errors.isEmpty()) {
final RuntimeException exception = new RuntimeException("Failed to start Raknetify server");
errors.forEach(exception::addSuppressed);
throw exception;
}
} catch (Throwable t) {
throw new RuntimeException("Failed to start Raknetify server", t);
}
}
public static void injectChannel(BungeeCord instance, Channel listener, boolean throwErrors) {
try {
if (!active) return;
final boolean hasPortOverride = portOverride > 0 && portOverride < 65535;
if (hasPortOverride && !channels.isEmpty()) return; // avoid duplicate listeners
if (!(listener.localAddress() instanceof InetSocketAddress)) return;
ChannelInitializer<Channel> initializer = null;
ListenerInfo info = null;
for (String name : listener.pipeline().names()) {
final ChannelHandler handler = listener.pipeline().get(name);
if (handler instanceof QueryHandler) {
return;
}
if (handler != null && ACCEPTOR_CLASS.isAssignableFrom(handler.getClass())) {
try {
initializer = (ChannelInitializer<Channel>) accessible(ACCEPTOR_CLASS.getDeclaredField("childHandler")).get(handler);
final Map.Entry<AttributeKey<?>, ?>[] attrs = (Map.Entry<AttributeKey<?>, ?>[])
accessible(ACCEPTOR_CLASS.getDeclaredField("childAttrs")).get(handler);
for (var attr : attrs) {
if (attr.getKey() == PipelineUtils.LISTENER) {
info = (ListenerInfo) attr.getValue();
}
}
} catch (IllegalAccessException | NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
}
if (initializer == null) {
RaknetifyBungeePlugin.LOGGER.severe("Unable to find channel initializer for listener %s".formatted(listener));
return;
}
if (info == null) {
RaknetifyBungeePlugin.LOGGER.severe("Unable to find listener info for listener %s".formatted(listener));
return;
}
if (listener.getClass().getName().startsWith("org.geysermc.geyser.network")) { // filter out geyser
return;
}
if (((InetSocketAddress) info.getSocketAddress()).getAddress().isAnyLocalAddress()) {
for (NetworkInterface networkInterface : NetworkInterface.networkInterfaces().toList()) {
final Iterator<InetAddress> iterator = networkInterface.getInetAddresses().asIterator();
while (iterator.hasNext()) {
final InetAddress address = iterator.next();
try {
startServer(instance, listener, initializer, info, address);
} catch (Throwable t) {
RaknetifyBungeePlugin.LOGGER.log(Level.SEVERE, "Failed to start Raknetify server", t);
}
}
}
} else {
startServer(instance, listener, initializer, info, null);
}
} catch (Throwable t) {
if (throwErrors) throw new RuntimeException("Failed to start Raknetify server", t);
else RaknetifyBungeePlugin.LOGGER.log(Level.SEVERE, "Failed to start Raknetify server", t);
}
}
private static void startServer(BungeeCord instance, Channel listener, ChannelInitializer<Channel> initializer, ListenerInfo info, InetAddress address) throws NoSuchFieldException, IllegalAccessException {
if (address != null) {
final ReferenceOpenHashSet<ChannelFuture> futures = channels.get(listener);
if (futures != null) {
for (ChannelFuture future : futures) {
if (((InetSocketAddress) future.channel().localAddress()).getAddress().equals(address))
return; // avoid duplicate
}
}
}
final boolean hasPortOverride = portOverride > 0 && portOverride < 65535;
final ReflectiveChannelFactory<? extends DatagramChannel> factory = new ReflectiveChannelFactory<>(PipelineUtils.getDatagramChannel());
final InetAddress actualAddress = address == null ? ((InetSocketAddress) info.getSocketAddress()).getAddress() : address;
final ChannelFuture future = new ServerBootstrap()
.channelFactory(() -> new RakNetServerChannel(() -> {
final DatagramChannel channel = factory.newChannel();
channel.config().setOption(ChannelOption.IP_TOS, RakNetConnectionUtil.DEFAULT_IP_TOS);
channel.config().setRecvByteBufAllocator(new FixedRecvByteBufAllocator(Constants.LARGE_MTU + 512).maxMessagesPerRead(128));
return channel;
}))
.option(ChannelOption.SO_REUSEADDR, true)
.childAttr(PipelineUtils.LISTENER, info)
.childHandler(new ChannelInitializer<>() {
@Override
protected void initChannel(Channel channel) throws Exception {
RakNetBungeeConnectionUtil.initChannel(channel);
INIT_CHANNEL.invoke(initializer, channel);
RakNetBungeeConnectionUtil.postInitChannel(channel, false);
}
})
.group(getBossEventLoopGroup(instance), getWorkerEventLoopGroup(instance))
.localAddress(hasPortOverride ? new InetSocketAddress(actualAddress, portOverride) : new InetSocketAddress(actualAddress, ((InetSocketAddress) info.getSocketAddress()).getPort()))
.bind()
.syncUninterruptibly();
if (address == null) {
nonWildcardChannels.add(future);
} else {
channels.computeIfAbsent(listener, unused -> new ReferenceOpenHashSet<>()).add(future);
}
RaknetifyBungeePlugin.LOGGER.info("Raknetify server started on %s".formatted(future.channel().localAddress()));
}
public static void stopAll() {
if (!active) return;
for (ChannelFuture future : Stream.concat(channels.values().stream().flatMap(Collection::stream), nonWildcardChannels.stream()).toList()) {
RaknetifyBungeePlugin.LOGGER.info("Closing Raknetify server %s".formatted(future.channel().localAddress()));
try {
future.channel().close().sync();
} catch (InterruptedException e) {
RaknetifyBungeePlugin.LOGGER.severe("Interrupted whilst closing raknetify server");
}
}
channels.clear();
nonWildcardChannels.clear();
}
public static void disable() {
if (!active) return;
stopAll();
active = false;
}
private static EventLoopGroup getBossEventLoopGroup(BungeeCord instance) throws NoSuchFieldException, IllegalAccessException {
try {
return (EventLoopGroup) accessible(BungeeCord.class.getDeclaredField("eventLoops")).get(instance);
} catch (NoSuchFieldException e) { // waterfall: use split boss and worker group
//noinspection JavaReflectionMemberAccess
return (EventLoopGroup) accessible(BungeeCord.class.getDeclaredField("bossEventLoopGroup")).get(instance);
}
}
private static EventLoopGroup getWorkerEventLoopGroup(BungeeCord instance) throws NoSuchFieldException, IllegalAccessException {
try {
return (EventLoopGroup) accessible(BungeeCord.class.getDeclaredField("eventLoops")).get(instance);
} catch (NoSuchFieldException e) { // waterfall: use split boss and worker group
//noinspection JavaReflectionMemberAccess
return (EventLoopGroup) accessible(BungeeCord.class.getDeclaredField("workerEventLoopGroup")).get(instance);
}
}
}
================================================
FILE: bungee/src/main/java/com/ishland/raknetify/bungee/init/InjectedSet.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.bungee.init;
import com.google.common.collect.ForwardingSet;
import com.ishland.raknetify.bungee.RaknetifyBungeePlugin;
import io.netty.channel.Channel;
import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.api.ProxyServer;
import java.util.Set;
import java.util.concurrent.TimeUnit;
public class InjectedSet extends ForwardingSet<Channel> {
private final Set<Channel> delegate;
public InjectedSet(Set<Channel> delegate) {
this.delegate = delegate;
}
@Override
public boolean add(Channel element) {
ProxyServer.getInstance().getScheduler().schedule(
RaknetifyBungeePlugin.INSTANCE,
() -> BungeeRaknetifyServer.injectChannel((BungeeCord) ProxyServer.getInstance(), element, false),
100, TimeUnit.MILLISECONDS);
return super.add(element);
}
@Override
public void clear() {
BungeeRaknetifyServer.stopAll();
super.clear();
}
@Override
protected Set<Channel> delegate() {
return this.delegate;
}
}
================================================
FILE: bungee/src/main/resources/bungee.yml
================================================
#
# This file is a part of the Raknetify project, licensed under MIT.
#
# Copyright (c) 2022-2025 ishland
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
name: raknetify
main: com.ishland.raknetify.bungee.RaknetifyBungeePlugin
version: ${version}
author: ishland
softDepends:
- ViaVersion
================================================
FILE: common/build.gradle
================================================
dependencies {
api("com.github.RelativityMC.netty-raknet:netty-raknet-common") {
transitive = false
}
api("com.github.RelativityMC.netty-raknet:netty-raknet-client")
api("com.github.RelativityMC.netty-raknet:netty-raknet-server")
api("org.apache.commons:commons-math3:3.6.1")
//noinspection GradlePackageUpdate
compileOnly("io.netty:netty-all:4.1.25.Final")
compileOnly("com.google.code.gson:gson:2.9.0")
compileOnly("com.google.guava:guava:31.1-jre")
}
================================================
FILE: common/src/main/java/com/ishland/raknetify/common/Constants.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.common;
public class Constants {
public static final boolean DEBUG = Boolean.getBoolean("raknetify.debug");
public static final String RAKNET_PREFIX = "raknet;";
public static final String RAKNET_LARGE_MTU_PREFIX = "raknetl;";
public static final int RAKNET_PING_PACKET_ID = 0xFA;
public static final int RAKNET_GAME_PACKET_ID = 0xFD;
public static final int RAKNET_STREAMING_COMPRESSION_PACKET_ID = 0xED;
public static final int RAKNET_STREAMING_COMPRESSION_HANDSHAKE_PACKET_ID = 0xEC;
public static final int RAKNET_SYNC_PACKET_ID = 0xFC;
public static final int RAKNET_METRICS_SYNC_PACKET_ID = 0xFB;
public static final int MAX_QUEUED_SIZE = 256 * 1024 * 1024;
public static final int DEFAULT_MTU = 1400;
public static final int LARGE_MTU = 8192;
public static final int MAX_PENDING_FRAME_SETS = 512;
public static final int DEFAULT_PENDING_FRAME_SETS = 4;
public static final int[] SYNC_IGNORE_CHANNELS = new int[] {1};
private Constants() {
}
}
================================================
FILE: common/src/main/java/com/ishland/raknetify/common/connection/ByteBufCopyDecoder.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.common.connection;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class ByteBufCopyDecoder extends ChannelInboundHandlerAdapter {
public static final String NAME = "raknetify-byte-buf-copy-decoder";
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof ByteBuf buf && !buf.hasMemoryAddress()) {
ctx.fireChannelRead(ctx.alloc().directBuffer(buf.readableBytes()).writeBytes(buf));
buf.release();
return;
}
super.channelRead(ctx, msg);
}
}
================================================
FILE: common/src/main/java/com/ishland/raknetify/common/connection/FrameDataBlocker.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.common.connection;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import network.ycc.raknet.frame.FrameData;
public class FrameDataBlocker extends ChannelInboundHandlerAdapter {
private static final boolean printBlockedFrames = Boolean.getBoolean("raknetify.printBlockedFrames");
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FrameData data) {
if (printBlockedFrames) System.out.println("Blocked %s".formatted(msg));
data.release();
return;
}
ctx.fireChannelRead(msg);
}
}
================================================
FILE: common/src/main/java/com/ishland/raknetify/common/connection/MetricsSynchronizationHandler.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.common.connection;
import com.ishland.raknetify.common.Constants;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.concurrent.ScheduledFuture;
import network.ycc.raknet.RakNet;
import network.ycc.raknet.frame.FrameData;
import network.ycc.raknet.packet.FramedPacket;
import java.util.concurrent.TimeUnit;
public class MetricsSynchronizationHandler extends ChannelDuplexHandler {
// Packet format:
// byte: version (currently 0x00)
// long: packet sent time (used to filter out old packets)
// int: buffer size in bytes
// int: burst size
// double: error rate
// int: tx
// int: rx
private static final byte VERSION = 0x00;
private ScheduledFuture<?> future;
private ChannelHandlerContext ctx;
@Override
public void handlerAdded(ChannelHandlerContext ctx) {
this.ctx = ctx;
this.future = ctx.channel().eventLoop().scheduleAtFixedRate(this::sendSyncPacket, 200, 200, TimeUnit.MILLISECONDS);
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) {
if (future != null) future.cancel(false);
}
private void sendSyncPacket() {
if (this.ctx.channel().config() instanceof RakNet.Config config && config.getMetrics() instanceof SimpleMetricsLogger logger){
ByteBuf buffer = null;
try {
buffer = this.ctx.alloc().buffer(1 + 8 + 4 + 4 + 8 + 4 + 4);
buffer.writeByte(VERSION);
buffer.writeLong(System.currentTimeMillis());
buffer.writeInt(logger.getCurrentQueuedBytes());
buffer.writeInt((int) (logger.getMeasureBurstTokens() + config.getDefaultPendingFrameSets()));
buffer.writeDouble(logger.getMeasureErrorRate());
buffer.writeInt(logger.getMeasureTX());
buffer.writeInt(logger.getMeasureRX());
final FrameData frameData = FrameData.create(this.ctx.alloc(), Constants.RAKNET_METRICS_SYNC_PACKET_ID, buffer);
frameData.setReliability(FramedPacket.Reliability.UNRELIABLE);
this.ctx.write(frameData);
} finally {
if (buffer != null) buffer.release();
}
}
}
private boolean isRemoteSupported = false;
private long lastRecv = 0L;
private int queuedBytes;
private int burst;
private double errorRate;
private int tx;
private int rx;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FrameData frameData && frameData.getDataSize() > 0 && frameData.getPacketId() == Constants.RAKNET_METRICS_SYNC_PACKET_ID) {
ByteBuf byteBuf = null;
try {
byteBuf = frameData.createData().skipBytes(1);
final byte version = byteBuf.readByte();
if (version != VERSION) return;
final long time = byteBuf.readLong();
if (time < this.lastRecv) return;
this.lastRecv = time;
this.isRemoteSupported = true;
this.queuedBytes = byteBuf.readInt();
this.burst = byteBuf.readInt();
this.errorRate = byteBuf.readDouble();
this.tx = byteBuf.readInt();
this.rx = byteBuf.readInt();
} finally {
if (byteBuf != null) byteBuf.release();
frameData.release();
}
return;
}
super.channelRead(ctx, msg);
}
public boolean isRemoteSupported() {
return this.isRemoteSupported;
}
public int getQueuedBytes() {
return queuedBytes;
}
public int getBurst() {
return burst;
}
public double getErrorRate() {
return errorRate;
}
public int getTX() {
return tx;
}
public int getRX() {
return rx;
}
}
================================================
FILE: common/src/main/java/com/ishland/raknetify/common/connection/MultiChannelingStreamingCompression.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.common.connection;
import com.ishland.raknetify.common.Constants;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import network.ycc.raknet.frame.FrameData;
import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
public class MultiChannelingStreamingCompression extends ChannelDuplexHandler {
public static final String NAME = "raknetify-multichannel-streaming-compression";
public static final long SERVER_HANDSHAKE = 0x40000010;
public static final long CHANNEL_START = 0x40000012;
private final Inflater[] inflaters = new Inflater[8];
private final Deflater[] deflaters = new Deflater[8];
private final IntOpenHashSet channelsToIgnoreWhenReinit = new IntOpenHashSet();
private final byte[] inflateBuffer = new byte[256 * 1024];
private final byte[] deflateBuffer = new byte[256 * 1024];
private final int rawPacketId;
private final int compressedPacketId;
private volatile long outBytesRaw = 0L;
private volatile long outBytesCompressed = 0L;
private volatile long inBytesCompressed = 0L;
private volatile long inBytesRaw = 0L;
private boolean active = false;
public MultiChannelingStreamingCompression(int rawPacketId, int compressedPacketId) {
this.rawPacketId = rawPacketId;
this.compressedPacketId = compressedPacketId;
}
private void doServerHandshake(ChannelHandlerContext ctx) {
final ByteBuf buf = ctx.alloc().buffer().writeLong(SERVER_HANDSHAKE);
try {
final FrameData data = FrameData.create(ctx.alloc(), Constants.RAKNET_STREAMING_COMPRESSION_HANDSHAKE_PACKET_ID,
buf);
ctx.write(data);
} finally {
buf.release();
}
}
private void doChannelStart(ChannelHandlerContext ctx) {
if (!active) return;
ByteBuf buf = ctx.alloc().buffer().writeLong(CHANNEL_START);
try {
for (int i = 0; i < 8; i++) {
final FrameData data = FrameData.create(ctx.alloc(), Constants.RAKNET_STREAMING_COMPRESSION_HANDSHAKE_PACKET_ID,
buf);
data.setOrderChannel(i);
ctx.write(data);
initDeflater(i);
}
} finally {
buf.release();
}
}
private void initDeflater(int channel) {
if (!active) return;
if (deflaters[channel] != null) deflaters[channel].end();
deflaters[channel] = new Deflater();
if (Constants.DEBUG) System.out.println("Raknetify: Streaming compression deflater for ch%d is ready".formatted(channel));
}
private void initInflater(int channel) {
if (!active) return;
if (inflaters[channel] != null) inflaters[channel].end();
inflaters[channel] = new Inflater();
if (Constants.DEBUG) System.out.println("Raknetify: Streaming compression inflater for ch%d is ready".formatted(channel));
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
doServerHandshake(ctx);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FrameData compressedFrameData) {
compressedFrameData.touch();
if (compressedFrameData.getPacketId() == Constants.RAKNET_STREAMING_COMPRESSION_HANDSHAKE_PACKET_ID) {
final int orderChannel = compressedFrameData.getOrderChannel();
ByteBuf payload = null;
try {
payload = compressedFrameData.createData().skipBytes(1);
if (payload.readableBytes() == 8) {
final long l = payload.readLong();
if (l == CHANNEL_START) {
initInflater(orderChannel);
return;
} else if (l == SERVER_HANDSHAKE) {
active = true;
doChannelStart(ctx);
return;
}
}
} finally {
compressedFrameData.release();
if (payload != null) payload.release();
}
} else if (compressedFrameData.getPacketId() == compressedPacketId && compressedFrameData.getReliability().isReliable && compressedFrameData.getReliability().isOrdered && !compressedFrameData.getReliability().isSequenced && inflaters[compressedFrameData.getOrderChannel()] != null) {
final int orderChannel = compressedFrameData.getOrderChannel();
final Inflater inflater = inflaters[orderChannel];
final ByteBuf data = compressedFrameData.createData().skipBytes(1);
ByteBuf out = null;
FrameData rawFrameData = null;
try {
inflater.setInput(data.nioBuffer());
inBytesCompressed += data.readableBytes();
out = ctx.alloc().buffer();
{
int inflatedBytes;
while ((inflatedBytes = inflater.inflate(inflateBuffer)) != 0) {
out.writeBytes(inflateBuffer, 0, inflatedBytes);
}
}
inBytesRaw += out.writerIndex();
rawFrameData = FrameData.create(ctx.alloc(), rawPacketId, out);
rawFrameData.setReliability(compressedFrameData.getReliability());
rawFrameData.setOrderChannel(orderChannel);
ctx.fireChannelRead(rawFrameData);
rawFrameData = null;
return;
} finally {
data.release();
compressedFrameData.release();
if (out != null) out.release();
if (rawFrameData != null) rawFrameData.release();
}
}
}
super.channelRead(ctx, msg);
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if (msg == SynchronizationLayer.SYNC_REQUEST_OBJECT) {
super.write(ctx, msg, promise);
doChannelStart(ctx);
return;
} else if (msg instanceof FrameData rawFrameData) {
rawFrameData.touch();
if (rawFrameData.getPacketId() == rawPacketId && rawFrameData.getReliability().isReliable && rawFrameData.getReliability().isOrdered && !rawFrameData.getReliability().isSequenced && deflaters[rawFrameData.getOrderChannel()] != null) {
if (rawFrameData.getDataSize() < 16 + 1) {
outBytesRaw += rawFrameData.getDataSize() - 1;
outBytesCompressed += rawFrameData.getDataSize() - 1;
ctx.write(rawFrameData, promise);
return;
}
final int orderChannel = rawFrameData.getOrderChannel();
final Deflater deflater = deflaters[orderChannel];
final ByteBuf data = rawFrameData.createData().skipBytes(1);
ByteBuf out = null;
FrameData compressedFrameData = null;
try {
deflater.setInput(data.nioBuffer());
outBytesRaw += data.readableBytes();
out = ctx.alloc().buffer();
{
int deflatedBytes;
while ((deflatedBytes = deflater.deflate(deflateBuffer, 0, deflateBuffer.length, Deflater.SYNC_FLUSH)) != 0) {
out.writeBytes(deflateBuffer, 0, deflatedBytes);
}
}
outBytesCompressed += out.writerIndex();
compressedFrameData = FrameData.create(ctx.alloc(), compressedPacketId, out);
compressedFrameData.setReliability(rawFrameData.getReliability());
compressedFrameData.setOrderChannel(orderChannel);
ctx.write(compressedFrameData, promise);
compressedFrameData = null;
return;
} finally {
data.release();
rawFrameData.release();
if (out != null) out.release();
if (compressedFrameData != null) compressedFrameData.release();
}
}
}
super.write(ctx, msg, promise);
}
public long getInBytesCompressed() {
return inBytesCompressed;
}
public long getInBytesRaw() {
return inBytesRaw;
}
public long getOutBytesCompressed() {
return outBytesCompressed;
}
public long getOutBytesRaw() {
return outBytesRaw;
}
public boolean isActive() {
return active;
}
private ScheduledFuture<?> future;
public void handlerAdded(ChannelHandlerContext ctx) {
this.future = ctx.channel().eventLoop().scheduleAtFixedRate(this::tickMetrics, 1000, 1000, TimeUnit.MILLISECONDS);
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) {
if (future != null) future.cancel(false);
for (int i = 0; i < 8; i++) {
if (inflaters[i] != null) inflaters[i].end();
if (deflaters[i] != null) deflaters[i].end();
}
}
private long lastInBytesCompressed;
private long lastInBytesRaw;
private long lastOutBytesRaw;
private long lastOutBytesCompressed;
private final DescriptiveStatistics inCompressionRatioStats = new DescriptiveStatistics(16);
private final DescriptiveStatistics outCompressionRatioStats = new DescriptiveStatistics(16);
private volatile double inCompressionRatio;
private volatile double outCompressionRatio;
private void tickMetrics() {
long deltaInBytesCompressed = this.inBytesCompressed - this.lastInBytesCompressed;
long deltaInBytesRaw = this.inBytesRaw - this.lastInBytesRaw;
long deltaOutBytesCompressed = this.outBytesCompressed - this.lastOutBytesCompressed;
long deltaOutBytesRaw = this.outBytesRaw - this.lastOutBytesRaw;
if (deltaInBytesRaw != 0) {
double currentInCompressionRatio = deltaInBytesCompressed / (double) deltaInBytesRaw;
inCompressionRatioStats.addValue(currentInCompressionRatio);
}
if (deltaOutBytesRaw != 0) {
double currentOutCompressionRatio = deltaOutBytesCompressed / (double) deltaOutBytesRaw;
outCompressionRatioStats.addValue(currentOutCompressionRatio);
}
this.inCompressionRatio = inCompressionRatioStats.getMean();
this.outCompressionRatio = outCompressionRatioStats.getMean();
this.lastInBytesCompressed = this.inBytesCompressed;
this.lastInBytesRaw = this.inBytesRaw;
this.lastOutBytesCompressed = this.outBytesCompressed;
this.lastOutBytesRaw = this.outBytesRaw;
}
public double getInCompressionRatio() {
return inCompressionRatio;
}
public double getOutCompressionRatio() {
return outCompressionRatio;
}
}
================================================
FILE: common/src/main/java/com/ishland/raknetify/common/connection/MultiChannellingEncryption.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.common.connection;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import network.ycc.raknet.frame.FrameData;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import java.security.GeneralSecurityException;
import java.util.Objects;
public class MultiChannellingEncryption extends ChannelDuplexHandler {
public static final String NAME = "raknetify-multichannel-encryption";
private final PacketEncryptionManager decryption;
private final PacketEncryptionManager encryption;
public MultiChannellingEncryption(SecretKey key) throws GeneralSecurityException {
Cipher decryption = Cipher.getInstance("AES/CFB8/NoPadding");
decryption.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(key.getEncoded()));
this.decryption = new PacketEncryptionManager(decryption);
Cipher encryption = Cipher.getInstance("AES/CFB8/NoPadding");
encryption.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(key.getEncoded()));
this.encryption = new PacketEncryptionManager(encryption);
}
public MultiChannellingEncryption(Cipher decryption, Cipher encryption) {
this.decryption = new PacketEncryptionManager(Objects.requireNonNull(decryption));
this.encryption = new PacketEncryptionManager(Objects.requireNonNull(encryption));
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if (msg instanceof FrameData data) {
data.touch();
final ByteBuf buf = data.createData().skipBytes(1);
ByteBuf res = null;
FrameData resFrame = null;
try {
res = ctx.alloc().buffer(data.getDataSize());
encryption.doWork(buf, res);
resFrame = FrameData.create(ctx.alloc(), data.getPacketId(), res);
// res = null;
resFrame.setOrderChannel(data.getOrderChannel());
resFrame.setReliability(data.getReliability());
ctx.write(resFrame, promise);
resFrame = null;
return;
} finally {
buf.release();
data.release();
if (res != null) res.release();
if (resFrame != null) resFrame.release();
}
}
super.write(ctx, msg, promise);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FrameData data) {
data.touch();
final ByteBuf buf = data.createData().skipBytes(1);
ByteBuf res = null;
FrameData resFrame = null;
try {
res = ctx.alloc().buffer(data.getDataSize());
decryption.doWork(buf, res);
resFrame = FrameData.create(ctx.alloc(), data.getPacketId(), res);
resFrame.setOrderChannel(data.getOrderChannel());
resFrame.setReliability(data.getReliability());
ctx.fireChannelRead(resFrame);
resFrame = null;
return;
} finally {
buf.release();
data.release();
if (res != null) res.release();
if (resFrame != null) resFrame.release();
}
}
super.channelRead(ctx, msg);
}
}
================================================
FILE: common/src/main/java/com/ishland/raknetify/common/connection/NoFlush.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.common.connection;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
public class NoFlush extends ChannelDuplexHandler {
@Override
public void flush(ChannelHandlerContext ctx) {
// no-op
}
}
================================================
FILE: common/src/main/java/com/ishland/raknetify/common/connection/PacketEncryptionManager.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.common.connection;
import io.netty.buffer.ByteBuf;
import javax.crypto.Cipher;
import java.security.GeneralSecurityException;
// TODO [VanillaCopy] from fabric
public class PacketEncryptionManager {
private final Cipher cipher;
private byte[] conversionBuffer = new byte[0];
private byte[] encryptionBuffer = new byte[0];
protected PacketEncryptionManager(Cipher cipher) {
this.cipher = cipher;
}
private byte[] toByteArray(ByteBuf buf) {
int i = buf.readableBytes();
if (this.conversionBuffer.length < i) {
this.conversionBuffer = new byte[i];
}
buf.readBytes(this.conversionBuffer, 0, i);
return this.conversionBuffer;
}
public void doWork(ByteBuf buf, ByteBuf result) throws GeneralSecurityException {
int i = buf.readableBytes();
byte[] bs = this.toByteArray(buf);
int outputSize = this.cipher.getOutputSize(i);
if (this.encryptionBuffer.length < outputSize) {
this.encryptionBuffer = new byte[outputSize];
}
result.writeBytes(this.encryptionBuffer, 0, this.cipher.doFinal(bs, 0, i, this.encryptionBuffer));
}
}
================================================
FILE: common/src/main/java/com/ishland/raknetify/common/connection/RakNetConnectionUtil.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.common.connection;
import com.ishland.raknetify.common.Constants;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.handler.timeout.ReadTimeoutHandler;
import network.ycc.raknet.RakNet;
import network.ycc.raknet.client.channel.RakNetClientThreadedChannel;
import network.ycc.raknet.frame.Frame;
import network.ycc.raknet.pipeline.ReliabilityHandler;
import network.ycc.raknet.server.channel.RakNetApplicationChannel;
import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.concurrent.TimeUnit;
import static com.ishland.raknetify.common.util.ReflectionUtil.accessible;
public class RakNetConnectionUtil {
public static final int IP_TOS_LOWDELAY = 0b00010000;
public static final int IP_TOS_THROUGHPUT = 0b00001000;
public static final int IP_TOS_RELIABILITY = 0b00000100;
public static final int DEFAULT_IP_TOS = IP_TOS_LOWDELAY | IP_TOS_THROUGHPUT;
private RakNetConnectionUtil() {
}
private static final Comparator<Frame> cmp =
Comparator
.comparingInt((Frame frame) -> frame.getReliability().isReliable ? 1 : 0) // unreliable then reliable
.thenComparingInt(frame -> frame.getReliability().isOrdered ? 1 : 0) // unordered then ordered
.thenComparingInt(Frame::getOrderChannel) // lower channel first
.thenComparingInt(Frame::getOrderIndex); // lower index first
public static void initChannel(Channel channel) {
if (channel.config() instanceof RakNet.Config config) {
config.setMaxQueuedBytes(Constants.MAX_QUEUED_SIZE);
config.setMaxPendingFrameSets(Constants.MAX_PENDING_FRAME_SETS);
config.setRetryDelayNanos(TimeUnit.NANOSECONDS.convert(50, TimeUnit.MILLISECONDS));
config.setDefaultPendingFrameSets(Constants.DEFAULT_PENDING_FRAME_SETS);
config.setNACKEnabled(false);
config.setNoDelayEnabled(false);
// config.setIgnoreResendGauge(true);
initRaknetChannel(channel);
// channel.pipeline().addLast("raknetify-flush-enforcer", new FlushEnforcer());
// channel.pipeline().addLast("raknetify-flush-consolidation", new FlushConsolidationHandler(Integer.MAX_VALUE, true));
channel.pipeline().addLast("raknetify-no-flush", new NoFlush());
channel.pipeline().addLast(MultiChannelingStreamingCompression.NAME, new MultiChannelingStreamingCompression(Constants.RAKNET_GAME_PACKET_ID, Constants.RAKNET_STREAMING_COMPRESSION_PACKET_ID));
// channel.pipeline().addLast(MultiChannellingDataCodec.NAME, new MultiChannellingDataCodec(Constants.RAKNET_GAME_PACKET_ID));
channel.pipeline().addLast("raknetify-frame-data-blocker", new FrameDataBlocker());
}
}
private static void initRaknetChannel(Channel appChannel) {
final Channel channel;
final String threadedReadHandlerName;
if (appChannel instanceof RakNetApplicationChannel) {
channel = appChannel.parent();
threadedReadHandlerName = RakNetApplicationChannel.NAME_SERVER_PARENT_THREADED_READ_HANDLER;
} else if (appChannel instanceof RakNetClientThreadedChannel) {
channel = appChannel.parent();
threadedReadHandlerName = RakNetClientThreadedChannel.NAME_CLIENT_PARENT_THREADED_READ_HANDLER;
} else {
channel = appChannel;
threadedReadHandlerName = null;
}
channel.pipeline().addLast(new ChannelInitializer<>() {
@Override
protected void initChannel(Channel ch) {
final RakNet.Config config = (RakNet.Config) ch.config();
final SimpleMetricsLogger simpleMetricsLogger = new SimpleMetricsLogger();
config.setMetrics(simpleMetricsLogger);
final MetricsSynchronizationHandler metricsSynchronizationHandler = new MetricsSynchronizationHandler();
simpleMetricsLogger.setMetricsSynchronizationHandler(metricsSynchronizationHandler);
final SynchronizationLayer synchronizationLayer = new SynchronizationLayer(Constants.SYNC_IGNORE_CHANNELS);
reInitChannelForOrdering(channel);
if (threadedReadHandlerName != null) {
ch.pipeline().addBefore(threadedReadHandlerName, "raknetify-metrics-sync", metricsSynchronizationHandler);
ch.pipeline().addBefore(threadedReadHandlerName, "raknetify-synchronization-layer", synchronizationLayer);
} else {
ch.pipeline().addLast("raknetify-metrics-sync", metricsSynchronizationHandler);
ch.pipeline().addLast("raknetify-synchronization-layer", synchronizationLayer);
}
ch.pipeline().addFirst("raknetify-timeout", new ReadTimeoutHandler(15));
}
});
}
// public static void postInitChannel(Channel channel, boolean isClientSide) {
// if (channel.config() instanceof RakNet.Config) {
// ViaFabricCompatInjector.inject(channel, isClientSide);
// channel.pipeline().replace("timeout", "timeout", new ChannelDuplexHandler()); // no-op
// channel.pipeline().replace("splitter", "splitter", new ChannelDuplexHandler()); // no-op
// channel.pipeline().replace("prepender", "prepender", new ChannelDuplexHandler()); // no-op
// final MultiChannellingPacketCapture handler = new MultiChannellingPacketCapture();
// channel.pipeline().addLast("raknetify-multi-channel-packet-cature", handler);
// channel.pipeline().get(MultiChannellingDataCodec.class).setCapture(handler);
// }
// }
@SuppressWarnings("unchecked")
private static void reInitChannelForOrdering(Channel channel) {
if (channel.config() instanceof RakNet.Config config) {
try {
final ReliabilityHandler reliabilityHandler = channel.pipeline().get(ReliabilityHandler.class);
final Field frameQueueField = accessible(ReliabilityHandler.class.getDeclaredField("frameQueue"));
PriorityQueue<Frame> reliabilityHandlerFrameQueue = (PriorityQueue<Frame>) frameQueueField.get(reliabilityHandler);
final PriorityQueue<Frame> newSet = new PriorityQueue<>(cmp);
newSet.addAll(reliabilityHandlerFrameQueue);
frameQueueField.set(reliabilityHandler, newSet);
} catch (Throwable t) {
System.err.println("Raknetify: Error occurred while reinitializing channel ordering");
t.printStackTrace();
}
}
}
}
================================================
FILE: common/src/main/java/com/ishland/raknetify/common/connection/RakNetSimpleMultiChannelCodec.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.common.connection;
import com.ishland.raknetify.common.Constants;
import com.ishland.raknetify.common.util.MathUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import network.ycc.raknet.frame.FrameData;
import network.ycc.raknet.packet.FramedPacket;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
public class RakNetSimpleMultiChannelCodec extends ChannelDuplexHandler {
public static final String NAME = "raknetify-simple-multi-channel-data-codec";
public static final Object SIGNAL_START_MULTICHANNEL = new Object();
private final int packetId;
public RakNetSimpleMultiChannelCodec(int packetId) {
this.packetId = packetId;
}
private final ObjectArrayList<OverrideHandler> handlers = new ObjectArrayList<>();
public RakNetSimpleMultiChannelCodec addHandler(OverrideHandler handler) {
synchronized (handlers) {
handlers.add(handler);
}
return this;
}
public void removeHandler(OverrideHandler handler) {
synchronized (handlers) {
handlers.remove(handler);
}
}
public <T> T getHandler(Class<T> clazz) {
synchronized (handlers) {
for (OverrideHandler handler : handlers) {
if (clazz.isInstance(handler)) return clazz.cast(handler);
}
}
return null;
}
private boolean isMultichannelEnabled;
private boolean queuePendingWrites = false;
private final Queue<PendingWrite> pendingWrites = new LinkedList<>();
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
super.handlerRemoved(ctx);
for (PendingWrite pendingWrite : pendingWrites) {
pendingWrite.promise.setFailure(new IllegalStateException("Channel closed"));
pendingWrite.frameData.release();
}
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
if (this.queuePendingWrites && msg instanceof ByteBuf buf) {
final FrameData data = encode0(ctx, buf);
if (data != null) {
pendingWrites.add(new PendingWrite(data, promise));
} else {
promise.setSuccess();
}
buf.release();
return;
}
if (msg == SIGNAL_START_MULTICHANNEL) {
promise.trySuccess();
if (this.isMultichannelEnabled) return;
if (!this.isMultichannelAvailable()) {
System.out.println("Raknetify: [MultiChannellingDataCodec] Failed to start multichannel: not available");
return;
}
final ByteBuf buf = ctx.alloc().buffer(1).writeByte(0);
try {
final FrameData frameData = FrameData.create(ctx.alloc(), Constants.RAKNET_PING_PACKET_ID, buf);
frameData.setOrderChannel(7);
this.queuePendingWrites = true;
ctx.write(frameData).addListener(future -> {
isMultichannelEnabled = true;
if (Constants.DEBUG) System.out.println("Raknetify: [MultiChannellingDataCodec] Started multichannel");
flushPendingWrites(ctx);
});
} finally {
buf.release();
}
return;
}
if (msg == SynchronizationLayer.SYNC_REQUEST_OBJECT) {
if (this.isMultichannelEnabled) {
if (Constants.DEBUG) System.out.println("Raknetify: [MultiChannellingDataCodec] Stopped multichannel");
this.isMultichannelEnabled = false;
super.write(ctx, msg, promise);
}
promise.setSuccess();
return; // discard sync request when multichannel is not active
}
if (msg instanceof ByteBuf buf && buf.isReadable()) {
try {
final FrameData frameData = encode0(ctx, buf);
if (frameData != null) {
ctx.write(frameData, promise);
} else {
promise.setSuccess();
}
} finally {
buf.release();
}
return;
}
super.write(ctx, msg, promise);
}
private FrameData encode0(ChannelHandlerContext ctx, ByteBuf buf) {
if (buf.isReadable()) {
final int packetChannelOverride = getChannelOverride(buf, !isMultichannelEnabled);
if (packetChannelOverride == Integer.MIN_VALUE) {
return null; // the void
}
final FrameData frameData = FrameData.create(ctx.alloc(), packetId, buf);
if (isMultichannelEnabled) {
if (packetChannelOverride >= 0)
frameData.setOrderChannel(packetChannelOverride);
else if (packetChannelOverride == -1)
frameData.setReliability(FramedPacket.Reliability.RELIABLE);
else if (packetChannelOverride == -2)
frameData.setReliability(FramedPacket.Reliability.UNRELIABLE);
}
return frameData;
}
return null;
}
private void flushPendingWrites(ChannelHandlerContext ctx) {
this.queuePendingWrites = false;
PendingWrite pendingWrite;
while ((pendingWrite = this.pendingWrites.poll()) != null) {
try {
super.write(ctx, pendingWrite.frameData, pendingWrite.promise);
} catch (Throwable t) {
ctx.fireExceptionCaught(t);
}
}
}
protected boolean isMultichannelAvailable() {
synchronized (handlers) {
return !handlers.isEmpty();
}
}
protected int getChannelOverride(ByteBuf buf, boolean suppressWarning) {
synchronized (handlers) {
for (OverrideHandler handler : handlers) {
final int override = handler.getChannelOverride(buf, suppressWarning);
if (override != 0) return override;
}
}
return 0;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FrameData packet && !packet.isFragment() && packet.getDataSize() > 0) {
try {
if (packetId == packet.getPacketId()) {
ctx.fireChannelRead(packet.createData().skipBytes(1));
} else if (packet.getPacketId() == Constants.RAKNET_PING_PACKET_ID) {
return;
} else {
ctx.fireChannelRead(packet.retain());
}
} finally {
packet.release();
}
return;
}
super.channelRead(ctx, msg);
}
protected void decode(ChannelHandlerContext ctx, FrameData packet, List<Object> out) {
assert !packet.isFragment();
if (packet.getDataSize() > 0) {
if (packetId == packet.getPacketId()) {
out.add(packet.createData().skipBytes(1));
} else if (packet.getPacketId() == Constants.RAKNET_PING_PACKET_ID) {
return;
} else {
out.add(packet.retain());
}
}
}
public interface OverrideHandler {
int getChannelOverride(ByteBuf buf, boolean suppressWarning);
}
public static class PacketIdBasedOverrideHandler implements OverrideHandler {
private final IntOpenHashSet unknownPacketIds = new IntOpenHashSet();
private final Int2IntOpenHashMap channelMapping;
private final String descriptiveProtocolStatus;
public PacketIdBasedOverrideHandler(Int2IntMap channelMapping, String descriptiveProtocolStatus) {
this.channelMapping = new Int2IntOpenHashMap(channelMapping);
this.descriptiveProtocolStatus = descriptiveProtocolStatus;
}
@Override
public int getChannelOverride(ByteBuf buf, boolean suppressWarning) {
final ByteBuf slice = buf.slice();
final int packetId = MathUtil.readVarInt(slice);
final int override = this.channelMapping.get(packetId);
if (override == Integer.MAX_VALUE) {
if (!suppressWarning) {
if (this.unknownPacketIds.add(packetId)) {
System.err.println("Raknetify: Unknown packet id %d for %s".formatted(packetId, descriptiveProtocolStatus));
}
}
return 7;
}
return override;
}
}
private record PendingWrite(FrameData frameData, ChannelPromise promise) {
}
}
================================================
FILE: common/src/main/java/com/ishland/raknetify/common/connection/RaknetifyEventLoops.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.common.connection;
import com.google.common.base.Suppliers;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import io.netty.channel.DefaultEventLoopGroup;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.concurrent.FastThreadLocalThread;
import java.util.function.Supplier;
public class RaknetifyEventLoops {
public static final Supplier<NioEventLoopGroup> NIO_EVENT_LOOP_GROUP =
Suppliers.memoize(() -> new NioEventLoopGroup(
0,
new ThreadFactoryBuilder()
.setThreadFactory(FastThreadLocalThread::new)
.setNameFormat("Netty Server NIO Raknetify #%d")
.setDaemon(true)
.build()
)
);
public static final Supplier<NioEventLoopGroup> NIO_CLIENT_EVENT_LOOP_GROUP =
Suppliers.memoize(() -> new NioEventLoopGroup(
0,
new ThreadFactoryBuilder()
.setThreadFactory(FastThreadLocalThread::new)
.setNameFormat("Netty Client NIO Raknetify #%d")
.setDaemon(true)
.build()
)
);
public static final Supplier<EpollEventLoopGroup> EPOLL_EVENT_LOOP_GROUP =
Suppliers.memoize(() -> new EpollEventLoopGroup(
0,
new ThreadFactoryBuilder()
.setThreadFactory(FastThreadLocalThread::new)
.setNameFormat("Netty Server Epoll Raknetify #%d")
.setDaemon(true)
.build()
)
);
public static final Supplier<EpollEventLoopGroup> EPOLL_CLIENT_EVENT_LOOP_GROUP =
Suppliers.memoize(() -> new EpollEventLoopGroup(
0,
new ThreadFactoryBuilder()
.setThreadFactory(FastThreadLocalThread::new)
.setNameFormat("Netty Client Epoll Raknetify #%d")
.setDaemon(true)
.build()
)
);
public static final Supplier<DefaultEventLoopGroup> DEFAULT_EVENT_LOOP_GROUP =
Suppliers.memoize(() -> new DefaultEventLoopGroup(
0,
new ThreadFactoryBuilder()
.setThreadFactory(FastThreadLocalThread::new)
.setNameFormat("Netty Server App Raknetify #%d")
.setDaemon(true)
.build()
)
);
public static final Supplier<DefaultEventLoopGroup> DEFAULT_CLIENT_EVENT_LOOP_GROUP =
Suppliers.memoize(() -> new DefaultEventLoopGroup(
0,
new ThreadFactoryBuilder()
.setThreadFactory(FastThreadLocalThread::new)
.setNameFormat("Netty Client App Raknetify #%d")
.setDaemon(true)
.build()
)
);
}
================================================
FILE: common/src/main/java/com/ishland/raknetify/common/connection/SimpleMetricsLogger.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.common.connection;
import com.ishland.raknetify.common.util.MathUtil;
import network.ycc.raknet.RakNet;
import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
/**
* This implementation is only designed to be modified single-threaded
*/
@SuppressWarnings("NonAtomicOperationOnVolatileField")
public class SimpleMetricsLogger implements RakNet.MetricsLogger {
// ========== Loggers ==========
private volatile long packetsIn = 0L;
private volatile long framesIn = 0L;
private volatile long framesError = 0L;
private volatile long bytesIn = 0L;
private volatile long packetsOut = 0L;
private volatile long framesOut = 0L;
private volatile long bytesOut = 0L;
private volatile long bytesRecalled = 0L;
private volatile long bytesACKd = 0L;
private volatile long bytesNACKd = 0L;
private volatile long acksSent = 0L;
private volatile long nacksSent = 0L;
private volatile long measureRTTns = 0L;
private volatile long measureRTTnsStdDev = 0L;
private volatile long measureBurstTokens = 0L;
private volatile int currentQueuedBytes = 0;
@Override
public void packetsIn(int delta) {
packetsIn += delta;
}
@Override
public void framesIn(int delta) {
framesIn += delta;
}
@Override
public void frameError(int delta) {
framesError += delta;
}
@Override
public void bytesIn(int delta) {
bytesIn += delta;
}
@Override
public void packetsOut(int delta) {
packetsOut += delta;
}
@Override
public void framesOut(int delta) {
framesOut += delta;
}
@Override
public void bytesOut(int delta) {
bytesOut += delta;
tick();
}
@Override
public void bytesRecalled(int delta) {
bytesRecalled += delta;
}
@Override
public void bytesACKd(int delta) {
bytesACKd += delta;
}
@Override
public void bytesNACKd(int delta) {
bytesNACKd += delta;
}
@Override
public void acksSent(int delta) {
acksSent += delta;
}
@Override
public void nacksSent(int delta) {
nacksSent += delta;
}
@Override
public void measureRTTns(long n) {
measureRTTns = n;
}
@Override
public void measureRTTnsStdDev(long n) {
measureRTTnsStdDev = n;
tick();
}
@Override
public void measureBurstTokens(int n) {
measureBurstTokens = n;
}
@Override
public void currentQueuedBytes(int bytes) {
currentQueuedBytes = bytes;
}
// ========== Calculations ==========
private long lastMeasureMillis = System.currentTimeMillis();
private synchronized void tick() {
final long measureMillis = System.currentTimeMillis();
final long deltaTime = measureMillis - lastMeasureMillis;
if (deltaTime < 990) return; // throttle
this.lastMeasureMillis = measureMillis;
tickErrorRate();
tickRXTX(deltaTime);
}
private final DescriptiveStatistics errorStats = new DescriptiveStatistics(16);
private long lastBytesTotal = 0L;
private long lastBytesRecalled = 0L;
private volatile double measureErrorRate = 0.0D;
private void tickErrorRate() {
final long bytesTotal = this.bytesIn + this.bytesOut;
final long bytesRecalled = this.bytesRecalled;
final long bytesTotalDelta = bytesTotal - lastBytesTotal;
final long bytesRecalledDelta = bytesRecalled - this.lastBytesRecalled;
if (bytesTotalDelta != 0) {
this.errorStats.addValue(bytesRecalledDelta / (double) bytesTotalDelta);
this.measureErrorRate = this.errorStats.getMean();
}
this.lastBytesTotal = bytesTotal;
this.lastBytesRecalled = bytesRecalled;
}
private final DescriptiveStatistics rxStats = new DescriptiveStatistics(8);
private final DescriptiveStatistics txStats = new DescriptiveStatistics(8);
private long lastPacketsIn = 0L;
private long lastPacketsOut = 0L;
private long lastBytesIn = 0L;
private long lastBytesOut = 0L;
private volatile int measureRX = 0;
private volatile int measureTX = 0;
private volatile long measureBytesInRate = 0;
private volatile long measureBytesOutRate = 0;
private volatile String measureTrafficInFormatted = "...";
private volatile String measureTrafficOutFormatted = "...";
private void tickRXTX(long deltaTime) {
final long packetsIn = this.packetsIn;
final long packetsOut = this.packetsOut;
final long bytesIn = this.bytesIn;
final long bytesOut = this.bytesOut;
final double timeDeltaS = deltaTime / 1000.0;
this.rxStats.addValue((packetsIn - this.lastPacketsIn) / timeDeltaS);
this.txStats.addValue((packetsOut - this.lastPacketsOut) / timeDeltaS);
this.measureRX = (int) this.rxStats.getMean();
this.measureTX = (int) this.txStats.getMean();
this.measureBytesInRate = (long) ((bytesIn - this.lastBytesIn) / timeDeltaS);
this.measureBytesOutRate = (long) ((bytesOut - this.lastBytesOut) / timeDeltaS);
this.measureTrafficInFormatted = MathUtil.humanReadableByteCountBin(this.measureBytesInRate) + "/s";
this.measureTrafficOutFormatted = MathUtil.humanReadableByteCountBin(this.measureBytesOutRate) + "/s";
this.lastPacketsIn = packetsIn;
this.lastPacketsOut = packetsOut;
this.lastBytesIn = bytesIn;
this.lastBytesOut = bytesOut;
}
// ========== Getters ==========
public long getMeasureRTTns() {
return measureRTTns;
}
public long getMeasureRTTnsStdDev() {
return measureRTTnsStdDev;
}
public double getMeasureErrorRate() {
return measureErrorRate;
}
public int getMeasureRX() {
return measureRX;
}
public int getMeasureTX() {
return measureTX;
}
public int getCurrentQueuedBytes() {
return currentQueuedBytes;
}
public long getMeasureBurstTokens() {
return measureBurstTokens;
}
public long getMeasureBytesInRate() {
return measureBytesInRate;
}
public long getMeasureBytesOutRate() {
return measureBytesOutRate;
}
public String getMeasureTrafficInFormatted() {
return measureTrafficInFormatted;
}
public String getMeasureTrafficOutFormatted() {
return measureTrafficOutFormatted;
}
public long getBytesIn() {
return bytesIn;
}
// ========== Misc ==========
private MetricsSynchronizationHandler metricsSynchronizationHandler;
public MetricsSynchronizationHandler getMetricsSynchronizationHandler() {
return this.metricsSynchronizationHandler;
}
public void setMetricsSynchronizationHandler(MetricsSynchronizationHandler metricsSynchronizationHandler) {
this.metricsSynchronizationHandler = metricsSynchronizationHandler;
}
}
================================================
FILE: common/src/main/java/com/ishland/raknetify/common/connection/SynchronizationLayer.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.common.connection;
import com.ishland.raknetify.common.Constants;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import it.unimi.dsi.fastutil.objects.Reference2ReferenceLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet;
import network.ycc.raknet.frame.Frame;
import network.ycc.raknet.frame.FrameData;
import network.ycc.raknet.packet.FrameSet;
import network.ycc.raknet.packet.FramedPacket;
import network.ycc.raknet.pipeline.FrameJoiner;
import network.ycc.raknet.pipeline.FrameOrderIn;
import network.ycc.raknet.pipeline.FrameOrderOut;
import network.ycc.raknet.pipeline.ReliabilityHandler;
import org.apache.commons.math3.util.Pair;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.PriorityQueue;
import static com.ishland.raknetify.common.util.ReflectionUtil.accessible;
public class SynchronizationLayer extends ChannelDuplexHandler {
// Request structure:
// byte: total channel count `n`
// next `n` groups: {
// byte: channel index
// integer: current orderIndex (or lastOrderIndex, (nextOrderIndex - 1))
// }
// integer: current seqId (or lastReceivedSeqId, (nextSendSeqId - 1))
//
// Response callback is handled using reliable transport
public static final Object SYNC_REQUEST_OBJECT = new Object();
static final Class<?> CLASS_QUEUE;
static final Class<?> CLASS_FRAME_JOINER_BUILDER;
static final Field FIELD_QUEUE_LAST_ORDER_INDEX;
static final Method METHOD_QUEUE_BUILDER_RELEASE;
static final Method METHOD_QUEUE_CLEAR;
static final Field FIELD_RELIABILITY_NEXT_SEND_SEQ_ID;
static final Field FIELD_RELIABILITY_LAST_RECEIVED_SEQ_ID;
static final Field FIELD_RELIABILITY_QUEUED_BYTES;
static final Field FIELD_FRAME_JOINER_BUILDER_SAMPLE_PACKET;
static {
try {
CLASS_QUEUE = Class.forName("network.ycc.raknet.pipeline.FrameOrderIn$OrderedChannelPacketQueue");
CLASS_FRAME_JOINER_BUILDER = Class.forName("network.ycc.raknet.pipeline.FrameJoiner$Builder");
FIELD_QUEUE_LAST_ORDER_INDEX = accessible(CLASS_QUEUE.getDeclaredField("lastOrderIndex"));
FIELD_RELIABILITY_NEXT_SEND_SEQ_ID = accessible(ReliabilityHandler.class.getDeclaredField("nextSendSeqId"));
FIELD_RELIABILITY_LAST_RECEIVED_SEQ_ID = accessible(ReliabilityHandler.class.getDeclaredField("lastReceivedSeqId"));
FIELD_RELIABILITY_QUEUED_BYTES = accessible(ReliabilityHandler.class.getDeclaredField("queuedBytes"));
FIELD_FRAME_JOINER_BUILDER_SAMPLE_PACKET = accessible(CLASS_FRAME_JOINER_BUILDER.getDeclaredField("samplePacket"));
METHOD_QUEUE_BUILDER_RELEASE = accessible(CLASS_FRAME_JOINER_BUILDER.getDeclaredMethod("release"));
METHOD_QUEUE_CLEAR = accessible(CLASS_QUEUE.getDeclaredMethod("clear"));
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
private final IntSet channelToIgnore = new IntOpenHashSet();
private FrameOrderIn frameOrderIn;
private Object[] frameOrderInQueues;
private FrameOrderOut frameOrderOut;
private int[] frameOrderOutNextOrderIndex;
private ReliabilityHandler reliabilityHandler;
private PriorityQueue<Frame> reliabilityHandlerFrameQueue;
private Int2ObjectMap<FrameSet> reliabilityHandlerPendingFrameSets;
private FrameJoiner frameJoiner;
private Int2ObjectOpenHashMap<?> frameJoinerPendingPackets;
private int channelsLength;
private boolean initialized = false;
public SynchronizationLayer(int... channelsToIgnore) {
for (int ch : channelsToIgnore) {
channelToIgnore.add(ch);
}
}
@SuppressWarnings("unchecked")
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
initializeIfNecessary(ctx);
}
private void initializeIfNecessary(ChannelHandlerContext ctx) {
if (initialized) return;
try {
this.frameOrderIn = ctx.pipeline().get(FrameOrderIn.class);
Object frameOrderInQueueArray = accessible(FrameOrderIn.class.getDeclaredField("channels")).get(this.frameOrderIn);
this.frameOrderInQueues = new Object[Array.getLength(frameOrderInQueueArray)];
for (int i = 0; i < this.frameOrderInQueues.length; i++) {
this.frameOrderInQueues[i] = Array.get(frameOrderInQueueArray, i);
}
this.frameOrderOut = ctx.pipeline().get(FrameOrderOut.class);
this.frameOrderOutNextOrderIndex = (int[]) accessible(FrameOrderOut.class.getDeclaredField("nextOrderIndex")).get(this.frameOrderOut);
this.reliabilityHandler = ctx.pipeline().get(ReliabilityHandler.class);
this.reliabilityHandlerFrameQueue = (PriorityQueue<Frame>) accessible(ReliabilityHandler.class.getDeclaredField("frameQueue")).get(this.reliabilityHandler);
this.reliabilityHandlerPendingFrameSets = (Int2ObjectMap<FrameSet>) accessible(ReliabilityHandler.class.getDeclaredField("pendingFrameSets")).get(this.reliabilityHandler);
int originalChannelsLength = this.frameOrderOutNextOrderIndex.length;
//noinspection deprecation
this.channelsLength = (int) (originalChannelsLength - this.channelToIgnore.stream().filter(value -> value < originalChannelsLength).count());
this.frameJoiner = ctx.pipeline().get(FrameJoiner.class);
this.frameJoinerPendingPackets = (Int2ObjectOpenHashMap<?>) accessible(FrameJoiner.class.getDeclaredField("pendingPackets")).get(this.frameJoiner);
initialized = true;
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
initializeIfNecessary(ctx);
if (msg instanceof FrameData packet && packet.getPacketId() == Constants.RAKNET_SYNC_PACKET_ID) {
// read
{
if (Constants.DEBUG) System.out.println("Raknetify: Received sync packet");
ctx.fireUserEventTriggered(SYNC_REQUEST_OBJECT);
final ByteBuf byteBuf = packet.createData().skipBytes(1);
try {
final byte count = byteBuf.readByte();
for (int i = 0; i < count; i++) {
final byte channel = byteBuf.readByte();
final int orderIndex = byteBuf.readInt();
if (Constants.DEBUG)
System.out.println("Raknetify: Channel %d: %d -> %d"
.formatted(channel,
(int) FIELD_QUEUE_LAST_ORDER_INDEX.get(frameOrderInQueues[channel]),
orderIndex
));
FIELD_QUEUE_LAST_ORDER_INDEX.set(frameOrderInQueues[channel], orderIndex);
final ObjectIterator<?> iterator = this.frameJoinerPendingPackets.values().iterator();
while (iterator.hasNext()) {
final Object next = iterator.next();
try {
final Frame frame = (Frame) FIELD_FRAME_JOINER_BUILDER_SAMPLE_PACKET.get(next);
if (frame.getReliability().isOrdered && frame.getOrderChannel() == channel) {
METHOD_QUEUE_BUILDER_RELEASE.invoke(next);
iterator.remove();
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
final int seqId = byteBuf.readInt();
if (Constants.DEBUG)
System.out.println("Raknetify: ReliabilityHandler: %d -> %d".formatted(
(int) FIELD_RELIABILITY_LAST_RECEIVED_SEQ_ID.get(this.reliabilityHandler),
seqId
));
FIELD_RELIABILITY_LAST_RECEIVED_SEQ_ID.set(this.reliabilityHandler, seqId);
} finally {
byteBuf.release();
}
}
return;
}
ctx.fireChannelRead(msg);
}
private final ReferenceLinkedOpenHashSet<Pair<ChannelPromise, Object>> queue = new ReferenceLinkedOpenHashSet<>();
private final ObjectArrayList<Frame> queuedFrames = new ObjectArrayList<>();
private boolean isWaitingForResponse = false;
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
initializeIfNecessary(ctx);
if (msg == SYNC_REQUEST_OBJECT) {
if (isWaitingForResponse) {
promise.setSuccess();
return;
}
dropSenderPackets();
final ByteBuf byteBuf = ctx.alloc().buffer(1 + channelsLength * 5 + 4);
byteBuf.writeByte(channelsLength);
for (int channel = 0, frameOrderOutNextOrderIndexLength = frameOrderOutNextOrderIndex.length; channel < frameOrderOutNextOrderIndexLength; channel++) {
if (channelToIgnore.contains(channel)) continue;
int orderOutNextOrderIndex = frameOrderOutNextOrderIndex[channel];
if (Constants.DEBUG)
System.out.println("Raknetify: Writing sync packet: Channel %d: %d".formatted(channel, orderOutNextOrderIndex - 1));
byteBuf.writeByte(channel);
byteBuf.writeInt(orderOutNextOrderIndex - 1);
}
int seqId = (int) FIELD_RELIABILITY_NEXT_SEND_SEQ_ID.get(this.reliabilityHandler); // TODO implementation details (probable lib bug): nextSendSeqId == lastReceivedSeqId
if (Constants.DEBUG)
System.out.println("Raknetify: Writing sync packet: ReliabilityHandler: %d".formatted(seqId));
byteBuf.writeInt(seqId);
final FrameData frameData = FrameData.create(ctx.alloc(), Constants.RAKNET_SYNC_PACKET_ID, byteBuf);
frameData.setReliability(FramedPacket.Reliability.RELIABLE);
this.isWaitingForResponse = true;
ctx.write(frameData).addListener(future -> this.flushQueue(ctx));
byteBuf.release();
promise.setSuccess();
return;
}
if (isWaitingForResponse) {
this.queue.add(Pair.create(promise, msg));
return;
}
super.write(ctx, msg, promise);
}
private void dropSenderPackets() {
int droppedFrames = 0;
ArrayList<Frame> retainedFrameList = new ArrayList<>();
//noinspection CollectionAddAllCanBeReplacedWithConstructor
retainedFrameList.addAll(this.reliabilityHandlerFrameQueue);
this.reliabilityHandlerFrameQueue.clear();
for (FrameSet frameSet : this.reliabilityHandlerPendingFrameSets.values()) {
frameSet.createFrames(retainedFrameList::add);
frameSet.release();
}
this.reliabilityHandlerPendingFrameSets.clear();
int byteSize = 0;
for (Iterator<Frame> iterator = retainedFrameList.iterator(); iterator.hasNext(); ) {
Frame frame = iterator.next();
if (frame.getReliability().isOrdered && !channelToIgnore.contains(frame.getOrderChannel())) {
final ChannelPromise promise1 = frame.getPromise();
if (promise1 != null) promise1.trySuccess();
iterator.remove();
frame.release();
droppedFrames++;
} else {
byteSize += frame.getRoughPacketSize();
}
}
this.queuedFrames.addAll(retainedFrameList);
if (Constants.DEBUG) System.out.println("Raknetify: Dropping %d frames".formatted(droppedFrames));
try {
FIELD_RELIABILITY_QUEUED_BYTES.set(this.reliabilityHandler, byteSize);
} catch (Throwable t) {
throw new RuntimeException(t);
}
this.reliabilityHandlerPendingFrameSets.clear();
}
private void flushQueue(ChannelHandlerContext ctx) {
if (!isWaitingForResponse) {
if (Constants.DEBUG) System.out.println("Raknetify: Ignoring duplicate call to flushQueue()");
return;
}
if (!ctx.channel().eventLoop().inEventLoop()) {
ctx.channel().eventLoop().execute(() -> flushQueue(ctx));
return;
}
this.isWaitingForResponse = false;
if (Constants.DEBUG) System.out.println("Raknetify: Picking up %d queued frames".formatted(this.queuedFrames.size()));
this.reliabilityHandlerFrameQueue.addAll(this.queuedFrames);
this.queuedFrames.clear();
if (Constants.DEBUG) System.out.println("Raknetify: Flushing %d queued packets as synchronization finished".formatted(this.queue.size()));
while (!this.queue.isEmpty()) {
Pair<ChannelPromise, Object> pair = this.queue.removeFirst();
final ChannelPromise promise = pair.getFirst();
final Object msg = pair.getSecond();
ctx.write(msg, promise);
}
}
}
================================================
FILE: common/src/main/java/com/ishland/raknetify/common/connection/multichannel/CustomPayloadChannel.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.common.connection.multichannel;
import com.ishland.raknetify.common.Constants;
import com.ishland.raknetify.common.connection.RakNetSimpleMultiChannelCodec;
import com.ishland.raknetify.common.util.MathUtil;
import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.util.Objects;
import java.util.function.IntPredicate;
public class CustomPayloadChannel {
public static final Object2IntOpenHashMap<String> identifier2channel;
static {
// See .fabric.common.connection.RakNetMultiChannel
identifier2channel = new Object2IntOpenHashMap<>();
identifier2channel.defaultReturnValue(0);
identifier2channel.put("porting_lib:extra_data_entity_spawn", 2);
identifier2channel.put("porting_lib:extra_entity_spawn_data", 2); // https://github.com/Fabricators-of-Create/Porting-Lib/commit/4b0cd845731f89eafd9fb39e13e1a7d87f5e14a4
}
public static class OverrideHandler implements RakNetSimpleMultiChannelCodec.OverrideHandler {
private final IntPredicate isCustomPayload;
public OverrideHandler(IntPredicate isCustomPayload) {
this.isCustomPayload = Objects.requireNonNull(isCustomPayload);
}
@Override
public int getChannelOverride(ByteBuf origBuf, boolean suppressWarning) {
ByteBuf buf = origBuf.slice();
final int packetId = MathUtil.readVarInt(buf);
if (isCustomPayload.test(packetId)) {
final String identifier = MathUtil.readString(buf); // we assume modern custom payloads
if (Constants.DEBUG) System.out.println("Raknetify: Handling custom payload: " + identifier);
return identifier2channel.getInt(identifier);
} else {
return 0;
}
}
}
}
================================================
FILE: common/src/main/java/com/ishland/raknetify/common/data/ProtocolMultiChannelMappings.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.common.data;
import com.google.gson.Gson;
import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class ProtocolMultiChannelMappings {
public static final ProtocolMultiChannelMappings INSTANCE;
static {
final InputStream resource = ProtocolMultiChannelMappings.class.getClassLoader().getResourceAsStream("raknetify-channel-mappings.json");
if (resource == null) {
System.err.println("Raknetify: Failed to load raknetify channel mappings");
INSTANCE = new ProtocolMultiChannelMappings();
} else {
ProtocolMultiChannelMappings read = new ProtocolMultiChannelMappings();
try (var in = resource;
var reader = new InputStreamReader(resource)) {
final Gson gson = new Gson();
read = gson.fromJson(reader, ProtocolMultiChannelMappings.class);
} catch (IOException e) {
System.err.println("Raknetify: Failed to load raknetify channel mappings");
e.printStackTrace();
}
INSTANCE = read;
}
}
public static void init() {
}
public Int2ObjectArrayMap<VersionMapping> mappings = new Int2ObjectArrayMap<>();
public static class VersionMapping {
public Int2IntArrayMap s2c = new Int2IntArrayMap();
public Int2IntArrayMap c2s = new Int2IntArrayMap();
}
}
================================================
FILE: common/src/main/java/com/ishland/raknetify/common/package-info.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.common;
================================================
FILE: common/src/main/java/com/ishland/raknetify/common/util/DebugUtil.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.common.util;
import com.ishland.raknetify.common.connection.MetricsSynchronizationHandler;
import com.ishland.raknetify.common.connection.MultiChannelingStreamingCompression;
import com.ishland.raknetify.common.connection.SimpleMetricsLogger;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import network.ycc.raknet.RakNet;
public class DebugUtil {
public static String printChannelDetails(Channel channel) {
final StringBuilder b = new StringBuilder();
b.append("Channel details for ").append(channel.toString()).append(' ').append('(').append(channel.getClass().getName()).append(')').append('\n');
b.append("Connection: ").append(channel.localAddress()).append(" <--> ").append(channel.remoteAddress()).append('\n');
b.append("Open: ").append(channel.isOpen()).append('\n');
b.append("Active: ").append(channel.isActive()).append('\n');
b.append("Auto Read: ").append(channel.config().isAutoRead()).append('\n');
if (channel.config() instanceof RakNet.Config config) {
b.append("MTU: ").append(config.getMTU()).append('\n');
b.append("RTT: %.2f/%.2fms"
.formatted(
config.getRTTNanos() / 1_000_000.0,
config.getRTTStdDevNanos() / 1_000_000.0
)).append('\n');
if (config.getMetrics() instanceof SimpleMetricsLogger logger) {
final MetricsSynchronizationHandler sync = logger.getMetricsSynchronizationHandler();
if (sync != null && sync.isRemoteSupported()) {
b.append("Local Buffer: %.2fMB; Remote buffer: %.2fMB"
.formatted(
logger.getCurrentQueuedBytes() / 1024.0 / 1024.0,
sync.getQueuedBytes() / 1024.0 / 1024.0
)).append('\n');
} else {
b.append("Local buffer: %.2fMB"
.formatted(
logger.getCurrentQueuedBytes() / 1024.0 / 1024.0
)).append('\n');
}
b.append("Local traffic: I: %s, O: %s"
.formatted(
logger.getMeasureTrafficInFormatted(),
logger.getMeasureTrafficOutFormatted()
)).append('\n');
b.append("Local Statistics: ERR: %.4f%%, %d tx, %d rx, Burst: %d"
.formatted(
logger.getMeasureErrorRate() * 100.0,
logger.getMeasureTX(), logger.getMeasureRX(),
logger.getMeasureBurstTokens() + config.getDefaultPendingFrameSets()
)).append('\n');
if (sync != null && sync.isRemoteSupported()) {
b.append("Remote Statistics: ERR: %.4f%%, %d tx, %d rx, Burst: %d"
.formatted(
sync.getErrorRate() * 100.0,
sync.getTX(), sync.getRX(),
sync.getBurst()
)).append('\n');
}
final MultiChannelingStreamingCompression compression = channel.pipeline().get(MultiChannelingStreamingCompression.class);
if (compression != null && compression.isActive()) {
b.append("Local Streaming Compression Ratio: I: %.2f%%, O: %.2f%%"
.formatted(compression.getInCompressionRatio() * 100, compression.getOutCompressionRatio() * 100)).append('\n');
}
}
}
b.append('\n');
b.append("Pipeline: ").append('\n');
for (String name : channel.pipeline().names()) {
final ChannelHandler channelHandler = channel.pipeline().get(name);
if (channelHandler == null) {
b.append("\t").append(name).append(": \t").append("null").append('\n');
} else {
b.append("\t").append(name).append(": \t").append(channelHandler.toString()).append("(").append(channelHandler.getClass().getName()).append(")").append('\n');
}
}
if (channel.parent() != null) {
b.append('\n');
b.append("Parent: ").append('\n');
b.append(printChannelDetails(channel.parent()).replace("\n", "\n\t"));
}
return b.toString();
}
}
================================================
FILE: common/src/main/java/com/ishland/raknetify/common/util/MathUtil.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.common.util;
import com.google.common.base.Charsets;
import io.netty.buffer.ByteBuf;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
public class MathUtil {
public static String humanReadableByteCountBin(long bytes) {
long absB = bytes == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(bytes);
if (absB < 1024) {
return bytes + " B";
}
long value = absB;
CharacterIterator ci = new StringCharacterIterator("KMGTPE");
for (int i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10) {
value >>= 10;
ci.next();
}
value *= Long.signum(bytes);
return String.format("%.2f %ciB", value / 1024.0, ci.current());
}
public static int readVarInt(ByteBuf buf) {
// TODO [VanillaCopy]
int i = 0;
int j = 0;
byte b;
do {
b = buf.readByte();
i |= (b & 127) << j++ * 7;
if (j > 5) {
throw new RuntimeException("VarInt too big");
}
} while ((b & 128) == 128);
return i;
}
public static String readString(ByteBuf buf) {
return readString(buf, Short.MAX_VALUE);
}
public static String readString(ByteBuf buf, int maxLen) {
// Copied from BungeeCord
int len = readVarInt(buf);
if (len > maxLen * 3) {
throw new IllegalArgumentException("Cannot receive string longer than " + maxLen * 3 + " (got " + len + " bytes)");
}
String s = buf.toString(buf.readerIndex(), len, Charsets.UTF_8);
buf.readerIndex(buf.readerIndex() + len);
if (s.length() > maxLen) {
throw new IllegalArgumentException("Cannot receive string longer than " + maxLen + " (got " + s.length() + " characters)");
}
return s;
}
}
================================================
FILE: common/src/main/java/com/ishland/raknetify/common/util/NetworkInterfaceListener.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.common.util;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
import it.unimi.dsi.fastutil.objects.ReferenceSet;
import it.unimi.dsi.fastutil.objects.ReferenceSets;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
public class NetworkInterfaceListener {
private static final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(
r -> {
final Thread thread = new Thread(r, "Raknetify IFListener");
thread.setDaemon(true);
thread.setPriority(Thread.NORM_PRIORITY - 1);
return thread;
}
);
static {
scheduler.scheduleAtFixedRate(() -> {
try {
pollChanges();
} catch (Throwable t) {
t.printStackTrace();
}
}, 0, 10, TimeUnit.SECONDS);
}
public static void init() {
}
private static final ObjectOpenHashSet<InetAddress> knownAddresses = new ObjectOpenHashSet<>();
private static final ReferenceSet<Consumer<InterfaceAddressChangeEvent>> listeners = ReferenceSets.synchronize(new ReferenceOpenHashSet<>());
private static void pollChanges() {
try {
final List<NetworkInterface> networkInterfaces = NetworkInterface.networkInterfaces().toList();
ObjectOpenHashSet<InetAddress> currentAddresses = new ObjectOpenHashSet<>();
for (NetworkInterface networkInterface : networkInterfaces) {
if (networkInterface.isUp()) {
for (InetAddress address : networkInterface.inetAddresses().toList()) {
currentAddresses.add(address);
if (knownAddresses.add(address)) {
listeners.forEach(consumer -> consumer.accept(new InterfaceAddressChangeEvent(true, address)));
}
}
}
}
final ObjectIterator<InetAddress> iterator = knownAddresses.iterator();
while (iterator.hasNext()) {
final InetAddress address = iterator.next();
if (!currentAddresses.contains(address)) {
iterator.remove();
listeners.forEach(consumer -> consumer.accept(new InterfaceAddressChangeEvent(false, address)));
}
}
} catch (Throwable t) {
t.printStackTrace();
}
}
public static void addListener(Consumer<InterfaceAddressChangeEvent> consumer) {
listeners.add(consumer);
}
public static void removeListener(Consumer<InterfaceAddressChangeEvent> consumer) {
listeners.remove(consumer);
}
public record InterfaceAddressChangeEvent(boolean added, InetAddress address) {
}
}
================================================
FILE: common/src/main/java/com/ishland/raknetify/common/util/PrefixUtil.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.common.util;
import com.ishland.raknetify.common.Constants;
public class PrefixUtil {
public static Info getInfo(String address) {
if (address.startsWith(Constants.RAKNET_PREFIX)) {
return new Info(true, address.substring(Constants.RAKNET_PREFIX.length()), false);
} else if (address.startsWith(Constants.RAKNET_LARGE_MTU_PREFIX)) {
return new Info(true, address.substring(Constants.RAKNET_LARGE_MTU_PREFIX.length()), true);
} else {
return new Info(false, address, false);
}
}
public record Info(boolean useRakNet, String stripped, boolean largeMTU) {
}
}
================================================
FILE: common/src/main/java/com/ishland/raknetify/common/util/ReflectionUtil.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.common.util;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionUtil {
public static Field accessible(Field field) {
field.setAccessible(true);
return field;
}
public static Method accessible(Method method) {
method.setAccessible(true);
return method;
}
}
================================================
FILE: common/src/main/java/com/ishland/raknetify/common/util/ThreadLocalUtil.java
================================================
/*
* This file is a part of the Raknetify project, licensed under MIT.
*
* Copyright (c) 2022-2025 ishland
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.ishland.raknetify.common.util;
public class ThreadLocalUtil {
private ThreadLocalUtil() {
}
private static final ThreadLocal<Boolean> initializingRaknet = ThreadLocal.withInitial(() -> false);
private static final ThreadLocal<Boolean> initializingRaknetLargeMTU = ThreadLocal.withInitial(() -> false);
public static void setInitializingRaknet(boolean value) {
initializingRaknet.set(value);
}
public static void setInitializingRaknetLargeMTU(boolean value) {
initializingRaknetLargeMTU.set(value);
}
public static boolean isInitializingRaknet() {
return initializingRaknet.get();
}
public static boolean isInitializingRaknetLargeMTU() {
return initializingRaknetLargeMTU.get();
}
}
================================================
FILE: common/src/main/resources/raknetify-channel-mappings.json
================================================
{
"mappings": {
"755": {
"s2c": {
"0": 2,
"1": 2,
"2": 2,
"3": 2,
"4": 1,
"5": 4,
"6": 2,
"7": -1,
"8": 7,
"9": 7,
"10": 7,
"11": 7,
"12": 7,
"13": 1,
"14": 1,
"15": 1,
"16": 1,
"17": 1,
"18": 1,
"19": 3,
"20": 3,
"21": 3,
"22": 3,
"23": 1,
"24": 3,
"25": 4,
"26": -1,
"27": 2,
"28": 2,
"29": 7,
"30": 2,
"31": 3,
"32": 3,
"33": -1,
"34": 7,
"35": 7,
"36": 4,
"37": 7,
"38": 2,
"39": 4,
"40": 3,
"41": 2,
"42": 2,
"43": 2,
"44": 2,
"45": 3,
"46": 3,
"47": 7,
"48": -1,
"49": 3,
"50": 2,
"51": 2,
"52": 2,
"53": 1,
"54": 1,
"55": 1,
"56": 1,
"57": 3,
"58": 2,
"59": 2,
"60": -1,
"61": 3,
"62": 2,
"63": 7,
"64": -1,
"65": 1,
"66": 3,
"67": 3,
"68": 3,
"69": 3,
"70": 3,
"71": 2,
"72": 3,
"73": 7,
"74": 7,
"75": 2,
"76": 1,
"77": 2,
"78": 2,
"79": 2,
"80": 2,
"81": 1,
"82": 1,
"83": 1,
"84": 2,
"85": 1,
"86": 1,
"87": 1,
"88": 1,
"89": 1,
"90": 1,
"91": 4,
"92": 4,
"93": 4,
"94": 1,
"95": 3,
"96": 2,
"97": 2,
"98": 1,
"99": 2,
"100": 2,
"101": 1,
"102": 1
},
"c2s": {
"0": 3,
"1": 3,
"2": 1,
"3": 1,
"4": 3,
"5": 1,
"6": 1,
"7": 1,
"8": 1,
"9": 3,
"10": 3,
"11": 3,
"12": 3,
"13": 2,
"14": 7,
"15": -1,
"16": 1,
"17": 2,
"18": 2,
"19": 2,
"20": 2,
"21": 2,
"22": 2,
"23": 3,
"24": 3,
"25": 3,
"26": 1,
"27": 1,
"28": 1,
"29": -1,
"30": 3,
"31": 3,
"32": 3,
"33": -1,
"34": -1,
"35": 3,
"36": 3,
"37": 3,
"38": 3,
"39": 3,
"40": 3,
"41": 3,
"42": 3,
"43": 7,
"44": 2,
"45": 3,
"46": 2,
"47": 2
}
},
"756": {
"s2c": {
"0": 2,
"1": 2,
"2": 2,
"3": 2,
"4": 1,
"5": 4,
"6": 2,
"7": -1,
"8": 7,
"9": 7,
"10": 7,
"11": 7,
"12": 7,
"13": 1,
"14": 1,
"15": 1,
"16": 1,
"17": 1,
"18": 1,
"19": 3,
"20": 3,
"21": 3,
"22": 3,
"23": 1,
"24": 3,
"25": 4,
"26": -1,
"27": 2,
"28": 2,
"29": 7,
"30": 2,
"31": 3,
"32": 3,
"33": -1,
"34": 7,
"35": 7,
"36": 4,
"37": 7,
"38": 2,
"39": 4,
"40": 3,
"41": 2,
"42": 2,
"43": 2,
"44": 2,
"45": 3,
"46": 3,
"47": 7,
"48": -1,
"49": 3,
"50": 2,
"51": 2,
"52": 2,
"53": 1,
"54": 1,
"55": 1,
"56": 1,
"57": 3,
"58": 2,
"59": 2,
"60": -1,
"61": 3,
"62": 2,
"63": 7,
"64": -1,
"65": 1,
"66": 3,
"67": 3,
"68": 3,
"69": 3,
"70": 3,
"71": 2,
"72": 3,
"73": 7,
"74": 7,
"75": 2,
"76": 1,
"77": 2,
"78": 2,
"79": 2,
"80": 2,
"81": 1,
"82": 1,
"83": 1,
"84": 2,
"85": 1,
"86": 1,
"87": 1,
"88": 1,
"89": 1,
"90": 1,
"91": 4,
"92": 4,
"93": 4,
"94": 1,
"95": 3,
"96": 2,
"97": 2,
"98": 1,
"99": 2,
"100": 2,
"101": 1,
"102": 1
},
"c2s": {
"0": 3,
"1": 3,
"2": 1,
"3": 1,
"4": 3,
"5": 1,
"6": 1,
"7": 1,
"8": 1,
"9": 3,
"10": 3,
"11": 3,
"12": 3,
"13": 2,
"14": 7,
"15": -1,
"16": 1,
"17": 2,
"18": 2,
"19": 2,
"20": 2,
"21": 2,
"22": 2,
"23": 3,
"24": 3,
"25": 3,
"26": 1,
"27": 1,
"28": 1,
"29": -1,
"30": 3,
"31": 3,
"32": 3,
"33": -1,
"34": -1,
"35": 3,
"36": 3,
"37": 3,
"38": 3,
"39": 3,
"40": 3,
"41": 3,
"42": 3,
"43": 7,
"44": 2,
"45": 3,
"46": 2,
"47": 2
}
},
"757": {
"s2c": {
"0": 2,
"1": 2,
"2": 2,
"3": 2,
"4": 1,
"5": 4,
"6": 2,
"7": -1,
"8": 7,
"9": 7,
"10": 7,
"11": 7,
"12": 7,
"13": 1,
"14": 1,
"15": 1,
"16": 1,
"17": 1,
"18": 1,
"19": 3,
"20": 3,
"21": 3,
"22": 3,
"23": 1,
"24": 3,
"25": 4,
"26": -1,
"27": 2,
"28": 2,
"29": 7,
"30": 2,
"31": 3,
"32": 3,
"33": -1,
"34": 7,
"35": 7,
"36": 4,
"37": 7,
"38": 2,
"39": 4,
"40": 3,
"41": 2,
"42": 2,
"43": 2,
"44": 2,
"45": 3,
"46": 3,
"47": 7,
"48": -1,
"49": 3,
"50": 2,
"51": 2,
"52": 2,
"53": 1,
"54": 1,
"55": 1,
"56": 1,
"57": 3,
"58": 2,
"59": 2,
"60": -1,
"61": 3,
"62": 2,
"63": 7,
"64": -1,
"65": 1,
"66": 3,
"67": 3,
"68": 3,
"69": 3,
"70": 3,
"71": 2,
"72": 3,
"73": 7,
"74": 7,
"75": 2,
"76": 1,
"77": 2,
"78": 2,
"79": 2,
"80": 2,
"81": 1,
"82": 1,
"83": 1,
"84": 2,
"85": 1,
"86": 1,
"87": 7,
"88": 1,
"89": 1,
"90": 1,
"91": 1,
"92": 4,
"93": 4,
"94": 4,
"95": 1,
"96": 3,
"97": 2,
"98": 2,
"99": 1,
"100": 2,
"101": 2,
"102": 1,
"103": 1
},
"c2s": {
"0": 3,
"1": 3,
"2": 1,
"3": 1,
"4": 3,
"5": 1,
"6": 1,
"7": 1,
"8": 1,
"9": 3,
"10": 3,
"11": 3,
"12": 3,
"13": 2,
"14": 7,
"15": -1,
"16": 1,
"17": 2,
"18": 2,
"19": 2,
"20": 2,
"21": 2,
"22": 2,
"23": 3,
"24": 3,
"25": 3,
"26": 1,
"27": 1,
"28": 1,
"29": -1,
"30": 3,
"31": 3,
"32": 3,
"33": -1,
"34": -1,
"35": 3,
"36": 3,
"37": 3,
"38": 3,
"39": 3,
"40": 3,
"41": 3,
"42": 3,
"43": 7,
"44": 2,
"45": 3,
"46": 2,
"47": 2
}
},
"758": {
"s2c": {
"0": 2,
"1": 2,
"2": 2,
"3": 2,
"4": 1,
"5": 4,
"6": 2,
"7": -1,
"8": 7,
"9": 7,
"10": 7,
"11": 7,
"12": 7,
"13": 1,
"14": 1,
"15": 1,
"16": 1,
"17": 1,
"18": 1,
"19": 3,
"20": 3,
"21": 3,
"22": 3,
"23": 1,
"24": 3,
"25": 4,
"26": -1,
"27": 2,
"28": 2,
"29": 7,
"30": 2,
"31": 3,
"32": 3,
"33": -1,
"34": 7,
"35": 7,
"36": 4,
"37": 7,
"38": 2,
"39": 4,
"40": 3,
"41": 2,
"42": 2,
"43": 2,
"44": 2,
"45": 3,
"46": 3,
"47": 7,
"48": -1,
"49": 3,
"50": 2,
"51": 2,
"52": 2,
"53": 1,
"54": 1,
"55": 1,
"56": 1,
"57": 3,
"58": 2,
"59": 2,
"60": -1,
"61": 3,
"62": 2,
"63": 7,
"64": -1,
"65": 1,
"66": 3,
"67": 3,
"68": 3,
"69": 3,
"70": 3,
"71": 2,
"72": 3,
"73": 7,
"74": 7,
"75": 2,
"76": 1,
"77": 2,
"78": 2,
"79": 2,
"80": 2,
"81": 1,
"82": 1,
"83": 1,
"84": 2,
"85": 1,
"86": 1,
"87": 7,
"88": 1,
"89": 1,
"90": 1,
"91": 1,
"92": 4,
"93": 4,
"94": 4,
"95": 1,
"96": 3,
"97": 2,
"98": 2,
"99": 1,
"100": 2,
"101": 2,
"102": 1,
"103": 1
},
"c2s": {
"0": 3,
"1": 3,
"2": 1,
"3": 1,
"4": 3,
"5": 1,
"6": 1,
"7": 1,
"8": 1,
"9": 3,
"10": 3,
"11": 3,
"12": 3,
"13": 2,
"14": 7,
"15": -1,
"16": 1,
"17": 2,
"18": 2,
"19": 2,
"20": 2,
"21": 2,
"22": 2,
"23": 3,
"24": 3,
"25": 3,
"26": 1,
"27": 1,
"28": 1,
"29": -1,
"30": 3,
"31": 3,
"32": 3,
"33": -1,
"34": -1,
"35": 3,
"36": 3,
"37": 3,
"38": 3,
"39": 3,
"40": 3,
"41": 3,
"42": 3,
"43": 7,
"44": 2,
"45": 3,
"46": 2,
"47": 2
}
},
"759": {
"s2c": {
"0": 2,
"1": 2,
"2": 1,
"3": 2,
"4": -1,
"5": 7,
"6": 7,
"7": 7,
"8": 7,
"9": 7,
"10": 1,
"11": 1,
"12": 1,
"13": 1,
"14": 1,
"15": 1,
"16": 3,
"17": 3,
"18": 3,
"19": 3,
"20": 1,
"21": 3,
"22": 4,
"23": -1,
"24": 2,
"25": 2,
"26": 7,
"27": 2,
"28": 3,
"29": 3,
"30": -1,
"31": 7,
"32": 7,
"33": 4,
"34": 7,
"35": 2,
"36": 4,
"37": 3,
"38": 2,
"39": 2,
"40": 2,
"41": 2,
"42": 3,
"43": 3,
"44": 7,
"45": -1,
"46": 3,
"47": 2,
"48": 1,
"49": 2,
"50": 2,
"51": 1,
"52": 1,
"53": 1,
"54": 1,
"55": 3,
"56": 2,
"57": 2,
"58": -1,
"59": 3,
"60": 2,
"61": 7,
"62": -1,
"63": 1,
"64": 1,
"65": 3,
"66": 3,
"67": 3,
"68": 3,
"69": 3,
"70": 2,
"71": 3,
"72": 7,
"73": 7,
"74": 2,
"75": 1,
"76": 1,
"77": 2,
"78": 2,
"79": 2,
"80": 2,
"81": 1,
"82": 1,
"83": 1,
"84": 2,
"85": 1,
"86": 1,
"87": 7,
"88": 1,
"89": 1,
"90": 1,
"91": 1,
"92": 4,
"93": 4,
"94": 4,
"95": 1,
"96": 1,
"97": 3,
"98": 2,
"99": 2,
"100": 1,
"101": 2,
"102": 2,
"103": 1,
"104": 1
},
"c2s": {
"0": 3,
"1": 3,
"2": 1,
"3": 1,
"4": 1,
"5": 1,
"6": 3,
"7": 1,
"8": 1,
"9": 1,
"10": 1,
"11": 3,
"12": 3,
"13": 3,
"14": 3,
"15": 2,
"16": 7,
"17": -1,
"18": 1,
"19": 2,
"20": 2,
"21": 2,
"22": 2,
"23": 2,
"24": 2,
"25": 3,
"26": 3,
"27": 3,
"28": 1,
"29": 1,
"30": 1,
"31": -1,
"32": 3,
"33": 3,
"34": 3,
"35": -1,
"36": -1,
"37": 3,
"38": 3,
"39": 3,
"40": 3,
"41": 3,
"42": 3,
"43": 3,
"44": 3,
"45": 7,
"46": 2,
"47": 3,
"48": 2,
"49": 2
}
},
"760": {
"s2c": {
"0": 2,
"1": 2,
"2": 1,
"3": 2,
"4": -1,
"5": 7,
"6": 7,
"7": 7,
"8": 7,
"9": 7,
"10": 1,
"11": 1,
"12": 1,
"13": 1,
"14": 1,
"15": 1,
"16": 3,
"17": 3,
"18": 3,
"19": 3,
"20": 1,
"21": 1,
"22": 3,
"23": 4,
"24": 1,
"25": -1,
"26": 2,
"27": 2,
"28": 7,
"29": 2,
"30": 3,
"31": 3,
"32": -1,
"33": 7,
"34": 7,
"35": 4,
"36": 7,
"37": 2,
"38": 4,
"39": 3,
"40": 2,
"41": 2,
"42": 2,
"43": 2,
"44": 3,
"45": 3,
"46": 7,
"47": -1,
"48": 3,
"49": 2,
"50": 1,
"51": 1,
"52": 2,
"53": 2,
"54": 1,
"55": 1,
"56": 1,
"57": 1,
"58": 3,
"59": 2,
"60": 2,
"61": -1,
"62": 3,
"63": 2,
"64": 7,
"65": -1,
"66": 1,
"67": 1,
"68": 3,
"69": 3,
"70": 3,
"71": 3,
"72": 3,
"73": 2,
"74": 3,
"75": 7,
"76": 7,
"77": 2,
"78": 1,
"79": 1,
"80": 2,
"81": 2,
"82": 2,
"83": 2,
"84": 1,
"85": 1,
"86": 1,
"87": 2,
"88": 1,
"89": 1,
"90": 7,
"91": 1,
"92": 1,
"93": 1,
"94": 1,
"95": 4,
"96": 4,
"97": 4,
"98": 1,
"99": 1,
"100": 3,
"101": 2,
"102": 2,
"103": 1,
"104": 2,
"105": 2,
"106": 1,
"107": 1
},
"c2s": {
"0": 3,
"1": 3,
"2": 1,
"3": 1,
"4": 1,
"5": 1,
"6": 1,
"7": 3,
"8": 1,
"9": 1,
"10": 1,
"11": 1,
"12": 3,
"13": 3,
"14": 3,
"15": 3,
"16": 2,
"17": 7,
"18": -1,
"19": 1,
"20": 2,
"21": 2,
"22": 2,
"23": 2,
"24": 2,
"25": 2,
"26": 3,
"27": 3,
"28": 3,
"29": 1,
"30": 1,
"31": 1,
"32": -1,
"33": 3,
"34": 3,
"35": 3,
"36": -1,
"37": -1,
"38": 3,
"39": 3,
"40": 3,
"41": 3,
"42": 3,
"43": 3,
"44": 3,
"45": 3,
"46": 7,
"47": 2,
"48": 3,
"49": 2,
"50": 2
}
},
"761": {
"s2c": {
"0": 2,
"1": 2,
"2": 1,
"3": 2,
"4": -1,
"5": 7,
"6": 7,
"7": 7,
"8": 7,
"9": 7,
"10": 1,
"11": 1,
"12": 1,
"13": 1,
"14": 1,
"15": 3,
"16": 3,
"17": 3,
"18": 3,
"19": 1,
"20": 1,
"21": 3,
"22": 1,
"23": -1,
"24": 1,
"25": 2,
"26": 2,
"27": 7,
"28": 2,
"29": 3,
"30": 3,
"31": -1,
"32": 7,
"33": 7,
"34": 4,
"35": 7,
"36": 2,
"37": 4,
"38": 3,
"39": 2,
"40": 2,
"41": 2,
"42": 2,
"43": 3,
"44": 3,
"45": 7,
"46": -1,
"47": 3,
"48": 2,
"49": 1,
"50": 2,
"51": 2,
"52": 1,
"53": 1,
"54": 1,
"55": 1,
"56": 1,
"57": 3,
"58": 2,
"59": 2,
"60": -1,
"61": 3,
"62": 2,
"63": 7,
"64": -1,
"65": 1,
"66": 1,
"67": 3,
"68": 3,
"69": 3,
"70": 3,
"71": 3,
"72": 2,
"73": 3,
"74": 7,
"75": 7,
"76": 2,
"77": 1,
"78": 2,
"79": 2,
"80": 2,
"81": 2,
"82": 1,
"83": 1,
"84": 1,
"85": 2,
"86": 1,
"87": 1,
"88": 7,
"89": 1,
"90": 1,
"91": 1,
"92": 1,
"93": 4,
"94": 4,
"95": 4,
"96": 1,
"97": 1,
"98": 3,
"99": 2,
"100": 2,
"101": 1,
"102": 2,
"103": 2,
"104": 2,
"105": 1,
"106": 1
},
"c2s": {
"0": 3,
"1": 3,
"2": 1,
"3": 1,
"4": 1,
"5": 1,
"6": 3,
"7": 1,
"8": 1,
"9": 1,
"10": 1,
"11": 3,
"12": 3,
"13": 3,
"14": 3,
"15": 2,
"16": 7,
"17": -1,
"18": 1,
"19": 2,
"20": 2,
"21": 2,
"22": 2,
"23": 2,
"24": 2,
"25": 3,
"26": 3,
"27": 3,
"28": 1,
"29": 1,
"30": 1,
"31": -1,
"32": 1,
"33": 3,
"34": 3,
"35": 3,
"36": -1,
"37": -1,
"38": 3,
"39": 3,
"40": 3,
"41": 3,
"42": 3,
"43": 3,
"44": 3,
"45": 3,
"46": 7,
"47": 2,
"48": 3,
"49": 2,
"50": 2
}
},
"762": {
"s2c": {
"0": -2147483648,
"1": 2,
"2": 2,
"3": 1,
"4": 2,
"5": -1,
"6": 7,
"7": 7,
"8": 7,
"9": 7,
"10": 7,
"11": 1,
"12": 1,
"13": 7,
"14": 1,
"15": 1,
"16": 1,
"17": 3,
"18": 3,
"19": 3,
"20": 3,
"21": 1,
"22": 1,
"23": 3,
"24": 2,
"25": 1,
"26": -1,
"27": 1,
"28": 2,
"29": 2,
"30": 7,
"31": 2,
"32": 3,
"33": 2,
"34": 3,
"35": -1,
"36": 7,
"37": 7,
"38": 4,
"39": 7,
"40": 2,
"41": 4,
"42": 3,
"43": 2,
"44": 2,
"45": 2,
"46": 2,
"47": 3,
"48": 3,
"49": 7,
"50": -1,
"51": 3,
"52": 2,
"53": 1,
"54": 2,
"55": 2,
"56": 1,
"57": 1,
"58": 1,
"59": 1,
"60": 1,
"61": 3,
"62": 2,
"63": 2,
"64": -1,
"65": 3,
"66": 2,
"67": 7,
"68": -1,
"69": 1,
"70": 1,
"71": 3,
"72": 3,
"73": 3,
"74": 3,
"75": 3,
"76": 2,
"77": 3,
"78": 7,
"79": 7,
"80": 2,
"81": 1,
"82": 2,
"83": 2,
"84": 2,
"85": 2,
"86": 1,
"87": 1,
"88": 1,
"89": 2,
"90": 1,
"91": 1,
"92": 7,
"93": 1,
"94": 1,
"95": 1,
"96": 1,
"97": 4,
"98": 4,
"99": 4,
"100": 1,
"101": 1,
"102": 3,
"103": 2,
"104": 2,
"105": 1,
"106": 2,
"107": 2,
"108": 2,
"109": 1,
"110": 1
},
"c2s": {
"
gitextract_gavr_y8s/
├── .github/
│ └── workflows/
│ ├── build.yml
│ └── checkMappings.yml
├── .gitignore
├── .gitmodules
├── .run/
│ ├── BungeeCord Server.run.xml
│ └── Velocity Server.run.xml
├── HEADER.txt
├── Jenkinsfile
├── LICENSE
├── README.md
├── build.gradle
├── buildSrc/
│ ├── build.gradle
│ └── src/
│ └── main/
│ └── java/
│ └── com/
│ └── ishland/
│ └── buildscript/
│ └── ParseGItHubActionChangelog.java
├── bungee/
│ ├── build.gradle
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── ishland/
│ │ └── raknetify/
│ │ └── bungee/
│ │ ├── RaknetifyBungeePlugin.java
│ │ ├── connection/
│ │ │ ├── RakNetBungeeClientChannelEventListener.java
│ │ │ ├── RakNetBungeeConnectionUtil.java
│ │ │ ├── RakNetBungeePingUpdater.java
│ │ │ ├── RakNetBungeeServerChannelEventListener.java
│ │ │ └── StripFrameHandler.java
│ │ └── init/
│ │ ├── BungeeRaknetifyServer.java
│ │ └── InjectedSet.java
│ └── resources/
│ └── bungee.yml
├── common/
│ ├── build.gradle
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── ishland/
│ │ └── raknetify/
│ │ └── common/
│ │ ├── Constants.java
│ │ ├── connection/
│ │ │ ├── ByteBufCopyDecoder.java
│ │ │ ├── FrameDataBlocker.java
│ │ │ ├── MetricsSynchronizationHandler.java
│ │ │ ├── MultiChannelingStreamingCompression.java
│ │ │ ├── MultiChannellingEncryption.java
│ │ │ ├── NoFlush.java
│ │ │ ├── PacketEncryptionManager.java
│ │ │ ├── RakNetConnectionUtil.java
│ │ │ ├── RakNetSimpleMultiChannelCodec.java
│ │ │ ├── RaknetifyEventLoops.java
│ │ │ ├── SimpleMetricsLogger.java
│ │ │ ├── SynchronizationLayer.java
│ │ │ └── multichannel/
│ │ │ └── CustomPayloadChannel.java
│ │ ├── data/
│ │ │ └── ProtocolMultiChannelMappings.java
│ │ ├── package-info.java
│ │ └── util/
│ │ ├── DebugUtil.java
│ │ ├── MathUtil.java
│ │ ├── NetworkInterfaceListener.java
│ │ ├── PrefixUtil.java
│ │ ├── ReflectionUtil.java
│ │ └── ThreadLocalUtil.java
│ └── resources/
│ └── raknetify-channel-mappings.json
├── fabric/
│ ├── build.gradle
│ └── src/
│ └── main/
│ ├── java/
│ │ └── com/
│ │ └── ishland/
│ │ └── raknetify/
│ │ └── fabric/
│ │ ├── RaknetifyFabric.java
│ │ ├── common/
│ │ │ ├── client/
│ │ │ │ ├── DebugHudUtil.java
│ │ │ │ └── DebugHudUtil1_21_9.java
│ │ │ ├── compat/
│ │ │ │ ├── package-info.java
│ │ │ │ └── viafabric/
│ │ │ │ └── ViaFabricCompatInjector.java
│ │ │ ├── connection/
│ │ │ │ ├── MultiChannellingPacketCapture.java
│ │ │ │ ├── RakNetClientConnectionUtil.java
│ │ │ │ ├── RakNetCompressionCompatibilityHandler.java
│ │ │ │ ├── RakNetFabricChannelEventListener.java
│ │ │ │ ├── RakNetFabricConnectionUtil.java
│ │ │ │ ├── RakNetMultiChannel.java
│ │ │ │ ├── RakNetNetworkTransitionUtil.java
│ │ │ │ └── encryption/
│ │ │ │ └── PacketEncryptionManagerInterface.java
│ │ │ ├── package-info.java
│ │ │ ├── quirks/
│ │ │ │ └── ClientHungerManager.java
│ │ │ └── util/
│ │ │ ├── FieldSignatureParser.java
│ │ │ ├── LegacySupportUtil.java
│ │ │ ├── MultiVersionUtil.java
│ │ │ └── NetworkStates.java
│ │ └── mixin/
│ │ ├── RaknetifyFabricMixinPlugin.java
│ │ ├── access/
│ │ │ ├── IClientConnection.java
│ │ │ ├── IClientPlayNetworkHandler.java
│ │ │ ├── IDebugHudEntries.java
│ │ │ ├── INetworkState1_20_4.java
│ │ │ ├── INetworkStateInternalPacketHandler.java
│ │ │ ├── IPacketCodecDispatcher.java
│ │ │ ├── IPacketCodecDispatcherPacketType.java
│ │ │ ├── IPacketEncryptionManager.java
│ │ │ ├── IServerPlayNetworkHandler.java
│ │ │ └── IWorld.java
│ │ ├── client/
│ │ │ ├── MixinClientPlayNetworkHandler.java
│ │ │ ├── MixinConnectionScreen1.java
│ │ │ ├── MixinMultiplayerServerListPinger.java
│ │ │ ├── MixinMultiplayerServerListPinger1.java
│ │ │ ├── MixinMultiplayerServerListPinger1_20_2.java
│ │ │ ├── MixinMultiplayerServerListPinger1_20_5.java
│ │ │ ├── MixinMultiplayerServerListPinger1_21_11.java
│ │ │ └── hud/
│ │ │ └── MixinDebugHud1_21_8.java
│ │ ├── common/
│ │ │ ├── MixinCCConnect.java
│ │ │ ├── MixinClientConnection.java
│ │ │ ├── MixinClientConnection1.java
│ │ │ ├── MixinClientConnection1_20_2.java
│ │ │ ├── MixinServerAddress.java
│ │ │ ├── encryption/
│ │ │ │ ├── MixinClientConnection.java
│ │ │ │ ├── MixinPacketDecryptor.java
│ │ │ │ ├── MixinPacketEncryptionManager.java
│ │ │ │ └── MixinPacketEncryptor.java
│ │ │ └── quirks/
│ │ │ ├── MixinPlayerEntity.java
│ │ │ └── MixinSampleSubscriptionTracker.java
│ │ ├── compat/
│ │ │ ├── fabricapi/
│ │ │ │ └── MixinServerLoginNetworkAddon.java
│ │ │ ├── krypton/
│ │ │ │ └── MixinServerLoginNetworkHandler.java
│ │ │ ├── package-info.java
│ │ │ └── qsl/
│ │ │ └── MixinServerLoginNetworkAddon.java
│ │ ├── package-info.java
│ │ └── server/
│ │ ├── MixinPlayerManager1_20_1.java
│ │ ├── MixinPlayerManager1_20_2.java
│ │ ├── MixinServerCommonNetworkHandler.java
│ │ ├── MixinServerLoginNetworkHandler.java
│ │ ├── MixinServerNetworkIo.java
│ │ ├── MixinServerNetworkIo1.java
│ │ └── MixinServerPlayNetworkHandler1_20_1.java
│ └── resources/
│ ├── fabric.mod.json
│ ├── raknetify-fabric.accesswidener
│ └── raknetify-fabric.mixins.json
├── genMappings.sh
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── jitpack.yml
├── modrinth_license.txt
├── settings.gradle
└── velocity/
├── HEADER.txt
├── LICENSE
├── build.gradle
└── src/
└── main/
├── java/
│ └── com/
│ └── ishland/
│ └── raknetify/
│ └── velocity/
│ ├── RaknetifyVelocityLaunchWrapper.java
│ ├── RaknetifyVelocityPlugin.java
│ ├── connection/
│ │ ├── RakNetVelocityChannelEventListener.java
│ │ ├── RakNetVelocityConnectionUtil.java
│ │ ├── RakNetVelocityPingUpdater.java
│ │ └── RakNetVelocityServerChannelEventListener.java
│ └── init/
│ ├── VelocityPacketRegistryInjector.java
│ └── VelocityRaknetifyServer.java
└── resources/
└── velocity-plugin.json
SYMBOL INDEX (406 symbols across 95 files)
FILE: buildSrc/src/main/java/com/ishland/buildscript/ParseGItHubActionChangelog.java
class ParseGItHubActionChangelog (line 11) | public class ParseGItHubActionChangelog {
method getChangelog (line 13) | public static String getChangelog() throws Throwable {
FILE: bungee/src/main/java/com/ishland/raknetify/bungee/RaknetifyBungeePlugin.java
class RaknetifyBungeePlugin (line 38) | public class RaknetifyBungeePlugin extends Plugin implements Listener {
method onEnable (line 43) | @Override
method onDisable (line 57) | @Override
method onPostLogin (line 64) | @EventHandler
method handleServerSwitch (line 69) | @EventHandler
FILE: bungee/src/main/java/com/ishland/raknetify/bungee/connection/RakNetBungeeClientChannelEventListener.java
class RakNetBungeeClientChannelEventListener (line 57) | public class RakNetBungeeClientChannelEventListener extends ChannelDuple...
method write (line 67) | @Override
method channelRead (line 112) | @Override
method getSecretUnchecked (line 126) | private static SecretKey getSecretUnchecked(EncryptionResponse resp) t...
method setProtocol (line 132) | public void setProtocol(Protocol protocol, int protocolVersion) {
method exceptionCaught (line 137) | @Override
FILE: bungee/src/main/java/com/ishland/raknetify/bungee/connection/RakNetBungeeConnectionUtil.java
class RakNetBungeeConnectionUtil (line 53) | public class RakNetBungeeConnectionUtil {
method RakNetBungeeConnectionUtil (line 69) | private RakNetBungeeConnectionUtil() {
method initChannel (line 72) | public static void initChannel(Channel channel) {
method postInitChannel (line 80) | public static void postInitChannel(Channel channel, boolean isClientSi...
method onPlayerLogin (line 96) | public static void onPlayerLogin(PostLoginEvent evt) {
method handleServerSwitch (line 138) | public static void handleServerSwitch(ServerConnectedEvent evt) {
FILE: bungee/src/main/java/com/ishland/raknetify/bungee/connection/RakNetBungeePingUpdater.java
class RakNetBungeePingUpdater (line 39) | public class RakNetBungeePingUpdater extends ChannelDuplexHandler {
method RakNetBungeePingUpdater (line 47) | public RakNetBungeePingUpdater(UserConnection player) {
method handlerAdded (line 51) | public void handlerAdded(ChannelHandlerContext ctx) {
method handlerRemoved (line 60) | public void handlerRemoved(ChannelHandlerContext ctx) {
FILE: bungee/src/main/java/com/ishland/raknetify/bungee/connection/RakNetBungeeServerChannelEventListener.java
class RakNetBungeeServerChannelEventListener (line 45) | public class RakNetBungeeServerChannelEventListener extends ChannelDuple...
method RakNetBungeeServerChannelEventListener (line 51) | public RakNetBungeeServerChannelEventListener(Channel clientChannel) {
method channelRead (line 56) | @Override
method exceptionCaught (line 74) | @Override
FILE: bungee/src/main/java/com/ishland/raknetify/bungee/connection/StripFrameHandler.java
class StripFrameHandler (line 34) | @ChannelHandler.Sharable
method StripFrameHandler (line 41) | private StripFrameHandler() {
method write (line 44) | @Override
FILE: bungee/src/main/java/com/ishland/raknetify/bungee/init/BungeeRaknetifyServer.java
class BungeeRaknetifyServer (line 71) | public class BungeeRaknetifyServer {
method inject (line 99) | public static void inject() {
method injectChannel (line 162) | public static void injectChannel(BungeeCord instance, Channel listener...
method startServer (line 231) | private static void startServer(BungeeCord instance, Channel listener,...
method stopAll (line 275) | public static void stopAll() {
method disable (line 290) | public static void disable() {
method getBossEventLoopGroup (line 296) | private static EventLoopGroup getBossEventLoopGroup(BungeeCord instanc...
method getWorkerEventLoopGroup (line 305) | private static EventLoopGroup getWorkerEventLoopGroup(BungeeCord insta...
FILE: bungee/src/main/java/com/ishland/raknetify/bungee/init/InjectedSet.java
class InjectedSet (line 36) | public class InjectedSet extends ForwardingSet<Channel> {
method InjectedSet (line 40) | public InjectedSet(Set<Channel> delegate) {
method add (line 44) | @Override
method clear (line 54) | @Override
method delegate (line 61) | @Override
FILE: common/src/main/java/com/ishland/raknetify/common/Constants.java
class Constants (line 27) | public class Constants {
method Constants (line 46) | private Constants() {
FILE: common/src/main/java/com/ishland/raknetify/common/connection/ByteBufCopyDecoder.java
class ByteBufCopyDecoder (line 31) | public class ByteBufCopyDecoder extends ChannelInboundHandlerAdapter {
method channelRead (line 35) | @Override
FILE: common/src/main/java/com/ishland/raknetify/common/connection/FrameDataBlocker.java
class FrameDataBlocker (line 31) | public class FrameDataBlocker extends ChannelInboundHandlerAdapter {
method channelRead (line 35) | @Override
FILE: common/src/main/java/com/ishland/raknetify/common/connection/MetricsSynchronizationHandler.java
class MetricsSynchronizationHandler (line 38) | public class MetricsSynchronizationHandler extends ChannelDuplexHandler {
method handlerAdded (line 54) | @Override
method handlerRemoved (line 60) | @Override
method sendSyncPacket (line 65) | private void sendSyncPacket() {
method channelRead (line 94) | @Override
method isRemoteSupported (line 123) | public boolean isRemoteSupported() {
method getQueuedBytes (line 127) | public int getQueuedBytes() {
method getBurst (line 131) | public int getBurst() {
method getErrorRate (line 135) | public double getErrorRate() {
method getTX (line 139) | public int getTX() {
method getRX (line 143) | public int getRX() {
FILE: common/src/main/java/com/ishland/raknetify/common/connection/MultiChannelingStreamingCompression.java
class MultiChannelingStreamingCompression (line 41) | public class MultiChannelingStreamingCompression extends ChannelDuplexHa...
method MultiChannelingStreamingCompression (line 65) | public MultiChannelingStreamingCompression(int rawPacketId, int compre...
method doServerHandshake (line 70) | private void doServerHandshake(ChannelHandlerContext ctx) {
method doChannelStart (line 81) | private void doChannelStart(ChannelHandlerContext ctx) {
method initDeflater (line 97) | private void initDeflater(int channel) {
method initInflater (line 104) | private void initInflater(int channel) {
method channelActive (line 111) | @Override
method channelRead (line 117) | @Override
method write (line 180) | @Override
method getInBytesCompressed (line 234) | public long getInBytesCompressed() {
method getInBytesRaw (line 238) | public long getInBytesRaw() {
method getOutBytesCompressed (line 242) | public long getOutBytesCompressed() {
method getOutBytesRaw (line 246) | public long getOutBytesRaw() {
method isActive (line 250) | public boolean isActive() {
method handlerAdded (line 256) | public void handlerAdded(ChannelHandlerContext ctx) {
method handlerRemoved (line 260) | @Override
method tickMetrics (line 278) | private void tickMetrics() {
method getInCompressionRatio (line 305) | public double getInCompressionRatio() {
method getOutCompressionRatio (line 309) | public double getOutCompressionRatio() {
FILE: common/src/main/java/com/ishland/raknetify/common/connection/MultiChannellingEncryption.java
class MultiChannellingEncryption (line 39) | public class MultiChannellingEncryption extends ChannelDuplexHandler {
method MultiChannellingEncryption (line 46) | public MultiChannellingEncryption(SecretKey key) throws GeneralSecurit...
method MultiChannellingEncryption (line 55) | public MultiChannellingEncryption(Cipher decryption, Cipher encryption) {
method write (line 60) | @Override
method channelRead (line 87) | @Override
FILE: common/src/main/java/com/ishland/raknetify/common/connection/NoFlush.java
class NoFlush (line 30) | public class NoFlush extends ChannelDuplexHandler {
method flush (line 32) | @Override
FILE: common/src/main/java/com/ishland/raknetify/common/connection/PacketEncryptionManager.java
class PacketEncryptionManager (line 33) | public class PacketEncryptionManager {
method PacketEncryptionManager (line 38) | protected PacketEncryptionManager(Cipher cipher) {
method toByteArray (line 42) | private byte[] toByteArray(ByteBuf buf) {
method doWork (line 52) | public void doWork(ByteBuf buf, ByteBuf result) throws GeneralSecurity...
FILE: common/src/main/java/com/ishland/raknetify/common/connection/RakNetConnectionUtil.java
class RakNetConnectionUtil (line 44) | public class RakNetConnectionUtil {
method RakNetConnectionUtil (line 52) | private RakNetConnectionUtil() {
method initChannel (line 62) | public static void initChannel(Channel channel) {
method initRaknetChannel (line 83) | private static void initRaknetChannel(Channel appChannel) {
method reInitChannelForOrdering (line 130) | @SuppressWarnings("unchecked")
FILE: common/src/main/java/com/ishland/raknetify/common/connection/RakNetSimpleMultiChannelCodec.java
class RakNetSimpleMultiChannelCodec (line 44) | public class RakNetSimpleMultiChannelCodec extends ChannelDuplexHandler {
method RakNetSimpleMultiChannelCodec (line 52) | public RakNetSimpleMultiChannelCodec(int packetId) {
method addHandler (line 58) | public RakNetSimpleMultiChannelCodec addHandler(OverrideHandler handle...
method removeHandler (line 65) | public void removeHandler(OverrideHandler handler) {
method getHandler (line 71) | public <T> T getHandler(Class<T> clazz) {
method handlerRemoved (line 85) | @Override
method write (line 94) | @Override
method encode0 (line 156) | private FrameData encode0(ChannelHandlerContext ctx, ByteBuf buf) {
method flushPendingWrites (line 176) | private void flushPendingWrites(ChannelHandlerContext ctx) {
method isMultichannelAvailable (line 188) | protected boolean isMultichannelAvailable() {
method getChannelOverride (line 194) | protected int getChannelOverride(ByteBuf buf, boolean suppressWarning) {
method channelRead (line 204) | @Override
method decode (line 223) | protected void decode(ChannelHandlerContext ctx, FrameData packet, Lis...
type OverrideHandler (line 236) | public interface OverrideHandler {
method getChannelOverride (line 237) | int getChannelOverride(ByteBuf buf, boolean suppressWarning);
class PacketIdBasedOverrideHandler (line 240) | public static class PacketIdBasedOverrideHandler implements OverrideHa...
method PacketIdBasedOverrideHandler (line 246) | public PacketIdBasedOverrideHandler(Int2IntMap channelMapping, Strin...
method getChannelOverride (line 251) | @Override
FILE: common/src/main/java/com/ishland/raknetify/common/connection/RaknetifyEventLoops.java
class RaknetifyEventLoops (line 36) | public class RaknetifyEventLoops {
FILE: common/src/main/java/com/ishland/raknetify/common/connection/SimpleMetricsLogger.java
class SimpleMetricsLogger (line 34) | @SuppressWarnings("NonAtomicOperationOnVolatileField")
method packetsIn (line 57) | @Override
method framesIn (line 62) | @Override
method frameError (line 67) | @Override
method bytesIn (line 72) | @Override
method packetsOut (line 77) | @Override
method framesOut (line 82) | @Override
method bytesOut (line 87) | @Override
method bytesRecalled (line 93) | @Override
method bytesACKd (line 98) | @Override
method bytesNACKd (line 103) | @Override
method acksSent (line 108) | @Override
method nacksSent (line 113) | @Override
method measureRTTns (line 118) | @Override
method measureRTTnsStdDev (line 123) | @Override
method measureBurstTokens (line 129) | @Override
method currentQueuedBytes (line 134) | @Override
method tick (line 143) | private synchronized void tick() {
method tickErrorRate (line 158) | private void tickErrorRate() {
method tickRXTX (line 187) | private void tickRXTX(long deltaTime) {
method getMeasureRTTns (line 216) | public long getMeasureRTTns() {
method getMeasureRTTnsStdDev (line 220) | public long getMeasureRTTnsStdDev() {
method getMeasureErrorRate (line 224) | public double getMeasureErrorRate() {
method getMeasureRX (line 228) | public int getMeasureRX() {
method getMeasureTX (line 232) | public int getMeasureTX() {
method getCurrentQueuedBytes (line 236) | public int getCurrentQueuedBytes() {
method getMeasureBurstTokens (line 240) | public long getMeasureBurstTokens() {
method getMeasureBytesInRate (line 244) | public long getMeasureBytesInRate() {
method getMeasureBytesOutRate (line 248) | public long getMeasureBytesOutRate() {
method getMeasureTrafficInFormatted (line 252) | public String getMeasureTrafficInFormatted() {
method getMeasureTrafficOutFormatted (line 256) | public String getMeasureTrafficOutFormatted() {
method getBytesIn (line 260) | public long getBytesIn() {
method getMetricsSynchronizationHandler (line 268) | public MetricsSynchronizationHandler getMetricsSynchronizationHandler() {
method setMetricsSynchronizationHandler (line 272) | public void setMetricsSynchronizationHandler(MetricsSynchronizationHan...
FILE: common/src/main/java/com/ishland/raknetify/common/connection/SynchronizationLayer.java
class SynchronizationLayer (line 59) | public class SynchronizationLayer extends ChannelDuplexHandler {
method SynchronizationLayer (line 116) | public SynchronizationLayer(int... channelsToIgnore) {
method channelActive (line 122) | @SuppressWarnings("unchecked")
method initializeIfNecessary (line 129) | private void initializeIfNecessary(ChannelHandlerContext ctx) {
method channelRead (line 159) | @Override
method write (line 214) | @Override
method dropSenderPackets (line 255) | private void dropSenderPackets() {
method flushQueue (line 295) | private void flushQueue(ChannelHandlerContext ctx) {
FILE: common/src/main/java/com/ishland/raknetify/common/connection/multichannel/CustomPayloadChannel.java
class CustomPayloadChannel (line 36) | public class CustomPayloadChannel {
class OverrideHandler (line 49) | public static class OverrideHandler implements RakNetSimpleMultiChanne...
method OverrideHandler (line 53) | public OverrideHandler(IntPredicate isCustomPayload) {
method getChannelOverride (line 57) | @Override
FILE: common/src/main/java/com/ishland/raknetify/common/data/ProtocolMultiChannelMappings.java
class ProtocolMultiChannelMappings (line 36) | public class ProtocolMultiChannelMappings {
method init (line 59) | public static void init() {
class VersionMapping (line 64) | public static class VersionMapping {
FILE: common/src/main/java/com/ishland/raknetify/common/util/DebugUtil.java
class DebugUtil (line 34) | public class DebugUtil {
method printChannelDetails (line 36) | public static String printChannelDetails(Channel channel) {
FILE: common/src/main/java/com/ishland/raknetify/common/util/MathUtil.java
class MathUtil (line 33) | public class MathUtil {
method humanReadableByteCountBin (line 35) | public static String humanReadableByteCountBin(long bytes) {
method readVarInt (line 50) | public static int readVarInt(ByteBuf buf) {
method readString (line 67) | public static String readString(ByteBuf buf) {
method readString (line 71) | public static String readString(ByteBuf buf, int maxLen) {
FILE: common/src/main/java/com/ishland/raknetify/common/util/NetworkInterfaceListener.java
class NetworkInterfaceListener (line 41) | public class NetworkInterfaceListener {
method init (line 62) | public static void init() {
method pollChanges (line 68) | private static void pollChanges() {
method addListener (line 95) | public static void addListener(Consumer<InterfaceAddressChangeEvent> c...
method removeListener (line 99) | public static void removeListener(Consumer<InterfaceAddressChangeEvent...
FILE: common/src/main/java/com/ishland/raknetify/common/util/PrefixUtil.java
class PrefixUtil (line 29) | public class PrefixUtil {
method getInfo (line 31) | public static Info getInfo(String address) {
FILE: common/src/main/java/com/ishland/raknetify/common/util/ReflectionUtil.java
class ReflectionUtil (line 30) | public class ReflectionUtil {
method accessible (line 32) | public static Field accessible(Field field) {
method accessible (line 37) | public static Method accessible(Method method) {
FILE: common/src/main/java/com/ishland/raknetify/common/util/ThreadLocalUtil.java
class ThreadLocalUtil (line 27) | public class ThreadLocalUtil {
method ThreadLocalUtil (line 29) | private ThreadLocalUtil() {
method setInitializingRaknet (line 35) | public static void setInitializingRaknet(boolean value) {
method setInitializingRaknetLargeMTU (line 39) | public static void setInitializingRaknetLargeMTU(boolean value) {
method isInitializingRaknet (line 43) | public static boolean isInitializingRaknet() {
method isInitializingRaknetLargeMTU (line 47) | public static boolean isInitializingRaknetLargeMTU() {
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/RaknetifyFabric.java
class RaknetifyFabric (line 89) | public class RaknetifyFabric implements ModInitializer, PreLaunchEntrypo...
method onPreLaunch (line 94) | @Override
method onInitialize (line 98) | @Override
method handleMappings (line 122) | private static void handleMappings() {
method getPacketIdsFromPacketHandler (line 274) | public static Object2IntMap<Class<? extends Packet<?>>> getPacketIdsFr...
method auditMixins (line 286) | private static void auditMixins() {
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/common/client/DebugHudUtil.java
class DebugHudUtil (line 40) | public class DebugHudUtil {
method getDebugString (line 42) | public static void getDebugString(Consumer<String> consumer) {
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/common/client/DebugHudUtil1_21_9.java
class DebugHudUtil1_21_9 (line 37) | public class DebugHudUtil1_21_9 {
method init (line 41) | public static void init() {
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/common/compat/viafabric/ViaFabricCompatInjector.java
class ViaFabricCompatInjector (line 38) | public class ViaFabricCompatInjector {
method inject (line 40) | public static void inject(Channel channel, boolean isClientSide) {
method determineEncoderName (line 83) | private static String determineEncoderName(ChannelPipeline pipeline) {
method determineDecoderName (line 89) | private static String determineDecoderName(ChannelPipeline pipeline) {
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/common/connection/MultiChannellingPacketCapture.java
class MultiChannellingPacketCapture (line 37) | @ChannelHandler.Sharable
method write (line 42) | @Override
method getPacketClass (line 52) | public Class<?> getPacketClass() {
method setPacketClass (line 56) | public void setPacketClass(Class<?> packetClass) {
method getCaptureBasedHandler (line 60) | public RakNetSimpleMultiChannelCodec.OverrideHandler getCaptureBasedHa...
method getCustomPayloadHandler (line 64) | public RakNetSimpleMultiChannelCodec.OverrideHandler getCustomPayloadH...
class CaptureBasedHandler (line 68) | private class CaptureBasedHandler implements RakNetSimpleMultiChannelC...
method getChannelOverride (line 70) | @Override
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/common/connection/RakNetClientConnectionUtil.java
class RakNetClientConnectionUtil (line 34) | public class RakNetClientConnectionUtil {
method RakNetClientConnectionUtil (line 36) | private RakNetClientConnectionUtil() {
method connect (line 39) | public static ClientConnection connect(InetSocketAddress address, bool...
method connect (line 55) | public static ClientConnection connect(InetSocketAddress address, Obje...
method connect (line 67) | public static ChannelFuture connect(InetSocketAddress address, boolean...
method connect (line 79) | public static ChannelFuture connect(InetSocketAddress address, Object ...
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/common/connection/RakNetCompressionCompatibilityHandler.java
class RakNetCompressionCompatibilityHandler (line 32) | public class RakNetCompressionCompatibilityHandler extends ChannelDuplex...
method write (line 34) | @Override
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/common/connection/RakNetFabricChannelEventListener.java
class RakNetFabricChannelEventListener (line 40) | public class RakNetFabricChannelEventListener extends ChannelDuplexHandl...
method write (line 57) | @Override
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/common/connection/RakNetFabricConnectionUtil.java
class RakNetFabricConnectionUtil (line 47) | public class RakNetFabricConnectionUtil {
method RakNetFabricConnectionUtil (line 51) | private RakNetFabricConnectionUtil() {
method initChannel (line 54) | public static void initChannel(Channel channel) {
method postInitChannel (line 61) | public static void postInitChannel(Channel channel, boolean isClientSi...
method onPipelineReorder (line 78) | static void onPipelineReorder(ChannelPipeline pipeline) {
method fromSocketChannel (line 90) | public static DatagramChannel fromSocketChannel(Class<? extends Socket...
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/common/connection/RakNetMultiChannel.java
class RakNetMultiChannel (line 35) | public class RakNetMultiChannel {
method RakNetMultiChannel (line 37) | private RakNetMultiChannel() {
method createClassSet (line 40) | private static Set<Class<?>> createClassSet(String[] classNames) {
method getPacketChannelOverride (line 408) | public static int getPacketChannelOverride(Class<?> clazz, boolean sup...
method init (line 427) | public static void init() {
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/common/connection/RakNetNetworkTransitionUtil.java
class RakNetNetworkTransitionUtil (line 29) | public class RakNetNetworkTransitionUtil {
method handleTransition (line 31) | static Object handleTransition(Object msg) {
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/common/connection/encryption/PacketEncryptionManagerInterface.java
type PacketEncryptionManagerInterface (line 29) | public interface PacketEncryptionManagerInterface {
method setContext (line 31) | void setContext(ChannelHandlerContext ctx);
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/common/quirks/ClientHungerManager.java
class ClientHungerManager (line 37) | public class ClientHungerManager extends HungerManager {
method from (line 39) | public static ClientHungerManager from(HungerManager hungerManager) {
method add (line 73) | public void add(int food, float saturationModifier) {
method eat (line 77) | public void eat(FoodComponent foodComponent) {
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/common/util/FieldSignatureParser.java
class FieldSignatureParser (line 35) | public class FieldSignatureParser extends SignatureVisitor {
method FieldSignatureParser (line 39) | private FieldSignatureParser() {
method parse (line 43) | public static List<Type> parse(String signature) {
method visitClassType (line 52) | @Override
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/common/util/LegacySupportUtil.java
class LegacySupportUtil (line 31) | public class LegacySupportUtil {
method getEntityWorld (line 33) | public static World getEntityWorld(Entity entity) {
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/common/util/MultiVersionUtil.java
class MultiVersionUtil (line 51) | public class MultiVersionUtil {
method tryLocateFields (line 231) | private static List<Field> tryLocateFields(Class<?> clazz, Class<?> fi...
method init (line 254) | public static void init() {
method tryLocateClass (line 257) | public static Class<?> tryLocateClass(String name) {
method getOrNull (line 265) | private static <T> T getOrNull(SupplierThrowable<T> supplier, Class<? ...
type SupplierThrowable (line 279) | private interface SupplierThrowable<T> {
method get (line 280) | T get() throws Throwable;
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/common/util/NetworkStates.java
class NetworkStates (line 29) | public class NetworkStates {
method getName (line 31) | public static String getName(NetworkPhase state) {
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/RaknetifyFabricMixinPlugin.java
class RaknetifyFabricMixinPlugin (line 39) | public class RaknetifyFabricMixinPlugin implements IMixinConfigPlugin {
method onLoad (line 66) | @Override
method getRefMapperConfig (line 72) | @Override
method shouldApplyMixin (line 77) | @Override
method acceptTargets (line 109) | @Override
method getMixins (line 114) | @Override
method preApply (line 119) | @Override
method postApply (line 124) | @Override
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/access/IClientConnection.java
type IClientConnection (line 32) | @Mixin(ClientConnection.class)
method getChannel (line 35) | @Accessor
method setEncrypted (line 38) | @Accessor
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/access/IClientPlayNetworkHandler.java
type IClientPlayNetworkHandler (line 32) | @Mixin(ClientPlayNetworkHandler.class)
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/access/IDebugHudEntries.java
type IDebugHudEntries (line 33) | @Mixin(DebugHudEntries.class)
method invokeRegister (line 36) | @Invoker
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/access/INetworkState1_20_4.java
type INetworkState1_20_4 (line 36) | @Pseudo
method getPacketHandlers (line 40) | @Dynamic
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/access/INetworkStateInternalPacketHandler.java
type INetworkStateInternalPacketHandler (line 33) | @Mixin(targets = "net/minecraft/class_2539$class_4532")
method getPacketIds (line 36) | @Accessor(value = "field_20596")
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/access/IPacketCodecDispatcher.java
type IPacketCodecDispatcher (line 34) | @Mixin(PacketCodecDispatcher.class)
method getPacketTypes (line 37) | @Accessor
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/access/IPacketCodecDispatcherPacketType.java
type IPacketCodecDispatcherPacketType (line 30) | @Mixin(PacketCodecDispatcher.PacketType.class)
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/access/IPacketEncryptionManager.java
type IPacketEncryptionManager (line 35) | @Mixin(PacketEncryptionManager.class)
method invokeDecrypt (line 38) | @Invoker
method invokeEncrypt (line 41) | @Invoker
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/access/IServerPlayNetworkHandler.java
type IServerPlayNetworkHandler (line 30) | @Mixin(ServerPlayNetworkHandler.class)
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/access/IWorld.java
type IWorld (line 31) | @Mixin(World.class)
method raknetify$isClient (line 34) | @Accessor("isClient")
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/client/MixinClientPlayNetworkHandler.java
class MixinClientPlayNetworkHandler (line 41) | @Mixin(ClientPlayNetworkHandler.class)
method postGameJoin (line 44) | @Inject(method = "onGameJoin", at = @At("RETURN"))
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/client/MixinConnectionScreen1.java
class MixinConnectionScreen1 (line 48) | @Mixin(targets = "net/minecraft/client/gui/screen/multiplayer/ConnectScr...
method onInit (line 62) | @Inject(method = "<init>*", at = @At("RETURN"), remap = false)
method connectRaknet (line 72) | @Dynamic
method connectRaknet (line 78) | @Dynamic
method connectRaknet (line 84) | @WrapOperation(method = "run()V", at = @At(value = "INVOKE", target = ...
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/client/MixinMultiplayerServerListPinger.java
class MixinMultiplayerServerListPinger (line 42) | @Mixin(MultiplayerServerListPinger.class)
method modifyRaknetAddress (line 45) | @Dynamic
method redirectConnect (line 52) | @Dynamic
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/client/MixinMultiplayerServerListPinger1.java
class MixinMultiplayerServerListPinger1 (line 48) | @Mixin(targets = "net/minecraft/client/network/MultiplayerServerListPing...
method setPingImmediately (line 59) | @Inject(method = "onResponse(Lnet/minecraft/network/packet/s2c/query/Q...
method noPingRaknet (line 67) | @Dynamic
method noPingRaknet (line 74) | @WrapWithCondition(method = "onDisconnected", at = @At(value = "INVOKE...
method noPingRaknet (line 80) | @Dynamic
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/client/MixinMultiplayerServerListPinger1_20_2.java
class MixinMultiplayerServerListPinger1_20_2 (line 41) | @Mixin(MultiplayerServerListPinger.class)
method redirectConnect (line 44) | @Dynamic
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/client/MixinMultiplayerServerListPinger1_20_5.java
class MixinMultiplayerServerListPinger1_20_5 (line 43) | @Mixin(MultiplayerServerListPinger.class)
method redirectConnect (line 46) | @Dynamic
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/client/MixinMultiplayerServerListPinger1_21_11.java
class MixinMultiplayerServerListPinger1_21_11 (line 41) | @Mixin(MultiplayerServerListPinger.class)
method redirectConnect (line 44) | @WrapOperation(method = "add", at = @At(value = "INVOKE", target = "Ln...
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/client/hud/MixinDebugHud1_21_8.java
class MixinDebugHud1_21_8 (line 37) | @Mixin(DebugHud.class)
method getLeftText (line 40) | @Dynamic
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/common/MixinCCConnect.java
class MixinCCConnect (line 57) | @Mixin(ClientConnection.class)
method redirectGroup (line 60) | @Dynamic("method_10753, method_52271 for compat")
method redirectChannel (line 69) | @Dynamic("method_10753, method_52271 for compat")
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/common/MixinClientConnection.java
class MixinClientConnection (line 50) | @Mixin(ClientConnection.class)
method getAddress (line 56) | @Shadow public abstract SocketAddress getAddress();
method getSide (line 60) | @Shadow public abstract NetworkSide getSide();
method noDisconnectWait (line 66) | @Redirect(method = {"disconnect(Lnet/minecraft/text/Text;)V", "disconn...
method redirectIsOpen (line 77) | @Redirect(method = "*", at = @At(value = "INVOKE", target = "Lio/netty...
method onExceptionCaught (line 94) | @Inject(method = "exceptionCaught", at = @At("HEAD"))
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/common/MixinClientConnection1.java
class MixinClientConnection1 (line 35) | @Mixin(targets = "net/minecraft/network/ClientConnection$1")
method onChannelInit (line 38) | @Inject(method = "initChannel(Lio/netty/channel/Channel;)V", at = @At(...
method postChannelInit (line 43) | @Inject(method = "initChannel(Lio/netty/channel/Channel;)V", at = @At(...
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/common/MixinClientConnection1_20_2.java
class MixinClientConnection1_20_2 (line 40) | @Mixin(ClientConnection.class)
method onPacketLoggerPush (line 49) | @Inject(method = "tick", at = @At(value = "INVOKE", target = "Lnet/min...
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/common/MixinServerAddress.java
class MixinServerAddress (line 34) | @Mixin(ServerAddress.class)
method wrapParsing (line 37) | @WrapMethod(method = "parse")
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/common/encryption/MixinClientConnection.java
class MixinClientConnection (line 44) | @Mixin(ClientConnection.class)
method beforeSetupEncryption (line 53) | @Inject(method = "setupEncryption", at = @At("HEAD"), cancellable = true)
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/common/encryption/MixinPacketDecryptor.java
class MixinPacketDecryptor (line 41) | @Mixin(PacketDecryptor.class)
method preDecrypt (line 46) | @Inject(method = "decode(Lio/netty/channel/ChannelHandlerContext;Lio/n...
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/common/encryption/MixinPacketEncryptionManager.java
class MixinPacketEncryptionManager (line 41) | @Mixin(PacketEncryptionManager.class)
method setContext (line 47) | @Override
method redirectDecrypt (line 52) | @Redirect(method = "decrypt", at = @At(value = "INVOKE", target = "Lja...
method redirectEncrypt (line 61) | @Redirect(method = "encrypt", at = @At(value = "INVOKE", target = "Lja...
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/common/encryption/MixinPacketEncryptor.java
class MixinPacketEncryptor (line 39) | @Mixin(PacketEncryptor.class)
method preEncrypt (line 44) | @Inject(method = "encode(Lio/netty/channel/ChannelHandlerContext;Lio/n...
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/common/quirks/MixinPlayerEntity.java
class MixinPlayerEntity (line 41) | @Mixin(PlayerEntity.class)
method MixinPlayerEntity (line 46) | protected MixinPlayerEntity(EntityType<? extends LivingEntity> entityT...
method replaceHungerManager (line 50) | @Inject(method = "<init>", at = @At("RETURN"), remap = false)
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/common/quirks/MixinSampleSubscriptionTracker.java
class MixinSampleSubscriptionTracker (line 38) | @Pseudo
method cleanQueue (line 44) | @Dynamic
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/compat/fabricapi/MixinServerLoginNetworkAddon.java
class MixinServerLoginNetworkAddon (line 43) | @Pseudo
method stopCompressionIfStreamingCompressionExists (line 51) | @Dynamic("Pseudo")
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/compat/krypton/MixinServerLoginNetworkHandler.java
class MixinServerLoginNetworkHandler (line 48) | @Mixin(ServerLoginNetworkHandler.class)
method afterSetupEncryption (line 59) | @Inject(method = "onKey", at = @At(value = "INVOKE", target = "Lnet/mi...
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/compat/qsl/MixinServerLoginNetworkAddon.java
class MixinServerLoginNetworkAddon (line 43) | @Pseudo
method stopCompressionIfStreamingCompressionExists (line 51) | @Dynamic("Pseudo")
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/server/MixinPlayerManager1_20_1.java
class MixinPlayerManager1_20_1 (line 41) | @Mixin(PlayerManager.class)
method onJoin (line 44) | @Dynamic
method onJoin (line 52) | @Surrogate
method postJoin (line 59) | @Dynamic
method postJoin (line 71) | @Surrogate
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/server/MixinPlayerManager1_20_2.java
class MixinPlayerManager1_20_2 (line 42) | @Mixin(PlayerManager.class)
method onJoin (line 45) | @Inject(method = "onPlayerConnect", at = @At("HEAD"))
method postJoin (line 52) | @Inject(method = "onPlayerConnect", at = @At("RETURN"))
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/server/MixinServerCommonNetworkHandler.java
class MixinServerCommonNetworkHandler (line 47) | @Mixin(ServerCommonNetworkHandler.class)
method disableKeepAlive (line 54) | @ModifyExpressionValue(method = "baseTick", at = @At(value = "FIELD", ...
method stopTimeoutPlayersOnKeepAlive (line 62) | @WrapWithCondition(method = "onKeepAlive", at = @At(value = "INVOKE", ...
method redirectPingStoring (line 67) | @WrapWithCondition(method = "onKeepAlive", at = @At(value = "FIELD", t...
method onTick (line 72) | @Inject(method = "baseTick", at = @At("HEAD"))
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/server/MixinServerLoginNetworkHandler.java
class MixinServerLoginNetworkHandler (line 43) | @Mixin(value = ServerLoginNetworkHandler.class, priority = 900)
method stopCompressionIfStreamingCompressionExists (line 50) | @Dynamic
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/server/MixinServerNetworkIo.java
class MixinServerNetworkIo (line 67) | @Mixin(ServerNetworkIo.class)
method bind (line 77) | @Shadow
method bindUdp (line 89) | @Inject(method = "bind", at = @At("HEAD"))
method redirectGroup (line 160) | @WrapOperation(method = "bind", at = @At(value = "INVOKE", target = "L...
method redirectChannel (line 168) | @WrapOperation(method = "bind", at = @At(value = "INVOKE", target = "L...
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/server/MixinServerNetworkIo1.java
class MixinServerNetworkIo1 (line 35) | @Mixin(targets = "net/minecraft/server/ServerNetworkIo$1")
method onChannelInit (line 38) | @Inject(method = "initChannel(Lio/netty/channel/Channel;)V", at = @At(...
method postChannelInit (line 43) | @Inject(method = "initChannel(Lio/netty/channel/Channel;)V", at = @At(...
FILE: fabric/src/main/java/com/ishland/raknetify/fabric/mixin/server/MixinServerPlayNetworkHandler1_20_1.java
class MixinServerPlayNetworkHandler1_20_1 (line 47) | @Mixin(ServerPlayNetworkHandler.class)
method disableKeepAlive (line 52) | @Dynamic
method stopTimeoutPlayersOnKeepAlive (line 61) | @Dynamic
method redirectPingStoring (line 67) | @Dynamic
method onTick (line 73) | @Inject(method = "tick", at = @At("HEAD"))
FILE: velocity/src/main/java/com/ishland/raknetify/velocity/RaknetifyVelocityLaunchWrapper.java
class RaknetifyVelocityLaunchWrapper (line 37) | public class RaknetifyVelocityLaunchWrapper {
method launch (line 39) | public static void launch() {
method isCompatible (line 59) | private static boolean isCompatible() {
FILE: velocity/src/main/java/com/ishland/raknetify/velocity/RaknetifyVelocityPlugin.java
class RaknetifyVelocityPlugin (line 39) | @Plugin(
method isExcluded (line 59) | private static boolean isExcluded(String name) {
method onProxyInit (line 77) | @Subscribe
method isDevLaunch (line 105) | private static boolean isDevLaunch() {
class RaknetifyURLClassLoader (line 114) | private static class RaknetifyURLClassLoader extends URLClassLoader {
method RaknetifyURLClassLoader (line 116) | public RaknetifyURLClassLoader(URL[] urls, ClassLoader parent) {
method RaknetifyURLClassLoader (line 120) | public RaknetifyURLClassLoader(URL[] urls) {
method RaknetifyURLClassLoader (line 124) | public RaknetifyURLClassLoader(URL[] urls, ClassLoader parent, URLSt...
method RaknetifyURLClassLoader (line 128) | public RaknetifyURLClassLoader(String name, URL[] urls, ClassLoader ...
method RaknetifyURLClassLoader (line 132) | public RaknetifyURLClassLoader(String name, URL[] urls, ClassLoader ...
method loadClass (line 136) | @Override
FILE: velocity/src/main/java/com/ishland/raknetify/velocity/connection/RakNetVelocityChannelEventListener.java
class RakNetVelocityChannelEventListener (line 48) | public class RakNetVelocityChannelEventListener extends ChannelDuplexHan...
method write (line 54) | @Override
method channelRead (line 75) | @Override
method userEventTriggered (line 88) | @Override
FILE: velocity/src/main/java/com/ishland/raknetify/velocity/connection/RakNetVelocityConnectionUtil.java
class RakNetVelocityConnectionUtil (line 49) | public class RakNetVelocityConnectionUtil {
method RakNetVelocityConnectionUtil (line 51) | private RakNetVelocityConnectionUtil() {
method initChannel (line 54) | public static void initChannel(Channel channel) {
method postInitChannel (line 63) | public static void postInitChannel(Channel channel, boolean isClientSi...
method onPlayerLogin (line 78) | public static void onPlayerLogin(LoginEvent evt) {
method onServerSwitch (line 109) | public static void onServerSwitch(ServerPostConnectEvent evt) {
FILE: velocity/src/main/java/com/ishland/raknetify/velocity/connection/RakNetVelocityPingUpdater.java
class RakNetVelocityPingUpdater (line 35) | public class RakNetVelocityPingUpdater extends ChannelDuplexHandler {
method RakNetVelocityPingUpdater (line 53) | public RakNetVelocityPingUpdater(ConnectedPlayer player) {
method handlerAdded (line 57) | public void handlerAdded(ChannelHandlerContext ctx) {
method handlerRemoved (line 72) | public void handlerRemoved(ChannelHandlerContext ctx) {
FILE: velocity/src/main/java/com/ishland/raknetify/velocity/connection/RakNetVelocityServerChannelEventListener.java
class RakNetVelocityServerChannelEventListener (line 36) | public class RakNetVelocityServerChannelEventListener extends ChannelDup...
method RakNetVelocityServerChannelEventListener (line 42) | public RakNetVelocityServerChannelEventListener(Channel clientChannel) {
method channelRead (line 47) | @Override
method exceptionCaught (line 62) | @Override
FILE: velocity/src/main/java/com/ishland/raknetify/velocity/init/VelocityPacketRegistryInjector.java
class VelocityPacketRegistryInjector (line 35) | public class VelocityPacketRegistryInjector {
method inject (line 37) | public static void inject() {
FILE: velocity/src/main/java/com/ishland/raknetify/velocity/init/VelocityRaknetifyServer.java
class VelocityRaknetifyServer (line 56) | public class VelocityRaknetifyServer {
method start (line 102) | public static void start(ListenerBoundEvent evt) {
method startServer (line 129) | private static void startServer(InetSocketAddress address) {
method stop (line 179) | public static void stop(ListenerCloseEvent evt) {
method closeServer (line 193) | private static void closeServer(ChannelFuture channel) {
Condensed preview — 133 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (606K chars).
[
{
"path": ".github/workflows/build.yml",
"chars": 2036,
"preview": "# Automatically build the project and run any configured tests for every push\n# and submitted pull request. This can hel"
},
{
"path": ".github/workflows/checkMappings.yml",
"chars": 1755,
"preview": "name: check mappings\non: [pull_request, push]\n\njobs:\n checkMappings:\n strategy:\n matrix:\n # Use these Ja"
},
{
"path": ".gitignore",
"chars": 189,
"preview": "# gradle\n\n.gradle/\nbuild/\nout/\nclasses/\n\n# eclipse\n\n*.launch\n\n# idea\n\n.idea/\n*.iml\n*.ipr\n*.iws\n\n# vscode\n\n.settings/\n.vs"
},
{
"path": ".gitmodules",
"chars": 104,
"preview": "[submodule \"netty-raknet\"]\n\tpath = netty-raknet\n\turl = https://github.com/RelativityMC/netty-raknet.git\n"
},
{
"path": ".run/BungeeCord Server.run.xml",
"chars": 1121,
"preview": "<component name=\"ProjectRunConfigurationManager\">\n <configuration default=\"false\" name=\"BungeeCord Server\" type=\"Applic"
},
{
"path": ".run/Velocity Server.run.xml",
"chars": 773,
"preview": "<component name=\"ProjectRunConfigurationManager\">\n <configuration default=\"false\" name=\"Velocity Server\" type=\"Applicat"
},
{
"path": "HEADER.txt",
"chars": 1123,
"preview": "This file is a part of the Raknetify project, licensed under MIT.\n\nCopyright (c) 2022-2025 ishland\n\nPermission is hereby"
},
{
"path": "Jenkinsfile",
"chars": 927,
"preview": "pipeline {\n agent { label 'slave' }\n options { timestamps() }\n stages {\n stage('SCM-SKIP') {\n "
},
{
"path": "LICENSE",
"chars": 1079,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2022-2025 ishland\n\nPermission is hereby granted, free of charge, to any person obta"
},
{
"path": "README.md",
"chars": 1507,
"preview": "# Raknetify\nA Fabric mod / Velocity plugin / BungeeCord plugin that uses RakNet to improve multiplayer experience signif"
},
{
"path": "build.gradle",
"chars": 3418,
"preview": "plugins {\n\tid 'fabric-loom' version '1.13-SNAPSHOT' apply false\n\tid 'com.gradleup.shadow' version '9.2.1' apply false\n\ti"
},
{
"path": "buildSrc/build.gradle",
"chars": 308,
"preview": "plugins {\n id 'java'\n}\n\njava {\n sourceCompatibility = JavaVersion.VERSION_17\n targetCompatibility = JavaVersion"
},
{
"path": "buildSrc/src/main/java/com/ishland/buildscript/ParseGItHubActionChangelog.java",
"chars": 1661,
"preview": "package com.ishland.buildscript;\n\nimport com.google.gson.Gson;\nimport com.google.gson.JsonArray;\nimport com.google.gson."
},
{
"path": "bungee/build.gradle",
"chars": 2325,
"preview": "import java.nio.file.Files\nimport java.nio.file.StandardCopyOption\n\nevaluationDependsOn(\":\")\n\napply plugin: 'com.gradleu"
},
{
"path": "bungee/src/main/java/com/ishland/raknetify/bungee/RaknetifyBungeePlugin.java",
"chars": 2562,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "bungee/src/main/java/com/ishland/raknetify/bungee/connection/RakNetBungeeClientChannelEventListener.java",
"chars": 8844,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "bungee/src/main/java/com/ishland/raknetify/bungee/connection/RakNetBungeeConnectionUtil.java",
"chars": 9007,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "bungee/src/main/java/com/ishland/raknetify/bungee/connection/RakNetBungeePingUpdater.java",
"chars": 2585,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "bungee/src/main/java/com/ishland/raknetify/bungee/connection/RakNetBungeeServerChannelEventListener.java",
"chars": 3956,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "bungee/src/main/java/com/ishland/raknetify/bungee/connection/StripFrameHandler.java",
"chars": 2117,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "bungee/src/main/java/com/ishland/raknetify/bungee/init/BungeeRaknetifyServer.java",
"chars": 14801,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "bungee/src/main/java/com/ishland/raknetify/bungee/init/InjectedSet.java",
"chars": 2271,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "bungee/src/main/resources/bungee.yml",
"chars": 1303,
"preview": "#\n# This file is a part of the Raknetify project, licensed under MIT.\n#\n# Copyright (c) 2022-2025 ishland\n#\n# Permission"
},
{
"path": "common/build.gradle",
"chars": 500,
"preview": "dependencies {\n api(\"com.github.RelativityMC.netty-raknet:netty-raknet-common\") {\n transitive = false\n }\n "
},
{
"path": "common/src/main/java/com/ishland/raknetify/common/Constants.java",
"chars": 2243,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "common/src/main/java/com/ishland/raknetify/common/connection/ByteBufCopyDecoder.java",
"chars": 1887,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "common/src/main/java/com/ishland/raknetify/common/connection/FrameDataBlocker.java",
"chars": 1892,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "common/src/main/java/com/ishland/raknetify/common/connection/MetricsSynchronizationHandler.java",
"chars": 5228,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "common/src/main/java/com/ishland/raknetify/common/connection/MultiChannelingStreamingCompression.java",
"chars": 12852,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "common/src/main/java/com/ishland/raknetify/common/connection/MultiChannellingEncryption.java",
"chars": 4765,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "common/src/main/java/com/ishland/raknetify/common/connection/NoFlush.java",
"chars": 1477,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "common/src/main/java/com/ishland/raknetify/common/connection/PacketEncryptionManager.java",
"chars": 2401,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "common/src/main/java/com/ishland/raknetify/common/connection/RakNetConnectionUtil.java",
"chars": 7976,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "common/src/main/java/com/ishland/raknetify/common/connection/RakNetSimpleMultiChannelCodec.java",
"chars": 10308,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "common/src/main/java/com/ishland/raknetify/common/connection/RaknetifyEventLoops.java",
"chars": 4821,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "common/src/main/java/com/ishland/raknetify/common/connection/SimpleMetricsLogger.java",
"chars": 8245,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "common/src/main/java/com/ishland/raknetify/common/connection/SynchronizationLayer.java",
"chars": 15185,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "common/src/main/java/com/ishland/raknetify/common/connection/multichannel/CustomPayloadChannel.java",
"chars": 3056,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "common/src/main/java/com/ishland/raknetify/common/data/ProtocolMultiChannelMappings.java",
"chars": 2818,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "common/src/main/java/com/ishland/raknetify/common/package-info.java",
"chars": 1228,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "common/src/main/java/com/ishland/raknetify/common/util/DebugUtil.java",
"chars": 5828,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "common/src/main/java/com/ishland/raknetify/common/util/MathUtil.java",
"chars": 3100,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "common/src/main/java/com/ishland/raknetify/common/util/NetworkInterfaceListener.java",
"chars": 4329,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "common/src/main/java/com/ishland/raknetify/common/util/PrefixUtil.java",
"chars": 1866,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "common/src/main/java/com/ishland/raknetify/common/util/ReflectionUtil.java",
"chars": 1566,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "common/src/main/java/com/ishland/raknetify/common/util/ThreadLocalUtil.java",
"chars": 1972,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "common/src/main/resources/raknetify-channel-mappings.json",
"chars": 64014,
"preview": "{\n \"mappings\": {\n \"755\": {\n \"s2c\": {\n \"0\": 2,\n \"1\": 2,\n \"2\": 2,\n \"3\": 2,\n \"4"
},
{
"path": "fabric/build.gradle",
"chars": 5284,
"preview": "evaluationDependsOn(\":\")\n\napply plugin: 'fabric-loom'\napply plugin: 'com.gradleup.shadow'\napply plugin: 'com.modrinth.mi"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/RaknetifyFabric.java",
"chars": 18139,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/common/client/DebugHudUtil.java",
"chars": 6334,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/common/client/DebugHudUtil1_21_9.java",
"chars": 2417,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/common/compat/package-info.java",
"chars": 1242,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/common/compat/viafabric/ViaFabricCompatInjector.java",
"chars": 5835,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/common/connection/MultiChannellingPacketCapture.java",
"chars": 3073,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/common/connection/RakNetClientConnectionUtil.java",
"chars": 3968,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/common/connection/RakNetCompressionCompatibilityHandler.java",
"chars": 1892,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/common/connection/RakNetFabricChannelEventListener.java",
"chars": 3640,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/common/connection/RakNetFabricConnectionUtil.java",
"chars": 5121,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/common/connection/RakNetMultiChannel.java",
"chars": 22516,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/common/connection/RakNetNetworkTransitionUtil.java",
"chars": 1859,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/common/connection/encryption/PacketEncryptionManagerInterface.java",
"chars": 1410,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/common/package-info.java",
"chars": 1235,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/common/quirks/ClientHungerManager.java",
"chars": 3439,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/common/util/FieldSignatureParser.java",
"chars": 2099,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/common/util/LegacySupportUtil.java",
"chars": 1814,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/common/util/MultiVersionUtil.java",
"chars": 14935,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/common/util/NetworkStates.java",
"chars": 1615,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/RaknetifyFabricMixinPlugin.java",
"chars": 6235,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/access/IClientConnection.java",
"chars": 1583,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/access/IClientPlayNetworkHandler.java",
"chars": 1530,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/access/IDebugHudEntries.java",
"chars": 1692,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/access/INetworkState1_20_4.java",
"chars": 1735,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/access/INetworkStateInternalPacketHandler.java",
"chars": 1690,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/access/IPacketCodecDispatcher.java",
"chars": 1641,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/access/IPacketCodecDispatcherPacketType.java",
"chars": 1446,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/access/IPacketEncryptionManager.java",
"chars": 1813,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/access/IServerPlayNetworkHandler.java",
"chars": 1434,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/access/IWorld.java",
"chars": 1478,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/client/MixinClientPlayNetworkHandler.java",
"chars": 2717,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/client/MixinConnectionScreen1.java",
"chars": 4693,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/client/MixinMultiplayerServerListPinger.java",
"chars": 3139,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/client/MixinMultiplayerServerListPinger1.java",
"chars": 4755,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/client/MixinMultiplayerServerListPinger1_20_2.java",
"chars": 2685,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/client/MixinMultiplayerServerListPinger1_20_5.java",
"chars": 2861,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/client/MixinMultiplayerServerListPinger1_21_11.java",
"chars": 2743,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/client/hud/MixinDebugHud1_21_8.java",
"chars": 1921,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/common/MixinCCConnect.java",
"chars": 6219,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/common/MixinClientConnection.java",
"chars": 5564,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/common/MixinClientConnection1.java",
"chars": 2203,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/common/MixinClientConnection1_20_2.java",
"chars": 2661,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/common/MixinServerAddress.java",
"chars": 2208,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/common/encryption/MixinClientConnection.java",
"chars": 3074,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/common/encryption/MixinPacketDecryptor.java",
"chars": 2330,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/common/encryption/MixinPacketEncryptionManager.java",
"chars": 3264,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/common/encryption/MixinPacketEncryptor.java",
"chars": 2314,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/common/quirks/MixinPlayerEntity.java",
"chars": 2500,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/common/quirks/MixinSampleSubscriptionTracker.java",
"chars": 1996,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/compat/fabricapi/MixinServerLoginNetworkAddon.java",
"chars": 2893,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/compat/krypton/MixinServerLoginNetworkHandler.java",
"chars": 4097,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/compat/package-info.java",
"chars": 1241,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/compat/qsl/MixinServerLoginNetworkAddon.java",
"chars": 2883,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/package-info.java",
"chars": 1234,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/server/MixinPlayerManager1_20_1.java",
"chars": 4003,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/server/MixinPlayerManager1_20_2.java",
"chars": 3102,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/server/MixinServerCommonNetworkHandler.java",
"chars": 4278,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/server/MixinServerLoginNetworkHandler.java",
"chars": 2927,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/server/MixinServerNetworkIo.java",
"chars": 10046,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/server/MixinServerNetworkIo1.java",
"chars": 2201,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/java/com/ishland/raknetify/fabric/mixin/server/MixinServerPlayNetworkHandler1_20_1.java",
"chars": 4412,
"preview": "/*\n * This file is a part of the Raknetify project, licensed under MIT.\n *\n * Copyright (c) 2022-2025 ishland\n *\n * Perm"
},
{
"path": "fabric/src/main/resources/fabric.mod.json",
"chars": 675,
"preview": "{\n \"schemaVersion\": 1,\n \"id\": \"raknetify\",\n \"version\": \"${version}\",\n\n \"name\": \"Raknetify (Fabric)\",\n \"description\""
},
{
"path": "fabric/src/main/resources/raknetify-fabric.accesswidener",
"chars": 384,
"preview": "accessWidener v1 named\n\naccessible class net/minecraft/network/handler/PacketCodecDispatcher$PacketType\n\naccessibl"
},
{
"path": "fabric/src/main/resources/raknetify-fabric.mixins.json",
"chars": 1964,
"preview": "{\n \"required\": true,\n \"minVersion\": \"0.8\",\n \"package\": \"com.ishland.raknetify.fabric.mixin\",\n \"plugin\": \"com.ishland"
},
{
"path": "genMappings.sh",
"chars": 1150,
"preview": "#!/bin/bash\n\nloader_version=0.18.4\ninstaller_version=1.1.1\n\n#declare -a vers\nif [[ -z \"$@\" ]]; then\n vers=\"1.17 1.17.1 "
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 202,
"preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributi"
},
{
"path": "gradle.properties",
"chars": 693,
"preview": "# Done to increase the memory available to gradle.\norg.gradle.jvmargs=-Xmx4G\norg.gradle.parallel=true\n\ngradle-curseforge"
},
{
"path": "gradlew",
"chars": 8024,
"preview": "#!/bin/sh\n\n#\n# Copyright 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"Licen"
},
{
"path": "gradlew.bat",
"chars": 2674,
"preview": "@rem\n@rem Copyright 2015 the original author or authors.\n@rem\n@rem Licensed under the Apache License, Version 2.0 (the \""
},
{
"path": "jitpack.yml",
"chars": 139,
"preview": "before_install:\n - source \"$HOME/.sdkman/bin/sdkman-init.sh\"\n - sdk update\n - sdk install java 17.0.2-zulu\n - sdk us"
},
{
"path": "modrinth_license.txt",
"chars": 260,
"preview": "Licensing\n\nRaknetify for fabric and BungeeCord is licensed under the MIT license,\nyou can find the license in the LICENS"
},
{
"path": "settings.gradle",
"chars": 744,
"preview": "pluginManagement {\n repositories {\n maven {\n name = 'Fabric'\n url = 'https://maven.fabri"
},
{
"path": "velocity/HEADER.txt",
"chars": 745,
"preview": "This file is a part of the Velocity implementation of the Raknetify\nproject, licensed under GPLv3.\n\nCopyright (c) 2022-2"
},
{
"path": "velocity/LICENSE",
"chars": 35149,
"preview": " GNU GENERAL PUBLIC LICENSE\n Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
},
{
"path": "velocity/build.gradle",
"chars": 2713,
"preview": "import java.nio.file.Files\nimport java.nio.file.Paths\nimport java.nio.file.StandardCopyOption\n\nevaluationDependsOn(\":\")\n"
},
{
"path": "velocity/src/main/java/com/ishland/raknetify/velocity/RaknetifyVelocityLaunchWrapper.java",
"chars": 3202,
"preview": "/*\n * This file is a part of the Velocity implementation of the Raknetify\n * project, licensed under GPLv3.\n *\n * Copyri"
},
{
"path": "velocity/src/main/java/com/ishland/raknetify/velocity/RaknetifyVelocityPlugin.java",
"chars": 6517,
"preview": "/*\n * This file is a part of the Velocity implementation of the Raknetify\n * project, licensed under GPLv3.\n *\n * Copyri"
},
{
"path": "velocity/src/main/java/com/ishland/raknetify/velocity/connection/RakNetVelocityChannelEventListener.java",
"chars": 6584,
"preview": "/*\n * This file is a part of the Velocity implementation of the Raknetify\n * project, licensed under GPLv3.\n *\n * Copyri"
},
{
"path": "velocity/src/main/java/com/ishland/raknetify/velocity/connection/RakNetVelocityConnectionUtil.java",
"chars": 7518,
"preview": "/*\n * This file is a part of the Velocity implementation of the Raknetify\n * project, licensed under GPLv3.\n *\n * Copyri"
},
{
"path": "velocity/src/main/java/com/ishland/raknetify/velocity/connection/RakNetVelocityPingUpdater.java",
"chars": 2808,
"preview": "/*\n * This file is a part of the Velocity implementation of the Raknetify\n * project, licensed under GPLv3.\n *\n * Copyri"
},
{
"path": "velocity/src/main/java/com/ishland/raknetify/velocity/connection/RakNetVelocityServerChannelEventListener.java",
"chars": 3142,
"preview": "/*\n * This file is a part of the Velocity implementation of the Raknetify\n * project, licensed under GPLv3.\n *\n * Copyri"
},
{
"path": "velocity/src/main/java/com/ishland/raknetify/velocity/init/VelocityPacketRegistryInjector.java",
"chars": 2925,
"preview": "/*\n * This file is a part of the Velocity implementation of the Raknetify\n * project, licensed under GPLv3.\n *\n * Copyri"
},
{
"path": "velocity/src/main/java/com/ishland/raknetify/velocity/init/VelocityRaknetifyServer.java",
"chars": 9775,
"preview": "/*\n * This file is a part of the Velocity implementation of the Raknetify\n * project, licensed under GPLv3.\n *\n * Copyri"
},
{
"path": "velocity/src/main/resources/velocity-plugin.json",
"chars": 297,
"preview": "{\n \"id\": \"raknetify\",\n \"name\": \"Raknetify (Velocity)\",\n \"version\": \"${version}\",\n \"description\": \"A Velocity plugin "
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the RelativityMC/raknetify GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 133 files (553.6 KB), approximately 139.6k tokens, and a symbol index with 406 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.