mirror of
https://github.com/ViaVersion/ViaProxy.git
synced 2024-11-24 12:16:40 +01:00
Initial commit
This commit is contained in:
parent
d1acd4fb31
commit
886fbd9c5e
9
.gitattributes
vendored
Normal file
9
.gitattributes
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
#
|
||||
# https://help.github.com/articles/dealing-with-line-endings/
|
||||
#
|
||||
# Linux start script should use lf
|
||||
/gradlew text eol=lf
|
||||
|
||||
# These are Windows script files and should use crlf
|
||||
*.bat text eol=crlf
|
||||
|
6
.github/dependabot.yml
vendored
Normal file
6
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "gradle"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
# Compiled class file
|
||||
*.class
|
||||
|
||||
# Log file
|
||||
*.log
|
||||
*.log.gz
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
# Project files
|
||||
/.idea/
|
||||
/.gradle/
|
||||
/build/
|
||||
/out/
|
||||
/run/
|
25
README.md
Normal file
25
README.md
Normal file
@ -0,0 +1,25 @@
|
||||
# ViaProxy
|
||||
Standalone proxy which uses ViaVersion to translate between minecraft versions.
|
||||
|
||||
## Supported Server versions
|
||||
- Classic (c0.0.15 - c0.30 including [CPE](https://wiki.vg/Classic_Protocol_Extension))
|
||||
- Alpha (a1.0.15 - a1.2.6)
|
||||
- Beta (b1.0 - b1.8.1)
|
||||
- Release (1.0.0 - 1.19.3)
|
||||
- April Fools (3D Shareware, 20w14infinite)
|
||||
- Combat Snapshots (Combat Test 8c)
|
||||
|
||||
## Supported Client versions
|
||||
- Release (1.7.2 - 1.19.3)
|
||||
- April Fools (3D Shareware)
|
||||
|
||||
## Releases
|
||||
### Gradle/Maven
|
||||
To use ViaProxy with Gradle/Maven you can use this [Maven server](https://maven.lenni0451.net/#/releases/net/raphimc/ViaProxy) or [Jitpack](https://jitpack.io/#RaphiMC/ViaProxy).
|
||||
You can also find instructions how to implement it into your build script there.
|
||||
|
||||
### Jar File
|
||||
If you just want the latest jar file you can download it from this [Jenkins](https://build.lenni0451.net/job/ViaProxy/).
|
||||
|
||||
## Usage
|
||||
TODO
|
110
build.gradle
Normal file
110
build.gradle
Normal file
@ -0,0 +1,110 @@
|
||||
plugins {
|
||||
id "java"
|
||||
id "maven-publish"
|
||||
}
|
||||
|
||||
java.toolchain.languageVersion = JavaLanguageVersion.of(8)
|
||||
compileJava.options.encoding = compileTestJava.options.encoding = javadoc.options.encoding = "UTF-8"
|
||||
|
||||
group = project.maven_group
|
||||
archivesBaseName = project.maven_name
|
||||
version = project.maven_version
|
||||
|
||||
configurations {
|
||||
include
|
||||
|
||||
implementation.extendsFrom include
|
||||
api.extendsFrom include
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
name = "Jitpack"
|
||||
url = "https://jitpack.io"
|
||||
}
|
||||
maven {
|
||||
name = "Lenni0451"
|
||||
url "https://maven.lenni0451.net/releases"
|
||||
}
|
||||
maven {
|
||||
name = "ViaVersion"
|
||||
url "https://repo.viaversion.com"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
include "com.viaversion:viaversion:4.5.2-SNAPSHOT"
|
||||
include("com.viaversion:viabackwards-common:4.5.2-SNAPSHOT") {
|
||||
exclude group: "com.viaversion", module: "viaversion"
|
||||
exclude group: "io.netty", module: "netty-all"
|
||||
exclude group: "com.google.guava", module: "guava"
|
||||
}
|
||||
include "com.viaversion:viarewind-core:2.0.3-SNAPSHOT"
|
||||
include "net.raphimc:ViaLegacy:2.0.3"
|
||||
include("net.raphimc:ViaProtocolHack:2.0.1") {
|
||||
exclude group: "org.slf4j", module: "slf4j-api"
|
||||
}
|
||||
|
||||
include "com.formdev:flatlaf:3.0"
|
||||
include "com.google.guava:guava:31.1-jre"
|
||||
include "net.sf.jopt-simple:jopt-simple:5.0.4"
|
||||
include "org.apache.logging.log4j:log4j-core:2.19.0"
|
||||
include "org.apache.logging.log4j:log4j-slf4j-impl:2.19.0"
|
||||
include "com.github.GeyserMC:MCAuthLib:1.4"
|
||||
include "net.lenni0451.classtransform:mixinstranslator:1.7.5"
|
||||
include "net.lenni0451.classtransform:mixinsdummy:1.7.5"
|
||||
include "net.lenni0451.classtransform:additionalclassprovider:1.7.5"
|
||||
include "net.lenni0451:Reflect:1.0.2"
|
||||
include "net.lenni0451:LambdaEvents:2.0.3"
|
||||
include "net.raphimc.netminecraft:all:2.2.2"
|
||||
}
|
||||
|
||||
java {
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
jar {
|
||||
dependsOn configurations.include
|
||||
from {
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
configurations.include.collect {
|
||||
zipTree(it)
|
||||
}
|
||||
} {
|
||||
exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA"
|
||||
}
|
||||
|
||||
manifest {
|
||||
attributes(
|
||||
"Main-Class": "net.raphimc.viaproxy.ViaProxy",
|
||||
"Multi-Release": "true"
|
||||
)
|
||||
}
|
||||
|
||||
from("LICENSE") {
|
||||
rename { "${it}_${project.archivesBaseName}" }
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven {
|
||||
name = "reposilite"
|
||||
url = "https://maven.lenni0451.net/releases"
|
||||
credentials(PasswordCredentials)
|
||||
authentication {
|
||||
basic(BasicAuthentication)
|
||||
}
|
||||
}
|
||||
}
|
||||
publications {
|
||||
maven(MavenPublication) {
|
||||
groupId = project.maven_group
|
||||
artifactId = project.maven_name
|
||||
version = project.maven_version
|
||||
|
||||
from components.java
|
||||
}
|
||||
}
|
||||
}
|
9
gradle.properties
Normal file
9
gradle.properties
Normal file
@ -0,0 +1,9 @@
|
||||
# Gradle properties
|
||||
org.gradle.daemon=true
|
||||
org.gradle.parallel=true
|
||||
org.gradle.configureondemand=true
|
||||
|
||||
# Project properties
|
||||
maven_name=ViaProxy
|
||||
maven_group=net.raphimc
|
||||
maven_version=3.0.0
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
240
gradlew
vendored
Normal file
240
gradlew
vendored
Normal file
@ -0,0 +1,240 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
91
gradlew.bat
vendored
Normal file
91
gradlew.bat
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
8
settings.gradle
Normal file
8
settings.gradle
Normal file
@ -0,0 +1,8 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "ViaProxy"
|
114
src/main/java/net/raphimc/viaproxy/ViaProxy.java
Normal file
114
src/main/java/net/raphimc/viaproxy/ViaProxy.java
Normal file
@ -0,0 +1,114 @@
|
||||
package net.raphimc.viaproxy;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.group.ChannelGroup;
|
||||
import io.netty.channel.group.DefaultChannelGroup;
|
||||
import io.netty.util.ResourceLeakDetector;
|
||||
import io.netty.util.concurrent.GlobalEventExecutor;
|
||||
import net.lenni0451.classtransform.TransformerManager;
|
||||
import net.lenni0451.classtransform.additionalclassprovider.GuavaClassPathProvider;
|
||||
import net.lenni0451.classtransform.mixinstranslator.MixinsTranslator;
|
||||
import net.lenni0451.classtransform.utils.loader.EnumLoaderPriority;
|
||||
import net.lenni0451.classtransform.utils.loader.InjectionClassLoader;
|
||||
import net.lenni0451.reflect.ClassLoaders;
|
||||
import net.raphimc.netminecraft.constants.MCPipeline;
|
||||
import net.raphimc.netminecraft.netty.connection.NetServer;
|
||||
import net.raphimc.viaproxy.cli.ConsoleHandler;
|
||||
import net.raphimc.viaproxy.cli.options.Options;
|
||||
import net.raphimc.viaproxy.plugins.PluginManager;
|
||||
import net.raphimc.viaproxy.protocolhack.ProtocolHack;
|
||||
import net.raphimc.viaproxy.proxy.ProxyConnection;
|
||||
import net.raphimc.viaproxy.proxy.client2proxy.Client2ProxyChannelInitializer;
|
||||
import net.raphimc.viaproxy.proxy.client2proxy.Client2ProxyHandler;
|
||||
import net.raphimc.viaproxy.ui.ViaProxyUI;
|
||||
import net.raphimc.viaproxy.util.logging.Logger;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class ViaProxy {
|
||||
|
||||
public static NetServer currentProxyServer;
|
||||
public static Thread loaderThread;
|
||||
public static ChannelGroup c2pChannels;
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
final TransformerManager transformerManager = new TransformerManager(new GuavaClassPathProvider());
|
||||
transformerManager.addTransformerPreprocessor(new MixinsTranslator());
|
||||
transformerManager.addTransformer("net.raphimc.viaproxy.injection.transformer.**");
|
||||
transformerManager.addTransformer("net.raphimc.viaproxy.injection.mixins.**");
|
||||
final InjectionClassLoader injectionClassLoader = new InjectionClassLoader(transformerManager, ClassLoaders.getSystemClassPath());
|
||||
injectionClassLoader.setPriority(EnumLoaderPriority.PARENT_FIRST);
|
||||
injectionClassLoader.executeMain(ViaProxy.class.getName(), "injectedMain", args);
|
||||
}
|
||||
|
||||
public static void injectedMain(String[] args) throws InterruptedException {
|
||||
Logger.setup();
|
||||
ConsoleHandler.hookConsole();
|
||||
Logger.LOGGER.info("Initializing ViaProxy...");
|
||||
setNettyParameters();
|
||||
MCPipeline.useOptimizedPipeline();
|
||||
c2pChannels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
|
||||
loaderThread = new Thread(() -> {
|
||||
ProtocolHack.init();
|
||||
PluginManager.loadPlugins();
|
||||
}, "ViaProtocolHack-Loader");
|
||||
|
||||
if (args.length == 0 && !GraphicsEnvironment.isHeadless()) {
|
||||
loaderThread.start();
|
||||
final ViaProxyUI[] ui = new ViaProxyUI[1];
|
||||
SwingUtilities.invokeLater(() -> ui[0] = new ViaProxyUI());
|
||||
loaderThread.join();
|
||||
ui[0].setReady();
|
||||
Logger.LOGGER.info("ViaProxy started successfully!");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Options.parse(args);
|
||||
} catch (Throwable t) {
|
||||
Logger.LOGGER.fatal("[" + t.getClass().getSimpleName() + "] " + t.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
loaderThread.start();
|
||||
loaderThread.join();
|
||||
Logger.LOGGER.info("ViaProxy started successfully!");
|
||||
startProxy();
|
||||
}
|
||||
|
||||
public static void startProxy() {
|
||||
if (currentProxyServer != null) {
|
||||
throw new IllegalStateException("Proxy is already running");
|
||||
}
|
||||
currentProxyServer = new NetServer(Client2ProxyHandler::new, Client2ProxyChannelInitializer::new);
|
||||
Logger.LOGGER.info("Binding proxy server to " + Options.BIND_ADDRESS + ":" + Options.BIND_PORT);
|
||||
currentProxyServer.bind(Options.BIND_ADDRESS, Options.BIND_PORT, false);
|
||||
}
|
||||
|
||||
public static void stopProxy() {
|
||||
if (currentProxyServer != null) {
|
||||
Logger.LOGGER.info("Stopping proxy server");
|
||||
currentProxyServer.getChannel().close();
|
||||
currentProxyServer = null;
|
||||
|
||||
for (Channel channel : c2pChannels) {
|
||||
try {
|
||||
ProxyConnection.fromChannel(channel).kickClient("§cViaProxy has been stopped");
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void setNettyParameters() {
|
||||
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED);
|
||||
if (System.getProperty("io.netty.allocator.maxOrder") == null) {
|
||||
System.setProperty("io.netty.allocator.maxOrder", "9");
|
||||
}
|
||||
if (Options.NETTY_THREADS > 0) {
|
||||
System.setProperty("io.netty.eventLoopThreads", Integer.toString(Options.NETTY_THREADS));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
87
src/main/java/net/raphimc/viaproxy/cli/ConsoleFormatter.java
Normal file
87
src/main/java/net/raphimc/viaproxy/cli/ConsoleFormatter.java
Normal file
@ -0,0 +1,87 @@
|
||||
package net.raphimc.viaproxy.cli;
|
||||
|
||||
public class ConsoleFormatter {
|
||||
|
||||
private static final String PREFIX = "\033[";
|
||||
private static final String SUFFIX = "m";
|
||||
|
||||
public static String convert(final String s) {
|
||||
StringBuilder out = new StringBuilder();
|
||||
char[] chars = s.toCharArray();
|
||||
for (int i = 0; i < chars.length; i++) {
|
||||
char c = chars[i];
|
||||
char next = i + 1 < chars.length ? chars[i + 1] : '\0';
|
||||
if (c == '§') {
|
||||
if (next != '\0') {
|
||||
if (isColor(next)) out.append(convertToAnsi('r'));
|
||||
out.append(convertToAnsi(next));
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
out.append(c);
|
||||
}
|
||||
}
|
||||
return out + convertToAnsi('r');
|
||||
}
|
||||
|
||||
//The ANSI codes are an approximation. True color is used to get the exact color.
|
||||
private static String convertToAnsi(final char color) {
|
||||
switch (Character.toLowerCase(color)) {
|
||||
case '0': //Black
|
||||
return PREFIX + getColor("30", 0x00_00_00) + SUFFIX;
|
||||
case '1': //Dark Blue
|
||||
return PREFIX + getColor("34", 0x00_00_AA) + SUFFIX;
|
||||
case '2': //Dark Green
|
||||
return PREFIX + getColor("32", 0x00_AA_00) + SUFFIX;
|
||||
case '3': //Dark Aqua
|
||||
return PREFIX + getColor("36", 0x00_AA_AA) + SUFFIX;
|
||||
case '4': //Dark Red
|
||||
return PREFIX + getColor("31", 0xAA_00_00) + SUFFIX;
|
||||
case '5': //Dark Purple
|
||||
return PREFIX + getColor("35", 0xAA_00_AA) + SUFFIX;
|
||||
case '6': //Gold
|
||||
return PREFIX + getColor("33", 0xFF_AA_00) + SUFFIX;
|
||||
case '7': //Gray
|
||||
return PREFIX + getColor("37", 0xAA_AA_AA) + SUFFIX;
|
||||
case '8': //Dark Gray
|
||||
return PREFIX + getColor("90", 0x55_55_55) + SUFFIX;
|
||||
case '9': //Blue
|
||||
return PREFIX + getColor("94", 0x55_55_FF) + SUFFIX;
|
||||
case 'a': //Green
|
||||
return PREFIX + getColor("92", 0x55_FF_55) + SUFFIX;
|
||||
case 'b': //Aqua
|
||||
return PREFIX + getColor("96", 0x55_FF_FF) + SUFFIX;
|
||||
case 'c': //Red
|
||||
return PREFIX + getColor("91", 0xFF_55_55) + SUFFIX;
|
||||
case 'd': //Light Purple
|
||||
return PREFIX + getColor("95", 0xFF_55_FF) + SUFFIX;
|
||||
case 'e': //Yellow
|
||||
return PREFIX + getColor("93", 0xFF_FF_55) + SUFFIX;
|
||||
case 'f': //White
|
||||
return PREFIX + getColor("97", 0xFF_FF_FF) + SUFFIX;
|
||||
case 'k': //Obfuscated
|
||||
return ""; //Not supported in terminal
|
||||
case 'l': //Bold
|
||||
return PREFIX + "1" + SUFFIX;
|
||||
case 'm': //Strikethrough
|
||||
return PREFIX + "9" + SUFFIX;
|
||||
case 'n': //Underline
|
||||
return PREFIX + "4" + SUFFIX;
|
||||
case 'o': //Italic
|
||||
return PREFIX + "3" + SUFFIX;
|
||||
case 'r': //Reset
|
||||
default:
|
||||
return PREFIX + 0 + SUFFIX;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isColor(char color) {
|
||||
color = Character.toLowerCase(color);
|
||||
return color >= '0' && color <= '9' || color >= 'a' && color <= 'f';
|
||||
}
|
||||
|
||||
private static String getColor(final String ansi, final int rgb) {
|
||||
return String.format("38;2;%d;%d;%d", (rgb >> 16) & 255, (rgb >> 8) & 255, rgb & 255);
|
||||
}
|
||||
|
||||
}
|
42
src/main/java/net/raphimc/viaproxy/cli/ConsoleHandler.java
Normal file
42
src/main/java/net/raphimc/viaproxy/cli/ConsoleHandler.java
Normal file
@ -0,0 +1,42 @@
|
||||
package net.raphimc.viaproxy.cli;
|
||||
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.connection.UserConnectionImpl;
|
||||
import net.raphimc.viaprotocolhack.commands.UserCommandSender;
|
||||
import net.raphimc.viaproxy.plugins.PluginManager;
|
||||
import net.raphimc.viaproxy.plugins.events.ConsoleCommandEvent;
|
||||
import net.raphimc.viaproxy.util.ArrayHelper;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Scanner;
|
||||
|
||||
public class ConsoleHandler {
|
||||
|
||||
public static void hookConsole() {
|
||||
new Thread(ConsoleHandler::listen, "Console-Handler").start();
|
||||
}
|
||||
|
||||
private static void listen() {
|
||||
final Scanner scanner = new Scanner(System.in);
|
||||
while (scanner.hasNextLine()) {
|
||||
final String line = scanner.nextLine();
|
||||
final String[] parts = line.split(" ");
|
||||
if (parts.length == 0) continue;
|
||||
final String command = parts[0];
|
||||
final ArrayHelper args = new ArrayHelper(Arrays.copyOfRange(parts, 1, parts.length));
|
||||
|
||||
if (command.equalsIgnoreCase("gc")) {
|
||||
System.gc();
|
||||
System.out.println("GC Done");
|
||||
} else if (command.equalsIgnoreCase("via")) {
|
||||
Via.getManager().getCommandHandler().onCommand(new UserCommandSender(new UserConnectionImpl(null, true)), args.getAsArray());
|
||||
} else {
|
||||
if (PluginManager.EVENT_MANAGER.call(new ConsoleCommandEvent(command, args.getAsArray())).isCancelled()) continue;
|
||||
System.out.println("Invalid Command!");
|
||||
System.out.println(" via | Run a viaversion command");
|
||||
System.out.println(" gc | Run the garbage collector");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package net.raphimc.viaproxy.cli.options;
|
||||
|
||||
import joptsimple.BuiltinHelpFormatter;
|
||||
import joptsimple.OptionDescriptor;
|
||||
import joptsimple.internal.Classes;
|
||||
import joptsimple.internal.Strings;
|
||||
|
||||
public class BetterHelpFormatter extends BuiltinHelpFormatter {
|
||||
|
||||
public BetterHelpFormatter() {
|
||||
super(250, 4);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String extractTypeIndicator(OptionDescriptor descriptor) {
|
||||
String indicator = descriptor.argumentTypeIndicator();
|
||||
if (indicator != null && indicator.startsWith("[")) return indicator.substring(1, indicator.length() - 1);
|
||||
return !Strings.isNullOrEmpty(indicator) && !String.class.getName().equals(indicator) ? Classes.shortNameOf(indicator) : "String";
|
||||
}
|
||||
|
||||
}
|
72
src/main/java/net/raphimc/viaproxy/cli/options/Options.java
Normal file
72
src/main/java/net/raphimc/viaproxy/cli/options/Options.java
Normal file
@ -0,0 +1,72 @@
|
||||
package net.raphimc.viaproxy.cli.options;
|
||||
|
||||
import joptsimple.*;
|
||||
import net.raphimc.vialegacy.util.VersionEnum;
|
||||
import net.raphimc.viaproxy.util.logging.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
public class Options {
|
||||
|
||||
public static String BIND_ADDRESS = "0.0.0.0";
|
||||
public static int BIND_PORT = 25568;
|
||||
|
||||
public static boolean SRV_MODE; // Example: lenni0451.net_25565_1.8.x.viaproxy.127.0.0.1.nip.io
|
||||
public static boolean INTERNAL_SRV_MODE; // Example: ip\7port\7version\7mppass
|
||||
public static boolean ONLINE_MODE;
|
||||
public static int NETTY_THREADS = 0;
|
||||
public static int COMPRESSION_THRESHOLD = 256;
|
||||
|
||||
public static String CONNECT_ADDRESS;
|
||||
public static int CONNECT_PORT;
|
||||
public static VersionEnum PROTOCOL_VERSION;
|
||||
|
||||
public static boolean OPENAUTHMOD_AUTH;
|
||||
public static boolean LOCAL_SOCKET_AUTH;
|
||||
public static boolean BETACRAFT_AUTH;
|
||||
|
||||
public static void parse(final String[] args) throws IOException {
|
||||
VersionEnum.init(); // We need to init the version list already here
|
||||
|
||||
final OptionParser parser = new OptionParser();
|
||||
final OptionSpec<Void> help = parser.acceptsAll(asList("help", "h", "?"), "Get a list of all arguments").forHelp();
|
||||
|
||||
final OptionSpec<String> bindAddress = parser.acceptsAll(asList("bind_address", "bind_ip", "ba"), "The address the proxy should bind to").withRequiredArg().ofType(String.class).defaultsTo(BIND_ADDRESS);
|
||||
final OptionSpec<Integer> bindPort = parser.acceptsAll(asList("bind_port", "bp"), "The port the proxy should bind to").withRequiredArg().ofType(Integer.class).defaultsTo(BIND_PORT);
|
||||
final OptionSpec<Void> srvMode = parser.acceptsAll(asList("srv_mode", "srv", "s"), "Enable srv mode");
|
||||
final OptionSpec<Void> iSrvMode = parser.acceptsAll(asList("internal_srv_mode", "isrv"), "Enable internal srv mode").availableUnless(srvMode);
|
||||
final OptionSpec<Void> onlineMode = parser.acceptsAll(asList("online_mode", "om", "o"), "Enable online mode");
|
||||
final OptionSpec<Integer> nettyThreads = parser.acceptsAll(asList("netty_threads", "t"), "The amount of netty threads to use").withRequiredArg().ofType(Integer.class).defaultsTo(NETTY_THREADS);
|
||||
final OptionSpec<Integer> compressionThreshold = parser.acceptsAll(asList("compression_threshold", "ct", "c"), "The threshold for packet compression").withRequiredArg().ofType(Integer.class).defaultsTo(COMPRESSION_THRESHOLD);
|
||||
final OptionSpec<String> connectAddress = parser.acceptsAll(asList("connect_address", "target_ip", "ca", "a"), "The address of the target server").withRequiredArg().ofType(String.class).required();
|
||||
final OptionSpec<Integer> connectPort = parser.acceptsAll(asList("connect_port", "target_port", "cp", "p"), "The port of the target server").withRequiredArg().ofType(Integer.class).defaultsTo(CONNECT_PORT);
|
||||
final OptionSpec<VersionEnum> version = parser.acceptsAll(asList("version", "v"), "The version of the target server").withRequiredArg().withValuesConvertedBy(new VersionEnumConverter()).required();
|
||||
final OptionSpec<Void> openAuthModAuth = parser.acceptsAll(asList("openauthmod_auth", "oam_auth"), "Enable OpenAuthMod authentication");
|
||||
final OptionSpec<Void> localSocketAuth = parser.accepts("local_socket_auth", "Enable authentication over a local socket");
|
||||
final OptionSpec<Void> betaCraftAuth = parser.accepts("betacraft_auth", "Use BetaCraft authentication servers for classic");
|
||||
|
||||
final OptionSet options = parser.parse(args);
|
||||
if (options.has(help)) {
|
||||
parser.formatHelpWith(new BetterHelpFormatter());
|
||||
parser.printHelpOn(Logger.SYSOUT);
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
BIND_ADDRESS = options.valueOf(bindAddress);
|
||||
BIND_PORT = options.valueOf(bindPort);
|
||||
SRV_MODE = options.has(srvMode);
|
||||
INTERNAL_SRV_MODE = options.has(iSrvMode);
|
||||
ONLINE_MODE = options.has(onlineMode);
|
||||
NETTY_THREADS = options.valueOf(nettyThreads);
|
||||
CONNECT_ADDRESS = options.valueOf(connectAddress);
|
||||
CONNECT_PORT = options.valueOf(connectPort);
|
||||
PROTOCOL_VERSION = options.valueOf(version);
|
||||
COMPRESSION_THRESHOLD = options.valueOf(compressionThreshold);
|
||||
OPENAUTHMOD_AUTH = options.has(openAuthModAuth);
|
||||
LOCAL_SOCKET_AUTH = options.has(localSocketAuth);
|
||||
BETACRAFT_AUTH = options.has(betaCraftAuth);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package net.raphimc.viaproxy.cli.options;
|
||||
|
||||
import joptsimple.ValueConversionException;
|
||||
import joptsimple.ValueConverter;
|
||||
import net.raphimc.vialegacy.util.VersionEnum;
|
||||
|
||||
public class VersionEnumConverter implements ValueConverter<VersionEnum> {
|
||||
|
||||
@Override
|
||||
public VersionEnum convert(String s) {
|
||||
for (VersionEnum version : VersionEnum.getAllVersions()) {
|
||||
if (version.getName().equalsIgnoreCase(s)) return version;
|
||||
}
|
||||
throw new ValueConversionException("Unable to find version '" + s + "'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<VersionEnum> valueType() {
|
||||
return VersionEnum.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String valuePattern() {
|
||||
StringBuilder s = new StringBuilder();
|
||||
for (VersionEnum version : VersionEnum.getAllVersions()) {
|
||||
s.append((s.length() == 0) ? "" : ", ").append(version.getName());
|
||||
}
|
||||
return "[" + s + "]";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,173 @@
|
||||
package net.raphimc.viaproxy.injection;
|
||||
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
|
||||
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
|
||||
import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.libs.opennbt.tag.builtin.*;
|
||||
import com.viaversion.viaversion.protocols.protocol1_17to1_16_4.Protocol1_17To1_16_4;
|
||||
import com.viaversion.viaversion.protocols.protocol1_17to1_16_4.types.Chunk1_17Type;
|
||||
import net.raphimc.vialegacy.protocols.classic.protocola1_0_15toc0_28_30.model.ClassicLevel;
|
||||
import net.raphimc.vialegacy.protocols.classic.protocola1_0_15toc0_28_30.providers.ClassicWorldHeightProvider;
|
||||
import net.raphimc.vialegacy.protocols.classic.protocola1_0_15toc0_28_30.storage.ClassicLevelStorage;
|
||||
import net.raphimc.vialegacy.util.VersionEnum;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class ClassicWorldHeightInjection {
|
||||
|
||||
public static PacketRemapper handleJoinGame(final Protocol1_17To1_16_4 protocol, final PacketRemapper parentRemapper) {
|
||||
return new PacketRemapper() {
|
||||
@Override
|
||||
public void registerMap() {
|
||||
handler(wrapper -> {
|
||||
parentRemapper.remap(wrapper);
|
||||
if (wrapper.isCancelled()) return;
|
||||
|
||||
if (VersionEnum.fromUserConnection(wrapper.user()).isOlderThanOrEqualTo(VersionEnum.c0_28toc0_30)) {
|
||||
for (Tag dimension : wrapper.get(Type.NBT, 0).<CompoundTag>get("minecraft:dimension_type").<ListTag>get("value")) {
|
||||
changeDimensionTagHeight(wrapper.user(), ((CompoundTag) dimension).get("element"));
|
||||
}
|
||||
changeDimensionTagHeight(wrapper.user(), wrapper.get(Type.NBT, 1));
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static PacketRemapper handleRespawn(final Protocol1_17To1_16_4 protocol, final PacketRemapper parentRemapper) {
|
||||
return new PacketRemapper() {
|
||||
@Override
|
||||
public void registerMap() {
|
||||
handler(wrapper -> {
|
||||
parentRemapper.remap(wrapper);
|
||||
if (wrapper.isCancelled()) return;
|
||||
|
||||
if (VersionEnum.fromUserConnection(wrapper.user()).isOlderThanOrEqualTo(VersionEnum.c0_28toc0_30)) {
|
||||
changeDimensionTagHeight(wrapper.user(), wrapper.get(Type.NBT, 0));
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static PacketRemapper handleChunkData(final Protocol1_17To1_16_4 protocol, final PacketRemapper parentRemapper) {
|
||||
return new PacketRemapper() {
|
||||
@Override
|
||||
public void registerMap() {
|
||||
handler(wrapper -> {
|
||||
parentRemapper.remap(wrapper);
|
||||
if (wrapper.isCancelled()) return;
|
||||
|
||||
if (VersionEnum.fromUserConnection(wrapper.user()).isOlderThanOrEqualTo(VersionEnum.c0_28toc0_30)) {
|
||||
wrapper.resetReader();
|
||||
final Chunk chunk = wrapper.read(new Chunk1_17Type(16));
|
||||
wrapper.write(new Chunk1_17Type(chunk.getSections().length), chunk);
|
||||
|
||||
final ClassicWorldHeightProvider heightProvider = Via.getManager().getProviders().get(ClassicWorldHeightProvider.class);
|
||||
if (chunk.getSections().length < heightProvider.getMaxChunkSectionCount(wrapper.user())) { // Increase available sections to match new world height
|
||||
final ChunkSection[] newArray = new ChunkSection[heightProvider.getMaxChunkSectionCount(wrapper.user())];
|
||||
System.arraycopy(chunk.getSections(), 0, newArray, 0, chunk.getSections().length);
|
||||
chunk.setSections(newArray);
|
||||
}
|
||||
|
||||
final BitSet chunkMask = new BitSet();
|
||||
for (int i = 0; i < chunk.getSections().length; i++) {
|
||||
if (chunk.getSections()[i] != null) chunkMask.set(i);
|
||||
}
|
||||
chunk.setChunkMask(chunkMask);
|
||||
|
||||
final int[] newBiomeData = new int[chunk.getSections().length * 4 * 4 * 4];
|
||||
System.arraycopy(chunk.getBiomeData(), 0, newBiomeData, 0, chunk.getBiomeData().length);
|
||||
for (int i = 64; i < chunk.getSections().length * 4; i++) { // copy top layer of old biome data all the way to max world height
|
||||
System.arraycopy(chunk.getBiomeData(), chunk.getBiomeData().length - 16, newBiomeData, i * 16, 16);
|
||||
}
|
||||
chunk.setBiomeData(newBiomeData);
|
||||
|
||||
chunk.setHeightMap(new CompoundTag()); // rip heightmap :(
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static PacketRemapper handleUpdateLight(final Protocol1_17To1_16_4 protocol, final PacketRemapper parentRemapper) {
|
||||
final PacketRemapper classicLightHandler = new PacketRemapper() {
|
||||
@Override
|
||||
public void registerMap() {
|
||||
map(Type.VAR_INT); // x
|
||||
map(Type.VAR_INT); // y
|
||||
map(Type.BOOLEAN); // trust edges
|
||||
handler(wrapper -> {
|
||||
wrapper.read(Type.VAR_INT); // sky light mask
|
||||
wrapper.read(Type.VAR_INT); // block light mask
|
||||
final int emptySkyLightMask = wrapper.read(Type.VAR_INT); // empty sky light mask
|
||||
final int emptyBlockLightMask = wrapper.read(Type.VAR_INT); // empty block light mask
|
||||
|
||||
final ClassicLevel level = wrapper.user().get(ClassicLevelStorage.class).getClassicLevel();
|
||||
final ClassicWorldHeightProvider heightProvider = Via.getManager().getProviders().get(ClassicWorldHeightProvider.class);
|
||||
|
||||
int sectionYCount = level.getSizeY() >> 4;
|
||||
if (level.getSizeY() % 16 != 0) sectionYCount++;
|
||||
if (sectionYCount > heightProvider.getMaxChunkSectionCount(wrapper.user())) {
|
||||
sectionYCount = heightProvider.getMaxChunkSectionCount(wrapper.user());
|
||||
}
|
||||
|
||||
final List<byte[]> lightArrays = new ArrayList<>();
|
||||
while (wrapper.isReadable(Type.BYTE_ARRAY_PRIMITIVE, 0)) {
|
||||
lightArrays.add(wrapper.read(Type.BYTE_ARRAY_PRIMITIVE));
|
||||
}
|
||||
|
||||
int skyLightCount = 16;
|
||||
int blockLightCount = sectionYCount;
|
||||
if (lightArrays.size() == 16 + 0 + 2) {
|
||||
blockLightCount = 0;
|
||||
} else if (lightArrays.size() == 16 + sectionYCount + 2) {
|
||||
} else if (lightArrays.size() == sectionYCount + sectionYCount + 2) {
|
||||
skyLightCount = sectionYCount;
|
||||
}
|
||||
skyLightCount += 2; // Chunk below 0 and above 255
|
||||
|
||||
final BitSet skyLightMask = new BitSet();
|
||||
final BitSet blockLightMask = new BitSet();
|
||||
skyLightMask.set(0, skyLightCount);
|
||||
blockLightMask.set(0, blockLightCount);
|
||||
|
||||
wrapper.write(Type.LONG_ARRAY_PRIMITIVE, skyLightMask.toLongArray());
|
||||
wrapper.write(Type.LONG_ARRAY_PRIMITIVE, blockLightMask.toLongArray());
|
||||
wrapper.write(Type.LONG_ARRAY_PRIMITIVE, new long[emptySkyLightMask]);
|
||||
wrapper.write(Type.LONG_ARRAY_PRIMITIVE, new long[emptyBlockLightMask]);
|
||||
|
||||
wrapper.write(Type.VAR_INT, skyLightCount);
|
||||
for (int i = 0; i < skyLightCount; i++) {
|
||||
wrapper.write(Type.BYTE_ARRAY_PRIMITIVE, lightArrays.remove(0));
|
||||
}
|
||||
wrapper.write(Type.VAR_INT, blockLightCount);
|
||||
for (int i = 0; i < blockLightCount; i++) {
|
||||
wrapper.write(Type.BYTE_ARRAY_PRIMITIVE, lightArrays.remove(0));
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return new PacketRemapper() {
|
||||
@Override
|
||||
public void registerMap() {
|
||||
handler(wrapper -> {
|
||||
if (VersionEnum.fromUserConnection(wrapper.user()).isOlderThanOrEqualTo(VersionEnum.c0_28toc0_30)) {
|
||||
classicLightHandler.remap(wrapper);
|
||||
} else {
|
||||
parentRemapper.remap(wrapper);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void changeDimensionTagHeight(final UserConnection user, final CompoundTag tag) {
|
||||
tag.put("height", new IntTag(Via.getManager().getProviders().get(ClassicWorldHeightProvider.class).getMaxChunkSectionCount(user) << 4));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package net.raphimc.viaproxy.injection.mixins;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.minecraft.BlockFace;
|
||||
import com.viaversion.viaversion.api.minecraft.Position;
|
||||
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections.AbstractFenceConnectionHandler;
|
||||
import net.raphimc.vialegacy.util.VersionEnum;
|
||||
import org.spongepowered.asm.mixin.*;
|
||||
|
||||
@Mixin(value = AbstractFenceConnectionHandler.class, remap = false)
|
||||
public abstract class MixinAbstractFenceConnectionHandler {
|
||||
|
||||
@Shadow
|
||||
protected abstract boolean connects(BlockFace side, int blockState, boolean pre1_12);
|
||||
|
||||
@Shadow
|
||||
public abstract int getBlockData(UserConnection user, Position position);
|
||||
|
||||
/**
|
||||
* @author RK_01
|
||||
* @reason Fixes version comparisons
|
||||
*/
|
||||
@Overwrite
|
||||
public byte getStates(UserConnection user, Position position, int blockState) {
|
||||
byte states = 0;
|
||||
boolean pre1_12 = VersionEnum.fromUserConnection(user).isOlderThan(VersionEnum.r1_12);
|
||||
if (connects(BlockFace.EAST, getBlockData(user, position.getRelative(BlockFace.EAST)), pre1_12)) states |= 1;
|
||||
if (connects(BlockFace.NORTH, getBlockData(user, position.getRelative(BlockFace.NORTH)), pre1_12)) states |= 2;
|
||||
if (connects(BlockFace.SOUTH, getBlockData(user, position.getRelative(BlockFace.SOUTH)), pre1_12)) states |= 4;
|
||||
if (connects(BlockFace.WEST, getBlockData(user, position.getRelative(BlockFace.WEST)), pre1_12)) states |= 8;
|
||||
return states;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package net.raphimc.viaproxy.injection.mixins;
|
||||
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.minecraft.nbt.BinaryTagIO;
|
||||
import com.viaversion.viaversion.libs.opennbt.tag.builtin.CompoundTag;
|
||||
import com.viaversion.viaversion.protocols.protocol1_12to1_11_1.ChatItemRewriter;
|
||||
import net.raphimc.vialegacy.util.ViaStringTagReader1_11_2;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Mixin(value = ChatItemRewriter.class, remap = false)
|
||||
public abstract class MixinChatItemRewriter {
|
||||
|
||||
@Redirect(method = "toClient", at = @At(value = "INVOKE", target = "Ljava/util/regex/Pattern;matcher(Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;"))
|
||||
private static Matcher rewriteShowItem(Pattern pattern, CharSequence input) {
|
||||
try {
|
||||
final CompoundTag tag = ViaStringTagReader1_11_2.getTagFromJson(input.toString());
|
||||
input = BinaryTagIO.writeString(tag);
|
||||
} catch (Throwable e) {
|
||||
Via.getPlatform().getLogger().log(Level.WARNING, "Error converting 1.11.2 nbt to 1.12.2 nbt: '" + input + "'", e);
|
||||
}
|
||||
return Pattern.compile("$^").matcher(input);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package net.raphimc.viaproxy.injection.mixins;
|
||||
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.minecraft.chunks.*;
|
||||
import com.viaversion.viaversion.protocols.protocol1_9to1_8.types.Chunk1_8Type;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@Mixin(value = Chunk1_8Type.class, remap = false)
|
||||
public abstract class MixinChunk1_8Type {
|
||||
|
||||
@Shadow
|
||||
public static Chunk deserialize(int chunkX, int chunkZ, boolean fullChunk, boolean skyLight, int bitmask, byte[] data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Redirect(method = "read(Lio/netty/buffer/ByteBuf;Lcom/viaversion/viaversion/protocols/protocol1_9_3to1_9_1_2/storage/ClientWorld;)Lcom/viaversion/viaversion/api/minecraft/chunks/Chunk;", at = @At(value = "INVOKE", target = "Lcom/viaversion/viaversion/protocols/protocol1_9to1_8/types/Chunk1_8Type;deserialize(IIZZI[B)Lcom/viaversion/viaversion/api/minecraft/chunks/Chunk;"))
|
||||
private Chunk fixAegis(int chunkX, int chunkZ, boolean fullChunk, boolean skyLight, int bitmask, byte[] data) {
|
||||
try {
|
||||
return deserialize(chunkX, chunkZ, fullChunk, skyLight, bitmask, data);
|
||||
} catch (Throwable e) {
|
||||
Via.getPlatform().getLogger().log(Level.WARNING, "The server sent an invalid chunk data packet, returning an empty chunk", e);
|
||||
final ChunkSection[] airSections = new ChunkSection[16];
|
||||
for (int i = 0; i < airSections.length; i++) {
|
||||
airSections[i] = new ChunkSectionImpl(true);
|
||||
airSections[i].palette(PaletteType.BLOCKS).addId(0);
|
||||
}
|
||||
return new BaseChunk(chunkX, chunkZ, fullChunk, false, 65535, airSections, new int[256], new ArrayList<>());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package net.raphimc.viaproxy.injection.mixins;
|
||||
|
||||
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.CommandBlockProvider;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.Constant;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyConstant;
|
||||
|
||||
@Mixin(value = CommandBlockProvider.class, remap = false)
|
||||
public abstract class MixinCommandBlockProvider {
|
||||
|
||||
@ModifyConstant(method = "sendPermission", constant = @Constant(intValue = 26))
|
||||
private int modifyOpLevel() {
|
||||
return 28; // Op Level 4
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package net.raphimc.viaproxy.injection.mixins;
|
||||
|
||||
import com.viaversion.viaversion.legacy.bossbar.CommonBoss;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(value = CommonBoss.class, remap = false)
|
||||
public abstract class MixinCommonBoss {
|
||||
|
||||
@Redirect(method = {"<init>", "setHealth"}, at = @At(value = "INVOKE", target = "Lcom/google/common/base/Preconditions;checkArgument(ZLjava/lang/Object;)V"))
|
||||
private void removeBoundChecks(boolean expression, Object errorMessage) {
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package net.raphimc.viaproxy.injection.mixins;
|
||||
|
||||
import com.viaversion.viaversion.api.protocol.Protocol;
|
||||
import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;
|
||||
import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
|
||||
import com.viaversion.viaversion.protocols.protocol1_16_2to1_16_1.ClientboundPackets1_16_2;
|
||||
import com.viaversion.viaversion.protocols.protocol1_17to1_16_4.Protocol1_17To1_16_4;
|
||||
import com.viaversion.viaversion.protocols.protocol1_17to1_16_4.packets.EntityPackets;
|
||||
import net.raphimc.viaproxy.injection.ClassicWorldHeightInjection;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(value = EntityPackets.class, remap = false)
|
||||
public abstract class MixinEntityPackets1_17 {
|
||||
|
||||
@Redirect(method = "registerPackets", at = @At(value = "INVOKE", target = "Lcom/viaversion/viaversion/protocols/protocol1_17to1_16_4/Protocol1_17To1_16_4;registerClientbound(Lcom/viaversion/viaversion/api/protocol/packet/ClientboundPacketType;Lcom/viaversion/viaversion/api/protocol/remapper/PacketRemapper;)V"))
|
||||
private void handleClassicWorldHeight(Protocol1_17To1_16_4 instance, ClientboundPacketType packetType, PacketRemapper packetRemapper) {
|
||||
if (packetType == ClientboundPackets1_16_2.JOIN_GAME) packetRemapper = ClassicWorldHeightInjection.handleJoinGame(instance, packetRemapper);
|
||||
if (packetType == ClientboundPackets1_16_2.RESPAWN) packetRemapper = ClassicWorldHeightInjection.handleRespawn(instance, packetRemapper);
|
||||
|
||||
((Protocol) instance).registerClientbound(packetType, packetRemapper);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package net.raphimc.viaproxy.injection.mixins;
|
||||
|
||||
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.protocols.protocol1_9to1_8.Protocol1_9To1_8;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(targets = "com.viaversion.viaversion.protocols.protocol1_9to1_8.packets.EntityPackets$6$1", remap = false)
|
||||
public abstract class MixinEntityPackets_6_1 {
|
||||
|
||||
@SuppressWarnings({"UnresolvedMixinReference", "MixinAnnotationTarget"})
|
||||
@Inject(method = "transform(Lcom/viaversion/viaversion/api/protocol/packet/PacketWrapper;Ljava/lang/Short;)Ljava/lang/Integer;", at = @At(value = "INVOKE", target = "Lcom/viaversion/viaversion/api/data/entity/EntityTracker;clientEntityId()I"), cancellable = true)
|
||||
private void fixOutOfBoundsSlot(PacketWrapper wrapper, Short slot, CallbackInfoReturnable<Integer> cir) throws Exception {
|
||||
final int entityId = wrapper.get(Type.VAR_INT, 0);
|
||||
final int clientPlayerId = wrapper.user().getEntityTracker(Protocol1_9To1_8.class).clientEntityId();
|
||||
if (slot < 0 || slot > 4 || (entityId == clientPlayerId && slot > 3)) {
|
||||
wrapper.cancel();
|
||||
cir.setReturnValue(0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package net.raphimc.viaproxy.injection.mixins;
|
||||
|
||||
import com.viaversion.viaversion.api.minecraft.metadata.Metadata;
|
||||
import com.viaversion.viaversion.protocols.protocol1_9to1_8.storage.EntityTracker1_9;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.*;
|
||||
|
||||
@Mixin(value = EntityTracker1_9.class, remap = false)
|
||||
public abstract class MixinEntityTracker1_9 {
|
||||
|
||||
@Redirect(method = "handleMetadata", at = @At(value = "INVOKE", target = "Ljava/lang/Math;min(FF)F"), slice = @Slice(from = @At(value = "INVOKE", target = "Lcom/viaversion/viaversion/api/configuration/ViaVersionConfig;isBossbarAntiflicker()Z")))
|
||||
private float removeMin(float a, float b) {
|
||||
return a;
|
||||
}
|
||||
|
||||
@Redirect(method = "handleMetadata", at = @At(value = "INVOKE", target = "Ljava/lang/Math;max(FF)F"), slice = @Slice(from = @At(value = "INVOKE", target = "Lcom/viaversion/viaversion/api/configuration/ViaVersionConfig;isBossbarAntiflicker()Z")))
|
||||
private float removeMax(float a, float b) {
|
||||
return b;
|
||||
}
|
||||
|
||||
@Redirect(method = "handleMetadata", at = @At(value = "INVOKE", target = "Lcom/viaversion/viaversion/api/minecraft/metadata/Metadata;getValue()Ljava/lang/Object;"), slice = @Slice(from = @At(value = "INVOKE", target = "Lcom/viaversion/viaversion/api/configuration/ViaVersionConfig;isBossbarAntiflicker()Z")))
|
||||
private Object remapNaNToZero(Metadata instance) {
|
||||
if (instance.getValue() instanceof Float && ((Float) instance.getValue()).isNaN()) {
|
||||
return 0F;
|
||||
}
|
||||
|
||||
return instance.getValue();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package net.raphimc.viaproxy.injection.mixins;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.ProtocolInfo;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.minecraft.Position;
|
||||
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections.AbstractFenceConnectionHandler;
|
||||
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections.GlassConnectionHandler;
|
||||
import net.raphimc.vialegacy.util.VersionEnum;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
|
||||
@Mixin(value = GlassConnectionHandler.class, remap = false)
|
||||
public abstract class MixinGlassConnectionHandler extends AbstractFenceConnectionHandler {
|
||||
|
||||
protected MixinGlassConnectionHandler(String blockConnections) {
|
||||
super(blockConnections);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author RK_01
|
||||
* @reason Fixes version comparisons
|
||||
*/
|
||||
@Overwrite
|
||||
public byte getStates(UserConnection user, Position position, int blockState) {
|
||||
byte states = super.getStates(user, position, blockState);
|
||||
if (states != 0) return states;
|
||||
|
||||
ProtocolInfo protocolInfo = user.getProtocolInfo();
|
||||
return VersionEnum.fromUserConnection(user).isOlderThanOrEqualTo(VersionEnum.r1_8) && protocolInfo.getServerProtocolVersion() != -1 ? 0xF : states;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package net.raphimc.viaproxy.injection.mixins;
|
||||
|
||||
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(targets = "com.viaversion.viaversion.protocols.protocol1_14to1_13_2.packets.InventoryPackets$3$1", remap = false)
|
||||
public abstract class MixinInventoryPackets_3_1 {
|
||||
|
||||
@SuppressWarnings({"UnresolvedMixinReference", "MixinAnnotationTarget"})
|
||||
@Inject(method = "handle", at = @At(value = "FIELD", target = "Lcom/viaversion/viaversion/api/type/Type;BOOLEAN:Lcom/viaversion/viaversion/api/type/types/BooleanType;", ordinal = 2, shift = At.Shift.BEFORE))
|
||||
private void removeExtraData(PacketWrapper wrapper, CallbackInfo ci) {
|
||||
wrapper.clearInputBuffer();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package net.raphimc.viaproxy.injection.mixins;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
|
||||
import com.viaversion.viaversion.api.minecraft.metadata.Metadata;
|
||||
import com.viaversion.viaversion.protocols.protocol1_9to1_8.Protocol1_9To1_8;
|
||||
import com.viaversion.viaversion.protocols.protocol1_9to1_8.metadata.MetadataRewriter1_9To1_8;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mixin(value = MetadataRewriter1_9To1_8.class, remap = false)
|
||||
public abstract class MixinMetadataRewriter1_9To1_8 {
|
||||
|
||||
@Inject(method = "handleMetadata", at = @At(value = "FIELD", target = "Lcom/viaversion/viaversion/protocols/protocol1_9to1_8/metadata/MetaIndex;PLAYER_HAND:Lcom/viaversion/viaversion/protocols/protocol1_9to1_8/metadata/MetaIndex;", ordinal = 0, shift = At.Shift.BEFORE), cancellable = true)
|
||||
private void preventMetadataForClientPlayer(int entityId, EntityType type, Metadata metadata, List<Metadata> metadatas, UserConnection connection, CallbackInfo ci) {
|
||||
if (connection.getEntityTracker(Protocol1_9To1_8.class).clientEntityId() == entityId) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package net.raphimc.viaproxy.injection.mixins;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
||||
import com.viaversion.viaversion.api.protocol.version.VersionRange;
|
||||
import com.viaversion.viaversion.util.Pair;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.*;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Mixin(value = ProtocolVersion.class, remap = false)
|
||||
public abstract class MixinProtocolVersion {
|
||||
|
||||
@Unique
|
||||
private static Set<String> skips;
|
||||
|
||||
@Unique
|
||||
private static Map<String, Pair<String, VersionRange>> remaps;
|
||||
|
||||
@Inject(method = "<clinit>", at = @At("HEAD"))
|
||||
private static void initMaps(CallbackInfo ci) {
|
||||
skips = ImmutableSet.of("1.4.6/7", "1.5.1", "1.5.2", "1.6.1", "1.6.2", "1.6.3", "1.6.4");
|
||||
remaps = new HashMap<>();
|
||||
remaps.put("1.7-1.7.5", new Pair<>("1.7.2-1.7.5", new VersionRange("1.7", 2, 5)));
|
||||
remaps.put("1.9.3/4", new Pair<>("1.9.3-1.9.4", null));
|
||||
remaps.put("1.11.1/2", new Pair<>("1.11.1-1.11.2", null));
|
||||
remaps.put("1.16.4/5", new Pair<>("1.16.4-1.16.5", null));
|
||||
remaps.put("1.18/1.18.1", new Pair<>("1.18-1.18.1", null));
|
||||
remaps.put("1.19.1/2", new Pair<>("1.19.1-1.19.2", null));
|
||||
}
|
||||
|
||||
@Redirect(method = "<clinit>", at = @At(value = "INVOKE", target = "Lcom/viaversion/viaversion/api/protocol/version/ProtocolVersion;register(ILjava/lang/String;)Lcom/viaversion/viaversion/api/protocol/version/ProtocolVersion;"))
|
||||
private static ProtocolVersion unregisterAndRenameVersions(int version, String name) {
|
||||
if (skips.contains(name)) return null;
|
||||
final Pair<String, VersionRange> remapEntry = remaps.get(name);
|
||||
if (remapEntry != null) {
|
||||
if (remapEntry.key() != null) name = remapEntry.key();
|
||||
}
|
||||
|
||||
return ProtocolVersion.register(version, name);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"UnresolvedMixinReference", "MixinAnnotationTarget", "InvalidInjectorMethodSignature"}) // Optional injection
|
||||
@Redirect(method = "<clinit>", at = @At(value = "INVOKE", target = "Lcom/viaversion/viaversion/api/protocol/version/ProtocolVersion;register(IILjava/lang/String;)Lcom/viaversion/viaversion/api/protocol/version/ProtocolVersion;"), require = 0)
|
||||
private static ProtocolVersion unregisterAndRenameVersions(int version, int snapshotVersion, String name) {
|
||||
if (skips.contains(name)) return null;
|
||||
final Pair<String, VersionRange> remapEntry = remaps.get(name);
|
||||
if (remapEntry != null) {
|
||||
if (remapEntry.key() != null) name = remapEntry.key();
|
||||
}
|
||||
|
||||
return ProtocolVersion.register(version, snapshotVersion, name);
|
||||
}
|
||||
|
||||
@Redirect(method = "<clinit>", at = @At(value = "INVOKE", target = "Lcom/viaversion/viaversion/api/protocol/version/ProtocolVersion;register(ILjava/lang/String;Lcom/viaversion/viaversion/api/protocol/version/VersionRange;)Lcom/viaversion/viaversion/api/protocol/version/ProtocolVersion;"))
|
||||
private static ProtocolVersion unregisterAndRenameVersions(int version, String name, VersionRange versionRange) {
|
||||
if (skips.contains(name)) return null;
|
||||
final Pair<String, VersionRange> remapEntry = remaps.get(name);
|
||||
if (remapEntry != null) {
|
||||
if (remapEntry.key() != null) name = remapEntry.key();
|
||||
if (remapEntry.value() != null) versionRange = remapEntry.value();
|
||||
}
|
||||
|
||||
return ProtocolVersion.register(version, name, versionRange);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package net.raphimc.viaproxy.injection.mixins;
|
||||
|
||||
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.packets.WorldPackets;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(value = WorldPackets.class, remap = false)
|
||||
public abstract class MixinWorldPackets1_13 {
|
||||
|
||||
@Inject(method = "toNewId", at = @At(value = "RETURN", ordinal = 2), cancellable = true)
|
||||
private static void returnAirDefault(int oldId, CallbackInfoReturnable<Integer> cir) {
|
||||
cir.setReturnValue(0);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package net.raphimc.viaproxy.injection.mixins;
|
||||
|
||||
import com.viaversion.viaversion.api.protocol.Protocol;
|
||||
import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;
|
||||
import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
|
||||
import com.viaversion.viaversion.protocols.protocol1_16_2to1_16_1.ClientboundPackets1_16_2;
|
||||
import com.viaversion.viaversion.protocols.protocol1_17to1_16_4.Protocol1_17To1_16_4;
|
||||
import com.viaversion.viaversion.protocols.protocol1_17to1_16_4.packets.WorldPackets;
|
||||
import net.raphimc.viaproxy.injection.ClassicWorldHeightInjection;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(value = WorldPackets.class, remap = false)
|
||||
public abstract class MixinWorldPackets1_17 {
|
||||
|
||||
@Redirect(method = "register", at = @At(value = "INVOKE", target = "Lcom/viaversion/viaversion/protocols/protocol1_17to1_16_4/Protocol1_17To1_16_4;registerClientbound(Lcom/viaversion/viaversion/api/protocol/packet/ClientboundPacketType;Lcom/viaversion/viaversion/api/protocol/remapper/PacketRemapper;)V"))
|
||||
private static void handleClassicWorldHeight(Protocol1_17To1_16_4 instance, ClientboundPacketType packetType, PacketRemapper packetRemapper) {
|
||||
if (packetType == ClientboundPackets1_16_2.CHUNK_DATA) packetRemapper = ClassicWorldHeightInjection.handleChunkData(instance, packetRemapper);
|
||||
if (packetType == ClientboundPackets1_16_2.UPDATE_LIGHT) packetRemapper = ClassicWorldHeightInjection.handleUpdateLight(instance, packetRemapper);
|
||||
|
||||
((Protocol) instance).registerClientbound(packetType, packetRemapper);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package net.raphimc.viaproxy.injection.mixins;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.Constant;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyConstant;
|
||||
|
||||
@Mixin(targets = "com.viaversion.viaversion.protocols.protocol1_16_2to1_16_1.packets.WorldPackets$2", remap = false)
|
||||
public abstract class MixinWorldPackets_2 {
|
||||
|
||||
@ModifyConstant(method = "lambda$registerMap$0", constant = @Constant(intValue = 16))
|
||||
private static int modifySectionCountToSupportClassicWorldHeight() {
|
||||
return 64;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package net.raphimc.viaproxy.injection.transformer;
|
||||
|
||||
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.blockconnections.ConnectionData;
|
||||
import net.lenni0451.classtransform.annotations.CTransformer;
|
||||
import net.lenni0451.classtransform.annotations.injection.CASM;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.tree.*;
|
||||
|
||||
@CTransformer(ConnectionData.class)
|
||||
public abstract class ConnectionDataTransformer {
|
||||
|
||||
@CASM("update")
|
||||
public static void preventBlockChangeSpam1(MethodNode method) {
|
||||
LabelNode continueLabel = new LabelNode();
|
||||
InsnList checkCode = new InsnList();
|
||||
checkCode.add(new VarInsnNode(Opcodes.ILOAD, 7));
|
||||
checkCode.add(new VarInsnNode(Opcodes.ILOAD, 9));
|
||||
checkCode.add(new JumpInsnNode(Opcodes.IF_ICMPEQ, continueLabel));
|
||||
|
||||
for (AbstractInsnNode insn : method.instructions.toArray()) {
|
||||
if (checkCode != null && insn.getOpcode() == Opcodes.ISTORE) {
|
||||
VarInsnNode varInsn = (VarInsnNode) insn;
|
||||
if (varInsn.var == 9) {
|
||||
method.instructions.insert(insn, checkCode);
|
||||
checkCode = null;
|
||||
}
|
||||
} else if (continueLabel != null && insn.getOpcode() == Opcodes.IINC) {
|
||||
method.instructions.insertBefore(insn, continueLabel);
|
||||
continueLabel = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CASM("updateBlock")
|
||||
public static void preventBlockChangeSpam2(MethodNode method) {
|
||||
LabelNode addLabel = new LabelNode();
|
||||
InsnList checkCode = new InsnList();
|
||||
checkCode.add(new VarInsnNode(Opcodes.ILOAD, 3));
|
||||
checkCode.add(new VarInsnNode(Opcodes.ILOAD, 5));
|
||||
checkCode.add(new JumpInsnNode(Opcodes.IF_ICMPNE, addLabel));
|
||||
checkCode.add(new InsnNode(Opcodes.RETURN));
|
||||
checkCode.add(addLabel);
|
||||
|
||||
for (AbstractInsnNode insn : method.instructions.toArray()) {
|
||||
if (insn.getOpcode() == Opcodes.INVOKEINTERFACE) {
|
||||
method.instructions.insertBefore(insn, checkCode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package net.raphimc.viaproxy.injection.transformer;
|
||||
|
||||
import com.viaversion.viaversion.protocols.protocol1_9to1_8.storage.MovementTracker;
|
||||
import net.lenni0451.classtransform.annotations.CShadow;
|
||||
import net.lenni0451.classtransform.annotations.CTransformer;
|
||||
|
||||
@CTransformer(MovementTracker.class)
|
||||
public abstract class MovementTrackerTransformer {
|
||||
|
||||
@CShadow
|
||||
private boolean ground = false;
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package net.raphimc.viaproxy.plugins;
|
||||
|
||||
import net.lenni0451.lambdaevents.LambdaManager;
|
||||
import net.lenni0451.lambdaevents.generator.LambdaMetaFactoryGenerator;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.*;
|
||||
|
||||
public class PluginManager {
|
||||
|
||||
public static final LambdaManager EVENT_MANAGER = LambdaManager.threadSafe(new LambdaMetaFactoryGenerator());
|
||||
|
||||
private static final Yaml YAML = new Yaml();
|
||||
private static final File PLUGINS_DIR = new File("plugins");
|
||||
private static final List<ViaProxyPlugin> PLUGINS = new ArrayList<>();
|
||||
|
||||
public static void loadPlugins() {
|
||||
if (!PLUGINS_DIR.exists() || !PLUGINS_DIR.isDirectory()) return;
|
||||
|
||||
File[] files = PLUGINS_DIR.listFiles();
|
||||
if (files == null) return;
|
||||
|
||||
for (File file : files) {
|
||||
if (!file.getName().toLowerCase().endsWith(".jar")) continue;
|
||||
try {
|
||||
loadAndScanJar(file);
|
||||
} catch (Throwable t) {
|
||||
new Exception("Unable to load plugin '" + file.getName() + "'", t).printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadAndScanJar(final File file) throws Throwable {
|
||||
URLClassLoader loader = new URLClassLoader(new URL[]{new URL("jar:file:" + file.getAbsolutePath() + "!/")}, PluginManager.class.getClassLoader());
|
||||
InputStream viaproxyYml = loader.getResourceAsStream("viaproxy.yml");
|
||||
if (viaproxyYml == null)
|
||||
throw new IllegalStateException("Plugin '" + file.getName() + "' does not have a viaproxy.yml");
|
||||
Map<String, Object> yaml = YAML.load(viaproxyYml);
|
||||
if (!yaml.containsKey("name"))
|
||||
throw new IllegalStateException("Plugin '" + file.getName() + "' does not have a name attribute in the viaproxy.yml");
|
||||
if (!yaml.containsKey("author"))
|
||||
throw new IllegalStateException("Plugin '" + file.getName() + "' does not have a author attribute in the viaproxy.yml");
|
||||
if (!yaml.containsKey("version"))
|
||||
throw new IllegalStateException("Plugin '" + file.getName() + "' does not have a version attribute in the viaproxy.yml");
|
||||
if (!yaml.containsKey("main"))
|
||||
throw new IllegalStateException("Plugin '" + file.getName() + "' does not have a main attribute in the viaproxy.yml");
|
||||
|
||||
String main = (String) yaml.get("main");
|
||||
|
||||
Class<?> mainClass = loader.loadClass(main);
|
||||
if (!ViaProxyPlugin.class.isAssignableFrom(mainClass))
|
||||
throw new IllegalStateException("Class '" + mainClass.getName() + "' from '" + file.getName() + "' does not extend ViaProxyPlugin");
|
||||
Object instance = mainClass.newInstance();
|
||||
ViaProxyPlugin plugin = (ViaProxyPlugin) instance;
|
||||
PLUGINS.add(plugin);
|
||||
|
||||
plugin.onEnable();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package net.raphimc.viaproxy.plugins;
|
||||
|
||||
public abstract class ViaProxyPlugin {
|
||||
|
||||
public abstract void onEnable();
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package net.raphimc.viaproxy.plugins.events;
|
||||
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import net.raphimc.viaproxy.plugins.events.types.ICancellable;
|
||||
import net.raphimc.viaproxy.plugins.events.types.ITyped;
|
||||
|
||||
public class Client2ProxyChannelInitializeEvent implements ICancellable, ITyped {
|
||||
|
||||
private final Type type;
|
||||
private final SocketChannel channel;
|
||||
|
||||
private boolean cancelled;
|
||||
|
||||
public Client2ProxyChannelInitializeEvent(final Type type, final SocketChannel channel) {
|
||||
this.type = type;
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public SocketChannel getChannel() {
|
||||
return this.channel;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return this.cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancelled) {
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package net.raphimc.viaproxy.plugins.events;
|
||||
|
||||
import net.raphimc.viaproxy.plugins.events.types.ICancellable;
|
||||
|
||||
public class ConsoleCommandEvent implements ICancellable {
|
||||
|
||||
private final String command;
|
||||
private final String[] args;
|
||||
private boolean cancelled;
|
||||
|
||||
public ConsoleCommandEvent(final String command, final String[] args) {
|
||||
this.command = command;
|
||||
this.args = args;
|
||||
}
|
||||
|
||||
public String getCommand() {
|
||||
return this.command;
|
||||
}
|
||||
|
||||
public String[] getArgs() {
|
||||
return this.args;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancelled) {
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return this.cancelled;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package net.raphimc.viaproxy.plugins.events;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import net.raphimc.netminecraft.util.ServerAddress;
|
||||
import net.raphimc.vialegacy.util.VersionEnum;
|
||||
import net.raphimc.viaproxy.plugins.events.types.ICancellable;
|
||||
|
||||
public class PreConnectEvent implements ICancellable {
|
||||
|
||||
private final ServerAddress serverAddress;
|
||||
private final VersionEnum serverVersion;
|
||||
private final VersionEnum clientVersion;
|
||||
private final Channel clientChannel;
|
||||
|
||||
private boolean cancelled;
|
||||
private String cancelMessage = "§cCould not connect to the backend server! (Server is blacklisted)";
|
||||
|
||||
public PreConnectEvent(final ServerAddress serverAddress, final VersionEnum serverVersion, final VersionEnum clientVersion, final Channel clientChannel) {
|
||||
this.serverAddress = serverAddress;
|
||||
this.serverVersion = serverVersion;
|
||||
this.clientVersion = clientVersion;
|
||||
this.clientChannel = clientChannel;
|
||||
}
|
||||
|
||||
public ServerAddress getServerAddress() {
|
||||
return this.serverAddress;
|
||||
}
|
||||
|
||||
public VersionEnum getServerVersion() {
|
||||
return this.serverVersion;
|
||||
}
|
||||
|
||||
public VersionEnum getClientVersion() {
|
||||
return this.clientVersion;
|
||||
}
|
||||
|
||||
public Channel getClientChannel() {
|
||||
return this.clientChannel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancelled) {
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return this.cancelled;
|
||||
}
|
||||
|
||||
public String getCancelMessage() {
|
||||
return this.cancelMessage;
|
||||
}
|
||||
|
||||
public void setCancelMessage(final String cancelMessage) {
|
||||
this.cancelMessage = cancelMessage;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package net.raphimc.viaproxy.plugins.events;
|
||||
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import net.raphimc.viaproxy.plugins.events.types.ICancellable;
|
||||
import net.raphimc.viaproxy.plugins.events.types.ITyped;
|
||||
|
||||
public class Proxy2ServerChannelInitializeEvent implements ICancellable, ITyped {
|
||||
|
||||
private final Type type;
|
||||
private final SocketChannel channel;
|
||||
|
||||
private boolean cancelled;
|
||||
|
||||
public Proxy2ServerChannelInitializeEvent(final Type type, final SocketChannel channel) {
|
||||
this.type = type;
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public SocketChannel getChannel() {
|
||||
return this.channel;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return this.cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancelled) {
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package net.raphimc.viaproxy.plugins.events.types;
|
||||
|
||||
public interface ICancellable {
|
||||
|
||||
void setCancelled(final boolean cancelled);
|
||||
|
||||
boolean isCancelled();
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package net.raphimc.viaproxy.plugins.events.types;
|
||||
|
||||
public interface ITyped {
|
||||
|
||||
Type getType();
|
||||
|
||||
|
||||
enum Type {
|
||||
PRE, POST
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package net.raphimc.viaproxy.protocolhack;
|
||||
|
||||
import net.raphimc.viaprotocolhack.ViaProtocolHack;
|
||||
import net.raphimc.viaprotocolhack.impl.platform.*;
|
||||
import net.raphimc.viaproxy.protocolhack.impl.ViaProxyVPLoader;
|
||||
import net.raphimc.viaproxy.protocolhack.impl.ViaProxyViaVersionPlatformImpl;
|
||||
|
||||
public class ProtocolHack {
|
||||
|
||||
public static void init() {
|
||||
ViaProtocolHack.init(new ViaProxyViaVersionPlatformImpl(), new ViaProxyVPLoader(), null, null, ViaBackwardsPlatformImpl::new, ViaRewindPlatformImpl::new, ViaLegacyPlatformImpl::new);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package net.raphimc.viaproxy.protocolhack.impl;
|
||||
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.protocol.version.VersionProvider;
|
||||
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.CompressionProvider;
|
||||
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.HandItemProvider;
|
||||
import net.raphimc.vialegacy.protocols.classic.protocola1_0_15toc0_28_30.providers.*;
|
||||
import net.raphimc.vialegacy.protocols.release.protocol1_3_1_2to1_2_4_5.providers.OldAuthProvider;
|
||||
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.providers.EncryptionProvider;
|
||||
import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.providers.GameProfileFetcher;
|
||||
import net.raphimc.viaprotocolhack.impl.viaversion.VPLoader;
|
||||
import net.raphimc.viaproxy.protocolhack.providers.*;
|
||||
|
||||
public class ViaProxyVPLoader extends VPLoader {
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
super.load();
|
||||
|
||||
Via.getManager().getProviders().use(CompressionProvider.class, new ViaProxyCompressionProvider());
|
||||
Via.getManager().getProviders().use(HandItemProvider.class, new ViaProxyHandItemProvider());
|
||||
Via.getManager().getProviders().use(VersionProvider.class, new ViaProxyVersionProvider());
|
||||
|
||||
Via.getManager().getProviders().use(GameProfileFetcher.class, new ViaProxyGameProfileFetcher());
|
||||
Via.getManager().getProviders().use(EncryptionProvider.class, new ViaProxyEncryptionProvider());
|
||||
Via.getManager().getProviders().use(OldAuthProvider.class, new ViaProxyOldAuthProvider());
|
||||
Via.getManager().getProviders().use(ClassicWorldHeightProvider.class, new ViaProxyClassicWorldHeightProvider());
|
||||
Via.getManager().getProviders().use(ClassicCustomCommandProvider.class, new ViaProxyClassicCustomCommandProvider());
|
||||
Via.getManager().getProviders().use(ClassicMPPassProvider.class, new ViaProxyClassicMPPassProvider());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package net.raphimc.viaproxy.protocolhack.impl;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.exception.CancelCodecException;
|
||||
import com.viaversion.viaversion.util.PipelineUtil;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import net.raphimc.viaprotocolhack.netty.ViaDecodeHandler;
|
||||
import net.raphimc.viaproxy.util.logging.Logger;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ViaProxyViaDecodeHandler extends ViaDecodeHandler {
|
||||
|
||||
public ViaProxyViaDecodeHandler(final UserConnection info) {
|
||||
super(info);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> out) throws Exception {
|
||||
try {
|
||||
super.decode(ctx, bytebuf, out);
|
||||
} catch (Throwable e) {
|
||||
if (PipelineUtil.containsCause(e, CancelCodecException.class)) throw e;
|
||||
Logger.LOGGER.error("ProtocolHack Packet Error occurred", e);
|
||||
Logger.u_err("ProtocolHack Error", this.user, "Caught unhandled exception: " + e.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package net.raphimc.viaproxy.protocolhack.impl;
|
||||
|
||||
import net.raphimc.viaprotocolhack.impl.platform.ViaVersionPlatformImpl;
|
||||
import net.raphimc.viaproxy.cli.ConsoleFormatter;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class ViaProxyViaVersionPlatformImpl extends ViaVersionPlatformImpl {
|
||||
|
||||
public ViaProxyViaVersionPlatformImpl() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(UUID uuid, String msg) {
|
||||
super.sendMessage(uuid, ConsoleFormatter.convert(msg));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package net.raphimc.viaproxy.protocolhack.providers;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import net.raphimc.vialegacy.ViaLegacy;
|
||||
import net.raphimc.vialegacy.protocols.alpha.protocola1_0_17_1_0_17_4toa1_0_16_2.storage.TimeLockStorage;
|
||||
import net.raphimc.vialegacy.protocols.classic.protocola1_0_15toc0_28_30.providers.ClassicCustomCommandProvider;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class ViaProxyClassicCustomCommandProvider extends ClassicCustomCommandProvider {
|
||||
|
||||
@Override
|
||||
public boolean handleChatMessage(UserConnection user, String message) {
|
||||
try {
|
||||
if (message.startsWith("/")) {
|
||||
message = message.substring(1);
|
||||
final String[] args = message.split(" ");
|
||||
if (args.length <= 0) return super.handleChatMessage(user, message);
|
||||
if (args[0].equals("settime")) {
|
||||
try {
|
||||
if (args.length > 1) {
|
||||
final long time = Long.parseLong(args[1]) % 24_000L;
|
||||
user.get(TimeLockStorage.class).setTime(time);
|
||||
this.sendFeedback(user, "§aTime has been set to §6" + time);
|
||||
} else {
|
||||
throw new RuntimeException("Invalid usage");
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
this.sendFeedback(user, "§cUsage: /settime <Time (Long)>");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
ViaLegacy.getPlatform().getLogger().log(Level.WARNING, "Error handling custom classic command", e);
|
||||
}
|
||||
|
||||
return super.handleChatMessage(user, message);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package net.raphimc.viaproxy.protocolhack.providers;
|
||||
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.google.common.io.Resources;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import net.raphimc.vialegacy.protocols.classic.protocola1_0_15toc0_28_30.providers.ClassicMPPassProvider;
|
||||
import net.raphimc.vialegacy.protocols.release.protocol1_3_1_2to1_2_4_5.providers.OldAuthProvider;
|
||||
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.storage.HandshakeStorage;
|
||||
import net.raphimc.viaproxy.cli.options.Options;
|
||||
import net.raphimc.viaproxy.proxy.ProxyConnection;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class ViaProxyClassicMPPassProvider extends ClassicMPPassProvider {
|
||||
|
||||
@Override
|
||||
public String getMpPass(UserConnection user) {
|
||||
final String mppass = ProxyConnection.fromUserConnection(user).getClassicMpPass();
|
||||
if (mppass != null && !mppass.isEmpty() && !mppass.equals("0")) {
|
||||
return mppass;
|
||||
} else if (Options.BETACRAFT_AUTH) {
|
||||
final HandshakeStorage handshakeStorage = user.get(HandshakeStorage.class);
|
||||
return getBetacraftMpPass(user, user.getProtocolInfo().getUsername(), handshakeStorage.getHostname(), handshakeStorage.getPort());
|
||||
} else {
|
||||
return super.getMpPass(user);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getBetacraftMpPass(final UserConnection user, final String username, final String serverIp, final int port) {
|
||||
try {
|
||||
final String server = InetAddress.getByName(serverIp).getHostAddress() + ":" + port;
|
||||
Via.getManager().getProviders().get(OldAuthProvider.class).sendAuthRequest(user, Hashing.sha1().hashBytes(server.getBytes()).toString());
|
||||
final String mppass = Resources.toString(new URL("http://api.betacraft.uk/getmppass.jsp?user=" + username + "&server=" + server), StandardCharsets.UTF_8);
|
||||
if (mppass.contains("FAILED") || mppass.contains("SERVER NOT FOUND")) return "0";
|
||||
return mppass;
|
||||
} catch (Throwable e) {
|
||||
Via.getPlatform().getLogger().log(Level.WARNING, "An unknown error occurred while authenticating with BetaCraft", e);
|
||||
}
|
||||
return "0";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package net.raphimc.viaproxy.protocolhack.providers;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import net.raphimc.vialegacy.protocols.classic.protocola1_0_15toc0_28_30.providers.ClassicWorldHeightProvider;
|
||||
import net.raphimc.vialegacy.util.VersionEnum;
|
||||
import net.raphimc.viaproxy.proxy.ProxyConnection;
|
||||
|
||||
public class ViaProxyClassicWorldHeightProvider extends ClassicWorldHeightProvider {
|
||||
|
||||
@Override
|
||||
public short getMaxChunkSectionCount(UserConnection user) {
|
||||
if (ProxyConnection.fromUserConnection(user).getClientVersion().isNewerThanOrEqualTo(VersionEnum.r1_17)) {
|
||||
return 64;
|
||||
}
|
||||
return 16;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package net.raphimc.viaproxy.protocolhack.providers;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.CompressionProvider;
|
||||
import net.raphimc.netminecraft.constants.MCPipeline;
|
||||
|
||||
public class ViaProxyCompressionProvider extends CompressionProvider {
|
||||
|
||||
@Override
|
||||
public void handlePlayCompression(UserConnection user, int threshold) {
|
||||
if (!user.isClientSide()) {
|
||||
throw new IllegalStateException("PLAY state Compression packet is unsupported");
|
||||
}
|
||||
user.getChannel().attr(MCPipeline.COMPRESSION_THRESHOLD_ATTRIBUTE_KEY).set(threshold);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package net.raphimc.viaproxy.protocolhack.providers;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.providers.EncryptionProvider;
|
||||
import net.raphimc.viaproxy.proxy.ProxyConnection;
|
||||
|
||||
public class ViaProxyEncryptionProvider extends EncryptionProvider {
|
||||
|
||||
@Override
|
||||
public void enableDecryption(UserConnection user) {
|
||||
try {
|
||||
ProxyConnection.fromUserConnection(user).enablePreNettyEncryption();
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package net.raphimc.viaproxy.protocolhack.providers;
|
||||
|
||||
import com.github.steveice10.mc.auth.exception.profile.ProfileException;
|
||||
import com.github.steveice10.mc.auth.exception.profile.ProfileNotFoundException;
|
||||
import com.github.steveice10.mc.auth.service.ProfileService;
|
||||
import com.github.steveice10.mc.auth.service.SessionService;
|
||||
import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.model.GameProfile;
|
||||
import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.providers.GameProfileFetcher;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class ViaProxyGameProfileFetcher extends GameProfileFetcher {
|
||||
|
||||
public static SessionService sessionService = new SessionService();
|
||||
public static ProfileService profileService = new ProfileService();
|
||||
|
||||
@Override
|
||||
public UUID loadMojangUUID(String playerName) throws ExecutionException, InterruptedException {
|
||||
final CompletableFuture<com.github.steveice10.mc.auth.data.GameProfile> future = new CompletableFuture<>();
|
||||
profileService.findProfilesByName(new String[]{playerName}, new ProfileService.ProfileLookupCallback() {
|
||||
@Override
|
||||
public void onProfileLookupSucceeded(com.github.steveice10.mc.auth.data.GameProfile profile) {
|
||||
future.complete(profile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProfileLookupFailed(com.github.steveice10.mc.auth.data.GameProfile profile, Exception e) {
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
if (!future.isDone()) {
|
||||
future.completeExceptionally(new ProfileNotFoundException());
|
||||
}
|
||||
return future.get().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GameProfile loadGameProfile(UUID uuid) throws ProfileException {
|
||||
final com.github.steveice10.mc.auth.data.GameProfile inProfile = new com.github.steveice10.mc.auth.data.GameProfile(uuid, null);
|
||||
final com.github.steveice10.mc.auth.data.GameProfile mojangProfile = sessionService.fillProfileProperties(inProfile);
|
||||
|
||||
final GameProfile gameProfile = new GameProfile(mojangProfile.getName(), mojangProfile.getId());
|
||||
for (com.github.steveice10.mc.auth.data.GameProfile.Property prop : mojangProfile.getProperties()) {
|
||||
gameProfile.addProperty(new GameProfile.Property(prop.getName(), prop.getValue(), prop.getSignature()));
|
||||
}
|
||||
return gameProfile;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package net.raphimc.viaproxy.protocolhack.providers;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.minecraft.item.Item;
|
||||
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.HandItemProvider;
|
||||
|
||||
public class ViaProxyHandItemProvider extends HandItemProvider {
|
||||
|
||||
@Override
|
||||
public Item getHandItem(final UserConnection info) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package net.raphimc.viaproxy.protocolhack.providers;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import net.raphimc.vialegacy.protocols.release.protocol1_3_1_2to1_2_4_5.providers.OldAuthProvider;
|
||||
import net.raphimc.viaproxy.proxy.CustomPayloadInterface;
|
||||
import net.raphimc.viaproxy.proxy.ProxyConnection;
|
||||
|
||||
public class ViaProxyOldAuthProvider extends OldAuthProvider {
|
||||
|
||||
@Override
|
||||
public void sendAuthRequest(final UserConnection user, final String serverId) throws Throwable {
|
||||
CustomPayloadInterface.joinServer(serverId, ProxyConnection.fromUserConnection(user));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package net.raphimc.viaproxy.protocolhack.providers;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.protocols.base.BaseVersionProvider;
|
||||
import net.raphimc.viaproxy.proxy.ProxyConnection;
|
||||
|
||||
public class ViaProxyVersionProvider extends BaseVersionProvider {
|
||||
|
||||
@Override
|
||||
public int getClosestServerProtocol(UserConnection connection) throws Exception {
|
||||
if (connection.isClientSide()) return ProxyConnection.fromUserConnection(connection).getServerVersion().getVersion();
|
||||
return super.getClosestServerProtocol(connection);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package net.raphimc.viaproxy.proxy;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.raphimc.netminecraft.packet.PacketTypes;
|
||||
import net.raphimc.netminecraft.packet.impl.login.C2SLoginKeyPacket1_19;
|
||||
import net.raphimc.viaproxy.cli.options.Options;
|
||||
import net.raphimc.viaproxy.util.LocalSocketClient;
|
||||
import net.raphimc.viaproxy.util.logging.Logger;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class CustomPayloadInterface {
|
||||
|
||||
public static final String OPENAUTHMOD_BASE_CHANNEL = "oam:";
|
||||
public static final byte[] OPENAUTHMOD_LEGACY_MAGIC_BYTES = new byte[]{2, 20, 12, 3}; // 1.8 - 1.12.2
|
||||
public static final String OPENAUTHMOD_LEGACY_MAGIC_STRING = new String(OPENAUTHMOD_LEGACY_MAGIC_BYTES, StandardCharsets.UTF_8); // 1.8 - 1.12.2
|
||||
public static final int OPENAUTHMOD_LEGACY_MAGIC_INT = new BigInteger(OPENAUTHMOD_LEGACY_MAGIC_BYTES).intValueExact(); // 1.8 - 1.12.2
|
||||
|
||||
// Request
|
||||
public static final String OPENAUTHMOD_JOIN_CHANNEL = OPENAUTHMOD_BASE_CHANNEL + "join"; // 1.8 - latest
|
||||
public static final String OPENAUTHMOD_SIGN_NONCE_CHANNEL = OPENAUTHMOD_BASE_CHANNEL + "sign_nonce"; // 1.19 - latest
|
||||
|
||||
// Response
|
||||
public static final String OPENAUTHMOD_DATA_CHANNEL = OPENAUTHMOD_BASE_CHANNEL + "data"; // 1.8 - latest
|
||||
|
||||
public static void joinServer(final String serverIdHash, final ProxyConnection proxyConnection) throws InterruptedException, ExecutionException {
|
||||
Logger.u_info("auth", proxyConnection.getC2P().remoteAddress(), proxyConnection.getGameProfile(), "Trying to join online mode server");
|
||||
if (Options.OPENAUTHMOD_AUTH) {
|
||||
try {
|
||||
final ByteBuf response = proxyConnection.sendCustomPayload(OPENAUTHMOD_JOIN_CHANNEL, PacketTypes.writeString(Unpooled.buffer(), serverIdHash)).get(6, TimeUnit.SECONDS);
|
||||
if (response == null) throw new TimeoutException();
|
||||
if (response.isReadable() && !response.readBoolean()) throw new TimeoutException();
|
||||
} catch (TimeoutException e) {
|
||||
proxyConnection.kickClient("§cAuthentication cancelled! You need to install OpenAuthMod in order to join this server.");
|
||||
}
|
||||
} else if (Options.LOCAL_SOCKET_AUTH) {
|
||||
new LocalSocketClient(48941).request("authenticate", serverIdHash);
|
||||
}
|
||||
}
|
||||
|
||||
public static void signNonce(final byte[] nonce, final C2SLoginKeyPacket1_19 packet, final ProxyConnection proxyConnection) throws InterruptedException, ExecutionException {
|
||||
Logger.u_info("auth", proxyConnection.getC2P().remoteAddress(), proxyConnection.getGameProfile(), "Requesting nonce signature");
|
||||
if (Options.OPENAUTHMOD_AUTH) {
|
||||
try {
|
||||
final ByteBuf response = proxyConnection.sendCustomPayload(OPENAUTHMOD_SIGN_NONCE_CHANNEL, PacketTypes.writeByteArray(Unpooled.buffer(), nonce)).get(4, TimeUnit.SECONDS);
|
||||
if (response == null) throw new TimeoutException();
|
||||
if (!response.readBoolean()) throw new TimeoutException();
|
||||
packet.salt = response.readLong();
|
||||
packet.signature = PacketTypes.readByteArray(response);
|
||||
} catch (TimeoutException e) {
|
||||
proxyConnection.kickClient("§cAuthentication cancelled! You need to install OpenAuthMod in order to join this server.");
|
||||
}
|
||||
} else if (Options.LOCAL_SOCKET_AUTH) {
|
||||
final String[] response = new LocalSocketClient(48941).request("sign_nonce", Base64.getEncoder().encodeToString(nonce));
|
||||
if (response != null && response[0].equals("success")) {
|
||||
packet.salt = Long.valueOf(response[1]);
|
||||
packet.signature = Base64.getDecoder().decode(response[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
9
src/main/java/net/raphimc/viaproxy/proxy/LoginState.java
Normal file
9
src/main/java/net/raphimc/viaproxy/proxy/LoginState.java
Normal file
@ -0,0 +1,9 @@
|
||||
package net.raphimc.viaproxy.proxy;
|
||||
|
||||
public enum LoginState {
|
||||
|
||||
FIRST_PACKET,
|
||||
SENT_HELLO,
|
||||
SENT_KEY
|
||||
|
||||
}
|
248
src/main/java/net/raphimc/viaproxy/proxy/ProxyConnection.java
Normal file
248
src/main/java/net/raphimc/viaproxy/proxy/ProxyConnection.java
Normal file
@ -0,0 +1,248 @@
|
||||
package net.raphimc.viaproxy.proxy;
|
||||
|
||||
import com.github.steveice10.mc.auth.data.GameProfile;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.libs.gson.JsonObject;
|
||||
import com.viaversion.viaversion.libs.gson.JsonPrimitive;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.buffer.*;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.util.AttributeKey;
|
||||
import net.raphimc.netminecraft.constants.*;
|
||||
import net.raphimc.netminecraft.netty.connection.NetClient;
|
||||
import net.raphimc.netminecraft.netty.crypto.AESEncryption;
|
||||
import net.raphimc.netminecraft.packet.PacketTypes;
|
||||
import net.raphimc.netminecraft.packet.impl.login.*;
|
||||
import net.raphimc.netminecraft.packet.impl.status.S2CStatusResponsePacket;
|
||||
import net.raphimc.netminecraft.packet.registry.PacketRegistryUtil;
|
||||
import net.raphimc.netminecraft.util.ServerAddress;
|
||||
import net.raphimc.vialegacy.util.VersionEnum;
|
||||
import net.raphimc.viaproxy.proxy.util.CloseAndReturn;
|
||||
import net.raphimc.viaproxy.util.logging.Logger;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.Key;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ProxyConnection extends NetClient {
|
||||
|
||||
public static final AttributeKey<ProxyConnection> PROXY_CONNECTION_ATTRIBUTE_KEY = AttributeKey.valueOf("proxy_connection");
|
||||
|
||||
private final SocketChannel c2p;
|
||||
private final AtomicInteger customPayloadId = new AtomicInteger(0);
|
||||
private final Map<Integer, CompletableFuture<ByteBuf>> customPayloadListener = new ConcurrentHashMap<>();
|
||||
|
||||
private ServerAddress serverAddress;
|
||||
private VersionEnum serverVersion;
|
||||
|
||||
private VersionEnum clientVersion;
|
||||
private GameProfile gameProfile;
|
||||
private C2SLoginHelloPacket1_7 loginHelloPacket;
|
||||
private UserConnection userConnection;
|
||||
private ConnectionState connectionState = ConnectionState.HANDSHAKING;
|
||||
|
||||
private Key storedSecretKey;
|
||||
private String classicMpPass;
|
||||
|
||||
public ProxyConnection(final Supplier<ChannelHandler> handlerSupplier, final Function<Supplier<ChannelHandler>, ChannelInitializer<SocketChannel>> channelInitializerSupplier, final SocketChannel c2p) {
|
||||
super(handlerSupplier, channelInitializerSupplier);
|
||||
this.c2p = c2p;
|
||||
}
|
||||
|
||||
public static ProxyConnection fromChannel(final Channel channel) {
|
||||
return channel.attr(PROXY_CONNECTION_ATTRIBUTE_KEY).get();
|
||||
}
|
||||
|
||||
public static ProxyConnection fromUserConnection(final UserConnection userConnection) {
|
||||
return fromChannel(userConnection.getChannel());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public void connect(final ServerAddress serverAddress) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(final Bootstrap bootstrap) {
|
||||
bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 4_000);
|
||||
bootstrap.attr(ProxyConnection.PROXY_CONNECTION_ATTRIBUTE_KEY, this);
|
||||
super.initialize(bootstrap);
|
||||
}
|
||||
|
||||
public void connectToServer(final ServerAddress serverAddress, final VersionEnum targetVersion) {
|
||||
this.serverAddress = serverAddress;
|
||||
this.serverVersion = targetVersion;
|
||||
super.connect(serverAddress);
|
||||
}
|
||||
|
||||
public SocketChannel getC2P() {
|
||||
return this.c2p;
|
||||
}
|
||||
|
||||
public ServerAddress getServerAddress() {
|
||||
return this.serverAddress;
|
||||
}
|
||||
|
||||
public VersionEnum getServerVersion() {
|
||||
return this.serverVersion;
|
||||
}
|
||||
|
||||
public void setKeyForPreNettyEncryption(Key key) {
|
||||
this.storedSecretKey = key;
|
||||
}
|
||||
|
||||
public void enablePreNettyEncryption() throws GeneralSecurityException {
|
||||
this.getChannel().attr(MCPipeline.ENCRYPTION_ATTRIBUTE_KEY).set(new AESEncryption(this.storedSecretKey));
|
||||
}
|
||||
|
||||
public void setClientVersion(VersionEnum clientVersion) {
|
||||
this.clientVersion = clientVersion;
|
||||
}
|
||||
|
||||
public VersionEnum getClientVersion() {
|
||||
return this.clientVersion;
|
||||
}
|
||||
|
||||
public void setGameProfile(GameProfile gameProfile) {
|
||||
this.gameProfile = gameProfile;
|
||||
}
|
||||
|
||||
public GameProfile getGameProfile() {
|
||||
return this.gameProfile;
|
||||
}
|
||||
|
||||
public void setLoginHelloPacket(final C2SLoginHelloPacket1_7 loginHelloPacket) {
|
||||
this.loginHelloPacket = loginHelloPacket;
|
||||
}
|
||||
|
||||
public C2SLoginHelloPacket1_7 getLoginHelloPacket() {
|
||||
return this.loginHelloPacket;
|
||||
}
|
||||
|
||||
public void setUserConnection(UserConnection userConnection) {
|
||||
this.userConnection = userConnection;
|
||||
}
|
||||
|
||||
public UserConnection getUserConnection() {
|
||||
return this.userConnection;
|
||||
}
|
||||
|
||||
public void setConnectionState(ConnectionState connectionState) {
|
||||
this.connectionState = connectionState;
|
||||
switch (this.connectionState) {
|
||||
case HANDSHAKING:
|
||||
if (this.getChannel() != null)
|
||||
this.getChannel().attr(MCPipeline.PACKET_REGISTRY_ATTRIBUTE_KEY).set(PacketRegistryUtil.getHandshakeRegistry(true));
|
||||
this.c2p.attr(MCPipeline.PACKET_REGISTRY_ATTRIBUTE_KEY).set(PacketRegistryUtil.getHandshakeRegistry(false));
|
||||
break;
|
||||
case STATUS:
|
||||
if (this.getChannel() != null)
|
||||
this.getChannel().attr(MCPipeline.PACKET_REGISTRY_ATTRIBUTE_KEY).set(PacketRegistryUtil.getStatusRegistry(true));
|
||||
this.c2p.attr(MCPipeline.PACKET_REGISTRY_ATTRIBUTE_KEY).set(PacketRegistryUtil.getStatusRegistry(false));
|
||||
break;
|
||||
case LOGIN:
|
||||
if (this.getChannel() != null)
|
||||
this.getChannel().attr(MCPipeline.PACKET_REGISTRY_ATTRIBUTE_KEY).set(PacketRegistryUtil.getLoginRegistry(true, this.clientVersion.getVersion()));
|
||||
this.c2p.attr(MCPipeline.PACKET_REGISTRY_ATTRIBUTE_KEY).set(PacketRegistryUtil.getLoginRegistry(false, this.clientVersion.getVersion()));
|
||||
break;
|
||||
case PLAY:
|
||||
if (this.getChannel() != null)
|
||||
this.getChannel().attr(MCPipeline.PACKET_REGISTRY_ATTRIBUTE_KEY).set(PacketRegistryUtil.getPlayRegistry(true, this.clientVersion.getVersion()));
|
||||
this.c2p.attr(MCPipeline.PACKET_REGISTRY_ATTRIBUTE_KEY).set(PacketRegistryUtil.getPlayRegistry(false, this.clientVersion.getVersion()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public ConnectionState getConnectionState() {
|
||||
return this.connectionState;
|
||||
}
|
||||
|
||||
public CompletableFuture<ByteBuf> sendCustomPayload(final String channel, final ByteBuf data) {
|
||||
if (channel.length() > 20) throw new IllegalStateException("Channel name can't be longer than 20 characters");
|
||||
final CompletableFuture<ByteBuf> future = new CompletableFuture<>();
|
||||
final int id = this.customPayloadId.getAndIncrement();
|
||||
|
||||
switch (this.connectionState) {
|
||||
case LOGIN:
|
||||
if (this.clientVersion.isNewerThanOrEqualTo(VersionEnum.r1_13)) {
|
||||
this.c2p.writeAndFlush(new S2CLoginCustomPayloadPacket(id, channel, PacketTypes.readReadableBytes(data))).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
} else {
|
||||
final ByteBuf disconnectPacketData = Unpooled.buffer();
|
||||
PacketTypes.writeString(disconnectPacketData, channel);
|
||||
PacketTypes.writeVarInt(disconnectPacketData, id);
|
||||
disconnectPacketData.writeBytes(data);
|
||||
this.c2p.writeAndFlush(new S2CLoginDisconnectPacket(messageToJson("§cYou need to install OpenAuthMod in order to join this server.§k\n" + Base64.getEncoder().encodeToString(ByteBufUtil.getBytes(disconnectPacketData)) + "\n" + CustomPayloadInterface.OPENAUTHMOD_LEGACY_MAGIC_STRING))).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
}
|
||||
break;
|
||||
case PLAY:
|
||||
final ByteBuf customPayloadPacket = Unpooled.buffer();
|
||||
PacketTypes.writeVarInt(customPayloadPacket, MCPackets.S2C_PLUGIN_MESSAGE.getId(this.clientVersion.getVersion()));
|
||||
PacketTypes.writeString(customPayloadPacket, channel); // channel
|
||||
PacketTypes.writeVarInt(customPayloadPacket, id);
|
||||
customPayloadPacket.writeBytes(data);
|
||||
this.c2p.writeAndFlush(customPayloadPacket).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Can't send a custom payload packet during " + this.connectionState);
|
||||
}
|
||||
|
||||
this.customPayloadListener.put(id, future);
|
||||
return future;
|
||||
}
|
||||
|
||||
public boolean handleCustomPayload(final int id, final ByteBuf data) {
|
||||
if (this.customPayloadListener.containsKey(id)) {
|
||||
this.customPayloadListener.remove(id).complete(data);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setClassicMpPass(final String classicMpPass) {
|
||||
this.classicMpPass = classicMpPass;
|
||||
}
|
||||
|
||||
public String getClassicMpPass() {
|
||||
return this.classicMpPass;
|
||||
}
|
||||
|
||||
public void kickClient(final String message) throws InterruptedException, CloseAndReturn {
|
||||
Logger.u_err("kick", this.c2p.remoteAddress(), this.getGameProfile(), message.replaceAll("§.", ""));
|
||||
|
||||
final ChannelFuture future;
|
||||
if (this.connectionState == ConnectionState.LOGIN) {
|
||||
future = this.c2p.writeAndFlush(new S2CLoginDisconnectPacket(messageToJson(message)));
|
||||
} else if (this.connectionState == ConnectionState.PLAY) {
|
||||
final ByteBuf disconnectPacket = Unpooled.buffer();
|
||||
PacketTypes.writeVarInt(disconnectPacket, MCPackets.S2C_DISCONNECT.getId(this.clientVersion.getVersion()));
|
||||
PacketTypes.writeString(disconnectPacket, messageToJson(message));
|
||||
future = this.c2p.writeAndFlush(disconnectPacket);
|
||||
} else if (this.connectionState == ConnectionState.STATUS) {
|
||||
future = this.c2p.writeAndFlush(new S2CStatusResponsePacket("{\"players\":{\"max\":0,\"online\":0},\"description\":" + new JsonPrimitive(message) + ",\"version\":{\"protocol\":-1,\"name\":\"ViaProxy\"}}"));
|
||||
} else {
|
||||
future = this.c2p.newSucceededFuture();
|
||||
}
|
||||
|
||||
future.await().channel().close();
|
||||
throw CloseAndReturn.INSTANCE;
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return !this.c2p.isOpen() || (this.getChannel() != null && !this.getChannel().isOpen());
|
||||
}
|
||||
|
||||
private static String messageToJson(final String message) {
|
||||
final JsonObject obj = new JsonObject();
|
||||
obj.addProperty("text", message);
|
||||
return obj.toString();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package net.raphimc.viaproxy.proxy.client2proxy;
|
||||
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import net.raphimc.netminecraft.constants.MCPipeline;
|
||||
import net.raphimc.netminecraft.netty.connection.MinecraftChannelInitializer;
|
||||
import net.raphimc.netminecraft.packet.registry.PacketRegistryUtil;
|
||||
import net.raphimc.viaproxy.plugins.PluginManager;
|
||||
import net.raphimc.viaproxy.plugins.events.Client2ProxyChannelInitializeEvent;
|
||||
import net.raphimc.viaproxy.plugins.events.types.ITyped;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class Client2ProxyChannelInitializer extends MinecraftChannelInitializer {
|
||||
|
||||
public Client2ProxyChannelInitializer(final Supplier<ChannelHandler> handlerSupplier) {
|
||||
super(handlerSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initChannel(SocketChannel socketChannel) {
|
||||
if (PluginManager.EVENT_MANAGER.call(new Client2ProxyChannelInitializeEvent(ITyped.Type.PRE, socketChannel)).isCancelled()) {
|
||||
socketChannel.close();
|
||||
return;
|
||||
}
|
||||
|
||||
super.initChannel(socketChannel);
|
||||
socketChannel.attr(MCPipeline.PACKET_REGISTRY_ATTRIBUTE_KEY).set(PacketRegistryUtil.getHandshakeRegistry(false));
|
||||
|
||||
if (PluginManager.EVENT_MANAGER.call(new Client2ProxyChannelInitializeEvent(ITyped.Type.POST, socketChannel)).isCancelled()) {
|
||||
socketChannel.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,292 @@
|
||||
package net.raphimc.viaproxy.proxy.client2proxy;
|
||||
|
||||
import com.github.steveice10.mc.auth.data.GameProfile;
|
||||
import com.github.steveice10.mc.auth.util.UUIDSerializer;
|
||||
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import net.raphimc.netminecraft.constants.*;
|
||||
import net.raphimc.netminecraft.netty.crypto.AESEncryption;
|
||||
import net.raphimc.netminecraft.netty.crypto.CryptUtil;
|
||||
import net.raphimc.netminecraft.packet.*;
|
||||
import net.raphimc.netminecraft.packet.impl.handshake.C2SHandshakePacket;
|
||||
import net.raphimc.netminecraft.packet.impl.login.*;
|
||||
import net.raphimc.netminecraft.util.ServerAddress;
|
||||
import net.raphimc.vialegacy.util.VersionEnum;
|
||||
import net.raphimc.viaproxy.ViaProxy;
|
||||
import net.raphimc.viaproxy.cli.options.Options;
|
||||
import net.raphimc.viaproxy.plugins.PluginManager;
|
||||
import net.raphimc.viaproxy.plugins.events.PreConnectEvent;
|
||||
import net.raphimc.viaproxy.protocolhack.providers.ViaProxyGameProfileFetcher;
|
||||
import net.raphimc.viaproxy.proxy.*;
|
||||
import net.raphimc.viaproxy.proxy.proxy2server.Proxy2ServerChannelInitializer;
|
||||
import net.raphimc.viaproxy.proxy.proxy2server.Proxy2ServerHandler;
|
||||
import net.raphimc.viaproxy.proxy.util.CloseAndReturn;
|
||||
import net.raphimc.viaproxy.proxy.util.ExceptionUtil;
|
||||
import net.raphimc.viaproxy.util.ArrayHelper;
|
||||
import net.raphimc.viaproxy.util.LocalSocketClient;
|
||||
import net.raphimc.viaproxy.util.logging.Logger;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.*;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class Client2ProxyHandler extends SimpleChannelInboundHandler<IPacket> {
|
||||
|
||||
private static final KeyPair KEY_PAIR;
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
static {
|
||||
if (Options.ONLINE_MODE) {
|
||||
KEY_PAIR = CryptUtil.generateKeyPair();
|
||||
} else {
|
||||
KEY_PAIR = null;
|
||||
}
|
||||
}
|
||||
|
||||
private ProxyConnection proxyConnection;
|
||||
private LoginState loginState = LoginState.FIRST_PACKET;
|
||||
|
||||
private final byte[] verifyToken = new byte[4];
|
||||
private int customPayloadPacketId = -1;
|
||||
private int chatSessionUpdatePacketId = -1;
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
super.channelActive(ctx);
|
||||
|
||||
if (Options.ONLINE_MODE) RANDOM.nextBytes(this.verifyToken);
|
||||
this.proxyConnection = new ProxyConnection(Proxy2ServerHandler::new, Proxy2ServerChannelInitializer::new, (SocketChannel) ctx.channel());
|
||||
ctx.channel().attr(ProxyConnection.PROXY_CONNECTION_ATTRIBUTE_KEY).set(this.proxyConnection);
|
||||
|
||||
ViaProxy.c2pChannels.add(ctx.channel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
super.channelInactive(ctx);
|
||||
|
||||
try {
|
||||
this.proxyConnection.getChannel().close();
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, IPacket packet) throws Exception {
|
||||
if (this.proxyConnection.isClosed()) return;
|
||||
|
||||
switch (this.proxyConnection.getConnectionState()) {
|
||||
case HANDSHAKING:
|
||||
if (packet instanceof C2SHandshakePacket) this.handleHandshake((C2SHandshakePacket) packet);
|
||||
else break;
|
||||
|
||||
return;
|
||||
case LOGIN:
|
||||
if (packet instanceof C2SLoginHelloPacket1_7) this.handleLoginHello((C2SLoginHelloPacket1_7) packet);
|
||||
else if (packet instanceof C2SLoginKeyPacket1_7) this.handleLoginKey((C2SLoginKeyPacket1_7) packet);
|
||||
else if (packet instanceof C2SLoginCustomPayloadPacket) this.handleLoginCustomPayload((C2SLoginCustomPayloadPacket) packet);
|
||||
else break;
|
||||
|
||||
return;
|
||||
case PLAY:
|
||||
final UnknownPacket unknownPacket = (UnknownPacket) packet;
|
||||
if (unknownPacket.packetId == this.customPayloadPacketId) {
|
||||
if (this.handlePlayCustomPayload(Unpooled.wrappedBuffer(unknownPacket.data))) return;
|
||||
} else if (unknownPacket.packetId == this.chatSessionUpdatePacketId && this.proxyConnection.getChannel().attr(MCPipeline.ENCRYPTION_ATTRIBUTE_KEY).get() == null) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
this.proxyConnection.getChannel().writeAndFlush(packet).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
ExceptionUtil.handleNettyException(ctx, cause, this.proxyConnection);
|
||||
}
|
||||
|
||||
private void handleHandshake(final C2SHandshakePacket packet) throws InterruptedException {
|
||||
String address = packet.address.split("\0")[0];
|
||||
final VersionEnum clientVersion = VersionEnum.fromProtocolVersion(ProtocolVersion.getProtocol(packet.protocolVersion));
|
||||
|
||||
if (ConnectionState.LOGIN.equals(packet.intendedState)) {
|
||||
if (clientVersion == VersionEnum.UNKNOWN) throw CloseAndReturn.INSTANCE;
|
||||
} else if (!ConnectionState.STATUS.equals(packet.intendedState)) {
|
||||
throw CloseAndReturn.INSTANCE;
|
||||
}
|
||||
this.proxyConnection.setClientVersion(clientVersion);
|
||||
this.proxyConnection.setConnectionState(packet.intendedState);
|
||||
|
||||
this.customPayloadPacketId = MCPackets.C2S_PLUGIN_MESSAGE.getId(clientVersion.getVersion());
|
||||
this.chatSessionUpdatePacketId = MCPackets.C2S_CHAT_SESSION_UPDATE.getId(clientVersion.getVersion());
|
||||
|
||||
String connectIP = Options.CONNECT_ADDRESS;
|
||||
int connectPort = Options.CONNECT_PORT;
|
||||
VersionEnum serverVersion = Options.PROTOCOL_VERSION;
|
||||
|
||||
if (Options.INTERNAL_SRV_MODE) {
|
||||
final ArrayHelper arrayHelper = ArrayHelper.instanceOf(address.split("\7"));
|
||||
connectIP = arrayHelper.get(0);
|
||||
connectPort = arrayHelper.getInteger(1);
|
||||
final String versionString = arrayHelper.get(2);
|
||||
if (arrayHelper.isIndexValid(3)) {
|
||||
this.proxyConnection.setClassicMpPass(arrayHelper.getString(3));
|
||||
}
|
||||
for (VersionEnum v : VersionEnum.getAllVersions()) {
|
||||
if (v.getName().equalsIgnoreCase(versionString)) {
|
||||
serverVersion = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (serverVersion == null) throw CloseAndReturn.INSTANCE;
|
||||
} else if (Options.SRV_MODE) {
|
||||
try {
|
||||
if (address.toLowerCase().contains(".viaproxy.")) {
|
||||
address = address.substring(0, address.toLowerCase().lastIndexOf(".viaproxy."));
|
||||
} else {
|
||||
throw CloseAndReturn.INSTANCE;
|
||||
}
|
||||
final ArrayHelper arrayHelper = ArrayHelper.instanceOf(address.split(Pattern.quote("_")));
|
||||
if (arrayHelper.getLength() < 3) {
|
||||
throw CloseAndReturn.INSTANCE;
|
||||
}
|
||||
connectIP = arrayHelper.getAsString(0, arrayHelper.getLength() - 3, "_");
|
||||
connectPort = arrayHelper.getInteger(arrayHelper.getLength() - 2);
|
||||
final String versionString = arrayHelper.get(arrayHelper.getLength() - 1);
|
||||
for (VersionEnum v : VersionEnum.getAllVersions()) {
|
||||
if (v.getName().replace(" ", "-").equalsIgnoreCase(versionString)) {
|
||||
serverVersion = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (serverVersion == null) throw CloseAndReturn.INSTANCE;
|
||||
} catch (CloseAndReturn e) {
|
||||
this.proxyConnection.kickClient("§cWrong SRV syntax! §6Please use:\n§7ip_port_version.viaproxy.hostname");
|
||||
}
|
||||
}
|
||||
|
||||
final ServerAddress serverAddress;
|
||||
if (serverVersion.isOlderThan(VersionEnum.r1_3_1tor1_3_2)) {
|
||||
serverAddress = new ServerAddress(connectIP, connectPort);
|
||||
} else {
|
||||
serverAddress = ServerAddress.fromSRV(connectIP + ":" + connectPort);
|
||||
}
|
||||
|
||||
final PreConnectEvent preConnectEvent = new PreConnectEvent(serverAddress, serverVersion, clientVersion, this.proxyConnection.getC2P());
|
||||
if (PluginManager.EVENT_MANAGER.call(preConnectEvent).isCancelled()) {
|
||||
this.proxyConnection.kickClient(preConnectEvent.getCancelMessage());
|
||||
}
|
||||
|
||||
Logger.u_info("connect", this.proxyConnection.getC2P().remoteAddress(), this.proxyConnection.getGameProfile(), "[" + clientVersion.getName() + " <-> " + serverVersion.getName() + "] Connecting to " + serverAddress.getAddress() + ":" + serverAddress.getPort());
|
||||
try {
|
||||
this.proxyConnection.connectToServer(serverAddress, serverVersion);
|
||||
this.proxyConnection.getChannel().writeAndFlush(new C2SHandshakePacket(clientVersion.getOriginalVersion(), serverAddress.getAddress(), serverAddress.getPort(), packet.intendedState)).await().addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
this.proxyConnection.setConnectionState(packet.intendedState);
|
||||
} catch (Throwable e) {
|
||||
this.proxyConnection.kickClient("§cCould not connect to the backend server!\n§cTry again in a few seconds.");
|
||||
}
|
||||
}
|
||||
|
||||
private void handleLoginHello(C2SLoginHelloPacket1_7 packet) throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
if (this.loginState != LoginState.FIRST_PACKET) throw CloseAndReturn.INSTANCE;
|
||||
this.loginState = LoginState.SENT_HELLO;
|
||||
|
||||
if (packet instanceof C2SLoginHelloPacket1_19) {
|
||||
final C2SLoginHelloPacket1_19 packet1_19 = (C2SLoginHelloPacket1_19) packet;
|
||||
if (packet1_19.expiresAt != null && packet1_19.expiresAt.isBefore(Instant.now())) {
|
||||
throw new IllegalStateException("Expired public key");
|
||||
}
|
||||
}
|
||||
|
||||
this.proxyConnection.setLoginHelloPacket(packet);
|
||||
this.proxyConnection.setGameProfile(new GameProfile((UUID) null, packet.name));
|
||||
|
||||
if (Options.ONLINE_MODE) {
|
||||
this.proxyConnection.getC2P().writeAndFlush(new S2CLoginKeyPacket1_8("", KEY_PAIR.getPublic().getEncoded(), this.verifyToken)).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
} else {
|
||||
if (Options.LOCAL_SOCKET_AUTH) {
|
||||
String[] response = new LocalSocketClient(48941).request("getusername");
|
||||
if (response != null && response[0].equals("success")) {
|
||||
this.proxyConnection.setGameProfile(new GameProfile((UUID) null, response[1]));
|
||||
}
|
||||
response = new LocalSocketClient(48941).request("get_public_key_data");
|
||||
if (response != null && response[0].equals("success")) {
|
||||
final UUID uuid = UUIDSerializer.fromString(response[1].replaceFirst("(\\p{XDigit}{8})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}+)", "$1-$2-$3-$4-$5"));
|
||||
final PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(response[2])));
|
||||
packet = new C2SLoginHelloPacket1_19_3(packet.name, Instant.ofEpochMilli(Long.parseLong(response[4])), publicKey, Base64.getDecoder().decode(response[3]), uuid);
|
||||
}
|
||||
}
|
||||
|
||||
this.proxyConnection.getChannel().writeAndFlush(packet).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleLoginKey(final C2SLoginKeyPacket1_7 packet) throws GeneralSecurityException, InterruptedException {
|
||||
if (this.proxyConnection.getClientVersion().isOlderThanOrEqualTo(VersionEnum.r1_12_2) && new String(packet.encryptedNonce, StandardCharsets.UTF_8).equals(CustomPayloadInterface.OPENAUTHMOD_DATA_CHANNEL)) { // 1.8-1.12.2 OpenAuthMod response handling
|
||||
final ByteBuf byteBuf = Unpooled.wrappedBuffer(packet.encryptedSecretKey);
|
||||
this.proxyConnection.handleCustomPayload(PacketTypes.readVarInt(byteBuf), byteBuf);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.loginState != LoginState.SENT_HELLO) throw CloseAndReturn.INSTANCE;
|
||||
this.loginState = LoginState.SENT_KEY;
|
||||
|
||||
if (packet.encryptedNonce != null) {
|
||||
if (!Arrays.equals(this.verifyToken, CryptUtil.decryptData(KEY_PAIR.getPrivate(), packet.encryptedNonce))) {
|
||||
Logger.u_err("auth", this.proxyConnection.getC2P().remoteAddress(), this.proxyConnection.getGameProfile(), "Invalid verify token");
|
||||
this.proxyConnection.kickClient("§cInvalid verify token!");
|
||||
}
|
||||
} else {
|
||||
final C2SLoginKeyPacket1_19 keyPacket = (C2SLoginKeyPacket1_19) packet;
|
||||
final C2SLoginHelloPacket1_19 helloPacket = (C2SLoginHelloPacket1_19) this.proxyConnection.getLoginHelloPacket();
|
||||
if (helloPacket.key == null || !CryptUtil.verifySignedNonce(helloPacket.key, this.verifyToken, keyPacket.salt, keyPacket.signature)) {
|
||||
Logger.u_err("auth", this.proxyConnection.getC2P().remoteAddress(), this.proxyConnection.getGameProfile(), "Invalid verify token");
|
||||
this.proxyConnection.kickClient("§cInvalid verify token!");
|
||||
}
|
||||
}
|
||||
|
||||
final SecretKey secretKey = CryptUtil.decryptSecretKey(KEY_PAIR.getPrivate(), packet.encryptedSecretKey);
|
||||
this.proxyConnection.getC2P().attr(MCPipeline.ENCRYPTION_ATTRIBUTE_KEY).set(new AESEncryption(secretKey));
|
||||
|
||||
final String userName = this.proxyConnection.getGameProfile().getName();
|
||||
|
||||
try {
|
||||
final String serverHash = new BigInteger(CryptUtil.computeServerIdHash("", KEY_PAIR.getPublic(), secretKey)).toString(16);
|
||||
this.proxyConnection.setGameProfile(ViaProxyGameProfileFetcher.sessionService.getProfileByServer(userName, serverHash));
|
||||
if (this.proxyConnection.getGameProfile() == null) {
|
||||
Logger.u_err("auth", this.proxyConnection.getC2P().remoteAddress(), this.proxyConnection.getGameProfile(), "Invalid session");
|
||||
this.proxyConnection.kickClient("§cInvalid session! Please restart minecraft (and the launcher) and try again.");
|
||||
}
|
||||
Logger.u_info("auth", this.proxyConnection.getC2P().remoteAddress(), this.proxyConnection.getGameProfile(), "Authenticated as " + this.proxyConnection.getGameProfile().getIdAsString());
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("Failed to make session request for user '" + userName + "'!", e);
|
||||
}
|
||||
|
||||
this.proxyConnection.getChannel().writeAndFlush(this.proxyConnection.getLoginHelloPacket()).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
}
|
||||
|
||||
private void handleLoginCustomPayload(final C2SLoginCustomPayloadPacket packet) {
|
||||
if (packet.response == null || !this.proxyConnection.handleCustomPayload(packet.queryId, Unpooled.wrappedBuffer(packet.response))) {
|
||||
this.proxyConnection.getChannel().writeAndFlush(packet).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean handlePlayCustomPayload(final ByteBuf packet) {
|
||||
final String channel = PacketTypes.readString(packet, Short.MAX_VALUE); // channel
|
||||
if (channel.equals(CustomPayloadInterface.OPENAUTHMOD_DATA_CHANNEL)) {
|
||||
return this.proxyConnection.handleCustomPayload(PacketTypes.readVarInt(packet), packet);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package net.raphimc.viaproxy.proxy.proxy2server;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.connection.UserConnectionImpl;
|
||||
import com.viaversion.viaversion.protocol.ProtocolPipelineImpl;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import net.raphimc.netminecraft.constants.MCPipeline;
|
||||
import net.raphimc.netminecraft.netty.connection.MinecraftChannelInitializer;
|
||||
import net.raphimc.netminecraft.packet.registry.PacketRegistryUtil;
|
||||
import net.raphimc.vialegacy.netty.PreNettyDecoder;
|
||||
import net.raphimc.vialegacy.netty.PreNettyEncoder;
|
||||
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.baseprotocols.PreNettyBaseProtocol;
|
||||
import net.raphimc.vialegacy.util.VersionEnum;
|
||||
import net.raphimc.viaprotocolhack.netty.ViaEncodeHandler;
|
||||
import net.raphimc.viaprotocolhack.netty.ViaPipeline;
|
||||
import net.raphimc.viaproxy.plugins.PluginManager;
|
||||
import net.raphimc.viaproxy.plugins.events.Proxy2ServerChannelInitializeEvent;
|
||||
import net.raphimc.viaproxy.plugins.events.types.ITyped;
|
||||
import net.raphimc.viaproxy.protocolhack.impl.ViaProxyViaDecodeHandler;
|
||||
import net.raphimc.viaproxy.proxy.ProxyConnection;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class Proxy2ServerChannelInitializer extends MinecraftChannelInitializer {
|
||||
|
||||
public Proxy2ServerChannelInitializer(final Supplier<ChannelHandler> handlerSupplier) {
|
||||
super(handlerSupplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initChannel(SocketChannel socketChannel) {
|
||||
if (PluginManager.EVENT_MANAGER.call(new Proxy2ServerChannelInitializeEvent(ITyped.Type.PRE, socketChannel)).isCancelled()) {
|
||||
socketChannel.close();
|
||||
return;
|
||||
}
|
||||
|
||||
final UserConnection user = new UserConnectionImpl(socketChannel, true);
|
||||
new ProtocolPipelineImpl(user);
|
||||
ProxyConnection.fromChannel(socketChannel).setUserConnection(user);
|
||||
|
||||
super.initChannel(socketChannel);
|
||||
socketChannel.attr(MCPipeline.PACKET_REGISTRY_ATTRIBUTE_KEY).set(PacketRegistryUtil.getHandshakeRegistry(true));
|
||||
socketChannel.pipeline().addBefore(MCPipeline.PACKET_CODEC_HANDLER_NAME, ViaPipeline.HANDLER_ENCODER_NAME, new ViaEncodeHandler(user));
|
||||
socketChannel.pipeline().addBefore(MCPipeline.PACKET_CODEC_HANDLER_NAME, ViaPipeline.HANDLER_DECODER_NAME, new ViaProxyViaDecodeHandler(user));
|
||||
|
||||
if (ProxyConnection.fromChannel(socketChannel).getServerVersion().isOlderThanOrEqualTo(VersionEnum.r1_6_4)) {
|
||||
user.getProtocolInfo().getPipeline().add(PreNettyBaseProtocol.INSTANCE);
|
||||
socketChannel.pipeline().addBefore(MCPipeline.SIZER_HANDLER_NAME, ViaPipeline.HANDLER_PRE_NETTY_ENCODER_NAME, new PreNettyEncoder(user));
|
||||
socketChannel.pipeline().addBefore(MCPipeline.SIZER_HANDLER_NAME, ViaPipeline.HANDLER_PRE_NETTY_DECODER_NAME, new PreNettyDecoder(user));
|
||||
}
|
||||
|
||||
if (PluginManager.EVENT_MANAGER.call(new Proxy2ServerChannelInitializeEvent(ITyped.Type.POST, socketChannel)).isCancelled()) {
|
||||
socketChannel.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
package net.raphimc.viaproxy.proxy.proxy2server;
|
||||
|
||||
import com.github.steveice10.mc.auth.data.GameProfile;
|
||||
import io.netty.channel.*;
|
||||
import net.raphimc.netminecraft.constants.ConnectionState;
|
||||
import net.raphimc.netminecraft.constants.MCPipeline;
|
||||
import net.raphimc.netminecraft.netty.crypto.AESEncryption;
|
||||
import net.raphimc.netminecraft.netty.crypto.CryptUtil;
|
||||
import net.raphimc.netminecraft.packet.IPacket;
|
||||
import net.raphimc.netminecraft.packet.impl.login.*;
|
||||
import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.storage.ProtocolMetadataStorage;
|
||||
import net.raphimc.vialegacy.util.VersionEnum;
|
||||
import net.raphimc.viaproxy.cli.options.Options;
|
||||
import net.raphimc.viaproxy.proxy.CustomPayloadInterface;
|
||||
import net.raphimc.viaproxy.proxy.ProxyConnection;
|
||||
import net.raphimc.viaproxy.proxy.util.ExceptionUtil;
|
||||
import net.raphimc.viaproxy.util.logging.Logger;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.math.BigInteger;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PublicKey;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class Proxy2ServerHandler extends SimpleChannelInboundHandler<IPacket> {
|
||||
|
||||
private ProxyConnection proxyConnection;
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception {
|
||||
super.channelActive(ctx);
|
||||
|
||||
this.proxyConnection = ProxyConnection.fromChannel(ctx.channel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
super.channelInactive(ctx);
|
||||
|
||||
Logger.u_info("disconnect", this.proxyConnection.getC2P().remoteAddress(), this.proxyConnection.getGameProfile(), "Connection closed");
|
||||
try {
|
||||
this.proxyConnection.getC2P().close();
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, IPacket packet) throws Exception {
|
||||
if (this.proxyConnection.isClosed()) return;
|
||||
|
||||
switch (this.proxyConnection.getConnectionState()) {
|
||||
case LOGIN:
|
||||
if (packet instanceof S2CLoginKeyPacket1_7) this.handleLoginKey((S2CLoginKeyPacket1_7) packet);
|
||||
else if (packet instanceof S2CLoginSuccessPacket1_7) this.handleLoginSuccess((S2CLoginSuccessPacket1_7) packet);
|
||||
else if (packet instanceof S2CLoginCompressionPacket) this.handleLoginCompression((S2CLoginCompressionPacket) packet);
|
||||
else break;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.proxyConnection.getC2P().writeAndFlush(packet).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
||||
ExceptionUtil.handleNettyException(ctx, cause, this.proxyConnection);
|
||||
}
|
||||
|
||||
private void handleLoginKey(final S2CLoginKeyPacket1_7 packet) throws InterruptedException, GeneralSecurityException, ExecutionException {
|
||||
final PublicKey publicKey = CryptUtil.decodeRsaPublicKey(packet.publicKey);
|
||||
final SecretKey secretKey = CryptUtil.generateSecretKey();
|
||||
final String serverHash = new BigInteger(CryptUtil.computeServerIdHash(packet.serverId, publicKey, secretKey)).toString(16);
|
||||
|
||||
boolean auth = true;
|
||||
if (this.proxyConnection.getServerVersion().isOlderThanOrEqualTo(VersionEnum.r1_6_4)) {
|
||||
auth = this.proxyConnection.getUserConnection().get(ProtocolMetadataStorage.class).authenticate;
|
||||
}
|
||||
if (auth) {
|
||||
CustomPayloadInterface.joinServer(serverHash, this.proxyConnection);
|
||||
}
|
||||
|
||||
final byte[] encryptedSecretKey = CryptUtil.encryptData(publicKey, secretKey.getEncoded());
|
||||
final byte[] encryptedNonce = CryptUtil.encryptData(publicKey, packet.nonce);
|
||||
|
||||
final C2SLoginKeyPacket1_19_3 loginKey = new C2SLoginKeyPacket1_19_3(encryptedSecretKey, encryptedNonce);
|
||||
if (this.proxyConnection.getLoginHelloPacket() instanceof C2SLoginHelloPacket1_19 && ((C2SLoginHelloPacket1_19) this.proxyConnection.getLoginHelloPacket()).key != null) {
|
||||
CustomPayloadInterface.signNonce(packet.nonce, loginKey, this.proxyConnection);
|
||||
}
|
||||
this.proxyConnection.getChannel().writeAndFlush(loginKey).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
|
||||
if (this.proxyConnection.getServerVersion().isNewerThanOrEqualTo(VersionEnum.r1_7_2tor1_7_5)) {
|
||||
this.proxyConnection.getChannel().attr(MCPipeline.ENCRYPTION_ATTRIBUTE_KEY).set(new AESEncryption(secretKey));
|
||||
} else {
|
||||
this.proxyConnection.setKeyForPreNettyEncryption(secretKey);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleLoginSuccess(final S2CLoginSuccessPacket1_7 packet) throws Exception {
|
||||
if (this.proxyConnection.getClientVersion().isNewerThanOrEqualTo(VersionEnum.r1_8)) {
|
||||
if (Options.COMPRESSION_THRESHOLD > -1) {
|
||||
this.proxyConnection.getC2P().writeAndFlush(new S2CLoginCompressionPacket(Options.COMPRESSION_THRESHOLD)).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE).await();
|
||||
this.proxyConnection.getC2P().attr(MCPipeline.COMPRESSION_THRESHOLD_ATTRIBUTE_KEY).set(Options.COMPRESSION_THRESHOLD);
|
||||
}
|
||||
}
|
||||
|
||||
this.proxyConnection.setGameProfile(new GameProfile(packet.uuid, packet.name));
|
||||
|
||||
Logger.u_info("connect", this.proxyConnection.getC2P().remoteAddress(), this.proxyConnection.getGameProfile(), "Connected successfully! Switching to PLAY state");
|
||||
this.proxyConnection.getC2P().writeAndFlush(packet).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE).await();
|
||||
this.proxyConnection.setConnectionState(ConnectionState.PLAY);
|
||||
}
|
||||
|
||||
private void handleLoginCompression(final S2CLoginCompressionPacket packet) {
|
||||
this.proxyConnection.getChannel().attr(MCPipeline.COMPRESSION_THRESHOLD_ATTRIBUTE_KEY).set(packet.compressionThreshold);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package net.raphimc.viaproxy.proxy.util;
|
||||
|
||||
public class CloseAndReturn extends RuntimeException {
|
||||
|
||||
public static final CloseAndReturn INSTANCE = new CloseAndReturn();
|
||||
|
||||
CloseAndReturn() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Throwable fillInStackTrace() {
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package net.raphimc.viaproxy.proxy.util;
|
||||
|
||||
import com.viaversion.viaversion.exception.InformativeException;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.EncoderException;
|
||||
import net.raphimc.viaproxy.proxy.ProxyConnection;
|
||||
import net.raphimc.viaproxy.util.logging.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
public class ExceptionUtil {
|
||||
|
||||
private static Field infoField;
|
||||
|
||||
static {
|
||||
try {
|
||||
infoField = InformativeException.class.getDeclaredField("info");
|
||||
infoField.setAccessible(true);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleNettyException(ChannelHandlerContext ctx, Throwable cause, ProxyConnection proxyConnection) {
|
||||
if (!ctx.channel().isOpen() || !ctx.channel().isActive()) return;
|
||||
if (cause instanceof ClosedChannelException) return;
|
||||
if (cause instanceof IOException) return;
|
||||
if (cause instanceof CloseAndReturn) {
|
||||
ctx.channel().close();
|
||||
return;
|
||||
}
|
||||
Logger.LOGGER.error("Caught unhandled netty exception", cause);
|
||||
try {
|
||||
proxyConnection.kickClient("§cAn unhandled error occurred in your connection and it has been closed.\n§aError details for report:§f" + ExceptionUtil.prettyPrint(cause));
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
ctx.channel().close();
|
||||
}
|
||||
|
||||
public static String prettyPrint(Throwable t) {
|
||||
StringBuilder msg = new StringBuilder();
|
||||
if (t instanceof EncoderException) t = t.getCause();
|
||||
while (t != null) {
|
||||
String exceptionMessage = t.getMessage();
|
||||
if (t instanceof InformativeException) {
|
||||
exceptionMessage = getMessageFor((InformativeException) t);
|
||||
}
|
||||
msg.append("\n");
|
||||
msg.append("§c").append(t.getClass().getSimpleName()).append("§7: §f").append(exceptionMessage);
|
||||
t = t.getCause();
|
||||
if (t != null) {
|
||||
msg.append(" §9Caused by");
|
||||
}
|
||||
}
|
||||
return msg.toString();
|
||||
}
|
||||
|
||||
private static String getMessageFor(final InformativeException e) {
|
||||
Map<String, Object> info = null;
|
||||
try {
|
||||
info = (Map<String, Object>) infoField.get(e);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
if (info != null) {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
boolean first = true;
|
||||
for (Iterator<Map.Entry<String, Object>> var3 = info.entrySet().iterator(); var3.hasNext(); first = false) {
|
||||
Map.Entry<String, Object> entry = var3.next();
|
||||
if (!first) {
|
||||
builder.append(", ");
|
||||
}
|
||||
builder.append(entry.getKey()).append(": ").append(entry.getValue());
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
return "Unable to get source info";
|
||||
}
|
||||
|
||||
}
|
238
src/main/java/net/raphimc/viaproxy/ui/ViaProxyUI.java
Normal file
238
src/main/java/net/raphimc/viaproxy/ui/ViaProxyUI.java
Normal file
@ -0,0 +1,238 @@
|
||||
package net.raphimc.viaproxy.ui;
|
||||
|
||||
import com.formdev.flatlaf.FlatDarkLaf;
|
||||
import com.google.common.net.HostAndPort;
|
||||
import net.raphimc.vialegacy.util.VersionEnum;
|
||||
import net.raphimc.viaproxy.ViaProxy;
|
||||
import net.raphimc.viaproxy.cli.options.Options;
|
||||
import net.raphimc.viaproxy.util.logging.Logger;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class ViaProxyUI extends JFrame {
|
||||
|
||||
private final JPanel contentPane = new JPanel();
|
||||
|
||||
private ImageIcon icon;
|
||||
|
||||
private JTextField serverAddress;
|
||||
private JComboBox<VersionEnum> serverVersion;
|
||||
private JSpinner bindPort;
|
||||
private JComboBox<String> authMethod;
|
||||
private JCheckBox betaCraftAuth;
|
||||
private JLabel stateLabel;
|
||||
private JButton stateButton;
|
||||
|
||||
public ViaProxyUI() {
|
||||
this.applyDarkFlatLafTheme();
|
||||
this.loadIcons();
|
||||
this.initWindow();
|
||||
this.initElements();
|
||||
|
||||
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
|
||||
Logger.LOGGER.error("Caught exception in thread " + t.getName(), e);
|
||||
final StringBuilder builder = new StringBuilder("An error occurred:\n");
|
||||
builder.append("[").append(e.getClass().getSimpleName()).append("] ").append(e.getMessage()).append("\n");
|
||||
for (StackTraceElement element : e.getStackTrace()) {
|
||||
builder.append("\tat ").append(element.toString()).append("\n");
|
||||
}
|
||||
this.showError(builder.toString());
|
||||
});
|
||||
|
||||
SwingUtilities.updateComponentTreeUI(this);
|
||||
this.setVisible(true);
|
||||
}
|
||||
|
||||
private void applyDarkFlatLafTheme() {
|
||||
try {
|
||||
UIManager.setLookAndFeel(new FlatDarkLaf());
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void loadIcons() {
|
||||
this.icon = new ImageIcon(this.getClass().getClassLoader().getResource("assets/icons/icon.png"));
|
||||
}
|
||||
|
||||
private void initWindow() {
|
||||
this.setTitle("ViaProxy");
|
||||
this.setIconImage(this.icon.getImage());
|
||||
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
this.setSize(500, 370);
|
||||
this.setResizable(false);
|
||||
this.setLocationRelativeTo(null);
|
||||
this.setContentPane(this.contentPane);
|
||||
}
|
||||
|
||||
private void initElements() {
|
||||
this.contentPane.setLayout(null);
|
||||
{
|
||||
JLabel titleLabel = new JLabel("ViaProxy");
|
||||
titleLabel.setBounds(0, 0, 500, 50);
|
||||
titleLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
titleLabel.setFont(titleLabel.getFont().deriveFont(30F));
|
||||
this.contentPane.add(titleLabel);
|
||||
}
|
||||
{
|
||||
JLabel addressLabel = new JLabel("Server Address:");
|
||||
addressLabel.setBounds(10, 50, 100, 20);
|
||||
this.contentPane.add(addressLabel);
|
||||
|
||||
this.serverAddress = new JTextField();
|
||||
this.serverAddress.setBounds(10, 70, 465, 20);
|
||||
this.contentPane.add(this.serverAddress);
|
||||
}
|
||||
{
|
||||
JLabel serverVersionLabel = new JLabel("Server Version:");
|
||||
serverVersionLabel.setBounds(10, 100, 100, 20);
|
||||
this.contentPane.add(serverVersionLabel);
|
||||
|
||||
this.serverVersion = new JComboBox<>(VersionEnum.RENDER_VERSIONS.toArray(new VersionEnum[0]));
|
||||
this.serverVersion.setBounds(10, 120, 465, 20);
|
||||
this.serverVersion.setRenderer(new DefaultListCellRenderer() {
|
||||
@Override
|
||||
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
|
||||
if (value instanceof VersionEnum) {
|
||||
VersionEnum version = (VersionEnum) value;
|
||||
value = version.getName();
|
||||
}
|
||||
return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||
}
|
||||
});
|
||||
this.contentPane.add(this.serverVersion);
|
||||
}
|
||||
{
|
||||
JLabel bindPortLabel = new JLabel("Bind Port:");
|
||||
bindPortLabel.setBounds(10, 150, 100, 20);
|
||||
this.contentPane.add(bindPortLabel);
|
||||
|
||||
this.bindPort = new JSpinner(new SpinnerNumberModel(25568, 1, 65535, 1));
|
||||
this.bindPort.setBounds(10, 170, 465, 20);
|
||||
this.bindPort.setEditor(new JSpinner.NumberEditor(this.bindPort, "#"));
|
||||
((JSpinner.DefaultEditor) this.bindPort.getEditor()).getTextField().setHorizontalAlignment(SwingConstants.LEFT);
|
||||
this.contentPane.add(this.bindPort);
|
||||
}
|
||||
{
|
||||
JLabel authMethodLabel = new JLabel("Auth Method:");
|
||||
authMethodLabel.setBounds(10, 200, 100, 20);
|
||||
this.contentPane.add(authMethodLabel);
|
||||
|
||||
this.authMethod = new JComboBox<>(new String[]{"OpenAuthMod"});
|
||||
this.authMethod.setBounds(10, 220, 465, 20);
|
||||
this.contentPane.add(this.authMethod);
|
||||
}
|
||||
{
|
||||
this.betaCraftAuth = new JCheckBox("BetaCraft Auth (Classic)");
|
||||
this.betaCraftAuth.setBounds(10, 250, 465, 20);
|
||||
this.contentPane.add(this.betaCraftAuth);
|
||||
}
|
||||
{
|
||||
this.stateLabel = new JLabel();
|
||||
this.stateLabel.setBounds(14, 280, 465, 20);
|
||||
this.stateLabel.setVisible(false);
|
||||
this.contentPane.add(this.stateLabel);
|
||||
}
|
||||
{
|
||||
this.stateButton = new JButton("Loading ViaProxy...");
|
||||
this.stateButton.setBounds(10, 300, 465, 20);
|
||||
this.stateButton.addActionListener(e -> {
|
||||
if (this.stateButton.getText().equalsIgnoreCase("Start")) this.start();
|
||||
else if (this.stateButton.getText().equalsIgnoreCase("Stop")) this.stop();
|
||||
});
|
||||
this.stateButton.setEnabled(false);
|
||||
this.contentPane.add(this.stateButton);
|
||||
}
|
||||
}
|
||||
|
||||
private void setComponentsEnabled(final boolean state) {
|
||||
this.serverAddress.setEnabled(state);
|
||||
this.serverVersion.setEnabled(state);
|
||||
this.bindPort.setEnabled(state);
|
||||
this.authMethod.setEnabled(state);
|
||||
this.betaCraftAuth.setEnabled(state);
|
||||
}
|
||||
|
||||
private void updateStateLabel() {
|
||||
this.stateLabel.setText("ViaProxy is running! Connect with Minecraft 1.7+ to 127.0.0.1:" + this.bindPort.getValue());
|
||||
this.stateLabel.setVisible(true);
|
||||
}
|
||||
|
||||
private void start() {
|
||||
this.setComponentsEnabled(false);
|
||||
this.stateButton.setEnabled(false);
|
||||
this.stateButton.setText("Starting...");
|
||||
|
||||
new Thread(() -> {
|
||||
final String serverAddress = this.serverAddress.getText();
|
||||
final VersionEnum serverVersion = (VersionEnum) this.serverVersion.getSelectedItem();
|
||||
final int bindPort = (int) this.bindPort.getValue();
|
||||
final String authMethod = (String) this.authMethod.getSelectedItem();
|
||||
final boolean betaCraftAuth = this.betaCraftAuth.isSelected();
|
||||
|
||||
try {
|
||||
final HostAndPort hostAndPort = HostAndPort.fromString(serverAddress);
|
||||
|
||||
Options.BIND_ADDRESS = "127.0.0.1";
|
||||
Options.BIND_PORT = bindPort;
|
||||
Options.CONNECT_ADDRESS = hostAndPort.getHost();
|
||||
Options.CONNECT_PORT = hostAndPort.getPortOrDefault(25565);
|
||||
Options.PROTOCOL_VERSION = serverVersion;
|
||||
|
||||
Options.OPENAUTHMOD_AUTH = true;
|
||||
Options.BETACRAFT_AUTH = betaCraftAuth;
|
||||
|
||||
ViaProxy.startProxy();
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
this.updateStateLabel();
|
||||
this.stateButton.setEnabled(true);
|
||||
this.stateButton.setText("Stop");
|
||||
});
|
||||
} catch (Throwable e) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
this.showError("Invalid server address!");
|
||||
this.setComponentsEnabled(true);
|
||||
this.stateButton.setEnabled(true);
|
||||
this.stateButton.setText("Start");
|
||||
this.stateLabel.setVisible(false);
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void stop() {
|
||||
ViaProxy.stopProxy();
|
||||
|
||||
this.stateLabel.setVisible(false);
|
||||
this.stateButton.setText("Start");
|
||||
this.setComponentsEnabled(true);
|
||||
}
|
||||
|
||||
|
||||
public void setReady() {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
this.stateButton.setText("Start");
|
||||
this.stateButton.setEnabled(true);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void showInfo(final String message) {
|
||||
this.showNotification(message, JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
|
||||
public void showWarning(final String message) {
|
||||
this.showNotification(message, JOptionPane.WARNING_MESSAGE);
|
||||
}
|
||||
|
||||
public void showError(final String message) {
|
||||
this.showNotification(message, JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
public void showNotification(final String message, final int type) {
|
||||
JOptionPane.showMessageDialog(this, message, "ViaProxy", type);
|
||||
}
|
||||
|
||||
}
|
353
src/main/java/net/raphimc/viaproxy/util/ArrayHelper.java
Normal file
353
src/main/java/net/raphimc/viaproxy/util/ArrayHelper.java
Normal file
@ -0,0 +1,353 @@
|
||||
package net.raphimc.viaproxy.util;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
|
||||
public class ArrayHelper {
|
||||
|
||||
public static ArrayHelper instanceOf(final String... array) {
|
||||
return new ArrayHelper(array);
|
||||
}
|
||||
|
||||
|
||||
private String[] array;
|
||||
|
||||
public ArrayHelper(final String[] array) {
|
||||
this.array = array;
|
||||
}
|
||||
|
||||
public int getLength() {
|
||||
return this.array.length;
|
||||
}
|
||||
|
||||
public boolean isLength(final int length) {
|
||||
return this.getLength() == length;
|
||||
}
|
||||
|
||||
public boolean isSmaller(final int length) {
|
||||
return this.getLength() < length;
|
||||
}
|
||||
|
||||
public boolean isSmallerOrEqual(final int length) {
|
||||
return this.getLength() <= length;
|
||||
}
|
||||
|
||||
public boolean isLarger(final int length) {
|
||||
return this.getLength() > length;
|
||||
}
|
||||
|
||||
public boolean isLargerOrEqual(final int length) {
|
||||
return this.getLength() >= length;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return this.getLength() == 0;
|
||||
}
|
||||
|
||||
public boolean isIndexValid(final int index) {
|
||||
return index >= 0 && index < this.getLength();
|
||||
}
|
||||
|
||||
public String get(final int index) {
|
||||
if (!this.isIndexValid(index)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.array[index];
|
||||
}
|
||||
|
||||
|
||||
public boolean isString(final int index) {
|
||||
return this.isIndexValid(index);
|
||||
}
|
||||
|
||||
public boolean isBoolean(final int index) {
|
||||
if (!this.isIndexValid(index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
Boolean.valueOf(this.getString(index));
|
||||
return true;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isChar(final int index) {
|
||||
if (!this.isIndexValid(index) || !this.isString(index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.getString(index).length() == 1;
|
||||
}
|
||||
|
||||
public boolean isShort(final int index) {
|
||||
if (!this.isIndexValid(index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
Short.valueOf(this.get(index));
|
||||
return true;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isInteger(final int index) {
|
||||
if (!this.isIndexValid(index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
Integer.valueOf(this.get(index));
|
||||
return true;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isLong(final int index) {
|
||||
if (!this.isIndexValid(index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
Long.valueOf(this.get(index));
|
||||
return true;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isFloat(final int index) {
|
||||
if (!this.isIndexValid(index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
Float.valueOf(this.get(index));
|
||||
return true;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isDouble(final int index) {
|
||||
if (!this.isIndexValid(index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
Double.valueOf(this.get(index));
|
||||
return true;
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public String getString(final int index, final String standart) {
|
||||
if (!this.isIndexValid(index) || !this.isString(index)) {
|
||||
return standart;
|
||||
}
|
||||
|
||||
return this.get(index);
|
||||
}
|
||||
|
||||
public boolean getBoolean(final int index, final boolean standart) {
|
||||
if (!this.isIndexValid(index) || !this.isBoolean(index)) {
|
||||
return standart;
|
||||
}
|
||||
|
||||
return Boolean.parseBoolean(this.getString(index));
|
||||
}
|
||||
|
||||
public char getChar(final int index, final char standart) {
|
||||
if (!this.isIndexValid(index) || !this.isChar(index)) {
|
||||
return standart;
|
||||
}
|
||||
|
||||
return this.getString(index, String.valueOf(standart)).charAt(0);
|
||||
}
|
||||
|
||||
public short getShort(final int index, final short standart) {
|
||||
if (!this.isIndexValid(index) || !this.isShort(index)) {
|
||||
return standart;
|
||||
}
|
||||
|
||||
return Short.parseShort(this.get(index));
|
||||
}
|
||||
|
||||
public int getInteger(final int index, final int standart) {
|
||||
if (!this.isIndexValid(index) || !this.isInteger(index)) {
|
||||
return standart;
|
||||
}
|
||||
|
||||
return Integer.parseInt(this.get(index));
|
||||
}
|
||||
|
||||
public long getLong(final int index, final long standart) {
|
||||
if (!this.isIndexValid(index) || !this.isLong(index)) {
|
||||
return standart;
|
||||
}
|
||||
|
||||
return Long.parseLong(this.get(index));
|
||||
}
|
||||
|
||||
public float getFloat(final int index, final float standart) {
|
||||
if (!this.isIndexValid(index) || !this.isFloat(index)) {
|
||||
return standart;
|
||||
}
|
||||
|
||||
return Float.parseFloat(this.get(index));
|
||||
}
|
||||
|
||||
public double getDouble(final int index, final double standart) {
|
||||
if (!this.isIndexValid(index) || !this.isDouble(index)) {
|
||||
return standart;
|
||||
}
|
||||
|
||||
return Double.parseDouble(this.get(index));
|
||||
}
|
||||
|
||||
|
||||
public String getString(final int index) {
|
||||
return this.getString(index, "");
|
||||
}
|
||||
|
||||
public boolean getBoolean(final int index) {
|
||||
return this.getBoolean(index, false);
|
||||
}
|
||||
|
||||
public char getChar(final int index) {
|
||||
return this.getChar(index, "A".toCharArray()[0]);
|
||||
}
|
||||
|
||||
public short getShort(final int index) {
|
||||
return this.getShort(index, (short) 0);
|
||||
}
|
||||
|
||||
public int getInteger(final int index) {
|
||||
return this.getInteger(index, 0);
|
||||
}
|
||||
|
||||
public long getLong(final int index) {
|
||||
return this.getLong(index, 0);
|
||||
}
|
||||
|
||||
public float getFloat(final int index) {
|
||||
return this.getFloat(index, 0);
|
||||
}
|
||||
|
||||
public double getDouble(final int index) {
|
||||
return this.getDouble(index, 0);
|
||||
}
|
||||
|
||||
public ArrayHelper add(final String object, final String... objects) {
|
||||
this.array = this.advance(object, objects);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public String[] advance(final String obToAdd, final String... obs) {
|
||||
String[] newArray = new String[this.getLength() + 1 + obs.length];
|
||||
|
||||
int i = 0;
|
||||
for (String ob : this.array) {
|
||||
Array.set(newArray, i, ob);
|
||||
|
||||
i++;
|
||||
}
|
||||
Array.set(newArray, i, obToAdd);
|
||||
i++;
|
||||
for (String ob : obs) {
|
||||
Array.set(newArray, i, ob);
|
||||
i++;
|
||||
}
|
||||
|
||||
return newArray;
|
||||
}
|
||||
|
||||
public String[] advanceToStrings(final String strToAdd, final String... strs) {
|
||||
String[] newArray = new String[this.getLength() + 1 + strs.length];
|
||||
|
||||
int i = 0;
|
||||
for (Object ob : this.array) {
|
||||
newArray[i] = ob.toString();
|
||||
|
||||
i++;
|
||||
}
|
||||
newArray[i] = strToAdd;
|
||||
i++;
|
||||
for (String str : strs) {
|
||||
newArray[i] = str;
|
||||
i++;
|
||||
}
|
||||
|
||||
return newArray;
|
||||
}
|
||||
|
||||
public String[] getAsArray() {
|
||||
return this.array;
|
||||
}
|
||||
|
||||
public String getAsString() {
|
||||
return this.getAsString(0, " ");
|
||||
}
|
||||
|
||||
public String getAsString(final String combiner) {
|
||||
return this.getAsString(0, combiner);
|
||||
}
|
||||
|
||||
public String getAsString(final int start) {
|
||||
return this.getAsString(start, " ");
|
||||
}
|
||||
|
||||
public String getAsString(final int start, final String combiner) {
|
||||
return this.getAsString(start, this.getLength() - 1, combiner);
|
||||
}
|
||||
|
||||
public String getAsString(final int start, final int end) {
|
||||
return this.getAsString(start, end, " ");
|
||||
}
|
||||
|
||||
public String getAsString(int start, int end, final String combiner) {
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
if (end > this.getLength() - 1) {
|
||||
end = this.getLength() - 1;
|
||||
}
|
||||
if (end < start) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String out = "";
|
||||
for (int i = start; i <= end; i++) {
|
||||
if (out.isEmpty()) {
|
||||
out = this.getString(i);
|
||||
} else {
|
||||
out += combiner + this.getString(i);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String complete = "";
|
||||
for (String t : this.array) {
|
||||
complete += (complete.isEmpty() ? "" : ", ") + t;
|
||||
}
|
||||
return "[" + complete + "]";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package net.raphimc.viaproxy.util;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
|
||||
public class LocalSocketClient {
|
||||
|
||||
private final int port;
|
||||
|
||||
public LocalSocketClient(final int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public String[] request(final String command, final String... args) {
|
||||
try {
|
||||
final Socket socket = new Socket();
|
||||
socket.setSoTimeout(500);
|
||||
socket.connect(new InetSocketAddress("127.0.0.1", this.port), 500);
|
||||
final DataOutputStream out = new DataOutputStream(socket.getOutputStream());
|
||||
final DataInputStream in = new DataInputStream(socket.getInputStream());
|
||||
|
||||
out.writeUTF(command);
|
||||
out.writeInt(args.length);
|
||||
for (String s : args) out.writeUTF(s);
|
||||
|
||||
final String[] response = new String[in.readInt()];
|
||||
for (int i = 0; i < response.length; i++) response[i] = in.readUTF();
|
||||
socket.close();
|
||||
return response;
|
||||
} catch (Throwable e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
46
src/main/java/net/raphimc/viaproxy/util/logging/Logger.java
Normal file
46
src/main/java/net/raphimc/viaproxy/util/logging/Logger.java
Normal file
@ -0,0 +1,46 @@
|
||||
package net.raphimc.viaproxy.util.logging;
|
||||
|
||||
import com.github.steveice10.mc.auth.data.GameProfile;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.Locale;
|
||||
|
||||
public class Logger {
|
||||
|
||||
public static final org.apache.logging.log4j.Logger LOGGER = LogManager.getLogger("ViaProxy");
|
||||
|
||||
public static final PrintStream SYSOUT = System.out;
|
||||
public static final PrintStream SYSERR = System.err;
|
||||
|
||||
public static void setup() {
|
||||
System.setErr(new LoggerPrintStream("STDERR", SYSERR));
|
||||
System.setOut(new LoggerPrintStream("STDOUT", SYSOUT));
|
||||
}
|
||||
|
||||
public static void u_info(final String title, final SocketAddress address, final GameProfile gameProfile, final String msg) {
|
||||
u_log(Level.INFO, title, address, gameProfile, msg);
|
||||
}
|
||||
|
||||
public static void u_err(final String title, final SocketAddress address, final GameProfile gameProfile, final String msg) {
|
||||
u_log(Level.ERROR, title, address, gameProfile, msg);
|
||||
}
|
||||
|
||||
public static void u_err(final String title, final UserConnection user, final String msg) {
|
||||
GameProfile gameProfile = null;
|
||||
if (user.getProtocolInfo().getUsername() != null) {
|
||||
gameProfile = new GameProfile(user.getProtocolInfo().getUuid(), user.getProtocolInfo().getUsername());
|
||||
}
|
||||
u_log(Level.ERROR, title, user.getChannel().remoteAddress(), gameProfile, msg);
|
||||
}
|
||||
|
||||
public static void u_log(final Level level, final String title, final SocketAddress address, final GameProfile gameProfile, final String msg) {
|
||||
final InetSocketAddress socketAddress = (InetSocketAddress) address;
|
||||
LOGGER.log(level, "[" + title.toUpperCase(Locale.ROOT) + "] (" + socketAddress.getAddress().getHostAddress() + " | " + (gameProfile != null ? gameProfile.getName() : "null") + ") " + msg);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package net.raphimc.viaproxy.util.logging;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
public class LoggerPrintStream extends PrintStream {
|
||||
|
||||
protected static final Logger LOGGER = LogManager.getLogger();
|
||||
protected final String name;
|
||||
|
||||
public LoggerPrintStream(final String name, final OutputStream out) {
|
||||
super(out);
|
||||
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void println(final String message) {
|
||||
this.log(message);
|
||||
}
|
||||
|
||||
public void println(final Object object) {
|
||||
this.log(String.valueOf(object));
|
||||
}
|
||||
|
||||
protected void log(final String message) {
|
||||
LOGGER.info("[{}]: {}", this.name, message);
|
||||
}
|
||||
|
||||
}
|
BIN
src/main/resources/assets/icons/icon.png
Normal file
BIN
src/main/resources/assets/icons/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 62 KiB |
39
src/main/resources/log4j2.xml
Normal file
39
src/main/resources/log4j2.xml
Normal file
@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="WARN">
|
||||
<Appenders>
|
||||
<!-- System out -->
|
||||
<Console name="SysOut" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%style{[%d{HH:mm:ss}]}{blue} %highlight{[%t/%level]}{FATAL=red, ERROR=red, WARN=yellow, INFO=green, DEBUG=green, TRACE=blue} %style{(%logger{1})}{cyan} %highlight{%msg%n}{FATAL=red, ERROR=red, WARN=normal, INFO=normal, DEBUG=normal, TRACE=normal}" disableAnsi="false"/>
|
||||
</Console>
|
||||
|
||||
<!-- latest.log same as vanilla -->
|
||||
<RollingRandomAccessFile name="LatestFile" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
|
||||
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level] (%logger{1}) %msg{nolookups}%n"/>
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy />
|
||||
<OnStartupTriggeringPolicy />
|
||||
</Policies>
|
||||
</RollingRandomAccessFile>
|
||||
|
||||
<!-- Debug log file -->
|
||||
<RollingRandomAccessFile name="DebugFile" fileName="logs/debug.log" filePattern="logs/debug-%i.log.gz">
|
||||
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level] (%logger) %msg{nolookups}%n"/>
|
||||
|
||||
<!-- Keep 5 files max -->
|
||||
<DefaultRolloverStrategy max="5" fileIndex="min"/>
|
||||
|
||||
<Policies>
|
||||
<SizeBasedTriggeringPolicy size="200MB"/>
|
||||
<OnStartupTriggeringPolicy />
|
||||
</Policies>
|
||||
</RollingRandomAccessFile>
|
||||
</Appenders>
|
||||
|
||||
<Loggers>
|
||||
<Root level="all">
|
||||
<AppenderRef ref="DebugFile" level="debug"/>
|
||||
<AppenderRef ref="SysOut" level="info"/>
|
||||
<AppenderRef ref="LatestFile" level="info"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
Loading…
Reference in New Issue
Block a user